@class-kit/react 0.1.0 → 0.1.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 (275) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +20 -2
  3. package/README.md +10 -2
  4. package/dist/components/affix.cjs.map +1 -1
  5. package/dist/components/affix.js.map +1 -1
  6. package/dist/components/backtop.cjs.map +1 -1
  7. package/dist/components/backtop.js.map +1 -1
  8. package/dist/components/badge.cjs.map +1 -1
  9. package/dist/components/badge.js.map +1 -1
  10. package/dist/components/barcode.cjs.map +1 -1
  11. package/dist/components/barcode.js.map +1 -1
  12. package/dist/components/breadcrumb.cjs.map +1 -1
  13. package/dist/components/breadcrumb.js.map +1 -1
  14. package/dist/components/button.cjs.map +1 -1
  15. package/dist/components/button.js.map +1 -1
  16. package/dist/components/calendar.cjs.map +1 -1
  17. package/dist/components/calendar.js.map +1 -1
  18. package/dist/components/canvas-editor.cjs.map +1 -1
  19. package/dist/components/canvas-editor.js.map +1 -1
  20. package/dist/components/canvas-image.cjs.map +1 -1
  21. package/dist/components/canvas-image.js.map +1 -1
  22. package/dist/components/chat-textarea.cjs +103 -0
  23. package/dist/components/chat-textarea.cjs.map +1 -0
  24. package/dist/components/chat-textarea.d.ts +3 -0
  25. package/dist/components/chat-textarea.d.ts.map +1 -0
  26. package/dist/components/chat-textarea.js +101 -0
  27. package/dist/components/chat-textarea.js.map +1 -0
  28. package/dist/components/chat-virtual-list.cjs +91 -6
  29. package/dist/components/chat-virtual-list.cjs.map +1 -1
  30. package/dist/components/chat-virtual-list.js +91 -6
  31. package/dist/components/chat-virtual-list.js.map +1 -1
  32. package/dist/components/checkbox.cjs.map +1 -1
  33. package/dist/components/checkbox.js.map +1 -1
  34. package/dist/components/checked.cjs.map +1 -1
  35. package/dist/components/checked.js.map +1 -1
  36. package/dist/components/code-preview.cjs +190 -0
  37. package/dist/components/code-preview.cjs.map +1 -0
  38. package/dist/components/code-preview.d.ts +3 -0
  39. package/dist/components/code-preview.d.ts.map +1 -0
  40. package/dist/components/code-preview.js +188 -0
  41. package/dist/components/code-preview.js.map +1 -0
  42. package/dist/components/color-picker.cjs +2 -2
  43. package/dist/components/color-picker.cjs.map +1 -1
  44. package/dist/components/color-picker.js +2 -2
  45. package/dist/components/color-picker.js.map +1 -1
  46. package/dist/components/comic-reader.cjs.map +1 -1
  47. package/dist/components/comic-reader.js.map +1 -1
  48. package/dist/components/config-provider.cjs.map +1 -1
  49. package/dist/components/config-provider.js.map +1 -1
  50. package/dist/components/config-table.cjs.map +1 -1
  51. package/dist/components/config-table.js.map +1 -1
  52. package/dist/components/countdown.cjs.map +1 -1
  53. package/dist/components/countdown.js.map +1 -1
  54. package/dist/components/danmaku.cjs.map +1 -1
  55. package/dist/components/danmaku.js.map +1 -1
  56. package/dist/components/date-picker.cjs.map +1 -1
  57. package/dist/components/date-picker.js.map +1 -1
  58. package/dist/components/date-range-picker.cjs.map +1 -1
  59. package/dist/components/date-range-picker.js.map +1 -1
  60. package/dist/components/design-effect.cjs.map +1 -1
  61. package/dist/components/design-effect.js.map +1 -1
  62. package/dist/components/drag-drop-board.cjs +24 -0
  63. package/dist/components/drag-drop-board.cjs.map +1 -1
  64. package/dist/components/drag-drop-board.js +24 -0
  65. package/dist/components/drag-drop-board.js.map +1 -1
  66. package/dist/components/draggable.cjs.map +1 -1
  67. package/dist/components/draggable.js.map +1 -1
  68. package/dist/components/ellipsis-text.cjs.map +1 -1
  69. package/dist/components/ellipsis-text.js.map +1 -1
  70. package/dist/components/empty.cjs.map +1 -1
  71. package/dist/components/empty.js.map +1 -1
  72. package/dist/components/field.cjs.map +1 -1
  73. package/dist/components/field.js.map +1 -1
  74. package/dist/components/file-preview.cjs.map +1 -1
  75. package/dist/components/file-preview.js.map +1 -1
  76. package/dist/components/floating-ball.cjs.map +1 -1
  77. package/dist/components/floating-ball.js.map +1 -1
  78. package/dist/components/form.cjs.map +1 -1
  79. package/dist/components/form.js.map +1 -1
  80. package/dist/components/gradient-text.cjs.map +1 -1
  81. package/dist/components/gradient-text.js.map +1 -1
  82. package/dist/components/height-transition.cjs +35 -0
  83. package/dist/components/height-transition.cjs.map +1 -0
  84. package/dist/components/height-transition.d.ts +3 -0
  85. package/dist/components/height-transition.d.ts.map +1 -0
  86. package/dist/components/height-transition.js +33 -0
  87. package/dist/components/height-transition.js.map +1 -0
  88. package/dist/components/input.cjs.map +1 -1
  89. package/dist/components/input.js.map +1 -1
  90. package/dist/components/lazy-image.cjs.map +1 -1
  91. package/dist/components/lazy-image.js.map +1 -1
  92. package/dist/components/live-room.cjs.map +1 -1
  93. package/dist/components/live-room.js.map +1 -1
  94. package/dist/components/loading.cjs.map +1 -1
  95. package/dist/components/loading.js.map +1 -1
  96. package/dist/components/marquee.cjs.map +1 -1
  97. package/dist/components/marquee.js.map +1 -1
  98. package/dist/components/masonry-virtual-list.cjs.map +1 -1
  99. package/dist/components/masonry-virtual-list.js.map +1 -1
  100. package/dist/components/menu.cjs +100 -0
  101. package/dist/components/menu.cjs.map +1 -0
  102. package/dist/components/menu.d.ts +3 -0
  103. package/dist/components/menu.d.ts.map +1 -0
  104. package/dist/components/menu.js +98 -0
  105. package/dist/components/menu.js.map +1 -0
  106. package/dist/components/modal.cjs.map +1 -1
  107. package/dist/components/modal.js.map +1 -1
  108. package/dist/components/multi-column-picker.cjs.map +1 -1
  109. package/dist/components/multi-column-picker.js.map +1 -1
  110. package/dist/components/novel-reader.cjs.map +1 -1
  111. package/dist/components/novel-reader.js.map +1 -1
  112. package/dist/components/number-input.cjs.map +1 -1
  113. package/dist/components/number-input.js.map +1 -1
  114. package/dist/components/orbital-sphere.cjs +40 -0
  115. package/dist/components/orbital-sphere.cjs.map +1 -0
  116. package/dist/components/orbital-sphere.d.ts +3 -0
  117. package/dist/components/orbital-sphere.d.ts.map +1 -0
  118. package/dist/components/orbital-sphere.js +38 -0
  119. package/dist/components/orbital-sphere.js.map +1 -0
  120. package/dist/components/pagination.cjs.map +1 -1
  121. package/dist/components/pagination.js.map +1 -1
  122. package/dist/components/password-input.cjs.map +1 -1
  123. package/dist/components/password-input.js.map +1 -1
  124. package/dist/components/popconfirm.cjs +40 -6
  125. package/dist/components/popconfirm.cjs.map +1 -1
  126. package/dist/components/popconfirm.js +41 -7
  127. package/dist/components/popconfirm.js.map +1 -1
  128. package/dist/components/popup.cjs +2 -1
  129. package/dist/components/popup.cjs.map +1 -1
  130. package/dist/components/popup.js +2 -1
  131. package/dist/components/popup.js.map +1 -1
  132. package/dist/components/progress-bar.cjs.map +1 -1
  133. package/dist/components/progress-bar.js.map +1 -1
  134. package/dist/components/qr-code.cjs.map +1 -1
  135. package/dist/components/qr-code.js.map +1 -1
  136. package/dist/components/radio-group.cjs.map +1 -1
  137. package/dist/components/radio-group.js.map +1 -1
  138. package/dist/components/rating.cjs.map +1 -1
  139. package/dist/components/rating.js.map +1 -1
  140. package/dist/components/rolling-number.cjs.map +1 -1
  141. package/dist/components/rolling-number.js.map +1 -1
  142. package/dist/components/select.cjs.map +1 -1
  143. package/dist/components/select.js.map +1 -1
  144. package/dist/components/signature.cjs +1 -1
  145. package/dist/components/signature.cjs.map +1 -1
  146. package/dist/components/signature.js +1 -1
  147. package/dist/components/signature.js.map +1 -1
  148. package/dist/components/skeleton.cjs.map +1 -1
  149. package/dist/components/skeleton.js.map +1 -1
  150. package/dist/components/slide-captcha.cjs.map +1 -1
  151. package/dist/components/slide-captcha.js.map +1 -1
  152. package/dist/components/swiper.cjs.map +1 -1
  153. package/dist/components/swiper.js.map +1 -1
  154. package/dist/components/switch.cjs.map +1 -1
  155. package/dist/components/switch.js.map +1 -1
  156. package/dist/components/table.cjs.map +1 -1
  157. package/dist/components/table.js.map +1 -1
  158. package/dist/components/tabs.cjs.map +1 -1
  159. package/dist/components/tabs.js.map +1 -1
  160. package/dist/components/tag.cjs.map +1 -1
  161. package/dist/components/tag.js.map +1 -1
  162. package/dist/components/textarea.cjs.map +1 -1
  163. package/dist/components/textarea.js.map +1 -1
  164. package/dist/components/tilt-card.cjs.map +1 -1
  165. package/dist/components/tilt-card.js.map +1 -1
  166. package/dist/components/timeline.cjs.map +1 -1
  167. package/dist/components/timeline.js.map +1 -1
  168. package/dist/components/toast.cjs +146 -14
  169. package/dist/components/toast.cjs.map +1 -1
  170. package/dist/components/toast.d.ts +1 -1
  171. package/dist/components/toast.d.ts.map +1 -1
  172. package/dist/components/toast.js +144 -15
  173. package/dist/components/toast.js.map +1 -1
  174. package/dist/components/tooltip.cjs +38 -5
  175. package/dist/components/tooltip.cjs.map +1 -1
  176. package/dist/components/tooltip.js +39 -6
  177. package/dist/components/tooltip.js.map +1 -1
  178. package/dist/components/typewriter-text.cjs.map +1 -1
  179. package/dist/components/typewriter-text.js.map +1 -1
  180. package/dist/components/upload.cjs.map +1 -1
  181. package/dist/components/upload.js.map +1 -1
  182. package/dist/components/verification-code.cjs.map +1 -1
  183. package/dist/components/verification-code.js.map +1 -1
  184. package/dist/components/video-detail-transition.cjs.map +1 -1
  185. package/dist/components/video-detail-transition.js.map +1 -1
  186. package/dist/components/video-player.cjs.map +1 -1
  187. package/dist/components/video-player.js.map +1 -1
  188. package/dist/components/virtual-list.cjs.map +1 -1
  189. package/dist/components/virtual-list.js.map +1 -1
  190. package/dist/components/virtual-select.cjs.map +1 -1
  191. package/dist/components/virtual-select.js.map +1 -1
  192. package/dist/components/virtual-table.cjs.map +1 -1
  193. package/dist/components/virtual-table.js.map +1 -1
  194. package/dist/{import-transform-jqLjtD6M.js → import-transform-Cw4K2Iky.js} +7 -1
  195. package/dist/import-transform-Cw4K2Iky.js.map +1 -0
  196. package/dist/{import-transform-DIRVOYvz.js → import-transform-ieMdZBuN.js} +7 -1
  197. package/dist/import-transform-ieMdZBuN.js.map +1 -0
  198. package/dist/import-transform.d.ts.map +1 -1
  199. package/dist/index.cjs +657 -35
  200. package/dist/index.cjs.map +1 -1
  201. package/dist/index.d.ts +71 -8
  202. package/dist/index.d.ts.map +1 -1
  203. package/dist/index.js +635 -38
  204. package/dist/index.js.map +1 -1
  205. package/dist/styles/base.css +168 -41
  206. package/dist/styles/components/barcode.css +206 -0
  207. package/dist/styles/components/button.css +7 -2
  208. package/dist/styles/components/calendar.css +0 -23
  209. package/dist/styles/components/canvas-editor.css +18 -21
  210. package/dist/styles/components/canvas-image.css +0 -23
  211. package/dist/styles/components/chat-textarea.css +124 -0
  212. package/dist/styles/components/chat-virtual-list.css +15 -26
  213. package/dist/styles/components/checkbox.css +39 -23
  214. package/dist/styles/components/checked.css +39 -23
  215. package/dist/styles/components/code-preview.css +208 -0
  216. package/dist/styles/components/color-picker.css +46 -27
  217. package/dist/styles/components/config-table.css +0 -23
  218. package/dist/styles/components/date-picker.css +39 -23
  219. package/dist/styles/components/date-range-picker.css +39 -23
  220. package/dist/styles/components/design-effect.css +5 -1
  221. package/dist/styles/components/drag-drop-board.css +6 -1
  222. package/dist/styles/components/field.css +39 -23
  223. package/dist/styles/components/form.css +39 -23
  224. package/dist/styles/components/gradient-text.css +4 -0
  225. package/dist/styles/components/height-transition.css +50 -0
  226. package/dist/styles/components/input.css +39 -23
  227. package/dist/styles/components/loading.css +47 -0
  228. package/dist/styles/components/menu.css +286 -0
  229. package/dist/styles/components/modal.css +2 -23
  230. package/dist/styles/components/number-input.css +39 -23
  231. package/dist/styles/components/orbital-sphere.css +224 -0
  232. package/dist/styles/components/password-input.css +39 -23
  233. package/dist/styles/components/popconfirm.css +5 -0
  234. package/dist/styles/components/popup.css +2 -23
  235. package/dist/styles/components/qr-code.css +206 -0
  236. package/dist/styles/components/radio-group.css +39 -23
  237. package/dist/styles/components/rating.css +39 -23
  238. package/dist/styles/components/select.css +39 -23
  239. package/dist/styles/components/signature.css +39 -23
  240. package/dist/styles/components/switch.css +39 -23
  241. package/dist/styles/components/table.css +0 -23
  242. package/dist/styles/components/tabs.css +10 -0
  243. package/dist/styles/components/textarea.css +39 -23
  244. package/dist/styles/components/tilt-card.css +2 -23
  245. package/dist/styles/components/toast.css +109 -48
  246. package/dist/styles/components/tooltip.css +8 -23
  247. package/dist/styles/components/typewriter-text.css +4 -0
  248. package/dist/styles/components/upload.css +39 -23
  249. package/dist/styles/components/verification-code.css +39 -23
  250. package/dist/styles/components/virtual-select.css +39 -23
  251. package/dist/styles/components/virtual-table.css +0 -23
  252. package/dist/styles/components.css +1250 -165
  253. package/dist/styles/themes/business-blue.css +375 -0
  254. package/dist/styles/themes/cartoon-3d.css +695 -0
  255. package/dist/styles/themes/creative-purple.css +376 -0
  256. package/dist/styles/themes/dark-tech.css +495 -0
  257. package/dist/styles/themes/liquid-glass.css +526 -21
  258. package/dist/styles/themes/minimal.css +406 -14
  259. package/dist/styles/themes/soft-pink.css +376 -0
  260. package/dist/styles.css +1250 -165
  261. package/dist/vite.cjs +1 -1
  262. package/dist/vite.cjs.map +1 -1
  263. package/dist/vite.d.ts +1 -1
  264. package/dist/vite.js +1 -1
  265. package/dist/vite.js.map +1 -1
  266. package/dist/webpack-loader.cjs +1 -1
  267. package/dist/webpack-loader.js +1 -1
  268. package/package.json +4 -4
  269. package/dist/import-transform-DIRVOYvz.js.map +0 -1
  270. package/dist/import-transform-jqLjtD6M.js.map +0 -1
  271. package/dist/styles/themes/amber.css +0 -19
  272. package/dist/styles/themes/emerald.css +0 -19
  273. package/dist/styles/themes/rose.css +0 -19
  274. package/dist/styles/themes/sky.css +0 -19
  275. package/dist/styles/themes/violet.css +0 -19
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React, { useEffect, useMemo, useContext, useSyncExternalStore, createContext } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
- import { ToastManager, configureClassComponents, FormController, getClassComponentsLocale, PasswordInputController, ColorPickerController, DatePickerController, DateRangePickerController, VerificationCodeController, SlideCaptchaController, VirtualSelectController, VirtualListController, BadgeController, TagController, SkeletonController, BreadcrumbController, PaginationController, getSafeTooltipPlacement, BacktopController, AffixController, ModalController, PopupController, TooltipController, TabsController, RollingNumberController, CountdownController, CalendarController, TypewriterTextController, DesignEffectController, GradientTextController, LazyImageController, TiltCardController, MarqueeController, ProgressController, SignatureController, VideoPlayerController, VideoDetailTransitionController, FilePreviewController, CanvasImageController, CanvasEditorController, TimelineController, DragController, DropController, GestureController, FloatingBallController, MultiColumnPickerController, SwiperController, MasonryVirtualListController, TableController, UploadController, NovelReaderController, ComicReaderController, DanmakuController, LiveRoomController, ChatVirtualListController } from '@class-kit/core';
5
- export { AffixController, BacktopController, BadgeController, BreadcrumbController, CalendarController, CanvasEditorController, CanvasImageController, ChatVirtualListController, ColorPickerController, ComicReaderController, CountdownController, DanmakuController, DatePickerController, DateRangePickerController, DesignEffectController, DragController, DropController, FilePreviewController, FloatingBallController, FormController, GestureController, GradientTextController, LazyImageController, LiveRoomController, MasonryVirtualListController, ModalController, MultiColumnPickerController, NovelReaderController, PaginationController, PasswordInputController, PopupController, ProgressController, SignatureController, SkeletonController, SlideCaptchaController, SwiperController, TableController, TabsController, TagController, TiltCardController, TimelineController, ToastManager, TooltipController, TypewriterTextController, UploadController, VerificationCodeController, VideoDetailTransitionController, VideoPlayerController, VirtualListController, VirtualSelectController, configureClassComponents, getClassComponentsConfig, getClassComponentsLocale, resetClassComponentsConfig } from '@class-kit/core';
4
+ import { ToastManager, configureClassComponents, FormController, getClassComponentsLocale, PasswordInputController, ColorPickerController, DatePickerController, DateRangePickerController, VerificationCodeController, SlideCaptchaController, ChatTextareaController, VirtualSelectController, VirtualListController, BadgeController, TagController, SkeletonController, BreadcrumbController, MenuController, PaginationController, getSafeTooltipPlacement, BacktopController, AffixController, ModalController, PopupController, TooltipController, TabsController, RollingNumberController, CountdownController, CalendarController, TypewriterTextController, DesignEffectController, GradientTextController, LazyImageController, TiltCardController, OrbitalSphereController, HeightTransitionController, CodePreviewController, MarqueeController, ProgressController, SignatureController, VideoPlayerController, VideoDetailTransitionController, FilePreviewController, CanvasImageController, CanvasEditorController, TimelineController, DragController, DropController, GestureController, FloatingBallController, MultiColumnPickerController, SwiperController, MasonryVirtualListController, TableController, UploadController, NovelReaderController, ComicReaderController, DanmakuController, LiveRoomController, ChatVirtualListController } from '@class-kit/core';
5
+ export { AffixController, BacktopController, BadgeController, BreadcrumbController, CalendarController, CanvasEditorController, CanvasImageController, ChatTextareaController, ChatVirtualListController, CodePreviewController, ColorPickerController, ComicReaderController, CountdownController, DanmakuController, DatePickerController, DateRangePickerController, DesignEffectController, DragController, DropController, FilePreviewController, FloatingBallController, FormController, GestureController, GradientTextController, HeightTransitionController, LazyImageController, LiveRoomController, MasonryVirtualListController, MenuController, ModalController, MultiColumnPickerController, NovelReaderController, OrbitalSphereController, PaginationController, PasswordInputController, PopupController, ProgressController, SignatureController, SkeletonController, SlideCaptchaController, SwiperController, TableController, TabsController, TagController, TiltCardController, TimelineController, ToastManager, TooltipController, TypewriterTextController, UploadController, VerificationCodeController, VideoDetailTransitionController, VideoPlayerController, VirtualListController, VirtualSelectController, configureClassComponents, getClassComponentsConfig, getClassComponentsLocale, resetClassComponentsConfig } from '@class-kit/core';
6
6
 
