@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.
- 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 +139 -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 +131 -135
- 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,183 +65,173 @@ 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((
|
|
193
|
+
keys.value.map((config) => {
|
|
214
194
|
const {
|
|
215
195
|
key,
|
|
216
|
-
placement:
|
|
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
|
|
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
|
|
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(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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.
|
|
232
|
+
offset={positionResult.notificationPosition.get(strKey)}
|
|
245
233
|
notificationIndex={notificationIndex}
|
|
246
|
-
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
|
-
|
|
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={{ ...
|
|
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
|
|
283
|
-
topNoticeHeight={topNoticeHeight
|
|
284
|
-
topNoticeWidth={topNoticeWidth
|
|
285
|
-
|
|
286
|
-
style={
|
|
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
|
-
|
|
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,
|
|
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
|