@comitor/ui 2.1.0 → 3.0.0

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 (393) hide show
  1. package/README.md +59 -24
  2. package/dist/chart/chart.d.ts +6 -1
  3. package/dist/chart/chart.js +3 -1
  4. package/dist/chart/chart.js.map +1 -1
  5. package/dist/chart/comitor-chart.d.ts +4 -1
  6. package/dist/chart/comitor-chart.js +3 -1
  7. package/dist/chart/comitor-chart.js.map +1 -1
  8. package/dist/components/composite/avatar-group.d.ts +9 -6
  9. package/dist/components/composite/avatar-group.js +6 -2
  10. package/dist/components/composite/avatar-group.js.map +1 -1
  11. package/dist/components/composite/combobox.d.ts +11 -11
  12. package/dist/components/composite/combobox.js +25 -11
  13. package/dist/components/composite/combobox.js.map +1 -1
  14. package/dist/components/composite/command-palette-constants.d.ts +15 -5
  15. package/dist/components/composite/command-palette-constants.js +3 -2
  16. package/dist/components/composite/command-palette-constants.js.map +1 -1
  17. package/dist/components/composite/command-palette.d.ts +2 -2
  18. package/dist/components/composite/command-palette.js +8 -5
  19. package/dist/components/composite/command-palette.js.map +1 -1
  20. package/dist/components/composite/confirm-dialog.d.ts +8 -8
  21. package/dist/components/composite/confirm-dialog.js +8 -3
  22. package/dist/components/composite/confirm-dialog.js.map +1 -1
  23. package/dist/components/composite/copy-button.d.ts +7 -3
  24. package/dist/components/composite/copy-button.js +7 -3
  25. package/dist/components/composite/copy-button.js.map +1 -1
  26. package/dist/components/composite/data-table-constants.d.ts +7 -3
  27. package/dist/components/composite/data-table-constants.js +2 -9
  28. package/dist/components/composite/data-table-constants.js.map +1 -1
  29. package/dist/components/composite/data-table.js +3 -3
  30. package/dist/components/composite/data-table.js.map +1 -1
  31. package/dist/components/composite/date-picker-constants.d.ts +32 -9
  32. package/dist/components/composite/date-picker-constants.js +16 -11
  33. package/dist/components/composite/date-picker-constants.js.map +1 -1
  34. package/dist/components/composite/date-picker.d.ts +19 -10
  35. package/dist/components/composite/date-picker.js +33 -9
  36. package/dist/components/composite/date-picker.js.map +1 -1
  37. package/dist/components/composite/filter-chips.d.ts +4 -3
  38. package/dist/components/composite/filter-chips.js +6 -2
  39. package/dist/components/composite/filter-chips.js.map +1 -1
  40. package/dist/components/composite/inline-edit.d.ts +14 -3
  41. package/dist/components/composite/inline-edit.js +8 -4
  42. package/dist/components/composite/inline-edit.js.map +1 -1
  43. package/dist/components/composite/relative-time.d.ts +4 -4
  44. package/dist/components/composite/relative-time.js +4 -2
  45. package/dist/components/composite/relative-time.js.map +1 -1
  46. package/dist/components/composite/route-tabs-nav.d.ts +9 -0
  47. package/dist/components/composite/route-tabs-nav.js +11 -0
  48. package/dist/components/composite/route-tabs-nav.js.map +1 -0
  49. package/dist/components/composite/route-tabs.d.ts +4 -1
  50. package/dist/components/composite/route-tabs.js +3 -2
  51. package/dist/components/composite/route-tabs.js.map +1 -1
  52. package/dist/components/composite/search-filter-bar-constants.d.ts +18 -8
  53. package/dist/components/composite/search-filter-bar-constants.js +2 -14
  54. package/dist/components/composite/search-filter-bar-constants.js.map +1 -1
  55. package/dist/components/composite/search-filter-bar.d.ts +5 -2
  56. package/dist/components/composite/search-filter-bar.js +2 -2
  57. package/dist/components/composite/search-filter-bar.js.map +1 -1
  58. package/dist/components/composite/stat-card.d.ts +1 -1
  59. package/dist/components/composite/table-pagination-constants.d.ts +16 -11
  60. package/dist/components/composite/table-pagination-constants.js +2 -12
  61. package/dist/components/composite/table-pagination-constants.js.map +1 -1
  62. package/dist/components/composite/table-pagination.d.ts +6 -3
  63. package/dist/components/composite/table-pagination.js +3 -5
  64. package/dist/components/composite/table-pagination.js.map +1 -1
  65. package/dist/components/composite/toast.d.ts +4 -2
  66. package/dist/components/composite/toast.js +9 -1
  67. package/dist/components/composite/toast.js.map +1 -1
  68. package/dist/components/ui/action-bar-constants.d.ts +8 -4
  69. package/dist/components/ui/action-bar-constants.js +2 -5
  70. package/dist/components/ui/action-bar-constants.js.map +1 -1
  71. package/dist/components/ui/action-bar.d.ts +5 -4
  72. package/dist/components/ui/action-bar.js +2 -2
  73. package/dist/components/ui/action-bar.js.map +1 -1
  74. package/dist/components/ui/alert.d.ts +1 -1
  75. package/dist/components/ui/badge-overflow.d.ts +2 -2
  76. package/dist/components/ui/badge-overflow.js +4 -2
  77. package/dist/components/ui/badge-overflow.js.map +1 -1
  78. package/dist/components/ui/badge.d.ts +1 -1
  79. package/dist/components/ui/banner.d.ts +6 -5
  80. package/dist/components/ui/banner.js +3 -1
  81. package/dist/components/ui/banner.js.map +1 -1
  82. package/dist/components/ui/breadcrumb.d.ts +11 -4
  83. package/dist/components/ui/breadcrumb.js +7 -2
  84. package/dist/components/ui/breadcrumb.js.map +1 -1
  85. package/dist/components/ui/button.d.ts +2 -2
  86. package/dist/components/ui/calendar.d.ts +12 -5
  87. package/dist/components/ui/calendar.js +38 -3
  88. package/dist/components/ui/calendar.js.map +1 -1
  89. package/dist/components/ui/carousel.d.ts +4 -3
  90. package/dist/components/ui/carousel.js +11 -4
  91. package/dist/components/ui/carousel.js.map +1 -1
  92. package/dist/components/ui/circular-progress.js.map +1 -1
  93. package/dist/components/ui/command.d.ts +5 -4
  94. package/dist/components/ui/command.js +6 -2
  95. package/dist/components/ui/command.js.map +1 -1
  96. package/dist/components/ui/cropper-constants.d.ts +8 -3
  97. package/dist/components/ui/cropper-constants.js +2 -6
  98. package/dist/components/ui/cropper-constants.js.map +1 -1
  99. package/dist/components/ui/cropper.d.ts +2 -1
  100. package/dist/components/ui/cropper.js +2 -2
  101. package/dist/components/ui/cropper.js.map +1 -1
  102. package/dist/components/ui/dialog.d.ts +2 -2
  103. package/dist/components/ui/dialog.js +4 -1
  104. package/dist/components/ui/dialog.js.map +1 -1
  105. package/dist/components/ui/item.d.ts +2 -2
  106. package/dist/components/ui/kanban-constants.d.ts +7 -1
  107. package/dist/components/ui/kanban-constants.js +2 -17
  108. package/dist/components/ui/kanban-constants.js.map +1 -1
  109. package/dist/components/ui/kanban.d.ts +2 -2
  110. package/dist/components/ui/kanban.js +2 -2
  111. package/dist/components/ui/kanban.js.map +1 -1
  112. package/dist/components/ui/key-value-constants.d.ts +7 -3
  113. package/dist/components/ui/key-value-constants.js +2 -7
  114. package/dist/components/ui/key-value-constants.js.map +1 -1
  115. package/dist/components/ui/key-value.d.ts +5 -5
  116. package/dist/components/ui/key-value.js +2 -2
  117. package/dist/components/ui/key-value.js.map +1 -1
  118. package/dist/components/ui/mask-input-constants.d.ts +2 -3
  119. package/dist/components/ui/mask-input-constants.js +5 -6
  120. package/dist/components/ui/mask-input-constants.js.map +1 -1
  121. package/dist/components/ui/mask-input.d.ts +7 -4
  122. package/dist/components/ui/mask-input.js +4 -2
  123. package/dist/components/ui/mask-input.js.map +1 -1
  124. package/dist/components/ui/mention.d.ts +3 -2
  125. package/dist/components/ui/mention.js +4 -6
  126. package/dist/components/ui/mention.js.map +1 -1
  127. package/dist/components/ui/pagination.d.ts +13 -9
  128. package/dist/components/ui/pagination.js +15 -6
  129. package/dist/components/ui/pagination.js.map +1 -1
  130. package/dist/components/ui/phone-input-constants.d.ts +17 -6
  131. package/dist/components/ui/phone-input-constants.js +2 -5
  132. package/dist/components/ui/phone-input-constants.js.map +1 -1
  133. package/dist/components/ui/phone-input.d.ts +18 -11
  134. package/dist/components/ui/phone-input.js +9 -10
  135. package/dist/components/ui/phone-input.js.map +1 -1
  136. package/dist/components/ui/progress.d.ts +2 -2
  137. package/dist/components/ui/rating.d.ts +2 -1
  138. package/dist/components/ui/rating.js +4 -2
  139. package/dist/components/ui/rating.js.map +1 -1
  140. package/dist/components/ui/scroll-spy.js +3 -1
  141. package/dist/components/ui/scroll-spy.js.map +1 -1
  142. package/dist/components/ui/sheet.d.ts +2 -2
  143. package/dist/components/ui/sheet.js +10 -1
  144. package/dist/components/ui/sheet.js.map +1 -1
  145. package/dist/components/ui/spinner.js +4 -1
  146. package/dist/components/ui/spinner.js.map +1 -1
  147. package/dist/components/ui/stepper-constants.d.ts +7 -2
  148. package/dist/components/ui/stepper-constants.js +2 -5
  149. package/dist/components/ui/stepper-constants.js.map +1 -1
  150. package/dist/components/ui/stepper.d.ts +2 -1
  151. package/dist/components/ui/stepper.js +2 -2
  152. package/dist/components/ui/stepper.js.map +1 -1
  153. package/dist/components/ui/tags-input.d.ts +3 -2
  154. package/dist/components/ui/tags-input.js +4 -2
  155. package/dist/components/ui/tags-input.js.map +1 -1
  156. package/dist/components/ui/time-picker-constants.d.ts +12 -9
  157. package/dist/components/ui/time-picker-constants.js +2 -10
  158. package/dist/components/ui/time-picker-constants.js.map +1 -1
  159. package/dist/components/ui/time-picker.d.ts +11 -6
  160. package/dist/components/ui/time-picker.js +5 -7
  161. package/dist/components/ui/time-picker.js.map +1 -1
  162. package/dist/components/ui/toggle.d.ts +1 -1
  163. package/dist/components/ui/tour-constants.d.ts +7 -2
  164. package/dist/components/ui/tour-constants.js +2 -8
  165. package/dist/components/ui/tour-constants.js.map +1 -1
  166. package/dist/components/ui/tour.d.ts +3 -2
  167. package/dist/components/ui/tour.js +2 -2
  168. package/dist/components/ui/tour.js.map +1 -1
  169. package/dist/data-grid/data-grid-cell-variants.js +4 -4
  170. package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
  171. package/dist/data-grid/data-grid-column-header.js +1 -1
  172. package/dist/data-grid/data-grid-column-header.js.map +1 -1
  173. package/dist/data-grid/data-grid-filter-menu.d.ts +5 -4
  174. package/dist/data-grid/data-grid-filter-menu.js +13 -8
  175. package/dist/data-grid/data-grid-filter-menu.js.map +1 -1
  176. package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +4 -4
  177. package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -1
  178. package/dist/data-grid/data-grid-locale.d.ts +58 -6
  179. package/dist/data-grid/data-grid-locale.js +41 -9
  180. package/dist/data-grid/data-grid-locale.js.map +1 -1
  181. package/dist/data-grid/data-grid-row-height-menu.d.ts +4 -3
  182. package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
  183. package/dist/data-grid/data-grid-sort-menu.d.ts +4 -4
  184. package/dist/data-grid/data-grid-sort-menu.js +1 -1
  185. package/dist/data-grid/data-grid-sort-menu.js.map +1 -1
  186. package/dist/data-grid/data-grid-view-menu.d.ts +4 -4
  187. package/dist/data-grid/data-grid-view-menu.js.map +1 -1
  188. package/dist/data-grid/data-grid.d.ts +4 -3
  189. package/dist/data-grid/data-grid.js +2 -3
  190. package/dist/data-grid/data-grid.js.map +1 -1
  191. package/dist/data-grid/index.d.ts +1 -0
  192. package/dist/data-grid/index.js.map +1 -1
  193. package/dist/data-grid/messages-en.d.ts +23 -4
  194. package/dist/data-grid/messages-en.js +20 -0
  195. package/dist/data-grid/messages-en.js.map +1 -1
  196. package/dist/data-grid/messages-vi.d.ts +3 -1
  197. package/dist/data-grid/messages-vi.js +14 -0
  198. package/dist/data-grid/messages-vi.js.map +1 -1
  199. package/dist/data-grid/messages.d.ts +29 -3
  200. package/dist/data-grid/messages.js.map +1 -1
  201. package/dist/data-grid/sortable.js +16 -19
  202. package/dist/data-grid/sortable.js.map +1 -1
  203. package/dist/data-grid/types.d.ts +1 -1
  204. package/dist/data-grid/use-data-grid-undo-redo.d.ts +5 -4
  205. package/dist/data-grid/use-data-grid-undo-redo.js +2 -2
  206. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
  207. package/dist/data-grid/use-data-grid.d.ts +21 -6
  208. package/dist/data-grid/use-data-grid.js +8 -6
  209. package/dist/data-grid/use-data-grid.js.map +1 -1
  210. package/dist/data-grid/utils.d.ts +24 -4
  211. package/dist/data-grid/utils.js +15 -5
  212. package/dist/data-grid/utils.js.map +1 -1
  213. package/dist/i18n/locale-provider.d.ts +109 -0
  214. package/dist/i18n/locale-provider.js +47 -0
  215. package/dist/i18n/locale-provider.js.map +1 -0
  216. package/dist/i18n/locale.d.ts +27 -0
  217. package/dist/i18n/locale.js +10 -0
  218. package/dist/i18n/locale.js.map +1 -0
  219. package/dist/i18n/messages.d.ts +116 -0
  220. package/dist/i18n/messages.js +149 -0
  221. package/dist/i18n/messages.js.map +1 -0
  222. package/dist/i18n/namespaces/action-bar.d.ts +7 -0
  223. package/dist/i18n/namespaces/action-bar.js +15 -0
  224. package/dist/i18n/namespaces/action-bar.js.map +1 -0
  225. package/dist/i18n/namespaces/avatar-group.d.ts +14 -0
  226. package/dist/i18n/namespaces/avatar-group.js +13 -0
  227. package/dist/i18n/namespaces/avatar-group.js.map +1 -0
  228. package/dist/i18n/namespaces/badge-overflow.d.ts +12 -0
  229. package/dist/i18n/namespaces/badge-overflow.js +11 -0
  230. package/dist/i18n/namespaces/badge-overflow.js.map +1 -0
  231. package/dist/i18n/namespaces/banner.d.ts +9 -0
  232. package/dist/i18n/namespaces/banner.js +7 -0
  233. package/dist/i18n/namespaces/banner.js.map +1 -0
  234. package/dist/i18n/namespaces/breadcrumb.d.ts +11 -0
  235. package/dist/i18n/namespaces/breadcrumb.js +13 -0
  236. package/dist/i18n/namespaces/breadcrumb.js.map +1 -0
  237. package/dist/i18n/namespaces/calendar.d.ts +40 -0
  238. package/dist/i18n/namespaces/calendar.js +25 -0
  239. package/dist/i18n/namespaces/calendar.js.map +1 -0
  240. package/dist/i18n/namespaces/carousel.d.ts +15 -0
  241. package/dist/i18n/namespaces/carousel.js +17 -0
  242. package/dist/i18n/namespaces/carousel.js.map +1 -0
  243. package/dist/i18n/namespaces/combobox.d.ts +21 -0
  244. package/dist/i18n/namespaces/combobox.js +23 -0
  245. package/dist/i18n/namespaces/combobox.js.map +1 -0
  246. package/dist/i18n/namespaces/command-palette.d.ts +20 -0
  247. package/dist/i18n/namespaces/command-palette.js +19 -0
  248. package/dist/i18n/namespaces/command-palette.js.map +1 -0
  249. package/dist/i18n/namespaces/command.d.ts +11 -0
  250. package/dist/i18n/namespaces/command.js +13 -0
  251. package/dist/i18n/namespaces/command.js.map +1 -0
  252. package/dist/i18n/namespaces/confirm-dialog.d.ts +13 -0
  253. package/dist/i18n/namespaces/confirm-dialog.js +15 -0
  254. package/dist/i18n/namespaces/confirm-dialog.js.map +1 -0
  255. package/dist/i18n/namespaces/contrast.d.ts +7 -0
  256. package/dist/i18n/namespaces/contrast.js +13 -0
  257. package/dist/i18n/namespaces/contrast.js.map +1 -0
  258. package/dist/i18n/namespaces/copy-button.d.ts +11 -0
  259. package/dist/i18n/namespaces/copy-button.js +13 -0
  260. package/dist/i18n/namespaces/copy-button.js.map +1 -0
  261. package/dist/i18n/namespaces/cropper.d.ts +7 -0
  262. package/dist/i18n/namespaces/cropper.js +17 -0
  263. package/dist/i18n/namespaces/cropper.js.map +1 -0
  264. package/dist/i18n/namespaces/data-table.d.ts +7 -0
  265. package/dist/i18n/namespaces/data-table.js +23 -0
  266. package/dist/i18n/namespaces/data-table.js.map +1 -0
  267. package/dist/i18n/namespaces/date-picker.d.ts +42 -0
  268. package/dist/i18n/namespaces/date-picker.js +35 -0
  269. package/dist/i18n/namespaces/date-picker.js.map +1 -0
  270. package/dist/i18n/namespaces/dialog.d.ts +9 -0
  271. package/dist/i18n/namespaces/dialog.js +7 -0
  272. package/dist/i18n/namespaces/dialog.js.map +1 -0
  273. package/dist/i18n/namespaces/display-axis.d.ts +25 -0
  274. package/dist/i18n/namespaces/display-axis.js +21 -0
  275. package/dist/i18n/namespaces/display-axis.js.map +1 -0
  276. package/dist/i18n/namespaces/file-upload.d.ts +11 -0
  277. package/dist/i18n/namespaces/file-upload.js +19 -0
  278. package/dist/i18n/namespaces/file-upload.js.map +1 -0
  279. package/dist/i18n/namespaces/filter-chips.d.ts +11 -0
  280. package/dist/i18n/namespaces/filter-chips.js +13 -0
  281. package/dist/i18n/namespaces/filter-chips.js.map +1 -0
  282. package/dist/i18n/namespaces/image-upload-field.d.ts +12 -0
  283. package/dist/i18n/namespaces/image-upload-field.js +23 -0
  284. package/dist/i18n/namespaces/image-upload-field.js.map +1 -0
  285. package/dist/i18n/namespaces/inline-edit.d.ts +15 -0
  286. package/dist/i18n/namespaces/inline-edit.js +17 -0
  287. package/dist/i18n/namespaces/inline-edit.js.map +1 -0
  288. package/dist/i18n/namespaces/kanban.d.ts +11 -0
  289. package/dist/i18n/namespaces/kanban.js +39 -0
  290. package/dist/i18n/namespaces/kanban.js.map +1 -0
  291. package/dist/i18n/namespaces/key-value.d.ts +10 -0
  292. package/dist/i18n/namespaces/key-value.js +19 -0
  293. package/dist/i18n/namespaces/key-value.js.map +1 -0
  294. package/dist/i18n/namespaces/mention.d.ts +9 -0
  295. package/dist/i18n/namespaces/mention.js +7 -0
  296. package/dist/i18n/namespaces/mention.js.map +1 -0
  297. package/dist/i18n/namespaces/pagination.d.ts +19 -0
  298. package/dist/i18n/namespaces/pagination.js +21 -0
  299. package/dist/i18n/namespaces/pagination.js.map +1 -0
  300. package/dist/i18n/namespaces/phone-input.d.ts +10 -0
  301. package/dist/i18n/namespaces/phone-input.js +17 -0
  302. package/dist/i18n/namespaces/phone-input.js.map +1 -0
  303. package/dist/i18n/namespaces/rating.d.ts +12 -0
  304. package/dist/i18n/namespaces/rating.js +11 -0
  305. package/dist/i18n/namespaces/rating.js.map +1 -0
  306. package/dist/i18n/namespaces/route-tabs.d.ts +9 -0
  307. package/dist/i18n/namespaces/route-tabs.js +7 -0
  308. package/dist/i18n/namespaces/route-tabs.js.map +1 -0
  309. package/dist/i18n/namespaces/scroll-spy.d.ts +9 -0
  310. package/dist/i18n/namespaces/scroll-spy.js +7 -0
  311. package/dist/i18n/namespaces/scroll-spy.js.map +1 -0
  312. package/dist/i18n/namespaces/search-filter-bar.d.ts +15 -0
  313. package/dist/i18n/namespaces/search-filter-bar.js +32 -0
  314. package/dist/i18n/namespaces/search-filter-bar.js.map +1 -0
  315. package/dist/i18n/namespaces/sheet.d.ts +9 -0
  316. package/dist/i18n/namespaces/sheet.js +7 -0
  317. package/dist/i18n/namespaces/sheet.js.map +1 -0
  318. package/dist/i18n/namespaces/shell.d.ts +17 -0
  319. package/dist/i18n/namespaces/shell.js +75 -0
  320. package/dist/i18n/namespaces/shell.js.map +1 -0
  321. package/dist/i18n/namespaces/spinner.d.ts +9 -0
  322. package/dist/i18n/namespaces/spinner.js +7 -0
  323. package/dist/i18n/namespaces/spinner.js.map +1 -0
  324. package/dist/i18n/namespaces/stepper.d.ts +7 -0
  325. package/dist/i18n/namespaces/stepper.js +15 -0
  326. package/dist/i18n/namespaces/stepper.js.map +1 -0
  327. package/dist/i18n/namespaces/table-pagination.d.ts +7 -0
  328. package/dist/i18n/namespaces/table-pagination.js +29 -0
  329. package/dist/i18n/namespaces/table-pagination.js.map +1 -0
  330. package/dist/i18n/namespaces/tags-input.d.ts +12 -0
  331. package/dist/i18n/namespaces/tags-input.js +11 -0
  332. package/dist/i18n/namespaces/tags-input.js.map +1 -0
  333. package/dist/i18n/namespaces/time-picker.d.ts +15 -0
  334. package/dist/i18n/namespaces/time-picker.js +25 -0
  335. package/dist/i18n/namespaces/time-picker.js.map +1 -0
  336. package/dist/i18n/namespaces/toast.d.ts +15 -0
  337. package/dist/i18n/namespaces/toast.js +7 -0
  338. package/dist/i18n/namespaces/toast.js.map +1 -0
  339. package/dist/i18n/namespaces/tour.d.ts +7 -0
  340. package/dist/i18n/namespaces/tour.js +21 -0
  341. package/dist/i18n/namespaces/tour.js.map +1 -0
  342. package/dist/index.d.ts +32 -0
  343. package/dist/index.js +24 -1
  344. package/dist/index.js.map +1 -1
  345. package/dist/shell/app-header.js.map +1 -1
  346. package/dist/shell/app-shell.d.ts +20 -2
  347. package/dist/shell/app-shell.js +4 -1
  348. package/dist/shell/app-shell.js.map +1 -1
  349. package/dist/shell/contrast-constants.d.ts +9 -1
  350. package/dist/shell/contrast-constants.js +2 -4
  351. package/dist/shell/contrast-constants.js.map +1 -1
  352. package/dist/shell/contrast-toggle.d.ts +4 -1
  353. package/dist/shell/contrast-toggle.js +2 -2
  354. package/dist/shell/contrast-toggle.js.map +1 -1
  355. package/dist/shell/display-axis-constants.d.ts +2 -1
  356. package/dist/shell/display-axis-constants.js.map +1 -1
  357. package/dist/shell/display-axis-controls.d.ts +15 -6
  358. package/dist/shell/display-axis-controls.js +11 -9
  359. package/dist/shell/display-axis-controls.js.map +1 -1
  360. package/dist/shell/mobile-menu.js.map +1 -1
  361. package/dist/shell/notifications-button.d.ts +1 -1
  362. package/dist/shell/notifications-button.js +3 -2
  363. package/dist/shell/notifications-button.js.map +1 -1
  364. package/dist/shell/shell-context.d.ts +4 -1
  365. package/dist/shell/shell-context.js +2 -4
  366. package/dist/shell/shell-context.js.map +1 -1
  367. package/dist/shell/sidebar.d.ts +1 -1
  368. package/dist/shell/sidebar.js.map +1 -1
  369. package/dist/shell/theme-toggle.d.ts +6 -3
  370. package/dist/shell/theme-toggle.js +2 -2
  371. package/dist/shell/theme-toggle.js.map +1 -1
  372. package/dist/shell/types.d.ts +14 -8
  373. package/dist/shell/types.js +2 -35
  374. package/dist/shell/types.js.map +1 -1
  375. package/dist/shell/user-menu.d.ts +6 -6
  376. package/dist/shell/user-menu.js.map +1 -1
  377. package/dist/shell/utils.d.ts +1 -1
  378. package/dist/shell/utils.js.map +1 -1
  379. package/dist/shell/workspace-switcher.js +1 -1
  380. package/dist/shell/workspace-switcher.js.map +1 -1
  381. package/dist/uploader/file-upload-constants.d.ts +13 -0
  382. package/dist/uploader/file-upload-constants.js +4 -16
  383. package/dist/uploader/file-upload-constants.js.map +1 -1
  384. package/dist/uploader/file-upload.d.ts +4 -1
  385. package/dist/uploader/file-upload.js +2 -2
  386. package/dist/uploader/file-upload.js.map +1 -1
  387. package/dist/uploader/image-upload-field.d.ts +5 -1
  388. package/dist/uploader/image-upload-field.js +2 -2
  389. package/dist/uploader/image-upload-field.js.map +1 -1
  390. package/dist/uploader/use-file-upload.d.ts +4 -0
  391. package/dist/uploader/use-file-upload.js +6 -2
  392. package/dist/uploader/use-file-upload.js.map +1 -1
  393. package/package.json +4 -3
