mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-06-14 02:18:16 +08:00
feat(design): popover 支持点击外部关闭
新增 closeOnClickOutside 与 clickOutsideIgnore 配置, 兼容 element-plus / tdesign 衍生浮层。 历史列表面板改用 v-model:visible 配合自动收起。
This commit is contained in:
parent
fd652b0d13
commit
846f05e04d
@ -43,8 +43,16 @@ const props = withDefaults(defineProps<PopoverProps>(), {
|
||||
visible: undefined,
|
||||
tabindex: 0,
|
||||
destroyOnClose: false,
|
||||
closeOnClickOutside: true,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
/** 受控模式(传入了 visible)下点击外部收起时触发,便于配合 v-model:visible。 */
|
||||
'update:visible': [_visible: boolean];
|
||||
/** 点击 popover 及其衍生浮层以外的区域时触发。 */
|
||||
clickoutside: [_event: MouseEvent];
|
||||
}>();
|
||||
|
||||
const popoverVisible = ref(false);
|
||||
|
||||
const visibleWatch = watch(
|
||||
@ -179,6 +187,70 @@ if (props.trigger === 'hover' && typeof props.visible === 'undefined') {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* popover 内部触发、却挂载到 body(在 popper 之外)的浮层:弹窗、二次确认框、tooltip、
|
||||
* 下拉 / 日期选择等。点击它们属于 popover 内部交互,不应顺带把 popover 关闭。
|
||||
*
|
||||
* 由于 @tmagic/design 通过适配器支持 element-plus、tdesign 等多套 UI 库,这里同时列出
|
||||
* 两套库的浮层 class(class 名互不冲突,未命中的选择器无副作用),避免切换适配器后失效。
|
||||
*/
|
||||
const DEFAULT_CLICK_OUTSIDE_IGNORE = [
|
||||
// @tmagic/design 自身(与适配器无关)
|
||||
'.tmagic-design-dialog',
|
||||
// element-plus
|
||||
'.el-overlay',
|
||||
'.el-message-box',
|
||||
'.el-popper',
|
||||
'.el-select-dropdown',
|
||||
'.el-picker__popper',
|
||||
'.el-dropdown__popper',
|
||||
'.el-cascader__dropdown',
|
||||
// tdesign:弹窗 / 消息确认(DialogPlugin / MessagePlugin)与各类浮层(tooltip / select / dropdown / 日期选择等均挂在 .t-popup 内)
|
||||
'.t-dialog__ctx',
|
||||
'.t-dialog',
|
||||
'.t-message',
|
||||
'.t-popup',
|
||||
].join(',');
|
||||
|
||||
const clickOutsideIgnoreSelector = computed(() =>
|
||||
[DEFAULT_CLICK_OUTSIDE_IGNORE, props.clickOutsideIgnore].filter(Boolean).join(','),
|
||||
);
|
||||
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
if (props.disabled) return;
|
||||
|
||||
const target = e.target as HTMLElement | null;
|
||||
if (!target) return;
|
||||
|
||||
// 点击 reference、popper 自身或衍生浮层时保持打开
|
||||
if (referenceElementRef.value?.contains(target)) return;
|
||||
if (popperElementRef.value?.contains(target)) return;
|
||||
if (target.closest(clickOutsideIgnoreSelector.value)) return;
|
||||
|
||||
emit('clickoutside', e);
|
||||
|
||||
// 非受控:直接收起;受控:通过 update:visible 通知父级(可配合 v-model:visible)
|
||||
if (typeof props.visible === 'undefined') {
|
||||
popoverVisible.value = false;
|
||||
} else {
|
||||
emit('update:visible', false);
|
||||
}
|
||||
};
|
||||
|
||||
const bindClickOutside = () => globalThis.document?.addEventListener('click', handleClickOutside);
|
||||
const unbindClickOutside = () => globalThis.document?.removeEventListener('click', handleClickOutside);
|
||||
|
||||
watch(popoverVisible, (visible) => {
|
||||
if (!props.closeOnClickOutside) return;
|
||||
|
||||
if (visible) {
|
||||
// 延后到「打开 popover 的这一次点击」冒泡结束后再监听,避免刚打开就被立即关闭
|
||||
nextTick(bindClickOutside);
|
||||
} else {
|
||||
unbindClickOutside();
|
||||
}
|
||||
});
|
||||
|
||||
const destroy = () => {
|
||||
if (!instanceRef.value) return;
|
||||
|
||||
@ -188,5 +260,6 @@ const destroy = () => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
destroy();
|
||||
unbindClickOutside();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -258,6 +258,13 @@ export interface PopoverProps {
|
||||
popperClass?: string;
|
||||
tabindex?: number;
|
||||
destroyOnClose?: boolean;
|
||||
/** 点击 popover 及其衍生浮层以外的区域时收起,默认开启。 */
|
||||
closeOnClickOutside?: boolean;
|
||||
/**
|
||||
* 追加的「点击不关闭」选择器,会与内置的弹窗 / 确认框 / 下拉等浮层选择器合并。
|
||||
* 用于 popover 内部触发、挂载到 body 之外的浮层不应顺带关闭 popover 的场景。
|
||||
*/
|
||||
clickOutsideIgnore?: string;
|
||||
}
|
||||
|
||||
export interface RadioProps {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
popper-class="m-editor-history-list-popover"
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
:visible="visible"
|
||||
v-model:visible="visible"
|
||||
:width="660"
|
||||
>
|
||||
<div class="m-editor-history-list">
|
||||
@ -163,7 +163,10 @@ const ClockIcon = markRaw(Clock);
|
||||
const CloseIcon = markRaw(Close);
|
||||
const activeTab = ref<string>('page');
|
||||
|
||||
/** 面板显隐受控:reference 图标点击切换,右上角关闭按钮置为 false。 */
|
||||
/**
|
||||
* 面板显隐受控:reference 图标点击切换,右上角关闭按钮置为 false。
|
||||
* 点击面板以外区域的自动收起由 TMagicPopover 通过 v-model:visible 回写完成。
|
||||
*/
|
||||
const visible = ref(false);
|
||||
|
||||
const tabPaneComponent = getDesignConfig('components')?.tabPane;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user