refactor: menu

feat-new-menu
tanjinzhou 2021-05-19 15:34:25 +08:00
parent a887ec5b8f
commit 9bb16977a9
14 changed files with 106 additions and 86 deletions

View File

@ -3,6 +3,7 @@ import {
CSSProperties, CSSProperties,
defineComponent, defineComponent,
nextTick, nextTick,
Ref,
Transition as T, Transition as T,
TransitionGroup as TG, TransitionGroup as TG,
} from 'vue'; } from 'vue';
@ -91,17 +92,17 @@ export declare type MotionEvent = (TransitionEvent | AnimationEvent) & {
deadline?: boolean; deadline?: boolean;
}; };
export declare type MotionEventHandler = ( export declare type MotionEventHandler = (element: Element, done?: () => void) => CSSProperties;
element: Element,
done?: () => void,
) => CSSProperties | void;
export declare type MotionEndEventHandler = (element: Element, done?: () => void) => boolean | void; export declare type MotionEndEventHandler = (element: Element, done?: () => void) => boolean | void;
// ================== Collapse Motion ================== // ================== Collapse Motion ==================
const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 }); const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 });
const getRealHeight: MotionEventHandler = node => ({ height: node.scrollHeight, opacity: 1 }); const getRealHeight: MotionEventHandler = node => ({
const getCurrentHeight: MotionEventHandler = (node: any) => ({ height: node.offsetHeight }); height: `${node.scrollHeight}px`,
opacity: 1,
});
const getCurrentHeight: MotionEventHandler = (node: any) => ({ height: `${node.offsetHeight}px` });
// const skipOpacityTransition: MotionEndEventHandler = (_, event) => // const skipOpacityTransition: MotionEndEventHandler = (_, event) =>
// (event as TransitionEvent).propertyName === 'height'; // (event as TransitionEvent).propertyName === 'height';
@ -110,14 +111,38 @@ export interface CSSMotionProps extends Partial<BaseTransitionProps<Element>> {
css?: boolean; css?: boolean;
} }
const collapseMotion: CSSMotionProps = { const collapseMotion = (style: Ref<CSSProperties>, className: Ref<string>): CSSMotionProps => {
return {
name: 'ant-motion-collapse', name: 'ant-motion-collapse',
appear: true, appear: true,
// onAppearStart: getCollapsedHeight, css: true,
onBeforeEnter: getCollapsedHeight, onBeforeEnter: node => {
onEnter: getRealHeight, className.value = 'ant-motion-collapse';
onBeforeLeave: getCurrentHeight, style.value = getCollapsedHeight(node);
onLeave: getCollapsedHeight, },
onEnter: node => {
nextTick(() => {
style.value = getRealHeight(node);
});
},
onAfterEnter: () => {
className.value = '';
style.value = {};
},
onBeforeLeave: node => {
className.value = 'ant-motion-collapse';
style.value = getCurrentHeight(node);
},
onLeave: node => {
window.setTimeout(() => {
style.value = getCollapsedHeight(node);
});
},
onAfterLeave: () => {
className.value = '';
style.value = {};
},
};
}; };
export { Transition, TransitionGroup, collapseMotion }; export { Transition, TransitionGroup, collapseMotion };

View File

