@rainbow-oh/yee-c 0.8.1 → 0.8.3

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 (225) hide show
  1. package/dist/cjs/Cascader/cascader.js +38 -30
  2. package/dist/cjs/Cascader/column.js +17 -9
  3. package/dist/cjs/Cascader/search-list.js +30 -22
  4. package/dist/cjs/Cascader/utils/index.js +1 -5
  5. package/dist/cjs/Checkbox/checkbox-group.js +23 -14
  6. package/dist/cjs/Dialog/dialog.js +1 -1
  7. package/dist/cjs/Dialog/interface.d.ts +1 -1
  8. package/dist/cjs/Dialog/style/index.less +2 -1
  9. package/dist/cjs/Drawer/drawer.js +9 -1
  10. package/dist/cjs/Form/Group.js +1 -1
  11. package/dist/cjs/Form/demo/tooltip.js +1 -3
  12. package/dist/cjs/Input/style/index.less +1 -0
  13. package/dist/cjs/List/hooks/useListKeyboard.js +3 -1
  14. package/dist/cjs/Menu/menu.js +18 -2
  15. package/dist/cjs/Pagination/items.js +54 -26
  16. package/dist/cjs/Pagination/pagination.js +25 -9
  17. package/dist/cjs/PickerPanel/panels/DateTimePanel/index.d.ts +1 -1
  18. package/dist/cjs/PickerPanel/panels/MonthPanel/MonthHeader.js +7 -1
  19. package/dist/cjs/Rate/star.js +27 -3
  20. package/dist/cjs/Segmented/demo/variant.js +15 -1
  21. package/dist/cjs/Segmented/index.d.ts +1 -1
  22. package/dist/cjs/Segmented/segmented.js +12 -15
  23. package/dist/cjs/Select/option.js +8 -1
  24. package/dist/cjs/Select/options.js +9 -1
  25. package/dist/cjs/Select/select.js +3 -1
  26. package/dist/cjs/Selector/style/index.less +1 -0
  27. package/dist/cjs/Steps/step.js +14 -2
  28. package/dist/cjs/Steps/steps.js +11 -1
  29. package/dist/cjs/Table/demo/components-override.js +31 -2
  30. package/dist/cjs/Table/demo/grouping.js +7 -1
  31. package/dist/cjs/Table/hooks/useColumns.d.ts +3 -3
  32. package/dist/cjs/Table/hooks/useColumns.js +11 -14
  33. package/dist/cjs/Table/icon/arrow-icon.js +1 -1
  34. package/dist/cjs/Table/index.d.ts +1 -1
  35. package/dist/cjs/Table/style/index.less +2 -4
  36. package/dist/cjs/Table/tbody/row.js +1 -4
  37. package/dist/cjs/Table/theader/cell.js +6 -3
  38. package/dist/cjs/Table/theader/index.js +12 -4
  39. package/dist/cjs/TextArea/style/index.less +1 -0
  40. package/dist/cjs/Tour/index.d.ts +1 -1
  41. package/dist/cjs/Tree/style/index.less +1 -5
  42. package/dist/cjs/Tree/tree.js +38 -35
  43. package/dist/cjs/VerificationCode/style/index.less +1 -0
  44. package/dist/cjs/hooks/useCountdown.d.ts +2 -1
  45. package/dist/cjs/hooks/useCountdown.js +46 -23
  46. package/dist/cjs/hooks/useEvent.js +1 -4
  47. package/dist/cjs/locale/interface.d.ts +3 -0
  48. package/dist/cjs/locale/lang/en_US.js +4 -1
  49. package/dist/cjs/locale/lang/ja_JP.js +4 -1
  50. package/dist/cjs/locale/lang/zh_CN.js +4 -1
  51. package/dist/cjs/locale/lang/zh_TW.js +4 -1
  52. package/dist/cjs/message/demo/base.js +1 -1
  53. package/dist/cjs/message/index.d.ts +1 -1
  54. package/dist/cjs/message/interface.d.ts +16 -7
  55. package/dist/cjs/message/message-provider.d.ts +2 -1
  56. package/dist/cjs/message/message-provider.js +1 -1
  57. package/dist/cjs/message/message.js +14 -9
  58. package/dist/cjs/message/style/index.less +3 -1
  59. package/dist/cjs/message/use-message.d.ts +3 -12
  60. package/dist/cjs/message/use-message.js +103 -45
  61. package/dist/cjs/message/wrapper.d.ts +21 -27
  62. package/dist/cjs/message/wrapper.js +49 -19
  63. package/dist/cjs/notice/index.d.ts +1 -1
  64. package/dist/cjs/notice/interface.d.ts +17 -7
  65. package/dist/cjs/notice/notice-list.d.ts +3 -3
  66. package/dist/cjs/notice/notice-provider.d.ts +2 -1
  67. package/dist/cjs/notice/notice-provider.js +1 -1
  68. package/dist/cjs/notice/notice.js +63 -20
  69. package/dist/cjs/notice/style/index.less +13 -1
  70. package/dist/cjs/notice/use-notice.d.ts +2 -11
  71. package/dist/cjs/notice/use-notice.js +156 -71
  72. package/dist/cjs/notice/wrapper.d.ts +19 -15
  73. package/dist/cjs/notice/wrapper.js +122 -52
  74. package/dist/cjs/style/size.css +5 -3
  75. package/dist/cjs/style/themes.css +1 -1
  76. package/dist/cjs/style/variables.css +6 -4
  77. package/dist/cjs/version.d.ts +1 -1
  78. package/dist/cjs/version.js +1 -1
  79. package/dist/esm/Dialog/dialog.js +2 -2
  80. package/dist/esm/Dialog/interface.d.ts +1 -1
  81. package/dist/esm/Dialog/style/index.less +2 -1
  82. package/dist/esm/Form/Group.js +1 -1
  83. package/dist/esm/Input/style/index.less +1 -0
  84. package/dist/esm/PickerPanel/panels/DateTimePanel/index.d.ts +1 -1
  85. package/dist/esm/Segmented/index.d.ts +1 -1
  86. package/dist/esm/Segmented/segmented.js +1 -1
  87. package/dist/esm/Selector/style/index.less +1 -0
  88. package/dist/esm/Table/demo/components-override.js +1 -0
  89. package/dist/esm/Table/hooks/useColumns.d.ts +3 -3
  90. package/dist/esm/Table/hooks/useColumns.js +13 -16
  91. package/dist/esm/Table/icon/arrow-icon.js +1 -1
  92. package/dist/esm/Table/index.d.ts +1 -1
  93. package/dist/esm/Table/style/index.less +2 -4
  94. package/dist/esm/Table/theader/cell.js +7 -3
  95. package/dist/esm/TextArea/style/index.less +1 -0
  96. package/dist/esm/Tour/index.d.ts +1 -1
  97. package/dist/esm/Tree/style/index.less +1 -5
  98. package/dist/esm/Tree/tree.js +1 -1
  99. package/dist/esm/VerificationCode/style/index.less +1 -0
  100. package/dist/esm/hooks/useCountdown.d.ts +2 -1
  101. package/dist/esm/hooks/useCountdown.js +44 -14
  102. package/dist/esm/locale/interface.d.ts +3 -0
  103. package/dist/esm/locale/lang/en_US.js +4 -1
  104. package/dist/esm/locale/lang/ja_JP.js +4 -1
  105. package/dist/esm/locale/lang/zh_CN.js +4 -1
  106. package/dist/esm/locale/lang/zh_TW.js +4 -1
  107. package/dist/esm/message/demo/base.js +4 -1
  108. package/dist/esm/message/index.d.ts +1 -1
  109. package/dist/esm/message/interface.d.ts +16 -7
  110. package/dist/esm/message/message-provider.d.ts +2 -1
  111. package/dist/esm/message/message-provider.js +1 -1
  112. package/dist/esm/message/message.js +22 -9
  113. package/dist/esm/message/style/index.less +3 -1
  114. package/dist/esm/message/use-message.d.ts +3 -12
  115. package/dist/esm/message/use-message.js +129 -79
  116. package/dist/esm/message/wrapper.d.ts +21 -27
  117. package/dist/esm/message/wrapper.js +60 -18
  118. package/dist/esm/notice/index.d.ts +1 -1
  119. package/dist/esm/notice/interface.d.ts +17 -7
  120. package/dist/esm/notice/notice-list.d.ts +3 -3
  121. package/dist/esm/notice/notice-provider.d.ts +2 -1
  122. package/dist/esm/notice/notice-provider.js +1 -1
  123. package/dist/esm/notice/notice.js +73 -21
  124. package/dist/esm/notice/style/index.less +13 -1
  125. package/dist/esm/notice/use-notice.d.ts +2 -11
  126. package/dist/esm/notice/use-notice.js +180 -100
  127. package/dist/esm/notice/wrapper.d.ts +19 -15
  128. package/dist/esm/notice/wrapper.js +144 -62
  129. package/dist/esm/style/size.css +5 -3
  130. package/dist/esm/style/themes.css +1 -1
  131. package/dist/esm/style/variables.css +6 -4
  132. package/dist/esm/version.d.ts +1 -1
  133. package/dist/esm/version.js +1 -1
  134. package/dist/umd/yee-c.min.css +2 -2
  135. package/dist/umd/yee-c.min.js +1 -1
  136. package/package.json +1 -1
  137. package/src/Anchor/anchor.tsx +2 -2
  138. package/src/Carousel/carousel.tsx +1 -2
  139. package/src/Cascader/cascader.tsx +5 -1
  140. package/src/Cascader/column.tsx +5 -1
  141. package/src/Cascader/search-list.tsx +10 -3
  142. package/src/Cascader/utils/index.ts +3 -6
  143. package/src/Checkbox/checkbox-group.tsx +6 -1
  144. package/src/Dialog/dialog.tsx +7 -3
  145. package/src/Dialog/interface.ts +1 -1
  146. package/src/Dialog/style/index.less +2 -1
  147. package/src/Drawer/drawer.tsx +7 -1
  148. package/src/Form/Group.tsx +2 -4
  149. package/src/Form/demo/tooltip.tsx +2 -11
  150. package/src/Form/utils/validate.ts +1 -4
  151. package/src/Input/style/index.less +1 -0
  152. package/src/List/hooks/useListKeyboard.ts +3 -3
  153. package/src/List/list.tsx +2 -1
  154. package/src/Menu/menu.tsx +14 -3
  155. package/src/Pagination/items.tsx +16 -4
  156. package/src/Pagination/pagination.tsx +18 -4
  157. package/src/PickerPanel/panels/DatePanel/DateBody.tsx +1 -1
  158. package/src/PickerPanel/panels/DateTimePanel/index.tsx +1 -1
  159. package/src/PickerPanel/panels/MonthPanel/MonthHeader.tsx +7 -2
  160. package/src/PickerPanel/panels/PanelBody.tsx +1 -1
  161. package/src/PickerPanel/panels/QuarterPanel/QuarterHeader.tsx +1 -1
  162. package/src/PickerPanel/panels/RangePanel/index.tsx +1 -1
  163. package/src/PickerPanel/panels/TimePanel/TimeBody.tsx +1 -1
  164. package/src/PickerPanel/panels/YearPanel/YearHeader.tsx +1 -1
  165. package/src/PickerPanel/utils/pickerUtils.ts +2 -1
  166. package/src/RangePicker/range-picker-mobile.tsx +1 -3
  167. package/src/Rate/star.tsx +16 -4
  168. package/src/Segmented/demo/variant.tsx +10 -2
  169. package/src/Segmented/index.ts +2 -2
  170. package/src/Segmented/segmented.tsx +4 -11
  171. package/src/Select/interface.ts +0 -1
  172. package/src/Select/option.tsx +8 -2
  173. package/src/Select/options.tsx +11 -4
  174. package/src/Select/select.tsx +4 -4
  175. package/src/Selector/style/index.less +1 -0
  176. package/src/Steps/step.tsx +11 -3
  177. package/src/Steps/steps.tsx +8 -1
  178. package/src/Table/demo/components-override.tsx +34 -7
  179. package/src/Table/demo/grouping.tsx +8 -4
  180. package/src/Table/hooks/useColumns.ts +14 -18
  181. package/src/Table/icon/arrow-icon.tsx +1 -1
  182. package/src/Table/index.ts +3 -3
  183. package/src/Table/style/index.less +2 -4
  184. package/src/Table/table.tsx +2 -4
  185. package/src/Table/tbody/row.tsx +1 -3
  186. package/src/Table/theader/cell.tsx +6 -3
  187. package/src/Table/theader/index.tsx +9 -10
  188. package/src/TextArea/style/index.less +1 -0
  189. package/src/Tour/demo/placement.tsx +2 -1
  190. package/src/Tour/index.ts +1 -1
  191. package/src/Tree/style/index.less +1 -5
  192. package/src/Tree/tree.tsx +16 -11
  193. package/src/Tree/utils/tree-tools.ts +4 -1
  194. package/src/VerificationCode/style/index.less +1 -0
  195. package/src/hooks/useCountdown.ts +52 -21
  196. package/src/hooks/useEvent.ts +1 -3
  197. package/src/locale/context.tsx +5 -1
  198. package/src/locale/interface.ts +6 -1
  199. package/src/locale/lang/en_US.ts +3 -0
  200. package/src/locale/lang/ja_JP.ts +3 -0
  201. package/src/locale/lang/zh_CN.ts +3 -0
  202. package/src/locale/lang/zh_TW.ts +3 -0
  203. package/src/message/demo/base.tsx +1 -1
  204. package/src/message/index.ts +1 -1
  205. package/src/message/interface.ts +22 -7
  206. package/src/message/message-provider.tsx +3 -2
  207. package/src/message/message.test.tsx +283 -0
  208. package/src/message/message.tsx +14 -9
  209. package/src/message/style/index.less +3 -1
  210. package/src/message/use-message.tsx +144 -67
  211. package/src/message/wrapper.tsx +62 -20
  212. package/src/notice/index.en-US.md +76 -75
  213. package/src/notice/index.ts +6 -1
  214. package/src/notice/index.zh-CN.md +3 -2
  215. package/src/notice/interface.ts +31 -14
  216. package/src/notice/notice-list.tsx +3 -9
  217. package/src/notice/notice-provider.tsx +3 -2
  218. package/src/notice/notice.test.tsx +450 -0
  219. package/src/notice/notice.tsx +69 -21
  220. package/src/notice/style/index.less +13 -1
  221. package/src/notice/use-notice.tsx +210 -94
  222. package/src/notice/wrapper.tsx +159 -71
  223. package/src/style/size.css +5 -3
  224. package/src/utils/debounce.ts +1 -3
  225. package/src/version.ts +1 -1
