@veltra/compositions 1.8.1 → 1.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/use-pop/index.js +5 -0
- package/dist/use-pop/index.js.map +1 -1
- package/package.json +1 -1
- package/src/use-pop/index.ts +6 -0
package/dist/index.js
CHANGED
|
@@ -6,8 +6,8 @@ import { useFallbackProps, useFormFallbackProps } from "./use-fallback-props/ind
|
|
|
6
6
|
import { useFocus } from "./use-focus/index.js";
|
|
7
7
|
import { useUserAction } from "./use-user-action/index.js";
|
|
8
8
|
import { useModel } from "./use-model/index.js";
|
|
9
|
-
import { usePop } from "./use-pop/index.js";
|
|
10
9
|
import { useObserverCallback, useResizeObserver } from "./use-resize-observer/index.js";
|
|
10
|
+
import { usePop } from "./use-pop/index.js";
|
|
11
11
|
import { useReactiveSize } from "./use-reactive-size/index.js";
|
|
12
12
|
import { useTransition } from "./use-transition/index.js";
|
|
13
13
|
import { useVirtualizer } from "./use-virtualizer/index.js";
|
package/dist/use-pop/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useResizeObserver } from "../use-resize-observer/index.js";
|
|
1
2
|
import { getScrollParents, setStyles } from "@veltra/utils";
|
|
2
3
|
import { isRef, onBeforeUnmount, watch } from "vue";
|
|
3
4
|
import { arrow, computePosition, flip, offset, shift } from "@floating-ui/dom";
|
|
@@ -69,6 +70,10 @@ function usePop(options) {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
let scrollParents = [];
|
|
73
|
+
useResizeObserver({
|
|
74
|
+
targets: contentRef,
|
|
75
|
+
onResize: () => update()
|
|
76
|
+
});
|
|
72
77
|
/** 为触发器元素的祖先元素添加滚动事件 */
|
|
73
78
|
function addScrollEvents() {
|
|
74
79
|
if (!triggerRef.value) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/use-pop/index.ts"],"sourcesContent":["import {\n computePosition,\n flip,\n shift,\n arrow,\n offset,\n type ComputePositionReturn,\n type Placement\n} from '@floating-ui/dom'\nimport { getScrollParents, setStyles } from '@veltra/utils'\nimport { isRef, onBeforeUnmount, watch, type Ref, type ShallowRef } from 'vue'\n\ntype TipDirection = 'top' | 'bottom' | 'left' | 'right'\ntype TipAlign = 'center' | 'start' | 'end'\n\ninterface Options {\n /** 触发元素 */\n triggerRef: ShallowRef<HTMLElement | undefined>\n /** 内容元素 */\n contentRef: ShallowRef<HTMLElement | undefined>\n /**\n * 箭头元素,如果存在,则会在弹框的箭头位置显示箭头\n */\n arrowRef?: ShallowRef<HTMLElement | undefined>\n /** 方向 */\n direction?: ShallowRef<TipDirection> | TipDirection\n /** 对齐方式 */\n alignment?: ShallowRef<TipAlign> | TipAlign\n /**\n * 箭头大小\n * @default 10\n */\n arrowSize?: number\n /**\n * 触发器元素位置变更时回调,\n * 一般用于在触发器元素位置变更时更新弹框位置\n */\n onTriggerPositionChange?: () => void\n /** 更新元素前回调 */\n onBeforeUpdate?: (triggerEl: HTMLElement, contentEl: HTMLElement) => void\n /** 更新元素后回调 */\n onAfterUpdate?: (position: ComputePositionReturn) => void\n /** 弹框弹出时回调 */\n onPop?: (position: ComputePositionReturn) => void\n}\n\ninterface PopResult {\n /**\n * 更新弹框位置\n */\n update: () => Promise<void>\n /** 浮框容器id */\n popperContainerId: string\n}\n\nlet popperContainer: HTMLElement | null = null\n\nconst popperContainerId = 'pop-container'\n\n/**\n * 浮框组合式函数\n * @param options 选项\n * @returns\n */\nexport function usePop(options: Options): PopResult {\n if (!popperContainer) {\n popperContainer = document.createElement('div')\n popperContainer.id = popperContainerId\n document.body.appendChild(popperContainer)\n }\n\n const {\n triggerRef,\n contentRef,\n arrowRef,\n arrowSize = 10,\n onTriggerPositionChange,\n onAfterUpdate,\n onBeforeUpdate,\n direction,\n alignment,\n onPop\n } = options\n\n /** 箭头位置 */\n const arrowPlacementDict = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }\n\n function getMaybeRefValue<T>(value?: Ref<T> | T) {\n return isRef(value) ? value.value : value\n }\n\n /**\n * 更新浮框位置\n * @param callbackOnPop 是否在弹出时回调\n */\n async function update(callbackOnPop = false) {\n const triggerEl = triggerRef.value\n const contentEl = contentRef.value\n\n if (!(triggerEl instanceof HTMLElement) || !(contentEl instanceof HTMLElement)) {\n return\n }\n\n onBeforeUpdate?.(triggerEl, contentEl)\n\n // 计算位置 ↓↓↓\n const middleware = [offset(arrowRef?.value ? arrowSize : 6), flip(), shift()]\n\n if (arrowRef?.value) {\n middleware.push(arrow({ element: arrowRef.value }))\n }\n\n const _direction = getMaybeRefValue(direction) ?? 'top'\n const _alignment = getMaybeRefValue(alignment) ?? 'center'\n\n const position = await computePosition(triggerEl, contentEl, {\n middleware,\n\n placement: `${_direction}${_alignment === 'center' ? '' : `-${_alignment}`}` as Placement\n })\n\n const { x, y, middlewareData, placement } = position\n\n setStyles(contentEl, { left: `${x}px`, top: `${y}px` })\n callbackOnPop && onPop?.(position)\n onAfterUpdate?.(position)\n\n // 设置箭头位置 ↓↓↓\n if (middlewareData.arrow) {\n const { x: arrowX, y: arrowY } = middlewareData.arrow\n\n const arrowPlacement =\n arrowPlacementDict[placement.split('-')[0]! as keyof typeof arrowPlacementDict]\n const size = `${arrowSize}px`\n // 箭头半径\n const arrowRadius = arrowSize / 2\n\n setStyles(arrowRef!.value!, {\n width: size,\n height: size,\n left: arrowX && `${arrowX - arrowRadius}px`,\n top: arrowY && `${arrowY - arrowRadius}px`,\n [arrowPlacement]: `-${arrowRadius}px`\n })\n }\n // 设置箭头位置 ↑↑↑\n }\n\n let scrollParents: HTMLElement[] = []\n\n /** 为触发器元素的祖先元素添加滚动事件 */\n function addScrollEvents() {\n if (!triggerRef.value) return\n if (onTriggerPositionChange) {\n scrollParents = getScrollParents(triggerRef.value)\n scrollParents.forEach((el) => {\n el.addEventListener('scroll', onTriggerPositionChange)\n })\n }\n }\n\n /** 移除触发器元素祖先元素的滚动事件 */\n function removeScrollEvents() {\n if (onTriggerPositionChange) {\n scrollParents.forEach((el) => {\n el.removeEventListener('scroll', onTriggerPositionChange)\n })\n }\n\n scrollParents = []\n }\n\n function addResizeEvents() {\n onTriggerPositionChange && window.addEventListener('resize', onTriggerPositionChange)\n }\n\n function removeResizeEvents() {\n onTriggerPositionChange && window.removeEventListener('resize', onTriggerPositionChange)\n }\n\n watch(\n [contentRef, () => getMaybeRefValue(direction), () => getMaybeRefValue(alignment)],\n ([content]) => {\n if (content) {\n update(true)\n addScrollEvents()\n addResizeEvents()\n return\n }\n removeScrollEvents()\n removeResizeEvents()\n }\n )\n\n onBeforeUnmount(() => {\n removeScrollEvents()\n removeResizeEvents()\n })\n\n return {\n /** 更新浮框位置 */\n update,\n /** 浮框容器id */\n popperContainerId\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/use-pop/index.ts"],"sourcesContent":["import {\n computePosition,\n flip,\n shift,\n arrow,\n offset,\n type ComputePositionReturn,\n type Placement\n} from '@floating-ui/dom'\nimport { getScrollParents, setStyles } from '@veltra/utils'\nimport { isRef, onBeforeUnmount, watch, type Ref, type ShallowRef } from 'vue'\n\nimport { useResizeObserver } from '../use-resize-observer'\n\ntype TipDirection = 'top' | 'bottom' | 'left' | 'right'\ntype TipAlign = 'center' | 'start' | 'end'\n\ninterface Options {\n /** 触发元素 */\n triggerRef: ShallowRef<HTMLElement | undefined>\n /** 内容元素 */\n contentRef: ShallowRef<HTMLElement | undefined>\n /**\n * 箭头元素,如果存在,则会在弹框的箭头位置显示箭头\n */\n arrowRef?: ShallowRef<HTMLElement | undefined>\n /** 方向 */\n direction?: ShallowRef<TipDirection> | TipDirection\n /** 对齐方式 */\n alignment?: ShallowRef<TipAlign> | TipAlign\n /**\n * 箭头大小\n * @default 10\n */\n arrowSize?: number\n /**\n * 触发器元素位置变更时回调,\n * 一般用于在触发器元素位置变更时更新弹框位置\n */\n onTriggerPositionChange?: () => void\n /** 更新元素前回调 */\n onBeforeUpdate?: (triggerEl: HTMLElement, contentEl: HTMLElement) => void\n /** 更新元素后回调 */\n onAfterUpdate?: (position: ComputePositionReturn) => void\n /** 弹框弹出时回调 */\n onPop?: (position: ComputePositionReturn) => void\n}\n\ninterface PopResult {\n /**\n * 更新弹框位置\n */\n update: () => Promise<void>\n /** 浮框容器id */\n popperContainerId: string\n}\n\nlet popperContainer: HTMLElement | null = null\n\nconst popperContainerId = 'pop-container'\n\n/**\n * 浮框组合式函数\n * @param options 选项\n * @returns\n */\nexport function usePop(options: Options): PopResult {\n if (!popperContainer) {\n popperContainer = document.createElement('div')\n popperContainer.id = popperContainerId\n document.body.appendChild(popperContainer)\n }\n\n const {\n triggerRef,\n contentRef,\n arrowRef,\n arrowSize = 10,\n onTriggerPositionChange,\n onAfterUpdate,\n onBeforeUpdate,\n direction,\n alignment,\n onPop\n } = options\n\n /** 箭头位置 */\n const arrowPlacementDict = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }\n\n function getMaybeRefValue<T>(value?: Ref<T> | T) {\n return isRef(value) ? value.value : value\n }\n\n /**\n * 更新浮框位置\n * @param callbackOnPop 是否在弹出时回调\n */\n async function update(callbackOnPop = false) {\n const triggerEl = triggerRef.value\n const contentEl = contentRef.value\n\n if (!(triggerEl instanceof HTMLElement) || !(contentEl instanceof HTMLElement)) {\n return\n }\n\n onBeforeUpdate?.(triggerEl, contentEl)\n\n // 计算位置 ↓↓↓\n const middleware = [offset(arrowRef?.value ? arrowSize : 6), flip(), shift()]\n\n if (arrowRef?.value) {\n middleware.push(arrow({ element: arrowRef.value }))\n }\n\n const _direction = getMaybeRefValue(direction) ?? 'top'\n const _alignment = getMaybeRefValue(alignment) ?? 'center'\n\n const position = await computePosition(triggerEl, contentEl, {\n middleware,\n\n placement: `${_direction}${_alignment === 'center' ? '' : `-${_alignment}`}` as Placement\n })\n\n const { x, y, middlewareData, placement } = position\n\n setStyles(contentEl, { left: `${x}px`, top: `${y}px` })\n callbackOnPop && onPop?.(position)\n onAfterUpdate?.(position)\n\n // 设置箭头位置 ↓↓↓\n if (middlewareData.arrow) {\n const { x: arrowX, y: arrowY } = middlewareData.arrow\n\n const arrowPlacement =\n arrowPlacementDict[placement.split('-')[0]! as keyof typeof arrowPlacementDict]\n const size = `${arrowSize}px`\n // 箭头半径\n const arrowRadius = arrowSize / 2\n\n setStyles(arrowRef!.value!, {\n width: size,\n height: size,\n left: arrowX && `${arrowX - arrowRadius}px`,\n top: arrowY && `${arrowY - arrowRadius}px`,\n [arrowPlacement]: `-${arrowRadius}px`\n })\n }\n // 设置箭头位置 ↑↑↑\n }\n\n let scrollParents: HTMLElement[] = []\n\n // 面板内容尺寸变化时(如向上弹出的 select 搜索后面板变矮)需重新定位,\n // 否则 top 仍按初始高度计算,面板底部会离触发器越来越远\n useResizeObserver({ targets: contentRef, onResize: () => update() })\n\n /** 为触发器元素的祖先元素添加滚动事件 */\n function addScrollEvents() {\n if (!triggerRef.value) return\n if (onTriggerPositionChange) {\n scrollParents = getScrollParents(triggerRef.value)\n scrollParents.forEach((el) => {\n el.addEventListener('scroll', onTriggerPositionChange)\n })\n }\n }\n\n /** 移除触发器元素祖先元素的滚动事件 */\n function removeScrollEvents() {\n if (onTriggerPositionChange) {\n scrollParents.forEach((el) => {\n el.removeEventListener('scroll', onTriggerPositionChange)\n })\n }\n\n scrollParents = []\n }\n\n function addResizeEvents() {\n onTriggerPositionChange && window.addEventListener('resize', onTriggerPositionChange)\n }\n\n function removeResizeEvents() {\n onTriggerPositionChange && window.removeEventListener('resize', onTriggerPositionChange)\n }\n\n watch(\n [contentRef, () => getMaybeRefValue(direction), () => getMaybeRefValue(alignment)],\n ([content]) => {\n if (content) {\n update(true)\n addScrollEvents()\n addResizeEvents()\n return\n }\n removeScrollEvents()\n removeResizeEvents()\n }\n )\n\n onBeforeUnmount(() => {\n removeScrollEvents()\n removeResizeEvents()\n })\n\n return {\n /** 更新浮框位置 */\n update,\n /** 浮框容器id */\n popperContainerId\n }\n}\n"],"mappings":";;;;;AAyDA,IAAI,kBAAsC;AAE1C,MAAM,oBAAoB;;;;;;AAO1B,SAAgB,OAAO,SAA6B;CAClD,IAAI,CAAC,iBAAiB;EACpB,kBAAkB,SAAS,cAAc,KAAK;EAC9C,gBAAgB,KAAK;EACrB,SAAS,KAAK,YAAY,eAAe;CAC3C;CAEA,MAAM,EACJ,YACA,YACA,UACA,YAAY,IACZ,yBACA,eACA,gBACA,WACA,WACA,UACE;;CAGJ,MAAM,qBAAqB;EAAE,KAAK;EAAU,OAAO;EAAQ,QAAQ;EAAO,MAAM;CAAQ;CAExF,SAAS,iBAAoB,OAAoB;EAC/C,OAAO,MAAM,KAAK,IAAI,MAAM,QAAQ;CACtC;;;;;CAMA,eAAe,OAAO,gBAAgB,OAAO;EAC3C,MAAM,YAAY,WAAW;EAC7B,MAAM,YAAY,WAAW;EAE7B,IAAI,EAAE,qBAAqB,gBAAgB,EAAE,qBAAqB,cAChE;EAGF,iBAAiB,WAAW,SAAS;EAGrC,MAAM,aAAa;GAAC,OAAO,UAAU,QAAQ,YAAY,CAAC;GAAG,KAAK;GAAG,MAAM;EAAC;EAE5E,IAAI,UAAU,OACZ,WAAW,KAAK,MAAM,EAAE,SAAS,SAAS,MAAM,CAAC,CAAC;EAGpD,MAAM,aAAa,iBAAiB,SAAS,KAAK;EAClD,MAAM,aAAa,iBAAiB,SAAS,KAAK;EAElD,MAAM,WAAW,MAAM,gBAAgB,WAAW,WAAW;GAC3D;GAEA,WAAW,GAAG,aAAa,eAAe,WAAW,KAAK,IAAI;EAChE,CAAC;EAED,MAAM,EAAE,GAAG,GAAG,gBAAgB,cAAc;EAE5C,UAAU,WAAW;GAAE,MAAM,GAAG,EAAE;GAAK,KAAK,GAAG,EAAE;EAAI,CAAC;EACtD,iBAAiB,QAAQ,QAAQ;EACjC,gBAAgB,QAAQ;EAGxB,IAAI,eAAe,OAAO;GACxB,MAAM,EAAE,GAAG,QAAQ,GAAG,WAAW,eAAe;GAEhD,MAAM,iBACJ,mBAAmB,UAAU,MAAM,GAAG,CAAC,CAAC;GAC1C,MAAM,OAAO,GAAG,UAAU;GAE1B,MAAM,cAAc,YAAY;GAEhC,UAAU,SAAU,OAAQ;IAC1B,OAAO;IACP,QAAQ;IACR,MAAM,UAAU,GAAG,SAAS,YAAY;IACxC,KAAK,UAAU,GAAG,SAAS,YAAY;KACtC,iBAAiB,IAAI,YAAY;GACpC,CAAC;EACH;CAEF;CAEA,IAAI,gBAA+B,CAAC;CAIpC,kBAAkB;EAAE,SAAS;EAAY,gBAAgB,OAAO;CAAE,CAAC;;CAGnE,SAAS,kBAAkB;EACzB,IAAI,CAAC,WAAW,OAAO;EACvB,IAAI,yBAAyB;GAC3B,gBAAgB,iBAAiB,WAAW,KAAK;GACjD,cAAc,SAAS,OAAO;IAC5B,GAAG,iBAAiB,UAAU,uBAAuB;GACvD,CAAC;EACH;CACF;;CAGA,SAAS,qBAAqB;EAC5B,IAAI,yBACF,cAAc,SAAS,OAAO;GAC5B,GAAG,oBAAoB,UAAU,uBAAuB;EAC1D,CAAC;EAGH,gBAAgB,CAAC;CACnB;CAEA,SAAS,kBAAkB;EACzB,2BAA2B,OAAO,iBAAiB,UAAU,uBAAuB;CACtF;CAEA,SAAS,qBAAqB;EAC5B,2BAA2B,OAAO,oBAAoB,UAAU,uBAAuB;CACzF;CAEA,MACE;EAAC;QAAkB,iBAAiB,SAAS;QAAS,iBAAiB,SAAS;CAAC,IAChF,CAAC,aAAa;EACb,IAAI,SAAS;GACX,OAAO,IAAI;GACX,gBAAgB;GAChB,gBAAgB;GAChB;EACF;EACA,mBAAmB;EACnB,mBAAmB;CACrB,CACF;CAEA,sBAAsB;EACpB,mBAAmB;EACnB,mBAAmB;CACrB,CAAC;CAED,OAAO;;EAEL;;EAEA;CACF;AACF"}
|
package/package.json
CHANGED
package/src/use-pop/index.ts
CHANGED
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
import { getScrollParents, setStyles } from '@veltra/utils'
|
|
11
11
|
import { isRef, onBeforeUnmount, watch, type Ref, type ShallowRef } from 'vue'
|
|
12
12
|
|
|
13
|
+
import { useResizeObserver } from '../use-resize-observer'
|
|
14
|
+
|
|
13
15
|
type TipDirection = 'top' | 'bottom' | 'left' | 'right'
|
|
14
16
|
type TipAlign = 'center' | 'start' | 'end'
|
|
15
17
|
|
|
@@ -148,6 +150,10 @@ export function usePop(options: Options): PopResult {
|
|
|
148
150
|
|
|
149
151
|
let scrollParents: HTMLElement[] = []
|
|
150
152
|
|
|
153
|
+
// 面板内容尺寸变化时(如向上弹出的 select 搜索后面板变矮)需重新定位,
|
|
154
|
+
// 否则 top 仍按初始高度计算,面板底部会离触发器越来越远
|
|
155
|
+
useResizeObserver({ targets: contentRef, onResize: () => update() })
|
|
156
|
+
|
|
151
157
|
/** 为触发器元素的祖先元素添加滚动事件 */
|
|
152
158
|
function addScrollEvents() {
|
|
153
159
|
if (!triggerRef.value) return
|