@v-c/notification 2.0.0-beta.1 → 2.0.0-rc.2

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 (51) hide show
  1. package/dist/Notification.d.ts +7 -234
  2. package/dist/Notification.js +199 -135
  3. package/dist/NotificationList/Content.d.ts +3 -80
  4. package/dist/NotificationList/Content.js +33 -47
  5. package/dist/NotificationList/index.d.ts +7 -128
  6. package/dist/NotificationList/index.js +139 -122
  7. package/dist/NotificationProvider.d.ts +1 -20
  8. package/dist/NotificationProvider.js +2 -13
  9. package/dist/Notifications.d.ts +10 -132
  10. package/dist/Notifications.js +123 -108
  11. package/dist/Progress.d.ts +3 -3
  12. package/dist/Progress.js +24 -11
  13. package/dist/hooks/useClosable.d.ts +6 -11
  14. package/dist/hooks/useClosable.js +7 -6
  15. package/dist/hooks/useListPosition/index.d.ts +14 -13
  16. package/dist/hooks/useListPosition/index.js +16 -23
  17. package/dist/hooks/useListPosition/useSizes.d.ts +3 -6
  18. package/dist/hooks/useListPosition/useSizes.js +12 -10
  19. package/dist/hooks/useNoticeTimer.d.ts +5 -4
  20. package/dist/hooks/useNoticeTimer.js +33 -41
  21. package/dist/hooks/useNotification.d.ts +25 -7
  22. package/dist/hooks/useNotification.js +20 -25
  23. package/dist/hooks/useStack.d.ts +9 -8
  24. package/dist/hooks/useStack.js +17 -11
  25. package/dist/index.d.ts +9 -8
  26. package/dist/index.js +2 -2
  27. package/dist/interface.d.ts +30 -0
  28. package/dist/interface.js +0 -0
  29. package/docs/context.vue +1 -1
  30. package/docs/hooks.vue +4 -4
  31. package/docs/index.less +143 -62
  32. package/docs/maxCount.vue +1 -1
  33. package/docs/showProgress.vue +2 -2
  34. package/docs/stack.vue +1 -1
  35. package/package.json +2 -3
  36. package/src/Notification.tsx +128 -165
  37. package/src/NotificationList/Content.tsx +36 -54
  38. package/src/NotificationList/index.tsx +131 -135
  39. package/src/NotificationProvider.tsx +3 -23
  40. package/src/Notifications.tsx +62 -84
  41. package/src/Progress.tsx +19 -15
  42. package/src/hooks/useClosable.ts +15 -16
  43. package/src/hooks/useListPosition/index.ts +24 -40
  44. package/src/hooks/useListPosition/useSizes.ts +16 -15
  45. package/src/hooks/useNoticeTimer.ts +34 -45
  46. package/src/hooks/useNotification.tsx +71 -43
  47. package/src/hooks/useStack.ts +20 -24
  48. package/src/index.ts +28 -13
  49. package/src/interface.ts +45 -0
  50. package/vitest.config.ts +1 -3
  51. package/tests/index.spec.tsx +0 -200
package/src/Progress.tsx CHANGED
@@ -1,23 +1,27 @@
1
- import type { CSSProperties, FunctionalComponent } from 'vue'
1
+ import type { CSSProperties } from 'vue'
2
+ import { defineComponent } from 'vue'
2
3
 
3
4
  export interface NotificationProgressProps {
4
- class?: string
5
+ className?: string
5
6
  style?: CSSProperties
6
7
  percent: number
7
8
  }
8
9
 
9
- const Progress: FunctionalComponent<NotificationProgressProps> = (props) => {
10
- return (
11
- <progress
12
- class={props.class}
13
- max="100"
14
- value={props.percent}
15
- style={props.style}
16
- />
17
- )
18
- }
19
-
20
- Progress.props = ['class', 'style', 'percent'] as any
21
- Progress.inheritAttrs = false
10
+ const Progress = defineComponent<NotificationProgressProps>(
11
+ (props) => {
12
+ return () => (
13
+ <progress
14
+ class={props.className}
15
+ max="100"
16
+ value={props.percent}
17
+ style={props.style}
18
+ />
19
+ )
20
+ },
21
+ {
22
+ name: 'NotificationProgress',
23
+ inheritAttrs: false,
24
+ },
25
+ )
22
26
 