@@ -1,117 +1,233 @@
1
- import React, { useCallback, useRef, useState } from 'react';
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
2
8
  import { createPortal } from 'react-dom';
3
- import { NoticeConfig, NoticeType, WrapperedNoticeConfig } from './interface';
9
+ import {
10
+ NoticeApi,
11
+ NoticeType,
12
+ PlacementType,
13
+ WrapperedNoticeConfig,
14
+ } from './interface';
4
15
  import NoticeList from './notice-list';
5
16
 
6
- type PlacementType =
7
- | 'topLeft'
8
- | 'topRight'
9
- | 'bottomLeft'
10
- | 'bottomRight'
11
- | 'top'
12
- | 'bottom';
17
+ const invokeOnClose = (notices: NoticeType[]) => {
18
+ const errors: unknown[] = [];
19
+
20
+ notices.forEach((notice) => {
21
+ try {
22
+ notice.onClose?.();
23
+ } catch (error) {
24
+ errors.push(error);
25
+ }
26
+ });
27
+
28
+ errors.forEach((error) => {
29
+ const reportError = (
30
+ globalThis as typeof globalThis & {
31
+ reportError?: (reportedError: unknown) => void;
32
+ }
33
+ ).reportError;
34
+ if (reportError) {
35
+ reportError(error);
36
+ } else {
37
+ console.error(error);
38
+ }
39
+ });
40
+ };
13
41
 
