diff --git a/packages/devui-vue/devui/overlay/src/fixed-overlay.tsx b/packages/devui-vue/devui/overlay/src/fixed-overlay.tsx index 4a84b1c438ceec8a7745bf597c223ba16d4500a3..c4ccfa03ec89e41c599e1037da30dfdfdbcf17a8 100644 --- a/packages/devui-vue/devui/overlay/src/fixed-overlay.tsx +++ b/packages/devui-vue/devui/overlay/src/fixed-overlay.tsx @@ -1,19 +1,13 @@ import { defineComponent, ref, renderSlot, CSSProperties, PropType } from 'vue'; import { CommonOverlay } from './common-overlay'; -import { overlayProps } from './overlay-types'; +import { fixedOverlayProps, FixedOverlayProps } from './overlay-types'; import { useOverlayLogic } from './utils'; import './overlay.scss'; export const FixedOverlay = defineComponent({ name: 'DFixedOverlay', - props: { - ...overlayProps, - overlayStyle: { - type: Object as PropType, - default: undefined, - }, - }, - setup(props, ctx) { + props: fixedOverlayProps, + setup(props: FixedOverlayProps, ctx) { const { backgroundClass, overlayClass, diff --git a/packages/devui-vue/devui/overlay/src/flexible-overlay.tsx b/packages/devui-vue/devui/overlay/src/flexible-overlay.tsx index 19c55c4f988267c2f0f427941ea9b7cada2ff06f..42556b58eac8d580e2c83d54579947b50d4b8a04 100644 --- a/packages/devui-vue/devui/overlay/src/flexible-overlay.tsx +++ b/packages/devui-vue/devui/overlay/src/flexible-overlay.tsx @@ -1,5 +1,4 @@ import { - ComponentPublicInstance, CSSProperties, defineComponent, getCurrentInstance, @@ -7,41 +6,26 @@ import { nextTick, onBeforeUnmount, onMounted, - PropType, reactive, ref, - Ref, renderSlot, toRef, watch, } from 'vue'; import { CommonOverlay } from './common-overlay'; -import { overlayProps } from './overlay-types'; +import { OriginOrDomRef, flexibleOverlayProps, FlexibleOverlayProps, Point, Origin, ConnectionPosition } from './overlay-types'; import { useOverlayLogic } from './utils'; +import { getElement, isComponent } from '../../shared/util/dom'; + /** * 弹性的 Overlay,用于连接固定的和相对点 */ export const FlexibleOverlay = defineComponent({ name: 'DFlexibleOverlay', - props: { - origin: { - type: Object as PropType, - require: true, - }, - position: { - type: Object as PropType, - default: () => ({ - originX: 'left', - originY: 'top', - overlayX: 'left', - overlayY: 'top', - }), - }, - ...overlayProps, - }, + props: flexibleOverlayProps, emits: ['onUpdate:visible'], - setup(props, ctx) { + setup(props: FlexibleOverlayProps, ctx) { // lift cycle const overlayRef = ref(null); const positionedStyle = reactive({ position: 'absolute' }); @@ -70,10 +54,12 @@ export const FlexibleOverlay = defineComponent({ positionedStyle.left = `${point.x}px`; positionedStyle.top = `${point.y}px`; }; - const handleChange = () => - handleRectChange(overlay.getBoundingClientRect()); + const handleChange = () => handleRectChange(overlay.getBoundingClientRect()); + + const visibleRef = toRef(props, 'visible'); + const positionRef = toRef(props, 'position'); - watch(toRef(props, 'visible'), (visible, ov, onInvalidate) => { + watch(visibleRef, (visible, ov, onInvalidate) => { if (visible) { subscribeLayoutEvent(handleChange); } else { @@ -84,7 +70,7 @@ export const FlexibleOverlay = defineComponent({ }); }); - watch(toRef(props, 'position'), () => { + watch([visibleRef, positionRef], () => { handleChange(); }); @@ -97,8 +83,7 @@ export const FlexibleOverlay = defineComponent({ }, instance); if (origin instanceof Element) { - // Only when the style changing, you can change - // the position. + // Only when the style changing, you can change the position. const observer = new MutationObserver(handleChange); observer.observe(origin, { attributeFilter: ['style'], @@ -118,7 +103,12 @@ export const FlexibleOverlay = defineComponent({ return () => ( -
+
- | Rect; - -type Origin = Element | Rect; - -type HorizontalConnectionPos = 'left' | 'center' | 'right'; -type VerticalConnectionPos = 'top' | 'center' | 'bottom'; - -interface ConnectionPosition { - originX: HorizontalConnectionPos - originY: VerticalConnectionPos - overlayX: HorizontalConnectionPos - overlayY: VerticalConnectionPos -} /** * 获取原点,可能是 Element 或者 Rect @@ -207,6 +139,10 @@ function getOrigin(origin: OriginOrDomRef): Origin { return getElement(origin.value); } + if (isComponent(origin)) { + return getElement(origin); + } + // is point { x: number, y: number, width: number, height: number } return origin; } @@ -243,9 +179,9 @@ function calculatePosition( /** * 返回原点元素的 ClientRect * @param origin - * @returns {ClientRect} + * @returns {DOMRect} */ -function getOriginRect(origin: Origin): ClientRect { +function getOriginRect(origin: Origin): DOMRect { if (origin instanceof Element) { return origin.getBoundingClientRect(); } @@ -261,7 +197,7 @@ function getOriginRect(origin: Origin): ClientRect { right: origin.x + width, height, width, - }; + } as DOMRect; } /** diff --git a/packages/devui-vue/devui/overlay/src/overlay-types.ts b/packages/devui-vue/devui/overlay/src/overlay-types.ts index 766963dc7da907808927892546d38d77a990c303..f90ccfb71b1086cdc7f528fffd8447deeccba49b 100644 --- a/packages/devui-vue/devui/overlay/src/overlay-types.ts +++ b/packages/devui-vue/devui/overlay/src/overlay-types.ts @@ -1,4 +1,4 @@ -import { ExtractPropTypes, PropType, CSSProperties } from 'vue'; +import { ExtractPropTypes, PropType, StyleValue, ComponentPublicInstance, Ref } from 'vue'; export const overlayProps = { visible: { @@ -16,7 +16,7 @@ export const overlayProps = { default: '' }, backgroundStyle: { - type: [String, Object] as PropType + type: [String, Object] as PropType }, backdropClick: { type: Function, @@ -24,8 +24,84 @@ export const overlayProps = { backdropClose: { type: Boolean, default: true - } + }, + hasBackdrop: { + type: Boolean, + default: true + }, } as const; +export type OverlayProps = ExtractPropTypes; + + +export const fixedOverlayProps = { + ...overlayProps, + overlayStyle: { + type: [String, Object] as PropType, + default: undefined, + }, +}; + +export type FixedOverlayProps = ExtractPropTypes; + +export const flexibleOverlayProps = { + origin: { + type: Object as PropType, + require: true, + }, + position: { + type: Object as PropType, + default: () => ({ + originX: 'left', + originY: 'top', + overlayX: 'left', + overlayY: 'top', + }), + }, + ...overlayProps, +} + + +export interface ClientRect { + bottom: number + readonly height: number + left: number + right: number + top: number + readonly width: number +} + +export interface Point { + x: number + y: number +} + +export interface Rect { + x: number + y: number + width?: number + height?: number +} + +export type Origin = Element | Rect; + +type HorizontalConnectionPos = 'left' | 'center' | 'right'; +type VerticalConnectionPos = 'top' | 'center' | 'bottom'; + +export interface ConnectionPosition { + originX: HorizontalConnectionPos + originY: VerticalConnectionPos + overlayX: HorizontalConnectionPos + overlayY: VerticalConnectionPos +} + +export type OriginOrDomRef = + | Element + | ComponentPublicInstance + | Ref + | Rect + | null; + +export type FlexibleOverlayProps = ExtractPropTypes; + -export type OverlayProps = ExtractPropTypes; \ No newline at end of file diff --git a/packages/devui-vue/devui/overlay/src/overlay.scss b/packages/devui-vue/devui/overlay/src/overlay.scss index 66c0f4a87b04436cedac17fbeef45687f602b06d..d9c754e5c1ebcc378eae097817f9d83982d9ae0c 100644 --- a/packages/devui-vue/devui/overlay/src/overlay.scss +++ b/packages/devui-vue/devui/overlay/src/overlay.scss @@ -15,6 +15,10 @@ z-index: 1000; pointer-events: auto; } + + &__disabled { + pointer-events: none; + } } .devui-overlay-fade { diff --git a/packages/devui-vue/devui/overlay/src/utils.ts b/packages/devui-vue/devui/overlay/src/utils.ts index 262253e8f273a532e2f8ce789eb87d3f22a9e70a..4f115130a5184a1421f5153a7ee85751fe26053a 100644 --- a/packages/devui-vue/devui/overlay/src/utils.ts +++ b/packages/devui-vue/devui/overlay/src/utils.ts @@ -10,7 +10,11 @@ interface CommonInfo { export function useOverlayLogic(props: OverlayProps): CommonInfo { const backgroundClass = computed(() => { - return ['devui-overlay-background', 'devui-overlay-background__color', props.backgroundClass]; + return [ + 'devui-overlay-background', + props.backgroundClass, + !props.hasBackdrop ? 'devui-overlay-background__disabled' : 'devui-overlay-background__color', + ]; }); const overlayClass = computed(() => { return 'devui-overlay'; diff --git a/packages/devui-vue/devui/shared/util/dom.ts b/packages/devui-vue/devui/shared/util/dom.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2cb9566889c4cd5613a7b3001ee216343c6da26 --- /dev/null +++ b/packages/devui-vue/devui/shared/util/dom.ts @@ -0,0 +1,31 @@ +import { ComponentPublicInstance } from "@vue/runtime-core"; + +/** + * + * @param {any} origin + * @returns + */ +export function isComponent(target: any): target is ComponentPublicInstance { + return !!(target?.$el); +} + +/** + * 提取 Vue Intance 中的元素,如果本身就是元素,直接返回。 + * @param {any} element + * @returns {Element | null} + */ +export function getElement( + element: Element | ComponentPublicInstance | null +): Element | null { + if (element instanceof Element) { + return element; + } + if ( + element && + typeof element === 'object' && + element.$el instanceof Element + ) { + return element.$el; + } + return null; +} \ No newline at end of file diff --git a/packages/devui-vue/docs/components/overlay/index.md b/packages/devui-vue/docs/components/overlay/index.md index 2fecc9afc2d97df22e5275eda525ff4e4e227f41..7d604b356d6046ecc2c653440c4c767f0b0a4bf0 100644 --- a/packages/devui-vue/docs/components/overlay/index.md +++ b/packages/devui-vue/docs/components/overlay/index.md @@ -169,7 +169,7 @@ export default defineComponent({ -### API +### API d-fixed-overlay 参数 | 参数 | 类型 | 默认 | 说明 | @@ -178,8 +178,10 @@ d-fixed-overlay 参数 | onUpdate:visible | `(value: boolean) => void` | -- | 可选,遮罩层取消可见事件 | | backgroundBlock | `boolean` | false | 可选,如果为 true,背景不能滚动 | | backgroundClass | `string` | -- | 可选,背景的样式类 | +| backgroundStyle | `StyleValue` | -- | 可选,背景的样式 | | backdropClick | `() => void` | -- | 可选,点击背景触发的事件 | | backdropClose | `boolean` | false | 可选,如果为true,点击背景将触发 `onUpdate:visible`,默认参数是 false | +| hasBackdrop | `boolean` | true | 可选,如果为false,背景元素的 `point-event` 会设为 `none`,且不显示默认背景 | | overlayStyle | `CSSProperties` | -- | 可选,遮罩层的样式 | d-flexible-overlay 参数 @@ -190,7 +192,33 @@ d-flexible-overlay 参数 | onUpdate:visible | `(value: boolean) => void` | -- | 可选,遮罩层取消可见事件 | | backgroundBlock | `boolean` | false | 可选,如果为 true,背景不能滚动 | | backgroundClass | `string` | -- | 可选,背景的样式类 | +| backgroundStyle | `StyleValue` | -- | 可选,背景的样式 | | backdropClick | `() => void` | -- | 可选,点击背景触发的事件 | | backdropClose | `boolean` | false | 可选,如果为true,点击背景将触发 `onUpdate:visible`,参数是 false | -| origin | `Element \| ComponentPublicInstance \| { x: number, y: number, width?: number, height?: number }` | false | 必选,你必须指定起点元素才能让遮罩层与该元素连接在一起 | -| position | `{originX: HorizontalPos, originY: VerticalPos, overlayX: HorizontalPos, overlayY: VerticalPos } (type HorizontalPos = 'left' \| 'center' \| 'right') ( type VerticalPos = 'top' \| 'center' \| 'bottom')` | false | 可选,指定遮罩层与原点的连接点 | +| hasBackdrop | `boolean` | true | 可选,如果为false,背景元素的 `point-event` 会设为 `none`,且不显示默认背景 | +| origin | `Element \| ComponentPublicInstance \| Rect` | false | 必选,你必须指定起点元素才能让遮罩层与该元素连接在一起 | +| position | `ConnectionPosition` | false | 可选,指定遮罩层与原点的连接点 | + +Rect 数据结构 +```typescript +interface Rect { + x: number + y: number + width?: number + height?: number +} +``` + +ConnectionPosition 数据结构 +```typescript +type HorizontalConnectionPos = 'left' | 'center' | 'right'; +type VerticalConnectionPos = 'top' | 'center' | 'bottom'; + +export interface ConnectionPosition { + originX: HorizontalConnectionPos + originY: VerticalConnectionPos + overlayX: HorizontalConnectionPos + overlayY: VerticalConnectionPos +} +``` +