Skip to content

Commit 9b5a072

Browse files
committed
refactor: popover & popconfirm
1 parent 47c84cd commit 9b5a072

File tree

6 files changed

+98
-84
lines changed

6 files changed

+98
-84
lines changed

components/_util/type.ts

+8
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ export function booleanType(defaultVal?: any) {
5555
return { type: Boolean, default: defaultVal as boolean };
5656
}
5757

58+
export function anyType<T = any>() {
59+
return { validator: () => true } as unknown as { type: PropType<T> };
60+
}
61+
62+
export function stringType<T extends string = string>(defaultVal?: string) {
63+
return { type: String as unknown as PropType<T>, default: defaultVal as T };
64+
}
65+
5866
export function someType<T>(types: any[], defaultVal?: any) {
5967
return { type: types as PropType<T>, default: defaultVal as T };
6068
}

components/popconfirm/demo/placement.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export default defineComponent({
133133
});
134134
</script>
135135
<style scoped>
136-
#components-a-popconfirm-demo-placement .ant-btn {
136+
:deep(#components-a-popconfirm-demo-placement) .ant-btn {
137137
width: 70px;
138138
text-align: center;
139139
padding: 0;

components/popconfirm/index.en-US.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,16 @@ The difference with the 'confirm' modal dialog is that it's more lightweight tha
2828
| okType | Button `type` of the Confirm button | string | `primary` | |
2929
| showCancel | Show cancel button | boolean | true | 3.0 |
3030
| title | title of the confirmation box | string\|slot | - | |
31+
| description | The description of the confirmation box title | string\|slot | - | 4.0 |
3132
| visible (v-model) | hide or show | boolean | - | |
3233

3334
### events
3435

35-
| Events Name | Description | Arguments | |
36-
| --- | --- | --- | --- |
37-
| cancel | callback of cancel | function(e) | - |
38-
| confirm | callback of confirmation | function(e) | - |
39-
| visibleChange | Callback executed when visibility of the tooltip card is changed | function(visible) | - |
36+
| Events Name | Description | Arguments | Version |
37+
| --- | --- | --- | --- | --- |
38+
| cancel | callback of cancel | function(e) | - | |
39+
| confirm | callback of confirmation | function(e) | - | |
40+
| openChange | Callback executed when visibility of the tooltip card is changed | function(open) | - | 4.0 |
4041

4142
Consult [Tooltip's documentation](/components/tooltip/#API) to find more APIs.
4243

components/popconfirm/index.tsx

+61-64
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
import type { ExtractPropTypes, HTMLAttributes, PropType } from 'vue';
2-
import { computed, onMounted, ref, toRef, defineComponent } from 'vue';
3-
import Tooltip from '../tooltip';
2+
import { computed, ref, toRef, defineComponent } from 'vue';
3+
import Popover from '../popover';
44
import abstractTooltipProps from '../tooltip/abstractTooltipProps';
5-
import PropTypes from '../_util/vue-types';
65
import { initDefaultProps } from '../_util/props-util';
76
import type { ButtonProps, LegacyButtonType } from '../button/buttonTypes';
87
import { convertLegacyProps } from '../button/buttonTypes';
98
import ExclamationCircleFilled from '@ant-design/icons-vue/ExclamationCircleFilled';
109
import Button from '../button';
1110
import { useLocaleReceiver } from '../locale-provider/LocaleReceiver';
1211
import defaultLocale from '../locale/en_US';
13-
import { withInstall } from '../_util/type';
12+
import { anyType, objectType, stringType, withInstall } from '../_util/type';
1413
import useMergedState from '../_util/hooks/useMergedState';
15-
import devWarning from '../vc-util/devWarning';
14+
1615
import KeyCode from '../_util/KeyCode';
1716
import useConfigInject from '../config-provider/hooks/useConfigInject';
1817
import classNames from '../_util/classNames';
@@ -21,28 +20,21 @@ import { cloneVNodes } from '../_util/vnode';
2120
import omit from '../_util/omit';
2221
import { tooltipDefaultProps } from '../tooltip/Tooltip';
2322
import ActionButton from '../_util/ActionButton';
23+
import usePopconfirmStyle from './style';
2424

2525
export const popconfirmProps = () => ({
2626
...abstractTooltipProps(),
2727
prefixCls: String,
28-
content: PropTypes.any,
29-
title: PropTypes.any,
30-
okType: {
31-
type: String as PropType<LegacyButtonType>,
32-
default: 'primary',
33-
},
28+
content: anyType(),
29+
title: anyType<string | number>(),
30+
description: anyType<string | number>(),
31+
okType: stringType<LegacyButtonType>('primary'),
3432
disabled: { type: Boolean, default: false },
35-
okText: PropTypes.any,
36-
cancelText: PropTypes.any,
37-
icon: PropTypes.any,
38-
okButtonProps: {
39-
type: Object as PropType<ButtonProps & HTMLAttributes>,
40-
default: undefined as ButtonProps & HTMLAttributes,
41-
},
42-
cancelButtonProps: {
43-
type: Object as PropType<ButtonProps & HTMLAttributes>,
44-
default: undefined as ButtonProps & HTMLAttributes,
45-
},
33+
okText: anyType(),
34+
cancelText: anyType(),
35+
icon: anyType(),
36+
okButtonProps: objectType<ButtonProps & HTMLAttributes>(),
37+
cancelButtonProps: objectType<ButtonProps & HTMLAttributes>(),
4638
showCancel: { type: Boolean, default: true },
4739
onConfirm: Function as PropType<(e: MouseEvent) => void>,
4840
onCancel: Function as PropType<(e: MouseEvent) => void>,
@@ -58,6 +50,7 @@ export interface PopconfirmLocale {
5850
const Popconfirm = defineComponent({
5951
compatConfig: { MODE: 3 },
6052
name: 'APopconfirm',
53+
inheritAttrs: false,
6154
props: initDefaultProps(popconfirmProps(), {
6255
...tooltipDefaultProps(),
6356
trigger: 'click',
@@ -71,75 +64,68 @@ const Popconfirm = defineComponent({
7164
disabled: false,
7265
}),
7366
slots: ['title', 'content', 'okText', 'icon', 'cancelText', 'cancelButton', 'okButton'],
74-
emits: ['update:visible', 'visibleChange'],
75-
setup(props: PopconfirmProps, { slots, emit, expose }) {
76-
onMounted(() => {
77-
devWarning(
78-
props.defaultVisible === undefined,
79-
'Popconfirm',
80-
`'defaultVisible' is deprecated, please use 'v-model:visible'`,
81-
);
82-
});
83-
const tooltipRef = ref();
67+
// emits: ['update:open', 'visibleChange'],
68+
setup(props: PopconfirmProps, { slots, emit, expose, attrs }) {
69+
const rootRef = ref();
8470
expose({
8571
getPopupDomNode: () => {
86-
return tooltipRef.value?.getPopupDomNode?.();
72+
return rootRef.value?.getPopupDomNode?.();
8773
},
8874
});
89-
const [visible, setVisible] = useMergedState(false, {
90-
value: toRef(props, 'visible'),
91-
defaultValue: props.defaultVisible,
75+
const [open, setOpen] = useMergedState(false, {
76+
value: toRef(props, 'open'),
9277
});
9378

94-
const settingVisible = (value: boolean, e?: MouseEvent | KeyboardEvent) => {
95-
if (props.visible === undefined) {
96-
setVisible(value);
79+
const settingOpen = (value: boolean, e?: MouseEvent | KeyboardEvent) => {
80+
if (props.open === undefined) {
81+
setOpen(value);
9782
}
9883

99-
emit('update:visible', value);
100-
emit('visibleChange', value, e);
84+
emit('update:open', value);
85+
emit('openChange', value, e);
10186
};
10287

10388
const close = (e: MouseEvent) => {
104-
settingVisible(false, e);
89+
settingOpen(false, e);
10590
};
10691

10792
const onConfirm = (e: MouseEvent) => {
10893
return props.onConfirm?.(e);
10994
};
11095

11196
const onCancel = (e: MouseEvent) => {
112-
settingVisible(false, e);
97+
settingOpen(false, e);
11398
props.onCancel?.(e);
11499
};
115100

116101
const onKeyDown = (e: KeyboardEvent) => {
117-
if (e.keyCode === KeyCode.ESC && visible) {
118-
settingVisible(false, e);
102+
if (e.keyCode === KeyCode.ESC && open) {
103+
settingOpen(false, e);
119104
}
120105
};
121106

122-
const onVisibleChange = (value: boolean) => {
107+
const onOpenChange = (value: boolean) => {
123108
const { disabled } = props;
124109
if (disabled) {
125110
return;
126111
}
127-
settingVisible(value);
112+
settingOpen(value);
128113
};
129114
const { prefixCls: prefixClsConfirm, getPrefixCls } = useConfigInject('popconfirm', props);
130115
const rootPrefixCls = computed(() => getPrefixCls());
131-
const popoverPrefixCls = computed(() => getPrefixCls('popover'));
132116
const btnPrefixCls = computed(() => getPrefixCls('btn'));
117+
const [wrapSSR] = usePopconfirmStyle(prefixClsConfirm);
133118
const [popconfirmLocale] = useLocaleReceiver('Popconfirm', defaultLocale.Popconfirm);
134119
const renderOverlay = () => {
135120
const {
136121
okButtonProps,
137122
cancelButtonProps,
138123
title = slots.title?.(),
124+
description = slots.description?.(),
139125
cancelText = slots.cancel?.(),
140126
okText = slots.okText?.(),
141127
okType,
142-
icon = slots.icon?.(),
128+
icon = slots.icon?.() || <ExclamationCircleFilled />,
143129
showCancel = true,
144130
} = props;
145131
const { cancelButton, okButton } = slots;
@@ -155,12 +141,20 @@ const Popconfirm = defineComponent({
155141
...okButtonProps,
156142
};
157143
return (
158-
<div class={`${popoverPrefixCls.value}-inner-content`}>
159-
<div class={`${popoverPrefixCls.value}-message`}>
160-
{icon || <ExclamationCircleFilled />}
161-
<div class={`${popoverPrefixCls.value}-message-title`}>{title}</div>
144+
<div class={`${prefixClsConfirm.value}-inner-content`}>
145+
<div class={`${prefixClsConfirm.value}-message`}>
146+
{icon && <span class={`${prefixClsConfirm.value}-message-icon`}>{icon}</span>}
147+
<div
148+
class={[
149+
`${prefixClsConfirm.value}-message-title`,
150+
{ [`${prefixClsConfirm.value}-message-title-only`]: !!description },
151+
]}
152+
>
153+
{title}
154+
</div>
162155
</div>
163-
<div class={`${popoverPrefixCls.value}-buttons`}>
156+
{description && <div class={`${prefixClsConfirm.value}-description`}>{description}</div>}
157+
<div class={`${prefixClsConfirm.value}-buttons`}>
164158
{showCancel ? (
165159
cancelButton ? (
166160
cancelButton(cancelProps)
@@ -188,28 +182,31 @@ const Popconfirm = defineComponent({
188182
};
189183

190184
return () => {
191-
const { placement, overlayClassName, ...restProps } = props;
185+
const { placement, overlayClassName, trigger = 'click', ...restProps } = props;
192186
const otherProps = omit(restProps, [
193187
'title',
194188
'content',
195189
'cancelText',
196190
'okText',
197-
'onUpdate:visible',
191+
'onUpdate:open',
198192
'onConfirm',
199193
'onCancel',
194+
'prefixCls',
200195
]);
201196
const overlayClassNames = classNames(prefixClsConfirm.value, overlayClassName);
202-
return (
203-
<Tooltip
197+
return wrapSSR(
198+
<Popover
204199
{...otherProps}
205-
prefixCls={popoverPrefixCls.value}
200+
{...attrs}
201+
trigger={trigger}
206202
placement={placement}
207-
onVisibleChange={onVisibleChange}
208-
visible={visible.value}
203+
onOpenChange={onOpenChange}
204+
open={open.value}
209205
overlayClassName={overlayClassNames}
210206
transitionName={getTransitionName(rootPrefixCls.value, 'zoom-big', props.transitionName)}
211-
v-slots={{ title: renderOverlay }}
212-
ref={tooltipRef}
207+
v-slots={{ content: renderOverlay }}
208+
ref={rootRef}
209+
data-popover-inject
213210
>
214211
{cloneVNodes(
215212
slots.default?.() || [],
@@ -220,7 +217,7 @@ const Popconfirm = defineComponent({
220217
},
221218
false,
222219
)}
223-
</Tooltip>
220+
</Popover>,
224221
);
225222
};
226223
},

components/popconfirm/index.zh-CN.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,16 @@ cover: https://gw.alipayobjects.com/zos/alicdn/fjMCD9xRq/Popconfirm.svg
2727
| okType | 确认按钮类型 | string | primary | |
2828
| showCancel | 是否显示取消按钮 | boolean | true | 3.0 |
2929
| title | 确认框的描述 | string\|slot || |
30-
| visible (v-model) | 是否显示 | boolean | - | |
30+
| description | 确认内容的详细描述 | string\|slot | - | 4.0 |
31+
| open (v-model) | 是否显示 | boolean | - | 4.0 |
3132

3233
### 事件
3334

34-
| 事件名称 | 说明 | 回调参数 |
35-
| ------------- | -------------- | ----------------- |
36-
| cancel | 点击取消的回调 | function(e) |
37-
| confirm | 点击确认的回调 | function(e) |
38-
| visibleChange | 显示隐藏的回调 | function(visible) |
35+
| 事件名称 | 说明 | 回调参数 | 版本 |
36+
| ---------- | -------------- | -------------- | ---- |
37+
| cancel | 点击取消的回调 | function(e) | |
38+
| confirm | 点击确认的回调 | function(e) | |
39+
| openChange | 显示隐藏的回调 | function(open) | 4.0 |
3940

4041
更多属性请参考 [Tooltip](/components/tooltip-cn/#API)
4142

components/popover/index.tsx

+15-8
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,27 @@ import type { ExtractPropTypes } from 'vue';
22
import { computed, defineComponent, ref } from 'vue';
33
import Tooltip from '../tooltip';
44
import abstractTooltipProps from '../tooltip/abstractTooltipProps';
5-
import PropTypes from '../_util/vue-types';
65
import { filterEmpty, initDefaultProps } from '../_util/props-util';
7-
import { withInstall } from '../_util/type';
6+
import { anyType, withInstall } from '../_util/type';
87
import useConfigInject from '../config-provider/hooks/useConfigInject';
98
import omit from '../_util/omit';
109
import { getTransitionName } from '../_util/transition';
1110
import { tooltipDefaultProps } from '../tooltip/Tooltip';
11+
import useStyle from './style';
12+
import classNames from '../_util/classNames';
1213

1314
export const popoverProps = () => ({
1415
...abstractTooltipProps(),
15-
content: PropTypes.any,
16-
title: PropTypes.any,
16+
content: anyType(),
17+
title: anyType(),
1718
});
1819

1920
export type PopoverProps = Partial<ExtractPropTypes<ReturnType<typeof popoverProps>>>;
2021

2122
const Popover = defineComponent({
2223
compatConfig: { MODE: 3 },
2324
name: 'APopover',
25+
inheritAttrs: false,
2426
props: initDefaultProps(popoverProps(), {
2527
...tooltipDefaultProps(),
2628
trigger: 'hover',
@@ -29,7 +31,7 @@ const Popover = defineComponent({
2931
mouseEnterDelay: 0.1,
3032
mouseLeaveDelay: 0.1,
3133
}),
32-
setup(props, { expose, slots }) {
34+
setup(props, { expose, slots, attrs }) {
3335
const tooltipRef = ref();
3436

3537
expose({
@@ -38,13 +40,14 @@ const Popover = defineComponent({
3840
},
3941
});
4042
const { prefixCls, configProvider } = useConfigInject('popover', props);
43+
const [wrapSSR, hashId] = useStyle(prefixCls);
4144
const rootPrefixCls = computed(() => configProvider.getPrefixCls());
4245
const getOverlay = () => {
4346
const { title = filterEmpty(slots.title?.()), content = filterEmpty(slots.content?.()) } =
4447
props;
4548
const hasTitle = !!(Array.isArray(title) ? title.length : title);
4649
const hasContent = !!(Array.isArray(content) ? content.length : title);
47-
if (!hasTitle && !hasContent) return undefined;
50+
if (!hasTitle && !hasContent) return null;
4851
return (
4952
<>
5053
{hasTitle && <div class={`${prefixCls.value}-title`}>{title}</div>}
@@ -53,14 +56,18 @@ const Popover = defineComponent({
5356
);
5457
};
5558
return () => {
56-
return (
59+
const overlayCls = classNames(props.overlayClassName, hashId.value);
60+
return wrapSSR(
5761
<Tooltip
5862
{...omit(props, ['title', 'content'])}
63+
{...attrs}
5964
prefixCls={prefixCls.value}
6065
ref={tooltipRef}
66+
overlayClassName={overlayCls}
6167
v-slots={{ title: getOverlay, default: slots.default }}
6268
transitionName={getTransitionName(rootPrefixCls.value, 'zoom-big', props.transitionName)}
63-
/>
69+
data-popover-inject
70+
/>,
6471
);
6572
};
6673
},

0 commit comments

Comments
 (0)