@v-c/notification 1.0.0 → 2.0.0-rc.1

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 (56) hide show
  1. package/dist/Notification.d.ts +59 -0
  2. package/dist/Notification.js +301 -0
  3. package/dist/NotificationList/Content.d.ts +11 -0
  4. package/dist/NotificationList/Content.js +60 -0
  5. package/dist/NotificationList/index.d.ts +35 -0
  6. package/dist/NotificationList/index.js +224 -0
  7. package/dist/NotificationProvider.js +3 -1
  8. package/dist/Notifications.d.ts +9 -3
  9. package/dist/Notifications.js +45 -21
  10. package/dist/Progress.d.ts +8 -0
  11. package/dist/Progress.js +31 -0
  12. package/dist/hooks/useClosable.d.ts +17 -0
  13. package/dist/hooks/useClosable.js +34 -0
  14. package/dist/hooks/useListPosition/index.d.ts +18 -0
  15. package/dist/hooks/useListPosition/index.js +41 -0
  16. package/dist/hooks/useListPosition/useSizes.d.ts +10 -0
  17. package/dist/hooks/useListPosition/useSizes.js +31 -0
  18. package/dist/hooks/useNoticeTimer.d.ts +7 -0
  19. package/dist/hooks/useNoticeTimer.js +63 -0
  20. package/dist/hooks/useNotification.d.ts +10 -8
  21. package/dist/hooks/useNotification.js +10 -4
  22. package/dist/hooks/useStack.d.ts +5 -0
  23. package/dist/hooks/useStack.js +13 -10
  24. package/dist/index.d.ts +9 -6
  25. package/dist/index.js +5 -3
  26. package/dist/interface.d.ts +18 -43
  27. package/package.json +3 -3
  28. package/src/Notification.tsx +326 -0
  29. package/src/NotificationList/Content.tsx +66 -0
  30. package/src/NotificationList/index.tsx +282 -0
  31. package/src/Notifications.tsx +58 -64
  32. package/src/Progress.tsx +27 -0
  33. package/src/hooks/useClosable.ts +53 -0
  34. package/src/hooks/useListPosition/index.ts +69 -0
  35. package/src/hooks/useListPosition/useSizes.ts +43 -0
  36. package/src/hooks/useNoticeTimer.ts +85 -0
  37. package/src/hooks/useNotification.tsx +30 -28
  38. package/src/hooks/useStack.ts +12 -8
  39. package/src/index.ts +47 -6
  40. package/src/interface.ts +28 -44
  41. package/vite.config.ts +4 -3
  42. package/dist/Notice.cjs +0 -235
  43. package/dist/Notice.d.ts +0 -15
  44. package/dist/Notice.js +0 -227
  45. package/dist/NoticeList.cjs +0 -170
  46. package/dist/NoticeList.d.ts +0 -13
  47. package/dist/NoticeList.js +0 -164
  48. package/dist/NotificationProvider.cjs +0 -14
  49. package/dist/Notifications.cjs +0 -146
  50. package/dist/_virtual/rolldown_runtime.cjs +0 -21
  51. package/dist/hooks/useNotification.cjs +0 -93
  52. package/dist/hooks/useStack.cjs +0 -27
  53. package/dist/index.cjs +0 -7
  54. package/dist/interface.cjs +0 -1
  55. package/src/Notice.tsx +0 -212
  56. package/src/NoticeList.tsx +0 -219
@@ -1,14 +1,22 @@
1
- import type { VueNode } from '@v-c/util/dist/type'
2
1
  import type { CSSProperties, TransitionGroupProps } from 'vue'
3
- import type { InnerOpenConfig, Key, OpenConfig, Placement, Placements, StackConfig } from './interface.ts'
2
+ import type { VueNode } from '@v-c/util/dist/type'
3
+ import type { InnerOpenConfig, Key, NotificationListConfig, Placement, Placements, StackConfig } from './interface'
4
+ import type { ComponentsType } from './Notification'
4
5
  import { defineComponent, shallowRef, Teleport, watch } from 'vue'