@ -1,4 +1,4 @@
import { computed, defineComponent, ref, watch } from '@vue/runtime-core'; import { computed, defineComponent, reactive, ref, watch } from '@vue/runtime-core';
import Transition from 'ant-design-vue/es/_util/transition'; import Transition from 'ant-design-vue/es/_util/transition';
import { useInjectMenu, MenuContextProvider } from './hooks/useMenuContext'; import { useInjectMenu, MenuContextProvider } from './hooks/useMenuContext';
import { MenuMode } from './interface'; import { MenuMode } from './interface';
@ -31,12 +31,13 @@ export default defineComponent({
}, },
{ flush: 'post' }, { flush: 'post' },
); );
const style = ref({});
const mergedMotion = computed(() => ({ const className = ref('');
...(motion.value || defaultMotions.value?.[fixedMode]), const mergedMotion = computed(() => {
appear: props.keyPath.length <= 1, const m = motion.value || defaultMotions.value?.[fixedMode];
})); const res = typeof m === 'function' ? m(style, className) : m;
return { ...res, appear: props.keyPath.length <= 1 };
});
return () => { return () => {
if (destroy.value) { if (destroy.value) {
return null; return null;
@ -49,7 +50,12 @@ export default defineComponent({
}} }}
> >
<Transition {...mergedMotion.value}> <Transition {...mergedMotion.value}>
<SubMenuList v-show={mergedOpen.value} id={props.id}> <SubMenuList
v-show={mergedOpen.value}
id={props.id}
style={style.value}
class={className.value}
>
{slots.default?.()} {slots.default?.()}
</SubMenuList> </SubMenuList>
</Transition> </Transition>

View File

@ -88,7 +88,6 @@ export default defineComponent({
watch( watch(
() => props.openKeys, () => props.openKeys,
(openKeys = mergedOpenKeys.value) => { (openKeys = mergedOpenKeys.value) => {
console.log('mergedOpenKeys', openKeys);
mergedOpenKeys.value = openKeys; mergedOpenKeys.value = openKeys;
}, },
{ immediate: true }, { immediate: true },

View File

@ -1,5 +1,14 @@
import { Key } from '../../../_util/type'; import { Key } from '../../../_util/type';
import { ComputedRef, defineComponent, inject, InjectionKey, provide, Ref, UnwrapRef } from 'vue'; import {
ComputedRef,
CSSProperties,
defineComponent,
inject,
InjectionKey,
provide,
Ref,
UnwrapRef,
} from 'vue';
import { BuiltinPlacements, MenuMode, MenuTheme, TriggerSubMenuAction } from '../interface'; import { BuiltinPlacements, MenuMode, MenuTheme, TriggerSubMenuAction } from '../interface';
import { CSSMotionProps } from '../../../_util/transition'; import { CSSMotionProps } from '../../../_util/transition';
@ -48,7 +57,13 @@ export interface MenuContextProps {
// // Motion // // Motion
motion?: ComputedRef<CSSMotionProps | null>; motion?: ComputedRef<CSSMotionProps | null>;
defaultMotions?: ComputedRef<Partial<{ [key in MenuMode | 'other']: CSSMotionProps }> | null>; defaultMotions?: ComputedRef<Partial<
{
[key in MenuMode | 'other']:
| CSSMotionProps
| ((style: Ref<CSSProperties>, className: Ref<string>) => CSSMotionProps);
}
> | null>;
// // Popup // // Popup
subMenuOpenDelay: ComputedRef<number>; subMenuOpenDelay: ComputedRef<number>;

View File

@ -239,7 +239,6 @@ a {
&[disabled] { &[disabled] {
color: @disabled-color; color: @disabled-color;
cursor: not-allowed; cursor: not-allowed;
pointer-events: none;
} }
} }

View File

@ -3,7 +3,6 @@
@import 'motion/move'; @import 'motion/move';
@import 'motion/other'; @import 'motion/other';
@import 'motion/slide'; @import 'motion/slide';
@import 'motion/swing';
@import 'motion/zoom'; @import 'motion/zoom';
// For common/openAnimation // For common/openAnimation

View File

@ -1,11 +1,12 @@
.fade-motion(@className, @keyframeName) { .fade-motion(@className, @keyframeName) {
.make-motion(@className, @keyframeName); @name: ~'@{ant-prefix}-@{className}';
.@{className}-enter, .make-motion(@name, @keyframeName);
.@{className}-appear { .@{name}-enter,
.@{name}-appear {
opacity: 0; opacity: 0;
animation-timing-function: linear; animation-timing-function: linear;
} }
.@{className}-leave { .@{name}-leave {
animation-timing-function: linear; animation-timing-function: linear;
} }
} }

View File

@ -1,11 +1,12 @@
.move-motion(@className, @keyframeName) { .move-motion(@className, @keyframeName) {
.make-motion(@className, @keyframeName); @name: ~'@{ant-prefix}-@{className}';
.@{className}-enter, .make-motion(@name, @keyframeName);
.@{className}-appear { .@{name}-enter,
.@{name}-appear {
opacity: 0; opacity: 0;
animation-timing-function: @ease-out-circ; animation-timing-function: @ease-out-circ;
} }
.@{className}-leave { .@{name}-leave {
animation-timing-function: @ease-in-circ; animation-timing-function: @ease-in-circ;
} }
} }

View File

@ -4,17 +4,23 @@
} }
} }
[ant-click-animating='true'], @click-animating-true: ~"[@{ant-prefix}-click-animating='true']";
[ant-click-animating-without-extra-node='true'] { @click-animating-with-extra-node-true: ~"[@{ant-prefix}-click-animating-without-extra-node='true']";
@{click-animating-true},
@{click-animating-with-extra-node-true} {
position: relative; position: relative;
} }
html { html {
--antd-wave-shadow-color: @primary-color; --antd-wave-shadow-color: @primary-color;
--scroll-bar: 0;
} }
[ant-click-animating-without-extra-node='true']::after, @click-animating-with-extra-node-true-after: ~'@{click-animating-with-extra-node-true}::after';
.ant-click-animating-node {
@{click-animating-with-extra-node-true-after},
.@{ant-prefix}-click-animating-node {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;

View File

@ -1,11 +1,12 @@
.slide-motion(@className, @keyframeName) { .slide-motion(@className, @keyframeName) {
.make-motion(@className, @keyframeName); @name: ~'@{ant-prefix}-@{className}';
.@{className}-enter, .make-motion(@name, @keyframeName);
.@{className}-appear { .@{name}-enter,
.@{name}-appear {
opacity: 0; opacity: 0;
animation-timing-function: @ease-out-quint; animation-timing-function: @ease-out-quint;
} }
.@{className}-leave { .@{name}-leave {
animation-timing-function: @ease-in-quint; animation-timing-function: @ease-in-quint;
} }
} }

View File

@ -1,34 +0,0 @@
.swing-motion(@className, @keyframeName) {
.@{className}-enter,
.@{className}-appear {
.motion-common();
animation-play-state: paused;
}
.@{className}-enter.@{className}-enter-active,
.@{className}-appear.@{className}-appear-active {
animation-name: ~'@{keyframeName}In';
animation-play-state: running;
}
}
.swing-motion(swing, antSwing);
@keyframes antSwingIn {
0%,
100% {
transform: translateX(0);
}
20% {
transform: translateX(-10px);
}
40% {
transform: translateX(10px);
}
60% {
transform: translateX(-5px);
}
80% {
transform: translateX(5px);
}
}

View File

@ -1,15 +1,17 @@
.zoom-motion(@className, @keyframeName, @duration: @animation-duration-base) { .zoom-motion(@className, @keyframeName, @duration: @animation-duration-base) {
.make-motion(@className, @keyframeName, @duration); @name: ~'@{ant-prefix}-@{className}';
.@{className}-enter, .make-motion(@name, @keyframeName, @duration);
.@{className}-appear { .@{name}-enter,
.@{name}-appear {
transform: scale(0); // need this by yiminghe transform: scale(0); // need this by yiminghe
opacity: 0; opacity: 0;
animation-timing-function: @ease-out-circ; animation-timing-function: @ease-out-circ;
&-prepare { &-prepare {
transform: none; transform: none;
} }
} }
.@{className}-leave { .@{name}-leave {
animation-timing-function: @ease-in-out-circ; animation-timing-function: @ease-in-out-circ;
} }
} }
@ -54,7 +56,7 @@
opacity: 0; opacity: 0;
} }
5% { 5% {
transform: scale(0.2); transform: scale(0.8);
opacity: 0; opacity: 0;
} }
100% { 100% {

View File

@ -67,15 +67,15 @@ export default defineComponent({
const selectedKeys = ref<string[]>(['1']); const selectedKeys = ref<string[]>(['1']);
const openKeys = ref<string[]>(['sub1']); const openKeys = ref<string[]>(['sub1']);
const handleClick = (e: Event) => { const handleClick = (e: Event) => {
console.log('click', e); // console.log('click', e);
}; };
const titleClick = (e: Event) => { const titleClick = (e: Event) => {
console.log('titleClick', e); // console.log('titleClick', e);
}; };
watch( watch(
() => openKeys, () => openKeys,
val => { val => {
console.log('openKeys', val); // console.log('openKeys', val);
}, },
); );
return { return {

2
v2-doc

@ -1 +1 @@
Subproject commit d197053285b81e77718621c0b5b94cb3b21831a2 Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557