├── .babelrc
├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── README.ru.md
├── assets
├── figure1.png
├── figure2.png
├── figure3.png
└── figure4.png
├── dist
├── global
│ ├── vue-simple-menu.js
│ └── vue-simple-menu.min.js
├── styles
│ ├── vue-simple-menu.default.css
│ └── vue-simple-menu.default.min.css
└── vue-simple-menu.js
├── docs
├── index.html
└── main.js
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
├── index.html
├── scripts
│ ├── index.js
│ ├── lib
│ │ ├── VueSimpleMenu.vue
│ │ └── VueSimpleMenuItem.vue
│ ├── plugin.js
│ ├── rawMenuData.js
│ └── rawMenuData4Test.js
└── styles
│ ├── default.sass
│ └── index.sass
├── test
└── index.js
├── webpack.config.js
├── webpack.docs.config.js
└── webpack.test.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": [
4 | "@babel/plugin-syntax-dynamic-import",
5 | "@babel/plugin-proposal-object-rest-spread"
6 | ],
7 | }
8 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "standard",
3 | "plugins": ["html"]
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | npm-debug.log
4 | dist/styles/*.js
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 | addons:
5 | chrome: stable
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 RG team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue simple menu
2 |
3 | Vue component for fast create simple menu block
4 |
5 | > I will be glad to correct the inaccuracy of the my English 😄
6 |
7 | [Описание на русском языке](README.ru.md)
8 |
9 | [](https://travis-ci.org/RGRU/vue-simple-menu)
10 | [](https://standardjs.com)
11 | [](https://badge.fury.io/js/vue-simple-menu)
12 |
13 | ## For whom?
14 |
15 | Simple and easy menu with a set of basic functionality, which is enought in 80% of cases:
16 | * Menu items with direct link (href="/url.html")
17 | * Compatibility with vue-router
18 | * Menu items can be toggle expand
19 | * Menu items with infinity nesting
20 | * Stylize as you want (you can select default or make and require own style)
21 |
22 | # Installation and usage
23 |
24 | ## ES6 via npm
25 |
26 | ```sh
27 | npm i vue-simple-menu -D
28 | ```
29 |
30 | ### Usage
31 |
32 | For example, we have app container, and menu component inside
33 |
34 | ```html
35 |
36 |
37 |
38 | ```
39 |
40 | For building menu, you need pass to `raw-menu-data` data of menu, must be of a certain format
41 |
42 | Params
43 |
44 | | Name | Type | Description |
45 | |:-- |:-- |:-- |
46 | | id | string | ID for item. It is link to itself id key (figure 1) ID format as you want |
47 | | name | string | Name or title for menu item element (figure 2) |
48 | | uri | string | Add link to item element (figure 3) |
49 | | list | array: object | Add children elements to item (figure 4) The structure of nesting objects repeats the main parent |
50 |
51 | __Pictures for data params__
52 |
53 | __figure 1__ Identificator for item. It is link to itself id key
54 | 
55 |
56 | __figure 2__ Name or title for menu item element
57 | 
58 |
59 | __figure 3__ Add link to item element
60 | 
61 |
62 | __figure 4__ Add children elements to item
63 | 
64 |
65 | For example file rawMenuData.js
66 |
67 | ```js
68 | export default {
69 | item1: {
70 | id: 'item1',
71 | name: 'Item 1',
72 |
73 | // Item can be as link
74 | uri: '//rg.ru'
75 | },
76 | item2: {
77 | id: 'item1',
78 | name: 'Item 1',
79 | uri: '//rg.ru',
80 |
81 | // Item can have as child items list
82 | list: {
83 | item1_1: {
84 | id: 'item1_1',
85 | name: 'Item 1_1',
86 |
87 | // List items may be endless
88 | list: {
89 | item1_1_1: {
90 | id: 'item1_1_1',
91 | name: 'Item 1_1_1',
92 | uri: '//rg.ru'
93 | }
94 | }
95 | }
96 | ...
97 | }
98 | }
99 | ...
100 | }
101 | ```
102 |
103 | And add imported menu component to Vue app. Menu data pass as component
104 |
105 | ```js
106 | import Vue from 'vue'
107 | import VueSimpleMenu from 'vue-simple-menu'
108 |
109 | // Data for menu, may get by APi or somehow else
110 | import rawMenuData from './rawMenuData'
111 |
112 | // Add style for menu
113 | require('../styles/default.sass')
114 |
115 | // Init vue application
116 | new Vue({
117 | el: '#app',
118 | data () {
119 | return {
120 |
121 | // Init default data for menu
122 | rawMenuData: {}
123 | }
124 | },
125 | components: {
126 | 'vue-simple-menu': VueSimpleMenu
127 | }
128 | })
129 |
130 | // Emulate async getting menu data
131 | setTimeout(function () {
132 | app.rawMenuData = rawMenuData
133 | }, 1000)
134 | ```
135 |
136 | ## Component as global in browser
137 |
138 | Pass to your html page scripts below
139 |
140 | ```html
141 |
142 |
143 |
144 |
145 |
146 | ```
147 |
148 | ### Usage
149 |
150 | Add element for our application with menu
151 |
152 | ```html
153 |
154 |
155 |
156 |
157 | ```
158 |
159 | And use in you scripts some as:
160 |
161 | ```js
162 | // Data for menu, may get by APi or somehow else
163 | import rawMenuData from './rawMenuData'
164 |
165 | // Init vue app with menu component in template
166 | new Vue({
167 | el: '#app',
168 | data () {
169 | return {
170 | rawMenuData: menuData
171 | }
172 | }
173 | })
174 | ```
175 |
176 | ## Usage with Vue Router
177 |
178 | You can use simple menu with [vue router](https://router.vuejs.org/en/essentials/getting-started.html) links
179 |
180 | Just add value `vueRouter: true` in rawMenuData, and items with this value will be work as vue-router link
181 |
182 | Is implied, the vue router is already connected in your app script
183 |
184 | Example below
185 |
186 | ```js
187 | articles: {
188 | id: 'articles',
189 | name: 'Статьи',
190 | uri: '/articles/list',
191 |
192 | // Add value for associate this item with vue-router
193 | vueRouter: true,
194 | ...
195 | }
196 | ```
197 |
198 | Done!
199 |
200 | ## Stylize
201 |
202 | You can use default styles for menu. Just require sass or css file to your project from styles folder in src. You should setup webpack config for processing styles (for example [css-loader](https://github.com/webpack-contrib/css-loader))
203 |
204 | Example
205 |
206 | ```js
207 | // Path where put styles in your own project
208 | require('../styles/default.sass')
209 | ```
210 |
211 | Or pass default style from CDN
212 |
213 | Example
214 |
215 | ```html
216 |
217 | ```
218 |
--------------------------------------------------------------------------------
/README.ru.md:
--------------------------------------------------------------------------------
1 | # Vue simple menu
2 |
3 | Компонент vue для быстрого создания блока с меню
4 |
5 | [](https://travis-ci.org/RGRU/vue-simple-menu)
6 | [](https://standardjs.com)
7 | [](https://badge.fury.io/js/vue-simple-menu)
8 |
9 | ## Для кого?
10 |
11 | Самое легкое меню, покрывающее своей функциональностью 80% случаев его использования:
12 | * По клику на элемент меню - переход на другую страницу
13 | * Совместимость с vue router (по клику на элемент, срабатывает хук роутера)
14 | * Элемент меню как раскрывающийся список подменю (без перехода по клику)
15 | * Вложенность подменю может быть бесконечной
16 | * Можно стилизовать блок как вам хочется или использовать как отправную точку стилизацию из этого пакета
17 |
18 | # Установка и использование
19 |
20 | ## ES6 через npm
21 |
22 | ```sh
23 | npm i vue-simple-menu -D
24 | ```
25 |
26 | ### Использование
27 |
28 | Например у нас есть контейнер `app` и компонент с меню внутри него
29 |
30 | ```html
31 |
32 |
33 |
34 | ```
35 |
36 | Для создания меню нам нужно передать в свойство `raw-menu-data` данные для меню. Причем они должны быть определенного формата, описанного ниже
37 |
38 | Параметры данных для меню
39 |
40 | | Название | Тип | Описание |
41 | |:-- |:-- |:-- |
42 | | id | string | ID для элемента, который нужно продублировать в свойство id (изображение 1) Формат id может быть любой, какой вы сами выберете |
43 | | name | string | Название элемента меню (изображение 2) |
44 | | uri | string | Ссылка с элемента меню (изображение 3) |
45 | | list | array: object | Дочерние подуровни у элемента меню (изображение 4) Структура дочерних объектов полностью повторяет основную родительскую |
46 |
47 | __Изображения к параметрам данных для меню__
48 |
49 | __Изображение 1__ ID для элемента меню
50 | 
51 |
52 | __Изображение 2__ Название элемента меню
53 | 
54 |
55 | __Изображение 3__ Добавление ссылки к элементу меню
56 | 
57 |
58 | __Изображение 4__ Добавление дочерних элементов
59 | 
60 |
61 | В итоге файл с данными получится примерно таким rawMenuData.js
62 |
63 | ```js
64 | export default {
65 | item1: {
66 | id: 'item1',
67 | name: 'Item 1',
68 |
69 | // Элемент меню как кликабельная ссылка,
70 | // из-за указания этого свойства
71 | uri: '//rg.ru'
72 | },
73 | item2: {
74 | id: 'item1',
75 | name: 'Item 1',
76 | uri: '//rg.ru',
77 |
78 | // Элемент имеет дочерние подуровни
79 | list: {
80 | item1_1: {
81 | id: 'item1_1',
82 | name: 'Item 1_1',
83 |
84 | // Подуровни могут быть бесконечными
85 | list: {
86 | item1_1_1: {
87 | id: 'item1_1_1',
88 | name: 'Item 1_1_1',
89 | uri: '//rg.ru'
90 | }
91 | }
92 | }
93 | ...
94 | }
95 | }
96 | ...
97 | }
98 | ```
99 |
100 | Добавляем компонент меню в приложение, как компонент и передаем в него заранее подготовленные данные
101 |
102 | ```js
103 | import Vue from 'vue'
104 | import VueSimpleMenu from 'vue-simple-menu'
105 |
106 | // Подготовленные данные для меню
107 | import rawMenuData from './rawMenuData'
108 |
109 | // Добавляем стили для меню
110 | require('../styles/default.sass')
111 |
112 | // Инициализация приложения
113 | new Vue({
114 | el: '#app',
115 | data () {
116 | return {
117 |
118 | // Инициализация данных для меню по-умолчанию
119 | rawMenuData: {}
120 | }
121 | },
122 |
123 | // Добавляем компонент
124 | components: {
125 | 'vue-simple-menu': VueSimpleMenu
126 | }
127 | })
128 |
129 | // Эмулируем асинхронное получение данных для меню
130 | setTimeout(function () {
131 | app.rawMenuData = rawMenuData
132 | }, 1000)
133 | ```
134 |
135 | ## Использование компонента напрямую в брузере
136 |
137 | Просто поместите на вашу страницу скрипты с библиотекой vue и самим компонентом меню
138 |
139 | ```html
140 |
141 |
142 |
143 |
144 |
145 | ```
146 |
147 | ### Использование
148 |
149 | Добавьте компонент в ваше приложение
150 |
151 | ```html
152 |
153 |
154 |
155 |
156 | ```
157 |
158 | В скриптах используйте так:
159 |
160 | ```js
161 | // Подготовленные данные для меню
162 | import rawMenuData from './rawMenuData'
163 |
164 | // Инициализируем компонент меню вместе с данными (он уже подключен глобально, отдельно его никак подключать не надо)
165 | new Vue({
166 | el: '#app',
167 | data () {
168 | return {
169 | rawMenuData: menuData
170 | }
171 | }
172 | })
173 | ```
174 | > Важное примечание. При использовании vue-simple-menu, подключая через тег script, он подключается глобально и поэтому может быть использован корректно только один на странице
175 |
176 | ## Использование вместе с vue router
177 |
178 | Вы можете использовать vue-simple-menu вместе с [vue router](https://router.vuejs.org/en/essentials/getting-started.html)
179 |
180 | Просто добавьте свойство `vueRouter: true` в подготавливаемые данные для меню, и тогда по клику на этот элемент, будет срабатывать событие, перехваченное vue router.
181 |
182 | > Подразумевается, что vue router уже подключен у вас на странице
183 |
184 | Пример
185 |
186 | ```js
187 | articles: {
188 | id: 'articles',
189 | name: 'Статьи',
190 | uri: '/articles/list',
191 |
192 | // Добавим свойство в данных, чтобы связать элемент меню с vue router
193 | vueRouter: true,
194 | ...
195 | }
196 | ```
197 |
198 | Готово!
199 |
200 | ## Стилизация
201 |
202 | Вы можете использовать стандартные стили для меню. Просто подключите sass или css файл в ваш проект из соответствующих папок репозитория vue-simple-menu. Так же вам нужно будет настроить сборку для корректной обработки стилей внутри js файла (например [css-loader](https://github.com/webpack-contrib/css-loader))
203 |
204 | Пример
205 |
206 | ```js
207 | // Подключаем стили для меню на страницу
208 | require('../styles/default.sass')
209 | ```
210 |
211 | Или подключить стандартные стили через CDN
212 |
213 | Пример
214 |
215 | ```html
216 |
217 | ```
218 |
--------------------------------------------------------------------------------
/assets/figure1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RGRU/vue-simple-menu/c08e15b20565696eb2912ba4f0c978e00be8406c/assets/figure1.png
--------------------------------------------------------------------------------
/assets/figure2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RGRU/vue-simple-menu/c08e15b20565696eb2912ba4f0c978e00be8406c/assets/figure2.png
--------------------------------------------------------------------------------
/assets/figure3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RGRU/vue-simple-menu/c08e15b20565696eb2912ba4f0c978e00be8406c/assets/figure3.png
--------------------------------------------------------------------------------
/assets/figure4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RGRU/vue-simple-menu/c08e15b20565696eb2912ba4f0c978e00be8406c/assets/figure4.png
--------------------------------------------------------------------------------
/dist/global/vue-simple-menu.js:
--------------------------------------------------------------------------------
1 | window["VueSimpleMenu"] =
2 | /******/ (function(modules) { // webpackBootstrap
3 | /******/ // The module cache
4 | /******/ var installedModules = {};
5 | /******/
6 | /******/ // The require function
7 | /******/ function __webpack_require__(moduleId) {
8 | /******/
9 | /******/ // Check if module is in cache
10 | /******/ if(installedModules[moduleId]) {
11 | /******/ return installedModules[moduleId].exports;
12 | /******/ }
13 | /******/ // Create a new module (and put it into the cache)
14 | /******/ var module = installedModules[moduleId] = {
15 | /******/ i: moduleId,
16 | /******/ l: false,
17 | /******/ exports: {}
18 | /******/ };
19 | /******/
20 | /******/ // Execute the module function
21 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22 | /******/
23 | /******/ // Flag the module as loaded
24 | /******/ module.l = true;
25 | /******/
26 | /******/ // Return the exports of the module
27 | /******/ return module.exports;
28 | /******/ }
29 | /******/
30 | /******/
31 | /******/ // expose the modules object (__webpack_modules__)
32 | /******/ __webpack_require__.m = modules;
33 | /******/
34 | /******/ // expose the module cache
35 | /******/ __webpack_require__.c = installedModules;
36 | /******/
37 | /******/ // define getter function for harmony exports
38 | /******/ __webpack_require__.d = function(exports, name, getter) {
39 | /******/ if(!__webpack_require__.o(exports, name)) {
40 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
41 | /******/ }
42 | /******/ };
43 | /******/
44 | /******/ // define __esModule on exports
45 | /******/ __webpack_require__.r = function(exports) {
46 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
47 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
48 | /******/ }
49 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
50 | /******/ };
51 | /******/
52 | /******/ // create a fake namespace object
53 | /******/ // mode & 1: value is a module id, require it
54 | /******/ // mode & 2: merge all properties of value into the ns
55 | /******/ // mode & 4: return value when already ns object
56 | /******/ // mode & 8|1: behave like require
57 | /******/ __webpack_require__.t = function(value, mode) {
58 | /******/ if(mode & 1) value = __webpack_require__(value);
59 | /******/ if(mode & 8) return value;
60 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
61 | /******/ var ns = Object.create(null);
62 | /******/ __webpack_require__.r(ns);
63 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
64 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
65 | /******/ return ns;
66 | /******/ };
67 | /******/
68 | /******/ // getDefaultExport function for compatibility with non-harmony modules
69 | /******/ __webpack_require__.n = function(module) {
70 | /******/ var getter = module && module.__esModule ?
71 | /******/ function getDefault() { return module['default']; } :
72 | /******/ function getModuleExports() { return module; };
73 | /******/ __webpack_require__.d(getter, 'a', getter);
74 | /******/ return getter;
75 | /******/ };
76 | /******/
77 | /******/ // Object.prototype.hasOwnProperty.call
78 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
79 | /******/
80 | /******/ // __webpack_public_path__
81 | /******/ __webpack_require__.p = "";
82 | /******/
83 | /******/
84 | /******/ // Load entry module and return exports
85 | /******/ return __webpack_require__(__webpack_require__.s = 0);
86 | /******/ })
87 | /************************************************************************/
88 | /******/ ([
89 | /* 0 */
90 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
91 |
92 | "use strict";
93 | __webpack_require__.r(__webpack_exports__);
94 |
95 | // CONCATENATED MODULE: ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./src/scripts/lib/VueSimpleMenuItem.vue?vue&type=template&id=52a158e0&
96 | var render = function() {
97 | var _vm = this
98 | var _h = _vm.$createElement
99 | var _c = _vm._self._c || _h
100 | return _c(
101 | "ul",
102 | { staticClass: "vue-simple-menu" },
103 | _vm._l(_vm.menu, function(item) {
104 | return _c(
105 | "li",
106 | {
107 | key: item.id,
108 | staticClass: "vue-simple-menu__item",
109 | class: {
110 | "vue-simple-menu__item_expand": item.expand,
111 | expanded: item.expanded
112 | }
113 | },
114 | [
115 | item.vueRouter
116 | ? [
117 | _c(
118 | "router-link",
119 | {
120 | staticClass: "vue-simple-menu__title vue-simple-menu__link",
121 | attrs: { to: item.uri }
122 | },
123 | [_vm._v(_vm._s(item.name))]
124 | )
125 | ]
126 | : [
127 | item.uri
128 | ? _c(
129 | "a",
130 | {
131 | staticClass:
132 | "vue-simple-menu__title vue-simple-menu__link",
133 | attrs: { href: item.uri }
134 | },
135 | [_vm._v(_vm._s(item.name))]
136 | )
137 | : _c(
138 | "span",
139 | {
140 | staticClass: "vue-simple-menu__title",
141 | on: {
142 | click: function($event) {
143 | _vm.expandTrigger(item)
144 | }
145 | }
146 | },
147 | [_vm._v(_vm._s(item.name))]
148 | )
149 | ],
150 | _vm._v(" "),
151 | item.list
152 | ? _c(
153 | "div",
154 | { staticClass: "vue-simple-menu__child" },
155 | [_c("vue-simple-menu-item", { attrs: { menu: item.list } })],
156 | 1
157 | )
158 | : _vm._e()
159 | ],
160 | 2
161 | )
162 | }),
163 | 0
164 | )
165 | }
166 | var staticRenderFns = []
167 | render._withStripped = true
168 |
169 |
170 | // CONCATENATED MODULE: ./src/scripts/lib/VueSimpleMenuItem.vue?vue&type=template&id=52a158e0&
171 |
172 | // CONCATENATED MODULE: ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options!./src/scripts/lib/VueSimpleMenuItem.vue?vue&type=script&lang=js&
173 | //
174 | //
175 | //
176 | //
177 | //
178 | //
179 | //
180 | //
181 | //
182 | //
183 | //
184 | //
185 | //
186 | //
187 | //
188 | //
189 | //
190 | //
191 | /* harmony default export */ var VueSimpleMenuItemvue_type_script_lang_js_ = ({
192 | name: 'VueSimpleMenuItem',
193 | props: {
194 | menu: {
195 | required: true,
196 | type: Array
197 | }
198 | },
199 | methods: {
200 | expandTrigger: function expandTrigger(item) {
201 | if (item.expand) item.expanded = !item.expanded;
202 | }
203 | }
204 | });
205 | // CONCATENATED MODULE: ./src/scripts/lib/VueSimpleMenuItem.vue?vue&type=script&lang=js&
206 | /* harmony default export */ var lib_VueSimpleMenuItemvue_type_script_lang_js_ = (VueSimpleMenuItemvue_type_script_lang_js_);
207 | // CONCATENATED MODULE: ./node_modules/vue-loader/lib/runtime/componentNormalizer.js
208 | /* globals __VUE_SSR_CONTEXT__ */
209 |
210 | // IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
211 | // This module is a runtime utility for cleaner component module output and will
212 | // be included in the final webpack user bundle.
213 |
214 | function normalizeComponent (
215 | scriptExports,
216 | render,
217 | staticRenderFns,
218 | functionalTemplate,
219 | injectStyles,
220 | scopeId,
221 | moduleIdentifier, /* server only */
222 | shadowMode /* vue-cli only */
223 | ) {
224 | // Vue.extend constructor export interop
225 | var options = typeof scriptExports === 'function'
226 | ? scriptExports.options
227 | : scriptExports
228 |
229 | // render functions
230 | if (render) {
231 | options.render = render
232 | options.staticRenderFns = staticRenderFns
233 | options._compiled = true
234 | }
235 |
236 | // functional template
237 | if (functionalTemplate) {
238 | options.functional = true
239 | }
240 |
241 | // scopedId
242 | if (scopeId) {
243 | options._scopeId = 'data-v-' + scopeId
244 | }
245 |
246 | var hook
247 | if (moduleIdentifier) { // server build
248 | hook = function (context) {
249 | // 2.3 injection
250 | context =
251 | context || // cached call
252 | (this.$vnode && this.$vnode.ssrContext) || // stateful
253 | (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
254 | // 2.2 with runInNewContext: true
255 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
256 | context = __VUE_SSR_CONTEXT__
257 | }
258 | // inject component styles
259 | if (injectStyles) {
260 | injectStyles.call(this, context)
261 | }
262 | // register component module identifier for async chunk inferrence
263 | if (context && context._registeredComponents) {
264 | context._registeredComponents.add(moduleIdentifier)
265 | }
266 | }
267 | // used by ssr in case component is cached and beforeCreate
268 | // never gets called
269 | options._ssrRegister = hook
270 | } else if (injectStyles) {
271 | hook = shadowMode
272 | ? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }
273 | : injectStyles
274 | }
275 |
276 | if (hook) {
277 | if (options.functional) {
278 | // for template-only hot-reload because in that case the render fn doesn't
279 | // go through the normalizer
280 | options._injectStyles = hook
281 | // register for functioal component in vue file
282 | var originalRender = options.render
283 | options.render = function renderWithStyleInjection (h, context) {
284 | hook.call(context)
285 | return originalRender(h, context)
286 | }
287 | } else {
288 | // inject component registration as beforeCreate hook
289 | var existing = options.beforeCreate
290 | options.beforeCreate = existing
291 | ? [].concat(existing, hook)
292 | : [hook]
293 | }
294 | }
295 |
296 | return {
297 | exports: scriptExports,
298 | options: options
299 | }
300 | }
301 |
302 | // CONCATENATED MODULE: ./src/scripts/lib/VueSimpleMenuItem.vue
303 |
304 |
305 |
306 |
307 |
308 | /* normalize component */
309 |
310 | var component = normalizeComponent(
311 | lib_VueSimpleMenuItemvue_type_script_lang_js_,
312 | render,
313 | staticRenderFns,
314 | false,
315 | null,
316 | null,
317 | null
318 |
319 | )
320 |
321 | /* hot reload */
322 | if (false) { var api; }
323 | component.options.__file = "src/scripts/lib/VueSimpleMenuItem.vue"
324 | /* harmony default export */ var VueSimpleMenuItem = (component.exports);
325 | // CONCATENATED MODULE: ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options!./src/scripts/lib/VueSimpleMenu.vue?vue&type=script&lang=js&
326 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
327 |
328 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
329 |
330 |
331 | /* harmony default export */ var VueSimpleMenuvue_type_script_lang_js_ = ({
332 | name: 'VueSimpleMenu',
333 | components: {
334 | 'vue-simple-menu-item': VueSimpleMenuItem
335 | },
336 | props: {
337 | rawMenuData: {
338 | type: Object,
339 | required: true
340 | },
341 | defaultName: {
342 | type: String,
343 | default: ''
344 | }
345 | },
346 | data: function data() {
347 | return {
348 | list: []
349 | };
350 | },
351 | mounted: function mounted() {
352 | if (this.rawMenuData) {
353 | this.list = this.generateBranch(this.rawMenuData);
354 | }
355 | },
356 | watch: {
357 | rawMenuData: function rawMenuData() {
358 | this.list = this.generateBranch(this.rawMenuData);
359 | }
360 | },
361 | methods: {
362 | /**
363 | * generateBranch - recursive function for generate menu branch
364 | *
365 | * @param {object} menuBranch branc menu for precessing
366 | * @return {array} complete menu data
367 | */
368 | generateBranch: function generateBranch(menuBranch) {
369 | var _this = this;
370 |
371 | return Object.keys(menuBranch).reduce(function (acc, item) {
372 | var menuItem = _objectSpread({}, menuBranch[item]); // If have child list items,
373 | // generate child branch
374 |
375 |
376 | if (menuItem.list) menuItem.list = _this.generateBranch(menuItem.list); // If item need expand behavoir
377 | // add property
378 |
379 | if (menuItem.list && !menuItem.uri) {
380 | menuItem.expand = true;
381 | menuItem.expanded = typeof menuItem.expanded === 'boolean' ? menuItem.expanded : true;
382 | }
383 |
384 | return acc.concat(menuItem);
385 | }, []);
386 | }
387 | },
388 | template: ' '
389 | });
390 | // CONCATENATED MODULE: ./src/scripts/lib/VueSimpleMenu.vue?vue&type=script&lang=js&
391 | /* harmony default export */ var lib_VueSimpleMenuvue_type_script_lang_js_ = (VueSimpleMenuvue_type_script_lang_js_);
392 | // CONCATENATED MODULE: ./src/scripts/lib/VueSimpleMenu.vue
393 | var VueSimpleMenu_render, VueSimpleMenu_staticRenderFns
394 |
395 |
396 |
397 |
398 | /* normalize component */
399 |
400 | var VueSimpleMenu_component = normalizeComponent(
401 | lib_VueSimpleMenuvue_type_script_lang_js_,
402 | VueSimpleMenu_render,
403 | VueSimpleMenu_staticRenderFns,
404 | false,
405 | null,
406 | null,
407 | null
408 |
409 | )
410 |
411 | /* hot reload */
412 | if (false) { var VueSimpleMenu_api; }
413 | VueSimpleMenu_component.options.__file = "src/scripts/lib/VueSimpleMenu.vue"
414 | /* harmony default export */ var VueSimpleMenu = (VueSimpleMenu_component.exports);
415 | // CONCATENATED MODULE: ./src/scripts/plugin.js
416 | /**
417 | * File use for create component for global element, include as script tag
418 | */
419 |
420 | var VueSimpleMenuPlugin = {
421 | install: function install(Vue) {
422 | Vue.component('vue-simple-menu', VueSimpleMenu);
423 | }
424 | };
425 |
426 | if (typeof window !== 'undefined' && window.Vue) {
427 | window.Vue.use(VueSimpleMenuPlugin);
428 | }
429 |
430 | /* harmony default export */ var scripts_plugin = __webpack_exports__["default"] = (VueSimpleMenuPlugin);
431 |
432 | /***/ })
433 | /******/ ])["default"];
--------------------------------------------------------------------------------
/dist/global/vue-simple-menu.min.js:
--------------------------------------------------------------------------------
1 | window.VueSimpleMenu=function(n){var r={};function i(e){if(r[e])return r[e].exports;var t=r[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,i),t.l=!0,t.exports}return i.m=n,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)i.d(n,r,function(e){return t[e]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i(i.s=0)}([function(e,t,n){"use strict";n.r(t);var r=function(){var n=this,e=n.$createElement,r=n._self._c||e;return r("ul",{staticClass:"vue-simple-menu"},n._l(n.menu,function(t){return r("li",{key:t.id,staticClass:"vue-simple-menu__item",class:{"vue-simple-menu__item_expand":t.expand,expanded:t.expanded}},[t.vueRouter?[r("router-link",{staticClass:"vue-simple-menu__title vue-simple-menu__link",attrs:{to:t.uri}},[n._v(n._s(t.name))])]:[t.uri?r("a",{staticClass:"vue-simple-menu__title vue-simple-menu__link",attrs:{href:t.uri}},[n._v(n._s(t.name))]):r("span",{staticClass:"vue-simple-menu__title",on:{click:function(e){n.expandTrigger(t)}}},[n._v(n._s(t.name))])],n._v(" "),t.list?r("div",{staticClass:"vue-simple-menu__child"},[r("vue-simple-menu-item",{attrs:{menu:t.list}})],1):n._e()],2)}),0)};function i(e,t,n,r,i,u,o,a){var s,l="function"==typeof e?e.options:e;if(t&&(l.render=t,l.staticRenderFns=n,l._compiled=!0),r&&(l.functional=!0),u&&(l._scopeId="data-v-"+u),o?(s=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),i&&i.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(o)},l._ssrRegister=s):i&&(s=a?function(){i.call(this,this.$root.$options.shadowRoot)}:i),s)if(l.functional){l._injectStyles=s;var c=l.render;l.render=function(e,t){return s.call(t),c(e,t)}}else{var p=l.beforeCreate;l.beforeCreate=p?[].concat(p,s):[s]}return{exports:e,options:l}}var u=i({name:"VueSimpleMenuItem",props:{menu:{required:r._withStripped=!0,type:Array}},methods:{expandTrigger:function(e){e.expand&&(e.expanded=!e.expanded)}}},r,[],!1,null,null,null);u.options.__file="src/scripts/lib/VueSimpleMenuItem.vue";var o=i({name:"VueSimpleMenu",components:{"vue-simple-menu-item":u.exports},props:{rawMenuData:{type:Object,required:!0},defaultName:{type:String,default:""}},data:function(){return{list:[]}},mounted:function(){this.rawMenuData&&(this.list=this.generateBranch(this.rawMenuData))},watch:{rawMenuData:function(){this.list=this.generateBranch(this.rawMenuData)}},methods:{generateBranch:function(r){var i=this;return Object.keys(r).reduce(function(e,t){var n=function(i){for(var e=1;e '},void 0,void 0,!1,null,null,null);o.options.__file="src/scripts/lib/VueSimpleMenu.vue";var a=o.exports,s={install:function(e){e.component("vue-simple-menu",a)}};"undefined"!=typeof window&&window.Vue&&window.Vue.use(s);t.default=s}]).default;
--------------------------------------------------------------------------------
/dist/styles/vue-simple-menu.default.css:
--------------------------------------------------------------------------------
1 | .vue-simple-menu {
2 | font-family: Helvetica, sans-serif;
3 | line-height: 1.2;
4 | margin: 0;
5 | padding: 0;
6 | list-style-type: none; }
7 | .vue-simple-menu__title {
8 | font-size: 14px;
9 | min-height: 14px;
10 | padding-top: 16px;
11 | padding-bottom: 16px;
12 | padding-left: 10px;
13 | display: block;
14 | color: white;
15 | cursor: pointer; }
16 | .vue-simple-menu__link {
17 | text-decoration: none; }
18 | .vue-simple-menu__child {
19 | padding-left: 10px; }
20 | .vue-simple-menu__item_expand.expanded {
21 | background: rgba(100, 0, 0, 0.1); }
22 | .vue-simple-menu__item_expand > .vue-simple-menu__title {
23 | position: relative;
24 | background: rgba(100, 0, 0, 0.1); }
25 | .vue-simple-menu__item_expand > .vue-simple-menu__title:after {
26 | content: '\203A';
27 | margin: auto;
28 | position: absolute;
29 | right: 10px;
30 | color: white;
31 | transition: transform .3s ease; }
32 | .vue-simple-menu__item_expand.expanded > .vue-simple-menu__title:after {
33 | transform: rotate(90deg); }
34 | .vue-simple-menu__item_expand > .vue-simple-menu__child {
35 | display: none; }
36 | .vue-simple-menu__item_expand.expanded > .vue-simple-menu__child {
37 | display: block; }
38 |
39 |
--------------------------------------------------------------------------------
/dist/styles/vue-simple-menu.default.min.css:
--------------------------------------------------------------------------------
1 | .vue-simple-menu{font-family:Helvetica,sans-serif;line-height:1.2;margin:0;padding:0;list-style-type:none}.vue-simple-menu__title{font-size:14px;min-height:14px;padding-top:16px;padding-bottom:16px;padding-left:10px;display:block;color:#fff;cursor:pointer}.vue-simple-menu__link{text-decoration:none}.vue-simple-menu__child{padding-left:10px}.vue-simple-menu__item_expand.expanded{background:rgba(100,0,0,.1)}.vue-simple-menu__item_expand>.vue-simple-menu__title{position:relative;background:rgba(100,0,0,.1)}.vue-simple-menu__item_expand>.vue-simple-menu__title:after{content:"\203A";margin:auto;position:absolute;right:10px;color:#fff;transition:transform .3s ease}.vue-simple-menu__item_expand.expanded>.vue-simple-menu__title:after{transform:rotate(90deg)}.vue-simple-menu__item_expand>.vue-simple-menu__child{display:none}.vue-simple-menu__item_expand.expanded>.vue-simple-menu__child{display:block}
--------------------------------------------------------------------------------
/dist/vue-simple-menu.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("VueSimpleMenu",[],t):"object"==typeof exports?exports.VueSimpleMenu=t():e.VueSimpleMenu=t()}(window,function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);var r=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("ul",{staticClass:"vue-simple-menu"},e._l(e.menu,function(t){return n("li",{key:t.id,staticClass:"vue-simple-menu__item",class:{"vue-simple-menu__item_expand":t.expand,expanded:t.expanded}},[t.vueRouter?[n("router-link",{staticClass:"vue-simple-menu__title vue-simple-menu__link",attrs:{to:t.uri}},[e._v(e._s(t.name))])]:[t.uri?n("a",{staticClass:"vue-simple-menu__title vue-simple-menu__link",attrs:{href:t.uri}},[e._v(e._s(t.name))]):n("span",{staticClass:"vue-simple-menu__title",on:{click:function(n){e.expandTrigger(t)}}},[e._v(e._s(t.name))])],e._v(" "),t.list?n("div",{staticClass:"vue-simple-menu__child"},[n("vue-simple-menu-item",{attrs:{menu:t.list}})],1):e._e()],2)}),0)};function i(e,t,n,r,i,u,o,a){var s,l="function"==typeof e?e.options:e;if(t&&(l.render=t,l.staticRenderFns=n,l._compiled=!0),r&&(l.functional=!0),u&&(l._scopeId="data-v-"+u),o?(s=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),i&&i.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(o)},l._ssrRegister=s):i&&(s=a?function(){i.call(this,this.$root.$options.shadowRoot)}:i),s)if(l.functional){l._injectStyles=s;var c=l.render;l.render=function(e,t){return s.call(t),c(e,t)}}else{var p=l.beforeCreate;l.beforeCreate=p?[].concat(p,s):[s]}return{exports:e,options:l}}r._withStripped=!0;var u=i({name:"VueSimpleMenuItem",props:{menu:{required:!0,type:Array}},methods:{expandTrigger:function(e){e.expand&&(e.expanded=!e.expanded)}}},r,[],!1,null,null,null);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}u.options.__file="src/scripts/lib/VueSimpleMenuItem.vue";var a=i({name:"VueSimpleMenu",components:{"vue-simple-menu-item":u.exports},props:{rawMenuData:{type:Object,required:!0},defaultName:{type:String,default:""}},data:function(){return{list:[]}},mounted:function(){this.rawMenuData&&(this.list=this.generateBranch(this.rawMenuData))},watch:{rawMenuData:function(){this.list=this.generateBranch(this.rawMenuData)}},methods:{generateBranch:function(e){var t=this;return Object.keys(e).reduce(function(n,r){var i=function(e){for(var t=1;t '},void 0,void 0,!1,null,null,null);a.options.__file="src/scripts/lib/VueSimpleMenu.vue";t.default=a.exports}])});
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vue simple menu demo
8 |
9 |
10 |
11 |
14 |
15 |
Demo example Vue Simple menu
16 |
17 | Элементы с пометкой with vue router привязаны к раутингу через vue router
18 | Элементы в данных которых не указано поле name выводят пустой блок
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | const webpackConfig = require('./webpack.test.config')
2 |
3 | module.exports = function(config) {
4 | config.set({
5 | browsers: ['ChromeHeadless'],
6 | frameworks: ['mocha', 'chai'],
7 | singleRun: true,
8 | autoWatch: false,
9 | reporters: ['mocha'],
10 | files: ['./test/index.js'],
11 | preprocessors: {
12 | './test/index.js': ['webpack']
13 | },
14 | webpack: webpackConfig,
15 | webpackMiddleware: {
16 | noInfo: true
17 | }
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-simple-menu",
3 | "version": "0.1.0",
4 | "description": "vue component for fast create simple menu block",
5 | "main": "dist/vue-simple-menu.js",
6 | "unpkg": "dist/global/vue-simple-menu.min.js",
7 | "files": [
8 | "dist"
9 | ],
10 | "scripts": {
11 | "test": "karma start karma.conf.js",
12 | "build": "npm run test && webpack --mode production && npm run docs",
13 | "build:watch": "webpack --mode production --watch && webpack --config webpack.docs.config.js",
14 | "docs": "webpack --config webpack.docs.config.js",
15 | "dev": "webpack-dev-server --config webpack.docs.config.js --watch"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/RGRU/vue-simple-menu.git"
20 | },
21 | "keywords": [
22 | "vue",
23 | "menu",
24 | "vue component",
25 | "simple menu"
26 | ],
27 | "author": "RGRU team | nanomen (https://github.com/RGRU/)",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/RGRU/vue-simple-menu/issues"
31 | },
32 | "homepage": "https://github.com/RGRU/vue-simple-menu#readme",
33 | "devDependencies": {
34 | "@babel/core": "^7.2.2",
35 | "@babel/plugin-proposal-object-rest-spread": "^7.3.1",
36 | "@babel/plugin-syntax-dynamic-import": "^7.2.0",
37 | "@babel/preset-env": "^7.3.1",
38 | "babel-loader": "^8.0.5",
39 | "chai": "^4.1.2",
40 | "clean-webpack-plugin": "^1.0.1",
41 | "css-loader": "^2.1.0",
42 | "eslint": "^4.12.1",
43 | "eslint-config-standard": "^10.2.1",
44 | "eslint-loader": "^2.1.1",
45 | "eslint-plugin-html": "^4.0.1",
46 | "eslint-plugin-import": "^2.8.0",
47 | "eslint-plugin-node": "^5.2.1",
48 | "eslint-plugin-promise": "^3.6.0",
49 | "eslint-plugin-standard": "^3.0.1",
50 | "html-webpack-plugin": "^3.2.0",
51 | "karma": "^4.0.0",
52 | "karma-chai": "^0.1.0",
53 | "karma-chrome-launcher": "^2.2.0",
54 | "karma-mocha": "^1.3.0",
55 | "karma-mocha-reporter": "^2.2.5",
56 | "karma-webpack": "^3.0.5",
57 | "mini-css-extract-plugin": "^0.5.0",
58 | "mocha": "^4.0.1",
59 | "node-sass": "^4.11.0",
60 | "optimize-css-assets-webpack-plugin": "^5.0.1",
61 | "sass-loader": "^7.1.0",
62 | "style-loader": "^0.23.1",
63 | "uglifyjs-webpack-plugin": "^2.1.1",
64 | "vue-loader": "^15.6.2",
65 | "vue-router": "^3.0.1",
66 | "vue-template-compiler": "^2.5.9",
67 | "webpack": "^4.29.0",
68 | "webpack-cli": "^3.2.1",
69 | "webpack-dev-server": "^3.1.14"
70 | },
71 | "dependencies": {
72 | "vue": "^2.5.9"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= htmlWebpackPlugin.options.title %>
8 |
9 |
10 |
11 |
14 |
15 |
Demo example Vue Simple menu
16 |
17 | Элементы с пометкой with vue router привязаны к раутингу через vue router
18 | Элементы в данных которых не указано поле name выводят пустой блок
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/scripts/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueSimpleMenu from './lib/VueSimpleMenu.vue'
3 | import rawMenuData from './rawMenuData'
4 |
5 | import VueRouter from 'vue-router'
6 |
7 | Vue.use(VueRouter)
8 |
9 | // Init vue application
10 | const app = new Vue({
11 | el: '#app',
12 | router: new VueRouter({
13 | routes: [
14 | { path: '/articles/list', component: { template: 'Статьи ' } },
15 | { path: '/rubrics/org', component: { template: 'Организации ' } },
16 | { path: '/rubrics/reg', component: { template: 'Регионы ' } }
17 | ]
18 | }),
19 | data () {
20 | return {
21 | rawMenuData: {}
22 | }
23 | },
24 | components: {
25 | 'vue-simple-menu': VueSimpleMenu
26 | }
27 | })
28 |
29 | // Add global style
30 | require('../styles/index.sass')
31 |
32 | // Add style for menu
33 | require('../styles/default.sass')
34 |
35 | // Emulate async
36 | setTimeout(function () {
37 | app.rawMenuData = rawMenuData
38 | }, 1000)
39 |
--------------------------------------------------------------------------------
/src/scripts/lib/VueSimpleMenu.vue:
--------------------------------------------------------------------------------
1 |
63 |
--------------------------------------------------------------------------------
/src/scripts/lib/VueSimpleMenuItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
35 |
--------------------------------------------------------------------------------
/src/scripts/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * File use for create component for global element, include as script tag
3 | */
4 |
5 | import VueSimpleMenu from './lib/VueSimpleMenu.vue'
6 |
7 | const VueSimpleMenuPlugin = {
8 | install (Vue) {
9 | Vue.component('vue-simple-menu', VueSimpleMenu)
10 | }
11 | }
12 |
13 | if (typeof window !== 'undefined' && window.Vue) {
14 | window.Vue.use(VueSimpleMenuPlugin)
15 | }
16 |
17 | export default VueSimpleMenuPlugin
18 |
--------------------------------------------------------------------------------
/src/scripts/rawMenuData.js:
--------------------------------------------------------------------------------
1 | export default {
2 |
3 | // Элементы меню
4 | articles: {
5 |
6 | // Параметры элемента
7 | id: 'articles',
8 | name: 'Статьи - with vue Router',
9 | vueRouter: true,
10 | uri: '/articles/list',
11 |
12 | // Если есть вложенность
13 | list: {
14 | item1: {
15 | id: 'item1',
16 | name: 'Вложенность 1.1'
17 | },
18 | item2: {
19 | id: 'item2',
20 | name: 'Вложенность 2.1',
21 | uri: '/test',
22 | list: {
23 | i1: {
24 | id: 'i1',
25 | name: 'Вложенность 2.1'
26 | },
27 | i2: {
28 | id: 'i2',
29 | name: 'Вложенность 2.2 - vue Router',
30 | list: {
31 | i1: {
32 | id: 'i1',
33 | name: 'Вложенность 3.1'
34 | },
35 | i2: {
36 | id: 'i2',
37 | name: 'Вложенность 3.2',
38 | uri: '/test2'
39 | },
40 | i3: {
41 | id: 'i3',
42 | name: 'Вложенность 3.3'
43 | }
44 | }
45 | },
46 | i3: {
47 | id: 'i3',
48 | name: 'Вложенность 2.3'
49 | }
50 | }
51 | }
52 | }
53 | },
54 |
55 | blocks: {
56 | id: 'blocks',
57 | name: 'Блоки',
58 | uri: '/blocks/list'
59 | },
60 |
61 | auth: {
62 | id: 'auth',
63 | list: {
64 | roles: {
65 | id: 'roles',
66 | name: 'Роли',
67 | uri: '/roles/list'
68 | },
69 | users: {
70 | id: 'users',
71 | name: 'Пользователи',
72 | uri: '/users/list'
73 | }
74 | }
75 | },
76 |
77 | masks: {
78 | id: 'masks',
79 | name: 'Маски'
80 | },
81 |
82 | sujets: {
83 | id: 'sujets',
84 | name: 'Сюжеты',
85 | uri: '/sujets/list'
86 | },
87 |
88 | rubrics: {
89 | id: 'rubrics',
90 | name: 'Рубрики',
91 | expanded: false,
92 | list: {
93 | thema: {
94 | id: 'thema',
95 | name: 'Тематический рубрикатор',
96 | uri: '/rubrics/thema',
97 | list: {
98 | item1: {
99 | id: 'item1',
100 | name: 'Вложенность 1.1'
101 | },
102 | item2: {
103 | id: 'item2',
104 | name: 'Вложенность 2.1',
105 | uri: '/test',
106 | list: {
107 | i1: {
108 | id: 'i1',
109 | name: 'Вложенность 2.1'
110 | },
111 | i2: {
112 | id: 'i2',
113 | name: 'Вложенность 2.2',
114 | list: {
115 | i1: {
116 | id: 'i1',
117 | name: 'Вложенность 3.1'
118 | },
119 | i2: {
120 | id: 'i2',
121 | name: 'Вложенность 3.2',
122 | uri: '/test2'
123 | },
124 | i3: {
125 | id: 'i3',
126 | name: 'Вложенность 3.3'
127 | }
128 | }
129 | },
130 | i3: {
131 | id: 'i3',
132 | name: 'Вложенность 2.3'
133 | }
134 | }
135 | }
136 | }
137 | },
138 | org: {
139 | id: 'org',
140 | name: 'Организации - with vue Router',
141 | vueRouter: true,
142 | uri: '/rubrics/org'
143 | },
144 | reg: {
145 | id: 'reg',
146 | name: 'Регионы - with vue Router',
147 | vueRouter: true,
148 | uri: '/rubrics/reg'
149 | }
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/scripts/rawMenuData4Test.js:
--------------------------------------------------------------------------------
1 | export default {
2 |
3 | // Элементы меню
4 | articles: {
5 |
6 | // Параметры элемента
7 | id: 'articles',
8 | name: 'Статьи',
9 | uri: '/articles/list',
10 |
11 | // Если есть вложенность
12 | list: {
13 | item1: {
14 | id: 'item1',
15 | name: 'Вложенность 1.1'
16 | },
17 | item2: {
18 | id: 'item2',
19 | name: 'Вложенность 2.1',
20 | uri: '/test',
21 | list: {
22 | i1: {
23 | id: 'i1',
24 | name: 'Вложенность 2.1'
25 | },
26 | i2: {
27 | id: 'i2',
28 | name: 'Вложенность 2.2',
29 | list: {
30 | i1: {
31 | id: 'i1',
32 | name: 'Вложенность 3.1'
33 | },
34 | i2: {
35 | id: 'i2',
36 | name: 'Вложенность 3.2',
37 | uri: '/test2'
38 | },
39 | i3: {
40 | id: 'i3',
41 | name: 'Вложенность 3.3'
42 | }
43 | }
44 | },
45 | i3: {
46 | id: 'i3',
47 | name: 'Вложенность 2.3'
48 | }
49 | }
50 | }
51 | }
52 | },
53 |
54 | blocks: {
55 | id: 'blocks',
56 | name: 'Блоки',
57 | uri: '/blocks/list'
58 | },
59 |
60 | auth: {
61 | id: 'auth',
62 | list: {
63 | roles: {
64 | id: 'roles',
65 | name: 'Роли',
66 | uri: '/roles/list'
67 | },
68 | users: {
69 | id: 'users',
70 | name: 'Пользователи',
71 | uri: '/users/list'
72 | }
73 | }
74 | },
75 |
76 | masks: {
77 | id: 'masks',
78 | name: 'Маски'
79 | },
80 |
81 | sujets: {
82 | id: 'sujets',
83 | name: 'Сюжеты',
84 | uri: '/sujets/list'
85 | },
86 |
87 | rubrics: {
88 | id: 'rubrics',
89 | name: 'Рубрики',
90 | expanded: false,
91 | list: {
92 | thema: {
93 | id: 'thema',
94 | name: 'Тематический рубрикатор',
95 | uri: '/rubrics/thema',
96 | list: {
97 | item1: {
98 | id: 'item1',
99 | name: 'Вложенность 1.1'
100 | },
101 | item2: {
102 | id: 'item2',
103 | name: 'Вложенность 2.1',
104 | uri: '/test',
105 | list: {
106 | i1: {
107 | id: 'i1',
108 | name: 'Вложенность 2.1'
109 | },
110 | i2: {
111 | id: 'i2',
112 | name: 'Вложенность 2.2',
113 | list: {
114 | i1: {
115 | id: 'i1',
116 | name: 'Вложенность 3.1'
117 | },
118 | i2: {
119 | id: 'i2',
120 | name: 'Вложенность 3.2',
121 | uri: '/test2'
122 | },
123 | i3: {
124 | id: 'i3',
125 | name: 'Вложенность 3.3'
126 | }
127 | }
128 | },
129 | i3: {
130 | id: 'i3',
131 | name: 'Вложенность 2.3'
132 | }
133 | }
134 | }
135 | }
136 | },
137 | org: {
138 | id: 'org',
139 | name: 'Организации',
140 | uri: '/rubrics/org'
141 | },
142 | reg: {
143 | id: 'reg',
144 | name: 'Регионы',
145 | uri: '/rubrics/reg'
146 | }
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/styles/default.sass:
--------------------------------------------------------------------------------
1 | $block: '.vue-simple-menu'
2 |
3 | $ff: 'Helvetica, sans-serif'
4 | $c-expand: rgba(100, 0, 0, .1)
5 | $c-expanded: rgba(100, 0, 0, .1)
6 | $c-text: white
7 |
8 | $height: 14px
9 | $indent-v: 16px
10 | $indent-g: 10px
11 |
12 | #{$block}
13 | font-family: #{$ff}
14 | line-height: 1.2
15 | margin: 0
16 | padding: 0
17 | list-style-type: none
18 |
19 | &__title
20 | font-size: $height
21 | min-height: $height
22 | padding-top: $indent-v
23 | padding-bottom: $indent-v
24 | padding-left: $indent-g
25 | display: block
26 | color: $c-text
27 | cursor: pointer
28 |
29 | &__link
30 | text-decoration: none
31 |
32 | &__item
33 |
34 | &__child
35 | padding-left: $indent-g
36 |
37 | &__item_expand
38 |
39 | &.expanded
40 | background: $c-expanded
41 |
42 | &__item_expand > &__title
43 | position: relative
44 | background: $c-expand
45 |
46 | &:after
47 | content: '\203A'
48 | margin: auto
49 | position: absolute
50 | right: $indent-g
51 | color: $c-text
52 | transition: transform .3s ease
53 |
54 | &__item_expand.expanded > &__title
55 |
56 | &:after
57 | transform: rotate(90deg)
58 |
59 | &__item_expand > &__child
60 | display: none
61 |
62 | &__item_expand.expanded > &__child
63 | display: block
64 |
--------------------------------------------------------------------------------
/src/styles/index.sass:
--------------------------------------------------------------------------------
1 | /**
2 | * This styles for demo page (docs)
3 | */
4 |
5 | html,
6 | body,
7 | .app
8 | font-family: sans-serif
9 | height: 100%
10 | margin: 0
11 | padding: 0
12 |
13 | .sidebar
14 | width: 300px
15 | float: left
16 | background: #35353b
17 | background: #b388ff
18 |
19 | .main
20 | width: calc(100% - 340px)
21 | min-height: 100%
22 | padding: 20px
23 | float: right
24 | background: #e4e9f0
25 |
26 | .title
27 | font-size: 24px
28 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | import VueSimpleMenu from '../src/scripts/lib/VueSimpleMenu.vue'
4 | import rawMenuData from '../src/scripts/rawMenuData4Test'
5 |
6 | describe('VueSimpleMenu.vue', () => {
7 | describe('# Init', () => {
8 | it('Component has registered in components list', done => {
9 |
10 | const vm = new Vue({
11 | template: '
',
12 | data () {
13 | return {
14 | rawMenuData: {}
15 | }
16 | },
17 | components: {
18 | 'vue-simple-menu': VueSimpleMenu
19 | }
20 | }).$mount()
21 |
22 | // Find component in components list
23 | vm.$nextTick(() => {
24 | expect(typeof vm.$options.components['vue-simple-menu']).to.equal('object')
25 |
26 | done()
27 | })
28 | })
29 |
30 | it('Render after mount', done => {
31 |
32 | const vm = new Vue({
33 | template: '
',
34 | data() {
35 | return {
36 | rawMenuData: {}
37 | }
38 | },
39 | components: {
40 | 'vue-simple-menu': VueSimpleMenu
41 | }
42 | }).$mount()
43 |
44 | vm.$nextTick(() => {
45 | // Find rendered menu component
46 | expect(!!vm.$el.querySelector('.vue-simple-menu')).to.be.true
47 |
48 | done()
49 | })
50 | })
51 |
52 | it('Render empty component when no raw-menu-data', done => {
53 |
54 | const vm = new Vue({
55 | template: '
',
56 | data () {
57 | return {
58 | rawMenuData: {}
59 | }
60 | },
61 | components: {
62 | 'vue-simple-menu': VueSimpleMenu
63 | }
64 | }).$mount()
65 |
66 | vm.$nextTick(() => {
67 | // Find rendered menu component
68 | expect(!!vm.$el.querySelector('.vue-simple-menu')).to.be.true
69 |
70 | // If raw data no pass, item elements will not create
71 | expect(vm.$el.querySelectorAll('.vue-simple-menu__item').length === 0).to.be.true
72 |
73 | done()
74 | })
75 | })
76 | })
77 |
78 | describe('# Temlpate structure', () => {
79 | it('Expect list items when pass raw data when created app', done => {
80 |
81 | const vm = new Vue({
82 | template: '
',
83 | data () {
84 | return {
85 | rawMenuData
86 | }
87 | },
88 | components: {
89 | 'vue-simple-menu': VueSimpleMenu
90 | }
91 | }).$mount()
92 |
93 | vm.$nextTick(() => {
94 | // If raw data pass, item elements exist
95 | expect(vm.$el.querySelectorAll('.vue-simple-menu__item').length > 0).to.be.true
96 |
97 | done()
98 | })
99 | })
100 |
101 | it('Expect list items when pass raw data async', done => {
102 |
103 | const vm = new Vue({
104 | template: '
',
105 | data () {
106 | return {
107 | rawMenuData: {}
108 | }
109 | },
110 | components: {
111 | 'vue-simple-menu': VueSimpleMenu
112 | }
113 | }).$mount()
114 |
115 | vm.$nextTick(() => {
116 | // If raw data no pass, item elements will not create
117 | expect(vm.$el.querySelectorAll('.vue-simple-menu__item').length === 0).to.be.true
118 |
119 | // Set raw data
120 | setTimeout(() => {
121 | vm.rawMenuData = rawMenuData
122 |
123 | vm.$nextTick(() => {
124 | // If raw data pass, item elements exist
125 | expect(vm.$el.querySelectorAll('.vue-simple-menu__item').length > 0).to.be.true
126 |
127 | done()
128 | })
129 | }, 1000)
130 | })
131 | })
132 |
133 | it('Check create child branch', done => {
134 |
135 | const vm = new Vue({
136 | template: '
',
137 | data () {
138 | return {
139 | rawMenuData
140 | }
141 | },
142 | components: {
143 | 'vue-simple-menu': VueSimpleMenu
144 | }
145 | }).$mount()
146 |
147 | vm.$nextTick(() => {
148 | // Find child elements
149 | expect(vm.$el.querySelectorAll('.vue-simple-menu__child').length > 0).to.be.true
150 |
151 | done()
152 | })
153 | })
154 |
155 | it('Check create current item name', done => {
156 |
157 | const vm = new Vue({
158 | template: '
',
159 | data () {
160 | return {
161 | rawMenuData
162 | }
163 | },
164 | components: {
165 | 'vue-simple-menu': VueSimpleMenu
166 | }
167 | }).$mount()
168 |
169 | vm.$nextTick(() => {
170 | // Check item name first level (articles)
171 | expect(
172 | vm.$el
173 | .querySelectorAll('.vue-simple-menu > .vue-simple-menu__item')[0]
174 | .querySelector('.vue-simple-menu__link').innerHTML
175 | ).to.equal(rawMenuData.articles.name)
176 |
177 | // Check item name from child level (rubrics -> thema -> item2 -> i2 -> i1 === Вложенность 3.1)
178 | expect(
179 | vm.$el
180 | .querySelector('.vue-simple-menu > .vue-simple-menu__item:nth-child(6)')
181 | .querySelector('.vue-simple-menu__child > .vue-simple-menu > .vue-simple-menu__item:nth-child(1)')
182 | .querySelector('.vue-simple-menu__child > .vue-simple-menu > .vue-simple-menu__item:nth-child(2)')
183 | .querySelector('.vue-simple-menu__child > .vue-simple-menu > .vue-simple-menu__item:nth-child(2)')
184 | .querySelector('.vue-simple-menu__child > .vue-simple-menu > .vue-simple-menu__item:nth-child(1)')
185 | .querySelector('.vue-simple-menu__title')
186 | .innerHTML
187 | ).to.equal(rawMenuData.rubrics.list.thema.list.item2.list.i2.list.i1.name)
188 |
189 | done()
190 | })
191 | })
192 |
193 | it('Check create link in item name', done => {
194 | const vm = new Vue({
195 | template: '
',
196 | data () {
197 | return {
198 | rawMenuData
199 | }
200 | },
201 | components: {
202 | 'vue-simple-menu': VueSimpleMenu
203 | }
204 | }).$mount()
205 |
206 | vm.$nextTick(() => {
207 | // Check item link first level (articles)
208 | expect(
209 | vm.$el
210 | .querySelectorAll('.vue-simple-menu > .vue-simple-menu__item')[0]
211 | .querySelector('.vue-simple-menu__link')
212 | .getAttribute('href')
213 | ).to.equal(rawMenuData.articles.uri)
214 |
215 | // Check item link from child level (articles -> thema -> item2 -> i2 -> i2 === /test2)
216 | expect(
217 | vm.$el
218 | .querySelector('.vue-simple-menu > .vue-simple-menu__item:nth-child(1)')
219 | .querySelector('.vue-simple-menu__child > .vue-simple-menu > .vue-simple-menu__item:nth-child(2)')
220 | .querySelector('.vue-simple-menu__child > .vue-simple-menu > .vue-simple-menu__item:nth-child(2)')
221 | .querySelector('.vue-simple-menu__link')
222 | .getAttribute('href')
223 | ).to.equal(rawMenuData.articles.list.item2.list.i2.list.i2.uri)
224 |
225 | done()
226 | })
227 | })
228 |
229 | it('Items with vue-router', done => {
230 | Vue.use(VueRouter)
231 |
232 | const router = new VueRouter({
233 | routes: [
234 | {
235 | path: '/articles/list',
236 | component: { template: '' }
237 | }
238 | ]
239 | })
240 |
241 | const vm = new Vue({
242 | template: `
243 |
244 |
245 |
246 |
`,
247 | router,
248 | data () {
249 | return {
250 | rawMenuData
251 | }
252 | },
253 | components: {
254 | 'vue-simple-menu': VueSimpleMenu
255 | }
256 | }).$mount()
257 |
258 | // Emulate click event
259 | router.push('/articles/list')
260 |
261 | vm.$nextTick(() => {
262 | expect(!!vm.$el.querySelector('#childMenu')).to.be.true
263 |
264 | // go back (to default route)
265 | router.go(-1)
266 |
267 | done()
268 | })
269 | })
270 |
271 | it('Several menu components in page', done => {
272 | Vue.use(VueRouter)
273 |
274 | const ArticlesList = {
275 | name: 'ArticlesList',
276 | data () {
277 | return {
278 | rawMenuDataTwo: {
279 | item1: {
280 | id: 'item1',
281 | name: 'Item 1',
282 | uri: '//rg.ru'
283 | },
284 | item2: {
285 | id: 'item2',
286 | name: 'Item 1',
287 | list: {
288 | item1_1: {
289 | id: 'item1_1',
290 | name: 'Item 1_1',
291 | list: {
292 | item1_1_1: {
293 | id: 'item1_1_1',
294 | name: 'Item 1_1_1',
295 | uri: '//rg.ru'
296 | }
297 | }
298 | }
299 | }
300 | }
301 | }
302 | }
303 | },
304 | components: {
305 | 'vue-simple-menu': VueSimpleMenu
306 | },
307 | template: `
308 | `
314 | }
315 |
316 | const router = new VueRouter({
317 | routes: [
318 | {
319 | path: '/articles/list',
320 | component: ArticlesList
321 | }
322 | ]
323 | })
324 |
325 | const vm = new Vue({
326 | template: `
327 |
328 |
329 |
330 |
`,
331 | router,
332 | data () {
333 | return {
334 | rawMenuData
335 | }
336 | },
337 | components: {
338 | 'vue-simple-menu': VueSimpleMenu
339 | }
340 | }).$mount()
341 |
342 | // Emulate click event
343 | router.push('/articles/list')
344 |
345 | vm.$nextTick(() => {
346 | expect(!!vm.$el.querySelector('#childMenu .vue-simple-menu')).to.be.true
347 |
348 | // go back (to default route)
349 | router.go(-1)
350 |
351 | done()
352 | })
353 | })
354 | })
355 |
356 | describe('# Behavior', () => {
357 | it('Trigger expand menu on click first level items', done => {
358 |
359 | const vm = new Vue({
360 | template: '
',
361 | data () {
362 | return {
363 | rawMenuData
364 | }
365 | },
366 | components: {
367 | 'vue-simple-menu': VueSimpleMenu
368 | }
369 | }).$mount()
370 |
371 | // Emulate click event
372 | // https://developer.mozilla.org/ru/docs/Web/API/Document/createEvent
373 | const event = document.createEvent('Event')
374 | event.initEvent('click', true, true)
375 |
376 | vm.$nextTick(() => {
377 | let targetItem = vm.$el.querySelector('.wrapper > .vue-simple-menu > .vue-simple-menu__item:nth-child(3)')
378 | let expandStartState = targetItem.classList.contains('expanded')
379 |
380 | // Emulate click event
381 | targetItem.querySelector('.vue-simple-menu__title').dispatchEvent(event)
382 |
383 | // Expand off
384 | vm.$nextTick(() => {
385 | expect(targetItem.classList.contains('expanded')).to.not.equal(expandStartState)
386 |
387 | done()
388 | })
389 | })
390 | })
391 |
392 | it('Trigger expand menu on click children items', done => {
393 |
394 | const vm = new Vue({
395 | template: '
',
396 | data () {
397 | return {
398 | rawMenuData
399 | }
400 | },
401 | components: {
402 | 'vue-simple-menu': VueSimpleMenu
403 | }
404 | }).$mount()
405 |
406 | // Emulate click event
407 | // https://developer.mozilla.org/ru/docs/Web/API/Document/createEvent
408 | const event = document.createEvent('Event')
409 | event.initEvent('click', true, true)
410 |
411 | vm.$nextTick(() => {
412 | let targetItem = vm.$el
413 | .querySelector('.wrapper > .vue-simple-menu > .vue-simple-menu__item:nth-child(1)')
414 | .querySelector('.vue-simple-menu__child > .vue-simple-menu > .vue-simple-menu__item:nth-child(2)')
415 | .querySelector('.vue-simple-menu__child > .vue-simple-menu > .vue-simple-menu__item:nth-child(2)')
416 |
417 | let expandStartState = targetItem.classList.contains('expanded')
418 |
419 | // Emulate click event
420 | targetItem.querySelector('.vue-simple-menu__title').dispatchEvent(event)
421 |
422 | // Expand off
423 | vm.$nextTick(() => {
424 | expect(targetItem.classList.contains('expanded')).to.not.equal(expandStartState)
425 |
426 | done()
427 | })
428 | })
429 | })
430 |
431 | it('Turn off menu item if its data has a expanded:false property', done => {
432 |
433 | const vm = new Vue({
434 | template: '
',
435 | data() {
436 | return {
437 | rawMenuData
438 | }
439 | },
440 | components: {
441 | 'vue-simple-menu': VueSimpleMenu
442 | }
443 | }).$mount()
444 |
445 | vm.$nextTick(() => {
446 | let itemEl = vm.$el.querySelectorAll('.vue-simple-menu')[0].lastChild
447 |
448 | expect(itemEl.classList.contains('vue-simple-menu__item_expand')).to.be.true
449 | expect(itemEl.classList.contains('expanded')).to.be.false
450 |
451 | done()
452 | })
453 | })
454 | })
455 | })
456 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
2 | const path = require('path')
3 | const VueLoaderPlugin = require('vue-loader/lib/plugin')
4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
5 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
6 |
7 | const config = {
8 | module: {
9 | rules: [
10 | {
11 | test: /\.(js|vue)&/,
12 | enforce: 'pre',
13 | use: ['eslint-loader']
14 | },
15 | {
16 | test: /\.js$/,
17 | loader: 'babel-loader'
18 | },
19 | {
20 | test: /\.vue$/,
21 | loader: 'vue-loader'
22 | }
23 | ]
24 | }
25 | }
26 |
27 | const config4Styles = {
28 | output: {
29 | path: path.join(__dirname, 'dist/styles'),
30 | filename: '[name].js'
31 | },
32 | plugins: [
33 | new MiniCssExtractPlugin({
34 | filename: '[name].css'
35 | })
36 | ]
37 | }
38 |
39 | module.exports = [
40 |
41 | // Build as UMD module
42 | Object.assign(
43 | {},
44 | config,
45 | {
46 | entry: path.join(__dirname, 'src/scripts/lib/VueSimpleMenu.vue'),
47 | output: {
48 | path: path.join(__dirname, 'dist'),
49 | filename: 'vue-simple-menu.js',
50 | libraryTarget: 'umd',
51 | library: 'VueSimpleMenu',
52 | umdNamedDefine: true
53 | },
54 | plugins: [
55 | new VueLoaderPlugin()
56 | ]
57 | }
58 | ),
59 |
60 | // Build for using in browser
61 | // as