mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-09-02 12:59:47 +08:00
feat(design,element-plus-adapter,tdesign-vue-next-adapter): 重新实现Popover组件,不再使用element-plus或tdesign组件
This commit is contained in:
parent
cab36b49a3
commit
5e61f23106
@ -36,6 +36,9 @@
|
||||
"vue3",
|
||||
"typescript"
|
||||
],
|
||||
"dependencies": {
|
||||
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.19.0",
|
||||
"@vitejs/plugin-vue": "^5.1.1",
|
||||
|
@ -1,28 +1,253 @@
|
||||
<template>
|
||||
<component class="tmagic-design-popover" :is="uiComponent" v-bind="uiProps">
|
||||
<slot></slot>
|
||||
|
||||
<template #reference>
|
||||
<slot name="reference"></slot>
|
||||
</template>
|
||||
</component>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="popoverVisible"
|
||||
class="tmagic-design-popper"
|
||||
tabindex="-1"
|
||||
ref="popperElementRef"
|
||||
:class="popperClass"
|
||||
:style="style"
|
||||
>
|
||||
<slot></slot>
|
||||
<span class="tmagic-design-popper-arrow" data-popper-arrow></span>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
||||
import type { Instance } from '@popperjs/core';
|
||||
import { createPopper } from '@popperjs/core';
|
||||
|
||||
import { getConfig } from './config';
|
||||
import { useZIndex } from './index';
|
||||
import type { PopoverProps } from './types';
|
||||
|
||||
defineSlots<{
|
||||
reference(props: {}): any;
|
||||
default(props: {}): any;
|
||||
}>();
|
||||
|
||||
defineOptions({
|
||||
name: 'TMPopover',
|
||||
name: 'TMPopper',
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = defineProps<PopoverProps>();
|
||||
const props = withDefaults(defineProps<PopoverProps>(), {
|
||||
trigger: 'hover',
|
||||
disabled: false,
|
||||
visible: undefined,
|
||||
});
|
||||
|
||||
const ui = getConfig('components')?.popover;
|
||||
const zIndex = useZIndex();
|
||||
const curZIndex = ref<number>(2);
|
||||
const popoverVisible = ref(false);
|
||||
|
||||
const uiComponent = ui?.component || 'el-popover';
|
||||
const visibleWatch = watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
console.log(visible);
|
||||
if (typeof visible === 'undefined') {
|
||||
nextTick(() => {
|
||||
visibleWatch();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const uiProps = computed(() => ui?.props(props) || props);
|
||||
popoverVisible.value = visible;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
const style = computed(() => {
|
||||
if (!props.width) {
|
||||
return {};
|
||||
}
|
||||
|
||||
let { width } = props;
|
||||
|
||||
if (typeof width === 'number') {
|
||||
width = `${width}px`;
|
||||
}
|
||||
|
||||
return {
|
||||
width,
|
||||
};
|
||||
});
|
||||
|
||||
const referenceElementRef = ref<HTMLElement>();
|
||||
const popperElementRef = ref<HTMLElement>();
|
||||
|
||||
const instanceRef = shallowRef<Instance | undefined>();
|
||||
|
||||
onMounted(() => {
|
||||
referenceElementRef.value = getCurrentInstance()?.proxy?.$el.nextElementSibling;
|
||||
});
|
||||
|
||||
watch([referenceElementRef, popperElementRef], ([referenceElement, popperElement]) => {
|
||||
destroy();
|
||||
if (!referenceElement || !popperElement) return;
|
||||
|
||||
popperElement.style.zIndex = `${curZIndex.value}`;
|
||||
popperElement.focus();
|
||||
|
||||
instanceRef.value = createPopper(referenceElement, popperElement, {
|
||||
placement: props.placement || 'bottom',
|
||||
strategy: 'absolute',
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [0, 10],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
const clickHandler = () => {
|
||||
if (props.disabled) return;
|
||||
|
||||
popoverVisible.value = !popoverVisible.value;
|
||||
};
|
||||
|
||||
const mouseenterHandler = () => {
|
||||
if (props.disabled) return;
|
||||
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
|
||||
popoverVisible.value = true;
|
||||
};
|
||||
|
||||
let timer: NodeJS.Timeout | null = null;
|
||||
const mouseleaveHandler = () => {
|
||||
if (props.disabled) return;
|
||||
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
|
||||
timer = setTimeout(() => {
|
||||
popoverVisible.value = false;
|
||||
}, 300);
|
||||
};
|
||||
|
||||
if (props.trigger === 'click' && typeof props.visible === 'undefined') {
|
||||
watch(
|
||||
referenceElementRef,
|
||||
(el, prevEl) => {
|
||||
el?.addEventListener('click', clickHandler);
|
||||
prevEl?.removeEventListener('click', clickHandler);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (props.trigger === 'hover' && typeof props.visible === 'undefined') {
|
||||
watch(
|
||||
referenceElementRef,
|
||||
(el, prevEl) => {
|
||||
el?.addEventListener('mouseenter', mouseenterHandler);
|
||||
prevEl?.removeEventListener('mouseenter', mouseenterHandler);
|
||||
el?.addEventListener('mouseleave', mouseleaveHandler);
|
||||
prevEl?.removeEventListener('mouseleave', mouseleaveHandler);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(popperElementRef, (el, prevEl) => {
|
||||
el?.addEventListener('mouseenter', mouseenterHandler);
|
||||
prevEl?.removeEventListener('mouseenter', mouseenterHandler);
|
||||
el?.addEventListener('mouseleave', mouseleaveHandler);
|
||||
prevEl?.removeEventListener('mouseleave', mouseleaveHandler);
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
popoverVisible,
|
||||
(popoverVisible) => {
|
||||
if (!popoverVisible) {
|
||||
return;
|
||||
}
|
||||
nextTick().then(() => {
|
||||
curZIndex.value = zIndex.nextZIndex();
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
const destroy = () => {
|
||||
if (!instanceRef.value) return;
|
||||
|
||||
instanceRef.value.destroy();
|
||||
instanceRef.value = undefined;
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
destroy();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.tmagic-design-popper {
|
||||
min-width: 150px;
|
||||
line-height: 1.4;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
|
||||
color: #606266;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
overflow-wrap: break-word;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tmagic-design-popper[data-popper-placement^='top'] > .tmagic-design-popper-arrow {
|
||||
bottom: -4px;
|
||||
}
|
||||
|
||||
.tmagic-design-popper[data-popper-placement^='bottom'] > .tmagic-design-popper-arrow {
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
.tmagic-design-popper[data-popper-placement^='left'] > .tmagic-design-popper-arrow {
|
||||
right: -4px;
|
||||
}
|
||||
|
||||
.tmagic-design-popper[data-popper-placement^='right'] > .tmagic-design-popper-arrow {
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.tmagic-design-popper-arrow,
|
||||
.tmagic-design-popper-arrow::before {
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.tmagic-design-popper-arrow {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.tmagic-design-popper-arrow::before {
|
||||
visibility: visible;
|
||||
content: '';
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ComputedRef, DefineComponent, Directive, Ref } from 'vue';
|
||||
import type { Placement } from '@popperjs/core';
|
||||
|
||||
export type FieldSize = 'large' | 'default' | 'small';
|
||||
|
||||
@ -223,15 +224,12 @@ export interface PaginationProps {
|
||||
}
|
||||
|
||||
export interface PopoverProps {
|
||||
placement?: string;
|
||||
placement?: Placement;
|
||||
width?: string | number;
|
||||
title?: string;
|
||||
trigger?: string;
|
||||
effect?: string;
|
||||
content?: string;
|
||||
trigger?: 'hover' | 'click';
|
||||
disabled?: boolean;
|
||||
popperClass?: string;
|
||||
visible?: boolean;
|
||||
popperClass?: string;
|
||||
}
|
||||
|
||||
export interface RadioProps {
|
||||
@ -570,11 +568,6 @@ export interface Components {
|
||||
props: (props: PaginationProps) => PaginationProps;
|
||||
};
|
||||
|
||||
popover: {
|
||||
component: DefineComponent<PopoverProps, {}, any> | string;
|
||||
props: (props: PopoverProps) => PopoverProps;
|
||||
};
|
||||
|
||||
radio: {
|
||||
component: DefineComponent<RadioProps, {}, any> | string;
|
||||
props: (props: RadioProps) => RadioProps;
|
||||
|
@ -81,8 +81,8 @@
|
||||
}
|
||||
|
||||
.page-bar-popover {
|
||||
&.el-popper.el-popover {
|
||||
padding: 10px 0;
|
||||
&.tmagic-design-popper {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
|
@ -43,7 +43,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tmagic/design": "workspace:*",
|
||||
"element-plus": ">=2.7.8",
|
||||
"element-plus": ">=2.8.0",
|
||||
"typescript": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
|
@ -27,7 +27,6 @@ import {
|
||||
ElOption,
|
||||
ElOptionGroup,
|
||||
ElPagination,
|
||||
ElPopover,
|
||||
ElRadio,
|
||||
ElRadioButton,
|
||||
ElRadioGroup,
|
||||
@ -76,7 +75,6 @@ import type {
|
||||
OptionProps,
|
||||
PaginationProps,
|
||||
PluginOptions,
|
||||
PopoverProps,
|
||||
RadioButtonProps,
|
||||
RadioGroupProps,
|
||||
RadioProps,
|
||||
@ -230,11 +228,6 @@ const adapter: PluginOptions = {
|
||||
props: (props: PaginationProps) => props,
|
||||
},
|
||||
|
||||
popover: {
|
||||
component: ElPopover as any,
|
||||
props: (props: PopoverProps) => props,
|
||||
},
|
||||
|
||||
radio: {
|
||||
component: ElRadio as any,
|
||||
props: (props: RadioProps) => props,
|
||||
|
@ -50,9 +50,24 @@ export interface ColumnConfig<T = any> {
|
||||
table?: ColumnConfig[];
|
||||
formatter?: 'datetime' | ((item: any, row: T) => any);
|
||||
popover?: {
|
||||
placement: string;
|
||||
width: string;
|
||||
trigger: string;
|
||||
placement:
|
||||
| 'auto'
|
||||
| 'auto-start'
|
||||
| 'auto-end'
|
||||
| 'left'
|
||||
| 'right'
|
||||
| 'top'
|
||||
| 'bottom'
|
||||
| 'top-start'
|
||||
| 'top-end'
|
||||
| 'bottom-start'
|
||||
| 'bottom-end'
|
||||
| 'right-start'
|
||||
| 'right-end'
|
||||
| 'left-start'
|
||||
| 'left-end';
|
||||
width: string | number;
|
||||
trigger: 'hover' | 'click';
|
||||
tableEmbed: boolean;
|
||||
};
|
||||
sortable?: boolean | 'custom';
|
||||
|
@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<TPopup
|
||||
:placement="placement"
|
||||
:trigger="trigger"
|
||||
:disabled="disabled"
|
||||
:visible="visible"
|
||||
:overlayClassName="popperClass"
|
||||
>
|
||||
<slot name="reference"></slot>
|
||||
|
||||
<template #content>
|
||||
<slot></slot>
|
||||
</template>
|
||||
</TPopup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { Popup as TPopup, type PopupPlacement } from 'tdesign-vue-next';
|
||||
|
||||
import type { PopoverProps } from '@tmagic/design';
|
||||
|
||||
const props = defineProps<PopoverProps>();
|
||||
|
||||
const placement = computed(() => props.placement as PopupPlacement);
|
||||
const trigger = computed(() => props.trigger as 'click' | 'focus' | 'mousedown' | 'context-menu' | 'hover');
|
||||
</script>
|
@ -64,7 +64,6 @@ import type {
|
||||
OptionGroupProps,
|
||||
OptionProps,
|
||||
PaginationProps,
|
||||
PopoverProps,
|
||||
RadioButtonProps,
|
||||
RadioGroupProps,
|
||||
RadioProps,
|
||||
@ -85,7 +84,6 @@ import type {
|
||||
import DatePicker from './DatePicker.vue';
|
||||
import Icon from './Icon.vue';
|
||||
import Input from './Input.vue';
|
||||
import Popover from './Popover.vue';
|
||||
import Scrollbar from './Scrollbar.vue';
|
||||
import TableColumn from './TableColumn.vue';
|
||||
|
||||
@ -348,11 +346,6 @@ const adapter: any = {
|
||||
}),
|
||||
},
|
||||
|
||||
popover: {
|
||||
component: Popover,
|
||||
props: (props: PopoverProps) => props,
|
||||
},
|
||||
|
||||
radio: {
|
||||
component: TRadio,
|
||||
props: (props: RadioProps) => ({
|
||||
|
Loading…
x
Reference in New Issue
Block a user