7
7
  const FormContext = /*#__PURE__*/ createContext(null);
8
8
  const defaultToastManager = /*#__PURE__*/ new ToastManager();
@@ -174,23 +174,27 @@ function usePresence(open, duration) {
174
174
  return [mounted, state];
175
175
  }
176
176
  function useAnimatedToasts(items, duration) {
177
- const [rendered, setRendered] = React.useState(() => items.map((item) => ({ item, state: "enter" })));
177
+ const [rendered, setRendered] = React.useState(() => items.map((item) => ({ item, motion: false, state: "enter" })));
178
178
  useEffect(() => {
179
179
  let enteringIds = [];
180
180
  setRendered((current) => {
181
181
  const currentIds = new Set(current.map(({ item }) => item.id));
182
182
  const incomingIds = new Set(items.map((item) => item.id));
183
+ const incomingById = new Map(items.map((item) => [item.id, item]));
183
184
  enteringIds = items
184
185
  .filter((item) => !currentIds.has(item.id))
185
186
  .map((item) => item.id);
186
- const next = items.map((item) => ({
187
- item,
188
- state: currentIds.has(item.id) ? "enter" : "exit",
187
+ const retainedOrExiting = current.map(({ item }) => ({
188
+ item: incomingById.get(item.id) ?? item,
189
+ motion: !incomingIds.has(item.id),
190
+ state: incomingIds.has(item.id)
191
+ ? "enter"
192
+ : "exit",
189
193
  }));
190
- const exiting = current
191
- .filter(({ item }) => !incomingIds.has(item.id))
192
- .map(({ item }) => ({ item, state: "exit" }));
193
- return [...next, ...exiting];
194
+ const entering = items
195
+ .filter((item) => !currentIds.has(item.id))
196
+ .map((item) => ({ item, motion: true, state: "prepare" }));
197
+ return [...retainedOrExiting, ...entering];
194
198
  });
195
199
  if (!enteringIds.length)
196
200
  return undefined;
@@ -201,6 +205,22 @@ function useAnimatedToasts(items, duration) {
201
205
  });
202
206
  return () => window.cancelAnimationFrame(frame);
203
207
  }, [items]);
208
+ useEffect(() => {
209
+ if (!rendered.some((entry) => entry.motion && entry.state === "enter"))
210
+ return;
211
+ if (duration <= 0) {
212
+ setRendered((current) => current.map((entry) => entry.motion && entry.state === "enter"
213
+ ? { ...entry, motion: false }
214
+ : entry));
215
+ return;
216
+ }
217
+ const timer = window.setTimeout(() => {
218
+ setRendered((current) => current.map((entry) => entry.motion && entry.state === "enter"
219
+ ? { ...entry, motion: false }
220
+ : entry));
221
+ }, duration);
222
+ return () => window.clearTimeout(timer);
223
+ }, [duration, rendered]);
204
224
  useEffect(() => {
205
225
  if (!rendered.some((entry) => entry.state === "exit"))
206
226
  return;
@@ -215,6 +235,80 @@ function useAnimatedToasts(items, duration) {
215
235
  }, [duration, rendered]);
216
236
  return rendered;
217
237
  }
238
+ function normalizeToastMaxVisible(maxVisible) {
239
+ if (typeof maxVisible !== "number" || !Number.isFinite(maxVisible))
240
+ return 3;
241
+ return Math.max(1, Math.min(6, Math.floor(maxVisible)));
242
+ }
243
+ function isToastTopPosition(position) {
244
+ return (position === "top" || position === "top-left" || position === "top-right");
245
+ }
246
+ function useToastFlip(entries, duration, easing) {
247
+ const nodeMapRef = React.useRef(new Map());
248
+ const rectMapRef = React.useRef(new Map());
249
+ const frameRef = React.useRef(0);
250
+ const setNode = React.useCallback((id, node) => {
251
+ if (node)
252
+ nodeMapRef.current.set(id, node);
253
+ else
254
+ nodeMapRef.current.delete(id);
255
+ }, []);
256
+ React.useLayoutEffect(() => {
257
+ if (frameRef.current)
258
+ window.cancelAnimationFrame(frameRef.current);
259
+ const previousRects = rectMapRef.current;
260
+ const nextRects = new Map();
261
+ const activeIds = new Set(entries
262
+ .filter((entry) => entry.motion && entry.state !== "exit")
263
+ .map((entry) => entry.item.id));
264
+ nodeMapRef.current.forEach((node, id) => {
265
+ if (!activeIds.has(id))
266
+ return;
267
+ const rect = node.getBoundingClientRect();
268
+ nextRects.set(id, rect);
269
+ const previous = previousRects.get(id);
270
+ if (!previous)
271
+ return;
272
+ const deltaY = previous.top - rect.top;
273
+ if (Math.abs(deltaY) < 1)
274
+ return;
275
+ node.style.transition = "none";
276
+ node.style.setProperty("--cc-toast-stack-offset", `${deltaY}px`);
277
+ node.style.willChange = "transform, opacity";
278
+ });
279
+ frameRef.current = window.requestAnimationFrame(() => {
280
+ nodeMapRef.current.forEach((node, id) => {
281
+ if (!nextRects.has(id))
282
+ return;
283
+ node.style.transition = `transform ${duration}ms ${easing}, opacity ${duration}ms ${easing}, margin ${duration}ms ${easing}`;
284
+ node.style.removeProperty("--cc-toast-stack-offset");
285
+ window.setTimeout(() => {
286
+ node.style.transition = "";
287
+ node.style.willChange = "";
288
+ }, duration);
289
+ });
290
+ frameRef.current = 0;
291
+ });
292
+ rectMapRef.current = nextRects;
293
+ return () => {
294
+ if (frameRef.current)
295
+ window.cancelAnimationFrame(frameRef.current);
296
+ };
297
+ }, [duration, easing, entries]);
298
+ return setNode;
299
+ }
300
+ function useToastItems(manager) {
301
+ const snapshotRef = React.useRef(manager.getToasts());
302
+ const managerRef = React.useRef(manager);
303
+ if (managerRef.current !== manager) {
304
+ managerRef.current = manager;
305
+ snapshotRef.current = manager.getToasts();
306
+ }
307
+ return useSyncExternalStore(React.useCallback((notify) => manager.subscribe((items) => {
308
+ snapshotRef.current = items;
309
+ notify();
310
+ }), [manager]), React.useCallback(() => snapshotRef.current, [manager]), React.useCallback(() => snapshotRef.current, [manager]));
311
+ }
218
312
  function resolveTabsDirection(tabs, previousId, nextId, direction) {
219
313
  if (direction === "none")
220
314
  return "none";
@@ -390,12 +484,12 @@ function PasswordInput({ name, label, className = "cc-field cc-password-field",
390
484
  const Field = Input;
391
485
  function ColorPicker({ name, label, className = "cc-field cc-color-picker", disabled, panelClassName = "cc-color-picker__panel", readOnly, initialValue, rules, showInputSteppers = false, validateOn, onChange, ...props }) {
392
486
  const field = useOptionalField(name, {
393
- initialValue: initialValue ?? "#7c3aed",
487
+ initialValue: initialValue ?? "#1d2129",
394
488
  rules,
395
489
  validateOn,
396
490
  });
397
491
  const interactivity = resolveFieldInteractivity(useOptionalFormInteractivity(), disabled, readOnly);
398
- const controller = React.useMemo(() => new ColorPickerController(String(field.value || "#7c3aed")), [field.value]);
492
+ const controller = React.useMemo(() => new ColorPickerController(String(field.value || "#1d2129")), [field.value]);
399
493
  const state = controller.getState();
400
494
  const rootRef = React.useRef(null);
401
495
  const panelRef = React.useRef(null);
@@ -1079,6 +1173,82 @@ function Textarea({ name, label, className = "cc-field", labelClassName = "cc-fi
1079
1173
  field.setValue(event.currentTarget.value);
1080
1174
  }, ...options }), canClear ? (jsx("button", { "aria-label": clearLabel, className: "cc-field__clear cc-field__clear--textarea", type: "button", onClick: () => field.setValue(""), children: clearIcon })) : null] }), jsx(FieldError, { className: errorClassName, errorId: errorId, errors: field.errors })] }));