5
- import NoticeList from './NoticeList.tsx'
6
+ import NotificationList, {
7
+ type NotificationClassNames,
8
+ type NotificationStyles,
9
+ } from './NotificationList'
6
10
 
7
11
  export interface NotificationsProps {
8
12
  prefixCls?: string
9
13
  motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
10
14
  container?: HTMLElement | ShadowRoot
11
15
  maxCount?: number
16
+ pauseOnHover?: boolean
17
+ classNames?: NotificationClassNames
18
+ styles?: NotificationStyles
19
+ components?: ComponentsType
12
20
  className?: (placement: Placement) => string
13
21
  style?: (placement: Placement) => CSSProperties
14
22
  onAllRemoved?: VoidFunction
@@ -20,7 +28,7 @@ export interface NotificationsProps {
20
28
  }
21
29
 
22
30
  export interface NotificationsRef {
23
- open: (config: OpenConfig) => void
31
+ open: (config: NotificationListConfig) => void
24
32
  close: (key: Key) => void
25
33
  destroy: () => void
26
34
  }
@@ -31,30 +39,25 @@ const defaults = {
31
39
 
32
40
  const Notifications = defineComponent<NotificationsProps>(
33
41
  (props = defaults, { expose }) => {
34
- const configList = shallowRef<OpenConfig[]>([])
35
- // ======================== Close =========================
42
+ const configList = shallowRef<NotificationListConfig[]>([])
43
+
36
44
  const onNoticeClose = (key: Key) => {
37
- // Trigger close event
38
45
  const config = configList.value.find(item => item.key === key)
39
46
  const closable = config?.closable
40
- const closableObj = closable && typeof closable === 'object' ? closable : {}
41
- closableObj.onClose?.()
47
+ const closableObj = closable && typeof closable === 'object' ? closable : null
48
+ closableObj?.onClose?.()
42
49
  config?.onClose?.()
43
50
  configList.value = configList.value.filter(item => item.key !== key)
44
51
  }
45
52
 
46
- // ========================= Refs =========================
47
53
  expose({
48
- open: (config: OpenConfig) => {
54
+ open: (config: NotificationListConfig) => {
49
55
  const list = configList.value
50
- let clone = [...configList.value]
51
- // Replace if exist
56
+ let clone = [...list]
52
57
  const index = clone.findIndex(item => item.key === config.key)
53
- const innerConfig: InnerOpenConfig = {
54
- ...config,
55
- }
58
+ const innerConfig: InnerOpenConfig = { ...config }
56
59
  if (index >= 0) {
57
- innerConfig.times = ((list[index] as InnerOpenConfig)?.times || 0) + 1
60
+ innerConfig.times = ((list[index] as InnerOpenConfig)?.times ?? 0) + 1
58
61
  clone[index] = innerConfig
59
62
  }
60
63
  else {
@@ -73,31 +76,22 @@ const Notifications = defineComponent<NotificationsProps>(
73
76
  },
74
77
  })
75
78
 
76
- // ====================== Placements ======================
77
-
78
79
  const placements = shallowRef<Placements>({})
79
80
 
80
- watch(
81
- configList,
82
- () => {
83
- const nextPlacements: Placements = {}
84
- configList.value.forEach((config) => {
85
- const { placement = 'topRight' } = config
86
- if (placement) {
87
- nextPlacements[placement] = nextPlacements[placement] || []
88
- nextPlacements[placement].push(config)
89
- }
90
- })
91
- // Fill exist placements to avoid empty list causing remove without motion
92
- Object.keys(placements.value).forEach((_placement) => {
93
- const placement = _placement as Placement
94
- nextPlacements[placement] = nextPlacements[placement] || []
95
- })
96
- placements.value = nextPlacements
97
- },
98
- )
81
+ watch(configList, () => {
82
+ const next: Placements = {}
83
+ configList.value.forEach((config) => {
84
+ const placement = (config.placement ?? 'topRight') as Placement
85
+ next[placement] = next[placement] || []
86
+ next[placement]!.push(config)
87
+ })
88
+ // Keep existing placements so empty lists can finish leave motion.
89
+ Object.keys(placements.value).forEach((placement) => {
90
+ next[placement as Placement] = next[placement as Placement] || []
91
+ })
92
+ placements.value = next
93
+ })
99
94
 
100
- // Clean up container if all notices fade out
101
95
  const onAllNoticeRemoved = (placement: Placement) => {
102
96
  const clone = { ...placements.value }
103
97
  const list = clone[placement] || []
@@ -107,50 +101,50 @@ const Notifications = defineComponent<NotificationsProps>(
107
101
  placements.value = clone
108
102
  }
109
103
 
110
- // Effect tell that placements is empty now
111
104
  const emptyRef = shallowRef(false)
112
-
113
- watch(
114
- placements,
115
- () => {
116
- if (Object.keys(placements.value).length > 0) {
117
- emptyRef.value = true
118
- }
119
- else if (emptyRef.value) {
120
- // Trigger only when from exist to empty
121
- props?.onAllRemoved?.()
122
- emptyRef.value = false
123
- }
124
- },
125
- )
105
+ watch(placements, () => {
106
+ if (Object.keys(placements.value).length > 0) {
107
+ emptyRef.value = true
108
+ }
109
+ else if (emptyRef.value) {
110
+ props?.onAllRemoved?.()
111
+ emptyRef.value = false
112
+ }
113
+ })
126
114
 
127
115
  return () => {
128
116
  const { container } = props
129
- const prefixCls = props.prefixCls ?? defaults.prefixCls ?? ''
130
- // ======================== Render ========================
117
+ const prefixCls = props.prefixCls ?? defaults.prefixCls!
131
118
  if (!container) {
132
119
  return null
133
120
  }
134
121
 
135
122
  return (
136
123
  <Teleport to={container}>
137
- {Object.keys(placements.value).map((placement) => {
138
- const placementConfigList = placements.value[placement as Placement]
124
+ {Object.keys(placements.value).map((rawPlacement) => {
125
+ const placement = rawPlacement as Placement
126
+ const placementConfigList = placements.value[placement]
139
127
  const list = (
140
- <NoticeList
128
+ <NotificationList
141
129
  key={placement}
142
130
  configList={placementConfigList}
143
- placement={placement as Placement}
131
+ placement={placement}
144
132
  prefixCls={prefixCls}
145
- class={props.className?.(placement as Placement)}
146
- style={props.style?.(placement as Placement)}
133
+ pauseOnHover={props.pauseOnHover}
134
+ classNames={props.classNames}
135
+ styles={props.styles}
136
+ components={props.components}
137
+ className={props.className?.(placement)}
138
+ style={props.style?.(placement)}
147
139
  motion={props.motion}
148
140
  stack={props.stack}
149
- onAllNoticeRemoved={() => onAllNoticeRemoved(placement as Placement)}
141
+ onAllRemoved={onAllNoticeRemoved}
150
142
  onNoticeClose={onNoticeClose}
151
143
  />
152
144
  )
153
- return props.renderNotifications ? props.renderNotifications(list, { prefixCls, key: placement }) : list
145
+ return props.renderNotifications
146
+ ? props.renderNotifications(list, { prefixCls, key: placement })
147
+ : list
154
148
  })}
155
149
  </Teleport>
156
150
  )
@@ -0,0 +1,27 @@
1
+ import type { CSSProperties } from 'vue'
2
+ import { defineComponent } from 'vue'
3
+
4
+ export interface NotificationProgressProps {
5
+ className?: string
6
+ style?: CSSProperties
7
+ percent: number
8
+ }
9
+
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
+ )
26
+
27
+ export default Progress
@@ -0,0 +1,53 @@
1
+ import type { AriaAttributes, ComputedRef } from 'vue'
2
+ import type { VueNode } from '@v-c/util/dist/type'
3
+ import pickAttrs from '@v-c/util/dist/pickAttrs'
4
+ import { computed } from 'vue'
5
+
6
+ export type ClosableConfig = {
7
+ closeIcon?: VueNode
8
+ disabled?: boolean
9
+ onClose?: VoidFunction
10
+ } & AriaAttributes & Record<`data-${string}`, unknown>
11
+
12
+ export type ClosableType = boolean | ClosableConfig | null | undefined
13
+
14
+ export interface ParsedClosableConfig extends ClosableConfig {
15
+ closeIcon: VueNode
16
+ disabled: boolean
17
+ }
18
+
19
+ /**
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.
22
+ */
23
+ export default function useClosable(
24
+ closable: ComputedRef<ClosableType>,
25
+ ): [ComputedRef<boolean>, ComputedRef<ParsedClosableConfig>, ComputedRef<Record<string, unknown>>] {
26
+ const closableObj = computed<ClosableConfig>(() => {
27
+ const value = closable.value
28
+ if (value === false) {
29
+ return { closeIcon: null, disabled: true }
30
+ }
31
+ if (typeof value === 'object' && value !== null) {
32
+ return value
33
+ }
34
+ return {}
35
+ })
36
+
37
+ const closableConfig = computed<ParsedClosableConfig>(() => {
38
+ const obj = closableObj.value
39
+ return {
40
+ ...obj,
41
+ closeIcon: 'closeIcon' in obj ? obj.closeIcon : '×',
42
+ disabled: obj.disabled ?? false,
43
+ }
44
+ })
45
+
46
+ const closableAriaProps = computed(() => pickAttrs(closableConfig.value, true))
47
+
48
+ return [
49
+ computed(() => !!closable.value),
50
+ closableConfig,
51
+ closableAriaProps,
52
+ ]
53
+ }
@@ -0,0 +1,69 @@
1
+ import type { ComputedRef, Ref } from 'vue'
2
+ import type { Key } from '../../interface'
3
+ import { computed } from 'vue'
4
+ import useSizes from './useSizes'
5
+
6
+ export interface ListPositionStackConfig {
7
+ threshold?: number
8
+ offset?: number
9
+ }
10
+
11
+ /**
12
+ * Calculates each notification's position and the full list height.
13
+ * Mirrors rc-notification@2.0 useListPosition.
14
+ */
15
+ export default function useListPosition(
16
+ configList: ComputedRef<{ key: Key }[]>,
17
+ stack: ComputedRef<ListPositionStackConfig | undefined>,
18
+ gap: Ref<number>,
19
+ ) {
20
+ const [sizeMap, setNodeSize] = useSizes()
21
+
22
+ const result = computed(() => {
23
+ let offsetY = 0
24
+ let nextTotalHeight = 0
25
+ const stackParams = stack.value
26
+ const stackThreshold = stackParams?.threshold ?? 0
27
+ const stackOffset = stackParams?.offset ?? 0
28
+ const notificationPosition = new Map<string, number>()
29
+ let topNoticeHeight: number | undefined
30
+ let topNoticeWidth: number | undefined
31
+
32
+ configList.value
33
+ .slice()
34
+ .reverse()
35
+ .forEach((config, index) => {
36
+ // Walk from newest to oldest so each notice can be positioned after the ones below it.
37
+ const key = String(config.key)
38
+ const height = sizeMap.value[key]?.height ?? 0
39
+ const y = stackParams && index > 0 ? offsetY + stackOffset - height : offsetY
40
+
41
+ notificationPosition.set(key, y)
42
+
43
+ if (index === 0) {
44
+ topNoticeHeight = height
45
+ topNoticeWidth = sizeMap.value[key]?.width ?? 0
46
+ }
47
+
48
+ if (!stackParams || index < stackThreshold) {
49
+ nextTotalHeight = Math.max(nextTotalHeight, y + height)
50
+ }
51
+
52
+ if (stackParams) {
53
+ offsetY = y + height
54
+ }
55
+ else {
56
+ offsetY += height + gap.value
57
+ }
58
+ })
59
+
60
+ return {
61
+ notificationPosition,
62
+ totalHeight: nextTotalHeight,
63
+ topNoticeHeight,
64
+ topNoticeWidth,
65
+ }
66
+ })
67
+
68
+ return [result, setNodeSize] as const
69
+ }
@@ -0,0 +1,43 @@
1
+ import { shallowRef } from 'vue'
2
+
3
+ export interface NodeSize {
4
+ width: number
5
+ height: number
6
+ }
7
+
8
+ export type NodeSizeMap = Record<string, NodeSize>
9
+
10
+ /**
11
+ * Stores measured node sizes by key and exposes a callback to update them.
12
+ * Mirrors rc-notification@2.0 useSizes.
13
+ */
14
+ export default function useSizes() {
15
+ const sizeMap = shallowRef<NodeSizeMap>({})
16
+
17
+ const setNodeSize = (key: string, node: HTMLDivElement | null) => {
18
+ if (!node) {
19
+ if (!(key in sizeMap.value)) {
20
+ return
21
+ }
22
+ const next = { ...sizeMap.value }
23
+ delete next[key]
24
+ sizeMap.value = next
25
+ return
26
+ }
27
+
28
+ const nextSize: NodeSize = {
29
+ width: node.offsetWidth,
30
+ height: node.offsetHeight,
31
+ }
32
+ const prev = sizeMap.value[key]
33
+ if (prev && prev.width === nextSize.width && prev.height === nextSize.height) {
34
+ return
35
+ }
36
+ sizeMap.value = {
37
+ ...sizeMap.value,
38
+ [key]: nextSize,
39
+ }
40
+ }
41
+
42
+ return [sizeMap, setNodeSize] as const
43
+ }
@@ -0,0 +1,85 @@
1
+ import type { ComputedRef } from 'vue'
2
+ import { onScopeDispose, shallowRef, watch } from 'vue'
3
+
4
+ /**
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
+ */
9
+ export default function useNoticeTimer(
10
+ duration: ComputedRef<number | false | null | undefined>,
11
+ onClose: () => void,
12
+ onUpdate: (percent: number) => void,
13
+ ): [() => void, () => void] {
14
+ const durationMs = shallowRef(0)
15
+ const walking = shallowRef(false)
16
+ let passTime = 0
17
+ let lastRafTime: number | null = null
18
+ let rafId: number | null = null
19
+
20
+ const syncPassTime = () => {
21
+ const now = Date.now()
22
+ if (lastRafTime !== null) {
23
+ passTime += now - lastRafTime
24
+ }
25
+ lastRafTime = now
26
+ }
27
+
28
+ const cancelStep = () => {
29
+ if (rafId !== null) {
30
+ cancelAnimationFrame(rafId)
31
+ rafId = null
32
+ }
33
+ }
34
+
35
+ const onPause = () => {
36
+ syncPassTime()
37
+ walking.value = false
38
+ }
39
+
40
+ const onResume = () => {
41
+ if (durationMs.value > 0) {
42
+ lastRafTime = Date.now()
43
+ walking.value = true
44
+ }
45
+ else {
46
+ onUpdate(0)
47
+ }
48
+ }
49
+
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
55
+ walking.value = durationMs.value > 0
56
+ }, { immediate: true })
57
+
58
+ // Drive the rAF loop while walking is true.
59
+ watch(walking, (isWalking) => {
60
+ cancelStep()
61
+ if (!isWalking) {
62
+ return
63
+ }
64
+ lastRafTime = Date.now()
65
+
66
+ const step = () => {
67
+ syncPassTime()
68
+ if (passTime >= durationMs.value) {
69
+ onUpdate(1)
70
+ onClose()
71
+ return
72
+ }
73
+ onUpdate(Math.min(passTime / durationMs.value, 1))
74
+ rafId = requestAnimationFrame(step)
75
+ }
76
+
77
+ step()
78
+ }, { immediate: true })
79
+
80
+ onScopeDispose(() => {
81
+ cancelStep()
82
+ })
83
+
84
+ return [onResume, onPause]
85
+ }
@@ -1,27 +1,32 @@
1
- import type { VueNode } from '@v-c/util/dist/type'
2
1
  import type { CSSProperties, MaybeRef, TransitionGroupProps } from 'vue'
3
- import type { Key, OpenConfig, Placement, StackConfig } from '../interface'
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
12
  const defaultGetContainer = () => document.body
9
13
 
10
- type OptionalConfig = Partial<OpenConfig>
14
+ type OptionalConfig = Partial<NotificationListConfig>
11
15
 
12
16
  export interface NotificationConfig {
13
17
  prefixCls?: string
14
18
  /** Customize container. It will repeat call which means you should return same container element. */
15
19
  getContainer?: () => HTMLElement | ShadowRoot
16
20
  motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
17
- closeIcon?: VueNode
18
- closable?:
19
- | boolean
20
- | ({ closeIcon?: VueNode, onClose?: VoidFunction } & Record<string, any>)
21
+ closable?: ClosableType
21
22
  maxCount?: number
22
23
  duration?: number | false | null
23
24
  showProgress?: boolean
24
25
  pauseOnHover?: boolean
26
+ placement?: Placement
27
+ classNames?: NotificationClassNames
28
+ styles?: NotificationStyles
29
+ components?: ComponentsType
25
30
  /** @private. Config for notification holder style. Safe to remove if refactor */
26
31
  className?: (placement: Placement) => string
27
32
  /** @private. Config for notification holder style. Safe to remove if refactor */
@@ -41,7 +46,7 @@ export interface NotificationAPI {
41
46
 
42
47
  interface OpenTask {
43
48
  type: 'open'
44
- config: OpenConfig
49
+ config: NotificationListConfig
45
50
  }
46
51
 
47
52
  interface CloseTask {
@@ -59,26 +64,24 @@ let uniqueKey = 0
59
64
 
60
65
  function mergeConfig<T>(...objList: Partial<T>[]): T {
61
66
  const clone: any = {}
62
-
63
67
  objList.forEach((obj: any) => {
64
68
  if (obj) {
65
69
  Object.keys(obj).forEach((key) => {
66
70
  const val = obj[key]
67
-
68
71
  if (val !== undefined) {
69
72
  clone[key] = val
70
73
  }
71
74
  })
72
75
  }
73
76
  })
74
-
75
77
  return clone
76
78
  }
77
79
 
78
- export default function useNotification(rootConfig: MaybeRef<NotificationConfig> = {}) {
80
+ export default function useNotification(
81
+ rootConfig: MaybeRef<NotificationConfig> = {},
82
+ ): [NotificationAPI, () => VueNode] {
79
83
  const configRef = computed(() => unref(rootConfig) || {})
80
84
  const container = shallowRef<HTMLElement | ShadowRoot>()
81
-
82
85
  const notificationRef = shallowRef<NotificationsRef>()
83
86
 
84
87
  const shareConfig = computed(() => {
@@ -92,6 +95,10 @@ export default function useNotification(rootConfig: MaybeRef<NotificationConfig>
92
95
  onAllRemoved,
93
96
  stack,
94
97
  renderNotifications,
98
+ pauseOnHover,
99
+ classNames,
100
+ styles,
101
+ components,
95
102
  ...restConfig
96
103
  } = configRef.value
97
104
  return restConfig
@@ -109,6 +116,10 @@ export default function useNotification(rootConfig: MaybeRef<NotificationConfig>
109
116
  prefixCls={configRef.value.prefixCls}
110
117
  motion={configRef.value.motion}
111
118
  maxCount={configRef.value.maxCount}
119
+ pauseOnHover={configRef.value.pauseOnHover}
120
+ classNames={configRef.value.classNames}
121
+ styles={configRef.value.styles}
122
+ components={configRef.value.components}
112
123
  className={configRef.value.className}
113
124
  style={configRef.value.style}
114
125
  onAllRemoved={configRef.value.onAllRemoved}
@@ -119,16 +130,13 @@ export default function useNotification(rootConfig: MaybeRef<NotificationConfig>
119
130
 
120
131
  const taskQueue = shallowRef<Task[]>([])
121
132
 
122
- // ========================= Refs =========================
123
-
124
133
  const api: NotificationAPI = {
125
134
  open(config) {
126
- const mergedConfig = mergeConfig(shareConfig.value, config)
135
+ const mergedConfig = mergeConfig<NotificationListConfig>(shareConfig.value as any, config)
127
136
  if (mergedConfig.key === null || mergedConfig.key === undefined) {
128
137
  mergedConfig.key = `vc-notification-${uniqueKey}`
129
138
  uniqueKey += 1
130
139
  }
131
-
132
140
  taskQueue.value = [...taskQueue.value, { type: 'open', config: mergedConfig }]
133
141
  },
134
142
  close(key) {
@@ -139,14 +147,9 @@ export default function useNotification(rootConfig: MaybeRef<NotificationConfig>
139
147
  },
140
148
  }
141
149
 
142
- // ======================= Container ======================
143
- // React 18 should all in effect that we will check container in each render
144
- // Which means getContainer should be stable.
145
- onMounted(
146
- () => {
147
- container.value = resolveContainer()
148
- },
149
- )
150
+ onMounted(() => {
151
+ container.value = resolveContainer()
152
+ })
150
153
  watch(
151
154
  () => configRef.value.getContainer,
152
155
  () => {
@@ -170,10 +173,9 @@ export default function useNotification(rootConfig: MaybeRef<NotificationConfig>
170
173
  break
171
174
  }
172
175
  })
173
- taskQueue.value = taskQueue.value.filter(task => !taskQueue.value.includes(task))
176
+ taskQueue.value = []
174
177
  }
175
178
  })
176
179
 
177
- // ======================== Return ========================
178
- return [api, contextHolder] as [NotificationAPI, () => VueNode]
180
+ return [api, contextHolder]
179
181
  }
@@ -4,25 +4,29 @@ import { computed, reactive, toRefs, unref, watchEffect } from 'vue'
4
4
 
5
5
  const DEFAULT_OFFSET = 8
6
6
  const DEFAULT_THRESHOLD = 3
7
- const DEFAULT_GAP = 16
8
7
 
9
8
  type StackParams = Exclude<StackConfig, boolean>
10
9
 
11
- type UseStack = (config?: MaybeRef<StackConfig | undefined>) => [ComputedRef<boolean>, ToRefs<StackParams>]
10
+ type UseStack = (
11
+ config?: MaybeRef<StackConfig | undefined>,
12
+ ) => [ComputedRef<boolean>, ToRefs<StackParams>]
12
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
+ */
13
19
  const useStack: UseStack = (config) => {
14
20
  const result: StackParams = reactive({
15
21
  offset: DEFAULT_OFFSET,
16
22
  threshold: DEFAULT_THRESHOLD,
17
- gap: DEFAULT_GAP,
18
23
  })
19
24
 
20
25
  watchEffect(() => {
21
- const _config = unref(config)
22
- if (_config && typeof _config === 'object') {
23
- result.offset = _config.offset ?? DEFAULT_OFFSET
24
- result.threshold = _config.threshold ?? DEFAULT_THRESHOLD
25
- result.gap = _config.gap ?? DEFAULT_GAP
26
+ const value = unref(config)
27
+ if (value && typeof value === 'object') {
28
+ result.offset = value.offset ?? DEFAULT_OFFSET
29
+ result.threshold = value.threshold ?? DEFAULT_THRESHOLD
26
30
  }
27
31
  })
28
32