@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
package/src/Progress.tsx
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
import type { CSSProperties
|
|
1
|
+
import type { CSSProperties } from 'vue'
|
|
2
|
+
import { defineComponent } from 'vue'
|
|
2
3
|
|
|
3
4
|
export interface NotificationProgressProps {
|
|
4
|
-
|
|
5
|
+
className?: string
|
|
5
6
|
style?: CSSProperties
|
|
6
7
|
percent: number
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
const Progress
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
const Progress = defineComponent<NotificationProgressProps>(
|
|
11
|
+
(props) => {
|
|
12
|
+
return () => (
|
|
13
|
+
<progress
|
|
14
|
+
class={props.className}
|
|
15
|
+
max="100"
|
|
16
|
+
value={props.percent}
|
|
17
|
+
style={props.style}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'NotificationProgress',
|
|
23
|
+
inheritAttrs: false,
|
|
24
|
+
},
|
|
25
|
+
)
|
|
22
26
|
|
|
23
27
|
export default Progress
|
package/src/hooks/useClosable.ts
CHANGED
|
@@ -1,33 +1,30 @@
|
|
|
1
|
+
import type { AriaAttributes, ComputedRef } from 'vue'
|
|
1
2
|
import type { VueNode } from '@v-c/util/dist/type'
|
|
2
|
-
import type { ComputedRef, MaybeRef } from 'vue'
|
|
3
3
|
import pickAttrs from '@v-c/util/dist/pickAttrs'
|
|
4
|
-
import { computed
|
|
4
|
+
import { computed } from 'vue'
|
|
5
5
|
|
|
6
6
|
export type ClosableConfig = {
|
|
7
7
|
closeIcon?: VueNode
|
|
8
8
|
disabled?: boolean
|
|
9
9
|
onClose?: VoidFunction
|
|
10
|
-
} & Record
|
|
10
|
+
} & AriaAttributes & Record<`data-${string}`, unknown>
|
|
11
11
|
|
|
12
12
|
export type ClosableType = boolean | ClosableConfig | null | undefined
|
|
13
13
|
|
|
14
|
-
export interface ParsedClosableConfig {
|
|
14
|
+
export interface ParsedClosableConfig extends ClosableConfig {
|
|
15
15
|
closeIcon: VueNode
|
|
16
16
|
disabled: boolean
|
|
17
|
-
onClose?: VoidFunction
|
|
18
|
-
[key: string]: any
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
/**
|
|
22
|
-
*
|
|
20
|
+
* Normalizes the closable option into a boolean flag, parsed config, and
|
|
21
|
+
* aria props for the close button. Mirrors rc-notification@2.0 useClosable.
|
|
23
22
|
*/
|
|
24
|
-
export default function useClosable(
|
|
25
|
-
ComputedRef<
|
|
26
|
-
|
|
27
|
-
ComputedRef<Record<string, any>>,
|
|
28
|
-
] {
|
|
23
|
+
export default function useClosable(
|
|
24
|
+
closable: ComputedRef<ClosableType>,
|
|
25
|
+
): [ComputedRef<boolean>, ComputedRef<ParsedClosableConfig>, ComputedRef<Record<string, unknown>>] {
|
|
29
26
|
const closableObj = computed<ClosableConfig>(() => {
|
|
30
|
-
const value =
|
|
27
|
+
const value = closable.value
|
|
31
28
|
if (value === false) {
|
|
32
29
|
return { closeIcon: null, disabled: true }
|
|
33
30
|
}
|
|
@@ -37,8 +34,6 @@ export default function useClosable(closable: MaybeRef<ClosableType>): [
|
|
|
37
34
|
return {}
|
|
38
35
|
})
|
|
39
36
|
|
|
40
|
-
const enabled = computed(() => !!unref(closable))
|
|
41
|
-
|
|
42
37
|
const closableConfig = computed<ParsedClosableConfig>(() => {
|
|
43
38
|
const obj = closableObj.value
|
|
44
39
|
return {
|
|
@@ -50,5 +45,9 @@ export default function useClosable(closable: MaybeRef<ClosableType>): [
|
|
|
50
45
|
|
|
51
46
|
const closableAriaProps = computed(() => pickAttrs(closableConfig.value, true))
|
|
52
47
|
|
|
53
|
-
return [
|
|
48
|
+
return [
|
|
49
|
+
computed(() => !!closable.value),
|
|
50
|
+
closableConfig,
|
|
51
|
+
closableAriaProps,
|
|
52
|
+
]
|
|
54
53
|
}
|
|
@@ -1,85 +1,69 @@
|
|
|
1
|
-
import type { ComputedRef,
|
|
2
|
-
import type {
|
|
3
|
-
import { computed
|
|
1
|
+
import type { ComputedRef, Ref } from 'vue'
|
|
2
|
+
import type { Key } from '../../interface'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
4
|
import useSizes from './useSizes'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
key: Key
|
|
6
|
+
export interface ListPositionStackConfig {
|
|
7
|
+
threshold?: number
|
|
8
|
+
offset?: number
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Calculates each notification's position and the full list height.
|
|
13
|
+
* Mirrors rc-notification@2.0 useListPosition.
|
|
14
14
|
*/
|
|
15
15
|
export default function useListPosition(
|
|
16
|
-
configList:
|
|
17
|
-
stack:
|
|
18
|
-
gap:
|
|
19
|
-
)
|
|
20
|
-
ComputedRef<Map<string, number>>,
|
|
21
|
-
(key: string, node: HTMLElement | null) => void,
|
|
22
|
-
ComputedRef<number>,
|
|
23
|
-
ComputedRef<number | undefined>,
|
|
24
|
-
ComputedRef<number | undefined>,
|
|
25
|
-
] {
|
|
16
|
+
configList: ComputedRef<{ key: Key }[]>,
|
|
17
|
+
stack: ComputedRef<ListPositionStackConfig | undefined>,
|
|
18
|
+
gap: Ref<number>,
|
|
19
|
+
) {
|
|
26
20
|
const [sizeMap, setNodeSize] = useSizes()
|
|
27
21
|
|
|
28
22
|
const result = computed(() => {
|
|
29
|
-
const list = unref(configList)
|
|
30
|
-
const stackValue = unref(stack)
|
|
31
|
-
const gapValue = unref(gap) ?? 0
|
|
32
|
-
|
|
33
23
|
let offsetY = 0
|
|
34
24
|
let nextTotalHeight = 0
|
|
35
|
-
const
|
|
36
|
-
const
|
|
25
|
+
const stackParams = stack.value
|
|
26
|
+
const stackThreshold = stackParams?.threshold ?? 0
|
|
27
|
+
const stackOffset = stackParams?.offset ?? 0
|
|
28
|
+
const notificationPosition = new Map<string, number>()
|
|
37
29
|
let topNoticeHeight: number | undefined
|
|
38
30
|
let topNoticeWidth: number | undefined
|
|
39
31
|
|
|
40
|
-
|
|
32
|
+
configList.value
|
|
41
33
|
.slice()
|
|
42
34
|
.reverse()
|
|
43
35
|
.forEach((config, index) => {
|
|
36
|
+
// Walk from newest to oldest so each notice can be positioned after the ones below it.
|
|
44
37
|
const key = String(config.key)
|
|
45
38
|
const height = sizeMap.value[key]?.height ?? 0
|
|
46
|
-
const y
|
|
47
|
-
= stackValue && index > 0
|
|
48
|
-
? offsetY + (stackValue.offset ?? 0) - height
|
|
49
|
-
: offsetY
|
|
39
|
+
const y = stackParams && index > 0 ? offsetY + stackOffset - height : offsetY
|
|
50
40
|
|
|
51
|
-
|
|
41
|
+
notificationPosition.set(key, y)
|
|
52
42
|
|
|
53
43
|
if (index === 0) {
|
|
54
44
|
topNoticeHeight = height
|
|
55
45
|
topNoticeWidth = sizeMap.value[key]?.width ?? 0
|
|
56
46
|
}
|
|
57
47
|
|
|
58
|
-
if (!
|
|
48
|
+
if (!stackParams || index < stackThreshold) {
|
|
59
49
|
nextTotalHeight = Math.max(nextTotalHeight, y + height)
|
|
60
50
|
}
|
|
61
51
|
|
|
62
|
-
if (
|
|
52
|
+
if (stackParams) {
|
|
63
53
|
offsetY = y + height
|
|
64
54
|
}
|
|
65
55
|
else {
|
|
66
|
-
offsetY += height +
|
|
56
|
+
offsetY += height + gap.value
|
|
67
57
|
}
|
|
68
58
|
})
|
|
69
59
|
|
|
70
60
|
return {
|
|
71
|
-
|
|
61
|
+
notificationPosition,
|
|
72
62
|
totalHeight: nextTotalHeight,
|
|
73
63
|
topNoticeHeight,
|
|
74
64
|
topNoticeWidth,
|
|
75
65
|
}
|
|
76
66
|
})
|
|
77
67
|
|
|
78
|
-
return [
|
|
79
|
-
computed(() => result.value.positions),
|
|
80
|
-
setNodeSize,
|
|
81
|
-
computed(() => result.value.totalHeight),
|
|
82
|
-
computed(() => result.value.topNoticeHeight),
|
|
83
|
-
computed(() => result.value.topNoticeWidth),
|
|
84
|
-
]
|
|
68
|
+
return [result, setNodeSize] as const
|
|
85
69
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ref } from 'vue'
|
|
1
|
+
import { shallowRef } from 'vue'
|
|
3
2
|
|
|
4
3
|
export interface NodeSize {
|
|
5
4
|
width: number
|
|
@@ -9,34 +8,36 @@ export interface NodeSize {
|
|
|
9
8
|
export type NodeSizeMap = Record<string, NodeSize>
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
|
-
*
|
|
11
|
+
* Stores measured node sizes by key and exposes a callback to update them.
|
|
12
|
+
* Mirrors rc-notification@2.0 useSizes.
|
|
13
13
|
*/
|
|
14
|
-
export default function useSizes()
|
|
15
|
-
|
|
16
|
-
(key: string, node: HTMLElement | null) => void,
|
|
17
|
-
] {
|
|
18
|
-
const sizeMap = ref<NodeSizeMap>({})
|
|
14
|
+
export default function useSizes() {
|
|
15
|
+
const sizeMap = shallowRef<NodeSizeMap>({})
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
const setNodeSize = (key: string, node: HTMLDivElement | null) => {
|
|
21
18
|
if (!node) {
|
|
22
19
|
if (!(key in sizeMap.value)) {
|
|
23
20
|
return
|
|
24
21
|
}
|
|
25
|
-
const
|
|
26
|
-
|
|
22
|
+
const next = { ...sizeMap.value }
|
|
23
|
+
delete next[key]
|
|
24
|
+
sizeMap.value = next
|
|
27
25
|
return
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
const
|
|
28
|
+
const nextSize: NodeSize = {
|
|
31
29
|
width: node.offsetWidth,
|
|
32
30
|
height: node.offsetHeight,
|
|
33
31
|
}
|
|
34
32
|
const prev = sizeMap.value[key]
|
|
35
|
-
if (prev && prev.width ===
|
|
33
|
+
if (prev && prev.width === nextSize.width && prev.height === nextSize.height) {
|
|
36
34
|
return
|
|
37
35
|
}
|
|
38
|
-
sizeMap.value = {
|
|
36
|
+
sizeMap.value = {
|
|
37
|
+
...sizeMap.value,
|
|
38
|
+
[key]: nextSize,
|
|
39
|
+
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
return [sizeMap, setNodeSize]
|
|
42
|
+
return [sizeMap, setNodeSize] as const
|
|
42
43
|
}
|
|
@@ -1,48 +1,43 @@
|
|
|
1
|
-
import type { ComputedRef
|
|
2
|
-
import
|
|
3
|
-
import { computed, onBeforeUnmount, shallowRef, unref, watch } from 'vue'
|
|
1
|
+
import type { ComputedRef } from 'vue'
|
|
2
|
+
import { onScopeDispose, shallowRef, watch } from 'vue'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
7
|
-
* Returns controls to pause and resume the timer.
|
|
5
|
+
* Runs the notice auto-close timer and reports progress updates via rAF.
|
|
6
|
+
* Returns controls to pause and resume the timer. Mirrors rc-notification@2.0
|
|
7
|
+
* useNoticeTimer.
|
|
8
8
|
*/
|
|
9
9
|
export default function useNoticeTimer(
|
|
10
|
-
duration:
|
|
10
|
+
duration: ComputedRef<number | false | null | undefined>,
|
|
11
11
|
onClose: () => void,
|
|
12
|
-
onUpdate: (
|
|
13
|
-
): [() => void, () => void
|
|
14
|
-
const durationMs =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return Math.max(merged, 0) * 1000
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const walking = shallowRef(durationMs.value > 0)
|
|
21
|
-
const passTime = shallowRef(0)
|
|
12
|
+
onUpdate: (percent: number) => void,
|
|
13
|
+
): [() => void, () => void] {
|
|
14
|
+
const durationMs = shallowRef(0)
|
|
15
|
+
const walking = shallowRef(false)
|
|
16
|
+
let passTime = 0
|
|
22
17
|
let lastRafTime: number | null = null
|
|
23
18
|
let rafId: number | null = null
|
|
24
19
|
|
|
25
|
-
|
|
20
|
+
const syncPassTime = () => {
|
|
26
21
|
const now = Date.now()
|
|
27
22
|
if (lastRafTime !== null) {
|
|
28
|
-
passTime
|
|
23
|
+
passTime += now - lastRafTime
|
|
29
24
|
}
|
|
30
25
|
lastRafTime = now
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
|
|
28
|
+
const cancelStep = () => {
|
|
34
29
|
if (rafId !== null) {
|
|
35
|
-
|
|
30
|
+
cancelAnimationFrame(rafId)
|
|
36
31
|
rafId = null
|
|
37
32
|
}
|
|
38
33
|
}
|
|
39
34
|
|
|
40
|
-
|
|
35
|
+
const onPause = () => {
|
|
41
36
|
syncPassTime()
|
|
42
37
|
walking.value = false
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
const onResume = () => {
|
|
46
41
|
if (durationMs.value > 0) {
|
|
47
42
|
lastRafTime = Date.now()
|
|
48
43
|
walking.value = true
|
|
@@ -52,45 +47,39 @@ export default function useNoticeTimer(
|
|
|
52
47
|
}
|
|
53
48
|
}
|
|
54
49
|
|
|
55
|
-
// Reset
|
|
56
|
-
watch(
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
// Reset accumulated passTime whenever duration changes.
|
|
51
|
+
watch(duration, () => {
|
|
52
|
+
const next = typeof duration.value === 'number' ? duration.value : 0
|
|
53
|
+
durationMs.value = Math.max(next, 0) * 1000
|
|
54
|
+
passTime = 0
|
|
59
55
|
walking.value = durationMs.value > 0
|
|
60
|
-
})
|
|
56
|
+
}, { immediate: true })
|
|
61
57
|
|
|
62
|
-
// Drive
|
|
58
|
+
// Drive the rAF loop while walking is true.
|
|
63
59
|
watch(walking, (isWalking) => {
|
|
64
|
-
|
|
60
|
+
cancelStep()
|
|
65
61
|
if (!isWalking) {
|
|
66
62
|
return
|
|
67
63
|
}
|
|
64
|
+
lastRafTime = Date.now()
|
|
68
65
|
|
|
69
|
-
|
|
66
|
+
const step = () => {
|
|
70
67
|
syncPassTime()
|
|
71
|
-
|
|
72
|
-
if (passTime.value >= durationMs.value) {
|
|
68
|
+
if (passTime >= durationMs.value) {
|
|
73
69
|
onUpdate(1)
|
|
74
70
|
onClose()
|
|
71
|
+
return
|
|
75
72
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
rafId = raf(step)
|
|
79
|
-
}
|
|
73
|
+
onUpdate(Math.min(passTime / durationMs.value, 1))
|
|
74
|
+
rafId = requestAnimationFrame(step)
|
|
80
75
|
}
|
|
81
76
|
|
|
82
77
|
step()
|
|
83
78
|
}, { immediate: true })
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
const percent = computed(() => {
|
|
90
|
-
if (durationMs.value <= 0)
|
|
91
|
-
return 0
|
|
92
|
-
return Math.min(passTime.value / durationMs.value, 1)
|
|
80
|
+
onScopeDispose(() => {
|
|
81
|
+
cancelStep()
|
|
93
82
|
})
|
|
94
83
|
|
|
95
|
-
return [onResume, onPause
|
|
84
|
+
return [onResume, onPause]
|
|
96
85
|
}
|
|
@@ -1,29 +1,41 @@
|
|
|
1
|
-
import type { VueNode } from '@v-c/util/dist/type'
|
|
2
1
|
import type { CSSProperties, MaybeRef, TransitionGroupProps } from 'vue'
|
|
3
|
-
import type {
|
|
2
|
+
import type { VueNode } from '@v-c/util/dist/type'
|
|
3
|
+
import type { ClosableType, Key, NotificationListConfig, Placement, StackConfig } from '../interface'
|
|
4
|
+
import type {
|
|
5
|
+
ComponentsType,
|
|
6
|
+
} from '../Notification'
|
|
7
|
+
import type { NotificationClassNames, NotificationStyles } from '../NotificationList'
|
|
4
8
|
import type { NotificationsProps, NotificationsRef } from '../Notifications'
|
|
5
9
|
import { computed, onMounted, shallowRef, unref, watch } from 'vue'
|
|
6
10
|
import Notifications from '../Notifications'
|
|
7
11
|
|
|
8
|
-
type Key = string | number | symbol
|
|
9
|
-
|
|
10
12
|
const defaultGetContainer = () => document.body
|
|
11
13
|
|
|
12
14
|
type OptionalConfig = Partial<NotificationListConfig>
|
|
13
|
-
type SharedConfig = Pick<
|
|
14
|
-
NotificationListConfig,
|
|
15
|
-
'placement' | 'closable' | 'duration' | 'showProgress'
|
|
16
|
-
>
|
|
17
15
|
|
|
18
|
-
export interface NotificationConfig
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
export interface NotificationConfig {
|
|
17
|
+
prefixCls?: string
|
|
18
|
+
/** Customize container. It will repeat call which means you should return same container element. */
|
|
21
19
|
getContainer?: () => HTMLElement | ShadowRoot
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
|
|
21
|
+
closable?: ClosableType
|
|
22
|
+
maxCount?: number
|
|
25
23
|
duration?: number | false | null
|
|
26
|
-
showProgress?:
|
|
24
|
+
showProgress?: boolean
|
|
25
|
+
pauseOnHover?: boolean
|
|
26
|
+
placement?: Placement
|
|
27
|
+
classNames?: NotificationClassNames
|
|
28
|
+
styles?: NotificationStyles
|
|
29
|
+
components?: ComponentsType
|
|
30
|
+
/** @private. Config for notification holder style. Safe to remove if refactor */
|
|
31
|
+
className?: (placement: Placement) => string
|
|
32
|
+
/** @private. Config for notification holder style. Safe to remove if refactor */
|
|
33
|
+
style?: (placement: Placement) => CSSProperties
|
|
34
|
+
/** @private Trigger when all the notification closed. */
|
|
35
|
+
onAllRemoved?: VoidFunction
|
|
36
|
+
stack?: StackConfig
|
|
37
|
+
/** @private Slot for style in Notifications */
|
|
38
|
+
renderNotifications?: NotificationsProps['renderNotifications']
|
|
27
39
|
}
|
|
28
40
|
|
|
29
41
|
export interface NotificationAPI {
|
|
@@ -51,13 +63,13 @@ type Task = OpenTask | CloseTask | DestroyTask
|
|
|
51
63
|
let uniqueKey = 0
|
|
52
64
|
|
|
53
65
|
function mergeConfig<T>(...objList: Partial<T>[]): T {
|
|
54
|
-
const clone = {}
|
|
55
|
-
objList.forEach((obj) => {
|
|
66
|
+
const clone: any = {}
|
|
67
|
+
objList.forEach((obj: any) => {
|
|
56
68
|
if (obj) {
|
|
57
69
|
Object.keys(obj).forEach((key) => {
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
60
|
-
|
|
70
|
+
const val = obj[key]
|
|
71
|
+
if (val !== undefined) {
|
|
72
|
+
clone[key] = val
|
|
61
73
|
}
|
|
62
74
|
})
|
|
63
75
|
}
|
|
@@ -70,15 +82,29 @@ export default function useNotification(
|
|
|
70
82
|
): [NotificationAPI, () => VueNode] {
|
|
71
83
|
const configRef = computed(() => unref(rootConfig) || {})
|
|
72
84
|
const container = shallowRef<HTMLElement | ShadowRoot>()
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
const notificationRef = shallowRef<NotificationsRef>()
|
|
86
|
+
|
|
87
|
+
const shareConfig = computed(() => {
|
|
88
|
+
const {
|
|
89
|
+
getContainer,
|
|
90
|
+
motion,
|
|
91
|
+
prefixCls,
|
|
92
|
+
maxCount,
|
|
93
|
+
className,
|
|
94
|
+
style,
|
|
95
|
+
onAllRemoved,
|
|
96
|
+
stack,
|
|
97
|
+
renderNotifications,
|
|
98
|
+
pauseOnHover,
|
|
99
|
+
classNames,
|
|
100
|
+
styles,
|
|
101
|
+
components,
|
|
102
|
+
...restConfig
|
|
103
|
+
} = configRef.value
|
|
104
|
+
return restConfig
|
|
79
105
|
})
|
|
80
106
|
|
|
81
|
-
|
|
107
|
+
const resolveContainer = () => {
|
|
82
108
|
const getContainer = configRef.value.getContainer || defaultGetContainer
|
|
83
109
|
return getContainer()
|
|
84
110
|
}
|
|
@@ -86,30 +112,32 @@ export default function useNotification(
|
|
|
86
112
|
const contextHolder = () => (
|
|
87
113
|
<Notifications
|
|
88
114
|
container={container.value}
|
|
89
|
-
ref={
|
|
115
|
+
ref={notificationRef}
|
|
90
116
|
prefixCls={configRef.value.prefixCls}
|
|
91
|
-
motion={configRef.value.motion
|
|
117
|
+
motion={configRef.value.motion}
|
|
92
118
|
maxCount={configRef.value.maxCount}
|
|
93
119
|
pauseOnHover={configRef.value.pauseOnHover}
|
|
94
120
|
classNames={configRef.value.classNames}
|
|
95
121
|
styles={configRef.value.styles}
|
|
96
122
|
components={configRef.value.components}
|
|
97
123
|
className={configRef.value.className}
|
|
98
|
-
style={configRef.value.style
|
|
124
|
+
style={configRef.value.style}
|
|
99
125
|
onAllRemoved={configRef.value.onAllRemoved}
|
|
100
|
-
stack={configRef.value.stack
|
|
126
|
+
stack={configRef.value.stack}
|
|
101
127
|
renderNotifications={configRef.value.renderNotifications}
|
|
102
128
|
/>
|
|
103
129
|
)
|
|
104
130
|
|
|
131
|
+
const taskQueue = shallowRef<Task[]>([])
|
|
132
|
+
|
|
105
133
|
const api: NotificationAPI = {
|
|
106
134
|
open(config) {
|
|
107
|
-
const
|
|
108
|
-
if (
|
|
109
|
-
|
|
135
|
+
const mergedConfig = mergeConfig<NotificationListConfig>(shareConfig.value as any, config)
|
|
136
|
+
if (mergedConfig.key === null || mergedConfig.key === undefined) {
|
|
137
|
+
mergedConfig.key = `vc-notification-${uniqueKey}`
|
|
110
138
|
uniqueKey += 1
|
|
111
139
|
}
|
|
112
|
-
taskQueue.value = [...taskQueue.value, { type: 'open', config:
|
|
140
|
+
taskQueue.value = [...taskQueue.value, { type: 'open', config: mergedConfig }]
|
|
113
141
|
},
|
|
114
142
|
close(key) {
|
|
115
143
|
taskQueue.value = [...taskQueue.value, { type: 'close', key }]
|
|
@@ -128,24 +156,24 @@ export default function useNotification(
|
|
|
128
156
|
container.value = resolveContainer()
|
|
129
157
|
},
|
|
130
158
|
)
|
|
131
|
-
|
|
132
159
|
watch(taskQueue, () => {
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
tasks.forEach((task) => {
|
|
160
|
+
if (notificationRef.value && taskQueue.value.length) {
|
|
161
|
+
taskQueue.value.forEach((task) => {
|
|
136
162
|
switch (task.type) {
|
|
137
163
|
case 'open':
|
|
138
|
-
|
|
164
|
+
notificationRef.value?.open(task.config)
|
|
139
165
|
break
|
|
140
166
|
case 'close':
|
|
141
|
-
|
|
167
|
+
notificationRef.value?.close(task.key)
|
|
142
168
|
break
|
|
143
169
|
case 'destroy':
|
|
144
|
-
|
|
170
|
+
notificationRef.value?.destroy()
|
|
171
|
+
break
|
|
172
|
+
default:
|
|
145
173
|
break
|
|
146
174
|
}
|
|
147
175
|
})
|
|
148
|
-
taskQueue.value =
|
|
176
|
+
taskQueue.value = []
|
|
149
177
|
}
|
|
150
178
|
})
|
|
151
179
|
|
package/src/hooks/useStack.ts
CHANGED
|
@@ -1,40 +1,36 @@
|
|
|
1
|
-
import type { ComputedRef, MaybeRef } from 'vue'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export interface StackConfig {
|
|
5
|
-
threshold?: number
|
|
6
|
-
offset?: number
|
|
7
|
-
}
|
|
1
|
+
import type { ComputedRef, MaybeRef, ToRefs } from 'vue'
|
|
2
|
+
import type { StackConfig } from '../interface'
|
|
3
|
+
import { computed, reactive, toRefs, unref, watchEffect } from 'vue'
|
|
8
4
|
|
|
9
5
|
const DEFAULT_OFFSET = 8
|
|
10
6
|
const DEFAULT_THRESHOLD = 3
|
|
11
7
|
|
|
12
|
-
type StackParams =
|
|
13
|
-
|
|
14
|
-
export type StackInput = boolean | StackConfig
|
|
8
|
+
type StackParams = Exclude<StackConfig, boolean>
|
|
15
9
|
|
|
16
10
|
type UseStack = (
|
|
17
|
-
config?: MaybeRef<
|
|
18
|
-
) => [ComputedRef<boolean>,
|
|
19
|
-
|
|
11
|
+
config?: MaybeRef<StackConfig | undefined>,
|
|
12
|
+
) => [ComputedRef<boolean>, ToRefs<StackParams>]
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Resolves the stack setting into an enabled flag and normalized stack params.
|
|
16
|
+
* Mirrors rc-notification@2.0 useStack. The `gap` config is no longer surfaced
|
|
17
|
+
* here — gap is now read from the list-content CSS `gap`/`row-gap`.
|
|
18
|
+
*/
|
|
20
19
|
const useStack: UseStack = (config) => {
|
|
21
|
-
const
|
|
20
|
+
const result: StackParams = reactive({
|
|
21
|
+
offset: DEFAULT_OFFSET,
|
|
22
|
+
threshold: DEFAULT_THRESHOLD,
|
|
23
|
+
})
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
watchEffect(() => {
|
|
24
26
|
const value = unref(config)
|
|
25
27
|
if (value && typeof value === 'object') {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
threshold: value.threshold ?? DEFAULT_THRESHOLD,
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
offset: DEFAULT_OFFSET,
|
|
33
|
-
threshold: DEFAULT_THRESHOLD,
|
|
28
|
+
result.offset = value.offset ?? DEFAULT_OFFSET
|
|
29
|
+
result.threshold = value.threshold ?? DEFAULT_THRESHOLD
|
|
34
30
|
}
|
|
35
31
|
})
|
|
36
32
|
|
|
37
|
-
return [
|
|
33
|
+
return [computed(() => !!unref(config)), toRefs(result)]
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
export default useStack
|