@v-c/notification 1.0.0 → 2.0.0-beta.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 (67) hide show
  1. package/dist/Notification.d.ts +286 -0
  2. package/dist/Notification.js +237 -0
  3. package/dist/NotificationList/Content.d.ts +88 -0
  4. package/dist/NotificationList/Content.js +74 -0
  5. package/dist/NotificationList/index.d.ts +156 -0
  6. package/dist/NotificationList/index.js +204 -0
  7. package/dist/NotificationProvider.d.ts +20 -1
  8. package/dist/NotificationProvider.js +16 -3
  9. package/dist/Notifications.d.ts +136 -8
  10. package/dist/Notifications.js +118 -109
  11. package/dist/Progress.d.ts +8 -0
  12. package/dist/Progress.js +18 -0
  13. package/dist/hooks/useClosable.d.ts +22 -0
  14. package/dist/hooks/useClosable.js +33 -0
  15. package/dist/hooks/useListPosition/index.d.ts +17 -0
  16. package/dist/hooks/useListPosition/index.js +48 -0
  17. package/dist/hooks/useListPosition/useSizes.d.ts +13 -0
  18. package/dist/hooks/useListPosition/useSizes.js +29 -0
  19. package/dist/hooks/useNoticeTimer.d.ts +6 -0
  20. package/dist/hooks/useNoticeTimer.js +71 -0
  21. package/dist/hooks/useNotification.d.ts +8 -24
  22. package/dist/hooks/useNotification.js +33 -22
  23. package/dist/hooks/useStack.d.ts +8 -4
  24. package/dist/hooks/useStack.js +15 -18
  25. package/dist/index.d.ts +7 -5
  26. package/dist/index.js +5 -3
  27. package/docs/context.vue +1 -1
  28. package/docs/hooks.vue +4 -4
  29. package/docs/index.less +62 -143
  30. package/docs/maxCount.vue +1 -1
  31. package/docs/showProgress.vue +2 -2
  32. package/docs/stack.vue +1 -1
  33. package/package.json +5 -4
  34. package/src/Notification.tsx +363 -0
  35. package/src/NotificationList/Content.tsx +84 -0
  36. package/src/NotificationList/index.tsx +298 -0
  37. package/src/NotificationProvider.tsx +23 -3
  38. package/src/Notifications.tsx +103 -87
  39. package/src/Progress.tsx +23 -0
  40. package/src/hooks/useClosable.ts +54 -0
  41. package/src/hooks/useListPosition/index.ts +85 -0
  42. package/src/hooks/useListPosition/useSizes.ts +42 -0
  43. package/src/hooks/useNoticeTimer.ts +96 -0
  44. package/src/hooks/useNotification.tsx +54 -80
  45. package/src/hooks/useStack.ts +26 -18
  46. package/src/index.ts +31 -5
  47. package/tests/index.spec.tsx +200 -0
  48. package/vite.config.ts +4 -3
  49. package/vitest.config.ts +3 -1
  50. package/dist/Notice.cjs +0 -235
  51. package/dist/Notice.d.ts +0 -15
  52. package/dist/Notice.js +0 -227
  53. package/dist/NoticeList.cjs +0 -170
  54. package/dist/NoticeList.d.ts +0 -13
  55. package/dist/NoticeList.js +0 -164
  56. package/dist/NotificationProvider.cjs +0 -14
  57. package/dist/Notifications.cjs +0 -146
  58. package/dist/_virtual/rolldown_runtime.cjs +0 -21
  59. package/dist/hooks/useNotification.cjs +0 -93
  60. package/dist/hooks/useStack.cjs +0 -27
  61. package/dist/index.cjs +0 -7
  62. package/dist/interface.cjs +0 -1
  63. package/dist/interface.d.ts +0 -55
  64. package/dist/interface.js +0 -0
  65. package/src/Notice.tsx +0 -212
  66. package/src/NoticeList.tsx +0 -219
  67. package/src/interface.ts +0 -61
