@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.
- package/README.md +63 -24
- package/dist/chart/chart.d.ts +6 -1
- package/dist/chart/chart.js +3 -1
- package/dist/chart/chart.js.map +1 -1
- package/dist/chart/comitor-chart.d.ts +4 -1
- package/dist/chart/comitor-chart.js +3 -1
- package/dist/chart/comitor-chart.js.map +1 -1
- package/dist/components/composite/avatar-group.d.ts +9 -6
- package/dist/components/composite/avatar-group.js +6 -2
- package/dist/components/composite/avatar-group.js.map +1 -1
- package/dist/components/composite/combobox.d.ts +11 -11
- package/dist/components/composite/combobox.js +25 -11
- package/dist/components/composite/combobox.js.map +1 -1
- package/dist/components/composite/command-palette-constants.d.ts +15 -5
- package/dist/components/composite/command-palette-constants.js +3 -2
- package/dist/components/composite/command-palette-constants.js.map +1 -1
- package/dist/components/composite/command-palette.d.ts +2 -2
- package/dist/components/composite/command-palette.js +8 -5
- package/dist/components/composite/command-palette.js.map +1 -1
- package/dist/components/composite/confirm-dialog.d.ts +8 -8
- package/dist/components/composite/confirm-dialog.js +8 -3
- package/dist/components/composite/confirm-dialog.js.map +1 -1
- package/dist/components/composite/copy-button.d.ts +7 -3
- package/dist/components/composite/copy-button.js +7 -3
- package/dist/components/composite/copy-button.js.map +1 -1
- package/dist/components/composite/data-table-constants.d.ts +7 -3
- package/dist/components/composite/data-table-constants.js +2 -9
- package/dist/components/composite/data-table-constants.js.map +1 -1
- package/dist/components/composite/data-table.js +3 -3
- package/dist/components/composite/data-table.js.map +1 -1
- package/dist/components/composite/date-picker-constants.d.ts +32 -9
- package/dist/components/composite/date-picker-constants.js +16 -11
- package/dist/components/composite/date-picker-constants.js.map +1 -1
- package/dist/components/composite/date-picker.d.ts +19 -10
- package/dist/components/composite/date-picker.js +33 -9
- package/dist/components/composite/date-picker.js.map +1 -1
- package/dist/components/composite/filter-chips.d.ts +4 -3
- package/dist/components/composite/filter-chips.js +6 -2
- package/dist/components/composite/filter-chips.js.map +1 -1
- package/dist/components/composite/inline-edit.d.ts +14 -3
- package/dist/components/composite/inline-edit.js +8 -4
- package/dist/components/composite/inline-edit.js.map +1 -1
- package/dist/components/composite/relative-time.d.ts +4 -4
- package/dist/components/composite/relative-time.js +4 -2
- package/dist/components/composite/relative-time.js.map +1 -1
- package/dist/components/composite/route-tabs-nav.d.ts +9 -0
- package/dist/components/composite/route-tabs-nav.js +11 -0
- package/dist/components/composite/route-tabs-nav.js.map +1 -0
- package/dist/components/composite/route-tabs.d.ts +4 -1
- package/dist/components/composite/route-tabs.js +3 -2
- package/dist/components/composite/route-tabs.js.map +1 -1
- package/dist/components/composite/search-filter-bar-constants.d.ts +18 -8
- package/dist/components/composite/search-filter-bar-constants.js +2 -14
- package/dist/components/composite/search-filter-bar-constants.js.map +1 -1
- package/dist/components/composite/search-filter-bar.d.ts +5 -2
- package/dist/components/composite/search-filter-bar.js +2 -2
- package/dist/components/composite/search-filter-bar.js.map +1 -1
- package/dist/components/composite/stat-card.d.ts +1 -1
- package/dist/components/composite/table-pagination-constants.d.ts +16 -11
- package/dist/components/composite/table-pagination-constants.js +2 -12
- package/dist/components/composite/table-pagination-constants.js.map +1 -1
- package/dist/components/composite/table-pagination.d.ts +6 -3
- package/dist/components/composite/table-pagination.js +3 -5
- package/dist/components/composite/table-pagination.js.map +1 -1
- package/dist/components/composite/toast.d.ts +5 -2
- package/dist/components/composite/toast.js +3 -1
- package/dist/components/composite/toast.js.map +1 -1
- package/dist/components/ui/action-bar-constants.d.ts +8 -4
- package/dist/components/ui/action-bar-constants.js +2 -5
- package/dist/components/ui/action-bar-constants.js.map +1 -1
- package/dist/components/ui/action-bar.d.ts +5 -4
- package/dist/components/ui/action-bar.js +47 -3
- package/dist/components/ui/action-bar.js.map +1 -1
- package/dist/components/ui/alert.d.ts +1 -1
- package/dist/components/ui/badge-overflow.d.ts +2 -2
- package/dist/components/ui/badge-overflow.js +4 -2
- package/dist/components/ui/badge-overflow.js.map +1 -1
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/banner.d.ts +6 -5
- package/dist/components/ui/banner.js +3 -1
- package/dist/components/ui/banner.js.map +1 -1
- package/dist/components/ui/breadcrumb.d.ts +11 -4
- package/dist/components/ui/breadcrumb.js +7 -2
- package/dist/components/ui/breadcrumb.js.map +1 -1
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/components/ui/calendar.d.ts +12 -5
- package/dist/components/ui/calendar.js +38 -3
- package/dist/components/ui/calendar.js.map +1 -1
- package/dist/components/ui/carousel.d.ts +4 -3
- package/dist/components/ui/carousel.js +11 -4
- package/dist/components/ui/carousel.js.map +1 -1
- package/dist/components/ui/circular-progress.js +10 -2
- package/dist/components/ui/circular-progress.js.map +1 -1
- package/dist/components/ui/command.d.ts +5 -4
- package/dist/components/ui/command.js +6 -2
- package/dist/components/ui/command.js.map +1 -1
- package/dist/components/ui/cropper-constants.d.ts +8 -3
- package/dist/components/ui/cropper-constants.js +2 -6
- package/dist/components/ui/cropper-constants.js.map +1 -1
- package/dist/components/ui/cropper.d.ts +2 -1
- package/dist/components/ui/cropper.js +2 -2
- package/dist/components/ui/cropper.js.map +1 -1
- package/dist/components/ui/dialog.d.ts +2 -2
- package/dist/components/ui/dialog.js +4 -1
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/item.d.ts +2 -2
- package/dist/components/ui/kanban-constants.d.ts +7 -1
- package/dist/components/ui/kanban-constants.js +2 -17
- package/dist/components/ui/kanban-constants.js.map +1 -1
- package/dist/components/ui/kanban.d.ts +2 -2
- package/dist/components/ui/kanban.js +11 -5
- package/dist/components/ui/kanban.js.map +1 -1
- package/dist/components/ui/key-value-constants.d.ts +7 -3
- package/dist/components/ui/key-value-constants.js +2 -7
- package/dist/components/ui/key-value-constants.js.map +1 -1
- package/dist/components/ui/key-value.d.ts +5 -5
- package/dist/components/ui/key-value.js +72 -78
- package/dist/components/ui/key-value.js.map +1 -1
- package/dist/components/ui/mask-input-constants.d.ts +2 -3
- package/dist/components/ui/mask-input-constants.js +36 -77
- package/dist/components/ui/mask-input-constants.js.map +1 -1
- package/dist/components/ui/mask-input.d.ts +7 -4
- package/dist/components/ui/mask-input.js +22 -22
- package/dist/components/ui/mask-input.js.map +1 -1
- package/dist/components/ui/mention.d.ts +3 -2
- package/dist/components/ui/mention.js +4 -6
- package/dist/components/ui/mention.js.map +1 -1
- package/dist/components/ui/pagination.d.ts +13 -9
- package/dist/components/ui/pagination.js +15 -6
- package/dist/components/ui/pagination.js.map +1 -1
- package/dist/components/ui/phone-input-constants.d.ts +17 -6
- package/dist/components/ui/phone-input-constants.js +2 -5
- package/dist/components/ui/phone-input-constants.js.map +1 -1
- package/dist/components/ui/phone-input.d.ts +18 -11
- package/dist/components/ui/phone-input.js +14 -12
- package/dist/components/ui/phone-input.js.map +1 -1
- package/dist/components/ui/progress.d.ts +2 -2
- package/dist/components/ui/rating.d.ts +2 -1
- package/dist/components/ui/rating.js +8 -4
- package/dist/components/ui/rating.js.map +1 -1
- package/dist/components/ui/scroll-spy.js +3 -1
- package/dist/components/ui/scroll-spy.js.map +1 -1
- package/dist/components/ui/sheet.d.ts +2 -2
- package/dist/components/ui/sheet.js +10 -1
- package/dist/components/ui/sheet.js.map +1 -1
- package/dist/components/ui/slider.d.ts +1 -1
- package/dist/components/ui/slider.js +15 -1
- package/dist/components/ui/slider.js.map +1 -1
- package/dist/components/ui/sonner.js +2 -0
- package/dist/components/ui/sonner.js.map +1 -1
- package/dist/components/ui/spinner.js +4 -1
- package/dist/components/ui/spinner.js.map +1 -1
- package/dist/components/ui/stepper-constants.d.ts +7 -2
- package/dist/components/ui/stepper-constants.js +2 -5
- package/dist/components/ui/stepper-constants.js.map +1 -1
- package/dist/components/ui/stepper.d.ts +2 -1
- package/dist/components/ui/stepper.js +2 -2
- package/dist/components/ui/stepper.js.map +1 -1
- package/dist/components/ui/tags-input.d.ts +3 -2
- package/dist/components/ui/tags-input.js +4 -2
- package/dist/components/ui/tags-input.js.map +1 -1
- package/dist/components/ui/time-picker-constants.d.ts +12 -9
- package/dist/components/ui/time-picker-constants.js +2 -10
- package/dist/components/ui/time-picker-constants.js.map +1 -1
- package/dist/components/ui/time-picker.d.ts +11 -6
- package/dist/components/ui/time-picker.js +5 -7
- package/dist/components/ui/time-picker.js.map +1 -1
- package/dist/components/ui/toggle-group.js +12 -1
- package/dist/components/ui/toggle-group.js.map +1 -1
- package/dist/components/ui/toggle.d.ts +1 -1
- package/dist/components/ui/tour-constants.d.ts +7 -2
- package/dist/components/ui/tour-constants.js +2 -8
- package/dist/components/ui/tour-constants.js.map +1 -1
- package/dist/components/ui/tour.d.ts +3 -2
- package/dist/components/ui/tour.js +5 -4
- package/dist/components/ui/tour.js.map +1 -1
- package/dist/data-grid/data-grid-cell-variants.js +4 -4
- package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
- package/dist/data-grid/data-grid-column-header.js +1 -1
- package/dist/data-grid/data-grid-column-header.js.map +1 -1
- package/dist/data-grid/data-grid-filter-menu.d.ts +5 -4
- package/dist/data-grid/data-grid-filter-menu.js +13 -8
- package/dist/data-grid/data-grid-filter-menu.js.map +1 -1
- package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +4 -4
- package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -1
- package/dist/data-grid/data-grid-locale.d.ts +58 -6
- package/dist/data-grid/data-grid-locale.js +41 -9
- package/dist/data-grid/data-grid-locale.js.map +1 -1
- package/dist/data-grid/data-grid-row-height-menu.d.ts +4 -3
- package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
- package/dist/data-grid/data-grid-sort-menu.d.ts +4 -4
- package/dist/data-grid/data-grid-sort-menu.js +1 -1
- package/dist/data-grid/data-grid-sort-menu.js.map +1 -1
- package/dist/data-grid/data-grid-view-menu.d.ts +4 -4
- package/dist/data-grid/data-grid-view-menu.js.map +1 -1
- package/dist/data-grid/data-grid.d.ts +4 -3
- package/dist/data-grid/data-grid.js +2 -3
- package/dist/data-grid/data-grid.js.map +1 -1
- package/dist/data-grid/index.d.ts +1 -0
- package/dist/data-grid/index.js.map +1 -1
- package/dist/data-grid/messages-en.d.ts +23 -4
- package/dist/data-grid/messages-en.js +20 -0
- package/dist/data-grid/messages-en.js.map +1 -1
- package/dist/data-grid/messages-vi.d.ts +3 -1
- package/dist/data-grid/messages-vi.js +14 -0
- package/dist/data-grid/messages-vi.js.map +1 -1
- package/dist/data-grid/messages.d.ts +29 -3
- package/dist/data-grid/messages.js.map +1 -1
- package/dist/data-grid/sortable.js +16 -19
- package/dist/data-grid/sortable.js.map +1 -1
- package/dist/data-grid/types.d.ts +1 -1
- package/dist/data-grid/use-data-grid-undo-redo.d.ts +5 -4
- package/dist/data-grid/use-data-grid-undo-redo.js +2 -2
- package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
- package/dist/data-grid/use-data-grid.d.ts +23 -8
- package/dist/data-grid/use-data-grid.js +8 -6
- package/dist/data-grid/use-data-grid.js.map +1 -1
- package/dist/data-grid/utils.d.ts +24 -4
- package/dist/data-grid/utils.js +15 -5
- package/dist/data-grid/utils.js.map +1 -1
- package/dist/i18n/locale-provider.d.ts +127 -0
- package/dist/i18n/locale-provider.js +47 -0
- package/dist/i18n/locale-provider.js.map +1 -0
- package/dist/i18n/locale.d.ts +27 -0
- package/dist/i18n/locale.js +10 -0
- package/dist/i18n/locale.js.map +1 -0
- package/dist/i18n/messages.d.ts +116 -0
- package/dist/i18n/messages.js +149 -0
- package/dist/i18n/messages.js.map +1 -0
- package/dist/i18n/namespaces/action-bar.d.ts +7 -0
- package/dist/i18n/namespaces/action-bar.js +15 -0
- package/dist/i18n/namespaces/action-bar.js.map +1 -0
- package/dist/i18n/namespaces/avatar-group.d.ts +14 -0
- package/dist/i18n/namespaces/avatar-group.js +13 -0
- package/dist/i18n/namespaces/avatar-group.js.map +1 -0
- package/dist/i18n/namespaces/badge-overflow.d.ts +12 -0
- package/dist/i18n/namespaces/badge-overflow.js +11 -0
- package/dist/i18n/namespaces/badge-overflow.js.map +1 -0
- package/dist/i18n/namespaces/banner.d.ts +9 -0
- package/dist/i18n/namespaces/banner.js +7 -0
- package/dist/i18n/namespaces/banner.js.map +1 -0
- package/dist/i18n/namespaces/breadcrumb.d.ts +11 -0
- package/dist/i18n/namespaces/breadcrumb.js +13 -0
- package/dist/i18n/namespaces/breadcrumb.js.map +1 -0
- package/dist/i18n/namespaces/calendar.d.ts +40 -0
- package/dist/i18n/namespaces/calendar.js +25 -0
- package/dist/i18n/namespaces/calendar.js.map +1 -0
- package/dist/i18n/namespaces/carousel.d.ts +15 -0
- package/dist/i18n/namespaces/carousel.js +17 -0
- package/dist/i18n/namespaces/carousel.js.map +1 -0
- package/dist/i18n/namespaces/combobox.d.ts +21 -0
- package/dist/i18n/namespaces/combobox.js +23 -0
- package/dist/i18n/namespaces/combobox.js.map +1 -0
- package/dist/i18n/namespaces/command-palette.d.ts +20 -0
- package/dist/i18n/namespaces/command-palette.js +19 -0
- package/dist/i18n/namespaces/command-palette.js.map +1 -0
- package/dist/i18n/namespaces/command.d.ts +11 -0
- package/dist/i18n/namespaces/command.js +13 -0
- package/dist/i18n/namespaces/command.js.map +1 -0
- package/dist/i18n/namespaces/confirm-dialog.d.ts +13 -0
- package/dist/i18n/namespaces/confirm-dialog.js +15 -0
- package/dist/i18n/namespaces/confirm-dialog.js.map +1 -0
- package/dist/i18n/namespaces/contrast.d.ts +7 -0
- package/dist/i18n/namespaces/contrast.js +13 -0
- package/dist/i18n/namespaces/contrast.js.map +1 -0
- package/dist/i18n/namespaces/copy-button.d.ts +11 -0
- package/dist/i18n/namespaces/copy-button.js +13 -0
- package/dist/i18n/namespaces/copy-button.js.map +1 -0
- package/dist/i18n/namespaces/cropper.d.ts +7 -0
- package/dist/i18n/namespaces/cropper.js +17 -0
- package/dist/i18n/namespaces/cropper.js.map +1 -0
- package/dist/i18n/namespaces/data-table.d.ts +7 -0
- package/dist/i18n/namespaces/data-table.js +23 -0
- package/dist/i18n/namespaces/data-table.js.map +1 -0
- package/dist/i18n/namespaces/date-picker.d.ts +42 -0
- package/dist/i18n/namespaces/date-picker.js +35 -0
- package/dist/i18n/namespaces/date-picker.js.map +1 -0
- package/dist/i18n/namespaces/dialog.d.ts +9 -0
- package/dist/i18n/namespaces/dialog.js +7 -0
- package/dist/i18n/namespaces/dialog.js.map +1 -0
- package/dist/i18n/namespaces/display-axis.d.ts +25 -0
- package/dist/i18n/namespaces/display-axis.js +21 -0
- package/dist/i18n/namespaces/display-axis.js.map +1 -0
- package/dist/i18n/namespaces/file-upload.d.ts +11 -0
- package/dist/i18n/namespaces/file-upload.js +19 -0
- package/dist/i18n/namespaces/file-upload.js.map +1 -0
- package/dist/i18n/namespaces/filter-chips.d.ts +11 -0
- package/dist/i18n/namespaces/filter-chips.js +13 -0
- package/dist/i18n/namespaces/filter-chips.js.map +1 -0
- package/dist/i18n/namespaces/image-upload-field.d.ts +12 -0
- package/dist/i18n/namespaces/image-upload-field.js +23 -0
- package/dist/i18n/namespaces/image-upload-field.js.map +1 -0
- package/dist/i18n/namespaces/inline-edit.d.ts +15 -0
- package/dist/i18n/namespaces/inline-edit.js +17 -0
- package/dist/i18n/namespaces/inline-edit.js.map +1 -0
- package/dist/i18n/namespaces/kanban.d.ts +11 -0
- package/dist/i18n/namespaces/kanban.js +39 -0
- package/dist/i18n/namespaces/kanban.js.map +1 -0
- package/dist/i18n/namespaces/key-value.d.ts +10 -0
- package/dist/i18n/namespaces/key-value.js +19 -0
- package/dist/i18n/namespaces/key-value.js.map +1 -0
- package/dist/i18n/namespaces/mention.d.ts +9 -0
- package/dist/i18n/namespaces/mention.js +7 -0
- package/dist/i18n/namespaces/mention.js.map +1 -0
- package/dist/i18n/namespaces/pagination.d.ts +19 -0
- package/dist/i18n/namespaces/pagination.js +21 -0
- package/dist/i18n/namespaces/pagination.js.map +1 -0
- package/dist/i18n/namespaces/phone-input.d.ts +10 -0
- package/dist/i18n/namespaces/phone-input.js +17 -0
- package/dist/i18n/namespaces/phone-input.js.map +1 -0
- package/dist/i18n/namespaces/rating.d.ts +12 -0
- package/dist/i18n/namespaces/rating.js +12 -0
- package/dist/i18n/namespaces/rating.js.map +1 -0
- package/dist/i18n/namespaces/route-tabs.d.ts +9 -0
- package/dist/i18n/namespaces/route-tabs.js +7 -0
- package/dist/i18n/namespaces/route-tabs.js.map +1 -0
- package/dist/i18n/namespaces/scroll-spy.d.ts +9 -0
- package/dist/i18n/namespaces/scroll-spy.js +7 -0
- package/dist/i18n/namespaces/scroll-spy.js.map +1 -0
- package/dist/i18n/namespaces/search-filter-bar.d.ts +15 -0
- package/dist/i18n/namespaces/search-filter-bar.js +32 -0
- package/dist/i18n/namespaces/search-filter-bar.js.map +1 -0
- package/dist/i18n/namespaces/sheet.d.ts +9 -0
- package/dist/i18n/namespaces/sheet.js +7 -0
- package/dist/i18n/namespaces/sheet.js.map +1 -0
- package/dist/i18n/namespaces/shell.d.ts +21 -0
- package/dist/i18n/namespaces/shell.js +75 -0
- package/dist/i18n/namespaces/shell.js.map +1 -0
- package/dist/i18n/namespaces/spinner.d.ts +9 -0
- package/dist/i18n/namespaces/spinner.js +7 -0
- package/dist/i18n/namespaces/spinner.js.map +1 -0
- package/dist/i18n/namespaces/stepper.d.ts +7 -0
- package/dist/i18n/namespaces/stepper.js +15 -0
- package/dist/i18n/namespaces/stepper.js.map +1 -0
- package/dist/i18n/namespaces/table-pagination.d.ts +7 -0
- package/dist/i18n/namespaces/table-pagination.js +29 -0
- package/dist/i18n/namespaces/table-pagination.js.map +1 -0
- package/dist/i18n/namespaces/tags-input.d.ts +12 -0
- package/dist/i18n/namespaces/tags-input.js +11 -0
- package/dist/i18n/namespaces/tags-input.js.map +1 -0
- package/dist/i18n/namespaces/time-picker.d.ts +15 -0
- package/dist/i18n/namespaces/time-picker.js +25 -0
- package/dist/i18n/namespaces/time-picker.js.map +1 -0
- package/dist/i18n/namespaces/toast.d.ts +16 -0
- package/dist/i18n/namespaces/toast.js +7 -0
- package/dist/i18n/namespaces/toast.js.map +1 -0
- package/dist/i18n/namespaces/tour.d.ts +7 -0
- package/dist/i18n/namespaces/tour.js +21 -0
- package/dist/i18n/namespaces/tour.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/toast-language.d.ts +8 -0
- package/dist/lib/toast-language.js +19 -0
- package/dist/lib/toast-language.js.map +1 -0
- package/dist/shell/app-header.js.map +1 -1
- package/dist/shell/app-shell.d.ts +20 -2
- package/dist/shell/app-shell.js +4 -1
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/contrast-constants.d.ts +9 -1
- package/dist/shell/contrast-constants.js +2 -4
- package/dist/shell/contrast-constants.js.map +1 -1
- package/dist/shell/contrast-toggle.d.ts +4 -1
- package/dist/shell/contrast-toggle.js +2 -2
- package/dist/shell/contrast-toggle.js.map +1 -1
- package/dist/shell/display-axis-constants.d.ts +2 -1
- package/dist/shell/display-axis-constants.js.map +1 -1
- package/dist/shell/display-axis-controls.d.ts +15 -6
- package/dist/shell/display-axis-controls.js +11 -9
- package/dist/shell/display-axis-controls.js.map +1 -1
- package/dist/shell/display-axis-provider.d.ts +1 -1
- package/dist/shell/mobile-menu.js.map +1 -1
- package/dist/shell/notifications-button.d.ts +1 -1
- package/dist/shell/notifications-button.js +3 -2
- package/dist/shell/notifications-button.js.map +1 -1
- package/dist/shell/shell-context.d.ts +4 -1
- package/dist/shell/shell-context.js +2 -4
- package/dist/shell/shell-context.js.map +1 -1
- package/dist/shell/sidebar.d.ts +1 -1
- package/dist/shell/sidebar.js.map +1 -1
- package/dist/shell/theme-toggle.d.ts +6 -3
- package/dist/shell/theme-toggle.js +2 -2
- package/dist/shell/theme-toggle.js.map +1 -1
- package/dist/shell/types.d.ts +14 -8
- package/dist/shell/types.js +2 -35
- package/dist/shell/types.js.map +1 -1
- package/dist/shell/user-menu.d.ts +6 -6
- package/dist/shell/user-menu.js.map +1 -1
- package/dist/shell/utils.d.ts +1 -1
- package/dist/shell/utils.js.map +1 -1
- package/dist/shell/workspace-switcher.js +1 -1
- package/dist/shell/workspace-switcher.js.map +1 -1
- package/dist/uploader/file-upload-constants.d.ts +13 -0
- package/dist/uploader/file-upload-constants.js +4 -16
- package/dist/uploader/file-upload-constants.js.map +1 -1
- package/dist/uploader/file-upload.d.ts +4 -1
- package/dist/uploader/file-upload.js +2 -2
- package/dist/uploader/file-upload.js.map +1 -1
- package/dist/uploader/image-upload-field.d.ts +5 -1
- package/dist/uploader/image-upload-field.js +2 -2
- package/dist/uploader/image-upload-field.js.map +1 -1
- package/dist/uploader/use-file-upload.d.ts +4 -0
- package/dist/uploader/use-file-upload.js +6 -2
- package/dist/uploader/use-file-upload.js.map +1 -1
- package/package.json +4 -3
|
@@ -3,6 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Slot } from "@radix-ui/react-slot";
|
|
4
4
|
import { Plus, X } from "lucide-react";
|
|
5
5
|
import * as React from "react";
|
|
6
|
+
import { useComitorLabels } from "../../i18n/locale-provider.js";
|
|
6
7
|
import { cn } from "../../lib/cn.js";
|
|
7
8
|
import { useComposedRefs } from "../../lib/compose-refs.js";
|
|
8
9
|
import { useAsRef } from "../../lib/use-as-ref.js";
|
|
@@ -11,7 +12,6 @@ import { useLazyRef } from "../../lib/use-lazy-ref.js";
|
|
|
11
12
|
import { VisuallyHiddenInput } from "../../lib/visually-hidden-input.js";
|
|
12
13
|
import { Button } from "./button.js";
|
|
13
14
|
import { Input } from "./input.js";
|
|
14
|
-
import { DEFAULT_KEY_VALUE_LABELS } from "./key-value-constants.js";
|
|
15
15
|
import { Textarea } from "./textarea.js";
|
|
16
16
|
const ROOT_NAME = "KeyValue";
|
|
17
17
|
const LIST_NAME = "KeyValueList";
|
|
@@ -65,6 +65,42 @@ function useKeyValueContext(consumerName) {
|
|
|
65
65
|
}
|
|
66
66
|
return context;
|
|
67
67
|
}
|
|
68
|
+
function commitItemField(store, context, itemId, field, text) {
|
|
69
|
+
const state = store.getState();
|
|
70
|
+
const newValue = state.value.map((item) => item.id === itemId ? { ...item, [field]: text } : item);
|
|
71
|
+
store.setState("value", newValue);
|
|
72
|
+
const updatedItemData = newValue.find((item) => item.id === itemId);
|
|
73
|
+
if (!updatedItemData) return;
|
|
74
|
+
const errors = {};
|
|
75
|
+
if (context.onKeyValidate) {
|
|
76
|
+
const keyError = context.onKeyValidate(updatedItemData.key, newValue);
|
|
77
|
+
if (keyError) errors.key = keyError;
|
|
78
|
+
}
|
|
79
|
+
if (!context.allowDuplicateKeys) {
|
|
80
|
+
const duplicateKey = newValue.find(
|
|
81
|
+
(item) => item.id !== updatedItemData.id && item.key === updatedItemData.key && updatedItemData.key !== ""
|
|
82
|
+
);
|
|
83
|
+
if (duplicateKey) {
|
|
84
|
+
errors.key = context.labels.duplicateKey;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (context.onValueValidate) {
|
|
88
|
+
const valueError = context.onValueValidate(updatedItemData.value, updatedItemData.key, newValue);
|
|
89
|
+
if (valueError) errors.value = valueError;
|
|
90
|
+
}
|
|
91
|
+
const newErrorsState = { ...state.errors };
|
|
92
|
+
if (Object.keys(errors).length > 0) {
|
|
93
|
+
newErrorsState[itemId] = errors;
|
|
94
|
+
} else {
|
|
95
|
+
delete newErrorsState[itemId];
|
|
96
|
+
}
|
|
97
|
+
store.setState("errors", newErrorsState);
|
|
98
|
+
}
|
|
99
|
+
function useTrimDraft(committed) {
|
|
100
|
+
const [draft, setDraft] = React.useState(null);
|
|
101
|
+
const shown = draft !== null && draft.trim() === committed ? draft : committed;
|
|
102
|
+
return [shown, setDraft];
|
|
103
|
+
}
|
|
68
104
|
function KeyValue(props) {
|
|
69
105
|
const {
|
|
70
106
|
value: valueProp,
|
|
@@ -96,7 +132,7 @@ function KeyValue(props) {
|
|
|
96
132
|
} = props;
|
|
97
133
|
const instanceId = React.useId();
|
|
98
134
|
const rootId = id ?? instanceId;
|
|
99
|
-
const labels =
|
|
135
|
+
const labels = useComitorLabels("keyValue", labelsProp);
|
|
100
136
|
const keyPlaceholder = keyPlaceholderProp ?? labels.keyPlaceholder;
|
|
101
137
|
const valuePlaceholder = valuePlaceholderProp ?? labels.valuePlaceholder;
|
|
102
138
|
const [formTrigger, setFormTrigger] = React.useState(null);
|
|
@@ -258,6 +294,7 @@ function KeyValueItem(props) {
|
|
|
258
294
|
function KeyValueKeyInput(props) {
|
|
259
295
|
const {
|
|
260
296
|
onChange: onChangeProp,
|
|
297
|
+
onBlur: onBlurProp,
|
|
261
298
|
onPaste: onPasteProp,
|
|
262
299
|
asChild,
|
|
263
300
|
disabled,
|
|
@@ -280,52 +317,29 @@ function KeyValueKeyInput(props) {
|
|
|
280
317
|
}, [focusedId, itemData.id]);
|
|
281
318
|
const propsRef = useAsRef({
|
|
282
319
|
onChange: onChangeProp,
|
|
320
|
+
onBlur: onBlurProp,
|
|
283
321
|
onPaste: onPasteProp
|
|
284
322
|
});
|
|
285
323
|
const isDisabled = disabled || context.disabled;
|
|
286
324
|
const isReadOnly = readOnly || context.readOnly;
|
|
287
325
|
const isRequired = required || context.required;
|
|
288
326
|
const isInvalid = errors[itemData.id]?.key !== void 0;
|
|
327
|
+
const [shownKey, setKeyDraft] = useTrimDraft(itemData.key);
|
|
289
328
|
const onChange = React.useCallback(
|
|
290
329
|
(event) => {
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const updated = { ...item, key: event.target.value };
|
|
295
|
-
if (context.trim) updated.key = updated.key.trim();
|
|
296
|
-
return updated;
|
|
297
|
-
});
|
|
298
|
-
store.setState("value", newValue);
|
|
299
|
-
const updatedItemData = newValue.find((item) => item.id === itemData.id);
|
|
300
|
-
if (updatedItemData) {
|
|
301
|
-
const errors2 = {};
|
|
302
|
-
if (context.onKeyValidate) {
|
|
303
|
-
const keyError = context.onKeyValidate(updatedItemData.key, newValue);
|
|
304
|
-
if (keyError) errors2.key = keyError;
|
|
305
|
-
}
|
|
306
|
-
if (!context.allowDuplicateKeys) {
|
|
307
|
-
const duplicateKey = newValue.find(
|
|
308
|
-
(item) => item.id !== updatedItemData.id && item.key === updatedItemData.key && updatedItemData.key !== ""
|
|
309
|
-
);
|
|
310
|
-
if (duplicateKey) {
|
|
311
|
-
errors2.key = context.labels.duplicateKey;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
if (context.onValueValidate) {
|
|
315
|
-
const valueError = context.onValueValidate(updatedItemData.value, updatedItemData.key, newValue);
|
|
316
|
-
if (valueError) errors2.value = valueError;
|
|
317
|
-
}
|
|
318
|
-
const newErrorsState = { ...state.errors };
|
|
319
|
-
if (Object.keys(errors2).length > 0) {
|
|
320
|
-
newErrorsState[itemData.id] = errors2;
|
|
321
|
-
} else {
|
|
322
|
-
delete newErrorsState[itemData.id];
|
|
323
|
-
}
|
|
324
|
-
store.setState("errors", newErrorsState);
|
|
325
|
-
}
|
|
330
|
+
const text = event.target.value;
|
|
331
|
+
if (context.trim) setKeyDraft(text);
|
|
332
|
+
commitItemField(store, context, itemData.id, "key", context.trim ? text.trim() : text);
|
|
326
333
|
propsRef.current.onChange?.(event);
|
|
327
334
|
},
|
|
328
|
-
[store, itemData.id, context, propsRef]
|
|
335
|
+
[store, itemData.id, context, propsRef, setKeyDraft]
|
|
336
|
+
);
|
|
337
|
+
const onBlur = React.useCallback(
|
|
338
|
+
(event) => {
|
|
339
|
+
propsRef.current.onBlur?.(event);
|
|
340
|
+
setKeyDraft(null);
|
|
341
|
+
},
|
|
342
|
+
[propsRef, setKeyDraft]
|
|
329
343
|
);
|
|
330
344
|
const onPaste = React.useCallback(
|
|
331
345
|
(event) => {
|
|
@@ -395,8 +409,9 @@ function KeyValueKeyInput(props) {
|
|
|
395
409
|
placeholder: context.keyPlaceholder,
|
|
396
410
|
...keyInputProps,
|
|
397
411
|
ref: composedRef,
|
|
398
|
-
value:
|
|
412
|
+
value: shownKey,
|
|
399
413
|
onChange,
|
|
414
|
+
onBlur,
|
|
400
415
|
onPaste
|
|
401
416
|
}
|
|
402
417
|
);
|
|
@@ -404,6 +419,7 @@ function KeyValueKeyInput(props) {
|
|
|
404
419
|
function KeyValueValueInput(props) {
|
|
405
420
|
const {
|
|
406
421
|
onChange: onChangeProp,
|
|
422
|
+
onBlur: onBlurProp,
|
|
407
423
|
asChild,
|
|
408
424
|
disabled,
|
|
409
425
|
readOnly,
|
|
@@ -418,53 +434,30 @@ function KeyValueValueInput(props) {
|
|
|
418
434
|
const store = useStoreContext(VALUE_INPUT_NAME);
|
|
419
435
|
const errors = useStore((state) => state.errors);
|
|
420
436
|
const propsRef = useAsRef({
|
|
421
|
-
onChange: onChangeProp
|
|
437
|
+
onChange: onChangeProp,
|
|
438
|
+
onBlur: onBlurProp
|
|
422
439
|
});
|
|
423
440
|
const isDisabled = disabled || context.disabled;
|
|
424
441
|
const isReadOnly = readOnly || context.readOnly;
|
|
425
442
|
const isRequired = required || context.required;
|
|
426
443
|
const isInvalid = errors[itemData.id]?.value !== void 0;
|
|
427
444
|
const maxHeight = maxRows ? `calc(${maxRows} * 1.5em + 1rem)` : void 0;
|
|
445
|
+
const [shownValue, setValueDraft] = useTrimDraft(itemData.value);
|
|
428
446
|
const onChange = React.useCallback(
|
|
429
447
|
(event) => {
|
|
430
448
|
propsRef.current.onChange?.(event);
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const updated = { ...item, value: event.target.value };
|
|
435
|
-
if (context.trim) updated.value = updated.value.trim();
|
|
436
|
-
return updated;
|
|
437
|
-
});
|
|
438
|
-
store.setState("value", newValue);
|
|
439
|
-
const updatedItemData = newValue.find((item) => item.id === itemData.id);
|
|
440
|
-
if (updatedItemData) {
|
|
441
|
-
const errors2 = {};
|
|
442
|
-
if (context.onKeyValidate) {
|
|
443
|
-
const keyError = context.onKeyValidate(updatedItemData.key, newValue);
|
|
444
|
-
if (keyError) errors2.key = keyError;
|
|
445
|
-
}
|
|
446
|
-
if (!context.allowDuplicateKeys) {
|
|
447
|
-
const duplicateKey = newValue.find(
|
|
448
|
-
(item) => item.id !== updatedItemData.id && item.key === updatedItemData.key && updatedItemData.key !== ""
|
|
449
|
-
);
|
|
450
|
-
if (duplicateKey) {
|
|
451
|
-
errors2.key = context.labels.duplicateKey;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
if (context.onValueValidate) {
|
|
455
|
-
const valueError = context.onValueValidate(updatedItemData.value, updatedItemData.key, newValue);
|
|
456
|
-
if (valueError) errors2.value = valueError;
|
|
457
|
-
}
|
|
458
|
-
const newErrorsState = { ...state.errors };
|
|
459
|
-
if (Object.keys(errors2).length > 0) {
|
|
460
|
-
newErrorsState[itemData.id] = errors2;
|
|
461
|
-
} else {
|
|
462
|
-
delete newErrorsState[itemData.id];
|
|
463
|
-
}
|
|
464
|
-
store.setState("errors", newErrorsState);
|
|
465
|
-
}
|
|
449
|
+
const text = event.target.value;
|
|
450
|
+
if (context.trim) setValueDraft(text);
|
|
451
|
+
commitItemField(store, context, itemData.id, "value", context.trim ? text.trim() : text);
|
|
466
452
|
},
|
|
467
|
-
[store, itemData.id, context, propsRef]
|
|
453
|
+
[store, itemData.id, context, propsRef, setValueDraft]
|
|
454
|
+
);
|
|
455
|
+
const onBlur = React.useCallback(
|
|
456
|
+
(event) => {
|
|
457
|
+
propsRef.current.onBlur?.(event);
|
|
458
|
+
setValueDraft(null);
|
|
459
|
+
},
|
|
460
|
+
[propsRef, setValueDraft]
|
|
468
461
|
);
|
|
469
462
|
const ValueInputPrimitive = asChild ? Slot : Textarea;
|
|
470
463
|
return /* @__PURE__ */ jsx(
|
|
@@ -500,8 +493,9 @@ function KeyValueValueInput(props) {
|
|
|
500
493
|
...style,
|
|
501
494
|
...maxHeight && { maxHeight }
|
|
502
495
|
},
|
|
503
|
-
value:
|
|
504
|
-
onChange
|
|
496
|
+
value: shownValue,
|
|
497
|
+
onChange,
|
|
498
|
+
onBlur
|
|
505
499
|
}
|
|
506
500
|
);
|
|
507
501
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/key-value.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/key-value) — 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 { Slot } from \"@radix-ui/react-slot\";\nimport { Plus, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { useLazyRef } from \"../../lib/use-lazy-ref.js\";\nimport { VisuallyHiddenInput } from \"../../lib/visually-hidden-input.js\";\nimport { Button } from \"./button.js\";\nimport { Input } from \"./input.js\";\nimport { DEFAULT_KEY_VALUE_LABELS, type KeyValueLabels } from \"./key-value-constants.js\";\nimport { Textarea } from \"./textarea.js\";\n\nconst ROOT_NAME = \"KeyValue\";\nconst LIST_NAME = \"KeyValueList\";\nconst ITEM_NAME = \"KeyValueItem\";\nconst KEY_INPUT_NAME = \"KeyValueKeyInput\";\nconst VALUE_INPUT_NAME = \"KeyValueValueInput\";\nconst REMOVE_NAME = \"KeyValueRemove\";\nconst ADD_NAME = \"KeyValueAdd\";\nconst ERROR_NAME = \"KeyValueError\";\n\ntype Orientation = \"vertical\" | \"horizontal\";\ntype Field = \"key\" | \"value\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype RootElement = React.ComponentRef<typeof KeyValue>;\ntype KeyInputElement = React.ComponentRef<typeof KeyValueKeyInput>;\ntype RemoveElement = React.ComponentRef<typeof KeyValueRemove>;\ntype AddElement = React.ComponentRef<typeof KeyValueAdd>;\n\nfunction getErrorId(rootId: string, itemId: string, field: Field) {\n return `${rootId}-${itemId}-${field}-error`;\n}\n\n/*\n * ⚠ Khác thượng nguồn: bản gốc gọi thẳng `crypto.randomUUID()`. Hàm đó chỉ tồn tại trong NGỮ CẢNH\n * AN TOÀN (HTTPS hoặc localhost) — mở trang quản trị qua `http://` bằng IP nội bộ là `TypeError`\n * ngay lúc render dòng đầu tiên. Id ở đây chỉ làm key của React và nối vào id của thông báo lỗi,\n * không cần ngẫu nhiên mật mã, nên có đường lui.\n */\nlet fallbackIdCounter = 0;\n\nfunction createItemId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n fallbackIdCounter += 1;\n return `kv-${Date.now().toString(36)}-${fallbackIdCounter}`;\n}\n\nfunction removeQuotes(string: string, shouldStrip: boolean): string {\n if (!shouldStrip) return string;\n\n const trimmed = string.trim();\n if ((trimmed.startsWith('\"') && trimmed.endsWith('\"')) || (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\"))) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => KeyValueState;\n setState: <K extends keyof KeyValueState>(key: K, value: KeyValueState[K]) => void;\n notify: () => void;\n}\n\nfunction useStore<T>(selector: (state: KeyValueState) => T, ogStore?: Store | null): T {\n const contextStore = React.useContext(StoreContext);\n\n const store = ogStore ?? contextStore;\n\n if (!store) {\n throw new Error(`\\`useKeyValueStore\\` phải được gọi bên trong \\`${ROOT_NAME}\\``);\n }\n\n const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\ninterface ItemData {\n id: string;\n key: string;\n value: string;\n}\n\ninterface KeyValueState {\n value: ItemData[];\n focusedId: string | null;\n errors: Record<string, { key?: string; value?: string }>;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface KeyValueContextValue {\n onPaste?: (event: ClipboardEvent, items: ItemData[]) => void;\n onAdd?: (value: ItemData) => void;\n onRemove?: (value: ItemData) => void;\n onKeyValidate?: (key: string, value: ItemData[]) => string | undefined;\n onValueValidate?: (value: string, key: string, items: ItemData[]) => string | undefined;\n rootId: string;\n maxItems?: number;\n minItems: number;\n keyPlaceholder: string;\n valuePlaceholder: string;\n labels: KeyValueLabels;\n allowDuplicateKeys: boolean;\n enablePaste: boolean;\n trim: boolean;\n stripQuotes: boolean;\n disabled: boolean;\n readOnly: boolean;\n required: boolean;\n}\n\nconst KeyValueContext = React.createContext<KeyValueContextValue | null>(null);\n\nfunction useKeyValueContext(consumerName: string) {\n const context = React.useContext(KeyValueContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface KeyValueProps extends Omit<DivProps, \"onPaste\" | \"defaultValue\"> {\n id?: string;\n defaultValue?: ItemData[];\n value?: ItemData[];\n onValueChange?: (value: ItemData[]) => void;\n maxItems?: number;\n minItems?: number;\n /** Placeholder của ô khoá — mặc định `labels.keyPlaceholder` (\"Khoá\"). */\n keyPlaceholder?: string;\n /** Placeholder của ô giá trị — mặc định `labels.valuePlaceholder` (\"Giá trị\"). */\n valuePlaceholder?: string;\n /**\n * Chuỗi hiển thị và chuỗi cho trình đọc màn hình — truyền MỘT PHẦN là đủ, phần còn lại lấy từ\n * `DEFAULT_KEY_VALUE_LABELS` (tiếng Việt). ⚠ Thêm so với thượng nguồn (bản gốc chôn cứng\n * \"Add\" / \"Duplicate key\" và để nút xoá không có tên).\n */\n labels?: Partial<KeyValueLabels>;\n name?: string;\n allowDuplicateKeys?: boolean;\n enablePaste?: boolean;\n trim?: boolean;\n stripQuotes?: boolean;\n disabled?: boolean;\n readOnly?: boolean;\n required?: boolean;\n onPaste?: (event: ClipboardEvent, items: ItemData[]) => void;\n onAdd?: (value: ItemData) => void;\n onRemove?: (value: ItemData) => void;\n onKeyValidate?: (key: string, value: ItemData[]) => string | undefined;\n onValueValidate?: (value: string, key: string, items: ItemData[]) => string | undefined;\n}\n\n/**\n * KeyValue — danh sách cặp khoá/giá trị sửa được: trường tuỳ biến của bản ghi CRM, header /\n * metadata của webhook ở trang quản trị. Dán nhiều dòng `KEY=value` (hoặc `KEY: value`, hoặc\n * cách bằng tab) vào ô khoá là tách thành nhiều cặp.\n */\nfunction KeyValue(props: KeyValueProps) {\n const {\n value: valueProp,\n defaultValue,\n onValueChange,\n onPaste,\n onAdd,\n onRemove,\n onKeyValidate,\n onValueValidate,\n maxItems,\n minItems = 0,\n labels: labelsProp,\n keyPlaceholder: keyPlaceholderProp,\n valuePlaceholder: valuePlaceholderProp,\n allowDuplicateKeys = false,\n asChild,\n enablePaste = true,\n trim = true,\n stripQuotes = true,\n disabled = false,\n readOnly = false,\n required = false,\n className,\n id,\n name,\n ref,\n ...rootProps\n } = props;\n\n const instanceId = React.useId();\n const rootId = id ?? instanceId;\n\n const labels = React.useMemo<KeyValueLabels>(() => ({ ...DEFAULT_KEY_VALUE_LABELS, ...labelsProp }), [labelsProp]);\n const keyPlaceholder = keyPlaceholderProp ?? labels.keyPlaceholder;\n const valuePlaceholder = valuePlaceholderProp ?? labels.valuePlaceholder;\n\n const [formTrigger, setFormTrigger] = React.useState<RootElement | null>(null);\n const composedRef = useComposedRefs(ref, (node) => setFormTrigger(node));\n const isFormControl = formTrigger ? !!formTrigger.closest(\"form\") : true;\n\n const listenersRef = useLazyRef(() => new Set<() => void>());\n const stateRef = useLazyRef<KeyValueState>(() => ({\n value: valueProp ?? defaultValue ?? [{ id: createItemId(), key: \"\", value: \"\" }],\n focusedId: null,\n errors: {}\n }));\n const propsRef = useAsRef({ onValueChange });\n\n const store = React.useMemo<Store>(() => {\n return {\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => stateRef.current,\n setState: (key, val) => {\n if (Object.is(stateRef.current[key], val)) return;\n\n if (key === \"value\" && Array.isArray(val)) {\n stateRef.current.value = val as ItemData[];\n propsRef.current.onValueChange?.(val as ItemData[]);\n } else {\n stateRef.current[key] = val;\n }\n\n store.notify();\n },\n notify: () => {\n for (const cb of listenersRef.current) {\n cb();\n }\n }\n };\n }, [listenersRef, stateRef, propsRef]);\n\n const value = useStore((state) => state.value, store);\n const errors = useStore((state) => state.errors, store);\n const isInvalid = Object.keys(errors).length > 0;\n\n useIsomorphicLayoutEffect(() => {\n if (valueProp !== undefined) {\n store.setState(\"value\", valueProp);\n }\n }, [valueProp]);\n\n const contextValue = React.useMemo<KeyValueContextValue>(\n () => ({\n onPaste,\n onAdd,\n onRemove,\n onKeyValidate,\n onValueValidate,\n rootId,\n maxItems,\n minItems,\n keyPlaceholder,\n valuePlaceholder,\n labels,\n allowDuplicateKeys,\n enablePaste,\n trim,\n stripQuotes,\n disabled,\n readOnly,\n required\n }),\n [\n onPaste,\n onAdd,\n onRemove,\n onKeyValidate,\n onValueValidate,\n rootId,\n disabled,\n readOnly,\n required,\n maxItems,\n minItems,\n keyPlaceholder,\n valuePlaceholder,\n labels,\n allowDuplicateKeys,\n enablePaste,\n trim,\n stripQuotes\n ]\n );\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StoreContext.Provider value={store}>\n <KeyValueContext.Provider value={contextValue}>\n <RootPrimitive\n id={id}\n data-slot=\"key-value\"\n data-disabled={disabled ? \"\" : undefined}\n data-invalid={isInvalid ? \"\" : undefined}\n data-readonly={readOnly ? \"\" : undefined}\n {...rootProps}\n ref={composedRef}\n className={cn(\"flex flex-col gap-2\", className)}\n />\n {isFormControl && (\n <VisuallyHiddenInput\n type=\"hidden\"\n control={formTrigger}\n name={name}\n value={value}\n disabled={disabled}\n readOnly={readOnly}\n required={required}\n />\n )}\n </KeyValueContext.Provider>\n </StoreContext.Provider>\n );\n}\n\ninterface KeyValueListProps extends DivProps {\n orientation?: Orientation;\n}\n\nfunction KeyValueList(props: KeyValueListProps) {\n const { orientation = \"vertical\", asChild, className, ...listProps } = props;\n\n const value = useStore((state) => state.value);\n\n const ListPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ListPrimitive\n role=\"list\"\n /*\n * ⚠ Khác thượng nguồn (a11y): bỏ `aria-orientation`. Thuộc tính đó KHÔNG được phép trên\n * `role=\"list\"` (ARIA 1.2 chỉ cho nó ở listbox, menu, tablist, toolbar, radiogroup, slider…)\n * — axe báo `aria-allowed-attr`. Hướng vẫn còn ở `data-orientation` cho CSS.\n */\n data-slot=\"key-value-list\"\n data-orientation={orientation}\n {...listProps}\n className={cn(\"flex\", orientation === \"vertical\" ? \"flex-col gap-2\" : \"flex-row gap-2\", className)}\n >\n {value.map((item) => {\n const children = React.Children.toArray(props.children);\n\n return (\n <KeyValueItemContext.Provider key={item.id} value={item}>\n {children}\n </KeyValueItemContext.Provider>\n );\n })}\n </ListPrimitive>\n );\n}\n\nconst KeyValueItemContext = React.createContext<ItemData | null>(null);\n\nfunction useKeyValueItemContext(consumerName: string) {\n const context = React.useContext(KeyValueItemContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${LIST_NAME}\\``);\n }\n return context;\n}\n\ninterface KeyValueItemProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\nfunction KeyValueItem(props: KeyValueItemProps) {\n const { asChild, className, ...itemProps } = props;\n const itemData = useKeyValueItemContext(ITEM_NAME);\n\n const focusedId = useStore((state) => state.focusedId);\n\n const ItemPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ItemPrimitive\n role=\"listitem\"\n data-slot=\"key-value-item\"\n data-highlighted={focusedId === itemData.id ? \"\" : undefined}\n {...itemProps}\n className={cn(\"flex items-start gap-2\", className)}\n />\n );\n}\n\ninterface KeyValueKeyInputProps extends React.ComponentProps<\"input\"> {\n asChild?: boolean;\n}\n\nfunction KeyValueKeyInput(props: KeyValueKeyInputProps) {\n const {\n onChange: onChangeProp,\n onPaste: onPasteProp,\n asChild,\n disabled,\n readOnly,\n required,\n ref,\n ...keyInputProps\n } = props;\n\n const context = useKeyValueContext(KEY_INPUT_NAME);\n const itemData = useKeyValueItemContext(KEY_INPUT_NAME);\n const store = useStoreContext(KEY_INPUT_NAME);\n const errors = useStore((state) => state.errors);\n const focusedId = useStore((state) => state.focusedId);\n\n const inputRef = React.useRef<KeyInputElement>(null);\n const composedRef = useComposedRefs(ref, inputRef);\n\n /*\n * ⚠ Khác thượng nguồn (a11y): `KeyValueAdd` ghi id dòng mới vào `focusedId` nhưng bản gốc không\n * bao giờ dùng nó để đưa focus — bấm \"Thêm\" xong focus vẫn đứng ở nút, người dùng bàn phím phải\n * tự Tab ngược lên dòng vừa thêm. Ở đây ô KHOÁ của dòng mới tự nhận focus. Chỉ chạy khi\n * `focusedId` ĐỔI sang dòng này (mà chỉ `KeyValueAdd` ghi nó), nên dòng có sẵn lúc mount và dòng\n * sinh ra do dán không bị cướp focus.\n */\n React.useEffect(() => {\n if (focusedId === itemData.id) {\n inputRef.current?.focus();\n }\n }, [focusedId, itemData.id]);\n\n const propsRef = useAsRef({\n onChange: onChangeProp,\n onPaste: onPasteProp\n });\n\n const isDisabled = disabled || context.disabled;\n const isReadOnly = readOnly || context.readOnly;\n const isRequired = required || context.required;\n const isInvalid = errors[itemData.id]?.key !== undefined;\n\n const onChange = React.useCallback(\n (event: React.ChangeEvent<KeyInputElement>) => {\n const state = store.getState();\n const newValue = state.value.map((item) => {\n if (item.id !== itemData.id) return item;\n const updated = { ...item, key: event.target.value };\n if (context.trim) updated.key = updated.key.trim();\n return updated;\n });\n\n store.setState(\"value\", newValue);\n\n const updatedItemData = newValue.find((item) => item.id === itemData.id);\n if (updatedItemData) {\n const errors: { key?: string; value?: string } = {};\n\n if (context.onKeyValidate) {\n const keyError = context.onKeyValidate(updatedItemData.key, newValue);\n if (keyError) errors.key = keyError;\n }\n\n if (!context.allowDuplicateKeys) {\n const duplicateKey = newValue.find(\n (item) => item.id !== updatedItemData.id && item.key === updatedItemData.key && updatedItemData.key !== \"\"\n );\n if (duplicateKey) {\n errors.key = context.labels.duplicateKey;\n }\n }\n\n if (context.onValueValidate) {\n const valueError = context.onValueValidate(updatedItemData.value, updatedItemData.key, newValue);\n if (valueError) errors.value = valueError;\n }\n\n const newErrorsState = { ...state.errors };\n if (Object.keys(errors).length > 0) {\n newErrorsState[itemData.id] = errors;\n } else {\n delete newErrorsState[itemData.id];\n }\n store.setState(\"errors\", newErrorsState);\n }\n\n propsRef.current.onChange?.(event);\n },\n [store, itemData.id, context, propsRef]\n );\n\n const onPaste = React.useCallback(\n (event: React.ClipboardEvent<KeyInputElement>) => {\n if (!context.enablePaste) return;\n\n propsRef.current.onPaste?.(event);\n if (event.defaultPrevented) return;\n\n const content = event.clipboardData.getData(\"text\");\n const lines = content.split(/\\r?\\n/).filter((line) => line.trim());\n\n if (lines.length > 1) {\n event.preventDefault();\n\n const parsed: ItemData[] = [];\n\n for (const line of lines) {\n let key = \"\";\n let value = \"\";\n\n if (line.includes(\"=\")) {\n const parts = line.split(\"=\");\n key = parts[0]?.trim() ?? \"\";\n value = removeQuotes(parts.slice(1).join(\"=\").trim(), context.stripQuotes);\n } else if (line.includes(\":\")) {\n const parts = line.split(\":\");\n key = parts[0]?.trim() ?? \"\";\n value = removeQuotes(parts.slice(1).join(\":\").trim(), context.stripQuotes);\n } else if (/\\s{2,}|\\t/.test(line)) {\n const parts = line.split(/\\s{2,}|\\t/);\n key = parts[0]?.trim() ?? \"\";\n value = removeQuotes(parts.slice(1).join(\" \").trim(), context.stripQuotes);\n }\n\n if (key) {\n parsed.push({ id: createItemId(), key, value });\n }\n }\n\n if (parsed.length > 0) {\n const state = store.getState();\n const currentIndex = state.value.findIndex((item) => item.id === itemData.id);\n\n let newValue: ItemData[];\n if (itemData.key === \"\" && itemData.value === \"\") {\n newValue = [...state.value.slice(0, currentIndex), ...parsed, ...state.value.slice(currentIndex + 1)];\n } else {\n newValue = [...state.value.slice(0, currentIndex + 1), ...parsed, ...state.value.slice(currentIndex + 1)];\n }\n\n if (context.maxItems !== undefined) {\n newValue = newValue.slice(0, context.maxItems);\n }\n\n store.setState(\"value\", newValue);\n\n if (context.onPaste) {\n context.onPaste(event.nativeEvent as unknown as ClipboardEvent, parsed);\n }\n }\n }\n },\n [context, store, itemData, propsRef]\n );\n\n const KeyInputPrimitive = asChild ? Slot : Input;\n\n return (\n <KeyInputPrimitive\n aria-invalid={isInvalid}\n aria-describedby={isInvalid ? getErrorId(context.rootId, itemData.id, \"key\") : undefined}\n data-slot=\"key-value-key-input\"\n autoCapitalize=\"off\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n spellCheck=\"false\"\n disabled={isDisabled}\n readOnly={isReadOnly}\n required={isRequired}\n placeholder={context.keyPlaceholder}\n {...keyInputProps}\n ref={composedRef}\n value={itemData.key}\n onChange={onChange}\n onPaste={onPaste}\n />\n );\n}\n\ninterface KeyValueValueInputProps extends Omit<React.ComponentProps<\"textarea\">, \"rows\"> {\n maxRows?: number;\n asChild?: boolean;\n}\n\nfunction KeyValueValueInput(props: KeyValueValueInputProps) {\n const {\n onChange: onChangeProp,\n asChild,\n disabled,\n readOnly,\n required,\n className,\n maxRows,\n style,\n ...valueInputProps\n } = props;\n\n const context = useKeyValueContext(VALUE_INPUT_NAME);\n const itemData = useKeyValueItemContext(VALUE_INPUT_NAME);\n const store = useStoreContext(VALUE_INPUT_NAME);\n const errors = useStore((state) => state.errors);\n\n const propsRef = useAsRef({\n onChange: onChangeProp\n });\n\n const isDisabled = disabled || context.disabled;\n const isReadOnly = readOnly || context.readOnly;\n const isRequired = required || context.required;\n const isInvalid = errors[itemData.id]?.value !== undefined;\n const maxHeight = maxRows ? `calc(${maxRows} * 1.5em + 1rem)` : undefined;\n\n const onChange = React.useCallback(\n (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n propsRef.current.onChange?.(event);\n\n const state = store.getState();\n const newValue = state.value.map((item) => {\n if (item.id !== itemData.id) return item;\n const updated = { ...item, value: event.target.value };\n if (context.trim) updated.value = updated.value.trim();\n return updated;\n });\n\n store.setState(\"value\", newValue);\n\n const updatedItemData = newValue.find((item) => item.id === itemData.id);\n if (updatedItemData) {\n const errors: { key?: string; value?: string } = {};\n\n if (context.onKeyValidate) {\n const keyError = context.onKeyValidate(updatedItemData.key, newValue);\n if (keyError) errors.key = keyError;\n }\n\n if (!context.allowDuplicateKeys) {\n const duplicateKey = newValue.find(\n (item) => item.id !== updatedItemData.id && item.key === updatedItemData.key && updatedItemData.key !== \"\"\n );\n if (duplicateKey) {\n errors.key = context.labels.duplicateKey;\n }\n }\n\n if (context.onValueValidate) {\n const valueError = context.onValueValidate(updatedItemData.value, updatedItemData.key, newValue);\n if (valueError) errors.value = valueError;\n }\n\n const newErrorsState = { ...state.errors };\n if (Object.keys(errors).length > 0) {\n newErrorsState[itemData.id] = errors;\n } else {\n delete newErrorsState[itemData.id];\n }\n store.setState(\"errors\", newErrorsState);\n }\n },\n [store, itemData.id, context, propsRef]\n );\n\n const ValueInputPrimitive = asChild ? Slot : Textarea;\n\n return (\n <ValueInputPrimitive\n aria-invalid={isInvalid}\n aria-describedby={isInvalid ? getErrorId(context.rootId, itemData.id, \"value\") : undefined}\n data-slot=\"key-value-value-input\"\n autoCapitalize=\"off\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n spellCheck=\"false\"\n disabled={isDisabled}\n readOnly={isReadOnly}\n required={isRequired}\n placeholder={context.valuePlaceholder}\n {...valueInputProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: ô giá trị phải trông như ô khoá (`Input`) đứng ngay cạnh khi còn MỘT\n * dòng. `Textarea` của gói theo comitor-ds: `text-base md:text-sm` + `py-2` (cao 38px ở\n * desktop, 42px và chữ 16px ở mobile), nền `bg-transparent dark:bg-input-fill`, có\n * `shadow-xs`; còn `Input` là `h-9 text-sm bg-background`, không bóng. Ghi đè ở ĐÂY chứ\n * không sửa hai primitive: `text-sm` ở mọi cỡ màn, `min-h-9` + `py-1.5` (dòng chữ 20px cách\n * mép trên 7px — đúng chỗ `Input` căn giữa dòng chữ trong 36px), `bg-background` ở cả hai\n * theme, bỏ bóng. Nhiều dòng thì `field-sizing-content` vẫn cho ô cao dần như cũ.\n */\n \"field-sizing-content min-h-9 resize-none py-1.5 text-sm md:text-sm bg-background dark:bg-background shadow-none\",\n maxRows && \"overflow-y-auto\",\n className\n )}\n style={{\n ...style,\n ...(maxHeight && { maxHeight })\n }}\n value={itemData.value}\n onChange={onChange}\n />\n );\n}\n\ninterface KeyValueRemoveProps extends React.ComponentProps<typeof Button> {}\n\nfunction KeyValueRemove(props: KeyValueRemoveProps) {\n const { onClick: onClickProp, children, className, ...removeProps } = props;\n\n const context = useKeyValueContext(REMOVE_NAME);\n const itemData = useKeyValueItemContext(REMOVE_NAME);\n const store = useStoreContext(REMOVE_NAME);\n\n const propsRef = useAsRef({\n onClick: onClickProp\n });\n const value = useStore((state) => state.value);\n const isDisabled = context.disabled || value.length <= context.minItems;\n const itemIndex = value.findIndex((item) => item.id === itemData.id);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<RemoveElement>) => {\n propsRef.current.onClick?.(event);\n\n const state = store.getState();\n if (state.value.length <= context.minItems) return;\n\n const itemToRemove = state.value.find((item) => item.id === itemData.id);\n if (!itemToRemove) return;\n\n /*\n * ⚠ Khác thượng nguồn (a11y): nút đang giữ focus biến mất cùng dòng của nó, và focus rơi về\n * `<body>` — người dùng bàn phím phải Tab lại từ đầu trang (WCAG 2.4.3). Nếu nút có focus lúc\n * bấm thì sau khi xoá, đưa focus sang nút xoá của dòng kế (hoặc dòng trước nếu vừa xoá dòng\n * cuối), không còn nút nào dùng được thì sang nút thêm.\n */\n const button = event.currentTarget;\n const hadFocus = button.ownerDocument.activeElement === button;\n const root = button.closest<HTMLElement>('[data-slot=\"key-value\"]');\n const removedIndex = state.value.indexOf(itemToRemove);\n\n const newValue = state.value.filter((item) => item.id !== itemData.id);\n const newErrors = { ...state.errors };\n delete newErrors[itemData.id];\n\n store.setState(\"value\", newValue);\n store.setState(\"errors\", newErrors);\n\n context.onRemove?.(itemToRemove);\n\n if (hadFocus && root) {\n requestAnimationFrame(() => {\n const removeButtons = root.querySelectorAll<HTMLElement>('[data-slot=\"key-value-remove\"]:not(:disabled)');\n const nextTarget =\n removeButtons[Math.min(removedIndex, removeButtons.length - 1)] ??\n root.querySelector<HTMLElement>('[data-slot=\"key-value-add\"]:not(:disabled)');\n nextTarget?.focus();\n });\n }\n },\n [store, context, itemData.id, propsRef]\n );\n\n return (\n <Button\n type=\"button\"\n data-slot=\"key-value-remove\"\n variant=\"outline\"\n size=\"icon\"\n /*\n * ⚠ Khác thượng nguồn (a11y): nút chỉ có icon ✕ và bản gốc không đặt tên nào — trình đọc màn\n * hình đọc \"nút\" trơn, lặp lại ở mọi dòng. Tên mặc định nói RÕ dòng nào: \"Xoá API_KEY\", hoặc\n * \"Xoá dòng 3\" khi khoá còn trống. Đổi qua `labels.remove` của `KeyValue`, hoặc `aria-label`.\n */\n aria-label={context.labels.remove(itemData.key, itemIndex + 1)}\n disabled={isDisabled}\n {...removeProps}\n // ⚠ Khác thượng nguồn: `shrink-0` — nút là mục flex cạnh hai ô `w-full`; thiếu nó thì flex\n // bóp nút còn ~18×36px. `size=\"icon\"` của `Button` đã là ô vuông 36×36 (`h-9 w-9`).\n className={cn(\"shrink-0\", className)}\n onClick={onClick}\n >\n {/* `size-4`: `Button` của gói không tự đặt cỡ cho svg con như shadcn — thiếu là icon 24px. */}\n {children ?? <X aria-hidden=\"true\" className=\"size-4\" />}\n </Button>\n );\n}\n\ntype KeyValueAddProps = React.ComponentProps<typeof Button>;\n\nfunction KeyValueAdd(props: KeyValueAddProps) {\n const { onClick: onClickProp, children, ...addProps } = props;\n\n const context = useKeyValueContext(ADD_NAME);\n const store = useStoreContext(ADD_NAME);\n\n const propsRef = useAsRef({\n onClick: onClickProp\n });\n const value = useStore((state) => state.value);\n const isDisabled = context.disabled || (context.maxItems !== undefined && value.length >= context.maxItems);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<AddElement>) => {\n propsRef.current.onClick?.(event);\n\n const state = store.getState();\n if (context.maxItems !== undefined && state.value.length >= context.maxItems) {\n return;\n }\n\n const newItem: ItemData = {\n id: createItemId(),\n key: \"\",\n value: \"\"\n };\n\n const newValue = [...state.value, newItem];\n store.setState(\"value\", newValue);\n store.setState(\"focusedId\", newItem.id);\n\n context.onAdd?.(newItem);\n },\n [store, context, propsRef]\n );\n\n return (\n <Button\n type=\"button\"\n data-slot=\"key-value-add\"\n variant=\"outline\"\n disabled={isDisabled}\n {...addProps}\n onClick={onClick}\n >\n {children ?? (\n <>\n <Plus aria-hidden=\"true\" className=\"size-4\" />\n {context.labels.add}\n </>\n )}\n </Button>\n );\n}\n\ninterface KeyValueErrorProps extends DivProps {\n field: Field;\n}\n\nfunction KeyValueError(props: KeyValueErrorProps) {\n const { field, asChild, className, ...errorProps } = props;\n\n const context = useKeyValueContext(ERROR_NAME);\n const itemData = useKeyValueItemContext(ERROR_NAME);\n\n const errors = useStore((state) => state.errors);\n const error = errors[itemData.id]?.[field];\n\n if (!error) return null;\n\n const ErrorPrimitive = asChild ? Slot : \"span\";\n\n return (\n <ErrorPrimitive\n id={getErrorId(context.rootId, itemData.id, field)}\n role=\"alert\"\n {...errorProps}\n className={cn(\n // ⚠ Khác thượng nguồn: `text-destructive` (bậc TÔ NỀN) → `text-destructive-ink`, bậc dành\n // cho chữ trên nền trang — cùng token với chữ lỗi của `Input`.\n \"text-sm font-medium text-destructive-ink\",\n className\n )}\n >\n {error}\n </ErrorPrimitive>\n );\n}\n\nexport {\n type ItemData as KeyValueItemData,\n KeyValue,\n KeyValueAdd,\n type KeyValueAddProps,\n KeyValueError,\n type KeyValueErrorProps,\n KeyValueItem,\n type KeyValueItemProps,\n KeyValueKeyInput,\n type KeyValueKeyInputProps,\n KeyValueList,\n type KeyValueListProps,\n type KeyValueProps,\n KeyValueRemove,\n type KeyValueRemoveProps,\n KeyValueValueInput,\n type KeyValueValueInputProps,\n useStore as useKeyValueStore\n};\n"],"mappings":"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","names":["errors"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/key-value.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/key-value) — 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 { Slot } from \"@radix-ui/react-slot\";\nimport { Plus, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport { useComitorLabels } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { useLazyRef } from \"../../lib/use-lazy-ref.js\";\nimport { VisuallyHiddenInput } from \"../../lib/visually-hidden-input.js\";\nimport { Button } from \"./button.js\";\nimport { Input } from \"./input.js\";\nimport type { KeyValueLabels } from \"./key-value-constants.js\";\nimport { Textarea } from \"./textarea.js\";\n\nconst ROOT_NAME = \"KeyValue\";\nconst LIST_NAME = \"KeyValueList\";\nconst ITEM_NAME = \"KeyValueItem\";\nconst KEY_INPUT_NAME = \"KeyValueKeyInput\";\nconst VALUE_INPUT_NAME = \"KeyValueValueInput\";\nconst REMOVE_NAME = \"KeyValueRemove\";\nconst ADD_NAME = \"KeyValueAdd\";\nconst ERROR_NAME = \"KeyValueError\";\n\ntype Orientation = \"vertical\" | \"horizontal\";\ntype Field = \"key\" | \"value\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype RootElement = React.ComponentRef<typeof KeyValue>;\ntype KeyInputElement = React.ComponentRef<typeof KeyValueKeyInput>;\ntype RemoveElement = React.ComponentRef<typeof KeyValueRemove>;\ntype AddElement = React.ComponentRef<typeof KeyValueAdd>;\n\nfunction getErrorId(rootId: string, itemId: string, field: Field) {\n return `${rootId}-${itemId}-${field}-error`;\n}\n\n/*\n * ⚠ Khác thượng nguồn: bản gốc gọi thẳng `crypto.randomUUID()`. Hàm đó chỉ tồn tại trong NGỮ CẢNH\n * AN TOÀN (HTTPS hoặc localhost) — mở trang quản trị qua `http://` bằng IP nội bộ là `TypeError`\n * ngay lúc render dòng đầu tiên. Id ở đây chỉ làm key của React và nối vào id của thông báo lỗi,\n * không cần ngẫu nhiên mật mã, nên có đường lui.\n */\nlet fallbackIdCounter = 0;\n\nfunction createItemId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n fallbackIdCounter += 1;\n return `kv-${Date.now().toString(36)}-${fallbackIdCounter}`;\n}\n\nfunction removeQuotes(string: string, shouldStrip: boolean): string {\n if (!shouldStrip) return string;\n\n const trimmed = string.trim();\n if ((trimmed.startsWith('\"') && trimmed.endsWith('\"')) || (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\"))) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => KeyValueState;\n setState: <K extends keyof KeyValueState>(key: K, value: KeyValueState[K]) => void;\n notify: () => void;\n}\n\nfunction useStore<T>(selector: (state: KeyValueState) => T, ogStore?: Store | null): T {\n const contextStore = React.useContext(StoreContext);\n\n const store = ogStore ?? contextStore;\n\n if (!store) {\n throw new Error(`\\`useKeyValueStore\\` phải được gọi bên trong \\`${ROOT_NAME}\\``);\n }\n\n const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\ninterface ItemData {\n id: string;\n key: string;\n value: string;\n}\n\ninterface KeyValueState {\n value: ItemData[];\n focusedId: string | null;\n errors: Record<string, { key?: string; value?: string }>;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface KeyValueContextValue {\n onPaste?: (event: ClipboardEvent, items: ItemData[]) => void;\n onAdd?: (value: ItemData) => void;\n onRemove?: (value: ItemData) => void;\n onKeyValidate?: (key: string, value: ItemData[]) => string | undefined;\n onValueValidate?: (value: string, key: string, items: ItemData[]) => string | undefined;\n rootId: string;\n maxItems?: number;\n minItems: number;\n keyPlaceholder: string;\n valuePlaceholder: string;\n labels: KeyValueLabels;\n allowDuplicateKeys: boolean;\n enablePaste: boolean;\n trim: boolean;\n stripQuotes: boolean;\n disabled: boolean;\n readOnly: boolean;\n required: boolean;\n}\n\nconst KeyValueContext = React.createContext<KeyValueContextValue | null>(null);\n\nfunction useKeyValueContext(consumerName: string) {\n const context = React.useContext(KeyValueContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\n/**\n * Ghi MỘT ô (khoá hoặc giá trị) của một dòng vào store rồi kiểm lỗi lại dòng đó. Dùng chung cho\n * hai ô nhập — trước `3.0.1` khối này chép nguyên hai lần, mỗi ô một bản.\n */\nfunction commitItemField(store: Store, context: KeyValueContextValue, itemId: string, field: Field, text: string) {\n const state = store.getState();\n const newValue = state.value.map((item) => (item.id === itemId ? { ...item, [field]: text } : item));\n\n store.setState(\"value\", newValue);\n\n const updatedItemData = newValue.find((item) => item.id === itemId);\n if (!updatedItemData) return;\n\n const errors: { key?: string; value?: string } = {};\n\n if (context.onKeyValidate) {\n const keyError = context.onKeyValidate(updatedItemData.key, newValue);\n if (keyError) errors.key = keyError;\n }\n\n if (!context.allowDuplicateKeys) {\n const duplicateKey = newValue.find(\n (item) => item.id !== updatedItemData.id && item.key === updatedItemData.key && updatedItemData.key !== \"\"\n );\n if (duplicateKey) {\n errors.key = context.labels.duplicateKey;\n }\n }\n\n if (context.onValueValidate) {\n const valueError = context.onValueValidate(updatedItemData.value, updatedItemData.key, newValue);\n if (valueError) errors.value = valueError;\n }\n\n const newErrorsState = { ...state.errors };\n if (Object.keys(errors).length > 0) {\n newErrorsState[itemId] = errors;\n } else {\n delete newErrorsState[itemId];\n }\n store.setState(\"errors\", newErrorsState);\n}\n\n/*\n * ⚠ Khác thượng nguồn (3.0.1): với `trim`, Ô NHẬP hiện đúng chữ đang gõ, còn STORE nhận bản đã cắt.\n * Bản gốc cắt ngay trong `onChange` rồi đưa chính chuỗi đã cắt về lại ô, nên dấu cách vừa gõ ở cuối\n * biến mất trước khi chữ kế tiếp tới — với `trim` mặc định `true`, không gõ được \"Contract term\" hay\n * \"Bearer token\" theo cách bình thường (ra \"Contractterm\").\n * Mọi thứ app nhận (`onValueChange`, ô ẩn trong form, kiểm lỗi, trùng khoá) vẫn LUÔN là bản đã cắt,\n * đúng như bản gốc — kể cả khi Enter gửi form ngay sau một dấu cách. Chỉ phần hiển thị giữ khoảng\n * trắng, cho tới khi rời ô.\n */\nfunction useTrimDraft(committed: string) {\n const [draft, setDraft] = React.useState<string | null>(null);\n // Bản nháp chỉ được hiện khi nó còn khớp store: store đổi từ nơi khác (app đặt `value`, dán nhiều\n // dòng) thì hiện giá trị mới.\n const shown = draft !== null && draft.trim() === committed ? draft : committed;\n return [shown, setDraft] as const;\n}\n\ninterface KeyValueProps extends Omit<DivProps, \"onPaste\" | \"defaultValue\"> {\n id?: string;\n defaultValue?: ItemData[];\n value?: ItemData[];\n onValueChange?: (value: ItemData[]) => void;\n maxItems?: number;\n minItems?: number;\n /** Placeholder của ô khoá — thắng `labels.keyPlaceholder` (mặc định \"Key\"). */\n keyPlaceholder?: string;\n /** Placeholder của ô giá trị — thắng `labels.valuePlaceholder` (mặc định \"Value\"). */\n valuePlaceholder?: string;\n /**\n * Chuỗi hiển thị và chuỗi cho trình đọc màn hình — truyền MỘT PHẦN là đủ, phần còn lại theo\n * namespace `keyValue` của `ComitorLocaleProvider` (mặc định tiếng Anh). ⚠ Thêm so với thượng\n * nguồn (bản gốc chôn cứng \"Add\" / \"Duplicate key\" và để nút xoá không có tên).\n */\n labels?: Partial<KeyValueLabels>;\n name?: string;\n allowDuplicateKeys?: boolean;\n enablePaste?: boolean;\n trim?: boolean;\n stripQuotes?: boolean;\n disabled?: boolean;\n readOnly?: boolean;\n required?: boolean;\n onPaste?: (event: ClipboardEvent, items: ItemData[]) => void;\n onAdd?: (value: ItemData) => void;\n onRemove?: (value: ItemData) => void;\n onKeyValidate?: (key: string, value: ItemData[]) => string | undefined;\n onValueValidate?: (value: string, key: string, items: ItemData[]) => string | undefined;\n}\n\n/**\n * KeyValue — danh sách cặp khoá/giá trị sửa được: trường tuỳ biến của bản ghi CRM, header /\n * metadata của webhook ở trang quản trị. Dán nhiều dòng `KEY=value` (hoặc `KEY: value`, hoặc\n * cách bằng tab) vào ô khoá là tách thành nhiều cặp.\n */\nfunction KeyValue(props: KeyValueProps) {\n const {\n value: valueProp,\n defaultValue,\n onValueChange,\n onPaste,\n onAdd,\n onRemove,\n onKeyValidate,\n onValueValidate,\n maxItems,\n minItems = 0,\n labels: labelsProp,\n keyPlaceholder: keyPlaceholderProp,\n valuePlaceholder: valuePlaceholderProp,\n allowDuplicateKeys = false,\n asChild,\n enablePaste = true,\n trim = true,\n stripQuotes = true,\n disabled = false,\n readOnly = false,\n required = false,\n className,\n id,\n name,\n ref,\n ...rootProps\n } = props;\n\n const instanceId = React.useId();\n const rootId = id ?? instanceId;\n\n const labels = useComitorLabels(\"keyValue\", labelsProp);\n const keyPlaceholder = keyPlaceholderProp ?? labels.keyPlaceholder;\n const valuePlaceholder = valuePlaceholderProp ?? labels.valuePlaceholder;\n\n const [formTrigger, setFormTrigger] = React.useState<RootElement | null>(null);\n const composedRef = useComposedRefs(ref, (node) => setFormTrigger(node));\n const isFormControl = formTrigger ? !!formTrigger.closest(\"form\") : true;\n\n const listenersRef = useLazyRef(() => new Set<() => void>());\n const stateRef = useLazyRef<KeyValueState>(() => ({\n value: valueProp ?? defaultValue ?? [{ id: createItemId(), key: \"\", value: \"\" }],\n focusedId: null,\n errors: {}\n }));\n const propsRef = useAsRef({ onValueChange });\n\n const store = React.useMemo<Store>(() => {\n return {\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => stateRef.current,\n setState: (key, val) => {\n if (Object.is(stateRef.current[key], val)) return;\n\n if (key === \"value\" && Array.isArray(val)) {\n stateRef.current.value = val as ItemData[];\n propsRef.current.onValueChange?.(val as ItemData[]);\n } else {\n stateRef.current[key] = val;\n }\n\n store.notify();\n },\n notify: () => {\n for (const cb of listenersRef.current) {\n cb();\n }\n }\n };\n }, [listenersRef, stateRef, propsRef]);\n\n const value = useStore((state) => state.value, store);\n const errors = useStore((state) => state.errors, store);\n const isInvalid = Object.keys(errors).length > 0;\n\n useIsomorphicLayoutEffect(() => {\n if (valueProp !== undefined) {\n store.setState(\"value\", valueProp);\n }\n }, [valueProp]);\n\n const contextValue = React.useMemo<KeyValueContextValue>(\n () => ({\n onPaste,\n onAdd,\n onRemove,\n onKeyValidate,\n onValueValidate,\n rootId,\n maxItems,\n minItems,\n keyPlaceholder,\n valuePlaceholder,\n labels,\n allowDuplicateKeys,\n enablePaste,\n trim,\n stripQuotes,\n disabled,\n readOnly,\n required\n }),\n [\n onPaste,\n onAdd,\n onRemove,\n onKeyValidate,\n onValueValidate,\n rootId,\n disabled,\n readOnly,\n required,\n maxItems,\n minItems,\n keyPlaceholder,\n valuePlaceholder,\n labels,\n allowDuplicateKeys,\n enablePaste,\n trim,\n stripQuotes\n ]\n );\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StoreContext.Provider value={store}>\n <KeyValueContext.Provider value={contextValue}>\n <RootPrimitive\n id={id}\n data-slot=\"key-value\"\n data-disabled={disabled ? \"\" : undefined}\n data-invalid={isInvalid ? \"\" : undefined}\n data-readonly={readOnly ? \"\" : undefined}\n {...rootProps}\n ref={composedRef}\n className={cn(\"flex flex-col gap-2\", className)}\n />\n {isFormControl && (\n <VisuallyHiddenInput\n type=\"hidden\"\n control={formTrigger}\n name={name}\n value={value}\n disabled={disabled}\n readOnly={readOnly}\n required={required}\n />\n )}\n </KeyValueContext.Provider>\n </StoreContext.Provider>\n );\n}\n\ninterface KeyValueListProps extends DivProps {\n orientation?: Orientation;\n}\n\nfunction KeyValueList(props: KeyValueListProps) {\n const { orientation = \"vertical\", asChild, className, ...listProps } = props;\n\n const value = useStore((state) => state.value);\n\n const ListPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ListPrimitive\n role=\"list\"\n /*\n * ⚠ Khác thượng nguồn (a11y): bỏ `aria-orientation`. Thuộc tính đó KHÔNG được phép trên\n * `role=\"list\"` (ARIA 1.2 chỉ cho nó ở listbox, menu, tablist, toolbar, radiogroup, slider…)\n * — axe báo `aria-allowed-attr`. Hướng vẫn còn ở `data-orientation` cho CSS.\n */\n data-slot=\"key-value-list\"\n data-orientation={orientation}\n {...listProps}\n className={cn(\"flex\", orientation === \"vertical\" ? \"flex-col gap-2\" : \"flex-row gap-2\", className)}\n >\n {value.map((item) => {\n const children = React.Children.toArray(props.children);\n\n return (\n <KeyValueItemContext.Provider key={item.id} value={item}>\n {children}\n </KeyValueItemContext.Provider>\n );\n })}\n </ListPrimitive>\n );\n}\n\nconst KeyValueItemContext = React.createContext<ItemData | null>(null);\n\nfunction useKeyValueItemContext(consumerName: string) {\n const context = React.useContext(KeyValueItemContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${LIST_NAME}\\``);\n }\n return context;\n}\n\ninterface KeyValueItemProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\nfunction KeyValueItem(props: KeyValueItemProps) {\n const { asChild, className, ...itemProps } = props;\n const itemData = useKeyValueItemContext(ITEM_NAME);\n\n const focusedId = useStore((state) => state.focusedId);\n\n const ItemPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ItemPrimitive\n role=\"listitem\"\n data-slot=\"key-value-item\"\n data-highlighted={focusedId === itemData.id ? \"\" : undefined}\n {...itemProps}\n className={cn(\"flex items-start gap-2\", className)}\n />\n );\n}\n\ninterface KeyValueKeyInputProps extends React.ComponentProps<\"input\"> {\n asChild?: boolean;\n}\n\nfunction KeyValueKeyInput(props: KeyValueKeyInputProps) {\n const {\n onChange: onChangeProp,\n onBlur: onBlurProp,\n onPaste: onPasteProp,\n asChild,\n disabled,\n readOnly,\n required,\n ref,\n ...keyInputProps\n } = props;\n\n const context = useKeyValueContext(KEY_INPUT_NAME);\n const itemData = useKeyValueItemContext(KEY_INPUT_NAME);\n const store = useStoreContext(KEY_INPUT_NAME);\n const errors = useStore((state) => state.errors);\n const focusedId = useStore((state) => state.focusedId);\n\n const inputRef = React.useRef<KeyInputElement>(null);\n const composedRef = useComposedRefs(ref, inputRef);\n\n /*\n * ⚠ Khác thượng nguồn (a11y): `KeyValueAdd` ghi id dòng mới vào `focusedId` nhưng bản gốc không\n * bao giờ dùng nó để đưa focus — bấm \"Thêm\" xong focus vẫn đứng ở nút, người dùng bàn phím phải\n * tự Tab ngược lên dòng vừa thêm. Ở đây ô KHOÁ của dòng mới tự nhận focus. Chỉ chạy khi\n * `focusedId` ĐỔI sang dòng này (mà chỉ `KeyValueAdd` ghi nó), nên dòng có sẵn lúc mount và dòng\n * sinh ra do dán không bị cướp focus.\n */\n React.useEffect(() => {\n if (focusedId === itemData.id) {\n inputRef.current?.focus();\n }\n }, [focusedId, itemData.id]);\n\n const propsRef = useAsRef({\n onChange: onChangeProp,\n onBlur: onBlurProp,\n onPaste: onPasteProp\n });\n\n const isDisabled = disabled || context.disabled;\n const isReadOnly = readOnly || context.readOnly;\n const isRequired = required || context.required;\n const isInvalid = errors[itemData.id]?.key !== undefined;\n const [shownKey, setKeyDraft] = useTrimDraft(itemData.key);\n\n const onChange = React.useCallback(\n (event: React.ChangeEvent<KeyInputElement>) => {\n const text = event.target.value;\n if (context.trim) setKeyDraft(text);\n commitItemField(store, context, itemData.id, \"key\", context.trim ? text.trim() : text);\n propsRef.current.onChange?.(event);\n },\n [store, itemData.id, context, propsRef, setKeyDraft]\n );\n\n const onBlur = React.useCallback(\n (event: React.FocusEvent<KeyInputElement>) => {\n propsRef.current.onBlur?.(event);\n setKeyDraft(null);\n },\n [propsRef, setKeyDraft]\n );\n\n const onPaste = React.useCallback(\n (event: React.ClipboardEvent<KeyInputElement>) => {\n if (!context.enablePaste) return;\n\n propsRef.current.onPaste?.(event);\n if (event.defaultPrevented) return;\n\n const content = event.clipboardData.getData(\"text\");\n const lines = content.split(/\\r?\\n/).filter((line) => line.trim());\n\n if (lines.length > 1) {\n event.preventDefault();\n\n const parsed: ItemData[] = [];\n\n for (const line of lines) {\n let key = \"\";\n let value = \"\";\n\n if (line.includes(\"=\")) {\n const parts = line.split(\"=\");\n key = parts[0]?.trim() ?? \"\";\n value = removeQuotes(parts.slice(1).join(\"=\").trim(), context.stripQuotes);\n } else if (line.includes(\":\")) {\n const parts = line.split(\":\");\n key = parts[0]?.trim() ?? \"\";\n value = removeQuotes(parts.slice(1).join(\":\").trim(), context.stripQuotes);\n } else if (/\\s{2,}|\\t/.test(line)) {\n const parts = line.split(/\\s{2,}|\\t/);\n key = parts[0]?.trim() ?? \"\";\n value = removeQuotes(parts.slice(1).join(\" \").trim(), context.stripQuotes);\n }\n\n if (key) {\n parsed.push({ id: createItemId(), key, value });\n }\n }\n\n if (parsed.length > 0) {\n const state = store.getState();\n const currentIndex = state.value.findIndex((item) => item.id === itemData.id);\n\n let newValue: ItemData[];\n if (itemData.key === \"\" && itemData.value === \"\") {\n newValue = [...state.value.slice(0, currentIndex), ...parsed, ...state.value.slice(currentIndex + 1)];\n } else {\n newValue = [...state.value.slice(0, currentIndex + 1), ...parsed, ...state.value.slice(currentIndex + 1)];\n }\n\n if (context.maxItems !== undefined) {\n newValue = newValue.slice(0, context.maxItems);\n }\n\n store.setState(\"value\", newValue);\n\n if (context.onPaste) {\n context.onPaste(event.nativeEvent as unknown as ClipboardEvent, parsed);\n }\n }\n }\n },\n [context, store, itemData, propsRef]\n );\n\n const KeyInputPrimitive = asChild ? Slot : Input;\n\n return (\n <KeyInputPrimitive\n aria-invalid={isInvalid}\n aria-describedby={isInvalid ? getErrorId(context.rootId, itemData.id, \"key\") : undefined}\n data-slot=\"key-value-key-input\"\n autoCapitalize=\"off\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n spellCheck=\"false\"\n disabled={isDisabled}\n readOnly={isReadOnly}\n required={isRequired}\n placeholder={context.keyPlaceholder}\n {...keyInputProps}\n ref={composedRef}\n value={shownKey}\n onChange={onChange}\n onBlur={onBlur}\n onPaste={onPaste}\n />\n );\n}\n\ninterface KeyValueValueInputProps extends Omit<React.ComponentProps<\"textarea\">, \"rows\"> {\n maxRows?: number;\n asChild?: boolean;\n}\n\nfunction KeyValueValueInput(props: KeyValueValueInputProps) {\n const {\n onChange: onChangeProp,\n onBlur: onBlurProp,\n asChild,\n disabled,\n readOnly,\n required,\n className,\n maxRows,\n style,\n ...valueInputProps\n } = props;\n\n const context = useKeyValueContext(VALUE_INPUT_NAME);\n const itemData = useKeyValueItemContext(VALUE_INPUT_NAME);\n const store = useStoreContext(VALUE_INPUT_NAME);\n const errors = useStore((state) => state.errors);\n\n const propsRef = useAsRef({\n onChange: onChangeProp,\n onBlur: onBlurProp\n });\n\n const isDisabled = disabled || context.disabled;\n const isReadOnly = readOnly || context.readOnly;\n const isRequired = required || context.required;\n const isInvalid = errors[itemData.id]?.value !== undefined;\n const maxHeight = maxRows ? `calc(${maxRows} * 1.5em + 1rem)` : undefined;\n const [shownValue, setValueDraft] = useTrimDraft(itemData.value);\n\n const onChange = React.useCallback(\n (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n propsRef.current.onChange?.(event);\n const text = event.target.value;\n if (context.trim) setValueDraft(text);\n commitItemField(store, context, itemData.id, \"value\", context.trim ? text.trim() : text);\n },\n [store, itemData.id, context, propsRef, setValueDraft]\n );\n\n const onBlur = React.useCallback(\n (event: React.FocusEvent<HTMLTextAreaElement>) => {\n propsRef.current.onBlur?.(event);\n setValueDraft(null);\n },\n [propsRef, setValueDraft]\n );\n\n const ValueInputPrimitive = asChild ? Slot : Textarea;\n\n return (\n <ValueInputPrimitive\n aria-invalid={isInvalid}\n aria-describedby={isInvalid ? getErrorId(context.rootId, itemData.id, \"value\") : undefined}\n data-slot=\"key-value-value-input\"\n autoCapitalize=\"off\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n spellCheck=\"false\"\n disabled={isDisabled}\n readOnly={isReadOnly}\n required={isRequired}\n placeholder={context.valuePlaceholder}\n {...valueInputProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: ô giá trị phải trông như ô khoá (`Input`) đứng ngay cạnh khi còn MỘT\n * dòng. `Textarea` của gói theo comitor-ds: `text-base md:text-sm` + `py-2` (cao 38px ở\n * desktop, 42px và chữ 16px ở mobile), nền `bg-transparent dark:bg-input-fill`, có\n * `shadow-xs`; còn `Input` là `h-9 text-sm bg-background`, không bóng. Ghi đè ở ĐÂY chứ\n * không sửa hai primitive: `text-sm` ở mọi cỡ màn, `min-h-9` + `py-1.5` (dòng chữ 20px cách\n * mép trên 7px — đúng chỗ `Input` căn giữa dòng chữ trong 36px), `bg-background` ở cả hai\n * theme, bỏ bóng. Nhiều dòng thì `field-sizing-content` vẫn cho ô cao dần như cũ.\n */\n \"field-sizing-content min-h-9 resize-none py-1.5 text-sm md:text-sm bg-background dark:bg-background shadow-none\",\n maxRows && \"overflow-y-auto\",\n className\n )}\n style={{\n ...style,\n ...(maxHeight && { maxHeight })\n }}\n value={shownValue}\n onChange={onChange}\n onBlur={onBlur}\n />\n );\n}\n\ninterface KeyValueRemoveProps extends React.ComponentProps<typeof Button> {}\n\nfunction KeyValueRemove(props: KeyValueRemoveProps) {\n const { onClick: onClickProp, children, className, ...removeProps } = props;\n\n const context = useKeyValueContext(REMOVE_NAME);\n const itemData = useKeyValueItemContext(REMOVE_NAME);\n const store = useStoreContext(REMOVE_NAME);\n\n const propsRef = useAsRef({\n onClick: onClickProp\n });\n const value = useStore((state) => state.value);\n const isDisabled = context.disabled || value.length <= context.minItems;\n const itemIndex = value.findIndex((item) => item.id === itemData.id);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<RemoveElement>) => {\n propsRef.current.onClick?.(event);\n\n const state = store.getState();\n if (state.value.length <= context.minItems) return;\n\n const itemToRemove = state.value.find((item) => item.id === itemData.id);\n if (!itemToRemove) return;\n\n /*\n * ⚠ Khác thượng nguồn (a11y): nút đang giữ focus biến mất cùng dòng của nó, và focus rơi về\n * `<body>` — người dùng bàn phím phải Tab lại từ đầu trang (WCAG 2.4.3). Nếu nút có focus lúc\n * bấm thì sau khi xoá, đưa focus sang nút xoá của dòng kế (hoặc dòng trước nếu vừa xoá dòng\n * cuối), không còn nút nào dùng được thì sang nút thêm.\n */\n const button = event.currentTarget;\n const hadFocus = button.ownerDocument.activeElement === button;\n const root = button.closest<HTMLElement>('[data-slot=\"key-value\"]');\n const removedIndex = state.value.indexOf(itemToRemove);\n\n const newValue = state.value.filter((item) => item.id !== itemData.id);\n const newErrors = { ...state.errors };\n delete newErrors[itemData.id];\n\n store.setState(\"value\", newValue);\n store.setState(\"errors\", newErrors);\n\n context.onRemove?.(itemToRemove);\n\n if (hadFocus && root) {\n requestAnimationFrame(() => {\n const removeButtons = root.querySelectorAll<HTMLElement>('[data-slot=\"key-value-remove\"]:not(:disabled)');\n const nextTarget =\n removeButtons[Math.min(removedIndex, removeButtons.length - 1)] ??\n root.querySelector<HTMLElement>('[data-slot=\"key-value-add\"]:not(:disabled)');\n nextTarget?.focus();\n });\n }\n },\n [store, context, itemData.id, propsRef]\n );\n\n return (\n <Button\n type=\"button\"\n data-slot=\"key-value-remove\"\n variant=\"outline\"\n size=\"icon\"\n /*\n * ⚠ Khác thượng nguồn (a11y): nút chỉ có icon ✕ và bản gốc không đặt tên nào — trình đọc màn\n * hình đọc \"nút\" trơn, lặp lại ở mọi dòng. Tên mặc định nói RÕ dòng nào: \"Remove API_KEY\",\n * hoặc \"Remove row 3\" khi khoá còn trống. Đổi qua `labels.remove` của `KeyValue`, hoặc `aria-label`.\n */\n aria-label={context.labels.remove(itemData.key, itemIndex + 1)}\n disabled={isDisabled}\n {...removeProps}\n // ⚠ Khác thượng nguồn: `shrink-0` — nút là mục flex cạnh hai ô `w-full`; thiếu nó thì flex\n // bóp nút còn ~18×36px. `size=\"icon\"` của `Button` đã là ô vuông 36×36 (`h-9 w-9`).\n className={cn(\"shrink-0\", className)}\n onClick={onClick}\n >\n {/* `size-4`: `Button` của gói không tự đặt cỡ cho svg con như shadcn — thiếu là icon 24px. */}\n {children ?? <X aria-hidden=\"true\" className=\"size-4\" />}\n </Button>\n );\n}\n\ntype KeyValueAddProps = React.ComponentProps<typeof Button>;\n\nfunction KeyValueAdd(props: KeyValueAddProps) {\n const { onClick: onClickProp, children, ...addProps } = props;\n\n const context = useKeyValueContext(ADD_NAME);\n const store = useStoreContext(ADD_NAME);\n\n const propsRef = useAsRef({\n onClick: onClickProp\n });\n const value = useStore((state) => state.value);\n const isDisabled = context.disabled || (context.maxItems !== undefined && value.length >= context.maxItems);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<AddElement>) => {\n propsRef.current.onClick?.(event);\n\n const state = store.getState();\n if (context.maxItems !== undefined && state.value.length >= context.maxItems) {\n return;\n }\n\n const newItem: ItemData = {\n id: createItemId(),\n key: \"\",\n value: \"\"\n };\n\n const newValue = [...state.value, newItem];\n store.setState(\"value\", newValue);\n store.setState(\"focusedId\", newItem.id);\n\n context.onAdd?.(newItem);\n },\n [store, context, propsRef]\n );\n\n return (\n <Button\n type=\"button\"\n data-slot=\"key-value-add\"\n variant=\"outline\"\n disabled={isDisabled}\n {...addProps}\n onClick={onClick}\n >\n {children ?? (\n <>\n <Plus aria-hidden=\"true\" className=\"size-4\" />\n {context.labels.add}\n </>\n )}\n </Button>\n );\n}\n\ninterface KeyValueErrorProps extends DivProps {\n field: Field;\n}\n\nfunction KeyValueError(props: KeyValueErrorProps) {\n const { field, asChild, className, ...errorProps } = props;\n\n const context = useKeyValueContext(ERROR_NAME);\n const itemData = useKeyValueItemContext(ERROR_NAME);\n\n const errors = useStore((state) => state.errors);\n const error = errors[itemData.id]?.[field];\n\n if (!error) return null;\n\n const ErrorPrimitive = asChild ? Slot : \"span\";\n\n return (\n <ErrorPrimitive\n id={getErrorId(context.rootId, itemData.id, field)}\n role=\"alert\"\n {...errorProps}\n className={cn(\n // ⚠ Khác thượng nguồn: `text-destructive` (bậc TÔ NỀN) → `text-destructive-ink`, bậc dành\n // cho chữ trên nền trang — cùng token với chữ lỗi của `Input`.\n \"text-sm font-medium text-destructive-ink\",\n className\n )}\n >\n {error}\n </ErrorPrimitive>\n );\n}\n\nexport {\n type ItemData as KeyValueItemData,\n KeyValue,\n KeyValueAdd,\n type KeyValueAddProps,\n KeyValueError,\n type KeyValueErrorProps,\n KeyValueItem,\n type KeyValueItemProps,\n KeyValueKeyInput,\n type KeyValueKeyInputProps,\n KeyValueList,\n type KeyValueListProps,\n type KeyValueProps,\n KeyValueRemove,\n type KeyValueRemoveProps,\n KeyValueValueInput,\n type KeyValueValueInputProps,\n useStore as useKeyValueStore\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
declare const DEFAULT_CURRENCY = "
|
|
2
|
-
declare const DEFAULT_LOCALE = "vi-VN";
|
|
1
|
+
declare const DEFAULT_CURRENCY = "USD";
|
|
3
2
|
declare const NUMERIC_MASK_PATTERNS: RegExp;
|
|
4
3
|
declare const CURRENCY_PERCENTAGE_SYMBOLS: RegExp;
|
|
5
4
|
declare const REGEX_CACHE: {
|
|
@@ -96,4 +95,4 @@ declare function getPatternCaretPosition(opts: {
|
|
|
96
95
|
previousUnmasked?: string;
|
|
97
96
|
}): number;
|
|
98
97
|
|
|
99
|
-
export { CURRENCY_PERCENTAGE_SYMBOLS, DEFAULT_CURRENCY,
|
|
98
|
+
export { CURRENCY_PERCENTAGE_SYMBOLS, DEFAULT_CURRENCY, MASK_PATTERNS, type MaskPattern, type MaskPatternKey, NUMERIC_MASK_PATTERNS, REGEX_CACHE, type TransformOptions, applyCurrencyMask, applyMask, applyPercentageMask, fromUnmaskedIndex, getCurrencyCaretPosition, getPatternCaretPosition, getUnmaskedValue, isCurrencyAtEnd, isCurrencyMask, toUnmaskedIndex };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const PAST_YEARS_LIMIT = 120;
|
|
2
2
|
const FUTURE_YEARS_LIMIT = 10;
|
|
3
|
-
const DEFAULT_CURRENCY = "
|
|
4
|
-
const DEFAULT_LOCALE = "
|
|
3
|
+
const DEFAULT_CURRENCY = "USD";
|
|
4
|
+
const DEFAULT_LOCALE = "en-US";
|
|
5
5
|
const NUMERIC_MASK_PATTERNS = /^(phone|zipCode|zipCodeExtended|ssn|ein|time|date|creditCard|creditCardExpiry)$/;
|
|
6
6
|
const CURRENCY_PERCENTAGE_SYMBOLS = /[€$%]/;
|
|
7
7
|
const formattersCache = /* @__PURE__ */ new Map();
|
|
@@ -111,6 +111,27 @@ function getCurrencyFractionDigits(opts) {
|
|
|
111
111
|
fractionDigitsCache.set(key, digits);
|
|
112
112
|
return digits;
|
|
113
113
|
}
|
|
114
|
+
function findCurrencyDecimalIndex(cleaned, decimal, group) {
|
|
115
|
+
const lastDot = cleaned.lastIndexOf(".");
|
|
116
|
+
const lastComma = cleaned.lastIndexOf(",");
|
|
117
|
+
if (lastDot !== -1 && lastComma !== -1) return Math.max(lastDot, lastComma);
|
|
118
|
+
const separator = lastDot !== -1 ? "." : lastComma !== -1 ? "," : null;
|
|
119
|
+
if (!separator) return -1;
|
|
120
|
+
const last = cleaned.lastIndexOf(separator);
|
|
121
|
+
if (separator === decimal) return last;
|
|
122
|
+
if (separator === group) {
|
|
123
|
+
return separator === "." && decimal === "," && last === cleaned.length - 1 ? last : -1;
|
|
124
|
+
}
|
|
125
|
+
const occurrences = cleaned.split(separator).length - 1;
|
|
126
|
+
const digitsAfter = cleaned.length - last - 1;
|
|
127
|
+
return occurrences === 1 && digitsAfter !== 3 ? last : -1;
|
|
128
|
+
}
|
|
129
|
+
function isCanonicalAmount(value, fractionDigits) {
|
|
130
|
+
const dot = value.indexOf(".");
|
|
131
|
+
const integerPart = dot === -1 ? value : value.slice(0, dot);
|
|
132
|
+
const fractionPart = dot === -1 ? "" : value.slice(dot + 1);
|
|
133
|
+
return /^\d*$/.test(integerPart) && /^\d*$/.test(fractionPart) && fractionPart.length <= fractionDigits;
|
|
134
|
+
}
|
|
114
135
|
function isCurrencyAtEnd(opts) {
|
|
115
136
|
const { locale, currency } = opts;
|
|
116
137
|
const key = `${locale}|${currency}`;
|
|
@@ -244,88 +265,27 @@ const MASK_PATTERNS = {
|
|
|
244
265
|
validate: (value) => REGEX_CACHE.zipCodeExtended.test(value.replace(REGEX_CACHE.nonDigits, ""))
|
|
245
266
|
},
|
|
246
267
|
/**
|
|
247
|
-
* Tiền tệ — định dạng thật đi qua `Intl.NumberFormat` theo `currency`/`locale` (mặc định
|
|
248
|
-
* vi-VN → `1.234.567 ₫`). Chuỗi `pattern` ở đây chỉ là DẤU NHẬN
|
|
249
|
-
* phải khuôn chữ số.
|
|
268
|
+
* Tiền tệ — định dạng thật đi qua `Intl.NumberFormat` theo `currency`/`locale` (mặc định USD /
|
|
269
|
+
* en-US → `$1,234,567`; `VND` / `vi-VN` → `1.234.567 ₫`). Chuỗi `pattern` ở đây chỉ là DẤU NHẬN
|
|
270
|
+
* DẠNG cho nhánh tiền tệ, không phải khuôn chữ số.
|
|
250
271
|
*/
|
|
251
272
|
currency: {
|
|
252
273
|
pattern: "$###,###.##",
|
|
253
274
|
transform: (value, { currency = DEFAULT_CURRENCY, locale = DEFAULT_LOCALE } = {}) => {
|
|
254
|
-
|
|
275
|
+
const fractionDigits = getCurrencyFractionDigits({ currency, locale });
|
|
276
|
+
if (fractionDigits === 0) {
|
|
255
277
|
return value.replace(REGEX_CACHE.nonDigits, "");
|
|
256
278
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const formatter = getCachedFormatter(locale, {
|
|
260
|
-
currency,
|
|
261
|
-
minimumFractionDigits: 0,
|
|
262
|
-
maximumFractionDigits: 2
|
|
263
|
-
});
|
|
264
|
-
const parts = formatter.formatToParts(1234.5);
|
|
265
|
-
const decimalPart = parts.find((part) => part.type === "decimal");
|
|
266
|
-
if (decimalPart) localeDecimalSeparator = decimalPart.value;
|
|
267
|
-
} catch {
|
|
268
|
-
}
|
|
279
|
+
if (isCanonicalAmount(value, fractionDigits)) return value;
|
|
280
|
+
const { decimal, group } = getCachedCurrencySymbols({ currency, locale });
|
|
269
281
|
const cleaned = value.replace(REGEX_CACHE.nonCurrencyChars, "");
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
let decimalIndex = -1;
|
|
274
|
-
if (localeDecimalSeparator === ",") {
|
|
275
|
-
const lastCommaIndex = cleaned.lastIndexOf(",");
|
|
276
|
-
if (lastCommaIndex !== -1) {
|
|
277
|
-
const afterComma = cleaned.substring(lastCommaIndex + 1);
|
|
278
|
-
if (afterComma.length <= 2 && /^\d*$/.test(afterComma)) {
|
|
279
|
-
hasDecimalSeparator = true;
|
|
280
|
-
decimalIndex = lastCommaIndex;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
if (!hasDecimalSeparator && dotIndex !== -1) {
|
|
284
|
-
const afterDot = cleaned.substring(dotIndex + 1);
|
|
285
|
-
if (afterDot.length <= 2 && /^\d*$/.test(afterDot)) {
|
|
286
|
-
hasDecimalSeparator = true;
|
|
287
|
-
decimalIndex = dotIndex;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
if (!hasDecimalSeparator && cleaned.length >= 4) {
|
|
291
|
-
const match = cleaned.match(/^(\d+)\.(\d{3})(\d{1,2})$/);
|
|
292
|
-
if (match) {
|
|
293
|
-
const [, beforeDot, thousandsPart, decimalPart] = match;
|
|
294
|
-
const integerPart = (beforeDot ?? "") + (thousandsPart ?? "");
|
|
295
|
-
const result = `${integerPart}.${decimalPart}`;
|
|
296
|
-
return result;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
} else {
|
|
300
|
-
const lastDotIndex = cleaned.lastIndexOf(".");
|
|
301
|
-
if (lastDotIndex !== -1) {
|
|
302
|
-
const afterDot = cleaned.substring(lastDotIndex + 1);
|
|
303
|
-
if (afterDot.length <= 2 && /^\d*$/.test(afterDot)) {
|
|
304
|
-
hasDecimalSeparator = true;
|
|
305
|
-
decimalIndex = lastDotIndex;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
if (!hasDecimalSeparator && commaIndex !== -1) {
|
|
309
|
-
const afterComma = cleaned.substring(commaIndex + 1);
|
|
310
|
-
const looksLikeThousands = commaIndex <= 3 && afterComma.length >= 3;
|
|
311
|
-
if (!looksLikeThousands && afterComma.length <= 2 && /^\d*$/.test(afterComma)) {
|
|
312
|
-
hasDecimalSeparator = true;
|
|
313
|
-
decimalIndex = commaIndex;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
if (hasDecimalSeparator && decimalIndex !== -1) {
|
|
318
|
-
const beforeDecimal = cleaned.substring(0, decimalIndex).replace(/[.,]/g, "");
|
|
319
|
-
const afterDecimal = cleaned.substring(decimalIndex + 1).replace(/[.,]/g, "");
|
|
320
|
-
if (afterDecimal === "") {
|
|
321
|
-
const result2 = `${beforeDecimal}.`;
|
|
322
|
-
return result2;
|
|
323
|
-
}
|
|
324
|
-
const result = `${beforeDecimal}.${afterDecimal.substring(0, 2)}`;
|
|
325
|
-
return result;
|
|
282
|
+
const decimalIndex = findCurrencyDecimalIndex(cleaned, decimal, group);
|
|
283
|
+
if (decimalIndex === -1) {
|
|
284
|
+
return cleaned.replace(/[.,]/g, "");
|
|
326
285
|
}
|
|
327
|
-
const
|
|
328
|
-
|
|
286
|
+
const integerPart = cleaned.substring(0, decimalIndex).replace(/[.,]/g, "");
|
|
287
|
+
const fractionPart = cleaned.substring(decimalIndex + 1).replace(/[.,]/g, "").substring(0, fractionDigits);
|
|
288
|
+
return `${integerPart}.${fractionPart}`;
|
|
329
289
|
},
|
|
330
290
|
validate: (value) => {
|
|
331
291
|
if (!REGEX_CACHE.currencyValidation.test(value)) return false;
|
|
@@ -583,7 +543,6 @@ function getPatternCaretPosition(opts) {
|
|
|
583
543
|
export {
|
|
584
544
|
CURRENCY_PERCENTAGE_SYMBOLS,
|
|
585
545
|
DEFAULT_CURRENCY,
|
|
586
|
-
DEFAULT_LOCALE,
|
|
587
546
|
MASK_PATTERNS,
|
|
588
547
|
NUMERIC_MASK_PATTERNS,
|
|
589
548
|
REGEX_CACHE,
|