@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/banner.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/banner) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n *\n * Dùng ở Comitor: thông báo TOÀN WORKSPACE ở đỉnh khung app (sắp hết hạn dùng thử, bảo trì, lỗi\n * thanh toán) — đóng được, khác `Alert` là khối tĩnh nằm trong nội dung trang.\n */\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { useLazyRef } from \"../../lib/use-lazy-ref.js\";\nimport { Button } from \"./button.js\";\n\nconst BANNER_ANIMATION_DURATION = 400;\nconst DEFAULT_BANNER_PRIORITY = 0;\nconst DEFAULT_BANNER_DISMISSIBLE = true;\n\ntype BannerVariant = \"default\" | \"info\" | \"success\" | \"warning\" | \"destructive\";\ntype BannerSide = \"top\" | \"bottom\";\ntype BannerStrategy = \"fixed\" | \"static\" | \"sticky\" | \"absolute\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype CloseElement = React.ComponentRef<typeof BannerClose>;\n\ninterface BannerRenderProps {\n id: string;\n variant?: BannerVariant;\n dismissible: boolean;\n onClose: () => void;\n onRemove: () => void;\n}\n\n// Thượng nguồn đặt tên kiểu này là `BannerContent` — trùng tên component `BannerContent`. Đổi tên\n// để `export { BannerContent }` không mang theo một KIỂU cùng tên mà nghĩa khác hẳn.\ntype BannerContentValue = React.ReactNode | ((props: BannerRenderProps) => React.ReactNode);\n\ninterface BannerData {\n id: string;\n content: BannerContentValue;\n variant?: BannerVariant;\n priority?: number;\n duration?: number;\n dismissible?: boolean;\n onDismiss?: () => void;\n}\n\ninterface StoreState {\n banners: BannerData[];\n removing: Set<string>;\n heights: Map<string, number>;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState;\n notify: () => void;\n onBannerAdd: (banner: Omit<BannerData, \"id\">) => string;\n onBannerRemove: (id: string) => void;\n onBannersClear: () => void;\n onRemovingChange: (id: string, value: boolean) => void;\n onHeightChange: (id: string, height: number) => void;\n onHeightRemove: (id: string) => void;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`Banners\\``);\n }\n return context;\n}\n\nfunction useStore<T>(store: Store, selector: (state: StoreState) => T): T {\n return React.useSyncExternalStore(\n store.subscribe,\n () => selector(store.getState()),\n () => selector(store.getState())\n );\n}\n\ninterface BannerContextValue {\n id?: string;\n variant?: BannerVariant | null;\n dismissible?: boolean;\n onClose?: () => void;\n}\n\nconst BannerContext = React.createContext<BannerContextValue | null>(null);\n\nfunction useBannerContext(consumerName: string) {\n const context = React.useContext(BannerContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`Banner\\``);\n }\n return context;\n}\n\nfunction useBanner() {\n const { id, variant, dismissible, onClose } = useBannerContext(\"useBanner\");\n const storeContext = React.useContext(StoreContext);\n\n return React.useMemo(() => {\n const onRemove = id && storeContext ? () => storeContext.onBannerRemove(id) : undefined;\n\n return {\n id,\n variant,\n dismissible,\n onClose,\n onRemove\n };\n }, [id, variant, dismissible, onClose, storeContext]);\n}\n\ninterface BannersProps {\n children?: React.ReactNode;\n maxVisible?: number;\n side?: BannerSide;\n strategy?: BannerStrategy;\n container?: Element | DocumentFragment | null;\n}\n\nfunction Banners(props: BannersProps) {\n const { children, maxVisible = 1, side = \"top\", strategy = \"fixed\", container: containerProp } = props;\n\n const instanceId = React.useId();\n const stateRef = useLazyRef<StoreState>(() => ({\n banners: [],\n removing: new Set(),\n heights: new Map()\n }));\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const timeoutsRef = useLazyRef<Map<string, ReturnType<typeof setTimeout>>>(() => new Map());\n const idCounterRef = React.useRef(0);\n\n const store: Store = React.useMemo(\n () => ({\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => stateRef.current,\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n },\n onBannerAdd: (banner) => {\n /*\n * ⚠ Khác thượng nguồn: bản gốc `crypto.randomUUID()` — hàm này CHỈ có trong secure\n * context (https, localhost). Mở app qua http bằng IP mạng nội bộ (máy dev, môi trường\n * nội bộ) thì nó là `undefined` và mọi lần thêm banner đều throw. Id chỉ cần duy nhất\n * trong một `Banners` → bộ đếm, tiền tố `useId()` cho chắc khi có nhiều `Banners`.\n */\n idCounterRef.current += 1;\n const id = `${instanceId}-${idCounterRef.current}`;\n const newBanner: BannerData = { ...banner, id };\n const priority = banner.priority ?? DEFAULT_BANNER_PRIORITY;\n\n const banners = [...stateRef.current.banners];\n const insertIndex = banners.findIndex((b) => (b.priority ?? DEFAULT_BANNER_PRIORITY) < priority);\n\n if (insertIndex === -1) {\n banners.push(newBanner);\n } else {\n banners.splice(insertIndex, 0, newBanner);\n }\n\n stateRef.current.banners = banners;\n store.notify();\n\n if (banner.duration && banner.duration > 0) {\n const timeoutId = setTimeout(() => {\n store.onRemovingChange(id, true);\n timeoutsRef.current.delete(id);\n }, banner.duration);\n timeoutsRef.current.set(id, timeoutId);\n }\n\n return id;\n },\n onBannerRemove: (id) => {\n const banner = stateRef.current.banners.find((b) => b.id === id);\n if (!banner) return;\n\n const timeoutId = timeoutsRef.current.get(id);\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutsRef.current.delete(id);\n }\n\n const newRemoving = new Set(stateRef.current.removing);\n newRemoving.delete(id);\n stateRef.current.removing = newRemoving;\n\n banner.onDismiss?.();\n stateRef.current.banners = stateRef.current.banners.filter((b) => b.id !== id);\n store.notify();\n },\n onBannersClear: () => {\n for (const timeoutId of timeoutsRef.current.values()) {\n clearTimeout(timeoutId);\n }\n timeoutsRef.current.clear();\n stateRef.current.removing = new Set();\n stateRef.current.heights = new Map();\n stateRef.current.banners = [];\n store.notify();\n },\n onRemovingChange: (id, value) => {\n const newSet = new Set(stateRef.current.removing);\n if (value) {\n newSet.add(id);\n } else {\n newSet.delete(id);\n }\n stateRef.current.removing = newSet;\n store.notify();\n },\n onHeightChange: (id, height) => {\n if (stateRef.current.heights.get(id) === height) return;\n const newHeights = new Map(stateRef.current.heights);\n newHeights.set(id, height);\n stateRef.current.heights = newHeights;\n store.notify();\n },\n onHeightRemove: (id) => {\n if (!stateRef.current.heights.has(id)) return;\n const newHeights = new Map(stateRef.current.heights);\n newHeights.delete(id);\n stateRef.current.heights = newHeights;\n store.notify();\n }\n }),\n [instanceId, stateRef, listenersRef, timeoutsRef]\n );\n\n const banners = useStore(store, (state) => state.banners);\n const heights = useStore(store, (state) => state.heights);\n const visibleBanners = banners.slice(0, maxVisible);\n\n /*\n * ⚠ Khác thượng nguồn: bản gốc đọc `globalThis.document?.body` NGAY TRONG render. Ở đây chỉ đọc\n * sau khi mount (cùng khuôn với `ActionBar`) — lượt render phía server và lượt hydrate không\n * chạm `document`.\n */\n const [mounted, setMounted] = React.useState(false);\n useIsomorphicLayoutEffect(() => {\n setMounted(true);\n }, []);\n\n const withPortal = strategy === \"fixed\" || strategy === \"absolute\";\n const container = withPortal ? (containerProp ?? (mounted ? globalThis.document?.body : null) ?? null) : null;\n\n const totalHeight = React.useMemo(() => {\n let total = 0;\n for (const banner of visibleBanners) {\n total += heights.get(banner.id) ?? 0;\n }\n return total;\n }, [visibleBanners, heights]);\n\n const bannerContainer = visibleBanners.length > 0 && (\n <div\n data-slot=\"banner-container\"\n data-side={side}\n data-strategy={strategy}\n className={cn(\n \"pointer-events-none right-0 left-0 isolate z-50\",\n /*\n * ⚠ Khác thượng nguồn: bản gốc ghi `transition` thẳng vào `style`, nên lớp\n * `motion-reduce:transition-none` của từng banner KHÔNG BAO GIỜ thắng được (style nội tuyến\n * luôn thắng lớp) — người bật \"giảm chuyển động\" vẫn thấy banner trượt. Nay thời lượng đi\n * qua `--banner-duration`, bằng 0 khi `prefers-reduced-motion`; banner con thừa kế biến này.\n */\n \"motion-reduce:[--banner-duration:0ms]\",\n strategy === \"fixed\" && \"fixed\",\n strategy === \"static\" && \"relative\",\n strategy === \"sticky\" && \"sticky\",\n strategy === \"absolute\" && \"absolute\",\n side === \"top\" ? \"top-0\" : \"bottom-0\"\n )}\n style={{\n height: totalHeight > 0 ? totalHeight : \"auto\",\n transition: `height var(--banner-duration, ${BANNER_ANIMATION_DURATION}ms) cubic-bezier(0.32, 0.72, 0, 1)`\n }}\n >\n {visibleBanners.map((banner, index) => (\n <BannerImpl key={banner.id} banner={banner} side={side} index={index} />\n ))}\n </div>\n );\n\n return (\n <StoreContext.Provider value={store}>\n {strategy === \"static\" || strategy === \"sticky\" ? (\n <>\n {side === \"top\" && bannerContainer}\n {children}\n {side === \"bottom\" && bannerContainer}\n </>\n ) : (\n <>\n {children}\n {container && bannerContainer && ReactDOM.createPortal(bannerContainer, container)}\n </>\n )}\n </StoreContext.Provider>\n );\n}\n\nfunction useBanners() {\n const store = useStoreContext(\"useBanners\");\n const banners = useStore(store, (state) => state.banners);\n\n return React.useMemo(\n () => ({\n onBannerAdd: store.onBannerAdd,\n onBannerRemove: store.onBannerRemove,\n onBannersClear: store.onBannersClear,\n banners\n }),\n [store, banners]\n );\n}\n\n/*\n * ⚠ Khác thượng nguồn: bản gốc tô bằng bảng màu mặc định của Tailwind (`bg-blue-50\n * text-blue-900`, `bg-red-50`… kèm `dark:`) — đi vòng qua hợp đồng token nên không đổi theo bảng\n * tương phản cao. Ở đây dùng ĐÚNG bộ lớp của `Alert` (alert.tsx) để Banner và Alert cùng tông\n * trông như một họ: nền `bg-card`, chữ + icon ở bậc `-ink` của tone. `Alert` chưa có `info` /\n * `success`; hai tông đó theo cùng khuôn với `warning` / `destructive` của nó.\n *\n * Nền ĐẶC `bg-card` chứ không tint `bg-<tone>/15`: ở chế độ `fixed`/`absolute`, banner nằm ĐÈ lên\n * nội dung trang — một nền trong suốt một phần sẽ cho chữ trang lộ xuyên qua chữ banner.\n *\n * Mô tả (`BannerDescription`) giữ `opacity-90` của bản gốc — trên nền đặc nó tương đương đúng\n * `text-<tone>-ink/90` của `AlertDescription`. Cổng không đo được `opacity`, nên số đo tay (trên\n * `--card`, tiêu đề / mô tả 90%):\n *\n * bảng tương phản cao light dark\n * info 6,68 / 5,31 9,54 / 7,92 ✓\n * success 6,52 / 5,20 10,19 / 8,44 ✓\n * warning 6,51 / 5,17 11,60 / 9,56 ✓\n * destructive 7,15 / 5,77 6,42 / 5,41 ✓\n * bảng mặc định (nợ token đã chấp nhận, cùng mức với `Alert`):\n * destructive 4,38 / 3,81 (light) · 4,06 / 3,50 (dark) · info dark 4,28 / 3,71\n */\nconst bannerVariants = cva(\n \"pointer-events-auto relative flex w-full items-center gap-3 border-b px-4 py-3 text-sm motion-reduce:transition-none\",\n {\n variants: {\n variant: {\n default: \"bg-card text-card-foreground\",\n info: \"bg-card text-info-ink\",\n success: \"bg-card text-success-ink\",\n warning: \"bg-card text-warning-ink\",\n destructive: \"bg-card text-destructive-ink\"\n }\n },\n defaultVariants: {\n variant: \"default\"\n }\n }\n);\n\ninterface BannerImplProps {\n banner: BannerData;\n side: BannerSide;\n index: number;\n}\n\nfunction BannerImpl(props: BannerImplProps) {\n const { banner, side, index } = props;\n\n const store = useStoreContext(\"BannerImpl\");\n const removing = useStore(store, (state) => state.removing.has(banner.id));\n const banners = useStore(store, (state) => state.banners);\n const heights = useStore(store, (state) => state.heights);\n\n const [mounted, setMounted] = React.useState(false);\n const bannerRef = React.useRef<HTMLDivElement>(null);\n const offsetBeforeRemoveRef = React.useRef(0);\n\n const offset = React.useMemo(() => {\n let total = 0;\n for (const b of banners) {\n if (b.id === banner.id) break;\n total += heights.get(b.id) ?? 0;\n }\n return total;\n }, [banners, heights, banner.id]);\n\n if (!removing) {\n offsetBeforeRemoveRef.current = offset;\n }\n\n React.useEffect(() => {\n const frame = requestAnimationFrame(() => setMounted(true));\n return () => cancelAnimationFrame(frame);\n }, []);\n\n useIsomorphicLayoutEffect(() => {\n if (!bannerRef.current || removing) return;\n const height = bannerRef.current.getBoundingClientRect().height;\n store.onHeightChange(banner.id, height);\n }, [store, banner.id, removing]);\n\n React.useEffect(() => {\n if (!removing) return;\n store.onHeightRemove(banner.id);\n const timeoutId = setTimeout(() => store.onBannerRemove(banner.id), BANNER_ANIMATION_DURATION);\n return () => clearTimeout(timeoutId);\n }, [removing, store, banner.id]);\n\n const onClose = React.useCallback(() => store.onRemovingChange(banner.id, true), [store, banner.id]);\n\n const onRemove = React.useCallback(() => store.onBannerRemove(banner.id), [store, banner.id]);\n\n const dismissible = banner.dismissible ?? DEFAULT_BANNER_DISMISSIBLE;\n\n const contextValue = React.useMemo<BannerContextValue>(\n () => ({ id: banner.id, variant: banner.variant, dismissible, onClose }),\n [banner.id, banner.variant, dismissible, onClose]\n );\n\n const renderProps = React.useMemo<BannerRenderProps>(\n () => ({\n id: banner.id,\n variant: banner.variant,\n dismissible,\n onClose,\n onRemove\n }),\n [banner.id, banner.variant, dismissible, onClose, onRemove]\n );\n\n const currentOffset = removing ? offsetBeforeRemoveRef.current : offset;\n const isTop = side === \"top\";\n\n function getTransform() {\n if (!mounted) return isTop ? \"translateY(-100%)\" : \"translateY(100%)\";\n if (removing) {\n return isTop ? `translateY(calc(${currentOffset}px - 100%))` : `translateY(calc(-${currentOffset}px + 100%))`;\n }\n return isTop ? `translateY(${currentOffset}px)` : `translateY(-${currentOffset}px)`;\n }\n\n const opacityDuration = removing ? BANNER_ANIMATION_DURATION / 2 : BANNER_ANIMATION_DURATION;\n\n return (\n <BannerContext.Provider value={contextValue}>\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"queued-banner\"\n data-state={removing ? \"closed\" : \"open\"}\n data-mounted={mounted}\n data-removed={removing}\n data-side={side}\n data-front={index === 0}\n data-index={index}\n ref={bannerRef}\n className={bannerVariants({ variant: banner.variant })}\n style={{\n position: \"absolute\",\n [isTop ? \"top\" : \"bottom\"]: 0,\n left: 0,\n right: 0,\n zIndex: removing ? 0 : 50 - index,\n transform: getTransform(),\n opacity: mounted && !removing ? 1 : 0,\n // Thời lượng qua `--banner-duration` (0 khi giảm chuyển động) — xem chú thích ở `Banners`.\n transition: `transform var(--banner-duration, ${BANNER_ANIMATION_DURATION}ms) cubic-bezier(0.32, 0.72, 0, 1), opacity var(--banner-duration, ${opacityDuration}ms) ease`\n }}\n >\n {typeof banner.content === \"function\" ? banner.content(renderProps) : banner.content}\n </div>\n </BannerContext.Provider>\n );\n}\n\ninterface BannerProps extends DivProps, VariantProps<typeof bannerVariants> {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n onDismiss?: () => void;\n priority?: number;\n duration?: number;\n dismissible?: boolean;\n}\n\nfunction Banner(props: BannerProps) {\n const {\n className,\n variant = \"default\",\n open: openProp,\n defaultOpen = true,\n onOpenChange,\n onDismiss,\n priority,\n duration,\n dismissible = DEFAULT_BANNER_DISMISSIBLE,\n children,\n asChild,\n ...rootProps\n } = props;\n\n const store = React.useContext(StoreContext);\n\n const isInsideStore = store !== null;\n const isControlled = openProp !== undefined;\n\n const openRef = useLazyRef(() => openProp ?? defaultOpen);\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const bannerIdRef = React.useRef<string | null>(null);\n const onDismissRef = useAsRef(onDismiss);\n const onOpenChangeRef = useAsRef(onOpenChange);\n\n if (isControlled) {\n openRef.current = openProp;\n }\n\n const subscribe = React.useCallback(\n (cb: () => void) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n [listenersRef]\n );\n\n const getSnapshot = React.useCallback(() => openRef.current, [openRef]);\n\n const open = React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n React.useEffect(() => {\n if (!isInsideStore || !store || !open) return;\n\n /*\n * ⚠ Khác thượng nguồn — hai lỗi của `<Banner>` đặt trong `<Banners>`:\n *\n * 1. Hàm dọn của effect này gỡ banner bằng `onBannerRemove`, mà hàm đó GỌI `onDismiss`. Bản\n * gốc vì thế báo \"người dùng đã đóng\" (`onDismiss` + `onOpenChange(false)`) mỗi khi effect\n * chạy lại — tức MỖI LẦN cha re-render, vì `children` là JSX mới — và cả khi unmount (đổi\n * route). Banner tự đóng, và app lưu \"đã tắt thông báo\" cho một lần người dùng chưa hề bấm.\n * Cờ `isCleaningUp` chặn đúng đường đó; đóng thật (nút X, hết `duration`) vẫn báo như cũ.\n * 2. Ở chế độ không kiểm soát, đóng qua hàng đợi không hạ `open` của chính `Banner`, nên lần\n * re-render kế tiếp thêm LẠI banner vừa đóng. Nay hạ `open` như `onClose` bên dưới.\n */\n let isCleaningUp = false;\n\n const id = store.onBannerAdd({\n content: children,\n variant: variant ?? undefined,\n priority,\n dismissible,\n duration,\n onDismiss: () => {\n if (isCleaningUp) return;\n if (!isControlled) {\n openRef.current = false;\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n onDismissRef.current?.();\n onOpenChangeRef.current?.(false);\n }\n });\n bannerIdRef.current = id;\n\n return () => {\n isCleaningUp = true;\n if (bannerIdRef.current) {\n store.onBannerRemove(bannerIdRef.current);\n bannerIdRef.current = null;\n }\n };\n }, [\n isInsideStore,\n store,\n open,\n isControlled,\n children,\n variant,\n priority,\n dismissible,\n duration,\n openRef,\n listenersRef,\n onDismissRef,\n onOpenChangeRef\n ]);\n\n const onClose = React.useCallback(() => {\n if (!isControlled) {\n openRef.current = false;\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n onOpenChangeRef.current?.(false);\n }, [isControlled, openRef, listenersRef, onOpenChangeRef]);\n\n const contextValue = React.useMemo<BannerContextValue>(\n () => ({\n variant,\n dismissible,\n onClose\n }),\n [variant, dismissible, onClose]\n );\n\n if (!open || isInsideStore) return null;\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <BannerContext.Provider value={contextValue}>\n <RootPrimitive\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"banner\"\n data-state=\"open\"\n className={cn(bannerVariants({ variant, className }))}\n {...rootProps}\n >\n {children}\n </RootPrimitive>\n </BannerContext.Provider>\n );\n}\n\nfunction BannerIcon(props: DivProps) {\n const { className, asChild, ...iconProps } = props;\n\n const IconPrimitive = asChild ? Slot : \"div\";\n\n return (\n <IconPrimitive\n data-slot=\"banner-icon\"\n className={cn(\"flex shrink-0 items-center [&>svg]:size-4\", className)}\n {...iconProps}\n />\n );\n}\n\nfunction BannerContent(props: DivProps) {\n const { className, asChild, ...contentProps } = props;\n\n const ContentPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ContentPrimitive\n data-slot=\"banner-content\"\n className={cn(\"flex min-w-0 flex-1 flex-col gap-1\", className)}\n {...contentProps}\n />\n );\n}\n\nfunction BannerTitle(props: React.ComponentProps<\"div\">) {\n const { className, ...titleProps } = props;\n\n return <div data-slot=\"banner-title\" className={cn(\"text-sm leading-none font-medium\", className)} {...titleProps} />;\n}\n\nfunction BannerDescription(props: React.ComponentProps<\"div\">) {\n const { className, ...descriptionProps } = props;\n\n return <div data-slot=\"banner-description\" className={cn(\"text-xs opacity-90\", className)} {...descriptionProps} />;\n}\n\nfunction BannerActions(props: DivProps) {\n const { className, asChild, ...actionsProps } = props;\n\n const ActionsPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ActionsPrimitive\n data-slot=\"banner-actions\"\n className={cn(\"flex items-center gap-2\", className)}\n {...actionsProps}\n />\n );\n}\n\ntype BannerCloseProps = React.ComponentProps<typeof Button>;\n\n/**\n * Nút đóng banner. Không truyền `children` thì vẽ icon X.\n *\n * ⚠ Khác thượng nguồn: có tên cho trình đọc màn hình — `aria-label` mặc định \"Đóng thông báo\",\n * ghi đè bằng chính prop `aria-label`. Bản gốc là `Button` chỉ chứa icon X, tức một nút KHÔNG\n * TÊN. Nếu `children` là CHỮ nhìn thấy được thì truyền `aria-label` trùng với chữ đó (WCAG\n * 2.5.3), hoặc `aria-label={undefined}`.\n */\nfunction BannerClose(props: BannerCloseProps) {\n const { onClick: onClickProp, disabled, children, ...closeProps } = props;\n\n const { dismissible = DEFAULT_BANNER_DISMISSIBLE, onClose } = useBannerContext(\"BannerClose\");\n\n const isDisabled = disabled ?? !dismissible;\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<CloseElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented || isDisabled) return;\n onClose?.();\n },\n [onClickProp, isDisabled, onClose]\n );\n\n return (\n <Button\n aria-label=\"Đóng thông báo\"\n data-slot=\"banner-close\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onClick}\n disabled={isDisabled}\n {...closeProps}\n >\n {children ?? <X aria-hidden=\"true\" className=\"size-3.5\" />}\n </Button>\n );\n}\n\nexport {\n Banner,\n BannerActions,\n BannerClose,\n type BannerCloseProps,\n BannerContent,\n type BannerData,\n BannerDescription,\n BannerIcon,\n type BannerProps,\n type BannerRenderProps,\n Banners,\n type BannersProps,\n BannerTitle,\n type BannerVariant,\n useBanner,\n useBanners\n};\n"],"mappings":"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","names":["banners"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/banner.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/banner) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n *\n * Dùng ở Comitor: thông báo TOÀN WORKSPACE ở đỉnh khung app (sắp hết hạn dùng thử, bảo trì, lỗi\n * thanh toán) — đóng được, khác `Alert` là khối tĩnh nằm trong nội dung trang.\n */\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { useComitorMessages } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { useLazyRef } from \"../../lib/use-lazy-ref.js\";\nimport { Button } from \"./button.js\";\n\nconst BANNER_ANIMATION_DURATION = 400;\nconst DEFAULT_BANNER_PRIORITY = 0;\nconst DEFAULT_BANNER_DISMISSIBLE = true;\n\ntype BannerVariant = \"default\" | \"info\" | \"success\" | \"warning\" | \"destructive\";\ntype BannerSide = \"top\" | \"bottom\";\ntype BannerStrategy = \"fixed\" | \"static\" | \"sticky\" | \"absolute\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype CloseElement = React.ComponentRef<typeof BannerClose>;\n\ninterface BannerRenderProps {\n id: string;\n variant?: BannerVariant;\n dismissible: boolean;\n onClose: () => void;\n onRemove: () => void;\n}\n\n// Thượng nguồn đặt tên kiểu này là `BannerContent` — trùng tên component `BannerContent`. Đổi tên\n// để `export { BannerContent }` không mang theo một KIỂU cùng tên mà nghĩa khác hẳn.\ntype BannerContentValue = React.ReactNode | ((props: BannerRenderProps) => React.ReactNode);\n\ninterface BannerData {\n id: string;\n content: BannerContentValue;\n variant?: BannerVariant;\n priority?: number;\n duration?: number;\n dismissible?: boolean;\n onDismiss?: () => void;\n}\n\ninterface StoreState {\n banners: BannerData[];\n removing: Set<string>;\n heights: Map<string, number>;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState;\n notify: () => void;\n onBannerAdd: (banner: Omit<BannerData, \"id\">) => string;\n onBannerRemove: (id: string) => void;\n onBannersClear: () => void;\n onRemovingChange: (id: string, value: boolean) => void;\n onHeightChange: (id: string, height: number) => void;\n onHeightRemove: (id: string) => void;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`Banners\\``);\n }\n return context;\n}\n\nfunction useStore<T>(store: Store, selector: (state: StoreState) => T): T {\n return React.useSyncExternalStore(\n store.subscribe,\n () => selector(store.getState()),\n () => selector(store.getState())\n );\n}\n\ninterface BannerContextValue {\n id?: string;\n variant?: BannerVariant | null;\n dismissible?: boolean;\n onClose?: () => void;\n}\n\nconst BannerContext = React.createContext<BannerContextValue | null>(null);\n\nfunction useBannerContext(consumerName: string) {\n const context = React.useContext(BannerContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`Banner\\``);\n }\n return context;\n}\n\nfunction useBanner() {\n const { id, variant, dismissible, onClose } = useBannerContext(\"useBanner\");\n const storeContext = React.useContext(StoreContext);\n\n return React.useMemo(() => {\n const onRemove = id && storeContext ? () => storeContext.onBannerRemove(id) : undefined;\n\n return {\n id,\n variant,\n dismissible,\n onClose,\n onRemove\n };\n }, [id, variant, dismissible, onClose, storeContext]);\n}\n\ninterface BannersProps {\n children?: React.ReactNode;\n maxVisible?: number;\n side?: BannerSide;\n strategy?: BannerStrategy;\n container?: Element | DocumentFragment | null;\n}\n\nfunction Banners(props: BannersProps) {\n const { children, maxVisible = 1, side = \"top\", strategy = \"fixed\", container: containerProp } = props;\n\n const instanceId = React.useId();\n const stateRef = useLazyRef<StoreState>(() => ({\n banners: [],\n removing: new Set(),\n heights: new Map()\n }));\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const timeoutsRef = useLazyRef<Map<string, ReturnType<typeof setTimeout>>>(() => new Map());\n const idCounterRef = React.useRef(0);\n\n const store: Store = React.useMemo(\n () => ({\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => stateRef.current,\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n },\n onBannerAdd: (banner) => {\n /*\n * ⚠ Khác thượng nguồn: bản gốc `crypto.randomUUID()` — hàm này CHỈ có trong secure\n * context (https, localhost). Mở app qua http bằng IP mạng nội bộ (máy dev, môi trường\n * nội bộ) thì nó là `undefined` và mọi lần thêm banner đều throw. Id chỉ cần duy nhất\n * trong một `Banners` → bộ đếm, tiền tố `useId()` cho chắc khi có nhiều `Banners`.\n */\n idCounterRef.current += 1;\n const id = `${instanceId}-${idCounterRef.current}`;\n const newBanner: BannerData = { ...banner, id };\n const priority = banner.priority ?? DEFAULT_BANNER_PRIORITY;\n\n const banners = [...stateRef.current.banners];\n const insertIndex = banners.findIndex((b) => (b.priority ?? DEFAULT_BANNER_PRIORITY) < priority);\n\n if (insertIndex === -1) {\n banners.push(newBanner);\n } else {\n banners.splice(insertIndex, 0, newBanner);\n }\n\n stateRef.current.banners = banners;\n store.notify();\n\n if (banner.duration && banner.duration > 0) {\n const timeoutId = setTimeout(() => {\n store.onRemovingChange(id, true);\n timeoutsRef.current.delete(id);\n }, banner.duration);\n timeoutsRef.current.set(id, timeoutId);\n }\n\n return id;\n },\n onBannerRemove: (id) => {\n const banner = stateRef.current.banners.find((b) => b.id === id);\n if (!banner) return;\n\n const timeoutId = timeoutsRef.current.get(id);\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutsRef.current.delete(id);\n }\n\n const newRemoving = new Set(stateRef.current.removing);\n newRemoving.delete(id);\n stateRef.current.removing = newRemoving;\n\n banner.onDismiss?.();\n stateRef.current.banners = stateRef.current.banners.filter((b) => b.id !== id);\n store.notify();\n },\n onBannersClear: () => {\n for (const timeoutId of timeoutsRef.current.values()) {\n clearTimeout(timeoutId);\n }\n timeoutsRef.current.clear();\n stateRef.current.removing = new Set();\n stateRef.current.heights = new Map();\n stateRef.current.banners = [];\n store.notify();\n },\n onRemovingChange: (id, value) => {\n const newSet = new Set(stateRef.current.removing);\n if (value) {\n newSet.add(id);\n } else {\n newSet.delete(id);\n }\n stateRef.current.removing = newSet;\n store.notify();\n },\n onHeightChange: (id, height) => {\n if (stateRef.current.heights.get(id) === height) return;\n const newHeights = new Map(stateRef.current.heights);\n newHeights.set(id, height);\n stateRef.current.heights = newHeights;\n store.notify();\n },\n onHeightRemove: (id) => {\n if (!stateRef.current.heights.has(id)) return;\n const newHeights = new Map(stateRef.current.heights);\n newHeights.delete(id);\n stateRef.current.heights = newHeights;\n store.notify();\n }\n }),\n [instanceId, stateRef, listenersRef, timeoutsRef]\n );\n\n const banners = useStore(store, (state) => state.banners);\n const heights = useStore(store, (state) => state.heights);\n const visibleBanners = banners.slice(0, maxVisible);\n\n /*\n * ⚠ Khác thượng nguồn: bản gốc đọc `globalThis.document?.body` NGAY TRONG render. Ở đây chỉ đọc\n * sau khi mount (cùng khuôn với `ActionBar`) — lượt render phía server và lượt hydrate không\n * chạm `document`.\n */\n const [mounted, setMounted] = React.useState(false);\n useIsomorphicLayoutEffect(() => {\n setMounted(true);\n }, []);\n\n const withPortal = strategy === \"fixed\" || strategy === \"absolute\";\n const container = withPortal ? (containerProp ?? (mounted ? globalThis.document?.body : null) ?? null) : null;\n\n const totalHeight = React.useMemo(() => {\n let total = 0;\n for (const banner of visibleBanners) {\n total += heights.get(banner.id) ?? 0;\n }\n return total;\n }, [visibleBanners, heights]);\n\n const bannerContainer = visibleBanners.length > 0 && (\n <div\n data-slot=\"banner-container\"\n data-side={side}\n data-strategy={strategy}\n className={cn(\n \"pointer-events-none right-0 left-0 isolate z-50\",\n /*\n * ⚠ Khác thượng nguồn: bản gốc ghi `transition` thẳng vào `style`, nên lớp\n * `motion-reduce:transition-none` của từng banner KHÔNG BAO GIỜ thắng được (style nội tuyến\n * luôn thắng lớp) — người bật \"giảm chuyển động\" vẫn thấy banner trượt. Nay thời lượng đi\n * qua `--banner-duration`, bằng 0 khi `prefers-reduced-motion`; banner con thừa kế biến này.\n */\n \"motion-reduce:[--banner-duration:0ms]\",\n strategy === \"fixed\" && \"fixed\",\n strategy === \"static\" && \"relative\",\n strategy === \"sticky\" && \"sticky\",\n strategy === \"absolute\" && \"absolute\",\n side === \"top\" ? \"top-0\" : \"bottom-0\"\n )}\n style={{\n height: totalHeight > 0 ? totalHeight : \"auto\",\n transition: `height var(--banner-duration, ${BANNER_ANIMATION_DURATION}ms) cubic-bezier(0.32, 0.72, 0, 1)`\n }}\n >\n {visibleBanners.map((banner, index) => (\n <BannerImpl key={banner.id} banner={banner} side={side} index={index} />\n ))}\n </div>\n );\n\n return (\n <StoreContext.Provider value={store}>\n {strategy === \"static\" || strategy === \"sticky\" ? (\n <>\n {side === \"top\" && bannerContainer}\n {children}\n {side === \"bottom\" && bannerContainer}\n </>\n ) : (\n <>\n {children}\n {container && bannerContainer && ReactDOM.createPortal(bannerContainer, container)}\n </>\n )}\n </StoreContext.Provider>\n );\n}\n\nfunction useBanners() {\n const store = useStoreContext(\"useBanners\");\n const banners = useStore(store, (state) => state.banners);\n\n return React.useMemo(\n () => ({\n onBannerAdd: store.onBannerAdd,\n onBannerRemove: store.onBannerRemove,\n onBannersClear: store.onBannersClear,\n banners\n }),\n [store, banners]\n );\n}\n\n/*\n * ⚠ Khác thượng nguồn: bản gốc tô bằng bảng màu mặc định của Tailwind (`bg-blue-50\n * text-blue-900`, `bg-red-50`… kèm `dark:`) — đi vòng qua hợp đồng token nên không đổi theo bảng\n * tương phản cao. Ở đây dùng ĐÚNG bộ lớp của `Alert` (alert.tsx) để Banner và Alert cùng tông\n * trông như một họ: nền `bg-card`, chữ + icon ở bậc `-ink` của tone. `Alert` chưa có `info` /\n * `success`; hai tông đó theo cùng khuôn với `warning` / `destructive` của nó.\n *\n * Nền ĐẶC `bg-card` chứ không tint `bg-<tone>/15`: ở chế độ `fixed`/`absolute`, banner nằm ĐÈ lên\n * nội dung trang — một nền trong suốt một phần sẽ cho chữ trang lộ xuyên qua chữ banner.\n *\n * Mô tả (`BannerDescription`) giữ `opacity-90` của bản gốc — trên nền đặc nó tương đương đúng\n * `text-<tone>-ink/90` của `AlertDescription`. Cổng không đo được `opacity`, nên số đo tay (trên\n * `--card`, tiêu đề / mô tả 90%):\n *\n * bảng tương phản cao light dark\n * info 6,68 / 5,31 9,54 / 7,92 ✓\n * success 6,52 / 5,20 10,19 / 8,44 ✓\n * warning 6,51 / 5,17 11,60 / 9,56 ✓\n * destructive 7,15 / 5,77 6,42 / 5,41 ✓\n * bảng mặc định (nợ token đã chấp nhận, cùng mức với `Alert`):\n * destructive 4,38 / 3,81 (light) · 4,06 / 3,50 (dark) · info dark 4,28 / 3,71\n */\nconst bannerVariants = cva(\n \"pointer-events-auto relative flex w-full items-center gap-3 border-b px-4 py-3 text-sm motion-reduce:transition-none\",\n {\n variants: {\n variant: {\n default: \"bg-card text-card-foreground\",\n info: \"bg-card text-info-ink\",\n success: \"bg-card text-success-ink\",\n warning: \"bg-card text-warning-ink\",\n destructive: \"bg-card text-destructive-ink\"\n }\n },\n defaultVariants: {\n variant: \"default\"\n }\n }\n);\n\ninterface BannerImplProps {\n banner: BannerData;\n side: BannerSide;\n index: number;\n}\n\nfunction BannerImpl(props: BannerImplProps) {\n const { banner, side, index } = props;\n\n const store = useStoreContext(\"BannerImpl\");\n const removing = useStore(store, (state) => state.removing.has(banner.id));\n const banners = useStore(store, (state) => state.banners);\n const heights = useStore(store, (state) => state.heights);\n\n const [mounted, setMounted] = React.useState(false);\n const bannerRef = React.useRef<HTMLDivElement>(null);\n const offsetBeforeRemoveRef = React.useRef(0);\n\n const offset = React.useMemo(() => {\n let total = 0;\n for (const b of banners) {\n if (b.id === banner.id) break;\n total += heights.get(b.id) ?? 0;\n }\n return total;\n }, [banners, heights, banner.id]);\n\n if (!removing) {\n offsetBeforeRemoveRef.current = offset;\n }\n\n React.useEffect(() => {\n const frame = requestAnimationFrame(() => setMounted(true));\n return () => cancelAnimationFrame(frame);\n }, []);\n\n useIsomorphicLayoutEffect(() => {\n if (!bannerRef.current || removing) return;\n const height = bannerRef.current.getBoundingClientRect().height;\n store.onHeightChange(banner.id, height);\n }, [store, banner.id, removing]);\n\n React.useEffect(() => {\n if (!removing) return;\n store.onHeightRemove(banner.id);\n const timeoutId = setTimeout(() => store.onBannerRemove(banner.id), BANNER_ANIMATION_DURATION);\n return () => clearTimeout(timeoutId);\n }, [removing, store, banner.id]);\n\n const onClose = React.useCallback(() => store.onRemovingChange(banner.id, true), [store, banner.id]);\n\n const onRemove = React.useCallback(() => store.onBannerRemove(banner.id), [store, banner.id]);\n\n const dismissible = banner.dismissible ?? DEFAULT_BANNER_DISMISSIBLE;\n\n const contextValue = React.useMemo<BannerContextValue>(\n () => ({ id: banner.id, variant: banner.variant, dismissible, onClose }),\n [banner.id, banner.variant, dismissible, onClose]\n );\n\n const renderProps = React.useMemo<BannerRenderProps>(\n () => ({\n id: banner.id,\n variant: banner.variant,\n dismissible,\n onClose,\n onRemove\n }),\n [banner.id, banner.variant, dismissible, onClose, onRemove]\n );\n\n const currentOffset = removing ? offsetBeforeRemoveRef.current : offset;\n const isTop = side === \"top\";\n\n function getTransform() {\n if (!mounted) return isTop ? \"translateY(-100%)\" : \"translateY(100%)\";\n if (removing) {\n return isTop ? `translateY(calc(${currentOffset}px - 100%))` : `translateY(calc(-${currentOffset}px + 100%))`;\n }\n return isTop ? `translateY(${currentOffset}px)` : `translateY(-${currentOffset}px)`;\n }\n\n const opacityDuration = removing ? BANNER_ANIMATION_DURATION / 2 : BANNER_ANIMATION_DURATION;\n\n return (\n <BannerContext.Provider value={contextValue}>\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"queued-banner\"\n data-state={removing ? \"closed\" : \"open\"}\n data-mounted={mounted}\n data-removed={removing}\n data-side={side}\n data-front={index === 0}\n data-index={index}\n ref={bannerRef}\n className={bannerVariants({ variant: banner.variant })}\n style={{\n position: \"absolute\",\n [isTop ? \"top\" : \"bottom\"]: 0,\n left: 0,\n right: 0,\n zIndex: removing ? 0 : 50 - index,\n transform: getTransform(),\n opacity: mounted && !removing ? 1 : 0,\n // Thời lượng qua `--banner-duration` (0 khi giảm chuyển động) — xem chú thích ở `Banners`.\n transition: `transform var(--banner-duration, ${BANNER_ANIMATION_DURATION}ms) cubic-bezier(0.32, 0.72, 0, 1), opacity var(--banner-duration, ${opacityDuration}ms) ease`\n }}\n >\n {typeof banner.content === \"function\" ? banner.content(renderProps) : banner.content}\n </div>\n </BannerContext.Provider>\n );\n}\n\ninterface BannerProps extends DivProps, VariantProps<typeof bannerVariants> {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n onDismiss?: () => void;\n priority?: number;\n duration?: number;\n dismissible?: boolean;\n}\n\nfunction Banner(props: BannerProps) {\n const {\n className,\n variant = \"default\",\n open: openProp,\n defaultOpen = true,\n onOpenChange,\n onDismiss,\n priority,\n duration,\n dismissible = DEFAULT_BANNER_DISMISSIBLE,\n children,\n asChild,\n ...rootProps\n } = props;\n\n const store = React.useContext(StoreContext);\n\n const isInsideStore = store !== null;\n const isControlled = openProp !== undefined;\n\n const openRef = useLazyRef(() => openProp ?? defaultOpen);\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const bannerIdRef = React.useRef<string | null>(null);\n const onDismissRef = useAsRef(onDismiss);\n const onOpenChangeRef = useAsRef(onOpenChange);\n\n if (isControlled) {\n openRef.current = openProp;\n }\n\n const subscribe = React.useCallback(\n (cb: () => void) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n [listenersRef]\n );\n\n const getSnapshot = React.useCallback(() => openRef.current, [openRef]);\n\n const open = React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n React.useEffect(() => {\n if (!isInsideStore || !store || !open) return;\n\n /*\n * ⚠ Khác thượng nguồn — hai lỗi của `<Banner>` đặt trong `<Banners>`:\n *\n * 1. Hàm dọn của effect này gỡ banner bằng `onBannerRemove`, mà hàm đó GỌI `onDismiss`. Bản\n * gốc vì thế báo \"người dùng đã đóng\" (`onDismiss` + `onOpenChange(false)`) mỗi khi effect\n * chạy lại — tức MỖI LẦN cha re-render, vì `children` là JSX mới — và cả khi unmount (đổi\n * route). Banner tự đóng, và app lưu \"đã tắt thông báo\" cho một lần người dùng chưa hề bấm.\n * Cờ `isCleaningUp` chặn đúng đường đó; đóng thật (nút X, hết `duration`) vẫn báo như cũ.\n * 2. Ở chế độ không kiểm soát, đóng qua hàng đợi không hạ `open` của chính `Banner`, nên lần\n * re-render kế tiếp thêm LẠI banner vừa đóng. Nay hạ `open` như `onClose` bên dưới.\n */\n let isCleaningUp = false;\n\n const id = store.onBannerAdd({\n content: children,\n variant: variant ?? undefined,\n priority,\n dismissible,\n duration,\n onDismiss: () => {\n if (isCleaningUp) return;\n if (!isControlled) {\n openRef.current = false;\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n onDismissRef.current?.();\n onOpenChangeRef.current?.(false);\n }\n });\n bannerIdRef.current = id;\n\n return () => {\n isCleaningUp = true;\n if (bannerIdRef.current) {\n store.onBannerRemove(bannerIdRef.current);\n bannerIdRef.current = null;\n }\n };\n }, [\n isInsideStore,\n store,\n open,\n isControlled,\n children,\n variant,\n priority,\n dismissible,\n duration,\n openRef,\n listenersRef,\n onDismissRef,\n onOpenChangeRef\n ]);\n\n const onClose = React.useCallback(() => {\n if (!isControlled) {\n openRef.current = false;\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n onOpenChangeRef.current?.(false);\n }, [isControlled, openRef, listenersRef, onOpenChangeRef]);\n\n const contextValue = React.useMemo<BannerContextValue>(\n () => ({\n variant,\n dismissible,\n onClose\n }),\n [variant, dismissible, onClose]\n );\n\n if (!open || isInsideStore) return null;\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <BannerContext.Provider value={contextValue}>\n <RootPrimitive\n role=\"status\"\n aria-live=\"polite\"\n data-slot=\"banner\"\n data-state=\"open\"\n className={cn(bannerVariants({ variant, className }))}\n {...rootProps}\n >\n {children}\n </RootPrimitive>\n </BannerContext.Provider>\n );\n}\n\nfunction BannerIcon(props: DivProps) {\n const { className, asChild, ...iconProps } = props;\n\n const IconPrimitive = asChild ? Slot : \"div\";\n\n return (\n <IconPrimitive\n data-slot=\"banner-icon\"\n className={cn(\"flex shrink-0 items-center [&>svg]:size-4\", className)}\n {...iconProps}\n />\n );\n}\n\nfunction BannerContent(props: DivProps) {\n const { className, asChild, ...contentProps } = props;\n\n const ContentPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ContentPrimitive\n data-slot=\"banner-content\"\n className={cn(\"flex min-w-0 flex-1 flex-col gap-1\", className)}\n {...contentProps}\n />\n );\n}\n\nfunction BannerTitle(props: React.ComponentProps<\"div\">) {\n const { className, ...titleProps } = props;\n\n return <div data-slot=\"banner-title\" className={cn(\"text-sm leading-none font-medium\", className)} {...titleProps} />;\n}\n\nfunction BannerDescription(props: React.ComponentProps<\"div\">) {\n const { className, ...descriptionProps } = props;\n\n return <div data-slot=\"banner-description\" className={cn(\"text-xs opacity-90\", className)} {...descriptionProps} />;\n}\n\nfunction BannerActions(props: DivProps) {\n const { className, asChild, ...actionsProps } = props;\n\n const ActionsPrimitive = asChild ? Slot : \"div\";\n\n return (\n <ActionsPrimitive\n data-slot=\"banner-actions\"\n className={cn(\"flex items-center gap-2\", className)}\n {...actionsProps}\n />\n );\n}\n\ntype BannerCloseProps = React.ComponentProps<typeof Button>;\n\n/**\n * Nút đóng banner. Không truyền `children` thì vẽ icon X.\n *\n * ⚠ Khác thượng nguồn: có tên cho trình đọc màn hình — `aria-label` mặc định lấy khoá\n * `closeLabel` của namespace `banner` (`ComitorLocaleProvider`), ghi đè bằng chính prop\n * `aria-label`. Bản gốc là `Button` chỉ chứa icon X, tức một nút KHÔNG TÊN. Nếu `children` là\n * CHỮ nhìn thấy được thì truyền `aria-label` trùng với chữ đó (WCAG 2.5.3), hoặc\n * `aria-label={undefined}`.\n */\nfunction BannerClose(props: BannerCloseProps) {\n const { onClick: onClickProp, disabled, children, ...closeProps } = props;\n\n const { dismissible = DEFAULT_BANNER_DISMISSIBLE, onClose } = useBannerContext(\"BannerClose\");\n const t = useComitorMessages(\"banner\");\n\n const isDisabled = disabled ?? !dismissible;\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<CloseElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented || isDisabled) return;\n onClose?.();\n },\n [onClickProp, isDisabled, onClose]\n );\n\n return (\n <Button\n aria-label={t.closeLabel}\n data-slot=\"banner-close\"\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onClick}\n disabled={isDisabled}\n {...closeProps}\n >\n {children ?? <X aria-hidden=\"true\" className=\"size-3.5\" />}\n </Button>\n );\n}\n\nexport {\n Banner,\n BannerActions,\n BannerClose,\n type BannerCloseProps,\n BannerContent,\n type BannerData,\n BannerDescription,\n BannerIcon,\n type BannerProps,\n type BannerRenderProps,\n Banners,\n type BannersProps,\n BannerTitle,\n type BannerVariant,\n useBanner,\n useBanners\n};\n"],"mappings":"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","names":["banners"]}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Breadcrumb — primitive
|
|
5
|
-
* Chuỗi
|
|
4
|
+
* Breadcrumb — primitive dựng đường dẫn phân cấp.
|
|
5
|
+
* Chuỗi cho trình đọc màn hình theo `ComitorLocaleProvider` (namespace `breadcrumb`, mặc định
|
|
6
|
+
* tiếng Anh); override từng chỗ qua `aria-label` / `srLabel`.
|
|
7
|
+
*
|
|
8
|
+
* `"use client"` từ `3.0.0`: tên landmark của `<nav>` và nhãn của `BreadcrumbEllipsis` đọc từ
|
|
9
|
+
* provider, mà context chỉ đọc được ở client. Trước đó file này tĩnh và ghim cứng tiếng Việt.
|
|
10
|
+
* Không prop nào là hàm/component nên Server Component (vd `PageHeader`) vẫn render được nó.
|
|
11
|
+
* Con của `BreadcrumbLink asChild` nay đi qua ranh giới RSC — tới dạng element lười thì `Slot`
|
|
12
|
+
* (`@radix-ui/react-slot` 1.2.4) tự mở bằng `use()`.
|
|
6
13
|
*
|
|
7
14
|
* Điều hướng: bọc `next/link` bằng `<BreadcrumbLink asChild><Link …/></BreadcrumbLink>`
|
|
8
15
|
* để primitive không phụ thuộc `next/*`.
|
|
@@ -16,7 +23,7 @@ type BreadcrumbLinkProps = React.ComponentProps<"a"> & {
|
|
|
16
23
|
type BreadcrumbPageProps = React.ComponentProps<"span">;
|
|
17
24
|
type BreadcrumbSeparatorProps = React.ComponentProps<"li">;
|
|
18
25
|
type BreadcrumbEllipsisProps = React.ComponentProps<"span"> & {
|
|
19
|
-
/** Nhãn chỉ dành cho trình đọc màn hình. */
|
|
26
|
+
/** Nhãn chỉ dành cho trình đọc màn hình. Mặc định: khoá `ellipsisLabel` của namespace `breadcrumb`. */
|
|
20
27
|
srLabel?: string;
|
|
21
28
|
};
|
|
22
29
|
declare function Breadcrumb({ ...props }: BreadcrumbProps): React.JSX.Element;
|
|
@@ -25,6 +32,6 @@ declare function BreadcrumbItem({ className, ...props }: BreadcrumbItemProps): R
|
|
|
25
32
|
declare function BreadcrumbLink({ asChild, className, ...props }: BreadcrumbLinkProps): React.JSX.Element;
|
|
26
33
|
declare function BreadcrumbPage({ className, ...props }: BreadcrumbPageProps): React.JSX.Element;
|
|
27
34
|
declare function BreadcrumbSeparator({ children, className, ...props }: BreadcrumbSeparatorProps): React.JSX.Element;
|
|
28
|
-
declare function BreadcrumbEllipsis({ className, srLabel, ...props }: BreadcrumbEllipsisProps): React.JSX.Element;
|
|
35
|
+
declare function BreadcrumbEllipsis({ className, srLabel: srLabelProp, ...props }: BreadcrumbEllipsisProps): React.JSX.Element;
|
|
29
36
|
|
|
30
37
|
export { Breadcrumb, BreadcrumbEllipsis, type BreadcrumbEllipsisProps, BreadcrumbItem, type BreadcrumbItemProps, BreadcrumbLink, type BreadcrumbLinkProps, BreadcrumbList, type BreadcrumbListProps, BreadcrumbPage, type BreadcrumbPageProps, type BreadcrumbProps, BreadcrumbSeparator, type BreadcrumbSeparatorProps };
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { Slot } from "@radix-ui/react-slot";
|
|
3
4
|
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
|
5
|
+
import { useComitorMessages } from "../../i18n/locale-provider.js";
|
|
4
6
|
import { cn } from "../../lib/cn.js";
|
|
5
7
|
function Breadcrumb({ ...props }) {
|
|
6
|
-
|
|
8
|
+
const t = useComitorMessages("breadcrumb");
|
|
9
|
+
return /* @__PURE__ */ jsx("nav", { "aria-label": t.navLabel, "data-slot": "breadcrumb", ...props });
|
|
7
10
|
}
|
|
8
11
|
function BreadcrumbList({ className, ...props }) {
|
|
9
12
|
return /* @__PURE__ */ jsx(
|
|
@@ -61,7 +64,9 @@ function BreadcrumbSeparator({ children, className, ...props }) {
|
|
|
61
64
|
}
|
|
62
65
|
);
|
|
63
66
|
}
|
|
64
|
-
function BreadcrumbEllipsis({ className, srLabel
|
|
67
|
+
function BreadcrumbEllipsis({ className, srLabel: srLabelProp, ...props }) {
|
|
68
|
+
const t = useComitorMessages("breadcrumb");
|
|
69
|
+
const srLabel = srLabelProp ?? t.ellipsisLabel;
|
|
65
70
|
return /* @__PURE__ */ jsxs(
|
|
66
71
|
"span",
|
|
67
72
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/breadcrumb.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/breadcrumb.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { ChevronRight, MoreHorizontal } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { useComitorMessages } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Breadcrumb — primitive dựng đường dẫn phân cấp.\n * Chuỗi cho trình đọc màn hình theo `ComitorLocaleProvider` (namespace `breadcrumb`, mặc định\n * tiếng Anh); override từng chỗ qua `aria-label` / `srLabel`.\n *\n * `\"use client\"` từ `3.0.0`: tên landmark của `<nav>` và nhãn của `BreadcrumbEllipsis` đọc từ\n * provider, mà context chỉ đọc được ở client. Trước đó file này tĩnh và ghim cứng tiếng Việt.\n * Không prop nào là hàm/component nên Server Component (vd `PageHeader`) vẫn render được nó.\n * Con của `BreadcrumbLink asChild` nay đi qua ranh giới RSC — tới dạng element lười thì `Slot`\n * (`@radix-ui/react-slot` 1.2.4) tự mở bằng `use()`.\n *\n * Điều hướng: bọc `next/link` bằng `<BreadcrumbLink asChild><Link …/></BreadcrumbLink>`\n * để primitive không phụ thuộc `next/*`.\n */\n\nexport type BreadcrumbProps = React.ComponentProps<\"nav\">;\nexport type BreadcrumbListProps = React.ComponentProps<\"ol\">;\nexport type BreadcrumbItemProps = React.ComponentProps<\"li\">;\nexport type BreadcrumbLinkProps = React.ComponentProps<\"a\"> & { asChild?: boolean };\nexport type BreadcrumbPageProps = React.ComponentProps<\"span\">;\nexport type BreadcrumbSeparatorProps = React.ComponentProps<\"li\">;\nexport type BreadcrumbEllipsisProps = React.ComponentProps<\"span\"> & {\n /** Nhãn chỉ dành cho trình đọc màn hình. Mặc định: khoá `ellipsisLabel` của namespace `breadcrumb`. */\n srLabel?: string;\n};\n\nfunction Breadcrumb({ ...props }: BreadcrumbProps) {\n const t = useComitorMessages(\"breadcrumb\");\n return <nav aria-label={t.navLabel} data-slot=\"breadcrumb\" {...props} />;\n}\n\nfunction BreadcrumbList({ className, ...props }: BreadcrumbListProps) {\n return (\n <ol\n data-slot=\"breadcrumb-list\"\n className={cn(\n \"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction BreadcrumbItem({ className, ...props }: BreadcrumbItemProps) {\n return <li data-slot=\"breadcrumb-item\" className={cn(\"inline-flex items-center gap-1.5\", className)} {...props} />;\n}\n\nfunction BreadcrumbLink({ asChild, className, ...props }: BreadcrumbLinkProps) {\n const Comp = asChild ? Slot : \"a\";\n\n return (\n <Comp\n data-slot=\"breadcrumb-link\"\n className={cn(\"hover:text-foreground transition-colors duration-150 ease-out\", className)}\n {...props}\n />\n );\n}\n\nfunction BreadcrumbPage({ className, ...props }: BreadcrumbPageProps) {\n return (\n // biome-ignore lint/a11y/useFocusableInteractive: giữ nguyên markup shadcn — mục hiện tại cố tình KHÔNG focus được (aria-disabled), chỉ mượn role=\"link\" để trình đọc màn hình đọc đúng ngữ cảnh\n <span\n data-slot=\"breadcrumb-page\"\n role=\"link\"\n aria-disabled=\"true\"\n aria-current=\"page\"\n className={cn(\"text-foreground font-normal\", className)}\n {...props}\n />\n );\n}\n\nfunction BreadcrumbSeparator({ children, className, ...props }: BreadcrumbSeparatorProps) {\n return (\n <li\n data-slot=\"breadcrumb-separator\"\n role=\"presentation\"\n aria-hidden=\"true\"\n className={cn(\"[&>svg]:size-3.5\", className)}\n {...props}\n >\n {children ?? <ChevronRight />}\n </li>\n );\n}\n\nfunction BreadcrumbEllipsis({ className, srLabel: srLabelProp, ...props }: BreadcrumbEllipsisProps) {\n const t = useComitorMessages(\"breadcrumb\");\n const srLabel = srLabelProp ?? t.ellipsisLabel;\n return (\n <span\n data-slot=\"breadcrumb-ellipsis\"\n role=\"presentation\"\n aria-hidden=\"true\"\n className={cn(\"flex size-9 items-center justify-center\", className)}\n {...props}\n >\n <MoreHorizontal className=\"size-4\" />\n <span className=\"sr-only\">{srLabel}</span>\n </span>\n );\n}\n\nexport {\n Breadcrumb,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator\n};\n"],"mappings":";AAoCS,cAgEL,YAhEK;AAlCT,SAAS,YAAY;AACrB,SAAS,cAAc,sBAAsB;AAE7C,SAAS,0BAA0B;AACnC,SAAS,UAAU;AA4BnB,SAAS,WAAW,EAAE,GAAG,MAAM,GAAoB;AACjD,QAAM,IAAI,mBAAmB,YAAY;AACzC,SAAO,oBAAC,SAAI,cAAY,EAAE,UAAU,aAAU,cAAc,GAAG,OAAO;AACxE;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SAAO,oBAAC,QAAG,aAAU,mBAAkB,WAAW,GAAG,oCAAoC,SAAS,GAAI,GAAG,OAAO;AAClH;AAEA,SAAS,eAAe,EAAE,SAAS,WAAW,GAAG,MAAM,GAAwB;AAC7E,QAAM,OAAO,UAAU,OAAO;AAE9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iEAAiE,SAAS;AAAA,MACvF,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE;AAAA;AAAA,IAEE;AAAA,MAAC;AAAA;AAAA,QACC,aAAU;AAAA,QACV,MAAK;AAAA,QACL,iBAAc;AAAA,QACd,gBAAa;AAAA,QACb,WAAW,GAAG,+BAA+B,SAAS;AAAA,QACrD,GAAG;AAAA;AAAA,IACN;AAAA;AAEJ;AAEA,SAAS,oBAAoB,EAAE,UAAU,WAAW,GAAG,MAAM,GAA6B;AACxF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,eAAY;AAAA,MACZ,WAAW,GAAG,oBAAoB,SAAS;AAAA,MAC1C,GAAG;AAAA,MAEH,sBAAY,oBAAC,gBAAa;AAAA;AAAA,EAC7B;AAEJ;AAEA,SAAS,mBAAmB,EAAE,WAAW,SAAS,aAAa,GAAG,MAAM,GAA4B;AAClG,QAAM,IAAI,mBAAmB,YAAY;AACzC,QAAM,UAAU,eAAe,EAAE;AACjC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,eAAY;AAAA,MACZ,WAAW,GAAG,2CAA2C,SAAS;AAAA,MACjE,GAAG;AAAA,MAEJ;AAAA,4BAAC,kBAAe,WAAU,UAAS;AAAA,QACnC,oBAAC,UAAK,WAAU,WAAW,mBAAQ;AAAA;AAAA;AAAA,EACrC;AAEJ;","names":[]}
|
|
@@ -3,8 +3,8 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
|
|
5
5
|
declare const buttonVariants: (props?: ({
|
|
6
|
-
variant?: "link" | "default" | "
|
|
7
|
-
size?: "
|
|
6
|
+
variant?: "link" | "default" | "destructive" | "primary" | "secondary" | "outline" | "ghost" | null | undefined;
|
|
7
|
+
size?: "default" | "sm" | "md" | "lg" | "icon" | "icon-sm" | "icon-lg" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface ButtonProps extends React.ComponentProps<"button">, VariantProps<typeof buttonVariants> {
|
|
10
10
|
/** Render ra chính phần tử con (Radix Slot) thay vì thẻ <button>. */
|
|
@@ -6,13 +6,20 @@ import 'class-variance-authority';
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Calendar (react-day-picker v9) — lịch trần, đã map class sang token Comitor.
|
|
9
|
-
* Bản có ô nhập
|
|
9
|
+
* Bản có ô nhập ngày + preset ("Today", "Last 7 days"…) là composite
|
|
10
10
|
* `DatePicker` / `DateRangePicker` ở Tier 2.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* ─ NGÔN NGỮ ─ (từ `3.0.0` theo `ComitorLocaleProvider`)
|
|
13
|
+
* · tên thứ/tháng: prop `locale` → locale date-fns của provider (`vi` hoặc `enUS`);
|
|
14
|
+
* · nhãn trình đọc màn hình (nút chuyển tháng, tên ô ngày…): prop `labels` → bản dịch mang sẵn
|
|
15
|
+
* trong `locale` nếu đó là locale của `react-day-picker/locale` → namespace `calendar` của
|
|
16
|
+
* provider. Locale date-fns trần không mang chuỗi giao diện, nên thiếu bậc cuối thì lịch đọc
|
|
17
|
+
* tiếng Anh cứng của react-day-picker giữa app tiếng Việt.
|
|
18
|
+
* Trước `3.0.0`: không truyền `locale` thì lưới lịch là `enUS` (mặc định của react-day-picker)
|
|
19
|
+
* còn tên tháng trong dropdown lại là `vi-VN` — hai nửa một lịch hai thứ tiếng.
|
|
13
20
|
*
|
|
14
|
-
* Khác bản comitor-ds: tên tháng rút gọn trong dropdown lấy theo `
|
|
15
|
-
* (
|
|
21
|
+
* Khác bản comitor-ds: tên tháng rút gọn trong dropdown lấy theo `code` của locale ĐANG HIỆU LỰC
|
|
22
|
+
* (prop hoặc provider) thay vì `"default"` — `"default"` phụ thuộc locale của môi
|
|
16
23
|
* trường chạy nên server và trình duyệt có thể ra chuỗi khác nhau → hydration mismatch.
|
|
17
24
|
*
|
|
18
25
|
* ─ Ô NGÀY ĐANG CHỌN: `bg-control-on` + `text-control-check` ─
|
|
@@ -44,7 +51,7 @@ type CalendarProps = React.ComponentProps<typeof DayPicker> & {
|
|
|
44
51
|
buttonVariant?: ButtonProps["variant"];
|
|
45
52
|
};
|
|
46
53
|
type CalendarDayButtonProps = React.ComponentProps<typeof DayButton>;
|
|
47
|
-
declare function Calendar({ className, classNames, showOutsideDays, captionLayout, buttonVariant, formatters, components, locale, ...props }: CalendarProps): React.JSX.Element;
|
|
54
|
+
declare function Calendar({ className, classNames, showOutsideDays, captionLayout, buttonVariant, formatters, components, locale: localeProp, labels: labelsProp, ...props }: CalendarProps): React.JSX.Element;
|
|
48
55
|
/**
|
|
49
56
|
* Ô ngày.
|
|
50
57
|
*
|
|
@@ -2,9 +2,34 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
|
4
4
|
import * as React from "react";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
DateLib,
|
|
7
|
+
DayPicker,
|
|
8
|
+
getDefaultClassNames
|
|
9
|
+
} from "react-day-picker";
|
|
10
|
+
import { useComitorLocale, useComitorMessages } from "../../i18n/locale-provider.js";
|
|
6
11
|
import { cn } from "../../lib/cn.js";
|
|
7
12
|
import { Button, buttonVariants } from "./button.js";
|
|
13
|
+
function toDayPickerLabels(t) {
|
|
14
|
+
return {
|
|
15
|
+
labelNav: () => t.navigation,
|
|
16
|
+
labelPrevious: () => t.previousMonth,
|
|
17
|
+
labelNext: () => t.nextMonth,
|
|
18
|
+
labelMonthDropdown: () => t.monthDropdown,
|
|
19
|
+
labelYearDropdown: () => t.yearDropdown,
|
|
20
|
+
labelDayButton: (date, modifiers, options, dateLib) => t.day((dateLib ?? new DateLib(options)).format(date, "PPPP"), {
|
|
21
|
+
today: modifiers.today === true,
|
|
22
|
+
selected: modifiers.selected === true
|
|
23
|
+
}),
|
|
24
|
+
// Ô ngày khi lịch KHÔNG tương tác được — react-day-picker không báo "đã chọn" ở đây.
|
|
25
|
+
labelGridcell: (date, modifiers, options, dateLib) => t.day((dateLib ?? new DateLib(options)).format(date, "PPPP"), {
|
|
26
|
+
today: modifiers?.today === true,
|
|
27
|
+
selected: false
|
|
28
|
+
}),
|
|
29
|
+
labelWeekNumber: (weekNumber) => t.weekNumber(weekNumber),
|
|
30
|
+
labelWeekNumberHeader: () => t.weekNumberHeader
|
|
31
|
+
};
|
|
32
|
+
}
|
|
8
33
|
function Calendar({
|
|
9
34
|
className,
|
|
10
35
|
classNames,
|
|
@@ -13,16 +38,26 @@ function Calendar({
|
|
|
13
38
|
buttonVariant = "ghost",
|
|
14
39
|
formatters,
|
|
15
40
|
components,
|
|
16
|
-
locale,
|
|
41
|
+
locale: localeProp,
|
|
42
|
+
labels: labelsProp,
|
|
17
43
|
...props
|
|
18
44
|
}) {
|
|
19
45
|
const defaultClassNames = getDefaultClassNames();
|
|
20
|
-
const
|
|
46
|
+
const { dateFnsLocale } = useComitorLocale();
|
|
47
|
+
const t = useComitorMessages("calendar");
|
|
48
|
+
const locale = localeProp ?? dateFnsLocale;
|
|
49
|
+
const localeCode = locale.code ?? dateFnsLocale.code;
|
|
50
|
+
const hasLocaleLabels = locale.labels !== void 0;
|
|
51
|
+
const labels = React.useMemo(
|
|
52
|
+
() => hasLocaleLabels ? labelsProp : { ...toDayPickerLabels(t), ...labelsProp },
|
|
53
|
+
[hasLocaleLabels, t, labelsProp]
|
|
54
|
+
);
|
|
21
55
|
return /* @__PURE__ */ jsx(
|
|
22
56
|
DayPicker,
|
|
23
57
|
{
|
|
24
58
|
showOutsideDays,
|
|
25
59
|
locale,
|
|
60
|
+
labels,
|
|
26
61
|
className: cn(
|
|
27
62
|
"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
|
28
63
|
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/calendar.tsx"],"sourcesContent":["\"use client\";\n\nimport { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { type DayButton, DayPicker, getDefaultClassNames } from \"react-day-picker\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button, type ButtonProps, buttonVariants } from \"./button.js\";\n\n/**\n * Calendar (react-day-picker v9) — lịch trần, đã map class sang token Comitor.\n * Bản có ô nhập `dd/MM/yyyy` + preset (\"Hôm nay\", \"7 ngày qua\"…) là composite\n * `DatePicker` / `DateRangePicker` ở Tier 2.\n *\n * Ngôn ngữ: truyền `locale={vi}` (date-fns/locale) để đổi tên thứ/tháng.\n *\n * Khác bản comitor-ds: tên tháng rút gọn trong dropdown lấy theo `locale.code`\n * (mặc định `vi-VN`) thay vì `\"default\"` — `\"default\"` phụ thuộc locale của môi\n * trường chạy nên server và trình duyệt có thể ra chuỗi khác nhau → hydration mismatch.\n *\n * ─ Ô NGÀY ĐANG CHỌN: `bg-control-on` + `text-control-check` ─\n * Chữ của ô ngày KHÔNG đổi màu khi được chọn (nghỉ và chọn đều là bậc mực), nên \"đang chọn\" chỉ\n * còn được kể bằng NỀN ô — thông tin thị giác, ngưỡng 3:1.\n *\n * Ô ngày đang chọn là một control ở trạng thái BẬT, y như ô Checkbox đã chọn, nên nó phải đi\n * bằng CẶP token của họ control: nền `--control-on`, chữ `--control-check`. `--control-check`\n * theo định nghĩa là \"dấu đứng trên --control-on\" — ghép nó với một nền khác là hai token trôi\n * độc lập, và sẽ có ngày lệch nhau.\n *\n * ⚠ Bản trước ghép `bg-primary-ink` + `text-control-check`. Ở MỌI bảng màu trừ một, hai token đó\n * bằng nhau (`--primary-ink: var(--primary)` = `--control-on`) nên cặp sai vẫn cho ra đúng màu —\n * lỗi ngủ yên. Đến khi bảng tương phản cao sáng riêng `--control-on` lên #B48700 mà `--primary-ink`\n * ở lại gold-500 (nó còn phải làm CHỮ, ngưỡng 4,5:1) thì mới lộ: chữ mực trên #7A5800 = 2,52:1 ✗.\n * Cổng tương phản bắt được; mắt thường thì không.\n *\n * nền ô ⇄ nền lịch : 1,86:1 (bảng duyệt — khoản đã chấp nhận, cùng nhóm với rãnh Switch BẬT)\n * 3,28:1 (bảng tương phản cao) ✓\n * chữ mực trên nền ô : 10,19:1 (bảng duyệt) · 5,77:1 (tương phản cao) ✓\n *\n * ⚠ CÒN TỒN, cố ý chưa động: `today` và `range_middle` dùng `bg-accent` — 1,09:1 (light) /\n * 1,17:1 (dark) so với nền lịch. Đưa dải giữa lên 3:1 thì cả tuần biến thành một khối đặc,\n * nên đây là quyết định THIẾT KẾ (thêm viền/gạch chân cho \"hôm nay\"?) chứ không sửa được bằng\n * đổi token — để dành cho vòng riêng.\n */\n\nexport type CalendarProps = React.ComponentProps<typeof DayPicker> & {\n /** Biến thể Button dùng cho nút điều hướng tháng. */\n buttonVariant?: ButtonProps[\"variant\"];\n};\nexport type CalendarDayButtonProps = React.ComponentProps<typeof DayButton>;\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = \"label\",\n buttonVariant = \"ghost\",\n formatters,\n components,\n locale,\n ...props\n}: CalendarProps) {\n const defaultClassNames = getDefaultClassNames();\n const localeCode = locale?.code ?? \"vi-VN\";\n\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n locale={locale}\n className={cn(\n \"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent\",\n String.raw`rtl:**:[.rdp-button\\_next>svg]:rotate-180`,\n String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\n className\n )}\n captionLayout={captionLayout}\n formatters={{\n formatMonthDropdown: (date) => date.toLocaleString(localeCode, { month: \"short\" }),\n ...formatters\n }}\n classNames={{\n root: cn(\"w-fit\", defaultClassNames.root),\n months: cn(\"flex gap-4 flex-col md:flex-row relative\", defaultClassNames.months),\n month: cn(\"flex flex-col w-full gap-4\", defaultClassNames.month),\n nav: cn(\"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between\", defaultClassNames.nav),\n button_previous: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none\",\n defaultClassNames.button_previous\n ),\n button_next: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none\",\n defaultClassNames.button_next\n ),\n month_caption: cn(\n \"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)\",\n defaultClassNames.month_caption\n ),\n dropdowns: cn(\n \"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5\",\n defaultClassNames.dropdowns\n ),\n dropdown_root: cn(\n \"relative has-focus:outline-none border border-input shadow-xs has-focus:ring-2 has-focus:ring-ring/50 has-focus:ring-offset-2 has-focus:ring-offset-background rounded-md\",\n defaultClassNames.dropdown_root\n ),\n dropdown: cn(\"absolute bg-popover inset-0 opacity-0\", defaultClassNames.dropdown),\n caption_label: cn(\n \"select-none font-medium\",\n captionLayout === \"label\"\n ? \"text-sm\"\n : \"rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5\",\n defaultClassNames.caption_label\n ),\n table: \"w-full border-collapse\",\n weekdays: cn(\"flex\", defaultClassNames.weekdays),\n weekday: cn(\n \"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none\",\n defaultClassNames.weekday\n ),\n week: cn(\"flex w-full mt-2\", defaultClassNames.week),\n week_number_header: cn(\"select-none w-(--cell-size)\", defaultClassNames.week_number_header),\n week_number: cn(\"text-[0.8rem] select-none text-muted-foreground\", defaultClassNames.week_number),\n day: cn(\n \"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none\",\n defaultClassNames.day\n ),\n range_start: cn(\"rounded-l-md bg-accent\", defaultClassNames.range_start),\n range_middle: cn(\"rounded-none\", defaultClassNames.range_middle),\n range_end: cn(\"rounded-r-md bg-accent\", defaultClassNames.range_end),\n today: cn(\n \"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none\",\n defaultClassNames.today\n ),\n outside: cn(\"text-muted-foreground aria-selected:text-muted-foreground\", defaultClassNames.outside),\n disabled: cn(\"text-muted-foreground opacity-50\", defaultClassNames.disabled),\n hidden: cn(\"invisible\", defaultClassNames.hidden),\n ...classNames\n }}\n components={{\n Root: ({ className: rootClassName, rootRef, ...rootProps }) => {\n return <div data-slot=\"calendar\" ref={rootRef} className={cn(rootClassName)} {...rootProps} />;\n },\n Chevron: ({ className: chevronClassName, orientation, ...chevronProps }) => {\n if (orientation === \"left\") {\n return <ChevronLeftIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n }\n\n if (orientation === \"right\") {\n return <ChevronRightIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n }\n\n return <ChevronDownIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n },\n DayButton: CalendarDayButton,\n WeekNumber: ({ children, week: _week, ...weekProps }) => {\n return (\n <td {...weekProps}>\n <div className=\"flex size-(--cell-size) items-center justify-center text-center\">{children}</div>\n </td>\n );\n },\n ...components\n }}\n {...props}\n />\n );\n}\n\n/**\n * Ô ngày.\n *\n * ─ `[&>span]:opacity-90` — MỨC MỜ TỐI ĐA CÒN ĐẠT AA ─\n * `<span>` là nội dung phụ app truyền vào ô ngày (số việc, chấm sự kiện…), cỡ `text-xs` = 12px\n * nên chịu ngưỡng CHỮ 4,5:1. Nút ngày VẪN bấm được và vẫn nhận focus → `opacity-*` chồng lên nó\n * KHÔNG được miễn trừ theo 1.4.3. Ca tệ nhất là ngày ĐÃ CHỌN ở theme sáng của bảng TƯƠNG PHẢN\n * CAO — chữ mực trên `bg-control-on` #B48700:\n *\n * opacity-80 → 4,43:1 ✗ ← mức cũ, KHÔNG còn đạt\n * opacity-85 → 4,79:1 ✓\n * opacity-90 → 5,15:1 ✓ ← đang dùng, còn biên cho lần chỉnh gold sau\n *\n * ⚠ Bộ số cũ ở đây (70% → 4,12:1 · 80% → 4,85:1) đo trên MỘT CẶP KHÁC: nền #7A5800 với chữ\n * TRẮNG. Từ khi bảng tương phản cao bỏ gold-500 (nâu-ô-liu, xem khối trên) và tô control bằng\n * #B48700 thì chữ quay về MỰC, ca tệ nhất chặt lại và 80% rơi xuống dưới ngưỡng. Đây là kiểu lỗi\n * mà một lần đổi token ở styles.css kéo theo, và chỉ cổng tương phản mới thấy.\n *\n * Hai bảng còn lại rộng rãi ở 90%: bảng duyệt (mực trên #E8B824) 8,60:1, dark (mực trên #F5D060)\n * 10,43:1. Các trạng thái khác cũng thoải mái: giữa khoảng & hôm nay 9,70:1 (light) · 10,05:1\n * (dark), ngày thường 10,26:1 · 11,41:1.\n */\nfunction CalendarDayButton({ className, day, modifiers, ...props }: CalendarDayButtonProps) {\n const defaultClassNames = getDefaultClassNames();\n\n const ref = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (modifiers.focused) ref.current?.focus();\n }, [modifiers.focused]);\n\n return (\n <Button\n ref={ref}\n variant=\"ghost\"\n size=\"icon\"\n data-day={day.date.toLocaleDateString()}\n data-selected-single={\n modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle\n }\n data-range-start={modifiers.range_start}\n data-range-end={modifiers.range_end}\n data-range-middle={modifiers.range_middle}\n className={cn(\n \"data-[selected-single=true]:bg-control-on data-[selected-single=true]:text-control-check data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-control-on data-[range-start=true]:text-control-check data-[range-end=true]:bg-control-on data-[range-end=true]:text-control-check group-data-[focused=true]/day:outline-none group-data-[focused=true]/day:ring-ring/50 group-data-[focused=true]/day:ring-offset-2 group-data-[focused=true]/day:ring-offset-background dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-2 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-90\",\n defaultClassNames.day,\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Calendar, CalendarDayButton };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/calendar.tsx"],"sourcesContent":["\"use client\";\n\nimport { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport {\n DateLib,\n type DayButton,\n DayPicker,\n type DayPickerLocale,\n getDefaultClassNames,\n type Labels\n} from \"react-day-picker\";\nimport { useComitorLocale, useComitorMessages } from \"../../i18n/locale-provider.js\";\nimport type { CalendarMessages } from \"../../i18n/namespaces/calendar.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button, type ButtonProps, buttonVariants } from \"./button.js\";\n\n/**\n * Calendar (react-day-picker v9) — lịch trần, đã map class sang token Comitor.\n * Bản có ô nhập ngày + preset (\"Today\", \"Last 7 days\"…) là composite\n * `DatePicker` / `DateRangePicker` ở Tier 2.\n *\n * ─ NGÔN NGỮ ─ (từ `3.0.0` theo `ComitorLocaleProvider`)\n * · tên thứ/tháng: prop `locale` → locale date-fns của provider (`vi` hoặc `enUS`);\n * · nhãn trình đọc màn hình (nút chuyển tháng, tên ô ngày…): prop `labels` → bản dịch mang sẵn\n * trong `locale` nếu đó là locale của `react-day-picker/locale` → namespace `calendar` của\n * provider. Locale date-fns trần không mang chuỗi giao diện, nên thiếu bậc cuối thì lịch đọc\n * tiếng Anh cứng của react-day-picker giữa app tiếng Việt.\n * Trước `3.0.0`: không truyền `locale` thì lưới lịch là `enUS` (mặc định của react-day-picker)\n * còn tên tháng trong dropdown lại là `vi-VN` — hai nửa một lịch hai thứ tiếng.\n *\n * Khác bản comitor-ds: tên tháng rút gọn trong dropdown lấy theo `code` của locale ĐANG HIỆU LỰC\n * (prop hoặc provider) thay vì `\"default\"` — `\"default\"` phụ thuộc locale của môi\n * trường chạy nên server và trình duyệt có thể ra chuỗi khác nhau → hydration mismatch.\n *\n * ─ Ô NGÀY ĐANG CHỌN: `bg-control-on` + `text-control-check` ─\n * Chữ của ô ngày KHÔNG đổi màu khi được chọn (nghỉ và chọn đều là bậc mực), nên \"đang chọn\" chỉ\n * còn được kể bằng NỀN ô — thông tin thị giác, ngưỡng 3:1.\n *\n * Ô ngày đang chọn là một control ở trạng thái BẬT, y như ô Checkbox đã chọn, nên nó phải đi\n * bằng CẶP token của họ control: nền `--control-on`, chữ `--control-check`. `--control-check`\n * theo định nghĩa là \"dấu đứng trên --control-on\" — ghép nó với một nền khác là hai token trôi\n * độc lập, và sẽ có ngày lệch nhau.\n *\n * ⚠ Bản trước ghép `bg-primary-ink` + `text-control-check`. Ở MỌI bảng màu trừ một, hai token đó\n * bằng nhau (`--primary-ink: var(--primary)` = `--control-on`) nên cặp sai vẫn cho ra đúng màu —\n * lỗi ngủ yên. Đến khi bảng tương phản cao sáng riêng `--control-on` lên #B48700 mà `--primary-ink`\n * ở lại gold-500 (nó còn phải làm CHỮ, ngưỡng 4,5:1) thì mới lộ: chữ mực trên #7A5800 = 2,52:1 ✗.\n * Cổng tương phản bắt được; mắt thường thì không.\n *\n * nền ô ⇄ nền lịch : 1,86:1 (bảng duyệt — khoản đã chấp nhận, cùng nhóm với rãnh Switch BẬT)\n * 3,28:1 (bảng tương phản cao) ✓\n * chữ mực trên nền ô : 10,19:1 (bảng duyệt) · 5,77:1 (tương phản cao) ✓\n *\n * ⚠ CÒN TỒN, cố ý chưa động: `today` và `range_middle` dùng `bg-accent` — 1,09:1 (light) /\n * 1,17:1 (dark) so với nền lịch. Đưa dải giữa lên 3:1 thì cả tuần biến thành một khối đặc,\n * nên đây là quyết định THIẾT KẾ (thêm viền/gạch chân cho \"hôm nay\"?) chứ không sửa được bằng\n * đổi token — để dành cho vòng riêng.\n */\n\nexport type CalendarProps = React.ComponentProps<typeof DayPicker> & {\n /** Biến thể Button dùng cho nút điều hướng tháng. */\n buttonVariant?: ButtonProps[\"variant\"];\n};\nexport type CalendarDayButtonProps = React.ComponentProps<typeof DayButton>;\n\n/** Bảng chữ `calendar` → prop `labels` của react-day-picker (cùng cách định dạng ngày `PPPP` của nó). */\nfunction toDayPickerLabels(t: CalendarMessages): Partial<Labels> {\n return {\n labelNav: () => t.navigation,\n labelPrevious: () => t.previousMonth,\n labelNext: () => t.nextMonth,\n labelMonthDropdown: () => t.monthDropdown,\n labelYearDropdown: () => t.yearDropdown,\n labelDayButton: (date, modifiers, options, dateLib) =>\n t.day((dateLib ?? new DateLib(options)).format(date, \"PPPP\"), {\n today: modifiers.today === true,\n selected: modifiers.selected === true\n }),\n // Ô ngày khi lịch KHÔNG tương tác được — react-day-picker không báo \"đã chọn\" ở đây.\n labelGridcell: (date, modifiers, options, dateLib) =>\n t.day((dateLib ?? new DateLib(options)).format(date, \"PPPP\"), {\n today: modifiers?.today === true,\n selected: false\n }),\n labelWeekNumber: (weekNumber) => t.weekNumber(weekNumber),\n labelWeekNumberHeader: () => t.weekNumberHeader\n };\n}\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = \"label\",\n buttonVariant = \"ghost\",\n formatters,\n components,\n locale: localeProp,\n labels: labelsProp,\n ...props\n}: CalendarProps) {\n const defaultClassNames = getDefaultClassNames();\n const { dateFnsLocale } = useComitorLocale();\n const t = useComitorMessages(\"calendar\");\n const locale: Partial<DayPickerLocale> = localeProp ?? dateFnsLocale;\n const localeCode = locale.code ?? dateFnsLocale.code;\n // Locale của `react-day-picker/locale` mang sẵn bản dịch nhãn → để nó thắng bảng của provider.\n // Prop `labels` của app thì thắng cả hai (react-day-picker xếp prop trên locale).\n const hasLocaleLabels = locale.labels !== undefined;\n const labels = React.useMemo(\n () => (hasLocaleLabels ? labelsProp : { ...toDayPickerLabels(t), ...labelsProp }),\n [hasLocaleLabels, t, labelsProp]\n );\n\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n locale={locale}\n labels={labels}\n className={cn(\n \"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent\",\n String.raw`rtl:**:[.rdp-button\\_next>svg]:rotate-180`,\n String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\n className\n )}\n captionLayout={captionLayout}\n formatters={{\n formatMonthDropdown: (date) => date.toLocaleString(localeCode, { month: \"short\" }),\n ...formatters\n }}\n classNames={{\n root: cn(\"w-fit\", defaultClassNames.root),\n months: cn(\"flex gap-4 flex-col md:flex-row relative\", defaultClassNames.months),\n month: cn(\"flex flex-col w-full gap-4\", defaultClassNames.month),\n nav: cn(\"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between\", defaultClassNames.nav),\n button_previous: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none\",\n defaultClassNames.button_previous\n ),\n button_next: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none\",\n defaultClassNames.button_next\n ),\n month_caption: cn(\n \"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)\",\n defaultClassNames.month_caption\n ),\n dropdowns: cn(\n \"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5\",\n defaultClassNames.dropdowns\n ),\n dropdown_root: cn(\n \"relative has-focus:outline-none border border-input shadow-xs has-focus:ring-2 has-focus:ring-ring/50 has-focus:ring-offset-2 has-focus:ring-offset-background rounded-md\",\n defaultClassNames.dropdown_root\n ),\n dropdown: cn(\"absolute bg-popover inset-0 opacity-0\", defaultClassNames.dropdown),\n caption_label: cn(\n \"select-none font-medium\",\n captionLayout === \"label\"\n ? \"text-sm\"\n : \"rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5\",\n defaultClassNames.caption_label\n ),\n table: \"w-full border-collapse\",\n weekdays: cn(\"flex\", defaultClassNames.weekdays),\n weekday: cn(\n \"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none\",\n defaultClassNames.weekday\n ),\n week: cn(\"flex w-full mt-2\", defaultClassNames.week),\n week_number_header: cn(\"select-none w-(--cell-size)\", defaultClassNames.week_number_header),\n week_number: cn(\"text-[0.8rem] select-none text-muted-foreground\", defaultClassNames.week_number),\n day: cn(\n \"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none\",\n defaultClassNames.day\n ),\n range_start: cn(\"rounded-l-md bg-accent\", defaultClassNames.range_start),\n range_middle: cn(\"rounded-none\", defaultClassNames.range_middle),\n range_end: cn(\"rounded-r-md bg-accent\", defaultClassNames.range_end),\n today: cn(\n \"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none\",\n defaultClassNames.today\n ),\n outside: cn(\"text-muted-foreground aria-selected:text-muted-foreground\", defaultClassNames.outside),\n disabled: cn(\"text-muted-foreground opacity-50\", defaultClassNames.disabled),\n hidden: cn(\"invisible\", defaultClassNames.hidden),\n ...classNames\n }}\n components={{\n Root: ({ className: rootClassName, rootRef, ...rootProps }) => {\n return <div data-slot=\"calendar\" ref={rootRef} className={cn(rootClassName)} {...rootProps} />;\n },\n Chevron: ({ className: chevronClassName, orientation, ...chevronProps }) => {\n if (orientation === \"left\") {\n return <ChevronLeftIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n }\n\n if (orientation === \"right\") {\n return <ChevronRightIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n }\n\n return <ChevronDownIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n },\n DayButton: CalendarDayButton,\n WeekNumber: ({ children, week: _week, ...weekProps }) => {\n return (\n <td {...weekProps}>\n <div className=\"flex size-(--cell-size) items-center justify-center text-center\">{children}</div>\n </td>\n );\n },\n ...components\n }}\n {...props}\n />\n );\n}\n\n/**\n * Ô ngày.\n *\n * ─ `[&>span]:opacity-90` — MỨC MỜ TỐI ĐA CÒN ĐẠT AA ─\n * `<span>` là nội dung phụ app truyền vào ô ngày (số việc, chấm sự kiện…), cỡ `text-xs` = 12px\n * nên chịu ngưỡng CHỮ 4,5:1. Nút ngày VẪN bấm được và vẫn nhận focus → `opacity-*` chồng lên nó\n * KHÔNG được miễn trừ theo 1.4.3. Ca tệ nhất là ngày ĐÃ CHỌN ở theme sáng của bảng TƯƠNG PHẢN\n * CAO — chữ mực trên `bg-control-on` #B48700:\n *\n * opacity-80 → 4,43:1 ✗ ← mức cũ, KHÔNG còn đạt\n * opacity-85 → 4,79:1 ✓\n * opacity-90 → 5,15:1 ✓ ← đang dùng, còn biên cho lần chỉnh gold sau\n *\n * ⚠ Bộ số cũ ở đây (70% → 4,12:1 · 80% → 4,85:1) đo trên MỘT CẶP KHÁC: nền #7A5800 với chữ\n * TRẮNG. Từ khi bảng tương phản cao bỏ gold-500 (nâu-ô-liu, xem khối trên) và tô control bằng\n * #B48700 thì chữ quay về MỰC, ca tệ nhất chặt lại và 80% rơi xuống dưới ngưỡng. Đây là kiểu lỗi\n * mà một lần đổi token ở styles.css kéo theo, và chỉ cổng tương phản mới thấy.\n *\n * Hai bảng còn lại rộng rãi ở 90%: bảng duyệt (mực trên #E8B824) 8,60:1, dark (mực trên #F5D060)\n * 10,43:1. Các trạng thái khác cũng thoải mái: giữa khoảng & hôm nay 9,70:1 (light) · 10,05:1\n * (dark), ngày thường 10,26:1 · 11,41:1.\n */\nfunction CalendarDayButton({ className, day, modifiers, ...props }: CalendarDayButtonProps) {\n const defaultClassNames = getDefaultClassNames();\n\n const ref = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (modifiers.focused) ref.current?.focus();\n }, [modifiers.focused]);\n\n return (\n <Button\n ref={ref}\n variant=\"ghost\"\n size=\"icon\"\n data-day={day.date.toLocaleDateString()}\n data-selected-single={\n modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle\n }\n data-range-start={modifiers.range_start}\n data-range-end={modifiers.range_end}\n data-range-middle={modifiers.range_middle}\n className={cn(\n \"data-[selected-single=true]:bg-control-on data-[selected-single=true]:text-control-check data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-control-on data-[range-start=true]:text-control-check data-[range-end=true]:bg-control-on data-[range-end=true]:text-control-check group-data-[focused=true]/day:outline-none group-data-[focused=true]/day:ring-ring/50 group-data-[focused=true]/day:ring-offset-2 group-data-[focused=true]/day:ring-offset-background dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-2 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-90\",\n defaultClassNames.day,\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Calendar, CalendarDayButton };\n"],"mappings":"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","names":[]}
|
|
@@ -7,7 +7,8 @@ import 'class-variance-authority';
|
|
|
7
7
|
/**
|
|
8
8
|
* Carousel (embla) — băng chuyền ngang/dọc, điều hướng được bằng phím ←/→.
|
|
9
9
|
* `useCarousel()` được export để app tự dựng chấm chỉ báo hoặc nút điều khiển riêng.
|
|
10
|
-
* Chuỗi cho trình đọc màn hình
|
|
10
|
+
* Chuỗi cho trình đọc màn hình theo `ComitorLocaleProvider` (namespace `carousel`, mặc định tiếng
|
|
11
|
+
* Anh); override qua prop `srLabel` / `aria-roledescription`.
|
|
11
12
|
*/
|
|
12
13
|
type CarouselApi = UseEmblaCarouselType[1];
|
|
13
14
|
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
|
|
@@ -40,7 +41,7 @@ declare function useCarousel(): CarouselContextProps;
|
|
|
40
41
|
declare function Carousel({ orientation, opts, setApi, plugins, className, children, ...props }: CarouselProps): React.JSX.Element;
|
|
41
42
|
declare function CarouselContent({ className, ...props }: CarouselContentProps): React.JSX.Element;
|
|
42
43
|
declare function CarouselItem({ className, ...props }: CarouselItemProps): React.JSX.Element;
|
|
43
|
-
declare function CarouselPrevious({ className, variant, size, srLabel, ...props }: CarouselPreviousProps): React.JSX.Element;
|
|
44
|
-
declare function CarouselNext({ className, variant, size, srLabel, ...props }: CarouselNextProps): React.JSX.Element;
|
|
44
|
+
declare function CarouselPrevious({ className, variant, size, srLabel: srLabelProp, ...props }: CarouselPreviousProps): React.JSX.Element;
|
|
45
|
+
declare function CarouselNext({ className, variant, size, srLabel: srLabelProp, ...props }: CarouselNextProps): React.JSX.Element;
|
|
45
46
|
|
|
46
47
|
export { Carousel, type CarouselApi, CarouselContent, type CarouselContentProps, type CarouselContextProps, CarouselItem, type CarouselItemProps, CarouselNext, type CarouselNextProps, type CarouselOptions, type CarouselOwnProps, type CarouselPlugin, CarouselPrevious, type CarouselPreviousProps, type CarouselProps, useCarousel };
|
|
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import useEmblaCarousel from "embla-carousel-react";
|
|
4
4
|
import { ArrowLeft, ArrowRight } from "lucide-react";
|
|
5
5
|
import * as React from "react";
|
|
6
|
+
import { useComitorMessages } from "../../i18n/locale-provider.js";
|
|
6
7
|
import { cn } from "../../lib/cn.js";
|
|
7
8
|
import { Button } from "./button.js";
|
|
8
9
|
const CarouselContext = React.createContext(null);
|
|
@@ -14,6 +15,7 @@ function useCarousel() {
|
|
|
14
15
|
return context;
|
|
15
16
|
}
|
|
16
17
|
function Carousel({ orientation = "horizontal", opts, setApi, plugins, className, children, ...props }) {
|
|
18
|
+
const t = useComitorMessages("carousel");
|
|
17
19
|
const [carouselRef, api] = useEmblaCarousel(
|
|
18
20
|
{
|
|
19
21
|
...opts,
|
|
@@ -78,7 +80,7 @@ function Carousel({ orientation = "horizontal", opts, setApi, plugins, className
|
|
|
78
80
|
onKeyDownCapture: handleKeyDown,
|
|
79
81
|
className: cn("relative", className),
|
|
80
82
|
role: "region",
|
|
81
|
-
"aria-roledescription":
|
|
83
|
+
"aria-roledescription": t.roleDescription,
|
|
82
84
|
"data-slot": "carousel",
|
|
83
85
|
...props,
|
|
84
86
|
children
|
|
@@ -93,11 +95,12 @@ function CarouselContent({ className, ...props }) {
|
|
|
93
95
|
}
|
|
94
96
|
function CarouselItem({ className, ...props }) {
|
|
95
97
|
const { orientation } = useCarousel();
|
|
98
|
+
const t = useComitorMessages("carousel");
|
|
96
99
|
return /* @__PURE__ */ jsx(
|
|
97
100
|
"div",
|
|
98
101
|
{
|
|
99
102
|
role: "group",
|
|
100
|
-
"aria-roledescription":
|
|
103
|
+
"aria-roledescription": t.slideRoleDescription,
|
|
101
104
|
"data-slot": "carousel-item",
|
|
102
105
|
className: cn("min-w-0 shrink-0 grow-0 basis-full", orientation === "horizontal" ? "pl-4" : "pt-4", className),
|
|
103
106
|
...props
|
|
@@ -108,10 +111,12 @@ function CarouselPrevious({
|
|
|
108
111
|
className,
|
|
109
112
|
variant = "outline",
|
|
110
113
|
size = "icon",
|
|
111
|
-
srLabel
|
|
114
|
+
srLabel: srLabelProp,
|
|
112
115
|
...props
|
|
113
116
|
}) {
|
|
114
117
|
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
|
|
118
|
+
const t = useComitorMessages("carousel");
|
|
119
|
+
const srLabel = srLabelProp ?? t.previousLabel;
|
|
115
120
|
return /* @__PURE__ */ jsxs(
|
|
116
121
|
Button,
|
|
117
122
|
{
|
|
@@ -137,10 +142,12 @@ function CarouselNext({
|
|
|
137
142
|
className,
|
|
138
143
|
variant = "outline",
|
|
139
144
|
size = "icon",
|
|
140
|
-
srLabel
|
|
145
|
+
srLabel: srLabelProp,
|
|
141
146
|
...props
|
|
142
147
|
}) {
|
|
143
148
|
const { orientation, scrollNext, canScrollNext } = useCarousel();
|
|
149
|
+
const t = useComitorMessages("carousel");
|
|
150
|
+
const srLabel = srLabelProp ?? t.nextLabel;
|
|
144
151
|
return /* @__PURE__ */ jsxs(
|
|
145
152
|
Button,
|
|
146
153
|
{
|