@@ -0,0 +1,298 @@
1
+ import type { CSSProperties, PropType, TransitionGroupProps } from 'vue'
2
+ import type { StackInput } from '../hooks/useStack'
3
+ import type {
4
+ ComponentsType,
5
+ NotificationClassNames as NoticeClassNames,
6
+ NotificationStyles as NoticeStyles,
7
+ NotificationProps,
8
+ } from '../Notification'
9
+ import { classNames as clsx } from '@v-c/util'
10
+ import { getTransitionGroupProps } from '@v-c/util/dist/utils/transition'
11
+ import { computed, defineComponent, inject, nextTick, onMounted, ref, shallowRef, toRef, watch } from 'vue'
12
+ import useListPosition from '../hooks/useListPosition'
13
+ import useStack from '../hooks/useStack'
14
+ import Notification from '../Notification'
15
+ import { NotificationContext } from '../NotificationProvider'
16
+ import Content from './Content'
17
+
18
+ type Key = string | number | symbol
19
+
20
+ export type Placement = 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight'
21
+
22
+ export type { StackConfig, StackInput } from '../hooks/useStack'
23
+ export type { ComponentsType } from '../Notification'
24
+
25
+ export interface NotificationListConfig extends Omit<NotificationProps, 'prefixCls'> {
26
+ key: Key
27
+ placement?: Placement
28
+ times?: number
29
+ }
30
+
31
+ export interface NotificationClassNames extends NoticeClassNames {
32
+ list?: string
33
+ listContent?: string
34
+ }
35
+
36
+ export interface NotificationStyles extends NoticeStyles {
37
+ list?: CSSProperties
38
+ listContent?: CSSProperties
39
+ }
40
+
41
+ export interface NotificationListProps {
42
+ configList?: NotificationListConfig[]
43
+ prefixCls?: string
44
+ placement: Placement
45
+ pauseOnHover?: boolean
46
+ classNames?: NotificationClassNames
47
+ styles?: NotificationStyles
48
+ components?: ComponentsType
49
+ stack?: StackInput
50
+ motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
51
+ class?: string
52
+ style?: CSSProperties
53
+ onNoticeClose?: (key: Key) => void
54
+ onAllRemoved?: (placement: Placement) => void
55
+ }
56
+
57
+ const noticeSlotKeys: (keyof NoticeClassNames)[] = [
58
+ 'wrapper',
59
+ 'root',
60
+ 'icon',
61
+ 'section',
62
+ 'title',
63
+ 'description',
64
+ 'actions',
65
+ 'close',
66
+ 'progress',
67
+ ]
68
+
69
+ function fillClassNames(
70
+ classNamesList: (NotificationClassNames | undefined)[],
71
+ ): NotificationClassNames {
72
+ return noticeSlotKeys.reduce<NotificationClassNames>((merged, key) => {
73
+ merged[key] = clsx(...classNamesList.map(cn => cn?.[key]))
74
+ return merged
75
+ }, {})
76
+ }
77
+
78
+ function fillStyles(stylesList: (NotificationStyles | undefined)[]): NotificationStyles {
79
+ return noticeSlotKeys.reduce<NotificationStyles>((merged, key) => {
80
+ merged[key] = Object.assign({}, ...stylesList.map(s => s?.[key]))
81
+ return merged
82
+ }, {})
83
+ }
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({}))
108
+
109
+ // ========================== Data ==========================
110
+ const keys = computed(() =>
111
+ props.configList.map(config => ({
112
+ config,
113
+ key: String(config.key),
114
+ })),
115
+ )
116
+
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
+ // ====================== Stack State =======================
137
+ const [stackEnabled, stackParams] = useStack(toRef(props, 'stack'))
138
+ const listHovering = shallowRef(false)
139
+ const expanded = computed(
140
+ () =>
141
+ stackEnabled.value
142
+ && (listHovering.value || keys.value.length <= stackParams.value.threshold),
143
+ )
144
+
145
+ // ====================== Stack Layout ======================
146
+ const stackPosition = computed(() => {
147
+ if (!stackEnabled.value || expanded.value) {
148
+ return undefined
149
+ }
150
+ return {
151
+ offset: stackParams.value.offset,
152
+ threshold: stackParams.value.threshold,
153
+ }
154
+ })
155
+
156
+ // ====================== 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)
169
+
170
+ function syncGap() {
171
+ const node = contentRef.value?.nativeElement
172
+ if (!node)
173
+ 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
178
+ }
179
+ }
180
+
181
+ onMounted(syncGap)
182
+ watch(hasConfigList, () => {
183
+ nextTick(syncGap)
184
+ })
185
+
186
+ // ====================== All Removed =======================
187
+ function checkAllClosed() {
188
+ if (!props.placement) {
189
+ return
190
+ }
191
+ if (keys.value.length === 0) {
192
+ props.onAllRemoved?.(props.placement)
193
+ }
194
+ }
195
+
196
+ return () => {
197
+ const {
198
+ prefixCls = 'vc-notification',
199
+ placement,
200
+ classNames,
201
+ styles,
202
+ components,
203
+ pauseOnHover,
204
+ class: className,
205
+ style,
206
+ onNoticeClose,
207
+ } = props
208
+
209
+ const listPrefixCls = `${prefixCls}-list`
210
+ const ctxClassNames = (ctx.value as any)?.classNames
211
+
212
+ const renderItems = () =>
213
+ keys.value.map(({ config }) => {
214
+ const {
215
+ key,
216
+ placement: itemPlacement,
217
+ onClose: configOnClose,
218
+ ...notificationConfig
219
+ } = config
220
+ 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
224
+ const stackInThreshold
225
+ = stackEnabled.value
226
+ && notificationIndex !== undefined
227
+ && notificationIndex < stackParams.value.threshold
228
+
229
+ return (
230
+ <Notification
231
+ {...(notificationConfig as any)}
232
+ key={strKey}
233
+ ref={(el: any) => {
234
+ const node = el?.nativeElement ?? null
235
+ setNodeSize(strKey, node)
236
+ }}
237
+ 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 }}
242
+ hovering={stackEnabled.value && listHovering.value}
243
+ pauseOnHover={config.pauseOnHover ?? pauseOnHover}
244
+ offset={notificationPosition.value.get(strKey)}
245
+ notificationIndex={notificationIndex}
246
+ stackInThreshold={!!stackInThreshold}
247
+ onClose={() => {
248
+ configOnClose?.()
249
+ onNoticeClose?.(key)
250
+ }}
251
+ />
252
+ )
253
+ })
254
+
255
+ return (
256
+ <div
257
+ class={clsx(
258
+ prefixCls,
259
+ listPrefixCls,
260
+ `${prefixCls}-${placement}`,
261
+ ctxClassNames?.list,
262
+ className,
263
+ (attrs as any).class,
264
+ classNames?.list,
265
+ {
266
+ [`${prefixCls}-stack`]: stackEnabled.value,
267
+ [`${prefixCls}-stack-expanded`]: expanded.value,
268
+ [`${listPrefixCls}-hovered`]: listHovering.value,
269
+ },
270
+ )}
271
+ onMouseenter={() => {
272
+ listHovering.value = true
273
+ }}
274
+ onMouseleave={() => {
275
+ listHovering.value = false
276
+ }}
277
+ style={{ ...styles?.list, ...style }}
278
+ >
279
+ <Content
280
+ ref={contentRef as any}
281
+ 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}
289
+ >
290
+ {renderItems()}
291
+ </Content>
292
+ </div>
293
+ )
294
+ }
295
+ },
296
+ })
297
+
298
+ export default NotificationList
@@ -1,5 +1,5 @@
1
- import type { InjectionKey, Ref } from 'vue'
2
- import { inject, provide, ref } from 'vue'
1
+ import type { InjectionKey, PropType, Ref } from 'vue'
2
+ import { computed, defineComponent, inject, provide, ref } from 'vue'
3
3
 
