@v-c/notification 0.0.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/bump.config.ts +6 -0
  3. package/dist/Notice.cjs +230 -0
  4. package/dist/Notice.d.ts +15 -0
  5. package/dist/Notice.js +225 -0
  6. package/dist/NoticeList.cjs +157 -0
  7. package/dist/NoticeList.d.ts +13 -0
  8. package/dist/NoticeList.js +154 -0
  9. package/dist/NotificationProvider.cjs +13 -0
  10. package/dist/NotificationProvider.d.ts +10 -0
  11. package/dist/NotificationProvider.js +10 -0
  12. package/dist/Notifications.cjs +146 -0
  13. package/dist/Notifications.d.ts +24 -0
  14. package/dist/Notifications.js +143 -0
  15. package/dist/_virtual/rolldown_runtime.cjs +21 -0
  16. package/dist/hooks/useNotification.cjs +80 -0
  17. package/dist/hooks/useNotification.d.ts +36 -0
  18. package/dist/hooks/useNotification.js +78 -0
  19. package/dist/hooks/useStack.cjs +24 -0
  20. package/dist/hooks/useStack.d.ts +6 -0
  21. package/dist/hooks/useStack.js +22 -0
  22. package/dist/index.cjs +6 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.js +4 -0
  25. package/dist/interface.cjs +0 -0
  26. package/dist/interface.d.ts +55 -0
  27. package/dist/interface.js +0 -0
  28. package/docs/context.vue +34 -0
  29. package/docs/hooks.vue +89 -0
  30. package/docs/index.less +265 -0
  31. package/docs/maxCount.vue +24 -0
  32. package/docs/motion.ts +33 -0
  33. package/docs/notification.stories.vue +31 -0
  34. package/docs/showProgress.vue +34 -0
  35. package/docs/stack.vue +39 -0
  36. package/package.json +30 -0
  37. package/src/Notice.tsx +212 -0
  38. package/src/NoticeList.tsx +203 -0
  39. package/src/NotificationProvider.tsx +19 -0
  40. package/src/Notifications.tsx +164 -0
  41. package/src/hooks/useNotification.tsx +163 -0
  42. package/src/hooks/useStack.ts +32 -0
  43. package/src/index.ts +9 -0
  44. package/src/interface.ts +61 -0
  45. package/tsconfig.json +7 -0
  46. package/vite.config.ts +18 -0
  47. package/vitest.config.ts +9 -0
