@veltra/compositions 1.0.0
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.d.ts +14 -0
- package/dist/index.js +14 -0
- package/dist/use-component-props/index.d.ts +12 -0
- package/dist/use-component-props/index.js +52 -0
- package/dist/use-component-props/index.js.map +1 -0
- package/dist/use-config/index.d.ts +25 -0
- package/dist/use-config/index.js +51 -0
- package/dist/use-config/index.js.map +1 -0
- package/dist/use-drag/index.d.ts +47 -0
- package/dist/use-drag/index.js +87 -0
- package/dist/use-drag/index.js.map +1 -0
- package/dist/use-fallback-props/index.d.ts +31 -0
- package/dist/use-fallback-props/index.js +35 -0
- package/dist/use-fallback-props/index.js.map +1 -0
- package/dist/use-focus/index.d.ts +16 -0
- package/dist/use-focus/index.js +27 -0
- package/dist/use-focus/index.js.map +1 -0
- package/dist/use-form-component/index.d.ts +22 -0
- package/dist/use-form-component/index.js +15 -0
- package/dist/use-form-component/index.js.map +1 -0
- package/dist/use-lock/index.d.ts +28 -0
- package/dist/use-lock/index.js +36 -0
- package/dist/use-lock/index.js.map +1 -0
- package/dist/use-model/index.d.ts +35 -0
- package/dist/use-model/index.js +46 -0
- package/dist/use-model/index.js.map +1 -0
- package/dist/use-pop/index.d.ts +53 -0
- package/dist/use-pop/index.js +121 -0
- package/dist/use-pop/index.js.map +1 -0
- package/dist/use-reactive-size/index.d.ts +17 -0
- package/dist/use-reactive-size/index.js +40 -0
- package/dist/use-reactive-size/index.js.map +1 -0
- package/dist/use-resize-observer/index.d.ts +33 -0
- package/dist/use-resize-observer/index.js +92 -0
- package/dist/use-resize-observer/index.js.map +1 -0
- package/dist/use-transition/index.d.ts +18 -0
- package/dist/use-transition/index.js +11 -0
- package/dist/use-transition/index.js.map +1 -0
- package/dist/use-transition/type.d.ts +47 -0
- package/dist/use-transition/use-css-transition.js +129 -0
- package/dist/use-transition/use-css-transition.js.map +1 -0
- package/dist/use-transition/use-style-transition.js +127 -0
- package/dist/use-transition/use-style-transition.js.map +1 -0
- package/dist/use-transition/utils.js +41 -0
- package/dist/use-transition/utils.js.map +1 -0
- package/dist/use-virtual/index.d.ts +30 -0
- package/dist/use-virtual/index.js +67 -0
- package/dist/use-virtual/index.js.map +1 -0
- package/package.json +32 -0
- package/src/index.ts +25 -0
- package/src/use-component-props/index.ts +63 -0
- package/src/use-config/index.ts +77 -0
- package/src/use-drag/index.ts +151 -0
- package/src/use-fallback-props/index.ts +76 -0
- package/src/use-focus/index.ts +26 -0
- package/src/use-form-component/index.ts +31 -0
- package/src/use-lock/index.ts +50 -0
- package/src/use-model/index.ts +96 -0
- package/src/use-pop/index.ts +206 -0
- package/src/use-reactive-size/index.ts +53 -0
- package/src/use-resize-observer/index.ts +148 -0
- package/src/use-transition/index.ts +22 -0
- package/src/use-transition/type.ts +50 -0
- package/src/use-transition/use-css-transition.ts +186 -0
- package/src/use-transition/use-style-transition.ts +182 -0
- package/src/use-transition/utils.ts +56 -0
- package/src/use-virtual/index.ts +130 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-css-transition.js","names":[],"sources":["../../src/use-transition/use-css-transition.ts"],"sourcesContent":["import { createToggle } from '@veltra/utils'\nimport { isRef, watch, onBeforeUnmount, computed } from 'vue'\n\nimport type { Returned, CssTransitionOptions } from './type'\n\nconst increaseTransitionCount = (el: HTMLElement & { _count?: number }) => {\n el._count = (el._count ?? 0) + 1\n}\n\nconst decreaseTransitionCount = (el: HTMLElement & { _count?: number }) => {\n el._count = (el._count ?? 0) - 1\n if (el._count <= 0) {\n delete el._count\n }\n}\n\n/**\n * 使用css过渡\n * @param options 过渡选项\n */\nexport function useCssTransition(options: CssTransitionOptions): Returned {\n const {\n name,\n target,\n afterEnter,\n afterLeave,\n enterCanceled,\n leaveCanceled,\n keepEnterTo = false\n } = options\n\n const getDom = (): (HTMLElement & { _count?: number }) | undefined =>\n isRef(target) ? target.value : target\n\n const classes = computed(() => {\n const _name = typeof name === 'string' ? name : name.value\n return {\n /** 进入前的类 */\n enterFrom: `${_name}-enter-from`,\n /** 进入后最终的类 */\n enterTo: `${_name}-enter-to`,\n /** 【进入动画】持续时的类 */\n enterActive: `${_name}-enter-active`,\n /** 离开前的类 */\n leaveFrom: `${_name}-leave-from`,\n /** 离开类 */\n leaveTo: `${_name}-leave-to`,\n /** 【离开动画】持续时的类 */\n leaveActive: `${_name}-leave-active`\n }\n })\n\n /** 开始进入动画 */\n const startTransitionIn = () => {\n const { enterActive, enterTo, enterFrom } = classes.value\n const dom = getDom()\n\n dom?.classList.add(enterFrom)\n\n requestAnimationFrame(() => {\n dom?.classList.add(enterActive)\n requestAnimationFrame(() => {\n dom?.classList.remove(enterFrom)\n dom?.classList.add(enterTo)\n })\n })\n }\n\n /** 开始离开动画 */\n const startTransitionOut = () => {\n const { leaveTo, leaveActive, leaveFrom, enterTo } = classes.value\n const dom = getDom()\n\n // 标记动画进入离开状态\n if (keepEnterTo) {\n dom?.classList.remove(enterTo)\n }\n dom?.classList.add(leaveFrom, leaveActive)\n\n requestAnimationFrame(() => {\n dom?.classList.add(leaveActive)\n requestAnimationFrame(() => {\n dom?.classList.remove(leaveFrom)\n dom?.classList.add(leaveTo)\n })\n })\n }\n\n const [active, toggle] = createToggle(false, (_active) => {\n _active ? startTransitionIn() : startTransitionOut()\n })\n\n const transitionEndHandler = (e: TransitionEvent) => {\n e.stopPropagation()\n\n const { leaveActive, enterActive, enterTo, leaveTo } = classes.value\n const dom = getDom()\n\n if (dom !== e.target) return\n\n decreaseTransitionCount(dom)\n\n if (dom._count) return\n\n // 激活状态,移除enter-active类\n if (active.value) {\n if (keepEnterTo) {\n dom?.classList.remove(enterActive)\n } else {\n dom?.classList.remove(enterActive, enterTo)\n }\n afterEnter?.()\n } else {\n dom?.classList.remove(leaveActive, leaveTo)\n afterLeave?.()\n }\n }\n\n const transitionRunHandler = (e: TransitionEvent) => {\n e.stopPropagation()\n const dom = getDom()\n if (dom !== e.target) return\n increaseTransitionCount(dom)\n }\n\n const transitionCancelHandler = (e: TransitionEvent) => {\n e.stopPropagation()\n const dom = getDom()\n\n if (dom !== e.target) return\n decreaseTransitionCount(dom)\n\n if (dom._count) return\n\n const { leaveActive, enterActive } = classes.value\n\n // 激活状态,移除enter-active类\n if (active.value) {\n dom?.classList.remove(enterActive)\n enterCanceled?.()\n } else {\n dom?.classList.remove(leaveActive)\n leaveCanceled?.()\n }\n }\n\n /** 添加事件 */\n const addEvent = (el?: HTMLElement) => {\n el?.addEventListener('transitioncancel', transitionCancelHandler)\n el?.addEventListener('transitionend', transitionEndHandler)\n el?.addEventListener('transitionrun', transitionRunHandler)\n }\n\n /** 移除事件 */\n const removeEvent = (el?: HTMLElement) => {\n el?.removeEventListener('transitioncancel', transitionCancelHandler)\n el?.removeEventListener('transitionend', transitionEndHandler)\n el?.removeEventListener('transitionrun', transitionRunHandler)\n }\n\n if (isRef(target)) {\n watch(target, (_target, oldTarget) => {\n if (oldTarget) {\n removeEvent(oldTarget)\n }\n _target && addEvent(_target)\n })\n } else if (target) {\n addEvent(target)\n }\n\n onBeforeUnmount(() => {\n const dom = getDom()\n removeEvent(dom)\n })\n\n return {\n toggle,\n enter() {\n toggle(true)\n },\n leave() {\n toggle(false)\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { watchTransition } from "./utils.js";
|
|
2
|
+
import { createToggle, nextFrame, setStyles } from "@veltra/utils";
|
|
3
|
+
import { isRef, watch } from "vue";
|
|
4
|
+
//#region src/use-transition/use-style-transition.ts
|
|
5
|
+
function useStyleTransition(options) {
|
|
6
|
+
const { enterTo, enterActive, leaveActive, target, afterEnter, afterLeave, enterCanceled, leaveCanceled } = options;
|
|
7
|
+
const getDom = () => isRef(target) ? target.value : target;
|
|
8
|
+
/** 进入动画前的初始状态 */
|
|
9
|
+
const transitionOriginStyle = {};
|
|
10
|
+
/** 获取过渡样式的初始样式 */
|
|
11
|
+
const getOriginStyles = (styles) => {
|
|
12
|
+
return Object.fromEntries(Object.keys(styles).map((key) => {
|
|
13
|
+
return [key, transitionOriginStyle[key]];
|
|
14
|
+
}));
|
|
15
|
+
};
|
|
16
|
+
watch(() => getDom(), (dom) => {
|
|
17
|
+
if (dom) {
|
|
18
|
+
const map = dom.attributeStyleMap;
|
|
19
|
+
~[...Object.keys(enterTo), ...Object.keys(enterActive)].forEach((key) => {
|
|
20
|
+
transitionOriginStyle[key] = map.get(key);
|
|
21
|
+
});
|
|
22
|
+
} else Object.keys(transitionOriginStyle).forEach((key) => {
|
|
23
|
+
delete transitionOriginStyle[key];
|
|
24
|
+
});
|
|
25
|
+
}, { immediate: true });
|
|
26
|
+
/**
|
|
27
|
+
* 添加过渡进入时并持续时的样式
|
|
28
|
+
* @param dom 元素
|
|
29
|
+
*/
|
|
30
|
+
const addEnterActive = (dom) => {
|
|
31
|
+
setStyles(dom, enterActive);
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* 移除过渡进入时并持续时的样式
|
|
35
|
+
* @param dom 元素
|
|
36
|
+
*/
|
|
37
|
+
const removeEnterActive = (dom) => {
|
|
38
|
+
setStyles(dom, getOriginStyles(enterActive));
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* 添加过渡离开并持续时的样式
|
|
42
|
+
* @param dom 元素
|
|
43
|
+
*/
|
|
44
|
+
const addLeaveActive = (dom) => {
|
|
45
|
+
setStyles(dom, leaveActive);
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* 移除过渡离开并持续时的样式
|
|
49
|
+
* @param dom 元素
|
|
50
|
+
*/
|
|
51
|
+
const removeLeaveActive = (dom) => {
|
|
52
|
+
setStyles(dom, getOriginStyles(leaveActive));
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* 添加过渡目标样式
|
|
56
|
+
* @param dom 元素
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* 移除过渡目标样式
|
|
60
|
+
* @param dom 元素
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* 添加过渡目标样式
|
|
64
|
+
* @param dom 元素
|
|
65
|
+
*/
|
|
66
|
+
const addEnterToStyle = (dom) => {
|
|
67
|
+
setStyles(dom, enterTo);
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* 移除过渡目标样式
|
|
71
|
+
* @param dom 元素
|
|
72
|
+
*/
|
|
73
|
+
const removeEnterToStyle = (dom) => {
|
|
74
|
+
setStyles(dom, getOriginStyles(enterTo));
|
|
75
|
+
};
|
|
76
|
+
/** 开始进入动画 */
|
|
77
|
+
const startEnter = () => {
|
|
78
|
+
const dom = getDom();
|
|
79
|
+
if (!dom) return;
|
|
80
|
+
addEnterActive(dom);
|
|
81
|
+
nextFrame(() => {
|
|
82
|
+
addEnterToStyle(dom);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
/** 开始离开动画 */
|
|
86
|
+
const startLeave = () => {
|
|
87
|
+
const dom = getDom();
|
|
88
|
+
if (!dom) return;
|
|
89
|
+
addLeaveActive(dom);
|
|
90
|
+
nextFrame(() => {
|
|
91
|
+
removeEnterToStyle(dom);
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
const [active, toggle] = createToggle(false, (active) => {
|
|
95
|
+
active ? startEnter() : startLeave();
|
|
96
|
+
});
|
|
97
|
+
watchTransition(getDom, {
|
|
98
|
+
styleKeys: Object.keys(enterTo),
|
|
99
|
+
onCancel(el) {
|
|
100
|
+
if (active.value) {
|
|
101
|
+
removeEnterActive(el);
|
|
102
|
+
enterCanceled?.();
|
|
103
|
+
} else {
|
|
104
|
+
removeLeaveActive(el);
|
|
105
|
+
leaveCanceled?.();
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
onEnd(el) {
|
|
109
|
+
if (active.value) {
|
|
110
|
+
removeEnterActive(el);
|
|
111
|
+
afterEnter?.();
|
|
112
|
+
} else {
|
|
113
|
+
removeLeaveActive(el);
|
|
114
|
+
afterLeave?.();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
toggle,
|
|
120
|
+
enter: () => toggle(true),
|
|
121
|
+
leave: () => toggle(false)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { useStyleTransition };
|
|
126
|
+
|
|
127
|
+
//# sourceMappingURL=use-style-transition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-style-transition.js","names":[],"sources":["../../src/use-transition/use-style-transition.ts"],"sourcesContent":["import { createToggle, nextFrame, setStyles } from '@veltra/utils'\nimport { isRef, watch, type CSSProperties } from 'vue'\n\nimport type { Returned, StyleTransitionOptions } from './type'\nimport { watchTransition } from './utils'\n\nexport function useStyleTransition(options: StyleTransitionOptions): Returned {\n const {\n // enterFrom,\n // leaveTo,\n enterTo,\n enterActive,\n leaveActive,\n target,\n afterEnter,\n afterLeave,\n enterCanceled,\n leaveCanceled\n } = options\n\n const getDom = (): HTMLElement | undefined => (isRef(target) ? target.value : target)\n\n /** 进入动画前的初始状态 */\n const transitionOriginStyle: CSSProperties = {}\n\n /** 获取过渡样式的初始样式 */\n const getOriginStyles = (styles: CSSProperties) => {\n return Object.fromEntries(\n Object.keys(styles).map((key) => {\n return [key, transitionOriginStyle[key as keyof CSSProperties]]\n })\n )\n }\n // 监听dom并获取dom在进入动画之前的样式\n // ...Object.keys(enterFrom ?? {}),\n // ...Object.keys(leaveTo ?? {}),\n watch(\n () => getDom(),\n (dom) => {\n if (dom) {\n const map = dom.attributeStyleMap\n ~[...Object.keys(enterTo), ...Object.keys(enterActive)].forEach((key) => {\n transitionOriginStyle[key] = map.get(key)\n })\n } else {\n Object.keys(transitionOriginStyle).forEach((key) => {\n delete transitionOriginStyle[key as keyof CSSProperties]\n })\n }\n },\n { immediate: true }\n )\n\n /**\n * 添加过渡进入时并持续时的样式\n * @param dom 元素\n */\n const addEnterActive = (dom: HTMLElement) => {\n setStyles(dom, enterActive)\n }\n\n /**\n * 移除过渡进入时并持续时的样式\n * @param dom 元素\n */\n const removeEnterActive = (dom: HTMLElement) => {\n setStyles(dom, getOriginStyles(enterActive))\n }\n\n /**\n * 添加过渡离开并持续时的样式\n * @param dom 元素\n */\n const addLeaveActive = (dom: HTMLElement) => {\n setStyles(dom, leaveActive)\n }\n\n /**\n * 移除过渡离开并持续时的样式\n * @param dom 元素\n */\n const removeLeaveActive = (dom: HTMLElement) => {\n setStyles(dom, getOriginStyles(leaveActive))\n }\n\n /**\n * 添加过渡目标样式\n * @param dom 元素\n */\n // const addEnterFromStyle = (dom: HTMLElement) => {\n // enterFrom && setStyles(dom, enterFrom)\n // }\n\n /**\n * 移除过渡目标样式\n * @param dom 元素\n */\n // const removeEnterFromStyle = (dom: HTMLElement) => {\n // if (!enterFrom) return\n\n // const canRemovedStyles = {}\n\n // for (const key in enterFrom) {\n // if (key in enterTo) continue\n // canRemovedStyles[key] = enterFrom[key]\n // }\n\n // setStyles(dom, getOriginStyles(canRemovedStyles))\n // }\n /**\n * 添加过渡目标样式\n * @param dom 元素\n */\n const addEnterToStyle = (dom: HTMLElement) => {\n setStyles(dom, enterTo)\n }\n\n /**\n * 移除过渡目标样式\n * @param dom 元素\n */\n const removeEnterToStyle = (dom: HTMLElement) => {\n setStyles(dom, getOriginStyles(enterTo))\n }\n\n /** 开始进入动画 */\n const startEnter = () => {\n const dom = getDom()\n if (!dom) return\n addEnterActive(dom)\n // 在下一帧插入动画运动目标状态\n nextFrame(() => {\n addEnterToStyle(dom)\n })\n }\n\n /** 开始离开动画 */\n const startLeave = () => {\n const dom = getDom()\n if (!dom) return\n\n // 标记动画进入离开状态\n addLeaveActive(dom)\n\n // 在下一帧移除动画运动目标状态恢复原状或者应用新的状态\n nextFrame(() => {\n removeEnterToStyle(dom)\n })\n }\n\n const [active, toggle] = createToggle(false, (active) => {\n active ? startEnter() : startLeave()\n })\n\n watchTransition(getDom, {\n styleKeys: Object.keys(enterTo),\n onCancel(el) {\n // 激活状态,移除enter-active类\n if (active.value) {\n removeEnterActive(el)\n enterCanceled?.()\n } else {\n removeLeaveActive(el)\n leaveCanceled?.()\n }\n },\n\n onEnd(el) {\n if (active.value) {\n removeEnterActive(el)\n // removeEnterFromStyle(el)\n afterEnter?.()\n } else {\n removeLeaveActive(el)\n\n afterLeave?.()\n }\n }\n })\n\n return { toggle, enter: () => toggle(true), leave: () => toggle(false) }\n}\n"],"mappings":";;;;AAMA,SAAgB,mBAAmB,SAA2C;CAC5E,MAAM,EAGJ,SACA,aACA,aACA,QACA,YACA,YACA,eACA,kBACE;CAEJ,MAAM,eAAyC,MAAM,OAAO,GAAG,OAAO,QAAQ;;CAG9E,MAAM,wBAAuC,EAAE;;CAG/C,MAAM,mBAAmB,WAA0B;AACjD,SAAO,OAAO,YACZ,OAAO,KAAK,OAAO,CAAC,KAAK,QAAQ;AAC/B,UAAO,CAAC,KAAK,sBAAsB,KAA4B;IAC/D,CACH;;AAKH,aACQ,QAAQ,GACb,QAAQ;AACP,MAAI,KAAK;GACP,MAAM,MAAM,IAAI;AAChB,IAAC,CAAC,GAAG,OAAO,KAAK,QAAQ,EAAE,GAAG,OAAO,KAAK,YAAY,CAAC,CAAC,SAAS,QAAQ;AACvE,0BAAsB,OAAO,IAAI,IAAI,IAAI;KACzC;QAEF,QAAO,KAAK,sBAAsB,CAAC,SAAS,QAAQ;AAClD,UAAO,sBAAsB;IAC7B;IAGN,EAAE,WAAW,MAAM,CACpB;;;;;CAMD,MAAM,kBAAkB,QAAqB;AAC3C,YAAU,KAAK,YAAY;;;;;;CAO7B,MAAM,qBAAqB,QAAqB;AAC9C,YAAU,KAAK,gBAAgB,YAAY,CAAC;;;;;;CAO9C,MAAM,kBAAkB,QAAqB;AAC3C,YAAU,KAAK,YAAY;;;;;;CAO7B,MAAM,qBAAqB,QAAqB;AAC9C,YAAU,KAAK,gBAAgB,YAAY,CAAC;;;;;;;;;;;;;;CA+B9C,MAAM,mBAAmB,QAAqB;AAC5C,YAAU,KAAK,QAAQ;;;;;;CAOzB,MAAM,sBAAsB,QAAqB;AAC/C,YAAU,KAAK,gBAAgB,QAAQ,CAAC;;;CAI1C,MAAM,mBAAmB;EACvB,MAAM,MAAM,QAAQ;AACpB,MAAI,CAAC,IAAK;AACV,iBAAe,IAAI;AAEnB,kBAAgB;AACd,mBAAgB,IAAI;IACpB;;;CAIJ,MAAM,mBAAmB;EACvB,MAAM,MAAM,QAAQ;AACpB,MAAI,CAAC,IAAK;AAGV,iBAAe,IAAI;AAGnB,kBAAgB;AACd,sBAAmB,IAAI;IACvB;;CAGJ,MAAM,CAAC,QAAQ,UAAU,aAAa,QAAQ,WAAW;AACvD,WAAS,YAAY,GAAG,YAAY;GACpC;AAEF,iBAAgB,QAAQ;EACtB,WAAW,OAAO,KAAK,QAAQ;EAC/B,SAAS,IAAI;AAEX,OAAI,OAAO,OAAO;AAChB,sBAAkB,GAAG;AACrB,qBAAiB;UACZ;AACL,sBAAkB,GAAG;AACrB,qBAAiB;;;EAIrB,MAAM,IAAI;AACR,OAAI,OAAO,OAAO;AAChB,sBAAkB,GAAG;AAErB,kBAAc;UACT;AACL,sBAAkB,GAAG;AAErB,kBAAc;;;EAGnB,CAAC;AAEF,QAAO;EAAE;EAAQ,aAAa,OAAO,KAAK;EAAE,aAAa,OAAO,MAAM;EAAE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { onBeforeUnmount, watch } from "vue";
|
|
2
|
+
//#region src/use-transition/utils.ts
|
|
3
|
+
/**
|
|
4
|
+
* 监听过渡
|
|
5
|
+
* @param domGetter 元素获取函数
|
|
6
|
+
* @param config 配置
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
function watchTransition(domGetter, config) {
|
|
10
|
+
const runCallback = (e, cb) => {
|
|
11
|
+
e.stopPropagation();
|
|
12
|
+
if (e.target !== domGetter() || !config.styleKeys.includes(e.propertyName)) return;
|
|
13
|
+
cb(e.target);
|
|
14
|
+
};
|
|
15
|
+
const transitionEndHandler = (e) => {
|
|
16
|
+
if (!domGetter()) return;
|
|
17
|
+
runCallback(e, config.onEnd);
|
|
18
|
+
};
|
|
19
|
+
const transitionCancelHandler = (e) => {
|
|
20
|
+
if (!domGetter()) return;
|
|
21
|
+
runCallback(e, config.onCancel);
|
|
22
|
+
};
|
|
23
|
+
const addEvent = (el) => {
|
|
24
|
+
el.addEventListener("transitionend", transitionEndHandler, false);
|
|
25
|
+
};
|
|
26
|
+
const removeEvent = (el) => {
|
|
27
|
+
el?.removeEventListener("transitionend", transitionEndHandler);
|
|
28
|
+
el?.removeEventListener("transitioncancel", transitionCancelHandler);
|
|
29
|
+
};
|
|
30
|
+
watch(domGetter, (target, oldTarget) => {
|
|
31
|
+
removeEvent(oldTarget);
|
|
32
|
+
target && addEvent(target);
|
|
33
|
+
}, { immediate: true });
|
|
34
|
+
onBeforeUnmount(() => {
|
|
35
|
+
removeEvent(domGetter());
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { watchTransition };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../src/use-transition/utils.ts"],"sourcesContent":["import { watch, onBeforeUnmount } from 'vue'\n\n/**\n * 监听过渡\n * @param domGetter 元素获取函数\n * @param config 配置\n * @returns\n */\nexport function watchTransition(\n domGetter: () => HTMLElement | undefined,\n config: {\n styleKeys: string[]\n onEnd: (dom: HTMLElement) => void\n onCancel: (dom: HTMLElement) => void\n }\n): void {\n const runCallback = (e: TransitionEvent, cb: (el: HTMLElement) => void) => {\n e.stopPropagation()\n if (e.target !== domGetter() || !config.styleKeys.includes(e.propertyName)) {\n return\n }\n\n cb(e.target as HTMLElement)\n }\n\n const transitionEndHandler = (e: TransitionEvent) => {\n if (!domGetter()) return\n runCallback(e, config.onEnd)\n }\n const transitionCancelHandler = (e: TransitionEvent) => {\n if (!domGetter()) return\n runCallback(e, config.onCancel)\n }\n const addEvent = (el: HTMLElement) => {\n el.addEventListener('transitionend', transitionEndHandler, false)\n // el.addEventListener('transitioncancel', transitionCancelHandler, false)\n }\n\n const removeEvent = (el?: HTMLElement) => {\n el?.removeEventListener('transitionend', transitionEndHandler)\n el?.removeEventListener('transitioncancel', transitionCancelHandler)\n }\n\n watch(\n domGetter,\n (target, oldTarget) => {\n removeEvent(oldTarget)\n target && addEvent(target)\n },\n { immediate: true }\n )\n\n onBeforeUnmount(() => {\n removeEvent(domGetter())\n })\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,gBACd,WACA,QAKM;CACN,MAAM,eAAe,GAAoB,OAAkC;AACzE,IAAE,iBAAiB;AACnB,MAAI,EAAE,WAAW,WAAW,IAAI,CAAC,OAAO,UAAU,SAAS,EAAE,aAAa,CACxE;AAGF,KAAG,EAAE,OAAsB;;CAG7B,MAAM,wBAAwB,MAAuB;AACnD,MAAI,CAAC,WAAW,CAAE;AAClB,cAAY,GAAG,OAAO,MAAM;;CAE9B,MAAM,2BAA2B,MAAuB;AACtD,MAAI,CAAC,WAAW,CAAE;AAClB,cAAY,GAAG,OAAO,SAAS;;CAEjC,MAAM,YAAY,OAAoB;AACpC,KAAG,iBAAiB,iBAAiB,sBAAsB,MAAM;;CAInE,MAAM,eAAe,OAAqB;AACxC,MAAI,oBAAoB,iBAAiB,qBAAqB;AAC9D,MAAI,oBAAoB,oBAAoB,wBAAwB;;AAGtE,OACE,YACC,QAAQ,cAAc;AACrB,cAAY,UAAU;AACtB,YAAU,SAAS,OAAO;IAE5B,EAAE,WAAW,MAAM,CACpB;AAED,uBAAsB;AACpB,cAAY,WAAW,CAAC;GACxB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ComputedRef, Ref, ShallowRef } from "vue";
|
|
2
|
+
import { VirtualItem } from "@tanstack/vue-virtual";
|
|
3
|
+
|
|
4
|
+
//#region src/use-virtual/index.d.ts
|
|
5
|
+
interface Options {
|
|
6
|
+
/** 指定启用虚拟列表的阈值 */
|
|
7
|
+
virtualThreshold?: number | Ref<number | undefined>;
|
|
8
|
+
/** 数量 */
|
|
9
|
+
count: Ref<number>;
|
|
10
|
+
/** 滚动容器 */
|
|
11
|
+
scrollEl: ShallowRef<HTMLElement | null>;
|
|
12
|
+
/** 估算高度(宽度) */
|
|
13
|
+
estimateSize?: (index: number) => number;
|
|
14
|
+
/** 列表项之间的间距 */
|
|
15
|
+
gap?: number;
|
|
16
|
+
}
|
|
17
|
+
type CustomVirtualItem = Omit<VirtualItem, 'key'> & {
|
|
18
|
+
key: number | string;
|
|
19
|
+
};
|
|
20
|
+
type VirtualReturned = {
|
|
21
|
+
/** 虚拟列表 */virtualList: ShallowRef<CustomVirtualItem[]>; /** 总高度 */
|
|
22
|
+
totalHeight: ShallowRef<number>; /** 测量元素高度 */
|
|
23
|
+
measureElement: (el: any) => void; /** 滚动到指定索引 */
|
|
24
|
+
scrollTo: (index: number) => void; /** 是否启用虚拟列表 */
|
|
25
|
+
virtualEnabled: ComputedRef<boolean>;
|
|
26
|
+
};
|
|
27
|
+
declare function useVirtual(options: Options): VirtualReturned;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { VirtualReturned, useVirtual };
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { computed, isRef, onScopeDispose, shallowRef, watch } from "vue";
|
|
2
|
+
import { Virtualizer, elementScroll, observeElementOffset, observeElementRect } from "@tanstack/vue-virtual";
|
|
3
|
+
//#region src/use-virtual/index.ts
|
|
4
|
+
const defaultEstimateSize = () => 34;
|
|
5
|
+
function useVirtual(options) {
|
|
6
|
+
const { count, scrollEl, estimateSize, virtualThreshold, gap } = options;
|
|
7
|
+
const enabled = computed(() => {
|
|
8
|
+
if (isRef(virtualThreshold)) return virtualThreshold.value ? count.value > virtualThreshold.value : true;
|
|
9
|
+
return virtualThreshold ? count.value > virtualThreshold : true;
|
|
10
|
+
});
|
|
11
|
+
const virtualList = shallowRef([]);
|
|
12
|
+
/** 总高度 */
|
|
13
|
+
const totalHeight = shallowRef(0);
|
|
14
|
+
function updateVirtualList() {
|
|
15
|
+
if (enabled.value) {
|
|
16
|
+
totalHeight.value = v.getTotalSize();
|
|
17
|
+
virtualList.value = v.getVirtualItems();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const virtualizerOptions = computed(() => {
|
|
21
|
+
return {
|
|
22
|
+
enabled: enabled.value,
|
|
23
|
+
count: count.value,
|
|
24
|
+
getScrollElement: () => scrollEl.value,
|
|
25
|
+
estimateSize: estimateSize ?? defaultEstimateSize,
|
|
26
|
+
overscan: 3,
|
|
27
|
+
gap,
|
|
28
|
+
observeElementRect,
|
|
29
|
+
observeElementOffset,
|
|
30
|
+
scrollToFn: elementScroll,
|
|
31
|
+
onChange: updateVirtualList
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
const v = new Virtualizer(virtualizerOptions.value);
|
|
35
|
+
updateVirtualList();
|
|
36
|
+
const cleanup = v._didMount();
|
|
37
|
+
watch(scrollEl, (el) => {
|
|
38
|
+
el && v._willUpdate();
|
|
39
|
+
}, { immediate: true });
|
|
40
|
+
watch(() => virtualizerOptions.value, (o) => {
|
|
41
|
+
v.setOptions(o);
|
|
42
|
+
v._willUpdate();
|
|
43
|
+
updateVirtualList();
|
|
44
|
+
});
|
|
45
|
+
onScopeDispose(() => {
|
|
46
|
+
cleanup();
|
|
47
|
+
});
|
|
48
|
+
function scrollTo(index) {
|
|
49
|
+
v.scrollToIndex(index, { align: "center" });
|
|
50
|
+
}
|
|
51
|
+
/** 测量元素高度 */
|
|
52
|
+
function measureElement(el) {
|
|
53
|
+
if (!el) return;
|
|
54
|
+
v.measureElement(el);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
virtualEnabled: enabled,
|
|
58
|
+
virtualList,
|
|
59
|
+
totalHeight,
|
|
60
|
+
measureElement,
|
|
61
|
+
scrollTo
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { useVirtual };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/use-virtual/index.ts"],"sourcesContent":["import {\n elementScroll,\n observeElementOffset,\n observeElementRect,\n type VirtualItem,\n Virtualizer\n} from '@tanstack/vue-virtual'\nimport {\n computed,\n isRef,\n onScopeDispose,\n shallowRef,\n watch,\n type ComputedRef,\n type Ref,\n type ShallowRef\n} from 'vue'\n\ninterface Options {\n /** 指定启用虚拟列表的阈值 */\n virtualThreshold?: number | Ref<number | undefined>\n /** 数量 */\n count: Ref<number>\n /** 滚动容器 */\n scrollEl: ShallowRef<HTMLElement | null>\n /** 估算高度(宽度) */\n estimateSize?: (index: number) => number\n /** 列表项之间的间距 */\n gap?: number\n}\n\ntype CustomVirtualItem = Omit<VirtualItem, 'key'> & { key: number | string }\n\nexport type VirtualReturned = {\n /** 虚拟列表 */\n virtualList: ShallowRef<CustomVirtualItem[]>\n /** 总高度 */\n totalHeight: ShallowRef<number>\n /** 测量元素高度 */\n measureElement: (el: any) => void\n /** 滚动到指定索引 */\n scrollTo: (index: number) => void\n /** 是否启用虚拟列表 */\n virtualEnabled: ComputedRef<boolean>\n}\n\nconst defaultEstimateSize = () => 34\n\nexport function useVirtual(options: Options): VirtualReturned {\n const { count, scrollEl, estimateSize, virtualThreshold, gap } = options\n\n const enabled = computed(() => {\n if (isRef(virtualThreshold)) {\n return virtualThreshold.value ? count.value > virtualThreshold.value : true\n }\n\n return virtualThreshold ? count.value > virtualThreshold : true\n })\n\n const virtualList = shallowRef<CustomVirtualItem[]>([])\n\n /** 总高度 */\n const totalHeight = shallowRef(0)\n\n function updateVirtualList() {\n if (enabled.value) {\n totalHeight.value = v.getTotalSize()\n virtualList.value = v.getVirtualItems() as CustomVirtualItem[]\n }\n }\n\n const virtualizerOptions = computed(() => {\n return {\n enabled: enabled.value,\n count: count.value,\n getScrollElement: () => scrollEl.value,\n estimateSize: estimateSize ?? defaultEstimateSize,\n overscan: 3,\n gap,\n observeElementRect: observeElementRect,\n observeElementOffset: observeElementOffset,\n scrollToFn: elementScroll,\n onChange: updateVirtualList\n }\n })\n\n const v = new Virtualizer(virtualizerOptions.value)\n\n updateVirtualList()\n\n const cleanup = v._didMount()\n\n watch(\n scrollEl,\n (el) => {\n el && v._willUpdate()\n },\n { immediate: true }\n )\n\n watch(\n () => virtualizerOptions.value,\n (o) => {\n v.setOptions(o)\n\n v._willUpdate()\n\n updateVirtualList()\n }\n )\n\n onScopeDispose(() => {\n cleanup()\n })\n\n function scrollTo(index: number) {\n v.scrollToIndex(index, { align: 'center' })\n }\n\n /** 测量元素高度 */\n function measureElement(el: Element) {\n if (!el) return\n\n v.measureElement(el)\n\n return undefined\n }\n\n return { virtualEnabled: enabled, virtualList, totalHeight, measureElement, scrollTo }\n}\n"],"mappings":";;;AA8CA,MAAM,4BAA4B;AAElC,SAAgB,WAAW,SAAmC;CAC5D,MAAM,EAAE,OAAO,UAAU,cAAc,kBAAkB,QAAQ;CAEjE,MAAM,UAAU,eAAe;AAC7B,MAAI,MAAM,iBAAiB,CACzB,QAAO,iBAAiB,QAAQ,MAAM,QAAQ,iBAAiB,QAAQ;AAGzE,SAAO,mBAAmB,MAAM,QAAQ,mBAAmB;GAC3D;CAEF,MAAM,cAAc,WAAgC,EAAE,CAAC;;CAGvD,MAAM,cAAc,WAAW,EAAE;CAEjC,SAAS,oBAAoB;AAC3B,MAAI,QAAQ,OAAO;AACjB,eAAY,QAAQ,EAAE,cAAc;AACpC,eAAY,QAAQ,EAAE,iBAAiB;;;CAI3C,MAAM,qBAAqB,eAAe;AACxC,SAAO;GACL,SAAS,QAAQ;GACjB,OAAO,MAAM;GACb,wBAAwB,SAAS;GACjC,cAAc,gBAAgB;GAC9B,UAAU;GACV;GACoB;GACE;GACtB,YAAY;GACZ,UAAU;GACX;GACD;CAEF,MAAM,IAAI,IAAI,YAAY,mBAAmB,MAAM;AAEnD,oBAAmB;CAEnB,MAAM,UAAU,EAAE,WAAW;AAE7B,OACE,WACC,OAAO;AACN,QAAM,EAAE,aAAa;IAEvB,EAAE,WAAW,MAAM,CACpB;AAED,aACQ,mBAAmB,QACxB,MAAM;AACL,IAAE,WAAW,EAAE;AAEf,IAAE,aAAa;AAEf,qBAAmB;GAEtB;AAED,sBAAqB;AACnB,WAAS;GACT;CAEF,SAAS,SAAS,OAAe;AAC/B,IAAE,cAAc,OAAO,EAAE,OAAO,UAAU,CAAC;;;CAI7C,SAAS,eAAe,IAAa;AACnC,MAAI,CAAC,GAAI;AAET,IAAE,eAAe,GAAG;;AAKtB,QAAO;EAAE,gBAAgB;EAAS;EAAa;EAAa;EAAgB;EAAU"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@veltra/compositions",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist",
|
|
6
|
+
"src"
|
|
7
|
+
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"development": "./src/index.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsdown",
|
|
18
|
+
"check-types": "tsc -p tsconfig.json --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@cat-kit/core": "^1.0.1",
|
|
22
|
+
"@floating-ui/dom": "^1.7.6",
|
|
23
|
+
"@tanstack/vue-virtual": "^3.13.23",
|
|
24
|
+
"@veltra/utils": "workspace:*"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"tsdown": "^0.21.7"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"vue": "^3.5.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export * from './use-component-props'
|
|
2
|
+
|
|
3
|
+
export * from './use-config'
|
|
4
|
+
|
|
5
|
+
export * from './use-drag'
|
|
6
|
+
|
|
7
|
+
export * from './use-fallback-props'
|
|
8
|
+
|
|
9
|
+
export * from './use-focus'
|
|
10
|
+
|
|
11
|
+
export * from './use-form-component'
|
|
12
|
+
|
|
13
|
+
export * from './use-lock'
|
|
14
|
+
|
|
15
|
+
export * from './use-model'
|
|
16
|
+
|
|
17
|
+
export * from './use-pop'
|
|
18
|
+
|
|
19
|
+
export * from './use-reactive-size'
|
|
20
|
+
|
|
21
|
+
export * from './use-resize-observer'
|
|
22
|
+
|
|
23
|
+
export * from './use-transition'
|
|
24
|
+
|
|
25
|
+
export * from './use-virtual'
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { extractNormalVNodes } from '@veltra/utils'
|
|
2
|
+
import { defineComponent, isRef, type MaybeRef, createVNode, cloneVNode, type Component } from 'vue'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 生成一个用于设置组件通用属性的组件
|
|
6
|
+
* @param props 组件通用的属性
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export function useComponentProps<T extends Record<string, any>>(
|
|
10
|
+
props: MaybeRef<T & Record<string, any>>
|
|
11
|
+
): Component {
|
|
12
|
+
return defineComponent({
|
|
13
|
+
name: 'ComponentCommonProps',
|
|
14
|
+
inheritAttrs: false,
|
|
15
|
+
|
|
16
|
+
props: {
|
|
17
|
+
/** 渲染一个标准html5标签 */
|
|
18
|
+
tag: { type: String }
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
setup(componentProps, { slots, attrs }) {
|
|
22
|
+
const isPropsRef = isRef(props)
|
|
23
|
+
// 非 ref 时 keys 固定,缓存避免每次 render 重复计算
|
|
24
|
+
const staticKeys = isPropsRef ? null : Object.keys(props)
|
|
25
|
+
|
|
26
|
+
const mergeNodesProps = (commonProps: Record<string, any>) => {
|
|
27
|
+
const nodes = extractNormalVNodes(slots.default?.() ?? [])
|
|
28
|
+
if (!nodes?.length) return undefined
|
|
29
|
+
|
|
30
|
+
const keys = staticKeys ?? Object.keys(commonProps)
|
|
31
|
+
return nodes.map((node) => {
|
|
32
|
+
const mergedProps: Record<string, any> = {}
|
|
33
|
+
let count = 0
|
|
34
|
+
for (let i = 0; i < keys.length; i++) {
|
|
35
|
+
const key = keys[i]!
|
|
36
|
+
// node中已定义的属性优先
|
|
37
|
+
if (node.props?.[key] !== undefined) continue
|
|
38
|
+
mergedProps[key] = attrs[key] !== undefined ? attrs[key] : commonProps[key]
|
|
39
|
+
count++
|
|
40
|
+
}
|
|
41
|
+
return count > 0 ? cloneVNode(node, mergedProps) : node
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return () => {
|
|
46
|
+
const _props = isPropsRef ? props.value : props
|
|
47
|
+
const nodes = mergeNodesProps(_props)
|
|
48
|
+
|
|
49
|
+
if (componentProps.tag) {
|
|
50
|
+
if (!nodes) return undefined
|
|
51
|
+
const tagProps = Object.keys(attrs).reduce<Record<string, any>>((acc, cur) => {
|
|
52
|
+
if (!(cur in _props)) {
|
|
53
|
+
acc[cur] = attrs[cur]
|
|
54
|
+
}
|
|
55
|
+
return acc
|
|
56
|
+
}, {})
|
|
57
|
+
return createVNode(componentProps.tag, tagProps, nodes)
|
|
58
|
+
}
|
|
59
|
+
return nodes
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { isObj } from '@cat-kit/core'
|
|
2
|
+
import type { ComponentSize } from '@veltra/utils/types'
|
|
3
|
+
import { reactive, readonly, watch } from 'vue'
|
|
4
|
+
|
|
5
|
+
interface State {
|
|
6
|
+
/** 是否开启动画,机器老可以关闭动画来获得性能 */
|
|
7
|
+
animation: boolean
|
|
8
|
+
/** 组件尺寸大小 */
|
|
9
|
+
size: ComponentSize
|
|
10
|
+
/** 表单 */
|
|
11
|
+
form: {
|
|
12
|
+
/** 标签宽度 */
|
|
13
|
+
labelWidth?: number | number
|
|
14
|
+
}
|
|
15
|
+
paginator: { pageSize: number; pageSizeOptions: number[] }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const state = reactive<State>({
|
|
19
|
+
animation: true,
|
|
20
|
+
size: 'default',
|
|
21
|
+
form: { labelWidth: 80 },
|
|
22
|
+
paginator: { pageSize: 40, pageSizeOptions: [40, 100, 200, 500, 1000] }
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export function setDocumentSize(size: ComponentSize, oldSize?: ComponentSize): void {
|
|
26
|
+
if (typeof document === 'undefined') return
|
|
27
|
+
if (oldSize) {
|
|
28
|
+
document.documentElement.classList.remove(oldSize)
|
|
29
|
+
}
|
|
30
|
+
document.documentElement.classList.add(size)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let stopDocumentSizeSync: (() => void) | null = null
|
|
34
|
+
|
|
35
|
+
function ensureDocumentSizeSync(): void {
|
|
36
|
+
if (stopDocumentSizeSync) return
|
|
37
|
+
if (typeof document === 'undefined') return
|
|
38
|
+
|
|
39
|
+
stopDocumentSizeSync = watch(
|
|
40
|
+
() => state.size,
|
|
41
|
+
(size, oldSize) => setDocumentSize(size, oldSize)
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function deepSet(original: Record<string, any>, extend: Record<string, any>) {
|
|
46
|
+
Object.keys(extend).forEach((key) => {
|
|
47
|
+
const val = original[key]
|
|
48
|
+
const targetVal = extend[key]
|
|
49
|
+
if (isObj(val)) {
|
|
50
|
+
if (isObj(targetVal)) {
|
|
51
|
+
deepSet(val, targetVal)
|
|
52
|
+
} else {
|
|
53
|
+
console.warn(`extend['${key}']应该是一个对象`)
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
original[key] = targetVal
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function useConfig(): {
|
|
62
|
+
config: Readonly<State>
|
|
63
|
+
setConfig: (conf: Partial<State>) => void
|
|
64
|
+
} {
|
|
65
|
+
ensureDocumentSizeSync()
|
|
66
|
+
return {
|
|
67
|
+
/** 全局配置 */
|
|
68
|
+
config: readonly(state) as Readonly<State>,
|
|
69
|
+
/**
|
|
70
|
+
* 设置全局配置项
|
|
71
|
+
* @param conf
|
|
72
|
+
*/
|
|
73
|
+
setConfig(conf: Partial<State>) {
|
|
74
|
+
deepSet(state, conf)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { type Ref, type ShallowRef, watch, onBeforeUnmount } from 'vue'
|
|
2
|
+
|
|
3
|
+
interface DragParams {
|
|
4
|
+
/** 本次拖动水平距离 */
|
|
5
|
+
x: number
|
|
6
|
+
/** 本次拖动垂直距离 */
|
|
7
|
+
y: number
|
|
8
|
+
/** 拖拽目标水平偏移量 */
|
|
9
|
+
offsetX: number
|
|
10
|
+
/** 拖拽目标垂直偏移量 */
|
|
11
|
+
offsetY: number
|
|
12
|
+
/** 鼠标事件 */
|
|
13
|
+
e: MouseEvent
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface DragOptions {
|
|
17
|
+
/** 拖动目标 */
|
|
18
|
+
target: ShallowRef<HTMLElement | undefined | null> | Ref<HTMLElement | undefined | null>
|
|
19
|
+
/** 拖动开始 */
|
|
20
|
+
onDragStart?(e: MouseEvent): void
|
|
21
|
+
/** 拖动结束 */
|
|
22
|
+
onDragEnd?(params: DragParams): void
|
|
23
|
+
/** 拖动时 */
|
|
24
|
+
onDrag?(params: DragParams): void
|
|
25
|
+
/** 水平拖动范围 */
|
|
26
|
+
rangeX?: [number, number]
|
|
27
|
+
/** 垂直拖动范围 */
|
|
28
|
+
rangeY?: [number, number]
|
|
29
|
+
/** 初始偏移量 */
|
|
30
|
+
initial?: { offsetX?: number; offsetY?: number }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 拖动组合式方法
|
|
35
|
+
* @param options 拖动选项
|
|
36
|
+
*/
|
|
37
|
+
export function useDrag(options: DragOptions): {
|
|
38
|
+
update: (options: { offsetX?: number; offsetY?: number }) => void
|
|
39
|
+
} {
|
|
40
|
+
const { target, onDragStart, onDrag, onDragEnd, rangeX, rangeY, initial } = options
|
|
41
|
+
|
|
42
|
+
// 鼠标拖拽前的坐标
|
|
43
|
+
let originX = 0
|
|
44
|
+
let originY = 0
|
|
45
|
+
|
|
46
|
+
let offsetX = initial?.offsetX ?? 0
|
|
47
|
+
let offsetY = initial?.offsetY ?? 0
|
|
48
|
+
|
|
49
|
+
// 拖拽参数
|
|
50
|
+
const dragParams: DragParams = { x: 0, y: 0, offsetX: 0, offsetY: 0, e: null as any }
|
|
51
|
+
|
|
52
|
+
// 先取
|
|
53
|
+
const onselectstart = document.onselectstart
|
|
54
|
+
|
|
55
|
+
const handleMousedown = (e: MouseEvent) => {
|
|
56
|
+
// 阻止事件冒泡
|
|
57
|
+
e.stopPropagation()
|
|
58
|
+
// 鼠标左键按下有效
|
|
59
|
+
if (e.button !== 0) return
|
|
60
|
+
// 放置拖拽时选择内容
|
|
61
|
+
window.getSelection()?.removeAllRanges()
|
|
62
|
+
// 阻止后续的事件监听器被执行
|
|
63
|
+
e.stopImmediatePropagation()
|
|
64
|
+
|
|
65
|
+
originX = e.x
|
|
66
|
+
originY = e.y
|
|
67
|
+
|
|
68
|
+
onDragStart?.(e)
|
|
69
|
+
|
|
70
|
+
// 禁止浏览器的选中事件, 直到mouseup事件触发时还原
|
|
71
|
+
document.onselectstart = () => false
|
|
72
|
+
document.addEventListener('mousemove', handleMousemove, { passive: true })
|
|
73
|
+
document.addEventListener('mouseup', handleMouseup)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const getOffsetXWithRange = (deltaX: number) => {
|
|
77
|
+
return Math.max(rangeX![0], Math.min(rangeX![1], offsetX + deltaX))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const getOffsetXWithoutRange = (deltaX: number) => {
|
|
81
|
+
return offsetX + deltaX
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const getOffsetYWithRange = (deltaY: number) => {
|
|
85
|
+
return Math.max(rangeY![0], Math.min(rangeY![1], offsetY + deltaY))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const getOffsetYWithoutRange = (deltaY: number) => {
|
|
89
|
+
return offsetY + deltaY
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const getOffsetX = rangeX ? getOffsetXWithRange : getOffsetXWithoutRange
|
|
93
|
+
const getOffsetY = rangeY ? getOffsetYWithRange : getOffsetYWithoutRange
|
|
94
|
+
|
|
95
|
+
// 避免重复创建对象影响内存占用
|
|
96
|
+
const setDragParam = (e: MouseEvent) => {
|
|
97
|
+
dragParams.x = e.x - originX
|
|
98
|
+
dragParams.y = e.y - originY
|
|
99
|
+
dragParams.offsetX = getOffsetX(dragParams.x)
|
|
100
|
+
dragParams.offsetY = getOffsetY(dragParams.y)
|
|
101
|
+
dragParams.e = e
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const handleMousemove = (e: MouseEvent) => {
|
|
105
|
+
setDragParam(e)
|
|
106
|
+
onDrag?.(dragParams)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const handleMouseup = (e: MouseEvent) => {
|
|
110
|
+
setDragParam(e)
|
|
111
|
+
offsetX = dragParams.offsetX
|
|
112
|
+
offsetY = dragParams.offsetY
|
|
113
|
+
|
|
114
|
+
onDragEnd?.(dragParams)
|
|
115
|
+
document.onselectstart = onselectstart
|
|
116
|
+
|
|
117
|
+
cleanup()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 统一的清理函数
|
|
121
|
+
const cleanup = () => {
|
|
122
|
+
document.removeEventListener('mousemove', handleMousemove)
|
|
123
|
+
document.removeEventListener('mouseup', handleMouseup)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
watch(
|
|
127
|
+
target,
|
|
128
|
+
(dom, oldDom) => {
|
|
129
|
+
oldDom?.removeEventListener('mousedown', handleMousedown)
|
|
130
|
+
if (!dom) return
|
|
131
|
+
dom.addEventListener('mousedown', handleMousedown)
|
|
132
|
+
},
|
|
133
|
+
{ immediate: true }
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
onBeforeUnmount(() => {
|
|
137
|
+
target.value?.removeEventListener('mousedown', handleMousedown)
|
|
138
|
+
cleanup()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
update(options: { offsetX?: number; offsetY?: number }) {
|
|
143
|
+
if (options.offsetX !== undefined) {
|
|
144
|
+
offsetX = options.offsetX
|
|
145
|
+
}
|
|
146
|
+
if (options.offsetY !== undefined) {
|
|
147
|
+
offsetY = options.offsetY
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|