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",
|
"vue3",
|
||||||
"typescript"
|
"typescript"
|
||||||
],
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18.19.0",
|
"@types/node": "^18.19.0",
|
||||||
"@vitejs/plugin-vue": "^5.1.1",
|
"@vitejs/plugin-vue": "^5.1.1",
|
||||||
|
@ -1,28 +1,253 @@
|
|||||||
<template>
|
<template>
|
||||||
<component class="tmagic-design-popover" :is="uiComponent" v-bind="uiProps">
|
<slot name="reference"></slot>
|
||||||
<slot></slot>
|
<Teleport to="body">
|
||||||
|
<div
|
||||||
<template #reference>
|
v-if="popoverVisible"
|
||||||
<slot name="reference"></slot>
|
class="tmagic-design-popper"
|
||||||
</template>
|
tabindex="-1"
|
||||||
</component>
|
ref="popperElementRef"
|
||||||
|
:class="popperClass"
|
||||||
|
:style="style"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
<span class="tmagic-design-popper-arrow" data-popper-arrow></span>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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';
|
import type { PopoverProps } from './types';
|
||||||
|
|
||||||
|
defineSlots<{
|
||||||
|
reference(props: {}): any;
|
||||||
|
default(props: {}): any;
|
||||||
|
}>();
|
||||||
|
|
||||||
defineOptions({
|
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>
|
</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 { ComputedRef, DefineComponent, Directive, Ref } from 'vue';
|
||||||
|
import type { Placement } from '@popperjs/core';
|
||||||
|
|
||||||
export type FieldSize = 'large' | 'default' | 'small';
|
export type FieldSize = 'large' | 'default' | 'small';
|
||||||
|
|
||||||
@ -223,15 +224,12 @@ export interface PaginationProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PopoverProps {
|
export interface PopoverProps {
|
||||||
placement?: string;
|
placement?: Placement;
|
||||||
width?: string | number;
|
width?: string | number;
|
||||||
title?: string;
|
trigger?: 'hover' | 'click';
|
||||||
trigger?: string;
|
|
||||||
effect?: string;
|
|
||||||
content?: string;
|
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
popperClass?: string;
|
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
|
popperClass?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RadioProps {
|
export interface RadioProps {
|
||||||
@ -570,11 +568,6 @@ export interface Components {
|
|||||||
props: (props: PaginationProps) => PaginationProps;
|
props: (props: PaginationProps) => PaginationProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
popover: {
|
|
||||||
component: DefineComponent<PopoverProps, {}, any> | string;
|
|
||||||
props: (props: PopoverProps) => PopoverProps;
|
|
||||||
};
|
|
||||||
|
|
||||||
radio: {
|
radio: {
|
||||||
component: DefineComponent<RadioProps, {}, any> | string;
|
component: DefineComponent<RadioProps, {}, any> | string;
|
||||||
props: (props: RadioProps) => RadioProps;
|
props: (props: RadioProps) => RadioProps;
|
||||||
|
@ -81,8 +81,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page-bar-popover {
|
.page-bar-popover {
|
||||||
&.el-popper.el-popover {
|
&.tmagic-design-popper {
|
||||||
padding: 10px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item {
|
.menu-item {
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tmagic/design": "workspace:*",
|
"@tmagic/design": "workspace:*",
|
||||||
"element-plus": ">=2.7.8",
|
"element-plus": ">=2.8.0",
|
||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
|
@ -27,7 +27,6 @@ import {
|
|||||||
ElOption,
|
ElOption,
|
||||||
ElOptionGroup,
|
ElOptionGroup,
|
||||||
ElPagination,
|
ElPagination,
|
||||||
ElPopover,
|
|
||||||
ElRadio,
|
ElRadio,
|
||||||
ElRadioButton,
|
ElRadioButton,
|
||||||
ElRadioGroup,
|
ElRadioGroup,
|
||||||
@ -76,7 +75,6 @@ import type {
|
|||||||
OptionProps,
|
OptionProps,
|
||||||
PaginationProps,
|
PaginationProps,
|
||||||
PluginOptions,
|
PluginOptions,
|
||||||
PopoverProps,
|
|
||||||
RadioButtonProps,
|
RadioButtonProps,
|
||||||
RadioGroupProps,
|
RadioGroupProps,
|
||||||
RadioProps,
|
RadioProps,
|
||||||
@ -230,11 +228,6 @@ const adapter: PluginOptions = {
|
|||||||
props: (props: PaginationProps) => props,
|
props: (props: PaginationProps) => props,
|
||||||
},
|
},
|
||||||
|
|
||||||
popover: {
|
|
||||||
component: ElPopover as any,
|
|
||||||
props: (props: PopoverProps) => props,
|
|
||||||
},
|
|
||||||
|
|
||||||
radio: {
|
radio: {
|
||||||
component: ElRadio as any,
|
component: ElRadio as any,
|
||||||
props: (props: RadioProps) => props,
|
props: (props: RadioProps) => props,
|
||||||
|
@ -50,9 +50,24 @@ export interface ColumnConfig<T = any> {
|
|||||||
table?: ColumnConfig[];
|
table?: ColumnConfig[];
|
||||||
formatter?: 'datetime' | ((item: any, row: T) => any);
|
formatter?: 'datetime' | ((item: any, row: T) => any);
|
||||||
popover?: {
|
popover?: {
|
||||||
placement: string;
|
placement:
|
||||||
width: string;
|
| 'auto'
|
||||||
trigger: string;
|
| '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;
|
tableEmbed: boolean;
|
||||||
};
|
};
|
||||||
sortable?: boolean | 'custom';
|
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,
|
OptionGroupProps,
|
||||||
OptionProps,
|
OptionProps,
|
||||||
PaginationProps,
|
PaginationProps,
|
||||||
PopoverProps,
|
|
||||||
RadioButtonProps,
|
RadioButtonProps,
|
||||||
RadioGroupProps,
|
RadioGroupProps,
|
||||||
RadioProps,
|
RadioProps,
|
||||||
@ -85,7 +84,6 @@ import type {
|
|||||||
import DatePicker from './DatePicker.vue';
|
import DatePicker from './DatePicker.vue';
|
||||||
import Icon from './Icon.vue';
|
import Icon from './Icon.vue';
|
||||||
import Input from './Input.vue';
|
import Input from './Input.vue';
|
||||||
import Popover from './Popover.vue';
|
|
||||||
import Scrollbar from './Scrollbar.vue';
|
import Scrollbar from './Scrollbar.vue';
|
||||||
import TableColumn from './TableColumn.vue';
|
import TableColumn from './TableColumn.vue';
|
||||||
|
|
||||||
@ -348,11 +346,6 @@ const adapter: any = {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
popover: {
|
|
||||||
component: Popover,
|
|
||||||
props: (props: PopoverProps) => props,
|
|
||||||
},
|
|
||||||
|
|
||||||
radio: {
|
radio: {
|
||||||
component: TRadio,
|
component: TRadio,
|
||||||
props: (props: RadioProps) => ({
|
props: (props: RadioProps) => ({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user