-
-
-
-
-
-
-
Here is content of Modal
-
-
-
-
+
+
+
+
+
+
+
+
+
Here is content of Modal
+
+
-
-
`;
diff --git a/components/modal/__tests__/confirm.test.js b/components/modal/__tests__/confirm.test.js
index 3d410f4d1..a1f524eb8 100644
--- a/components/modal/__tests__/confirm.test.js
+++ b/components/modal/__tests__/confirm.test.js
@@ -1,6 +1,7 @@
import Modal from '..';
import { sleep } from '../../../tests/utils';
const { confirm } = Modal;
+jest.mock('../../_util/Portal');
describe('Modal.confirm triggers callbacks correctly', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
@@ -8,6 +9,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
afterEach(() => {
errorSpy.mockReset();
document.body.innerHTML = '';
+ Modal.destroyAll();
});
afterAll(() => {
@@ -19,11 +21,14 @@ describe('Modal.confirm triggers callbacks correctly', () => {
}
function open(args) {
+ jest.useFakeTimers();
confirm({
title: 'Want to delete these items?',
content: 'some descriptions',
...args,
});
+ jest.runAllTimers();
+ jest.useRealTimers();
}
it('trigger onCancel once when click on cancel button', async () => {
diff --git a/components/style/mixins/customize.less b/components/style/mixins/customize.less
new file mode 100644
index 000000000..e393c8ca3
--- /dev/null
+++ b/components/style/mixins/customize.less
@@ -0,0 +1,178 @@
+// customize dark components background in popover containers(like Modal, Drawer, Card, Popover, Popconfirm, Notification, ...)
+// for dark theme
+.popover-customize-bg(@containerClass, @background: @popover-background, @prefix: @ant-prefix)
+ when
+ (@theme = dark) {
+ @picker-prefix-cls: ~'@{prefix}-picker';
+ @slider-prefix-cls: ~'@{prefix}-slider';
+ @anchor-prefix-cls: ~'@{prefix}-anchor';
+ @collapse-prefix-cls: ~'@{prefix}-collapse';
+ @tab-prefix-cls: ~'@{prefix}-tabs';
+ @timeline-prefix-cls: ~'@{prefix}-timeline';
+ @tree-prefix-cls: ~'@{prefix}-tree';
+ @card-prefix-cls: ~'@{prefix}-card';
+ @badge-prefix-cls: ~'@{prefix}-badge';
+ @transfer-prefix-cls: ~'@{prefix}-transfer';
+ @calendar-prefix-cls: ~'@{prefix}-picker-calendar';
+ @calendar-picker-prefix-cls: ~'@{prefix}-picker';
+ @table-prefix-cls: ~'@{prefix}-table';
+
+ @popover-border: @border-width-base @border-style-base @popover-customize-border-color;
+
+ .@{containerClass} {
+ .@{picker-prefix-cls}-clear,
+ .@{slider-prefix-cls}-handle,
+ .@{anchor-prefix-cls}-wrapper,
+ .@{collapse-prefix-cls}-content,
+ .@{timeline-prefix-cls}-item-head,
+ .@{card-prefix-cls} {
+ background-color: @background;
+ }
+
+ .@{transfer-prefix-cls} {
+ &-list {
+ &-header {
+ background: @background;
+ border-bottom: @popover-border;
+ }
+ &-content-item:not(.@{transfer-prefix-cls}-list-content-item-disabled):hover {
+ background-color: @item-hover-bg;
+ }
+ }
+ }
+
+ tr.@{table-prefix-cls}-expanded-row {
+ &,
+ &:hover {
+ > td {
+ background: #272727;
+ }
+ }
+ }
+ .@{table-prefix-cls}.@{table-prefix-cls}-small {
+ thead {
+ > tr {
+ > th {
+ background-color: @background;
+ border-bottom: @popover-border;
+ }
+ }
+ }
+ }
+ .@{table-prefix-cls} {
+ background-color: @background;
+ .@{table-prefix-cls}-row-expand-icon {
+ border: @popover-border;
+ }
+ tfoot {
+ > tr {
+ > th,
+ > td {
+ border-bottom: @popover-border;
+ }
+ }
+ }
+ thead {
+ > tr {
+ > th {
+ background-color: #272727;
+ border-bottom: @popover-border;
+ }
+ }
+ }
+ tbody {
+ > tr {
+ > td {
+ border-bottom: @popover-border;
+ &.@{table-prefix-cls}-cell-fix-left,
+ &.@{table-prefix-cls}-cell-fix-right {
+ background-color: @background;
+ }
+ }
+ &.@{table-prefix-cls}-row:hover {
+ > td {
+ background: @table-header-sort-active-bg;
+ }
+ }
+ }
+ }
+ &.@{table-prefix-cls}-bordered {
+ .@{table-prefix-cls}-title {
+ border: @popover-border;
+ }
+
+ // ============================= Cell =============================
+ thead > tr > th,
+ tbody > tr > td,
+ tfoot > tr > th,
+ tfoot > tr > td {
+ border-right: @popover-border;
+ }
+
+ // Fixed right should provides additional border
+ .@{table-prefix-cls}-cell-fix-right-first::after {
+ border-right: @popover-border;
+ }
+
+ // ============================ Header ============================
+ table > {
+ thead {
+ > tr:not(:last-child) > th {
+ border-bottom: @border-width-base @border-style-base @border-color-split;
+ }
+ }
+ }
+
+ // =========================== Content ============================
+ .@{table-prefix-cls}-container {
+ border: @popover-border;
+ }
+
+ // ========================== Expandable ==========================
+ .@{table-prefix-cls}-expanded-row-fixed {
+ &::after {
+ border-right: @popover-border;
+ }
+ }
+
+ .@{table-prefix-cls}-footer {
+ border: @popover-border;
+ }
+ }
+ .@{table-prefix-cls}-filter-trigger-container-open {
+ background-color: #525252;
+ }
+ }
+
+ .@{calendar-prefix-cls}-full {
+ background-color: @background;
+ .@{calendar-picker-prefix-cls}-panel {
+ background-color: @background;
+ .@{calendar-prefix-cls}-date {
+ border-top: 2px solid @popover-customize-border-color;
+ }
+ }
+ }
+
+ .@{tab-prefix-cls} {
+ &.@{tab-prefix-cls}-card .@{tab-prefix-cls}-card-bar .@{tab-prefix-cls}-tab-active {
+ background-color: @background;
+ border-bottom: @border-width-base solid @background;
+ }
+ }
+
+ .@{badge-prefix-cls} {
+ &-count {
+ box-shadow: 0 0 0 1px @background;
+ }
+ }
+
+ .@{tree-prefix-cls} {
+ &-show-line {
+ .@{tree-prefix-cls}-switcher {
+ background: @background;
+ }
+ }
+ }
+ }
+}
diff --git a/components/style/mixins/index.less b/components/style/mixins/index.less
index 802d774c9..c36323ed2 100644
--- a/components/style/mixins/index.less
+++ b/components/style/mixins/index.less
@@ -8,5 +8,6 @@
@import 'reset';
@import 'operation-unit';
@import 'typography';
+@import 'customize';
@import 'box';
@import 'modal-mask';
diff --git a/components/table/__tests__/Table.rowSelection.test.js b/components/table/__tests__/Table.rowSelection.test.js
index ac505b2c5..867c1ad5e 100644
--- a/components/table/__tests__/Table.rowSelection.test.js
+++ b/components/table/__tests__/Table.rowSelection.test.js
@@ -1,12 +1,13 @@
import { mount } from '@vue/test-utils';
import { asyncExpect, sleep } from '../../../tests/utils';
import Table from '..';
-
+jest.mock('../../_util/Portal');
describe('Table.rowSelection', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
afterEach(() => {
errorSpy.mockReset();
+ document.body.innerHTML = '';
});
afterAll(() => {
@@ -582,8 +583,9 @@ describe('Table.rowSelection', () => {
});
// Check Lucy
- clickFilter([0, 1]);
-
+ clickFilter([0]);
+ await sleep();
+ clickFilter([1]);
await asyncExpect(() => {
expect(wrapper.findAll('tbody tr').length).toBe(1);
});
diff --git a/components/vc-drawer/index.ts b/components/vc-drawer/index.ts
index 2049a56b9..58e0138c0 100644
--- a/components/vc-drawer/index.ts
+++ b/components/vc-drawer/index.ts
@@ -1,3 +1,4 @@
+// base rc-drawer 4.4.2
import Drawer from './src/DrawerWrapper';
export default Drawer;
diff --git a/components/vc-drawer/src/DrawerChild.tsx b/components/vc-drawer/src/DrawerChild.tsx
index c690a3229..071de2356 100644
--- a/components/vc-drawer/src/DrawerChild.tsx
+++ b/components/vc-drawer/src/DrawerChild.tsx
@@ -1,12 +1,19 @@
-import { defineComponent, reactive, onMounted, onUpdated, onUnmounted, nextTick, watch } from 'vue';
+import {
+ defineComponent,
+ reactive,
+ onMounted,
+ computed,
+ onUnmounted,
+ nextTick,
+ watch,
+ ref,
+} from 'vue';
import classnames from '../../_util/classNames';
import getScrollBarSize from '../../_util/getScrollBarSize';
import KeyCode from '../../_util/KeyCode';
import omit from '../../_util/omit';
import supportsPassive from '../../_util/supportsPassive';
-import { DrawerChildProps } from './IDrawerPropTypes';
-import type { IDrawerChildProps } from './IDrawerPropTypes';
-import setStyle from '../../_util/setStyle';
+import { drawerChildProps } from './IDrawerPropTypes';
import {
addEventListener,
@@ -25,127 +32,92 @@ export interface scrollLockOptions {
container: HTMLElement;
}
-const createScrollLocker = (options: scrollLockOptions) => {
- const scrollBarSize = 0;
- const cacheStyleMap = new Map();
- return {
- getContainer: () => {
- return options?.container;
- },
- getCacheStyleMap: () => {
- return cacheStyleMap;
- },
- lock: () => {
- cacheStyleMap.set(
- options.container,
- setStyle(
- {
- width: scrollBarSize !== 0 ? `calc(100% - ${scrollBarSize}px)` : undefined,
- overflow: 'hidden',
- overflowX: 'hidden',
- overflowY: 'hidden',
- },
- {
- element: options.container || document.body,
- },
- ),
- );
- },
- unLock: () => {
- setStyle(cacheStyleMap.get(options.container) || {}, { element: options.container });
- cacheStyleMap.delete(options.container);
- },
- };
-};
-
const DrawerChild = defineComponent({
inheritAttrs: false,
- props: DrawerChildProps,
+ props: drawerChildProps(),
emits: ['close', 'handleClick', 'change'],
setup(props, { emit, slots }) {
const state = reactive({
- levelDom: [],
- dom: null,
- contentWrapper: null,
- contentDom: null,
- maskDom: null,
- handlerDom: null,
- drawerId: null,
- timeout: null,
- passive: null,
startPos: {
x: null,
y: null,
},
- scrollLocker: null,
});
+ let timeout;
+ const contentWrapper = ref
();
+ const dom = ref();
+ const maskDom = ref();
+ const handlerDom = ref();
+ const contentDom = ref();
+ let levelDom = [];
+ const drawerId = `drawer_id_${Number(
+ (Date.now() + Math.random())
+ .toString()
+ .replace('.', Math.round(Math.random() * 9).toString()),
+ ).toString(16)}`;
+
+ const passive = !windowIsUndefined && supportsPassive ? { passive: false } : false;
onMounted(() => {
nextTick(() => {
- if (!windowIsUndefined) {
- state.passive = supportsPassive ? { passive: false } : false;
- }
- const { open, getContainer, showMask, autoFocus } = props;
+ const { open, getContainer, showMask, autofocus } = props;
const container = getContainer?.();
- state.drawerId = `drawer_id_${Number(
- (Date.now() + Math.random())
- .toString()
- .replace('.', Math.round(Math.random() * 9).toString()),
- ).toString(16)}`;
getLevelDom(props);
-
- if (container) {
- state.scrollLocker = createScrollLocker({
- container: container.parentNode,
- });
- }
-
if (open) {
if (container && container.parentNode === document.body) {
- currentDrawer[state.drawerId] = open;
+ currentDrawer[drawerId] = open;
}
// 默认打开状态时推出 level;
openLevelTransition();
nextTick(() => {
- if (autoFocus) {
+ if (autofocus) {
domFocus();
}
});
if (showMask) {
- state.scrollLocker?.lock();
+ props.scrollLocker?.lock();
}
}
});
});
-
- onUpdated(() => {
- const { open, getContainer, showMask, autoFocus } = props;
-
- const container = getContainer?.();
- if (container && container.parentNode === document.body) {
- currentDrawer[state.drawerId] = !!open;
- }
- openLevelTransition();
- if (open) {
- if (autoFocus) {
- domFocus();
+ 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 (showMask) {
- state.scrollLocker?.lock();
+ openLevelTransition();
+ if (open) {
+ if (autofocus) {
+ domFocus();
+ }
+ if (showMask) {
+ scrollLocker?.lock();
+ }
+ } else {
+ scrollLocker?.unLock();
}
- } else {
- state.scrollLocker?.unLock();
- }
- });
+ },
+ { flush: 'post' },
+ );
onUnmounted(() => {
const { open } = props;
- delete currentDrawer[state.drawerId];
+ delete currentDrawer[drawerId];
if (open) {
setLevelTransform(false);
document.body.style.touchAction = '';
}
- state.scrollLocker?.unLock();
+ props.scrollLocker?.unLock();
});
watch(
@@ -153,19 +125,13 @@ const DrawerChild = defineComponent({
val => {
if (val) {
// test 的 bug, 有动画过场,删除 dom
- state.contentDom = null;
- if (state.contentWrapper) {
- state.contentWrapper.style.transition = `none`;
- setTimeout(() => {
- state.contentWrapper.style.transition = ``;
- });
- }
+ contentDom.value = null;
}
},
);
const domFocus = () => {
- state.dom?.focus?.();
+ dom.value?.focus?.();
};
const removeStartHandler = (e: TouchEvent) => {
@@ -186,9 +152,9 @@ const DrawerChild = defineComponent({
const differX = e.changedTouches[0].clientX - state.startPos.x;
const differY = e.changedTouches[0].clientY - state.startPos.y;
if (
- (currentTarget === state.maskDom ||
- currentTarget === state.handlerDom ||
- (currentTarget === state.contentDom &&
+ (currentTarget === maskDom.value ||
+ currentTarget === handlerDom.value ||
+ (currentTarget === contentDom.value &&
getTouchParentScroll(currentTarget, e.target as HTMLElement, differX, differY))) &&
e.cancelable
) {
@@ -201,6 +167,9 @@ const DrawerChild = defineComponent({
removeEventListener(dom, transitionEndFun, transitionEnd);
dom.style.transition = '';
};
+ const onClose = (e: Event) => {
+ emit('close', e);
+ };
const onKeyDown = (e: KeyboardEvent) => {
if (e.keyCode === KeyCode.ESC) {
@@ -209,19 +178,15 @@ const DrawerChild = defineComponent({
}
};
- const onClose = (e: Event) => {
- emit('close', e);
- };
-
const onWrapperTransitionEnd = (e: TransitionEvent) => {
const { open, afterVisibleChange } = props;
- if (e.target === state.contentWrapper && e.propertyName.match(/transform$/)) {
- state.dom.style.transition = '';
+ if (e.target === contentWrapper.value && e.propertyName.match(/transform$/)) {
+ dom.value.style.transition = '';
if (!open && getCurrentDrawerSome()) {
document.body.style.overflowX = '';
- if (state.maskDom) {
- state.maskDom.style.left = '';
- state.maskDom.style.width = '';
+ if (maskDom.value) {
+ maskDom.value.style.left = '';
+ maskDom.value.style.width = '';
}
}
if (afterVisibleChange) {
@@ -230,11 +195,21 @@ const DrawerChild = defineComponent({
}
};
+ const horizontalBoolAndPlacementName = computed(() => {
+ const { placement } = props;
+ const isHorizontal = placement === 'left' || placement === 'right';
+ const placementName = `translate${isHorizontal ? 'X' : 'Y'}`;
+ return {
+ isHorizontal,
+ placementName,
+ };
+ });
+
const openLevelTransition = () => {
const { open, width, height } = props;
- const { isHorizontal, placementName } = getHorizontalBoolAndPlacementName();
- const contentValue = state.contentDom
- ? state.contentDom.getBoundingClientRect()[isHorizontal ? 'width' : 'height']
+ const { isHorizontal, placementName } = horizontalBoolAndPlacementName.value;
+ const contentValue = contentDom.value
+ ? contentDom.value.getBoundingClientRect()[isHorizontal ? 'width' : 'height']
: 0;
const value = (isHorizontal ? width : height) || contentValue;
setLevelAndScrolling(open, placementName, value);
@@ -248,7 +223,7 @@ const DrawerChild = defineComponent({
) => {
const { placement, levelMove, duration, ease, showMask } = props;
// router 切换时可能会导至页面失去滚动条,所以需要时时获取。
- state.levelDom.forEach(dom => {
+ levelDom.forEach(dom => {
dom.style.transition = `transform ${duration} ${ease}`;
addEventListener(dom, transitionEndFun, transitionEnd);
let levelValue = open ? value : 0;
@@ -290,7 +265,7 @@ const DrawerChild = defineComponent({
// 处理 body 滚动
if (container && container.parentNode === document.body && showMask) {
const eventArray = ['touchstart'];
- const domArray = [document.body, state.maskDom, state.handlerDom, state.contentDom];
+ const domArray = [document.body, maskDom.value, handlerDom.value, contentDom.value];
if (open && document.body.style.overflow !== 'hidden') {
if (right) {
addScrollingEffect(right);
@@ -305,7 +280,7 @@ const DrawerChild = defineComponent({
item,
eventArray[i] || 'touchmove',
i ? removeMoveHandler : removeStartHandler,
- state.passive,
+ passive,
);
});
} else if (getCurrentDrawerSome()) {
@@ -322,7 +297,7 @@ const DrawerChild = defineComponent({
item,
eventArray[i] || 'touchmove',
i ? removeMoveHandler : removeStartHandler,
- state.passive,
+ passive,
);
});
}
@@ -333,25 +308,25 @@ const DrawerChild = defineComponent({
const { placement, duration, ease } = props;
const widthTransition = `width ${duration} ${ease}`;
const transformTransition = `transform ${duration} ${ease}`;
- state.dom.style.transition = 'none';
+ dom.value.style.transition = 'none';
switch (placement) {
case 'right':
- state.dom.style.transform = `translateX(-${right}px)`;
+ dom.value.style.transform = `translateX(-${right}px)`;
break;
case 'top':
case 'bottom':
- state.dom.style.width = `calc(100% - ${right}px)`;
- state.dom.style.transform = 'translateZ(0)';
+ dom.value.style.width = `calc(100% - ${right}px)`;
+ dom.value.style.transform = 'translateZ(0)';
break;
default:
break;
}
- clearTimeout(state.timeout);
- state.timeout = setTimeout(() => {
- if (state.dom) {
- state.dom.style.transition = `${transformTransition},${widthTransition}`;
- state.dom.style.width = '';
- state.dom.style.transform = '';
+ clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ if (dom.value) {
+ dom.value.style.transition = `${transformTransition},${widthTransition}`;
+ dom.value.style.width = '';
+ dom.value.style.transform = '';
}
});
};
@@ -359,59 +334,59 @@ const DrawerChild = defineComponent({
const remScrollingEffect = (right: number) => {
const { placement, duration, ease } = props;
- state.dom.style.transition = 'none';
+ dom.value.style.transition = 'none';
let heightTransition: string;
let widthTransition = `width ${duration} ${ease}`;
const transformTransition = `transform ${duration} ${ease}`;
switch (placement) {
case 'left': {
- state.dom.style.width = '100%';
+ dom.value.style.width = '100%';
widthTransition = `width 0s ${ease} ${duration}`;
break;
}
case 'right': {
- state.dom.style.transform = `translateX(${right}px)`;
- state.dom.style.width = '100%';
+ dom.value.style.transform = `translateX(${right}px)`;
+ dom.value.style.width = '100%';
widthTransition = `width 0s ${ease} ${duration}`;
- if (state.maskDom) {
- state.maskDom.style.left = `-${right}px`;
- state.maskDom.style.width = `calc(100% + ${right}px)`;
+ if (maskDom.value) {
+ maskDom.value.style.left = `-${right}px`;
+ maskDom.value.style.width = `calc(100% + ${right}px)`;
}
break;
}
case 'top':
case 'bottom': {
- state.dom.style.width = `calc(100% + ${right}px)`;
- state.dom.style.height = '100%';
- state.dom.style.transform = 'translateZ(0)';
+ dom.value.style.width = `calc(100% + ${right}px)`;
+ dom.value.style.height = '100%';
+ dom.value.style.transform = 'translateZ(0)';
heightTransition = `height 0s ${ease} ${duration}`;
break;
}
default:
break;
}
- clearTimeout(state.timeout);
- state.timeout = setTimeout(() => {
- if (state.dom) {
- state.dom.style.transition = `${transformTransition},${
+ clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ if (dom.value) {
+ dom.value.style.transition = `${transformTransition},${
heightTransition ? `${heightTransition},` : ''
}${widthTransition}`;
- state.dom.style.transform = '';
- state.dom.style.width = '';
- state.dom.style.height = '';
+ dom.value.style.transform = '';
+ dom.value.style.width = '';
+ dom.value.style.height = '';
}
});
};
const getCurrentDrawerSome = () => !Object.keys(currentDrawer).some(key => currentDrawer[key]);
- const getLevelDom = ({ level, getContainer }: IDrawerChildProps) => {
+ const getLevelDom = ({ level, getContainer }) => {
if (windowIsUndefined) {
return;
}
const container = getContainer?.();
const parent = container ? (container.parentNode as HTMLElement) : null;
- state.levelDom = [];
+ levelDom = [];
if (level === 'all') {
const children: HTMLElement[] = parent ? Array.prototype.slice.call(parent.children) : [];
children.forEach((child: HTMLElement) => {
@@ -421,28 +396,28 @@ const DrawerChild = defineComponent({
child.nodeName !== 'LINK' &&
child !== container
) {
- state.levelDom.push(child);
+ levelDom.push(child);
}
});
} else if (level) {
dataToArray(level).forEach(key => {
document.querySelectorAll(key).forEach(item => {
- state.levelDom.push(item);
+ levelDom.push(item);
});
});
}
};
- const getHorizontalBoolAndPlacementName = () => {
- const { placement } = props;
- const isHorizontal = placement === 'left' || placement === 'right';
- const placementName = `translate${isHorizontal ? 'X' : 'Y'}`;
- return {
- isHorizontal,
- placementName,
- };
+ const onHandleClick = e => {
+ emit('handleClick', e);
};
+ const canOpen = ref(false);
+ watch(dom, () => {
+ nextTick(() => {
+ canOpen.value = true;
+ });
+ });
return () => {
const {
width,
@@ -465,17 +440,19 @@ const DrawerChild = defineComponent({
scrollLocker,
contentWrapperStyle,
style,
+ class: className,
...otherProps
} = props;
// 首次渲染都将是关闭状态。
- const open = state.dom ? $open : false;
+ const open = $open && canOpen.value;
const wrapperClassName = classnames(prefixCls, {
[`${prefixCls}-${placement}`]: true,
[`${prefixCls}-open`]: open,
+ [className]: !!className,
'no-mask': !showMask,
});
- const { placementName } = getHorizontalBoolAndPlacementName();
+ const { placementName } = horizontalBoolAndPlacementName.value;
// 百分比与像素动画不同步,第一次打用后全用像素动画。
// const defaultValue = !this.contentDom || !level ? '100%' : `${value}px`;
const placementPos = placement === 'left' || placement === 'top' ? '-100%' : '100%';
@@ -483,13 +460,11 @@ const DrawerChild = defineComponent({
return (
{
- state.dom = c as HTMLElement;
- }}
+ ref={dom}
onKeydown={open && keyboard ? onKeyDown : undefined}
onTransitionend={onWrapperTransitionEnd}
>
@@ -498,9 +473,7 @@ const DrawerChild = defineComponent({
class={`${prefixCls}-mask`}
onClick={maskClosable ? onClose : undefined}
style={maskStyle}
- ref={c => {
- state.maskDom = c as HTMLElement;
- }}
+ ref={maskDom}
/>
)}
{
- state.contentWrapper = c as HTMLElement;
- }}
+ ref={contentWrapper}
>
-
{
- state.contentDom = c as HTMLElement;
- }}
- >
- {slots.children?.()}
+
+ {slots.default?.()}
+ {slots.handler ? (
+
+ {slots.handler?.()}
+
+ ) : null}
);
diff --git a/components/vc-drawer/src/DrawerWrapper.tsx b/components/vc-drawer/src/DrawerWrapper.tsx
index 855eddecc..0984d3287 100644
--- a/components/vc-drawer/src/DrawerWrapper.tsx
+++ b/components/vc-drawer/src/DrawerWrapper.tsx
@@ -1,12 +1,12 @@
import Child from './DrawerChild';
import { initDefaultProps } from '../../_util/props-util';
-import { Teleport, defineComponent, ref, watch } from 'vue';
-import { DrawerProps } from './IDrawerPropTypes';
-import type { IDrawerProps } from './IDrawerPropTypes';
+import { defineComponent, ref } from 'vue';
+import { drawerProps } from './IDrawerPropTypes';
+import PortalWrapper from '../../_util/PortalWrapper';
const DrawerWrapper = defineComponent({
inheritAttrs: false,
- props: initDefaultProps(DrawerProps, {
+ props: initDefaultProps(drawerProps(), {
prefixCls: 'drawer',
placement: 'left',
getContainer: 'body',
@@ -17,55 +17,16 @@ const DrawerWrapper = defineComponent({
showMask: true,
maskClosable: true,
maskStyle: {},
- wrapperClassName: null,
+ wrapperClassName: '',
keyboard: true,
forceRender: false,
- autoFocus: true,
+ autofocus: true,
}),
emits: ['handleClick', 'close'],
-
- setup(props, { emit, expose, slots }) {
+ slots: ['handler'],
+ setup(props, { emit, slots }) {
const dom = ref
(null);
- const container = ref(props.getContainer || null);
-
- const open = ref(props.open);
-
- const $forceRender = ref(props.forceRender);
-
- watch(
- () => props.open,
- val => {
- if (!dom.value) {
- $forceRender.value = true;
- open.value = false;
- setTimeout(() => {
- open.value = true;
- });
- } else {
- open.value = val;
- }
- },
- );
-
- const getDerivedStateFromProps = (
- props: IDrawerProps,
- { prevProps }: { prevProps: IDrawerProps },
- ) => {
- const newState: {
- open?: boolean;
- prevProps: IDrawerProps;
- } = {
- prevProps: props,
- };
- if (typeof prevProps !== 'undefined' && props.open !== prevProps.open) {
- newState.open = props.open;
- }
- return newState;
- };
-
- expose({ getDerivedStateFromProps });
-
const onHandleClick = (e: MouseEvent | KeyboardEvent) => {
emit('handleClick', e);
};
@@ -81,11 +42,11 @@ const DrawerWrapper = defineComponent({
let portal = null;
if (!getContainer) {
return (
-
+
dom.value}
onClose={onClose}
onHandleClick={onHandleClick}
@@ -93,21 +54,33 @@ const DrawerWrapper = defineComponent({
);
}
- if ($forceRender.value || open.value || dom.value) {
+
+ // 如果有 handler 为内置强制渲染;
+ const $forceRender = !!slots.handler || forceRender;
+ if ($forceRender || props.open || dom.value) {
portal = (
-
-
- dom.value}
- afterVisibleChange={afterVisibleChange}
- onClose={onClose}
- onHandleClick={onHandleClick}
- />
-
-
+
(
+
+ ),
+ }}
+ >
);
}
return portal;
diff --git a/components/vc-drawer/src/IDrawerPropTypes.ts b/components/vc-drawer/src/IDrawerPropTypes.ts
index a23808d18..5a3d24ee6 100644
--- a/components/vc-drawer/src/IDrawerPropTypes.ts
+++ b/components/vc-drawer/src/IDrawerPropTypes.ts
@@ -1,34 +1,38 @@
import PropTypes from '../../_util/vue-types';
-import type { PropType, ExtractPropTypes } from 'vue';
+import type { PropType } from 'vue';
export type IPlacement = 'left' | 'top' | 'right' | 'bottom';
-
-const Props = {
+type ILevelMove = number | [number, number];
+const props = () => ({
prefixCls: PropTypes.string,
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- style: PropTypes.object,
+ style: PropTypes.style,
+ class: PropTypes.string,
placement: {
type: String as PropType
,
},
- class: PropTypes.string,
- level: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
- levelMove: PropTypes.oneOfType([PropTypes.number, PropTypes.func, PropTypes.array]),
+ wrapperClassName: PropTypes.string,
+ level: { type: [String, Array] as PropType },
+ levelMove: {
+ type: [Number, Function, Array] as PropType<
+ ILevelMove | ((e: { target: HTMLElement; open: boolean }) => ILevelMove)
+ >,
+ },
duration: PropTypes.string,
ease: PropTypes.string,
showMask: PropTypes.looseBool,
maskClosable: PropTypes.looseBool,
- maskStyle: PropTypes.object,
+ maskStyle: PropTypes.style,
afterVisibleChange: PropTypes.func,
keyboard: PropTypes.looseBool,
- contentWrapperStyle: PropTypes.object,
- autoFocus: PropTypes.looseBool,
+ contentWrapperStyle: PropTypes.style,
+ autofocus: PropTypes.looseBool,
open: PropTypes.looseBool,
-};
+});
-const DrawerProps = {
- ...Props,
- wrapperClassName: PropTypes.string,
+const drawerProps = () => ({
+ ...props(),
forceRender: PropTypes.looseBool,
getContainer: PropTypes.oneOfType([
PropTypes.string,
@@ -36,18 +40,14 @@ const DrawerProps = {
PropTypes.object,
PropTypes.looseBool,
]),
-};
+});
-type IDrawerProps = Partial>;
-
-const DrawerChildProps = {
- ...Props,
+const drawerChildProps = () => ({
+ ...props(),
getContainer: PropTypes.func,
getOpenCount: PropTypes.func,
scrollLocker: PropTypes.any,
switchScrollingEffect: PropTypes.func,
-};
+});
-type IDrawerChildProps = Partial>;
-
-export { DrawerProps, DrawerChildProps, IDrawerProps, IDrawerChildProps };
+export { drawerProps, drawerChildProps };
diff --git a/components/vc-trigger/Popup/useVisibleStatus.ts b/components/vc-trigger/Popup/useVisibleStatus.ts
index 331e7f4a5..888744878 100644
--- a/components/vc-trigger/Popup/useVisibleStatus.ts
+++ b/components/vc-trigger/Popup/useVisibleStatus.ts
@@ -1,5 +1,5 @@
import type { Ref } from 'vue';
-import { nextTick, onBeforeUnmount, ref, watch } from 'vue';
+import { nextTick, onBeforeUnmount, ref, watch, onMounted } from 'vue';
import raf from '../../_util/raf';
/**
@@ -62,29 +62,32 @@ export default (
},
{ immediate: true, flush: 'post' },
);
- // Go next status
- watch(
- status,
- () => {
- switch (status.value) {
- case 'measure':
- doMeasure();
- break;
- default:
- }
+ onMounted(() => {
+ // Go next status
+ watch(
+ status,
+ () => {
+ switch (status.value) {
+ case 'measure':
+ doMeasure();
+ break;
+ default:
+ }
+
+ if (status.value) {
+ nextTick(() => {
+ const index = StatusQueue.indexOf(status.value);
+ const nextStatus = StatusQueue[index + 1];
+ if (nextStatus && index !== -1) {
+ setStatus(nextStatus);
+ }
+ });
+ }
+ },
+ { immediate: true, flush: 'post' },
+ );
+ });
- if (status.value) {
- nextTick(() => {
- const index = StatusQueue.indexOf(status.value);
- const nextStatus = StatusQueue[index + 1];
- if (nextStatus && index !== -1) {
- setStatus(nextStatus);
- }
- });
- }
- },
- { immediate: true, flush: 'post' },
- );
onBeforeUnmount(() => {
destroyRef.value = true;
cancelRaf();
diff --git a/components/vc-util/Dom/scrollLocker.ts b/components/vc-util/Dom/scrollLocker.ts
new file mode 100644
index 000000000..87320ffc9
--- /dev/null
+++ b/components/vc-util/Dom/scrollLocker.ts
@@ -0,0 +1,130 @@
+import type { CSSProperties } from '@vue/runtime-dom';
+import getScrollBarSize from '../../_util/getScrollBarSize';
+import setStyle from '../../_util/setStyle';
+
+export interface scrollLockOptions {
+ container: HTMLElement;
+}
+
+interface Ilocks {
+ target: typeof uuid;
+ options: scrollLockOptions;
+}
+
+let locks: Ilocks[] = [];
+const scrollingEffectClassName = 'ant-scrolling-effect';
+const scrollingEffectClassNameReg = new RegExp(`${scrollingEffectClassName}`, 'g');
+
+let uuid = 0;
+
+// https://github.com/ant-design/ant-design/issues/19340
+// https://github.com/ant-design/ant-design/issues/19332
+const cacheStyle = new Map();
+
+export default class ScrollLocker {
+ private lockTarget: typeof uuid;
+
+ private options: scrollLockOptions;
+
+ constructor(options?: scrollLockOptions) {
+ // eslint-disable-next-line no-plusplus
+ this.lockTarget = uuid++;
+ this.options = options;
+ }
+
+ getContainer = (): HTMLElement | undefined => {
+ return this.options?.container;
+ };
+
+ // if options change...
+ reLock = (options?: scrollLockOptions) => {
+ const findLock = locks.find(({ target }) => target === this.lockTarget);
+
+ if (findLock) {
+ this.unLock();
+ }
+
+ this.options = options;
+
+ if (findLock) {
+ findLock.options = options;
+ this.lock();
+ }
+ };
+
+ lock = () => {
+ // If lockTarget exist return
+ if (locks.some(({ target }) => target === this.lockTarget)) {
+ return;
+ }
+
+ // If same container effect, return
+ if (locks.some(({ options }) => options?.container === this.options?.container)) {
+ locks = [...locks, { target: this.lockTarget, options: this.options }];
+ return;
+ }
+
+ let scrollBarSize = 0;
+ const container = this.options?.container || document.body;
+
+ if (
+ (container === document.body &&
+ window.innerWidth - document.documentElement.clientWidth > 0) ||
+ container.scrollHeight > container.clientHeight
+ ) {
+ scrollBarSize = getScrollBarSize();
+ }
+
+ const containerClassName = container.className;
+
+ if (
+ locks.filter(({ options }) => options?.container === this.options?.container).length === 0
+ ) {
+ cacheStyle.set(
+ container,
+ setStyle(
+ {
+ width: scrollBarSize !== 0 ? `calc(100% - ${scrollBarSize}px)` : undefined,
+ overflow: 'hidden',
+ overflowX: 'hidden',
+ overflowY: 'hidden',
+ },
+ {
+ element: container,
+ },
+ ),
+ );
+ }
+
+ // https://github.com/ant-design/ant-design/issues/19729
+ if (!scrollingEffectClassNameReg.test(containerClassName)) {
+ const addClassName = `${containerClassName} ${scrollingEffectClassName}`;
+ container.className = addClassName.trim();
+ }
+
+ locks = [...locks, { target: this.lockTarget, options: this.options }];
+ };
+
+ unLock = () => {
+ const findLock = locks.find(({ target }) => target === this.lockTarget);
+
+ locks = locks.filter(({ target }) => target !== this.lockTarget);
+
+ if (
+ !findLock ||
+ locks.some(({ options }) => options?.container === findLock.options?.container)
+ ) {
+ return;
+ }
+
+ // Remove Effect
+ const container = this.options?.container || document.body;
+ const containerClassName = container.className;
+
+ if (!scrollingEffectClassNameReg.test(containerClassName)) return;
+
+ setStyle(cacheStyle.get(container), { element: container });
+ cacheStyle.delete(container);
+ container.className = container.className.replace(scrollingEffectClassNameReg, '').trim();
+ };
+}