@@ -22,8 +22,9 @@ interface ActionBarProps extends DivProps {
22
22
  orientation?: Orientation;
23
23
  loop?: boolean;
24
24
  /**
25
- * Chuỗi hiển thị và chuỗi a11y (tên thanh, dòng "Đã chọn N", tên nút đóng) — mặc định
26
- * `DEFAULT_ACTION_BAR_LABELS` (tiếng Việt); truyền một phần là đủ.
25
+ * Chuỗi hiển thị và chuỗi a11y (tên thanh, dòng "N selected", tên nút đóng). Không truyền thì
26
+ * theo namespace `actionBar` của `ComitorLocaleProvider` (mặc định tiếng Anh); truyền một phần
27
+ * là đủ.
27
28
  *
28
29
  * ⚠ Thêm so với thượng nguồn: bản gốc không có chuỗi nào — thanh `role="toolbar"` không tên và
29
30
  * nút đóng chỉ chứa `<X />` là một nút KHÔNG TÊN với trình đọc màn hình.
@@ -33,7 +34,7 @@ interface ActionBarProps extends DivProps {
33
34
  declare function ActionBar(props: ActionBarProps): React.JSX.Element | null;
34
35
  interface ActionBarSelectionProps extends DivProps {
35
36
  /**
36
- * Số mục đang chọn. Có giá trị thì phần tử tự in `labels.selected(count)` ("Đã chọn 3") TRƯỚC
37
+ * Số mục đang chọn. Có giá trị thì phần tử tự in `labels.selected(count)` ("3 selected") TRƯỚC
37
38
  * `children` — nên `children` chỉ còn là dấu ngăn + nút đóng.
38
39
  *
39
40
  * ⚠ Thêm so với thượng nguồn: bản gốc để app tự viết chữ ("{n} selected"), tức chuỗi hiển thị
@@ -54,7 +55,7 @@ interface ActionBarCloseProps extends React.ComponentProps<"button"> {
54
55
  * Nút đóng thanh (thường là "bỏ chọn"). Không truyền `children` thì vẽ icon X.
55
56
  *
56
57
  * ⚠ Khác thượng nguồn: có tên cho trình đọc màn hình — `aria-label` mặc định `labels.close`
57
- * ("Bỏ chọn"). Bản gốc chỉ render `children`, và khuôn dùng của chính thượng nguồn là
58
+ * ("Clear selection"). Bản gốc chỉ render `children`, và khuôn dùng của chính thượng nguồn là
58
59
  * `<ActionBarClose><X /></ActionBarClose>`: một nút KHÔNG TÊN. Nếu `children` là CHỮ nhìn thấy
59
60
  * được thì truyền `aria-label` trùng với chữ đó (WCAG 2.5.3), hoặc `aria-label={undefined}`.
60
61
  */
@@ -5,11 +5,11 @@ import { Slot } from "@radix-ui/react-slot";
5
5
  import { X } from "lucide-react";
6
6
  import * as React from "react";
7
7
  import * as ReactDOM from "react-dom";
8
+ import { useComitorLabels } from "../../i18n/locale-provider.js";
8
9
  import { cn } from "../../lib/cn.js";
9
10
  import { useComposedRefs } from "../../lib/compose-refs.js";
10
11
  import { useAsRef } from "../../lib/use-as-ref.js";
11
12
  import { useIsomorphicLayoutEffect } from "../../lib/use-isomorphic-layout-effect.js";
12
- import { DEFAULT_ACTION_BAR_LABELS } from "./action-bar-constants.js";
13
13
  import { Button } from "./button.js";
14
14
  const ROOT_NAME = "ActionBar";
15
15
  const GROUP_NAME = "ActionBarGroup";
@@ -81,7 +81,7 @@ function ActionBar(props) {
81
81
  onOpenChange
82
82
  });
83
83
  const dir = useDirection(dirProp);
