@v-c/notification 2.0.0-beta.1 → 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 (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 +142 -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 +125 -141
  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,185 +65,162 @@ 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 }) => {
214
- const {
215
- key,
216
- placement: itemPlacement,
217
- onClose: configOnClose,
218
- ...notificationConfig
219
- } = config
193
+ keys.value.map((config) => {
194
+ const { key, placement: _itemPlacement, classNames: configClassNames, styles: configStyles, className: configClassName, style: configStyle, ...notificationConfig } = config
220
195
  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
196
+ const notificationIndex = getIndex(keys.value, key)
224
197
  const stackInThreshold
225
- = stackEnabled.value
226
- && notificationIndex !== undefined
227
- && notificationIndex < stackParams.value.threshold
198
+ = stackEnabled.value && notificationIndex !== undefined && notificationIndex < (stackParams.threshold?.value ?? 0)
228
199
 
229
200
  return (
230
201
  <Notification
231
- {...(notificationConfig as any)}
232
202
  key={strKey}
203
+ {...notificationConfig}
233
204
  ref={(el: any) => {
234
- const node = el?.nativeElement ?? null
235
- setNodeSize(strKey, node)
205
+ const node = unrefElement<HTMLDivElement>(el?.nativeElement as any)
206
+ setNodeSize(strKey, node ?? null)
236
207
  }}
237
208
  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 }}
209
+ class={clsx((ctx.value as any)?.classNames?.notice, configClassName)}
210
+ style={configStyle}
211
+ classNames={fillClassNames([ncs, configClassNames])}
212
+ styles={fillStyles([nss, configStyles])}
213
+ components={{
214
+ ...components,
215
+ ...(config as NotificationListConfig).components,
216
+ }}
242
217
  hovering={stackEnabled.value && listHovering.value}
243
218
  pauseOnHover={config.pauseOnHover ?? pauseOnHover}
244
- offset={notificationPosition.value.get(strKey)}
219
+ offset={positionResult.notificationPosition.get(strKey)}
245
220
  notificationIndex={notificationIndex}
246
- stackInThreshold={!!stackInThreshold}
221
+ stackInThreshold={stackInThreshold}
247
222
  onClose={() => {
248
- configOnClose?.()
223
+ (config as NotificationListConfig).onClose?.()
249
224
  onNoticeClose?.(key)
250
225
  }}
251
226
  />
@@ -258,10 +233,10 @@ const NotificationList = defineComponent({
258
233
  prefixCls,
259
234
  listPrefixCls,
260
235
  `${prefixCls}-${placement}`,
261
- ctxClassNames?.list,
236
+ (ctx.value as any)?.classNames?.list,
262
237
  className,
238
+ ncs?.list,
263
239
  (attrs as any).class,
264
- classNames?.list,
265
240
  {
266
241
  [`${prefixCls}-stack`]: stackEnabled.value,
267
242
  [`${prefixCls}-stack-expanded`]: expanded.value,
@@ -274,25 +249,34 @@ const NotificationList = defineComponent({
274
249
  onMouseleave={() => {
275
250
  listHovering.value = false
276
251
  }}
277
- style={{ ...styles?.list, ...style }}
252
+ style={{ ...nss?.list, ...style, ...((attrs as any).style ?? {}) } as CSSProperties}
278
253
  >
279
254
  <Content
280
255
  ref={contentRef as any}
281
256
  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}
257
+ height={positionResult.totalHeight}
258
+ topNoticeHeight={positionResult.topNoticeHeight}
259
+ topNoticeWidth={positionResult.topNoticeWidth}
260
+ className={ncs?.listContent}
261
+ style={nss?.listContent}
289
262
  >
290
- {renderItems()}
263
+ <TransitionGroup
264
+ tag={false as any}
265
+ appear
266
+ {...motionGroupProps}
267
+ onAfterLeave={checkAllClosed}
268
+ >
269
+ {renderItems()}
270
+ </TransitionGroup>
291
271
  </Content>
292
272
  </div>
293
273
  )
294
274
  }
295
275
  },
296
- })
276
+ {
277
+ name: 'NotificationList',
278
+ inheritAttrs: false,
279
+ },
280
+ )
297
281
 
298
282
  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