@comitor/ui 2.1.0 → 3.0.1

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 (405) hide show
  1. package/README.md +63 -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 +5 -2
  66. package/dist/components/composite/toast.js +3 -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 +47 -3
  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 +10 -2
  93. package/dist/components/ui/circular-progress.js.map +1 -1
  94. package/dist/components/ui/command.d.ts +5 -4
  95. package/dist/components/ui/command.js +6 -2
  96. package/dist/components/ui/command.js.map +1 -1
  97. package/dist/components/ui/cropper-constants.d.ts +8 -3
  98. package/dist/components/ui/cropper-constants.js +2 -6
  99. package/dist/components/ui/cropper-constants.js.map +1 -1
  100. package/dist/components/ui/cropper.d.ts +2 -1
  101. package/dist/components/ui/cropper.js +2 -2
  102. package/dist/components/ui/cropper.js.map +1 -1
  103. package/dist/components/ui/dialog.d.ts +2 -2
  104. package/dist/components/ui/dialog.js +4 -1
  105. package/dist/components/ui/dialog.js.map +1 -1
  106. package/dist/components/ui/item.d.ts +2 -2
  107. package/dist/components/ui/kanban-constants.d.ts +7 -1
  108. package/dist/components/ui/kanban-constants.js +2 -17
  109. package/dist/components/ui/kanban-constants.js.map +1 -1
  110. package/dist/components/ui/kanban.d.ts +2 -2
  111. package/dist/components/ui/kanban.js +11 -5
  112. package/dist/components/ui/kanban.js.map +1 -1
  113. package/dist/components/ui/key-value-constants.d.ts +7 -3
  114. package/dist/components/ui/key-value-constants.js +2 -7
  115. package/dist/components/ui/key-value-constants.js.map +1 -1
  116. package/dist/components/ui/key-value.d.ts +5 -5
  117. package/dist/components/ui/key-value.js +72 -78
  118. package/dist/components/ui/key-value.js.map +1 -1
  119. package/dist/components/ui/mask-input-constants.d.ts +2 -3
  120. package/dist/components/ui/mask-input-constants.js +36 -77
  121. package/dist/components/ui/mask-input-constants.js.map +1 -1
  122. package/dist/components/ui/mask-input.d.ts +7 -4
  123. package/dist/components/ui/mask-input.js +22 -22
  124. package/dist/components/ui/mask-input.js.map +1 -1
  125. package/dist/components/ui/mention.d.ts +3 -2
  126. package/dist/components/ui/mention.js +4 -6
  127. package/dist/components/ui/mention.js.map +1 -1
  128. package/dist/components/ui/pagination.d.ts +13 -9
  129. package/dist/components/ui/pagination.js +15 -6
  130. package/dist/components/ui/pagination.js.map +1 -1
  131. package/dist/components/ui/phone-input-constants.d.ts +17 -6
  132. package/dist/components/ui/phone-input-constants.js +2 -5
  133. package/dist/components/ui/phone-input-constants.js.map +1 -1
  134. package/dist/components/ui/phone-input.d.ts +18 -11
  135. package/dist/components/ui/phone-input.js +14 -12
  136. package/dist/components/ui/phone-input.js.map +1 -1
  137. package/dist/components/ui/progress.d.ts +2 -2
  138. package/dist/components/ui/rating.d.ts +2 -1
  139. package/dist/components/ui/rating.js +8 -4
  140. package/dist/components/ui/rating.js.map +1 -1
  141. package/dist/components/ui/scroll-spy.js +3 -1
  142. package/dist/components/ui/scroll-spy.js.map +1 -1
  143. package/dist/components/ui/sheet.d.ts +2 -2
  144. package/dist/components/ui/sheet.js +10 -1
  145. package/dist/components/ui/sheet.js.map +1 -1
  146. package/dist/components/ui/slider.d.ts +1 -1
  147. package/dist/components/ui/slider.js +15 -1
  148. package/dist/components/ui/slider.js.map +1 -1
  149. package/dist/components/ui/sonner.js +2 -0
  150. package/dist/components/ui/sonner.js.map +1 -1
  151. package/dist/components/ui/spinner.js +4 -1
  152. package/dist/components/ui/spinner.js.map +1 -1
  153. package/dist/components/ui/stepper-constants.d.ts +7 -2
  154. package/dist/components/ui/stepper-constants.js +2 -5
  155. package/dist/components/ui/stepper-constants.js.map +1 -1
  156. package/dist/components/ui/stepper.d.ts +2 -1
  157. package/dist/components/ui/stepper.js +2 -2
  158. package/dist/components/ui/stepper.js.map +1 -1
  159. package/dist/components/ui/tags-input.d.ts +3 -2
  160. package/dist/components/ui/tags-input.js +4 -2
  161. package/dist/components/ui/tags-input.js.map +1 -1
  162. package/dist/components/ui/time-picker-constants.d.ts +12 -9
  163. package/dist/components/ui/time-picker-constants.js +2 -10
  164. package/dist/components/ui/time-picker-constants.js.map +1 -1
  165. package/dist/components/ui/time-picker.d.ts +11 -6
  166. package/dist/components/ui/time-picker.js +5 -7
  167. package/dist/components/ui/time-picker.js.map +1 -1
  168. package/dist/components/ui/toggle-group.js +12 -1
  169. package/dist/components/ui/toggle-group.js.map +1 -1
  170. package/dist/components/ui/toggle.d.ts +1 -1
  171. package/dist/components/ui/tour-constants.d.ts +7 -2
  172. package/dist/components/ui/tour-constants.js +2 -8
  173. package/dist/components/ui/tour-constants.js.map +1 -1
  174. package/dist/components/ui/tour.d.ts +3 -2
  175. package/dist/components/ui/tour.js +5 -4
  176. package/dist/components/ui/tour.js.map +1 -1
  177. package/dist/data-grid/data-grid-cell-variants.js +4 -4
  178. package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
  179. package/dist/data-grid/data-grid-column-header.js +1 -1
  180. package/dist/data-grid/data-grid-column-header.js.map +1 -1
  181. package/dist/data-grid/data-grid-filter-menu.d.ts +5 -4
  182. package/dist/data-grid/data-grid-filter-menu.js +13 -8
  183. package/dist/data-grid/data-grid-filter-menu.js.map +1 -1
  184. package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +4 -4
  185. package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -1
  186. package/dist/data-grid/data-grid-locale.d.ts +58 -6
  187. package/dist/data-grid/data-grid-locale.js +41 -9
  188. package/dist/data-grid/data-grid-locale.js.map +1 -1
  189. package/dist/data-grid/data-grid-row-height-menu.d.ts +4 -3
  190. package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
  191. package/dist/data-grid/data-grid-sort-menu.d.ts +4 -4
  192. package/dist/data-grid/data-grid-sort-menu.js +1 -1
  193. package/dist/data-grid/data-grid-sort-menu.js.map +1 -1
  194. package/dist/data-grid/data-grid-view-menu.d.ts +4 -4
  195. package/dist/data-grid/data-grid-view-menu.js.map +1 -1
  196. package/dist/data-grid/data-grid.d.ts +4 -3
  197. package/dist/data-grid/data-grid.js +2 -3
  198. package/dist/data-grid/data-grid.js.map +1 -1
  199. package/dist/data-grid/index.d.ts +1 -0
  200. package/dist/data-grid/index.js.map +1 -1
  201. package/dist/data-grid/messages-en.d.ts +23 -4
  202. package/dist/data-grid/messages-en.js +20 -0
  203. package/dist/data-grid/messages-en.js.map +1 -1
  204. package/dist/data-grid/messages-vi.d.ts +3 -1
  205. package/dist/data-grid/messages-vi.js +14 -0
  206. package/dist/data-grid/messages-vi.js.map +1 -1
  207. package/dist/data-grid/messages.d.ts +29 -3
  208. package/dist/data-grid/messages.js.map +1 -1
  209. package/dist/data-grid/sortable.js +16 -19
  210. package/dist/data-grid/sortable.js.map +1 -1
  211. package/dist/data-grid/types.d.ts +1 -1
  212. package/dist/data-grid/use-data-grid-undo-redo.d.ts +5 -4
  213. package/dist/data-grid/use-data-grid-undo-redo.js +2 -2
  214. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
  215. package/dist/data-grid/use-data-grid.d.ts +23 -8
  216. package/dist/data-grid/use-data-grid.js +8 -6
  217. package/dist/data-grid/use-data-grid.js.map +1 -1
  218. package/dist/data-grid/utils.d.ts +24 -4
  219. package/dist/data-grid/utils.js +15 -5
  220. package/dist/data-grid/utils.js.map +1 -1
  221. package/dist/i18n/locale-provider.d.ts +127 -0
  222. package/dist/i18n/locale-provider.js +47 -0
  223. package/dist/i18n/locale-provider.js.map +1 -0
  224. package/dist/i18n/locale.d.ts +27 -0
  225. package/dist/i18n/locale.js +10 -0
  226. package/dist/i18n/locale.js.map +1 -0
  227. package/dist/i18n/messages.d.ts +116 -0
  228. package/dist/i18n/messages.js +149 -0
  229. package/dist/i18n/messages.js.map +1 -0
  230. package/dist/i18n/namespaces/action-bar.d.ts +7 -0
  231. package/dist/i18n/namespaces/action-bar.js +15 -0
  232. package/dist/i18n/namespaces/action-bar.js.map +1 -0
  233. package/dist/i18n/namespaces/avatar-group.d.ts +14 -0
  234. package/dist/i18n/namespaces/avatar-group.js +13 -0
  235. package/dist/i18n/namespaces/avatar-group.js.map +1 -0
  236. package/dist/i18n/namespaces/badge-overflow.d.ts +12 -0
  237. package/dist/i18n/namespaces/badge-overflow.js +11 -0
  238. package/dist/i18n/namespaces/badge-overflow.js.map +1 -0
  239. package/dist/i18n/namespaces/banner.d.ts +9 -0
  240. package/dist/i18n/namespaces/banner.js +7 -0
  241. package/dist/i18n/namespaces/banner.js.map +1 -0
  242. package/dist/i18n/namespaces/breadcrumb.d.ts +11 -0
  243. package/dist/i18n/namespaces/breadcrumb.js +13 -0
  244. package/dist/i18n/namespaces/breadcrumb.js.map +1 -0
  245. package/dist/i18n/namespaces/calendar.d.ts +40 -0
  246. package/dist/i18n/namespaces/calendar.js +25 -0
  247. package/dist/i18n/namespaces/calendar.js.map +1 -0
  248. package/dist/i18n/namespaces/carousel.d.ts +15 -0
  249. package/dist/i18n/namespaces/carousel.js +17 -0
  250. package/dist/i18n/namespaces/carousel.js.map +1 -0
  251. package/dist/i18n/namespaces/combobox.d.ts +21 -0
  252. package/dist/i18n/namespaces/combobox.js +23 -0
  253. package/dist/i18n/namespaces/combobox.js.map +1 -0
  254. package/dist/i18n/namespaces/command-palette.d.ts +20 -0
  255. package/dist/i18n/namespaces/command-palette.js +19 -0
  256. package/dist/i18n/namespaces/command-palette.js.map +1 -0
  257. package/dist/i18n/namespaces/command.d.ts +11 -0
  258. package/dist/i18n/namespaces/command.js +13 -0
  259. package/dist/i18n/namespaces/command.js.map +1 -0
  260. package/dist/i18n/namespaces/confirm-dialog.d.ts +13 -0
  261. package/dist/i18n/namespaces/confirm-dialog.js +15 -0
  262. package/dist/i18n/namespaces/confirm-dialog.js.map +1 -0
  263. package/dist/i18n/namespaces/contrast.d.ts +7 -0
  264. package/dist/i18n/namespaces/contrast.js +13 -0
  265. package/dist/i18n/namespaces/contrast.js.map +1 -0
  266. package/dist/i18n/namespaces/copy-button.d.ts +11 -0
  267. package/dist/i18n/namespaces/copy-button.js +13 -0
  268. package/dist/i18n/namespaces/copy-button.js.map +1 -0
  269. package/dist/i18n/namespaces/cropper.d.ts +7 -0
  270. package/dist/i18n/namespaces/cropper.js +17 -0
  271. package/dist/i18n/namespaces/cropper.js.map +1 -0
  272. package/dist/i18n/namespaces/data-table.d.ts +7 -0
  273. package/dist/i18n/namespaces/data-table.js +23 -0
  274. package/dist/i18n/namespaces/data-table.js.map +1 -0
  275. package/dist/i18n/namespaces/date-picker.d.ts +42 -0
  276. package/dist/i18n/namespaces/date-picker.js +35 -0
  277. package/dist/i18n/namespaces/date-picker.js.map +1 -0
  278. package/dist/i18n/namespaces/dialog.d.ts +9 -0
  279. package/dist/i18n/namespaces/dialog.js +7 -0
  280. package/dist/i18n/namespaces/dialog.js.map +1 -0
  281. package/dist/i18n/namespaces/display-axis.d.ts +25 -0
  282. package/dist/i18n/namespaces/display-axis.js +21 -0
  283. package/dist/i18n/namespaces/display-axis.js.map +1 -0
  284. package/dist/i18n/namespaces/file-upload.d.ts +11 -0
  285. package/dist/i18n/namespaces/file-upload.js +19 -0
  286. package/dist/i18n/namespaces/file-upload.js.map +1 -0
  287. package/dist/i18n/namespaces/filter-chips.d.ts +11 -0
  288. package/dist/i18n/namespaces/filter-chips.js +13 -0
  289. package/dist/i18n/namespaces/filter-chips.js.map +1 -0
  290. package/dist/i18n/namespaces/image-upload-field.d.ts +12 -0
  291. package/dist/i18n/namespaces/image-upload-field.js +23 -0
  292. package/dist/i18n/namespaces/image-upload-field.js.map +1 -0
  293. package/dist/i18n/namespaces/inline-edit.d.ts +15 -0
  294. package/dist/i18n/namespaces/inline-edit.js +17 -0
  295. package/dist/i18n/namespaces/inline-edit.js.map +1 -0
  296. package/dist/i18n/namespaces/kanban.d.ts +11 -0
  297. package/dist/i18n/namespaces/kanban.js +39 -0
  298. package/dist/i18n/namespaces/kanban.js.map +1 -0
  299. package/dist/i18n/namespaces/key-value.d.ts +10 -0
  300. package/dist/i18n/namespaces/key-value.js +19 -0
  301. package/dist/i18n/namespaces/key-value.js.map +1 -0
  302. package/dist/i18n/namespaces/mention.d.ts +9 -0
  303. package/dist/i18n/namespaces/mention.js +7 -0
  304. package/dist/i18n/namespaces/mention.js.map +1 -0
  305. package/dist/i18n/namespaces/pagination.d.ts +19 -0
  306. package/dist/i18n/namespaces/pagination.js +21 -0
  307. package/dist/i18n/namespaces/pagination.js.map +1 -0
  308. package/dist/i18n/namespaces/phone-input.d.ts +10 -0
  309. package/dist/i18n/namespaces/phone-input.js +17 -0
  310. package/dist/i18n/namespaces/phone-input.js.map +1 -0
  311. package/dist/i18n/namespaces/rating.d.ts +12 -0
  312. package/dist/i18n/namespaces/rating.js +12 -0
  313. package/dist/i18n/namespaces/rating.js.map +1 -0
  314. package/dist/i18n/namespaces/route-tabs.d.ts +9 -0
  315. package/dist/i18n/namespaces/route-tabs.js +7 -0
  316. package/dist/i18n/namespaces/route-tabs.js.map +1 -0
  317. package/dist/i18n/namespaces/scroll-spy.d.ts +9 -0
  318. package/dist/i18n/namespaces/scroll-spy.js +7 -0
  319. package/dist/i18n/namespaces/scroll-spy.js.map +1 -0
  320. package/dist/i18n/namespaces/search-filter-bar.d.ts +15 -0
  321. package/dist/i18n/namespaces/search-filter-bar.js +32 -0
  322. package/dist/i18n/namespaces/search-filter-bar.js.map +1 -0
  323. package/dist/i18n/namespaces/sheet.d.ts +9 -0
  324. package/dist/i18n/namespaces/sheet.js +7 -0
  325. package/dist/i18n/namespaces/sheet.js.map +1 -0
  326. package/dist/i18n/namespaces/shell.d.ts +21 -0
  327. package/dist/i18n/namespaces/shell.js +75 -0
  328. package/dist/i18n/namespaces/shell.js.map +1 -0
  329. package/dist/i18n/namespaces/spinner.d.ts +9 -0
  330. package/dist/i18n/namespaces/spinner.js +7 -0
  331. package/dist/i18n/namespaces/spinner.js.map +1 -0
  332. package/dist/i18n/namespaces/stepper.d.ts +7 -0
  333. package/dist/i18n/namespaces/stepper.js +15 -0
  334. package/dist/i18n/namespaces/stepper.js.map +1 -0
  335. package/dist/i18n/namespaces/table-pagination.d.ts +7 -0
  336. package/dist/i18n/namespaces/table-pagination.js +29 -0
  337. package/dist/i18n/namespaces/table-pagination.js.map +1 -0
  338. package/dist/i18n/namespaces/tags-input.d.ts +12 -0
  339. package/dist/i18n/namespaces/tags-input.js +11 -0
  340. package/dist/i18n/namespaces/tags-input.js.map +1 -0
  341. package/dist/i18n/namespaces/time-picker.d.ts +15 -0
  342. package/dist/i18n/namespaces/time-picker.js +25 -0
  343. package/dist/i18n/namespaces/time-picker.js.map +1 -0
  344. package/dist/i18n/namespaces/toast.d.ts +16 -0
  345. package/dist/i18n/namespaces/toast.js +7 -0
  346. package/dist/i18n/namespaces/toast.js.map +1 -0
  347. package/dist/i18n/namespaces/tour.d.ts +7 -0
  348. package/dist/i18n/namespaces/tour.js +21 -0
  349. package/dist/i18n/namespaces/tour.js.map +1 -0
  350. package/dist/index.d.ts +32 -0
  351. package/dist/index.js +24 -1
  352. package/dist/index.js.map +1 -1
  353. package/dist/lib/toast-language.d.ts +8 -0
  354. package/dist/lib/toast-language.js +19 -0
  355. package/dist/lib/toast-language.js.map +1 -0
  356. package/dist/shell/app-header.js.map +1 -1
  357. package/dist/shell/app-shell.d.ts +20 -2
  358. package/dist/shell/app-shell.js +4 -1
  359. package/dist/shell/app-shell.js.map +1 -1
  360. package/dist/shell/contrast-constants.d.ts +9 -1
  361. package/dist/shell/contrast-constants.js +2 -4
  362. package/dist/shell/contrast-constants.js.map +1 -1
  363. package/dist/shell/contrast-toggle.d.ts +4 -1
  364. package/dist/shell/contrast-toggle.js +2 -2
  365. package/dist/shell/contrast-toggle.js.map +1 -1
  366. package/dist/shell/display-axis-constants.d.ts +2 -1
  367. package/dist/shell/display-axis-constants.js.map +1 -1
  368. package/dist/shell/display-axis-controls.d.ts +15 -6
  369. package/dist/shell/display-axis-controls.js +11 -9
  370. package/dist/shell/display-axis-controls.js.map +1 -1
  371. package/dist/shell/display-axis-provider.d.ts +1 -1
  372. package/dist/shell/mobile-menu.js.map +1 -1
  373. package/dist/shell/notifications-button.d.ts +1 -1
  374. package/dist/shell/notifications-button.js +3 -2
  375. package/dist/shell/notifications-button.js.map +1 -1
  376. package/dist/shell/shell-context.d.ts +4 -1
  377. package/dist/shell/shell-context.js +2 -4
  378. package/dist/shell/shell-context.js.map +1 -1
  379. package/dist/shell/sidebar.d.ts +1 -1
  380. package/dist/shell/sidebar.js.map +1 -1
  381. package/dist/shell/theme-toggle.d.ts +6 -3
  382. package/dist/shell/theme-toggle.js +2 -2
  383. package/dist/shell/theme-toggle.js.map +1 -1
  384. package/dist/shell/types.d.ts +14 -8
  385. package/dist/shell/types.js +2 -35
  386. package/dist/shell/types.js.map +1 -1
  387. package/dist/shell/user-menu.d.ts +6 -6
  388. package/dist/shell/user-menu.js.map +1 -1
  389. package/dist/shell/utils.d.ts +1 -1
  390. package/dist/shell/utils.js.map +1 -1
  391. package/dist/shell/workspace-switcher.js +1 -1
  392. package/dist/shell/workspace-switcher.js.map +1 -1
  393. package/dist/uploader/file-upload-constants.d.ts +13 -0
  394. package/dist/uploader/file-upload-constants.js +4 -16
  395. package/dist/uploader/file-upload-constants.js.map +1 -1
  396. package/dist/uploader/file-upload.d.ts +4 -1
  397. package/dist/uploader/file-upload.js +2 -2
  398. package/dist/uploader/file-upload.js.map +1 -1
  399. package/dist/uploader/image-upload-field.d.ts +5 -1
  400. package/dist/uploader/image-upload-field.js +2 -2
  401. package/dist/uploader/image-upload-field.js.map +1 -1
  402. package/dist/uploader/use-file-upload.d.ts +4 -0
  403. package/dist/uploader/use-file-upload.js +6 -2
  404. package/dist/uploader/use-file-upload.js.map +1 -1
  405. package/package.json +4 -3
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/mask-input-constants.ts"],"sourcesContent":["/*\n * Port từ Dice UI (https://diceui.com/docs/components/mask-input) — 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\n/**\n * Lõi THUẦN của `MaskInput`: bảng mẫu `MASK_PATTERNS` và các hàm định dạng/bóc mặt nạ.\n *\n * File này là NỬA TRÊN của `mask-input.tsx` thượng nguồn (mọi thứ đứng trước component), tách ra\n * nguyên khối, đúng thứ tự, để còn diff ngược được. Nó **CỐ Ý không có `\"use client\"`**: mọi\n * export của một file `\"use client\"` tới Server Component là client-reference proxy — `MASK_PATTERNS`\n * sẽ đọc ra `undefined` mà không throw — và `applyCurrencyMask` là thứ trang server hay cần nhất\n * (in số tiền VND ra bảng, email, PDF) nên phải chạy được ở cả hai phía.\n *\n * ⚠ Barrel `src/index.ts` xuất module này TRỰC TIẾP, và CHỌN LỌC (không `export *`): ngoài các tên\n * công khai của thượng nguồn, file còn export vài helper NỘI BỘ cho riêng `mask-input.tsx`\n * (liệt kê ở khối export cuối file). `mask-input.tsx` chỉ `import`, KHÔNG `export` lại.\n */\n\nconst PAST_YEARS_LIMIT = 120;\nconst FUTURE_YEARS_LIMIT = 10;\n/*\n * ⚠ Khác thượng nguồn: thượng nguồn mặc định \"USD\" / \"en-US\". Comitor mặc định tiền Việt — giá\n * trị hợp đồng CRM tính bằng VND. Prop `currency` / `locale` của `MaskInput` vẫn đổi được.\n */\nconst DEFAULT_CURRENCY = \"VND\";\nconst DEFAULT_LOCALE = \"vi-VN\";\n\nconst NUMERIC_MASK_PATTERNS = /^(phone|zipCode|zipCodeExtended|ssn|ein|time|date|creditCard|creditCardExpiry)$/;\nconst CURRENCY_PERCENTAGE_SYMBOLS = /[€$%]/;\n\ninterface CurrencySymbols {\n currency: string;\n decimal: string;\n group: string;\n}\n\nconst formattersCache = new Map<string, Intl.NumberFormat>();\nconst currencyAtEndCache = new Map<string, boolean>();\nconst currencySymbolsCache = new Map<string, CurrencySymbols>();\nconst fractionDigitsCache = new Map<string, number>();\nconst daysInMonthCache = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] as const;\n\nconst REGEX_CACHE = {\n digitsOnly: /^\\d+$/,\n nonDigits: /\\D/g,\n nonAlphaNumeric: /[^A-Z0-9]/gi,\n nonNumericDot: /[^0-9.]/g,\n nonCurrencyChars: /[^\\d.,]/g,\n hashPattern: /#/g,\n currencyAtEnd: /\\d\\s*[^\\d\\s]+$/,\n percentageChars: /[^\\d.]/g,\n phone: /^\\d{10}$/,\n ssn: /^\\d{9}$/,\n zipCode: /^\\d{5}$/,\n zipCodeExtended: /^\\d{9}$/,\n isbn: /^\\d{13}$/,\n ein: /^\\d{9}$/,\n time: /^\\d{4}$/,\n creditCard: /^\\d{13,19}$/,\n creditCardExpiry: /^\\d{4}$/,\n licensePlate: /^[A-Z0-9]{6}$/,\n macAddress: /^[A-F0-9]{12}$/,\n currencyValidation: /^\\d+(\\.\\d{1,2})?$/,\n ipv4Segment: /^\\d{1,3}$/\n} as const;\n\nfunction getCachedFormatter(locale: string | undefined, opts: Intl.NumberFormatOptions): Intl.NumberFormat {\n const { currency, minimumFractionDigits = 0, maximumFractionDigits = 2 } = opts;\n\n const key = `${locale}|${currency}|${minimumFractionDigits}|${maximumFractionDigits}`;\n\n if (!formattersCache.has(key)) {\n try {\n formattersCache.set(\n key,\n new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n ...opts\n })\n );\n } catch {\n formattersCache.set(\n key,\n new Intl.NumberFormat(DEFAULT_LOCALE, {\n style: \"currency\",\n currency: DEFAULT_CURRENCY,\n ...opts\n })\n );\n }\n }\n\n const formatter = formattersCache.get(key);\n if (!formatter) {\n throw new Error(`Không tạo được bộ định dạng tiền tệ cho ${key}`);\n }\n return formatter;\n}\n\nfunction getCachedCurrencySymbols(opts: TransformOptions): CurrencySymbols {\n const { locale, currency } = opts;\n\n const key = `${locale}|${currency}`;\n const cached = currencySymbolsCache.get(key);\n if (cached) {\n return cached;\n }\n\n let currencySymbol = \"$\";\n let decimalSeparator = \".\";\n let groupSeparator = \",\";\n\n try {\n const formatter = getCachedFormatter(locale, {\n currency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 2\n });\n const parts = formatter.formatToParts(1234.5);\n const currencyPart = parts.find((part) => part.type === \"currency\");\n const decimalPart = parts.find((part) => part.type === \"decimal\");\n const groupPart = parts.find((part) => part.type === \"group\");\n\n if (currencyPart) currencySymbol = currencyPart.value;\n if (decimalPart) decimalSeparator = decimalPart.value;\n if (groupPart) groupSeparator = groupPart.value;\n } catch {\n // Giữ giá trị mặc định.\n }\n\n const symbols: CurrencySymbols = {\n currency: currencySymbol,\n decimal: decimalSeparator,\n group: groupSeparator\n };\n currencySymbolsCache.set(key, symbols);\n return symbols;\n}\n\n/**\n * Số chữ số thập phân TỰ NHIÊN của đồng tiền theo ISO 4217 / CLDR: VND 0, JPY 0, USD 2, EUR 2.\n * Chặn trần ở 2 vì phần còn lại của logic (validate, cắt phần lẻ) chỉ hiểu tối đa 2 chữ số.\n *\n * ⚠ Khác thượng nguồn — hàm MỚI. Thượng nguồn ghim cứng `maximumFractionDigits: 2` cho mọi đồng\n * tiền, và với locale dùng `,` làm dấu thập phân (de-DE, vi-VN) thì `transform` còn đoán \"chữ số\n * gõ sau một nhóm nghìn là phần lẻ\". Với VND, đo trên chính mã thượng nguồn, gõ lần lượt\n * `1234567` ra:\n *\n * 1 ₫ → 12 ₫ → 123 ₫ → 1.234 ₫ → 1.234,5 ₫ → 1.234,56 ₫ → 1.234.567 ₫\n *\n * tức chữ số thứ 5 và 6 biến thành HÀO, và giá trị thô báo ra `onValueChange` là \"1234.5\" trong\n * khi người dùng đang gõ 12.345 ₫. Đồng tiền không có phần lẻ thì mọi `.`/`,` đều là dấu nhóm.\n */\nfunction getCurrencyFractionDigits(opts: TransformOptions): number {\n const { locale, currency } = opts;\n\n const key = `${locale}|${currency}`;\n const cached = fractionDigitsCache.get(key);\n if (cached !== undefined) {\n return cached;\n }\n\n let digits = 2;\n try {\n const resolved = new Intl.NumberFormat(locale, { style: \"currency\", currency }).resolvedOptions();\n digits = Math.min(2, resolved.maximumFractionDigits ?? 2);\n } catch {\n // Đồng tiền/locale không hợp lệ → giữ 2 như thượng nguồn.\n }\n\n fractionDigitsCache.set(key, digits);\n return digits;\n}\n\nfunction isCurrencyAtEnd(opts: TransformOptions): boolean {\n const { locale, currency } = opts;\n\n const key = `${locale}|${currency}`;\n const cached = currencyAtEndCache.get(key);\n if (cached !== undefined) {\n return cached;\n }\n\n try {\n const formatter = getCachedFormatter(locale, {\n currency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 0\n });\n const sample = formatter.format(123);\n const result = REGEX_CACHE.currencyAtEnd.test(sample);\n currencyAtEndCache.set(key, result);\n return result;\n } catch {\n currencyAtEndCache.set(key, false);\n return false;\n }\n}\n\nfunction isCurrencyMask(opts: { mask: MaskPatternKey | MaskPattern | undefined; pattern?: string }): boolean {\n const { mask, pattern } = opts;\n\n return mask === \"currency\" || Boolean(pattern && (pattern.includes(\"$\") || pattern.includes(\"€\")));\n}\n\ninterface TransformOptions {\n currency?: string;\n locale?: string;\n}\n\ninterface ValidateOptions {\n min?: number;\n max?: number;\n}\n\ninterface MaskPattern {\n pattern: string;\n transform?: (value: string, opts?: TransformOptions) => string;\n validate?: (value: string, opts?: ValidateOptions) => boolean;\n}\n\ntype MaskPatternKey =\n | \"phone\"\n | \"ssn\"\n | \"date\"\n | \"time\"\n | \"creditCard\"\n | \"creditCardExpiry\"\n | \"zipCode\"\n | \"zipCodeExtended\"\n | \"currency\"\n | \"percentage\"\n | \"licensePlate\"\n | \"ipv4\"\n | \"macAddress\"\n | \"isbn\"\n | \"ein\";\n\n/**\n * Các mẫu dựng sẵn — giữ NGUYÊN bộ của thượng nguồn, không thêm không bớt. Nhiều mẫu là định dạng\n * Hoa Kỳ (`phone`, `ssn`, `date`, `zipCode`, `zipCodeExtended`, `ein`); app Comitor cần định dạng\n * khác thì truyền một `MaskPattern` riêng vào prop `mask`.\n */\nconst MASK_PATTERNS: Record<MaskPatternKey, MaskPattern> = {\n /**\n * Số điện thoại **Hoa Kỳ** `(###) ###-####`, đúng 10 chữ số.\n *\n * ⚠ Không dùng cho số điện thoại ở app Comitor: số Việt Nam (và mọi số quốc tế) đi qua\n * `PhoneInput` — có chọn quốc gia, tự nhận mã vùng và lưu dạng `+84…`.\n */\n phone: {\n pattern: \"(###) ###-####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.phone.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n ssn: {\n pattern: \"###-##-####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.ssn.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n /**\n * Ngày kiểu **Hoa Kỳ** `MM/DD/YYYY` — `validate` đọc 2 chữ số ĐẦU là THÁNG. Ngày kiểu Việt Nam\n * (`dd/MM/yyyy`) thì dùng `DatePicker` của gói, hoặc một `MaskPattern` riêng.\n */\n date: {\n pattern: \"##/##/####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => {\n const cleaned = value.replace(REGEX_CACHE.nonDigits, \"\");\n if (cleaned.length !== 8) return false;\n const month = Number.parseInt(cleaned.substring(0, 2), 10);\n const day = Number.parseInt(cleaned.substring(2, 4), 10);\n const year = Number.parseInt(cleaned.substring(4, 8), 10);\n\n const currentYear = new Date().getFullYear();\n const minYear = currentYear - PAST_YEARS_LIMIT;\n const maxYear = currentYear + FUTURE_YEARS_LIMIT;\n if (month < 1 || month > 12 || day < 1 || year < minYear || year > maxYear) return false;\n\n const maxDays =\n month === 2 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0)\n ? 29\n : (daysInMonthCache[month - 1] ?? 31);\n\n return day <= maxDays;\n }\n },\n time: {\n pattern: \"##:##\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => {\n const cleaned = value.replace(REGEX_CACHE.nonDigits, \"\");\n if (!REGEX_CACHE.time.test(cleaned)) return false;\n const hours = Number.parseInt(cleaned.substring(0, 2), 10);\n const minutes = Number.parseInt(cleaned.substring(2, 4), 10);\n return hours <= 23 && minutes <= 59;\n }\n },\n creditCard: {\n pattern: \"#### #### #### ####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => {\n const cleaned = value.replace(REGEX_CACHE.nonDigits, \"\");\n if (!REGEX_CACHE.creditCard.test(cleaned)) return false;\n\n let sum = 0;\n let isEven = false;\n for (let i = cleaned.length - 1; i >= 0; i--) {\n const digitChar = cleaned[i];\n if (!digitChar) continue;\n let digit = Number.parseInt(digitChar, 10);\n if (isEven) {\n digit *= 2;\n if (digit > 9) {\n digit -= 9;\n }\n }\n sum += digit;\n isEven = !isEven;\n }\n return sum % 10 === 0;\n }\n },\n creditCardExpiry: {\n pattern: \"##/##\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => {\n const cleaned = value.replace(REGEX_CACHE.nonDigits, \"\");\n if (!REGEX_CACHE.creditCardExpiry.test(cleaned)) return false;\n\n const month = Number.parseInt(cleaned.substring(0, 2), 10);\n const year = Number.parseInt(cleaned.substring(2, 4), 10);\n\n if (month < 1 || month > 12) return false;\n\n const now = new Date();\n const currentYear = now.getFullYear();\n const currentMonth = now.getMonth() + 1;\n\n const fullYear = year <= 75 ? 2000 + year : 1900 + year;\n\n if (fullYear < currentYear || (fullYear === currentYear && month < currentMonth)) {\n return false;\n }\n\n const maxYear = currentYear + 50;\n if (fullYear > maxYear) {\n return false;\n }\n\n return true;\n }\n },\n zipCode: {\n pattern: \"#####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.zipCode.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n zipCodeExtended: {\n pattern: \"#####-####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.zipCodeExtended.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n /**\n * Tiền tệ — định dạng thật đi qua `Intl.NumberFormat` theo `currency`/`locale` (mặc định VND /\n * vi-VN → `1.234.567 ₫`). Chuỗi `pattern` ở đây chỉ là DẤU NHẬN DẠNG cho nhánh tiền tệ, không\n * phải khuôn chữ số.\n */\n currency: {\n pattern: \"$###,###.##\",\n transform: (value, { currency = DEFAULT_CURRENCY, locale = DEFAULT_LOCALE } = {}) => {\n // ⚠ Khác thượng nguồn: đồng tiền không có phần lẻ (VND, JPY) → mọi `.`/`,` là dấu nhóm,\n // giá trị thô chỉ còn chữ số. Lý do: xem `getCurrencyFractionDigits`.\n if (getCurrencyFractionDigits({ currency, locale }) === 0) {\n return value.replace(REGEX_CACHE.nonDigits, \"\");\n }\n\n let localeDecimalSeparator = \".\";\n\n try {\n const formatter = getCachedFormatter(locale, {\n currency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 2\n });\n const parts = formatter.formatToParts(1234.5);\n const decimalPart = parts.find((part) => part.type === \"decimal\");\n\n if (decimalPart) localeDecimalSeparator = decimalPart.value;\n } catch {\n // Giữ giá trị mặc định.\n }\n\n const cleaned = value.replace(REGEX_CACHE.nonCurrencyChars, \"\");\n\n const dotIndex = cleaned.indexOf(\".\");\n const commaIndex = cleaned.indexOf(\",\");\n\n let hasDecimalSeparator = false;\n let decimalIndex = -1;\n\n if (localeDecimalSeparator === \",\") {\n const lastCommaIndex = cleaned.lastIndexOf(\",\");\n if (lastCommaIndex !== -1) {\n const afterComma = cleaned.substring(lastCommaIndex + 1);\n if (afterComma.length <= 2 && /^\\d*$/.test(afterComma)) {\n hasDecimalSeparator = true;\n decimalIndex = lastCommaIndex;\n }\n }\n\n if (!hasDecimalSeparator && dotIndex !== -1) {\n const afterDot = cleaned.substring(dotIndex + 1);\n if (afterDot.length <= 2 && /^\\d*$/.test(afterDot)) {\n hasDecimalSeparator = true;\n decimalIndex = dotIndex;\n }\n }\n\n if (!hasDecimalSeparator && cleaned.length >= 4) {\n const match = cleaned.match(/^(\\d+)\\.(\\d{3})(\\d{1,2})$/);\n if (match) {\n const [, beforeDot, thousandsPart, decimalPart] = match;\n const integerPart = (beforeDot ?? \"\") + (thousandsPart ?? \"\");\n const result = `${integerPart}.${decimalPart}`;\n return result;\n }\n }\n } else {\n const lastDotIndex = cleaned.lastIndexOf(\".\");\n if (lastDotIndex !== -1) {\n const afterDot = cleaned.substring(lastDotIndex + 1);\n if (afterDot.length <= 2 && /^\\d*$/.test(afterDot)) {\n hasDecimalSeparator = true;\n decimalIndex = lastDotIndex;\n }\n }\n\n if (!hasDecimalSeparator && commaIndex !== -1) {\n const afterComma = cleaned.substring(commaIndex + 1);\n const looksLikeThousands = commaIndex <= 3 && afterComma.length >= 3;\n if (!looksLikeThousands && afterComma.length <= 2 && /^\\d*$/.test(afterComma)) {\n hasDecimalSeparator = true;\n decimalIndex = commaIndex;\n }\n }\n }\n\n if (hasDecimalSeparator && decimalIndex !== -1) {\n const beforeDecimal = cleaned.substring(0, decimalIndex).replace(/[.,]/g, \"\");\n const afterDecimal = cleaned.substring(decimalIndex + 1).replace(/[.,]/g, \"\");\n\n if (afterDecimal === \"\") {\n const result = `${beforeDecimal}.`;\n return result;\n }\n\n const result = `${beforeDecimal}.${afterDecimal.substring(0, 2)}`;\n return result;\n }\n\n const digitsOnly = cleaned.replace(/[.,]/g, \"\");\n return digitsOnly;\n },\n validate: (value) => {\n if (!REGEX_CACHE.currencyValidation.test(value)) return false;\n const num = Number.parseFloat(value);\n return !Number.isNaN(num) && num >= 0;\n }\n },\n /**\n * Phần trăm `##.##%` — dấu thập phân là `.` cố định, KHÔNG theo `locale` (dấu `,` bị bỏ qua).\n */\n percentage: {\n pattern: \"##.##%\",\n transform: (value) => {\n const cleaned = value.replace(REGEX_CACHE.percentageChars, \"\");\n const parts = cleaned.split(\".\");\n if (parts.length > 2) {\n return `${parts[0]}.${parts.slice(1).join(\"\")}`;\n }\n if (parts[1] && parts[1].length > 2) {\n return `${parts[0]}.${parts[1].substring(0, 2)}`;\n }\n return cleaned;\n },\n validate: (value, opts = {}) => {\n const num = Number.parseFloat(value);\n const min = opts.min ?? 0;\n const max = opts.max ?? 100;\n return !Number.isNaN(num) && num >= min && num <= max;\n }\n },\n licensePlate: {\n pattern: \"###-###\",\n transform: (value) => value.replace(REGEX_CACHE.nonAlphaNumeric, \"\").toUpperCase(),\n validate: (value) => REGEX_CACHE.licensePlate.test(value)\n },\n ipv4: {\n pattern: \"###.###.###.###\",\n transform: (value) => value.replace(REGEX_CACHE.nonNumericDot, \"\"),\n validate: (value) => {\n if (value.includes(\".\")) {\n const segments = value.split(\".\");\n if (segments.length > 4) return false;\n\n return segments.every((segment) => {\n if (segment === \"\") return true;\n if (!REGEX_CACHE.ipv4Segment.test(segment)) return false;\n const num = Number.parseInt(segment, 10);\n return num <= 255;\n });\n }\n\n if (!REGEX_CACHE.digitsOnly.test(value)) return false;\n if (value.length > 12) return false;\n\n const chunks = [];\n for (let i = 0; i < value.length; i += 3) {\n chunks.push(value.substring(i, i + 3));\n }\n\n if (chunks.length > 4) return false;\n\n return chunks.every((chunk) => {\n const num = Number.parseInt(chunk, 10);\n return num >= 0 && num <= 255;\n });\n }\n },\n macAddress: {\n pattern: \"##:##:##:##:##:##\",\n transform: (value) => value.replace(REGEX_CACHE.nonAlphaNumeric, \"\").toUpperCase(),\n validate: (value) => REGEX_CACHE.macAddress.test(value)\n },\n isbn: {\n pattern: \"###-#-###-#####-#\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.isbn.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n ein: {\n pattern: \"##-#######\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.ein.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n }\n};\n\nfunction applyMask(opts: {\n value: string;\n pattern: string;\n currency?: string;\n locale?: string;\n mask?: MaskPatternKey | MaskPattern;\n}): string {\n const { value, pattern, currency, locale, mask } = opts;\n\n const cleanValue = value;\n\n if (pattern.includes(\"$\") || pattern.includes(\"€\") || mask === \"currency\") {\n return applyCurrencyMask({\n value: cleanValue,\n currency: currency ?? DEFAULT_CURRENCY,\n locale: locale ?? DEFAULT_LOCALE\n });\n }\n\n if (pattern.includes(\"%\")) {\n return applyPercentageMask(cleanValue);\n }\n\n if (mask === \"ipv4\") {\n return cleanValue;\n }\n\n const maskedChars: string[] = [];\n let valueIndex = 0;\n\n for (let i = 0; i < pattern.length && valueIndex < cleanValue.length; i++) {\n const patternChar = pattern[i];\n const valueChar = cleanValue[valueIndex];\n\n if (patternChar === \"#\" && valueChar) {\n maskedChars.push(valueChar);\n valueIndex++;\n } else if (patternChar) {\n maskedChars.push(patternChar);\n }\n }\n\n return maskedChars.join(\"\");\n}\n\nfunction applyCurrencyMask(opts: { value: string; currency?: string; locale?: string }): string {\n const { value, currency = DEFAULT_CURRENCY, locale = DEFAULT_LOCALE } = opts;\n\n if (!value) return \"\";\n\n const {\n currency: currencySymbol,\n decimal: decimalSeparator,\n group: groupSeparator\n } = getCachedCurrencySymbols({ locale, currency });\n\n const normalizedValue = value\n .replace(new RegExp(`\\\\${groupSeparator.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}`, \"g\"), \"\")\n .replace(decimalSeparator, \".\");\n\n const parts = normalizedValue.split(\".\");\n const integerPart = parts[0] ?? \"\";\n const fractionalPart = parts[1] ?? \"\";\n\n if (!integerPart && !fractionalPart) return \"\";\n\n // ⚠ Khác thượng nguồn: cắt phần lẻ theo số chữ số TỰ NHIÊN của đồng tiền (VND 0) thay vì ghim\n // cứng 2, và đồng tiền không có phần lẻ thì không bao giờ vẽ dấu thập phân treo (\"1.234, ₫\").\n const fractionDigits = getCurrencyFractionDigits({ currency, locale });\n\n const intValue = integerPart ?? \"0\";\n const fracValue = fractionalPart.slice(0, fractionDigits);\n\n const num = Number(`${intValue}.${fracValue ?? \"\"}`);\n\n if (Number.isNaN(num)) {\n const cleanedDigits = value.replace(/[^\\d]/g, \"\");\n if (!cleanedDigits) return \"\";\n return `${currencySymbol}${cleanedDigits}`;\n }\n\n const hasExplicitDecimal = fractionDigits > 0 && (value.includes(\".\") || value.includes(decimalSeparator));\n\n try {\n const formatter = getCachedFormatter(locale, {\n currency,\n minimumFractionDigits: fracValue ? fracValue.length : 0,\n maximumFractionDigits: 2\n });\n const result = formatter.format(num);\n\n if (hasExplicitDecimal && !fracValue) {\n if (result.match(/^[^\\d\\s]+/)) {\n const finalResult = result.replace(/(\\d)$/, `$1${decimalSeparator}`);\n return finalResult;\n }\n\n const finalResult = result.replace(/(\\d)(\\s*)([^\\d\\s]+)$/, `$1${decimalSeparator}$2$3`);\n return finalResult;\n }\n\n return result;\n } catch {\n const formattedInt = intValue.replace(/\\B(?=(\\d{3})+(?!\\d))/g, groupSeparator);\n let result = `${currencySymbol}${formattedInt}`;\n if (hasExplicitDecimal) {\n result += `${decimalSeparator}${fracValue}`;\n }\n\n return result;\n }\n}\n\nfunction applyPercentageMask(value: string): string {\n if (!value) return \"\";\n\n const parts = value.split(\".\");\n let result = parts[0] ?? \"0\";\n\n if (value.includes(\".\")) {\n result += `.${(parts[1] ?? \"\").substring(0, 2)}`;\n }\n\n return `${result}%`;\n}\n\nfunction getUnmaskedValue(opts: {\n value: string;\n currency?: string;\n locale?: string;\n transform?: (value: string, opts?: TransformOptions) => string;\n}): string {\n const { value, transform, currency, locale } = opts;\n\n return transform ? transform(value, { currency, locale }) : value.replace(REGEX_CACHE.nonDigits, \"\");\n}\n\nfunction toUnmaskedIndex(opts: { masked: string; pattern: string; caret: number }): number {\n const { masked, pattern, caret } = opts;\n\n let idx = 0;\n for (let i = 0; i < caret && i < masked.length && i < pattern.length; i++) {\n if (pattern[i] === \"#\") {\n idx++;\n }\n }\n\n return idx;\n}\n\nfunction fromUnmaskedIndex(opts: { masked: string; pattern: string; unmaskedIndex: number }): number {\n const { masked, pattern, unmaskedIndex } = opts;\n\n let seen = 0;\n for (let i = 0; i < masked.length && i < pattern.length; i++) {\n if (pattern[i] === \"#\") {\n seen++;\n if (seen === unmaskedIndex) {\n return i + 1;\n }\n }\n }\n\n return masked.length;\n}\n\nfunction getCurrencyCaretPosition(opts: {\n newValue: string;\n mask: MaskPatternKey | MaskPattern | undefined;\n transformOpts: TransformOptions;\n oldCursorPosition?: number;\n oldValue?: string;\n previousUnmasked?: string;\n}): number {\n const { newValue, mask, transformOpts, oldCursorPosition, oldValue, previousUnmasked } = opts;\n\n if (oldCursorPosition !== undefined && oldValue && previousUnmasked !== undefined) {\n if (oldCursorPosition < oldValue.length) {\n const digitsBeforeCursor = oldValue.substring(0, oldCursorPosition).replace(/\\D/g, \"\").length;\n\n let digitCount = 0;\n for (let i = 0; i < newValue.length; i++) {\n if (/\\d/.test(newValue[i] ?? \"\")) {\n digitCount++;\n if (digitCount === digitsBeforeCursor) {\n return i + 1;\n }\n }\n }\n }\n }\n\n if (mask === \"currency\") {\n const currencyAtEnd = isCurrencyAtEnd(transformOpts);\n if (currencyAtEnd) {\n const match = newValue.match(/(\\d)\\s*([^\\d\\s]+)$/);\n if (match?.[1]) {\n return newValue.lastIndexOf(match[1]) + 1;\n }\n return newValue.length;\n }\n return newValue.length;\n }\n return newValue.length;\n}\n\nfunction getPatternCaretPosition(opts: {\n newValue: string;\n maskPattern: MaskPattern;\n currentUnmasked: string;\n oldCursorPosition?: number;\n oldValue?: string;\n previousUnmasked?: string;\n}): number {\n const { newValue, maskPattern, currentUnmasked, oldCursorPosition, oldValue, previousUnmasked } = opts;\n let position = 0;\n let unmaskedCount = 0;\n\n if (oldCursorPosition !== undefined && oldValue && previousUnmasked !== undefined) {\n const oldUnmaskedIndex = toUnmaskedIndex({\n masked: oldValue,\n pattern: maskPattern.pattern,\n caret: oldCursorPosition\n });\n\n if (oldCursorPosition < oldValue.length) {\n const targetUnmaskedIndex = Math.min(oldUnmaskedIndex, currentUnmasked.length);\n\n for (let i = 0; i < maskPattern.pattern.length && i < newValue.length; i++) {\n if (maskPattern.pattern[i] === \"#\") {\n unmaskedCount++;\n if (unmaskedCount <= targetUnmaskedIndex) {\n position = i + 1;\n }\n }\n }\n\n return position;\n }\n }\n\n for (let i = 0; i < maskPattern.pattern.length && i < newValue.length; i++) {\n if (maskPattern.pattern[i] === \"#\") {\n unmaskedCount++;\n if (unmaskedCount <= currentUnmasked.length) {\n position = i + 1;\n }\n }\n }\n\n return position;\n}\n\n/*\n * ⚠ Khối export này trộn HAI nhóm (biome gộp chúng lại theo a-z):\n * - CÔNG KHAI — đúng các tên thượng nguồn xuất ra: `applyCurrencyMask`, `applyMask`,\n * `applyPercentageMask`, `fromUnmaskedIndex`, `getUnmaskedValue`, `MASK_PATTERNS`,\n * `MaskPattern`, `toUnmaskedIndex`. Barrel `src/index.ts` liệt kê TƯỜNG MINH đúng các tên này.\n * - NỘI BỘ — mọi tên còn lại, chỉ cho `mask-input.tsx`. Đừng đổi dòng barrel thành `export *`:\n * `DEFAULT_CURRENCY`, `REGEX_CACHE`… lọt ra entry chính là mời đụng tên và khoá cứng API.\n */\nexport {\n applyCurrencyMask,\n applyMask,\n applyPercentageMask,\n CURRENCY_PERCENTAGE_SYMBOLS,\n DEFAULT_CURRENCY,\n DEFAULT_LOCALE,\n fromUnmaskedIndex,\n getCurrencyCaretPosition,\n getPatternCaretPosition,\n getUnmaskedValue,\n isCurrencyAtEnd,\n isCurrencyMask,\n MASK_PATTERNS,\n type MaskPattern,\n type MaskPatternKey,\n NUMERIC_MASK_PATTERNS,\n REGEX_CACHE,\n type TransformOptions,\n toUnmaskedIndex\n};\n"],"mappings":"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","names":["result","finalResult"]}
1
+ {"version":3,"sources":["../../../src/components/ui/mask-input-constants.ts"],"sourcesContent":["/*\n * Port từ Dice UI (https://diceui.com/docs/components/mask-input) — 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\n/**\n * Lõi THUẦN của `MaskInput`: bảng mẫu `MASK_PATTERNS` và các hàm định dạng/bóc mặt nạ.\n *\n * File này là NỬA TRÊN của `mask-input.tsx` thượng nguồn (mọi thứ đứng trước component), tách ra\n * nguyên khối, đúng thứ tự, để còn diff ngược được. Nó **CỐ Ý không có `\"use client\"`**: mọi\n * export của một file `\"use client\"` tới Server Component là client-reference proxy — `MASK_PATTERNS`\n * sẽ đọc ra `undefined` mà không throw — và `applyCurrencyMask` là thứ trang server hay cần nhất\n * (in số tiền ra bảng, email, PDF) nên phải chạy được ở cả hai phía.\n *\n * ⚠ Barrel `src/index.ts` xuất module này TRỰC TIẾP, và CHỌN LỌC (không `export *`): ngoài các tên\n * công khai của thượng nguồn, file còn export vài helper NỘI BỘ cho riêng `mask-input.tsx`\n * (liệt kê ở khối export cuối file). `mask-input.tsx` chỉ `import`, KHÔNG `export` lại.\n */\n\nconst PAST_YEARS_LIMIT = 120;\nconst FUTURE_YEARS_LIMIT = 10;\n/*\n * \"USD\" / \"en-US\" như thượng nguồn — mặc định của các hàm THUẦN ở đây (gọi thẳng ở server, email,\n * PDF). Tiền tệ là thiết lập của VÙNG / nghiệp vụ, không phải của ngôn ngữ (xem\n * `src/i18n/locale.ts`): app Comitor truyền `currency` từ cài đặt workspace. Component `MaskInput`\n * dùng `DEFAULT_CURRENCY` nhưng lấy `locale` mặc định từ `ComitorLocaleProvider`, không từ hằng\n * `DEFAULT_LOCALE`. Trước `3.0.0` hai hằng là \"VND\" / \"vi-VN\".\n */\nconst DEFAULT_CURRENCY = \"USD\";\nconst DEFAULT_LOCALE = \"en-US\";\n\nconst NUMERIC_MASK_PATTERNS = /^(phone|zipCode|zipCodeExtended|ssn|ein|time|date|creditCard|creditCardExpiry)$/;\nconst CURRENCY_PERCENTAGE_SYMBOLS = /[€$%]/;\n\ninterface CurrencySymbols {\n currency: string;\n decimal: string;\n group: string;\n}\n\nconst formattersCache = new Map<string, Intl.NumberFormat>();\nconst currencyAtEndCache = new Map<string, boolean>();\nconst currencySymbolsCache = new Map<string, CurrencySymbols>();\nconst fractionDigitsCache = new Map<string, number>();\nconst daysInMonthCache = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] as const;\n\nconst REGEX_CACHE = {\n digitsOnly: /^\\d+$/,\n nonDigits: /\\D/g,\n nonAlphaNumeric: /[^A-Z0-9]/gi,\n nonNumericDot: /[^0-9.]/g,\n nonCurrencyChars: /[^\\d.,]/g,\n hashPattern: /#/g,\n currencyAtEnd: /\\d\\s*[^\\d\\s]+$/,\n percentageChars: /[^\\d.]/g,\n phone: /^\\d{10}$/,\n ssn: /^\\d{9}$/,\n zipCode: /^\\d{5}$/,\n zipCodeExtended: /^\\d{9}$/,\n isbn: /^\\d{13}$/,\n ein: /^\\d{9}$/,\n time: /^\\d{4}$/,\n creditCard: /^\\d{13,19}$/,\n creditCardExpiry: /^\\d{4}$/,\n licensePlate: /^[A-Z0-9]{6}$/,\n macAddress: /^[A-F0-9]{12}$/,\n currencyValidation: /^\\d+(\\.\\d{1,2})?$/,\n ipv4Segment: /^\\d{1,3}$/\n} as const;\n\nfunction getCachedFormatter(locale: string | undefined, opts: Intl.NumberFormatOptions): Intl.NumberFormat {\n const { currency, minimumFractionDigits = 0, maximumFractionDigits = 2 } = opts;\n\n const key = `${locale}|${currency}|${minimumFractionDigits}|${maximumFractionDigits}`;\n\n if (!formattersCache.has(key)) {\n try {\n formattersCache.set(\n key,\n new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n ...opts\n })\n );\n } catch {\n formattersCache.set(\n key,\n new Intl.NumberFormat(DEFAULT_LOCALE, {\n style: \"currency\",\n currency: DEFAULT_CURRENCY,\n ...opts\n })\n );\n }\n }\n\n const formatter = formattersCache.get(key);\n if (!formatter) {\n throw new Error(`Không tạo được bộ định dạng tiền tệ cho ${key}`);\n }\n return formatter;\n}\n\nfunction getCachedCurrencySymbols(opts: TransformOptions): CurrencySymbols {\n const { locale, currency } = opts;\n\n const key = `${locale}|${currency}`;\n const cached = currencySymbolsCache.get(key);\n if (cached) {\n return cached;\n }\n\n let currencySymbol = \"$\";\n let decimalSeparator = \".\";\n let groupSeparator = \",\";\n\n try {\n const formatter = getCachedFormatter(locale, {\n currency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 2\n });\n const parts = formatter.formatToParts(1234.5);\n const currencyPart = parts.find((part) => part.type === \"currency\");\n const decimalPart = parts.find((part) => part.type === \"decimal\");\n const groupPart = parts.find((part) => part.type === \"group\");\n\n if (currencyPart) currencySymbol = currencyPart.value;\n if (decimalPart) decimalSeparator = decimalPart.value;\n if (groupPart) groupSeparator = groupPart.value;\n } catch {\n // Giữ giá trị mặc định.\n }\n\n const symbols: CurrencySymbols = {\n currency: currencySymbol,\n decimal: decimalSeparator,\n group: groupSeparator\n };\n currencySymbolsCache.set(key, symbols);\n return symbols;\n}\n\n/**\n * Số chữ số thập phân TỰ NHIÊN của đồng tiền theo ISO 4217 / CLDR: VND 0, JPY 0, USD 2, EUR 2.\n * Chặn trần ở 2 vì phần còn lại của logic (validate, cắt phần lẻ) chỉ hiểu tối đa 2 chữ số.\n *\n * ⚠ Khác thượng nguồn — hàm MỚI. Thượng nguồn ghim cứng `maximumFractionDigits: 2` cho mọi đồng\n * tiền, và với locale dùng `,` làm dấu thập phân (de-DE, vi-VN) thì `transform` còn đoán \"chữ số\n * gõ sau một nhóm nghìn là phần lẻ\". Với VND, đo trên chính mã thượng nguồn, gõ lần lượt\n * `1234567` ra:\n *\n * 1 ₫ → 12 ₫ → 123 ₫ → 1.234 ₫ → 1.234,5 ₫ → 1.234,56 ₫ → 1.234.567 ₫\n *\n * tức chữ số thứ 5 và 6 biến thành HÀO, và giá trị thô báo ra `onValueChange` là \"1234.5\" trong\n * khi người dùng đang gõ 12.345 ₫. Đồng tiền không có phần lẻ thì mọi `.`/`,` đều là dấu nhóm.\n */\nfunction getCurrencyFractionDigits(opts: TransformOptions): number {\n const { locale, currency } = opts;\n\n const key = `${locale}|${currency}`;\n const cached = fractionDigitsCache.get(key);\n if (cached !== undefined) {\n return cached;\n }\n\n let digits = 2;\n try {\n const resolved = new Intl.NumberFormat(locale, { style: \"currency\", currency }).resolvedOptions();\n digits = Math.min(2, resolved.maximumFractionDigits ?? 2);\n } catch {\n // Đồng tiền/locale không hợp lệ → giữ 2 như thượng nguồn.\n }\n\n fractionDigitsCache.set(key, digits);\n return digits;\n}\n\n/**\n * Vị trí dấu THẬP PHÂN trong chuỗi đã lọc (chỉ còn chữ số, `.` và `,`), hoặc `-1` nếu không có.\n *\n * Chuỗi tới đây là một trong hai thứ: chuỗi mặt nạ tự định dạng cộng MỘT lần sửa (gõ / xoá một ký\n * tự), hoặc chữ người dùng dán vào. Luật, theo thứ tự:\n * 1. Có cả `.` lẫn `,` → dấu xuất hiện SAU CÙNG là thập phân. Dán `1,234.56` hay `1.234,56` vào ô\n * nào cũng ra 1234.56; chuỗi tự định dạng `1.234,5` (vi-VN) cũng khớp luật này.\n * 2. Chỉ có dấu thập phân của locale → lần xuất hiện cuối.\n * 3. Chỉ có dấu NHÓM của locale → là dấu nhóm, KHÔNG BAO GIỜ là phần lẻ. Đây là luật sửa lỗi:\n * sau một lần xoá, `$123,45` vẫn là 12345 chứ không phải 123,45.\n * Ngoại lệ duy nhất: dấu `.` đứng CUỐI ở locale dùng `,` làm thập phân. Chuỗi tự định dạng không\n * bao giờ kết thúc bằng dấu nhóm, nên `.` ở cuối chỉ có thể là người dùng vừa bấm phím `.` của\n * bàn phím số — ý họ là dấu thập phân. Ở locale dùng `.` làm thập phân thì `,` ở cuối vẫn là\n * người gõ tay dấu nhóm, không đoán.\n * 4. Dấu KHÔNG thuộc locale (vd `.` ở fr-FR, nơi dấu nhóm là khoảng trắng hẹp): xuất hiện một lần\n * và không có đúng 3 chữ số theo sau → thập phân; còn lại (`1.234`, `1.234.567`) → nhóm.\n *\n * ⚠ Cái giá có chủ ý: dán `1234,5` (kiểu Việt) vào ô en-US ra 12.345 — `,` là dấu nhóm của en-US.\n * Kết quả HIỆN RA ngay trong ô nên người dùng thấy và sửa được; còn đoán theo kiểu cũ thì chính lúc\n * sửa số (thao tác thường gặp nhất) lại âm thầm đổi giá trị. Chiều ngược lại (`1234.5` vào ô vi-VN)\n * thì đúng — đó là dạng chuẩn, xem `isCanonicalAmount`.\n */\nfunction findCurrencyDecimalIndex(cleaned: string, decimal: string, group: string): number {\n const lastDot = cleaned.lastIndexOf(\".\");\n const lastComma = cleaned.lastIndexOf(\",\");\n\n if (lastDot !== -1 && lastComma !== -1) return Math.max(lastDot, lastComma);\n\n const separator = lastDot !== -1 ? \".\" : lastComma !== -1 ? \",\" : null;\n if (!separator) return -1;\n\n const last = cleaned.lastIndexOf(separator);\n\n if (separator === decimal) return last;\n\n if (separator === group) {\n return separator === \".\" && decimal === \",\" && last === cleaned.length - 1 ? last : -1;\n }\n\n const occurrences = cleaned.split(separator).length - 1;\n const digitsAfter = cleaned.length - last - 1;\n return occurrences === 1 && digitsAfter !== 3 ? last : -1;\n}\n\n/**\n * Chuỗi đã ở DẠNG CHUẨN — đúng dạng vế thứ hai của `onValueChange` (`\"12345.56\"`, `\"12.\"`,\n * `\"12345\"`): chỉ chữ số, nhiều nhất một `.`, phần sau `.` không dài hơn số lẻ của đồng tiền.\n *\n * Vì sao cần (3.0.1): `transform` đọc cả chuỗi HIỂN THỊ lẫn giá trị app trả về qua `value`. App\n * controlled thường lưu đúng số thô rồi trả lại (`value={amount}`) — và ở locale dùng `.` làm dấu\n * NHÓM (vi-VN, de-DE…), luật 3 của `findCurrencyDecimalIndex` đọc \"12345.56\" thành 1.234.556.\n * Dạng chuẩn không nhập nhằng với chuỗi hiển thị: chuỗi hiển thị luôn mang ký hiệu tiền tệ, còn\n * chuỗi trần có `.` rồi ĐÚNG 3 chữ số (\"12.345\" — một nhóm nghìn) thì dài hơn phần lẻ (tối đa 2\n * chữ số, xem `getCurrencyFractionDigits`) nên không lọt vào đây.\n */\nfunction isCanonicalAmount(value: string, fractionDigits: number): boolean {\n const dot = value.indexOf(\".\");\n const integerPart = dot === -1 ? value : value.slice(0, dot);\n const fractionPart = dot === -1 ? \"\" : value.slice(dot + 1);\n return /^\\d*$/.test(integerPart) && /^\\d*$/.test(fractionPart) && fractionPart.length <= fractionDigits;\n}\n\nfunction isCurrencyAtEnd(opts: TransformOptions): boolean {\n const { locale, currency } = opts;\n\n const key = `${locale}|${currency}`;\n const cached = currencyAtEndCache.get(key);\n if (cached !== undefined) {\n return cached;\n }\n\n try {\n const formatter = getCachedFormatter(locale, {\n currency,\n minimumFractionDigits: 0,\n maximumFractionDigits: 0\n });\n const sample = formatter.format(123);\n const result = REGEX_CACHE.currencyAtEnd.test(sample);\n currencyAtEndCache.set(key, result);\n return result;\n } catch {\n currencyAtEndCache.set(key, false);\n return false;\n }\n}\n\nfunction isCurrencyMask(opts: { mask: MaskPatternKey | MaskPattern | undefined; pattern?: string }): boolean {\n const { mask, pattern } = opts;\n\n return mask === \"currency\" || Boolean(pattern && (pattern.includes(\"$\") || pattern.includes(\"€\")));\n}\n\ninterface TransformOptions {\n currency?: string;\n locale?: string;\n}\n\ninterface ValidateOptions {\n min?: number;\n max?: number;\n}\n\ninterface MaskPattern {\n pattern: string;\n transform?: (value: string, opts?: TransformOptions) => string;\n validate?: (value: string, opts?: ValidateOptions) => boolean;\n}\n\ntype MaskPatternKey =\n | \"phone\"\n | \"ssn\"\n | \"date\"\n | \"time\"\n | \"creditCard\"\n | \"creditCardExpiry\"\n | \"zipCode\"\n | \"zipCodeExtended\"\n | \"currency\"\n | \"percentage\"\n | \"licensePlate\"\n | \"ipv4\"\n | \"macAddress\"\n | \"isbn\"\n | \"ein\";\n\n/**\n * Các mẫu dựng sẵn — giữ NGUYÊN bộ của thượng nguồn, không thêm không bớt. Nhiều mẫu là định dạng\n * Hoa Kỳ (`phone`, `ssn`, `date`, `zipCode`, `zipCodeExtended`, `ein`); app Comitor cần định dạng\n * khác thì truyền một `MaskPattern` riêng vào prop `mask`.\n */\nconst MASK_PATTERNS: Record<MaskPatternKey, MaskPattern> = {\n /**\n * Số điện thoại **Hoa Kỳ** `(###) ###-####`, đúng 10 chữ số.\n *\n * ⚠ Không dùng cho số điện thoại ở app Comitor: số Việt Nam (và mọi số quốc tế) đi qua\n * `PhoneInput` — có chọn quốc gia, tự nhận mã vùng và lưu dạng `+84…`.\n */\n phone: {\n pattern: \"(###) ###-####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.phone.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n ssn: {\n pattern: \"###-##-####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.ssn.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n /**\n * Ngày kiểu **Hoa Kỳ** `MM/DD/YYYY` — `validate` đọc 2 chữ số ĐẦU là THÁNG. Ngày kiểu Việt Nam\n * (`dd/MM/yyyy`) thì dùng `DatePicker` của gói, hoặc một `MaskPattern` riêng.\n */\n date: {\n pattern: \"##/##/####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => {\n const cleaned = value.replace(REGEX_CACHE.nonDigits, \"\");\n if (cleaned.length !== 8) return false;\n const month = Number.parseInt(cleaned.substring(0, 2), 10);\n const day = Number.parseInt(cleaned.substring(2, 4), 10);\n const year = Number.parseInt(cleaned.substring(4, 8), 10);\n\n const currentYear = new Date().getFullYear();\n const minYear = currentYear - PAST_YEARS_LIMIT;\n const maxYear = currentYear + FUTURE_YEARS_LIMIT;\n if (month < 1 || month > 12 || day < 1 || year < minYear || year > maxYear) return false;\n\n const maxDays =\n month === 2 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0)\n ? 29\n : (daysInMonthCache[month - 1] ?? 31);\n\n return day <= maxDays;\n }\n },\n time: {\n pattern: \"##:##\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => {\n const cleaned = value.replace(REGEX_CACHE.nonDigits, \"\");\n if (!REGEX_CACHE.time.test(cleaned)) return false;\n const hours = Number.parseInt(cleaned.substring(0, 2), 10);\n const minutes = Number.parseInt(cleaned.substring(2, 4), 10);\n return hours <= 23 && minutes <= 59;\n }\n },\n creditCard: {\n pattern: \"#### #### #### ####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => {\n const cleaned = value.replace(REGEX_CACHE.nonDigits, \"\");\n if (!REGEX_CACHE.creditCard.test(cleaned)) return false;\n\n let sum = 0;\n let isEven = false;\n for (let i = cleaned.length - 1; i >= 0; i--) {\n const digitChar = cleaned[i];\n if (!digitChar) continue;\n let digit = Number.parseInt(digitChar, 10);\n if (isEven) {\n digit *= 2;\n if (digit > 9) {\n digit -= 9;\n }\n }\n sum += digit;\n isEven = !isEven;\n }\n return sum % 10 === 0;\n }\n },\n creditCardExpiry: {\n pattern: \"##/##\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => {\n const cleaned = value.replace(REGEX_CACHE.nonDigits, \"\");\n if (!REGEX_CACHE.creditCardExpiry.test(cleaned)) return false;\n\n const month = Number.parseInt(cleaned.substring(0, 2), 10);\n const year = Number.parseInt(cleaned.substring(2, 4), 10);\n\n if (month < 1 || month > 12) return false;\n\n const now = new Date();\n const currentYear = now.getFullYear();\n const currentMonth = now.getMonth() + 1;\n\n const fullYear = year <= 75 ? 2000 + year : 1900 + year;\n\n if (fullYear < currentYear || (fullYear === currentYear && month < currentMonth)) {\n return false;\n }\n\n const maxYear = currentYear + 50;\n if (fullYear > maxYear) {\n return false;\n }\n\n return true;\n }\n },\n zipCode: {\n pattern: \"#####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.zipCode.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n zipCodeExtended: {\n pattern: \"#####-####\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.zipCodeExtended.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n /**\n * Tiền tệ — định dạng thật đi qua `Intl.NumberFormat` theo `currency`/`locale` (mặc định USD /\n * en-US → `$1,234,567`; `VND` / `vi-VN` → `1.234.567 ₫`). Chuỗi `pattern` ở đây chỉ là DẤU NHẬN\n * DẠNG cho nhánh tiền tệ, không phải khuôn chữ số.\n */\n currency: {\n pattern: \"$###,###.##\",\n transform: (value, { currency = DEFAULT_CURRENCY, locale = DEFAULT_LOCALE } = {}) => {\n // ⚠ Khác thượng nguồn: đồng tiền không có phần lẻ (VND, JPY) → mọi `.`/`,` là dấu nhóm,\n // giá trị thô chỉ còn chữ số. Lý do: xem `getCurrencyFractionDigits`.\n const fractionDigits = getCurrencyFractionDigits({ currency, locale });\n if (fractionDigits === 0) {\n return value.replace(REGEX_CACHE.nonDigits, \"\");\n }\n\n /*\n * ⚠ Khác thượng nguồn (3.0.1): thay cả khối đoán \"dấu có ≤ 2 chữ số theo sau là phần lẻ\" bằng\n * `findCurrencyDecimalIndex`. Khối đó không phân biệt được dấu NHÓM do chính mặt nạ chèn vào\n * với dấu thập phân người dùng gõ, và hỏng ở cả hai phía — đo bằng chính helper này:\n * · en-US (mặc định của gói): xoá một chữ số khỏi `$123,456` → `$123.45` (123,45 đô) thay vì\n * `$12,345`, vì `,` còn lại có đúng 2 chữ số theo sau.\n * · vi-VN / de-DE với USD, EUR: gõ thẳng `12345` → `1.234,5` (giá trị thô \"1234.5\"), gõ thêm\n * thì nhảy về `1.234.567` — số người dùng thấy đổi nghĩa giữa chừng.\n * Bản `2.1.0` chỉ vá được nhánh không phần lẻ (VND) ở trên; nhánh có phần lẻ vẫn đoán.\n */\n if (isCanonicalAmount(value, fractionDigits)) return value;\n\n const { decimal, group } = getCachedCurrencySymbols({ currency, locale });\n const cleaned = value.replace(REGEX_CACHE.nonCurrencyChars, \"\");\n const decimalIndex = findCurrencyDecimalIndex(cleaned, decimal, group);\n\n if (decimalIndex === -1) {\n return cleaned.replace(/[.,]/g, \"\");\n }\n\n const integerPart = cleaned.substring(0, decimalIndex).replace(/[.,]/g, \"\");\n const fractionPart = cleaned\n .substring(decimalIndex + 1)\n .replace(/[.,]/g, \"\")\n .substring(0, fractionDigits);\n\n // Phần lẻ rỗng vẫn giữ dấu chấm (\"12.\") để ô còn hiện dấu thập phân vừa gõ.\n return `${integerPart}.${fractionPart}`;\n },\n validate: (value) => {\n if (!REGEX_CACHE.currencyValidation.test(value)) return false;\n const num = Number.parseFloat(value);\n return !Number.isNaN(num) && num >= 0;\n }\n },\n /**\n * Phần trăm `##.##%` — dấu thập phân là `.` cố định, KHÔNG theo `locale` (dấu `,` bị bỏ qua).\n */\n percentage: {\n pattern: \"##.##%\",\n transform: (value) => {\n const cleaned = value.replace(REGEX_CACHE.percentageChars, \"\");\n const parts = cleaned.split(\".\");\n if (parts.length > 2) {\n return `${parts[0]}.${parts.slice(1).join(\"\")}`;\n }\n if (parts[1] && parts[1].length > 2) {\n return `${parts[0]}.${parts[1].substring(0, 2)}`;\n }\n return cleaned;\n },\n validate: (value, opts = {}) => {\n const num = Number.parseFloat(value);\n const min = opts.min ?? 0;\n const max = opts.max ?? 100;\n return !Number.isNaN(num) && num >= min && num <= max;\n }\n },\n licensePlate: {\n pattern: \"###-###\",\n transform: (value) => value.replace(REGEX_CACHE.nonAlphaNumeric, \"\").toUpperCase(),\n validate: (value) => REGEX_CACHE.licensePlate.test(value)\n },\n ipv4: {\n pattern: \"###.###.###.###\",\n transform: (value) => value.replace(REGEX_CACHE.nonNumericDot, \"\"),\n validate: (value) => {\n if (value.includes(\".\")) {\n const segments = value.split(\".\");\n if (segments.length > 4) return false;\n\n return segments.every((segment) => {\n if (segment === \"\") return true;\n if (!REGEX_CACHE.ipv4Segment.test(segment)) return false;\n const num = Number.parseInt(segment, 10);\n return num <= 255;\n });\n }\n\n if (!REGEX_CACHE.digitsOnly.test(value)) return false;\n if (value.length > 12) return false;\n\n const chunks = [];\n for (let i = 0; i < value.length; i += 3) {\n chunks.push(value.substring(i, i + 3));\n }\n\n if (chunks.length > 4) return false;\n\n return chunks.every((chunk) => {\n const num = Number.parseInt(chunk, 10);\n return num >= 0 && num <= 255;\n });\n }\n },\n macAddress: {\n pattern: \"##:##:##:##:##:##\",\n transform: (value) => value.replace(REGEX_CACHE.nonAlphaNumeric, \"\").toUpperCase(),\n validate: (value) => REGEX_CACHE.macAddress.test(value)\n },\n isbn: {\n pattern: \"###-#-###-#####-#\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.isbn.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n },\n ein: {\n pattern: \"##-#######\",\n transform: (value) => value.replace(REGEX_CACHE.nonDigits, \"\"),\n validate: (value) => REGEX_CACHE.ein.test(value.replace(REGEX_CACHE.nonDigits, \"\"))\n }\n};\n\nfunction applyMask(opts: {\n value: string;\n pattern: string;\n currency?: string;\n locale?: string;\n mask?: MaskPatternKey | MaskPattern;\n}): string {\n const { value, pattern, currency, locale, mask } = opts;\n\n const cleanValue = value;\n\n if (pattern.includes(\"$\") || pattern.includes(\"€\") || mask === \"currency\") {\n return applyCurrencyMask({\n value: cleanValue,\n currency: currency ?? DEFAULT_CURRENCY,\n locale: locale ?? DEFAULT_LOCALE\n });\n }\n\n if (pattern.includes(\"%\")) {\n return applyPercentageMask(cleanValue);\n }\n\n if (mask === \"ipv4\") {\n return cleanValue;\n }\n\n const maskedChars: string[] = [];\n let valueIndex = 0;\n\n for (let i = 0; i < pattern.length && valueIndex < cleanValue.length; i++) {\n const patternChar = pattern[i];\n const valueChar = cleanValue[valueIndex];\n\n if (patternChar === \"#\" && valueChar) {\n maskedChars.push(valueChar);\n valueIndex++;\n } else if (patternChar) {\n maskedChars.push(patternChar);\n }\n }\n\n return maskedChars.join(\"\");\n}\n\nfunction applyCurrencyMask(opts: { value: string; currency?: string; locale?: string }): string {\n const { value, currency = DEFAULT_CURRENCY, locale = DEFAULT_LOCALE } = opts;\n\n if (!value) return \"\";\n\n const {\n currency: currencySymbol,\n decimal: decimalSeparator,\n group: groupSeparator\n } = getCachedCurrencySymbols({ locale, currency });\n\n const normalizedValue = value\n .replace(new RegExp(`\\\\${groupSeparator.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}`, \"g\"), \"\")\n .replace(decimalSeparator, \".\");\n\n const parts = normalizedValue.split(\".\");\n const integerPart = parts[0] ?? \"\";\n const fractionalPart = parts[1] ?? \"\";\n\n if (!integerPart && !fractionalPart) return \"\";\n\n // ⚠ Khác thượng nguồn: cắt phần lẻ theo số chữ số TỰ NHIÊN của đồng tiền (VND 0) thay vì ghim\n // cứng 2, và đồng tiền không có phần lẻ thì không bao giờ vẽ dấu thập phân treo (\"1.234, ₫\").\n const fractionDigits = getCurrencyFractionDigits({ currency, locale });\n\n const intValue = integerPart ?? \"0\";\n const fracValue = fractionalPart.slice(0, fractionDigits);\n\n const num = Number(`${intValue}.${fracValue ?? \"\"}`);\n\n if (Number.isNaN(num)) {\n const cleanedDigits = value.replace(/[^\\d]/g, \"\");\n if (!cleanedDigits) return \"\";\n return `${currencySymbol}${cleanedDigits}`;\n }\n\n const hasExplicitDecimal = fractionDigits > 0 && (value.includes(\".\") || value.includes(decimalSeparator));\n\n try {\n const formatter = getCachedFormatter(locale, {\n currency,\n minimumFractionDigits: fracValue ? fracValue.length : 0,\n maximumFractionDigits: 2\n });\n const result = formatter.format(num);\n\n if (hasExplicitDecimal && !fracValue) {\n if (result.match(/^[^\\d\\s]+/)) {\n const finalResult = result.replace(/(\\d)$/, `$1${decimalSeparator}`);\n return finalResult;\n }\n\n const finalResult = result.replace(/(\\d)(\\s*)([^\\d\\s]+)$/, `$1${decimalSeparator}$2$3`);\n return finalResult;\n }\n\n return result;\n } catch {\n const formattedInt = intValue.replace(/\\B(?=(\\d{3})+(?!\\d))/g, groupSeparator);\n let result = `${currencySymbol}${formattedInt}`;\n if (hasExplicitDecimal) {\n result += `${decimalSeparator}${fracValue}`;\n }\n\n return result;\n }\n}\n\nfunction applyPercentageMask(value: string): string {\n if (!value) return \"\";\n\n const parts = value.split(\".\");\n let result = parts[0] ?? \"0\";\n\n if (value.includes(\".\")) {\n result += `.${(parts[1] ?? \"\").substring(0, 2)}`;\n }\n\n return `${result}%`;\n}\n\nfunction getUnmaskedValue(opts: {\n value: string;\n currency?: string;\n locale?: string;\n transform?: (value: string, opts?: TransformOptions) => string;\n}): string {\n const { value, transform, currency, locale } = opts;\n\n return transform ? transform(value, { currency, locale }) : value.replace(REGEX_CACHE.nonDigits, \"\");\n}\n\nfunction toUnmaskedIndex(opts: { masked: string; pattern: string; caret: number }): number {\n const { masked, pattern, caret } = opts;\n\n let idx = 0;\n for (let i = 0; i < caret && i < masked.length && i < pattern.length; i++) {\n if (pattern[i] === \"#\") {\n idx++;\n }\n }\n\n return idx;\n}\n\nfunction fromUnmaskedIndex(opts: { masked: string; pattern: string; unmaskedIndex: number }): number {\n const { masked, pattern, unmaskedIndex } = opts;\n\n let seen = 0;\n for (let i = 0; i < masked.length && i < pattern.length; i++) {\n if (pattern[i] === \"#\") {\n seen++;\n if (seen === unmaskedIndex) {\n return i + 1;\n }\n }\n }\n\n return masked.length;\n}\n\nfunction getCurrencyCaretPosition(opts: {\n newValue: string;\n mask: MaskPatternKey | MaskPattern | undefined;\n transformOpts: TransformOptions;\n oldCursorPosition?: number;\n oldValue?: string;\n previousUnmasked?: string;\n}): number {\n const { newValue, mask, transformOpts, oldCursorPosition, oldValue, previousUnmasked } = opts;\n\n if (oldCursorPosition !== undefined && oldValue && previousUnmasked !== undefined) {\n if (oldCursorPosition < oldValue.length) {\n const digitsBeforeCursor = oldValue.substring(0, oldCursorPosition).replace(/\\D/g, \"\").length;\n\n let digitCount = 0;\n for (let i = 0; i < newValue.length; i++) {\n if (/\\d/.test(newValue[i] ?? \"\")) {\n digitCount++;\n if (digitCount === digitsBeforeCursor) {\n return i + 1;\n }\n }\n }\n }\n }\n\n if (mask === \"currency\") {\n const currencyAtEnd = isCurrencyAtEnd(transformOpts);\n if (currencyAtEnd) {\n const match = newValue.match(/(\\d)\\s*([^\\d\\s]+)$/);\n if (match?.[1]) {\n return newValue.lastIndexOf(match[1]) + 1;\n }\n return newValue.length;\n }\n return newValue.length;\n }\n return newValue.length;\n}\n\nfunction getPatternCaretPosition(opts: {\n newValue: string;\n maskPattern: MaskPattern;\n currentUnmasked: string;\n oldCursorPosition?: number;\n oldValue?: string;\n previousUnmasked?: string;\n}): number {\n const { newValue, maskPattern, currentUnmasked, oldCursorPosition, oldValue, previousUnmasked } = opts;\n let position = 0;\n let unmaskedCount = 0;\n\n if (oldCursorPosition !== undefined && oldValue && previousUnmasked !== undefined) {\n const oldUnmaskedIndex = toUnmaskedIndex({\n masked: oldValue,\n pattern: maskPattern.pattern,\n caret: oldCursorPosition\n });\n\n if (oldCursorPosition < oldValue.length) {\n const targetUnmaskedIndex = Math.min(oldUnmaskedIndex, currentUnmasked.length);\n\n for (let i = 0; i < maskPattern.pattern.length && i < newValue.length; i++) {\n if (maskPattern.pattern[i] === \"#\") {\n unmaskedCount++;\n if (unmaskedCount <= targetUnmaskedIndex) {\n position = i + 1;\n }\n }\n }\n\n return position;\n }\n }\n\n for (let i = 0; i < maskPattern.pattern.length && i < newValue.length; i++) {\n if (maskPattern.pattern[i] === \"#\") {\n unmaskedCount++;\n if (unmaskedCount <= currentUnmasked.length) {\n position = i + 1;\n }\n }\n }\n\n return position;\n}\n\n/*\n * ⚠ Khối export này trộn HAI nhóm (biome gộp chúng lại theo a-z):\n * - CÔNG KHAI — đúng các tên thượng nguồn xuất ra: `applyCurrencyMask`, `applyMask`,\n * `applyPercentageMask`, `fromUnmaskedIndex`, `getUnmaskedValue`, `MASK_PATTERNS`,\n * `MaskPattern`, `toUnmaskedIndex`. Barrel `src/index.ts` liệt kê TƯỜNG MINH đúng các tên này.\n * - NỘI BỘ — mọi tên còn lại, chỉ cho `mask-input.tsx`. Đừng đổi dòng barrel thành `export *`:\n * `DEFAULT_CURRENCY`, `REGEX_CACHE`… lọt ra entry chính là mời đụng tên và khoá cứng API.\n */\nexport {\n applyCurrencyMask,\n applyMask,\n applyPercentageMask,\n CURRENCY_PERCENTAGE_SYMBOLS,\n DEFAULT_CURRENCY,\n fromUnmaskedIndex,\n getCurrencyCaretPosition,\n getPatternCaretPosition,\n getUnmaskedValue,\n isCurrencyAtEnd,\n isCurrencyMask,\n MASK_PATTERNS,\n type MaskPattern,\n type MaskPatternKey,\n NUMERIC_MASK_PATTERNS,\n REGEX_CACHE,\n type TransformOptions,\n toUnmaskedIndex\n};\n"],"mappings":"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","names":["finalResult"]}
@@ -15,16 +15,19 @@ interface MaskInputProps extends React.ComponentProps<"input"> {
15
15
  mask?: MaskPatternKey | MaskPattern;
16
16
  maskPlaceholder?: string;
17
17
  /**
18
- * Mã tiền tệ ISO 4217 cho `mask="currency"`. Mặc định `"VND"`.
18
+ * Mã tiền tệ ISO 4217 cho `mask="currency"`. Mặc định `"USD"`, như thượng nguồn.
19
19
  *
20
- * ⚠ Khác thượng nguồn: thượng nguồn mặc định `"USD"`.
20
+ * Tiền tệ là thiết lập VÙNG / nghiệp vụ, không suy từ ngôn ngữ: app Comitor truyền từ cài đặt
21
+ * workspace (vd `"VND"`). Trước `3.0.0` mặc định cứng là `"VND"`.
21
22
  */
22
23
  currency?: string;
23
24
  /**
24
25
  * Locale BCP 47 cho `mask="currency"` — quyết định dấu nhóm, dấu thập phân và vị trí ký hiệu.
25
- * Mặc định `"vi-VN"` (`1.234.567 ₫`).
26
+ * Không truyền thì theo `locale` của `ComitorLocaleProvider` (không có provider → `"en"` →
27
+ * `$1,234,567`; `"vi"` kèm `currency="VND"` → `1.234.567 ₫`).
26
28
  *
27
- * ⚠ Khác thượng nguồn: thượng nguồn mặc định `"en-US"`.
29
+ * ⚠ Khác thượng nguồn: thượng nguồn mặc định cứng `"en-US"`. Trước `3.0.0` mặc định cứng là
30
+ * `"vi-VN"`.
28
31
  */
29
32
  locale?: string;
30
33
  asChild?: boolean;
@@ -2,13 +2,13 @@
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { Slot } from "@radix-ui/react-slot";
4
4
  import * as React from "react";
5
+ import { useComitorLocale } 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 {
8
9
  applyMask,
9
10
  CURRENCY_PERCENTAGE_SYMBOLS,
10
11
  DEFAULT_CURRENCY,
11
- DEFAULT_LOCALE,
12
12
  fromUnmaskedIndex,
13
13
  getCurrencyCaretPosition,
14
14
  getPatternCaretPosition,
@@ -36,7 +36,7 @@ function MaskInput(props) {
36
36
  mask,
37
37
  maskPlaceholder,
38
38
  currency = DEFAULT_CURRENCY,
39
- locale = DEFAULT_LOCALE,
39
+ locale: localeProp,
40
40
  placeholder,
41
41
  inputMode,
42
42
  min,
@@ -52,6 +52,8 @@ function MaskInput(props) {
52
52
  ref,
53
53
  ...inputProps
54
54
  } = props;
55
+ const { locale: providerLocale } = useComitorLocale();
56
+ const locale = localeProp ?? providerLocale;
55
57
  const [internalValue, setInternalValue] = React.useState(defaultValue ?? "");
56
58
  const [focused, setFocused] = React.useState(false);
57
59
  const [composing, setComposing] = React.useState(false);
@@ -357,31 +359,29 @@ function MaskInput(props) {
357
359
  mask
358
360
  });
359
361
  target.value = newMaskedValue;
362
+ let newCursorPosition = newMaskedValue.length;
360
363
  if (isCurrencyMask({ mask, pattern: maskPattern.pattern })) {
361
364
  const currencyAtEnd = isCurrencyAtEnd(transformOpts);
362
- const caret = currencyAtEnd ? newMaskedValue.search(/\s*[^\d\s]+$/) : newMaskedValue.length;
363
- target.setSelectionRange(caret, caret);
364
- return;
365
- }
366
- if (maskPattern.pattern.includes("%")) {
367
- target.setSelectionRange(newMaskedValue.length - 1, newMaskedValue.length - 1);
368
- return;
369
- }
370
- let newCursorPosition = newMaskedValue.length;
371
- try {
372
- const unmaskedCount = unmasked.length;
373
- let position = 0;
374
- let count = 0;
375
- for (let i = 0; i < maskPattern.pattern.length && i < newMaskedValue.length; i++) {
376
- if (maskPattern.pattern[i] === "#") {
377
- count++;
378
- if (count <= unmaskedCount) {
379
- position = i + 1;
365
+ const symbolStart = newMaskedValue.search(/\s*[^\d\s]+$/);
366
+ newCursorPosition = currencyAtEnd && symbolStart !== -1 ? symbolStart : newMaskedValue.length;
367
+ } else if (maskPattern.pattern.includes("%")) {
368
+ newCursorPosition = Math.max(0, newMaskedValue.length - 1);
369
+ } else {
370
+ try {
371
+ const unmaskedCount = unmasked.length;
372
+ let position = 0;
373
+ let count = 0;
374
+ for (let i = 0; i < maskPattern.pattern.length && i < newMaskedValue.length; i++) {
375
+ if (maskPattern.pattern[i] === "#") {
376
+ count++;
377
+ if (count <= unmaskedCount) {
378
+ position = i + 1;
379
+ }
380
380
  }
381
381
  }
382
+ newCursorPosition = position;
383
+ } catch {
382
384
  }
383
- newCursorPosition = position;
384
- } catch {
385
385
  }
386
386
  target.setSelectionRange(newCursorPosition, newCursorPosition);
387
387
  if (!isControlled) setInternalValue(newMaskedValue);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/mask-input.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/mask-input) — 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\n/*\n * ⚠ Khác thượng nguồn: thượng nguồn là MỘT file. Ở đây nửa trên (bảng `MASK_PATTERNS` + mọi hàm\n * thuần) nằm ở `mask-input-constants.ts` — hằng dữ liệu export ra không được sống trong file\n * `\"use client\"` (Server Component đọc ra `undefined`, không throw). File này chỉ còn component,\n * và chỉ `import` từ đó, KHÔNG `export` lại (re-export vòng qua file client là vô hiệu).\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 {\n applyMask,\n CURRENCY_PERCENTAGE_SYMBOLS,\n DEFAULT_CURRENCY,\n DEFAULT_LOCALE,\n fromUnmaskedIndex,\n getCurrencyCaretPosition,\n getPatternCaretPosition,\n getUnmaskedValue,\n isCurrencyAtEnd,\n isCurrencyMask,\n MASK_PATTERNS,\n type MaskPattern,\n type MaskPatternKey,\n NUMERIC_MASK_PATTERNS,\n REGEX_CACHE,\n toUnmaskedIndex\n} from \"./mask-input-constants.js\";\n\ntype InputElement = React.ComponentRef<\"input\">;\n\ninterface MaskInputProps extends React.ComponentProps<\"input\"> {\n value?: string;\n defaultValue?: string;\n onValueChange?: (maskedValue: string, unmaskedValue: string) => void;\n onValidate?: (isValid: boolean, unmaskedValue: string) => void;\n validationMode?: \"onChange\" | \"onBlur\" | \"onSubmit\" | \"onTouched\" | \"all\";\n /**\n * Tên một mẫu dựng sẵn (`\"currency\"`, `\"percentage\"`, `\"time\"`, `\"creditCard\"`…) hoặc một\n * `MaskPattern` tự viết. ⚠ `\"phone\"`, `\"date\"`, `\"ssn\"`, `\"zipCode\"`, `\"ein\"` là định dạng Hoa Kỳ\n * — số điện thoại ở app Comitor dùng `PhoneInput`.\n */\n mask?: MaskPatternKey | MaskPattern;\n maskPlaceholder?: string;\n /**\n * Mã tiền tệ ISO 4217 cho `mask=\"currency\"`. Mặc định `\"VND\"`.\n *\n * ⚠ Khác thượng nguồn: thượng nguồn mặc định `\"USD\"`.\n */\n currency?: string;\n /**\n * Locale BCP 47 cho `mask=\"currency\"` — quyết định dấu nhóm, dấu thập phân và vị trí ký hiệu.\n * Mặc định `\"vi-VN\"` (`1.234.567 ₫`).\n *\n * ⚠ Khác thượng nguồn: thượng nguồn mặc định `\"en-US\"`.\n */\n locale?: string;\n asChild?: boolean;\n invalid?: boolean;\n withoutMask?: boolean;\n}\n\nfunction MaskInput(props: MaskInputProps) {\n const {\n value: valueProp,\n defaultValue,\n onValueChange: onValueChangeProp,\n onValidate,\n onBlur: onBlurProp,\n onFocus: onFocusProp,\n onKeyDown: onKeyDownProp,\n onPaste: onPasteProp,\n onCompositionStart: onCompositionStartProp,\n onCompositionEnd: onCompositionEndProp,\n validationMode = \"onChange\",\n mask,\n maskPlaceholder,\n currency = DEFAULT_CURRENCY,\n locale = DEFAULT_LOCALE,\n placeholder,\n inputMode,\n min,\n max,\n maxLength,\n asChild = false,\n disabled = false,\n invalid = false,\n readOnly = false,\n required = false,\n withoutMask = false,\n className,\n ref,\n ...inputProps\n } = props;\n\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const [focused, setFocused] = React.useState(false);\n const [composing, setComposing] = React.useState(false);\n const [touched, setTouched] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const composedRef = useComposedRefs(ref, inputRef);\n\n const isControlled = valueProp !== undefined;\n const value = isControlled ? valueProp : internalValue;\n\n const maskPattern = React.useMemo(() => {\n if (typeof mask === \"string\") {\n return MASK_PATTERNS[mask];\n }\n return mask;\n }, [mask]);\n\n const transformOpts = React.useMemo(\n () => ({\n currency,\n locale\n }),\n [currency, locale]\n );\n\n const placeholderValue = React.useMemo(() => {\n if (withoutMask) return placeholder;\n\n if (placeholder && maskPlaceholder) {\n return focused ? maskPlaceholder : placeholder;\n }\n\n if (maskPlaceholder) {\n return focused ? maskPlaceholder : undefined;\n }\n\n return placeholder;\n }, [placeholder, maskPlaceholder, focused, withoutMask]);\n\n const displayValue = React.useMemo(() => {\n if (withoutMask || !maskPattern || !value) return value ?? \"\";\n const unmasked = getUnmaskedValue({\n value,\n transform: maskPattern.transform,\n ...transformOpts\n });\n return applyMask({\n value: unmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n }, [value, maskPattern, withoutMask, transformOpts, mask]);\n\n const tokenCount = React.useMemo(() => {\n if (!maskPattern || CURRENCY_PERCENTAGE_SYMBOLS.test(maskPattern.pattern)) return undefined;\n return maskPattern.pattern.match(REGEX_CACHE.hashPattern)?.length ?? 0;\n }, [maskPattern]);\n\n const calculatedMaxLength = tokenCount ? maskPattern?.pattern.length : maxLength;\n\n const calculatedInputMode = React.useMemo(() => {\n if (inputMode) return inputMode;\n if (!maskPattern) return undefined;\n\n if (mask === \"currency\" || mask === \"percentage\" || mask === \"ipv4\") {\n return \"decimal\";\n }\n\n if (typeof mask === \"string\" && NUMERIC_MASK_PATTERNS.test(mask)) {\n return \"numeric\";\n }\n return undefined;\n }, [maskPattern, mask, inputMode]);\n\n const shouldValidate = React.useCallback(\n (trigger: \"change\" | \"blur\") => {\n if (!onValidate || !maskPattern?.validate) return false;\n\n switch (validationMode) {\n case \"onChange\":\n return trigger === \"change\";\n case \"onBlur\":\n return trigger === \"blur\";\n case \"onSubmit\":\n return false;\n case \"onTouched\":\n return touched ? trigger === \"change\" : trigger === \"blur\";\n case \"all\":\n return true;\n default:\n return trigger === \"change\";\n }\n },\n [onValidate, maskPattern, validationMode, touched]\n );\n\n const validationOpts = React.useMemo(\n () => ({\n min: typeof min === \"string\" ? Number.parseFloat(min) : min,\n max: typeof max === \"string\" ? Number.parseFloat(max) : max\n }),\n [min, max]\n );\n\n const onInputValidate = React.useCallback(\n (unmaskedValue: string) => {\n if (onValidate && maskPattern?.validate) {\n const isValid = maskPattern.validate(unmaskedValue, validationOpts);\n onValidate(isValid, unmaskedValue);\n }\n },\n [onValidate, maskPattern?.validate, validationOpts]\n );\n\n const onValueChange = React.useCallback(\n (event: React.ChangeEvent<InputElement>) => {\n const inputValue = event.target.value;\n let newValue = inputValue;\n let unmaskedValue = inputValue;\n\n if (composing) {\n if (!isControlled) setInternalValue(inputValue);\n return;\n }\n\n if (withoutMask || !maskPattern) {\n if (!isControlled) setInternalValue(inputValue);\n if (shouldValidate(\"change\")) onValidate?.(true, inputValue);\n onValueChangeProp?.(inputValue, inputValue);\n return;\n }\n\n if (maskPattern) {\n unmaskedValue = getUnmaskedValue({\n value: inputValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n newValue = applyMask({\n value: unmaskedValue,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n if (inputRef.current && newValue !== inputValue) {\n const inputElement = inputRef.current;\n if (!(inputElement instanceof HTMLInputElement)) return;\n\n const oldCursorPosition = inputElement.selectionStart ?? 0;\n\n inputElement.value = newValue;\n\n const currentUnmasked = getUnmaskedValue({\n value: newValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n\n let newCursorPosition: number;\n\n const previousUnmasked = getUnmaskedValue({\n value,\n transform: maskPattern.transform,\n ...transformOpts\n });\n\n if (CURRENCY_PERCENTAGE_SYMBOLS.test(maskPattern.pattern)) {\n newCursorPosition = getCurrencyCaretPosition({\n newValue,\n mask,\n transformOpts,\n oldCursorPosition,\n oldValue: inputValue,\n previousUnmasked\n });\n } else {\n newCursorPosition = getPatternCaretPosition({\n newValue,\n maskPattern,\n currentUnmasked,\n oldCursorPosition,\n oldValue: inputValue,\n previousUnmasked\n });\n }\n\n if (isCurrencyMask({ mask, pattern: maskPattern.pattern })) {\n if (mask === \"currency\") {\n const currencyAtEnd = isCurrencyAtEnd(transformOpts);\n if (!currencyAtEnd) {\n newCursorPosition = Math.max(1, newCursorPosition);\n }\n } else {\n newCursorPosition = Math.max(1, newCursorPosition);\n }\n } else if (maskPattern.pattern.includes(\"%\")) {\n newCursorPosition = Math.min(newValue.length - 1, newCursorPosition);\n }\n\n newCursorPosition = Math.min(newCursorPosition, newValue.length);\n\n inputElement.setSelectionRange(newCursorPosition, newCursorPosition);\n }\n }\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n\n if (shouldValidate(\"change\")) {\n onInputValidate(unmaskedValue);\n }\n\n onValueChangeProp?.(newValue, unmaskedValue);\n },\n [\n maskPattern,\n isControlled,\n onValueChangeProp,\n onValidate,\n onInputValidate,\n composing,\n shouldValidate,\n withoutMask,\n transformOpts,\n mask,\n value\n ]\n );\n\n const onFocus = React.useCallback(\n (event: React.FocusEvent<InputElement>) => {\n onFocusProp?.(event);\n if (event.defaultPrevented) return;\n\n setFocused(true);\n },\n [onFocusProp]\n );\n\n const onBlur = React.useCallback(\n (event: React.FocusEvent<InputElement>) => {\n onBlurProp?.(event);\n if (event.defaultPrevented) return;\n\n setFocused(false);\n\n if (!touched) {\n setTouched(true);\n }\n\n if (shouldValidate(\"blur\")) {\n const currentValue = event.target.value;\n const unmaskedValue = maskPattern\n ? getUnmaskedValue({\n value: currentValue,\n transform: maskPattern.transform,\n ...transformOpts\n })\n : currentValue;\n onInputValidate(unmaskedValue);\n }\n },\n [onBlurProp, touched, shouldValidate, onInputValidate, maskPattern, transformOpts]\n );\n\n const onCompositionStart = React.useCallback(\n (event: React.CompositionEvent<InputElement>) => {\n onCompositionStartProp?.(event);\n if (event.defaultPrevented) return;\n\n setComposing(true);\n },\n [onCompositionStartProp]\n );\n\n const onCompositionEnd = React.useCallback(\n (event: React.CompositionEvent<InputElement>) => {\n onCompositionEndProp?.(event);\n if (event.defaultPrevented) return;\n\n setComposing(false);\n\n const inputElement = inputRef.current;\n if (!inputElement) return;\n if (!(inputElement instanceof HTMLInputElement)) return;\n const inputValue = inputElement.value;\n\n if (!maskPattern || withoutMask) {\n if (!isControlled) setInternalValue(inputValue);\n if (shouldValidate(\"change\")) onValidate?.(true, inputValue);\n onValueChangeProp?.(inputValue, inputValue);\n return;\n }\n\n const unmasked = getUnmaskedValue({\n value: inputValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n const masked = applyMask({\n value: unmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n if (!isControlled) setInternalValue(masked);\n if (shouldValidate(\"change\")) onInputValidate(unmasked);\n onValueChangeProp?.(masked, unmasked);\n },\n [\n onCompositionEndProp,\n maskPattern,\n withoutMask,\n isControlled,\n shouldValidate,\n onValidate,\n onValueChangeProp,\n transformOpts,\n mask,\n onInputValidate\n ]\n );\n\n const onPaste = React.useCallback(\n (event: React.ClipboardEvent<InputElement>) => {\n onPasteProp?.(event);\n if (event.defaultPrevented) return;\n\n if (withoutMask || !maskPattern) return;\n\n if (mask === \"ipv4\") return;\n\n const target = event.target as InputElement;\n if (!(target instanceof HTMLInputElement)) return;\n\n const pastedData = event.clipboardData.getData(\"text\");\n if (!pastedData) return;\n\n event.preventDefault();\n\n const currentValue = target.value;\n const selectionStart = target.selectionStart ?? 0;\n const selectionEnd = target.selectionEnd ?? 0;\n\n const beforeSelection = currentValue.slice(0, selectionStart);\n const afterSelection = currentValue.slice(selectionEnd);\n const newInputValue = beforeSelection + pastedData + afterSelection;\n\n const unmasked = getUnmaskedValue({\n value: newInputValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n const newMaskedValue = applyMask({\n value: unmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n target.value = newMaskedValue;\n\n if (isCurrencyMask({ mask, pattern: maskPattern.pattern })) {\n const currencyAtEnd = isCurrencyAtEnd(transformOpts);\n const caret = currencyAtEnd ? newMaskedValue.search(/\\s*[^\\d\\s]+$/) : newMaskedValue.length;\n target.setSelectionRange(caret, caret);\n return;\n }\n\n if (maskPattern.pattern.includes(\"%\")) {\n target.setSelectionRange(newMaskedValue.length - 1, newMaskedValue.length - 1);\n return;\n }\n\n let newCursorPosition = newMaskedValue.length;\n try {\n const unmaskedCount = unmasked.length;\n let position = 0;\n let count = 0;\n\n for (let i = 0; i < maskPattern.pattern.length && i < newMaskedValue.length; i++) {\n if (maskPattern.pattern[i] === \"#\") {\n count++;\n if (count <= unmaskedCount) {\n position = i + 1;\n }\n }\n }\n newCursorPosition = position;\n } catch {\n // Lỗi thì để con trỏ ở cuối.\n }\n\n target.setSelectionRange(newCursorPosition, newCursorPosition);\n\n if (!isControlled) setInternalValue(newMaskedValue);\n if (shouldValidate(\"change\")) onInputValidate(unmasked);\n onValueChangeProp?.(newMaskedValue, unmasked);\n },\n [\n onPasteProp,\n withoutMask,\n maskPattern,\n mask,\n transformOpts,\n isControlled,\n shouldValidate,\n onInputValidate,\n onValueChangeProp\n ]\n );\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<InputElement>) => {\n onKeyDownProp?.(event);\n if (event.defaultPrevented) return;\n\n if (withoutMask || !maskPattern) return;\n\n if (mask === \"ipv4\") return;\n\n if (event.key === \"Backspace\") {\n const target = event.target as InputElement;\n if (!(target instanceof HTMLInputElement)) return;\n const cursorPosition = target.selectionStart ?? 0;\n const selectionEnd = target.selectionEnd ?? 0;\n const currentValue = target.value;\n\n if (\n mask === \"currency\" ||\n mask === \"percentage\" ||\n maskPattern.pattern.includes(\"$\") ||\n maskPattern.pattern.includes(\"€\") ||\n maskPattern.pattern.includes(\"%\")\n ) {\n return;\n }\n\n if (cursorPosition !== selectionEnd) {\n return;\n }\n\n if (cursorPosition > 0) {\n const charBeforeCursor = currentValue[cursorPosition - 1];\n\n if (charBeforeCursor) {\n event.preventDefault();\n\n const unmaskedIndex = toUnmaskedIndex({\n masked: currentValue,\n pattern: maskPattern.pattern,\n caret: cursorPosition\n });\n\n if (unmaskedIndex > 0) {\n const currentUnmasked = getUnmaskedValue({\n value: currentValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n const nextUnmasked = currentUnmasked.slice(0, unmaskedIndex - 1) + currentUnmasked.slice(unmaskedIndex);\n const nextMasked = applyMask({\n value: nextUnmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n target.value = nextMasked;\n const nextCaret = fromUnmaskedIndex({\n masked: nextMasked,\n pattern: maskPattern.pattern,\n unmaskedIndex: unmaskedIndex - 1\n });\n\n target.setSelectionRange(nextCaret, nextCaret);\n\n onValueChangeProp?.(nextMasked, nextUnmasked);\n }\n return;\n }\n }\n }\n\n if (event.key === \"Delete\") {\n const target = event.target as InputElement;\n if (!(target instanceof HTMLInputElement)) return;\n const cursorPosition = target.selectionStart ?? 0;\n const selectionEnd = target.selectionEnd ?? 0;\n const currentValue = target.value;\n\n if (\n mask === \"currency\" ||\n mask === \"percentage\" ||\n maskPattern.pattern.includes(\"$\") ||\n maskPattern.pattern.includes(\"€\") ||\n maskPattern.pattern.includes(\"%\")\n ) {\n return;\n }\n\n if (cursorPosition !== selectionEnd) {\n return;\n }\n\n if (cursorPosition < currentValue.length) {\n const charAtCursor = currentValue[cursorPosition];\n\n if (charAtCursor) {\n event.preventDefault();\n\n const unmaskedIndex = toUnmaskedIndex({\n masked: currentValue,\n pattern: maskPattern.pattern,\n caret: cursorPosition\n });\n\n const currentUnmasked = getUnmaskedValue({\n value: currentValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n\n if (unmaskedIndex < currentUnmasked.length) {\n const nextUnmasked = currentUnmasked.slice(0, unmaskedIndex) + currentUnmasked.slice(unmaskedIndex + 1);\n const nextMasked = applyMask({\n value: nextUnmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n target.value = nextMasked;\n const nextCaret = fromUnmaskedIndex({\n masked: nextMasked,\n pattern: maskPattern.pattern,\n unmaskedIndex: unmaskedIndex\n });\n\n target.setSelectionRange(nextCaret, nextCaret);\n\n onValueChangeProp?.(nextMasked, nextUnmasked);\n }\n return;\n }\n }\n }\n },\n [maskPattern, onKeyDownProp, onValueChangeProp, transformOpts, mask, withoutMask]\n );\n\n const InputPrimitive = asChild ? Slot : \"input\";\n\n return (\n <InputPrimitive\n // ⚠ Khác thượng nguồn: chỉ phát thuộc tính khi THẬT SỰ lỗi — `aria-invalid=\"false\"` hợp lệ\n // nhưng thừa, và cùng quy ước với `Input`/`Combobox` của gói. Đặt TRƯỚC spread để\n // `aria-invalid` do `<FormControl>` dán vào vẫn thắng.\n aria-invalid={invalid || undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-invalid={invalid ? \"\" : undefined}\n data-readonly={readOnly ? \"\" : undefined}\n data-required={required ? \"\" : undefined}\n data-slot=\"mask-input\"\n {...inputProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: chuỗi lớp của `Input` Comitor thay cho của shadcn, để ô có mặt nạ\n * đứng cạnh ô nhập thường trong cùng một form không lệch nhau (cao, nền, viền, cỡ chữ,\n * focus). Số đo của từng vai trò ở chú thích đầu `input.tsx`:\n * viền nghỉ `border-input` · hover `--control-edge-strong` · focus `--primary-ink`;\n * lỗi `border-destructive-ink` (không phải `border-destructive` — token TÔ NỀN).\n * Bỏ `dark:bg-input/30` (viết thẳng thang màu — bảng tương phản cao không đổi được) và\n * `shadow-xs` (không có trong bộ bóng của gói).\n */\n \"w-full h-9 px-3 rounded-md border bg-background text-sm text-foreground placeholder:text-muted-foreground\",\n \"transition-colors duration-150 ease-out\",\n \"focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-primary-ink\",\n \"disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-muted\",\n \"border-input hover:border-control-edge-strong\",\n \"aria-invalid:border-destructive-ink aria-invalid:hover:border-destructive-ink aria-invalid:focus:border-destructive-ink aria-invalid:focus:ring-destructive/30\",\n className\n )}\n placeholder={placeholderValue}\n ref={composedRef}\n value={displayValue}\n disabled={disabled}\n maxLength={calculatedMaxLength}\n readOnly={readOnly}\n required={required}\n inputMode={calculatedInputMode}\n min={min}\n max={max}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n onPaste={onPaste}\n onChange={onValueChange}\n onCompositionStart={onCompositionStart}\n onCompositionEnd={onCompositionEnd}\n />\n );\n}\n\nexport { MaskInput, type MaskInputProps };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/mask-input.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/mask-input) — 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\n/*\n * ⚠ Khác thượng nguồn: thượng nguồn là MỘT file. Ở đây nửa trên (bảng `MASK_PATTERNS` + mọi hàm\n * thuần) nằm ở `mask-input-constants.ts` — hằng dữ liệu export ra không được sống trong file\n * `\"use client\"` (Server Component đọc ra `undefined`, không throw). File này chỉ còn component,\n * và chỉ `import` từ đó, KHÔNG `export` lại (re-export vòng qua file client là vô hiệu).\n */\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { useComitorLocale } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport {\n applyMask,\n CURRENCY_PERCENTAGE_SYMBOLS,\n DEFAULT_CURRENCY,\n fromUnmaskedIndex,\n getCurrencyCaretPosition,\n getPatternCaretPosition,\n getUnmaskedValue,\n isCurrencyAtEnd,\n isCurrencyMask,\n MASK_PATTERNS,\n type MaskPattern,\n type MaskPatternKey,\n NUMERIC_MASK_PATTERNS,\n REGEX_CACHE,\n toUnmaskedIndex\n} from \"./mask-input-constants.js\";\n\ntype InputElement = React.ComponentRef<\"input\">;\n\ninterface MaskInputProps extends React.ComponentProps<\"input\"> {\n value?: string;\n defaultValue?: string;\n onValueChange?: (maskedValue: string, unmaskedValue: string) => void;\n onValidate?: (isValid: boolean, unmaskedValue: string) => void;\n validationMode?: \"onChange\" | \"onBlur\" | \"onSubmit\" | \"onTouched\" | \"all\";\n /**\n * Tên một mẫu dựng sẵn (`\"currency\"`, `\"percentage\"`, `\"time\"`, `\"creditCard\"`…) hoặc một\n * `MaskPattern` tự viết. ⚠ `\"phone\"`, `\"date\"`, `\"ssn\"`, `\"zipCode\"`, `\"ein\"` là định dạng Hoa Kỳ\n * — số điện thoại ở app Comitor dùng `PhoneInput`.\n */\n mask?: MaskPatternKey | MaskPattern;\n maskPlaceholder?: string;\n /**\n * Mã tiền tệ ISO 4217 cho `mask=\"currency\"`. Mặc định `\"USD\"`, như thượng nguồn.\n *\n * Tiền tệ là thiết lập VÙNG / nghiệp vụ, không suy từ ngôn ngữ: app Comitor truyền từ cài đặt\n * workspace (vd `\"VND\"`). Trước `3.0.0` mặc định cứng là `\"VND\"`.\n */\n currency?: string;\n /**\n * Locale BCP 47 cho `mask=\"currency\"` — quyết định dấu nhóm, dấu thập phân và vị trí ký hiệu.\n * Không truyền thì theo `locale` của `ComitorLocaleProvider` (không có provider → `\"en\"` →\n * `$1,234,567`; `\"vi\"` kèm `currency=\"VND\"` → `1.234.567 ₫`).\n *\n * ⚠ Khác thượng nguồn: thượng nguồn mặc định cứng `\"en-US\"`. Trước `3.0.0` mặc định cứng là\n * `\"vi-VN\"`.\n */\n locale?: string;\n asChild?: boolean;\n invalid?: boolean;\n withoutMask?: boolean;\n}\n\nfunction MaskInput(props: MaskInputProps) {\n const {\n value: valueProp,\n defaultValue,\n onValueChange: onValueChangeProp,\n onValidate,\n onBlur: onBlurProp,\n onFocus: onFocusProp,\n onKeyDown: onKeyDownProp,\n onPaste: onPasteProp,\n onCompositionStart: onCompositionStartProp,\n onCompositionEnd: onCompositionEndProp,\n validationMode = \"onChange\",\n mask,\n maskPlaceholder,\n currency = DEFAULT_CURRENCY,\n locale: localeProp,\n placeholder,\n inputMode,\n min,\n max,\n maxLength,\n asChild = false,\n disabled = false,\n invalid = false,\n readOnly = false,\n required = false,\n withoutMask = false,\n className,\n ref,\n ...inputProps\n } = props;\n\n const { locale: providerLocale } = useComitorLocale();\n const locale = localeProp ?? providerLocale;\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const [focused, setFocused] = React.useState(false);\n const [composing, setComposing] = React.useState(false);\n const [touched, setTouched] = React.useState(false);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const composedRef = useComposedRefs(ref, inputRef);\n\n const isControlled = valueProp !== undefined;\n const value = isControlled ? valueProp : internalValue;\n\n const maskPattern = React.useMemo(() => {\n if (typeof mask === \"string\") {\n return MASK_PATTERNS[mask];\n }\n return mask;\n }, [mask]);\n\n const transformOpts = React.useMemo(\n () => ({\n currency,\n locale\n }),\n [currency, locale]\n );\n\n const placeholderValue = React.useMemo(() => {\n if (withoutMask) return placeholder;\n\n if (placeholder && maskPlaceholder) {\n return focused ? maskPlaceholder : placeholder;\n }\n\n if (maskPlaceholder) {\n return focused ? maskPlaceholder : undefined;\n }\n\n return placeholder;\n }, [placeholder, maskPlaceholder, focused, withoutMask]);\n\n const displayValue = React.useMemo(() => {\n if (withoutMask || !maskPattern || !value) return value ?? \"\";\n const unmasked = getUnmaskedValue({\n value,\n transform: maskPattern.transform,\n ...transformOpts\n });\n return applyMask({\n value: unmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n }, [value, maskPattern, withoutMask, transformOpts, mask]);\n\n const tokenCount = React.useMemo(() => {\n if (!maskPattern || CURRENCY_PERCENTAGE_SYMBOLS.test(maskPattern.pattern)) return undefined;\n return maskPattern.pattern.match(REGEX_CACHE.hashPattern)?.length ?? 0;\n }, [maskPattern]);\n\n const calculatedMaxLength = tokenCount ? maskPattern?.pattern.length : maxLength;\n\n const calculatedInputMode = React.useMemo(() => {\n if (inputMode) return inputMode;\n if (!maskPattern) return undefined;\n\n if (mask === \"currency\" || mask === \"percentage\" || mask === \"ipv4\") {\n return \"decimal\";\n }\n\n if (typeof mask === \"string\" && NUMERIC_MASK_PATTERNS.test(mask)) {\n return \"numeric\";\n }\n return undefined;\n }, [maskPattern, mask, inputMode]);\n\n const shouldValidate = React.useCallback(\n (trigger: \"change\" | \"blur\") => {\n if (!onValidate || !maskPattern?.validate) return false;\n\n switch (validationMode) {\n case \"onChange\":\n return trigger === \"change\";\n case \"onBlur\":\n return trigger === \"blur\";\n case \"onSubmit\":\n return false;\n case \"onTouched\":\n return touched ? trigger === \"change\" : trigger === \"blur\";\n case \"all\":\n return true;\n default:\n return trigger === \"change\";\n }\n },\n [onValidate, maskPattern, validationMode, touched]\n );\n\n const validationOpts = React.useMemo(\n () => ({\n min: typeof min === \"string\" ? Number.parseFloat(min) : min,\n max: typeof max === \"string\" ? Number.parseFloat(max) : max\n }),\n [min, max]\n );\n\n const onInputValidate = React.useCallback(\n (unmaskedValue: string) => {\n if (onValidate && maskPattern?.validate) {\n const isValid = maskPattern.validate(unmaskedValue, validationOpts);\n onValidate(isValid, unmaskedValue);\n }\n },\n [onValidate, maskPattern?.validate, validationOpts]\n );\n\n const onValueChange = React.useCallback(\n (event: React.ChangeEvent<InputElement>) => {\n const inputValue = event.target.value;\n let newValue = inputValue;\n let unmaskedValue = inputValue;\n\n if (composing) {\n if (!isControlled) setInternalValue(inputValue);\n return;\n }\n\n if (withoutMask || !maskPattern) {\n if (!isControlled) setInternalValue(inputValue);\n if (shouldValidate(\"change\")) onValidate?.(true, inputValue);\n onValueChangeProp?.(inputValue, inputValue);\n return;\n }\n\n if (maskPattern) {\n unmaskedValue = getUnmaskedValue({\n value: inputValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n newValue = applyMask({\n value: unmaskedValue,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n if (inputRef.current && newValue !== inputValue) {\n const inputElement = inputRef.current;\n if (!(inputElement instanceof HTMLInputElement)) return;\n\n const oldCursorPosition = inputElement.selectionStart ?? 0;\n\n inputElement.value = newValue;\n\n const currentUnmasked = getUnmaskedValue({\n value: newValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n\n let newCursorPosition: number;\n\n const previousUnmasked = getUnmaskedValue({\n value,\n transform: maskPattern.transform,\n ...transformOpts\n });\n\n if (CURRENCY_PERCENTAGE_SYMBOLS.test(maskPattern.pattern)) {\n newCursorPosition = getCurrencyCaretPosition({\n newValue,\n mask,\n transformOpts,\n oldCursorPosition,\n oldValue: inputValue,\n previousUnmasked\n });\n } else {\n newCursorPosition = getPatternCaretPosition({\n newValue,\n maskPattern,\n currentUnmasked,\n oldCursorPosition,\n oldValue: inputValue,\n previousUnmasked\n });\n }\n\n if (isCurrencyMask({ mask, pattern: maskPattern.pattern })) {\n if (mask === \"currency\") {\n const currencyAtEnd = isCurrencyAtEnd(transformOpts);\n if (!currencyAtEnd) {\n newCursorPosition = Math.max(1, newCursorPosition);\n }\n } else {\n newCursorPosition = Math.max(1, newCursorPosition);\n }\n } else if (maskPattern.pattern.includes(\"%\")) {\n newCursorPosition = Math.min(newValue.length - 1, newCursorPosition);\n }\n\n newCursorPosition = Math.min(newCursorPosition, newValue.length);\n\n inputElement.setSelectionRange(newCursorPosition, newCursorPosition);\n }\n }\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n\n if (shouldValidate(\"change\")) {\n onInputValidate(unmaskedValue);\n }\n\n onValueChangeProp?.(newValue, unmaskedValue);\n },\n [\n maskPattern,\n isControlled,\n onValueChangeProp,\n onValidate,\n onInputValidate,\n composing,\n shouldValidate,\n withoutMask,\n transformOpts,\n mask,\n value\n ]\n );\n\n const onFocus = React.useCallback(\n (event: React.FocusEvent<InputElement>) => {\n onFocusProp?.(event);\n if (event.defaultPrevented) return;\n\n setFocused(true);\n },\n [onFocusProp]\n );\n\n const onBlur = React.useCallback(\n (event: React.FocusEvent<InputElement>) => {\n onBlurProp?.(event);\n if (event.defaultPrevented) return;\n\n setFocused(false);\n\n if (!touched) {\n setTouched(true);\n }\n\n if (shouldValidate(\"blur\")) {\n const currentValue = event.target.value;\n const unmaskedValue = maskPattern\n ? getUnmaskedValue({\n value: currentValue,\n transform: maskPattern.transform,\n ...transformOpts\n })\n : currentValue;\n onInputValidate(unmaskedValue);\n }\n },\n [onBlurProp, touched, shouldValidate, onInputValidate, maskPattern, transformOpts]\n );\n\n const onCompositionStart = React.useCallback(\n (event: React.CompositionEvent<InputElement>) => {\n onCompositionStartProp?.(event);\n if (event.defaultPrevented) return;\n\n setComposing(true);\n },\n [onCompositionStartProp]\n );\n\n const onCompositionEnd = React.useCallback(\n (event: React.CompositionEvent<InputElement>) => {\n onCompositionEndProp?.(event);\n if (event.defaultPrevented) return;\n\n setComposing(false);\n\n const inputElement = inputRef.current;\n if (!inputElement) return;\n if (!(inputElement instanceof HTMLInputElement)) return;\n const inputValue = inputElement.value;\n\n if (!maskPattern || withoutMask) {\n if (!isControlled) setInternalValue(inputValue);\n if (shouldValidate(\"change\")) onValidate?.(true, inputValue);\n onValueChangeProp?.(inputValue, inputValue);\n return;\n }\n\n const unmasked = getUnmaskedValue({\n value: inputValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n const masked = applyMask({\n value: unmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n if (!isControlled) setInternalValue(masked);\n if (shouldValidate(\"change\")) onInputValidate(unmasked);\n onValueChangeProp?.(masked, unmasked);\n },\n [\n onCompositionEndProp,\n maskPattern,\n withoutMask,\n isControlled,\n shouldValidate,\n onValidate,\n onValueChangeProp,\n transformOpts,\n mask,\n onInputValidate\n ]\n );\n\n const onPaste = React.useCallback(\n (event: React.ClipboardEvent<InputElement>) => {\n onPasteProp?.(event);\n if (event.defaultPrevented) return;\n\n if (withoutMask || !maskPattern) return;\n\n if (mask === \"ipv4\") return;\n\n const target = event.target as InputElement;\n if (!(target instanceof HTMLInputElement)) return;\n\n const pastedData = event.clipboardData.getData(\"text\");\n if (!pastedData) return;\n\n event.preventDefault();\n\n const currentValue = target.value;\n const selectionStart = target.selectionStart ?? 0;\n const selectionEnd = target.selectionEnd ?? 0;\n\n const beforeSelection = currentValue.slice(0, selectionStart);\n const afterSelection = currentValue.slice(selectionEnd);\n const newInputValue = beforeSelection + pastedData + afterSelection;\n\n const unmasked = getUnmaskedValue({\n value: newInputValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n const newMaskedValue = applyMask({\n value: unmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n target.value = newMaskedValue;\n\n /*\n * ⚠ Khác thượng nguồn (3.0.1): nhánh tiền tệ và phần trăm chỉ đặt con trỏ rồi đi TIẾP xuống\n * phần báo giá trị. Bản gốc `return` ngay trong hai nhánh này, nên dán vào ô tiền/phần trăm\n * chỉ đổi DOM: `onValueChange` không chạy, state nội bộ không đổi, và lần render kế tiếp trả\n * ô về số cũ — đo trên comitor-ds: dán \"777\" hiện `777 US$`, app vẫn giữ 12345.56.\n */\n let newCursorPosition = newMaskedValue.length;\n if (isCurrencyMask({ mask, pattern: maskPattern.pattern })) {\n const currencyAtEnd = isCurrencyAtEnd(transformOpts);\n const symbolStart = newMaskedValue.search(/\\s*[^\\d\\s]+$/);\n newCursorPosition = currencyAtEnd && symbolStart !== -1 ? symbolStart : newMaskedValue.length;\n } else if (maskPattern.pattern.includes(\"%\")) {\n newCursorPosition = Math.max(0, newMaskedValue.length - 1);\n } else {\n try {\n const unmaskedCount = unmasked.length;\n let position = 0;\n let count = 0;\n\n for (let i = 0; i < maskPattern.pattern.length && i < newMaskedValue.length; i++) {\n if (maskPattern.pattern[i] === \"#\") {\n count++;\n if (count <= unmaskedCount) {\n position = i + 1;\n }\n }\n }\n newCursorPosition = position;\n } catch {\n // Lỗi thì để con trỏ ở cuối.\n }\n }\n\n target.setSelectionRange(newCursorPosition, newCursorPosition);\n\n if (!isControlled) setInternalValue(newMaskedValue);\n if (shouldValidate(\"change\")) onInputValidate(unmasked);\n onValueChangeProp?.(newMaskedValue, unmasked);\n },\n [\n onPasteProp,\n withoutMask,\n maskPattern,\n mask,\n transformOpts,\n isControlled,\n shouldValidate,\n onInputValidate,\n onValueChangeProp\n ]\n );\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<InputElement>) => {\n onKeyDownProp?.(event);\n if (event.defaultPrevented) return;\n\n if (withoutMask || !maskPattern) return;\n\n if (mask === \"ipv4\") return;\n\n if (event.key === \"Backspace\") {\n const target = event.target as InputElement;\n if (!(target instanceof HTMLInputElement)) return;\n const cursorPosition = target.selectionStart ?? 0;\n const selectionEnd = target.selectionEnd ?? 0;\n const currentValue = target.value;\n\n if (\n mask === \"currency\" ||\n mask === \"percentage\" ||\n maskPattern.pattern.includes(\"$\") ||\n maskPattern.pattern.includes(\"€\") ||\n maskPattern.pattern.includes(\"%\")\n ) {\n return;\n }\n\n if (cursorPosition !== selectionEnd) {\n return;\n }\n\n if (cursorPosition > 0) {\n const charBeforeCursor = currentValue[cursorPosition - 1];\n\n if (charBeforeCursor) {\n event.preventDefault();\n\n const unmaskedIndex = toUnmaskedIndex({\n masked: currentValue,\n pattern: maskPattern.pattern,\n caret: cursorPosition\n });\n\n if (unmaskedIndex > 0) {\n const currentUnmasked = getUnmaskedValue({\n value: currentValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n const nextUnmasked = currentUnmasked.slice(0, unmaskedIndex - 1) + currentUnmasked.slice(unmaskedIndex);\n const nextMasked = applyMask({\n value: nextUnmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n target.value = nextMasked;\n const nextCaret = fromUnmaskedIndex({\n masked: nextMasked,\n pattern: maskPattern.pattern,\n unmaskedIndex: unmaskedIndex - 1\n });\n\n target.setSelectionRange(nextCaret, nextCaret);\n\n onValueChangeProp?.(nextMasked, nextUnmasked);\n }\n return;\n }\n }\n }\n\n if (event.key === \"Delete\") {\n const target = event.target as InputElement;\n if (!(target instanceof HTMLInputElement)) return;\n const cursorPosition = target.selectionStart ?? 0;\n const selectionEnd = target.selectionEnd ?? 0;\n const currentValue = target.value;\n\n if (\n mask === \"currency\" ||\n mask === \"percentage\" ||\n maskPattern.pattern.includes(\"$\") ||\n maskPattern.pattern.includes(\"€\") ||\n maskPattern.pattern.includes(\"%\")\n ) {\n return;\n }\n\n if (cursorPosition !== selectionEnd) {\n return;\n }\n\n if (cursorPosition < currentValue.length) {\n const charAtCursor = currentValue[cursorPosition];\n\n if (charAtCursor) {\n event.preventDefault();\n\n const unmaskedIndex = toUnmaskedIndex({\n masked: currentValue,\n pattern: maskPattern.pattern,\n caret: cursorPosition\n });\n\n const currentUnmasked = getUnmaskedValue({\n value: currentValue,\n transform: maskPattern.transform,\n ...transformOpts\n });\n\n if (unmaskedIndex < currentUnmasked.length) {\n const nextUnmasked = currentUnmasked.slice(0, unmaskedIndex) + currentUnmasked.slice(unmaskedIndex + 1);\n const nextMasked = applyMask({\n value: nextUnmasked,\n pattern: maskPattern.pattern,\n ...transformOpts,\n mask\n });\n\n target.value = nextMasked;\n const nextCaret = fromUnmaskedIndex({\n masked: nextMasked,\n pattern: maskPattern.pattern,\n unmaskedIndex: unmaskedIndex\n });\n\n target.setSelectionRange(nextCaret, nextCaret);\n\n onValueChangeProp?.(nextMasked, nextUnmasked);\n }\n return;\n }\n }\n }\n },\n [maskPattern, onKeyDownProp, onValueChangeProp, transformOpts, mask, withoutMask]\n );\n\n const InputPrimitive = asChild ? Slot : \"input\";\n\n return (\n <InputPrimitive\n // ⚠ Khác thượng nguồn: chỉ phát thuộc tính khi THẬT SỰ lỗi — `aria-invalid=\"false\"` hợp lệ\n // nhưng thừa, và cùng quy ước với `Input`/`Combobox` của gói. Đặt TRƯỚC spread để\n // `aria-invalid` do `<FormControl>` dán vào vẫn thắng.\n aria-invalid={invalid || undefined}\n data-disabled={disabled ? \"\" : undefined}\n data-invalid={invalid ? \"\" : undefined}\n data-readonly={readOnly ? \"\" : undefined}\n data-required={required ? \"\" : undefined}\n data-slot=\"mask-input\"\n {...inputProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: chuỗi lớp của `Input` Comitor thay cho của shadcn, để ô có mặt nạ\n * đứng cạnh ô nhập thường trong cùng một form không lệch nhau (cao, nền, viền, cỡ chữ,\n * focus). Số đo của từng vai trò ở chú thích đầu `input.tsx`:\n * viền nghỉ `border-input` · hover `--control-edge-strong` · focus `--primary-ink`;\n * lỗi `border-destructive-ink` (không phải `border-destructive` — token TÔ NỀN).\n * Bỏ `dark:bg-input/30` (viết thẳng thang màu — bảng tương phản cao không đổi được) và\n * `shadow-xs` (không có trong bộ bóng của gói).\n */\n \"w-full h-9 px-3 rounded-md border bg-background text-sm text-foreground placeholder:text-muted-foreground\",\n \"transition-colors duration-150 ease-out\",\n \"focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-primary-ink\",\n \"disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-muted\",\n \"border-input hover:border-control-edge-strong\",\n \"aria-invalid:border-destructive-ink aria-invalid:hover:border-destructive-ink aria-invalid:focus:border-destructive-ink aria-invalid:focus:ring-destructive/30\",\n className\n )}\n placeholder={placeholderValue}\n ref={composedRef}\n value={displayValue}\n disabled={disabled}\n maxLength={calculatedMaxLength}\n readOnly={readOnly}\n required={required}\n inputMode={calculatedInputMode}\n min={min}\n max={max}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n onPaste={onPaste}\n onChange={onValueChange}\n onCompositionStart={onCompositionStart}\n onCompositionEnd={onCompositionEnd}\n />\n );\n}\n\nexport { MaskInput, type MaskInputProps };\n"],"mappings":"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","names":[]}
@@ -22,9 +22,10 @@ declare function MentionInput({ className, ...props }: MentionInputProps): React
22
22
  type MentionContentProps = React.ComponentProps<typeof MentionPrimitive.Content>;
23
23
  /**
24
24
  * Danh sách gợi ý (`role="listbox"`), render qua portal.
25
- * Tên cho trình đọc màn hình mặc định "Danh sách gợi ý" — đổi bằng `aria-label`.
25
+ * Tên cho trình đọc màn hình mặc định là khoá `listLabel` của namespace `mention`
26
+ * (`ComitorLocaleProvider`) — đổi bằng `aria-label`.
26
27
  */
27
- declare function MentionContent({ className, children, "aria-label": ariaLabel, ...props }: MentionContentProps): React.JSX.Element;
28
+ declare function MentionContent({ className, children, "aria-label": ariaLabelProp, ...props }: MentionContentProps): React.JSX.Element;
28
29
  type MentionItemProps = React.ComponentProps<typeof MentionPrimitive.Item>;
29
30
  declare function MentionItem({ className, children, ...props }: MentionItemProps): React.JSX.Element;
30
31
 
@@ -3,6 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import * as MentionPrimitive from "@diceui/mention";
4
4
  import { useDirection } from "@radix-ui/react-direction";
5
5
  import * as React from "react";
6
+ import { useComitorMessages } from "../../i18n/locale-provider.js";
6
7
  import { cn } from "../../lib/cn.js";
7
8
  const MentionListIdContext = React.createContext(void 0);
8
9
  function Mention({ className, dir: dirProp, ...props }) {
@@ -69,13 +70,10 @@ function MentionInput({ className, ...props }) {
69
70
  }
70
71
  );
71
72
  }
72
- function MentionContent({
73
- className,
74
- children,
75
- "aria-label": ariaLabel = "Danh s\xE1ch g\u1EE3i \xFD",
76
- ...props
77
- }) {
73
+ function MentionContent({ className, children, "aria-label": ariaLabelProp, ...props }) {
78
74
  const listId = React.useContext(MentionListIdContext);
75
+ const t = useComitorMessages("mention");
76
+ const ariaLabel = ariaLabelProp ?? t.listLabel;
79
77
  return /* @__PURE__ */ jsx(MentionPrimitive.Portal, { children: /* @__PURE__ */ jsx(
80
78
  MentionPrimitive.Content,
81
79
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/mention.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/mention) — 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\nimport * as MentionPrimitive from \"@diceui/mention\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * ⚠ Khác thượng nguồn: context RIÊNG của lớp bọc, chỉ mang một id — xem chú thích ở `MentionInput`.\n */\nconst MentionListIdContext = React.createContext<string | undefined>(undefined);\n\n/**\n * Mention — ô nhập có gợi ý `@nhắc tên`: gõ ký tự kích hoạt (mặc định `@`) là mở danh sách, chọn\n * một mục thì chèn `@Tên` vào ô và đánh dấu nó thành một khối (xoá là xoá cả khối).\n * Dùng cho ô soạn tin của Chat và bình luận trong task.\n *\n * Phần lõi (dò ký tự kích hoạt, lọc, điều hướng bàn phím, định vị danh sách theo con trỏ) là gói\n * headless `@diceui/mention`; file này chỉ phủ style theo token của Comitor.\n */\ntype MentionProps = React.ComponentProps<typeof MentionPrimitive.Root>;\n\nfunction Mention({ className, dir: dirProp, ...props }: MentionProps) {\n /*\n * ⚠ Khác thượng nguồn: `@diceui/shared` có `useDirection` RIÊNG, đọc một `DirectionContext` mà\n * gói không xuất ra — nên `DirectionProvider` của Radix (nguồn hướng chữ của mọi component khác\n * trong gói) không tới được nó. Đọc hướng từ Radix rồi truyền xuống bằng prop `dir`.\n */\n const dir = useDirection(dirProp);\n const listId = React.useId();\n\n return (\n <MentionListIdContext.Provider value={listId}>\n <MentionPrimitive.Root\n data-slot=\"mention\"\n dir={dir}\n className={cn(\n /*\n * Khối `@Tên` đã chèn. Gói vẽ nó bằng một lớp phủ (`MentionHighlighter`) định vị TUYỆT\n * ĐỐI nằm ĐÈ LÊN ô nhập: chữ thường của lớp phủ trong suốt để lộ chữ của ô bên dưới,\n * chỉ đoạn `[data-tag]` là có nền.\n *\n * ⚠ Khác thượng nguồn: bản gốc tô `bg-blue-200 text-blue-950` / `dark:bg-blue-800\n * dark:text-blue-50` (bảng màu mặc định của Tailwind). Đổi sang cặp tonal của gói\n * (`info` = teal — bậc \"xanh\" của Comitor), và CHỮ là `text-foreground` chứ không phải\n * `text-info-ink`: nền tint `/15` trong suốt nên chữ của ô nhập vẫn lộ ra dưới khối,\n * chữ khối vẽ đè đúng chỗ đó — hai lớp chữ phải CÙNG MÀU, lệch màu là thành viền răng\n * cưa quanh từng nét. (Bản gốc cũng chọn chữ gần như trùng màu chữ thường ở cả hai theme.)\n * Khối không chỉ nói bằng màu: nó luôn mang ký tự kích hoạt `@` ở đầu.\n * chữ ⇄ khối: 15,69:1 (light, trên nền trắng) · 15,15:1 (dark) · tệ nhất 13,44:1\n * (dark tương phản cao, `--teal` #2DD4BF). Khối ⇄ nền trang 1,21:1 — cố ý nhạt.\n */\n \"**:data-tag:rounded **:data-tag:bg-info/15 **:data-tag:py-px **:data-tag:text-foreground\",\n className\n )}\n {...props}\n />\n </MentionListIdContext.Provider>\n );\n}\n\ntype MentionLabelProps = React.ComponentProps<typeof MentionPrimitive.Label>;\n\nfunction MentionLabel({ className, ...props }: MentionLabelProps) {\n return (\n <MentionPrimitive.Label\n data-slot=\"mention-label\"\n className={cn(\"px-0.5 py-1.5 text-sm font-semibold\", className)}\n {...props}\n />\n );\n}\n\ntype MentionInputProps = React.ComponentProps<typeof MentionPrimitive.Input>;\n\n/**\n * Ô nhập của `Mention`. Muốn nhiều dòng (ô soạn tin) thì `asChild` với một `<textarea />`.\n * Không có `MentionLabel` thì PHẢI truyền `aria-label`.\n */\nfunction MentionInput({ className, ...props }: MentionInputProps) {\n const listId = React.useContext(MentionListIdContext);\n\n return (\n <MentionPrimitive.Input\n data-slot=\"mention-input\"\n /*\n * ⚠ Khác thượng nguồn (a11y): ô nhập là `role=\"combobox\"` và gói đặt `aria-controls` trỏ tới\n * một id sinh trong gốc — nhưng `MentionContent` của gói KHÔNG gắn id đó lên `role=\"listbox\"`,\n * nên quan hệ combobox → listbox trỏ vào hư không. Lớp bọc tự sinh một id, gắn ở CẢ HAI đầu\n * (props của ta được trải SAU props của gói nên thắng).\n */\n aria-controls={listId}\n className={cn(\n /*\n * Khuôn viền/focus của `Input`: `border-input` lúc nghỉ, `--control-edge-strong` khi rê,\n * `--primary-ink` + `ring-2 ring/50` khi focus. Nền theo `Textarea` (ô soạn tin thường là\n * `asChild` + `<textarea>`): trong suốt, ở dark tô `--input-fill` — không `bg-input/30`,\n * xem chú thích đầu textarea.tsx.\n * ⚠ Khác thượng nguồn: `focus-visible:ring-1 ring-ring` → khuôn focus của `Input`; bỏ\n * `shadow-xs` (bóng của gói chỉ đi qua `--elevation-*`, `Input` không có bóng).\n */\n \"flex w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm text-foreground transition-colors duration-150 ease-out placeholder:text-muted-foreground hover:border-control-edge-strong focus:border-primary-ink focus:outline-none focus:ring-2 focus:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input-fill\",\n className\n )}\n {...props}\n />\n );\n}\n\ntype MentionContentProps = React.ComponentProps<typeof MentionPrimitive.Content>;\n\n/**\n * Danh sách gợi ý (`role=\"listbox\"`), render qua portal.\n * Tên cho trình đọc màn hình mặc định \"Danh sách gợi ý\" — đổi bằng `aria-label`.\n */\nfunction MentionContent({\n className,\n children,\n \"aria-label\": ariaLabel = \"Danh sách gợi ý\",\n ...props\n}: MentionContentProps) {\n const listId = React.useContext(MentionListIdContext);\n\n return (\n <MentionPrimitive.Portal>\n <MentionPrimitive.Content\n data-slot=\"mention-content\"\n // ⚠ Khác thượng nguồn (a11y): id để `aria-controls` của ô nhập trỏ tới được — xem\n // `MentionInput`. Và `role=\"listbox\"` BẮT BUỘC có tên (ARIA 1.2); bản gốc không đặt.\n id={listId}\n aria-label={ariaLabel}\n className={cn(\n /*\n * Mặt nổi cùng bộ với `PopoverContent`/`Command`: `bg-popover text-popover-foreground`,\n * bóng `shadow-overlay` (đổi theo theme qua `--elevation-*`).\n * ⚠ Khác thượng nguồn: `shadow-md` → `shadow-overlay`; thêm `origin-(--dice-transform-origin)`\n * — biến gói headless tính sẵn theo cạnh bám, để hiệu ứng `zoom-in-95` nở ra TỪ phía con\n * trỏ như `PopoverContent` (bản gốc nở từ tâm).\n */\n \"relative z-50 min-w-32 origin-(--dice-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-overlay data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n >\n {children}\n </MentionPrimitive.Content>\n </MentionPrimitive.Portal>\n );\n}\n\ntype MentionItemProps = React.ComponentProps<typeof MentionPrimitive.Item>;\n\nfunction MentionItem({ className, children, ...props }: MentionItemProps) {\n return (\n <MentionPrimitive.Item\n data-slot=\"mention-item\"\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: bộ lớp của `CommandItem` (mục đang sáng `bg-accent`/`text-accent-\n * foreground`, khoảng đệm theo trục mật độ `--menu-item-py`, icon con mặc định\n * `text-muted-foreground` cỡ 4) — chỉ đổi thuộc tính trạng thái sang dạng của gói headless\n * (`data-highlighted` / `data-disabled` là thuộc tính CÓ MẶT, không phải `=\"true\"`).\n */\n \"relative mt-1 flex w-full cursor-default items-center gap-2 rounded-sm px-2 py-(--menu-item-py) text-sm outline-hidden select-none first:mt-0 data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-accent data-highlighted:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className\n )}\n {...props}\n >\n {children}\n </MentionPrimitive.Item>\n );\n}\n\nexport {\n Mention,\n MentionContent,\n type MentionContentProps,\n MentionInput,\n type MentionInputProps,\n MentionItem,\n type MentionItemProps,\n MentionLabel,\n type MentionLabelProps,\n type MentionProps\n};\n"],"mappings":";AAsCM;AA/BN,YAAY,sBAAsB;AAClC,SAAS,oBAAoB;AAC7B,YAAY,WAAW;AACvB,SAAS,UAAU;AAKnB,MAAM,uBAAuB,MAAM,cAAkC,MAAS;AAY9E,SAAS,QAAQ,EAAE,WAAW,KAAK,SAAS,GAAG,MAAM,GAAiB;AAMpE,QAAM,MAAM,aAAa,OAAO;AAChC,QAAM,SAAS,MAAM,MAAM;AAE3B,SACE,oBAAC,qBAAqB,UAArB,EAA8B,OAAO,QACpC;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAgBT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;AAIA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,uCAAuC,SAAS;AAAA,MAC7D,GAAG;AAAA;AAAA,EACN;AAEJ;AAQA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,QAAM,SAAS,MAAM,WAAW,oBAAoB;AAEpD,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MAOV,iBAAe;AAAA,MACf,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAST;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAQA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA,cAAc,YAAY;AAAA,EAC1B,GAAG;AACL,GAAwB;AACtB,QAAM,SAAS,MAAM,WAAW,oBAAoB;AAEpD,SACE,oBAAC,iBAAiB,QAAjB,EACC;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MAGV,IAAI;AAAA,MACJ,cAAY;AAAA,MACZ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH,GACF;AAEJ;AAIA,SAAS,YAAY,EAAE,WAAW,UAAU,GAAG,MAAM,GAAqB;AACxE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/mention.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/mention) — 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\nimport * as MentionPrimitive from \"@diceui/mention\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport * as React from \"react\";\nimport { useComitorMessages } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * ⚠ Khác thượng nguồn: context RIÊNG của lớp bọc, chỉ mang một id — xem chú thích ở `MentionInput`.\n */\nconst MentionListIdContext = React.createContext<string | undefined>(undefined);\n\n/**\n * Mention — ô nhập có gợi ý `@nhắc tên`: gõ ký tự kích hoạt (mặc định `@`) là mở danh sách, chọn\n * một mục thì chèn `@Tên` vào ô và đánh dấu nó thành một khối (xoá là xoá cả khối).\n * Dùng cho ô soạn tin của Chat và bình luận trong task.\n *\n * Phần lõi (dò ký tự kích hoạt, lọc, điều hướng bàn phím, định vị danh sách theo con trỏ) là gói\n * headless `@diceui/mention`; file này chỉ phủ style theo token của Comitor.\n */\ntype MentionProps = React.ComponentProps<typeof MentionPrimitive.Root>;\n\nfunction Mention({ className, dir: dirProp, ...props }: MentionProps) {\n /*\n * ⚠ Khác thượng nguồn: `@diceui/shared` có `useDirection` RIÊNG, đọc một `DirectionContext` mà\n * gói không xuất ra — nên `DirectionProvider` của Radix (nguồn hướng chữ của mọi component khác\n * trong gói) không tới được nó. Đọc hướng từ Radix rồi truyền xuống bằng prop `dir`.\n */\n const dir = useDirection(dirProp);\n const listId = React.useId();\n\n return (\n <MentionListIdContext.Provider value={listId}>\n <MentionPrimitive.Root\n data-slot=\"mention\"\n dir={dir}\n className={cn(\n /*\n * Khối `@Tên` đã chèn. Gói vẽ nó bằng một lớp phủ (`MentionHighlighter`) định vị TUYỆT\n * ĐỐI nằm ĐÈ LÊN ô nhập: chữ thường của lớp phủ trong suốt để lộ chữ của ô bên dưới,\n * chỉ đoạn `[data-tag]` là có nền.\n *\n * ⚠ Khác thượng nguồn: bản gốc tô `bg-blue-200 text-blue-950` / `dark:bg-blue-800\n * dark:text-blue-50` (bảng màu mặc định của Tailwind). Đổi sang cặp tonal của gói\n * (`info` = teal — bậc \"xanh\" của Comitor), và CHỮ là `text-foreground` chứ không phải\n * `text-info-ink`: nền tint `/15` trong suốt nên chữ của ô nhập vẫn lộ ra dưới khối,\n * chữ khối vẽ đè đúng chỗ đó — hai lớp chữ phải CÙNG MÀU, lệch màu là thành viền răng\n * cưa quanh từng nét. (Bản gốc cũng chọn chữ gần như trùng màu chữ thường ở cả hai theme.)\n * Khối không chỉ nói bằng màu: nó luôn mang ký tự kích hoạt `@` ở đầu.\n * chữ ⇄ khối: 15,69:1 (light, trên nền trắng) · 15,15:1 (dark) · tệ nhất 13,44:1\n * (dark tương phản cao, `--teal` #2DD4BF). Khối ⇄ nền trang 1,21:1 — cố ý nhạt.\n */\n \"**:data-tag:rounded **:data-tag:bg-info/15 **:data-tag:py-px **:data-tag:text-foreground\",\n className\n )}\n {...props}\n />\n </MentionListIdContext.Provider>\n );\n}\n\ntype MentionLabelProps = React.ComponentProps<typeof MentionPrimitive.Label>;\n\nfunction MentionLabel({ className, ...props }: MentionLabelProps) {\n return (\n <MentionPrimitive.Label\n data-slot=\"mention-label\"\n className={cn(\"px-0.5 py-1.5 text-sm font-semibold\", className)}\n {...props}\n />\n );\n}\n\ntype MentionInputProps = React.ComponentProps<typeof MentionPrimitive.Input>;\n\n/**\n * Ô nhập của `Mention`. Muốn nhiều dòng (ô soạn tin) thì `asChild` với một `<textarea />`.\n * Không có `MentionLabel` thì PHẢI truyền `aria-label`.\n */\nfunction MentionInput({ className, ...props }: MentionInputProps) {\n const listId = React.useContext(MentionListIdContext);\n\n return (\n <MentionPrimitive.Input\n data-slot=\"mention-input\"\n /*\n * ⚠ Khác thượng nguồn (a11y): ô nhập là `role=\"combobox\"` và gói đặt `aria-controls` trỏ tới\n * một id sinh trong gốc — nhưng `MentionContent` của gói KHÔNG gắn id đó lên `role=\"listbox\"`,\n * nên quan hệ combobox → listbox trỏ vào hư không. Lớp bọc tự sinh một id, gắn ở CẢ HAI đầu\n * (props của ta được trải SAU props của gói nên thắng).\n */\n aria-controls={listId}\n className={cn(\n /*\n * Khuôn viền/focus của `Input`: `border-input` lúc nghỉ, `--control-edge-strong` khi rê,\n * `--primary-ink` + `ring-2 ring/50` khi focus. Nền theo `Textarea` (ô soạn tin thường là\n * `asChild` + `<textarea>`): trong suốt, ở dark tô `--input-fill` — không `bg-input/30`,\n * xem chú thích đầu textarea.tsx.\n * ⚠ Khác thượng nguồn: `focus-visible:ring-1 ring-ring` → khuôn focus của `Input`; bỏ\n * `shadow-xs` (bóng của gói chỉ đi qua `--elevation-*`, `Input` không có bóng).\n */\n \"flex w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm text-foreground transition-colors duration-150 ease-out placeholder:text-muted-foreground hover:border-control-edge-strong focus:border-primary-ink focus:outline-none focus:ring-2 focus:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input-fill\",\n className\n )}\n {...props}\n />\n );\n}\n\ntype MentionContentProps = React.ComponentProps<typeof MentionPrimitive.Content>;\n\n/**\n * Danh sách gợi ý (`role=\"listbox\"`), render qua portal.\n * Tên cho trình đọc màn hình mặc định là khoá `listLabel` của namespace `mention`\n * (`ComitorLocaleProvider`) — đổi bằng `aria-label`.\n */\nfunction MentionContent({ className, children, \"aria-label\": ariaLabelProp, ...props }: MentionContentProps) {\n const listId = React.useContext(MentionListIdContext);\n const t = useComitorMessages(\"mention\");\n const ariaLabel = ariaLabelProp ?? t.listLabel;\n\n return (\n <MentionPrimitive.Portal>\n <MentionPrimitive.Content\n data-slot=\"mention-content\"\n // ⚠ Khác thượng nguồn (a11y): id để `aria-controls` của ô nhập trỏ tới được — xem\n // `MentionInput`. Và `role=\"listbox\"` BẮT BUỘC có tên (ARIA 1.2); bản gốc không đặt.\n id={listId}\n aria-label={ariaLabel}\n className={cn(\n /*\n * Mặt nổi cùng bộ với `PopoverContent`/`Command`: `bg-popover text-popover-foreground`,\n * bóng `shadow-overlay` (đổi theo theme qua `--elevation-*`).\n * ⚠ Khác thượng nguồn: `shadow-md` → `shadow-overlay`; thêm `origin-(--dice-transform-origin)`\n * — biến gói headless tính sẵn theo cạnh bám, để hiệu ứng `zoom-in-95` nở ra TỪ phía con\n * trỏ như `PopoverContent` (bản gốc nở từ tâm).\n */\n \"relative z-50 min-w-32 origin-(--dice-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-overlay data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className\n )}\n {...props}\n >\n {children}\n </MentionPrimitive.Content>\n </MentionPrimitive.Portal>\n );\n}\n\ntype MentionItemProps = React.ComponentProps<typeof MentionPrimitive.Item>;\n\nfunction MentionItem({ className, children, ...props }: MentionItemProps) {\n return (\n <MentionPrimitive.Item\n data-slot=\"mention-item\"\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: bộ lớp của `CommandItem` (mục đang sáng `bg-accent`/`text-accent-\n * foreground`, khoảng đệm theo trục mật độ `--menu-item-py`, icon con mặc định\n * `text-muted-foreground` cỡ 4) — chỉ đổi thuộc tính trạng thái sang dạng của gói headless\n * (`data-highlighted` / `data-disabled` là thuộc tính CÓ MẶT, không phải `=\"true\"`).\n */\n \"relative mt-1 flex w-full cursor-default items-center gap-2 rounded-sm px-2 py-(--menu-item-py) text-sm outline-hidden select-none first:mt-0 data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-accent data-highlighted:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className\n )}\n {...props}\n >\n {children}\n </MentionPrimitive.Item>\n );\n}\n\nexport {\n Mention,\n MentionContent,\n type MentionContentProps,\n MentionInput,\n type MentionInputProps,\n MentionItem,\n type MentionItemProps,\n MentionLabel,\n type MentionLabelProps,\n type MentionProps\n};\n"],"mappings":";AAuCM;AAhCN,YAAY,sBAAsB;AAClC,SAAS,oBAAoB;AAC7B,YAAY,WAAW;AACvB,SAAS,0BAA0B;AACnC,SAAS,UAAU;AAKnB,MAAM,uBAAuB,MAAM,cAAkC,MAAS;AAY9E,SAAS,QAAQ,EAAE,WAAW,KAAK,SAAS,GAAG,MAAM,GAAiB;AAMpE,QAAM,MAAM,aAAa,OAAO;AAChC,QAAM,SAAS,MAAM,MAAM;AAE3B,SACE,oBAAC,qBAAqB,UAArB,EAA8B,OAAO,QACpC;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAgBT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;AAIA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,uCAAuC,SAAS;AAAA,MAC7D,GAAG;AAAA;AAAA,EACN;AAEJ;AAQA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,QAAM,SAAS,MAAM,WAAW,oBAAoB;AAEpD,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MAOV,iBAAe;AAAA,MACf,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAST;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AASA,SAAS,eAAe,EAAE,WAAW,UAAU,cAAc,eAAe,GAAG,MAAM,GAAwB;AAC3G,QAAM,SAAS,MAAM,WAAW,oBAAoB;AACpD,QAAM,IAAI,mBAAmB,SAAS;AACtC,QAAM,YAAY,iBAAiB,EAAE;AAErC,SACE,oBAAC,iBAAiB,QAAjB,EACC;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MAGV,IAAI;AAAA,MACJ,cAAY;AAAA,MACZ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH,GACF;AAEJ;AAIA,SAAS,YAAY,EAAE,WAAW,UAAU,GAAG,MAAM,GAAqB;AACxE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;","names":[]}