84
- const labels = React.useMemo(() => ({ ...DEFAULT_ACTION_BAR_LABELS, ...labelsProp }), [labelsProp]);
84
+ const labels = useComitorLabels("actionBar", labelsProp);
85
85
  useIsomorphicLayoutEffect(() => {
86
86
  setMounted(true);
87
87
  }, []);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/action-bar.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/action-bar) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n *\n * Dùng ở Comitor: thanh THAO TÁC HÀNG LOẠT nổi ở đáy màn hình khi có dòng được chọn trong\n * `DataTable` / `DataGrid` — `open={selectedIds.length > 0}`, và `onOpenChange(false)` xoá vùng chọn.\n */\n\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { type ActionBarLabels, DEFAULT_ACTION_BAR_LABELS } from \"./action-bar-constants.js\";\nimport { Button } from \"./button.js\";\n\nconst ROOT_NAME = \"ActionBar\";\nconst GROUP_NAME = \"ActionBarGroup\";\nconst ITEM_NAME = \"ActionBarItem\";\nconst CLOSE_NAME = \"ActionBarClose\";\nconst SELECTION_NAME = \"ActionBarSelection\";\nconst SEPARATOR_NAME = \"ActionBarSeparator\";\nconst ITEM_SELECT = \"actionbar.itemSelect\";\nconst ENTRY_FOCUS = \"actionbarFocusGroup.onEntryFocus\";\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\ntype Direction = \"ltr\" | \"rtl\";\ntype Orientation = \"horizontal\" | \"vertical\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype RootElement = React.ComponentRef<typeof ActionBar>;\ntype ItemElement = React.ComponentRef<typeof ActionBarItem>;\ntype CloseElement = React.ComponentRef<typeof ActionBarClose>;\n\nfunction focusFirst(candidates: React.RefObject<HTMLElement | null>[], preventScroll = false) {\n const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;\n for (const candidateRef of candidates) {\n const candidate = candidateRef.current;\n if (!candidate) continue;\n if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;\n candidate.focus({ preventScroll });\n if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;\n }\n}\n\nfunction wrapArray<T>(array: T[], startIndex: number) {\n return array.map<T>((_, index) => array[(startIndex + index) % array.length] as T);\n}\n\nfunction getDirectionAwareKey(key: string, dir?: Direction) {\n if (dir !== \"rtl\") return key;\n return key === \"ArrowLeft\" ? \"ArrowRight\" : key === \"ArrowRight\" ? \"ArrowLeft\" : key;\n}\n\ninterface ItemData {\n id: string;\n ref: React.RefObject<ItemElement | null>;\n disabled: boolean;\n}\n\ninterface ActionBarContextValue {\n onOpenChange?: (open: boolean) => void;\n dir: Direction;\n orientation: Orientation;\n loop: boolean;\n labels: ActionBarLabels;\n}\n\nconst ActionBarContext = React.createContext<ActionBarContextValue | null>(null);\n\nfunction useActionBarContext(consumerName: string) {\n const context = React.useContext(ActionBarContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface FocusContextValue {\n tabStopId: string | null;\n onItemFocus: (tabStopId: string) => void;\n onItemShiftTab: () => void;\n onFocusableItemAdd: () => void;\n onFocusableItemRemove: () => void;\n onItemRegister: (item: ItemData) => void;\n onItemUnregister: (id: string) => void;\n getItems: () => ItemData[];\n}\n\nconst FocusContext = React.createContext<FocusContextValue | null>(null);\n\nfunction useFocusContext(consumerName: string) {\n const context = React.useContext(FocusContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`FocusProvider\\``);\n }\n return context;\n}\n\ninterface ActionBarProps extends DivProps {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n onEscapeKeyDown?: (event: KeyboardEvent) => void;\n align?: \"start\" | \"center\" | \"end\";\n alignOffset?: number;\n side?: \"top\" | \"bottom\";\n sideOffset?: number;\n portalContainer?: Element | DocumentFragment | null;\n dir?: Direction;\n orientation?: Orientation;\n loop?: boolean;\n /**\n * Chuỗi hiển thị và chuỗi a11y (tên thanh, dòng \"Đã chọn N\", tên nút đóng) — mặc định\n * `DEFAULT_ACTION_BAR_LABELS` (tiếng Việt); truyền một phần là đủ.\n *\n * ⚠ Thêm so với thượng nguồn: bản gốc không có chuỗi nào — thanh `role=\"toolbar\"` không tên và\n * nút đóng chỉ chứa `<X />` là một nút KHÔNG TÊN với trình đọc màn hình.\n */\n labels?: Partial<ActionBarLabels>;\n}\n\nfunction ActionBar(props: ActionBarProps) {\n const {\n open = false,\n onOpenChange,\n onEscapeKeyDown,\n side = \"bottom\",\n alignOffset = 0,\n align = \"center\",\n sideOffset = 16,\n portalContainer: portalContainerProp,\n dir: dirProp,\n orientation = \"horizontal\",\n loop = true,\n labels: labelsProp,\n className,\n style,\n ref,\n asChild,\n ...rootProps\n } = props;\n\n const [mounted, setMounted] = React.useState(false);\n\n const rootRef = React.useRef<RootElement>(null);\n const composedRef = useComposedRefs(ref, rootRef);\n\n const propsRef = useAsRef({\n onEscapeKeyDown,\n onOpenChange\n });\n\n const dir = useDirection(dirProp);\n\n const labels = React.useMemo<ActionBarLabels>(() => ({ ...DEFAULT_ACTION_BAR_LABELS, ...labelsProp }), [labelsProp]);\n\n // Chỉ đọc `document.body` SAU khi mount — lượt render phía server không chạm `document`.\n useIsomorphicLayoutEffect(() => {\n setMounted(true);\n }, []);\n\n React.useEffect(() => {\n if (!open) return;\n\n const ownerDocument = rootRef.current?.ownerDocument ?? document;\n\n function onKeyDown(event: KeyboardEvent) {\n /*\n * ⚠ Khác thượng nguồn: bỏ qua phím Escape ĐÃ được xử lý (`defaultPrevented`) hoặc đang gõ dở\n * bằng bộ gõ (`isComposing` — Telex/VNI qua IME hệ điều hành). Bản gốc nghe Escape ở cấp\n * `document`, nên Escape để đóng một Dialog/Popover/DropdownMenu của Radix (các lớp đó\n * `preventDefault()` khi tự đóng) cũng đóng luôn thanh này — tức XOÁ VÙNG CHỌN của người dùng\n * chỉ vì họ đóng một menu.\n */\n if (event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n\n propsRef.current.onEscapeKeyDown?.(event);\n if (!event.defaultPrevented) {\n propsRef.current.onOpenChange?.(false);\n }\n }\n\n ownerDocument.addEventListener(\"keydown\", onKeyDown);\n return () => ownerDocument.removeEventListener(\"keydown\", onKeyDown);\n }, [open, propsRef]);\n\n const contextValue = React.useMemo<ActionBarContextValue>(\n () => ({\n onOpenChange,\n dir,\n orientation,\n loop,\n labels\n }),\n [onOpenChange, dir, orientation, loop, labels]\n );\n\n const portalContainer = portalContainerProp ?? (mounted ? globalThis.document?.body : null);\n\n if (!portalContainer || !open) return null;\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ActionBarContext.Provider value={contextValue}>\n {ReactDOM.createPortal(\n <RootPrimitive\n role=\"toolbar\"\n aria-label={labels.toolbar}\n aria-orientation={orientation}\n data-slot=\"action-bar\"\n data-side={side}\n data-align={align}\n data-orientation={orientation}\n dir={dir}\n {...rootProps}\n ref={composedRef}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: mặt nổi đi qua bộ token `popover` + `shadow-overlay` như mọi lớp\n * nổi của gói (Popover, DropdownMenu). Bản gốc `bg-card shadow-lg` — `shadow-lg` là\n * bóng mặc định của Tailwind, không có bản dark của gói (xem CLAUDE.md \"Bóng\").\n */\n \"fixed z-50 rounded-lg border bg-popover text-popover-foreground shadow-overlay outline-none\",\n \"animate-in duration-250 fade-in-0 zoom-in-95 [animation-timing-function:cubic-bezier(0.16,1,0.3,1)]\",\n \"data-[side=bottom]:slide-in-from-bottom-4 data-[side=top]:slide-in-from-top-4\",\n \"motion-reduce:animate-none motion-reduce:transition-none\",\n /*\n * ⚠ Khác thượng nguồn: chặn bề rộng theo khung nhìn và cho hàng ngang XUỐNG DÒNG. Bản gốc\n * không có giới hạn nào — đo ở 375px: thanh rộng 413px, bắt đầu ở x = −19px, hai mép bị\n * cắt và \"Đã chọn 3\" vỡ thành 3 dòng. Xuống dòng chứ không cuộn ngang: vùng cuộn sẽ cắt\n * mất vòng focus của các nút ở mép.\n */\n \"max-w-[calc(100vw-1rem)]\",\n orientation === \"horizontal\"\n ? \"flex flex-row flex-wrap items-center gap-2 px-2 py-1.5\"\n : \"flex flex-col items-start gap-2 px-1.5 py-2\",\n className\n )}\n style={{\n [side]: `${sideOffset}px`,\n ...(align === \"center\" && {\n left: \"50%\",\n translate: \"-50% 0\"\n }),\n ...(align === \"start\" && { left: `${alignOffset}px` }),\n ...(align === \"end\" && { right: `${alignOffset}px` }),\n ...style\n }}\n />,\n portalContainer\n )}\n </ActionBarContext.Provider>\n );\n}\n\ninterface ActionBarSelectionProps extends DivProps {\n /**\n * Số mục đang chọn. Có giá trị thì phần tử tự in `labels.selected(count)` (\"Đã chọn 3\") TRƯỚC\n * `children` — nên `children` chỉ còn là dấu ngăn + nút đóng.\n *\n * ⚠ Thêm so với thượng nguồn: bản gốc để app tự viết chữ (\"{n} selected\"), tức chuỗi hiển thị\n * không đi qua `labels`. Bỏ qua khi `asChild` (Slot chỉ nhận đúng MỘT phần tử con).\n */\n count?: number;\n}\n\nfunction ActionBarSelection(props: ActionBarSelectionProps) {\n const { className, asChild, count, children, ...selectionProps } = props;\n\n const { labels } = useActionBarContext(SELECTION_NAME);\n\n const SelectionPrimitive = asChild ? Slot : \"div\";\n\n return (\n <SelectionPrimitive\n data-slot=\"action-bar-selection\"\n {...selectionProps}\n // ⚠ Khác thượng nguồn: `whitespace-nowrap shrink-0` — con số đếm không được vỡ dòng khi thanh hẹp.\n className={cn(\n \"flex shrink-0 items-center gap-1 whitespace-nowrap rounded-sm border px-2 py-1 text-sm font-medium tabular-nums\",\n className\n )}\n >\n {asChild ? (\n children\n ) : (\n <>\n {count !== undefined && labels.selected(count)}\n {children}\n </>\n )}\n </SelectionPrimitive>\n );\n}\n\nfunction ActionBarGroup(props: DivProps) {\n const {\n onBlur: onBlurProp,\n onFocus: onFocusProp,\n onMouseDown: onMouseDownProp,\n className,\n asChild,\n ref,\n ...groupProps\n } = props;\n\n const [tabStopId, setTabStopId] = React.useState<string | null>(null);\n const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);\n const [focusableItemCount, setFocusableItemCount] = React.useState(0);\n\n const groupRef = React.useRef<HTMLDivElement>(null);\n const composedRef = useComposedRefs(ref, groupRef);\n const isClickFocusRef = React.useRef(false);\n const itemsRef = React.useRef<Map<string, ItemData>>(new Map());\n\n const { dir, orientation } = useActionBarContext(GROUP_NAME);\n\n const onItemFocus = React.useCallback((tabStopId: string) => {\n setTabStopId(tabStopId);\n }, []);\n\n const onItemShiftTab = React.useCallback(() => {\n setIsTabbingBackOut(true);\n }, []);\n\n const onFocusableItemAdd = React.useCallback(() => {\n setFocusableItemCount((prevCount) => prevCount + 1);\n }, []);\n\n const onFocusableItemRemove = React.useCallback(() => {\n setFocusableItemCount((prevCount) => prevCount - 1);\n }, []);\n\n const onItemRegister = React.useCallback((item: ItemData) => {\n itemsRef.current.set(item.id, item);\n }, []);\n\n const onItemUnregister = React.useCallback((id: string) => {\n itemsRef.current.delete(id);\n }, []);\n\n const getItems = React.useCallback(() => {\n return Array.from(itemsRef.current.values())\n .filter((item) => item.ref.current)\n .sort((a, b) => {\n const elementA = a.ref.current;\n const elementB = b.ref.current;\n if (!elementA || !elementB) return 0;\n const position = elementA.compareDocumentPosition(elementB);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n return -1;\n }\n if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n return 1;\n }\n return 0;\n });\n }, []);\n\n const onBlur = React.useCallback(\n (event: React.FocusEvent<HTMLDivElement>) => {\n onBlurProp?.(event);\n if (event.defaultPrevented) return;\n\n setIsTabbingBackOut(false);\n },\n [onBlurProp]\n );\n\n const onFocus = React.useCallback(\n (event: React.FocusEvent<HTMLDivElement>) => {\n onFocusProp?.(event);\n if (event.defaultPrevented) return;\n\n const isKeyboardFocus = !isClickFocusRef.current;\n if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {\n const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);\n event.currentTarget.dispatchEvent(entryFocusEvent);\n\n if (!entryFocusEvent.defaultPrevented) {\n const items = Array.from(itemsRef.current.values()).filter((item) => !item.disabled);\n const currentItem = items.find((item) => item.id === tabStopId);\n\n const candidateItems = [currentItem, ...items].filter(Boolean) as ItemData[];\n const candidateRefs = candidateItems.map((item) => item.ref);\n focusFirst(candidateRefs, false);\n }\n }\n isClickFocusRef.current = false;\n },\n [onFocusProp, isTabbingBackOut, tabStopId]\n );\n\n const onMouseDown = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n onMouseDownProp?.(event);\n if (event.defaultPrevented) return;\n\n isClickFocusRef.current = true;\n },\n [onMouseDownProp]\n );\n\n const focusContextValue = React.useMemo<FocusContextValue>(\n () => ({\n tabStopId,\n onItemFocus,\n onItemShiftTab,\n onFocusableItemAdd,\n onFocusableItemRemove,\n onItemRegister,\n onItemUnregister,\n getItems\n }),\n [\n tabStopId,\n onItemFocus,\n onItemShiftTab,\n onFocusableItemAdd,\n onFocusableItemRemove,\n onItemRegister,\n onItemUnregister,\n getItems\n ]\n );\n\n const GroupPrimitive = asChild ? Slot : \"div\";\n\n return (\n <FocusContext.Provider value={focusContextValue}>\n <GroupPrimitive\n role=\"group\"\n data-slot=\"action-bar-group\"\n data-orientation={orientation}\n dir={dir}\n tabIndex={isTabbingBackOut || focusableItemCount === 0 ? -1 : 0}\n {...groupProps}\n ref={composedRef}\n className={cn(\n \"flex gap-2 outline-none\",\n orientation === \"horizontal\" ? \"items-center\" : \"w-full flex-col items-start\",\n className\n )}\n onBlur={onBlur}\n onFocus={onFocus}\n onMouseDown={onMouseDown}\n />\n </FocusContext.Provider>\n );\n}\n\ninterface ActionBarItemProps extends Omit<React.ComponentProps<typeof Button>, \"onSelect\"> {\n onSelect?: (event: Event) => void;\n}\n\nfunction ActionBarItem(props: ActionBarItemProps) {\n const {\n onSelect,\n onClick: onClickProp,\n onFocus: onFocusProp,\n onKeyDown: onKeyDownProp,\n onMouseDown: onMouseDownProp,\n className,\n disabled,\n ref,\n ...itemProps\n } = props;\n\n const itemRef = React.useRef<ItemElement>(null);\n const composedRef = useComposedRefs(ref, itemRef);\n const isMouseClickRef = React.useRef(false);\n\n const { onOpenChange, dir, orientation, loop } = useActionBarContext(ITEM_NAME);\n const focusContext = useFocusContext(ITEM_NAME);\n\n const itemId = React.useId();\n const isTabStop = focusContext.tabStopId === itemId;\n\n useIsomorphicLayoutEffect(() => {\n focusContext.onItemRegister({\n id: itemId,\n ref: itemRef,\n disabled: !!disabled\n });\n\n if (!disabled) {\n focusContext.onFocusableItemAdd();\n }\n\n return () => {\n focusContext.onItemUnregister(itemId);\n if (!disabled) {\n focusContext.onFocusableItemRemove();\n }\n };\n }, [focusContext, itemId, disabled]);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<ItemElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n const item = itemRef.current;\n if (!item) return;\n\n const itemSelectEvent = new CustomEvent(ITEM_SELECT, {\n bubbles: true,\n cancelable: true\n });\n\n item.addEventListener(ITEM_SELECT, (event) => onSelect?.(event), {\n once: true\n });\n\n item.dispatchEvent(itemSelectEvent);\n\n if (!itemSelectEvent.defaultPrevented) {\n onOpenChange?.(false);\n }\n },\n [onClickProp, onOpenChange, onSelect]\n );\n\n const onFocus = React.useCallback(\n (event: React.FocusEvent<ItemElement>) => {\n onFocusProp?.(event);\n if (event.defaultPrevented) return;\n\n focusContext.onItemFocus(itemId);\n isMouseClickRef.current = false;\n },\n [onFocusProp, focusContext, itemId]\n );\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<ItemElement>) => {\n onKeyDownProp?.(event);\n if (event.defaultPrevented) return;\n\n if (event.key === \"Tab\" && event.shiftKey) {\n focusContext.onItemShiftTab();\n return;\n }\n\n if (event.target !== event.currentTarget) return;\n\n const key = getDirectionAwareKey(event.key, dir);\n let focusIntent: \"first\" | \"last\" | \"prev\" | \"next\" | undefined;\n\n if (orientation === \"horizontal\") {\n if (key === \"ArrowLeft\") focusIntent = \"prev\";\n else if (key === \"ArrowRight\") focusIntent = \"next\";\n else if (key === \"Home\") focusIntent = \"first\";\n else if (key === \"End\") focusIntent = \"last\";\n } else {\n if (key === \"ArrowUp\") focusIntent = \"prev\";\n else if (key === \"ArrowDown\") focusIntent = \"next\";\n else if (key === \"Home\") focusIntent = \"first\";\n else if (key === \"End\") focusIntent = \"last\";\n }\n\n if (focusIntent !== undefined) {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;\n event.preventDefault();\n\n const items = focusContext.getItems().filter((item) => !item.disabled);\n let candidateRefs = items.map((item) => item.ref);\n\n if (focusIntent === \"last\") {\n candidateRefs.reverse();\n } else if (focusIntent === \"prev\" || focusIntent === \"next\") {\n if (focusIntent === \"prev\") candidateRefs.reverse();\n const currentIndex = candidateRefs.findIndex((ref) => ref.current === event.currentTarget);\n candidateRefs = loop ? wrapArray(candidateRefs, currentIndex + 1) : candidateRefs.slice(currentIndex + 1);\n }\n\n queueMicrotask(() => focusFirst(candidateRefs));\n }\n },\n [onKeyDownProp, focusContext, dir, orientation, loop]\n );\n\n const onMouseDown = React.useCallback(\n (event: React.MouseEvent<ItemElement>) => {\n onMouseDownProp?.(event);\n if (event.defaultPrevented) return;\n\n isMouseClickRef.current = true;\n\n if (disabled) {\n event.preventDefault();\n } else {\n focusContext.onItemFocus(itemId);\n }\n },\n [onMouseDownProp, focusContext, itemId, disabled]\n );\n\n return (\n <Button\n type=\"button\"\n data-slot=\"action-bar-item\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={disabled}\n tabIndex={isTabStop ? 0 : -1}\n {...itemProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: `Button` của gói KHÔNG tự đặt cỡ `svg` con (khác shadcn gốc), nên\n * `<Copy />` trần trong mục ra 24px giữa một nút cao 32px. Đặt cỡ ở đây, đúng như\n * `DropdownMenuItem`/`CommandItem` của gói — không thêm luật đó vào `Button` (CHANGELOG 2.0.0).\n */\n \"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n orientation === \"vertical\" && \"w-full\",\n className\n )}\n ref={composedRef}\n onClick={onClick}\n onFocus={onFocus}\n onKeyDown={onKeyDown}\n onMouseDown={onMouseDown}\n />\n );\n}\n\ninterface ActionBarCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\n/**\n * Nút đóng thanh (thường là \"bỏ chọn\"). Không truyền `children` thì vẽ icon X.\n *\n * ⚠ Khác thượng nguồn: có tên cho trình đọc màn hình — `aria-label` mặc định `labels.close`\n * (\"Bỏ chọn\"). Bản gốc chỉ render `children`, và khuôn dùng của chính thượng nguồn là\n * `<ActionBarClose><X /></ActionBarClose>`: một nút KHÔNG TÊN. Nếu `children` là CHỮ nhìn thấy\n * được thì truyền `aria-label` trùng với chữ đó (WCAG 2.5.3), hoặc `aria-label={undefined}`.\n */\nfunction ActionBarClose(props: ActionBarCloseProps) {\n const { asChild, className, onClick, children, ...closeProps } = props;\n\n const { onOpenChange, labels } = useActionBarContext(CLOSE_NAME);\n\n const onCloseClick = React.useCallback(\n (event: React.MouseEvent<CloseElement>) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n\n onOpenChange?.(false);\n },\n [onOpenChange, onClick]\n );\n\n const ClosePrimitive = asChild ? Slot : \"button\";\n\n return (\n <ClosePrimitive\n type=\"button\"\n aria-label={labels.close}\n data-slot=\"action-bar-close\"\n {...closeProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: cùng khuôn với nút đóng của `Dialog` — `text-muted-foreground` →\n * `hover:text-foreground`, vòng focus `ring-ring/50` có `ring-offset`. Bản gốc\n * `opacity-70 hover:opacity-100` và `focus-visible:border-ring` (nút không có viền nên\n * dòng đó không vẽ gì).\n * ⚠ Và vùng bấm: icon 14px trần là 14×14px, dưới mức 24×24 của WCAG 2.5.8. `p-[5px]` nâng\n * vùng bấm lên 24px, `-m-[5px]` trả lại đúng chừng đó để chip chứa nó không phình ra.\n */\n \"-m-[5px] rounded-xs p-[5px] text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5\",\n className\n )}\n onClick={onCloseClick}\n >\n {children ?? (asChild ? null : <X aria-hidden=\"true\" />)}\n </ClosePrimitive>\n );\n}\n\ninterface ActionBarSeparatorProps extends DivProps {\n orientation?: Orientation;\n}\n\nfunction ActionBarSeparator(props: ActionBarSeparatorProps) {\n const { orientation: orientationProp, asChild, className, ...separatorProps } = props;\n\n const context = useActionBarContext(SEPARATOR_NAME);\n const orientation = orientationProp ?? context.orientation;\n\n const SeparatorPrimitive = asChild ? Slot : \"div\";\n\n return (\n <SeparatorPrimitive\n /*\n * ⚠ Khác thượng nguồn: bỏ thuộc tính role \"separator\" và `aria-orientation` — bản gốc đặt\n * chúng CÙNG `aria-hidden=\"true\"`, tức phần tử đã ra khỏi cây a11y và hai thuộc tính kia\n * không tới được trình đọc màn hình. Để lại thì cổng tương phản đọc thuộc tính role như dấu\n * hiệu CONTROL (thanh chia `Resizable`) và bắt vạch trang trí 1px này chịu 3:1. Hướng vẫn\n * có ở `data-orientation`.\n */\n aria-hidden=\"true\"\n data-orientation={orientation}\n data-slot=\"action-bar-separator\"\n {...separatorProps}\n className={cn(\n \"bg-border in-data-[slot=action-bar-selection]:ml-0.5 in-data-[slot=action-bar-selection]:h-4 in-data-[slot=action-bar-selection]:w-px\",\n orientation === \"horizontal\" ? \"h-6 w-px\" : \"h-px w-full\",\n className\n )}\n />\n );\n}\n\nexport {\n ActionBar,\n ActionBarClose,\n type ActionBarCloseProps,\n ActionBarGroup,\n ActionBarItem,\n type ActionBarItemProps,\n type ActionBarProps,\n ActionBarSelection,\n type ActionBarSelectionProps,\n ActionBarSeparator,\n type ActionBarSeparatorProps\n};\n"],"mappings":"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","names":["tabStopId","event","ref"]}
