Commit 841cb1fe67c9b6143a89d30f8c51ef0f4264e57d

Authored by Aresn
1 parent 75c32564

Menu support transition fixed #514

examples/routers/form.vue
@@ -2,12 +2,7 @@ @@ -2,12 +2,7 @@
2 <div style="width: 300px;"> 2 <div style="width: 300px;">
3 <i-form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="80"> 3 <i-form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="80">
4 <Form-item label="爱好" prop="interest"> 4 <Form-item label="爱好" prop="interest">
5 - <Checkbox-group v-model="formValidate.interest">  
6 - <Checkbox label="吃饭"></Checkbox>  
7 - <Checkbox label="睡觉"></Checkbox>  
8 - <Checkbox label="跑步"></Checkbox>  
9 - <Checkbox label="看电影"></Checkbox>  
10 - </Checkbox-group> 5 + <Input v-model="formValidate.interest" number></Input>
11 </Form-item> 6 </Form-item>
12 <Form-item> 7 <Form-item>
13 <i-button type="primary" @click="handleSubmit('formValidate')">提交</i-button> 8 <i-button type="primary" @click="handleSubmit('formValidate')">提交</i-button>
@@ -21,12 +16,15 @@ @@ -21,12 +16,15 @@
21 data () { 16 data () {
22 return { 17 return {
23 formValidate: { 18 formValidate: {
24 - interest: ['吃饭', '跑步'] 19 + interest: ''
25 }, 20 },
26 ruleValidate: { 21 ruleValidate: {
27 interest: [ 22 interest: [
28 - { required: true, type: 'array', min: 1, message: '至少选择一个爱好', trigger: 'change' },  
29 - { type: 'array', max: 2, message: '最多选择两个爱好', trigger: 'change' } 23 + {
  24 + required: true,
  25 + type: 'number',
  26 + trigger: 'change'
  27 + }
30 ] 28 ]
31 } 29 }
32 } 30 }
examples/routers/menu.vue
1 <template> 1 <template>
2 <div> 2 <div>
3 - <Menu mode="horizontal" :theme="theme1" @on-select="s">  
4 - <Row type="flex" justify="center" align="middle">  
5 - <i-col span="12">  
6 - <Menu-item name="1"> 3 + <Row>
  4 + <i-col span="8">
  5 + <Menu :theme="theme2">
  6 + <Submenu name="1">
  7 + <template slot="title">
7 <Icon type="ios-paper"></Icon> 8 <Icon type="ios-paper"></Icon>
8 内容管理 9 内容管理
9 - </Menu-item>  
10 - <Menu-item name="2"> 10 + </template>
  11 + <Menu-item name="1-1">文章管理</Menu-item>
  12 + <Menu-item name="1-2">评论管理</Menu-item>
  13 + <Menu-item name="1-3">举报管理</Menu-item>
  14 + </Submenu>
  15 + <Submenu name="2">
  16 + <template slot="title">
11 <Icon type="ios-people"></Icon> 17 <Icon type="ios-people"></Icon>
12 用户管理 18 用户管理
13 - </Menu-item>  
14 - <Submenu name="3">  
15 - <template slot="title">  
16 - <Icon type="stats-bars"></Icon>  
17 - 统计分析  
18 - </template>  
19 - <Menu-group title="使用">  
20 - <Menu-item name="3-1">新增和启动</Menu-item>  
21 - <Menu-item name="3-2">活跃分析</Menu-item>  
22 - <Menu-item name="3-3">时段分析</Menu-item>  
23 - </Menu-group>  
24 - <Menu-group title="留存">  
25 - <Menu-item name="3-4">用户留存</Menu-item>  
26 - <Menu-item name="3-5">流失用户</Menu-item>  
27 - </Menu-group>  
28 - </Submenu>  
29 - <Menu-item name="4">  
30 - <Icon type="settings"></Icon>  
31 - 综合设置  
32 - </Menu-item>  
33 - </i-col>  
34 - </Row>  
35 - </Menu> 19 + </template>
  20 + <Menu-item name="2-1">新增用户</Menu-item>
  21 + <Menu-item name="2-2">活跃用户</Menu-item>
  22 + </Submenu>
  23 + <Submenu name="3">
  24 + <template slot="title">
  25 + <Icon type="stats-bars"></Icon>
  26 + 统计分析
  27 + </template>
  28 + <Menu-group title="使用">
  29 + <Menu-item name="3-1">新增和启动</Menu-item>
  30 + <Menu-item name="3-2">活跃分析</Menu-item>
  31 + <Menu-item name="3-3">时段分析</Menu-item>
  32 + </Menu-group>
  33 + <Menu-group title="留存">
  34 + <Menu-item name="3-4">用户留存</Menu-item>
  35 + <Menu-item name="3-5">流失用户</Menu-item>
  36 + </Menu-group>
  37 + </Submenu>
  38 + </Menu>
  39 + </i-col>
  40 + </Row>
36 <br> 41 <br>
37 <p>切换主题</p> 42 <p>切换主题</p>
38 - <Radio-group v-model="theme1"> 43 + <Radio-group v-model="theme2">
39 <Radio label="light"></Radio> 44 <Radio label="light"></Radio>
40 <Radio label="dark"></Radio> 45 <Radio label="dark"></Radio>
41 - <Radio label="primary"></Radio>  
42 </Radio-group> 46 </Radio-group>
43 </div> 47 </div>
44 </template> 48 </template>
@@ -46,149 +50,8 @@ @@ -46,149 +50,8 @@
46 export default { 50 export default {
47 data () { 51 data () {
48 return { 52 return {
49 - theme1: 'light'  
50 - }  
51 - },  
52 - methods: {  
53 - s (s) {  
54 - console.log(s) 53 + theme2: 'light'
55 } 54 }
56 } 55 }
57 } 56 }
58 </script> 57 </script>
59 -  
60 -  
61 -<!--<template>-->  
62 - <!--<div>-->  
63 - <!--<Row>-->  
64 - <!--<i-col span="8">-->  
65 - <!--<Menu :theme="theme2" @on-select="s">-->  
66 - <!--<Submenu name="1">-->  
67 - <!--<template slot="title">-->  
68 - <!--<Icon type="ios-paper"></Icon>-->  
69 - <!--内容管理-->  
70 - <!--</template>-->  
71 - <!--<Menu-item name="1-1">文章管理</Menu-item>-->  
72 - <!--<Menu-item name="1-2">评论管理</Menu-item>-->  
73 - <!--<Menu-item name="1-3">举报管理</Menu-item>-->  
74 - <!--</Submenu>-->  
75 - <!--<Submenu name="2">-->  
76 - <!--<template slot="title">-->  
77 - <!--<Icon type="ios-people"></Icon>-->  
78 - <!--用户管理-->  
79 - <!--</template>-->  
80 - <!--<Menu-item name="2-1">新增用户</Menu-item>-->  
81 - <!--<Menu-item name="2-2">活跃用户</Menu-item>-->  
82 - <!--</Submenu>-->  
83 - <!--<Submenu name="3">-->  
84 - <!--<template slot="title">-->  
85 - <!--<Icon type="stats-bars"></Icon>-->  
86 - <!--统计分析-->  
87 - <!--</template>-->  
88 - <!--<Menu-group title="使用">-->  
89 - <!--<Menu-item name="3-1">新增和启动</Menu-item>-->  
90 - <!--<Menu-item name="3-2">活跃分析</Menu-item>-->  
91 - <!--<Menu-item name="3-3">时段分析</Menu-item>-->  
92 - <!--</Menu-group>-->  
93 - <!--<Menu-group title="留存">-->  
94 - <!--<Menu-item name="3-4">用户留存</Menu-item>-->  
95 - <!--<Menu-item name="3-5">流失用户</Menu-item>-->  
96 - <!--</Menu-group>-->  
97 - <!--</Submenu>-->  
98 - <!--</Menu>-->  
99 - <!--</i-col>-->  
100 - <!--<i-col span="8">-->  
101 - <!--<Menu :theme="theme2" active-name="1-2" :open-names="['1']" @on-select="s">-->  
102 - <!--<Submenu name="1">-->  
103 - <!--<template slot="title">-->  
104 - <!--<Icon type="ios-paper"></Icon>-->  
105 - <!--内容管理-->  
106 - <!--</template>-->  
107 - <!--<Menu-item name="1-1">文章管理</Menu-item>-->  
108 - <!--<Menu-item name="1-2">评论管理</Menu-item>-->  
109 - <!--<Menu-item name="1-3">举报管理</Menu-item>-->  
110 - <!--</Submenu>-->  
111 - <!--<Submenu name="2">-->  
112 - <!--<template slot="title">-->  
113 - <!--<Icon type="ios-people"></Icon>-->  
114 - <!--用户管理-->  
115 - <!--</template>-->  
116 - <!--<Menu-item name="2-1">新增用户</Menu-item>-->  
117 - <!--<Menu-item name="2-2">活跃用户</Menu-item>-->  
118 - <!--</Submenu>-->  
119 - <!--<Submenu name="3">-->  
120 - <!--<template slot="title">-->  
121 - <!--<Icon type="stats-bars"></Icon>-->  
122 - <!--统计分析-->  
123 - <!--</template>-->  
124 - <!--<Menu-group title="使用">-->  
125 - <!--<Menu-item name="3-1">新增和启动</Menu-item>-->  
126 - <!--<Menu-item name="3-2">活跃分析</Menu-item>-->  
127 - <!--<Menu-item name="3-3">时段分析</Menu-item>-->  
128 - <!--</Menu-group>-->  
129 - <!--<Menu-group title="留存">-->  
130 - <!--<Menu-item name="3-4">用户留存</Menu-item>-->  
131 - <!--<Menu-item name="3-5">流失用户</Menu-item>-->  
132 - <!--</Menu-group>-->  
133 - <!--</Submenu>-->  
134 - <!--</Menu>-->  
135 - <!--</i-col>-->  
136 - <!--<i-col span="8">-->  
137 - <!--<Menu :theme="theme2" :open-names="['1']" accordion @on-select="s">-->  
138 - <!--<Submenu name="1">-->  
139 - <!--<template slot="title">-->  
140 - <!--<Icon type="ios-paper"></Icon>-->  
141 - <!--内容管理-->  
142 - <!--</template>-->  
143 - <!--<Menu-item name="1-1">文章管理</Menu-item>-->  
144 - <!--<Menu-item name="1-2">评论管理</Menu-item>-->  
145 - <!--<Menu-item name="1-3">举报管理</Menu-item>-->  
146 - <!--</Submenu>-->  
147 - <!--<Submenu name="2">-->  
148 - <!--<template slot="title">-->  
149 - <!--<Icon type="ios-people"></Icon>-->  
150 - <!--用户管理-->  
151 - <!--</template>-->  
152 - <!--<Menu-item name="2-1">新增用户</Menu-item>-->  
153 - <!--<Menu-item name="2-2">活跃用户</Menu-item>-->  
154 - <!--</Submenu>-->  
155 - <!--<Submenu name="3">-->  
156 - <!--<template slot="title">-->  
157 - <!--<Icon type="stats-bars"></Icon>-->  
158 - <!--统计分析-->  
159 - <!--</template>-->  
160 - <!--<Menu-group title="使用">-->  
161 - <!--<Menu-item name="3-1">新增和启动</Menu-item>-->  
162 - <!--<Menu-item name="3-2">活跃分析</Menu-item>-->  
163 - <!--<Menu-item name="3-3">时段分析</Menu-item>-->  
164 - <!--</Menu-group>-->  
165 - <!--<Menu-group title="留存">-->  
166 - <!--<Menu-item name="3-4">用户留存</Menu-item>-->  
167 - <!--<Menu-item name="3-5">流失用户</Menu-item>-->  
168 - <!--</Menu-group>-->  
169 - <!--</Submenu>-->  
170 - <!--</Menu>-->  
171 - <!--</i-col>-->  
172 - <!--</Row>-->  
173 - <!--<br>-->  
174 - <!--<p>切换主题</p>-->  
175 - <!--<Radio-group v-model="theme2">-->  
176 - <!--<Radio label="light"></Radio>-->  
177 - <!--<Radio label="dark"></Radio>-->  
178 - <!--</Radio-group>-->  
179 - <!--</div>-->  
180 -<!--</template>-->  
181 -<!--<script>-->  
182 - <!--export default {-->  
183 - <!--data () {-->  
184 - <!--return {-->  
185 - <!--theme2: 'light'-->  
186 - <!--}-->  
187 - <!--},-->  
188 - <!--methods: {-->  
189 - <!--s (s) {-->  
190 - <!--console.log(s);-->  
191 - <!--}-->  
192 - <!--}-->  
193 - <!--}-->  
194 -<!--</script>-->  
src/components/base/collapse-transition.js 0 → 100644
  1 +// Thanks to https://github.com/ElemeFE/element/blob/dev/src/transitions/collapse-transition.js
  2 +
  3 +import { addClass, removeClass } from '../../utils/assist';
  4 +
  5 +const Transition = {
  6 + beforeEnter(el) {
  7 + addClass(el, 'collapse-transition');
  8 + if (!el.dataset) el.dataset = {};
  9 +
  10 + el.dataset.oldPaddingTop = el.style.paddingTop;
  11 + el.dataset.oldPaddingBottom = el.style.paddingBottom;
  12 +
  13 + el.style.height = '0';
  14 + el.style.paddingTop = 0;
  15 + el.style.paddingBottom = 0;
  16 + },
  17 +
  18 + enter(el) {
  19 + el.dataset.oldOverflow = el.style.overflow;
  20 + if (el.scrollHeight !== 0) {
  21 + el.style.height = el.scrollHeight + 'px';
  22 + el.style.paddingTop = el.dataset.oldPaddingTop;
  23 + el.style.paddingBottom = el.dataset.oldPaddingBottom;
  24 + } else {
  25 + el.style.height = '';
  26 + el.style.paddingTop = el.dataset.oldPaddingTop;
  27 + el.style.paddingBottom = el.dataset.oldPaddingBottom;
  28 + }
  29 +
  30 + el.style.overflow = 'hidden';
  31 + },
  32 +
  33 + afterEnter(el) {
  34 + // for safari: remove class then reset height is necessary
  35 + removeClass(el, 'collapse-transition');
  36 + el.style.height = '';
  37 + el.style.overflow = el.dataset.oldOverflow;
  38 + },
  39 +
  40 + beforeLeave(el) {
  41 + if (!el.dataset) el.dataset = {};
  42 + el.dataset.oldPaddingTop = el.style.paddingTop;
  43 + el.dataset.oldPaddingBottom = el.style.paddingBottom;
  44 + el.dataset.oldOverflow = el.style.overflow;
  45 +
  46 + el.style.height = el.scrollHeight + 'px';
  47 + el.style.overflow = 'hidden';
  48 + },
  49 +
  50 + leave(el) {
  51 + if (el.scrollHeight !== 0) {
  52 + // for safari: add class after set height, or it will jump to zero height suddenly, weired
  53 + addClass(el, 'collapse-transition');
  54 + el.style.height = 0;
  55 + el.style.paddingTop = 0;
  56 + el.style.paddingBottom = 0;
  57 + }
  58 + },
  59 +
  60 + afterLeave(el) {
  61 + removeClass(el, 'collapse-transition');
  62 + el.style.height = '';
  63 + el.style.overflow = el.dataset.oldOverflow;
  64 + el.style.paddingTop = el.dataset.oldPaddingTop;
  65 + el.style.paddingBottom = el.dataset.oldPaddingBottom;
  66 + }
  67 +};
  68 +
  69 +export default {
  70 + name: 'CollapseTransition',
  71 + functional: true,
  72 + render(h, { children }) {
  73 + const data = {
  74 + on: Transition
  75 + };
  76 +
  77 + return h('transition', data, children);
  78 + }
  79 +};
src/components/menu/submenu.vue
@@ -4,7 +4,9 @@ @@ -4,7 +4,9 @@
4 <slot name="title"></slot> 4 <slot name="title"></slot>
5 <Icon type="ios-arrow-down" :class="[prefixCls + '-submenu-title-icon']"></Icon> 5 <Icon type="ios-arrow-down" :class="[prefixCls + '-submenu-title-icon']"></Icon>
6 </div> 6 </div>
7 - <ul :class="[prefixCls]" v-if="mode === 'vertical'" v-show="opened"><slot></slot></ul> 7 + <collapse-transition v-if="mode === 'vertical'">
  8 + <ul :class="[prefixCls]" v-show="opened"><slot></slot></ul>
  9 + </collapse-transition>
8 <transition name="slide-up" v-else> 10 <transition name="slide-up" v-else>
9 <Drop 11 <Drop
10 v-show="opened" 12 v-show="opened"
@@ -17,6 +19,7 @@ @@ -17,6 +19,7 @@
17 <script> 19 <script>
18 import Drop from '../select/dropdown.vue'; 20 import Drop from '../select/dropdown.vue';
19 import Icon from '../icon/icon.vue'; 21 import Icon from '../icon/icon.vue';
  22 + import CollapseTransition from '../base/collapse-transition';
20 import { getStyle, findComponentUpward } from '../../utils/assist'; 23 import { getStyle, findComponentUpward } from '../../utils/assist';
21 import Emitter from '../../mixins/emitter'; 24 import Emitter from '../../mixins/emitter';
22 25
@@ -25,7 +28,7 @@ @@ -25,7 +28,7 @@
25 export default { 28 export default {
26 name: 'Submenu', 29 name: 'Submenu',
27 mixins: [ Emitter ], 30 mixins: [ Emitter ],
28 - components: { Icon, Drop }, 31 + components: { Icon, Drop, CollapseTransition },
29 props: { 32 props: {
30 name: { 33 name: {
31 type: [String, Number], 34 type: [String, Number],
src/styles/animation/index.less
@@ -25,4 +25,8 @@ @@ -25,4 +25,8 @@
25 @import "fade"; 25 @import "fade";
26 @import "move"; 26 @import "move";
27 @import "ease"; 27 @import "ease";
28 -@import "slide";  
29 \ No newline at end of file 28 \ No newline at end of file
  29 +@import "slide";
  30 +
  31 +.collapse-transition {
  32 + transition: @transition-time height ease-in-out, @transition-time padding-top ease-in-out, @transition-time padding-bottom ease-in-out;
  33 +}
30 \ No newline at end of file 34 \ No newline at end of file
src/utils/assist.js
@@ -232,4 +232,66 @@ function findComponentsDownward (context, componentName, components = []) { @@ -232,4 +232,66 @@ function findComponentsDownward (context, componentName, components = []) {
232 } 232 }
233 return components; 233 return components;
234 } 234 }
235 -export {findComponentsDownward};  
236 \ No newline at end of file 235 \ No newline at end of file
  236 +export {findComponentsDownward};
  237 +
  238 +/* istanbul ignore next */
  239 +const trim = function(string) {
  240 + return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
  241 +};
  242 +
  243 +/* istanbul ignore next */
  244 +export function hasClass(el, cls) {
  245 + if (!el || !cls) return false;
  246 + if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
  247 + if (el.classList) {
  248 + return el.classList.contains(cls);
  249 + } else {
  250 + return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
  251 + }
  252 +}
  253 +
  254 +/* istanbul ignore next */
  255 +export function addClass(el, cls) {
  256 + if (!el) return;
  257 + let curClass = el.className;
  258 + const classes = (cls || '').split(' ');
  259 +
  260 + for (let i = 0, j = classes.length; i < j; i++) {
  261 + const clsName = classes[i];
  262 + if (!clsName) continue;
  263 +
  264 + if (el.classList) {
  265 + el.classList.add(clsName);
  266 + } else {
  267 + if (!hasClass(el, clsName)) {
  268 + curClass += ' ' + clsName;
  269 + }
  270 + }
  271 + }
  272 + if (!el.classList) {
  273 + el.className = curClass;
  274 + }
  275 +}
  276 +
  277 +/* istanbul ignore next */
  278 +export function removeClass(el, cls) {
  279 + if (!el || !cls) return;
  280 + const classes = cls.split(' ');
  281 + let curClass = ' ' + el.className + ' ';
  282 +
  283 + for (let i = 0, j = classes.length; i < j; i++) {
  284 + const clsName = classes[i];
  285 + if (!clsName) continue;
  286 +
  287 + if (el.classList) {
  288 + el.classList.remove(clsName);
  289 + } else {
  290 + if (hasClass(el, clsName)) {
  291 + curClass = curClass.replace(' ' + clsName + ' ', ' ');
  292 + }
  293 + }
  294 + }
  295 + if (!el.classList) {
  296 + el.className = trim(curClass);
  297 + }
  298 +}
237 \ No newline at end of file 299 \ No newline at end of file