@comitor/ui 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -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 +4 -2
- package/dist/components/composite/toast.js +9 -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 +2 -2
- 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.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 +2 -2
- 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 +2 -2
- 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 +5 -6
- 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 +4 -2
- 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 +9 -10
- 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 +4 -2
- 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/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.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 +2 -2
- 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 +21 -6
- 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 +109 -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 +11 -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 +17 -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 +15 -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/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/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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/composite/route-tabs.tsx"],"sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { TABS_TRIGGER_BASE, type TabsVariant, tabsListVariants, tabsTriggerVariants } from \"../ui/tabs-variants.js\";\n\n/**\n * Tab điều hướng — mỗi mục là một ROUTE THẬT, không phải một panel trong cùng trang.\n *\n * ─ VÌ SAO KHÔNG DÙNG \\`Tabs\\` CHO VIỆC NÀY ─\n * \\`Tabs\\` (Radix) dựng \\`role=\"tablist\"\\` + \\`role=\"tab\"\\` + \\`aria-controls\\` trỏ tới một\n * \\`role=\"tabpanel\"\\` CÙNG TRANG. Khi mỗi mục là một URL riêng thì:\n * • không có tabpanel nào để trỏ tới — \\`aria-controls\\` thành id treo, một lỗi a11y thật;\n * • \\`role=\"tab\"\\` nói với trình đọc màn hình rằng bấm vào sẽ đổi panel tại chỗ, trong khi thực\n * tế là điều hướng sang trang khác;\n * • roving focus của tablist nuốt phím mũi tên, thứ mà một danh sách link không nên làm.\n * Đây là \\`<nav>\\` chứa link thật, trạng thái đi bằng \\`aria-current=\"page\"\\`.\n *\n * Nhìn thì KHÔNG khác \\`Tabs\\`: cả hai đọc chung một bảng lớp (\\`ui/tabs-variants.ts\\`), nên đổi\n * kiểu tab ở một chỗ là cả hai đi theo.\n *\n * ─ LINK ─\n * Mặc định render \\`<a>\\` (tải lại cả trang). Trên Next phải truyền \\`LinkComponent={Link}\\` —\n * cùng quy ước với \\`Breadcrumb\\` và \\`AppShell\\`; gói cố ý không \\`import \"next/link\"\\` để còn chạy\n * được ngoài Next.\n *\n * ```tsx\n * \"use client\";\n * const pathname = usePathname();\n * <RouteTabs\n * variant=\"pills\"\n * pathname={pathname}\n * LinkComponent={Link}\n * items={[\n * { href: \"/cai-dat\", label: \"Chung\" },\n * { href: \"/cai-dat/thong-bao\", label: \"Thông báo\" }\n * ]}\n * />\n * ```\n */\n\n/** Props tối thiểu mà component link phải nhận — \\`next/link\\` thoả sẵn. */\nexport interface RouteTabsLinkProps {\n href: string;\n className?: string;\n children: ReactNode;\n \"aria-current\"?: \"page\" | undefined;\n \"data-state\"?: \"active\" | \"inactive\";\n}\n\nexport type RouteTabsLinkComponent = ComponentType<RouteTabsLinkProps>;\n\nexport interface RouteTabItem {\n href: string;\n label: ReactNode;\n /**\n * Mặc định khớp CHÍNH XÁC \\`pathname === href\\`.\n *\n * \\`prefix\\`: khớp cả route con (\\`/cai-dat/thanh-vien/abc\\` vẫn sáng ở \\`/cai-dat/thanh-vien\\`).\n * ⚠ Mục có href là đường dẫn GỐC của nhóm (\\`/cai-dat\\`) mà để \\`prefix\\` thì nó sáng ở MỌI tab —\n * đúng lỗi mà tab dạng \\`?query\\` từng mắc. Để mặc định cho mục gốc.\n */\n match?: \"exact\" | \"prefix\";\n}\n\nexport interface RouteTabsProps {\n items: RouteTabItem[];\n /** Đường dẫn hiện tại — \\`usePathname()\\` trên Next. */\n pathname: string;\n variant?: TabsVariant;\n /** Component link của app. Bỏ trống → \\`<a>\\` thô, tải lại cả trang. */\n LinkComponent?: RouteTabsLinkComponent;\n
|
|
1
|
+
{"version":3,"sources":["../../../src/components/composite/route-tabs.tsx"],"sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { TABS_TRIGGER_BASE, type TabsVariant, tabsListVariants, tabsTriggerVariants } from \"../ui/tabs-variants.js\";\nimport { RouteTabsNav } from \"./route-tabs-nav.js\";\n\n/**\n * Tab điều hướng — mỗi mục là một ROUTE THẬT, không phải một panel trong cùng trang.\n *\n * ─ VÌ SAO KHÔNG DÙNG \\`Tabs\\` CHO VIỆC NÀY ─\n * \\`Tabs\\` (Radix) dựng \\`role=\"tablist\"\\` + \\`role=\"tab\"\\` + \\`aria-controls\\` trỏ tới một\n * \\`role=\"tabpanel\"\\` CÙNG TRANG. Khi mỗi mục là một URL riêng thì:\n * • không có tabpanel nào để trỏ tới — \\`aria-controls\\` thành id treo, một lỗi a11y thật;\n * • \\`role=\"tab\"\\` nói với trình đọc màn hình rằng bấm vào sẽ đổi panel tại chỗ, trong khi thực\n * tế là điều hướng sang trang khác;\n * • roving focus của tablist nuốt phím mũi tên, thứ mà một danh sách link không nên làm.\n * Đây là \\`<nav>\\` chứa link thật, trạng thái đi bằng \\`aria-current=\"page\"\\`.\n *\n * Nhìn thì KHÔNG khác \\`Tabs\\`: cả hai đọc chung một bảng lớp (\\`ui/tabs-variants.ts\\`), nên đổi\n * kiểu tab ở một chỗ là cả hai đi theo.\n *\n * ─ LINK ─\n * Mặc định render \\`<a>\\` (tải lại cả trang). Trên Next phải truyền \\`LinkComponent={Link}\\` —\n * cùng quy ước với \\`Breadcrumb\\` và \\`AppShell\\`; gói cố ý không \\`import \"next/link\"\\` để còn chạy\n * được ngoài Next.\n *\n * ```tsx\n * \"use client\";\n * const pathname = usePathname();\n * <RouteTabs\n * variant=\"pills\"\n * pathname={pathname}\n * LinkComponent={Link}\n * items={[\n * { href: \"/cai-dat\", label: \"Chung\" },\n * { href: \"/cai-dat/thong-bao\", label: \"Thông báo\" }\n * ]}\n * />\n * ```\n */\n\n/** Props tối thiểu mà component link phải nhận — \\`next/link\\` thoả sẵn. */\nexport interface RouteTabsLinkProps {\n href: string;\n className?: string;\n children: ReactNode;\n \"aria-current\"?: \"page\" | undefined;\n \"data-state\"?: \"active\" | \"inactive\";\n}\n\nexport type RouteTabsLinkComponent = ComponentType<RouteTabsLinkProps>;\n\nexport interface RouteTabItem {\n href: string;\n label: ReactNode;\n /**\n * Mặc định khớp CHÍNH XÁC \\`pathname === href\\`.\n *\n * \\`prefix\\`: khớp cả route con (\\`/cai-dat/thanh-vien/abc\\` vẫn sáng ở \\`/cai-dat/thanh-vien\\`).\n * ⚠ Mục có href là đường dẫn GỐC của nhóm (\\`/cai-dat\\`) mà để \\`prefix\\` thì nó sáng ở MỌI tab —\n * đúng lỗi mà tab dạng \\`?query\\` từng mắc. Để mặc định cho mục gốc.\n */\n match?: \"exact\" | \"prefix\";\n}\n\nexport interface RouteTabsProps {\n items: RouteTabItem[];\n /** Đường dẫn hiện tại — \\`usePathname()\\` trên Next. */\n pathname: string;\n variant?: TabsVariant;\n /** Component link của app. Bỏ trống → \\`<a>\\` thô, tải lại cả trang. */\n LinkComponent?: RouteTabsLinkComponent;\n /**\n * Nhãn của vùng điều hướng, cho trình đọc màn hình. Không truyền thì lấy khoá `label` của\n * namespace `routeTabs` (`ComitorLocaleProvider`, mặc định tiếng Anh).\n */\n label?: string;\n className?: string;\n /** Lớp thêm cho từng link. */\n itemClassName?: string;\n}\n\nfunction DefaultLink({ href, className, children, ...rest }: RouteTabsLinkProps) {\n return (\n <a href={href} className={className} {...rest}>\n {children}\n </a>\n );\n}\n\nfunction isItemActive(item: RouteTabItem, pathname: string): boolean {\n if (item.match === \"prefix\") return pathname === item.href || pathname.startsWith(`${item.href}/`);\n return pathname === item.href;\n}\n\nexport function RouteTabs({\n items,\n pathname,\n variant = \"default\",\n LinkComponent = DefaultLink,\n label,\n className,\n itemClassName\n}: RouteTabsProps) {\n /*\n * File này CỐ Ý không có `\"use client\"`: `LinkComponent` là component do Server Component truyền\n * vào, không qua được ranh giới RSC. Chỉ tên của `<nav>` cần đọc provider, nên nó nằm ở lá client\n * `RouteTabsNav` (`route-tabs-nav.tsx`); các link vẫn dựng ở đây rồi đi vào đó làm `children`.\n */\n return (\n <RouteTabsNav label={label}>\n {/*\n `overflow-x-auto` để danh sách CUỘN NGANG khi hẹp, thay vì xuống dòng — nhãn tiếng Việt\n thường hai từ nên rất dễ vỡ dòng và làm pill cao gấp đôi. `whitespace-nowrap` ở từng mục\n là nửa còn lại của cùng một quyết định.\n */}\n <div\n data-slot=\"route-tabs-list\"\n className={cn(\"flex gap-0 overflow-x-auto\", tabsListVariants[variant], className)}\n >\n {items.map((item) => {\n const active = isItemActive(item, pathname);\n return (\n <LinkComponent\n key={item.href}\n href={item.href}\n // `data-state` để dùng lại nguyên bảng lớp của Tabs (chúng nhắm `data-[state=active]`).\n data-state={active ? \"active\" : \"inactive\"}\n aria-current={active ? \"page\" : undefined}\n className={cn(\n tabsTriggerVariants[variant],\n TABS_TRIGGER_BASE,\n \"shrink-0 whitespace-nowrap text-center\",\n itemClassName\n )}\n >\n {item.label}\n </LinkComponent>\n );\n })}\n </div>\n </RouteTabsNav>\n );\n}\n"],"mappings":"AAmFI;AAlFJ,SAAS,UAAU;AACnB,SAAS,mBAAqC,kBAAkB,2BAA2B;AAC3F,SAAS,oBAAoB;AA8E7B,SAAS,YAAY,EAAE,MAAM,WAAW,UAAU,GAAG,KAAK,GAAuB;AAC/E,SACE,oBAAC,OAAE,MAAY,WAAuB,GAAG,MACtC,UACH;AAEJ;AAEA,SAAS,aAAa,MAAoB,UAA2B;AACnE,MAAI,KAAK,UAAU,SAAU,QAAO,aAAa,KAAK,QAAQ,SAAS,WAAW,GAAG,KAAK,IAAI,GAAG;AACjG,SAAO,aAAa,KAAK;AAC3B;AAEO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AACF,GAAmB;AAMjB,SACE,oBAAC,gBAAa,OAMZ;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,8BAA8B,iBAAiB,OAAO,GAAG,SAAS;AAAA,MAE/E,gBAAM,IAAI,CAAC,SAAS;AACnB,cAAM,SAAS,aAAa,MAAM,QAAQ;AAC1C,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAM,KAAK;AAAA,YAEX,cAAY,SAAS,WAAW;AAAA,YAChC,gBAAc,SAAS,SAAS;AAAA,YAChC,WAAW;AAAA,cACT,oBAAoB,OAAO;AAAA,cAC3B;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,YAEC,eAAK;AAAA;AAAA,UAZD,KAAK;AAAA,QAaZ;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH,GACF;AAEJ;","names":[]}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Nhãn mặc định của `SearchFilterBar
|
|
4
|
+
* Nhãn mặc định của `SearchFilterBar`.
|
|
5
|
+
*
|
|
6
|
+
* Đuôi `.tsx` là di sản: JSX của `resultSummary` nay nằm ở bảng chữ
|
|
7
|
+
* (`src/i18n/namespaces/search-filter-bar.tsx`). Giữ đuôi cũ vì đổi không mua được gì — đường dẫn
|
|
8
|
+
* đã build (`.js`) và mọi `import` đều như nhau.
|
|
5
9
|
*
|
|
6
10
|
* Module **CỐ Ý không có `"use client"`** — xem `src/chart/chart-colors.ts` để biết lý do đầy
|
|
7
11
|
* đủ: mọi export của một file `"use client"` đến tay Server Component dưới dạng
|
|
@@ -14,31 +18,33 @@ import { ReactNode } from 'react';
|
|
|
14
18
|
*/
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
|
-
* Chuỗi hiển thị + chuỗi a11y của `SearchFilterBar` — cùng khuôn `DataTableLabels`.
|
|
21
|
+
* Chuỗi hiển thị + chuỗi a11y của `SearchFilterBar` — cùng khuôn `DataTableLabels`. Theo
|
|
22
|
+
* `ComitorLocaleProvider` (namespace `searchFilterBar`, mặc định tiếng Anh); override từng khoá
|
|
23
|
+
* qua prop `labels`.
|
|
18
24
|
*
|
|
19
25
|
* `searchPlaceholder` và `unitLabel` vốn đã là prop rời và vẫn được giữ (prop thắng `labels`);
|
|
20
26
|
* các khoá còn lại trước đây chôn cứng, trong đó có `sr-only` và nội dung nút mà KHÔNG prop nào
|
|
21
|
-
* chạm tới —
|
|
27
|
+
* chạm tới — ghép thanh này với `DataTable` đã dịch sẽ ra giao diện nửa nọ nửa kia.
|
|
22
28
|
*/
|
|
23
29
|
interface SearchFilterBarLabels {
|
|
24
30
|
/** Placeholder + `aria-label` của ô tìm */
|
|
25
31
|
searchPlaceholder: string;
|
|
26
32
|
/**
|
|
27
|
-
* Đơn vị đếm mặc định ("
|
|
33
|
+
* Đơn vị đếm mặc định ("items" / "mục") — prop `unitLabel` vẫn ghi đè được riêng.
|
|
28
34
|
*
|
|
29
35
|
* Cùng lý do như `TablePaginationLabels.unitLabel`: mặc định là chuỗi theo NGÔN NGỮ nên
|
|
30
36
|
* phải override được qua `labels`, còn prop `unitLabel` là chuỗi theo NỘI DUNG của từng
|
|
31
|
-
* bảng ("
|
|
37
|
+
* bảng ("tasks", "contacts") nên vẫn là prop rời và vẫn thắng `labels`.
|
|
32
38
|
*/
|
|
33
39
|
unitLabel: string;
|
|
34
40
|
/** `sr-only` của nút xoá từ khoá trong ô tìm */
|
|
35
41
|
clearSearch: string;
|
|
36
|
-
/** Huy hiệu đếm bộ lọc đang bật: "3
|
|
42
|
+
/** Huy hiệu đếm bộ lọc đang bật: "3 filters" */
|
|
37
43
|
filterCount: (count: number) => string;
|
|
38
44
|
/** Nhãn nút xoá toàn bộ bộ lọc */
|
|
39
45
|
clearFilters: string;
|
|
40
46
|
/**
|
|
41
|
-
* Dòng đếm kết quả sau lọc: "**3**
|
|
47
|
+
* Dòng đếm kết quả sau lọc: "**3** of 57 items" (`total` bỏ trống thì chỉ "**3** items").
|
|
42
48
|
*
|
|
43
49
|
* Trả `ReactNode` chứ không phải chuỗi vì con số cần thẻ riêng (`tabular-nums` để số không
|
|
44
50
|
* nhảy cột, `text-foreground` để nổi trên nền chữ mờ) — ép về chuỗi là mất phần định dạng đó.
|
|
@@ -46,7 +52,11 @@ interface SearchFilterBarLabels {
|
|
|
46
52
|
*/
|
|
47
53
|
resultSummary: (resultCount: number, total: number | undefined, unitLabel: string) => ReactNode;
|
|
48
54
|
}
|
|
49
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Nhãn TIẾNG ANH mặc định (từ `3.0.0`; trước đó là tiếng Việt). Bảng tiếng Việt:
|
|
57
|
+
* `comitorMessagesVi.searchFilterBar`. Component không đọc thẳng hằng này — nó đọc bảng của
|
|
58
|
+
* `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.
|
|
59
|
+
*/
|
|
50
60
|
declare const DEFAULT_SEARCH_FILTER_BAR_LABELS: SearchFilterBarLabels;
|
|
51
61
|
|
|
52
62
|
export { DEFAULT_SEARCH_FILTER_BAR_LABELS, type SearchFilterBarLabels };
|
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const DEFAULT_SEARCH_FILTER_BAR_LABELS = {
|
|
3
|
-
searchPlaceholder: "T\xECm ki\u1EBFm\u2026",
|
|
4
|
-
unitLabel: "m\u1EE5c",
|
|
5
|
-
clearSearch: "X\xF3a t\u1EEB kh\xF3a",
|
|
6
|
-
filterCount: (count) => `${count} b\u1ED9 l\u1ECDc`,
|
|
7
|
-
clearFilters: "X\xF3a l\u1ECDc",
|
|
8
|
-
resultSummary: (resultCount, total, unitLabel) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9
|
-
/* @__PURE__ */ jsx("strong", { className: "font-medium text-foreground tabular-nums", children: resultCount }),
|
|
10
|
-
total !== void 0 ? ` / ${total}` : "",
|
|
11
|
-
" ",
|
|
12
|
-
unitLabel
|
|
13
|
-
] })
|
|
14
|
-
};
|
|
1
|
+
import { searchFilterBarEn } from "../../i18n/namespaces/search-filter-bar.js";
|
|
2
|
+
const DEFAULT_SEARCH_FILTER_BAR_LABELS = { ...searchFilterBarEn };
|
|
15
3
|
export {
|
|
16
4
|
DEFAULT_SEARCH_FILTER_BAR_LABELS
|
|
17
5
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/composite/search-filter-bar-constants.tsx"],"sourcesContent":["/**\n * Nhãn mặc định của `SearchFilterBar` (
|
|
1
|
+
{"version":3,"sources":["../../../src/components/composite/search-filter-bar-constants.tsx"],"sourcesContent":["/**\n * Nhãn mặc định của `SearchFilterBar`.\n *\n * Đuôi `.tsx` là di sản: JSX của `resultSummary` nay nằm ở bảng chữ\n * (`src/i18n/namespaces/search-filter-bar.tsx`). Giữ đuôi cũ vì đổi không mua được gì — đường dẫn\n * đã build (`.js`) và mọi `import` đều như nhau.\n *\n * Module **CỐ Ý không có `\"use client\"`** — xem `src/chart/chart-colors.ts` để biết lý do đầy\n * đủ: mọi export của một file `\"use client\"` đến tay Server Component dưới dạng\n * client-reference proxy, đọc ra `undefined` mà KHÔNG throw, KHÔNG warning. Hằng dữ liệu thuần\n * (nhãn mặc định, preset, sentinel) là thứ trang server hay đọc nhất — spread để override một\n * khoá, đối chiếu giá trị, dựng danh sách — nên phải sống ngoài ranh giới client.\n *\n * ⚠ Barrel `src/index.ts` xuất module này TRỰC TIẾP. `search-filter-bar.tsx` chỉ được `import`,\n * KHÔNG được `export` lại (re-export qua file client là vô hiệu, và đụng tên với `export *`).\n */\nimport type { ReactNode } from \"react\";\nimport { searchFilterBarEn } from \"../../i18n/namespaces/search-filter-bar.js\";\n\n/**\n * Chuỗi hiển thị + chuỗi a11y của `SearchFilterBar` — cùng khuôn `DataTableLabels`. Theo\n * `ComitorLocaleProvider` (namespace `searchFilterBar`, mặc định tiếng Anh); override từng khoá\n * qua prop `labels`.\n *\n * `searchPlaceholder` và `unitLabel` vốn đã là prop rời và vẫn được giữ (prop thắng `labels`);\n * các khoá còn lại trước đây chôn cứng, trong đó có `sr-only` và nội dung nút mà KHÔNG prop nào\n * chạm tới — ghép thanh này với `DataTable` đã dịch sẽ ra giao diện nửa nọ nửa kia.\n */\nexport interface SearchFilterBarLabels {\n /** Placeholder + `aria-label` của ô tìm */\n searchPlaceholder: string;\n /**\n * Đơn vị đếm mặc định (\"items\" / \"mục\") — prop `unitLabel` vẫn ghi đè được riêng.\n *\n * Cùng lý do như `TablePaginationLabels.unitLabel`: mặc định là chuỗi theo NGÔN NGỮ nên\n * phải override được qua `labels`, còn prop `unitLabel` là chuỗi theo NỘI DUNG của từng\n * bảng (\"tasks\", \"contacts\") nên vẫn là prop rời và vẫn thắng `labels`.\n */\n unitLabel: string;\n /** `sr-only` của nút xoá từ khoá trong ô tìm */\n clearSearch: string;\n /** Huy hiệu đếm bộ lọc đang bật: \"3 filters\" */\n filterCount: (count: number) => string;\n /** Nhãn nút xoá toàn bộ bộ lọc */\n clearFilters: string;\n /**\n * Dòng đếm kết quả sau lọc: \"**3** of 57 items\" (`total` bỏ trống thì chỉ \"**3** items\").\n *\n * Trả `ReactNode` chứ không phải chuỗi vì con số cần thẻ riêng (`tabular-nums` để số không\n * nhảy cột, `text-foreground` để nổi trên nền chữ mờ) — ép về chuỗi là mất phần định dạng đó.\n * Là hàm vì trật tự số/đơn vị không giống nhau giữa các ngôn ngữ.\n */\n resultSummary: (resultCount: number, total: number | undefined, unitLabel: string) => ReactNode;\n}\n\n/**\n * Nhãn TIẾNG ANH mặc định (từ `3.0.0`; trước đó là tiếng Việt). Bảng tiếng Việt:\n * `comitorMessagesVi.searchFilterBar`. Component không đọc thẳng hằng này — nó đọc bảng của\n * `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.\n */\nexport const DEFAULT_SEARCH_FILTER_BAR_LABELS: SearchFilterBarLabels = { ...searchFilterBarEn };\n"],"mappings":"AAiBA,SAAS,yBAAyB;AA2C3B,MAAM,mCAA0D,EAAE,GAAG,kBAAkB;","names":[]}
|
|
@@ -23,9 +23,12 @@ interface SearchFilterBarProps {
|
|
|
23
23
|
resultCount?: number;
|
|
24
24
|
/** Tổng số bản ghi khi chưa lọc */
|
|
25
25
|
total?: number;
|
|
26
|
-
/** Đơn vị đếm theo nội dung bảng, vd "
|
|
26
|
+
/** Đơn vị đếm theo nội dung bảng, vd "tasks", "contacts" — mặc định `labels.unitLabel` ("items") */
|
|
27
27
|
unitLabel?: string;
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Ghi đè chuỗi hiển thị/a11y — không truyền thì theo namespace `searchFilterBar` của
|
|
30
|
+
* `ComitorLocaleProvider` (mặc định tiếng Anh). Xem `SearchFilterBarLabels`.
|
|
31
|
+
*/
|
|
29
32
|
labels?: Partial<SearchFilterBarLabels>;
|
|
30
33
|
className?: string;
|
|
31
34
|
}
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Search, SlidersHorizontal, X } from "lucide-react";
|
|
4
4
|
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { useComitorLabels } from "../../i18n/locale-provider.js";
|
|
5
6
|
import { cn } from "../../lib/cn.js";
|
|
6
7
|
import { Badge } from "../ui/badge.js";
|
|
7
8
|
import { Button } from "../ui/button.js";
|
|
8
9
|
import { Input } from "../ui/input.js";
|
|
9
|
-
import { DEFAULT_SEARCH_FILTER_BAR_LABELS } from "./search-filter-bar-constants.js";
|
|
10
10
|
function SearchFilterBar({
|
|
11
11
|
searchValue,
|
|
12
12
|
onSearchChange,
|
|
@@ -24,7 +24,7 @@ function SearchFilterBar({
|
|
|
24
24
|
labels: labelsProp,
|
|
25
25
|
className
|
|
26
26
|
}) {
|
|
27
|
-
const labels =
|
|
27
|
+
const labels = useComitorLabels("searchFilterBar", labelsProp);
|
|
28
28
|
const placeholder = searchPlaceholder ?? labels.searchPlaceholder;
|
|
29
29
|
const unitLabel = unitLabelProp ?? labels.unitLabel;
|
|
30
30
|
const [localValue, setLocalValue] = useState(searchValue);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/composite/search-filter-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport { Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { type ComponentPropsWithoutRef, type ReactNode, type Ref, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Badge } from \"../ui/badge.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Input } from \"../ui/input.js\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/components/composite/search-filter-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport { Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { type ComponentPropsWithoutRef, type ReactNode, type Ref, useEffect, useRef, useState } from \"react\";\nimport { useComitorLabels } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Badge } from \"../ui/badge.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Input } from \"../ui/input.js\";\nimport type { SearchFilterBarLabels } from \"./search-filter-bar-constants.js\";\n\nexport interface SearchFilterBarProps {\n /** Giá trị ô tìm kiếm (controlled) */\n searchValue: string;\n onSearchChange: (value: string) => void;\n searchPlaceholder?: string;\n /** Trễ trước khi gọi `onSearchChange` (ms). 0 = gọi ngay. Gõ tìm gọi API thì đặt 250–350. */\n debounceMs?: number;\n searchInputRef?: Ref<HTMLInputElement>;\n /** Props đặt thẳng lên ô nhập (id, name, aria-label, enterKeyHint…) */\n searchInputProps?: ComponentPropsWithoutRef<\"input\">;\n\n /** Slot bộ lọc cạnh ô tìm (Select, Combobox, DatePicker…) */\n children?: ReactNode;\n /** Slot hành động bên phải (nút Tạo mới, menu cột…) */\n actions?: ReactNode;\n\n /** Số bộ lọc đang bật — hiện huy hiệu và mở dòng đếm kết quả */\n activeFilterCount?: number;\n onClearFilters?: () => void;\n /** Số bản ghi sau lọc */\n resultCount?: number;\n /** Tổng số bản ghi khi chưa lọc */\n total?: number;\n /** Đơn vị đếm theo nội dung bảng, vd \"tasks\", \"contacts\" — mặc định `labels.unitLabel` (\"items\") */\n unitLabel?: string;\n /**\n * Ghi đè chuỗi hiển thị/a11y — không truyền thì theo namespace `searchFilterBar` của\n * `ComitorLocaleProvider` (mặc định tiếng Anh). Xem `SearchFilterBarLabels`.\n */\n labels?: Partial<SearchFilterBarLabels>;\n className?: string;\n}\n\n/**\n * Thanh tìm kiếm + lọc chuẩn cho trang danh sách.\n *\n * Ô tìm giữ giá trị cục bộ và chỉ đẩy lên sau `debounceMs` — nếu bind thẳng vào state của trang,\n * mỗi phím gõ là một lần render cả bảng (và thường là một request).\n *\n * Dòng đếm kết quả có `min-h-7` cố định để việc bật/tắt lọc không làm bảng nhảy lên xuống.\n */\nexport function SearchFilterBar({\n searchValue,\n onSearchChange,\n searchPlaceholder,\n debounceMs = 250,\n searchInputRef,\n searchInputProps,\n children,\n actions,\n activeFilterCount = 0,\n onClearFilters,\n resultCount,\n total,\n unitLabel: unitLabelProp,\n labels: labelsProp,\n className\n}: SearchFilterBarProps) {\n const labels = useComitorLabels(\"searchFilterBar\", labelsProp);\n const placeholder = searchPlaceholder ?? labels.searchPlaceholder;\n const unitLabel = unitLabelProp ?? labels.unitLabel;\n const [localValue, setLocalValue] = useState(searchValue);\n const lastEmittedRef = useRef(searchValue);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const onSearchChangeRef = useRef(onSearchChange);\n\n useEffect(() => {\n onSearchChangeRef.current = onSearchChange;\n });\n\n // Đồng bộ khi giá trị bị đổi từ bên ngoài (nút xoá bộ lọc, điều hướng lại trang) — nhưng\n // không đè lên những gì người dùng đang gõ dở.\n useEffect(() => {\n if (searchValue !== lastEmittedRef.current) {\n lastEmittedRef.current = searchValue;\n setLocalValue(searchValue);\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }\n }, [searchValue]);\n\n useEffect(\n () => () => {\n if (timerRef.current) clearTimeout(timerRef.current);\n },\n []\n );\n\n const emit = (value: string) => {\n setLocalValue(value);\n if (timerRef.current) clearTimeout(timerRef.current);\n if (!debounceMs) {\n lastEmittedRef.current = value;\n onSearchChangeRef.current(value);\n return;\n }\n timerRef.current = setTimeout(() => {\n lastEmittedRef.current = value;\n onSearchChangeRef.current(value);\n }, debounceMs);\n };\n\n const clearSearch = () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n setLocalValue(\"\");\n lastEmittedRef.current = \"\";\n onSearchChangeRef.current(\"\");\n };\n\n const hasFilters = activeFilterCount > 0;\n const showCount = hasFilters || Boolean(localValue);\n\n return (\n <div data-slot=\"search-filter-bar\" className={cn(\"flex flex-col gap-2\", className)}>\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"relative min-w-[12rem] flex-1 sm:max-w-sm\">\n <Search\n aria-hidden=\"true\"\n className=\"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground\"\n />\n <Input\n ref={searchInputRef}\n type=\"search\"\n value={localValue}\n placeholder={placeholder}\n aria-label={placeholder}\n onChange={(event) => emit(event.target.value)}\n className=\"pl-9 pr-8 [&::-webkit-search-cancel-button]:hidden\"\n {...searchInputProps}\n />\n {localValue && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={clearSearch}\n className=\"absolute right-1 top-1/2 size-6 -translate-y-1/2\"\n >\n <X aria-hidden=\"true\" className=\"size-3.5\" />\n <span className=\"sr-only\">{labels.clearSearch}</span>\n </Button>\n )}\n </div>\n\n {children}\n\n {hasFilters && (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <SlidersHorizontal aria-hidden=\"true\" />\n {labels.filterCount(activeFilterCount)}\n </Badge>\n )}\n\n {actions && <div className=\"ml-auto flex items-center gap-2\">{actions}</div>}\n </div>\n\n {showCount && (\n <div role=\"status\" className=\"flex min-h-7 items-center gap-2 text-sm text-muted-foreground\">\n {resultCount !== undefined && <span>{labels.resultSummary(resultCount, total, unitLabel)}</span>}\n {hasFilters && onClearFilters && (\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={onClearFilters} className=\"h-7 gap-1.5 px-2\">\n <X aria-hidden=\"true\" className=\"size-3.5\" />\n {labels.clearFilters}\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -21,7 +21,7 @@ import { IconComponent } from './empty-state.js';
|
|
|
21
21
|
* light và `--yellow` ở dark, nên một class là đủ.
|
|
22
22
|
*/
|
|
23
23
|
declare const statCardIconVariants: (props?: ({
|
|
24
|
-
tone?: "default" | "
|
|
24
|
+
tone?: "default" | "destructive" | "warning" | "success" | "primary" | "info" | "app" | null | undefined;
|
|
25
25
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
26
26
|
type StatCardTone = NonNullable<VariantProps<typeof statCardIconVariants>["tone"]>;
|
|
27
27
|
type StatCardDeltaDirection = "up" | "down" | "flat";
|
|
@@ -14,12 +14,13 @@
|
|
|
14
14
|
declare const DEFAULT_PAGE_SIZE_OPTIONS: readonly [20, 50, 100];
|
|
15
15
|
/**
|
|
16
16
|
* Toàn bộ chuỗi hiển thị và chuỗi a11y của `TablePagination` — cùng khuôn `DataTableLabels`.
|
|
17
|
+
* Theo `ComitorLocaleProvider` (namespace `tablePagination`, mặc định tiếng Anh); override từng
|
|
18
|
+
* khoá qua prop `labels`.
|
|
17
19
|
*
|
|
18
20
|
* Vì sao phải có, dù component đã nhận `label`/`unitLabel` qua prop rời: 4 nhãn `sr-only` của
|
|
19
|
-
* cụm nút (
|
|
20
|
-
* (`range`, `pageStatus`) KHÔNG có prop nào chạm tới được, nên
|
|
21
|
-
* (đã
|
|
22
|
-
* nhất quán tiếng Việt.
|
|
21
|
+
* cụm nút (trang đầu/trước/sau/cuối), `aria-label` của ô chọn cỡ trang và hai dòng đếm
|
|
22
|
+
* (`range`, `pageStatus`) KHÔNG có prop nào chạm tới được, nên một bảng dùng `DataTable`
|
|
23
|
+
* (đã dịch) kèm `TablePagination` (chưa) sẽ ra một cái bảng dịch NỬA VỜI.
|
|
23
24
|
*
|
|
24
25
|
* Khoá nào có số/đơn vị chèn vào thì là HÀM: nối chuỗi bằng dấu cộng ở phía app là cách chắc
|
|
25
26
|
* chắn sai trật tự từ với các ngôn ngữ khác.
|
|
@@ -28,11 +29,11 @@ interface TablePaginationLabels {
|
|
|
28
29
|
/** `aria-label` của vùng phân trang (`<nav>`) — prop `label` vẫn ghi đè được riêng */
|
|
29
30
|
region: string;
|
|
30
31
|
/**
|
|
31
|
-
* Đơn vị đếm mặc định ("
|
|
32
|
+
* Đơn vị đếm mặc định ("items" / "mục") — prop `unitLabel` vẫn ghi đè được riêng.
|
|
32
33
|
*
|
|
33
|
-
* Nằm trong `labels` vì đây là chuỗi theo NGÔN NGỮ: app
|
|
34
|
-
*
|
|
35
|
-
* prop `unitLabel` là chuỗi theo NỘI DUNG ("
|
|
34
|
+
* Nằm trong `labels` vì đây là chuỗi theo NGÔN NGỮ: app override cả bộ `labels` mà đơn vị vẫn
|
|
35
|
+
* kẹt ở ngôn ngữ mặc định thì đúng là cái giao diện nửa vời mà cơ chế này sinh ra để chặn. Còn
|
|
36
|
+
* prop `unitLabel` là chuỗi theo NỘI DUNG ("tasks", "contacts") — mỗi bảng một khác, nên vẫn
|
|
36
37
|
* là prop rời và vẫn thắng `labels`, đúng cặp `label`/`labels.region` ở trên.
|
|
37
38
|
*/
|
|
38
39
|
unitLabel: string;
|
|
@@ -40,9 +41,9 @@ interface TablePaginationLabels {
|
|
|
40
41
|
pageSizePrefix: string;
|
|
41
42
|
/** `aria-label` của ô chọn cỡ trang */
|
|
42
43
|
pageSizeSelect: (unitLabel: string) => string;
|
|
43
|
-
/** Dòng đếm bản ghi: "1–20
|
|
44
|
+
/** Dòng đếm bản ghi: "1–20 of 57 items" */
|
|
44
45
|
range: (start: number, end: number, total: number, unitLabel: string) => string;
|
|
45
|
-
/** Dòng đếm trang: "
|
|
46
|
+
/** Dòng đếm trang: "Page 2 of 5" */
|
|
46
47
|
pageStatus: (page: number, pageCount: number) => string;
|
|
47
48
|
/** `sr-only` của 4 nút điều hướng */
|
|
48
49
|
firstPage: string;
|
|
@@ -50,7 +51,11 @@ interface TablePaginationLabels {
|
|
|
50
51
|
nextPage: string;
|
|
51
52
|
lastPage: string;
|
|
52
53
|
}
|
|
53
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Nhãn TIẾNG ANH mặc định (từ `3.0.0`; trước đó là tiếng Việt). Bảng tiếng Việt:
|
|
56
|
+
* `comitorMessagesVi.tablePagination`. Component không đọc thẳng hằng này — nó đọc bảng của
|
|
57
|
+
* `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.
|
|
58
|
+
*/
|
|
54
59
|
declare const DEFAULT_TABLE_PAGINATION_LABELS: TablePaginationLabels;
|
|
55
60
|
|
|
56
61
|
export { DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_TABLE_PAGINATION_LABELS, type TablePaginationLabels };
|
|
@@ -1,16 +1,6 @@
|
|
|
1
|
+
import { tablePaginationEn } from "../../i18n/namespaces/table-pagination.js";
|
|
1
2
|
const DEFAULT_PAGE_SIZE_OPTIONS = [20, 50, 100];
|
|
2
|
-
const DEFAULT_TABLE_PAGINATION_LABELS = {
|
|
3
|
-
region: "Ph\xE2n trang",
|
|
4
|
-
unitLabel: "m\u1EE5c",
|
|
5
|
-
pageSizePrefix: "Hi\u1EC3n th\u1ECB",
|
|
6
|
-
pageSizeSelect: (unitLabel) => `S\u1ED1 ${unitLabel} m\u1ED7i trang`,
|
|
7
|
-
range: (start, end, total, unitLabel) => `${start}\u2013${end} tr\xEAn t\u1ED5ng ${total} ${unitLabel}`,
|
|
8
|
-
pageStatus: (page, pageCount) => `Trang ${page} / ${pageCount}`,
|
|
9
|
-
firstPage: "Trang \u0111\u1EA7u",
|
|
10
|
-
previousPage: "Trang tr\u01B0\u1EDBc",
|
|
11
|
-
nextPage: "Trang sau",
|
|
12
|
-
lastPage: "Trang cu\u1ED1i"
|
|
13
|
-
};
|
|
3
|
+
const DEFAULT_TABLE_PAGINATION_LABELS = { ...tablePaginationEn };
|
|
14
4
|
export {
|
|
15
5
|
DEFAULT_PAGE_SIZE_OPTIONS,
|
|
16
6
|
DEFAULT_TABLE_PAGINATION_LABELS
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/composite/table-pagination-constants.ts"],"sourcesContent":["/**\n * Cỡ trang và nhãn mặc định của `TablePagination`.\n *\n * Module **CỐ Ý không có `\"use client\"`** — xem `src/chart/chart-colors.ts` để biết lý do đầy\n * đủ: mọi export của một file `\"use client\"` đến tay Server Component dưới dạng\n * client-reference proxy, đọc ra `undefined` mà KHÔNG throw, KHÔNG warning. Hằng dữ liệu thuần\n * (nhãn mặc định, preset, sentinel) là thứ trang server hay đọc nhất — spread để override một\n * khoá, đối chiếu giá trị, dựng danh sách — nên phải sống ngoài ranh giới client.\n *\n * ⚠ Barrel `src/index.ts` xuất module này TRỰC TIẾP. `table-pagination.tsx` chỉ được `import`,\n * KHÔNG được `export` lại (re-export qua file client là vô hiệu, và đụng tên với `export *`).\n */\n\n/** Cỡ trang mặc định — chốt ở 3 mức để mọi bảng trong hệ sinh thái giống nhau. */\nexport const DEFAULT_PAGE_SIZE_OPTIONS = [20, 50, 100] as const;\n\n/**\n * Toàn bộ chuỗi hiển thị và chuỗi a11y của `TablePagination` — cùng khuôn `DataTableLabels`.\n *\n * Vì sao phải có, dù component đã nhận `label`/`unitLabel` qua prop rời: 4 nhãn `sr-only` của\n * cụm nút (
|
|
1
|
+
{"version":3,"sources":["../../../src/components/composite/table-pagination-constants.ts"],"sourcesContent":["/**\n * Cỡ trang và nhãn mặc định của `TablePagination`.\n *\n * Module **CỐ Ý không có `\"use client\"`** — xem `src/chart/chart-colors.ts` để biết lý do đầy\n * đủ: mọi export của một file `\"use client\"` đến tay Server Component dưới dạng\n * client-reference proxy, đọc ra `undefined` mà KHÔNG throw, KHÔNG warning. Hằng dữ liệu thuần\n * (nhãn mặc định, preset, sentinel) là thứ trang server hay đọc nhất — spread để override một\n * khoá, đối chiếu giá trị, dựng danh sách — nên phải sống ngoài ranh giới client.\n *\n * ⚠ Barrel `src/index.ts` xuất module này TRỰC TIẾP. `table-pagination.tsx` chỉ được `import`,\n * KHÔNG được `export` lại (re-export qua file client là vô hiệu, và đụng tên với `export *`).\n */\n\nimport { tablePaginationEn } from \"../../i18n/namespaces/table-pagination.js\";\n\n/** Cỡ trang mặc định — chốt ở 3 mức để mọi bảng trong hệ sinh thái giống nhau. */\nexport const DEFAULT_PAGE_SIZE_OPTIONS = [20, 50, 100] as const;\n\n/**\n * Toàn bộ chuỗi hiển thị và chuỗi a11y của `TablePagination` — cùng khuôn `DataTableLabels`.\n * Theo `ComitorLocaleProvider` (namespace `tablePagination`, mặc định tiếng Anh); override từng\n * khoá qua prop `labels`.\n *\n * Vì sao phải có, dù component đã nhận `label`/`unitLabel` qua prop rời: 4 nhãn `sr-only` của\n * cụm nút (trang đầu/trước/sau/cuối), `aria-label` của ô chọn cỡ trang và hai dòng đếm\n * (`range`, `pageStatus`) KHÔNG có prop nào chạm tới được, nên một bảng dùng `DataTable`\n * (đã dịch) kèm `TablePagination` (chưa) sẽ ra một cái bảng dịch NỬA VỜI.\n *\n * Khoá nào có số/đơn vị chèn vào thì là HÀM: nối chuỗi bằng dấu cộng ở phía app là cách chắc\n * chắn sai trật tự từ với các ngôn ngữ khác.\n */\nexport interface TablePaginationLabels {\n /** `aria-label` của vùng phân trang (`<nav>`) — prop `label` vẫn ghi đè được riêng */\n region: string;\n /**\n * Đơn vị đếm mặc định (\"items\" / \"mục\") — prop `unitLabel` vẫn ghi đè được riêng.\n *\n * Nằm trong `labels` vì đây là chuỗi theo NGÔN NGỮ: app override cả bộ `labels` mà đơn vị vẫn\n * kẹt ở ngôn ngữ mặc định thì đúng là cái giao diện nửa vời mà cơ chế này sinh ra để chặn. Còn\n * prop `unitLabel` là chuỗi theo NỘI DUNG (\"tasks\", \"contacts\") — mỗi bảng một khác, nên vẫn\n * là prop rời và vẫn thắng `labels`, đúng cặp `label`/`labels.region` ở trên.\n */\n unitLabel: string;\n /** Chữ đứng trước ô chọn cỡ trang (ẩn ở màn hình hẹp) */\n pageSizePrefix: string;\n /** `aria-label` của ô chọn cỡ trang */\n pageSizeSelect: (unitLabel: string) => string;\n /** Dòng đếm bản ghi: \"1–20 of 57 items\" */\n range: (start: number, end: number, total: number, unitLabel: string) => string;\n /** Dòng đếm trang: \"Page 2 of 5\" */\n pageStatus: (page: number, pageCount: number) => string;\n /** `sr-only` của 4 nút điều hướng */\n firstPage: string;\n previousPage: string;\n nextPage: string;\n lastPage: string;\n}\n\n/**\n * Nhãn TIẾNG ANH mặc định (từ `3.0.0`; trước đó là tiếng Việt). Bảng tiếng Việt:\n * `comitorMessagesVi.tablePagination`. Component không đọc thẳng hằng này — nó đọc bảng của\n * `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.\n */\nexport const DEFAULT_TABLE_PAGINATION_LABELS: TablePaginationLabels = { ...tablePaginationEn };\n"],"mappings":"AAaA,SAAS,yBAAyB;AAG3B,MAAM,4BAA4B,CAAC,IAAI,IAAI,GAAG;AA+C9C,MAAM,kCAAyD,EAAE,GAAG,kBAAkB;","names":[]}
|
|
@@ -10,13 +10,16 @@ interface TablePaginationProps {
|
|
|
10
10
|
pageSizeOptions?: readonly number[];
|
|
11
11
|
onPageChange: (page: number) => void;
|
|
12
12
|
onPageSizeChange: (pageSize: number) => void;
|
|
13
|
-
/** Đơn vị đếm theo nội dung bảng, vd "
|
|
13
|
+
/** Đơn vị đếm theo nội dung bảng, vd "tasks", "contacts" — mặc định `labels.unitLabel` ("items") */
|
|
14
14
|
unitLabel?: string;
|
|
15
15
|
/** Ẩn ô chọn số dòng/trang (khi cỡ trang do hệ thống quyết định) */
|
|
16
16
|
hidePageSize?: boolean;
|
|
17
17
|
/** Nhãn vùng phân trang — cần khi một trang có nhiều bảng; mặc định `labels.region` */
|
|
18
18
|
label?: string;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Ghi đè chuỗi hiển thị/a11y — không truyền thì theo namespace `tablePagination` của
|
|
21
|
+
* `ComitorLocaleProvider` (mặc định tiếng Anh). Xem `TablePaginationLabels`.
|
|
22
|
+
*/
|
|
20
23
|
labels?: Partial<TablePaginationLabels>;
|
|
21
24
|
className?: string;
|
|
22
25
|
}
|
|
@@ -24,7 +27,7 @@ interface TablePaginationProps {
|
|
|
24
27
|
* Thanh phân trang đi kèm `DataTable`.
|
|
25
28
|
*
|
|
26
29
|
* Tổng số trang tính từ `total`/`pageSize` ngay tại đây thay vì bắt app truyền vào — hai bên
|
|
27
|
-
* tự tính rất dễ lệch nhau một trang ở bản ghi cuối, và người dùng sẽ thấy nút
|
|
30
|
+
* tự tính rất dễ lệch nhau một trang ở bản ghi cuối, và người dùng sẽ thấy nút sang trang sau
|
|
28
31
|
* còn bấm được trong khi bảng đã hết dữ liệu.
|
|
29
32
|
*/
|
|
30
33
|
declare function TablePagination({ page, pageSize, total, pageSizeOptions, onPageChange, onPageSizeChange, unitLabel: unitLabelProp, hidePageSize, label, labels: labelsProp, className }: TablePaginationProps): React.JSX.Element;
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react";
|
|
4
|
+
import { useComitorLabels } from "../../i18n/locale-provider.js";
|
|
4
5
|
import { cn } from "../../lib/cn.js";
|
|
5
6
|
import { Button } from "../ui/button.js";
|
|
6
7
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select.js";
|
|
7
|
-
import {
|
|
8
|
-
DEFAULT_PAGE_SIZE_OPTIONS,
|
|
9
|
-
DEFAULT_TABLE_PAGINATION_LABELS
|
|
10
|
-
} from "./table-pagination-constants.js";
|
|
8
|
+
import { DEFAULT_PAGE_SIZE_OPTIONS } from "./table-pagination-constants.js";
|
|
11
9
|
function TablePagination({
|
|
12
10
|
page,
|
|
13
11
|
pageSize,
|
|
@@ -21,7 +19,7 @@ function TablePagination({
|
|
|
21
19
|
labels: labelsProp,
|
|
22
20
|
className
|
|
23
21
|
}) {
|
|
24
|
-
const labels =
|
|
22
|
+
const labels = useComitorLabels("tablePagination", labelsProp);
|
|
25
23
|
const unitLabel = unitLabelProp ?? labels.unitLabel;
|
|
26
24
|
const pageCount = Math.max(1, Math.ceil(total / Math.max(1, pageSize)));
|
|
27
25
|
const current = Math.min(Math.max(1, page), pageCount);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/composite/table-pagination.tsx"],"sourcesContent":["\"use client\";\n\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from \"lucide-react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"../ui/select.js\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/components/composite/table-pagination.tsx"],"sourcesContent":["\"use client\";\n\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from \"lucide-react\";\nimport { useComitorLabels } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"../ui/select.js\";\nimport { DEFAULT_PAGE_SIZE_OPTIONS, type TablePaginationLabels } from \"./table-pagination-constants.js\";\n\nexport interface TablePaginationProps {\n /** Trang hiện tại, đếm từ 1 */\n page: number;\n pageSize: number;\n /** Tổng số bản ghi (sau khi lọc) */\n total: number;\n pageSizeOptions?: readonly number[];\n onPageChange: (page: number) => void;\n onPageSizeChange: (pageSize: number) => void;\n /** Đơn vị đếm theo nội dung bảng, vd \"tasks\", \"contacts\" — mặc định `labels.unitLabel` (\"items\") */\n unitLabel?: string;\n /** Ẩn ô chọn số dòng/trang (khi cỡ trang do hệ thống quyết định) */\n hidePageSize?: boolean;\n /** Nhãn vùng phân trang — cần khi một trang có nhiều bảng; mặc định `labels.region` */\n label?: string;\n /**\n * Ghi đè chuỗi hiển thị/a11y — không truyền thì theo namespace `tablePagination` của\n * `ComitorLocaleProvider` (mặc định tiếng Anh). Xem `TablePaginationLabels`.\n */\n labels?: Partial<TablePaginationLabels>;\n className?: string;\n}\n\n/**\n * Thanh phân trang đi kèm `DataTable`.\n *\n * Tổng số trang tính từ `total`/`pageSize` ngay tại đây thay vì bắt app truyền vào — hai bên\n * tự tính rất dễ lệch nhau một trang ở bản ghi cuối, và người dùng sẽ thấy nút sang trang sau\n * còn bấm được trong khi bảng đã hết dữ liệu.\n */\nexport function TablePagination({\n page,\n pageSize,\n total,\n pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,\n onPageChange,\n onPageSizeChange,\n unitLabel: unitLabelProp,\n hidePageSize = false,\n label,\n labels: labelsProp,\n className\n}: TablePaginationProps) {\n const labels = useComitorLabels(\"tablePagination\", labelsProp);\n const unitLabel = unitLabelProp ?? labels.unitLabel;\n const pageCount = Math.max(1, Math.ceil(total / Math.max(1, pageSize)));\n const current = Math.min(Math.max(1, page), pageCount);\n const start = total === 0 ? 0 : (current - 1) * pageSize + 1;\n const end = Math.min(current * pageSize, total);\n const isFirst = current <= 1;\n const isLast = current >= pageCount;\n\n return (\n <nav\n aria-label={label ?? labels.region}\n data-slot=\"table-pagination\"\n className={cn(\"flex flex-wrap items-center justify-between gap-3 py-2 text-sm\", className)}\n >\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n {!hidePageSize && (\n <>\n <span className=\"hidden sm:inline\">{labels.pageSizePrefix}</span>\n <Select value={String(pageSize)} onValueChange={(value) => onPageSizeChange(Number(value))}>\n <SelectTrigger className=\"h-8 w-[4.5rem]\" aria-label={labels.pageSizeSelect(unitLabel)}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {pageSizeOptions.map((size) => (\n <SelectItem key={size} value={String(size)}>\n {size}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n )}\n <span role=\"status\" className=\"tabular-nums\">\n {labels.range(start, end, total, unitLabel)}\n </span>\n </div>\n\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"outline\"\n size=\"icon-sm\"\n disabled={isFirst}\n onClick={() => onPageChange(1)}\n className=\"hidden sm:inline-flex\"\n >\n <ChevronsLeft aria-hidden=\"true\" className=\"size-4\" />\n <span className=\"sr-only\">{labels.firstPage}</span>\n </Button>\n <Button variant=\"outline\" size=\"icon-sm\" disabled={isFirst} onClick={() => onPageChange(current - 1)}>\n <ChevronLeft aria-hidden=\"true\" className=\"size-4\" />\n <span className=\"sr-only\">{labels.previousPage}</span>\n </Button>\n\n <span role=\"status\" className=\"px-2 tabular-nums text-muted-foreground\">\n {labels.pageStatus(current, pageCount)}\n </span>\n\n <Button variant=\"outline\" size=\"icon-sm\" disabled={isLast} onClick={() => onPageChange(current + 1)}>\n <ChevronRight aria-hidden=\"true\" className=\"size-4\" />\n <span className=\"sr-only\">{labels.nextPage}</span>\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon-sm\"\n disabled={isLast}\n onClick={() => onPageChange(pageCount)}\n className=\"hidden sm:inline-flex\"\n >\n <ChevronsRight aria-hidden=\"true\" className=\"size-4\" />\n <span className=\"sr-only\">{labels.lastPage}</span>\n </Button>\n </div>\n </nav>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -38,8 +38,10 @@ declare function toast(title: ReactNode, options?: ToastOptions): string | numbe
|
|
|
38
38
|
/** Đóng một toast theo id, hoặc đóng tất cả khi không truyền id. */
|
|
39
39
|
declare function dismissToast(id?: string | number): void;
|
|
40
40
|
/**
|
|
41
|
-
* Toast bám theo một Promise: "
|
|
42
|
-
*
|
|
41
|
+
* Toast bám theo một Promise: "đang lưu…" → "đã lưu" / "lưu thất bại". `success` và `error` luôn
|
|
42
|
+
* do app truyền; `loading` bỏ trống thì lấy khoá `toast.loading` theo ngôn ngữ của `<Toaster />`
|
|
43
|
+
* (tức của `ComitorLocaleProvider` bọc nó — xem `activeToastMessages` ở đầu file). Truyền
|
|
44
|
+
* `loading` tường minh vẫn thắng.
|
|
43
45
|
*/
|
|
44
46
|
declare function toastPromise<T>(promise: Promise<T>, messages: {
|
|
45
47
|
loading?: ReactNode;
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { AlertTriangle, CheckCircle2, Info, XCircle } from "lucide-react";
|
|
4
4
|
import { Toaster as SonnerToaster, toast as sonnerToast } from "sonner";
|
|
5
|
+
import { useComitorMessages } from "../../i18n/locale-provider.js";
|
|
6
|
+
import { toastEn } from "../../i18n/namespaces/toast.js";
|
|
7
|
+
import { useIsomorphicLayoutEffect } from "../../lib/use-isomorphic-layout-effect.js";
|
|
8
|
+
let activeToastMessages = toastEn;
|
|
5
9
|
function Toaster({ theme = "system", position = "bottom-right", ...props }) {
|
|
10
|
+
const toastMessages = useComitorMessages("toast");
|
|
11
|
+
useIsomorphicLayoutEffect(() => {
|
|
12
|
+
activeToastMessages = toastMessages;
|
|
13
|
+
}, [toastMessages]);
|
|
6
14
|
return /* @__PURE__ */ jsx(
|
|
7
15
|
SonnerToaster,
|
|
8
16
|
{
|
|
@@ -59,7 +67,7 @@ function dismissToast(id) {
|
|
|
59
67
|
}
|
|
60
68
|
function toastPromise(promise, messages) {
|
|
61
69
|
return sonnerToast.promise(promise, {
|
|
62
|
-
loading: messages.loading ??
|
|
70
|
+
loading: messages.loading ?? activeToastMessages.loading,
|
|
63
71
|
success: messages.success,
|
|
64
72
|
error: messages.error
|
|
65
73
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/composite/toast.tsx"],"sourcesContent":["\"use client\";\n\nimport { AlertTriangle, CheckCircle2, Info, XCircle } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Toaster as SonnerToaster, toast as sonnerToast } from \"sonner\";\n\nexport type ToasterProps = ComponentProps<typeof SonnerToaster>;\n\nexport type ToastVariant = \"default\" | \"success\" | \"warning\" | \"destructive\" | \"info\";\n\nexport interface ToastOptions {\n description?: ReactNode;\n variant?: ToastVariant;\n /** ms; mặc định theo sonner (4000). `Number.POSITIVE_INFINITY` = giữ đến khi người dùng đóng. */\n duration?: number;\n /** Nút hành động, vd { label: \"Hoàn tác\", onClick } */\n action?: { label: string; onClick: () => void };\n /** id để cập nhật/đóng đúng toast đó (dùng với luồng \"đang lưu…\" → \"đã lưu\") */\n id?: string | number;\n}\n\n/**\n * Vùng hiển thị toast — đặt MỘT LẦN ở gốc app.\n *\n * `theme` mặc định \"system\" và KHÔNG đọc `next-themes`: entry \".\" của package phải chạy được ở\n * app không có next-themes. App dùng next-themes hãy truyền xuống, nếu không toast sẽ theo\n * `prefers-color-scheme` của hệ điều hành và lệch với theme người dùng tự chọn:\n *\n * const { resolvedTheme } = useTheme();\n * <Toaster theme={resolvedTheme as \"light\" | \"dark\"} />\n */\nexport function Toaster({ theme = \"system\", position = \"bottom-right\", ...props }: ToasterProps) {\n return (\n <SonnerToaster\n theme={theme}\n position={position}\n className=\"toaster group\"\n // Icon tô bằng bậc `--x-ink`, KHÔNG phải token tô nền `--x`. Icon là thành phần phi văn\n // bản (ngưỡng 3:1) đứng trên nền `--popover`: `text-warning` (= `--yellow` #F2C94C) trên\n // nền trắng chỉ **1,59:1** ✗ — gần như vô hình ở light (dark thì 11,60:1, lại một chỗ mỗi\n // theme hỏng một nửa); `text-success` 3,13:1 và `text-info` 4,14:1 tuy qua ngưỡng nhưng\n // sát mép. Bậc ink (light / dark): success 6,52 / 10,19 · warning 6,51 / 11,60 ·\n // destructive 7,15 / 6,42 · info 6,68 / 9,54 ✓.\n icons={{\n success: <CheckCircle2 className=\"size-4 text-success-ink\" />,\n warning: <AlertTriangle className=\"size-4 text-warning-ink\" />,\n error: <XCircle className=\"size-4 text-destructive-ink\" />,\n info: <Info className=\"size-4 text-info-ink\" />\n }}\n toastOptions={{\n classNames: {\n toast:\n \"group toast group-[.toaster]:bg-popover group-[.toaster]:text-popover-foreground group-[.toaster]:border-border group-[.toaster]:rounded-lg group-[.toaster]:shadow-card\",\n title: \"group-[.toast]:text-sm group-[.toast]:font-medium\",\n description: \"group-[.toast]:text-sm group-[.toast]:text-muted-foreground\",\n // Hai nút này là CONTROL đứng trên nền `--popover`, nên ngoài tương phản CHỮ còn phải\n // nhận ra được RANH GIỚI (WCAG 1.4.11, ngưỡng 3:1):\n // actionButton chữ 10,19:1 (L) · 12,67:1 (D) ✓, nhưng ruột `bg-primary` (gold) chỉ\n // lệch nền popover 1,86:1 ở light. Giữ đúng cách variant `default` của\n // `Button` xử lý — viền trong suốt, để `shadow-control` làm tín hiệu\n // độ cao; đổi khác ở đây sẽ lệch với nút của chính gói.\n // cancelButton chữ 4,89:1 (L) · 4,86:1 (D) ✓, nhưng ruột `bg-muted` chỉ lệch nền\n // popover 1,07:1 (L) · 1,10:1 (D) → nút không có biên nào, đọc như chữ\n // trơn. `border-control-edge`: 3,83:1 (L) · 4,64:1 (D) so với popover,\n // 3,58:1 · 4,21:1 so với chính ruột `bg-muted` ✓ (hợp đồng §3).\n actionButton:\n \"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground group-[.toast]:shadow-control\",\n cancelButton:\n \"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground group-[.toast]:border group-[.toast]:border-control-edge\"\n }\n }}\n {...props}\n />\n );\n}\n\n/**\n * Bắn toast theo biến thể của Comitor.\n *\n * Vì sao bọc `sonner` thay vì để app gọi thẳng: tên biến thể ở đây khớp với ngôn ngữ của design\n * system (`destructive`, không phải `error`), và khi cần đổi thư viện toast thì chỉ sửa một file.\n */\nexport function toast(title: ReactNode, options: ToastOptions = {}): string | number {\n const { variant = \"default\", description, duration, action, id } = options;\n const config = { description, duration, action, id };\n\n switch (variant) {\n case \"success\":\n return sonnerToast.success(title, config);\n case \"warning\":\n return sonnerToast.warning(title, config);\n case \"destructive\":\n return sonnerToast.error(title, config);\n case \"info\":\n return sonnerToast.info(title, config);\n default:\n return sonnerToast(title, config);\n }\n}\n\n/** Đóng một toast theo id, hoặc đóng tất cả khi không truyền id. */\nexport function dismissToast(id?: string | number): void {\n sonnerToast.dismiss(id);\n}\n\n/**\n * Toast bám theo một Promise: \"
|
|
1
|
+
{"version":3,"sources":["../../../src/components/composite/toast.tsx"],"sourcesContent":["\"use client\";\n\nimport { AlertTriangle, CheckCircle2, Info, XCircle } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { Toaster as SonnerToaster, toast as sonnerToast } from \"sonner\";\nimport { useComitorMessages } from \"../../i18n/locale-provider.js\";\nimport { toastEn } from \"../../i18n/namespaces/toast.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\n\nexport type ToasterProps = ComponentProps<typeof SonnerToaster>;\n\n/*\n * Bảng chữ `toast` mà `toastPromise` dùng khi app không truyền `loading`.\n *\n * `toastPromise` là hàm THƯỜNG gọi từ event handler — ngoài lượt render, không gọi được hook, nên\n * không đọc được `ComitorLocaleProvider`. Nhưng `<Toaster />` thì LUÔN nằm trong cây React (đặt một\n * lần ở gốc, bên trong provider): nó ghi bảng chữ hiện hành vào đây mỗi khi ngôn ngữ đổi, và\n * `toastPromise` đọc từ đây. Không có `<Toaster />` thì cũng không có toast nào hiện ra, nên thiếu\n * nó thì chữ rơi về tiếng Anh cũng không ai thấy.\n */\nlet activeToastMessages = toastEn;\n\nexport type ToastVariant = \"default\" | \"success\" | \"warning\" | \"destructive\" | \"info\";\n\nexport interface ToastOptions {\n description?: ReactNode;\n variant?: ToastVariant;\n /** ms; mặc định theo sonner (4000). `Number.POSITIVE_INFINITY` = giữ đến khi người dùng đóng. */\n duration?: number;\n /** Nút hành động, vd { label: \"Hoàn tác\", onClick } */\n action?: { label: string; onClick: () => void };\n /** id để cập nhật/đóng đúng toast đó (dùng với luồng \"đang lưu…\" → \"đã lưu\") */\n id?: string | number;\n}\n\n/**\n * Vùng hiển thị toast — đặt MỘT LẦN ở gốc app.\n *\n * `theme` mặc định \"system\" và KHÔNG đọc `next-themes`: entry \".\" của package phải chạy được ở\n * app không có next-themes. App dùng next-themes hãy truyền xuống, nếu không toast sẽ theo\n * `prefers-color-scheme` của hệ điều hành và lệch với theme người dùng tự chọn:\n *\n * const { resolvedTheme } = useTheme();\n * <Toaster theme={resolvedTheme as \"light\" | \"dark\"} />\n */\nexport function Toaster({ theme = \"system\", position = \"bottom-right\", ...props }: ToasterProps) {\n const toastMessages = useComitorMessages(\"toast\");\n useIsomorphicLayoutEffect(() => {\n activeToastMessages = toastMessages;\n }, [toastMessages]);\n\n return (\n <SonnerToaster\n theme={theme}\n position={position}\n className=\"toaster group\"\n // Icon tô bằng bậc `--x-ink`, KHÔNG phải token tô nền `--x`. Icon là thành phần phi văn\n // bản (ngưỡng 3:1) đứng trên nền `--popover`: `text-warning` (= `--yellow` #F2C94C) trên\n // nền trắng chỉ **1,59:1** ✗ — gần như vô hình ở light (dark thì 11,60:1, lại một chỗ mỗi\n // theme hỏng một nửa); `text-success` 3,13:1 và `text-info` 4,14:1 tuy qua ngưỡng nhưng\n // sát mép. Bậc ink (light / dark): success 6,52 / 10,19 · warning 6,51 / 11,60 ·\n // destructive 7,15 / 6,42 · info 6,68 / 9,54 ✓.\n icons={{\n success: <CheckCircle2 className=\"size-4 text-success-ink\" />,\n warning: <AlertTriangle className=\"size-4 text-warning-ink\" />,\n error: <XCircle className=\"size-4 text-destructive-ink\" />,\n info: <Info className=\"size-4 text-info-ink\" />\n }}\n toastOptions={{\n classNames: {\n toast:\n \"group toast group-[.toaster]:bg-popover group-[.toaster]:text-popover-foreground group-[.toaster]:border-border group-[.toaster]:rounded-lg group-[.toaster]:shadow-card\",\n title: \"group-[.toast]:text-sm group-[.toast]:font-medium\",\n description: \"group-[.toast]:text-sm group-[.toast]:text-muted-foreground\",\n // Hai nút này là CONTROL đứng trên nền `--popover`, nên ngoài tương phản CHỮ còn phải\n // nhận ra được RANH GIỚI (WCAG 1.4.11, ngưỡng 3:1):\n // actionButton chữ 10,19:1 (L) · 12,67:1 (D) ✓, nhưng ruột `bg-primary` (gold) chỉ\n // lệch nền popover 1,86:1 ở light. Giữ đúng cách variant `default` của\n // `Button` xử lý — viền trong suốt, để `shadow-control` làm tín hiệu\n // độ cao; đổi khác ở đây sẽ lệch với nút của chính gói.\n // cancelButton chữ 4,89:1 (L) · 4,86:1 (D) ✓, nhưng ruột `bg-muted` chỉ lệch nền\n // popover 1,07:1 (L) · 1,10:1 (D) → nút không có biên nào, đọc như chữ\n // trơn. `border-control-edge`: 3,83:1 (L) · 4,64:1 (D) so với popover,\n // 3,58:1 · 4,21:1 so với chính ruột `bg-muted` ✓ (hợp đồng §3).\n actionButton:\n \"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground group-[.toast]:shadow-control\",\n cancelButton:\n \"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground group-[.toast]:border group-[.toast]:border-control-edge\"\n }\n }}\n {...props}\n />\n );\n}\n\n/**\n * Bắn toast theo biến thể của Comitor.\n *\n * Vì sao bọc `sonner` thay vì để app gọi thẳng: tên biến thể ở đây khớp với ngôn ngữ của design\n * system (`destructive`, không phải `error`), và khi cần đổi thư viện toast thì chỉ sửa một file.\n */\nexport function toast(title: ReactNode, options: ToastOptions = {}): string | number {\n const { variant = \"default\", description, duration, action, id } = options;\n const config = { description, duration, action, id };\n\n switch (variant) {\n case \"success\":\n return sonnerToast.success(title, config);\n case \"warning\":\n return sonnerToast.warning(title, config);\n case \"destructive\":\n return sonnerToast.error(title, config);\n case \"info\":\n return sonnerToast.info(title, config);\n default:\n return sonnerToast(title, config);\n }\n}\n\n/** Đóng một toast theo id, hoặc đóng tất cả khi không truyền id. */\nexport function dismissToast(id?: string | number): void {\n sonnerToast.dismiss(id);\n}\n\n/**\n * Toast bám theo một Promise: \"đang lưu…\" → \"đã lưu\" / \"lưu thất bại\". `success` và `error` luôn\n * do app truyền; `loading` bỏ trống thì lấy khoá `toast.loading` theo ngôn ngữ của `<Toaster />`\n * (tức của `ComitorLocaleProvider` bọc nó — xem `activeToastMessages` ở đầu file). Truyền\n * `loading` tường minh vẫn thắng.\n */\nexport function toastPromise<T>(\n promise: Promise<T>,\n messages: {\n loading?: ReactNode;\n success: ReactNode | ((data: T) => ReactNode);\n error: ReactNode | ((error: unknown) => ReactNode);\n }\n) {\n return sonnerToast.promise(promise, {\n loading: messages.loading ?? activeToastMessages.loading,\n success: messages.success,\n error: messages.error\n });\n}\n"],"mappings":";AA+DiB;AA7DjB,SAAS,eAAe,cAAc,MAAM,eAAe;AAE3D,SAAS,WAAW,eAAe,SAAS,mBAAmB;AAC/D,SAAS,0BAA0B;AACnC,SAAS,eAAe;AACxB,SAAS,iCAAiC;AAa1C,IAAI,sBAAsB;AAyBnB,SAAS,QAAQ,EAAE,QAAQ,UAAU,WAAW,gBAAgB,GAAG,MAAM,GAAiB;AAC/F,QAAM,gBAAgB,mBAAmB,OAAO;AAChD,4BAA0B,MAAM;AAC9B,0BAAsB;AAAA,EACxB,GAAG,CAAC,aAAa,CAAC;AAElB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,WAAU;AAAA,MAOV,OAAO;AAAA,QACL,SAAS,oBAAC,gBAAa,WAAU,2BAA0B;AAAA,QAC3D,SAAS,oBAAC,iBAAc,WAAU,2BAA0B;AAAA,QAC5D,OAAO,oBAAC,WAAQ,WAAU,+BAA8B;AAAA,QACxD,MAAM,oBAAC,QAAK,WAAU,wBAAuB;AAAA,MAC/C;AAAA,MACA,cAAc;AAAA,QACZ,YAAY;AAAA,UACV,OACE;AAAA,UACF,OAAO;AAAA,UACP,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAWb,cACE;AAAA,UACF,cACE;AAAA,QACJ;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAQO,SAAS,MAAM,OAAkB,UAAwB,CAAC,GAAoB;AACnF,QAAM,EAAE,UAAU,WAAW,aAAa,UAAU,QAAQ,GAAG,IAAI;AACnE,QAAM,SAAS,EAAE,aAAa,UAAU,QAAQ,GAAG;AAEnD,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO,YAAY,QAAQ,OAAO,MAAM;AAAA,IAC1C,KAAK;AACH,aAAO,YAAY,QAAQ,OAAO,MAAM;AAAA,IAC1C,KAAK;AACH,aAAO,YAAY,MAAM,OAAO,MAAM;AAAA,IACxC,KAAK;AACH,aAAO,YAAY,KAAK,OAAO,MAAM;AAAA,IACvC;AACE,aAAO,YAAY,OAAO,MAAM;AAAA,EACpC;AACF;AAGO,SAAS,aAAa,IAA4B;AACvD,cAAY,QAAQ,EAAE;AACxB;AAQO,SAAS,aACd,SACA,UAKA;AACA,SAAO,YAAY,QAAQ,SAAS;AAAA,IAClC,SAAS,SAAS,WAAW,oBAAoB;AAAA,IACjD,SAAS,SAAS;AAAA,IAClB,OAAO,SAAS;AAAA,EAClB,CAAC;AACH;","names":[]}
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
* được `export` lại (re-export qua file client là vô hiệu, và đụng tên với `export *`).
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
|
-
* Toàn bộ chuỗi hiển thị và chuỗi a11y của `ActionBar
|
|
13
|
-
*
|
|
12
|
+
* Toàn bộ chuỗi hiển thị và chuỗi a11y của `ActionBar`. Theo `ComitorLocaleProvider` (mặc định
|
|
13
|
+
* tiếng Anh, bảng tiếng Việt dựng sẵn); truyền một phần qua prop `labels` của `ActionBar` để đè.
|
|
14
14
|
*
|
|
15
15
|
* Khoá có số chèn vào là HÀM: nối chuỗi ở phía app là cách chắc chắn sai trật tự từ ở ngôn ngữ khác.
|
|
16
16
|
*/
|
|
17
17
|
interface ActionBarLabels {
|
|
18
18
|
/** `aria-label` của thanh (`role="toolbar"`) — prop `aria-label` của `ActionBar` vẫn thắng. */
|
|
19
19
|
toolbar: string;
|
|
20
|
-
/** Dòng đếm `ActionBarSelection` hiện khi có prop `count
|
|
20
|
+
/** Dòng đếm `ActionBarSelection` hiện khi có prop `count` ("3 selected" / "Đã chọn 3"). */
|
|
21
21
|
selected: (count: number) => string;
|
|
22
22
|
/**
|
|
23
23
|
* Tên cho trình đọc màn hình của `ActionBarClose` — nút chỉ có icon X. Đóng thanh ở khuôn dùng
|
|
@@ -25,7 +25,11 @@ interface ActionBarLabels {
|
|
|
25
25
|
*/
|
|
26
26
|
close: string;
|
|
27
27
|
}
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Nhãn TIẾNG ANH mặc định (từ `3.0.0`; trước đó là tiếng Việt). Bảng tiếng Việt:
|
|
30
|
+
* `comitorMessagesVi.actionBar`. Component không đọc thẳng hằng này — nó đọc bảng của
|
|
31
|
+
* `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.
|
|
32
|
+
*/
|
|
29
33
|
declare const DEFAULT_ACTION_BAR_LABELS: ActionBarLabels;
|
|
30
34
|
|
|
31
35
|
export { type ActionBarLabels, DEFAULT_ACTION_BAR_LABELS };
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
selected: (count) => `\u0110\xE3 ch\u1ECDn ${count}`,
|
|
4
|
-
close: "B\u1ECF ch\u1ECDn"
|
|
5
|
-
};
|
|
1
|
+
import { actionBarEn } from "../../i18n/namespaces/action-bar.js";
|
|
2
|
+
const DEFAULT_ACTION_BAR_LABELS = { ...actionBarEn };
|
|
6
3
|
export {
|
|
7
4
|
DEFAULT_ACTION_BAR_LABELS
|
|
8
5
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/action-bar-constants.ts"],"sourcesContent":["/**\n * Nhãn mặc định của `ActionBar`.\n *\n * Module **CỐ Ý không có `\"use client\"`** — xem `src/chart/chart-colors.ts` để biết lý do đầy đủ:\n * mọi export của một file `\"use client\"` đến tay Server Component dưới dạng client-reference\n * proxy, đọc ra `undefined` mà KHÔNG throw, KHÔNG warning.\n *\n * ⚠ Barrel `src/index.ts` xuất module này TRỰC TIẾP. `action-bar.tsx` chỉ được `import`, KHÔNG\n * được `export` lại (re-export qua file client là vô hiệu, và đụng tên với `export *`).\n */\n\n/**\n * Toàn bộ chuỗi hiển thị và chuỗi a11y của `ActionBar`
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/action-bar-constants.ts"],"sourcesContent":["/**\n * Nhãn mặc định của `ActionBar`.\n *\n * Module **CỐ Ý không có `\"use client\"`** — xem `src/chart/chart-colors.ts` để biết lý do đầy đủ:\n * mọi export của một file `\"use client\"` đến tay Server Component dưới dạng client-reference\n * proxy, đọc ra `undefined` mà KHÔNG throw, KHÔNG warning.\n *\n * ⚠ Barrel `src/index.ts` xuất module này TRỰC TIẾP. `action-bar.tsx` chỉ được `import`, KHÔNG\n * được `export` lại (re-export qua file client là vô hiệu, và đụng tên với `export *`).\n */\n\nimport { actionBarEn } from \"../../i18n/namespaces/action-bar.js\";\n\n/**\n * Toàn bộ chuỗi hiển thị và chuỗi a11y của `ActionBar`. Theo `ComitorLocaleProvider` (mặc định\n * tiếng Anh, bảng tiếng Việt dựng sẵn); truyền một phần qua prop `labels` của `ActionBar` để đè.\n *\n * Khoá có số chèn vào là HÀM: nối chuỗi ở phía app là cách chắc chắn sai trật tự từ ở ngôn ngữ khác.\n */\nexport interface ActionBarLabels {\n /** `aria-label` của thanh (`role=\"toolbar\"`) — prop `aria-label` của `ActionBar` vẫn thắng. */\n toolbar: string;\n /** Dòng đếm `ActionBarSelection` hiện khi có prop `count` (\"3 selected\" / \"Đã chọn 3\"). */\n selected: (count: number) => string;\n /**\n * Tên cho trình đọc màn hình của `ActionBarClose` — nút chỉ có icon X. Đóng thanh ở khuôn dùng\n * chuẩn là BỎ CHỌN (app xoá vùng chọn trong `onOpenChange(false)`), nên tên nói đúng việc đó.\n */\n close: string;\n}\n\n/**\n * Nhãn TIẾNG ANH mặc định (từ `3.0.0`; trước đó là tiếng Việt). Bảng tiếng Việt:\n * `comitorMessagesVi.actionBar`. Component không đọc thẳng hằng này — nó đọc bảng của\n * `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.\n */\nexport const DEFAULT_ACTION_BAR_LABELS: ActionBarLabels = { ...actionBarEn };\n"],"mappings":"AAWA,SAAS,mBAAmB;AAyBrB,MAAM,4BAA6C,EAAE,GAAG,YAAY;","names":[]}
|