ant-design-vue/components/vc-drawer/src/DrawerChild.tsx

258 lines
6.5 KiB
Vue

import {
Transition,
defineComponent,
onMounted,
onUnmounted,
nextTick,
watch,
shallowRef,
} from 'vue';
import classnames from '../../_util/classNames';
import KeyCode from '../../_util/KeyCode';
import omit from '../../_util/omit';
import { drawerChildProps } from './IDrawerPropTypes';
import { dataToArray, windowIsUndefined } from './utils';
const currentDrawer: Record<string, boolean> = {};
export interface scrollLockOptions {
container: HTMLElement;
}
const DrawerChild = defineComponent({
compatConfig: { MODE: 3 },
inheritAttrs: false,
props: drawerChildProps(),
emits: ['close', 'handleClick', 'change'],
setup(props, { emit, slots }) {
const contentWrapper = shallowRef<HTMLElement>();
const dom = shallowRef<HTMLElement>();
const maskDom = shallowRef<HTMLElement>();
const handlerDom = shallowRef<HTMLElement>();
const contentDom = shallowRef<HTMLElement>();
let levelDom = [];
const drawerId = `drawer_id_${Number(
(Date.now() + Math.random())
.toString()
.replace('.', Math.round(Math.random() * 9).toString()),
).toString(16)}`;
onMounted(() => {
nextTick(() => {
const { open, getContainer, showMask, autofocus } = props;
const container = getContainer?.();
getLevelDom(props);
if (open) {
if (container && container.parentNode === document.body) {
currentDrawer[drawerId] = open;
}
nextTick(() => {
if (autofocus) {
domFocus();
}
});
if (showMask) {
props.scrollLocker?.lock();
}
}
});
});
watch(
() => props.level,
() => {
getLevelDom(props);
},
{ flush: 'post' },
);
watch(
() => props.open,
() => {
const { open, getContainer, scrollLocker, showMask, autofocus } = props;
const container = getContainer?.();
if (container && container.parentNode === document.body) {
currentDrawer[drawerId] = !!open;
}
if (open) {
if (autofocus) {
domFocus();
}
if (showMask) {
scrollLocker?.lock();
}
} else {
scrollLocker?.unLock();
}
},
{ flush: 'post' },
);
onUnmounted(() => {
const { open } = props;
delete currentDrawer[drawerId];
if (open) {
document.body.style.touchAction = '';
}
props.scrollLocker?.unLock();
});
watch(
() => props.placement,
val => {
if (val) {
// test 的 bug, 有动画过场,删除 dom
contentDom.value = null;
}
},
);
const domFocus = () => {
dom.value?.focus?.();
};
const onClose = (e: Event) => {
emit('close', e);
};
const onKeyDown = (e: KeyboardEvent) => {
if (e.keyCode === KeyCode.ESC) {
e.stopPropagation();
onClose(e);
}
};
const onAfterVisibleChange = () => {
const { open, afterVisibleChange } = props;
if (afterVisibleChange) {
afterVisibleChange(!!open);
}
};
const getLevelDom = ({ level, getContainer }) => {
if (windowIsUndefined) {
return;
}
const container = getContainer?.();
const parent = container ? (container.parentNode as HTMLElement) : null;
levelDom = [];
if (level === 'all') {
const children: HTMLElement[] = parent ? Array.prototype.slice.call(parent.children) : [];
children.forEach((child: HTMLElement) => {
if (
child.nodeName !== 'SCRIPT' &&
child.nodeName !== 'STYLE' &&
child.nodeName !== 'LINK' &&
child !== container
) {
levelDom.push(child);
}
});
} else if (level) {
dataToArray(level).forEach(key => {
document.querySelectorAll(key).forEach(item => {
levelDom.push(item);
});
});
}
};
const onHandleClick = e => {
emit('handleClick', e);
};
const canOpen = shallowRef(false);
watch(dom, () => {
nextTick(() => {
canOpen.value = true;
});
});
return () => {
const {
width,
height,
open: $open,
prefixCls,
placement,
level,
levelMove,
ease,
duration,
getContainer,
onChange,
afterVisibleChange,
showMask,
maskClosable,
maskStyle,
keyboard,
getOpenCount,
scrollLocker,
contentWrapperStyle,
style,
class: className,
rootClassName,
rootStyle,
maskMotion,
motion,
inline,
...otherProps
} = props;
// 首次渲染都将是关闭状态。
const open = $open && canOpen.value;
const wrapperClassName = classnames(prefixCls, {
[`${prefixCls}-${placement}`]: true,
[`${prefixCls}-open`]: open,
[`${prefixCls}-inline`]: inline,
'no-mask': !showMask,
[rootClassName]: true,
});
const motionProps = typeof motion === 'function' ? motion(placement) : motion;
return (
<div
{...omit(otherProps, ['autofocus'])}
tabindex={-1}
class={wrapperClassName}
style={rootStyle}
ref={dom}
onKeydown={open && keyboard ? onKeyDown : undefined}
>
<Transition {...maskMotion}>
{showMask && (
<div
v-show={open}
class={`${prefixCls}-mask`}
onClick={maskClosable ? onClose : undefined}
style={maskStyle}
ref={maskDom}
/>
)}
</Transition>
<Transition
{...motionProps}
onAfterEnter={onAfterVisibleChange}
onAfterLeave={onAfterVisibleChange}
>
<div
v-show={open}
class={`${prefixCls}-content-wrapper`}
style={[contentWrapperStyle]}
ref={contentWrapper}
>
<div class={[`${prefixCls}-content`, className]} style={style} ref={contentDom}>
{slots.default?.()}
</div>
{slots.handler ? (
<div onClick={onHandleClick} ref={handlerDom}>
{slots.handler?.()}
</div>
) : null}
</div>
</Transition>
</div>
);
};
},
});
export default DrawerChild;