@@ -0,0 +1,163 @@
1
+ import type { VueNode } from '@v-c/util/dist/type'
2
+ import type { CSSProperties, TransitionGroupProps } from 'vue'
3
+ import type { Key, OpenConfig, Placement, StackConfig } from '../interface'
4
+ import type { NotificationsProps, NotificationsRef } from '../Notifications'
5
+ import { onMounted, shallowRef, watch } from 'vue'
6
+ import Notifications from '../Notifications'
7
+
8
+ const defaultGetContainer = () => document.body
9
+
10
+ type OptionalConfig = Partial<OpenConfig>
11
+
12
+ export interface NotificationConfig {
13
+ prefixCls?: string
14
+ /** Customize container. It will repeat call which means you should return same container element. */
15
+ getContainer?: () => HTMLElement | ShadowRoot
16
+ motion?: TransitionGroupProps | ((placement: Placement) => TransitionGroupProps)
17
+ closeIcon?: VueNode
18
+ closable?:
19
+ | boolean
20
+ | ({ closeIcon?: VueNode, onClose?: VoidFunction } & Record<string, any>)
21
+ maxCount?: number
22
+ duration?: number | false | null
23
+ showProgress?: boolean
24
+ pauseOnHover?: boolean
25
+ /** @private. Config for notification holder style. Safe to remove if refactor */
26
+ className?: (placement: Placement) => string
27
+ /** @private. Config for notification holder style. Safe to remove if refactor */
28
+ style?: (placement: Placement) => CSSProperties
29
+ /** @private Trigger when all the notification closed. */
30
+ onAllRemoved?: VoidFunction
31
+ stack?: StackConfig
32
+ /** @private Slot for style in Notifications */
33
+ renderNotifications?: NotificationsProps['renderNotifications']
34
+ }
35
+
36
+ export interface NotificationAPI {
37
+ open: (config: OptionalConfig) => void
38
+ close: (key: Key) => void
39
+ destroy: () => void
40
+ }
41
+
42
+ interface OpenTask {
43
+ type: 'open'
44
+ config: OpenConfig
45
+ }
46
+
47
+ interface CloseTask {
48
+ type: 'close'
49
+ key: Key
50
+ }
51
+
52
+ interface DestroyTask {
53
+ type: 'destroy'
54
+ }
55
+
56
+ type Task = OpenTask | CloseTask | DestroyTask
57
+
58
+ let uniqueKey = 0
59
+
60
+ function mergeConfig<T>(...objList: Partial<T>[]): T {
61
+ const clone: any = {}
62
+
63
+ objList.forEach((obj: any) => {
64
+ if (obj) {
65
+ Object.keys(obj).forEach((key) => {
66
+ const val = obj[key]
67
+
68
+ if (val !== undefined) {
69
+ clone[key] = val
70
+ }
71
+ })
72
+ }
73
+ })
74
+
75
+ return clone
76
+ }
77
+
78
+ export default function useNotification(rootConfig: NotificationConfig = {}) {
79
+ const {
80
+ getContainer = defaultGetContainer,
81
+ motion,
82
+ prefixCls,
83
+ maxCount,
84
+ className,
85
+ style,
86
+ onAllRemoved,
87
+ stack,
88
+ renderNotifications,
89
+ ...shareConfig
90
+ } = rootConfig
91
+ const container = shallowRef<HTMLElement | ShadowRoot>()
92
+
93
+ const notificationRef = shallowRef<NotificationsRef>()
94
+
95
+ const contextHolder = () => (
96
+ <Notifications
97
+ container={container.value}
98
+ ref={notificationRef}
99
+ prefixCls={prefixCls}
100
+ motion={motion}
101
+ maxCount={maxCount}
102
+ className={className}
103
+ style={style}
104
+ onAllRemoved={onAllRemoved}
105
+ stack={stack}
106
+ renderNotifications={renderNotifications}
107
+ />
108
+ )
109
+
110
+ const taskQueue = shallowRef<Task[]>([])
111
+
112
+ // ========================= Refs =========================
113
+
114
+ const api: NotificationAPI = {
115
+ open(config) {
116
+ const mergedConfig = mergeConfig(shareConfig, config)
117
+ if (mergedConfig.key === null || mergedConfig.key === undefined) {
118
+ mergedConfig.key = `vc-notification-${uniqueKey}`
119
+ uniqueKey += 1
120
+ }
121
+
122
+ taskQueue.value = [...taskQueue.value, { type: 'open', config: mergedConfig }]
123
+ },
124
+ close(key) {
125
+ taskQueue.value = [...taskQueue.value, { type: 'close', key }]
126
+ },
127
+ destroy() {
128
+ taskQueue.value = [...taskQueue.value, { type: 'destroy' }]
129
+ },
130
+ }
131
+
132
+ // ======================= Container ======================
133
+ // React 18 should all in effect that we will check container in each render
134
+ // Which means getContainer should be stable.
135
+ onMounted(
136
+ () => {
137
+ container.value = getContainer()
138
+ },
139
+ )
140
+ watch(taskQueue, () => {
141
+ if (notificationRef.value && taskQueue.value.length) {
142
+ taskQueue.value.forEach((task) => {
143
+ switch (task.type) {
144
+ case 'open':
145
+ notificationRef.value?.open(task.config)
146
+ break
147
+ case 'close':
148
+ notificationRef.value?.close(task.key)
149
+ break
150
+ case 'destroy':
151
+ notificationRef.value?.destroy()
152
+ break
153
+ default:
154
+ break
155
+ }
156
+ })
157
+ taskQueue.value = taskQueue.value.filter(task => !taskQueue.value.includes(task))
158
+ }
159
+ })
160
+
161
+ // ======================== Return ========================
162
+ return [api, contextHolder] as [NotificationAPI, () => VueNode]
163
+ }
@@ -0,0 +1,32 @@
1
+ import type { ComputedRef, MaybeRef, ToRefs } from 'vue'
2
+ import type { StackConfig } from '../interface'
3
+ import { computed, reactive, toRefs, unref, watchEffect } from 'vue'
4
+
5
+ const DEFAULT_OFFSET = 8
6
+ const DEFAULT_THRESHOLD = 3
7
+ const DEFAULT_GAP = 16
8
+
9
+ type StackParams = Exclude<StackConfig, boolean>
10
+
11
+ type UseStack = (config?: MaybeRef<StackConfig | undefined>) => [ComputedRef<boolean>, ToRefs<StackParams>]
12
+
13
+ const useStack: UseStack = (config) => {
14
+ const result: StackParams = reactive({
15
+ offset: DEFAULT_OFFSET,
16
+ threshold: DEFAULT_THRESHOLD,
17
+ gap: DEFAULT_GAP,
18
+ })
19
+
20
+ watchEffect(() => {
21
+ const _config = unref(config)
22
+ if (_config && typeof _config === 'object') {
23
+ result.offset = _config.offset ?? DEFAULT_OFFSET
24
+ result.threshold = _config.threshold ?? DEFAULT_THRESHOLD
25
+ result.gap = _config.gap ?? DEFAULT_GAP
26
+ }
27
+ })
28
+
29
+ return [computed(() => !!unref(config)), toRefs(result)]
30
+ }
31
+
32
+ export default useStack
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { NotificationAPI, NotificationConfig } from './hooks/useNotification'
2
+ import useNotification from './hooks/useNotification'
3
+ import Notice from './Notice'
4
+ import { useNotificationProvider } from './NotificationProvider'
5
+
6
+ export { Notice, useNotification, useNotificationProvider }
7
+ export type { NotificationAPI, NotificationConfig }
8
+
9
+ export type { NoticeProps } from './Notice'
@@ -0,0 +1,61 @@
1
+ import type { VueNode } from '@v-c/util/dist/type'
2
+ import type { CSSProperties } from 'vue'
3
+
4
+ export type Placement = 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight'
5
+
6
+ type NoticeSemanticProps = 'wrapper'
7
+
8
+ export type Key = string | number
9
+ export interface NoticeConfig {
10
+ content?: VueNode
11
+ duration?: number | false | null
12
+ showProgress?: boolean
13
+ pauseOnHover?: boolean
14
+ closeIcon?: VueNode
15
+ closable?:
16
+ | boolean
17
+ | ({ closeIcon?: VueNode; onClose?: VoidFunction } & Record<string, any>)
18
+ className?: string
19
+ style?: CSSProperties
20
+ classNames?: {
21
+ [key in NoticeSemanticProps]?: string;
22
+ }
23
+ styles?: {
24
+ [key in NoticeSemanticProps]?: CSSProperties;
25
+ }
26
+ /** @private Internal usage. Do not override in your code */
27
+ props?: Record<string, any>
28
+
29
+ onClose?: VoidFunction
30
+ onClick?: (event: Event) => void
31
+ }
32
+
33
+ export interface OpenConfig extends NoticeConfig {
34
+ key: Key
35
+ placement?: Placement
36
+ content?: VueNode
37
+ duration?: number | false | null
38
+ }
39
+
40
+ export type InnerOpenConfig = OpenConfig & { times?: number }
41
+
42
+ export type Placements = Partial<Record<Placement, OpenConfig[]>>
43
+
44
+ export type StackConfig
45
+ = | boolean
46
+ | {
47
+ /**
48
+ * When number is greater than threshold, notifications will be stacked together.
49
+ * @default 3
50
+ */
51
+ threshold?: number
52
+ /**
53
+ * Offset when notifications are stacked together.
54
+ * @default 8
55
+ */
56
+ offset?: number
57
+ /**
58
+ * Spacing between each notification when expanded.
59
+ */
60
+ gap?: number
61
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": ["../../tsconfig.base.json"],
3
+ "compilerOptions": {
4
+
5
+ },
6
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"]
7
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { UserConfig } from 'vite'
2
+ import fg from 'fast-glob'
3
+ import { defineConfig, mergeConfig } from 'vite'
4
+ import { buildCommon } from '../../scripts/build.common'
5
+
6
+ const entry = fg.sync(['src/**/*.ts', 'src/**/*.tsx', '!src/**/*.test.ts', '!src/**/tests'])
7
+
8
+ export default defineConfig({
9
+ ...mergeConfig(buildCommon({
10
+ external: ['vue', 'classnames', /^@v-c\/util/],
11
+ }), {
12
+ build: {
13
+ lib: {
14
+ entry,
15
+ },
16
+ },
17
+ } as UserConfig),
18
+ })
@@ -0,0 +1,9 @@
1
+ import { defineProject, mergeConfig } from 'vitest/config'
2
+ import configShared from '../../vitest.config.ts'
3
+
4
+ export default mergeConfig(
5
+ configShared,
6
+ defineProject({
7
+
8
+ }),
9
+ )