1
+ {"version":3,"sources":["../../../src/components/ui/action-bar.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/action-bar) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n *\n * Dùng ở Comitor: thanh THAO TÁC HÀNG LOẠT nổi ở đáy màn hình khi có dòng được chọn trong\n * `DataTable` / `DataGrid` — `open={selectedIds.length > 0}`, và `onOpenChange(false)` xoá vùng chọn.\n */\n\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { useComitorLabels } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport type { ActionBarLabels } from \"./action-bar-constants.js\";\nimport { Button } from \"./button.js\";\n\nconst ROOT_NAME = \"ActionBar\";\nconst GROUP_NAME = \"ActionBarGroup\";\nconst ITEM_NAME = \"ActionBarItem\";\nconst CLOSE_NAME = \"ActionBarClose\";\nconst SELECTION_NAME = \"ActionBarSelection\";\nconst SEPARATOR_NAME = \"ActionBarSeparator\";\nconst ITEM_SELECT = \"actionbar.itemSelect\";\nconst ENTRY_FOCUS = \"actionbarFocusGroup.onEntryFocus\";\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\ntype Direction = \"ltr\" | \"rtl\";\ntype Orientation = \"horizontal\" | \"vertical\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype RootElement = React.ComponentRef<typeof ActionBar>;\ntype ItemElement = React.ComponentRef<typeof ActionBarItem>;\ntype CloseElement = React.ComponentRef<typeof ActionBarClose>;\n\nfunction focusFirst(candidates: React.RefObject<HTMLElement | null>[], preventScroll = false) {\n const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;\n for (const candidateRef of candidates) {\n const candidate = candidateRef.current;\n if (!candidate) continue;\n if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;\n candidate.focus({ preventScroll });\n if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;\n }\n}\n\nfunction wrapArray<T>(array: T[], startIndex: number) {\n return array.map<T>((_, index) => array[(startIndex + index) % array.length] as T);\n}\n\nfunction getDirectionAwareKey(key: string, dir?: Direction) {\n if (dir !== \"rtl\") return key;\n return key === \"ArrowLeft\" ? \"ArrowRight\" : key === \"ArrowRight\" ? \"ArrowLeft\" : key;\n}\n\ninterface ItemData {\n id: string;\n ref: React.RefObject<ItemElement | null>;\n disabled: boolean;\n}\n\ninterface ActionBarContextValue {\n onOpenChange?: (open: boolean) => void;\n dir: Direction;\n orientation: Orientation;\n loop: boolean;\n labels: ActionBarLabels;\n}\n\nconst ActionBarContext = React.createContext<ActionBarContextValue | null>(null);\n\nfunction useActionBarContext(consumerName: string) {\n const context = React.useContext(ActionBarContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface FocusContextValue {\n tabStopId: string | null;\n onItemFocus: (tabStopId: string) => void;\n onItemShiftTab: () => void;\n onFocusableItemAdd: () => void;\n onFocusableItemRemove: () => void;\n onItemRegister: (item: ItemData) => void;\n onItemUnregister: (id: string) => void;\n getItems: () => ItemData[];\n}\n\nconst FocusContext = React.createContext<FocusContextValue | null>(null);\n\nfunction useFocusContext(consumerName: string) {\n const context = React.useContext(FocusContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`FocusProvider\\``);\n }\n return context;\n}\n\ninterface ActionBarProps extends DivProps {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n onEscapeKeyDown?: (event: KeyboardEvent) => void;\n align?: \"start\" | \"center\" | \"end\";\n alignOffset?: number;\n side?: \"top\" | \"bottom\";\n sideOffset?: number;\n portalContainer?: Element | DocumentFragment | null;\n dir?: Direction;\n orientation?: Orientation;\n loop?: boolean;\n /**\n * Chuỗi hiển thị và chuỗi a11y (tên thanh, dòng \"N selected\", tên nút đóng). Không truyền thì\n * theo namespace `actionBar` của `ComitorLocaleProvider` (mặc định tiếng Anh); truyền một phần\n * là đủ.\n *\n * ⚠ Thêm so với thượng nguồn: bản gốc không có chuỗi nào — thanh `role=\"toolbar\"` không tên và\n * nút đóng chỉ chứa `<X />` là một nút KHÔNG TÊN với trình đọc màn hình.\n */\n labels?: Partial<ActionBarLabels>;\n}\n\nfunction ActionBar(props: ActionBarProps) {\n const {\n open = false,\n onOpenChange,\n onEscapeKeyDown,\n side = \"bottom\",\n alignOffset = 0,\n align = \"center\",\n sideOffset = 16,\n portalContainer: portalContainerProp,\n dir: dirProp,\n orientation = \"horizontal\",\n loop = true,\n labels: labelsProp,\n className,\n style,\n ref,\n asChild,\n ...rootProps\n } = props;\n\n const [mounted, setMounted] = React.useState(false);\n\n const rootRef = React.useRef<RootElement>(null);\n const composedRef = useComposedRefs(ref, rootRef);\n\n const propsRef = useAsRef({\n onEscapeKeyDown,\n onOpenChange\n });\n\n const dir = useDirection(dirProp);\n\n const labels = useComitorLabels(\"actionBar\", labelsProp);\n\n // Chỉ đọc `document.body` SAU khi mount — lượt render phía server không chạm `document`.\n useIsomorphicLayoutEffect(() => {\n setMounted(true);\n }, []);\n\n React.useEffect(() => {\n if (!open) return;\n\n const ownerDocument = rootRef.current?.ownerDocument ?? document;\n\n function onKeyDown(event: KeyboardEvent) {\n /*\n * ⚠ Khác thượng nguồn: bỏ qua phím Escape ĐÃ được xử lý (`defaultPrevented`) hoặc đang gõ dở\n * bằng bộ gõ (`isComposing` — Telex/VNI qua IME hệ điều hành). Bản gốc nghe Escape ở cấp\n * `document`, nên Escape để đóng một Dialog/Popover/DropdownMenu của Radix (các lớp đó\n * `preventDefault()` khi tự đóng) cũng đóng luôn thanh này — tức XOÁ VÙNG CHỌN của người dùng\n * chỉ vì họ đóng một menu.\n */\n if (event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n\n propsRef.current.onEscapeKeyDown?.(event);\n if (!event.defaultPrevented) {\n propsRef.current.onOpenChange?.(false);\n }\n }\n\n ownerDocument.addEventListener(\"keydown\", onKeyDown);\n return () => ownerDocument.removeEventListener(\"keydown\", onKeyDown);\n }, [open, propsRef]);\n\n const contextValue = React.useMemo<ActionBarContextValue>(\n () => ({\n onOpenChange,\n dir,\n orientation,\n loop,\n labels\n }),\n [onOpenChange, dir, orientation, loop, labels]\n );\n\n const portalContainer = portalContainerProp ?? (mounted ? globalThis.document?.body : null);\n\n if (!portalContainer || !open) return null;\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ActionBarContext.Provider value={contextValue}>\n {ReactDOM.createPortal(\n <RootPrimitive\n role=\"toolbar\"\n aria-label={labels.toolbar}\n aria-orientation={orientation}\n data-slot=\"action-bar\"\n data-side={side}\n data-align={align}\n data-orientation={orientation}\n dir={dir}\n {...rootProps}\n ref={composedRef}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: mặt nổi đi qua bộ token `popover` + `shadow-overlay` như mọi lớp\n * nổi của gói (Popover, DropdownMenu). Bản gốc `bg-card shadow-lg` — `shadow-lg` là\n * bóng mặc định của Tailwind, không có bản dark của gói (xem CLAUDE.md \"Bóng\").\n */\n \"fixed z-50 rounded-lg border bg-popover text-popover-foreground shadow-overlay outline-none\",\n \"animate-in duration-250 fade-in-0 zoom-in-95 [animation-timing-function:cubic-bezier(0.16,1,0.3,1)]\",\n \"data-[side=bottom]:slide-in-from-bottom-4 data-[side=top]:slide-in-from-top-4\",\n \"motion-reduce:animate-none motion-reduce:transition-none\",\n /*\n * ⚠ Khác thượng nguồn: chặn bề rộng theo khung nhìn và cho hàng ngang XUỐNG DÒNG. Bản gốc\n * không có giới hạn nào — đo ở 375px: thanh rộng 413px, bắt đầu ở x = −19px, hai mép bị\n * cắt và \"Đã chọn 3\" vỡ thành 3 dòng. Xuống dòng chứ không cuộn ngang: vùng cuộn sẽ cắt\n * mất vòng focus của các nút ở mép.\n */\n \"max-w-[calc(100vw-1rem)]\",\n orientation === \"horizontal\"\n ? \"flex flex-row flex-wrap items-center gap-2 px-2 py-1.5\"\n : \"flex flex-col items-start gap-2 px-1.5 py-2\",\n className\n )}\n style={{\n [side]: `${sideOffset}px`,\n ...(align === \"center\" && {\n left: \"50%\",\n translate: \"-50% 0\"\n }),\n ...(align === \"start\" && { left: `${alignOffset}px` }),\n ...(align === \"end\" && { right: `${alignOffset}px` }),\n ...style\n }}\n />,\n portalContainer\n )}\n </ActionBarContext.Provider>\n );\n}\n\ninterface ActionBarSelectionProps extends DivProps {\n /**\n * Số mục đang chọn. Có giá trị thì phần tử tự in `labels.selected(count)` (\"3 selected\") TRƯỚC\n * `children` — nên `children` chỉ còn là dấu ngăn + nút đóng.\n *\n * ⚠ Thêm so với thượng nguồn: bản gốc để app tự viết chữ (\"{n} selected\"), tức chuỗi hiển thị\n * không đi qua `labels`. Bỏ qua khi `asChild` (Slot chỉ nhận đúng MỘT phần tử con).\n */\n count?: number;\n}\n\nfunction ActionBarSelection(props: ActionBarSelectionProps) {\n const { className, asChild, count, children, ...selectionProps } = props;\n\n const { labels } = useActionBarContext(SELECTION_NAME);\n\n const SelectionPrimitive = asChild ? Slot : \"div\";\n\n return (\n <SelectionPrimitive\n data-slot=\"action-bar-selection\"\n {...selectionProps}\n // ⚠ Khác thượng nguồn: `whitespace-nowrap shrink-0` — con số đếm không được vỡ dòng khi thanh hẹp.\n className={cn(\n \"flex shrink-0 items-center gap-1 whitespace-nowrap rounded-sm border px-2 py-1 text-sm font-medium tabular-nums\",\n className\n )}\n >\n {asChild ? (\n children\n ) : (\n <>\n {count !== undefined && labels.selected(count)}\n {children}\n </>\n )}\n </SelectionPrimitive>\n );\n}\n\nfunction ActionBarGroup(props: DivProps) {\n const {\n onBlur: onBlurProp,\n onFocus: onFocusProp,\n onMouseDown: onMouseDownProp,\n className,\n asChild,\n ref,\n ...groupProps\n } = props;\n\n const [tabStopId, setTabStopId] = React.useState<string | null>(null);\n const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);\n const [focusableItemCount, setFocusableItemCount] = React.useState(0);\n\n const groupRef = React.useRef<HTMLDivElement>(null);\n const composedRef = useComposedRefs(ref, groupRef);\n const isClickFocusRef = React.useRef(false);\n const itemsRef = React.useRef<Map<string, ItemData>>(new Map());\n\n const { dir, orientation } = useActionBarContext(GROUP_NAME);\n\n const onItemFocus = React.useCallback((tabStopId: string) => {\n setTabStopId(tabStopId);\n }, []);\n\n const onItemShiftTab = React.useCallback(() => {\n setIsTabbingBackOut(true);\n }, []);\n\n const onFocusableItemAdd = React.useCallback(() => {\n setFocusableItemCount((prevCount) => prevCount + 1);\n }, []);\n\n const onFocusableItemRemove = React.useCallback(() => {\n setFocusableItemCount((prevCount) => prevCount - 1);\n }, []);\n\n const onItemRegister = React.useCallback((item: ItemData) => {\n itemsRef.current.set(item.id, item);\n }, []);\n\n const onItemUnregister = React.useCallback((id: string) => {\n itemsRef.current.delete(id);\n }, []);\n\n const getItems = React.useCallback(() => {\n return Array.from(itemsRef.current.values())\n .filter((item) => item.ref.current)\n .sort((a, b) => {\n const elementA = a.ref.current;\n const elementB = b.ref.current;\n if (!elementA || !elementB) return 0;\n const position = elementA.compareDocumentPosition(elementB);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n return -1;\n }\n if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n return 1;\n }\n return 0;\n });\n }, []);\n\n const onBlur = React.useCallback(\n (event: React.FocusEvent<HTMLDivElement>) => {\n onBlurProp?.(event);\n if (event.defaultPrevented) return;\n\n setIsTabbingBackOut(false);\n },\n [onBlurProp]\n );\n\n const onFocus = React.useCallback(\n (event: React.FocusEvent<HTMLDivElement>) => {\n onFocusProp?.(event);\n if (event.defaultPrevented) return;\n\n const isKeyboardFocus = !isClickFocusRef.current;\n if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {\n const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);\n event.currentTarget.dispatchEvent(entryFocusEvent);\n\n if (!entryFocusEvent.defaultPrevented) {\n const items = Array.from(itemsRef.current.values()).filter((item) => !item.disabled);\n const currentItem = items.find((item) => item.id === tabStopId);\n\n const candidateItems = [currentItem, ...items].filter(Boolean) as ItemData[];\n const candidateRefs = candidateItems.map((item) => item.ref);\n focusFirst(candidateRefs, false);\n }\n }\n isClickFocusRef.current = false;\n },\n [onFocusProp, isTabbingBackOut, tabStopId]\n );\n\n const onMouseDown = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n onMouseDownProp?.(event);\n if (event.defaultPrevented) return;\n\n isClickFocusRef.current = true;\n },\n [onMouseDownProp]\n );\n\n const focusContextValue = React.useMemo<FocusContextValue>(\n () => ({\n tabStopId,\n onItemFocus,\n onItemShiftTab,\n onFocusableItemAdd,\n onFocusableItemRemove,\n onItemRegister,\n onItemUnregister,\n getItems\n }),\n [\n tabStopId,\n onItemFocus,\n onItemShiftTab,\n onFocusableItemAdd,\n onFocusableItemRemove,\n onItemRegister,\n onItemUnregister,\n getItems\n ]\n );\n\n const GroupPrimitive = asChild ? Slot : \"div\";\n\n return (\n <FocusContext.Provider value={focusContextValue}>\n <GroupPrimitive\n role=\"group\"\n data-slot=\"action-bar-group\"\n data-orientation={orientation}\n dir={dir}\n tabIndex={isTabbingBackOut || focusableItemCount === 0 ? -1 : 0}\n {...groupProps}\n ref={composedRef}\n className={cn(\n \"flex gap-2 outline-none\",\n orientation === \"horizontal\" ? \"items-center\" : \"w-full flex-col items-start\",\n className\n )}\n onBlur={onBlur}\n onFocus={onFocus}\n onMouseDown={onMouseDown}\n />\n </FocusContext.Provider>\n );\n}\n\ninterface ActionBarItemProps extends Omit<React.ComponentProps<typeof Button>, \"onSelect\"> {\n onSelect?: (event: Event) => void;\n}\n\nfunction ActionBarItem(props: ActionBarItemProps) {\n const {\n onSelect,\n onClick: onClickProp,\n onFocus: onFocusProp,\n onKeyDown: onKeyDownProp,\n onMouseDown: onMouseDownProp,\n className,\n disabled,\n ref,\n ...itemProps\n } = props;\n\n const itemRef = React.useRef<ItemElement>(null);\n const composedRef = useComposedRefs(ref, itemRef);\n const isMouseClickRef = React.useRef(false);\n\n const { onOpenChange, dir, orientation, loop } = useActionBarContext(ITEM_NAME);\n const focusContext = useFocusContext(ITEM_NAME);\n\n const itemId = React.useId();\n const isTabStop = focusContext.tabStopId === itemId;\n\n useIsomorphicLayoutEffect(() => {\n focusContext.onItemRegister({\n id: itemId,\n ref: itemRef,\n disabled: !!disabled\n });\n\n if (!disabled) {\n focusContext.onFocusableItemAdd();\n }\n\n return () => {\n focusContext.onItemUnregister(itemId);\n if (!disabled) {\n focusContext.onFocusableItemRemove();\n }\n };\n }, [focusContext, itemId, disabled]);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<ItemElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n const item = itemRef.current;\n if (!item) return;\n\n const itemSelectEvent = new CustomEvent(ITEM_SELECT, {\n bubbles: true,\n cancelable: true\n });\n\n item.addEventListener(ITEM_SELECT, (event) => onSelect?.(event), {\n once: true\n });\n\n item.dispatchEvent(itemSelectEvent);\n\n if (!itemSelectEvent.defaultPrevented) {\n onOpenChange?.(false);\n }\n },\n [onClickProp, onOpenChange, onSelect]\n );\n\n const onFocus = React.useCallback(\n (event: React.FocusEvent<ItemElement>) => {\n onFocusProp?.(event);\n if (event.defaultPrevented) return;\n\n focusContext.onItemFocus(itemId);\n isMouseClickRef.current = false;\n },\n [onFocusProp, focusContext, itemId]\n );\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<ItemElement>) => {\n onKeyDownProp?.(event);\n if (event.defaultPrevented) return;\n\n if (event.key === \"Tab\" && event.shiftKey) {\n focusContext.onItemShiftTab();\n return;\n }\n\n if (event.target !== event.currentTarget) return;\n\n const key = getDirectionAwareKey(event.key, dir);\n let focusIntent: \"first\" | \"last\" | \"prev\" | \"next\" | undefined;\n\n if (orientation === \"horizontal\") {\n if (key === \"ArrowLeft\") focusIntent = \"prev\";\n else if (key === \"ArrowRight\") focusIntent = \"next\";\n else if (key === \"Home\") focusIntent = \"first\";\n else if (key === \"End\") focusIntent = \"last\";\n } else {\n if (key === \"ArrowUp\") focusIntent = \"prev\";\n else if (key === \"ArrowDown\") focusIntent = \"next\";\n else if (key === \"Home\") focusIntent = \"first\";\n else if (key === \"End\") focusIntent = \"last\";\n }\n\n if (focusIntent !== undefined) {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;\n event.preventDefault();\n\n const items = focusContext.getItems().filter((item) => !item.disabled);\n let candidateRefs = items.map((item) => item.ref);\n\n if (focusIntent === \"last\") {\n candidateRefs.reverse();\n } else if (focusIntent === \"prev\" || focusIntent === \"next\") {\n if (focusIntent === \"prev\") candidateRefs.reverse();\n const currentIndex = candidateRefs.findIndex((ref) => ref.current === event.currentTarget);\n candidateRefs = loop ? wrapArray(candidateRefs, currentIndex + 1) : candidateRefs.slice(currentIndex + 1);\n }\n\n queueMicrotask(() => focusFirst(candidateRefs));\n }\n },\n [onKeyDownProp, focusContext, dir, orientation, loop]\n );\n\n const onMouseDown = React.useCallback(\n (event: React.MouseEvent<ItemElement>) => {\n onMouseDownProp?.(event);\n if (event.defaultPrevented) return;\n\n isMouseClickRef.current = true;\n\n if (disabled) {\n event.preventDefault();\n } else {\n focusContext.onItemFocus(itemId);\n }\n },\n [onMouseDownProp, focusContext, itemId, disabled]\n );\n\n return (\n <Button\n type=\"button\"\n data-slot=\"action-bar-item\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={disabled}\n tabIndex={isTabStop ? 0 : -1}\n {...itemProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: `Button` của gói KHÔNG tự đặt cỡ `svg` con (khác shadcn gốc), nên\n * `<Copy />` trần trong mục ra 24px giữa một nút cao 32px. Đặt cỡ ở đây, đúng như\n * `DropdownMenuItem`/`CommandItem` của gói — không thêm luật đó vào `Button` (CHANGELOG 2.0.0).\n */\n \"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n orientation === \"vertical\" && \"w-full\",\n className\n )}\n ref={composedRef}\n onClick={onClick}\n onFocus={onFocus}\n onKeyDown={onKeyDown}\n onMouseDown={onMouseDown}\n />\n );\n}\n\ninterface ActionBarCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\n/**\n * Nút đóng thanh (thường là \"bỏ chọn\"). Không truyền `children` thì vẽ icon X.\n *\n * ⚠ Khác thượng nguồn: có tên cho trình đọc màn hình — `aria-label` mặc định `labels.close`\n * (\"Clear selection\"). Bản gốc chỉ render `children`, và khuôn dùng của chính thượng nguồn là\n * `<ActionBarClose><X /></ActionBarClose>`: một nút KHÔNG TÊN. Nếu `children` là CHỮ nhìn thấy\n * được thì truyền `aria-label` trùng với chữ đó (WCAG 2.5.3), hoặc `aria-label={undefined}`.\n */\nfunction ActionBarClose(props: ActionBarCloseProps) {\n const { asChild, className, onClick, children, ...closeProps } = props;\n\n const { onOpenChange, labels } = useActionBarContext(CLOSE_NAME);\n\n const onCloseClick = React.useCallback(\n (event: React.MouseEvent<CloseElement>) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n\n onOpenChange?.(false);\n },\n [onOpenChange, onClick]\n );\n\n const ClosePrimitive = asChild ? Slot : \"button\";\n\n return (\n <ClosePrimitive\n type=\"button\"\n aria-label={labels.close}\n data-slot=\"action-bar-close\"\n {...closeProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: cùng khuôn với nút đóng của `Dialog` — `text-muted-foreground` →\n * `hover:text-foreground`, vòng focus `ring-ring/50` có `ring-offset`. Bản gốc\n * `opacity-70 hover:opacity-100` và `focus-visible:border-ring` (nút không có viền nên\n * dòng đó không vẽ gì).\n * ⚠ Và vùng bấm: icon 14px trần là 14×14px, dưới mức 24×24 của WCAG 2.5.8. `p-[5px]` nâng\n * vùng bấm lên 24px, `-m-[5px]` trả lại đúng chừng đó để chip chứa nó không phình ra.\n */\n \"-m-[5px] rounded-xs p-[5px] text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5\",\n className\n )}\n onClick={onCloseClick}\n >\n {children ?? (asChild ? null : <X aria-hidden=\"true\" />)}\n </ClosePrimitive>\n );\n}\n\ninterface ActionBarSeparatorProps extends DivProps {\n orientation?: Orientation;\n}\n\nfunction ActionBarSeparator(props: ActionBarSeparatorProps) {\n const { orientation: orientationProp, asChild, className, ...separatorProps } = props;\n\n const context = useActionBarContext(SEPARATOR_NAME);\n const orientation = orientationProp ?? context.orientation;\n\n const SeparatorPrimitive = asChild ? Slot : \"div\";\n\n return (\n <SeparatorPrimitive\n /*\n * ⚠ Khác thượng nguồn: bỏ thuộc tính role \"separator\" và `aria-orientation` — bản gốc đặt\n * chúng CÙNG `aria-hidden=\"true\"`, tức phần tử đã ra khỏi cây a11y và hai thuộc tính kia\n * không tới được trình đọc màn hình. Để lại thì cổng tương phản đọc thuộc tính role như dấu\n * hiệu CONTROL (thanh chia `Resizable`) và bắt vạch trang trí 1px này chịu 3:1. Hướng vẫn\n * có ở `data-orientation`.\n */\n aria-hidden=\"true\"\n data-orientation={orientation}\n data-slot=\"action-bar-separator\"\n {...separatorProps}\n className={cn(\n \"bg-border in-data-[slot=action-bar-selection]:ml-0.5 in-data-[slot=action-bar-selection]:h-4 in-data-[slot=action-bar-selection]:w-px\",\n orientation === \"horizontal\" ? \"h-6 w-px\" : \"h-px w-full\",\n className\n )}\n />\n );\n}\n\nexport {\n ActionBar,\n ActionBarClose,\n type ActionBarCloseProps,\n ActionBarGroup,\n ActionBarItem,\n type ActionBarItemProps,\n type ActionBarProps,\n ActionBarSelection,\n type ActionBarSelectionProps,\n ActionBarSeparator,\n type ActionBarSeparatorProps\n};\n"],"mappings":"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","names":["tabStopId","event","ref"]}
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
3
3
  import * as React from 'react';