4
4
  export interface NotificationContextProps {
5
5
  classNames?: {
@@ -7,7 +7,10 @@ export interface NotificationContextProps {
7
7
  list?: string
8
8
  }
9
9
  }
10
- export const NotificationContext: InjectionKey<Ref<NotificationContextProps>> = Symbol('NotificationContext')
10
+
11
+ export const NotificationContext: InjectionKey<Ref<NotificationContextProps>> = Symbol(
12
+ 'NotificationContext',
13
+ )
11
14
 
12
15
  export function useNotificationProvider(props: Ref<NotificationContextProps>) {
13
16
  provide(NotificationContext, props)
@@ -17,3 +20,20 @@ export function useNotificationProvider(props: Ref<NotificationContextProps>) {
17
20
  export function useNotificationContext() {
18
21
  return inject(NotificationContext, ref({}))
19
22
  }
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,18 +1,34 @@
1
1
  import type { VueNode } from '@v-c/util/dist/type'
2
- import type { CSSProperties, TransitionGroupProps } from 'vue'
3
- import type { InnerOpenConfig, Key, OpenConfig, Placement, Placements, StackConfig } from './interface.ts'
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'
4
11
  import { defineComponent, shallowRef, Teleport, watch } from 'vue'
5
- import NoticeList from './NoticeList.tsx'
12
+ import NotificationList from './NotificationList'
13
+
14
+ type Key = string | number | symbol
6
15
 
7
16
  export interface NotificationsProps {
8
17
  prefixCls?: string
9
- motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
10
- container?: HTMLElement | ShadowRoot
11
- maxCount?: number
18
+ classNames?: NotificationClassNames
19
+ styles?: NotificationStyles
20
+ components?: ComponentsType
12
21
  className?: (placement: Placement) => string
13
22
  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
+
14
31
  onAllRemoved?: VoidFunction
15
- stack?: StackConfig
16
32
  renderNotifications?: (
17
33
  node: VueNode,
18
34
  info: { prefixCls: string, key: Key },
@@ -20,41 +36,50 @@ export interface NotificationsProps {
20
36
  }
21
37
 
22
38
  export interface NotificationsRef {
23
- open: (config: OpenConfig) => void
39
+ open: (config: NotificationListConfig) => void
24
40
  close: (key: Key) => void
25
41
  destroy: () => void
26
42
  }
27
43
 
28
- const defaults = {
29
- prefixCls: 'vc-notification',
30
- } as NotificationsProps
31
-
32
- const Notifications = defineComponent<NotificationsProps>(
33
- (props = defaults, { expose }) => {
34
- const configList = shallowRef<OpenConfig[]>([])
35
- // ======================== Close =========================
36
- const onNoticeClose = (key: Key) => {
37
- // Trigger close event
38
- const config = configList.value.find(item => item.key === key)
39
- const closable = config?.closable
40
- const closableObj = closable && typeof closable === 'object' ? closable : {}
41
- closableObj.onClose?.()
42
- config?.onClose?.()
43
- configList.value = configList.value.filter(item => item.key !== key)
44
- }
44
+ type Placements = Partial<Record<Placement, NotificationListConfig[]>>
45
+
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 }) {
71
+ const configList = shallowRef<NotificationListConfig[]>([])
72
+ const placements = shallowRef<Placements>({})
73
+ const emptyRef = shallowRef(false)
45
74
 
46
- // ========================= Refs =========================
47
75
  expose({
48
- open: (config: OpenConfig) => {
76
+ open: (config: NotificationListConfig) => {
49
77
  const list = configList.value
50
- let clone = [...configList.value]
51
- // Replace if exist
78
+ let clone = [...list]
52
79
  const index = clone.findIndex(item => item.key === config.key)
53
- const innerConfig: InnerOpenConfig = {
54
- ...config,
55
- }
80
+ const innerConfig: NotificationListConfig = { ...config }
56
81
  if (index >= 0) {
57
- innerConfig.times = ((list[index] as InnerOpenConfig)?.times || 0) + 1
82
+ innerConfig.times = (list[index]?.times ?? 0) + 1
58
83
  clone[index] = innerConfig
59
84
  }
60
85
  else {
@@ -67,98 +92,89 @@ const Notifications = defineComponent<NotificationsProps>(
67
92
  }
68
93
  configList.value = clone
69
94
  },
70
- close: onNoticeClose,
95
+ close: (key: Key) => {
96
+ configList.value = configList.value.filter(item => item.key !== key)
97
+ },
71
98
  destroy: () => {
72
99
  configList.value = []
73
100
  },
74
- })
75
-
76
- // ====================== Placements ======================
77
-
78
- const placements = shallowRef<Placements>({})
101
+ } as NotificationsRef)
79
102
 
80
103
  watch(
81
104
  configList,
82
105
  () => {
83
- const nextPlacements: Placements = {}
106
+ const next: Placements = {}
84
107
  configList.value.forEach((config) => {
85
- const { placement = 'topRight' } = config
86
- if (placement) {
87
- nextPlacements[placement] = nextPlacements[placement] || []
88
- nextPlacements[placement].push(config)
89
- }
108
+ const placement = config.placement ?? 'topRight'
109
+ next[placement] = next[placement] || []
110
+ next[placement]!.push(config)
90
111
  })
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] || []
112
+ Object.keys(placements.value).forEach((placement) => {
113
+ next[placement as Placement] = next[placement as Placement] || []
95
114
  })
96
- placements.value = nextPlacements
115
+ placements.value = next
97
116
  },
117
+ { immediate: true },
98
118
  )
99
119
 
100
- // Clean up container if all notices fade out
101
- const onAllNoticeRemoved = (placement: Placement) => {
120
+ function onAllNoticeRemoved(placement: Placement) {
102
121
  const clone = { ...placements.value }
103
- const list = clone[placement] || []
104
- if (!list.length) {
122
+ if (!(clone[placement] || []).length) {
105
123
  delete clone[placement]
106
124
  }
107
125
  placements.value = clone
108
126
  }
109
127
 
110
- // Effect tell that placements is empty now
111
- 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
- )
128
+ watch(placements, () => {
129
+ if (Object.keys(placements.value).length > 0) {
130
+ emptyRef.value = true
131
+ }
132
+ else if (emptyRef.value) {
133
+ props.onAllRemoved?.()
134
+ emptyRef.value = false
135
+ }
136
+ })
126
137
 
127
138
  return () => {
128
- const { container } = props
129
- const prefixCls = props.prefixCls ?? defaults.prefixCls ?? ''
130
- // ======================== Render ========================
139
+ const { container, prefixCls = 'vc-notification' } = props
131
140
  if (!container) {
132
141
  return null
133
142
  }
134
143
 
144
+ const placementList = Object.keys(placements.value) as Placement[]
145
+
135
146
  return (
136
147
  <Teleport to={container}>
137
- {Object.keys(placements.value).map((placement) => {
138
- const placementConfigList = placements.value[placement as Placement]
148
+ {placementList.map((placement) => {
139
149
  const list = (
140
- <NoticeList
150
+ <NotificationList
141
151
  key={placement}
142
- configList={placementConfigList}
143
- placement={placement as Placement}
152
+ configList={placements.value[placement]}
153
+ placement={placement}
144
154
  prefixCls={prefixCls}
145
- class={props.className?.(placement as Placement)}
146
- style={props.style?.(placement as Placement)}
155
+ pauseOnHover={props.pauseOnHover}
156
+ classNames={props.classNames}
157
+ styles={props.styles}
158
+ components={props.components}
159
+ class={props.className?.(placement)}
160
+ style={props.style?.(placement)}
147
161
  motion={props.motion}
148
162
  stack={props.stack}
149
- onAllNoticeRemoved={() => onAllNoticeRemoved(placement as Placement)}
150
- onNoticeClose={onNoticeClose}
163
+ onNoticeClose={(key) => {
164
+ configList.value = configList.value.filter(item => item.key !== key)
165
+ }}
166
+ onAllRemoved={onAllNoticeRemoved}
151
167
  />
152
168
  )
153
- return props.renderNotifications ? props.renderNotifications(list, { prefixCls, key: placement }) : list
169
+
170
+ return props.renderNotifications
171
+ ? props.renderNotifications(list, { prefixCls, key: placement })
172
+ : list
154
173
  })}
155
174
  </Teleport>
156
175
  )
157
176
  }
158
177
  },
159
- {
160
- name: 'Notifications',
161
- },
162
- )
178
+ })
163
179
 
164
180
  export default Notifications
@@ -0,0 +1,23 @@
1
+ import type { CSSProperties, FunctionalComponent } from 'vue'
2
+
3
+ export interface NotificationProgressProps {
4
+ class?: string
5
+ style?: CSSProperties
6
+ percent: number
7
+ }
8
+
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
22
+
23
+ export default Progress
@@ -0,0 +1,54 @@
1
+ import type { VueNode } from '@v-c/util/dist/type'
2
+ import type { ComputedRef, MaybeRef } from 'vue'
3
+ import pickAttrs from '@v-c/util/dist/pickAttrs'
4
+ import { computed, unref } from 'vue'
5
+
6
+ export type ClosableConfig = {
7
+ closeIcon?: VueNode
8
+ disabled?: boolean
9
+ onClose?: VoidFunction
10
+ } & Record<string, any>
11
+
12
+ export type ClosableType = boolean | ClosableConfig | null | undefined
13
+
14
+ export interface ParsedClosableConfig {
15
+ closeIcon: VueNode
16
+ disabled: boolean
17
+ onClose?: VoidFunction
18
+ [key: string]: any
19
+ }
20
+
21
+ /**
22
+ * Normalize the closable option into an enabled flag, parsed config, and aria props.
23
+ */
24
+ export default function useClosable(closable: MaybeRef<ClosableType>): [
25
+ ComputedRef<boolean>,
26
+ ComputedRef<ParsedClosableConfig>,
27
+ ComputedRef<Record<string, any>>,
28
+ ] {
29
+ const closableObj = computed<ClosableConfig>(() => {
30
+ const value = unref(closable)
31
+ if (value === false) {
32
+ return { closeIcon: null, disabled: true }
33
+ }
34
+ if (typeof value === 'object' && value !== null) {
35
+ return value
36
+ }
37
+ return {}
38
+ })
39
+
40
+ const enabled = computed(() => !!unref(closable))
41
+
42
+ const closableConfig = computed<ParsedClosableConfig>(() => {
43
+ const obj = closableObj.value
44
+ return {
45
+ ...obj,
46
+ closeIcon: 'closeIcon' in obj ? obj.closeIcon : '×',
47
+ disabled: obj.disabled ?? false,
48
+ }
49
+ })
50
+
51
+ const closableAriaProps = computed(() => pickAttrs(closableConfig.value, true))
52
+
53
+ return [enabled, closableConfig, closableAriaProps]
54
+ }