@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
@@ -1,26 +1,24 @@
1
- import type { CSSProperties, PropType, TransitionGroupProps } from 'vue'
2
- import type { StackInput } from '../hooks/useStack'
1
+ import type { CSSProperties, TransitionGroupProps } from 'vue'
2
+ import type { Key, StackConfig } from '../interface'
3
3
  import type {
4
4
  ComponentsType,
5
5
  NotificationClassNames as NoticeClassNames,
6
- NotificationStyles as NoticeStyles,
7
6
  NotificationProps,
7
+ NotificationStyles as NoticeStyles,
8
8
  } from '../Notification'
9
- import { classNames as clsx } from '@v-c/util'
9
+ import { clsx } from '@v-c/util'
10
10
  import { getTransitionGroupProps } from '@v-c/util/dist/utils/transition'
11
- import { computed, defineComponent, inject, nextTick, onMounted, ref, shallowRef, toRef, watch } from 'vue'
11
+ import { unrefElement } from '@v-c/util/dist/vueuse/unref-element'
12
+ import { computed, defineComponent, ref, shallowRef, toRef, TransitionGroup, watch, watchEffect } from 'vue'
12
13
  import useListPosition from '../hooks/useListPosition'
13
14
  import useStack from '../hooks/useStack'
14
15
  import Notification from '../Notification'
15
- import { NotificationContext } from '../NotificationProvider'
16
+ import { useNotificationContext } from '../NotificationProvider'
16
17
  import Content from './Content'
17
18
 
18
- type Key = string | number | symbol
19
-
20
19
  export type Placement = 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight'
21
20
 
22
- export type { StackConfig, StackInput } from '../hooks/useStack'
23
- export type { ComponentsType } from '../Notification'
21
+ export type { StackConfig }
24
22
 