4
4
 
5
5
  declare const alertVariants: (props?: ({
6
- variant?: "default" | "warning" | "destructive" | null | undefined;
6
+ variant?: "default" | "destructive" | "warning" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  interface AlertProps extends React.ComponentProps<"div">, VariantProps<typeof alertVariants> {
9
9
  }
@@ -14,8 +14,8 @@ type BadgeOverflowProps<T = string> = React.ComponentProps<"div"> & (T extends o
14
14
  renderBadge: (item: T, label: string) => React.ReactNode;
15
15
  renderOverflow?: (count: number) => React.ReactNode;
16
16
  /**
17
- * Câu trình đọc màn hình đọc thay cho chữ "+N" của ô tràn MẶC ĐỊNH — mặc định
18
- * `(count) => "và " + count + " mục khác"`.
17
+ * Câu trình đọc màn hình đọc thay cho chữ "+N" của ô tràn MẶC ĐỊNH — mặc định khoá
18
+ * `overflowLabel` của namespace `badgeOverflow` (`ComitorLocaleProvider`).
19
19
  *
20
20
  * ⚠ Thêm so với thượng nguồn: "+3" đọc lên thành "cộng ba" — không nói ba CÁI GÌ. Chữ "+N"
21
21
  * vẫn hiện nhưng ẩn khỏi trình đọc màn hình; câu này nằm trong `sr-only` ngay cạnh. Là HÀM
@@ -2,11 +2,11 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Slot } from "@radix-ui/react-slot";
4
4
  import * as React from "react";
5
+ import { useComitorMessages } from "../../i18n/locale-provider.js";
5
6
  import { cn } from "../../lib/cn.js";
6
7
  import { useComposedRefs } from "../../lib/compose-refs.js";
7
8
  import { useIsomorphicLayoutEffect } from "../../lib/use-isomorphic-layout-effect.js";
8
9
  import { Badge } from "./badge.js";
9
- const defaultOverflowLabel = (count) => `v\xE0 ${count} m\u1EE5c kh\xE1c`;
10
10
  function BadgeOverflow(props) {
11
11
  const {
12
12
  items,
@@ -14,13 +14,15 @@ function BadgeOverflow(props) {
14
14
  lineCount = 1,
15
15
  renderBadge,
16
16
  renderOverflow,
17
- overflowLabel = defaultOverflowLabel,
17
+ overflowLabel: overflowLabelProp,
18
18
  asChild,
19
19
  className,
20
20
  style,
21
21
  ref,
22
22
  ...rootProps
23
23
  } = props;
24
+ const t = useComitorMessages("badgeOverflow");
25
+ const overflowLabel = overflowLabelProp ?? t.overflowLabel;
24
26
  const getBadgeLabel = React.useCallback(
25
27
  (item) => {
26
28
  if (typeof item === "object" && !getBadgeLabelProp) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/badge-overflow.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/badge-overflow) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n *\n * ⚠ Cố ý KHÔNG dùng `src/data-grid/use-badge-overflow.ts` (biến thể của tablecn): tầng\n * `data-grid` là entry riêng kéo theo peer `@tanstack/react-table`, entry chính không được chạm.\n */\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { Badge } from \"./badge.js\";\n\ninterface GetBadgeLabel<T> {\n /**\n * Trả về chuỗi nhãn của mỗi badge.\n * Không bắt buộc với mảng giá trị nguyên thuỷ (chuỗi, số); BẮT BUỘC với mảng object.\n * @example getBadgeLabel={(item) => item.name}\n */\n getBadgeLabel: (item: T) => string;\n}\n\ntype BadgeOverflowElement = React.ComponentRef<typeof BadgeOverflow>;\n\ntype BadgeOverflowProps<T = string> = React.ComponentProps<\"div\"> &\n (T extends object ? GetBadgeLabel<T> : Partial<GetBadgeLabel<T>>) & {\n items: T[];\n lineCount?: number;\n renderBadge: (item: T, label: string) => React.ReactNode;\n renderOverflow?: (count: number) => React.ReactNode;\n /**\n * Câu trình đọc màn hình đọc thay cho chữ \"+N\" của ô tràn MẶC ĐỊNH — mặc định\n * `(count) => \"và \" + count + \" mục khác\"`.\n *\n * ⚠ Thêm so với thượng nguồn: \"+3\" đọc lên thành \"cộng ba\" — không nói ba CÁI GÌ. Chữ \"+N\"\n * vẫn hiện nhưng ẩn khỏi trình đọc màn hình; câu này nằm trong `sr-only` ngay cạnh. Là HÀM\n * vì có số chèn vào giữa câu. Không áp dụng khi truyền `renderOverflow` — khi đó nội dung ô\n * tràn (kể cả phần cho trình đọc màn hình) do app tự quyết.\n */\n overflowLabel?: (count: number) => string;\n asChild?: boolean;\n };\n\nconst defaultOverflowLabel = (count: number) => `và ${count} mục khác`;\n\nfunction BadgeOverflow<T = string>(props: BadgeOverflowProps<T>) {\n const {\n items,\n getBadgeLabel: getBadgeLabelProp,\n lineCount = 1,\n renderBadge,\n renderOverflow,\n overflowLabel = defaultOverflowLabel,\n asChild,\n className,\n style,\n ref,\n ...rootProps\n } = props;\n\n const getBadgeLabel = React.useCallback(\n (item: T): string => {\n if (typeof item === \"object\" && !getBadgeLabelProp) {\n throw new Error(\"`getBadgeLabel` là bắt buộc khi phần tử là object\");\n }\n return getBadgeLabelProp ? getBadgeLabelProp(item) : (item as string);\n },\n [getBadgeLabelProp]\n );\n\n const rootRef = React.useRef<BadgeOverflowElement | null>(null);\n const composedRef = useComposedRefs(ref, rootRef);\n const measureRef = React.useRef<HTMLDivElement | null>(null);\n const [containerWidth, setContainerWidth] = React.useState(0);\n const [badgeGap, setBadgeGap] = React.useState(4);\n const [badgeHeight, setBadgeHeight] = React.useState(20);\n const [overflowBadgeWidth, setOverflowBadgeWidth] = React.useState(40);\n const [isMeasured, setIsMeasured] = React.useState(false);\n const [badgeWidths, setBadgeWidths] = React.useState<Map<string, number>>(new Map());\n\n // ⚠ Khác thượng nguồn: `useIsomorphicLayoutEffect` thay `React.useLayoutEffect` — an toàn khi SSR.\n useIsomorphicLayoutEffect(() => {\n if (!rootRef.current || !measureRef.current) return;\n\n function measureContainer() {\n if (!rootRef.current || !measureRef.current) return;\n\n const computedStyle = getComputedStyle(rootRef.current);\n\n const gapValue = computedStyle.gap;\n const gap = gapValue ? parseFloat(gapValue) : 4;\n setBadgeGap(gap);\n\n const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;\n const paddingRight = parseFloat(computedStyle.paddingRight) || 0;\n const totalPadding = paddingLeft + paddingRight;\n\n const widthMap = new Map<string, number>();\n const measureChildren = measureRef.current.children;\n\n for (let i = 0; i < items.length; i++) {\n const child = measureChildren[i] as HTMLElement | undefined;\n if (child) {\n const label = getBadgeLabel(items[i] as T);\n widthMap.set(label, child.offsetWidth);\n }\n }\n setBadgeWidths(widthMap);\n\n const firstBadge = measureChildren[0] as HTMLElement | undefined;\n if (firstBadge) {\n setBadgeHeight(firstBadge.offsetHeight || 20);\n }\n\n const overflowChild = measureChildren[items.length] as HTMLElement | undefined;\n\n if (overflowChild) {\n setOverflowBadgeWidth(overflowChild.offsetWidth || 40);\n }\n\n const width = rootRef.current.clientWidth - totalPadding;\n setContainerWidth(width);\n setIsMeasured(true);\n }\n\n measureContainer();\n\n const resizeObserver = new ResizeObserver(measureContainer);\n resizeObserver.observe(rootRef.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [items, getBadgeLabel]);\n\n const placeholderHeight = React.useMemo(\n () => badgeHeight * lineCount + badgeGap * (lineCount - 1),\n [badgeHeight, badgeGap, lineCount]\n );\n\n const { visibleItems, hiddenCount } = React.useMemo(() => {\n if (!containerWidth || items.length === 0 || badgeWidths.size === 0) {\n return { visibleItems: items, hiddenCount: 0 };\n }\n\n let currentLineWidth = 0;\n let currentLine = 1;\n const visible: T[] = [];\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (!item) continue;\n\n const label = getBadgeLabel(item);\n const badgeWidth = badgeWidths.get(label);\n\n if (!badgeWidth) {\n // Bỏ qua mục chưa đo được.\n continue;\n }\n\n const widthWithGap = badgeWidth + badgeGap;\n const isLastLine = currentLine === lineCount;\n const hasMoreItems = i < items.length - 1;\n\n const availableWidth =\n isLastLine && hasMoreItems ? containerWidth - overflowBadgeWidth - badgeGap : containerWidth;\n\n if (currentLineWidth + widthWithGap <= availableWidth) {\n currentLineWidth += widthWithGap;\n visible.push(item);\n } else if (currentLine < lineCount) {\n currentLine++;\n currentLineWidth = widthWithGap;\n visible.push(item);\n } else {\n // Đang ở dòng cuối và badge này không vừa.\n break;\n }\n }\n\n return {\n visibleItems: visible,\n hiddenCount: Math.max(0, items.length - visible.length)\n };\n }, [items, getBadgeLabel, containerWidth, lineCount, badgeGap, overflowBadgeWidth, badgeWidths]);\n\n /*\n * ⚠ Khác thượng nguồn: ô tràn mặc định là `Badge variant=\"outline\"` của gói (cùng khuôn \"+N\"\n * với `Combobox`) thay cho một `div` tự vẽ viền. Bản gốc chỉ viết `border` trần, mà ở gói này\n * `border` trần nhận viền TRANG TRÍ 1,16:1 — ô \"+N\" nền trong suốt sẽ gần như tàng hình. Biến\n * thể `outline` dùng `border-control-edge` (3,83:1 light · 4,94:1 dark) và cao bằng `Badge`\n * mà app gần như chắc chắn dùng trong `renderBadge`.\n *\n * Chữ \"+N\" ẩn khỏi trình đọc màn hình, câu `overflowLabel` đọc thay — xem prop đó.\n */\n const renderDefaultOverflow = (count: number) => (\n <Badge variant=\"outline\" className=\"shrink-0\">\n <span aria-hidden=\"true\">+{count}</span>\n <span className=\"sr-only\">{overflowLabel(count)}</span>\n </Badge>\n );\n\n const Comp = asChild ? Slot : \"div\";\n\n return (\n <>\n <div ref={measureRef} className=\"pointer-events-none invisible absolute flex flex-wrap\" style={{ gap: badgeGap }}>\n {items.map((item, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: thượng nguồn khoá theo chỉ số — nhãn có thể trùng nhau\n <React.Fragment key={index}>{renderBadge(item, getBadgeLabel(item))}</React.Fragment>\n ))}\n {renderOverflow ? renderOverflow(99) : renderDefaultOverflow(99)}\n </div>\n {isMeasured ? (\n <Comp\n data-slot=\"badge-overflow\"\n {...rootProps}\n ref={composedRef}\n className={cn(\"flex flex-wrap\", className)}\n style={{\n gap: badgeGap,\n ...style\n }}\n >\n {visibleItems.map((item, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: thượng nguồn khoá theo chỉ số — nhãn có thể trùng nhau\n <React.Fragment key={index}>{renderBadge(item, getBadgeLabel(item))}</React.Fragment>\n ))}\n {hiddenCount > 0 && (renderOverflow ? renderOverflow(hiddenCount) : renderDefaultOverflow(hiddenCount))}\n </Comp>\n ) : (\n <Comp\n data-slot=\"badge-overflow\"\n {...rootProps}\n ref={composedRef}\n className={cn(\"flex flex-wrap\", className)}\n style={{\n gap: badgeGap,\n minHeight: placeholderHeight,\n ...style\n }}\n >\n {items.slice(0, Math.min(items.length, lineCount * 3 - (lineCount > 1 ? 1 : 0))).map((item, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: thượng nguồn khoá theo chỉ số — nhãn có thể trùng nhau\n <React.Fragment key={index}>{renderBadge(item, getBadgeLabel(item))}</React.Fragment>\n ))}\n </Comp>\n )}\n </>\n );\n}\n\nexport { BadgeOverflow, type BadgeOverflowProps };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/badge-overflow.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/badge-overflow) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n *\n * ⚠ Cố ý KHÔNG dùng `src/data-grid/use-badge-overflow.ts` (biến thể của tablecn): tầng\n * `data-grid` là entry riêng kéo theo peer `@tanstack/react-table`, entry chính không được chạm.\n */\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { useComitorMessages } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { Badge } from \"./badge.js\";\n\ninterface GetBadgeLabel<T> {\n /**\n * Trả về chuỗi nhãn của mỗi badge.\n * Không bắt buộc với mảng giá trị nguyên thuỷ (chuỗi, số); BẮT BUỘC với mảng object.\n * @example getBadgeLabel={(item) => item.name}\n */\n getBadgeLabel: (item: T) => string;\n}\n\ntype BadgeOverflowElement = React.ComponentRef<typeof BadgeOverflow>;\n\ntype BadgeOverflowProps<T = string> = React.ComponentProps<\"div\"> &\n (T extends object ? GetBadgeLabel<T> : Partial<GetBadgeLabel<T>>) & {\n items: T[];\n lineCount?: number;\n renderBadge: (item: T, label: string) => React.ReactNode;\n renderOverflow?: (count: number) => React.ReactNode;\n /**\n * Câu trình đọc màn hình đọc thay cho chữ \"+N\" của ô tràn MẶC ĐỊNH — mặc định khoá\n * `overflowLabel` của namespace `badgeOverflow` (`ComitorLocaleProvider`).\n *\n * ⚠ Thêm so với thượng nguồn: \"+3\" đọc lên thành \"cộng ba\" — không nói ba CÁI GÌ. Chữ \"+N\"\n * vẫn hiện nhưng ẩn khỏi trình đọc màn hình; câu này nằm trong `sr-only` ngay cạnh. Là HÀM\n * vì có số chèn vào giữa câu. Không áp dụng khi truyền `renderOverflow` — khi đó nội dung ô\n * tràn (kể cả phần cho trình đọc màn hình) do app tự quyết.\n */\n overflowLabel?: (count: number) => string;\n asChild?: boolean;\n };\n\nfunction BadgeOverflow<T = string>(props: BadgeOverflowProps<T>) {\n const {\n items,\n getBadgeLabel: getBadgeLabelProp,\n lineCount = 1,\n renderBadge,\n renderOverflow,\n overflowLabel: overflowLabelProp,\n asChild,\n className,\n style,\n ref,\n ...rootProps\n } = props;\n\n const t = useComitorMessages(\"badgeOverflow\");\n const overflowLabel = overflowLabelProp ?? t.overflowLabel;\n\n const getBadgeLabel = React.useCallback(\n (item: T): string => {\n if (typeof item === \"object\" && !getBadgeLabelProp) {\n throw new Error(\"`getBadgeLabel` là bắt buộc khi phần tử là object\");\n }\n return getBadgeLabelProp ? getBadgeLabelProp(item) : (item as string);\n },\n [getBadgeLabelProp]\n );\n\n const rootRef = React.useRef<BadgeOverflowElement | null>(null);\n const composedRef = useComposedRefs(ref, rootRef);\n const measureRef = React.useRef<HTMLDivElement | null>(null);\n const [containerWidth, setContainerWidth] = React.useState(0);\n const [badgeGap, setBadgeGap] = React.useState(4);\n const [badgeHeight, setBadgeHeight] = React.useState(20);\n const [overflowBadgeWidth, setOverflowBadgeWidth] = React.useState(40);\n const [isMeasured, setIsMeasured] = React.useState(false);\n const [badgeWidths, setBadgeWidths] = React.useState<Map<string, number>>(new Map());\n\n // ⚠ Khác thượng nguồn: `useIsomorphicLayoutEffect` thay `React.useLayoutEffect` — an toàn khi SSR.\n useIsomorphicLayoutEffect(() => {\n if (!rootRef.current || !measureRef.current) return;\n\n function measureContainer() {\n if (!rootRef.current || !measureRef.current) return;\n\n const computedStyle = getComputedStyle(rootRef.current);\n\n const gapValue = computedStyle.gap;\n const gap = gapValue ? parseFloat(gapValue) : 4;\n setBadgeGap(gap);\n\n const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;\n const paddingRight = parseFloat(computedStyle.paddingRight) || 0;\n const totalPadding = paddingLeft + paddingRight;\n\n const widthMap = new Map<string, number>();\n const measureChildren = measureRef.current.children;\n\n for (let i = 0; i < items.length; i++) {\n const child = measureChildren[i] as HTMLElement | undefined;\n if (child) {\n const label = getBadgeLabel(items[i] as T);\n widthMap.set(label, child.offsetWidth);\n }\n }\n setBadgeWidths(widthMap);\n\n const firstBadge = measureChildren[0] as HTMLElement | undefined;\n if (firstBadge) {\n setBadgeHeight(firstBadge.offsetHeight || 20);\n }\n\n const overflowChild = measureChildren[items.length] as HTMLElement | undefined;\n\n if (overflowChild) {\n setOverflowBadgeWidth(overflowChild.offsetWidth || 40);\n }\n\n const width = rootRef.current.clientWidth - totalPadding;\n setContainerWidth(width);\n setIsMeasured(true);\n }\n\n measureContainer();\n\n const resizeObserver = new ResizeObserver(measureContainer);\n resizeObserver.observe(rootRef.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [items, getBadgeLabel]);\n\n const placeholderHeight = React.useMemo(\n () => badgeHeight * lineCount + badgeGap * (lineCount - 1),\n [badgeHeight, badgeGap, lineCount]\n );\n\n const { visibleItems, hiddenCount } = React.useMemo(() => {\n if (!containerWidth || items.length === 0 || badgeWidths.size === 0) {\n return { visibleItems: items, hiddenCount: 0 };\n }\n\n let currentLineWidth = 0;\n let currentLine = 1;\n const visible: T[] = [];\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (!item) continue;\n\n const label = getBadgeLabel(item);\n const badgeWidth = badgeWidths.get(label);\n\n if (!badgeWidth) {\n // Bỏ qua mục chưa đo được.\n continue;\n }\n\n const widthWithGap = badgeWidth + badgeGap;\n const isLastLine = currentLine === lineCount;\n const hasMoreItems = i < items.length - 1;\n\n const availableWidth =\n isLastLine && hasMoreItems ? containerWidth - overflowBadgeWidth - badgeGap : containerWidth;\n\n if (currentLineWidth + widthWithGap <= availableWidth) {\n currentLineWidth += widthWithGap;\n visible.push(item);\n } else if (currentLine < lineCount) {\n currentLine++;\n currentLineWidth = widthWithGap;\n visible.push(item);\n } else {\n // Đang ở dòng cuối và badge này không vừa.\n break;\n }\n }\n\n return {\n visibleItems: visible,\n hiddenCount: Math.max(0, items.length - visible.length)\n };\n }, [items, getBadgeLabel, containerWidth, lineCount, badgeGap, overflowBadgeWidth, badgeWidths]);\n\n /*\n * ⚠ Khác thượng nguồn: ô tràn mặc định là `Badge variant=\"outline\"` của gói (cùng khuôn \"+N\"\n * với `Combobox`) thay cho một `div` tự vẽ viền. Bản gốc chỉ viết `border` trần, mà ở gói này\n * `border` trần nhận viền TRANG TRÍ 1,16:1 — ô \"+N\" nền trong suốt sẽ gần như tàng hình. Biến\n * thể `outline` dùng `border-control-edge` (3,83:1 light · 4,94:1 dark) và cao bằng `Badge`\n * mà app gần như chắc chắn dùng trong `renderBadge`.\n *\n * Chữ \"+N\" ẩn khỏi trình đọc màn hình, câu `overflowLabel` đọc thay — xem prop đó.\n */\n const renderDefaultOverflow = (count: number) => (\n <Badge variant=\"outline\" className=\"shrink-0\">\n <span aria-hidden=\"true\">+{count}</span>\n <span className=\"sr-only\">{overflowLabel(count)}</span>\n </Badge>\n );\n\n const Comp = asChild ? Slot : \"div\";\n\n return (\n <>\n <div ref={measureRef} className=\"pointer-events-none invisible absolute flex flex-wrap\" style={{ gap: badgeGap }}>\n {items.map((item, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: thượng nguồn khoá theo chỉ số — nhãn có thể trùng nhau\n <React.Fragment key={index}>{renderBadge(item, getBadgeLabel(item))}</React.Fragment>\n ))}\n {renderOverflow ? renderOverflow(99) : renderDefaultOverflow(99)}\n </div>\n {isMeasured ? (\n <Comp\n data-slot=\"badge-overflow\"\n {...rootProps}\n ref={composedRef}\n className={cn(\"flex flex-wrap\", className)}\n style={{\n gap: badgeGap,\n ...style\n }}\n >\n {visibleItems.map((item, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: thượng nguồn khoá theo chỉ số — nhãn có thể trùng nhau\n <React.Fragment key={index}>{renderBadge(item, getBadgeLabel(item))}</React.Fragment>\n ))}\n {hiddenCount > 0 && (renderOverflow ? renderOverflow(hiddenCount) : renderDefaultOverflow(hiddenCount))}\n </Comp>\n ) : (\n <Comp\n data-slot=\"badge-overflow\"\n {...rootProps}\n ref={composedRef}\n className={cn(\"flex flex-wrap\", className)}\n style={{\n gap: badgeGap,\n minHeight: placeholderHeight,\n ...style\n }}\n >\n {items.slice(0, Math.min(items.length, lineCount * 3 - (lineCount > 1 ? 1 : 0))).map((item, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: thượng nguồn khoá theo chỉ số — nhãn có thể trùng nhau\n <React.Fragment key={index}>{renderBadge(item, getBadgeLabel(item))}</React.Fragment>\n ))}\n </Comp>\n )}\n </>\n );\n}\n\nexport { BadgeOverflow, type BadgeOverflowProps };\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
3
3
  import * as React from 'react';
4
4
 
5
5
  declare const badgeVariants: (props?: ({
6
- variant?: "default" | "primary" | "secondary" | "outline" | "success" | "warning" | "destructive" | null | undefined;
6
+ variant?: "default" | "destructive" | "warning" | "success" | "primary" | "secondary" | "outline" | null | undefined;
7
7
  size?: "sm" | "md" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  interface BadgeProps extends React.ComponentProps<"span">, VariantProps<typeof badgeVariants> {
@@ -48,7 +48,7 @@ declare function useBanners(): {
48
48
  banners: BannerData[];
49
49
  };
50
50
  declare const bannerVariants: (props?: ({
51
- variant?: "default" | "success" | "warning" | "destructive" | "info" | null | undefined;
51
+ variant?: "default" | "destructive" | "warning" | "success" | "info" | null | undefined;
52
52
  } & class_variance_authority_types.ClassProp) | undefined) => string;
53
53
  interface BannerProps extends DivProps, VariantProps<typeof bannerVariants> {
54
54
  open?: boolean;
@@ -69,10 +69,11 @@ type BannerCloseProps = React.ComponentProps<typeof Button>;
69
69
  /**
70
70
  * Nút đóng banner. Không truyền `children` thì vẽ icon X.
71
71
  *
72
- * ⚠ Khác thượng nguồn: có tên cho trình đọc màn hình — `aria-label` mặc định "Đóng thông báo",
73
- * ghi đè bằng chính prop `aria-label`. Bản gốc là `Button` chỉ chứa icon X, tức một nút KHÔNG
74
- * TÊN. Nếu `children` là CHỮ nhìn thấy được thì truyền `aria-label` trùng với chữ đó (WCAG
75
- * 2.5.3), hoặc `aria-label={undefined}`.
72
+ * ⚠ Khác thượng nguồn: có tên cho trình đọc màn hình — `aria-label` mặc định lấy khoá
73
+ * `closeLabel` của namespace `banner` (`ComitorLocaleProvider`), ghi đè bằng chính prop
74
+ * `aria-label`. Bản gốc là `Button` chỉ chứa icon X, tức một nút KHÔNG TÊN. Nếu `children` là
75
+ * CHỮ nhìn thấy được thì truyền `aria-label` trùng với chữ đó (WCAG 2.5.3), hoặc
76
+ * `aria-label={undefined}`.
76
77
  */
77
78
  declare function BannerClose(props: BannerCloseProps): React.JSX.Element;
78
79
 
@@ -5,6 +5,7 @@ import { cva } from "class-variance-authority";
5
5
  import { X } from "lucide-react";
6
6
  import * as React from "react";
7
7
  import * as ReactDOM from "react-dom";
8
+ import { useComitorMessages } from "../../i18n/locale-provider.js";
8
9
  import { cn } from "../../lib/cn.js";
9
10
  import { useAsRef } from "../../lib/use-as-ref.js";
10
11
  import { useIsomorphicLayoutEffect } from "../../lib/use-isomorphic-layout-effect.js";
@@ -480,6 +481,7 @@ function BannerActions(props) {
480
481
  function BannerClose(props) {
481
482
  const { onClick: onClickProp, disabled, children, ...closeProps } = props;
482
483
  const { dismissible = DEFAULT_BANNER_DISMISSIBLE, onClose } = useBannerContext("BannerClose");
484
+ const t = useComitorMessages("banner");
483
485
  const isDisabled = disabled ?? !dismissible;
484
486
  const onClick = React.useCallback(
485
487
  (event) => {
@@ -492,7 +494,7 @@ function BannerClose(props) {
492
494
  return /* @__PURE__ */ jsx(
493
495
  Button,
494
496
  {
495
- "aria-label": "\u0110\xF3ng th\xF4ng b\xE1o",
497
+ "aria-label": t.closeLabel,
496
498
  "data-slot": "banner-close",
497
499
  variant: "ghost",
498
500
  size: "icon-sm",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/banner.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/banner) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n *\n * Dùng ở Comitor: thông báo TOÀN WORKSPACE ở đỉnh khung app (sắp hết hạn dùng thử, bảo trì, lỗi\n * thanh toán) — đóng được, khác `Alert` là khối tĩnh nằm trong nội dung trang.\n */\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { useLazyRef } from \"../../lib/use-lazy-ref.js\";\nimport { Button } from \"./button.js\";\n\nconst BANNER_ANIMATION_DURATION = 400;\nconst DEFAULT_BANNER_PRIORITY = 0;\nconst DEFAULT_BANNER_DISMISSIBLE = true;\n\ntype BannerVariant = \"default\" | \"info\" | \"success\" | \"warning\" | \"destructive\";\ntype BannerSide = \"top\" | \"bottom\";\ntype BannerStrategy = \"fixed\" | \"static\" | \"sticky\" | \"absolute\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype CloseElement = React.ComponentRef<typeof BannerClose>;\n\ninterface BannerRenderProps {\n id: string;\n variant?: BannerVariant;\n dismissible: boolean;\n onClose: () => void;\n onRemove: () => void;\n}\n\n// Thượng nguồn đặt tên kiểu này là `BannerContent` — trùng tên component `BannerContent`. Đổi tên\n// để `export { BannerContent }` không mang theo một KIỂU cùng tên mà nghĩa khác hẳn.\ntype BannerContentValue = React.ReactNode | ((props: BannerRenderProps) => React.ReactNode);\n\ninterface BannerData {\n id: string;\n content: BannerContentValue;\n variant?: BannerVariant;\n priority?: number;\n duration?: number;\n dismissible?: boolean;\n onDismiss?: () => void;\n}\n\ninterface StoreState {\n banners: BannerData[];\n removing: Set<string>;\n heights: Map<string, number>;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState;\n notify: () => void;\n onBannerAdd: (banner: Omit<BannerData, \"id\">) => string;\n onBannerRemove: (id: string) => void;\n onBannersClear: () => void;\n onRemovingChange: (id: string, value: boolean) => void;\n onHeightChange: (id: string, height: number) => void;\n onHeightRemove: (id: string) => void;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`Banners\\``);\n }\n return context;\n}\n\nfunction useStore<T>(store: Store, selector: (state: StoreState) => T): T {\n return React.useSyncExternalStore(\n store.subscribe,\n () => selector(store.getState()),\n () => selector(store.getState())\n );\n}\n\ninterface BannerContextValue {\n id?: string;\n variant?: BannerVariant | null;\n dismissible?: boolean;\n onClose?: () => void;\n}\n\nconst BannerContext = React.createContext<BannerContextValue | null>(null);\n\nfunction useBannerContext(consumerName: string) {\n const context = React.useContext(BannerContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`Banner\\``);\n }\n return context;\n}\n\nfunction useBanner() {\n const { id, variant, dismissible, onClose } = useBannerContext(\"useBanner\");\n const storeContext = React.useContext(StoreContext);\n\n return React.useMemo(() => {\n const onRemove = id && storeContext ? () => storeContext.onBannerRemove(id) : undefined;\n\n return {\n id,\n variant,\n dismissible,\n onClose,\n onRemove\n };\n }, [id, variant, dismissible, onClose, storeContext]);\n}\n\ninterface BannersProps {\n children?: React.ReactNode;\n maxVisible?: number;\n side?: BannerSide;\n strategy?: BannerStrategy;\n container?: Element | DocumentFragment | null;\n}\n\nfunction Banners(props: BannersProps) {\n const { children, maxVisible = 1, side = \"top\", strategy = \"fixed\", container: containerProp } = props;\n\n const instanceId = React.useId();\n const stateRef = useLazyRef<StoreState>(() => ({\n banners: [],\n removing: new Set(),\n heights: new Map()\n }));\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const timeoutsRef = useLazyRef<Map<string, ReturnType<typeof setTimeout>>>(() => new Map());\n const idCounterRef = React.useRef(0);\n\n const store: Store = React.useMemo(\n () => ({\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => stateRef.current,\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n },\n onBannerAdd: (banner) => {\n /*\n * ⚠ Khác thượng nguồn: bản gốc `crypto.randomUUID()` — hàm này CHỈ có trong secure\n * context (https, localhost). Mở app qua http bằng IP mạng nội bộ (máy dev, môi trường\n * nội bộ) thì nó là `undefined` và mọi lần thêm banner đều throw. Id chỉ cần duy nhất\n * trong một `Banners` → bộ đếm, tiền tố `useId()` cho chắc khi có nhiều `Banners`.\n */\n idCounterRef.current += 1;\n const id = `${instanceId}-${idCounterRef.current}`;\n const newBanner: BannerData = { ...banner, id };\n const priority = banner.priority ?? DEFAULT_BANNER_PRIORITY;\n\n const banners = [...stateRef.current.banners];\n const insertIndex = banners.findIndex((b) => (b.priority ?? DEFAULT_BANNER_PRIORITY) < priority);\n\n if (insertIndex === -1) {\n banners.push(newBanner);\n } else {\n banners.splice(insertIndex, 0, newBanner);\n }\n\n stateRef.current.banners = banners;\n store.notify();\n\n if (banner.duration && banner.duration > 0) {\n const timeoutId = setTimeout(() => {\n store.onRemovingChange(id, true);\n timeoutsRef.current.delete(id);\n }, banner.duration);\n timeoutsRef.current.set(id, timeoutId);\n }\n\n return id;\n },\n onBannerRemove: (id) => {\n const banner = stateRef.current.banners.find((b) => b.id === id);\n if (!banner) return;\n\n const timeoutId = timeoutsRef.current.get(id);\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutsRef.current.delete(id);\n }\n\n const newRemoving = new Set(stateRef.current.removing);\n newRemoving.delete(id);\n stateRef.current.removing = newRemoving;\n\n banner.onDismiss?.();\n stateRef.current.banners = stateRef.current.banners.filter((b) => b.id !== id);\n store.notify();\n },\n onBannersClear: () => {\n for (const timeoutId of timeoutsRef.current.values()) {\n clearTimeout(timeoutId);\n }\n timeoutsRef.current.clear();\n stateRef.current.removing = new Set();\n stateRef.current.heights = new Map();\n stateRef.current.banners = [];\n store.notify();\n },\n onRemovingChange: (id, value) => {\n const newSet = new Set(stateRef.current.removing);\n if (value) {\n newSet.add(id);\n } else {\n newSet.delete(id);\n }\n stateRef.current.removing = newSet;\n store.notify();\n },\n onHeightChange: (id, height) => {\n if (stateRef.current.heights.get(id) === height) return;\n const newHeights = new Map(stateRef.current.heights);\n newHeights.set(id, height);\n stateRef.current.heights = newHeights;\n store.notify();\n },\n onHeightRemove: (id) => {\n if (!stateRef.current.heights.has(id)) return;\n const newHeights = new Map(stateRef.current.heights);\n newHeights.delete(id);\n stateRef.current.heights = newHeights;\n store.notify();\n }\n }),\n [instanceId, stateRef, listenersRef, timeoutsRef]\n );\n\n const banners = useStore(store, (state) => state.banners);\n const heights = useStore(store, (state) => state.heights);\n const visibleBanners = banners.slice(0, maxVisible);\n\n /*\n * ⚠ Khác thượng nguồn: bản gốc đọc `globalThis.document?.body` NGAY TRONG render. Ở đây chỉ đọc\n * sau khi mount (cùng khuôn với `ActionBar`) — lượt render phía server và lượt hydrate không\n * chạm `document`.\n */\n const [mounted, setMounted] = React.useState(false);\n useIsomorphicLayoutEffect(() => {\n setMounted(true);\n }, []);\n\n const withPortal = strategy === \"fixed\" || strategy === \"absolute\";\n const container = withPortal ? (containerProp ?? (mounted ? globalThis.document?.body : null) ?? null) : null;\n\n const totalHeight = React.useMemo(() => {\n let total = 0;\n for (const banner of visibleBanners) {\n total += heights.get(banner.id) ?? 0;\n }\n return total;\n }, [visibleBanners, heights]);\n\n const bannerContainer = visibleBanners.length > 0 && (\n <div\n data-slot=\"banner-container\"\n data-side={side}\n data-strategy={strategy}\n className={cn(\n \"pointer-events-none right-0 left-0 isolate z-50\",\n /*\n * ⚠ Khác thượng nguồn: bản gốc ghi `transition` thẳng vào `style`, nên lớp\n * `motion-reduce:transition-none` của từng banner KHÔNG BAO GIỜ thắng được (style nội tuyến\n * luôn thắng lớp) — người bật \"giảm chuyển động\" vẫn thấy banner trượt. Nay thời lượng đi\n * qua `--banner-duration`, bằng 0 khi `prefers-reduced-motion`; banner con thừa kế biến này.\n */\n \"motion-reduce:[--banner-duration:0ms]\",\n strategy === \"fixed\" && \"fixed\",\n strategy === \"static\" && \"relative\",\n strategy === \"sticky\" && \"sticky\",\n strategy === \"absolute\" && \"absolute\",\n side === \"top\" ? \"top-0\" : \"bottom-0\"\n )}\n style={{\n height: totalHeight > 0 ? totalHeight : \"auto\",\n transition: `height var(--banner-duration, ${BANNER_ANIMATION_DURATION}ms) cubic-bezier(0.32, 0.72, 0, 1)`\n }}\n >\n {visibleBanners.map((banner, index) => (\n <BannerImpl key={banner.id} banner={banner} side={side} index={index} />\n ))}\n </div>\n );\n\n return (\n <StoreContext.Provider value={store}>\n {strategy === \"static\" || strategy === \"sticky\" ? (\n <>\n {side === \"top\" && bannerContainer}\n {children}\n {side === \"bottom\" && bannerContainer}\n </>\n ) : (\n <>\n {children}\n {container && bannerContainer && ReactDOM.createPortal(bannerContainer, container)}\n </>\n )}\n </StoreContext.Provider>\n );\n}\n\nfunction useBanners() {\n const store = useStoreContext(\"useBanners\");\n const banners = useStore(store, (state) => state.banners);\n\n return React.useMemo(\n () => ({\n onBannerAdd: store.onBannerAdd,\n onBannerRemove: store.onBannerRemove,\n onBannersClear: store.onBannersClear,\n banners\n }),\n [store, banners]\n );\n}\n\n/*\n * ⚠ Khác thượng nguồn: bản gốc tô bằng bảng màu mặc định của Tailwind (`bg-blue-50\n * text-blue-900`, `bg-red-50`… kèm `dark:`) — đi vòng qua hợp đồng token nên không đổi theo bảng\n * tương phản cao. Ở đây dùng ĐÚNG bộ lớp của `Alert` (alert.tsx) để Banner và Alert cùng tông\n * trông như một họ: nền `bg-card`, chữ + icon ở bậc `-ink` của tone. `Alert` chưa có `info` /\n * `success`; hai tông đó theo cùng khuôn với `warning` / `destructive` của nó.\n *\n * Nền ĐẶC `bg-card` chứ không tint `bg-<tone>/15`: ở chế độ `fixed`/`absolute`, banner nằm ĐÈ lên\n * nội dung trang — một nền trong suốt một phần sẽ cho chữ trang lộ xuyên qua chữ banner.\n *\n * Mô tả (`BannerDescription`) giữ `opacity-90` của bản gốc — trên nền đặc nó tương đương đúng\n * `text-<tone>-ink/90` của `AlertDescription`. Cổng không đo được `opacity`, nên số đo tay (trên\n * `--card`, tiêu đề / mô tả 90%):\n *\n * bảng tương phản cao light dark\n * info 6,68 / 5,31 9,54 / 7,92 ✓\n * success 6,52 / 5,20 10,19 / 8,44 ✓\n * warning 6,51 / 5,17 11,60 / 9,56 ✓\n * destructive 7,15 / 5,77 6,42 / 5,41 ✓\n * bảng mặc định (nợ token đã chấp nhận, cùng mức với `Alert`):\n * destructive 4,38 / 3,81 (light) · 4,06 / 3,50 (dark) · info dark 4,28 / 3,71\n */\nconst bannerVariants = cva(\n \"pointer-events-auto relative flex w-full items-center gap-3 border-b px-4 py-3 text-sm motion-reduce:transition-none\",\n {\n variants: {\n variant: {\n default: \"bg-card text-card-foreground\",\n info: \"bg-card text-info-ink\",\n success: \"bg-card text-success-ink\",\n warning: \"bg-card text-warning-ink\",\n destructive: \"bg-card text-destructive-ink\"\n }\n },\n defaultVariants: {\n variant: \"default\"\n }\n }\n);\n\ninterface BannerImplProps {\n banner: BannerData;\n side: BannerSide;\n index: number;\n}\n\nfunction BannerImpl(props: BannerImplProps) {\n const { banner, side, index } = props;\n\n const store = useStoreContext(\"BannerImpl\");\n const removing = useStore(store, (state) => state.removing.has(banner.id));\n const banners = useStore(store, (state) => state.banners);\n const heights = useStore(store, (state) => state.heights);\n\n const [mounted, setMounted] = React.useState(false);\n const bannerRef = React.useRef<HTMLDivElement>(null);\n const offsetBeforeRemoveRef = React.useRef(0);\n\n const offset = React.useMemo(() => {\n let total = 0;\n for (const b of banners) {\n if (b.id === banner.id) break;\n total += heights.get(b.id) ?? 0;\n }\n return total;\n }, [banners, heights, banner.id]);\n\n if (!removing) {\n offsetBeforeRemoveRef.current = offset;\n }\n\n React.useEffect(() => {\n const frame = requestAnimationFrame(() => setMounted(true));\n return () => cancelAnimationFrame(frame);\n }, []);\n\n useIsomorphicLayoutEffect(() => {\n if (!bannerRef.current || removing) return;\n const height = bannerRef.current.getBoundingClientRect().height;\n store.onHeightChange(banner.id, height);\n }, [store, banner.id, removing]);\n\n React.useEffect(() => {\n if (!removing) return;\n store.onHeightRemove(banner.id);\n const timeoutId = setTimeout(() => store.onBannerRemove(banner.id), BANNER_ANIMATION_DURATION);\n return () => clearTimeout(timeoutId);\n }, [removing, store, banner.id]);\n\n const onClose = React.useCallback(() => store.onRemovingChange(banner.id, true), [store, banner.id]);\n\n const onRemove = React.useCallback(() => store.onBannerRemove(banner.id), [store, banner.id]);\n\n const dismissible = banner.dismissible ?? DEFAULT_BANNER_DISMISSIBLE;\n\n const contextValue = React.useMemo<BannerContextValue>(\n () => ({ id: banner.id, variant: banner.variant, dismissible, onClose }),\n [banner.id, banner.variant, dismissible, onClose]\n );\n\n const renderProps = React.useMemo<BannerRenderProps>(\n () => ({\n id: banner.id,\n variant: banner.variant,\n dismissible,\n onClose,\n onRemove\n }),\n [banner.id, banner.variant, dismissible, onClose, onRemove]\n );\n\n const currentOffset = removing ? offsetBeforeRemoveRef.current : offset;\n const isTop = side === \"top\";\n\n function getTransform() {\n if (!mounted) return isTop ? \"translateY(-100%)\" : \"translateY(100%)\";\n if (removing) {\n return isTop ? `translateY(calc(${currentOffset}px - 100%))` : `translateY(calc(-${currentOffset}px + 100%))`;\n }\n return isTop ? `translateY(${currentOffset}px)` : `translateY(-${currentOffset}px)`;\n }\n\n const opacityDuration = removing ? BANNER_ANIMATION_DURATION / 2 : BANNER_ANIMATION_DURATION;\n\n return (\n <BannerContext.Provider value={contextValue}>\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"queued-banner\"\n data-state={removing ? \"closed\" : \"open\"}\n data-mounted={mounted}\n data-removed={removing}\n data-side={side}\n data-front={index === 0}\n data-index={index}\n ref={bannerRef}\n className={bannerVariants({ variant: banner.variant })}\n style={{\n position: \"absolute\",\n [isTop ? \"top\" : \"bottom\"]: 0,\n left: 0,\n right: 0,\n zIndex: removing ? 0 : 50 - index,\n transform: getTransform(),\n opacity: mounted && !removing ? 1 : 0,\n // Thời lượng qua `--banner-duration` (0 khi giảm chuyển động) — xem chú thích ở `Banners`.\n transition: `transform var(--banner-duration, ${BANNER_ANIMATION_DURATION}ms) cubic-bezier(0.32, 0.72, 0, 1), opacity var(--banner-duration, ${opacityDuration}ms) ease`\n }}\n >\n {typeof banner.content === \"function\" ? banner.content(renderProps) : banner.content}\n </div>\n </BannerContext.Provider>\n );\n}\n\ninterface BannerProps extends DivProps, VariantProps<typeof bannerVariants> {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n onDismiss?: () => void;\n priority?: number;\n duration?: number;\n dismissible?: boolean;\n}\n\nfunction Banner(props: BannerProps) {\n const {\n className,\n variant = \"default\",\n open: openProp,\n defaultOpen = true,\n onOpenChange,\n onDismiss,\n priority,\n duration,\n dismissible = DEFAULT_BANNER_DISMISSIBLE,\n children,\n asChild,\n ...rootProps\n } = props;\n\n const store = React.useContext(StoreContext);\n\n const isInsideStore = store !== null;\n const isControlled = openProp !== undefined;\n\n const openRef = useLazyRef(() => openProp ?? defaultOpen);\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const bannerIdRef = React.useRef<string | null>(null);\n const onDismissRef = useAsRef(onDismiss);\n const onOpenChangeRef = useAsRef(onOpenChange);\n\n if (isControlled) {\n openRef.current = openProp;\n }\n\n const subscribe = React.useCallback(\n (cb: () => void) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n [listenersRef]\n );\n\n const getSnapshot = React.useCallback(() => openRef.current, [openRef]);\n\n const open = React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n React.useEffect(() => {\n if (!isInsideStore || !store || !open) return;\n\n /*\n * ⚠ Khác thượng nguồn — hai lỗi của `<Banner>` đặt trong `<Banners>`:\n *\n * 1. Hàm dọn của effect này gỡ banner bằng `onBannerRemove`, mà hàm đó GỌI `onDismiss`. Bản\n * gốc vì thế báo \"người dùng đã đóng\" (`onDismiss` + `onOpenChange(false)`) mỗi khi effect\n * chạy lại — tức MỖI LẦN cha re-render, vì `children` là JSX mới — và cả khi unmount (đổi\n * route). Banner tự đóng, và app lưu \"đã tắt thông báo\" cho một lần người dùng chưa hề bấm.\n * Cờ `isCleaningUp` chặn đúng đường đó; đóng thật (nút X, hết `duration`) vẫn báo như cũ.\n * 2. Ở chế độ không kiểm soát, đóng qua hàng đợi không hạ `open` của chính `Banner`, nên lần\n * re-render kế tiếp thêm LẠI banner vừa đóng. Nay hạ `open` như `onClose` bên dưới.\n */\n let isCleaningUp = false;\n\n const id = store.onBannerAdd({\n content: children,\n variant: variant ?? undefined,\n priority,\n dismissible,\n duration,\n onDismiss: () => {\n if (isCleaningUp) return;\n if (!isControlled) {\n openRef.current = false;\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n onDismissRef.current?.();\n onOpenChangeRef.current?.(false);\n }\n });\n bannerIdRef.current = id;\n\n return () => {\n isCleaningUp = true;\n if (bannerIdRef.current) {\n store.onBannerRemove(bannerIdRef.current);\n bannerIdRef.current = null;\n }\n };\n }, [\n isInsideStore,\n store,\n open,\n isControlled,\n children,\n variant,\n priority,\n dismissible,\n duration,\n openRef,\n listenersRef,\n onDismissRef,\n onOpenChangeRef\n ]);\n\n const onClose = React.useCallback(() => {\n if (!isControlled) {\n openRef.current = false;\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n onOpenChangeRef.current?.(false);\n }, [isControlled, openRef, listenersRef, onOpenChangeRef]);\n\n const contextValue = React.useMemo<BannerContextValue>(\n () => ({\n variant,\n dismissible,\n onClose\n }),\n [variant, dismissible, onClose]\n );\n\n if (!open || isInsideStore) return null;\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <BannerContext.Provider value={contextValue}>\n <RootPrimitive\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"banner\"\n data-state=\"open\"\n className={cn(bannerVariants({ variant, className }))}\n {...rootProps}\n >\n {children}\n </RootPrimitive>\n </BannerContext.Provider>\n );\n}\n\nfunction BannerIcon(props: DivProps) {\n const { className, asChild, ...iconProps } = props;\n\n const IconPrimitive = asChild ? Slot : \"div\";\n\n return (\n <IconPrimitive\n data-slot=\"banner-icon\"\n className={cn(\"flex shrink-0 items-center [&>svg]:size-4\", className)}\n {...iconProps}\n />\n );\n}\n\nfunction BannerContent(props: DivProps) {\n const { className, asChild, ...contentProps } = props;\n\n const ContentPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ContentPrimitive\n data-slot=\"banner-content\"\n className={cn(\"flex min-w-0 flex-1 flex-col gap-1\", className)}\n {...contentProps}\n />\n );\n}\n\nfunction BannerTitle(props: React.ComponentProps<\"div\">) {\n const { className, ...titleProps } = props;\n\n return <div data-slot=\"banner-title\" className={cn(\"text-sm leading-none font-medium\", className)} {...titleProps} />;\n}\n\nfunction BannerDescription(props: React.ComponentProps<\"div\">) {\n const { className, ...descriptionProps } = props;\n\n return <div data-slot=\"banner-description\" className={cn(\"text-xs opacity-90\", className)} {...descriptionProps} />;\n}\n\nfunction BannerActions(props: DivProps) {\n const { className, asChild, ...actionsProps } = props;\n\n const ActionsPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ActionsPrimitive\n data-slot=\"banner-actions\"\n className={cn(\"flex items-center gap-2\", className)}\n {...actionsProps}\n />\n );\n}\n\ntype BannerCloseProps = React.ComponentProps<typeof Button>;\n\n/**\n * Nút đóng banner. Không truyền `children` thì vẽ icon X.\n *\n * ⚠ Khác thượng nguồn: có tên cho trình đọc màn hình — `aria-label` mặc định \"Đóng thông báo\",\n * ghi đè bằng chính prop `aria-label`. Bản gốc là `Button` chỉ chứa icon X, tức một nút KHÔNG\n * TÊN. Nếu `children` là CHỮ nhìn thấy được thì truyền `aria-label` trùng với chữ đó (WCAG\n * 2.5.3), hoặc `aria-label={undefined}`.\n */\nfunction BannerClose(props: BannerCloseProps) {\n const { onClick: onClickProp, disabled, children, ...closeProps } = props;\n\n const { dismissible = DEFAULT_BANNER_DISMISSIBLE, onClose } = useBannerContext(\"BannerClose\");\n\n const isDisabled = disabled ?? !dismissible;\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<CloseElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented || isDisabled) return;\n onClose?.();\n },\n [onClickProp, isDisabled, onClose]\n );\n\n return (\n <Button\n aria-label=\"Đóng thông báo\"\n data-slot=\"banner-close\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onClick}\n disabled={isDisabled}\n {...closeProps}\n >\n {children ?? <X aria-hidden=\"true\" className=\"size-3.5\" />}\n </Button>\n );\n}\n\nexport {\n Banner,\n BannerActions,\n BannerClose,\n type BannerCloseProps,\n BannerContent,\n type BannerData,\n BannerDescription,\n BannerIcon,\n type BannerProps,\n type BannerRenderProps,\n Banners,\n type BannersProps,\n BannerTitle,\n type BannerVariant,\n useBanner,\n useBanners\n};\n"],"mappings":"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","names":["banners"]}
1
+ {"version":3,"sources":["../../../src/components/ui/banner.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/banner) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n *\n * Dùng ở Comitor: thông báo TOÀN WORKSPACE ở đỉnh khung app (sắp hết hạn dùng thử, bảo trì, lỗi\n * thanh toán) — đóng được, khác `Alert` là khối tĩnh nằm trong nội dung trang.\n */\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { useComitorMessages } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { useLazyRef } from \"../../lib/use-lazy-ref.js\";\nimport { Button } from \"./button.js\";\n\nconst BANNER_ANIMATION_DURATION = 400;\nconst DEFAULT_BANNER_PRIORITY = 0;\nconst DEFAULT_BANNER_DISMISSIBLE = true;\n\ntype BannerVariant = \"default\" | \"info\" | \"success\" | \"warning\" | \"destructive\";\ntype BannerSide = \"top\" | \"bottom\";\ntype BannerStrategy = \"fixed\" | \"static\" | \"sticky\" | \"absolute\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype CloseElement = React.ComponentRef<typeof BannerClose>;\n\ninterface BannerRenderProps {\n id: string;\n variant?: BannerVariant;\n dismissible: boolean;\n onClose: () => void;\n onRemove: () => void;\n}\n\n// Thượng nguồn đặt tên kiểu này là `BannerContent` — trùng tên component `BannerContent`. Đổi tên\n// để `export { BannerContent }` không mang theo một KIỂU cùng tên mà nghĩa khác hẳn.\ntype BannerContentValue = React.ReactNode | ((props: BannerRenderProps) => React.ReactNode);\n\ninterface BannerData {\n id: string;\n content: BannerContentValue;\n variant?: BannerVariant;\n priority?: number;\n duration?: number;\n dismissible?: boolean;\n onDismiss?: () => void;\n}\n\ninterface StoreState {\n banners: BannerData[];\n removing: Set<string>;\n heights: Map<string, number>;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState;\n notify: () => void;\n onBannerAdd: (banner: Omit<BannerData, \"id\">) => string;\n onBannerRemove: (id: string) => void;\n onBannersClear: () => void;\n onRemovingChange: (id: string, value: boolean) => void;\n onHeightChange: (id: string, height: number) => void;\n onHeightRemove: (id: string) => void;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`Banners\\``);\n }\n return context;\n}\n\nfunction useStore<T>(store: Store, selector: (state: StoreState) => T): T {\n return React.useSyncExternalStore(\n store.subscribe,\n () => selector(store.getState()),\n () => selector(store.getState())\n );\n}\n\ninterface BannerContextValue {\n id?: string;\n variant?: BannerVariant | null;\n dismissible?: boolean;\n onClose?: () => void;\n}\n\nconst BannerContext = React.createContext<BannerContextValue | null>(null);\n\nfunction useBannerContext(consumerName: string) {\n const context = React.useContext(BannerContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`Banner\\``);\n }\n return context;\n}\n\nfunction useBanner() {\n const { id, variant, dismissible, onClose } = useBannerContext(\"useBanner\");\n const storeContext = React.useContext(StoreContext);\n\n return React.useMemo(() => {\n const onRemove = id && storeContext ? () => storeContext.onBannerRemove(id) : undefined;\n\n return {\n id,\n variant,\n dismissible,\n onClose,\n onRemove\n };\n }, [id, variant, dismissible, onClose, storeContext]);\n}\n\ninterface BannersProps {\n children?: React.ReactNode;\n maxVisible?: number;\n side?: BannerSide;\n strategy?: BannerStrategy;\n container?: Element | DocumentFragment | null;\n}\n\nfunction Banners(props: BannersProps) {\n const { children, maxVisible = 1, side = \"top\", strategy = \"fixed\", container: containerProp } = props;\n\n const instanceId = React.useId();\n const stateRef = useLazyRef<StoreState>(() => ({\n banners: [],\n removing: new Set(),\n heights: new Map()\n }));\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const timeoutsRef = useLazyRef<Map<string, ReturnType<typeof setTimeout>>>(() => new Map());\n const idCounterRef = React.useRef(0);\n\n const store: Store = React.useMemo(\n () => ({\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => stateRef.current,\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n },\n onBannerAdd: (banner) => {\n /*\n * ⚠ Khác thượng nguồn: bản gốc `crypto.randomUUID()` — hàm này CHỈ có trong secure\n * context (https, localhost). Mở app qua http bằng IP mạng nội bộ (máy dev, môi trường\n * nội bộ) thì nó là `undefined` và mọi lần thêm banner đều throw. Id chỉ cần duy nhất\n * trong một `Banners` → bộ đếm, tiền tố `useId()` cho chắc khi có nhiều `Banners`.\n */\n idCounterRef.current += 1;\n const id = `${instanceId}-${idCounterRef.current}`;\n const newBanner: BannerData = { ...banner, id };\n const priority = banner.priority ?? DEFAULT_BANNER_PRIORITY;\n\n const banners = [...stateRef.current.banners];\n const insertIndex = banners.findIndex((b) => (b.priority ?? DEFAULT_BANNER_PRIORITY) < priority);\n\n if (insertIndex === -1) {\n banners.push(newBanner);\n } else {\n banners.splice(insertIndex, 0, newBanner);\n }\n\n stateRef.current.banners = banners;\n store.notify();\n\n if (banner.duration && banner.duration > 0) {\n const timeoutId = setTimeout(() => {\n store.onRemovingChange(id, true);\n timeoutsRef.current.delete(id);\n }, banner.duration);\n timeoutsRef.current.set(id, timeoutId);\n }\n\n return id;\n },\n onBannerRemove: (id) => {\n const banner = stateRef.current.banners.find((b) => b.id === id);\n if (!banner) return;\n\n const timeoutId = timeoutsRef.current.get(id);\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutsRef.current.delete(id);\n }\n\n const newRemoving = new Set(stateRef.current.removing);\n newRemoving.delete(id);\n stateRef.current.removing = newRemoving;\n\n banner.onDismiss?.();\n stateRef.current.banners = stateRef.current.banners.filter((b) => b.id !== id);\n store.notify();\n },\n onBannersClear: () => {\n for (const timeoutId of timeoutsRef.current.values()) {\n clearTimeout(timeoutId);\n }\n timeoutsRef.current.clear();\n stateRef.current.removing = new Set();\n stateRef.current.heights = new Map();\n stateRef.current.banners = [];\n store.notify();\n },\n onRemovingChange: (id, value) => {\n const newSet = new Set(stateRef.current.removing);\n if (value) {\n newSet.add(id);\n } else {\n newSet.delete(id);\n }\n stateRef.current.removing = newSet;\n store.notify();\n },\n onHeightChange: (id, height) => {\n if (stateRef.current.heights.get(id) === height) return;\n const newHeights = new Map(stateRef.current.heights);\n newHeights.set(id, height);\n stateRef.current.heights = newHeights;\n store.notify();\n },\n onHeightRemove: (id) => {\n if (!stateRef.current.heights.has(id)) return;\n const newHeights = new Map(stateRef.current.heights);\n newHeights.delete(id);\n stateRef.current.heights = newHeights;\n store.notify();\n }\n }),\n [instanceId, stateRef, listenersRef, timeoutsRef]\n );\n\n const banners = useStore(store, (state) => state.banners);\n const heights = useStore(store, (state) => state.heights);\n const visibleBanners = banners.slice(0, maxVisible);\n\n /*\n * ⚠ Khác thượng nguồn: bản gốc đọc `globalThis.document?.body` NGAY TRONG render. Ở đây chỉ đọc\n * sau khi mount (cùng khuôn với `ActionBar`) — lượt render phía server và lượt hydrate không\n * chạm `document`.\n */\n const [mounted, setMounted] = React.useState(false);\n useIsomorphicLayoutEffect(() => {\n setMounted(true);\n }, []);\n\n const withPortal = strategy === \"fixed\" || strategy === \"absolute\";\n const container = withPortal ? (containerProp ?? (mounted ? globalThis.document?.body : null) ?? null) : null;\n\n const totalHeight = React.useMemo(() => {\n let total = 0;\n for (const banner of visibleBanners) {\n total += heights.get(banner.id) ?? 0;\n }\n return total;\n }, [visibleBanners, heights]);\n\n const bannerContainer = visibleBanners.length > 0 && (\n <div\n data-slot=\"banner-container\"\n data-side={side}\n data-strategy={strategy}\n className={cn(\n \"pointer-events-none right-0 left-0 isolate z-50\",\n /*\n * ⚠ Khác thượng nguồn: bản gốc ghi `transition` thẳng vào `style`, nên lớp\n * `motion-reduce:transition-none` của từng banner KHÔNG BAO GIỜ thắng được (style nội tuyến\n * luôn thắng lớp) — người bật \"giảm chuyển động\" vẫn thấy banner trượt. Nay thời lượng đi\n * qua `--banner-duration`, bằng 0 khi `prefers-reduced-motion`; banner con thừa kế biến này.\n */\n \"motion-reduce:[--banner-duration:0ms]\",\n strategy === \"fixed\" && \"fixed\",\n strategy === \"static\" && \"relative\",\n strategy === \"sticky\" && \"sticky\",\n strategy === \"absolute\" && \"absolute\",\n side === \"top\" ? \"top-0\" : \"bottom-0\"\n )}\n style={{\n height: totalHeight > 0 ? totalHeight : \"auto\",\n transition: `height var(--banner-duration, ${BANNER_ANIMATION_DURATION}ms) cubic-bezier(0.32, 0.72, 0, 1)`\n }}\n >\n {visibleBanners.map((banner, index) => (\n <BannerImpl key={banner.id} banner={banner} side={side} index={index} />\n ))}\n </div>\n );\n\n return (\n <StoreContext.Provider value={store}>\n {strategy === \"static\" || strategy === \"sticky\" ? (\n <>\n {side === \"top\" && bannerContainer}\n {children}\n {side === \"bottom\" && bannerContainer}\n </>\n ) : (\n <>\n {children}\n {container && bannerContainer && ReactDOM.createPortal(bannerContainer, container)}\n </>\n )}\n </StoreContext.Provider>\n );\n}\n\nfunction useBanners() {\n const store = useStoreContext(\"useBanners\");\n const banners = useStore(store, (state) => state.banners);\n\n return React.useMemo(\n () => ({\n onBannerAdd: store.onBannerAdd,\n onBannerRemove: store.onBannerRemove,\n onBannersClear: store.onBannersClear,\n banners\n }),\n [store, banners]\n );\n}\n\n/*\n * ⚠ Khác thượng nguồn: bản gốc tô bằng bảng màu mặc định của Tailwind (`bg-blue-50\n * text-blue-900`, `bg-red-50`… kèm `dark:`) — đi vòng qua hợp đồng token nên không đổi theo bảng\n * tương phản cao. Ở đây dùng ĐÚNG bộ lớp của `Alert` (alert.tsx) để Banner và Alert cùng tông\n * trông như một họ: nền `bg-card`, chữ + icon ở bậc `-ink` của tone. `Alert` chưa có `info` /\n * `success`; hai tông đó theo cùng khuôn với `warning` / `destructive` của nó.\n *\n * Nền ĐẶC `bg-card` chứ không tint `bg-<tone>/15`: ở chế độ `fixed`/`absolute`, banner nằm ĐÈ lên\n * nội dung trang — một nền trong suốt một phần sẽ cho chữ trang lộ xuyên qua chữ banner.\n *\n * Mô tả (`BannerDescription`) giữ `opacity-90` của bản gốc — trên nền đặc nó tương đương đúng\n * `text-<tone>-ink/90` của `AlertDescription`. Cổng không đo được `opacity`, nên số đo tay (trên\n * `--card`, tiêu đề / mô tả 90%):\n *\n * bảng tương phản cao light dark\n * info 6,68 / 5,31 9,54 / 7,92 ✓\n * success 6,52 / 5,20 10,19 / 8,44 ✓\n * warning 6,51 / 5,17 11,60 / 9,56 ✓\n * destructive 7,15 / 5,77 6,42 / 5,41 ✓\n * bảng mặc định (nợ token đã chấp nhận, cùng mức với `Alert`):\n * destructive 4,38 / 3,81 (light) · 4,06 / 3,50 (dark) · info dark 4,28 / 3,71\n */\nconst bannerVariants = cva(\n \"pointer-events-auto relative flex w-full items-center gap-3 border-b px-4 py-3 text-sm motion-reduce:transition-none\",\n {\n variants: {\n variant: {\n default: \"bg-card text-card-foreground\",\n info: \"bg-card text-info-ink\",\n success: \"bg-card text-success-ink\",\n warning: \"bg-card text-warning-ink\",\n destructive: \"bg-card text-destructive-ink\"\n }\n },\n defaultVariants: {\n variant: \"default\"\n }\n }\n);\n\ninterface BannerImplProps {\n banner: BannerData;\n side: BannerSide;\n index: number;\n}\n\nfunction BannerImpl(props: BannerImplProps) {\n const { banner, side, index } = props;\n\n const store = useStoreContext(\"BannerImpl\");\n const removing = useStore(store, (state) => state.removing.has(banner.id));\n const banners = useStore(store, (state) => state.banners);\n const heights = useStore(store, (state) => state.heights);\n\n const [mounted, setMounted] = React.useState(false);\n const bannerRef = React.useRef<HTMLDivElement>(null);\n const offsetBeforeRemoveRef = React.useRef(0);\n\n const offset = React.useMemo(() => {\n let total = 0;\n for (const b of banners) {\n if (b.id === banner.id) break;\n total += heights.get(b.id) ?? 0;\n }\n return total;\n }, [banners, heights, banner.id]);\n\n if (!removing) {\n offsetBeforeRemoveRef.current = offset;\n }\n\n React.useEffect(() => {\n const frame = requestAnimationFrame(() => setMounted(true));\n return () => cancelAnimationFrame(frame);\n }, []);\n\n useIsomorphicLayoutEffect(() => {\n if (!bannerRef.current || removing) return;\n const height = bannerRef.current.getBoundingClientRect().height;\n store.onHeightChange(banner.id, height);\n }, [store, banner.id, removing]);\n\n React.useEffect(() => {\n if (!removing) return;\n store.onHeightRemove(banner.id);\n const timeoutId = setTimeout(() => store.onBannerRemove(banner.id), BANNER_ANIMATION_DURATION);\n return () => clearTimeout(timeoutId);\n }, [removing, store, banner.id]);\n\n const onClose = React.useCallback(() => store.onRemovingChange(banner.id, true), [store, banner.id]);\n\n const onRemove = React.useCallback(() => store.onBannerRemove(banner.id), [store, banner.id]);\n\n const dismissible = banner.dismissible ?? DEFAULT_BANNER_DISMISSIBLE;\n\n const contextValue = React.useMemo<BannerContextValue>(\n () => ({ id: banner.id, variant: banner.variant, dismissible, onClose }),\n [banner.id, banner.variant, dismissible, onClose]\n );\n\n const renderProps = React.useMemo<BannerRenderProps>(\n () => ({\n id: banner.id,\n variant: banner.variant,\n dismissible,\n onClose,\n onRemove\n }),\n [banner.id, banner.variant, dismissible, onClose, onRemove]\n );\n\n const currentOffset = removing ? offsetBeforeRemoveRef.current : offset;\n const isTop = side === \"top\";\n\n function getTransform() {\n if (!mounted) return isTop ? \"translateY(-100%)\" : \"translateY(100%)\";\n if (removing) {\n return isTop ? `translateY(calc(${currentOffset}px - 100%))` : `translateY(calc(-${currentOffset}px + 100%))`;\n }\n return isTop ? `translateY(${currentOffset}px)` : `translateY(-${currentOffset}px)`;\n }\n\n const opacityDuration = removing ? BANNER_ANIMATION_DURATION / 2 : BANNER_ANIMATION_DURATION;\n\n return (\n <BannerContext.Provider value={contextValue}>\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"queued-banner\"\n data-state={removing ? \"closed\" : \"open\"}\n data-mounted={mounted}\n data-removed={removing}\n data-side={side}\n data-front={index === 0}\n data-index={index}\n ref={bannerRef}\n className={bannerVariants({ variant: banner.variant })}\n style={{\n position: \"absolute\",\n [isTop ? \"top\" : \"bottom\"]: 0,\n left: 0,\n right: 0,\n zIndex: removing ? 0 : 50 - index,\n transform: getTransform(),\n opacity: mounted && !removing ? 1 : 0,\n // Thời lượng qua `--banner-duration` (0 khi giảm chuyển động) — xem chú thích ở `Banners`.\n transition: `transform var(--banner-duration, ${BANNER_ANIMATION_DURATION}ms) cubic-bezier(0.32, 0.72, 0, 1), opacity var(--banner-duration, ${opacityDuration}ms) ease`\n }}\n >\n {typeof banner.content === \"function\" ? banner.content(renderProps) : banner.content}\n </div>\n </BannerContext.Provider>\n );\n}\n\ninterface BannerProps extends DivProps, VariantProps<typeof bannerVariants> {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n onDismiss?: () => void;\n priority?: number;\n duration?: number;\n dismissible?: boolean;\n}\n\nfunction Banner(props: BannerProps) {\n const {\n className,\n variant = \"default\",\n open: openProp,\n defaultOpen = true,\n onOpenChange,\n onDismiss,\n priority,\n duration,\n dismissible = DEFAULT_BANNER_DISMISSIBLE,\n children,\n asChild,\n ...rootProps\n } = props;\n\n const store = React.useContext(StoreContext);\n\n const isInsideStore = store !== null;\n const isControlled = openProp !== undefined;\n\n const openRef = useLazyRef(() => openProp ?? defaultOpen);\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const bannerIdRef = React.useRef<string | null>(null);\n const onDismissRef = useAsRef(onDismiss);\n const onOpenChangeRef = useAsRef(onOpenChange);\n\n if (isControlled) {\n openRef.current = openProp;\n }\n\n const subscribe = React.useCallback(\n (cb: () => void) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n [listenersRef]\n );\n\n const getSnapshot = React.useCallback(() => openRef.current, [openRef]);\n\n const open = React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n React.useEffect(() => {\n if (!isInsideStore || !store || !open) return;\n\n /*\n * ⚠ Khác thượng nguồn — hai lỗi của `<Banner>` đặt trong `<Banners>`:\n *\n * 1. Hàm dọn của effect này gỡ banner bằng `onBannerRemove`, mà hàm đó GỌI `onDismiss`. Bản\n * gốc vì thế báo \"người dùng đã đóng\" (`onDismiss` + `onOpenChange(false)`) mỗi khi effect\n * chạy lại — tức MỖI LẦN cha re-render, vì `children` là JSX mới — và cả khi unmount (đổi\n * route). Banner tự đóng, và app lưu \"đã tắt thông báo\" cho một lần người dùng chưa hề bấm.\n * Cờ `isCleaningUp` chặn đúng đường đó; đóng thật (nút X, hết `duration`) vẫn báo như cũ.\n * 2. Ở chế độ không kiểm soát, đóng qua hàng đợi không hạ `open` của chính `Banner`, nên lần\n * re-render kế tiếp thêm LẠI banner vừa đóng. Nay hạ `open` như `onClose` bên dưới.\n */\n let isCleaningUp = false;\n\n const id = store.onBannerAdd({\n content: children,\n variant: variant ?? undefined,\n priority,\n dismissible,\n duration,\n onDismiss: () => {\n if (isCleaningUp) return;\n if (!isControlled) {\n openRef.current = false;\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n onDismissRef.current?.();\n onOpenChangeRef.current?.(false);\n }\n });\n bannerIdRef.current = id;\n\n return () => {\n isCleaningUp = true;\n if (bannerIdRef.current) {\n store.onBannerRemove(bannerIdRef.current);\n bannerIdRef.current = null;\n }\n };\n }, [\n isInsideStore,\n store,\n open,\n isControlled,\n children,\n variant,\n priority,\n dismissible,\n duration,\n openRef,\n listenersRef,\n onDismissRef,\n onOpenChangeRef\n ]);\n\n const onClose = React.useCallback(() => {\n if (!isControlled) {\n openRef.current = false;\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n onOpenChangeRef.current?.(false);\n }, [isControlled, openRef, listenersRef, onOpenChangeRef]);\n\n const contextValue = React.useMemo<BannerContextValue>(\n () => ({\n variant,\n dismissible,\n onClose\n }),\n [variant, dismissible, onClose]\n );\n\n if (!open || isInsideStore) return null;\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <BannerContext.Provider value={contextValue}>\n <RootPrimitive\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"banner\"\n data-state=\"open\"\n className={cn(bannerVariants({ variant, className }))}\n {...rootProps}\n >\n {children}\n </RootPrimitive>\n </BannerContext.Provider>\n );\n}\n\nfunction BannerIcon(props: DivProps) {\n const { className, asChild, ...iconProps } = props;\n\n const IconPrimitive = asChild ? Slot : \"div\";\n\n return (\n <IconPrimitive\n data-slot=\"banner-icon\"\n className={cn(\"flex shrink-0 items-center [&>svg]:size-4\", className)}\n {...iconProps}\n />\n );\n}\n\nfunction BannerContent(props: DivProps) {\n const { className, asChild, ...contentProps } = props;\n\n const ContentPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ContentPrimitive\n data-slot=\"banner-content\"\n className={cn(\"flex min-w-0 flex-1 flex-col gap-1\", className)}\n {...contentProps}\n />\n );\n}\n\nfunction BannerTitle(props: React.ComponentProps<\"div\">) {\n const { className, ...titleProps } = props;\n\n return <div data-slot=\"banner-title\" className={cn(\"text-sm leading-none font-medium\", className)} {...titleProps} />;\n}\n\nfunction BannerDescription(props: React.ComponentProps<\"div\">) {\n const { className, ...descriptionProps } = props;\n\n return <div data-slot=\"banner-description\" className={cn(\"text-xs opacity-90\", className)} {...descriptionProps} />;\n}\n\nfunction BannerActions(props: DivProps) {\n const { className, asChild, ...actionsProps } = props;\n\n const ActionsPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ActionsPrimitive\n data-slot=\"banner-actions\"\n className={cn(\"flex items-center gap-2\", className)}\n {...actionsProps}\n />\n );\n}\n\ntype BannerCloseProps = React.ComponentProps<typeof Button>;\n\n/**\n * Nút đóng banner. Không truyền `children` thì vẽ icon X.\n *\n * ⚠ Khác thượng nguồn: có tên cho trình đọc màn hình — `aria-label` mặc định lấy khoá\n * `closeLabel` của namespace `banner` (`ComitorLocaleProvider`), ghi đè bằng chính prop\n * `aria-label`. Bản gốc là `Button` chỉ chứa icon X, tức một nút KHÔNG TÊN. Nếu `children` là\n * CHỮ nhìn thấy được thì truyền `aria-label` trùng với chữ đó (WCAG 2.5.3), hoặc\n * `aria-label={undefined}`.\n */\nfunction BannerClose(props: BannerCloseProps) {\n const { onClick: onClickProp, disabled, children, ...closeProps } = props;\n\n const { dismissible = DEFAULT_BANNER_DISMISSIBLE, onClose } = useBannerContext(\"BannerClose\");\n const t = useComitorMessages(\"banner\");\n\n const isDisabled = disabled ?? !dismissible;\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<CloseElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented || isDisabled) return;\n onClose?.();\n },\n [onClickProp, isDisabled, onClose]\n );\n\n return (\n <Button\n aria-label={t.closeLabel}\n data-slot=\"banner-close\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onClick}\n disabled={isDisabled}\n {...closeProps}\n >\n {children ?? <X aria-hidden=\"true\" className=\"size-3.5\" />}\n </Button>\n );\n}\n\nexport {\n Banner,\n BannerActions,\n BannerClose,\n type BannerCloseProps,\n BannerContent,\n type BannerData,\n BannerDescription,\n BannerIcon,\n type BannerProps,\n type BannerRenderProps,\n Banners,\n type BannersProps,\n BannerTitle,\n type BannerVariant,\n useBanner,\n useBanners\n};\n"],"mappings":"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","names":["banners"]}