1081
1175
  }
1176
+ function getTextareaMetrics(element) {
1177
+ const computed = window.getComputedStyle(element);
1178
+ const fontSize = Number.parseFloat(computed.fontSize) || 14;
1179
+ const lineHeight = Number.parseFloat(computed.lineHeight) || fontSize * 1.5;
1180
+ const paddingY = (Number.parseFloat(computed.paddingTop) || 0) +
1181
+ (Number.parseFloat(computed.paddingBottom) || 0);
1182
+ const borderY = (Number.parseFloat(computed.borderTopWidth) || 0) +
1183
+ (Number.parseFloat(computed.borderBottomWidth) || 0);
1184
+ return { borderY, lineHeight, paddingY };
1185
+ }
1186
+ const ChatTextarea = /*#__PURE__*/ React.forwardRef(function ChatTextarea({ className = "cc-chat-textarea", clearable = true, clearIcon = jsx(ClassIcon, { name: "x" }), clearLabel = getClassComponentsLocale().inputClearLabel, defaultValue = "", disabled, maxRows = 3, minRows = 1, onValueChange, placeholder, readOnly, style, textareaClassName = "cc-chat-textarea__control", textareaStyle, value, ...props }, ref) {
1187
+ const textareaRef = React.useRef(null);
1188
+ const controllerRef = React.useRef(new ChatTextareaController({
1189
+ maxRows,
1190
+ minRows,
1191
+ value: value ?? defaultValue,
1192
+ }));
1193
+ const controlled = value !== undefined;
1194
+ const [state, setState] = React.useState(() => controllerRef.current.getState());
1195
+ const resize = React.useCallback(() => {
1196
+ const element = textareaRef.current;
1197
+ if (!element || typeof window === "undefined")
1198
+ return;
1199
+ const { borderY, lineHeight, paddingY } = getTextareaMetrics(element);
1200
+ const minHeight = lineHeight * state.minRows + paddingY + borderY;
1201
+ const maxHeight = lineHeight * state.maxRows + paddingY + borderY;
1202
+ element.style.height = "auto";
1203
+ const nextHeight = Math.min(Math.max(element.scrollHeight, minHeight), maxHeight);
1204
+ element.style.height = `${nextHeight}px`;
1205
+ element.style.overflowY =
1206
+ element.scrollHeight > maxHeight + 1 ? "auto" : "hidden";
1207
+ }, [state.maxRows, state.minRows, state.value]);
1208
+ React.useImperativeHandle(ref, () => ({
1209
+ blur: () => textareaRef.current?.blur(),
1210
+ focus: () => textareaRef.current?.focus(),
1211
+ getElement: () => textareaRef.current,
1212
+ }), []);
1213
+ React.useEffect(() => {
1214
+ const next = controllerRef.current.update({
1215
+ maxRows,
1216
+ minRows,
1217
+ value: controlled ? value : state.value,
1218
+ });
1219
+ setState(next);
1220
+ }, [controlled, maxRows, minRows, value]);
1221
+ React.useLayoutEffect(() => {
1222
+ resize();
1223
+ }, [resize]);
1224
+ React.useEffect(() => {
1225
+ if (typeof ResizeObserver === "undefined" || !textareaRef.current)
1226
+ return undefined;
1227
+ const observer = new ResizeObserver(resize);
1228
+ observer.observe(textareaRef.current);
1229
+ return () => observer.disconnect();
1230
+ }, [resize]);
1231
+ const commit = (nextValue, event) => {
1232
+ const nextState = controllerRef.current.setValue(nextValue);
1233
+ if (!controlled)
1234
+ setState(nextState);
1235
+ onValueChange?.(nextValue, event);
1236
+ };
1237
+ const currentValue = controlled ? (value ?? "") : state.value;
1238
+ const canClear = clearable && currentValue !== "" && !disabled && !readOnly;
1239
+ return (jsxs("div", { className: className, "data-disabled": Boolean(disabled), "data-readonly": Boolean(readOnly), "data-scrollable": currentValue.length > 0, style: style, children: [jsx("textarea", { ...props, className: `${textareaClassName}${canClear ? " cc-chat-textarea__control--clearable" : ""}`, disabled: disabled, placeholder: placeholder, readOnly: readOnly, ref: textareaRef, rows: state.minRows, style: {
1240
+ "--cc-chat-textarea-max-rows": state.maxRows,
1241
+ "--cc-chat-textarea-min-rows": state.minRows,
1242
+ ...textareaStyle,
1243
+ }, value: currentValue, onChange: (event) => {
1244
+ if (readOnly)
1245
+ return;
1246
+ commit(event.currentTarget.value, event);
1247
+ } }), canClear ? (jsx("button", { "aria-label": clearLabel, className: "cc-chat-textarea__clear", type: "button", onClick: (event) => {
1248
+ commit("", event);
1249
+ textareaRef.current?.focus();
1250
+ }, children: clearIcon })) : null] }));
1251
+ });
1082
1252
  function getSelectMenuPosition(trigger, preferredHeight, anchorTopPlacement = false) {
1083
1253
  if (!trigger || typeof window === "undefined") {
1084
1254
  return { placement: "bottom", style: {} };
@@ -1495,6 +1665,53 @@ function Breadcrumb({ className = "cc-breadcrumb", items, separator = "/", ...pr
1495
1665
  const state = useMemo(() => new BreadcrumbController(items).getState(), [items]);
1496
1666
  return (jsx("nav", { className: className, "aria-label": "Breadcrumb", ...props, children: state.items.map((item, index) => (jsxs("span", { className: "cc-breadcrumb__item", children: [index > 0 ? (jsx("span", { className: "cc-breadcrumb__separator", children: separator })) : null, item.href && !item.disabled ? (jsx("a", { href: item.href, children: item.label })) : (jsx("span", { "data-disabled": item.disabled, children: item.label }))] }, String(item.key)))) }));
1497
1667
  }
1668
+ function renderMenuIcon(item, collapsed, iconRender) {
1669
+ const rendered = iconRender?.(item);
1670
+ if (rendered !== undefined)
1671
+ return rendered;
1672
+ if (item.icon)
1673
+ return item.icon;
1674
+ return collapsed ? item.label.slice(0, 1) : null;
1675
+ }
1676
+ function renderMenuItems(items, collapsed, iconRender, onToggle, onSelect) {
1677
+ return items.map((item) => {
1678
+ const hasChildren = item.children.length > 0;
1679
+ const icon = renderMenuIcon(item, collapsed, iconRender);
1680
+ return (jsxs("li", { className: "cc-menu__item", "data-active": item.isActive, "data-disabled": item.disabled, "data-expanded": item.expanded, "data-level": item.level, children: [jsxs("button", { "aria-current": item.isActive ? "page" : undefined, "aria-expanded": hasChildren ? item.expanded : undefined, className: "cc-menu__button", disabled: item.disabled, title: collapsed ? item.label : undefined, type: "button", onClick: () => {
1681
+ if (hasChildren && !collapsed)
1682
+ onToggle(item.key);
1683
+ onSelect(item.key, item);
1684
+ }, children: [icon ? (jsx("span", { className: "cc-menu__icon", "aria-hidden": "true", children: icon })) : null, jsx("span", { className: "cc-menu__label", children: item.label }), hasChildren ? (jsx(ClassIcon, { className: "cc-menu__arrow", name: "chevronDown" })) : null] }), hasChildren ? (jsx(HeightTransition, { className: "cc-height-transition cc-menu__transition", contentClassName: "cc-height-transition__content cc-menu__transition-content", duration: 180, expanded: !collapsed && item.expanded, mode: "grid", children: jsx("ul", { className: "cc-menu__list cc-menu__list--child", children: renderMenuItems(item.children, collapsed, iconRender, onToggle, onSelect) }) })) : null] }, item.key));
1685
+ });
1686
+ }
1687
+ function Menu({ activeKey, className = "cc-menu", collapsed, collapsible = true, defaultExpandedKeys = [], iconRender, items, onCollapsedChange, onSelect, ...props }) {
1688
+ const controller = React.useMemo(() => new MenuController({
1689
+ activeKey,
1690
+ collapsed,
1691
+ defaultExpandedKeys,
1692
+ items,
1693
+ }), []);
1694
+ const [state, setState] = React.useState(() => controller.getState());
1695
+ React.useEffect(() => {
1696
+ setState(controller.update({
1697
+ activeKey,
1698
+ collapsed,
1699
+ defaultExpandedKeys,
1700
+ items,
1701
+ }));
1702
+ }, [activeKey, collapsed, controller, defaultExpandedKeys, items]);
1703
+ const commitSelect = React.useCallback((key, item) => {
1704
+ setState(controller.activate(key));
1705
+ onSelect?.(key, item);
1706
+ }, [controller, onSelect]);
1707
+ const toggleExpanded = React.useCallback((key) => setState(controller.toggleExpanded(key)), [controller]);
1708
+ const toggleCollapsed = React.useCallback(() => {
1709
+ const next = controller.toggleCollapsed();
1710
+ setState(next);
1711
+ onCollapsedChange?.(next.collapsed);
1712
+ }, [controller, onCollapsedChange]);
1713
+ return (jsxs("nav", { className: className, "data-collapsed": state.collapsed, "data-collapsible": collapsible, ...props, children: [collapsible ? (jsx("button", { "aria-label": state.collapsed ? "展开菜单" : "收起菜单", className: "cc-menu__collapse", type: "button", onClick: toggleCollapsed, children: jsx(ClassIcon, { name: state.collapsed ? "chevronRight" : "chevronLeft" }) })) : null, jsx("ul", { className: "cc-menu__list", children: renderMenuItems(state.items, state.collapsed, iconRender, toggleExpanded, commitSelect) })] }));
1714
+ }
1498
1715
  function Pagination({ boundaryCount = 1, className = "cc-pagination", current = 1, pageSize = 10, siblingCount = 1, total = 0, previousIcon = jsx(ClassIcon, { name: "chevronLeft" }), nextIcon = jsx(ClassIcon, { name: "chevronRight" }), onChange, ...props }) {
1499
1716
  const state = useMemo(() => new PaginationController({
1500
1717
  boundaryCount,
@@ -1505,14 +1722,29 @@ function Pagination({ boundaryCount = 1, className = "cc-pagination", current =
1505
1722
  }).getState(), [boundaryCount, current, pageSize, siblingCount, total]);
1506
1723
  return (jsxs("div", { className: className, ...props, children: [jsx("button", { disabled: state.current <= 1, type: "button", onClick: () => onChange?.(state.current - 1), children: previousIcon }), state.pages.map((page, index) => page === "ellipsis" ? (jsx("span", { "aria-hidden": "true", className: "cc-pagination__ellipsis", children: "\u2022\u2022\u2022" }, `ellipsis-${index}`)) : (jsx("button", { "data-active": page === state.current, type: "button", onClick: () => onChange?.(page), children: page }, page))), jsx("button", { disabled: state.current >= state.pageCount, type: "button", onClick: () => onChange?.(state.current + 1), children: nextIcon })] }));
1507
1724
  }
1508
- function Popconfirm({ children, animation, autoAdjust = true, placement = "top", content = getClassComponentsLocale().popconfirmContent, confirmText = getClassComponentsLocale().popconfirmConfirmText, cancelText = getClassComponentsLocale().popconfirmCancelText, closeOnEsc = true, closeOnOutsideClick = true, onConfirm, }) {
1725
+ function Popconfirm({ children, animation, autoAdjust = true, placement = "top", content = getClassComponentsLocale().popconfirmContent, confirmText = getClassComponentsLocale().popconfirmConfirmText, cancelText = getClassComponentsLocale().popconfirmCancelText, closeOnEsc = true, closeOnOutsideClick = true, onConfirm, portal = true, }) {
1509
1726
  const rootRef = React.useRef(null);
1510
1727
  const panelRef = React.useRef(null);
1511
1728
  const [open, setOpen] = React.useState(false);
1512
1729
  const [safePlacement, setSafePlacement] = React.useState(placement);
1730
+ const [anchorStyle, setAnchorStyle] = React.useState({
1731
+ height: 0,
1732
+ left: -9999,
1733
+ top: -9999,
1734
+ width: 0,
1735
+ });
1513
1736
  const motion = resolveAnimation(animation, "scale");
1514
1737
  const [mounted, presence] = usePresence(open, motion.enabled ? motion.duration : 0);
1515
1738
  const updatePlacement = React.useCallback(() => {
1739
+ if (rootRef.current) {
1740
+ const rect = rootRef.current.getBoundingClientRect();
1741
+ setAnchorStyle({
1742
+ height: rect.height,
1743
+ left: rect.left,
1744
+ top: rect.top,
1745
+ width: rect.width,
1746
+ });
1747
+ }
1516
1748
  if (!autoAdjust || !rootRef.current || !panelRef.current) {
1517
1749
  setSafePlacement(placement);
1518
1750
  return;
@@ -1525,7 +1757,8 @@ function Popconfirm({ children, animation, autoAdjust = true, placement = "top",
1525
1757
  requestAnimationFrame(updatePlacement);
1526
1758
  const close = (event) => {
1527
1759
  if (!closeOnOutsideClick ||
1528
- rootRef.current?.contains(event.target))
1760
+ rootRef.current?.contains(event.target) ||
1761
+ panelRef.current?.contains(event.target))
1529
1762
  return;
1530
1763
  setOpen(false);
1531
1764
  };
@@ -1550,10 +1783,27 @@ function Popconfirm({ children, animation, autoAdjust = true, placement = "top",
1550
1783
  if (open)
1551
1784
  updatePlacement();
1552
1785
  }, [open, placement, updatePlacement]);
1553
- return (jsxs("span", { className: ["cc-popconfirm", `cc-popconfirm--${safePlacement}`].join(" "), "data-animation": motion.name, ref: rootRef, style: animationStyle(motion), children: [jsx("span", { onClick: () => setOpen(true), children: children }), mounted ? (jsxs("span", { className: "cc-popconfirm__panel", "data-state": presence, ref: panelRef, role: "dialog", children: [jsx("span", { children: content }), jsxs("span", { className: "cc-popconfirm__actions", children: [jsx("button", { type: "button", onClick: () => setOpen(false), children: cancelText }), jsx("button", { type: "button", onClick: () => {
1554
- onConfirm?.();
1555
- setOpen(false);
1556
- }, children: confirmText })] })] })) : null] }));
1786
+ const rootClassName = [
1787
+ "cc-popconfirm",
1788
+ `cc-popconfirm--${safePlacement}`,
1789
+ ].join(" ");
1790
+ const panelNode = mounted ? (jsxs("span", { className: "cc-popconfirm__panel", "data-state": presence, ref: panelRef, role: "dialog", children: [jsx("span", { children: content }), jsxs("span", { className: "cc-popconfirm__actions", children: [jsx("button", { type: "button", onClick: () => setOpen(false), children: cancelText }), jsx("button", { type: "button", onClick: () => {
1791
+ onConfirm?.();
1792
+ setOpen(false);
1793
+ }, children: confirmText })] })] })) : null;
1794
+ const rootNode = (jsxs("span", { className: rootClassName, "data-animation": motion.name, ref: rootRef, style: animationStyle(motion), children: [jsx("span", { onClick: () => setOpen(true), children: children }), portal ? null : panelNode] }));
1795
+ if (!portal)
1796
+ return rootNode;
1797
+ const portalNode = mounted ? (jsx("span", { className: `${rootClassName} cc-popconfirm--portal`, "data-animation": motion.name, style: {
1798
+ ...animationStyle(motion),
1799
+ height: `${anchorStyle.height}px`,
1800
+ left: `${anchorStyle.left}px`,
1801
+ top: `${anchorStyle.top}px`,
1802
+ width: `${anchorStyle.width}px`,
1803
+ }, children: panelNode })) : null;
1804
+ return (jsxs(Fragment, { children: [rootNode, portalNode && typeof document !== "undefined"
1805
+ ? createPortal(portalNode, document.body)
1806
+ : null] }));
1557
1807
  }
1558
1808
  function getBacktopTarget(target) {
1559
1809
  if (typeof target === "string")
@@ -1785,13 +2035,14 @@ function Popup({ open, controller, placement = "bottom", title, children, animat
1785
2035
  }, []);
1786
2036
  if (!mounted)
1787
2037
  return null;
1788
- const popupContainer = containerRef?.current ?? null;
2038
+ const popupContainer = containerRef?.current ??
2039
+ (typeof document !== "undefined" ? document.body : null);
1789
2040
  const popupElement = (jsxs("div", { className: className, "data-animation": motion.name, "data-contained": popupContainer ? "true" : "false", "data-placement": popupState.placement, "data-state": animationState, role: "presentation", style: animationStyle(motion, style), ...props, children: [jsx("button", { "aria-label": "Close popup", className: overlayClassName, type: "button", onClick: closeOnOverlayClick ? close : undefined }), jsxs("section", { "aria-modal": "true", className: panelClassName, ref: panelRef, role: "dialog", tabIndex: -1, children: [title ? (jsxs("header", { className: "cc-popup__header", children: [jsx("span", { children: title }), jsx("button", { "aria-label": "Close popup", className: "cc-popup__close", type: "button", onClick: close, children: closeIcon })] })) : null, jsx("div", { className: "cc-popup__body", children: children })] })] }));
1790
2041
  return popupContainer
1791
2042
  ? createPortal(popupElement, popupContainer)
1792
2043
  : popupElement;
1793
2044
  }
1794
- function Tooltip({ content, children, animation, placement = "top", trigger = "hover", autoAdjust = true, closeOnEsc = true, closeOnOutsideClick = true, className, id, style, ...props }) {
2045
+ function Tooltip({ content, children, animation, placement = "top", trigger = "hover", autoAdjust = true, closeOnEsc = true, closeOnOutsideClick = true, portal = true, className, id, style, ...props }) {
1795
2046
  const rootRef = React.useRef(null);
1796
2047
  const contentRef = React.useRef(null);
1797
2048
  const generatedId = React.useId();
@@ -1799,6 +2050,7 @@ function Tooltip({ content, children, animation, placement = "top", trigger = "h
1799
2050
  const tooltipController = React.useMemo(() => new TooltipController(), []);
1800
2051
  const [open, setOpen] = React.useState(() => tooltipController.isOpen());
1801
2052
  const [safePlacement, setSafePlacement] = React.useState(placement);
2053
+ const [anchorStyle, setAnchorStyle] = React.useState(null);
1802
2054
  const motion = resolveAnimation(animation, "fade");
1803
2055
  useEffect(() => {
1804
2056
  const unsubscribe = tooltipController.subscribe((state) => {
@@ -1810,6 +2062,15 @@ function Tooltip({ content, children, animation, placement = "top", trigger = "h
1810
2062
  };
1811
2063
  }, [tooltipController]);
1812
2064
  const updatePlacement = React.useCallback(() => {
2065
+ if (rootRef.current) {
2066
+ const rect = rootRef.current.getBoundingClientRect();
2067
+ setAnchorStyle({
2068
+ height: rect.height,
2069
+ left: rect.left,
2070
+ top: rect.top,
2071
+ width: rect.width,
2072
+ });
2073
+ }
1813
2074
  if (!autoAdjust || !rootRef.current || !contentRef.current) {
1814
2075
  setSafePlacement(placement);
1815
2076
  return;
@@ -1832,7 +2093,8 @@ function Tooltip({ content, children, animation, placement = "top", trigger = "h
1832
2093
  return undefined;
1833
2094
  const handlePointerDown = (event) => {
1834
2095
  if (!closeOnOutsideClick ||
1835
- rootRef.current?.contains(event.target))
2096
+ rootRef.current?.contains(event.target) ||
2097
+ contentRef.current?.contains(event.target))
1836
2098
  return;
1837
2099
  tooltipController.close();
1838
2100
  };
@@ -1859,19 +2121,45 @@ function Tooltip({ content, children, animation, placement = "top", trigger = "h
1859
2121
  onMouseEnter: () => tooltipController.open(),
1860
2122
  onMouseLeave: () => tooltipController.close(),
1861
2123
  };
1862
- return (jsxs("span", { className: ["cc-tooltip", `cc-tooltip--${safePlacement}`, className]
1863
- .filter(Boolean)
1864
- .join(" "), "aria-describedby": open ? contentId : undefined, "data-animation": motion.name, "data-open": open, ref: rootRef, style: animationStyle(motion, style), tabIndex: trigger === "click" ? 0 : undefined, ...props, ...triggerProps, children: [children, jsx("span", { className: "cc-tooltip__content", id: contentId, ref: contentRef, role: "tooltip", children: content })] }));
2124
+ const rootClassName = [
2125
+ "cc-tooltip",
2126
+ `cc-tooltip--${safePlacement}`,
2127
+ className,
2128
+ ]
2129
+ .filter(Boolean)
2130
+ .join(" ");
2131
+ const tooltipContent = (jsx("span", { className: "cc-tooltip__content", id: contentId, ref: contentRef, role: "tooltip", children: content }));
2132
+ const rootNode = (jsxs("span", { className: rootClassName, "aria-describedby": open ? contentId : undefined, "data-animation": motion.name, "data-open": open, ref: rootRef, style: animationStyle(motion, style), tabIndex: trigger === "click" ? 0 : undefined, ...props, ...triggerProps, children: [children, portal ? null : tooltipContent] }));
2133
+ if (!portal || typeof document === "undefined")
2134
+ return rootNode;
2135
+ const portalNode = open || anchorStyle ? (jsx("span", { className: `${rootClassName} cc-tooltip--portal`, "data-animation": motion.name, "data-open": open, style: {
2136
+ ...animationStyle(motion),
2137
+ height: anchorStyle?.height ?? 0,
2138
+ left: anchorStyle?.left ?? -9999,
2139
+ top: anchorStyle?.top ?? -9999,
2140
+ width: anchorStyle?.width ?? 0,
2141
+ }, children: tooltipContent })) : null;
2142
+ return (jsxs(Fragment, { children: [rootNode, portalNode ? renderBodyPortal(portalNode) : null] }));
1865
2143
  }
1866
- function ToastViewport({ animation, manager = defaultToastManager, className = "cc-toast-viewport", position = "top-right", renderToast, style, ...props }) {
1867
- const [items, setItems] = React.useState(() => manager.getToasts());
2144
+ function ToastViewport({ animation, manager = defaultToastManager, className = "cc-toast-viewport", maxVisible, portal = false, position = "top-right", renderToast, style, ...props }) {
2145
+ const items = useToastItems(manager);
1868
2146
  const motion = resolveAnimation(animation, "slide");
1869
- const visibleItems = React.useMemo(() => items.filter((item) => item.position === position), [items, position]);
2147
+ const resolvedMaxVisible = normalizeToastMaxVisible(maxVisible);
2148
+ const visibleItems = React.useMemo(() => {
2149
+ const positionItems = items.filter((item) => item.position === position);
2150
+ const visible = positionItems.slice(Math.max(0, positionItems.length - resolvedMaxVisible));
2151
+ return isToastTopPosition(position) ? [...visible].reverse() : visible;
2152
+ }, [items, position, resolvedMaxVisible]);
1870
2153
  const renderedItems = useAnimatedToasts(visibleItems, motion.enabled ? motion.duration : 0);
1871
- useEffect(() => manager.subscribe(setItems), [manager]);
1872
- return (jsx("div", { className: [className, `cc-toast-viewport--${position}`]
2154
+ const setToastNode = useToastFlip(renderedItems, motion.enabled ? motion.duration : 0, motion.easing);
2155
+ const viewportNode = (jsx("div", { className: [className, `cc-toast-viewport--${position}`]
1873
2156
  .filter(Boolean)
1874
- .join(" "), role: "status", "aria-live": "polite", style: animationStyle(motion, style), ...props, children: renderedItems.map(({ item, state }) => (jsx(React.Fragment, { children: renderToast ? (renderToast(item, (id) => manager.dismiss(id), state)) : (jsxs("article", { className: `cc-toast cc-toast--${item.tone}`, "data-animation": motion.name, "data-state": state, children: [item.title ? (jsx("strong", { className: "cc-toast__title", children: item.title })) : null, jsx("span", { className: "cc-toast__message", children: item.message }), jsx("button", { "aria-label": "Dismiss toast", className: "cc-toast__close", type: "button", onClick: () => manager.dismiss(item.id), children: "x" })] })) }, item.id))) }));
2157
+ .join(" "), role: "status", "aria-live": "polite", style: animationStyle(motion, style), ...props, children: renderedItems.map(({ item, motion: isMotionActive, state }, index) => (jsx(React.Fragment, { children: renderToast ? (renderToast(item, (id) => manager.dismiss(id), state)) : (jsxs("article", { className: `cc-toast cc-toast--${item.tone}`, "data-animation": motion.name, "data-motion": isMotionActive ? "true" : "false", "data-state": state, ref: (node) => setToastNode(item.id, node), style: {
2158
+ "--cc-toast-stack-index": index,
2159
+ }, children: [item.title ? (jsx("strong", { className: "cc-toast__title", children: item.title })) : null, jsx("span", { className: "cc-toast__message", children: item.message }), jsx("button", { "aria-label": "Dismiss toast", className: "cc-toast__close", type: "button", onClick: () => manager.dismiss(item.id), children: jsx(ClassIcon, { name: "x" }) })] })) }, item.id))) }));
2160
+ return portal
2161
+ ? renderBodyPortal(viewportNode)
2162
+ : viewportNode;
1875
2163
  }
1876
2164
  function resolveTabsFlag(source, activeId) {
1877
2165
  if (!source)
@@ -2265,6 +2553,212 @@ function TiltCard({ children, className = "cc-tilt-card", contentClassName = "cc
2265
2553
  ...style,
2266
2554
  }, onPointerEnter: handlePointerEnter, onPointerLeave: handlePointerLeave, onPointerMove: handlePointerMove, ...props, children: jsxs("div", { className: surfaceClassName, children: [jsx("div", { className: contentClassName, children: children }), state.glare ? (jsx("span", { "aria-hidden": "true", className: glareClassName })) : null] }) }));
2267
2555
  }
2556
+ function OrbitalSphere({ animated = true, className = "cc-orbital-sphere", cycleDuration = 12000, depth = 0.72, disabled = false, duration = 9000, itemClassName = "cc-orbital-sphere__item", itemContentClassName = "cc-orbital-sphere__item-content", itemSize = 72, items, onItemClick, radius = "50%", renderItem, shellClassName = "cc-orbital-sphere__shell", size = "360px", spread = 0.86, stageClassName = "cc-orbital-sphere__stage", style, visibleCount = 10, ...props }) {
2557
+ const state = React.useMemo(() => new OrbitalSphereController({
2558
+ animated,
2559
+ cycleDuration,
2560
+ depth,
2561
+ disabled,
2562
+ duration,
2563
+ itemSize,
2564
+ items,
2565
+ radius,
2566
+ size,
2567
+ spread,
2568
+ visibleCount,
2569
+ }).getState(), [
2570
+ animated,
2571
+ cycleDuration,
2572
+ depth,
2573
+ disabled,
2574
+ duration,
2575
+ itemSize,
2576
+ items,
2577
+ radius,
2578
+ size,
2579
+ spread,
2580
+ visibleCount,
2581
+ ]);
2582
+ return (jsxs("div", { className: className, "data-animated": state.animated ? "true" : "false", "data-disabled": state.disabled ? "true" : "false", "data-overflow": state.items.length > state.visibleCount ? "true" : "false", style: { ...state.styleVars, ...style }, ...props, children: [jsx("span", { "aria-hidden": "true", className: shellClassName }), jsx("div", { className: stageClassName, children: state.items.map((item) => (jsx("button", { "aria-label": item.label, className: itemClassName, "data-tone": item.tone, disabled: state.disabled, style: {
2583
+ "--cc-orbital-index": item.index,
2584
+ ...item.styleVars,
2585
+ }, type: "button", onClick: (event) => onItemClick?.(item, event), children: jsx("span", { className: itemContentClassName, children: renderItem?.(item) ?? item.content }) }, item.id))) })] }));
2586
+ }
2587
+ function HeightTransition({ children, className = "cc-height-transition", contentClassName = "cc-height-transition__content", duration = 220, easing, expanded = false, mode = "auto", style, ...props }) {
2588
+ const contentRef = React.useRef(null);
2589
+ const controller = React.useMemo(() => new HeightTransitionController({ duration, easing, expanded, mode }), []);
2590
+ const [state, setState] = React.useState(() => controller.getState());
2591
+ const [height, setHeight] = React.useState(0);
2592
+ React.useEffect(() => {
2593
+ setState(controller.update({ duration, easing, expanded, mode }));
2594
+ }, [controller, duration, easing, expanded, mode]);
2595
+ React.useLayoutEffect(() => {
2596
+ const element = contentRef.current;
2597
+ if (!element || typeof ResizeObserver === "undefined") {
2598
+ setHeight(element?.scrollHeight ?? 0);
2599
+ return undefined;
2600
+ }
2601
+ const observer = new ResizeObserver(() => setHeight(element.scrollHeight));
2602
+ observer.observe(element);
2603
+ setHeight(element.scrollHeight);
2604
+ return () => observer.disconnect();
2605
+ }, [children]);
2606
+ return (jsx("div", { className: className, "data-expanded": state.expanded, "data-mode": state.mode, style: {
2607
+ "--cc-height-transition-duration": `${state.duration}ms`,
2608
+ "--cc-height-transition-easing": state.easing,
2609
+ "--cc-height-transition-height": `${height}px`,
2610
+ ...style,
2611
+ }, ...props, children: jsx("div", { className: contentClassName, ref: contentRef, children: children }) }));
2612
+ }
2613
+ function CodePreview({ className = "cc-code-preview", code, defaultExpanded = false, language = "Vue / TSX", onCopy, placement = "auto", strategy = "fixed", title = "示例源码", transitionDuration = 220, transitionEasing = "cubic-bezier(0.22, 0.8, 0.28, 1)", ...props }) {
2614
+ const rootRef = React.useRef(null);
2615
+ const panelRef = React.useRef(null);
2616
+ const controller = React.useMemo(() => new CodePreviewController({
2617
+ code,
2618
+ defaultExpanded,
2619
+ language,
2620
+ placement,
2621
+ title: String(title),
2622
+ }), []);
2623
+ const [state, setState] = React.useState(() => controller.getState());
2624
+ const [panelStyle, setPanelStyle] = React.useState();
2625
+ React.useEffect(() => {
2626
+ setState(controller.update({ code, language, placement, title: String(title) }));
2627
+ }, [code, controller, language, placement, title]);
2628
+ const updatePanelPosition = React.useCallback((estimate = false) => {
2629
+ const root = rootRef.current;
2630
+ const panel = panelRef.current;
2631
+ if (!root || (!panel && !estimate) || typeof window === "undefined")
2632
+ return undefined;
2633
+ const rect = root.getBoundingClientRect();
2634
+ const padding = 10;
2635
+ const gap = 10;
2636
+ const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
2637
+ const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
2638
+ const maxPanelHeight = Math.max(160, viewportHeight - padding * 2);
2639
+ const maxPanelWidth = Math.max(160, viewportWidth - padding * 2);
2640
+ const minPanelHeight = Math.min(160, maxPanelHeight);
2641
+ const panelContent = panel?.querySelector("pre");
2642
+ const measuredPanelHeight = Math.max(panel?.offsetHeight ?? 0, panel?.scrollHeight ?? 0, panelContent?.scrollHeight ?? 0, estimate ? 420 : 0);
2643
+ const preferredPanelHeight = Math.min(Math.max(160, measuredPanelHeight || 420), maxPanelHeight);
2644
+ const panelWidth = Math.min(Math.max(rect.width, panel?.offsetWidth || 720), maxPanelWidth);
2645
+ const availableBelow = Math.max(minPanelHeight, viewportHeight - rect.bottom - gap - padding);
2646
+ const availableAbove = Math.max(minPanelHeight, rect.top - gap - padding);
2647
+ const placementHeight = Math.min(preferredPanelHeight, Math.max(availableBelow, availableAbove));
2648
+ const next = controller.resolvePlacement({
2649
+ anchorBottom: rect.bottom,
2650
+ anchorLeft: rect.left,
2651
+ anchorRight: rect.right,
2652
+ anchorTop: rect.top,
2653
+ panelHeight: placementHeight,
2654
+ panelWidth,
2655
+ viewportHeight,
2656
+ viewportWidth,
2657
+ });
2658
+ setState(next);
2659
+ if (strategy !== "fixed") {
2660
+ setPanelStyle(undefined);
2661
+ return;
2662
+ }
2663
+ const clamp = (value, min, max) => Math.min(Math.max(min, value), max);
2664
+ const width = panelWidth;
2665
+ const maxLeft = Math.max(padding, viewportWidth - width - padding);
2666
+ const viewportPanelHeight = Math.min(preferredPanelHeight, maxPanelHeight);
2667
+ const maxTop = Math.max(padding, viewportHeight - viewportPanelHeight - padding);
2668
+ let left = clamp(rect.left, padding, maxLeft);
2669
+ let top = rect.bottom + gap;
2670
+ let maxHeight = Math.min(preferredPanelHeight, Math.max(minPanelHeight, viewportHeight - top - padding));
2671
+ let transformOrigin = "top left";
2672
+ if (next.resolvedPlacement === "top") {
2673
+ maxHeight = Math.min(preferredPanelHeight, availableAbove);
2674
+ top = Math.max(padding, rect.top - gap - maxHeight);
2675
+ transformOrigin = "bottom left";
2676
+ }
2677
+ else if (next.resolvedPlacement === "right") {
2678
+ left = clamp(rect.right + gap, padding, maxLeft);
2679
+ top = clamp(rect.top, padding, maxTop);
2680
+ maxHeight = viewportPanelHeight;
2681
+ transformOrigin = "top left";
2682
+ }
2683
+ else if (next.resolvedPlacement === "left") {
2684
+ left = clamp(rect.left - width - gap, padding, maxLeft);
2685
+ top = clamp(rect.top, padding, maxTop);
2686
+ maxHeight = viewportPanelHeight;
2687
+ transformOrigin = "top right";
2688
+ }
2689
+ setPanelStyle({
2690
+ left,
2691
+ maxHeight,
2692
+ top,
2693
+ transformOrigin,
2694
+ width,
2695
+ });
2696
+ return next;
2697
+ }, [controller, strategy]);
2698
+ const updatePanelPositionFromViewport = React.useCallback(() => {
2699
+ updatePanelPosition();
2700
+ }, [updatePanelPosition]);
2701
+ React.useLayoutEffect(() => {
2702
+ if (!state.expanded) {
2703
+ setPanelStyle(undefined);
2704
+ return undefined;
2705
+ }
2706
+ updatePanelPosition();
2707
+ window.addEventListener("resize", updatePanelPositionFromViewport);
2708
+ window.addEventListener("scroll", updatePanelPositionFromViewport, true);
2709
+ return () => {
2710
+ window.removeEventListener("resize", updatePanelPositionFromViewport);
2711
+ window.removeEventListener("scroll", updatePanelPositionFromViewport, true);
2712
+ };
2713
+ }, [
2714
+ code,
2715
+ state.expanded,
2716
+ updatePanelPosition,
2717
+ updatePanelPositionFromViewport,
2718
+ ]);
2719
+ React.useEffect(() => {
2720
+ if (!state.expanded || typeof document === "undefined")
2721
+ return undefined;
2722
+ const close = () => {
2723
+ setPanelStyle(undefined);
2724
+ setState(controller.close());
2725
+ };
2726
+ const closeOnOutside = (event) => {
2727
+ const target = event.target;
2728
+ if (target &&
2729
+ (rootRef.current?.contains(target) ||
2730
+ panelRef.current?.contains(target)))
2731
+ return;
2732
+ close();
2733
+ };
2734
+ const closeOnEscape = (event) => {
2735
+ if (event.key === "Escape")
2736
+ close();
2737
+ };
2738
+ document.addEventListener("pointerdown", closeOnOutside, true);
2739
+ document.addEventListener("keydown", closeOnEscape, true);
2740
+ return () => {
2741
+ document.removeEventListener("pointerdown", closeOnOutside, true);
2742
+ document.removeEventListener("keydown", closeOnEscape, true);
2743
+ };
2744
+ }, [controller, state.expanded]);
2745
+ const copy = async () => {
2746
+ await (onCopy ? onCopy(code) : navigator.clipboard?.writeText(code));
2747
+ setState(controller.copied());
2748
+ window.setTimeout(() => setState(controller.resetCopied()), 1200);
2749
+ };
2750
+ const panel = (jsx("div", { className: "cc-code-preview__panel", "data-placement": state.resolvedPlacement, "data-strategy": strategy, "data-expanded": state.expanded, ref: panelRef, style: panelStyle, children: jsx(HeightTransition, { className: "cc-height-transition cc-code-preview__transition", contentClassName: "cc-height-transition__content cc-code-preview__panel-content", duration: transitionDuration, easing: transitionEasing, expanded: state.expanded, mode: "auto", children: jsx("pre", { children: jsx("code", { children: state.code }) }) }) }));
2751
+ return (jsxs("div", { className: className, "data-expanded": state.expanded, "data-placement": state.resolvedPlacement, "data-strategy": strategy, ref: rootRef, ...props, children: [jsxs("div", { className: "cc-code-preview__bar", children: [jsxs("div", { className: "cc-code-preview__meta", children: [jsx("span", { className: "cc-code-preview__title", children: title }), jsx("span", { className: "cc-code-preview__lang", children: state.language })] }), jsxs("div", { className: "cc-code-preview__actions", children: [jsx("button", { type: "button", onClick: () => {
2752
+ const next = controller.toggle();
2753
+ if (!next.expanded) {
2754
+ setPanelStyle(undefined);
2755
+ setState(next);
2756
+ return;
2757
+ }
2758
+ setState(updatePanelPosition(true) ?? next);
2759
+ window.requestAnimationFrame(() => updatePanelPosition());
2760
+ }, children: state.expanded ? "收起" : "展开" }), jsx("button", { type: "button", onClick: () => void copy(), children: state.copied ? "已复制" : "复制" })] })] }), strategy === "fixed" ? renderBodyPortal(panel) : panel] }));
2761
+ }
2268
2762
  function QRCode({ value, size = 160, margin = 2, darkColor = "#1f1a2e", lightColor = "#ffffff", className = "cc-code cc-qr-code", errorCorrectionLevel = "M", errorText = "QR code unavailable", logoSrc, logoAlt = "QR code logo", logoSize = Math.round(size * 0.22), logoPadding = 6, logoRadius = 8, logoBackground = "#ffffff", renderError, ...props }) {
2269
2763
  const [svg, setSvg] = React.useState("");
2270
2764
  const [error, setError] = React.useState(null);
@@ -2700,7 +3194,7 @@ function signaturePointFromEvent(canvas, event) {
2700
3194
  y: event.clientY - rect.top,
2701
3195
  };
2702
3196
  }
2703
- const Signature = /*#__PURE__*/ React.forwardRef(function Signature({ backgroundColor, canvasClassName = "cc-signature__canvas", className = "cc-signature", clearText = getClassComponentsLocale().signatureClearText, disabled = false, errorClassName, exportType = "image/png", fieldClassName = "cc-field cc-signature-field", height = 220, initialValue, label, labelClassName = "cc-field__label", lineWidth = 3, name, penColor = "#111827", readOnly = false, rules, showToolbar = true, style, toolbarClassName = "cc-signature__toolbar", undoText = getClassComponentsLocale().signatureUndoText, validateOn, width = "100%", onChange, onClear, onEnd, onUndo, ...props }, ref) {
3197
+ const Signature = /*#__PURE__*/ React.forwardRef(function Signature({ backgroundColor, canvasClassName = "cc-signature__canvas", className = "cc-signature", clearText = getClassComponentsLocale().signatureClearText, disabled = false, errorClassName, exportType = "image/png", fieldClassName = "cc-field cc-signature-field", height = 220, initialValue, label, labelClassName = "cc-field__label", lineWidth = 3, name, penColor = "#1d2129", readOnly = false, rules, showToolbar = true, style, toolbarClassName = "cc-signature__toolbar", undoText = getClassComponentsLocale().signatureUndoText, validateOn, width = "100%", onChange, onClear, onEnd, onUndo, ...props }, ref) {
2704
3198
  const formContext = React.useContext(FormContext);
2705
3199
  const formState = useSyncExternalStore((listener) => formContext
2706
3200
  ? formContext.controller.subscribe(listener)
@@ -3483,6 +3977,28 @@ function Draggable({ axis = "xy", boundaryRef, children, className = "cc-draggab
3483
3977
  ? children({ dragging: state.dragging, position: state.position })
3484
3978
  : children }));
3485
3979
  }
3980
+ function readDragDropPreviewStyleVars(element) {
3981
+ if (!element || typeof window === "undefined")
3982
+ return {};
3983
+ const styles = window.getComputedStyle(element);
3984
+ const read = (name) => styles.getPropertyValue(name).trim();
3985
+ const slotDraggingBackground = read("--cc-drag-drop-slot-dragging-background");
3986
+ const slotDraggingBorderColor = read("--cc-drag-drop-slot-dragging-border-color");
3987
+ const previewBackground = read("--cc-drag-drop-preview-background");
3988
+ const previewBorderColor = read("--cc-drag-drop-preview-border-color");
3989
+ const values = {
3990
+ "--cc-drag-drop-preview-background": previewBackground && !previewBackground.includes("var(")
3991
+ ? previewBackground
3992
+ : slotDraggingBackground,
3993
+ "--cc-drag-drop-preview-border-color": previewBorderColor && !previewBorderColor.includes("var(")
3994
+ ? previewBorderColor
3995
+ : slotDraggingBorderColor,
3996
+ "--cc-drag-drop-preview-color": read("--cc-drag-drop-preview-color"),
3997
+ "--cc-drag-drop-preview-shadow": read("--cc-drag-drop-preview-shadow"),
3998
+ "--cc-drag-drop-motion-duration": read("--cc-drag-drop-motion-duration"),
3999
+ };
4000
+ return Object.fromEntries(Object.entries(values).filter(([, value]) => value.length > 0));
4001
+ }
3486
4002
  function reorderDropItems(items, fromIndex, toIndex, mode) {
3487
4003
  if (fromIndex < 0 || toIndex < 0 || fromIndex === toIndex)
3488
4004
  return [...items];
@@ -3749,6 +4265,7 @@ function DragDropBoard({ columns, items, mode = "insert", renderItem, className
3749
4265
  offsetY: event.clientY - rect.top,
3750
4266
  originX: rect.left,
3751
4267
  originY: rect.top,
4268
+ styleVars: readDragDropPreviewStyleVars(boardRef.current),
3752
4269
  width: rect.width,
3753
4270
  x: rect.left,
3754
4271
  y: rect.top,
@@ -3796,6 +4313,7 @@ function DragDropBoard({ columns, items, mode = "insert", renderItem, className
3796
4313
  }, onPointerUp: endDrag, onPointerCancel: cancelDrag, children: jsx("div", { className: "cc-drag-drop-board__item", children: renderItem(item, { dragging, over }) }) }, item.id));
3797
4314
  }), preview
3798
4315
  ? renderBodyPortal(jsx("div", { "aria-hidden": "true", className: "cc-drag-drop-board__preview", "data-dropping": preview.dropping, "data-returning": preview.returning, ref: previewRef, style: {
4316
+ ...preview.styleVars,
3799
4317
  height: preview.height,
3800
4318
  transform: `translate3d(${preview.x}px, ${preview.y}px, 0)`,
3801
4319
  width: preview.width,
@@ -4650,7 +5168,9 @@ function MasonryVirtualList({ columns, data, estimateSize = 220, gap = 16, heade
4650
5168
  item,
4651
5169
  }) }, item.key))) })] }) }));
4652
5170
  }
4653
- function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, defaultStickToBottom = true, enablePullUp = false, estimateSize = 72, getKey, height = 420, itemClassName = "cc-chat-virtual-list__item", itemContent, listClassName = "cc-chat-virtual-list__inner", appendScrollBehavior = "smooth", pullUpClassName = "cc-chat-virtual-list__pull-up", pullUpContent, pullUpLoading = false, pullUpMinDuration = 420, pullUpThreshold = 48, stickToBottomOnAppend = true, stickToBottomOnUpdate = true, topLoadingClassName = "cc-chat-virtual-list__top-loading", topLoadingContent = "正在加载历史消息...", topBuffer = 10, topThreshold = 96, className = "cc-chat-virtual-list", viewportClassName = "cc-chat-virtual-list__viewport", onPullUp, onReachTop, onRangeChange, style, ...props }, ref) {
5171
+ const CHAT_PREPEND_MOMENTUM_LOCK_MS = 420;
5172
+ const CHAT_PREPEND_STABILIZE_FRAMES = 24;
5173
+ function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, defaultStickToBottom = true, enablePullUp = false, estimateSize = 72, getKey, height = 420, itemClassName = "cc-chat-virtual-list__item", itemContent, listClassName = "cc-chat-virtual-list__inner", appendScrollBehavior = "smooth", pullUpClassName = "cc-chat-virtual-list__pull-up", pullUpContent, pullUpLoading = false, pullUpMinDuration = 420, pullUpThreshold = 48, stickToBottomOnAppend = true, stickToBottomOnUpdate = true, topLoadingClassName = "cc-chat-virtual-list__top-loading", topLoadingContent = "", topBuffer = 10, topThreshold = 96, className = "cc-chat-virtual-list", viewportClassName = "cc-chat-virtual-list__viewport", onPullUp, onReachTop, onRangeChange, style, ...props }, ref) {
4654
5174
  const viewportRef = React.useRef(null);
4655
5175
  const pullUpRef = React.useRef(null);
4656
5176
  const observersRef = React.useRef(new Map());
@@ -4668,6 +5188,7 @@ function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, de
4668
5188
  const bottomFrameRef = React.useRef();
4669
5189
  const bottomTimerRef = React.useRef();
4670
5190
  const prependFrameRef = React.useRef();
5191
+ const prependStabilizeFrameRef = React.useRef();
4671
5192
  const prependTimerRef = React.useRef();
4672
5193
  const topLoadingFallbackTimerRef = React.useRef();
4673
5194
  const chatControllerRef = React.useRef(new ChatVirtualListController({ bottomThreshold, topThreshold }));
@@ -4702,14 +5223,68 @@ function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, de
4702
5223
  }
4703
5224
  releaseTopLoadingLock();
4704
5225
  }, [releaseTopLoadingLock]);
4705
- const clearPendingPrependAnchor = React.useCallback(() => {
5226
+ const clearPendingPrependAnchor = React.useCallback((force = false) => {
5227
+ const pendingAnchor = pendingPrependAnchorRef.current;
5228
+ if (!force && pendingAnchor) {
5229
+ const remaining = pendingAnchor.releaseAt - Date.now();
5230
+ if (remaining > 0) {
5231
+ if (prependTimerRef.current !== undefined)
5232
+ window.clearTimeout(prependTimerRef.current);
5233
+ prependTimerRef.current = window.setTimeout(() => {
5234
+ clearPendingPrependAnchor(true);
5235
+ }, remaining);
5236
+ return;
5237
+ }
5238
+ }
4706
5239
  pendingPrependAnchorRef.current = null;
5240
+ if (prependStabilizeFrameRef.current !== undefined) {
5241
+ window.cancelAnimationFrame(prependStabilizeFrameRef.current);
5242
+ prependStabilizeFrameRef.current = undefined;
5243
+ }
4707
5244
  if (prependTimerRef.current !== undefined) {
4708
5245
  window.clearTimeout(prependTimerRef.current);
4709
5246
  prependTimerRef.current = undefined;
4710
5247
  }
4711
5248
  maybeReleaseTopLoadingLock();
4712
5249
  }, [maybeReleaseTopLoadingLock]);
5250
+ const applyPendingPrependAnchor = React.useCallback(() => {
5251
+ const pendingAnchor = pendingPrependAnchorRef.current;
5252
+ const viewport = viewportRef.current;
5253
+ if (!pendingAnchor || !viewport)
5254
+ return false;
5255
+ const anchorIndex = keys.findIndex((itemKey) => Object.is(itemKey, pendingAnchor.anchorKey));
5256
+ const nextScrollTop = anchorIndex >= 0
5257
+ ? Math.max(0, listControllerRef.current.getItem(anchorIndex).start -
5258
+ pendingAnchor.anchorOffset)
5259
+ : pendingAnchor.targetScrollTop;
5260
+ viewport.scrollTop = nextScrollTop;
5261
+ setScrollOffset(nextScrollTop);
5262
+ previousScrollTopRef.current = nextScrollTop;
5263
+ previousScrollHeightRef.current = viewport.scrollHeight;
5264
+ return true;
5265
+ }, [keys]);
5266
+ const schedulePrependStabilization = React.useCallback(() => {
5267
+ if (prependStabilizeFrameRef.current !== undefined) {
5268
+ window.cancelAnimationFrame(prependStabilizeFrameRef.current);
5269
+ }
5270
+ let remainingFrames = CHAT_PREPEND_STABILIZE_FRAMES;
5271
+ const stabilize = () => {
5272
+ const pendingAnchor = pendingPrependAnchorRef.current;
5273
+ if (!pendingAnchor) {
5274
+ prependStabilizeFrameRef.current = undefined;
5275
+ return;
5276
+ }
5277
+ applyPendingPrependAnchor();
5278
+ remainingFrames -= 1;
5279
+ if (remainingFrames <= 0 && pendingAnchor.releaseAt <= Date.now()) {
5280
+ prependStabilizeFrameRef.current = undefined;
5281
+ return;
5282
+ }
5283
+ prependStabilizeFrameRef.current =
5284
+ window.requestAnimationFrame(stabilize);
5285
+ };
5286
+ prependStabilizeFrameRef.current = window.requestAnimationFrame(stabilize);
5287
+ }, [applyPendingPrependAnchor]);
4713
5288
  const scheduleTopLoadingFallback = React.useCallback(() => {
4714
5289
  if (topLoadingFallbackTimerRef.current !== undefined) {
4715
5290
  window.clearTimeout(topLoadingFallbackTimerRef.current);
@@ -4855,8 +5430,12 @@ function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, de
4855
5430
  anchorOffset: previousAnchor.offset,
4856
5431
  measuredKeys: new Set(),
4857
5432
  prependKeys: new Set(keys.slice(0, prependCount)),
5433
+ releaseAt: Date.now() + CHAT_PREPEND_MOMENTUM_LOCK_MS,
5434
+ targetScrollTop: nextScrollTop,
4858
5435
  }
4859
5436
  : null;
5437
+ if (pendingPrependAnchorRef.current)
5438
+ schedulePrependStabilization();
4860
5439
  if (prependFrameRef.current !== undefined)
4861
5440
  window.cancelAnimationFrame(prependFrameRef.current);
4862
5441
  if (prependTimerRef.current !== undefined)
@@ -4906,6 +5485,7 @@ function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, de
4906
5485
  viewportHeight,
4907
5486
  scrollToBottom,
4908
5487
  clearPendingPrependAnchor,
5488
+ schedulePrependStabilization,
4909
5489
  maybeReleaseTopLoadingLock,
4910
5490
  ]);
4911
5491
  useEffect(() => {
@@ -4936,6 +5516,8 @@ function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, de
4936
5516
  window.clearTimeout(bottomTimerRef.current);
4937
5517
  if (prependFrameRef.current !== undefined)
4938
5518
  window.cancelAnimationFrame(prependFrameRef.current);
5519
+ if (prependStabilizeFrameRef.current !== undefined)
5520
+ window.cancelAnimationFrame(prependStabilizeFrameRef.current);
4939
5521
  if (prependTimerRef.current !== undefined)
4940
5522
  window.clearTimeout(prependTimerRef.current);
4941
5523
  if (topLoadingFallbackTimerRef.current !== undefined)
@@ -5015,12 +5597,19 @@ function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, de
5015
5597
  useEffect(() => {
5016
5598
  onRangeChange?.({ endIndex: range.endIndex, startIndex: range.startIndex });
5017
5599
  }, [onRangeChange, range.endIndex, range.startIndex]);
5600
+ const hasPendingKeySync = () => {
5601
+ const previousKeys = previousKeysRef.current;
5602
+ return (previousKeys.length !== keys.length ||
5603
+ previousKeys.some((itemKey, index) => !Object.is(itemKey, keys[index])));
5604
+ };
5018
5605
  const observeItem = (index, key, element) => {
5019
5606
  observersRef.current.get(key)?.disconnect();
5020
5607
  observersRef.current.delete(key);
5021
5608
  if (!element)
5022
5609
  return;
5023
5610
  const measure = () => {
5611
+ if (!pendingPrependAnchorRef.current && hasPendingKeySync())
5612
+ return;
5024
5613
  const previousTotal = listControllerRef.current.getTotalSize();
5025
5614
  const previousItem = listControllerRef.current.getItem(index);
5026
5615
  const viewport = viewportRef.current;
@@ -5039,12 +5628,15 @@ function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, de
5039
5628
  const anchorItem = listControllerRef.current.getItem(anchorIndex);
5040
5629
  const nextItem = listControllerRef.current.getItem(index);
5041
5630
  const delta = nextItem.size - previousItem.size;
5042
- viewport.scrollTop = Math.max(0, index < anchorIndex && delta !== 0
5631
+ const nextScrollTop = Math.max(0, index < anchorIndex && delta !== 0
5043
5632
  ? previousScrollTop + delta
5044
5633
  : anchorItem.start - pendingAnchor.anchorOffset);
5045
- setScrollOffset(viewport.scrollTop);
5046
- previousScrollTopRef.current = viewport.scrollTop;
5634
+ pendingAnchor.targetScrollTop = nextScrollTop;
5635
+ viewport.scrollTop = nextScrollTop;
5636
+ setScrollOffset(nextScrollTop);
5637
+ previousScrollTopRef.current = nextScrollTop;
5047
5638
  previousScrollHeightRef.current = viewport.scrollHeight;
5639
+ schedulePrependStabilization();
5048
5640
  }
5049
5641
  if (pendingAnchor.measuredKeys.size >= pendingAnchor.prependKeys.size) {
5050
5642
  clearPendingPrependAnchor();
@@ -5080,7 +5672,12 @@ function ChatVirtualListInner({ data, bottomBuffer = 8, bottomThreshold = 64, de
5080
5672
  return (jsxs("div", { className: className, "data-top-loading": topLoadingPending ? "true" : undefined, style: {
5081
5673
  "--cc-chat-virtual-list-height": resolvedHeight,
5082
5674
  ...style,
5083
- }, ...props, children: [jsx("div", { "aria-hidden": !topLoadingPending, className: topLoadingClassName, "data-active": topLoadingPending ? "true" : "false", role: topLoadingPending ? "status" : undefined, children: topLoadingContent }), jsxs("div", { className: viewportClassName, "data-top-loading": topLoadingPending ? "true" : undefined, ref: viewportRef, onScroll: (event) => {
5675
+ }, ...props, children: [jsx(Loading, { "aria-hidden": !topLoadingPending, className: topLoadingClassName, "data-active": topLoadingPending ? "true" : "false", role: topLoadingPending ? "status" : undefined, text: topLoadingContent }), jsxs("div", { className: viewportClassName, "data-top-loading": topLoadingPending ? "true" : undefined, ref: viewportRef, onScroll: (event) => {
5676
+ if (pendingPrependAnchorRef.current) {
5677
+ const stabilized = applyPendingPrependAnchor();
5678
+ if (stabilized)
5679
+ return;
5680
+ }
5084
5681
  const nextScrollTop = event.currentTarget.scrollTop;
5085
5682
  previousScrollTopRef.current = nextScrollTop;
5086
5683
  previousScrollHeightRef.current = event.currentTarget.scrollHeight;
@@ -5822,5 +6419,5 @@ function LiveRoom({ chats = EMPTY_LIVE_CHATS, className = "cc-live-room", danmak
5822
6419
  } }), jsx("button", { "aria-label": getClassComponentsLocale().liveRoomSendLabel, title: getClassComponentsLocale().liveRoomSendLabel, type: "button", onClick: send, children: jsx(LiveRoomIcon, { name: "send" }) })] })] })] }));
5823
6420
  }
5824
6421
 
5825
- export { Affix, Backtop, Badge, Barcode, Breadcrumb, Button, Calendar, CanvasEditor, CanvasImage, ChatVirtualList, Checkbox, Checked, ClassConfigProvider, ColorPicker, ComicReader, ConfigTable, Countdown, Danmaku, DatePicker, DateRangePicker, DesignEffect, DragDropBoard, Draggable, EllipsisText, Empty, Field, FilePreview, FloatingBall, FormProvider, GradientText, Input, LazyImage, LiveRoom, Loading, Marquee, MasonryVirtualList, Modal, MultiColumnPicker, NovelReader, NumberInput, Pagination, PasswordInput, Popconfirm, Popup, ProgressBar, QRCode, RadioGroup, Rating, RollingNumber, Select, Signature, Skeleton, SlideCaptcha, Swiper, Switch, Table, Tabs, Tag, Textarea, TiltCard, Timeline, ToastViewport, Tooltip, TypewriterText, Upload, VerificationCode, VideoDetailTransition, VideoPlayer, VirtualList, VirtualSelect, VirtualTable, defaultToastManager, useField, useForm, useFormState };
6422
+ export { Affix, Backtop, Badge, Barcode, Breadcrumb, Button, Calendar, CanvasEditor, CanvasImage, ChatTextarea, ChatVirtualList, Checkbox, Checked, ClassConfigProvider, CodePreview, ColorPicker, ComicReader, ConfigTable, Countdown, Danmaku, DatePicker, DateRangePicker, DesignEffect, DragDropBoard, Draggable, EllipsisText, Empty, Field, FilePreview, FloatingBall, FormProvider, GradientText, HeightTransition, Input, LazyImage, LiveRoom, Loading, Marquee, MasonryVirtualList, Menu, Modal, MultiColumnPicker, NovelReader, NumberInput, OrbitalSphere, Pagination, PasswordInput, Popconfirm, Popup, ProgressBar, QRCode, RadioGroup, Rating, RollingNumber, Select, Signature, Skeleton, SlideCaptcha, Swiper, Switch, Table, Tabs, Tag, Textarea, TiltCard, Timeline, ToastViewport, Tooltip, TypewriterText, Upload, VerificationCode, VideoDetailTransition, VideoPlayer, VirtualList, VirtualSelect, VirtualTable, defaultToastManager, useField, useForm, useFormState };
5826
6423
  //# sourceMappingURL=index.js.map