14
42
  const useNotice = () => {
15
43
  const [noticesByPlacement, setNoticesByPlacement] = useState<
16
44
  Map<PlacementType, NoticeType[]>
17
45
  >(new Map());
18
- const count = useRef(0);
19
-
20
- const add = useCallback((props: WrapperedNoticeConfig) => {
21
- const placement = props.placement || 'topRight';
22
- const id = count.current++;
46
+ const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(
47
+ null,
48
+ );
49
+ const noticesRef = useRef(new Map<PlacementType, NoticeType[]>());
50
+ const generationByKey = useRef(new Map<string | number, number>());
51
+ const generation = useRef(0);
52
+ const timerGeneration = useRef(0);
53
+ const autoKey = useRef(0);
23
54
 
24
- setNoticesByPlacement((prev) => {
25
- const newMap = new Map(prev);
26
- const notices = newMap.get(placement) || [];
27
- newMap.set(placement, [...notices, { key: id, ...props }]);
28
- return newMap;
29
- });
55
+ useEffect(() => {
56
+ setPortalContainer(document.body);
30
57
  }, []);
31
58
 
32
- const onDestroy = (key: string | number, placement: PlacementType) => {
33
- setNoticesByPlacement((prev) => {
34
- const newMap = new Map(prev);
35
- const notices = newMap.get(placement) || [];
36
- const filtered = notices.filter((notice) => notice.key !== key);
59
+ const updateNotices = useCallback(
60
+ (
61
+ updater: (
62
+ current: Map<PlacementType, NoticeType[]>,
63
+ ) => Map<PlacementType, NoticeType[]>,
64
+ ) => {
65
+ const nextNotices = updater(noticesRef.current);
66
+ noticesRef.current = nextNotices;
67
+ setNoticesByPlacement(nextNotices);
68
+ },
69
+ [],
70
+ );
71
+
72
+ const onDestroy = useCallback(
73
+ (key: string | number, expectedTimerGeneration?: number) => {
74
+ let target: NoticeType | undefined;
75
+ noticesRef.current.forEach((items) => {
76
+ target ??= items.find((item) => item.key === key);
77
+ });
37
78
 
38
- if (filtered.length === 0) {
39
- newMap.delete(placement);
40
- } else {
41
- newMap.set(placement, filtered);
79
+ if (
80
+ !target ||
81
+ (expectedTimerGeneration !== undefined &&
82
+ target.timerGeneration !== expectedTimerGeneration)
83
+ ) {
84
+ return;
42
85
  }
43
86
 
44
- return newMap;
45
- });
46
- };
47
-
48
- const noticeApi = {
49
- open: (props: string | NoticeConfig) => {
50
- add(
51
- typeof props === 'string'
52
- ? { content: props, status: 'info' }
53
- : { status: 'info', ...props },
54
- );
55
- },
56
- success: (props: string | NoticeConfig) => {
57
- add(
58
- typeof props === 'string'
59
- ? { content: props, status: 'success' }
60
- : { ...props, status: 'success' },
61
- );
62
- },
63
- error: (props: string | NoticeConfig) => {
64
- add(
65
- typeof props === 'string'
66
- ? { content: props, status: 'error' }
67
- : { ...props, status: 'error' },
68
- );
69
- },
70
- warning: (props: string | NoticeConfig) => {
71
- add(
72
- typeof props === 'string'
73
- ? { content: props, status: 'warning' }
74
- : { ...props, status: 'warning' },
75
- );
76
- },
77
- info: (props: string | NoticeConfig) => {
78
- add(
79
- typeof props === 'string'
80
- ? { content: props, status: 'info' }
81
- : { ...props, status: 'info' },
82
- );
87
+ generationByKey.current.delete(key);
88
+ updateNotices((current) => {
89
+ const next = new Map(current);
90
+ next.forEach((items, placement) => {
91
+ const filtered = items.filter((item) => item.key !== key);
92
+ if (filtered.length === 0) {
93
+ next.delete(placement);
94
+ } else if (filtered.length !== items.length) {
95
+ next.set(placement, filtered);
96
+ }
97
+ });
98
+ return next;
99
+ });
100
+ target.onClose?.();
83
101
  },
84
- destroy: (key: string | number) => {
85
- setNoticesByPlacement((prev) => {
86
- const newMap = new Map(prev);
102
+ [updateNotices],
103
+ );
87
104
 
88
- // Find and remove in all placements
89
- newMap.forEach((notices, placement) => {
90
- const filtered = notices.filter((notice) => notice.key !== key);
91
- if (filtered.length === 0) {
92
- newMap.delete(placement);
93
- } else if (filtered.length !== notices.length) {
94
- newMap.set(placement, filtered);
105
+ const add = useCallback(
106
+ (props: WrapperedNoticeConfig) => {
107
+ const placement = props.placement ?? 'topRight';
108
+ let key = props.key;
109
+ if (key === undefined || key === null) {
110
+ do {
111
+ key = `__yee_notice_auto_${autoKey.current++}`;
112
+ } while (generationByKey.current.has(key));
113
+ }
114
+
115
+ const keyGeneration =
116
+ generationByKey.current.get(key) ?? generation.current++;
117
+ generationByKey.current.set(key, keyGeneration);
118
+ const nextNotice = {
119
+ ...props,
120
+ key,
121
+ placement,
122
+ timerGeneration: timerGeneration.current++,
123
+ } as NoticeType;
124
+
125
+ updateNotices((current) => {
126
+ const next = new Map(current);
127
+ let existingIndex = -1;
128
+
129
+ next.forEach((items, currentPlacement) => {
130
+ const index = items.findIndex((item) => item.key === key);
131
+ if (index < 0) {
132
+ return;
133
+ }
134
+
135
+ if (currentPlacement === placement) {
136
+ existingIndex = index;
137
+ } else {
138
+ const filtered = items.filter((item) => item.key !== key);
139
+ if (filtered.length === 0) {
140
+ next.delete(currentPlacement);
141
+ } else {
142
+ next.set(currentPlacement, filtered);
143
+ }
95
144
  }
96
145
  });
97
146
 
98
- return newMap;
147
+ const targetNotices = (next.get(placement) || []).filter(
148
+ (item) => item.key !== key,
149
+ );
150
+ if (existingIndex >= 0) {
151
+ targetNotices.splice(existingIndex, 0, nextNotice);
152
+ } else {
153
+ targetNotices.push(nextNotice);
154
+ }
155
+ next.set(placement, targetNotices);
156
+ return next;
99
157
  });
158
+
159
+ return () => {
160
+ if (generationByKey.current.get(key) === keyGeneration) {
161
+ onDestroy(key);
162
+ }
163
+ };
100
164
  },
101
- clear: (placement?: PlacementType) => {
102
- if (placement) {
103
- setNoticesByPlacement((prev) => {
104
- const newMap = new Map(prev);
105
- newMap.delete(placement);
106
- return newMap;
107
- });
108
- } else {
109
- setNoticesByPlacement(new Map());
110
- }
165
+ [onDestroy, updateNotices],
166
+ );
167
+
168
+ const clear = useCallback(
169
+ (placement?: PlacementType) => {
170
+ const removed = placement
171
+ ? noticesRef.current.get(placement) || []
172
+ : Array.from(noticesRef.current.values()).flat();
173
+
174
+ removed.forEach((item) => generationByKey.current.delete(item.key));
175
+ updateNotices((current) => {
176
+ if (!placement) {
177
+ return new Map();
178
+ }
179
+ const next = new Map(current);
180
+ next.delete(placement);
181
+ return next;
182
+ });
183
+ invokeOnClose(removed);
111
184
  },
112
- };
185
+ [updateNotices],
186
+ );
187
+
188
+ const noticeApi = useMemo<NoticeApi>(
189
+ () => ({
190
+ open: (props) =>
191
+ add(
192
+ typeof props === 'string'
193
+ ? { content: props, status: 'info' }
194
+ : { status: 'info', ...props },
195
+ ),
196
+ success: (props) =>
197
+ add(
198
+ typeof props === 'string'
199
+ ? { content: props, status: 'success' }
200
+ : { ...props, status: 'success' },
201
+ ),
202
+ error: (props) =>
203
+ add(
204
+ typeof props === 'string'
205
+ ? { content: props, status: 'error' }
206
+ : { ...props, status: 'error' },
207
+ ),
208
+ warning: (props) =>
209
+ add(
210
+ typeof props === 'string'
211
+ ? { content: props, status: 'warning' }
212
+ : { ...props, status: 'warning' },
213
+ ),
214
+ info: (props) =>
215
+ add(
216
+ typeof props === 'string'
217
+ ? { content: props, status: 'info' }
218
+ : { ...props, status: 'info' },
219
+ ),
220
+ destroy: onDestroy,
221
+ clear,
222
+ }),
223
+ [add, clear, onDestroy],
224
+ );
225
+
226
+ const noticeHolders = useMemo(() => {
227
+ if (!portalContainer) {
228
+ return [];
229
+ }
113
230
 
114
- const noticeHolders = React.useMemo(() => {
115
231
  const holders: React.ReactPortal[] = [];
116
232
  noticesByPlacement.forEach((notices, placement) => {
117
233
  holders.push(
@@ -119,15 +235,15 @@ const useNotice = () => {
119
235
  <NoticeList
120
236
  items={notices}
121
237
  placement={placement}
122
- onDestroy={(key) => onDestroy(key, placement)}
238
+ onDestroy={onDestroy}
123
239
  />,
124
- document.body,
240
+ portalContainer,
125
241
  `notice-${placement}`,
126
242
  ),
127
243
  );
128
244
  });
129
245
  return holders;
130
- }, [noticesByPlacement]);
246
+ }, [noticesByPlacement, onDestroy, portalContainer]);
131
247
 
132
248
  return { noticeApi, noticeHolders };
133
249
  };
@@ -1,35 +1,70 @@
1
1
  import React from 'react';
2
2
  import { createRoot, Root } from 'react-dom/client';
3
- import { NoticeConfig, NoticeType, WrapperedNoticeConfig } from './interface';
3
+ import {
4
+ NoticeClose,
5
+ NoticeConfig,
6
+ NoticeType,
7
+ PlacementType,
8
+ WrapperedNoticeConfig,
9
+ } from './interface';
4
10
  import NoticeList from './notice-list';
5
11
 
6
- type PlacementType =
7
- | 'topLeft'
8
- | 'topRight'
9
- | 'bottomLeft'
10
- | 'bottomRight'
11
- | 'top'
12
- | 'bottom';
12
+ const noop: NoticeClose = () => {};
13
+
14
+ const invokeOnClose = (notices: NoticeType[]) => {
15
+ const errors: unknown[] = [];
16
+
17
+ notices.forEach((notice) => {
18
+ try {
19
+ notice.onClose?.();
20
+ } catch (error) {
21
+ errors.push(error);
22
+ }
23
+ });
24
+
25
+ errors.forEach((error) => {
26
+ const reportError = (
27
+ globalThis as typeof globalThis & {
28
+ reportError?: (reportedError: unknown) => void;
29
+ }
30
+ ).reportError;
31
+ if (reportError) {
32
+ reportError(error);
33
+ } else {
34
+ console.error(error);
35
+ }
36
+ });
37
+ };
13
38
 
14
39
  class NoticeWrapper {
15
40
  roots: Map<PlacementType, Root>;
16
41
  containers: Map<PlacementType, HTMLElement>;
17
42
  notices: Map<PlacementType, NoticeType[]>;
43
+ generationByKey: Map<string | number, number>;
44
+ generation: number;
45
+ timerGeneration: number;
18
46
 
19
47
  constructor() {
20
48
  this.roots = new Map();
21
49
  this.containers = new Map();
22
50
  this.notices = new Map();
51
+ this.generationByKey = new Map();
52
+ this.generation = 0;
53
+ this.timerGeneration = 0;
54
+ this.destroy = this.destroy.bind(this);
23
55
  }
24
56
 
25
57
  uuid(): string {
26
- return crypto.randomUUID
27
- ? crypto.randomUUID()
28
- : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
29
- const r = (Math.random() * 16) | 0;
30
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
31
- return v.toString(16);
32
- });
58
+ const randomUUID = globalThis.crypto?.randomUUID?.();
59
+ if (randomUUID) {
60
+ return randomUUID;
61
+ }
62
+
63
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
64
+ const r = (Math.random() * 16) | 0;
65
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
66
+ return v.toString(16);
67
+ });
33
68
  }
34
69
 
35
70
  private createContainer(placement: PlacementType): HTMLElement {
@@ -44,49 +79,52 @@ class NoticeWrapper {
44
79
 
45
80
  private cleanupContainer(placement: PlacementType) {
46
81
  const container = this.containers.get(placement);
47
- if (container && container.parentNode) {
82
+ if (container?.parentNode) {
48
83
  container.parentNode.removeChild(container);
49
- this.containers.delete(placement);
50
84
  }
85
+ this.containers.delete(placement);
51
86
  }
52
87
 
53
88
  unmount(placement?: PlacementType) {
54
- if (placement) {
55
- const root = this.roots.get(placement);
56
- if (root) {
57
- root.unmount();
58
- this.roots.delete(placement);
59
- this.cleanupContainer(placement);
60
- this.notices.delete(placement);
61
- }
62
- } else {
63
- // Clean up all
64
- this.roots.forEach((root, placement) => {
65
- root.unmount();
66
- this.cleanupContainer(placement);
67
- });
89
+ const placements = placement
90
+ ? [placement]
91
+ : Array.from(this.notices.keys());
92
+ const removed = placements.flatMap((item) => this.notices.get(item) || []);
93
+
94
+ placements.forEach((item) => {
95
+ this.roots.get(item)?.unmount();
96
+ this.roots.delete(item);
97
+ this.cleanupContainer(item);
98
+ this.notices.delete(item);
99
+ });
100
+
101
+ if (!placement) {
68
102
  this.roots.clear();
69
103
  this.containers.clear();
70
- this.notices.clear();
71
104
  }
105
+
106
+ removed.forEach((item) => {
107
+ this.generationByKey.delete(item.key);
108
+ });
109
+ invokeOnClose(removed);
72
110
  }
73
111
 
74
112
  render(placement: PlacementType) {
75
- const notices = this.notices.get(placement) || [];
113
+ if (typeof document === 'undefined') {
114
+ return;
115
+ }
76
116
 
117
+ const notices = this.notices.get(placement) || [];
77
118
  if (notices.length === 0) {
78
- const root = this.roots.get(placement);
79
- if (root) {
80
- root.unmount();
81
- this.roots.delete(placement);
82
- this.cleanupContainer(placement);
83
- }
119
+ this.roots.get(placement)?.unmount();
120
+ this.roots.delete(placement);
121
+ this.cleanupContainer(placement);
122
+ this.notices.delete(placement);
84
123
  return;
85
124
  }
86
125
 
87
126
  const container = this.createContainer(placement);
88
127
  let root = this.roots.get(placement);
89
-
90
128
  if (!root) {
91
129
  root = createRoot(container);
92
130
  this.roots.set(placement, root);
@@ -96,53 +134,102 @@ class NoticeWrapper {
96
134
  <NoticeList
97
135
  items={notices}
98
136
  placement={placement}
99
- onDestroy={(key) => this.destroy(key, placement)}
137
+ onDestroy={this.destroy}
100
138
  />,
101
139
  );
102
140
  }
103
141
 
104
- show(params: WrapperedNoticeConfig) {
105
- const placement = params.placement || 'topRight';
106
-
107
- if (!params.key) {
108
- params.key = this.uuid();
142
+ show(params: WrapperedNoticeConfig): NoticeClose {
143
+ if (typeof document === 'undefined') {
144
+ return noop;
109
145
  }
110
146
 
111
- const notices = this.notices.get(placement) || [];
112
- const existIndex = notices.findIndex((item) => item.key === params.key);
147
+ const placement = params.placement ?? 'topRight';
148
+ const key = params.key ?? this.uuid();
149
+ const keyGeneration =
150
+ this.generationByKey.get(key) ?? this.generation++;
151
+ this.generationByKey.set(key, keyGeneration);
113
152
 
114
- if (existIndex >= 0) {
115
- notices[existIndex] = params as NoticeType;
153
+ const affectedPlacements = new Set<PlacementType>([placement]);
154
+ this.notices.forEach((items, currentPlacement) => {
155
+ if (
156
+ currentPlacement !== placement &&
157
+ items.some((item) => item.key === key)
158
+ ) {
159
+ this.notices.set(
160
+ currentPlacement,
161
+ items.filter((item) => item.key !== key),
162
+ );
163
+ affectedPlacements.add(currentPlacement);
164
+ }
165
+ });
166
+
167
+ const currentNotices = this.notices.get(placement) || [];
168
+ const existingIndex = currentNotices.findIndex((item) => item.key === key);
169
+ const nextNotice = {
170
+ ...params,
171
+ key,
172
+ placement,
173
+ timerGeneration: this.timerGeneration++,
174
+ } as NoticeType;
175
+ const nextNotices = currentNotices.filter((item) => item.key !== key);
176
+
177
+ if (existingIndex >= 0) {
178
+ nextNotices.splice(existingIndex, 0, nextNotice);
116
179
  } else {
117
- notices.push(params as NoticeType);
180
+ nextNotices.push(nextNotice);
118
181
  }
119
182
 
120
- this.notices.set(placement, notices);
121
- this.render(placement);
183
+ this.notices.set(placement, nextNotices);
184
+ affectedPlacements.forEach((item) => this.render(item));
185
+
186
+ return () => {
187
+ if (this.generationByKey.get(key) === keyGeneration) {
188
+ this.destroy(key);
189
+ }
190
+ };
122
191
  }
123
192
 
124
- destroy(key: string | number, placement?: PlacementType) {
125
- if (placement) {
126
- const notices = this.notices.get(placement) || [];
127
- const filtered = notices.filter((item) => item.key !== key);
128
- this.notices.set(placement, filtered);
129
- this.render(placement);
130
- } else {
131
- // Find and remove in all placements
132
- this.notices.forEach((notices, p) => {
133
- const filtered = notices.filter((item) => item.key !== key);
134
- this.notices.set(p, filtered);
135
- this.render(p);
136
- });
193
+ destroy(key: string | number, expectedTimerGeneration?: number) {
194
+ let target: NoticeType | undefined;
195
+ this.notices.forEach((items) => {
196
+ target ??= items.find((item) => item.key === key);
197
+ });
198
+
199
+ if (
200
+ !target ||
201
+ (expectedTimerGeneration !== undefined &&
202
+ target.timerGeneration !== expectedTimerGeneration)
203
+ ) {
204
+ return;
137
205
  }
206
+
207
+ const affectedPlacements = new Set<PlacementType>();
208
+ this.notices.forEach((items, placement) => {
209
+ if (items.some((item) => item.key === key)) {
210
+ this.notices.set(
211
+ placement,
212
+ items.filter((item) => item.key !== key),
213
+ );
214
+ affectedPlacements.add(placement);
215
+ }
216
+ });
217
+
218
+ this.generationByKey.delete(key);
219
+ affectedPlacements.forEach((item) => this.render(item));
220
+ target.onClose?.();
221
+ }
222
+
223
+ clear(placement?: PlacementType) {
224
+ this.unmount(placement);
138
225
  }
139
226
 
140
227
  open(params: string | NoticeConfig) {
141
- this.show(typeof params === 'string' ? { content: params } : params);
228
+ return this.show(typeof params === 'string' ? { content: params } : params);
142
229
  }
143
230
 
144
231
  info(params: string | NoticeConfig) {
145
- this.show(
232
+ return this.show(
146
233
  typeof params === 'string'
147
234
  ? { status: 'info', content: params }
148
235
  : { ...params, status: 'info' },
@@ -150,7 +237,7 @@ class NoticeWrapper {
150
237
  }
151
238
 
152
239
  success(params: string | NoticeConfig) {
153
- this.show(
240
+ return this.show(
154
241
  typeof params === 'string'
155
242
  ? { status: 'success', content: params }
156
243
  : { ...params, status: 'success' },
@@ -158,7 +245,7 @@ class NoticeWrapper {
158
245
  }
159
246
 
160
247
  warning(params: string | NoticeConfig) {
161
- this.show(
248
+ return this.show(
162
249
  typeof params === 'string'
163
250
  ? { status: 'warning', content: params }
164
251
  : { ...params, status: 'warning' },
@@ -166,7 +253,7 @@ class NoticeWrapper {
166
253
  }
167
254
 
168
255
  error(params: string | NoticeConfig) {
169
- this.show(
256
+ return this.show(
170
257
  typeof params === 'string'
171
258
  ? { status: 'error', content: params }
172
259
  : { ...params, status: 'error' },
@@ -187,4 +274,5 @@ export const notice = {
187
274
  warning: noticeWrapper.warning.bind(noticeWrapper),
188
275
  error: noticeWrapper.error.bind(noticeWrapper),
189
276
  destroy: noticeWrapper.destroy.bind(noticeWrapper),
277
+ clear: noticeWrapper.clear.bind(noticeWrapper),
190
278
  };
@@ -224,6 +224,8 @@
224
224
  /* --Dialog-- */
225
225
  --yee-dialog-z-index: var(--yee-z-index-overlay);
226
226
  --yee-dialog-mask-z-index: var(--yee-z-index-mask);
227
+ --yee-dialog-title-font-size: 18px;
228
+ --yee-dialog-title-font-weight: 600;
227
229
  --yee-dialog-content-wrapper-z-index: var(--yee-z-index-overlay);
228
230
  --yee-dialog-header-margin-bottom: 12px;
229
231
  --yee-dialog-content-margin-top: 10%;
@@ -321,9 +323,9 @@
321
323
  --yee-menu-submenu-vertical-top: -4px;
322
324
 
323
325
  /* --Message-- */
324
- --yee-message-gap: 8px;
325
- --yee-message-padding: 12px 16px;
326
- --yee-message-border-radius: 4px;
326
+ --yee-message-gap: 6px;
327
+ --yee-message-padding: 9px 12px;
328
+ --yee-message-border-radius: 8px;
327
329
  --yee-message-box-shadow: 0 8px 24px 0 var(--yee-box-shadow-color);
328
330
  --yee-message-icon-size: 20px;
329
331
  --yee-message-list-top: 24px;