23
27
  export default Progress
@@ -1,33 +1,30 @@
1
+ import type { AriaAttributes, ComputedRef } from 'vue'
1
2
  import type { VueNode } from '@v-c/util/dist/type'
2
- import type { ComputedRef, MaybeRef } from 'vue'
3
3
  import pickAttrs from '@v-c/util/dist/pickAttrs'
4
- import { computed, unref } from 'vue'
4
+ import { computed } from 'vue'
5
5
 
6
6
  export type ClosableConfig = {
7
7
  closeIcon?: VueNode
8
8
  disabled?: boolean
9
9
  onClose?: VoidFunction
10
- } & Record<string, any>
10
+ } & AriaAttributes & Record<`data-${string}`, unknown>
11
11
 
12
12
  export type ClosableType = boolean | ClosableConfig | null | undefined
13
13
 
14
- export interface ParsedClosableConfig {
14
+ export interface ParsedClosableConfig extends ClosableConfig {
15
15
  closeIcon: VueNode
16
16
  disabled: boolean
17
- onClose?: VoidFunction
18
- [key: string]: any
19
17
  }
20
18
 
21
19
  /**
22
- * Normalize the closable option into an enabled flag, parsed config, and aria props.
20
+ * Normalizes the closable option into a boolean flag, parsed config, and
21
+ * aria props for the close button. Mirrors rc-notification@2.0 useClosable.
23
22
  */
24
- export default function useClosable(closable: MaybeRef<ClosableType>): [
25
- ComputedRef<boolean>,
26
- ComputedRef<ParsedClosableConfig>,
27
- ComputedRef<Record<string, any>>,
28
- ] {
23
+ export default function useClosable(
24
+ closable: ComputedRef<ClosableType>,
25
+ ): [ComputedRef<boolean>, ComputedRef<ParsedClosableConfig>, ComputedRef<Record<string, unknown>>] {
29
26
  const closableObj = computed<ClosableConfig>(() => {
30
- const value = unref(closable)
27
+ const value = closable.value
31
28
  if (value === false) {
32
29
  return { closeIcon: null, disabled: true }
33
30
  }
@@ -37,8 +34,6 @@ export default function useClosable(closable: MaybeRef<ClosableType>): [
37
34
  return {}
38
35
  })
39
36
 
40
- const enabled = computed(() => !!unref(closable))
41
-
42
37
  const closableConfig = computed<ParsedClosableConfig>(() => {
43
38
  const obj = closableObj.value
44
39
  return {
@@ -50,5 +45,9 @@ export default function useClosable(closable: MaybeRef<ClosableType>): [
50
45
 
51
46
  const closableAriaProps = computed(() => pickAttrs(closableConfig.value, true))
52
47
 
53
- return [enabled, closableConfig, closableAriaProps]
48
+ return [
49
+ computed(() => !!closable.value),
50
+ closableConfig,
51
+ closableAriaProps,
52
+ ]
54
53
  }
@@ -1,85 +1,69 @@
1
- import type { ComputedRef, MaybeRef } from 'vue'
2
- import type { StackConfig } from '../useStack'
3
- import { computed, unref } from 'vue'
1
+ import type { ComputedRef, Ref } from 'vue'
2
+ import type { Key } from '../../interface'
3
+ import { computed } from 'vue'
4
4
  import useSizes from './useSizes'
5
5
 
6
- type Key = string | number | symbol
7
-
8
- export interface ConfigItem {
9
- key: Key
6
+ export interface ListPositionStackConfig {
7
+ threshold?: number
8
+ offset?: number
10
9
  }
11
10
 
12
11
  /**
13
12
  * Calculates each notification's position and the full list height.
13
+ * Mirrors rc-notification@2.0 useListPosition.
14
14
  */
15
15
  export default function useListPosition(
16
- configList: MaybeRef<readonly ConfigItem[]>,
17
- stack: MaybeRef<StackConfig | undefined>,
18
- gap: MaybeRef<number> = 0,
19
- ): [
20
- ComputedRef<Map<string, number>>,
21
- (key: string, node: HTMLElement | null) => void,
22
- ComputedRef<number>,
23
- ComputedRef<number | undefined>,
24
- ComputedRef<number | undefined>,
25
- ] {
16
+ configList: ComputedRef<{ key: Key }[]>,
17
+ stack: ComputedRef<ListPositionStackConfig | undefined>,
18
+ gap: Ref<number>,
19
+ ) {
26
20
  const [sizeMap, setNodeSize] = useSizes()
27
21
 
28
22
  const result = computed(() => {
29
- const list = unref(configList)
30
- const stackValue = unref(stack)
31
- const gapValue = unref(gap) ?? 0
32
-
33
23
  let offsetY = 0
34
24
  let nextTotalHeight = 0
35
- const stackThreshold = stackValue?.threshold ?? 0
36
- const positions = new Map<string, number>()
25
+ const stackParams = stack.value
26
+ const stackThreshold = stackParams?.threshold ?? 0
27
+ const stackOffset = stackParams?.offset ?? 0
28
+ const notificationPosition = new Map<string, number>()
37
29
  let topNoticeHeight: number | undefined
38
30
  let topNoticeWidth: number | undefined
39
31
 
40
- list
32
+ configList.value
41
33
  .slice()
42
34
  .reverse()
43
35
  .forEach((config, index) => {
36
+ // Walk from newest to oldest so each notice can be positioned after the ones below it.
44
37
  const key = String(config.key)
45
38
  const height = sizeMap.value[key]?.height ?? 0
46
- const y
47
- = stackValue && index > 0
48
- ? offsetY + (stackValue.offset ?? 0) - height
49
- : offsetY
39
+ const y = stackParams && index > 0 ? offsetY + stackOffset - height : offsetY
50
40
 
51
- positions.set(key, y)
41
+ notificationPosition.set(key, y)
52
42
 
53
43
  if (index === 0) {
54
44
  topNoticeHeight = height
55
45
  topNoticeWidth = sizeMap.value[key]?.width ?? 0
56
46
  }
57
47
 
58
- if (!stackValue || index < stackThreshold) {
48
+ if (!stackParams || index < stackThreshold) {
59
49
  nextTotalHeight = Math.max(nextTotalHeight, y + height)
60
50
  }
61
51
 
62
- if (stackValue) {
52
+ if (stackParams) {
63
53
  offsetY = y + height
64
54
  }
65
55
  else {
66
- offsetY += height + gapValue
56
+ offsetY += height + gap.value
67
57
  }
68
58
  })
69
59
 
70
60
  return {
71
- positions,
61
+ notificationPosition,
72
62
  totalHeight: nextTotalHeight,
73
63
  topNoticeHeight,
74
64
  topNoticeWidth,
75
65
  }
76
66
  })
77
67
 
78
- return [
79
- computed(() => result.value.positions),
80
- setNodeSize,
81
- computed(() => result.value.totalHeight),
82
- computed(() => result.value.topNoticeHeight),
83
- computed(() => result.value.topNoticeWidth),
84
- ]
68
+ return [result, setNodeSize] as const
85
69
  }
@@ -1,5 +1,4 @@
1
- import type { Ref } from 'vue'
2
- import { ref } from 'vue'
1
+ import { shallowRef } from 'vue'
3
2
 
4
3
  export interface NodeSize {
5
4
  width: number
@@ -9,34 +8,36 @@ export interface NodeSize {
9
8
  export type NodeSizeMap = Record<string, NodeSize>
10
9
 
11
10
  /**
12
- * Track measured node sizes by key. Returns the size map ref and a setter callback.
11
+ * Stores measured node sizes by key and exposes a callback to update them.
12
+ * Mirrors rc-notification@2.0 useSizes.
13
13
  */
14
- export default function useSizes(): [
15
- Ref<NodeSizeMap>,
16
- (key: string, node: HTMLElement | null) => void,
17
- ] {
18
- const sizeMap = ref<NodeSizeMap>({})
14
+ export default function useSizes() {
15
+ const sizeMap = shallowRef<NodeSizeMap>({})
19
16
 
20
- function setNodeSize(key: string, node: HTMLElement | null) {
17
+ const setNodeSize = (key: string, node: HTMLDivElement | null) => {
21
18
  if (!node) {
22
19
  if (!(key in sizeMap.value)) {
23
20
  return
24
21
  }
25
- const { [key]: _, ...rest } = sizeMap.value
26
- sizeMap.value = rest
22
+ const next = { ...sizeMap.value }
23
+ delete next[key]
24
+ sizeMap.value = next
27
25
  return
28
26
  }
29
27
 
30
- const next: NodeSize = {
28
+ const nextSize: NodeSize = {
31
29
  width: node.offsetWidth,
32
30
  height: node.offsetHeight,
33
31
  }
34
32
  const prev = sizeMap.value[key]
35
- if (prev && prev.width === next.width && prev.height === next.height) {
33
+ if (prev && prev.width === nextSize.width && prev.height === nextSize.height) {
36
34
  return
37
35
  }
38
- sizeMap.value = { ...sizeMap.value, [key]: next }
36
+ sizeMap.value = {
37
+ ...sizeMap.value,
38
+ [key]: nextSize,
39
+ }
39
40
  }
40
41
 
41
- return [sizeMap, setNodeSize]
42
+ return [sizeMap, setNodeSize] as const
42
43
  }
@@ -1,48 +1,43 @@
1
- import type { ComputedRef, MaybeRef } from 'vue'
2
- import raf from '@v-c/util/dist/raf'
3
- import { computed, onBeforeUnmount, shallowRef, unref, watch } from 'vue'
1
+ import type { ComputedRef } from 'vue'
2
+ import { onScopeDispose, shallowRef, watch } from 'vue'
4
3
 
5
4
  /**
6
- * Run the auto-close timer for a notice and report progress updates.
7
- * Returns controls to pause and resume the timer.
5
+ * Runs the notice auto-close timer and reports progress updates via rAF.
6
+ * Returns controls to pause and resume the timer. Mirrors rc-notification@2.0
7
+ * useNoticeTimer.
8
8
  */
9
9
  export default function useNoticeTimer(
10
- duration: MaybeRef<number | false | null | undefined>,
10
+ duration: ComputedRef<number | false | null | undefined>,
11
11
  onClose: () => void,
12
- onUpdate: (ptg: number) => void,
13
- ): [() => void, () => void, ComputedRef<number>] {
14
- const durationMs = computed(() => {
15
- const value = unref(duration)
16
- const merged = typeof value === 'number' ? value : 0
17
- return Math.max(merged, 0) * 1000
18
- })
19
-
20
- const walking = shallowRef(durationMs.value > 0)
21
- const passTime = shallowRef(0)
12
+ onUpdate: (percent: number) => void,
13
+ ): [() => void, () => void] {
14
+ const durationMs = shallowRef(0)
15
+ const walking = shallowRef(false)
16
+ let passTime = 0
22
17
  let lastRafTime: number | null = null
23
18
  let rafId: number | null = null
24
19
 
25
- function syncPassTime() {
20
+ const syncPassTime = () => {
26
21
  const now = Date.now()
27
22
  if (lastRafTime !== null) {
28
- passTime.value += now - lastRafTime
23
+ passTime += now - lastRafTime
29
24
  }
30
25
  lastRafTime = now
31
26
  }
32
27
 
33
- function cancelRaf() {
28
+ const cancelStep = () => {
34
29
  if (rafId !== null) {
35
- raf.cancel(rafId)
30
+ cancelAnimationFrame(rafId)
36
31
  rafId = null
37
32
  }
38
33
  }
39
34
 
40
- function onPause() {
35
+ const onPause = () => {
41
36
  syncPassTime()
42
37
  walking.value = false
43
38
  }
44
39
 
45
- function onResume() {
40
+ const onResume = () => {
46
41
  if (durationMs.value > 0) {
47
42
  lastRafTime = Date.now()
48
43
  walking.value = true
@@ -52,45 +47,39 @@ export default function useNoticeTimer(
52
47
  }
53
48
  }
54
49
 
55
- // Reset when durationMs changed.
56
- watch(durationMs, () => {
57
- passTime.value = 0
58
- lastRafTime = null
50
+ // Reset accumulated passTime whenever duration changes.
51
+ watch(duration, () => {
52
+ const next = typeof duration.value === 'number' ? duration.value : 0
53
+ durationMs.value = Math.max(next, 0) * 1000
54
+ passTime = 0
59
55
  walking.value = durationMs.value > 0
60
- })
56
+ }, { immediate: true })
61
57
 
62
- // Drive raf loop while walking.
58
+ // Drive the rAF loop while walking is true.
63
59
  watch(walking, (isWalking) => {
64
- cancelRaf()
60
+ cancelStep()
65
61
  if (!isWalking) {
66
62
  return
67
63
  }
64
+ lastRafTime = Date.now()
68
65
 
69
- function step() {
66
+ const step = () => {
70
67
  syncPassTime()
71
-
72
- if (passTime.value >= durationMs.value) {
68
+ if (passTime >= durationMs.value) {
73
69
  onUpdate(1)
74
70
  onClose()
71
+ return
75
72
  }
76
- else {
77
- onUpdate(Math.min(passTime.value / durationMs.value, 1))
78
- rafId = raf(step)
79
- }
73
+ onUpdate(Math.min(passTime / durationMs.value, 1))
74
+ rafId = requestAnimationFrame(step)
80
75
  }
81
76
 
82
77
  step()
83
78
  }, { immediate: true })
84
79
 
85
- onBeforeUnmount(() => {
86
- cancelRaf()
87
- })
88
-
89
- const percent = computed(() => {
90
- if (durationMs.value <= 0)
91
- return 0
92
- return Math.min(passTime.value / durationMs.value, 1)
80
+ onScopeDispose(() => {
81
+ cancelStep()
93
82
  })
94
83
 
95
- return [onResume, onPause, percent]
84
+ return [onResume, onPause]
96
85
  }
@@ -1,29 +1,41 @@
1
- import type { VueNode } from '@v-c/util/dist/type'
2
1
  import type { CSSProperties, MaybeRef, TransitionGroupProps } from 'vue'
3
- import type { NotificationListConfig, Placement, StackInput } from '../NotificationList'
2
+ import type { VueNode } from '@v-c/util/dist/type'
3
+ import type { ClosableType, Key, NotificationListConfig, Placement, StackConfig } from '../interface'
4
+ import type {
5
+ ComponentsType,
6
+ } from '../Notification'
7
+ import type { NotificationClassNames, NotificationStyles } from '../NotificationList'
4
8
  import type { NotificationsProps, NotificationsRef } from '../Notifications'
5
9
  import { computed, onMounted, shallowRef, unref, watch } from 'vue'
6
10
  import Notifications from '../Notifications'
7
11
 
8
- type Key = string | number | symbol
9
-
10
12
  const defaultGetContainer = () => document.body
11
13
 
12
14
  type OptionalConfig = Partial<NotificationListConfig>
13
- type SharedConfig = Pick<
14
- NotificationListConfig,
15
- 'placement' | 'closable' | 'duration' | 'showProgress'
16
- >
17
15
 
18
- export interface NotificationConfig extends Omit<NotificationsProps, 'container'> {
19
- // UI
20
- placement?: Placement
16
+ export interface NotificationConfig {
17
+ prefixCls?: string
18
+ /** Customize container. It will repeat call which means you should return same container element. */
21
19
  getContainer?: () => HTMLElement | ShadowRoot
22
-
23
- // Behavior
24
- closable?: NotificationListConfig['closable']
20
+ motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
21
+ closable?: ClosableType
22
+ maxCount?: number
25
23
  duration?: number | false | null
26
- showProgress?: NotificationListConfig['showProgress']
24
+ showProgress?: boolean
25
+ pauseOnHover?: boolean
26
+ placement?: Placement
27
+ classNames?: NotificationClassNames
28
+ styles?: NotificationStyles
29
+ components?: ComponentsType
30
+ /** @private. Config for notification holder style. Safe to remove if refactor */
31
+ className?: (placement: Placement) => string
32
+ /** @private. Config for notification holder style. Safe to remove if refactor */
33
+ style?: (placement: Placement) => CSSProperties
34
+ /** @private Trigger when all the notification closed. */
35
+ onAllRemoved?: VoidFunction
36
+ stack?: StackConfig
37
+ /** @private Slot for style in Notifications */
38
+ renderNotifications?: NotificationsProps['renderNotifications']
27
39
  }
28
40
 
29
41
  export interface NotificationAPI {
@@ -51,13 +63,13 @@ type Task = OpenTask | CloseTask | DestroyTask
51
63
  let uniqueKey = 0
52
64
 
53
65
  function mergeConfig<T>(...objList: Partial<T>[]): T {
54
- const clone = {} as T
55
- objList.forEach((obj) => {
66
+ const clone: any = {}
67
+ objList.forEach((obj: any) => {
56
68
  if (obj) {
57
69
  Object.keys(obj).forEach((key) => {
58
- const value = (obj as any)[key]
59
- if (value !== undefined) {
60
- ;(clone as any)[key] = value
70
+ const val = obj[key]
71
+ if (val !== undefined) {
72
+ clone[key] = val
61
73
  }
62
74
  })
63
75
  }
@@ -70,15 +82,29 @@ export default function useNotification(
70
82
  ): [NotificationAPI, () => VueNode] {
71
83
  const configRef = computed(() => unref(rootConfig) || {})
72
84
  const container = shallowRef<HTMLElement | ShadowRoot>()
73
- const notificationsRef = shallowRef<NotificationsRef>()
74
- const taskQueue = shallowRef<Task[]>([])
75
-
76
- const shareConfig = computed<SharedConfig>(() => {
77
- const { placement, closable, duration, showProgress } = configRef.value
78
- return { placement, closable, duration, showProgress }
85
+ const notificationRef = shallowRef<NotificationsRef>()
86
+
87
+ const shareConfig = computed(() => {
88
+ const {
89
+ getContainer,
90
+ motion,
91
+ prefixCls,
92
+ maxCount,
93
+ className,
94
+ style,
95
+ onAllRemoved,
96
+ stack,
97
+ renderNotifications,
98
+ pauseOnHover,
99
+ classNames,
100
+ styles,
101
+ components,
102
+ ...restConfig
103
+ } = configRef.value
104
+ return restConfig
79
105
  })
80
106
 
81
- function resolveContainer() {
107
+ const resolveContainer = () => {
82
108
  const getContainer = configRef.value.getContainer || defaultGetContainer
83
109
  return getContainer()
84
110
  }
@@ -86,30 +112,32 @@ export default function useNotification(
86
112
  const contextHolder = () => (
87
113
  <Notifications
88
114
  container={container.value}
89
- ref={notificationsRef as any}
115
+ ref={notificationRef}
90
116
  prefixCls={configRef.value.prefixCls}
91
- motion={configRef.value.motion as TransitionGroupProps | ((p: Placement) => TransitionGroupProps) | undefined}
117
+ motion={configRef.value.motion}
92
118
  maxCount={configRef.value.maxCount}
93
119
  pauseOnHover={configRef.value.pauseOnHover}
94
120
  classNames={configRef.value.classNames}
95
121
  styles={configRef.value.styles}
96
122
  components={configRef.value.components}
97
123
  className={configRef.value.className}
98
- style={configRef.value.style as ((p: Placement) => CSSProperties) | undefined}
124
+ style={configRef.value.style}
99
125
  onAllRemoved={configRef.value.onAllRemoved}
100
- stack={configRef.value.stack as StackInput | undefined}
126
+ stack={configRef.value.stack}
101
127
  renderNotifications={configRef.value.renderNotifications}
102
128
  />
103
129
  )
104
130
 
131
+ const taskQueue = shallowRef<Task[]>([])
132
+
105
133
  const api: NotificationAPI = {
106
134
  open(config) {
107
- const merged = mergeConfig<NotificationListConfig>(shareConfig.value, config as any)
108
- if (merged.key === null || merged.key === undefined) {
109
- merged.key = `vc-notification-${uniqueKey}`
135
+ const mergedConfig = mergeConfig<NotificationListConfig>(shareConfig.value as any, config)
136
+ if (mergedConfig.key === null || mergedConfig.key === undefined) {
137
+ mergedConfig.key = `vc-notification-${uniqueKey}`
110
138
  uniqueKey += 1
111
139
  }
112
- taskQueue.value = [...taskQueue.value, { type: 'open', config: merged }]
140
+ taskQueue.value = [...taskQueue.value, { type: 'open', config: mergedConfig }]
113
141
  },
114
142
  close(key) {
115
143
  taskQueue.value = [...taskQueue.value, { type: 'close', key }]
@@ -128,24 +156,24 @@ export default function useNotification(
128
156
  container.value = resolveContainer()
129
157
  },
130
158
  )
131
-
132
159
  watch(taskQueue, () => {
133
- if (notificationsRef.value && taskQueue.value.length) {
134
- const tasks = taskQueue.value
135
- tasks.forEach((task) => {
160
+ if (notificationRef.value && taskQueue.value.length) {
161
+ taskQueue.value.forEach((task) => {
136
162
  switch (task.type) {
137
163
  case 'open':
138
- notificationsRef.value?.open(task.config)
164
+ notificationRef.value?.open(task.config)
139
165
  break
140
166
  case 'close':
141
- notificationsRef.value?.close(task.key)
167
+ notificationRef.value?.close(task.key)
142
168
  break
143
169
  case 'destroy':
144
- notificationsRef.value?.destroy()
170
+ notificationRef.value?.destroy()
171
+ break
172
+ default:
145
173
  break
146
174
  }
147
175
  })
148
- taskQueue.value = taskQueue.value.filter(task => !tasks.includes(task))
176
+ taskQueue.value = []
149
177
  }
150
178
  })
151
179
 
@@ -1,40 +1,36 @@
1
- import type { ComputedRef, MaybeRef } from 'vue'
2
- import { computed, unref } from 'vue'
3
-
4
- export interface StackConfig {
5
- threshold?: number
6
- offset?: number
7
- }
1
+ import type { ComputedRef, MaybeRef, ToRefs } from 'vue'
2
+ import type { StackConfig } from '../interface'
3
+ import { computed, reactive, toRefs, unref, watchEffect } from 'vue'
8
4
 
9
5
  const DEFAULT_OFFSET = 8
10
6
  const DEFAULT_THRESHOLD = 3
11
7
 
12
- type StackParams = Required<StackConfig>
13
-
14
- export type StackInput = boolean | StackConfig
8
+ type StackParams = Exclude<StackConfig, boolean>
15
9
 
16
10
  type UseStack = (
17
- config?: MaybeRef<StackInput | undefined>,
18
- ) => [ComputedRef<boolean>, ComputedRef<StackParams>]
19
-
11
+ config?: MaybeRef<StackConfig | undefined>,
12
+ ) => [ComputedRef<boolean>, ToRefs<StackParams>]
13
+
14
+ /**
15
+ * Resolves the stack setting into an enabled flag and normalized stack params.
16
+ * Mirrors rc-notification@2.0 useStack. The `gap` config is no longer surfaced
17
+ * here — gap is now read from the list-content CSS `gap`/`row-gap`.
18
+ */
20
19
  const useStack: UseStack = (config) => {
21
- const enabled = computed(() => !!unref(config))
20
+ const result: StackParams = reactive({
21
+ offset: DEFAULT_OFFSET,
22
+ threshold: DEFAULT_THRESHOLD,
23
+ })
22
24
 
23
- const params = computed<StackParams>(() => {
25
+ watchEffect(() => {
24
26
  const value = unref(config)
25
27
  if (value && typeof value === 'object') {
26
- return {
27
- offset: value.offset ?? DEFAULT_OFFSET,
28
- threshold: value.threshold ?? DEFAULT_THRESHOLD,
29
- }
30
- }
31
- return {
32
- offset: DEFAULT_OFFSET,
33
- threshold: DEFAULT_THRESHOLD,
28
+ result.offset = value.offset ?? DEFAULT_OFFSET
29
+ result.threshold = value.threshold ?? DEFAULT_THRESHOLD
34
30
  }
35
31
  })
36
32
 
37
- return [enabled, params]
33
+ return [computed(() => !!unref(config)), toRefs(result)]
38
34
  }
39
35
 
40
36
  export default useStack