25
23
  export interface NotificationListConfig extends Omit<NotificationProps, 'prefixCls'> {
26
24
  key: Key
@@ -46,9 +44,9 @@ export interface NotificationListProps {
46
44
  classNames?: NotificationClassNames
47
45
  styles?: NotificationStyles
48
46
  components?: ComponentsType
49
- stack?: StackInput
47
+ stack?: StackConfig
50
48
  motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
51
- class?: string
49
+ className?: string
52
50
  style?: CSSProperties
53
51
  onNoticeClose?: (key: Key) => void
54
52
  onAllRemoved?: (placement: Placement) => void
@@ -67,183 +65,173 @@ const noticeSlotKeys: (keyof NoticeClassNames)[] = [
67
65
  ]
68
66
 
69
67
  function fillClassNames(
70
- classNamesList: (NotificationClassNames | undefined)[],
68
+ list: (NotificationClassNames | undefined)[],
71
69
  ): NotificationClassNames {
72
70
  return noticeSlotKeys.reduce<NotificationClassNames>((merged, key) => {
73
- merged[key] = clsx(...classNamesList.map(cn => cn?.[key]))
71
+ merged[key] = clsx(...list.map(item => item?.[key]))
74
72
  return merged
75
73
  }, {})
76
74
  }
77
75
 
78
- function fillStyles(stylesList: (NotificationStyles | undefined)[]): NotificationStyles {
76
+ function fillStyles(
77
+ list: (NotificationStyles | undefined)[],
78
+ ): NotificationStyles {
79
79
  return noticeSlotKeys.reduce<NotificationStyles>((merged, key) => {
80
- merged[key] = Object.assign({}, ...stylesList.map(s => s?.[key]))
80
+ merged[key] = Object.assign({}, ...list.map(item => item?.[key]))
81
81
  return merged
82
82
  }, {})
83
83
  }
84
84
 
85
- const NotificationList = defineComponent({
86
- name: 'NotificationList',
87
- inheritAttrs: false,
88
- props: {
89
- configList: { type: Array as PropType<NotificationListConfig[]>, default: () => [] },
90
- prefixCls: { type: String, default: 'vc-notification' },
91
- placement: { type: String as PropType<Placement>, required: true },
92
- pauseOnHover: { type: Boolean, default: undefined },
93
- classNames: { type: Object as PropType<NotificationClassNames>, default: undefined },
94
- styles: { type: Object as PropType<NotificationStyles>, default: undefined },
95
- components: { type: Object as PropType<ComponentsType>, default: undefined },
96
- stack: { type: [Boolean, Object] as PropType<StackInput>, default: undefined },
97
- motion: {
98
- type: [Object, Function] as PropType<NotificationListProps['motion']>,
99
- default: undefined,
100
- },
101
- class: { type: String, default: undefined },
102
- style: { type: Object as PropType<CSSProperties>, default: undefined },
103
- onNoticeClose: { type: Function as PropType<(key: Key) => void>, default: undefined },
104
- onAllRemoved: { type: Function as PropType<(placement: Placement) => void>, default: undefined },
105
- },
106
- setup(props, { attrs }) {
107
- const ctx = inject(NotificationContext, ref({}))
85
+ function getIndex(keys: { key: Key }[], key: Key): number | undefined {
86
+ const strKey = String(key)
87
+ const index = keys.findIndex(item => String(item.key) === strKey)
88
+ if (index === -1) {
89
+ return undefined
90
+ }
91
+ return keys.length - index - 1
92
+ }
108
93
 
109
- // ========================== Data ==========================
94
+ const NotificationList = defineComponent<NotificationListProps>(
95
+ (props, { attrs }) => {
96
+ const ctx = useNotificationContext()
97
+
98
+ const configList = computed(() => props.configList ?? [])
110
99
  const keys = computed(() =>
111
- props.configList.map(config => ({
112
- config,
113
- key: String(config.key),
114
- })),
100
+ configList.value.map(config => ({ ...config, key: String(config.key) as Key })),
115
101
  )
116
102
 
117
- // ===================== Motion Config ======================
118
- const placementMotion = computed(() => {
119
- if (typeof props.motion === 'function') {
120
- return props.motion(props.placement)
121
- }
122
- return props.motion
123
- })
124
-
125
- const motionGroupProps = computed<TransitionGroupProps>(() => {
126
- const motionVal = placementMotion.value
127
- if (!motionVal) {
128
- return {}
129
- }
130
- if (motionVal.name) {
131
- return getTransitionGroupProps(motionVal.name, motionVal as any)
132
- }
133
- return { ...motionVal }
134
- })
135
-
136
103
  // ====================== Stack State =======================
137
- const [stackEnabled, stackParams] = useStack(toRef(props, 'stack'))
104
+ const stackConfig = toRef(props, 'stack')
105
+ const [stackEnabled, stackParams] = useStack(stackConfig)
138
106
  const listHovering = shallowRef(false)
139
- const expanded = computed(
140
- () =>
141
- stackEnabled.value
142
- && (listHovering.value || keys.value.length <= stackParams.value.threshold),
107
+ const expanded = computed(() =>
108
+ stackEnabled.value && (listHovering.value || keys.value.length <= (stackParams.threshold?.value ?? 0)),
143
109
  )
144
110
 
145
- // ====================== Stack Layout ======================
146
111
  const stackPosition = computed(() => {
147
112
  if (!stackEnabled.value || expanded.value) {
148
113
  return undefined
149
114
  }
150
115
  return {
151
- offset: stackParams.value.offset,
152
- threshold: stackParams.value.threshold,
116
+ offset: stackParams.offset?.value,
117
+ threshold: stackParams.threshold?.value,
153
118
  }
154
119
  })
155
120
 
156
121
  // ====================== List Measure ======================
157
- const gap = shallowRef(0)
158
- const contentRef = shallowRef<{ nativeElement: HTMLElement | null } | null>(null)
159
- const configListRef = computed(() => props.configList)
160
- const [
161
- notificationPosition,
162
- setNodeSize,
163
- totalHeight,
164
- topNoticeHeight,
165
- topNoticeWidth,
166
- ] = useListPosition(configListRef, stackPosition, gap)
167
-
168
- const hasConfigList = computed(() => keys.value.length > 0)
122
+ const gap = ref(0)
123
+ const contentRef = ref<{ nativeElement: { value: HTMLDivElement | null } } | null>(null)
124
+ const [position, setNodeSize] = useListPosition(keys as any, stackPosition as any, gap)
169
125
 
170
- function syncGap() {
171
- const node = contentRef.value?.nativeElement
172
- if (!node)
126
+ const hasConfigList = computed(() => !!configList.value.length)
127
+ watchEffect(() => {
128
+ if (!hasConfigList.value) {
173
129
  return
174
- const { gap: cssGap, rowGap } = window.getComputedStyle(node)
175
- const next = Number.parseFloat(rowGap || cssGap) || 0
176
- if (next !== gap.value) {
177
- gap.value = next
130
+ }
131
+ const listNode = unrefElement<HTMLDivElement>(contentRef.value?.nativeElement as any)
132
+ if (!listNode) {
133
+ return
134
+ }
135
+ const { gap: cssGap, rowGap } = window.getComputedStyle(listNode)
136
+ const nextGap = Number.parseFloat(rowGap || cssGap) || 0
137
+ if (gap.value !== nextGap) {
138
+ gap.value = nextGap
139
+ }
140
+ }, { flush: 'post' })
141
+
142
+ // Notify when list becomes empty (after motion finished).
143
+ const checkAllClosed = () => {
144
+ if (configList.value.length === 0) {
145
+ props.onAllRemoved?.(props.placement)
178
146
  }
179
147
  }
180
148
 
181
- onMounted(syncGap)
182
- watch(hasConfigList, () => {
183
- nextTick(syncGap)
149
+ // Compute motion props per placement.
150
+ const placementMotion = computed(() => {
151
+ if (typeof props.motion === 'function') {
152
+ return props.placement ? props.motion(props.placement) : undefined
153
+ }
154
+ return props.motion
184
155
  })
185
156
 
186
- // ====================== All Removed =======================
187
- function checkAllClosed() {
188
- if (!props.placement) {
157
+ // Cleanup node sizes when items leave permanently.
158
+ watch(keys, (next, prev) => {
159
+ if (!prev) {
189
160
  return
190
161
  }
191
- if (keys.value.length === 0) {
192
- props.onAllRemoved?.(props.placement)
193
- }
194
- }
162
+ const nextKeySet = new Set(next.map(item => String(item.key)))
163
+ prev.forEach((item) => {
164
+ const key = String(item.key)
165
+ if (!nextKeySet.has(key)) {
166
+ setNodeSize(key, null)
167
+ }
168
+ })
169
+ })
195
170
 
196
171
  return () => {
197
172
  const {
198
173
  prefixCls = 'vc-notification',
199
- placement,
200
- classNames,
201
- styles,
202
- components,
203
174
  pauseOnHover,
204
- class: className,
175
+ classNames: ncs,
176
+ styles: nss,
177
+ components,
178
+ placement,
179
+ className,
205
180
  style,
206
181
  onNoticeClose,
207
182
  } = props
208
183
 
209
184
  const listPrefixCls = `${prefixCls}-list`
210
- const ctxClassNames = (ctx.value as any)?.classNames
185
+ const positionResult = position.value
186
+
187
+ let motionGroupProps: TransitionGroupProps = {}
188
+ if (placementMotion.value) {
189
+ motionGroupProps = getTransitionGroupProps(placementMotion.value.name!, placementMotion.value)
190
+ }
211
191
 
212
192
  const renderItems = () =>
213
- keys.value.map(({ config }) => {
193
+ keys.value.map((config) => {
214
194
  const {
215
195
  key,
216
- placement: itemPlacement,
196
+ placement: _itemPlacement,
197
+ classNames: configClassNames,
198
+ styles: configStyles,
199
+ className: configClassName,
200
+ style: configStyle,
201
+ // Extract onClose so the spread below does not also bind it.
202
+ // Vue would otherwise merge our `onClose={...}` and the spread's
203
+ // `onClose` into an Array, breaking `props.onClose?.()` in the
204
+ // notice itself.
217
205
  onClose: configOnClose,
218
206
  ...notificationConfig
219
207
  } = config
220
208
  const strKey = String(key)
221
- const dataIndex = keys.value.findIndex(item => item.key === strKey)
222
- const notificationIndex
223
- = dataIndex === -1 ? undefined : keys.value.length - dataIndex - 1
209
+ const notificationIndex = getIndex(keys.value, key)
224
210
  const stackInThreshold
225
- = stackEnabled.value
226
- && notificationIndex !== undefined
227
- && notificationIndex < stackParams.value.threshold
211
+ = stackEnabled.value && notificationIndex !== undefined && notificationIndex < (stackParams.threshold?.value ?? 0)
228
212
 
229
213
  return (
230
214
  <Notification
231
- {...(notificationConfig as any)}
232
215
  key={strKey}
216
+ {...notificationConfig}
233
217
  ref={(el: any) => {
234
- const node = el?.nativeElement ?? null
235
- setNodeSize(strKey, node)
218
+ const node = unrefElement<HTMLDivElement>(el?.nativeElement as any)
219
+ setNodeSize(strKey, node ?? null)
236
220
  }}
237
221
  prefixCls={prefixCls}
238
- class={clsx(ctxClassNames?.notice, config.class)}
239
- classNames={fillClassNames([classNames, config.classNames])}
240
- styles={fillStyles([styles, config.styles])}
241
- components={{ ...components, ...config.components }}
222
+ class={clsx((ctx.value as any)?.classNames?.notice, configClassName)}
223
+ style={configStyle}
224
+ classNames={fillClassNames([ncs, configClassNames])}
225
+ styles={fillStyles([nss, configStyles])}
226
+ components={{
227
+ ...components,
228
+ ...(config as NotificationListConfig).components,
229
+ }}
242
230
  hovering={stackEnabled.value && listHovering.value}
243
231
  pauseOnHover={config.pauseOnHover ?? pauseOnHover}
244
- offset={notificationPosition.value.get(strKey)}
232
+ offset={positionResult.notificationPosition.get(strKey)}
245
233
  notificationIndex={notificationIndex}
246
- stackInThreshold={!!stackInThreshold}
234
+ stackInThreshold={stackInThreshold}
247
235
  onClose={() => {
248
236
  configOnClose?.()
249
237
  onNoticeClose?.(key)
@@ -258,10 +246,10 @@ const NotificationList = defineComponent({
258
246
  prefixCls,
259
247
  listPrefixCls,
260
248
  `${prefixCls}-${placement}`,
261
- ctxClassNames?.list,
249
+ (ctx.value as any)?.classNames?.list,
262
250
  className,
251
+ ncs?.list,
263
252
  (attrs as any).class,
264
- classNames?.list,
265
253
  {
266
254
  [`${prefixCls}-stack`]: stackEnabled.value,
267
255
  [`${prefixCls}-stack-expanded`]: expanded.value,
@@ -274,25 +262,33 @@ const NotificationList = defineComponent({
274
262
  onMouseleave={() => {
275
263
  listHovering.value = false
276
264
  }}
277
- style={{ ...styles?.list, ...style }}
265
+ style={{ ...nss?.list, ...style, ...((attrs as any).style ?? {}) } as CSSProperties}
278
266
  >
279
267
  <Content
280
268
  ref={contentRef as any}
281
269
  listPrefixCls={listPrefixCls}
282
- height={totalHeight.value}
283
- topNoticeHeight={topNoticeHeight.value}
284
- topNoticeWidth={topNoticeWidth.value}
285
- class={classNames?.listContent}
286
- style={styles?.listContent}
287
- motionProps={motionGroupProps.value}
288
- onAfterLeave={checkAllClosed}
270
+ height={positionResult.totalHeight}
271
+ topNoticeHeight={positionResult.topNoticeHeight}
272
+ topNoticeWidth={positionResult.topNoticeWidth}
273
+ className={ncs?.listContent}
274
+ style={nss?.listContent}
289
275
  >
290
- {renderItems()}
276
+ <TransitionGroup
277
+ appear
278
+ {...motionGroupProps}
279
+ onAfterLeave={checkAllClosed}
280
+ >
281
+ {renderItems()}
282
+ </TransitionGroup>
291
283
  </Content>
292
284
  </div>
293
285
  )
294
286
  }
295
287
  },
296
- })
288
+ {
289
+ name: 'NotificationList',
290
+ inheritAttrs: false,
291
+ },
292
+ )
297
293
 
298
294
  export default NotificationList
@@ -1,5 +1,5 @@
1
- import type { InjectionKey, PropType, Ref } from 'vue'
2
- import { computed, defineComponent, inject, provide, ref } from 'vue'
1
+ import type { InjectionKey, Ref } from 'vue'
2
+ import { inject, provide, ref } from 'vue'
3
3
 
4
4
  export interface NotificationContextProps {
5
5
  classNames?: {
@@ -7,10 +7,7 @@ export interface NotificationContextProps {
7
7
  list?: string
8
8
  }
9
9
  }
10
-
11
- export const NotificationContext: InjectionKey<Ref<NotificationContextProps>> = Symbol(
12
- 'NotificationContext',
13
- )
10
+ export const NotificationContext: InjectionKey<Ref<NotificationContextProps>> = Symbol('NotificationContext')
14
11
 
15
12
  export function useNotificationProvider(props: Ref<NotificationContextProps>) {
16
13
  provide(NotificationContext, props)
@@ -20,20 +17,3 @@ export function useNotificationProvider(props: Ref<NotificationContextProps>) {
20
17
  export function useNotificationContext() {
21
18
  return inject(NotificationContext, ref({}))
22
19
  }
23
-
24
- const NotificationProvider = defineComponent({
25
- name: 'NotificationProvider',
26
- props: {
27
- classNames: {
28
- type: Object as PropType<NotificationContextProps['classNames']>,
29
- default: undefined,
30
- },
31
- },
32
- setup(props, { slots }) {
33
- const ctx = computed<NotificationContextProps>(() => ({ classNames: props.classNames }))
34
- provide(NotificationContext, ctx)
35
- return () => slots.default?.()
36
- },
37
- })
38
-
39
- export default NotificationProvider
@@ -1,34 +1,26 @@
1
+ import type { CSSProperties, TransitionGroupProps } from 'vue'
1
2
  import type { VueNode } from '@v-c/util/dist/type'
2
- import type { CSSProperties, PropType, TransitionGroupProps } from 'vue'
3
- import type {
4
- ComponentsType,
5
- NotificationClassNames,
6
- NotificationListConfig,
7
- NotificationStyles,
8
- Placement,
9
- StackInput,
10
- } from './NotificationList'
3
+ import type { InnerOpenConfig, Key, NotificationListConfig, Placement, Placements, StackConfig } from './interface'
4
+ import type { ComponentsType } from './Notification'
11
5
  import { defineComponent, shallowRef, Teleport, watch } from 'vue'
12
- import NotificationList from './NotificationList'
13
-
14
- type Key = string | number | symbol
6
+ import NotificationList, {
7
+ type NotificationClassNames,
8
+ type NotificationStyles,
9
+ } from './NotificationList'
15
10
 
16
11
  export interface NotificationsProps {
17
12
  prefixCls?: string
13
+ motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
14
+ container?: HTMLElement | ShadowRoot
15
+ maxCount?: number
16
+ pauseOnHover?: boolean
18
17
  classNames?: NotificationClassNames
19
18
  styles?: NotificationStyles
20
19
  components?: ComponentsType
21
20
  className?: (placement: Placement) => string
22
21
  style?: (placement: Placement) => CSSProperties
23
-
24
- container?: HTMLElement | ShadowRoot
25
- motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
26
-
27
- maxCount?: number
28
- pauseOnHover?: boolean
29
- stack?: StackInput
30
-
31
22
  onAllRemoved?: VoidFunction
23
+ stack?: StackConfig
32
24
  renderNotifications?: (
33
25
  node: VueNode,
34
26
  info: { prefixCls: string, key: Key },
@@ -41,45 +33,31 @@ export interface NotificationsRef {
41
33
  destroy: () => void
42
34
  }
43
35
 
44
- type Placements = Partial<Record<Placement, NotificationListConfig[]>>
36
+ const defaults = {
37
+ prefixCls: 'vc-notification',
38
+ } as NotificationsProps
45
39
 
46
- const Notifications = defineComponent({
47
- name: 'Notifications',
48
- inheritAttrs: false,
49
- props: {
50
- prefixCls: { type: String, default: 'vc-notification' },
51
- classNames: { type: Object as PropType<NotificationClassNames>, default: undefined },
52
- styles: { type: Object as PropType<NotificationStyles>, default: undefined },
53
- components: { type: Object as PropType<ComponentsType>, default: undefined },
54
- className: { type: Function as PropType<(p: Placement) => string>, default: undefined },
55
- style: { type: Function as PropType<(p: Placement) => CSSProperties>, default: undefined },
56
- container: { type: Object as PropType<HTMLElement | ShadowRoot>, default: undefined },
57
- motion: {
58
- type: [Object, Function] as PropType<NotificationsProps['motion']>,
59
- default: undefined,
60
- },
61
- maxCount: { type: Number, default: undefined },
62
- pauseOnHover: { type: Boolean, default: undefined },
63
- stack: { type: [Boolean, Object] as PropType<StackInput>, default: undefined },
64
- onAllRemoved: { type: Function as PropType<VoidFunction>, default: undefined },
65
- renderNotifications: {
66
- type: Function as PropType<NotificationsProps['renderNotifications']>,
67
- default: undefined,
68
- },
69
- },
70
- setup(props, { expose }) {
40
+ const Notifications = defineComponent<NotificationsProps>(
41
+ (props = defaults, { expose }) => {
71
42
  const configList = shallowRef<NotificationListConfig[]>([])
72
- const placements = shallowRef<Placements>({})
73
- const emptyRef = shallowRef(false)
43
+
44
+ const onNoticeClose = (key: Key) => {
45
+ const config = configList.value.find(item => item.key === key)
46
+ const closable = config?.closable
47
+ const closableObj = closable && typeof closable === 'object' ? closable : null
48
+ closableObj?.onClose?.()
49
+ config?.onClose?.()
50
+ configList.value = configList.value.filter(item => item.key !== key)
51
+ }
74
52
 
75
53
  expose({
76
54
  open: (config: NotificationListConfig) => {
77
55
  const list = configList.value
78
56
  let clone = [...list]
79
57
  const index = clone.findIndex(item => item.key === config.key)
80
- const innerConfig: NotificationListConfig = { ...config }
58
+ const innerConfig: InnerOpenConfig = { ...config }
81
59
  if (index >= 0) {
82
- innerConfig.times = (list[index]?.times ?? 0) + 1
60
+ innerConfig.times = ((list[index] as InnerOpenConfig)?.times ?? 0) + 1
83
61
  clone[index] = innerConfig
84
62
  }
85
63
  else {
@@ -92,81 +70,78 @@ const Notifications = defineComponent({
92
70
  }
93
71
  configList.value = clone
94
72
  },
95
- close: (key: Key) => {
96
- configList.value = configList.value.filter(item => item.key !== key)
97
- },
73
+ close: onNoticeClose,
98
74
  destroy: () => {
99
75
  configList.value = []
100
76
  },
101
- } as NotificationsRef)
77
+ })
102
78
 
103
- watch(
104
- configList,
105
- () => {
106
- const next: Placements = {}
107
- configList.value.forEach((config) => {
108
- const placement = config.placement ?? 'topRight'
109
- next[placement] = next[placement] || []
110
- next[placement]!.push(config)
111
- })
112
- Object.keys(placements.value).forEach((placement) => {
113
- next[placement as Placement] = next[placement as Placement] || []
114
- })
115
- placements.value = next
116
- },
117
- { immediate: true },
118
- )
79
+ const placements = shallowRef<Placements>({})
80
+
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
+ })
119
94
 
120
- function onAllNoticeRemoved(placement: Placement) {
95
+ const onAllNoticeRemoved = (placement: Placement) => {
121
96
  const clone = { ...placements.value }
122
- if (!(clone[placement] || []).length) {
97
+ const list = clone[placement] || []
98
+ if (!list.length) {
123
99
  delete clone[placement]
124
100
  }
125
101
  placements.value = clone
126
102
  }
127
103
 
104
+ const emptyRef = shallowRef(false)
128
105
  watch(placements, () => {
129
106
  if (Object.keys(placements.value).length > 0) {
130
107
  emptyRef.value = true
131
108
  }
132
109
  else if (emptyRef.value) {
133
- props.onAllRemoved?.()
110
+ props?.onAllRemoved?.()
134
111
  emptyRef.value = false
135
112
  }
136
113
  })
137
114
 
138
115
  return () => {
139
- const { container, prefixCls = 'vc-notification' } = props
116
+ const { container } = props
117
+ const prefixCls = props.prefixCls ?? defaults.prefixCls!
140
118
  if (!container) {
141
119
  return null
142
120
  }
143
121
 
144
- const placementList = Object.keys(placements.value) as Placement[]
145
-
146
122
  return (
147
123
  <Teleport to={container}>
148
- {placementList.map((placement) => {
124
+ {Object.keys(placements.value).map((rawPlacement) => {
125
+ const placement = rawPlacement as Placement
126
+ const placementConfigList = placements.value[placement]
149
127
  const list = (
150
128
  <NotificationList
151
129
  key={placement}
152
- configList={placements.value[placement]}
130
+ configList={placementConfigList}
153
131
  placement={placement}
154
132
  prefixCls={prefixCls}
155
133
  pauseOnHover={props.pauseOnHover}
156
134
  classNames={props.classNames}
157
135
  styles={props.styles}
158
136
  components={props.components}
159
- class={props.className?.(placement)}
137
+ className={props.className?.(placement)}
160
138
  style={props.style?.(placement)}
161
139
  motion={props.motion}
162
140
  stack={props.stack}
163
- onNoticeClose={(key) => {
164
- configList.value = configList.value.filter(item => item.key !== key)
165
- }}
166
141
  onAllRemoved={onAllNoticeRemoved}
142
+ onNoticeClose={onNoticeClose}
167
143
  />
168
144
  )
169
-
170
145
  return props.renderNotifications
171
146
  ? props.renderNotifications(list, { prefixCls, key: placement })
172
147
  : list
@@ -175,6 +150,9 @@ const Notifications = defineComponent({
175
150
  )
176
151
  }
177
152
  },
178
- })
153
+ {
154
+ name: 'Notifications',
155
+ },
156
+ )
179
157
 
180
158
  export default Notifications