@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.
Files changed (67) hide show
  1. package/dist/index.d.ts +14 -0
  2. package/dist/index.js +14 -0
  3. package/dist/use-component-props/index.d.ts +12 -0
  4. package/dist/use-component-props/index.js +52 -0
  5. package/dist/use-component-props/index.js.map +1 -0
  6. package/dist/use-config/index.d.ts +25 -0
  7. package/dist/use-config/index.js +51 -0
  8. package/dist/use-config/index.js.map +1 -0
  9. package/dist/use-drag/index.d.ts +47 -0
  10. package/dist/use-drag/index.js +87 -0
  11. package/dist/use-drag/index.js.map +1 -0
  12. package/dist/use-fallback-props/index.d.ts +31 -0
  13. package/dist/use-fallback-props/index.js +35 -0
  14. package/dist/use-fallback-props/index.js.map +1 -0
  15. package/dist/use-focus/index.d.ts +16 -0
  16. package/dist/use-focus/index.js +27 -0
  17. package/dist/use-focus/index.js.map +1 -0
  18. package/dist/use-form-component/index.d.ts +22 -0
  19. package/dist/use-form-component/index.js +15 -0
  20. package/dist/use-form-component/index.js.map +1 -0
  21. package/dist/use-lock/index.d.ts +28 -0
  22. package/dist/use-lock/index.js +36 -0
  23. package/dist/use-lock/index.js.map +1 -0
  24. package/dist/use-model/index.d.ts +35 -0
  25. package/dist/use-model/index.js +46 -0
  26. package/dist/use-model/index.js.map +1 -0
  27. package/dist/use-pop/index.d.ts +53 -0
  28. package/dist/use-pop/index.js +121 -0
  29. package/dist/use-pop/index.js.map +1 -0
  30. package/dist/use-reactive-size/index.d.ts +17 -0
  31. package/dist/use-reactive-size/index.js +40 -0
  32. package/dist/use-reactive-size/index.js.map +1 -0
  33. package/dist/use-resize-observer/index.d.ts +33 -0
  34. package/dist/use-resize-observer/index.js +92 -0
  35. package/dist/use-resize-observer/index.js.map +1 -0
  36. package/dist/use-transition/index.d.ts +18 -0
  37. package/dist/use-transition/index.js +11 -0
  38. package/dist/use-transition/index.js.map +1 -0
  39. package/dist/use-transition/type.d.ts +47 -0
  40. package/dist/use-transition/use-css-transition.js +129 -0
  41. package/dist/use-transition/use-css-transition.js.map +1 -0
  42. package/dist/use-transition/use-style-transition.js +127 -0
  43. package/dist/use-transition/use-style-transition.js.map +1 -0
  44. package/dist/use-transition/utils.js +41 -0
  45. package/dist/use-transition/utils.js.map +1 -0
  46. package/dist/use-virtual/index.d.ts +30 -0
  47. package/dist/use-virtual/index.js +67 -0
  48. package/dist/use-virtual/index.js.map +1 -0
  49. package/package.json +32 -0
  50. package/src/index.ts +25 -0
  51. package/src/use-component-props/index.ts +63 -0
  52. package/src/use-config/index.ts +77 -0
  53. package/src/use-drag/index.ts +151 -0
  54. package/src/use-fallback-props/index.ts +76 -0
  55. package/src/use-focus/index.ts +26 -0
  56. package/src/use-form-component/index.ts +31 -0
  57. package/src/use-lock/index.ts +50 -0
  58. package/src/use-model/index.ts +96 -0
  59. package/src/use-pop/index.ts +206 -0
  60. package/src/use-reactive-size/index.ts +53 -0
  61. package/src/use-resize-observer/index.ts +148 -0
  62. package/src/use-transition/index.ts +22 -0
  63. package/src/use-transition/type.ts +50 -0
  64. package/src/use-transition/use-css-transition.ts +186 -0
  65. package/src/use-transition/use-style-transition.ts +182 -0
  66. package/src/use-transition/utils.ts +56 -0
  67. package/src/use-virtual/index.ts +130 -0
