@comitor/ui 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +35 -0
- package/README.md +67 -24
- package/dist/chart/chart.d.ts +6 -1
- package/dist/chart/chart.js +3 -1
- package/dist/chart/chart.js.map +1 -1
- package/dist/chart/comitor-chart.d.ts +4 -1
- package/dist/chart/comitor-chart.js +3 -1
- package/dist/chart/comitor-chart.js.map +1 -1
- package/dist/components/composite/avatar-group.d.ts +9 -6
- package/dist/components/composite/avatar-group.js +6 -2
- package/dist/components/composite/avatar-group.js.map +1 -1
- package/dist/components/composite/combobox.d.ts +11 -11
- package/dist/components/composite/combobox.js +25 -11
- package/dist/components/composite/combobox.js.map +1 -1
- package/dist/components/composite/command-palette-constants.d.ts +15 -5
- package/dist/components/composite/command-palette-constants.js +3 -2
- package/dist/components/composite/command-palette-constants.js.map +1 -1
- package/dist/components/composite/command-palette.d.ts +2 -2
- package/dist/components/composite/command-palette.js +8 -5
- package/dist/components/composite/command-palette.js.map +1 -1
- package/dist/components/composite/confirm-dialog.d.ts +8 -8
- package/dist/components/composite/confirm-dialog.js +8 -3
- package/dist/components/composite/confirm-dialog.js.map +1 -1
- package/dist/components/composite/copy-button.d.ts +7 -3
- package/dist/components/composite/copy-button.js +7 -3
- package/dist/components/composite/copy-button.js.map +1 -1
- package/dist/components/composite/data-table-constants.d.ts +7 -3
- package/dist/components/composite/data-table-constants.js +2 -9
- package/dist/components/composite/data-table-constants.js.map +1 -1
- package/dist/components/composite/data-table.js +3 -3
- package/dist/components/composite/data-table.js.map +1 -1
- package/dist/components/composite/date-picker-constants.d.ts +32 -9
- package/dist/components/composite/date-picker-constants.js +16 -11
- package/dist/components/composite/date-picker-constants.js.map +1 -1
- package/dist/components/composite/date-picker.d.ts +19 -10
- package/dist/components/composite/date-picker.js +33 -9
- package/dist/components/composite/date-picker.js.map +1 -1
- package/dist/components/composite/filter-chips.d.ts +4 -3
- package/dist/components/composite/filter-chips.js +6 -2
- package/dist/components/composite/filter-chips.js.map +1 -1
- package/dist/components/composite/inline-edit.d.ts +14 -3
- package/dist/components/composite/inline-edit.js +8 -4
- package/dist/components/composite/inline-edit.js.map +1 -1
- package/dist/components/composite/relative-time.d.ts +4 -4
- package/dist/components/composite/relative-time.js +4 -2
- package/dist/components/composite/relative-time.js.map +1 -1
- package/dist/components/composite/route-tabs-nav.d.ts +9 -0
- package/dist/components/composite/route-tabs-nav.js +11 -0
- package/dist/components/composite/route-tabs-nav.js.map +1 -0
- package/dist/components/composite/route-tabs.d.ts +4 -1
- package/dist/components/composite/route-tabs.js +3 -2
- package/dist/components/composite/route-tabs.js.map +1 -1
- package/dist/components/composite/search-filter-bar-constants.d.ts +18 -8
- package/dist/components/composite/search-filter-bar-constants.js +2 -14
- package/dist/components/composite/search-filter-bar-constants.js.map +1 -1
- package/dist/components/composite/search-filter-bar.d.ts +5 -2
- package/dist/components/composite/search-filter-bar.js +2 -2
- package/dist/components/composite/search-filter-bar.js.map +1 -1
- package/dist/components/composite/stat-card.d.ts +1 -1
- package/dist/components/composite/table-pagination-constants.d.ts +16 -11
- package/dist/components/composite/table-pagination-constants.js +2 -12
- package/dist/components/composite/table-pagination-constants.js.map +1 -1
- package/dist/components/composite/table-pagination.d.ts +6 -3
- package/dist/components/composite/table-pagination.js +3 -5
- package/dist/components/composite/table-pagination.js.map +1 -1
- package/dist/components/composite/toast.d.ts +4 -2
- package/dist/components/composite/toast.js +9 -1
- package/dist/components/composite/toast.js.map +1 -1
- package/dist/components/ui/action-bar-constants.d.ts +35 -0
- package/dist/components/ui/action-bar-constants.js +6 -0
- package/dist/components/ui/action-bar-constants.js.map +1 -0
- package/dist/components/ui/action-bar.d.ts +68 -0
- package/dist/components/ui/action-bar.js +522 -0
- package/dist/components/ui/action-bar.js.map +1 -0
- package/dist/components/ui/alert.d.ts +1 -1
- package/dist/components/ui/badge-overflow.d.ts +30 -0
- package/dist/components/ui/badge-overflow.js +181 -0
- package/dist/components/ui/badge-overflow.js.map +1 -0
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/banner.d.ts +80 -0
- package/dist/components/ui/banner.js +520 -0
- package/dist/components/ui/banner.js.map +1 -0
- 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.d.ts +50 -0
- package/dist/components/ui/circular-progress.js +266 -0
- package/dist/components/ui/circular-progress.js.map +1 -0
- 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 +39 -0
- package/dist/components/ui/cropper-constants.js +6 -0
- package/dist/components/ui/cropper-constants.js.map +1 -0
- package/dist/components/ui/cropper.d.ts +107 -0
- package/dist/components/ui/cropper.js +1269 -0
- package/dist/components/ui/cropper.js.map +1 -0
- 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/drawer.d.ts +1 -1
- package/dist/components/ui/drawer.js +2 -2
- package/dist/components/ui/drawer.js.map +1 -1
- package/dist/components/ui/item.d.ts +2 -2
- package/dist/components/ui/kanban-constants.d.ts +67 -0
- package/dist/components/ui/kanban-constants.js +6 -0
- package/dist/components/ui/kanban-constants.js.map +1 -0
- package/dist/components/ui/kanban.d.ts +72 -0
- package/dist/components/ui/kanban.js +787 -0
- package/dist/components/ui/kanban.js.map +1 -0
- package/dist/components/ui/key-value-constants.d.ts +37 -0
- package/dist/components/ui/key-value-constants.js +6 -0
- package/dist/components/ui/key-value-constants.js.map +1 -0
- package/dist/components/ui/key-value.d.ts +96 -0
- package/dist/components/ui/key-value.js +641 -0
- package/dist/components/ui/key-value.js.map +1 -0
- package/dist/components/ui/mask-input-constants.d.ts +98 -0
- package/dist/components/ui/mask-input-constants.js +600 -0
- package/dist/components/ui/mask-input-constants.js.map +1 -0
- package/dist/components/ui/mask-input.d.ts +39 -0
- package/dist/components/ui/mask-input.js +560 -0
- package/dist/components/ui/mask-input.js.map +1 -0
- package/dist/components/ui/mention.d.ts +32 -0
- package/dist/components/ui/mention.js +126 -0
- package/dist/components/ui/mention.js.map +1 -0
- 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 +65 -0
- package/dist/components/ui/phone-input-constants.js +6 -0
- package/dist/components/ui/phone-input-constants.js.map +1 -0
- package/dist/components/ui/phone-input.d.ts +92 -0
- package/dist/components/ui/phone-input.js +742 -0
- package/dist/components/ui/phone-input.js.map +1 -0
- package/dist/components/ui/progress.d.ts +2 -2
- package/dist/components/ui/rating.d.ts +57 -0
- package/dist/components/ui/rating.js +648 -0
- package/dist/components/ui/rating.js.map +1 -0
- package/dist/components/ui/responsive-dialog.d.ts +35 -0
- package/dist/components/ui/responsive-dialog.js +193 -0
- package/dist/components/ui/responsive-dialog.js.map +1 -0
- package/dist/components/ui/scroll-spy.d.ts +42 -0
- package/dist/components/ui/scroll-spy.js +336 -0
- package/dist/components/ui/scroll-spy.js.map +1 -0
- package/dist/components/ui/sheet.d.ts +2 -2
- package/dist/components/ui/sheet.js +10 -1
- package/dist/components/ui/sheet.js.map +1 -1
- package/dist/components/ui/spinner.js +4 -1
- package/dist/components/ui/spinner.js.map +1 -1
- package/dist/components/ui/stepper-constants.d.ts +34 -0
- package/dist/components/ui/stepper-constants.js +6 -0
- package/dist/components/ui/stepper-constants.js.map +1 -0
- package/dist/components/ui/stepper.d.ts +81 -0
- package/dist/components/ui/stepper.js +902 -0
- package/dist/components/ui/stepper.js.map +1 -0
- package/dist/components/ui/tags-input.d.ts +42 -0
- package/dist/components/ui/tags-input.js +129 -0
- package/dist/components/ui/tags-input.js.map +1 -0
- package/dist/components/ui/time-picker-constants.d.ts +51 -0
- package/dist/components/ui/time-picker-constants.js +6 -0
- package/dist/components/ui/time-picker-constants.js.map +1 -0
- package/dist/components/ui/time-picker.d.ts +120 -0
- package/dist/components/ui/time-picker.js +1618 -0
- package/dist/components/ui/time-picker.js.map +1 -0
- package/dist/components/ui/timeline.d.ts +32 -0
- package/dist/components/ui/timeline.js +559 -0
- package/dist/components/ui/timeline.js.map +1 -0
- package/dist/components/ui/toggle.d.ts +1 -1
- package/dist/components/ui/tour-constants.d.ts +44 -0
- package/dist/components/ui/tour-constants.js +6 -0
- package/dist/components/ui/tour-constants.js.map +1 -0
- package/dist/components/ui/tour.d.ts +125 -0
- package/dist/components/ui/tour.js +1207 -0
- package/dist/components/ui/tour.js.map +1 -0
- 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-cell-wrapper.js +1 -1
- package/dist/data-grid/data-grid-cell-wrapper.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-context-menu.js +1 -1
- package/dist/data-grid/data-grid-context-menu.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-paste-dialog.js +1 -1
- package/dist/data-grid/data-grid-paste-dialog.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-row.js +1 -1
- package/dist/data-grid/data-grid-row.js.map +1 -1
- package/dist/data-grid/data-grid-search.js +1 -1
- package/dist/data-grid/data-grid-search.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 +3 -4
- 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 +17 -20
- 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 +4 -4
- package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
- package/dist/data-grid/use-data-grid.d.ts +21 -6
- package/dist/data-grid/use-data-grid.js +11 -9
- package/dist/data-grid/use-data-grid.js.map +1 -1
- package/dist/data-grid/utils.d.ts +24 -4
- package/dist/data-grid/utils.js +15 -5
- package/dist/data-grid/utils.js.map +1 -1
- package/dist/i18n/locale-provider.d.ts +109 -0
- package/dist/i18n/locale-provider.js +47 -0
- package/dist/i18n/locale-provider.js.map +1 -0
- package/dist/i18n/locale.d.ts +27 -0
- package/dist/i18n/locale.js +10 -0
- package/dist/i18n/locale.js.map +1 -0
- package/dist/i18n/messages.d.ts +116 -0
- package/dist/i18n/messages.js +149 -0
- package/dist/i18n/messages.js.map +1 -0
- package/dist/i18n/namespaces/action-bar.d.ts +7 -0
- package/dist/i18n/namespaces/action-bar.js +15 -0
- package/dist/i18n/namespaces/action-bar.js.map +1 -0
- package/dist/i18n/namespaces/avatar-group.d.ts +14 -0
- package/dist/i18n/namespaces/avatar-group.js +13 -0
- package/dist/i18n/namespaces/avatar-group.js.map +1 -0
- package/dist/i18n/namespaces/badge-overflow.d.ts +12 -0
- package/dist/i18n/namespaces/badge-overflow.js +11 -0
- package/dist/i18n/namespaces/badge-overflow.js.map +1 -0
- package/dist/i18n/namespaces/banner.d.ts +9 -0
- package/dist/i18n/namespaces/banner.js +7 -0
- package/dist/i18n/namespaces/banner.js.map +1 -0
- package/dist/i18n/namespaces/breadcrumb.d.ts +11 -0
- package/dist/i18n/namespaces/breadcrumb.js +13 -0
- package/dist/i18n/namespaces/breadcrumb.js.map +1 -0
- package/dist/i18n/namespaces/calendar.d.ts +40 -0
- package/dist/i18n/namespaces/calendar.js +25 -0
- package/dist/i18n/namespaces/calendar.js.map +1 -0
- package/dist/i18n/namespaces/carousel.d.ts +15 -0
- package/dist/i18n/namespaces/carousel.js +17 -0
- package/dist/i18n/namespaces/carousel.js.map +1 -0
- package/dist/i18n/namespaces/combobox.d.ts +21 -0
- package/dist/i18n/namespaces/combobox.js +23 -0
- package/dist/i18n/namespaces/combobox.js.map +1 -0
- package/dist/i18n/namespaces/command-palette.d.ts +20 -0
- package/dist/i18n/namespaces/command-palette.js +19 -0
- package/dist/i18n/namespaces/command-palette.js.map +1 -0
- package/dist/i18n/namespaces/command.d.ts +11 -0
- package/dist/i18n/namespaces/command.js +13 -0
- package/dist/i18n/namespaces/command.js.map +1 -0
- package/dist/i18n/namespaces/confirm-dialog.d.ts +13 -0
- package/dist/i18n/namespaces/confirm-dialog.js +15 -0
- package/dist/i18n/namespaces/confirm-dialog.js.map +1 -0
- package/dist/i18n/namespaces/contrast.d.ts +7 -0
- package/dist/i18n/namespaces/contrast.js +13 -0
- package/dist/i18n/namespaces/contrast.js.map +1 -0
- package/dist/i18n/namespaces/copy-button.d.ts +11 -0
- package/dist/i18n/namespaces/copy-button.js +13 -0
- package/dist/i18n/namespaces/copy-button.js.map +1 -0
- package/dist/i18n/namespaces/cropper.d.ts +7 -0
- package/dist/i18n/namespaces/cropper.js +17 -0
- package/dist/i18n/namespaces/cropper.js.map +1 -0
- package/dist/i18n/namespaces/data-table.d.ts +7 -0
- package/dist/i18n/namespaces/data-table.js +23 -0
- package/dist/i18n/namespaces/data-table.js.map +1 -0
- package/dist/i18n/namespaces/date-picker.d.ts +42 -0
- package/dist/i18n/namespaces/date-picker.js +35 -0
- package/dist/i18n/namespaces/date-picker.js.map +1 -0
- package/dist/i18n/namespaces/dialog.d.ts +9 -0
- package/dist/i18n/namespaces/dialog.js +7 -0
- package/dist/i18n/namespaces/dialog.js.map +1 -0
- package/dist/i18n/namespaces/display-axis.d.ts +25 -0
- package/dist/i18n/namespaces/display-axis.js +21 -0
- package/dist/i18n/namespaces/display-axis.js.map +1 -0
- package/dist/i18n/namespaces/file-upload.d.ts +11 -0
- package/dist/i18n/namespaces/file-upload.js +19 -0
- package/dist/i18n/namespaces/file-upload.js.map +1 -0
- package/dist/i18n/namespaces/filter-chips.d.ts +11 -0
- package/dist/i18n/namespaces/filter-chips.js +13 -0
- package/dist/i18n/namespaces/filter-chips.js.map +1 -0
- package/dist/i18n/namespaces/image-upload-field.d.ts +12 -0
- package/dist/i18n/namespaces/image-upload-field.js +23 -0
- package/dist/i18n/namespaces/image-upload-field.js.map +1 -0
- package/dist/i18n/namespaces/inline-edit.d.ts +15 -0
- package/dist/i18n/namespaces/inline-edit.js +17 -0
- package/dist/i18n/namespaces/inline-edit.js.map +1 -0
- package/dist/i18n/namespaces/kanban.d.ts +11 -0
- package/dist/i18n/namespaces/kanban.js +39 -0
- package/dist/i18n/namespaces/kanban.js.map +1 -0
- package/dist/i18n/namespaces/key-value.d.ts +10 -0
- package/dist/i18n/namespaces/key-value.js +19 -0
- package/dist/i18n/namespaces/key-value.js.map +1 -0
- package/dist/i18n/namespaces/mention.d.ts +9 -0
- package/dist/i18n/namespaces/mention.js +7 -0
- package/dist/i18n/namespaces/mention.js.map +1 -0
- package/dist/i18n/namespaces/pagination.d.ts +19 -0
- package/dist/i18n/namespaces/pagination.js +21 -0
- package/dist/i18n/namespaces/pagination.js.map +1 -0
- package/dist/i18n/namespaces/phone-input.d.ts +10 -0
- package/dist/i18n/namespaces/phone-input.js +17 -0
- package/dist/i18n/namespaces/phone-input.js.map +1 -0
- package/dist/i18n/namespaces/rating.d.ts +12 -0
- package/dist/i18n/namespaces/rating.js +11 -0
- package/dist/i18n/namespaces/rating.js.map +1 -0
- package/dist/i18n/namespaces/route-tabs.d.ts +9 -0
- package/dist/i18n/namespaces/route-tabs.js +7 -0
- package/dist/i18n/namespaces/route-tabs.js.map +1 -0
- package/dist/i18n/namespaces/scroll-spy.d.ts +9 -0
- package/dist/i18n/namespaces/scroll-spy.js +7 -0
- package/dist/i18n/namespaces/scroll-spy.js.map +1 -0
- package/dist/i18n/namespaces/search-filter-bar.d.ts +15 -0
- package/dist/i18n/namespaces/search-filter-bar.js +32 -0
- package/dist/i18n/namespaces/search-filter-bar.js.map +1 -0
- package/dist/i18n/namespaces/sheet.d.ts +9 -0
- package/dist/i18n/namespaces/sheet.js +7 -0
- package/dist/i18n/namespaces/sheet.js.map +1 -0
- package/dist/i18n/namespaces/shell.d.ts +17 -0
- package/dist/i18n/namespaces/shell.js +75 -0
- package/dist/i18n/namespaces/shell.js.map +1 -0
- package/dist/i18n/namespaces/spinner.d.ts +9 -0
- package/dist/i18n/namespaces/spinner.js +7 -0
- package/dist/i18n/namespaces/spinner.js.map +1 -0
- package/dist/i18n/namespaces/stepper.d.ts +7 -0
- package/dist/i18n/namespaces/stepper.js +15 -0
- package/dist/i18n/namespaces/stepper.js.map +1 -0
- package/dist/i18n/namespaces/table-pagination.d.ts +7 -0
- package/dist/i18n/namespaces/table-pagination.js +29 -0
- package/dist/i18n/namespaces/table-pagination.js.map +1 -0
- package/dist/i18n/namespaces/tags-input.d.ts +12 -0
- package/dist/i18n/namespaces/tags-input.js +11 -0
- package/dist/i18n/namespaces/tags-input.js.map +1 -0
- package/dist/i18n/namespaces/time-picker.d.ts +15 -0
- package/dist/i18n/namespaces/time-picker.js +25 -0
- package/dist/i18n/namespaces/time-picker.js.map +1 -0
- package/dist/i18n/namespaces/toast.d.ts +15 -0
- package/dist/i18n/namespaces/toast.js +7 -0
- package/dist/i18n/namespaces/toast.js.map +1 -0
- package/dist/i18n/namespaces/tour.d.ts +7 -0
- package/dist/i18n/namespaces/tour.js +21 -0
- package/dist/i18n/namespaces/tour.js.map +1 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +66 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/compose-refs.js.map +1 -0
- package/dist/lib/use-as-ref.js.map +1 -0
- package/dist/lib/use-is-mobile.d.ts +10 -0
- package/dist/lib/use-is-mobile.js +21 -0
- package/dist/lib/use-is-mobile.js.map +1 -0
- package/dist/lib/use-isomorphic-layout-effect.js.map +1 -0
- package/dist/lib/use-lazy-ref.js.map +1 -0
- package/dist/lib/visually-hidden-input.d.ts +12 -0
- package/dist/lib/visually-hidden-input.js +105 -0
- package/dist/lib/visually-hidden-input.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/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 +8 -3
- package/styles.css +19 -0
- package/dist/data-grid/compose-refs.js.map +0 -1
- package/dist/data-grid/use-as-ref.js.map +0 -1
- package/dist/data-grid/use-isomorphic-layout-effect.js.map +0 -1
- package/dist/data-grid/use-lazy-ref.js.map +0 -1
- /package/dist/{data-grid → lib}/compose-refs.d.ts +0 -0
- /package/dist/{data-grid → lib}/compose-refs.js +0 -0
- /package/dist/{data-grid → lib}/use-as-ref.d.ts +0 -0
- /package/dist/{data-grid → lib}/use-as-ref.js +0 -0
- /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.d.ts +0 -0
- /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.js +0 -0
- /package/dist/{data-grid → lib}/use-lazy-ref.d.ts +0 -0
- /package/dist/{data-grid → lib}/use-lazy-ref.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/cropper.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/cropper) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n */\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport { useComitorLabels } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { useLazyRef } from \"../../lib/use-lazy-ref.js\";\nimport type { CropperLabels } from \"./cropper-constants.js\";\n\nconst ROOT_NAME = \"Cropper\";\nconst ROOT_IMPL_NAME = \"CropperImpl\";\nconst IMAGE_NAME = \"CropperImage\";\nconst VIDEO_NAME = \"CropperVideo\";\nconst AREA_NAME = \"CropperArea\";\n\ninterface Point {\n x: number;\n y: number;\n}\n\ninterface GestureEvent extends UIEvent {\n rotation: number;\n scale: number;\n clientX: number;\n clientY: number;\n}\n\ninterface Size {\n width: number;\n height: number;\n}\n\ninterface Area {\n width: number;\n height: number;\n x: number;\n y: number;\n}\n\ninterface MediaSize {\n width: number;\n height: number;\n naturalWidth: number;\n naturalHeight: number;\n}\n\ntype Shape = \"rectangle\" | \"circle\";\ntype ObjectFit = \"contain\" | \"cover\" | \"horizontal-cover\" | \"vertical-cover\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\nconst MAX_CACHE_SIZE = 200;\n\n/*\n * ⚠ Thêm so với thượng nguồn: phím + / - để thu phóng (xem `onKeyDown` của `CropperImpl`).\n * `=` và `_` là cùng phím với `+` / `-` khi KHÔNG giữ Shift trên bàn phím US.\n */\nconst ZOOM_IN_KEYS = new Set([\"+\", \"=\"]);\nconst ZOOM_OUT_KEYS = new Set([\"-\", \"_\"]);\nconst KEYBOARD_ZOOM_STEP = 0.1;\n\n/*\n * ⚠ Khác thượng nguồn: bản gốc chụp `window.devicePixelRatio` vào một HẰNG ở cấp module\n * (`const DPR = typeof window …`). Hệ quả: module chạm `window` ngay lúc nạp; server luôn ra 1 còn\n * client ra 2 nên `style` của ảnh lệch nhau lúc hydrate khi bật `snapPixels`; và zoom trình duyệt\n * thì con số không bao giờ cập nhật. Nay đọc LƯỜI — trong handler/effect gọi hàm này, còn lượt\n * render đi qua `useDevicePixelRatio` (server và lượt hydrate luôn nhận 1).\n */\nfunction getDevicePixelRatio(): number {\n return typeof window !== \"undefined\" ? window.devicePixelRatio || 1 : 1;\n}\n\nfunction getServerDevicePixelRatio(): number {\n return 1;\n}\n\nfunction subscribeToDevicePixelRatio(onChange: () => void) {\n // Zoom trình duyệt đổi DPR và bắn `resize`.\n window.addEventListener(\"resize\", onChange);\n return () => window.removeEventListener(\"resize\", onChange);\n}\n\nfunction useDevicePixelRatio(): number {\n return React.useSyncExternalStore(subscribeToDevicePixelRatio, getDevicePixelRatio, getServerDevicePixelRatio);\n}\n\nconst rotationSizeCache = new Map<string, Size>();\nconst cropSizeCache = new Map<string, Size>();\nconst croppedAreaCache = new Map<string, { croppedAreaPercentages: Area; croppedAreaPixels: Area }>();\nconst onPositionClampCache = new Map<string, Point>();\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction quantize(n: number, step = 2 / getDevicePixelRatio()): number {\n return Math.round(n / step) * step;\n}\n\nfunction quantizePosition(n: number, step = 4 / getDevicePixelRatio()): number {\n return Math.round(n / step) * step;\n}\n\nfunction quantizeZoom(n: number, step = 0.01): number {\n return Math.round(n / step) * step;\n}\n\nfunction quantizeRotation(n: number, step = 1.0): number {\n return Math.round(n / step) * step;\n}\n\nfunction snapToDevicePixel(n: number, dpr: number): number {\n return Math.round(n * dpr) / dpr;\n}\n\nfunction lruGet<K, V>(map: Map<K, V>, key: K): V | undefined {\n const v = map.get(key);\n if (v !== undefined) {\n map.delete(key);\n map.set(key, v);\n }\n return v;\n}\n\nfunction lruSet<K, V>(map: Map<K, V>, key: K, val: V, max = MAX_CACHE_SIZE): void {\n if (map.has(key)) {\n map.delete(key);\n }\n map.set(key, val);\n if (map.size > max) {\n const firstKey = map.keys().next().value;\n if (firstKey !== undefined) {\n map.delete(firstKey);\n }\n }\n}\n\nfunction getDistanceBetweenPoints(pointA: Point, pointB: Point): number {\n return Math.sqrt((pointA.y - pointB.y) ** 2 + (pointA.x - pointB.x) ** 2);\n}\n\nfunction getCenter(a: Point, b: Point): Point {\n return {\n x: (b.x + a.x) * 0.5,\n y: (b.y + a.y) * 0.5\n };\n}\n\nfunction getRotationBetweenPoints(pointA: Point, pointB: Point): number {\n return (Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x) * 180) / Math.PI;\n}\n\nfunction getRadianAngle(degreeValue: number): number {\n return (degreeValue * Math.PI) / 180;\n}\n\nfunction rotateSize(width: number, height: number, rotation: number): Size {\n const cacheKey = `${quantize(width)}-${quantize(height)}-${quantizeRotation(rotation)}`;\n\n const cached = lruGet(rotationSizeCache, cacheKey);\n if (cached) {\n return cached;\n }\n const rotRad = getRadianAngle(rotation);\n const cosRot = Math.cos(rotRad);\n const sinRot = Math.sin(rotRad);\n\n const result: Size = {\n width: Math.abs(cosRot * width) + Math.abs(sinRot * height),\n height: Math.abs(sinRot * width) + Math.abs(cosRot * height)\n };\n\n lruSet(rotationSizeCache, cacheKey, result, MAX_CACHE_SIZE);\n return result;\n}\n\nfunction getCropSize(\n mediaWidth: number,\n mediaHeight: number,\n contentWidth: number,\n contentHeight: number,\n aspect: number,\n rotation = 0\n): Size {\n const cacheKey = `${quantize(mediaWidth, 8)}-${quantize(mediaHeight, 8)}-${quantize(contentWidth, 8)}-${quantize(contentHeight, 8)}-${quantize(aspect, 0.01)}-${quantizeRotation(rotation)}`;\n\n const cached = lruGet(cropSizeCache, cacheKey);\n if (cached) {\n return cached;\n }\n const { width, height } = rotateSize(mediaWidth, mediaHeight, rotation);\n const fittingWidth = Math.min(width, contentWidth);\n const fittingHeight = Math.min(height, contentHeight);\n\n const result: Size =\n fittingWidth > fittingHeight * aspect\n ? {\n width: fittingHeight * aspect,\n height: fittingHeight\n }\n : {\n width: fittingWidth,\n height: fittingWidth / aspect\n };\n\n lruSet(cropSizeCache, cacheKey, result, MAX_CACHE_SIZE);\n return result;\n}\n\nfunction onPositionClamp(position: Point, mediaSize: Size, cropSize: Size, zoom: number, rotation = 0): Point {\n /*\n * ⚠ Khác thượng nguồn: bản gốc cache CẢ KẾT QUẢ, với khoá chứa vị trí đã lượng tử hoá (bước\n * 4/DPR px) — hai vị trí khác nhau rơi cùng một ô thì nhận lại kết quả của vị trí TRƯỚC. Phím\n * mũi tên dời 1px (Shift: 0,2px) nên vị trí mới luôn rơi vào ô của vị trí cũ và cache trả lại\n * đúng vị trí cũ: ảnh KẸT sau lần bấm đầu tiên — đo trên trình duyệt thật, 20 lần → phải đúng\n * 1px. Tức bàn phím không dời được ảnh. Nay chỉ cache GIỚI HẠN kẹp (thứ phụ thuộc kích thước,\n * zoom, góc — không phụ thuộc vị trí, cùng cách lượng tử hoá như bản gốc) và kẹp vị trí THẬT.\n */\n const cacheKey = `${quantize(mediaSize.width)}-${quantize(mediaSize.height)}-${quantize(cropSize.width)}-${quantize(cropSize.height)}-${quantizeZoom(zoom)}-${quantizeRotation(rotation)}`;\n\n let maxPosition = lruGet(onPositionClampCache, cacheKey);\n if (!maxPosition) {\n const { width, height } = rotateSize(mediaSize.width, mediaSize.height, rotation);\n\n maxPosition = {\n x: width * zoom * 0.5 - cropSize.width * 0.5,\n y: height * zoom * 0.5 - cropSize.height * 0.5\n };\n\n lruSet(onPositionClampCache, cacheKey, maxPosition, MAX_CACHE_SIZE);\n }\n\n return {\n x: clamp(position.x, -maxPosition.x, maxPosition.x),\n y: clamp(position.y, -maxPosition.y, maxPosition.y)\n };\n}\n\nfunction getCroppedArea(\n crop: Point,\n mediaSize: MediaSize,\n cropSize: Size,\n aspect: number,\n zoom: number,\n rotation = 0,\n allowOverflow = false\n): { croppedAreaPercentages: Area; croppedAreaPixels: Area } {\n const cacheKey = `${quantizePosition(crop.x)}-${quantizePosition(crop.y)}-${quantize(mediaSize.width)}-${quantize(mediaSize.height)}-${quantize(mediaSize.naturalWidth)}-${quantize(mediaSize.naturalHeight)}-${quantize(cropSize.width)}-${quantize(cropSize.height)}-${quantize(aspect, 0.01)}-${quantizeZoom(zoom)}-${quantizeRotation(rotation)}-${allowOverflow}`;\n\n const cached = lruGet(croppedAreaCache, cacheKey);\n\n if (cached) return cached;\n\n const onAreaLimit = !allowOverflow\n ? (max: number, value: number) => Math.min(max, Math.max(0, value))\n : (_max: number, value: number) => value;\n\n const mediaBBoxSize = rotateSize(mediaSize.width, mediaSize.height, rotation);\n const mediaNaturalBBoxSize = rotateSize(mediaSize.naturalWidth, mediaSize.naturalHeight, rotation);\n\n const croppedAreaPercentages: Area = {\n x: onAreaLimit(\n 100,\n (((mediaBBoxSize.width - cropSize.width / zoom) / 2 - crop.x / zoom) / mediaBBoxSize.width) * 100\n ),\n y: onAreaLimit(\n 100,\n (((mediaBBoxSize.height - cropSize.height / zoom) / 2 - crop.y / zoom) / mediaBBoxSize.height) * 100\n ),\n width: onAreaLimit(100, ((cropSize.width / mediaBBoxSize.width) * 100) / zoom),\n height: onAreaLimit(100, ((cropSize.height / mediaBBoxSize.height) * 100) / zoom)\n };\n\n const widthInPixels = Math.round(\n onAreaLimit(mediaNaturalBBoxSize.width, (croppedAreaPercentages.width * mediaNaturalBBoxSize.width) / 100)\n );\n const heightInPixels = Math.round(\n onAreaLimit(mediaNaturalBBoxSize.height, (croppedAreaPercentages.height * mediaNaturalBBoxSize.height) / 100)\n );\n const isImageWiderThanHigh = mediaNaturalBBoxSize.width >= mediaNaturalBBoxSize.height * aspect;\n\n const sizePixels: Size = isImageWiderThanHigh\n ? {\n width: Math.round(heightInPixels * aspect),\n height: heightInPixels\n }\n : {\n width: widthInPixels,\n height: Math.round(widthInPixels / aspect)\n };\n\n const croppedAreaPixels: Area = {\n ...sizePixels,\n x: Math.round(\n onAreaLimit(\n mediaNaturalBBoxSize.width - sizePixels.width,\n (croppedAreaPercentages.x * mediaNaturalBBoxSize.width) / 100\n )\n ),\n y: Math.round(\n onAreaLimit(\n mediaNaturalBBoxSize.height - sizePixels.height,\n (croppedAreaPercentages.y * mediaNaturalBBoxSize.height) / 100\n )\n )\n };\n\n const result = { croppedAreaPercentages, croppedAreaPixels };\n\n lruSet(croppedAreaCache, cacheKey, result, MAX_CACHE_SIZE);\n return result;\n}\n\ninterface StoreState {\n crop: Point;\n zoom: number;\n rotation: number;\n mediaSize: MediaSize | null;\n cropSize: Size | null;\n isDragging: boolean;\n isWheelZooming: boolean;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState;\n setState: <K extends keyof StoreState>(key: K, value: StoreState[K]) => void;\n notify: () => void;\n batch: (fn: () => void) => 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 \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\nfunction useStore<T>(selector: (state: StoreState) => T): T {\n // Tên trong thông báo lỗi là tên XUẤT RA NGOÀI (`useCropper`), không phải tên nội bộ.\n const store = useStoreContext(\"useCropper\");\n\n const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\ntype RootElement = React.ComponentRef<typeof CropperImpl>;\n\ninterface CropperContextValue {\n aspectRatio: number;\n minZoom: number;\n maxZoom: number;\n zoomSpeed: number;\n keyboardStep: number;\n shape: Shape;\n objectFit: ObjectFit;\n rootRef: React.RefObject<RootElement | null>;\n allowOverflow: boolean;\n preventScrollZoom: boolean;\n withGrid: boolean;\n labels: CropperLabels;\n}\n\nconst CropperContext = React.createContext<CropperContextValue | null>(null);\n\nfunction useCropperContext(consumerName: string) {\n const context = React.useContext(CropperContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface CropperProps extends DivProps {\n crop?: Point;\n zoom?: number;\n minZoom?: number;\n maxZoom?: number;\n zoomSpeed?: number;\n rotation?: number;\n keyboardStep?: number;\n aspectRatio?: number;\n shape?: Shape;\n objectFit?: ObjectFit;\n allowOverflow?: boolean;\n preventScrollZoom?: boolean;\n withGrid?: boolean;\n /**\n * Chuỗi a11y của vùng cắt — ghi đè từng khoá. Không truyền thì theo namespace `cropper` của\n * `ComitorLocaleProvider` (mặc định tiếng Anh).\n *\n * ⚠ Thêm so với thượng nguồn — xem `CropperLabels`.\n */\n labels?: Partial<CropperLabels>;\n onCropChange?: (crop: Point) => void;\n onCropSizeChange?: (cropSize: Size) => void;\n onCropAreaChange?: (croppedArea: Area, croppedAreaPixels: Area) => void;\n onCropComplete?: (croppedArea: Area, croppedAreaPixels: Area) => void;\n onZoomChange?: (zoom: number) => void;\n onRotationChange?: (rotation: number) => void;\n onMediaLoaded?: (mediaSize: MediaSize) => void;\n onInteractionStart?: () => void;\n onInteractionEnd?: () => void;\n onWheelZoom?: (event: WheelEvent) => void;\n}\n\n/**\n * Cropper — cắt ảnh/video: kéo để dời, lăn chuột/chụm hai ngón để phóng, phím mũi tên và + / -.\n *\n * Ở Comitor dùng để cắt ảnh đại diện hoặc logo công ty TRƯỚC khi tải lên. Component chỉ BÁO vùng\n * cắt (`onCropComplete(croppedArea, croppedAreaPixels)`) — việc vẽ ra canvas và tải lên là của\n * app. Cố ý không phụ thuộc gì ngoài peer bắt buộc để sống được ở entry chính; `ImageUploadField`\n * (entry `/uploader`, kéo theo `@uppy/*`) ghép với nó ở tầng app, không ở đây.\n *\n * `crop` / `zoom` / `rotation` là controlled KHI được truyền, uncontrolled khi bỏ trống.\n */\nfunction Cropper(props: CropperProps) {\n const {\n /*\n * ⚠ Khác thượng nguồn: bản gốc gán mặc định ngay ở đây (`crop = { x: 0, y: 0 }`, `zoom = 1`,\n * `rotation = 0`), nên nhánh `!== undefined` trong effect đồng bộ bên dưới LUÔN đúng, và\n * `{ x: 0, y: 0 }` là object MỚI mỗi lượt render. Hệ quả ở chế độ không kiểm soát: mỗi lần\n * component cha render lại — kể cả vì chính `onCropComplete` gọi `setState` ở cha — vị trí và\n * độ phóng bị kéo về mặc định ngay sau khi người dùng vừa chỉnh xong. Mặc định nay chỉ dùng\n * cho state KHỞI TẠO; effect chỉ đồng bộ prop nào thật sự được truyền, đúng ý của nhánh đó.\n */\n crop: cropProp,\n zoom: zoomProp,\n minZoom = 1,\n maxZoom = 3,\n zoomSpeed = 1,\n rotation: rotationProp,\n keyboardStep = 1,\n aspectRatio = 4 / 3,\n shape = \"rectangle\",\n objectFit = \"contain\",\n allowOverflow = false,\n preventScrollZoom = false,\n withGrid = false,\n labels: labelsProp,\n onCropChange,\n onCropSizeChange,\n onCropAreaChange,\n onCropComplete,\n onZoomChange,\n onRotationChange,\n onMediaLoaded,\n onInteractionStart,\n onInteractionEnd,\n className,\n ...rootProps\n } = props;\n\n const listenersRef = useLazyRef(() => new Set<() => void>());\n const stateRef = useLazyRef<StoreState>(() => ({\n crop: cropProp ?? { x: 0, y: 0 },\n zoom: zoomProp ?? 1,\n rotation: rotationProp ?? 0,\n mediaSize: null,\n cropSize: null,\n isDragging: false,\n isWheelZooming: false\n }));\n\n const propsRef = useAsRef({\n onCropChange,\n onCropSizeChange,\n onCropAreaChange,\n onCropComplete,\n onZoomChange,\n onRotationChange,\n onMediaLoaded,\n onInteractionStart,\n onInteractionEnd\n });\n\n const rootRef = React.useRef<RootElement | null>(null);\n\n const store = React.useMemo<Store>(() => {\n let isBatching = false;\n let raf: number | null = null;\n\n function notifyCropAreaChange() {\n if (raf != null) return;\n raf = requestAnimationFrame(() => {\n raf = null;\n const s = stateRef.current;\n if (s?.mediaSize && s.cropSize && propsRef.current.onCropAreaChange) {\n const { croppedAreaPercentages, croppedAreaPixels } = getCroppedArea(\n s.crop,\n s.mediaSize,\n s.cropSize,\n aspectRatio,\n s.zoom,\n s.rotation\n );\n propsRef.current.onCropAreaChange(croppedAreaPercentages, croppedAreaPixels);\n }\n });\n }\n\n return {\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => stateRef.current,\n setState: (key, value) => {\n if (Object.is(stateRef.current[key], value)) return;\n\n stateRef.current[key] = value;\n\n if (key === \"crop\" && typeof value === \"object\" && value && \"x\" in value) {\n propsRef.current.onCropChange?.(value);\n } else if (key === \"zoom\" && typeof value === \"number\") {\n propsRef.current.onZoomChange?.(value);\n } else if (key === \"rotation\" && typeof value === \"number\") {\n propsRef.current.onRotationChange?.(value);\n } else if (key === \"cropSize\" && typeof value === \"object\" && value && \"width\" in value) {\n propsRef.current.onCropSizeChange?.(value);\n } else if (key === \"mediaSize\" && typeof value === \"object\" && value && \"naturalWidth\" in value) {\n propsRef.current.onMediaLoaded?.(value);\n } else if (key === \"isDragging\") {\n if (value) {\n propsRef.current.onInteractionStart?.();\n } else {\n propsRef.current.onInteractionEnd?.();\n const currentState = stateRef.current;\n if (currentState?.mediaSize && currentState.cropSize && propsRef.current.onCropComplete) {\n const { croppedAreaPercentages, croppedAreaPixels } = getCroppedArea(\n currentState.crop,\n currentState.mediaSize,\n currentState.cropSize,\n aspectRatio,\n currentState.zoom,\n currentState.rotation\n );\n propsRef.current.onCropComplete(croppedAreaPercentages, croppedAreaPixels);\n }\n }\n }\n\n if (\n (key === \"crop\" || key === \"zoom\" || key === \"rotation\" || key === \"mediaSize\" || key === \"cropSize\") &&\n propsRef.current.onCropAreaChange\n ) {\n notifyCropAreaChange();\n }\n\n if (!isBatching) {\n store.notify();\n }\n },\n notify: () => {\n for (const cb of listenersRef.current) {\n cb();\n }\n },\n batch: (fn: () => void) => {\n if (isBatching) {\n fn();\n return;\n }\n isBatching = true;\n try {\n fn();\n } finally {\n isBatching = false;\n store.notify();\n }\n }\n };\n }, [listenersRef, stateRef, propsRef, aspectRatio]);\n\n useIsomorphicLayoutEffect(() => {\n const updates: Partial<StoreState> = {};\n let hasUpdates = false;\n let shouldRecompute = false;\n\n if (cropProp !== undefined) {\n const currentState = store.getState();\n if (!Object.is(currentState.crop, cropProp)) {\n updates.crop = cropProp;\n hasUpdates = true;\n }\n }\n\n if (zoomProp !== undefined) {\n const currentState = store.getState();\n if (currentState.zoom !== zoomProp) {\n updates.zoom = zoomProp;\n hasUpdates = true;\n shouldRecompute = true;\n }\n }\n\n if (rotationProp !== undefined) {\n const currentState = store.getState();\n if (currentState.rotation !== rotationProp) {\n updates.rotation = rotationProp;\n hasUpdates = true;\n shouldRecompute = true;\n }\n }\n\n if (hasUpdates) {\n store.batch(() => {\n Object.entries(updates).forEach(([key, value]) => {\n store.setState(key as keyof StoreState, value);\n });\n });\n\n if (shouldRecompute && rootRef.current) {\n requestAnimationFrame(() => {\n const currentState = store.getState();\n if (currentState.cropSize && currentState.mediaSize) {\n const newPosition = !allowOverflow\n ? onPositionClamp(\n currentState.crop,\n currentState.mediaSize,\n currentState.cropSize,\n currentState.zoom,\n currentState.rotation\n )\n : currentState.crop;\n\n if (\n Math.abs(newPosition.x - currentState.crop.x) > 0.001 ||\n Math.abs(newPosition.y - currentState.crop.y) > 0.001\n ) {\n store.setState(\"crop\", newPosition);\n }\n }\n });\n }\n }\n }, [cropProp, zoomProp, rotationProp, store, allowOverflow]);\n\n const labels = useComitorLabels(\"cropper\", labelsProp);\n\n const contextValue = React.useMemo<CropperContextValue>(\n () => ({\n minZoom,\n maxZoom,\n zoomSpeed,\n keyboardStep,\n aspectRatio,\n shape,\n objectFit,\n preventScrollZoom,\n allowOverflow,\n withGrid,\n rootRef,\n labels\n }),\n [\n minZoom,\n maxZoom,\n zoomSpeed,\n keyboardStep,\n aspectRatio,\n shape,\n objectFit,\n preventScrollZoom,\n allowOverflow,\n withGrid,\n labels\n ]\n );\n\n return (\n <StoreContext.Provider value={store}>\n <CropperContext.Provider value={contextValue}>\n <div data-slot=\"cropper-wrapper\" className={cn(\"relative size-full overflow-hidden\", className)}>\n <CropperImpl {...rootProps} />\n </div>\n </CropperContext.Provider>\n </StoreContext.Provider>\n );\n}\n\ninterface CropperImplProps extends CropperProps {\n onWheelZoom?: (event: WheelEvent) => void;\n}\n\nfunction CropperImpl(props: CropperImplProps) {\n const {\n onWheelZoom: onWheelZoomProp,\n onKeyUp: onKeyUpProp,\n onKeyDown: onKeyDownProp,\n onMouseDown: onMouseDownProp,\n onTouchStart: onTouchStartProp,\n \"aria-describedby\": ariaDescribedByProp,\n asChild,\n className,\n ref,\n ...rootImplProps\n } = props;\n\n const context = useCropperContext(ROOT_IMPL_NAME);\n const store = useStoreContext(ROOT_IMPL_NAME);\n const crop = useStore((state) => state.crop);\n const zoom = useStore((state) => state.zoom);\n const rotation = useStore((state) => state.rotation);\n const mediaSize = useStore((state) => state.mediaSize);\n const cropSize = useStore((state) => state.cropSize);\n\n const descriptionId = React.useId();\n // Câu đọc qua vùng `aria-live` — chỉ đổi khi phóng/thu BẰNG PHÍM (con lăn/chụm là thao tác nhìn).\n const [zoomStatus, setZoomStatus] = React.useState(\"\");\n\n const propsRef = useAsRef({\n onWheelZoom: onWheelZoomProp,\n onKeyUp: onKeyUpProp,\n onKeyDown: onKeyDownProp,\n onMouseDown: onMouseDownProp,\n onTouchStart: onTouchStartProp\n });\n\n const composedRef = useComposedRefs(ref, context.rootRef);\n const dragStartPositionRef = React.useRef<Point>({ x: 0, y: 0 });\n const dragStartCropRef = React.useRef<Point>({ x: 0, y: 0 });\n const contentPositionRef = React.useRef<Point>({ x: 0, y: 0 });\n const lastPinchDistanceRef = React.useRef(0);\n const lastPinchRotationRef = React.useRef(0);\n const rafDragTimeoutRef = React.useRef<number | null>(null);\n const rafPinchTimeoutRef = React.useRef<number | null>(null);\n const wheelTimerRef = React.useRef<number | null>(null);\n const isTouchingRef = React.useRef(false);\n const gestureZoomStartRef = React.useRef(0);\n const gestureRotationStartRef = React.useRef(0);\n\n const onRefsCleanup = React.useCallback(() => {\n if (rafDragTimeoutRef.current) {\n cancelAnimationFrame(rafDragTimeoutRef.current);\n rafDragTimeoutRef.current = null;\n }\n if (rafPinchTimeoutRef.current) {\n cancelAnimationFrame(rafPinchTimeoutRef.current);\n rafPinchTimeoutRef.current = null;\n }\n if (wheelTimerRef.current) {\n clearTimeout(wheelTimerRef.current);\n wheelTimerRef.current = null;\n }\n isTouchingRef.current = false;\n }, []);\n\n const onCacheCleanup = React.useCallback(() => {\n if (onPositionClampCache.size > MAX_CACHE_SIZE * 1.5) {\n onPositionClampCache.clear();\n }\n if (croppedAreaCache.size > MAX_CACHE_SIZE * 1.5) {\n croppedAreaCache.clear();\n }\n }, []);\n\n const getMousePoint = React.useCallback(\n (event: MouseEvent | React.MouseEvent) => ({\n x: Number(event.clientX),\n y: Number(event.clientY)\n }),\n []\n );\n\n const getTouchPoint = React.useCallback(\n (touch: Touch | React.Touch) => ({\n x: Number(touch.clientX),\n y: Number(touch.clientY)\n }),\n []\n );\n\n const onContentPositionChange = React.useCallback(() => {\n if (context.rootRef?.current) {\n const bounds = context.rootRef.current.getBoundingClientRect();\n contentPositionRef.current = { x: bounds.left, y: bounds.top };\n }\n }, [context.rootRef]);\n\n const getPointOnContent = React.useCallback(\n ({ x, y }: Point, contentTopLeft: Point): Point => {\n if (!context.rootRef?.current) {\n return { x: 0, y: 0 };\n }\n const contentRect = context.rootRef.current.getBoundingClientRect();\n return {\n x: contentRect.width / 2 - (x - contentTopLeft.x),\n y: contentRect.height / 2 - (y - contentTopLeft.y)\n };\n },\n [context.rootRef]\n );\n\n const getPointOnMedia = React.useCallback(\n ({ x, y }: Point) => {\n return {\n x: (x + crop.x) / zoom,\n y: (y + crop.y) / zoom\n };\n },\n [crop, zoom]\n );\n\n const recomputeCropPosition = React.useCallback(() => {\n if (!cropSize || !mediaSize) return;\n\n const newPosition = !context.allowOverflow ? onPositionClamp(crop, mediaSize, cropSize, zoom, rotation) : crop;\n\n if (Math.abs(newPosition.x - crop.x) > 0.001 || Math.abs(newPosition.y - crop.y) > 0.001) {\n store.setState(\"crop\", newPosition);\n }\n }, [cropSize, mediaSize, context.allowOverflow, crop, zoom, rotation, store]);\n\n const onZoomChange = React.useCallback(\n (newZoom: number, point: Point, shouldUpdatePosition = true) => {\n if (!cropSize || !mediaSize) return;\n\n const clampedZoom = clamp(newZoom, context.minZoom, context.maxZoom);\n\n store.batch(() => {\n if (shouldUpdatePosition) {\n const zoomPoint = getPointOnContent(point, contentPositionRef.current);\n const zoomTarget = getPointOnMedia(zoomPoint);\n const requestedPosition = {\n x: zoomTarget.x * clampedZoom - zoomPoint.x,\n y: zoomTarget.y * clampedZoom - zoomPoint.y\n };\n\n const newPosition = !context.allowOverflow\n ? onPositionClamp(requestedPosition, mediaSize, cropSize, clampedZoom, rotation)\n : requestedPosition;\n\n store.setState(\"crop\", newPosition);\n }\n store.setState(\"zoom\", clampedZoom);\n });\n\n requestAnimationFrame(() => {\n recomputeCropPosition();\n });\n },\n [\n cropSize,\n mediaSize,\n context.minZoom,\n context.maxZoom,\n context.allowOverflow,\n getPointOnContent,\n getPointOnMedia,\n rotation,\n store,\n recomputeCropPosition\n ]\n );\n\n const onDragStart = React.useCallback(\n ({ x, y }: Point) => {\n dragStartPositionRef.current = { x, y };\n dragStartCropRef.current = { ...crop };\n store.setState(\"isDragging\", true);\n },\n [crop, store]\n );\n\n const onDrag = React.useCallback(\n ({ x, y }: Point) => {\n if (rafDragTimeoutRef.current) {\n cancelAnimationFrame(rafDragTimeoutRef.current);\n }\n\n rafDragTimeoutRef.current = requestAnimationFrame(() => {\n if (!cropSize || !mediaSize) return;\n if (x === undefined || y === undefined) return;\n\n const offsetX = x - dragStartPositionRef.current.x;\n const offsetY = y - dragStartPositionRef.current.y;\n\n if (Math.abs(offsetX) < 2 && Math.abs(offsetY) < 2) {\n return;\n }\n\n const requestedPosition = {\n x: dragStartCropRef.current.x + offsetX,\n y: dragStartCropRef.current.y + offsetY\n };\n\n const newPosition = !context.allowOverflow\n ? onPositionClamp(requestedPosition, mediaSize, cropSize, zoom, rotation)\n : requestedPosition;\n\n const currentCrop = store.getState().crop;\n if (Math.abs(newPosition.x - currentCrop.x) > 1 || Math.abs(newPosition.y - currentCrop.y) > 1) {\n store.setState(\"crop\", newPosition);\n }\n });\n },\n [cropSize, mediaSize, context.allowOverflow, zoom, rotation, store]\n );\n\n const onMouseMove = React.useCallback((event: MouseEvent) => onDrag(getMousePoint(event)), [getMousePoint, onDrag]);\n\n const onTouchMove = React.useCallback(\n (event: TouchEvent) => {\n event.preventDefault();\n if (event.touches.length === 2) {\n const [firstTouch, secondTouch] = event.touches ?? [];\n if (firstTouch && secondTouch) {\n const pointA = getTouchPoint(firstTouch);\n const pointB = getTouchPoint(secondTouch);\n const center = getCenter(pointA, pointB);\n onDrag(center);\n\n if (rafPinchTimeoutRef.current) {\n cancelAnimationFrame(rafPinchTimeoutRef.current);\n }\n\n rafPinchTimeoutRef.current = requestAnimationFrame(() => {\n const distance = getDistanceBetweenPoints(pointA, pointB);\n const distanceRatio = distance / lastPinchDistanceRef.current;\n\n if (Math.abs(distanceRatio - 1) > 0.01) {\n const newZoom = zoom * distanceRatio;\n onZoomChange(newZoom, center, false);\n lastPinchDistanceRef.current = distance;\n }\n\n const rotationAngle = getRotationBetweenPoints(pointA, pointB);\n const rotationDiff = rotationAngle - lastPinchRotationRef.current;\n\n if (Math.abs(rotationDiff) > 0.5) {\n const newRotation = rotation + rotationDiff;\n store.setState(\"rotation\", newRotation);\n lastPinchRotationRef.current = rotationAngle;\n }\n });\n }\n } else if (event.touches.length === 1) {\n const firstTouch = event.touches[0];\n if (firstTouch) {\n onDrag(getTouchPoint(firstTouch));\n }\n }\n },\n [getTouchPoint, onDrag, zoom, onZoomChange, rotation, store]\n );\n\n const onGestureChange = React.useCallback(\n (event: GestureEvent) => {\n event.preventDefault();\n if (isTouchingRef.current) {\n return;\n }\n\n const point = { x: Number(event.clientX), y: Number(event.clientY) };\n const newZoom = gestureZoomStartRef.current - 1 + event.scale;\n onZoomChange(newZoom, point, true);\n\n const newRotation = gestureRotationStartRef.current + event.rotation;\n store.setState(\"rotation\", newRotation);\n },\n [onZoomChange, store]\n );\n\n const onGestureEnd = React.useCallback(() => {\n document.removeEventListener(\"gesturechange\", onGestureChange as EventListener);\n document.removeEventListener(\"gestureend\", onGestureEnd as EventListener);\n }, [onGestureChange]);\n\n const onGestureStart = React.useCallback(\n (event: GestureEvent) => {\n event.preventDefault();\n // ⚠ Khác thượng nguồn: cập nhật toạ độ khung trước khi phóng — xem chú thích ở `onWheelZoom`.\n onContentPositionChange();\n document.addEventListener(\"gesturechange\", onGestureChange as EventListener);\n document.addEventListener(\"gestureend\", onGestureEnd as EventListener);\n gestureZoomStartRef.current = zoom;\n gestureRotationStartRef.current = rotation;\n },\n [zoom, rotation, onGestureChange, onGestureEnd, onContentPositionChange]\n );\n\n const onSafariZoomPrevent = React.useCallback((event: Event) => event.preventDefault(), []);\n\n const onEventsCleanup = React.useCallback(() => {\n document.removeEventListener(\"mousemove\", onMouseMove);\n document.removeEventListener(\"touchmove\", onTouchMove);\n document.removeEventListener(\"gesturechange\", onGestureChange as EventListener);\n document.removeEventListener(\"gestureend\", onGestureEnd as EventListener);\n }, [onMouseMove, onTouchMove, onGestureChange, onGestureEnd]);\n\n const onDragStopped = React.useCallback(() => {\n isTouchingRef.current = false;\n store.setState(\"isDragging\", false);\n onRefsCleanup();\n document.removeEventListener(\"mouseup\", onDragStopped);\n document.removeEventListener(\"touchend\", onDragStopped);\n onEventsCleanup();\n }, [store, onEventsCleanup, onRefsCleanup]);\n\n const getWheelDelta = React.useCallback((event: WheelEvent) => {\n let deltaX = event.deltaX;\n let deltaY = event.deltaY;\n let deltaZ = event.deltaZ;\n\n if (event.deltaMode === 1) {\n deltaX *= 16;\n deltaY *= 16;\n deltaZ *= 16;\n } else if (event.deltaMode === 2) {\n deltaX *= 400;\n deltaY *= 400;\n deltaZ *= 400;\n }\n\n return { deltaX, deltaY, deltaZ };\n }, []);\n\n const onWheelZoom = React.useCallback(\n (event: WheelEvent) => {\n propsRef.current.onWheelZoom?.(event);\n if (event.defaultPrevented) return;\n\n event.preventDefault();\n /*\n * ⚠ Khác thượng nguồn: bản gốc chỉ cập nhật `contentPositionRef` ở `mousedown`/`touchstart`,\n * nên lăn chuột khi CHƯA kéo lần nào (hoặc sau khi trang đã cuộn) tính điểm neo bằng toạ độ\n * khung cũ — mặc định (0, 0) — và ảnh phóng lệch khỏi con trỏ đúng bằng khoảng cách từ mép\n * trang tới khung cắt.\n */\n onContentPositionChange();\n const point = getMousePoint(event);\n const { deltaY } = getWheelDelta(event);\n const newZoom = zoom - (deltaY * context.zoomSpeed) / 200;\n onZoomChange(newZoom, point, true);\n\n store.batch(() => {\n const currentState = store.getState();\n if (!currentState.isWheelZooming) {\n store.setState(\"isWheelZooming\", true);\n }\n if (!currentState.isDragging) {\n store.setState(\"isDragging\", true);\n }\n });\n\n if (wheelTimerRef.current) {\n clearTimeout(wheelTimerRef.current);\n }\n wheelTimerRef.current = window.setTimeout(() => {\n store.batch(() => {\n store.setState(\"isWheelZooming\", false);\n store.setState(\"isDragging\", false);\n });\n }, 250);\n },\n [propsRef, getMousePoint, zoom, context.zoomSpeed, onZoomChange, getWheelDelta, store, onContentPositionChange]\n );\n\n const onKeyUp = React.useCallback(\n (event: React.KeyboardEvent<RootElement>) => {\n propsRef.current.onKeyUp?.(event);\n if (event.defaultPrevented) return;\n\n const arrowKeys = new Set([\"ArrowUp\", \"ArrowDown\", \"ArrowLeft\", \"ArrowRight\"]);\n\n // Phím thu phóng cũng khép một lượt tương tác, để `onCropComplete` bắn như khi thả phím mũi tên.\n if (arrowKeys.has(event.key) || ZOOM_IN_KEYS.has(event.key) || ZOOM_OUT_KEYS.has(event.key)) {\n event.preventDefault();\n store.setState(\"isDragging\", false);\n }\n },\n [propsRef, store]\n );\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<RootElement>) => {\n propsRef.current.onKeyDown?.(event);\n if (event.defaultPrevented || !cropSize || !mediaSize) return;\n\n /*\n * ⚠ Khác thượng nguồn: thêm + / - để thu phóng quanh TÂM khung. Bản gốc chỉ phóng được bằng\n * con lăn chuột và cử chỉ chụm ngón — người chỉ dùng bàn phím không có đường nào đổi `zoom`\n * nếu app không tự dựng thêm Slider (WCAG 2.1.1). Ctrl/Cmd/Alt + phím là zoom TRÌNH DUYỆT,\n * nên để nguyên cho trình duyệt.\n */\n const zoomDirection = ZOOM_IN_KEYS.has(event.key) ? 1 : ZOOM_OUT_KEYS.has(event.key) ? -1 : 0;\n if (zoomDirection !== 0) {\n if (event.metaKey || event.ctrlKey || event.altKey) return;\n const root = context.rootRef.current;\n if (!root) return;\n\n event.preventDefault();\n\n if (!event.repeat) {\n store.setState(\"isDragging\", true);\n }\n\n const nextZoom = clamp(zoom + zoomDirection * KEYBOARD_ZOOM_STEP, context.minZoom, context.maxZoom);\n onContentPositionChange();\n const rect = root.getBoundingClientRect();\n onZoomChange(nextZoom, { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }, true);\n setZoomStatus(context.labels.zoomStatus(nextZoom));\n return;\n }\n\n let step = context.keyboardStep;\n if (event.shiftKey) {\n step *= 0.2;\n }\n\n const keyCallbacks: Record<string, () => Point> = {\n ArrowUp: () => ({ ...crop, y: crop.y - step }),\n ArrowDown: () => ({ ...crop, y: crop.y + step }),\n ArrowLeft: () => ({ ...crop, x: crop.x - step }),\n ArrowRight: () => ({ ...crop, x: crop.x + step })\n } as const;\n\n const callback = keyCallbacks[event.key];\n if (!callback) return;\n\n event.preventDefault();\n\n let newCrop = callback();\n\n if (!context.allowOverflow) {\n newCrop = onPositionClamp(newCrop, mediaSize, cropSize, zoom, rotation);\n }\n\n if (!event.repeat) {\n store.setState(\"isDragging\", true);\n }\n\n store.setState(\"crop\", newCrop);\n },\n [\n propsRef,\n cropSize,\n mediaSize,\n context.keyboardStep,\n context.allowOverflow,\n context.rootRef,\n context.minZoom,\n context.maxZoom,\n context.labels,\n crop,\n zoom,\n rotation,\n store,\n onContentPositionChange,\n onZoomChange\n ]\n );\n\n const onMouseDown = React.useCallback(\n (event: React.MouseEvent<RootElement>) => {\n propsRef.current.onMouseDown?.(event);\n if (event.defaultPrevented) return;\n\n event.preventDefault();\n document.addEventListener(\"mousemove\", onMouseMove);\n document.addEventListener(\"mouseup\", onDragStopped);\n onContentPositionChange();\n onDragStart(getMousePoint(event));\n },\n [propsRef, getMousePoint, onDragStart, onDragStopped, onMouseMove, onContentPositionChange]\n );\n\n const onTouchStart = React.useCallback(\n (event: React.TouchEvent<RootElement>) => {\n propsRef.current.onTouchStart?.(event);\n if (event.defaultPrevented) return;\n\n isTouchingRef.current = true;\n document.addEventListener(\"touchmove\", onTouchMove, { passive: false });\n document.addEventListener(\"touchend\", onDragStopped);\n onContentPositionChange();\n\n if (event.touches.length === 2) {\n const [firstTouch, secondTouch] = event.touches ? Array.from(event.touches) : [];\n if (firstTouch && secondTouch) {\n const pointA = getTouchPoint(firstTouch);\n const pointB = getTouchPoint(secondTouch);\n lastPinchDistanceRef.current = getDistanceBetweenPoints(pointA, pointB);\n lastPinchRotationRef.current = getRotationBetweenPoints(pointA, pointB);\n onDragStart(getCenter(pointA, pointB));\n }\n } else if (event.touches.length === 1) {\n const firstTouch = event.touches[0];\n if (firstTouch) {\n onDragStart(getTouchPoint(firstTouch));\n }\n }\n },\n [propsRef, onDragStopped, onTouchMove, onContentPositionChange, getTouchPoint, onDragStart]\n );\n\n React.useEffect(() => {\n const content = context.rootRef?.current;\n if (!content) return;\n\n if (!context.preventScrollZoom) {\n content.addEventListener(\"wheel\", onWheelZoom, { passive: false });\n }\n\n content.addEventListener(\"gesturestart\", onSafariZoomPrevent);\n content.addEventListener(\"gesturestart\", onGestureStart as EventListener);\n\n return () => {\n if (!context.preventScrollZoom) {\n content.removeEventListener(\"wheel\", onWheelZoom);\n }\n content.removeEventListener(\"gesturestart\", onSafariZoomPrevent);\n content.removeEventListener(\"gesturestart\", onGestureStart as EventListener);\n /*\n * ⚠ Khác thượng nguồn: bản gốc gọi `onRefsCleanup()` ở đây. Effect này chạy lại MỖI lần\n * `zoom` đổi (vì `onWheelZoom` phụ thuộc `zoom`), nên nó huỷ đúng cái hẹn giờ 250ms mà lượt\n * lăn chuột vừa đặt để khép tương tác: `isDragging` kẹt ở `true`, và `onCropComplete` /\n * `onInteractionEnd` KHÔNG BAO GIỜ bắn sau khi phóng bằng con lăn (đo trên trình duyệt thật:\n * lăn một nấc, chờ 400ms, `onCropComplete` 0 lần). Dọn khi gỡ component đã có effect ngay dưới.\n */\n };\n }, [context.rootRef, context.preventScrollZoom, onWheelZoom, onSafariZoomPrevent, onGestureStart]);\n\n React.useEffect(() => {\n return () => {\n onRefsCleanup();\n onCacheCleanup();\n };\n }, [onRefsCleanup, onCacheCleanup]);\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n const describedBy = [descriptionId, ariaDescribedByProp].filter(Boolean).join(\" \");\n\n return (\n <>\n <RootPrimitive\n /*\n * ⚠ Khác thượng nguồn: bản gốc là một `<div tabIndex={0}>` KHÔNG role, KHÔNG tên — Tab tới\n * được nhưng trình đọc màn hình không biết đó là gì, cũng không biết phím nào dùng được.\n * `role=\"application\"` (không phải `group`) là CỐ Ý: vùng này sống nhờ phím mũi tên và\n * + / -, mà với `group` thì NVDA/JAWS ở chế độ duyệt nuốt mất phím mũi tên trước khi tới\n * handler; `application` bật chế độ focus. Bên trong không có nội dung đọc được nào bị\n * che đi — chỉ có ảnh đang cắt và khung vẽ. App ghi đè được cả ba qua `labels` hoặc\n * truyền thẳng `aria-label` / `aria-roledescription`.\n */\n role=\"application\"\n aria-roledescription={context.labels.roleDescription}\n aria-label={context.labels.label}\n aria-describedby={describedBy}\n data-slot=\"cropper\"\n tabIndex={0}\n {...rootImplProps}\n ref={composedRef}\n className={cn(\n \"absolute inset-0 flex cursor-move touch-none items-center justify-center overflow-hidden outline-none select-none\",\n /*\n * ⚠ Khác thượng nguồn: bản gốc `outline-none` mà KHÔNG có chỉ báo focus nào (WCAG 2.4.7).\n * Vòng focus vẽ trên `::after` phủ kín khung chứ không trên chính phần tử: ảnh và khung\n * cắt là con `absolute` nên được vẽ ĐÈ lên outline của cha, còn `::after` là con cuối\n * cùng nên nằm trên cùng. Cùng khuôn `outline-ring` với `Button`, thụt vào trong vì hai\n * lớp bọc ngoài đều `overflow-hidden`.\n */\n \"after:pointer-events-none after:absolute after:inset-0 focus-visible:after:-outline-offset-2 focus-visible:after:outline-2 focus-visible:after:outline-ring\",\n className\n )}\n onKeyUp={onKeyUp}\n onKeyDown={onKeyDown}\n onMouseDown={onMouseDown}\n onTouchStart={onTouchStart}\n />\n {/* `hidden` vẫn làm nguồn được cho `aria-describedby`, và không bị đọc lặp ở chế độ duyệt. */}\n <span id={descriptionId} hidden>\n {context.labels.instructions}\n </span>\n <span aria-live=\"polite\" aria-atomic=\"true\" className=\"sr-only\">\n {zoomStatus}\n </span>\n </>\n );\n}\n\nconst cropperMediaVariants = cva(\"will-change-transform\", {\n variants: {\n objectFit: {\n contain: \"absolute inset-0 m-auto max-h-full max-w-full\",\n cover: \"h-auto w-full\",\n \"horizontal-cover\": \"h-auto w-full\",\n \"vertical-cover\": \"h-full w-auto\"\n }\n },\n defaultVariants: {\n objectFit: \"contain\"\n }\n});\n\ninterface UseMediaComputationProps<T extends HTMLImageElement | HTMLVideoElement> {\n mediaRef: React.RefObject<T | null>;\n context: CropperContextValue;\n store: Store;\n rotation: number;\n getNaturalDimensions: (media: T) => Size;\n}\n\nfunction useMediaComputation<T extends HTMLImageElement | HTMLVideoElement>({\n mediaRef,\n context,\n store,\n rotation,\n getNaturalDimensions\n}: UseMediaComputationProps<T>) {\n const computeSizes = React.useCallback(() => {\n const media = mediaRef.current;\n const content = context.rootRef?.current;\n if (!media || !content) return;\n\n const contentRect = content.getBoundingClientRect();\n const containerAspect = contentRect.width / contentRect.height;\n const { width: naturalWidth, height: naturalHeight } = getNaturalDimensions(media);\n const isScaledDown = media.offsetWidth < naturalWidth || media.offsetHeight < naturalHeight;\n const mediaAspect = naturalWidth / naturalHeight;\n\n let renderedMediaSize: Size;\n\n if (isScaledDown) {\n const objectFitCallbacks = {\n contain: () =>\n containerAspect > mediaAspect\n ? {\n width: contentRect.height * mediaAspect,\n height: contentRect.height\n }\n : {\n width: contentRect.width,\n height: contentRect.width / mediaAspect\n },\n \"horizontal-cover\": () => ({\n width: contentRect.width,\n height: contentRect.width / mediaAspect\n }),\n \"vertical-cover\": () => ({\n width: contentRect.height * mediaAspect,\n height: contentRect.height\n }),\n cover: () =>\n containerAspect < mediaAspect\n ? {\n width: contentRect.width,\n height: contentRect.width / mediaAspect\n }\n : {\n width: contentRect.height * mediaAspect,\n height: contentRect.height\n }\n } as const;\n\n const callback = objectFitCallbacks[context.objectFit];\n renderedMediaSize = callback\n ? callback()\n : containerAspect > mediaAspect\n ? {\n width: contentRect.height * mediaAspect,\n height: contentRect.height\n }\n : {\n width: contentRect.width,\n height: contentRect.width / mediaAspect\n };\n } else {\n renderedMediaSize = {\n width: media.offsetWidth,\n height: media.offsetHeight\n };\n }\n\n const mediaSize: MediaSize = {\n ...renderedMediaSize,\n naturalWidth,\n naturalHeight\n };\n\n store.setState(\"mediaSize\", mediaSize);\n\n const cropSize = getCropSize(\n mediaSize.width,\n mediaSize.height,\n contentRect.width,\n contentRect.height,\n context.aspectRatio,\n rotation\n );\n\n store.setState(\"cropSize\", cropSize);\n\n requestAnimationFrame(() => {\n const currentState = store.getState();\n if (currentState.cropSize && currentState.mediaSize) {\n const newPosition = onPositionClamp(\n currentState.crop,\n currentState.mediaSize,\n currentState.cropSize,\n currentState.zoom,\n currentState.rotation\n );\n\n if (\n Math.abs(newPosition.x - currentState.crop.x) > 0.001 ||\n Math.abs(newPosition.y - currentState.crop.y) > 0.001\n ) {\n store.setState(\"crop\", newPosition);\n }\n }\n });\n\n return { mediaSize, cropSize };\n }, [mediaRef, context.aspectRatio, context.rootRef, context.objectFit, store, rotation, getNaturalDimensions]);\n\n return { computeSizes };\n}\n\ninterface CropperImageProps extends React.ComponentProps<\"img\">, VariantProps<typeof cropperMediaVariants> {\n asChild?: boolean;\n snapPixels?: boolean;\n}\n\n/** Ảnh cần cắt. Nhớ truyền `alt` — ảnh ở đây mang nội dung, không phải trang trí. */\nfunction CropperImage(props: CropperImageProps) {\n const { className, style, asChild, ref, onLoad, objectFit, snapPixels = false, ...imageProps } = props;\n\n const context = useCropperContext(IMAGE_NAME);\n const store = useStoreContext(IMAGE_NAME);\n const crop = useStore((state) => state.crop);\n const zoom = useStore((state) => state.zoom);\n const rotation = useStore((state) => state.rotation);\n const dpr = useDevicePixelRatio();\n\n const imageRef = React.useRef<HTMLImageElement>(null);\n const composedRef = useComposedRefs(ref, imageRef);\n\n const getNaturalDimensions = React.useCallback(\n (image: HTMLImageElement) => ({\n width: image.naturalWidth,\n height: image.naturalHeight\n }),\n []\n );\n\n const { computeSizes } = useMediaComputation({\n mediaRef: imageRef,\n context,\n store,\n rotation,\n getNaturalDimensions\n });\n\n const onMediaLoad = React.useCallback(() => {\n const image = imageRef.current;\n if (!image) return;\n\n computeSizes();\n\n onLoad?.(new Event(\"load\") as unknown as React.SyntheticEvent<HTMLImageElement>);\n }, [computeSizes, onLoad]);\n\n React.useEffect(() => {\n const image = imageRef.current;\n if (image?.complete && image.naturalWidth > 0) {\n onMediaLoad();\n }\n }, [onMediaLoad]);\n\n React.useEffect(() => {\n const content = context.rootRef?.current;\n if (!content) return;\n\n if (typeof ResizeObserver !== \"undefined\") {\n let isFirstResize = true;\n const resizeObserver = new ResizeObserver(() => {\n if (isFirstResize) {\n isFirstResize = false;\n return;\n }\n\n const callback = () => {\n const image = imageRef.current;\n if (image?.complete && image.naturalWidth > 0) {\n computeSizes();\n }\n };\n\n if (\"requestIdleCallback\" in window) {\n requestIdleCallback(callback);\n } else {\n setTimeout(callback, 16);\n }\n });\n\n resizeObserver.observe(content);\n\n return () => {\n resizeObserver.disconnect();\n };\n } else {\n const onWindowResize = () => {\n const image = imageRef.current;\n if (image?.complete && image.naturalWidth > 0) {\n computeSizes();\n }\n };\n\n window.addEventListener(\"resize\", onWindowResize);\n return () => {\n window.removeEventListener(\"resize\", onWindowResize);\n };\n }\n }, [context.rootRef, computeSizes]);\n\n const ImagePrimitive = asChild ? Slot : \"img\";\n\n return (\n <ImagePrimitive\n data-slot=\"cropper-image\"\n {...imageProps}\n ref={composedRef}\n className={cn(\n cropperMediaVariants({\n objectFit: objectFit ?? context.objectFit,\n className\n })\n )}\n style={{\n transform: snapPixels\n ? `translate(${snapToDevicePixel(crop.x, dpr)}px, ${snapToDevicePixel(crop.y, dpr)}px) rotate(${rotation}deg) scale(${zoom})`\n : `translate(${crop.x}px, ${crop.y}px) rotate(${rotation}deg) scale(${zoom})`,\n ...style\n }}\n onLoad={onMediaLoad}\n />\n );\n}\n\ninterface CropperVideoProps extends React.ComponentProps<\"video\">, VariantProps<typeof cropperMediaVariants> {\n asChild?: boolean;\n snapPixels?: boolean;\n}\n\nfunction CropperVideo(props: CropperVideoProps) {\n const { className, style, asChild, ref, onLoadedMetadata, objectFit, snapPixels = false, ...videoProps } = props;\n\n const context = useCropperContext(VIDEO_NAME);\n const store = useStoreContext(VIDEO_NAME);\n const crop = useStore((state) => state.crop);\n const zoom = useStore((state) => state.zoom);\n const rotation = useStore((state) => state.rotation);\n const dpr = useDevicePixelRatio();\n\n const videoRef = React.useRef<HTMLVideoElement>(null);\n const composedRef = useComposedRefs(ref, videoRef);\n\n const getNaturalDimensions = React.useCallback(\n (video: HTMLVideoElement) => ({\n width: video.videoWidth,\n height: video.videoHeight\n }),\n []\n );\n\n const { computeSizes } = useMediaComputation({\n mediaRef: videoRef,\n context,\n store,\n rotation,\n getNaturalDimensions\n });\n\n const onMediaLoad = React.useCallback(() => {\n const video = videoRef.current;\n if (!video) return;\n\n computeSizes();\n\n onLoadedMetadata?.(new Event(\"loadedmetadata\") as unknown as React.SyntheticEvent<HTMLVideoElement>);\n }, [computeSizes, onLoadedMetadata]);\n\n React.useEffect(() => {\n const content = context.rootRef?.current;\n if (!content) return;\n\n if (typeof ResizeObserver !== \"undefined\") {\n let isFirstResize = true;\n const resizeObserver = new ResizeObserver(() => {\n if (isFirstResize) {\n isFirstResize = false;\n return;\n }\n\n const callback = () => {\n const video = videoRef.current;\n if (video && video.videoWidth > 0 && video.videoHeight > 0) {\n computeSizes();\n }\n };\n\n if (\"requestIdleCallback\" in window) {\n requestIdleCallback(callback);\n } else {\n setTimeout(callback, 16);\n }\n });\n\n resizeObserver.observe(content);\n\n return () => {\n resizeObserver.disconnect();\n };\n } else {\n const onWindowResize = () => {\n const video = videoRef.current;\n if (video && video.videoWidth > 0 && video.videoHeight > 0) {\n computeSizes();\n }\n };\n\n window.addEventListener(\"resize\", onWindowResize);\n return () => {\n window.removeEventListener(\"resize\", onWindowResize);\n };\n }\n }, [context.rootRef, computeSizes]);\n\n const VideoPrimitive = asChild ? Slot : \"video\";\n\n return (\n <VideoPrimitive\n data-slot=\"cropper-video\"\n autoPlay\n playsInline\n loop\n muted\n controls={false}\n {...videoProps}\n ref={composedRef}\n className={cn(\n cropperMediaVariants({\n objectFit: objectFit ?? context.objectFit,\n className\n })\n )}\n style={{\n transform: snapPixels\n ? `translate(${snapToDevicePixel(crop.x, dpr)}px, ${snapToDevicePixel(crop.y, dpr)}px) rotate(${rotation}deg) scale(${zoom})`\n : `translate(${crop.x}px, ${crop.y}px) rotate(${rotation}deg) scale(${zoom})`,\n ...style\n }}\n onLoadedMetadata={onMediaLoad}\n />\n );\n}\n\n/*\n * ⚠ Khác thượng nguồn — màu. Khung cắt nằm trên ẢNH, không trên nền trang, nên cặp màu ở đây\n * không đổi theo theme (ảnh là ảnh ở cả sáng lẫn tối):\n * · vùng ngoài khung tối đi bằng MÀU LỚP PHỦ của `DialogOverlay` (`ink/60`, `ink/80` ở tối),\n * vẽ bằng bóng 9999em như bản gốc — bản gốc viết thẳng `rgba(0,0,0,0.5)`. Đây là một tấm\n * phủ, KHÔNG phải bóng đổ độ cao, nên không đi qua thang `shadow-card`/`shadow-overlay`;\n * màu vẫn là token nên đổi theo `--ink` của app.\n * · khung là `paper` (bản gốc `white/90`): màu đi cặp với lớp phủ mực, sáng ở MỌI theme và mọi\n * bảng màu. Ca tệ nhất — điểm ảnh TRẮNG nằm dưới lớp phủ: 4,65:1 (ink/60) · 9,60:1 (ink/80),\n * ≥ 3:1 cho thành phần phi văn bản. `--background`/`--foreground` KHÔNG dùng được: chúng đảo\n * theo theme trong khi lớp phủ thì luôn tối.\n * · lưới 1/3 (`paper/50`) là đường gióng bố cục thuần trang trí, nằm trên phần ảnh KHÔNG phủ.\n */\nconst cropperAreaVariants = cva(\n \"absolute top-1/2 left-1/2 box-border -translate-x-1/2 -translate-y-1/2 overflow-hidden border border-[2.5px] border-paper shadow-[0_0_0_9999em] shadow-ink/60 dark:shadow-ink/80\",\n {\n variants: {\n shape: {\n rectangle: \"\",\n circle: \"rounded-full\"\n },\n withGrid: {\n true: \"before:absolute before:top-0 before:right-1/3 before:bottom-0 before:left-1/3 before:box-border before:border before:border-t-0 before:border-b-0 before:border-paper/50 before:content-[''] after:absolute after:top-1/3 after:right-0 after:bottom-1/3 after:left-0 after:box-border after:border after:border-r-0 after:border-l-0 after:border-paper/50 after:content-['']\",\n false: \"\"\n }\n },\n defaultVariants: {\n shape: \"rectangle\",\n withGrid: false\n }\n }\n);\n\ninterface CropperAreaProps extends DivProps, VariantProps<typeof cropperAreaVariants> {\n snapPixels?: boolean;\n}\n\nfunction CropperArea(props: CropperAreaProps) {\n const { className, style, asChild, ref, snapPixels = false, shape, withGrid, ...areaProps } = props;\n\n const context = useCropperContext(AREA_NAME);\n const cropSize = useStore((state) => state.cropSize);\n\n if (!cropSize) return null;\n\n const AreaPrimitive = asChild ? Slot : \"div\";\n\n return (\n <AreaPrimitive\n data-slot=\"cropper-area\"\n {...areaProps}\n ref={ref}\n className={cn(\n cropperAreaVariants({\n shape: shape ?? context.shape,\n withGrid: withGrid ?? context.withGrid,\n className\n })\n )}\n style={{\n width: snapPixels ? Math.round(cropSize.width) : cropSize.width,\n height: snapPixels ? Math.round(cropSize.height) : cropSize.height,\n ...style\n }}\n />\n );\n}\n\nexport {\n type Area as CropperAreaData,\n Cropper,\n CropperArea,\n type CropperAreaProps,\n CropperImage,\n type CropperImageProps,\n type CropperProps,\n CropperVideo,\n type CropperVideoProps,\n type MediaSize as CropperMediaSize,\n type ObjectFit as CropperObjectFit,\n type Point as CropperPoint,\n type Shape as CropperShape,\n type Size as CropperSize,\n useStore as useCropper\n};\n"],"mappings":"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","names":[]}
|
|
@@ -30,10 +30,10 @@ type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Content> &
|
|
|
30
30
|
showCloseButton?: boolean;
|
|
31
31
|
/** Cỡ hộp thoại theo breakpoint sm trở lên. Mặc định "md". */
|
|
32
32
|
size?: DialogSize;
|
|
33
|
-
/** Nhãn cho trình đọc màn hình của nút đóng. Mặc định
|
|
33
|
+
/** Nhãn cho trình đọc màn hình của nút đóng. Mặc định: khoá `closeLabel` của namespace `dialog`. */
|
|
34
34
|
closeLabel?: string;
|
|
35
35
|
};
|
|
36
|
-
declare function DialogContent({ className, children, showCloseButton, size, closeLabel, ...props }: DialogContentProps): React.JSX.Element;
|
|
36
|
+
declare function DialogContent({ className, children, showCloseButton, size, closeLabel: closeLabelProp, ...props }: DialogContentProps): React.JSX.Element;
|
|
37
37
|
type DialogBodyProps = React.ComponentProps<"div">;
|
|
38
38
|
/**
|
|
39
39
|
* Vùng nội dung CUỘN của hộp thoại, giữa header và footer.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
4
4
|
import { XIcon } from "lucide-react";
|
|
5
|
+
import { useComitorMessages } from "../../i18n/locale-provider.js";
|
|
5
6
|
import { cn } from "../../lib/cn.js";
|
|
6
7
|
function Dialog({ ...props }) {
|
|
7
8
|
return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
|
|
@@ -39,9 +40,11 @@ function DialogContent({
|
|
|
39
40
|
children,
|
|
40
41
|
showCloseButton = true,
|
|
41
42
|
size = "md",
|
|
42
|
-
closeLabel
|
|
43
|
+
closeLabel: closeLabelProp,
|
|
43
44
|
...props
|
|
44
45
|
}) {
|
|
46
|
+
const t = useComitorMessages("dialog");
|
|
47
|
+
const closeLabel = closeLabelProp ?? t.closeLabel;
|
|
45
48
|
return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
46
49
|
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
47
50
|
/* @__PURE__ */ jsxs(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { XIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Dialog — hộp thoại modal (Radix Dialog), style new-york + token Comitor.\n *\n * Khác bản shadcn gốc: có prop `size` (sm/md/lg/xl) và `DialogBody` để tách vùng\n * nội dung cuộn khỏi header/footer.\n */\n\nexport type DialogProps = React.ComponentProps<typeof DialogPrimitive.Root>;\n\nfunction Dialog({ ...props }: DialogProps) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nexport type DialogTriggerProps = React.ComponentProps<typeof DialogPrimitive.Trigger>;\n\nfunction DialogTrigger({ ...props }: DialogTriggerProps) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nexport type DialogPortalProps = React.ComponentProps<typeof DialogPrimitive.Portal>;\n\nfunction DialogPortal({ ...props }: DialogPortalProps) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nexport type DialogCloseProps = React.ComponentProps<typeof DialogPrimitive.Close>;\n\nfunction DialogClose({ ...props }: DialogCloseProps) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nexport type DialogOverlayProps = React.ComponentProps<typeof DialogPrimitive.Overlay>;\n\nfunction DialogOverlay({ className, ...props }: DialogOverlayProps) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-ink/60 dark:bg-ink/80 backdrop-blur-sm\",\n className\n )}\n {...props}\n />\n );\n}\n\n/** Bề ngang tối đa theo cỡ — khai tĩnh để Tailwind quét được (không sinh class động). */\nconst dialogSizeStyles = {\n sm: \"sm:max-w-sm\",\n md: \"sm:max-w-md\",\n lg: \"sm:max-w-lg\",\n xl: \"sm:max-w-xl\"\n} as const;\n\nexport type DialogSize = keyof typeof dialogSizeStyles;\n\nexport type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Content> & {\n /** Hiện nút đóng ở góc phải trên. Mặc định true. */\n showCloseButton?: boolean;\n /** Cỡ hộp thoại theo breakpoint sm trở lên. Mặc định \"md\". */\n size?: DialogSize;\n /** Nhãn cho trình đọc màn hình của nút đóng. Mặc định \"Đóng\". */\n closeLabel?: string;\n};\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n size = \"md\",\n closeLabel = \"Đóng\",\n ...props\n}: DialogContentProps) {\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n /*\n * ⚠ `max-h` + `overflow-y-auto` là BẮT BUỘC, không phải trang trí.\n *\n * Hộp thoại là `position: fixed` căn giữa bằng `translate-y-[-50%]`. Không có trần\n * chiều cao thì một biểu mẫu dài tràn ra CẢ HAI phía màn hình — và vì nó `fixed` nên\n * trang không cuộn nó được: phần đầu và phần cuối (kể cả nút Gửi) trở nên KHÔNG THỂ\n * chạm tới. Đo được ở hộp \"Mời thành viên\": nội dung 812px trong khung nhìn 613px.\n *\n * ⚠ `flex flex-col` chứ KHÔNG phải `grid`. Với `grid`, mọi hàng là `auto` nên không\n * hàng nào chịu co lại — muốn `DialogBody` cuộn thì phải khai `grid-template-rows`,\n * mà làm vậy là ghim thứ tự con (header phải là con thứ nhất). Flex cho `DialogBody`\n * co lại theo `min-h-0` bất kể nó nằm ở đâu.\n *\n * `overflow-y-auto` ở đây là LƯỚI AN TOÀN cho hộp thoại KHÔNG dùng `DialogBody`:\n * có `DialogBody` thì chính nó cuộn và thanh cuộn ngoài này không bao giờ xuất hiện.\n */\n \"bg-card data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 flex max-h-[calc(100dvh-2rem)] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] flex-col gap-4 overflow-y-auto rounded-xl border border-border p-6 shadow-overlay duration-200\",\n dialogSizeStyles[size],\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"absolute top-4 right-4 rounded-md p-1 text-muted-foreground transition-colors duration-150 ease-out hover:text-foreground hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n >\n <XIcon />\n <span className=\"sr-only\">{closeLabel}</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nexport type DialogBodyProps = React.ComponentProps<\"div\">;\n\n/**\n * Vùng nội dung CUỘN của hộp thoại, giữa header và footer.\n *\n * ⚠ Trước 1.3.2 component này render `<div className={cn(className)} />` — tức KHÔNG LÀM GÌ CẢ,\n * trong khi chính tài liệu của nó (và của `Dialog`) nói nó \"tách vùng nội dung cuộn khỏi\n * header/footer\". Một component không làm điều nó tự nhận là loại lỗi tệ nhất trong một gói dùng\n * chung: chỗ gọi đã làm ĐÚNG rồi mà vẫn hỏng, nên người ta đi tìm nguyên nhân ở app.\n *\n * ⚠ `flex-auto` chứ không `flex-1`: `flex-1` là `flex: 1 1 0%`, tức lấy 0 làm cỡ cơ sở. `flex-auto`\n * (`1 1 auto`) lấy cỡ NỘI DUNG làm cơ sở, nên hộp thoại ngắn vẫn ôm sát nội dung thay vì bị kéo\n * giãn. `min-h-0` là mảnh bắt buộc còn lại — thiếu nó, `min-height: auto` mặc định của flex item\n * giữ nguyên chiều cao nội dung và không gì cuộn được.\n */\nfunction DialogBody({ className, ...props }: DialogBodyProps) {\n return (\n <div\n data-slot=\"dialog-body\"\n /*\n * ⚠ `-m-1 p-1` KHÔNG phải để cho thoáng — nó là chỗ cho FOCUS RING thở.\n *\n * CSS không cho một trục `auto` còn trục kia `visible`: đặt `overflow-y: auto` thì\n * `overflow-x` **tự thành `auto`**, và hộp cắt xuất hiện ở CẢ HAI chiều. Con của vùng này\n * là `Input` `w-full`, tức mép nó trùng khít mép hộp cắt (đo được: chỗ trống hai bên đúng\n * 0px) — nên `focus:ring-2` nằm 2px bên ngoài bị cắt sạch. Ô đang focus trông như không\n * focus, và đó là dấu hiệu DUY NHẤT cho người dùng bàn phím.\n *\n * `p-1` cho 4,5px chỗ trống (rem gốc 18px); `-m-1` kéo hộp lại đúng bấy nhiêu nên vị trí\n * NỘI DUNG không đổi một pixel — khoảng cách tới header/footer vẫn là `gap-4`.\n *\n * ⚠ Đừng bê khuôn này sang `SheetContent`/`DrawerContent`: chúng `position: fixed` kèm\n * `inset-x-0`/`right-0`, nên margin ÂM làm chúng rộng ra 9px và tràn khỏi khung nhìn.\n * Ở đó, con phải tự mang padding — `SheetHeader`/`SheetFooter` đã có `p-4`.\n */\n className={cn(\"-m-1 min-h-0 flex-auto overflow-y-auto p-1\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogHeaderProps = React.ComponentProps<\"div\">;\n\nfunction DialogHeader({ className, ...props }: DialogHeaderProps) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogFooterProps = React.ComponentProps<\"div\">;\n\nfunction DialogFooter({ className, ...props }: DialogFooterProps) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogTitleProps = React.ComponentProps<typeof DialogPrimitive.Title>;\n\nfunction DialogTitle({ className, ...props }: DialogTitleProps) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg font-semibold text-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogDescriptionProps = React.ComponentProps<typeof DialogPrimitive.Description>;\n\nfunction DialogDescription({ className, ...props }: DialogDescriptionProps) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogBody,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { XIcon } 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 * Dialog — hộp thoại modal (Radix Dialog), style new-york + token Comitor.\n *\n * Khác bản shadcn gốc: có prop `size` (sm/md/lg/xl) và `DialogBody` để tách vùng\n * nội dung cuộn khỏi header/footer.\n */\n\nexport type DialogProps = React.ComponentProps<typeof DialogPrimitive.Root>;\n\nfunction Dialog({ ...props }: DialogProps) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nexport type DialogTriggerProps = React.ComponentProps<typeof DialogPrimitive.Trigger>;\n\nfunction DialogTrigger({ ...props }: DialogTriggerProps) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nexport type DialogPortalProps = React.ComponentProps<typeof DialogPrimitive.Portal>;\n\nfunction DialogPortal({ ...props }: DialogPortalProps) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nexport type DialogCloseProps = React.ComponentProps<typeof DialogPrimitive.Close>;\n\nfunction DialogClose({ ...props }: DialogCloseProps) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nexport type DialogOverlayProps = React.ComponentProps<typeof DialogPrimitive.Overlay>;\n\nfunction DialogOverlay({ className, ...props }: DialogOverlayProps) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-ink/60 dark:bg-ink/80 backdrop-blur-sm\",\n className\n )}\n {...props}\n />\n );\n}\n\n/** Bề ngang tối đa theo cỡ — khai tĩnh để Tailwind quét được (không sinh class động). */\nconst dialogSizeStyles = {\n sm: \"sm:max-w-sm\",\n md: \"sm:max-w-md\",\n lg: \"sm:max-w-lg\",\n xl: \"sm:max-w-xl\"\n} as const;\n\nexport type DialogSize = keyof typeof dialogSizeStyles;\n\nexport type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Content> & {\n /** Hiện nút đóng ở góc phải trên. Mặc định true. */\n showCloseButton?: boolean;\n /** Cỡ hộp thoại theo breakpoint sm trở lên. Mặc định \"md\". */\n size?: DialogSize;\n /** Nhãn cho trình đọc màn hình của nút đóng. Mặc định: khoá `closeLabel` của namespace `dialog`. */\n closeLabel?: string;\n};\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n size = \"md\",\n closeLabel: closeLabelProp,\n ...props\n}: DialogContentProps) {\n const t = useComitorMessages(\"dialog\");\n const closeLabel = closeLabelProp ?? t.closeLabel;\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n /*\n * ⚠ `max-h` + `overflow-y-auto` là BẮT BUỘC, không phải trang trí.\n *\n * Hộp thoại là `position: fixed` căn giữa bằng `translate-y-[-50%]`. Không có trần\n * chiều cao thì một biểu mẫu dài tràn ra CẢ HAI phía màn hình — và vì nó `fixed` nên\n * trang không cuộn nó được: phần đầu và phần cuối (kể cả nút Gửi) trở nên KHÔNG THỂ\n * chạm tới. Đo được ở hộp \"Mời thành viên\": nội dung 812px trong khung nhìn 613px.\n *\n * ⚠ `flex flex-col` chứ KHÔNG phải `grid`. Với `grid`, mọi hàng là `auto` nên không\n * hàng nào chịu co lại — muốn `DialogBody` cuộn thì phải khai `grid-template-rows`,\n * mà làm vậy là ghim thứ tự con (header phải là con thứ nhất). Flex cho `DialogBody`\n * co lại theo `min-h-0` bất kể nó nằm ở đâu.\n *\n * `overflow-y-auto` ở đây là LƯỚI AN TOÀN cho hộp thoại KHÔNG dùng `DialogBody`:\n * có `DialogBody` thì chính nó cuộn và thanh cuộn ngoài này không bao giờ xuất hiện.\n */\n \"bg-card data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 flex max-h-[calc(100dvh-2rem)] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] flex-col gap-4 overflow-y-auto rounded-xl border border-border p-6 shadow-overlay duration-200\",\n dialogSizeStyles[size],\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"absolute top-4 right-4 rounded-md p-1 text-muted-foreground transition-colors duration-150 ease-out hover:text-foreground hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n >\n <XIcon />\n <span className=\"sr-only\">{closeLabel}</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nexport type DialogBodyProps = React.ComponentProps<\"div\">;\n\n/**\n * Vùng nội dung CUỘN của hộp thoại, giữa header và footer.\n *\n * ⚠ Trước 1.3.2 component này render `<div className={cn(className)} />` — tức KHÔNG LÀM GÌ CẢ,\n * trong khi chính tài liệu của nó (và của `Dialog`) nói nó \"tách vùng nội dung cuộn khỏi\n * header/footer\". Một component không làm điều nó tự nhận là loại lỗi tệ nhất trong một gói dùng\n * chung: chỗ gọi đã làm ĐÚNG rồi mà vẫn hỏng, nên người ta đi tìm nguyên nhân ở app.\n *\n * ⚠ `flex-auto` chứ không `flex-1`: `flex-1` là `flex: 1 1 0%`, tức lấy 0 làm cỡ cơ sở. `flex-auto`\n * (`1 1 auto`) lấy cỡ NỘI DUNG làm cơ sở, nên hộp thoại ngắn vẫn ôm sát nội dung thay vì bị kéo\n * giãn. `min-h-0` là mảnh bắt buộc còn lại — thiếu nó, `min-height: auto` mặc định của flex item\n * giữ nguyên chiều cao nội dung và không gì cuộn được.\n */\nfunction DialogBody({ className, ...props }: DialogBodyProps) {\n return (\n <div\n data-slot=\"dialog-body\"\n /*\n * ⚠ `-m-1 p-1` KHÔNG phải để cho thoáng — nó là chỗ cho FOCUS RING thở.\n *\n * CSS không cho một trục `auto` còn trục kia `visible`: đặt `overflow-y: auto` thì\n * `overflow-x` **tự thành `auto`**, và hộp cắt xuất hiện ở CẢ HAI chiều. Con của vùng này\n * là `Input` `w-full`, tức mép nó trùng khít mép hộp cắt (đo được: chỗ trống hai bên đúng\n * 0px) — nên `focus:ring-2` nằm 2px bên ngoài bị cắt sạch. Ô đang focus trông như không\n * focus, và đó là dấu hiệu DUY NHẤT cho người dùng bàn phím.\n *\n * `p-1` cho 4,5px chỗ trống (rem gốc 18px); `-m-1` kéo hộp lại đúng bấy nhiêu nên vị trí\n * NỘI DUNG không đổi một pixel — khoảng cách tới header/footer vẫn là `gap-4`.\n *\n * ⚠ Đừng bê khuôn này sang `SheetContent`/`DrawerContent`: chúng `position: fixed` kèm\n * `inset-x-0`/`right-0`, nên margin ÂM làm chúng rộng ra 9px và tràn khỏi khung nhìn.\n * Ở đó, con phải tự mang padding — `SheetHeader`/`SheetFooter` đã có `p-4`.\n */\n className={cn(\"-m-1 min-h-0 flex-auto overflow-y-auto p-1\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogHeaderProps = React.ComponentProps<\"div\">;\n\nfunction DialogHeader({ className, ...props }: DialogHeaderProps) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogFooterProps = React.ComponentProps<\"div\">;\n\nfunction DialogFooter({ className, ...props }: DialogFooterProps) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogTitleProps = React.ComponentProps<typeof DialogPrimitive.Title>;\n\nfunction DialogTitle({ className, ...props }: DialogTitleProps) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg font-semibold text-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport type DialogDescriptionProps = React.ComponentProps<typeof DialogPrimitive.Description>;\n\nfunction DialogDescription({ className, ...props }: DialogDescriptionProps) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogBody,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger\n};\n"],"mappings":"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","names":[]}
|
|
@@ -9,7 +9,7 @@ import { Drawer as Drawer$1 } from 'vaul';
|
|
|
9
9
|
* style bám theo thuộc tính `data-vaul-drawer-direction` mà vaul tự gắn.
|
|
10
10
|
*/
|
|
11
11
|
type DrawerProps = React.ComponentProps<typeof Drawer$1.Root>;
|
|
12
|
-
declare function Drawer({ ...props }: DrawerProps): React.JSX.Element;
|
|
12
|
+
declare function Drawer({ autoFocus, ...props }: DrawerProps): React.JSX.Element;
|
|
13
13
|
type DrawerTriggerProps = React.ComponentProps<typeof Drawer$1.Trigger>;
|
|
14
14
|
declare function DrawerTrigger({ ...props }: DrawerTriggerProps): React.JSX.Element;
|
|
15
15
|
type DrawerPortalProps = React.ComponentProps<typeof Drawer$1.Portal>;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Drawer as DrawerPrimitive } from "vaul";
|
|
4
4
|
import { cn } from "../../lib/cn.js";
|
|
5
|
-
function Drawer({ ...props }) {
|
|
6
|
-
return /* @__PURE__ */ jsx(DrawerPrimitive.Root, { "data-slot": "drawer", ...props });
|
|
5
|
+
function Drawer({ autoFocus = true, ...props }) {
|
|
6
|
+
return /* @__PURE__ */ jsx(DrawerPrimitive.Root, { "data-slot": "drawer", autoFocus, ...props });
|
|
7
7
|
}
|
|
8
8
|
function DrawerTrigger({ ...props }) {
|
|
9
9
|
return /* @__PURE__ */ jsx(DrawerPrimitive.Trigger, { "data-slot": "drawer-trigger", ...props });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/drawer.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Drawer — panel kéo-thả (vaul), có quán tính và tay cầm kéo như app di động.\n *\n * Khác `Sheet` (Radix Dialog, trượt cứng): Drawer cho phép người dùng vuốt để đóng,\n * hợp với màn hình cảm ứng. Hướng đặt bằng prop `direction` của vaul —\n * style bám theo thuộc tính `data-vaul-drawer-direction` mà vaul tự gắn.\n */\n\nexport type DrawerProps = React.ComponentProps<typeof DrawerPrimitive.Root>;\n\nfunction Drawer({ ...props }: DrawerProps) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />;\n}\n\nexport type DrawerTriggerProps = React.ComponentProps<typeof DrawerPrimitive.Trigger>;\n\nfunction DrawerTrigger({ ...props }: DrawerTriggerProps) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />;\n}\n\nexport type DrawerPortalProps = React.ComponentProps<typeof DrawerPrimitive.Portal>;\n\nfunction DrawerPortal({ ...props }: DrawerPortalProps) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />;\n}\n\nexport type DrawerCloseProps = React.ComponentProps<typeof DrawerPrimitive.Close>;\n\nfunction DrawerClose({ ...props }: DrawerCloseProps) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />;\n}\n\nexport type DrawerOverlayProps = React.ComponentProps<typeof DrawerPrimitive.Overlay>;\n\nfunction DrawerOverlay({ className, ...props }: DrawerOverlayProps) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-ink/60 dark:bg-ink/80 backdrop-blur-sm\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type DrawerContentProps = React.ComponentProps<typeof DrawerPrimitive.Content>;\n\nfunction DrawerContent({ className, children, ...props }: DrawerContentProps) {\n return (\n <DrawerPortal data-slot=\"drawer-portal\">\n <DrawerOverlay />\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n /*\n * `overflow-y-auto`: `max-h-[80vh]` ở hai hướng trên/dưới đặt trần chiều cao nhưng\n * KHÔNG có gì cuộn phần vượt trần — nội dung dài bị cắt im lặng. Cùng lỗi với\n * `DialogContent` trước 1.3.2.\n */\n \"group/drawer-content bg-background fixed z-50 flex h-auto flex-col overflow-y-auto\",\n \"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b\",\n \"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t\",\n \"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm\",\n \"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm\",\n className\n )}\n {...props}\n >\n {/*\n Tay cầm kéo — chỉ hiện khi drawer trượt từ đáy lên.\n Màu `--control-edge`, KHÔNG phải `--muted`: mảng `bg-*` này CHÍNH LÀ hình dạng của dấu\n duy nhất nói \"kéo ở đây\", nên nó chịu ngưỡng 3:1 (WCAG 1.4.11) chứ không phải là một\n bề mặt. `bg-muted` trên `bg-background` của DrawerContent chỉ 1,07:1 (light) · 1,17:1\n (dark) — coi như không hiện. `bg-control-edge`: 3,83:1 (light) · 4,94:1 (dark) ✓\n */}\n <div className=\"bg-control-edge mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n );\n}\n\nexport type DrawerHeaderProps = React.ComponentProps<\"div\">;\n\nfunction DrawerHeader({ className, ...props }: DrawerHeaderProps) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\n \"flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type DrawerFooterProps = React.ComponentProps<\"div\">;\n\nfunction DrawerFooter({ className, ...props }: DrawerFooterProps) {\n return <div data-slot=\"drawer-footer\" className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)} {...props} />;\n}\n\nexport type DrawerTitleProps = React.ComponentProps<typeof DrawerPrimitive.Title>;\n\nfunction DrawerTitle({ className, ...props }: DrawerTitleProps) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn(\"text-foreground font-semibold\", className)}\n {...props}\n />\n );\n}\n\nexport type DrawerDescriptionProps = React.ComponentProps<typeof DrawerPrimitive.Description>;\n\nfunction DrawerDescription({ className, ...props }: DrawerDescriptionProps) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Drawer,\n DrawerClose,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerOverlay,\n DrawerPortal,\n DrawerTitle,\n DrawerTrigger\n};\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/drawer.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Drawer — panel kéo-thả (vaul), có quán tính và tay cầm kéo như app di động.\n *\n * Khác `Sheet` (Radix Dialog, trượt cứng): Drawer cho phép người dùng vuốt để đóng,\n * hợp với màn hình cảm ứng. Hướng đặt bằng prop `direction` của vaul —\n * style bám theo thuộc tính `data-vaul-drawer-direction` mà vaul tự gắn.\n */\n\nexport type DrawerProps = React.ComponentProps<typeof DrawerPrimitive.Root>;\n\n/*\n * ⚠ `autoFocus` mặc định `true` — NGƯỢC với vaul. vaul 1.1.2 để `autoFocus = false`, và khi đó\n * `Drawer.Content` gọi `preventDefault()` lên `onOpenAutoFocus` của Radix Dialog bên dưới: focus\n * ở lại trên nút mở, NẰM SAU lớp phủ. Bẫy focus của Radix (FocusScope) chỉ giữ focus đã vào\n * trong nó, nên nó không bao giờ bắt đầu — Tab đi khắp trang phía sau một hộp modal (WCAG 2.4.3).\n * Đã đo trên trình duyệt thật ở 375px, lúc port `ResponsiveDialog` (2.1.0). Với `true`, Radix\n * đưa focus vào phần tử focus được ĐẦU TIÊN trong ngăn kéo — đúng như `Dialog`/`Sheet` của gói.\n *\n * Cái giá: phần tử đầu tiên là ô nhập thì bàn phím ảo bật lên ngay lúc mở. Muốn focus chỗ khác\n * thì gọi `event.preventDefault()` trong `onOpenAutoFocus` của `DrawerContent` rồi tự `focus()`\n * phần tử muốn nhận. `autoFocus={false}` vẫn trả về hành vi gốc của vaul, nhưng khi đó chính app\n * phải tự đưa focus vào trong — không làm thì ngăn kéo lại thành một modal mà bàn phím đi xuyên qua.\n */\nfunction Drawer({ autoFocus = true, ...props }: DrawerProps) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" autoFocus={autoFocus} {...props} />;\n}\n\nexport type DrawerTriggerProps = React.ComponentProps<typeof DrawerPrimitive.Trigger>;\n\nfunction DrawerTrigger({ ...props }: DrawerTriggerProps) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />;\n}\n\nexport type DrawerPortalProps = React.ComponentProps<typeof DrawerPrimitive.Portal>;\n\nfunction DrawerPortal({ ...props }: DrawerPortalProps) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />;\n}\n\nexport type DrawerCloseProps = React.ComponentProps<typeof DrawerPrimitive.Close>;\n\nfunction DrawerClose({ ...props }: DrawerCloseProps) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />;\n}\n\nexport type DrawerOverlayProps = React.ComponentProps<typeof DrawerPrimitive.Overlay>;\n\nfunction DrawerOverlay({ className, ...props }: DrawerOverlayProps) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-ink/60 dark:bg-ink/80 backdrop-blur-sm\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type DrawerContentProps = React.ComponentProps<typeof DrawerPrimitive.Content>;\n\nfunction DrawerContent({ className, children, ...props }: DrawerContentProps) {\n return (\n <DrawerPortal data-slot=\"drawer-portal\">\n <DrawerOverlay />\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n /*\n * `overflow-y-auto`: `max-h-[80vh]` ở hai hướng trên/dưới đặt trần chiều cao nhưng\n * KHÔNG có gì cuộn phần vượt trần — nội dung dài bị cắt im lặng. Cùng lỗi với\n * `DialogContent` trước 1.3.2.\n */\n \"group/drawer-content bg-background fixed z-50 flex h-auto flex-col overflow-y-auto\",\n \"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b\",\n \"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t\",\n \"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm\",\n \"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm\",\n className\n )}\n {...props}\n >\n {/*\n Tay cầm kéo — chỉ hiện khi drawer trượt từ đáy lên.\n Màu `--control-edge`, KHÔNG phải `--muted`: mảng `bg-*` này CHÍNH LÀ hình dạng của dấu\n duy nhất nói \"kéo ở đây\", nên nó chịu ngưỡng 3:1 (WCAG 1.4.11) chứ không phải là một\n bề mặt. `bg-muted` trên `bg-background` của DrawerContent chỉ 1,07:1 (light) · 1,17:1\n (dark) — coi như không hiện. `bg-control-edge`: 3,83:1 (light) · 4,94:1 (dark) ✓\n */}\n <div className=\"bg-control-edge mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n );\n}\n\nexport type DrawerHeaderProps = React.ComponentProps<\"div\">;\n\nfunction DrawerHeader({ className, ...props }: DrawerHeaderProps) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\n \"flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type DrawerFooterProps = React.ComponentProps<\"div\">;\n\nfunction DrawerFooter({ className, ...props }: DrawerFooterProps) {\n return <div data-slot=\"drawer-footer\" className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)} {...props} />;\n}\n\nexport type DrawerTitleProps = React.ComponentProps<typeof DrawerPrimitive.Title>;\n\nfunction DrawerTitle({ className, ...props }: DrawerTitleProps) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn(\"text-foreground font-semibold\", className)}\n {...props}\n />\n );\n}\n\nexport type DrawerDescriptionProps = React.ComponentProps<typeof DrawerPrimitive.Description>;\n\nfunction DrawerDescription({ className, ...props }: DrawerDescriptionProps) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Drawer,\n DrawerClose,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerOverlay,\n DrawerPortal,\n DrawerTitle,\n DrawerTrigger\n};\n"],"mappings":";AA8BS,cA0CH,YA1CG;AA3BT,SAAS,UAAU,uBAAuB;AAC1C,SAAS,UAAU;AAyBnB,SAAS,OAAO,EAAE,YAAY,MAAM,GAAG,MAAM,GAAgB;AAC3D,SAAO,oBAAC,gBAAgB,MAAhB,EAAqB,aAAU,UAAS,WAAuB,GAAG,OAAO;AACnF;AAIA,SAAS,cAAc,EAAE,GAAG,MAAM,GAAuB;AACvD,SAAO,oBAAC,gBAAgB,SAAhB,EAAwB,aAAU,kBAAkB,GAAG,OAAO;AACxE;AAIA,SAAS,aAAa,EAAE,GAAG,MAAM,GAAsB;AACrD,SAAO,oBAAC,gBAAgB,QAAhB,EAAuB,aAAU,iBAAiB,GAAG,OAAO;AACtE;AAIA,SAAS,YAAY,EAAE,GAAG,MAAM,GAAqB;AACnD,SAAO,oBAAC,gBAAgB,OAAhB,EAAsB,aAAU,gBAAgB,GAAG,OAAO;AACpE;AAIA,SAAS,cAAc,EAAE,WAAW,GAAG,MAAM,GAAuB;AAClE,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,cAAc,EAAE,WAAW,UAAU,GAAG,MAAM,GAAuB;AAC5E,SACE,qBAAC,gBAAa,aAAU,iBACtB;AAAA,wBAAC,iBAAc;AAAA,IACf;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,aAAU;AAAA,QACV,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QASJ;AAAA,8BAAC,SAAI,WAAU,0IAAyI;AAAA,UACvJ;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAIA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SAAO,oBAAC,SAAI,aAAU,iBAAgB,WAAW,GAAG,mCAAmC,SAAS,GAAI,GAAG,OAAO;AAChH;AAIA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iCAAiC,SAAS;AAAA,MACvD,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,kBAAkB,EAAE,WAAW,GAAG,MAAM,GAA2B;AAC1E,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iCAAiC,SAAS;AAAA,MACvD,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -10,7 +10,7 @@ declare function ItemGroup({ className, ...props }: ItemGroupProps): React.JSX.E
|
|
|
10
10
|
declare function ItemSeparator({ className, ...props }: ItemSeparatorProps): React.JSX.Element;
|
|
11
11
|
declare const itemVariants: (props?: ({
|
|
12
12
|
variant?: "default" | "outline" | "muted" | null | undefined;
|
|
13
|
-
size?: "
|
|
13
|
+
size?: "default" | "sm" | null | undefined;
|
|
14
14
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
15
15
|
interface ItemProps extends React.ComponentProps<"div">, VariantProps<typeof itemVariants> {
|
|
16
16
|
/** Render ra chính phần tử con (Radix Slot) — dùng khi cần <a> hoặc <button>. */
|
|
@@ -18,7 +18,7 @@ interface ItemProps extends React.ComponentProps<"div">, VariantProps<typeof ite
|
|
|
18
18
|
}
|
|
19
19
|
declare function Item({ className, variant, size, asChild, ...props }: ItemProps): React.JSX.Element;
|
|
20
20
|
declare const itemMediaVariants: (props?: ({
|
|
21
|
-
variant?: "
|
|
21
|
+
variant?: "default" | "image" | "icon" | null | undefined;
|
|
22
22
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
23
23
|
interface ItemMediaProps extends React.ComponentProps<"div">, VariantProps<typeof itemMediaVariants> {
|
|
24
24
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { UniqueIdentifier } from '@dnd-kit/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Nhãn mặc định của `Kanban` — lời xướng cho trình đọc màn hình và tên của tay cầm kéo.
|
|
5
|
+
*
|
|
6
|
+
* Module **CỐ Ý không có `"use client"`** — xem `src/chart/chart-colors.ts` để biết lý do đầy đủ:
|
|
7
|
+
* mọi export của một file `"use client"` đến tay Server Component dưới dạng client-reference
|
|
8
|
+
* proxy, đọc ra `undefined` mà KHÔNG throw, KHÔNG warning. `DEFAULT_KANBAN_LABELS` là một object
|
|
9
|
+
* literal (dù phần lớn giá trị là HÀM) nên phải sống ngoài ranh giới client.
|
|
10
|
+
*
|
|
11
|
+
* ⚠ Barrel `src/index.ts` xuất module này TRỰC TIẾP. `kanban.tsx` chỉ được `import`, KHÔNG được
|
|
12
|
+
* `export` lại (re-export qua file client là vô hiệu, và đụng tên với `export *`).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Dữ liệu dnd-kit đã tính sẵn, đưa vào các hàm xướng của `KanbanLabels`. */
|
|
16
|
+
interface KanbanAnnouncementInfo {
|
|
17
|
+
/** `"column"` khi đang kéo cả CỘT, `"item"` khi đang kéo một THẺ. */
|
|
18
|
+
variant: "column" | "item";
|
|
19
|
+
/** `value` của cột/thẻ đang kéo — dùng để tra ra TÊN hiển thị khi tự viết nhãn. */
|
|
20
|
+
value: UniqueIdentifier;
|
|
21
|
+
/** Vị trí hiện tại, đếm từ 1. */
|
|
22
|
+
position: number;
|
|
23
|
+
/** Tổng số cột (khi kéo cột) hoặc tổng số thẻ trong cột đang đứng (khi kéo thẻ). */
|
|
24
|
+
total: number;
|
|
25
|
+
/**
|
|
26
|
+
* `value` của cột ĐÍCH — chỉ có khi thẻ vừa sang một cột KHÁC; `undefined` khi thẻ còn trong
|
|
27
|
+
* cột cũ, và luôn `undefined` khi kéo cột. Là khoá máy (`"inProgress"`), không phải tên hiển
|
|
28
|
+
* thị: app có tên cột thì tự viết hàm xướng và tra tên từ khoá này.
|
|
29
|
+
*/
|
|
30
|
+
column?: UniqueIdentifier;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Mọi chuỗi mà `Kanban` tự đọc/tự gắn. Chuỗi có số chèn vào là HÀM, không phải chuỗi mẫu.
|
|
34
|
+
* Theo `ComitorLocaleProvider` (mặc định tiếng Anh, bảng tiếng Việt dựng sẵn); đè từng khoá qua
|
|
35
|
+
* prop `labels` của `Kanban`.
|
|
36
|
+
*
|
|
37
|
+
* Bốn hàm đầu đi vào `accessibility.announcements` của dnd-kit (vùng `aria-live`). App vẫn có thể
|
|
38
|
+
* truyền thẳng `accessibility.announcements` như thượng nguồn — khi đó nó thắng hoàn toàn.
|
|
39
|
+
*/
|
|
40
|
+
interface KanbanLabels {
|
|
41
|
+
/** Xướng lúc nhấc một cột/thẻ lên. */
|
|
42
|
+
pickedUp: (info: KanbanAnnouncementInfo) => string;
|
|
43
|
+
/** Xướng mỗi lần cột/thẻ đang kéo đổi chỗ. */
|
|
44
|
+
moved: (info: KanbanAnnouncementInfo) => string;
|
|
45
|
+
/** Xướng lúc thả. */
|
|
46
|
+
dropped: (info: KanbanAnnouncementInfo) => string;
|
|
47
|
+
/** Xướng lúc huỷ kéo (Escape) — cột/thẻ trở về chỗ cũ. */
|
|
48
|
+
cancelled: (info: Pick<KanbanAnnouncementInfo, "variant" | "value">) => string;
|
|
49
|
+
/** Hướng dẫn thao tác bàn phím, gắn vào mọi phần tử kéo được qua `aria-describedby`. */
|
|
50
|
+
instructions: string;
|
|
51
|
+
/** `aria-roledescription` của cột kéo được (và tay cầm của nó) — dnd-kit mặc định là "sortable". */
|
|
52
|
+
columnRoleDescription: string;
|
|
53
|
+
/** `aria-roledescription` của thẻ kéo được (và tay cầm của nó) — dnd-kit mặc định là "sortable". */
|
|
54
|
+
itemRoleDescription: string;
|
|
55
|
+
/** Tên mặc định của `KanbanColumnHandle` — tay cầm thường chỉ có một icon. */
|
|
56
|
+
columnHandleLabel: string;
|
|
57
|
+
/** Tên mặc định của `KanbanItemHandle` — tay cầm thường chỉ có một icon. */
|
|
58
|
+
itemHandleLabel: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Nhãn TIẾNG ANH mặc định (từ `3.0.0`; trước đó là tiếng Việt). Bảng tiếng Việt:
|
|
62
|
+
* `comitorMessagesVi.kanban`. Component không đọc thẳng hằng này — nó đọc bảng của
|
|
63
|
+
* `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.
|
|
64
|
+
*/
|
|
65
|
+
declare const DEFAULT_KANBAN_LABELS: KanbanLabels;
|
|
66
|
+
|
|
67
|
+
export { DEFAULT_KANBAN_LABELS, type KanbanAnnouncementInfo, type KanbanLabels };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/kanban-constants.ts"],"sourcesContent":["/**\n * Nhãn mặc định của `Kanban` — lời xướng cho trình đọc màn hình và tên của tay cầm kéo.\n *\n * Module **CỐ Ý không có `\"use client\"`** — xem `src/chart/chart-colors.ts` để biết lý do đầy đủ:\n * mọi export của một file `\"use client\"` đến tay Server Component dưới dạng client-reference\n * proxy, đọc ra `undefined` mà KHÔNG throw, KHÔNG warning. `DEFAULT_KANBAN_LABELS` là một object\n * literal (dù phần lớn giá trị là HÀM) nên phải sống ngoài ranh giới client.\n *\n * ⚠ Barrel `src/index.ts` xuất module này TRỰC TIẾP. `kanban.tsx` chỉ được `import`, KHÔNG được\n * `export` lại (re-export qua file client là vô hiệu, và đụng tên với `export *`).\n */\n\nimport type { UniqueIdentifier } from \"@dnd-kit/core\";\nimport { kanbanEn } from \"../../i18n/namespaces/kanban.js\";\n\n/** Dữ liệu dnd-kit đã tính sẵn, đưa vào các hàm xướng của `KanbanLabels`. */\nexport interface KanbanAnnouncementInfo {\n /** `\"column\"` khi đang kéo cả CỘT, `\"item\"` khi đang kéo một THẺ. */\n variant: \"column\" | \"item\";\n /** `value` của cột/thẻ đang kéo — dùng để tra ra TÊN hiển thị khi tự viết nhãn. */\n value: UniqueIdentifier;\n /** Vị trí hiện tại, đếm từ 1. */\n position: number;\n /** Tổng số cột (khi kéo cột) hoặc tổng số thẻ trong cột đang đứng (khi kéo thẻ). */\n total: number;\n /**\n * `value` của cột ĐÍCH — chỉ có khi thẻ vừa sang một cột KHÁC; `undefined` khi thẻ còn trong\n * cột cũ, và luôn `undefined` khi kéo cột. Là khoá máy (`\"inProgress\"`), không phải tên hiển\n * thị: app có tên cột thì tự viết hàm xướng và tra tên từ khoá này.\n */\n column?: UniqueIdentifier;\n}\n\n/**\n * Mọi chuỗi mà `Kanban` tự đọc/tự gắn. Chuỗi có số chèn vào là HÀM, không phải chuỗi mẫu.\n * Theo `ComitorLocaleProvider` (mặc định tiếng Anh, bảng tiếng Việt dựng sẵn); đè từng khoá qua\n * prop `labels` của `Kanban`.\n *\n * Bốn hàm đầu đi vào `accessibility.announcements` của dnd-kit (vùng `aria-live`). App vẫn có thể\n * truyền thẳng `accessibility.announcements` như thượng nguồn — khi đó nó thắng hoàn toàn.\n */\nexport interface KanbanLabels {\n /** Xướng lúc nhấc một cột/thẻ lên. */\n pickedUp: (info: KanbanAnnouncementInfo) => string;\n /** Xướng mỗi lần cột/thẻ đang kéo đổi chỗ. */\n moved: (info: KanbanAnnouncementInfo) => string;\n /** Xướng lúc thả. */\n dropped: (info: KanbanAnnouncementInfo) => string;\n /** Xướng lúc huỷ kéo (Escape) — cột/thẻ trở về chỗ cũ. */\n cancelled: (info: Pick<KanbanAnnouncementInfo, \"variant\" | \"value\">) => string;\n /** Hướng dẫn thao tác bàn phím, gắn vào mọi phần tử kéo được qua `aria-describedby`. */\n instructions: string;\n /** `aria-roledescription` của cột kéo được (và tay cầm của nó) — dnd-kit mặc định là \"sortable\". */\n columnRoleDescription: string;\n /** `aria-roledescription` của thẻ kéo được (và tay cầm của nó) — dnd-kit mặc định là \"sortable\". */\n itemRoleDescription: string;\n /** Tên mặc định của `KanbanColumnHandle` — tay cầm thường chỉ có một icon. */\n columnHandleLabel: string;\n /** Tên mặc định của `KanbanItemHandle` — tay cầm thường chỉ có một icon. */\n itemHandleLabel: string;\n}\n\n/**\n * Nhãn TIẾNG ANH mặc định (từ `3.0.0`; trước đó là tiếng Việt). Bảng tiếng Việt:\n * `comitorMessagesVi.kanban`. Component không đọc thẳng hằng này — nó đọc bảng của\n * `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.\n */\nexport const DEFAULT_KANBAN_LABELS: KanbanLabels = { ...kanbanEn };\n"],"mappings":"AAaA,SAAS,gBAAgB;AAsDlB,MAAM,wBAAsC,EAAE,GAAG,SAAS;","names":[]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DndContextProps, UniqueIdentifier, DragEndEvent, DragOverlay } from '@dnd-kit/core';
|
|
2
|
+
import { SortableContextProps } from '@dnd-kit/sortable';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { KanbanLabels } from './kanban-constants.js';
|
|
5
|
+
|
|
6
|
+
interface GetItemValue<T> {
|
|
7
|
+
/**
|
|
8
|
+
* Trả về định danh duy nhất của mỗi thẻ. BẮT BUỘC khi thẻ là object.
|
|
9
|
+
* @example getItemValue={(item) => item.id}
|
|
10
|
+
*/
|
|
11
|
+
getItemValue: (item: T) => UniqueIdentifier;
|
|
12
|
+
}
|
|
13
|
+
type KanbanProps<T> = Omit<DndContextProps, "collisionDetection"> & (T extends object ? GetItemValue<T> : Partial<GetItemValue<T>>) & {
|
|
14
|
+
value: Record<UniqueIdentifier, T[]>;
|
|
15
|
+
onValueChange?: (columns: Record<UniqueIdentifier, T[]>) => void;
|
|
16
|
+
onMove?: (event: DragEndEvent & {
|
|
17
|
+
activeIndex: number;
|
|
18
|
+
overIndex: number;
|
|
19
|
+
}) => void;
|
|
20
|
+
strategy?: SortableContextProps["strategy"];
|
|
21
|
+
orientation?: "horizontal" | "vertical";
|
|
22
|
+
flatCursor?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Lời xướng cho trình đọc màn hình, hướng dẫn bàn phím và tên tay cầm kéo — ghi đè TỪNG khoá.
|
|
25
|
+
* Không truyền thì theo namespace `kanban` của `ComitorLocaleProvider` (mặc định tiếng Anh).
|
|
26
|
+
*
|
|
27
|
+
* ⚠ Thêm so với thượng nguồn: bản gốc chôn cứng câu tiếng Anh ("Picked up item at position
|
|
28
|
+
* 2 of 5") trong component; chỉ đổi được bằng cách viết lại cả `accessibility.announcements`,
|
|
29
|
+
* tức tự tính lại vị trí/tổng số. Ở đây các hàm nhận sẵn `{ variant, value, position, total,
|
|
30
|
+
* column }`. `accessibility` truyền thẳng vẫn được tôn trọng và THẮNG `labels`.
|
|
31
|
+
*/
|
|
32
|
+
labels?: Partial<KanbanLabels>;
|
|
33
|
+
};
|
|
34
|
+
declare function Kanban<T>(props: KanbanProps<T>): React.JSX.Element;
|
|
35
|
+
interface KanbanBoardProps extends React.ComponentProps<"div"> {
|
|
36
|
+
children: React.ReactNode;
|
|
37
|
+
asChild?: boolean;
|
|
38
|
+
}
|
|
39
|
+
declare function KanbanBoard(props: KanbanBoardProps): React.JSX.Element;
|
|
40
|
+
interface KanbanColumnProps extends React.ComponentProps<"div"> {
|
|
41
|
+
value: UniqueIdentifier;
|
|
42
|
+
children: React.ReactNode;
|
|
43
|
+
asChild?: boolean;
|
|
44
|
+
asHandle?: boolean;
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
}
|
|
47
|
+
declare function KanbanColumn(props: KanbanColumnProps): React.JSX.Element;
|
|
48
|
+
interface KanbanColumnHandleProps extends React.ComponentProps<"button"> {
|
|
49
|
+
asChild?: boolean;
|
|
50
|
+
}
|
|
51
|
+
declare function KanbanColumnHandle(props: KanbanColumnHandleProps): React.JSX.Element;
|
|
52
|
+
interface KanbanItemProps extends React.ComponentProps<"div"> {
|
|
53
|
+
value: UniqueIdentifier;
|
|
54
|
+
asHandle?: boolean;
|
|
55
|
+
asChild?: boolean;
|
|
56
|
+
disabled?: boolean;
|
|
57
|
+
}
|
|
58
|
+
declare function KanbanItem(props: KanbanItemProps): React.JSX.Element;
|
|
59
|
+
interface KanbanItemHandleProps extends React.ComponentProps<"button"> {
|
|
60
|
+
asChild?: boolean;
|
|
61
|
+
}
|
|
62
|
+
declare function KanbanItemHandle(props: KanbanItemHandleProps): React.JSX.Element;
|
|
63
|
+
interface KanbanOverlayProps extends Omit<React.ComponentProps<typeof DragOverlay>, "children"> {
|
|
64
|
+
container?: Element | DocumentFragment | null;
|
|
65
|
+
children?: React.ReactNode | ((params: {
|
|
66
|
+
value: UniqueIdentifier;
|
|
67
|
+
variant: "column" | "item";
|
|
68
|
+
}) => React.ReactNode);
|
|
69
|
+
}
|
|
70
|
+
declare function KanbanOverlay(props: KanbanOverlayProps): React.ReactPortal | null;
|
|
71
|
+
|
|
72
|
+
export { Kanban, KanbanBoard, type KanbanBoardProps, KanbanColumn, KanbanColumnHandle, type KanbanColumnHandleProps, type KanbanColumnProps, KanbanItem, KanbanItemHandle, type KanbanItemHandleProps, type KanbanItemProps, KanbanOverlay, type KanbanOverlayProps, type KanbanProps };
|