1 | <script type="text/jsx">
|
2 | import emitter from 'element-ui/src/mixins/emitter';
|
3 | import Migrating from 'element-ui/src/mixins/migrating';
|
4 | import Menubar from 'element-ui/src/utils/menu/aria-menubar';
|
5 | import { addClass, removeClass, hasClass } from 'element-ui/src/utils/dom';
|
6 |
|
7 | export default {
|
8 | name: 'ElMenu',
|
9 |
|
10 | render (h) {
|
11 | const component = (
|
12 | <ul
|
13 | role="menubar"
|
14 | key={ +this.collapse }
|
15 | style={{ backgroundColor: this.backgroundColor || '' }}
|
16 | class={{
|
17 | 'el-menu--horizontal': this.mode === 'horizontal',
|
18 | 'el-menu--collapse': this.collapse,
|
19 | "el-menu": true
|
20 | }}
|
21 | >
|
22 | { this.$slots.default }
|
23 | </ul>
|
24 | );
|
25 |
|
26 | if (this.collapseTransition) {
|
27 | return (
|
28 | <el-menu-collapse-transition>
|
29 | { component }
|
30 | </el-menu-collapse-transition>
|
31 | );
|
32 | } else {
|
33 | return component;
|
34 | }
|
35 | },
|
36 |
|
37 | componentName: 'ElMenu',
|
38 |
|
39 | mixins: [emitter, Migrating],
|
40 |
|
41 | provide() {
|
42 | return {
|
43 | rootMenu: this
|
44 | };
|
45 | },
|
46 |
|
47 | components: {
|
48 | 'el-menu-collapse-transition': {
|
49 | functional: true,
|
50 | render(createElement, context) {
|
51 | const data = {
|
52 | props: {
|
53 | mode: 'out-in'
|
54 | },
|
55 | on: {
|
56 | beforeEnter(el) {
|
57 | el.style.opacity = 0.2;
|
58 | },
|
59 |
|
60 | enter(el) {
|
61 | addClass(el, 'el-opacity-transition');
|
62 | el.style.opacity = 1;
|
63 | },
|
64 |
|
65 | afterEnter(el) {
|
66 | removeClass(el, 'el-opacity-transition');
|
67 | el.style.opacity = '';
|
68 | },
|
69 |
|
70 | beforeLeave(el) {
|
71 | if (!el.dataset) el.dataset = {};
|
72 |
|
73 | if (hasClass(el, 'el-menu--collapse')) {
|
74 | removeClass(el, 'el-menu--collapse');
|
75 | el.dataset.oldOverflow = el.style.overflow;
|
76 | el.dataset.scrollWidth = el.clientWidth;
|
77 | addClass(el, 'el-menu--collapse');
|
78 | } else {
|
79 | addClass(el, 'el-menu--collapse');
|
80 | el.dataset.oldOverflow = el.style.overflow;
|
81 | el.dataset.scrollWidth = el.clientWidth;
|
82 | removeClass(el, 'el-menu--collapse');
|
83 | }
|
84 |
|
85 | el.style.width = el.scrollWidth + 'px';
|
86 | el.style.overflow = 'hidden';
|
87 | },
|
88 |
|
89 | leave(el) {
|
90 | addClass(el, 'horizontal-collapse-transition');
|
91 | el.style.width = el.dataset.scrollWidth + 'px';
|
92 | }
|
93 | }
|
94 | };
|
95 | return createElement('transition', data, context.children);
|
96 | }
|
97 | }
|
98 | },
|
99 |
|
100 | props: {
|
101 | mode: {
|
102 | type: String,
|
103 | default: 'vertical'
|
104 | },
|
105 | defaultActive: {
|
106 | type: String,
|
107 | default: ''
|
108 | },
|
109 | defaultOpeneds: Array,
|
110 | uniqueOpened: Boolean,
|
111 | router: Boolean,
|
112 | menuTrigger: {
|
113 | type: String,
|
114 | default: 'hover'
|
115 | },
|
116 | collapse: Boolean,
|
117 | backgroundColor: String,
|
118 | textColor: String,
|
119 | activeTextColor: String,
|
120 | collapseTransition: {
|
121 | type: Boolean,
|
122 | default: true
|
123 | }
|
124 | },
|
125 | data() {
|
126 | return {
|
127 | activeIndex: this.defaultActive,
|
128 | openedMenus: (this.defaultOpeneds && !this.collapse) ? this.defaultOpeneds.slice(0) : [],
|
129 | items: {},
|
130 | submenus: {}
|
131 | };
|
132 | },
|
133 | computed: {
|
134 | hoverBackground() {
|
135 | return this.backgroundColor ? this.mixColor(this.backgroundColor, 0.2) : '';
|
136 | },
|
137 | isMenuPopup() {
|
138 | return this.mode === 'horizontal' || (this.mode === 'vertical' && this.collapse);
|
139 | }
|
140 | },
|
141 | watch: {
|
142 | defaultActive(value){
|
143 | if(!this.items[value]){
|
144 | this.activeIndex = null
|
145 | }
|
146 | this.updateActiveIndex(value)
|
147 | },
|
148 |
|
149 | defaultOpeneds(value) {
|
150 | if (!this.collapse) {
|
151 | this.openedMenus = value;
|
152 | }
|
153 | },
|
154 |
|
155 | collapse(value) {
|
156 | if (value) this.openedMenus = [];
|
157 | this.broadcast('ElSubmenu', 'toggle-collapse', value);
|
158 | }
|
159 | },
|
160 | methods: {
|
161 | updateActiveIndex(val) {
|
162 | const item = this.items[val] || this.items[this.activeIndex] || this.items[this.defaultActive];
|
163 | if (item) {
|
164 | this.activeIndex = item.index;
|
165 | this.initOpenedMenu();
|
166 | } else {
|
167 | this.activeIndex = null;
|
168 | }
|
169 | },
|
170 |
|
171 | getMigratingConfig() {
|
172 | return {
|
173 | props: {
|
174 | 'theme': 'theme is removed.'
|
175 | }
|
176 | };
|
177 | },
|
178 | getColorChannels(color) {
|
179 | color = color.replace('#', '');
|
180 | if (/^[0-9a-fA-F]{3}$/.test(color)) {
|
181 | color = color.split('');
|
182 | for (let i = 2; i >= 0; i--) {
|
183 | color.splice(i, 0, color[i]);
|
184 | }
|
185 | color = color.join('');
|
186 | }
|
187 | if (/^[0-9a-fA-F]{6}$/.test(color)) {
|
188 | return {
|
189 | red: parseInt(color.slice(0, 2), 16),
|
190 | green: parseInt(color.slice(2, 4), 16),
|
191 | blue: parseInt(color.slice(4, 6), 16)
|
192 | };
|
193 | } else {
|
194 | return {
|
195 | red: 255,
|
196 | green: 255,
|
197 | blue: 255
|
198 | };
|
199 | }
|
200 | },
|
201 | mixColor(color, percent) {
|
202 | let { red, green, blue } = this.getColorChannels(color);
|
203 | if (percent > 0) {
|
204 | red *= 1 - percent;
|
205 | green *= 1 - percent;
|
206 | blue *= 1 - percent;
|
207 | } else {
|
208 | red += (255 - red) * percent;
|
209 | green += (255 - green) * percent;
|
210 | blue += (255 - blue) * percent;
|
211 | }
|
212 | return `rgb(${ Math.round(red) }, ${ Math.round(green) }, ${ Math.round(blue) })`;
|
213 | },
|
214 | addItem(item) {
|
215 | this.$set(this.items, item.index, item);
|
216 | },
|
217 | removeItem(item) {
|
218 | delete this.items[item.index];
|
219 | },
|
220 | addSubmenu(item) {
|
221 | this.$set(this.submenus, item.index, item);
|
222 | },
|
223 | removeSubmenu(item) {
|
224 | delete this.submenus[item.index];
|
225 | },
|
226 | openMenu(index, indexPath) {
|
227 | let openedMenus = this.openedMenus;
|
228 | if (openedMenus.indexOf(index) !== -1) return;
|
229 |
|
230 |
|
231 | if (this.uniqueOpened) {
|
232 | this.openedMenus = openedMenus.filter(index => {
|
233 | return indexPath.indexOf(index) !== -1;
|
234 | });
|
235 | }
|
236 | this.openedMenus.push(index);
|
237 | },
|
238 | closeMenu(index) {
|
239 | const i = this.openedMenus.indexOf(index);
|
240 | if (i !== -1) {
|
241 | this.openedMenus.splice(i, 1);
|
242 | }
|
243 | },
|
244 | handleSubmenuClick(submenu) {
|
245 | const { index, indexPath } = submenu;
|
246 | let isOpened = this.openedMenus.indexOf(index) !== -1;
|
247 |
|
248 | if (isOpened) {
|
249 | this.closeMenu(index);
|
250 | this.$emit('close', index, indexPath);
|
251 | } else {
|
252 | this.openMenu(index, indexPath);
|
253 | this.$emit('open', index, indexPath);
|
254 | }
|
255 | },
|
256 | handleItemClick(item) {
|
257 | const { index, indexPath } = item;
|
258 | const oldActiveIndex = this.activeIndex;
|
259 | const hasIndex = item.index !== null;
|
260 |
|
261 | if (hasIndex) {
|
262 | this.activeIndex = item.index;
|
263 | }
|
264 |
|
265 | this.$emit('select', index, indexPath, item);
|
266 |
|
267 | if (this.mode === 'horizontal' || this.collapse) {
|
268 | this.openedMenus = [];
|
269 | }
|
270 |
|
271 | if (this.router && hasIndex) {
|
272 | this.routeToItem(item, (error) => {
|
273 | this.activeIndex = oldActiveIndex;
|
274 | if (error) {
|
275 |
|
276 |
|
277 | if (error.name === 'NavigationDuplicated') return
|
278 | console.error(error)
|
279 | }
|
280 | });
|
281 | }
|
282 | },
|
283 |
|
284 |
|
285 | initOpenedMenu() {
|
286 | const index = this.activeIndex;
|
287 | const activeItem = this.items[index];
|
288 | if (!activeItem || this.mode === 'horizontal' || this.collapse) return;
|
289 |
|
290 | let indexPath = activeItem.indexPath;
|
291 |
|
292 |
|
293 |
|
294 | indexPath.forEach(index => {
|
295 | let submenu = this.submenus[index];
|
296 | submenu && this.openMenu(index, submenu.indexPath);
|
297 | });
|
298 | },
|
299 | routeToItem(item, onError) {
|
300 | let route = item.route || item.index;
|
301 | try {
|
302 | this.$router.push(route, () => {}, onError);
|
303 | } catch (e) {
|
304 | console.error(e);
|
305 | }
|
306 | },
|
307 | open(index) {
|
308 | const { indexPath } = this.submenus[index.toString()];
|
309 | indexPath.forEach(i => this.openMenu(i, indexPath));
|
310 | },
|
311 | close(index) {
|
312 | this.closeMenu(index);
|
313 | }
|
314 | },
|
315 | mounted() {
|
316 | this.initOpenedMenu();
|
317 | this.$on('item-click', this.handleItemClick);
|
318 | this.$on('submenu-click', this.handleSubmenuClick);
|
319 | if (this.mode === 'horizontal') {
|
320 | new Menubar(this.$el);
|
321 | }
|
322 | this.$watch('items', this.updateActiveIndex);
|
323 | }
|
324 | };
|
325 | </script>
|