@@ -0,0 +1,50 @@
1
+ import type { CSSProperties, Ref, ShallowRef } from 'vue'
2
+
3
+ export interface TransitionBase {
4
+ /** 被应用的目标元素 */
5
+ target: ShallowRef<HTMLElement | undefined> | HTMLElement
6
+ /** 进入动画结束回调 */
7
+ afterEnter?: () => void
8
+ /** 进入动画被取消回调 */
9
+ enterCanceled?: () => void
10
+ /** 离开动画结束回调 */
11
+ afterLeave?: () => void
12
+ /** 离开动画被取消回调 */
13
+ leaveCanceled?: () => void
14
+ }
15
+
16
+ export interface CssTransitionOptions extends TransitionBase {
17
+ /** 类的名称, 会生成 `${name}-enter-${'to' | 'active' | 'from'}`, `${name}-leave-${'to' | 'active' | 'from'}这几种类` */
18
+ name: ShallowRef<string> | string | Ref<string>
19
+ /** 保留进入类 */
20
+ keepEnterTo?: boolean
21
+ }
22
+
23
+ export interface StyleTransitionOptions extends TransitionBase {
24
+ // /** 动画进入前的样式 */
25
+ // enterFrom?: CSSProperties
26
+ // /** 动画离开后的样式 */
27
+ // leaveTo?: CSSProperties
28
+ /** 进入后的样式 */
29
+ enterTo: CSSProperties
30
+ /** 进入过渡时的样式 */
31
+ enterActive: CSSProperties
32
+ /** 离开过渡时的样式 */
33
+ leaveActive: CSSProperties
34
+ }
35
+
36
+ export interface Returned {
37
+ /**
38
+ * 切换进入/离开动画
39
+ * @param active 是否激活
40
+ */
41
+ toggle(active: boolean | ((active: boolean) => boolean)): void
42
+ /**
43
+ * 标记进入动画, toggle(true)的别名
44
+ */
45
+ enter(): void
46
+ /**
47
+ * 标记离开动画, toggle(false)的别名
48
+ */
49
+ leave(): void
50
+ }
@@ -0,0 +1,186 @@
1
+ import { createToggle } from '@veltra/utils'
2
+ import { isRef, watch, onBeforeUnmount, computed } from 'vue'
3
+
4
+ import type { Returned, CssTransitionOptions } from './type'
5
+
6
+ const increaseTransitionCount = (el: HTMLElement & { _count?: number }) => {
7
+ el._count = (el._count ?? 0) + 1
8
+ }
9
+
10
+ const decreaseTransitionCount = (el: HTMLElement & { _count?: number }) => {
11
+ el._count = (el._count ?? 0) - 1
12
+ if (el._count <= 0) {
13
+ delete el._count
14
+ }
15
+ }
16
+
17
+ /**
18
+ * 使用css过渡
19
+ * @param options 过渡选项
20
+ */
21
+ export function useCssTransition(options: CssTransitionOptions): Returned {
22
+ const {
23
+ name,
24
+ target,
25
+ afterEnter,
26
+ afterLeave,
27
+ enterCanceled,
28
+ leaveCanceled,
29
+ keepEnterTo = false
30
+ } = options
31
+
32
+ const getDom = (): (HTMLElement & { _count?: number }) | undefined =>
33
+ isRef(target) ? target.value : target
34
+
35
+ const classes = computed(() => {
36
+ const _name = typeof name === 'string' ? name : name.value
37
+ return {
38
+ /** 进入前的类 */
39
+ enterFrom: `${_name}-enter-from`,
40
+ /** 进入后最终的类 */
41
+ enterTo: `${_name}-enter-to`,
42
+ /** 【进入动画】持续时的类 */
43
+ enterActive: `${_name}-enter-active`,
44
+ /** 离开前的类 */
45
+ leaveFrom: `${_name}-leave-from`,
46
+ /** 离开类 */
47
+ leaveTo: `${_name}-leave-to`,
48
+ /** 【离开动画】持续时的类 */
49
+ leaveActive: `${_name}-leave-active`
50
+ }
51
+ })
52
+
53
+ /** 开始进入动画 */
54
+ const startTransitionIn = () => {
55
+ const { enterActive, enterTo, enterFrom } = classes.value
56
+ const dom = getDom()
57
+
58
+ dom?.classList.add(enterFrom)
59
+
60
+ requestAnimationFrame(() => {
61
+ dom?.classList.add(enterActive)
62
+ requestAnimationFrame(() => {
63
+ dom?.classList.remove(enterFrom)
64
+ dom?.classList.add(enterTo)
65
+ })
66
+ })
67
+ }
68
+
69
+ /** 开始离开动画 */
70
+ const startTransitionOut = () => {
71
+ const { leaveTo, leaveActive, leaveFrom, enterTo } = classes.value
72
+ const dom = getDom()
73
+
74
+ // 标记动画进入离开状态
75
+ if (keepEnterTo) {
76
+ dom?.classList.remove(enterTo)
77
+ }
78
+ dom?.classList.add(leaveFrom, leaveActive)
79
+
80
+ requestAnimationFrame(() => {
81
+ dom?.classList.add(leaveActive)
82
+ requestAnimationFrame(() => {
83
+ dom?.classList.remove(leaveFrom)
84
+ dom?.classList.add(leaveTo)
85
+ })
86
+ })
87
+ }
88
+
89
+ const [active, toggle] = createToggle(false, (_active) => {
90
+ _active ? startTransitionIn() : startTransitionOut()
91
+ })
92
+
93
+ const transitionEndHandler = (e: TransitionEvent) => {
94
+ e.stopPropagation()
95
+
96
+ const { leaveActive, enterActive, enterTo, leaveTo } = classes.value
97
+ const dom = getDom()
98
+
99
+ if (dom !== e.target) return
100
+
101
+ decreaseTransitionCount(dom)
102
+
103
+ if (dom._count) return
104
+
105
+ // 激活状态,移除enter-active类
106
+ if (active.value) {
107
+ if (keepEnterTo) {
108
+ dom?.classList.remove(enterActive)
109
+ } else {
110
+ dom?.classList.remove(enterActive, enterTo)
111
+ }
112
+ afterEnter?.()
113
+ } else {
114
+ dom?.classList.remove(leaveActive, leaveTo)
115
+ afterLeave?.()
116
+ }
117
+ }
118
+
119
+ const transitionRunHandler = (e: TransitionEvent) => {
120
+ e.stopPropagation()
121
+ const dom = getDom()
122
+ if (dom !== e.target) return
123
+ increaseTransitionCount(dom)
124
+ }
125
+
126
+ const transitionCancelHandler = (e: TransitionEvent) => {
127
+ e.stopPropagation()
128
+ const dom = getDom()
129
+
130
+ if (dom !== e.target) return
131
+ decreaseTransitionCount(dom)
132
+
133
+ if (dom._count) return
134
+
135
+ const { leaveActive, enterActive } = classes.value
136
+
137
+ // 激活状态,移除enter-active类
138
+ if (active.value) {
139
+ dom?.classList.remove(enterActive)
140
+ enterCanceled?.()
141
+ } else {
142
+ dom?.classList.remove(leaveActive)
143
+ leaveCanceled?.()
144
+ }
145
+ }
146
+
147
+ /** 添加事件 */
148
+ const addEvent = (el?: HTMLElement) => {
149
+ el?.addEventListener('transitioncancel', transitionCancelHandler)
150
+ el?.addEventListener('transitionend', transitionEndHandler)
151
+ el?.addEventListener('transitionrun', transitionRunHandler)
152
+ }
153
+
154
+ /** 移除事件 */
155
+ const removeEvent = (el?: HTMLElement) => {
156
+ el?.removeEventListener('transitioncancel', transitionCancelHandler)
157
+ el?.removeEventListener('transitionend', transitionEndHandler)
158
+ el?.removeEventListener('transitionrun', transitionRunHandler)
159
+ }
160
+
161
+ if (isRef(target)) {
162
+ watch(target, (_target, oldTarget) => {
163
+ if (oldTarget) {
164
+ removeEvent(oldTarget)
165
+ }
166
+ _target && addEvent(_target)
167
+ })
168
+ } else if (target) {
169
+ addEvent(target)
170
+ }
171
+
172
+ onBeforeUnmount(() => {
173
+ const dom = getDom()
174
+ removeEvent(dom)
175
+ })
176
+
177
+ return {
178
+ toggle,
179
+ enter() {
180
+ toggle(true)
181
+ },
182
+ leave() {
183
+ toggle(false)
184
+ }
185
+ }
186
+ }
@@ -0,0 +1,182 @@
1
+ import { createToggle, nextFrame, setStyles } from '@veltra/utils'
2
+ import { isRef, watch, type CSSProperties } from 'vue'
3
+
4
+ import type { Returned, StyleTransitionOptions } from './type'
5
+ import { watchTransition } from './utils'
6
+
7
+ export function useStyleTransition(options: StyleTransitionOptions): Returned {
8
+ const {
9
+ // enterFrom,
10
+ // leaveTo,
11
+ enterTo,
12
+ enterActive,
13
+ leaveActive,
14
+ target,
15
+ afterEnter,
16
+ afterLeave,
17
+ enterCanceled,
18
+ leaveCanceled
19
+ } = options
20
+
21
+ const getDom = (): HTMLElement | undefined => (isRef(target) ? target.value : target)
22
+
23
+ /** 进入动画前的初始状态 */
24
+ const transitionOriginStyle: CSSProperties = {}
25
+
26
+ /** 获取过渡样式的初始样式 */
27
+ const getOriginStyles = (styles: CSSProperties) => {
28
+ return Object.fromEntries(
29
+ Object.keys(styles).map((key) => {
30
+ return [key, transitionOriginStyle[key as keyof CSSProperties]]
31
+ })
32
+ )
33
+ }
34
+ // 监听dom并获取dom在进入动画之前的样式
35
+ // ...Object.keys(enterFrom ?? {}),
36
+ // ...Object.keys(leaveTo ?? {}),
37
+ watch(
38
+ () => getDom(),
39
+ (dom) => {
40
+ if (dom) {
41
+ const map = dom.attributeStyleMap
42
+ ~[...Object.keys(enterTo), ...Object.keys(enterActive)].forEach((key) => {
43
+ transitionOriginStyle[key] = map.get(key)
44
+ })
45
+ } else {
46
+ Object.keys(transitionOriginStyle).forEach((key) => {
47
+ delete transitionOriginStyle[key as keyof CSSProperties]
48
+ })
49
+ }
50
+ },
51
+ { immediate: true }
52
+ )
53
+
54
+ /**
55
+ * 添加过渡进入时并持续时的样式
56
+ * @param dom 元素
57
+ */
58
+ const addEnterActive = (dom: HTMLElement) => {
59
+ setStyles(dom, enterActive)
60
+ }
61
+
62
+ /**
63
+ * 移除过渡进入时并持续时的样式
64
+ * @param dom 元素
65
+ */
66
+ const removeEnterActive = (dom: HTMLElement) => {
67
+ setStyles(dom, getOriginStyles(enterActive))
68
+ }
69
+
70
+ /**
71
+ * 添加过渡离开并持续时的样式
72
+ * @param dom 元素
73
+ */
74
+ const addLeaveActive = (dom: HTMLElement) => {
75
+ setStyles(dom, leaveActive)
76
+ }
77
+
78
+ /**
79
+ * 移除过渡离开并持续时的样式
80
+ * @param dom 元素
81
+ */
82
+ const removeLeaveActive = (dom: HTMLElement) => {
83
+ setStyles(dom, getOriginStyles(leaveActive))
84
+ }
85
+
86
+ /**
87
+ * 添加过渡目标样式
88
+ * @param dom 元素
89
+ */
90
+ // const addEnterFromStyle = (dom: HTMLElement) => {
91
+ // enterFrom && setStyles(dom, enterFrom)
92
+ // }
93
+
94
+ /**
95
+ * 移除过渡目标样式
96
+ * @param dom 元素
97
+ */
98
+ // const removeEnterFromStyle = (dom: HTMLElement) => {
99
+ // if (!enterFrom) return
100
+
101
+ // const canRemovedStyles = {}
102
+
103
+ // for (const key in enterFrom) {
104
+ // if (key in enterTo) continue
105
+ // canRemovedStyles[key] = enterFrom[key]
106
+ // }
107
+
108
+ // setStyles(dom, getOriginStyles(canRemovedStyles))
109
+ // }
110
+ /**
111
+ * 添加过渡目标样式
112
+ * @param dom 元素
113
+ */
114
+ const addEnterToStyle = (dom: HTMLElement) => {
115
+ setStyles(dom, enterTo)
116
+ }
117
+
118
+ /**
119
+ * 移除过渡目标样式
120
+ * @param dom 元素
121
+ */
122
+ const removeEnterToStyle = (dom: HTMLElement) => {
123
+ setStyles(dom, getOriginStyles(enterTo))
124
+ }
125
+
126
+ /** 开始进入动画 */
127
+ const startEnter = () => {
128
+ const dom = getDom()
129
+ if (!dom) return
130
+ addEnterActive(dom)
131
+ // 在下一帧插入动画运动目标状态
132
+ nextFrame(() => {
133
+ addEnterToStyle(dom)
134
+ })
135
+ }
136
+
137
+ /** 开始离开动画 */
138
+ const startLeave = () => {
139
+ const dom = getDom()
140
+ if (!dom) return
141
+
142
+ // 标记动画进入离开状态
143
+ addLeaveActive(dom)
144
+
145
+ // 在下一帧移除动画运动目标状态恢复原状或者应用新的状态
146
+ nextFrame(() => {
147
+ removeEnterToStyle(dom)
148
+ })
149
+ }
150
+
151
+ const [active, toggle] = createToggle(false, (active) => {
152
+ active ? startEnter() : startLeave()
153
+ })
154
+
155
+ watchTransition(getDom, {
156
+ styleKeys: Object.keys(enterTo),
157
+ onCancel(el) {
158
+ // 激活状态,移除enter-active类
159
+ if (active.value) {
160
+ removeEnterActive(el)
161
+ enterCanceled?.()
162
+ } else {
163
+ removeLeaveActive(el)
164
+ leaveCanceled?.()
165
+ }
166
+ },
167
+
168
+ onEnd(el) {
169
+ if (active.value) {
170
+ removeEnterActive(el)
171
+ // removeEnterFromStyle(el)
172
+ afterEnter?.()
173
+ } else {
174
+ removeLeaveActive(el)
175
+
176
+ afterLeave?.()
177
+ }
178
+ }
179
+ })
180
+
181
+ return { toggle, enter: () => toggle(true), leave: () => toggle(false) }
182
+ }
@@ -0,0 +1,56 @@
1
+ import { watch, onBeforeUnmount } from 'vue'
2
+
3
+ /**
4
+ * 监听过渡
5
+ * @param domGetter 元素获取函数
6
+ * @param config 配置
7
+ * @returns
8
+ */
9
+ export function watchTransition(
10
+ domGetter: () => HTMLElement | undefined,
11
+ config: {
12
+ styleKeys: string[]
13
+ onEnd: (dom: HTMLElement) => void
14
+ onCancel: (dom: HTMLElement) => void
15
+ }
16
+ ): void {
17
+ const runCallback = (e: TransitionEvent, cb: (el: HTMLElement) => void) => {
18
+ e.stopPropagation()
19
+ if (e.target !== domGetter() || !config.styleKeys.includes(e.propertyName)) {
20
+ return
21
+ }
22
+
23
+ cb(e.target as HTMLElement)
24
+ }
25
+
26
+ const transitionEndHandler = (e: TransitionEvent) => {
27
+ if (!domGetter()) return
28
+ runCallback(e, config.onEnd)
29
+ }
30
+ const transitionCancelHandler = (e: TransitionEvent) => {
31
+ if (!domGetter()) return
32
+ runCallback(e, config.onCancel)
33
+ }
34
+ const addEvent = (el: HTMLElement) => {
35
+ el.addEventListener('transitionend', transitionEndHandler, false)
36
+ // el.addEventListener('transitioncancel', transitionCancelHandler, false)
37
+ }
38
+
39
+ const removeEvent = (el?: HTMLElement) => {
40
+ el?.removeEventListener('transitionend', transitionEndHandler)
41
+ el?.removeEventListener('transitioncancel', transitionCancelHandler)
42
+ }
43
+
44
+ watch(
45
+ domGetter,
46
+ (target, oldTarget) => {
47
+ removeEvent(oldTarget)
48
+ target && addEvent(target)
49
+ },
50
+ { immediate: true }
51
+ )
52
+
53
+ onBeforeUnmount(() => {
54
+ removeEvent(domGetter())
55
+ })
56
+ }
@@ -0,0 +1,130 @@
1
+ import {
2
+ elementScroll,
3
+ observeElementOffset,
4
+ observeElementRect,
5
+ type VirtualItem,
6
+ Virtualizer
7
+ } from '@tanstack/vue-virtual'
8
+ import {
9
+ computed,
10
+ isRef,
11
+ onScopeDispose,
12
+ shallowRef,
13
+ watch,
14
+ type ComputedRef,
15
+ type Ref,
16
+ type ShallowRef
17
+ } from 'vue'
18
+
19
+ interface Options {
20
+ /** 指定启用虚拟列表的阈值 */
21
+ virtualThreshold?: number | Ref<number | undefined>
22
+ /** 数量 */
23
+ count: Ref<number>
24
+ /** 滚动容器 */
25
+ scrollEl: ShallowRef<HTMLElement | null>
26
+ /** 估算高度(宽度) */
27
+ estimateSize?: (index: number) => number
28
+ /** 列表项之间的间距 */
29
+ gap?: number
30
+ }
31
+
32
+ type CustomVirtualItem = Omit<VirtualItem, 'key'> & { key: number | string }
33
+
34
+ export type VirtualReturned = {
35
+ /** 虚拟列表 */
36
+ virtualList: ShallowRef<CustomVirtualItem[]>
37
+ /** 总高度 */
38
+ totalHeight: ShallowRef<number>
39
+ /** 测量元素高度 */
40
+ measureElement: (el: any) => void
41
+ /** 滚动到指定索引 */
42
+ scrollTo: (index: number) => void
43
+ /** 是否启用虚拟列表 */
44
+ virtualEnabled: ComputedRef<boolean>
45
+ }
46
+
47
+ const defaultEstimateSize = () => 34
48
+
49
+ export function useVirtual(options: Options): VirtualReturned {
50
+ const { count, scrollEl, estimateSize, virtualThreshold, gap } = options
51
+
52
+ const enabled = computed(() => {
53
+ if (isRef(virtualThreshold)) {
54
+ return virtualThreshold.value ? count.value > virtualThreshold.value : true
55
+ }
56
+
57
+ return virtualThreshold ? count.value > virtualThreshold : true
58
+ })
59
+
60
+ const virtualList = shallowRef<CustomVirtualItem[]>([])
61
+
62
+ /** 总高度 */
63
+ const totalHeight = shallowRef(0)
64
+
65
+ function updateVirtualList() {
66
+ if (enabled.value) {
67
+ totalHeight.value = v.getTotalSize()
68
+ virtualList.value = v.getVirtualItems() as CustomVirtualItem[]
69
+ }
70
+ }
71
+
72
+ const virtualizerOptions = computed(() => {
73
+ return {
74
+ enabled: enabled.value,
75
+ count: count.value,
76
+ getScrollElement: () => scrollEl.value,
77
+ estimateSize: estimateSize ?? defaultEstimateSize,
78
+ overscan: 3,
79
+ gap,
80
+ observeElementRect: observeElementRect,
81
+ observeElementOffset: observeElementOffset,
82
+ scrollToFn: elementScroll,
83
+ onChange: updateVirtualList
84
+ }
85
+ })
86
+
87
+ const v = new Virtualizer(virtualizerOptions.value)
88
+
89
+ updateVirtualList()
90
+
91
+ const cleanup = v._didMount()
92
+
93
+ watch(
94
+ scrollEl,
95
+ (el) => {
96
+ el && v._willUpdate()
97
+ },
98
+ { immediate: true }
99
+ )
100
+
101
+ watch(
102
+ () => virtualizerOptions.value,
103
+ (o) => {
104
+ v.setOptions(o)
105
+
106
+ v._willUpdate()
107
+
108
+ updateVirtualList()
109
+ }
110
+ )
111
+
112
+ onScopeDispose(() => {
113
+ cleanup()
114
+ })
115
+
116
+ function scrollTo(index: number) {
117
+ v.scrollToIndex(index, { align: 'center' })
118
+ }
119
+
120
+ /** 测量元素高度 */
121
+ function measureElement(el: Element) {
122
+ if (!el) return
123
+
124
+ v.measureElement(el)
125
+
126
+ return undefined
127
+ }
128
+
129
+ return { virtualEnabled: enabled, virtualList, totalHeight, measureElement, scrollTo }
130
+ }