@comitor/ui 2.1.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -24
- package/dist/chart/chart.d.ts +6 -1
- package/dist/chart/chart.js +3 -1
- package/dist/chart/chart.js.map +1 -1
- package/dist/chart/comitor-chart.d.ts +4 -1
- package/dist/chart/comitor-chart.js +3 -1
- package/dist/chart/comitor-chart.js.map +1 -1
- package/dist/components/composite/avatar-group.d.ts +9 -6
- package/dist/components/composite/avatar-group.js +6 -2
- package/dist/components/composite/avatar-group.js.map +1 -1
- package/dist/components/composite/combobox.d.ts +11 -11
- package/dist/components/composite/combobox.js +25 -11
- package/dist/components/composite/combobox.js.map +1 -1
- package/dist/components/composite/command-palette-constants.d.ts +15 -5
- package/dist/components/composite/command-palette-constants.js +3 -2
- package/dist/components/composite/command-palette-constants.js.map +1 -1
- package/dist/components/composite/command-palette.d.ts +2 -2
- package/dist/components/composite/command-palette.js +8 -5
- package/dist/components/composite/command-palette.js.map +1 -1
- package/dist/components/composite/confirm-dialog.d.ts +8 -8
- package/dist/components/composite/confirm-dialog.js +8 -3
- package/dist/components/composite/confirm-dialog.js.map +1 -1
- package/dist/components/composite/copy-button.d.ts +7 -3
- package/dist/components/composite/copy-button.js +7 -3
- package/dist/components/composite/copy-button.js.map +1 -1
- package/dist/components/composite/data-table-constants.d.ts +7 -3
- package/dist/components/composite/data-table-constants.js +2 -9
- package/dist/components/composite/data-table-constants.js.map +1 -1
- package/dist/components/composite/data-table.js +3 -3
- package/dist/components/composite/data-table.js.map +1 -1
- package/dist/components/composite/date-picker-constants.d.ts +32 -9
- package/dist/components/composite/date-picker-constants.js +16 -11
- package/dist/components/composite/date-picker-constants.js.map +1 -1
- package/dist/components/composite/date-picker.d.ts +19 -10
- package/dist/components/composite/date-picker.js +33 -9
- package/dist/components/composite/date-picker.js.map +1 -1
- package/dist/components/composite/filter-chips.d.ts +4 -3
- package/dist/components/composite/filter-chips.js +6 -2
- package/dist/components/composite/filter-chips.js.map +1 -1
- package/dist/components/composite/inline-edit.d.ts +14 -3
- package/dist/components/composite/inline-edit.js +8 -4
- package/dist/components/composite/inline-edit.js.map +1 -1
- package/dist/components/composite/relative-time.d.ts +4 -4
- package/dist/components/composite/relative-time.js +4 -2
- package/dist/components/composite/relative-time.js.map +1 -1
- package/dist/components/composite/route-tabs-nav.d.ts +9 -0
- package/dist/components/composite/route-tabs-nav.js +11 -0
- package/dist/components/composite/route-tabs-nav.js.map +1 -0
- package/dist/components/composite/route-tabs.d.ts +4 -1
- package/dist/components/composite/route-tabs.js +3 -2
- package/dist/components/composite/route-tabs.js.map +1 -1
- package/dist/components/composite/search-filter-bar-constants.d.ts +18 -8
- package/dist/components/composite/search-filter-bar-constants.js +2 -14
- package/dist/components/composite/search-filter-bar-constants.js.map +1 -1
- package/dist/components/composite/search-filter-bar.d.ts +5 -2
- package/dist/components/composite/search-filter-bar.js +2 -2
- package/dist/components/composite/search-filter-bar.js.map +1 -1
- package/dist/components/composite/stat-card.d.ts +1 -1
- package/dist/components/composite/table-pagination-constants.d.ts +16 -11
- package/dist/components/composite/table-pagination-constants.js +2 -12
- package/dist/components/composite/table-pagination-constants.js.map +1 -1
- package/dist/components/composite/table-pagination.d.ts +6 -3
- package/dist/components/composite/table-pagination.js +3 -5
- package/dist/components/composite/table-pagination.js.map +1 -1
- package/dist/components/composite/toast.d.ts +5 -2
- package/dist/components/composite/toast.js +3 -1
- package/dist/components/composite/toast.js.map +1 -1
- package/dist/components/ui/action-bar-constants.d.ts +8 -4
- package/dist/components/ui/action-bar-constants.js +2 -5
- package/dist/components/ui/action-bar-constants.js.map +1 -1
- package/dist/components/ui/action-bar.d.ts +5 -4
- package/dist/components/ui/action-bar.js +47 -3
- package/dist/components/ui/action-bar.js.map +1 -1
- package/dist/components/ui/alert.d.ts +1 -1
- package/dist/components/ui/badge-overflow.d.ts +2 -2
- package/dist/components/ui/badge-overflow.js +4 -2
- package/dist/components/ui/badge-overflow.js.map +1 -1
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/banner.d.ts +6 -5
- package/dist/components/ui/banner.js +3 -1
- package/dist/components/ui/banner.js.map +1 -1
- package/dist/components/ui/breadcrumb.d.ts +11 -4
- package/dist/components/ui/breadcrumb.js +7 -2
- package/dist/components/ui/breadcrumb.js.map +1 -1
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/components/ui/calendar.d.ts +12 -5
- package/dist/components/ui/calendar.js +38 -3
- package/dist/components/ui/calendar.js.map +1 -1
- package/dist/components/ui/carousel.d.ts +4 -3
- package/dist/components/ui/carousel.js +11 -4
- package/dist/components/ui/carousel.js.map +1 -1
- package/dist/components/ui/circular-progress.js +10 -2
- package/dist/components/ui/circular-progress.js.map +1 -1
- package/dist/components/ui/command.d.ts +5 -4
- package/dist/components/ui/command.js +6 -2
- package/dist/components/ui/command.js.map +1 -1
- package/dist/components/ui/cropper-constants.d.ts +8 -3
- package/dist/components/ui/cropper-constants.js +2 -6
- package/dist/components/ui/cropper-constants.js.map +1 -1
- package/dist/components/ui/cropper.d.ts +2 -1
- package/dist/components/ui/cropper.js +2 -2
- package/dist/components/ui/cropper.js.map +1 -1
- package/dist/components/ui/dialog.d.ts +2 -2
- package/dist/components/ui/dialog.js +4 -1
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/item.d.ts +2 -2
- package/dist/components/ui/kanban-constants.d.ts +7 -1
- package/dist/components/ui/kanban-constants.js +2 -17
- package/dist/components/ui/kanban-constants.js.map +1 -1
- package/dist/components/ui/kanban.d.ts +2 -2
- package/dist/components/ui/kanban.js +11 -5
- package/dist/components/ui/kanban.js.map +1 -1
- package/dist/components/ui/key-value-constants.d.ts +7 -3
- package/dist/components/ui/key-value-constants.js +2 -7
- package/dist/components/ui/key-value-constants.js.map +1 -1
- package/dist/components/ui/key-value.d.ts +5 -5
- package/dist/components/ui/key-value.js +72 -78
- package/dist/components/ui/key-value.js.map +1 -1
- package/dist/components/ui/mask-input-constants.d.ts +2 -3
- package/dist/components/ui/mask-input-constants.js +36 -77
- package/dist/components/ui/mask-input-constants.js.map +1 -1
- package/dist/components/ui/mask-input.d.ts +7 -4
- package/dist/components/ui/mask-input.js +22 -22
- package/dist/components/ui/mask-input.js.map +1 -1
- package/dist/components/ui/mention.d.ts +3 -2
- package/dist/components/ui/mention.js +4 -6
- package/dist/components/ui/mention.js.map +1 -1
- package/dist/components/ui/pagination.d.ts +13 -9
- package/dist/components/ui/pagination.js +15 -6
- package/dist/components/ui/pagination.js.map +1 -1
- package/dist/components/ui/phone-input-constants.d.ts +17 -6
- package/dist/components/ui/phone-input-constants.js +2 -5
- package/dist/components/ui/phone-input-constants.js.map +1 -1
- package/dist/components/ui/phone-input.d.ts +18 -11
- package/dist/components/ui/phone-input.js +14 -12
- package/dist/components/ui/phone-input.js.map +1 -1
- package/dist/components/ui/progress.d.ts +2 -2
- package/dist/components/ui/rating.d.ts +2 -1
- package/dist/components/ui/rating.js +8 -4
- package/dist/components/ui/rating.js.map +1 -1
- package/dist/components/ui/scroll-spy.js +3 -1
- package/dist/components/ui/scroll-spy.js.map +1 -1
- package/dist/components/ui/sheet.d.ts +2 -2
- package/dist/components/ui/sheet.js +10 -1
- package/dist/components/ui/sheet.js.map +1 -1
- package/dist/components/ui/slider.d.ts +1 -1
- package/dist/components/ui/slider.js +15 -1
- package/dist/components/ui/slider.js.map +1 -1
- package/dist/components/ui/sonner.js +2 -0
- package/dist/components/ui/sonner.js.map +1 -1
- package/dist/components/ui/spinner.js +4 -1
- package/dist/components/ui/spinner.js.map +1 -1
- package/dist/components/ui/stepper-constants.d.ts +7 -2
- package/dist/components/ui/stepper-constants.js +2 -5
- package/dist/components/ui/stepper-constants.js.map +1 -1
- package/dist/components/ui/stepper.d.ts +2 -1
- package/dist/components/ui/stepper.js +2 -2
- package/dist/components/ui/stepper.js.map +1 -1
- package/dist/components/ui/tags-input.d.ts +3 -2
- package/dist/components/ui/tags-input.js +4 -2
- package/dist/components/ui/tags-input.js.map +1 -1
- package/dist/components/ui/time-picker-constants.d.ts +12 -9
- package/dist/components/ui/time-picker-constants.js +2 -10
- package/dist/components/ui/time-picker-constants.js.map +1 -1
- package/dist/components/ui/time-picker.d.ts +11 -6
- package/dist/components/ui/time-picker.js +5 -7
- package/dist/components/ui/time-picker.js.map +1 -1
- package/dist/components/ui/toggle-group.js +12 -1
- package/dist/components/ui/toggle-group.js.map +1 -1
- package/dist/components/ui/toggle.d.ts +1 -1
- package/dist/components/ui/tour-constants.d.ts +7 -2
- package/dist/components/ui/tour-constants.js +2 -8
- package/dist/components/ui/tour-constants.js.map +1 -1
- package/dist/components/ui/tour.d.ts +3 -2
- package/dist/components/ui/tour.js +5 -4
- package/dist/components/ui/tour.js.map +1 -1
- package/dist/data-grid/data-grid-cell-variants.js +4 -4
- package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
- package/dist/data-grid/data-grid-column-header.js +1 -1
- package/dist/data-grid/data-grid-column-header.js.map +1 -1
- package/dist/data-grid/data-grid-filter-menu.d.ts +5 -4
- package/dist/data-grid/data-grid-filter-menu.js +13 -8
- package/dist/data-grid/data-grid-filter-menu.js.map +1 -1
- package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +4 -4
- package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -1
- package/dist/data-grid/data-grid-locale.d.ts +58 -6
- package/dist/data-grid/data-grid-locale.js +41 -9
- package/dist/data-grid/data-grid-locale.js.map +1 -1
- package/dist/data-grid/data-grid-row-height-menu.d.ts +4 -3
- package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
- package/dist/data-grid/data-grid-sort-menu.d.ts +4 -4
- package/dist/data-grid/data-grid-sort-menu.js +1 -1
- package/dist/data-grid/data-grid-sort-menu.js.map +1 -1
- package/dist/data-grid/data-grid-view-menu.d.ts +4 -4
- package/dist/data-grid/data-grid-view-menu.js.map +1 -1
- package/dist/data-grid/data-grid.d.ts +4 -3
- package/dist/data-grid/data-grid.js +2 -3
- package/dist/data-grid/data-grid.js.map +1 -1
- package/dist/data-grid/index.d.ts +1 -0
- package/dist/data-grid/index.js.map +1 -1
- package/dist/data-grid/messages-en.d.ts +23 -4
- package/dist/data-grid/messages-en.js +20 -0
- package/dist/data-grid/messages-en.js.map +1 -1
- package/dist/data-grid/messages-vi.d.ts +3 -1
- package/dist/data-grid/messages-vi.js +14 -0
- package/dist/data-grid/messages-vi.js.map +1 -1
- package/dist/data-grid/messages.d.ts +29 -3
- package/dist/data-grid/messages.js.map +1 -1
- package/dist/data-grid/sortable.js +16 -19
- package/dist/data-grid/sortable.js.map +1 -1
- package/dist/data-grid/types.d.ts +1 -1
- package/dist/data-grid/use-data-grid-undo-redo.d.ts +5 -4
- package/dist/data-grid/use-data-grid-undo-redo.js +2 -2
- package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
- package/dist/data-grid/use-data-grid.d.ts +23 -8
- package/dist/data-grid/use-data-grid.js +8 -6
- package/dist/data-grid/use-data-grid.js.map +1 -1
- package/dist/data-grid/utils.d.ts +24 -4
- package/dist/data-grid/utils.js +15 -5
- package/dist/data-grid/utils.js.map +1 -1
- package/dist/i18n/locale-provider.d.ts +127 -0
- package/dist/i18n/locale-provider.js +47 -0
- package/dist/i18n/locale-provider.js.map +1 -0
- package/dist/i18n/locale.d.ts +27 -0
- package/dist/i18n/locale.js +10 -0
- package/dist/i18n/locale.js.map +1 -0
- package/dist/i18n/messages.d.ts +116 -0
- package/dist/i18n/messages.js +149 -0
- package/dist/i18n/messages.js.map +1 -0
- package/dist/i18n/namespaces/action-bar.d.ts +7 -0
- package/dist/i18n/namespaces/action-bar.js +15 -0
- package/dist/i18n/namespaces/action-bar.js.map +1 -0
- package/dist/i18n/namespaces/avatar-group.d.ts +14 -0
- package/dist/i18n/namespaces/avatar-group.js +13 -0
- package/dist/i18n/namespaces/avatar-group.js.map +1 -0
- package/dist/i18n/namespaces/badge-overflow.d.ts +12 -0
- package/dist/i18n/namespaces/badge-overflow.js +11 -0
- package/dist/i18n/namespaces/badge-overflow.js.map +1 -0
- package/dist/i18n/namespaces/banner.d.ts +9 -0
- package/dist/i18n/namespaces/banner.js +7 -0
- package/dist/i18n/namespaces/banner.js.map +1 -0
- package/dist/i18n/namespaces/breadcrumb.d.ts +11 -0
- package/dist/i18n/namespaces/breadcrumb.js +13 -0
- package/dist/i18n/namespaces/breadcrumb.js.map +1 -0
- package/dist/i18n/namespaces/calendar.d.ts +40 -0
- package/dist/i18n/namespaces/calendar.js +25 -0
- package/dist/i18n/namespaces/calendar.js.map +1 -0
- package/dist/i18n/namespaces/carousel.d.ts +15 -0
- package/dist/i18n/namespaces/carousel.js +17 -0
- package/dist/i18n/namespaces/carousel.js.map +1 -0
- package/dist/i18n/namespaces/combobox.d.ts +21 -0
- package/dist/i18n/namespaces/combobox.js +23 -0
- package/dist/i18n/namespaces/combobox.js.map +1 -0
- package/dist/i18n/namespaces/command-palette.d.ts +20 -0
- package/dist/i18n/namespaces/command-palette.js +19 -0
- package/dist/i18n/namespaces/command-palette.js.map +1 -0
- package/dist/i18n/namespaces/command.d.ts +11 -0
- package/dist/i18n/namespaces/command.js +13 -0
- package/dist/i18n/namespaces/command.js.map +1 -0
- package/dist/i18n/namespaces/confirm-dialog.d.ts +13 -0
- package/dist/i18n/namespaces/confirm-dialog.js +15 -0
- package/dist/i18n/namespaces/confirm-dialog.js.map +1 -0
- package/dist/i18n/namespaces/contrast.d.ts +7 -0
- package/dist/i18n/namespaces/contrast.js +13 -0
- package/dist/i18n/namespaces/contrast.js.map +1 -0
- package/dist/i18n/namespaces/copy-button.d.ts +11 -0
- package/dist/i18n/namespaces/copy-button.js +13 -0
- package/dist/i18n/namespaces/copy-button.js.map +1 -0
- package/dist/i18n/namespaces/cropper.d.ts +7 -0
- package/dist/i18n/namespaces/cropper.js +17 -0
- package/dist/i18n/namespaces/cropper.js.map +1 -0
- package/dist/i18n/namespaces/data-table.d.ts +7 -0
- package/dist/i18n/namespaces/data-table.js +23 -0
- package/dist/i18n/namespaces/data-table.js.map +1 -0
- package/dist/i18n/namespaces/date-picker.d.ts +42 -0
- package/dist/i18n/namespaces/date-picker.js +35 -0
- package/dist/i18n/namespaces/date-picker.js.map +1 -0
- package/dist/i18n/namespaces/dialog.d.ts +9 -0
- package/dist/i18n/namespaces/dialog.js +7 -0
- package/dist/i18n/namespaces/dialog.js.map +1 -0
- package/dist/i18n/namespaces/display-axis.d.ts +25 -0
- package/dist/i18n/namespaces/display-axis.js +21 -0
- package/dist/i18n/namespaces/display-axis.js.map +1 -0
- package/dist/i18n/namespaces/file-upload.d.ts +11 -0
- package/dist/i18n/namespaces/file-upload.js +19 -0
- package/dist/i18n/namespaces/file-upload.js.map +1 -0
- package/dist/i18n/namespaces/filter-chips.d.ts +11 -0
- package/dist/i18n/namespaces/filter-chips.js +13 -0
- package/dist/i18n/namespaces/filter-chips.js.map +1 -0
- package/dist/i18n/namespaces/image-upload-field.d.ts +12 -0
- package/dist/i18n/namespaces/image-upload-field.js +23 -0
- package/dist/i18n/namespaces/image-upload-field.js.map +1 -0
- package/dist/i18n/namespaces/inline-edit.d.ts +15 -0
- package/dist/i18n/namespaces/inline-edit.js +17 -0
- package/dist/i18n/namespaces/inline-edit.js.map +1 -0
- package/dist/i18n/namespaces/kanban.d.ts +11 -0
- package/dist/i18n/namespaces/kanban.js +39 -0
- package/dist/i18n/namespaces/kanban.js.map +1 -0
- package/dist/i18n/namespaces/key-value.d.ts +10 -0
- package/dist/i18n/namespaces/key-value.js +19 -0
- package/dist/i18n/namespaces/key-value.js.map +1 -0
- package/dist/i18n/namespaces/mention.d.ts +9 -0
- package/dist/i18n/namespaces/mention.js +7 -0
- package/dist/i18n/namespaces/mention.js.map +1 -0
- package/dist/i18n/namespaces/pagination.d.ts +19 -0
- package/dist/i18n/namespaces/pagination.js +21 -0
- package/dist/i18n/namespaces/pagination.js.map +1 -0
- package/dist/i18n/namespaces/phone-input.d.ts +10 -0
- package/dist/i18n/namespaces/phone-input.js +17 -0
- package/dist/i18n/namespaces/phone-input.js.map +1 -0
- package/dist/i18n/namespaces/rating.d.ts +12 -0
- package/dist/i18n/namespaces/rating.js +12 -0
- package/dist/i18n/namespaces/rating.js.map +1 -0
- package/dist/i18n/namespaces/route-tabs.d.ts +9 -0
- package/dist/i18n/namespaces/route-tabs.js +7 -0
- package/dist/i18n/namespaces/route-tabs.js.map +1 -0
- package/dist/i18n/namespaces/scroll-spy.d.ts +9 -0
- package/dist/i18n/namespaces/scroll-spy.js +7 -0
- package/dist/i18n/namespaces/scroll-spy.js.map +1 -0
- package/dist/i18n/namespaces/search-filter-bar.d.ts +15 -0
- package/dist/i18n/namespaces/search-filter-bar.js +32 -0
- package/dist/i18n/namespaces/search-filter-bar.js.map +1 -0
- package/dist/i18n/namespaces/sheet.d.ts +9 -0
- package/dist/i18n/namespaces/sheet.js +7 -0
- package/dist/i18n/namespaces/sheet.js.map +1 -0
- package/dist/i18n/namespaces/shell.d.ts +21 -0
- package/dist/i18n/namespaces/shell.js +75 -0
- package/dist/i18n/namespaces/shell.js.map +1 -0
- package/dist/i18n/namespaces/spinner.d.ts +9 -0
- package/dist/i18n/namespaces/spinner.js +7 -0
- package/dist/i18n/namespaces/spinner.js.map +1 -0
- package/dist/i18n/namespaces/stepper.d.ts +7 -0
- package/dist/i18n/namespaces/stepper.js +15 -0
- package/dist/i18n/namespaces/stepper.js.map +1 -0
- package/dist/i18n/namespaces/table-pagination.d.ts +7 -0
- package/dist/i18n/namespaces/table-pagination.js +29 -0
- package/dist/i18n/namespaces/table-pagination.js.map +1 -0
- package/dist/i18n/namespaces/tags-input.d.ts +12 -0
- package/dist/i18n/namespaces/tags-input.js +11 -0
- package/dist/i18n/namespaces/tags-input.js.map +1 -0
- package/dist/i18n/namespaces/time-picker.d.ts +15 -0
- package/dist/i18n/namespaces/time-picker.js +25 -0
- package/dist/i18n/namespaces/time-picker.js.map +1 -0
- package/dist/i18n/namespaces/toast.d.ts +16 -0
- package/dist/i18n/namespaces/toast.js +7 -0
- package/dist/i18n/namespaces/toast.js.map +1 -0
- package/dist/i18n/namespaces/tour.d.ts +7 -0
- package/dist/i18n/namespaces/tour.js +21 -0
- package/dist/i18n/namespaces/tour.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/toast-language.d.ts +8 -0
- package/dist/lib/toast-language.js +19 -0
- package/dist/lib/toast-language.js.map +1 -0
- package/dist/shell/app-header.js.map +1 -1
- package/dist/shell/app-shell.d.ts +20 -2
- package/dist/shell/app-shell.js +4 -1
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/contrast-constants.d.ts +9 -1
- package/dist/shell/contrast-constants.js +2 -4
- package/dist/shell/contrast-constants.js.map +1 -1
- package/dist/shell/contrast-toggle.d.ts +4 -1
- package/dist/shell/contrast-toggle.js +2 -2
- package/dist/shell/contrast-toggle.js.map +1 -1
- package/dist/shell/display-axis-constants.d.ts +2 -1
- package/dist/shell/display-axis-constants.js.map +1 -1
- package/dist/shell/display-axis-controls.d.ts +15 -6
- package/dist/shell/display-axis-controls.js +11 -9
- package/dist/shell/display-axis-controls.js.map +1 -1
- package/dist/shell/display-axis-provider.d.ts +1 -1
- package/dist/shell/mobile-menu.js.map +1 -1
- package/dist/shell/notifications-button.d.ts +1 -1
- package/dist/shell/notifications-button.js +3 -2
- package/dist/shell/notifications-button.js.map +1 -1
- package/dist/shell/shell-context.d.ts +4 -1
- package/dist/shell/shell-context.js +2 -4
- package/dist/shell/shell-context.js.map +1 -1
- package/dist/shell/sidebar.d.ts +1 -1
- package/dist/shell/sidebar.js.map +1 -1
- package/dist/shell/theme-toggle.d.ts +6 -3
- package/dist/shell/theme-toggle.js +2 -2
- package/dist/shell/theme-toggle.js.map +1 -1
- package/dist/shell/types.d.ts +14 -8
- package/dist/shell/types.js +2 -35
- package/dist/shell/types.js.map +1 -1
- package/dist/shell/user-menu.d.ts +6 -6
- package/dist/shell/user-menu.js.map +1 -1
- package/dist/shell/utils.d.ts +1 -1
- package/dist/shell/utils.js.map +1 -1
- package/dist/shell/workspace-switcher.js +1 -1
- package/dist/shell/workspace-switcher.js.map +1 -1
- package/dist/uploader/file-upload-constants.d.ts +13 -0
- package/dist/uploader/file-upload-constants.js +4 -16
- package/dist/uploader/file-upload-constants.js.map +1 -1
- package/dist/uploader/file-upload.d.ts +4 -1
- package/dist/uploader/file-upload.js +2 -2
- package/dist/uploader/file-upload.js.map +1 -1
- package/dist/uploader/image-upload-field.d.ts +5 -1
- package/dist/uploader/image-upload-field.js +2 -2
- package/dist/uploader/image-upload-field.js.map +1 -1
- package/dist/uploader/use-file-upload.d.ts +4 -0
- package/dist/uploader/use-file-upload.js +6 -2
- package/dist/uploader/use-file-upload.js.map +1 -1
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/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 { 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, DEFAULT_CROPPER_LABELS } 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á, mặc định `DEFAULT_CROPPER_LABELS` (tiếng Việt).\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 = React.useMemo<CropperLabels>(() => ({ ...DEFAULT_CROPPER_LABELS, ...labelsProp }), [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":[]}
|
|
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":[]}
|
|
@@ -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
|
}
|
|
@@ -31,6 +31,8 @@ interface KanbanAnnouncementInfo {
|
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
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`.
|
|
34
36
|
*
|
|
35
37
|
* Bốn hàm đầu đi vào `accessibility.announcements` của dnd-kit (vùng `aria-live`). App vẫn có thể
|
|
36
38
|
* truyền thẳng `accessibility.announcements` như thượng nguồn — khi đó nó thắng hoàn toàn.
|
|
@@ -55,7 +57,11 @@ interface KanbanLabels {
|
|
|
55
57
|
/** Tên mặc định của `KanbanItemHandle` — tay cầm thường chỉ có một icon. */
|
|
56
58
|
itemHandleLabel: string;
|
|
57
59
|
}
|
|
58
|
-
/**
|
|
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
|
+
*/
|
|
59
65
|
declare const DEFAULT_KANBAN_LABELS: KanbanLabels;
|
|
60
66
|
|
|
61
67
|
export { DEFAULT_KANBAN_LABELS, type KanbanAnnouncementInfo, type KanbanLabels };
|
|
@@ -1,20 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
}
|
|
4
|
-
function capitalizedNoun(variant) {
|
|
5
|
-
return variant === "column" ? "C\u1ED9t" : "Th\u1EBB";
|
|
6
|
-
}
|
|
7
|
-
const DEFAULT_KANBAN_LABELS = {
|
|
8
|
-
pickedUp: ({ variant, position, total }) => `\u0110\xE3 nh\u1EA5c ${noun(variant)} \u1EDF v\u1ECB tr\xED ${position} tr\xEAn ${total}.`,
|
|
9
|
-
moved: ({ variant, position, total, column }) => column === void 0 ? `${capitalizedNoun(variant)} \u0111ang \u1EDF v\u1ECB tr\xED ${position} tr\xEAn ${total}.` : `${capitalizedNoun(variant)} \u0111ang \u1EDF v\u1ECB tr\xED ${position} tr\xEAn ${total} trong c\u1ED9t ${column}.`,
|
|
10
|
-
dropped: ({ variant, position, total, column }) => column === void 0 ? `\u0110\xE3 th\u1EA3 ${noun(variant)} \u1EDF v\u1ECB tr\xED ${position} tr\xEAn ${total}.` : `\u0110\xE3 th\u1EA3 ${noun(variant)} \u1EDF v\u1ECB tr\xED ${position} tr\xEAn ${total} trong c\u1ED9t ${column}.`,
|
|
11
|
-
cancelled: ({ variant }) => `\u0110\xE3 hu\u1EF7 k\xE9o. ${capitalizedNoun(variant)} tr\u1EDF v\u1EC1 v\u1ECB tr\xED ban \u0111\u1EA7u.`,
|
|
12
|
-
instructions: "\u0110\u1EC3 nh\u1EA5c m\u1ED9t th\u1EBB ho\u1EB7c m\u1ED9t c\u1ED9t, nh\u1EA5n Space ho\u1EB7c Enter. Trong l\xFAc k\xE9o, d\xF9ng c\xE1c ph\xEDm m\u0169i t\xEAn \u0111\u1EC3 di chuy\u1EC3n. Nh\u1EA5n Space ho\u1EB7c Enter l\u1EA7n n\u1EEFa \u0111\u1EC3 th\u1EA3 v\xE0o v\u1ECB tr\xED m\u1EDBi, ho\u1EB7c nh\u1EA5n Escape \u0111\u1EC3 hu\u1EF7.",
|
|
13
|
-
columnRoleDescription: "c\u1ED9t k\xE9o th\u1EA3 \u0111\u01B0\u1EE3c",
|
|
14
|
-
itemRoleDescription: "th\u1EBB k\xE9o th\u1EA3 \u0111\u01B0\u1EE3c",
|
|
15
|
-
columnHandleLabel: "K\xE9o \u0111\u1EC3 di chuy\u1EC3n c\u1ED9t",
|
|
16
|
-
itemHandleLabel: "K\xE9o \u0111\u1EC3 di chuy\u1EC3n th\u1EBB"
|
|
17
|
-
};
|
|
1
|
+
import { kanbanEn } from "../../i18n/namespaces/kanban.js";
|
|
2
|
+
const DEFAULT_KANBAN_LABELS = { ...kanbanEn };
|
|
18
3
|
export {
|
|
19
4
|
DEFAULT_KANBAN_LABELS
|
|
20
5
|
};
|