@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.
- package/dist/Notification.d.ts +7 -234
- package/dist/Notification.js +199 -135
- package/dist/NotificationList/Content.d.ts +3 -80
- package/dist/NotificationList/Content.js +33 -47
- package/dist/NotificationList/index.d.ts +7 -128
- package/dist/NotificationList/index.js +142 -122
- package/dist/NotificationProvider.d.ts +1 -20
- package/dist/NotificationProvider.js +2 -13
- package/dist/Notifications.d.ts +10 -132
- package/dist/Notifications.js +123 -108
- package/dist/Progress.d.ts +3 -3
- package/dist/Progress.js +24 -11
- package/dist/hooks/useClosable.d.ts +6 -11
- package/dist/hooks/useClosable.js +7 -6
- package/dist/hooks/useListPosition/index.d.ts +14 -13
- package/dist/hooks/useListPosition/index.js +16 -23
- package/dist/hooks/useListPosition/useSizes.d.ts +3 -6
- package/dist/hooks/useListPosition/useSizes.js +12 -10
- package/dist/hooks/useNoticeTimer.d.ts +5 -4
- package/dist/hooks/useNoticeTimer.js +33 -41
- package/dist/hooks/useNotification.d.ts +25 -7
- package/dist/hooks/useNotification.js +20 -25
- package/dist/hooks/useStack.d.ts +9 -8
- package/dist/hooks/useStack.js +17 -11
- package/dist/index.d.ts +9 -8
- package/dist/index.js +2 -2
- package/dist/interface.d.ts +30 -0
- package/dist/interface.js +0 -0
- package/docs/context.vue +1 -1
- package/docs/hooks.vue +4 -4
- package/docs/index.less +143 -62
- package/docs/maxCount.vue +1 -1
- package/docs/showProgress.vue +2 -2
- package/docs/stack.vue +1 -1
- package/package.json +2 -3
- package/src/Notification.tsx +128 -165
- package/src/NotificationList/Content.tsx +36 -54
- package/src/NotificationList/index.tsx +125 -141
- package/src/NotificationProvider.tsx +3 -23
- package/src/Notifications.tsx +62 -84
- package/src/Progress.tsx +19 -15
- package/src/hooks/useClosable.ts +15 -16
- package/src/hooks/useListPosition/index.ts +24 -40
- package/src/hooks/useListPosition/useSizes.ts +16 -15
- package/src/hooks/useNoticeTimer.ts +34 -45
- package/src/hooks/useNotification.tsx +71 -43
- package/src/hooks/useStack.ts +20 -24
- package/src/index.ts +28 -13
- package/src/interface.ts +45 -0
- package/vitest.config.ts +1 -3
- package/tests/index.spec.tsx +0 -200
|
@@ -1,26 +1,24 @@
|
|
|
1
|
-
import type { CSSProperties,
|
|
2
|
-
import type {
|
|
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 {
|
|
9
|
+
import { clsx } from '@v-c/util'
|
|
10
10
|
import { getTransitionGroupProps } from '@v-c/util/dist/utils/transition'
|
|
11
|
-
import {
|
|
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 {
|
|
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
|
|
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?:
|
|
47
|
+
stack?: StackConfig
|
|
50
48
|
motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
|
|
51
|
-
|
|
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
|
-
|
|
68
|
+
list: (NotificationClassNames | undefined)[],
|
|
71
69
|
): NotificationClassNames {
|
|
72
70
|
return noticeSlotKeys.reduce<NotificationClassNames>((merged, key) => {
|
|
73
|
-
merged[key] = clsx(...
|
|
71
|
+
merged[key] = clsx(...list.map(item => item?.[key]))
|
|
74
72
|
return merged
|
|
75
73
|
}, {})
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
function fillStyles(
|
|
76
|
+
function fillStyles(
|
|
77
|
+
list: (NotificationStyles | undefined)[],
|
|
78
|
+
): NotificationStyles {
|
|
79
79
|
return noticeSlotKeys.reduce<NotificationStyles>((merged, key) => {
|
|
80
|
-
merged[key] = Object.assign({}, ...
|
|
80
|
+
merged[key] = Object.assign({}, ...list.map(item => item?.[key]))
|
|
81
81
|
return merged
|
|
82
82
|
}, {})
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
152
|
-
threshold: stackParams.value
|
|
116
|
+
offset: stackParams.offset?.value,
|
|
117
|
+
threshold: stackParams.threshold?.value,
|
|
153
118
|
}
|
|
154
119
|
})
|
|
155
120
|
|
|
156
121
|
// ====================== List Measure ======================
|
|
157
|
-
const gap =
|
|
158
|
-
const contentRef =
|
|
159
|
-
const
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
if (!
|
|
126
|
+
const hasConfigList = computed(() => !!configList.value.length)
|
|
127
|
+
watchEffect(() => {
|
|
128
|
+
if (!hasConfigList.value) {
|
|
173
129
|
return
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
177
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
//
|
|
187
|
-
|
|
188
|
-
if (!
|
|
157
|
+
// Cleanup node sizes when items leave permanently.
|
|
158
|
+
watch(keys, (next, prev) => {
|
|
159
|
+
if (!prev) {
|
|
189
160
|
return
|
|
190
161
|
}
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
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
|
|
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((
|
|
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
|
|
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
|
|
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(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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.
|
|
219
|
+
offset={positionResult.notificationPosition.get(strKey)}
|
|
245
220
|
notificationIndex={notificationIndex}
|
|
246
|
-
stackInThreshold={
|
|
221
|
+
stackInThreshold={stackInThreshold}
|
|
247
222
|
onClose={() => {
|
|
248
|
-
|
|
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
|
-
|
|
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={{ ...
|
|
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
|
|
283
|
-
topNoticeHeight={topNoticeHeight
|
|
284
|
-
topNoticeWidth={topNoticeWidth
|
|
285
|
-
|
|
286
|
-
style={
|
|
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
|
-
|
|
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,
|
|
2
|
-
import {
|
|
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
|
package/src/Notifications.tsx
CHANGED
|
@@ -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 {
|
|
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
|
|
13
|
-
|
|
14
|
-
type
|
|
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
|
-
|
|
36
|
+
const defaults = {
|
|
37
|
+
prefixCls: 'vc-notification',
|
|
38
|
+
} as NotificationsProps
|
|
45
39
|
|
|
46
|
-
const Notifications = defineComponent(
|
|
47
|
-
|
|
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
|
-
|
|
73
|
-
const
|
|
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:
|
|
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:
|
|
96
|
-
configList.value = configList.value.filter(item => item.key !== key)
|
|
97
|
-
},
|
|
73
|
+
close: onNoticeClose,
|
|
98
74
|
destroy: () => {
|
|
99
75
|
configList.value = []
|
|
100
76
|
},
|
|
101
|
-
}
|
|
77
|
+
})
|
|
102
78
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
95
|
+
const onAllNoticeRemoved = (placement: Placement) => {
|
|
121
96
|
const clone = { ...placements.value }
|
|
122
|
-
|
|
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
|
|
110
|
+
props?.onAllRemoved?.()
|
|
134
111
|
emptyRef.value = false
|
|
135
112
|
}
|
|
136
113
|
})
|
|
137
114
|
|
|
138
115
|
return () => {
|
|
139
|
-
const { container
|
|
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
|
-
{
|
|
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={
|
|
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
|
-
|
|
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
|