@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,76 @@
1
+ import type { ComponentSize } from '@veltra/utils/types'
2
+ import { computed, type ComputedRef } from 'vue'
3
+
4
+ import { useConfig } from '../use-config'
5
+
6
+ /**
7
+ * 使用回滚属性,用于控制多级属性的使用优先级,如果多级属性中不存在该值,则使用全局配置中的属性,如再不存在则为undefined
8
+ * @param propsList 属性列表,最右边的属性优先级最高
9
+ * @param fallbackProps 要回滚的属性和默认值
10
+ */
11
+ export function useFallbackProps<
12
+ F extends Record<string, any>,
13
+ R extends {
14
+ [key in keyof F]: ComputedRef<F[key]>
15
+ }
16
+ >(propsList: Record<string, any>[], fallbackProps: F): R {
17
+ const { config } = useConfig()
18
+
19
+ let result = {} as R
20
+
21
+ for (const key in fallbackProps) {
22
+ if (fallbackProps.hasOwnProperty(key)) {
23
+ const defaultValue = fallbackProps[key]
24
+ const ref = computed<any>(() => {
25
+ for (let i = propsList.length - 1; i > -1; --i) {
26
+ const props = propsList[i]!
27
+
28
+ if (props[key] !== undefined) {
29
+ return props[key]
30
+ }
31
+ }
32
+
33
+ return config[key as string] ?? defaultValue
34
+ })
35
+
36
+ result[key as keyof F] = ref as R[keyof F]
37
+ }
38
+ }
39
+
40
+ return result
41
+ }
42
+
43
+ type FormFallbackProps = { size: ComponentSize; disabled: boolean; readonly: boolean }
44
+
45
+ /**
46
+ * 表单组件的回滚属性
47
+ * @param propsList props列表
48
+ * @returns
49
+ */
50
+ export function useFormFallbackProps(propsList: Record<string, any>[]): {
51
+ [key in keyof FormFallbackProps]: ComputedRef<FormFallbackProps[key]>
52
+ }
53
+
54
+ /**
55
+ * 表单组件的回滚属性
56
+ * @param propsList props列表
57
+ * @param fallbackProps 回滚属性,可以只指定部分表单属性
58
+ * @returns
59
+ */
60
+ export function useFormFallbackProps<F extends Partial<FormFallbackProps>>(
61
+ propsList: Record<string, any>[],
62
+ fallbackProps: F
63
+ ): {
64
+ [key in keyof F]: key extends keyof FormFallbackProps
65
+ ? ComputedRef<FormFallbackProps[key]>
66
+ : never
67
+ }
68
+ export function useFormFallbackProps(
69
+ propsList: Record<string, any>[],
70
+ fallbackProps?: Record<string, any>
71
+ ): any {
72
+ if (!fallbackProps) {
73
+ fallbackProps = { size: 'default', disabled: false, readonly: false }
74
+ }
75
+ return useFallbackProps(propsList, fallbackProps)
76
+ }
@@ -0,0 +1,26 @@
1
+ import { ref, type Ref } from 'vue'
2
+
3
+ /**
4
+ * 聚焦
5
+ * @param cb 回调
6
+ * @returns
7
+ */
8
+ export function useFocus(cb?: (focused: boolean) => void): {
9
+ focus: Ref<boolean>
10
+ handleBlur: () => void
11
+ handleFocus: () => void
12
+ } {
13
+ const focus = ref(false)
14
+
15
+ const handleFocus = () => {
16
+ focus.value = true
17
+ cb?.(focus.value)
18
+ }
19
+
20
+ const handleBlur = () => {
21
+ focus.value = false
22
+ cb?.(focus.value)
23
+ }
24
+
25
+ return { focus, handleBlur, handleFocus }
26
+ }
@@ -0,0 +1,31 @@
1
+ import type { FormContextInjectProps } from '@veltra/utils/types'
2
+ import { type InjectionKey, inject, provide } from 'vue'
3
+
4
+ type DIContext = {
5
+ /** 表单属性 */
6
+ formProps: FormContextInjectProps
7
+ }
8
+
9
+ const FormComponentDIKey: InjectionKey<DIContext> = Symbol('FormComponentDIKey')
10
+
11
+ /**
12
+ * 表单组件本身的组合式方法
13
+ * @param props 表单属性
14
+ * @returns
15
+ */
16
+ export function useFormComponent(props: FormContextInjectProps): void
17
+ /**
18
+ * 表单内的组件的组合式方法
19
+ * @returns 提供一个form的上下文
20
+ */
21
+ export function useFormComponent(): {
22
+ /** 是否在表单中 */
23
+ inForm: boolean
24
+ } & Partial<DIContext>
25
+ export function useFormComponent(props?: any): any {
26
+ if (props) {
27
+ return provide(FormComponentDIKey, { formProps: props })
28
+ }
29
+ const context = inject(FormComponentDIKey, undefined) || {}
30
+ return { inForm: !!context, ...context }
31
+ }
@@ -0,0 +1,50 @@
1
+ import { nextTick } from 'vue'
2
+
3
+ type Update = (fn: Function) => any
4
+ type Lock = (fn: Function) => Promise<void>
5
+
6
+ export interface Updater {
7
+ /**
8
+ * 更新
9
+ * @description 在非锁定时执行传入的函数
10
+ */
11
+ update: Update
12
+ /**
13
+ * 更新并锁定
14
+ * @description 执行传入的函数,并锁定更新操作,直到函数执行完成
15
+ */
16
+ updateAndLock: Lock
17
+ }
18
+
19
+ /**
20
+ * 数据更新锁
21
+ * @description
22
+ * 更新锁主要用于防止组件数据更新时,循环触发更新。
23
+ *
24
+ * @returns 该函数返回两个函数:
25
+ * 1. update: 更新函数,在非锁定时执行传入的函数
26
+ * 2. lock: 锁定函数,执行时会锁定更新操作,直到锁定操作结束
27
+ */
28
+ export function useUpdateLock(): Updater {
29
+ let lockedCount = 0
30
+
31
+ function update(fn: Function) {
32
+ if (lockedCount > 0) return
33
+ return fn()
34
+ }
35
+
36
+ async function updateAndLock(fn: Function) {
37
+ lockedCount++
38
+
39
+ try {
40
+ await fn()
41
+ } catch (error) {
42
+ console.error(error)
43
+ }
44
+ await nextTick()
45
+
46
+ lockedCount--
47
+ }
48
+
49
+ return { updateAndLock, update }
50
+ }
@@ -0,0 +1,96 @@
1
+ import { type Ref, ref, watch, shallowRef } from 'vue'
2
+
3
+ interface ModelOptions<Props extends Record<string, unknown>, Name extends keyof Props> {
4
+ /** 组件定义的属性 */
5
+ props: Props
6
+ /** 属性名称 */
7
+ propName?: Name
8
+ /** 事件触发函数 */
9
+ emit: (...args: any[]) => void
10
+ /** 是否为本地模式, 默认为true, 本地模式允许组件不受控来触发视图更新 */
11
+ local?: boolean | (() => boolean)
12
+ /** 默认值 */
13
+ defaultValue?: Props[Name]
14
+ /**
15
+ * 是否浅层响应
16
+ * @default false
17
+ */
18
+ shallow?: boolean
19
+ }
20
+
21
+ /**
22
+ * 返回一个基于提供的选项的响应式模型值。
23
+ * 该方法在将来可能会被替代, 目前使用是为了类型提示可用
24
+ * 如果 local 选项为true, 模型值将是响应式的,并与属性值同步。
25
+ * 如果 local 选项为 false,则模型值将是一个代理对象,具有 getter 和 setter。当值发生更改时,它会触发一个更新事件。
26
+ * @param options - 选项
27
+ * @returns - 一个模型值
28
+ */
29
+
30
+ export function useModel<
31
+ Props extends Record<string, any>,
32
+ Name extends keyof Props = 'modelValue'
33
+ >(
34
+ options: ModelOptions<Props, Name>
35
+ ): Ref<Props[Name] | undefined> | { __v_isRef: boolean; value: Props[Name] } {
36
+ const {
37
+ props,
38
+ propName = 'modelValue',
39
+ emit,
40
+ local = true,
41
+ defaultValue,
42
+ shallow = false
43
+ } = options
44
+
45
+ if (local) {
46
+ const _default = props[propName] ?? defaultValue
47
+ const r = shallow ? shallowRef : ref
48
+
49
+ // 创建一个响应式对象
50
+ const _value = r(_default)
51
+
52
+ // 监听属性的变更
53
+ watch(
54
+ () => props[propName],
55
+ (v) => {
56
+ _value.value = v
57
+ }
58
+ )
59
+
60
+ const getLocal = () => {
61
+ return typeof local === 'function' ? local() : local
62
+ }
63
+
64
+ const value = {
65
+ __v_isRef: true,
66
+ get value() {
67
+ return _value.value
68
+ },
69
+ set value(v) {
70
+ if (v !== _value.value) {
71
+ emit(`update:${propName as string}`, v)
72
+ }
73
+ if (getLocal()) {
74
+ _value.value = v
75
+ }
76
+ }
77
+ }
78
+
79
+ return value
80
+ }
81
+
82
+ // 创建一个拥有getter和setter的对象
83
+ const value = {
84
+ __v_isRef: true,
85
+
86
+ get value(): Props[Name] {
87
+ return (props[propName] ?? defaultValue) as Props[Name]
88
+ },
89
+
90
+ set value(v: Props[Name]) {
91
+ emit(`update:${propName as string}`, v)
92
+ }
93
+ }
94
+
95
+ return value
96
+ }
@@ -0,0 +1,206 @@
1
+ import {
2
+ computePosition,
3
+ flip,
4
+ shift,
5
+ arrow,
6
+ offset,
7
+ type ComputePositionReturn,
8
+ type Placement
9
+ } from '@floating-ui/dom'
10
+ import { getScrollParents, setStyles } from '@veltra/utils'
11
+ import { isRef, onBeforeUnmount, watch, type Ref, type ShallowRef } from 'vue'
12
+
13
+ type TipDirection = 'top' | 'bottom' | 'left' | 'right'
14
+ type TipAlign = 'center' | 'start' | 'end'
15
+
16
+ interface Options {
17
+ /** 触发元素 */
18
+ triggerRef: ShallowRef<HTMLElement | undefined>
19
+ /** 内容元素 */
20
+ contentRef: ShallowRef<HTMLElement | undefined>
21
+ /**
22
+ * 箭头元素,如果存在,则会在弹框的箭头位置显示箭头
23
+ */
24
+ arrowRef?: ShallowRef<HTMLElement | undefined>
25
+ /** 方向 */
26
+ direction?: ShallowRef<TipDirection> | TipDirection
27
+ /** 对齐方式 */
28
+ alignment?: ShallowRef<TipAlign> | TipAlign
29
+ /**
30
+ * 箭头大小
31
+ * @default 10
32
+ */
33
+ arrowSize?: number
34
+ /**
35
+ * 触发器元素位置变更时回调,
36
+ * 一般用于在触发器元素位置变更时更新弹框位置
37
+ */
38
+ onTriggerPositionChange?: () => void
39
+ /** 更新元素前回调 */
40
+ onBeforeUpdate?: (triggerEl: HTMLElement, contentEl: HTMLElement) => void
41
+ /** 更新元素后回调 */
42
+ onAfterUpdate?: (position: ComputePositionReturn) => void
43
+ /** 弹框弹出时回调 */
44
+ onPop?: (position: ComputePositionReturn) => void
45
+ }
46
+
47
+ interface PopResult {
48
+ /**
49
+ * 更新弹框位置
50
+ */
51
+ update: () => Promise<void>
52
+ /** 浮框容器id */
53
+ popperContainerId: string
54
+ }
55
+
56
+ let popperContainer: HTMLElement | null = null
57
+
58
+ const popperContainerId = 'pop-container'
59
+
60
+ /**
61
+ * 浮框组合式函数
62
+ * @param options 选项
63
+ * @returns
64
+ */
65
+ export function usePop(options: Options): PopResult {
66
+ if (!popperContainer) {
67
+ popperContainer = document.createElement('div')
68
+ popperContainer.id = popperContainerId
69
+ document.body.appendChild(popperContainer)
70
+ }
71
+
72
+ const {
73
+ triggerRef,
74
+ contentRef,
75
+ arrowRef,
76
+ arrowSize = 10,
77
+ onTriggerPositionChange,
78
+ onAfterUpdate,
79
+ onBeforeUpdate,
80
+ direction,
81
+ alignment,
82
+ onPop
83
+ } = options
84
+
85
+ /** 箭头位置 */
86
+ const arrowPlacementDict = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }
87
+
88
+ function getMaybeRefValue<T>(value?: Ref<T> | T) {
89
+ return isRef(value) ? value.value : value
90
+ }
91
+
92
+ /**
93
+ * 更新浮框位置
94
+ * @param callbackOnPop 是否在弹出时回调
95
+ */
96
+ async function update(callbackOnPop = false) {
97
+ const triggerEl = triggerRef.value
98
+ const contentEl = contentRef.value
99
+
100
+ if (!(triggerEl instanceof HTMLElement) || !(contentEl instanceof HTMLElement)) {
101
+ return
102
+ }
103
+
104
+ onBeforeUpdate?.(triggerEl, contentEl)
105
+
106
+ // 计算位置 ↓↓↓
107
+ const middleware = [offset(arrowRef?.value ? arrowSize : 6), flip(), shift()]
108
+
109
+ if (arrowRef?.value) {
110
+ middleware.push(arrow({ element: arrowRef.value }))
111
+ }
112
+
113
+ const _direction = getMaybeRefValue(direction) ?? 'top'
114
+ const _alignment = getMaybeRefValue(alignment) ?? 'center'
115
+
116
+ const position = await computePosition(triggerEl, contentEl, {
117
+ middleware,
118
+
119
+ placement: `${_direction}${_alignment === 'center' ? '' : `-${_alignment}`}` as Placement
120
+ })
121
+
122
+ const { x, y, middlewareData, placement } = position
123
+
124
+ setStyles(contentEl, { left: `${x}px`, top: `${y}px` })
125
+ callbackOnPop && onPop?.(position)
126
+ onAfterUpdate?.(position)
127
+
128
+ // 设置箭头位置 ↓↓↓
129
+ if (middlewareData.arrow) {
130
+ const { x: arrowX, y: arrowY } = middlewareData.arrow
131
+
132
+ const arrowPlacement =
133
+ arrowPlacementDict[placement.split('-')[0]! as keyof typeof arrowPlacementDict]
134
+ const size = `${arrowSize}px`
135
+ // 箭头半径
136
+ const arrowRadius = arrowSize / 2
137
+
138
+ setStyles(arrowRef!.value!, {
139
+ width: size,
140
+ height: size,
141
+ left: arrowX && `${arrowX - arrowRadius}px`,
142
+ top: arrowY && `${arrowY - arrowRadius}px`,
143
+ [arrowPlacement]: `-${arrowRadius}px`
144
+ })
145
+ }
146
+ // 设置箭头位置 ↑↑↑
147
+ }
148
+
149
+ let scrollParents: HTMLElement[] = []
150
+
151
+ /** 为触发器元素的祖先元素添加滚动事件 */
152
+ function addScrollEvents() {
153
+ if (!triggerRef.value) return
154
+ if (onTriggerPositionChange) {
155
+ scrollParents = getScrollParents(triggerRef.value)
156
+ scrollParents.forEach((el) => {
157
+ el.addEventListener('scroll', onTriggerPositionChange)
158
+ })
159
+ }
160
+ }
161
+
162
+ /** 移除触发器元素祖先元素的滚动事件 */
163
+ function removeScrollEvents() {
164
+ if (onTriggerPositionChange) {
165
+ scrollParents.forEach((el) => {
166
+ el.removeEventListener('scroll', onTriggerPositionChange)
167
+ })
168
+ }
169
+
170
+ scrollParents = []
171
+ }
172
+
173
+ function addResizeEvents() {
174
+ onTriggerPositionChange && window.addEventListener('resize', onTriggerPositionChange)
175
+ }
176
+
177
+ function removeResizeEvents() {
178
+ onTriggerPositionChange && window.removeEventListener('resize', onTriggerPositionChange)
179
+ }
180
+
181
+ watch(
182
+ [contentRef, () => getMaybeRefValue(direction), () => getMaybeRefValue(alignment)],
183
+ ([content]) => {
184
+ if (content) {
185
+ update(true)
186
+ addScrollEvents()
187
+ addResizeEvents()
188
+ return
189
+ }
190
+ removeScrollEvents()
191
+ removeResizeEvents()
192
+ }
193
+ )
194
+
195
+ onBeforeUnmount(() => {
196
+ removeScrollEvents()
197
+ removeResizeEvents()
198
+ })
199
+
200
+ return {
201
+ /** 更新浮框位置 */
202
+ update,
203
+ /** 浮框容器id */
204
+ popperContainerId
205
+ }
206
+ }
@@ -0,0 +1,53 @@
1
+ import { computed, reactive } from 'vue'
2
+
3
+ import { useResizeObserver, type RefElement } from '../use-resize-observer'
4
+
5
+ interface ElementSize {
6
+ width: number
7
+ height: number
8
+ }
9
+
10
+ /**
11
+ * 响应式尺寸
12
+ * @param targets 目标元素
13
+ * @returns 目标元素的宽高
14
+ */
15
+ export function useReactiveSize(target: RefElement): ElementSize
16
+ export function useReactiveSize(targets: RefElement[]): ElementSize[]
17
+ export function useReactiveSize(targets: RefElement | RefElement[]): ElementSize | ElementSize[] {
18
+ const sizes = Array.isArray(targets)
19
+ ? targets.map(() => {
20
+ return reactive({ width: 0, height: 0 })
21
+ })
22
+ : reactive({ width: 0, height: 0 })
23
+
24
+ const sizesMap = Array.isArray(targets)
25
+ ? computed(() => {
26
+ const entries = targets
27
+ .map((target, index) => {
28
+ return [target.value!, (sizes as ElementSize[])[index]!]
29
+ })
30
+ .filter(([target]) => target) as [HTMLElement, ElementSize][]
31
+
32
+ return new WeakMap(entries)
33
+ })
34
+ : computed(() => {
35
+ return new WeakMap(targets.value ? [[targets.value, sizes as ElementSize]] : [])
36
+ })
37
+
38
+ useResizeObserver({
39
+ targets,
40
+ onResize(entries) {
41
+ entries.forEach((entry) => {
42
+ const borderBoxSize = entry.borderBoxSize[0]!
43
+ const size = sizesMap.value.get(entry.target as HTMLElement)
44
+ if (size && borderBoxSize) {
45
+ size.width = borderBoxSize.inlineSize
46
+ size.height = borderBoxSize.blockSize
47
+ }
48
+ })
49
+ }
50
+ })
51
+
52
+ return sizes
53
+ }
@@ -0,0 +1,148 @@
1
+ import { type Ref, type ShallowRef, onBeforeUnmount, watch } from 'vue'
2
+
3
+ export type RefElement =
4
+ | ShallowRef<HTMLElement | undefined | null>
5
+ | Ref<HTMLElement | undefined | null>
6
+
7
+ interface ResizeObserverOptions {
8
+ /** 目标节点 */
9
+ targets: RefElement | RefElement[]
10
+ /** resize事件 */
11
+ onResize: ResizeObserverCallback
12
+ /** 指定观察条件 */
13
+ when?: () => boolean
14
+ }
15
+
16
+ /** 监听器 */
17
+ export type ResizeObserverReturn = {
18
+ /** 终止监听 */
19
+ disconnect: () => void
20
+ }
21
+
22
+ /**
23
+ * 取消监听
24
+ * @param targets 目标节点
25
+ * @param observer 观察器
26
+ */
27
+ function unobserve(targets: RefElement | RefElement[], observer?: ResizeObserver) {
28
+ if (Array.isArray(targets)) {
29
+ return targets.forEach((target) => unobserve(target, observer))
30
+ }
31
+ if (!targets.value || !observer) return
32
+ observer.unobserve(targets.value)
33
+ observer.disconnect()
34
+ }
35
+
36
+ /**
37
+ * 监听目标尺寸变化
38
+ * @param options 选项
39
+ */
40
+ export function useResizeObserver(options: ResizeObserverOptions): ResizeObserverReturn {
41
+ const { targets, onResize } = options
42
+
43
+ let observer: ResizeObserver | undefined
44
+
45
+ if (Array.isArray(targets)) {
46
+ watch(
47
+ targets,
48
+ (val, oldVal) => {
49
+ // 清除旧的观察
50
+ oldVal.forEach((target) => {
51
+ target && observer?.unobserve(target)
52
+ })
53
+
54
+ if (!observer && !!val.length) {
55
+ observer = new ResizeObserver(onResize)
56
+ }
57
+
58
+ val.forEach((target) => {
59
+ target && observer?.observe(target)
60
+ })
61
+ },
62
+ { immediate: true }
63
+ )
64
+ } else {
65
+ watch(
66
+ targets,
67
+ (val, oldVal) => {
68
+ oldVal && observer?.unobserve(oldVal)
69
+ if (!observer && val) {
70
+ observer = new ResizeObserver(onResize)
71
+ }
72
+ val && observer?.observe(val)
73
+ },
74
+ { immediate: true }
75
+ )
76
+ }
77
+
78
+ onBeforeUnmount(() => {
79
+ unobserve(targets, observer)
80
+ observer = undefined
81
+ })
82
+
83
+ return {
84
+ disconnect() {
85
+ unobserve(targets, observer)
86
+ observer = undefined
87
+ }
88
+ }
89
+ }
90
+
91
+ /**
92
+ * 监听元素尺寸变化
93
+ */
94
+ export function useObserverCallback(): {
95
+ observeEl: <El extends HTMLElement>(
96
+ el: El,
97
+ cb: (entry: Omit<ResizeObserverEntry, 'target'> & { target: El }) => void
98
+ ) => void
99
+ unobserveEl: (el: HTMLElement) => void
100
+ } {
101
+ const observerElMap = new Map<HTMLElement, Function>()
102
+
103
+ const observer = new ResizeObserver((entries) => {
104
+ entries.forEach((entry) => {
105
+ const target = entry.target as HTMLElement
106
+ if (!target.dataset.ob) {
107
+ target.dataset.ob = 'true'
108
+ return
109
+ }
110
+ const fn = observerElMap.get(target)
111
+
112
+ fn?.(entry)
113
+ })
114
+ })
115
+
116
+ /**
117
+ * 监听元素尺寸
118
+ * @param el 元素
119
+ * @param cb 回调
120
+ */
121
+ function observeEl<El extends HTMLElement>(
122
+ el: El,
123
+ cb: (entry: Omit<ResizeObserverEntry, 'target'> & { target: El }) => void
124
+ ) {
125
+ observer.observe(el)
126
+ observerElMap.set(el, cb)
127
+ }
128
+
129
+ /**
130
+ * 取消监听元素尺寸
131
+ * @param el 元素
132
+ */
133
+ function unobserveEl(el: HTMLElement) {
134
+ observer.unobserve(el)
135
+ delete el.dataset.ob
136
+ observerElMap.delete(el)
137
+ }
138
+
139
+ onBeforeUnmount(() => {
140
+ observerElMap.forEach((_, el) => {
141
+ observer.unobserve(el)
142
+ })
143
+ observerElMap.clear()
144
+ observer.disconnect()
145
+ })
146
+
147
+ return { observeEl, unobserveEl }
148
+ }
@@ -0,0 +1,22 @@
1
+ import type { Returned, CssTransitionOptions, StyleTransitionOptions } from './type'
2
+ import { useCssTransition } from './use-css-transition'
3
+ import { useStyleTransition } from './use-style-transition'
4
+
5
+ /**
6
+ * 使用CSS类过渡
7
+ * @param type 过渡类型
8
+ * @param options 过渡选项
9
+ */
10
+ export function useTransition(type: 'css', options: CssTransitionOptions): Returned
11
+ /**
12
+ * 使用style样式过渡
13
+ * @param type 过渡类型
14
+ * @param options 过渡选项
15
+ */
16
+ export function useTransition(type: 'style', options: StyleTransitionOptions): Returned
17
+ export function useTransition(type: string, options: any): Returned {
18
+ if (type === 'css') {
19
+ return useCssTransition(options)
20
+ }
21
+ return useStyleTransition(options)
22
+ }