@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
|
@@ -6,7 +6,7 @@ import * as React from 'react';
|
|
|
6
6
|
/** Toggle (Radix) — nút bật/tắt hai trạng thái, dùng cho thanh công cụ. */
|
|
7
7
|
declare const toggleVariants: (props?: ({
|
|
8
8
|
variant?: "default" | "outline" | null | undefined;
|
|
9
|
-
size?: "
|
|
9
|
+
size?: "default" | "sm" | "lg" | null | undefined;
|
|
10
10
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
11
11
|
type ToggleVariantProps = VariantProps<typeof toggleVariants>;
|
|
12
12
|
type ToggleProps = React.ComponentProps<typeof TogglePrimitive.Root> & ToggleVariantProps;
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* `src/chart/chart-colors.ts`. Barrel xuất module này TRỰC TIẾP; `tour.tsx` chỉ `import`.
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
* Chuỗi hiển thị và chuỗi a11y của `Tour
|
|
9
|
+
* Chuỗi hiển thị và chuỗi a11y của `Tour`. Theo `ComitorLocaleProvider` (mặc định tiếng Anh, bảng
|
|
10
|
+
* tiếng Việt dựng sẵn); truyền `labels` cho `Tour` để ghi đè từng khoá.
|
|
10
11
|
*
|
|
11
12
|
* ⚠ Thượng nguồn (Dice UI) chôn cứng chuỗi tiếng Anh trong từng nút ("Previous", "Next",
|
|
12
13
|
* "Finish", "Skip", "Close tour") và bộ đếm `1 / 5`. Gói dùng chung cho MỌI app Comitor nên
|
|
@@ -33,7 +34,11 @@ interface TourLabels {
|
|
|
33
34
|
*/
|
|
34
35
|
stepCounter: (current: number, total: number) => string;
|
|
35
36
|
}
|
|
36
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* 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:
|
|
39
|
+
* `comitorMessagesVi.tour`. Component không đọc thẳng hằng này — nó đọc bảng của
|
|
40
|
+
* `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.
|
|
41
|
+
*/
|
|
37
42
|
declare const DEFAULT_TOUR_LABELS: TourLabels;
|
|
38
43
|
|
|
39
44
|
export { DEFAULT_TOUR_LABELS, type TourLabels };
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
prev: "Quay l\u1EA1i",
|
|
4
|
-
next: "Ti\u1EBFp theo",
|
|
5
|
-
finish: "Ho\xE0n t\u1EA5t",
|
|
6
|
-
skip: "B\u1ECF qua",
|
|
7
|
-
stepCounter: (current, total) => `B\u01B0\u1EDBc ${current}/${total}`
|
|
8
|
-
};
|
|
1
|
+
import { tourEn } from "../../i18n/namespaces/tour.js";
|
|
2
|
+
const DEFAULT_TOUR_LABELS = { ...tourEn };
|
|
9
3
|
export {
|
|
10
4
|
DEFAULT_TOUR_LABELS
|
|
11
5
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/tour-constants.ts"],"sourcesContent":["/**\n * Nhãn mặc định của `Tour`.\n *\n * Module **CỐ Ý không có `\"use client\"`**: mọi export của một file `\"use client\"` tới Server\n * Component là client-reference proxy, đọc ra `undefined` mà KHÔNG throw — xem\n * `src/chart/chart-colors.ts`. Barrel xuất module này TRỰC TIẾP; `tour.tsx` chỉ `import`.\n */\n\n/**\n * Chuỗi hiển thị và chuỗi a11y của `Tour`
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/tour-constants.ts"],"sourcesContent":["/**\n * Nhãn mặc định của `Tour`.\n *\n * Module **CỐ Ý không có `\"use client\"`**: mọi export của một file `\"use client\"` tới Server\n * Component là client-reference proxy, đọc ra `undefined` mà KHÔNG throw — xem\n * `src/chart/chart-colors.ts`. Barrel xuất module này TRỰC TIẾP; `tour.tsx` chỉ `import`.\n */\n\nimport { tourEn } from \"../../i18n/namespaces/tour.js\";\n\n/**\n * Chuỗi hiển thị và chuỗi a11y của `Tour`. Theo `ComitorLocaleProvider` (mặc định tiếng Anh, bảng\n * tiếng Việt dựng sẵn); truyền `labels` cho `Tour` để ghi đè từng khoá.\n *\n * ⚠ Thượng nguồn (Dice UI) chôn cứng chuỗi tiếng Anh trong từng nút (\"Previous\", \"Next\",\n * \"Finish\", \"Skip\", \"Close tour\") và bộ đếm `1 / 5`. Gói dùng chung cho MỌI app Comitor nên\n * chuỗi cứng là ngõ cụt cho app đa ngôn ngữ.\n *\n * Từng chỗ vẫn ghi đè riêng được: `children` của `TourPrev`/`TourNext`/`TourSkip`/`TourClose`,\n * `aria-label` của `TourClose`, `format` của `TourStepCounter`. `labels` là để đổi NGÔN NGỮ một\n * lần cho cả tour.\n */\nexport interface TourLabels {\n /** `aria-label` của `TourClose` — nút chỉ có icon X, nên đây là tên DUY NHẤT của nó. */\n close: string;\n /** Chữ mặc định của `TourPrev` khi không có `children`. */\n prev: string;\n /** Chữ mặc định của `TourNext` ở mọi bước trừ bước cuối. */\n next: string;\n /** Chữ mặc định của `TourNext` ở bước CUỐI — bấm là kết thúc tour. */\n finish: string;\n /** Chữ mặc định của `TourSkip` khi không có `children`. */\n skip: string;\n /**\n * Nội dung mặc định của `TourStepCounter`. `current` đếm từ 1.\n * Là HÀM vì có số chèn vào giữa câu.\n */\n stepCounter: (current: number, total: number) => string;\n}\n\n/**\n * Nhãn TIẾNG ANH mặc định (từ `3.0.0`; trước đó là tiếng Việt). Bảng tiếng Việt:\n * `comitorMessagesVi.tour`. Component không đọc thẳng hằng này — nó đọc bảng của\n * `ComitorLocaleProvider`; hằng ở đây để app trải ra khi tự dựng nhãn.\n */\nexport const DEFAULT_TOUR_LABELS: TourLabels = { ...tourEn };\n"],"mappings":"AAQA,SAAS,cAAc;AAqChB,MAAM,sBAAkC,EAAE,GAAG,OAAO;","names":[]}
|
|
@@ -54,7 +54,8 @@ interface TourProps extends DivProps {
|
|
|
54
54
|
modal?: boolean;
|
|
55
55
|
stepFooter?: React.ReactElement;
|
|
56
56
|
/**
|
|
57
|
-
* Ghi đè chuỗi hiển thị/a11y
|
|
57
|
+
* Ghi đè chuỗi hiển thị/a11y — xem `TourLabels`. Không truyền thì theo namespace `tour` của
|
|
58
|
+
* `ComitorLocaleProvider` (mặc định tiếng Anh).
|
|
58
59
|
* ⚠ Thêm so với thượng nguồn: bản gốc chôn cứng chuỗi tiếng Anh trong từng nút.
|
|
59
60
|
*/
|
|
60
61
|
labels?: Partial<TourLabels>;
|
|
@@ -114,7 +115,7 @@ declare function TourNext(props: TourNextProps): React.JSX.Element;
|
|
|
114
115
|
type TourSkipProps = React.ComponentProps<typeof Button>;
|
|
115
116
|
declare function TourSkip(props: TourSkipProps): React.JSX.Element;
|
|
116
117
|
interface TourStepCounterProps extends DivProps {
|
|
117
|
-
/** Mặc định `labels.stepCounter` của `Tour` —
|
|
118
|
+
/** Mặc định `labels.stepCounter` của `Tour` — "Step 2 of 5". */
|
|
118
119
|
format?: (current: number, total: number) => string;
|
|
119
120
|
}
|
|
120
121
|
declare function TourStepCounter(props: TourStepCounterProps): React.JSX.Element;
|
|
@@ -15,13 +15,13 @@ import { Slot } from "@radix-ui/react-slot";
|
|
|
15
15
|
import { ChevronLeft, ChevronRight, X } from "lucide-react";
|
|
16
16
|
import * as React from "react";
|
|
17
17
|
import * as ReactDOM from "react-dom";
|
|
18
|
+
import { useComitorLabels } from "../../i18n/locale-provider.js";
|
|
18
19
|
import { cn } from "../../lib/cn.js";
|
|
19
20
|
import { useComposedRefs } from "../../lib/compose-refs.js";
|
|
20
21
|
import { useAsRef } from "../../lib/use-as-ref.js";
|
|
21
22
|
import { useIsomorphicLayoutEffect } from "../../lib/use-isomorphic-layout-effect.js";
|
|
22
23
|
import { useLazyRef } from "../../lib/use-lazy-ref.js";
|
|
23
24
|
import { Button } from "./button.js";
|
|
24
|
-
import { DEFAULT_TOUR_LABELS } from "./tour-constants.js";
|
|
25
25
|
const ROOT_NAME = "Tour";
|
|
26
26
|
const PORTAL_NAME = "TourPortal";
|
|
27
27
|
const STEP_NAME = "TourStep";
|
|
@@ -386,6 +386,7 @@ function Tour(props) {
|
|
|
386
386
|
},
|
|
387
387
|
setState: (key, value) => {
|
|
388
388
|
if (Object.is(stateRef.current[key], value)) return;
|
|
389
|
+
const previousValue = stateRef.current.value;
|
|
389
390
|
stateRef.current[key] = value;
|
|
390
391
|
if (key === "open" && typeof value === "boolean") {
|
|
391
392
|
propsRef.current.onOpenChange?.(value);
|
|
@@ -396,12 +397,12 @@ function Tour(props) {
|
|
|
396
397
|
}
|
|
397
398
|
}
|
|
398
399
|
} else {
|
|
399
|
-
if (stateRef.current.value < (stateRef.current.steps.length || 0)
|
|
400
|
+
if (stateRef.current.value < (stateRef.current.steps.length || 0)) {
|
|
400
401
|
propsRef.current.onSkip?.();
|
|
401
402
|
}
|
|
402
403
|
}
|
|
403
404
|
} else if (key === "value" && typeof value === "number") {
|
|
404
|
-
const prevStep = stateRef.current.steps[
|
|
405
|
+
const prevStep = stateRef.current.steps[previousValue];
|
|
405
406
|
const nextStep = stateRef.current.steps[value];
|
|
406
407
|
prevStep?.onStepLeave?.();
|
|
407
408
|
nextStep?.onStepEnter?.();
|
|
@@ -514,7 +515,7 @@ function Tour(props) {
|
|
|
514
515
|
store.setState("value", valueProp);
|
|
515
516
|
}
|
|
516
517
|
}, [valueProp, store]);
|
|
517
|
-
const labels =
|
|
518
|
+
const labels = useComitorLabels("tour", labelsProp);
|
|
518
519
|
const contextValue = React.useMemo(
|
|
519
520
|
() => ({
|
|
520
521
|
dir,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/tour.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/tour) — 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 {\n autoUpdate,\n flip,\n hide,\n limitShift,\n type Middleware,\n offset,\n arrow as onArrow,\n type Placement,\n shift,\n useFloating\n} from \"@floating-ui/react-dom\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { ChevronLeft, ChevronRight, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\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 { Button } from \"./button.js\";\nimport { DEFAULT_TOUR_LABELS, type TourLabels } from \"./tour-constants.js\";\n\nconst ROOT_NAME = \"Tour\";\nconst PORTAL_NAME = \"TourPortal\";\nconst STEP_NAME = \"TourStep\";\nconst ARROW_NAME = \"TourArrow\";\nconst HEADER_NAME = \"TourHeader\";\nconst TITLE_NAME = \"TourTitle\";\nconst DESCRIPTION_NAME = \"TourDescription\";\nconst CLOSE_NAME = \"TourClose\";\nconst PREV_NAME = \"TourPrev\";\nconst NEXT_NAME = \"TourNext\";\nconst SKIP_NAME = \"TourSkip\";\nconst STEP_COUNTER_NAME = \"TourStepCounter\";\nconst FOOTER_NAME = \"TourFooter\";\n\nconst POINTER_DOWN_OUTSIDE = \"tour.pointerDownOutside\";\nconst INTERACT_OUTSIDE = \"tour.interactOutside\";\nconst OPEN_AUTO_FOCUS = \"tour.openAutoFocus\";\nconst CLOSE_AUTO_FOCUS = \"tour.closeAutoFocus\";\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\nconst SIDE_OPTIONS = [\"top\", \"right\", \"bottom\", \"left\"] as const;\nconst ALIGN_OPTIONS = [\"start\", \"center\", \"end\"] as const;\n\nconst DEFAULT_ALIGN_OFFSET = 0;\nconst DEFAULT_SIDE_OFFSET = 16;\nconst DEFAULT_SPOTLIGHT_PADDING = 4;\n\ntype Side = (typeof SIDE_OPTIONS)[number];\ntype Align = (typeof ALIGN_OPTIONS)[number];\ntype Direction = \"ltr\" | \"rtl\";\n\ninterface ScrollOffset {\n top?: number;\n bottom?: number;\n left?: number;\n right?: number;\n}\n\ntype Boundary = Element | null;\n\n/*\n * ⚠ Khác thượng nguồn: mặc định `collisionBoundary` là MỘT mảng rỗng dùng chung thay vì `[]`\n * viết tại chỗ — mảng mới mỗi lần render sẽ làm dữ liệu bước \"đổi\" liên tục (xem `ownStepData`).\n */\nconst EMPTY_BOUNDARY: Boundary[] = [];\n\n/*\n * ⚠ Khác thượng nguồn: `RefObject<HTMLElement | null>` thay vì `RefObject<HTMLElement>`. Với\n * React 19, `useRef<HTMLDivElement>(null)` có kiểu `RefObject<HTMLDivElement | null>` — kiểu gốc\n * từ chối đúng cái ref mà app nào cũng tạo ra.\n */\ntype StepTarget = string | React.RefObject<HTMLElement | null> | HTMLElement;\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype StepElement = React.ComponentRef<typeof TourStep>;\ntype CloseElement = React.ComponentRef<typeof TourClose>;\ntype PrevElement = React.ComponentRef<typeof TourPrev>;\ntype NextElement = React.ComponentRef<typeof TourNext>;\ntype SkipElement = React.ComponentRef<typeof TourSkip>;\ntype FooterElement = React.ComponentRef<typeof TourFooter>;\n\nconst OPPOSITE_SIDE: Record<Side, Side> = {\n top: \"bottom\",\n right: \"left\",\n bottom: \"top\",\n left: \"right\"\n};\n\n/**\n * @see https://github.com/radix-ui/primitives/blob/main/packages/react/focus-guards/src/focus-guards.tsx\n */\nlet focusGuardCount = 0;\n\nfunction createFocusGuard() {\n const element = document.createElement(\"span\");\n element.setAttribute(\"data-tour-focus-guard\", \"\");\n element.tabIndex = 0;\n element.style.outline = \"none\";\n element.style.opacity = \"0\";\n element.style.position = \"fixed\";\n element.style.pointerEvents = \"none\";\n return element;\n}\n\n/*\n * ⚠ Khác thượng nguồn: nhận `enabled`. Bản gốc gọi hook này vô điều kiện ở MỌI `TourStep`, nên\n * chỉ cần `Tour` nằm sẵn trong layout (đúng cách dùng onboarding: gắn sẵn, đóng mặc định) là trang\n * có thêm HAI điểm dừng Tab VÔ HÌNH ở đầu và cuối `<body>` suốt vòng đời app — người dùng bàn phím\n * Tab vào một thứ không nhìn thấy. Radix chỉ cắm lính gác khi lớp modal đang MỞ.\n */\nfunction useFocusGuards(enabled: boolean) {\n React.useEffect(() => {\n if (!enabled) return;\n\n const edgeGuards = document.querySelectorAll(\"[data-tour-focus-guard]\");\n document.body.insertAdjacentElement(\"afterbegin\", edgeGuards[0] ?? createFocusGuard());\n document.body.insertAdjacentElement(\"beforeend\", edgeGuards[1] ?? createFocusGuard());\n focusGuardCount++;\n\n return () => {\n if (focusGuardCount === 1) {\n const guards = document.querySelectorAll(\"[data-tour-focus-guard]\");\n for (const node of guards) {\n node.remove();\n }\n }\n focusGuardCount--;\n };\n }, [enabled]);\n}\n\nfunction useFocusTrap(\n containerRef: React.RefObject<HTMLElement | null>,\n enabled: boolean,\n tourOpen: boolean,\n onOpenAutoFocus?: (event: OpenAutoFocusEvent) => void,\n onCloseAutoFocus?: (event: CloseAutoFocusEvent) => void\n) {\n const lastFocusedElementRef = React.useRef<HTMLElement | null>(null);\n const onOpenAutoFocusRef = useAsRef(onOpenAutoFocus);\n const onCloseAutoFocusRef = useAsRef(onCloseAutoFocus);\n const tourOpenRef = useAsRef(tourOpen);\n\n React.useEffect(() => {\n if (!enabled) return;\n\n const container = containerRef.current;\n if (!container) return;\n\n const previouslyFocusedElement = document.activeElement as HTMLElement | null;\n\n function getTabbableCandidates() {\n if (!container) return [];\n\n const nodes: HTMLElement[] = [];\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: Element) => {\n const element = node as HTMLElement;\n const isHiddenInput = element.tagName === \"INPUT\" && (element as HTMLInputElement).type === \"hidden\";\n /*\n * ⚠ Khác thượng nguồn: bỏ qua phần tử `disabled`. `TourPrev` bị khoá ở bước đầu mà\n * `tabIndex` vẫn là 0; nếu nó là ứng viên đầu tiên thì `focus()` thất bại IM LẶNG,\n * focus ở lại ngoài bước, và phím Tab kế tiếp bị tính là \"tương tác bên ngoài\" — tour\n * ĐÓNG trong tay người dùng bàn phím.\n */\n const isDisabled = (element as HTMLButtonElement).disabled === true;\n if (element.hidden || isHiddenInput || isDisabled) return NodeFilter.FILTER_SKIP;\n return element.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n }\n });\n while (walker.nextNode()) {\n nodes.push(walker.currentNode as HTMLElement);\n }\n return nodes;\n }\n\n function getTabbableEdges() {\n const candidates = getTabbableCandidates();\n const first = candidates[0];\n const last = candidates[candidates.length - 1];\n return [first, last] as const;\n }\n\n function onFocusIn(event: FocusEvent) {\n if (!container) return;\n\n const target = event.target as HTMLElement | null;\n if (container.contains(target)) {\n lastFocusedElementRef.current = target;\n } else {\n const elementToFocus = lastFocusedElementRef.current ?? getTabbableCandidates()[0];\n elementToFocus?.focus({ preventScroll: true });\n }\n }\n\n function onKeyDown(event: KeyboardEvent) {\n if (event.key !== \"Tab\" || event.altKey || event.ctrlKey || event.metaKey) return;\n\n const [first, last] = getTabbableEdges();\n const hasTabbableElements = first && last;\n\n if (!hasTabbableElements) {\n if (document.activeElement === container) event.preventDefault();\n return;\n }\n\n if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first?.focus({ preventScroll: true });\n } else if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last?.focus({ preventScroll: true });\n }\n }\n\n const openAutoFocusEvent = new CustomEvent(OPEN_AUTO_FOCUS, EVENT_OPTIONS);\n if (onOpenAutoFocusRef.current) {\n container.addEventListener(OPEN_AUTO_FOCUS, onOpenAutoFocusRef.current as EventListener, { once: true });\n }\n container.dispatchEvent(openAutoFocusEvent);\n\n if (!openAutoFocusEvent.defaultPrevented) {\n const tabbableCandidates = getTabbableCandidates();\n if (tabbableCandidates.length > 0) {\n tabbableCandidates[0]?.focus({ preventScroll: true });\n } else {\n container.focus({ preventScroll: true });\n }\n }\n\n document.addEventListener(\"focusin\", onFocusIn);\n container.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n document.removeEventListener(\"focusin\", onFocusIn);\n container.removeEventListener(\"keydown\", onKeyDown);\n\n if (!tourOpenRef.current) {\n setTimeout(() => {\n const closeAutoFocusEvent = new CustomEvent(CLOSE_AUTO_FOCUS, EVENT_OPTIONS);\n if (onCloseAutoFocusRef.current) {\n container.addEventListener(CLOSE_AUTO_FOCUS, onCloseAutoFocusRef.current as EventListener, {\n once: true\n });\n }\n container.dispatchEvent(closeAutoFocusEvent);\n\n if (!closeAutoFocusEvent.defaultPrevented) {\n if (previouslyFocusedElement && document.body.contains(previouslyFocusedElement)) {\n previouslyFocusedElement.focus({ preventScroll: true });\n }\n }\n\n if (onCloseAutoFocusRef.current) {\n container.removeEventListener(CLOSE_AUTO_FOCUS, onCloseAutoFocusRef.current as EventListener);\n }\n }, 0);\n }\n };\n }, [containerRef, enabled, onOpenAutoFocusRef, onCloseAutoFocusRef, tourOpenRef]);\n}\n\nfunction getDataState(open: boolean) {\n return open ? \"open\" : \"closed\";\n}\n\ninterface StepData {\n target: StepTarget;\n align?: Align;\n alignOffset?: number;\n side?: Side;\n sideOffset?: number;\n collisionBoundary?: Boundary | Boundary[];\n collisionPadding?: number | Partial<Record<Side, number>>;\n arrowPadding?: number;\n sticky?: \"partial\" | \"always\";\n hideWhenDetached?: boolean;\n avoidCollisions?: boolean;\n onStepEnter?: () => void;\n onStepLeave?: () => void;\n required?: boolean;\n}\n\ninterface StoreState {\n open: boolean;\n value: number;\n steps: StepData[];\n maskPath: string;\n spotlightRect: { x: number; y: number; width: number; height: number } | null;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState;\n setState: <K extends keyof StoreState>(key: K, value: StoreState[K], opts?: unknown) => void;\n notify: () => void;\n addStep: (stepData: StepData) => { id: string; index: number };\n removeStep: (id: string) => void;\n /** ⚠ Thêm so với thượng nguồn — xem chú thích ở `TourStep`. */\n updateStep: (id: string, stepData: StepData) => void;\n /** ⚠ Thêm so với thượng nguồn — vị trí HIỆN TẠI của một bước, `-1` nếu chưa đăng ký. */\n getStepIndex: (id: string) => number;\n}\n\nfunction useStore<T>(selector: (state: StoreState) => T, ogStore?: Store | null): T {\n const contextStore = React.useContext(StoreContext);\n\n const store = ogStore ?? contextStore;\n\n if (!store) {\n throw new Error(`\\`useStore\\` phải được gọi bên trong \\`${ROOT_NAME}\\``);\n }\n\n const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\nfunction getTargetElement(target: StepTarget): HTMLElement | null {\n if (typeof target === \"string\") {\n return document.querySelector(target);\n }\n if (target && \"current\" in target) {\n return target.current;\n }\n if (target instanceof HTMLElement) {\n return target;\n }\n return null;\n}\n\nfunction getDefaultScrollBehavior(): ScrollBehavior {\n if (typeof window === \"undefined\") return \"smooth\";\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ? \"auto\" : \"smooth\";\n}\n\n/*\n * ⚠ Khác thượng nguồn: tìm vùng cuộn gần nhất. Bản gốc chỉ `window.scrollTo`, nhưng khung\n * `AppShell` của Comitor là `h-dvh overflow-hidden` và nội dung cuộn trong `<main\n * overflow-y-auto>` — cửa sổ KHÔNG BAO GIỜ cuộn, nên `autoScroll` im lặng bỏ qua mọi đích nằm\n * dưới mép màn hình.\n */\nfunction getScrollParent(element: HTMLElement): HTMLElement | null {\n let parent = element.parentElement;\n while (parent && parent !== document.body && parent !== document.documentElement) {\n const { overflowY } = window.getComputedStyle(parent);\n const isScrollable = overflowY === \"auto\" || overflowY === \"scroll\" || overflowY === \"overlay\";\n if (isScrollable && parent.scrollHeight > parent.clientHeight) return parent;\n parent = parent.parentElement;\n }\n return null;\n}\n\nfunction onScrollToElement(\n element: HTMLElement,\n scrollBehavior: ScrollBehavior = getDefaultScrollBehavior(),\n scrollOffset?: ScrollOffset\n) {\n const offset: Required<ScrollOffset> = {\n top: 100,\n bottom: 100,\n left: 0,\n right: 0,\n ...scrollOffset\n };\n const rect = element.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n const viewportWidth = window.innerWidth;\n\n const isInViewport =\n rect.top >= offset.top &&\n rect.bottom <= viewportHeight - offset.bottom &&\n rect.left >= offset.left &&\n rect.right <= viewportWidth - offset.right;\n\n if (!isInViewport) {\n const scrollParent = getScrollParent(element);\n if (scrollParent) {\n // Cuộn vùng chứa đúng bấy nhiêu để đích dừng cách mép trên khung nhìn `offset.top` — cùng\n // điểm đến với nhánh `window` bên dưới.\n scrollParent.scrollBy({ top: rect.top - offset.top, behavior: scrollBehavior });\n return;\n }\n\n const elementTop = rect.top + window.scrollY;\n const scrollTop = elementTop - offset.top;\n\n window.scrollTo({\n top: Math.max(0, scrollTop),\n behavior: scrollBehavior\n });\n }\n}\n\nfunction getSideAndAlignFromPlacement(placement: Placement): [Side, Align] {\n const [side, align = \"center\"] = placement.split(\"-\") as [Side, Align?];\n return [side, align];\n}\n\nfunction getPlacement(side: Side, align: Align): Placement {\n if (align === \"center\") {\n return side as Placement;\n }\n return `${side}-${align}` as Placement;\n}\n\nfunction updateMask(store: Store, targetElement: HTMLElement, padding: number = DEFAULT_SPOTLIGHT_PADDING) {\n const clientRect = targetElement.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n const x = Math.max(0, clientRect.left - padding);\n const y = Math.max(0, clientRect.top - padding);\n const width = Math.min(viewportWidth - x, clientRect.width + padding * 2);\n const height = Math.min(viewportHeight - y, clientRect.height + padding * 2);\n\n const path = `polygon(0% 0%, 0% 100%, ${x}px 100%, ${x}px ${y}px, ${x + width}px ${y}px, ${x + width}px ${y + height}px, ${x}px ${y + height}px, ${x}px 100%, 100% 100%, 100% 0%)`;\n store.setState(\"maskPath\", path);\n store.setState(\"spotlightRect\", { x, y, width, height });\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface TourContextValue {\n dir: Direction;\n alignOffset: number;\n sideOffset: number;\n spotlightPadding: number;\n dismissible: boolean;\n modal: boolean;\n stepFooter?: React.ReactElement;\n labels: TourLabels;\n onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;\n onInteractOutside?: (event: InteractOutsideEvent) => void;\n onOpenAutoFocus?: (event: OpenAutoFocusEvent) => void;\n onCloseAutoFocus?: (event: CloseAutoFocusEvent) => void;\n}\n\nconst TourContext = React.createContext<TourContextValue | null>(null);\n\nfunction useTourContext(consumerName: string) {\n const context = React.useContext(TourContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface StepContextValue {\n arrowX?: number;\n arrowY?: number;\n placedAlign: Align;\n placedSide: Side;\n shouldHideArrow: boolean;\n /** ⚠ Thêm so với thượng nguồn — id cho `aria-labelledby`/`aria-describedby` của bước. */\n titleId: string;\n descriptionId: string;\n onArrowChange: (arrow: HTMLSpanElement | null) => void;\n onFooterChange: (footer: FooterElement | null) => void;\n}\n\nconst StepContext = React.createContext<StepContextValue | null>(null);\n\nfunction useStepContext(consumerName: string) {\n const context = React.useContext(StepContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${STEP_NAME}\\``);\n }\n return context;\n}\n\nconst DefaultFooterContext = React.createContext(false);\n\ninterface PortalContextValue {\n portal: HTMLElement | null;\n onPortalChange: (node: HTMLElement | null) => void;\n}\n\nconst PortalContext = React.createContext<PortalContextValue | null>(null);\n\nfunction usePortalContext(consumerName: string) {\n const context = React.useContext(PortalContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\nfunction useScrollLock(enabled: boolean) {\n React.useEffect(() => {\n if (!enabled) return;\n\n const originalStyle = window.getComputedStyle(document.body).overflow;\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;\n\n document.body.style.overflow = \"hidden\";\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${scrollbarWidth}px`;\n }\n\n return () => {\n document.body.style.overflow = originalStyle;\n document.body.style.paddingRight = \"\";\n };\n }, [enabled]);\n}\n\ntype PointerDownOutsideEvent = CustomEvent<{ originalEvent: PointerEvent }>;\ntype InteractOutsideEvent = CustomEvent<{\n originalEvent: PointerEvent | FocusEvent;\n}>;\ntype OpenAutoFocusEvent = CustomEvent<Record<string, never>>;\ntype CloseAutoFocusEvent = CustomEvent<Record<string, never>>;\n\ninterface TourProps extends DivProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n value?: number;\n defaultValue?: number;\n onValueChange?: (step: number) => void;\n onComplete?: () => void;\n onSkip?: () => void;\n onEscapeKeyDown?: (event: KeyboardEvent) => void;\n onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;\n onInteractOutside?: (event: InteractOutsideEvent) => void;\n onOpenAutoFocus?: (event: OpenAutoFocusEvent) => void;\n onCloseAutoFocus?: (event: CloseAutoFocusEvent) => void;\n dir?: Direction;\n alignOffset?: number;\n sideOffset?: number;\n spotlightPadding?: number;\n autoScroll?: boolean;\n /** Mặc định `\"smooth\"`, hoặc `\"auto\"` khi người dùng bật `prefers-reduced-motion`. */\n scrollBehavior?: ScrollBehavior;\n scrollOffset?: ScrollOffset;\n dismissible?: boolean;\n modal?: boolean;\n stepFooter?: React.ReactElement;\n /**\n * Ghi đè chuỗi hiển thị/a11y (mặc định tiếng Việt) — xem `TourLabels`.\n * ⚠ Thêm so với thượng nguồn: bản gốc chôn cứng chuỗi tiếng Anh trong từng nút.\n */\n labels?: Partial<TourLabels>;\n}\n\nfunction Tour(props: TourProps) {\n const {\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n value: valueProp,\n defaultValue = 0,\n onValueChange,\n onComplete,\n onSkip,\n autoScroll = true,\n /*\n * ⚠ Khác thượng nguồn: KHÔNG có giá trị mặc định ở đây. Bản gốc viết\n * `scrollBehavior = getDefaultScrollBehavior()`, tức đọc `window.matchMedia` NGAY TRONG\n * render. Để `undefined` thì tham số mặc định của `onScrollToElement` tính đúng giá trị đó\n * vào lúc CUỘN — ngoài render, và theo cài đặt reduced-motion ở thời điểm ấy.\n */\n scrollBehavior,\n scrollOffset,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n onOpenAutoFocus,\n onCloseAutoFocus,\n dir: dirProp,\n alignOffset = DEFAULT_ALIGN_OFFSET,\n sideOffset = DEFAULT_SIDE_OFFSET,\n spotlightPadding = DEFAULT_SPOTLIGHT_PADDING,\n dismissible = true,\n modal = true,\n stepFooter,\n labels: labelsProp,\n asChild,\n ...rootProps\n } = props;\n\n const dir = useDirection(dirProp);\n\n const [portal, setPortal] = React.useState<HTMLElement | null>(null);\n const prevOpenRef = React.useRef<boolean | undefined>(undefined);\n const previouslyFocusedElementRef = React.useRef<HTMLElement | null>(null);\n\n const stateRef = useLazyRef<StoreState>(() => ({\n open: openProp ?? defaultOpen,\n value: valueProp ?? defaultValue,\n steps: [],\n maskPath: \"\",\n spotlightRect: null\n }));\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const stepIdsMapRef = useLazyRef<Map<string, number>>(() => new Map());\n const stepIdCounterRef = useLazyRef(() => ({ current: 0 }));\n const propsRef = useAsRef({\n valueProp,\n onOpenChange,\n onValueChange,\n onComplete,\n onSkip,\n onEscapeKeyDown,\n onCloseAutoFocus,\n autoScroll,\n scrollBehavior,\n scrollOffset\n });\n\n const store: Store = React.useMemo(\n () => ({\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => {\n return stateRef.current;\n },\n setState: (key, value) => {\n if (Object.is(stateRef.current[key], value)) return;\n stateRef.current[key] = value;\n\n if (key === \"open\" && typeof value === \"boolean\") {\n propsRef.current.onOpenChange?.(value);\n\n if (value) {\n if (stateRef.current.steps.length > 0) {\n if (stateRef.current.value >= stateRef.current.steps.length) {\n store.setState(\"value\", 0);\n }\n }\n } else {\n if (stateRef.current.value < (stateRef.current.steps.length || 0) - 1) {\n propsRef.current.onSkip?.();\n }\n }\n } else if (key === \"value\" && typeof value === \"number\") {\n const prevStep = stateRef.current.steps[stateRef.current.value];\n const nextStep = stateRef.current.steps[value];\n\n prevStep?.onStepLeave?.();\n nextStep?.onStepEnter?.();\n\n if (value >= stateRef.current.steps.length) {\n propsRef.current.onComplete?.();\n\n if (propsRef.current.valueProp !== undefined) {\n propsRef.current.onValueChange?.(value);\n }\n\n store.setState(\"open\", false);\n return;\n }\n\n if (propsRef.current.valueProp !== undefined) {\n propsRef.current.onValueChange?.(value);\n return;\n }\n\n propsRef.current.onValueChange?.(value);\n\n if (nextStep && propsRef.current.autoScroll) {\n const targetElement = getTargetElement(nextStep.target);\n if (targetElement) {\n onScrollToElement(targetElement, propsRef.current.scrollBehavior, propsRef.current.scrollOffset);\n }\n }\n }\n\n store.notify();\n },\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n },\n addStep: (stepData) => {\n const id = `step-${stepIdCounterRef.current.current++}`;\n const index = stateRef.current.steps.length;\n stepIdsMapRef.current.set(id, index);\n stateRef.current.steps = [...stateRef.current.steps, stepData];\n store.notify();\n return { id, index };\n },\n removeStep: (id) => {\n const index = stepIdsMapRef.current.get(id);\n if (index === undefined) return;\n\n stateRef.current.steps = stateRef.current.steps.filter((_, i) => i !== index);\n\n stepIdsMapRef.current.delete(id);\n\n for (const [stepId, stepIndex] of stepIdsMapRef.current.entries()) {\n if (stepIndex > index) {\n stepIdsMapRef.current.set(stepId, stepIndex - 1);\n }\n }\n\n store.notify();\n },\n updateStep: (id, stepData) => {\n const index = stepIdsMapRef.current.get(id);\n if (index === undefined) return;\n if (stateRef.current.steps[index] === stepData) return;\n\n const steps = [...stateRef.current.steps];\n steps[index] = stepData;\n stateRef.current.steps = steps;\n store.notify();\n },\n getStepIndex: (id) => stepIdsMapRef.current.get(id) ?? -1\n }),\n [stateRef, listenersRef, stepIdsMapRef, stepIdCounterRef, propsRef]\n );\n\n const open = useStore((state) => state.open, store);\n\n /*\n * ⚠ Khác thượng nguồn: Escape mà tour đã DÙNG thì phải đánh dấu là đã dùng, và phải nghe ở pha\n * CAPTURE. Bản gốc nghe ở pha nổi bọt và không gọi `preventDefault()`, nên đo được: MỘT lần\n * Escape vừa đóng tour vừa đóng luôn `ActionBar` đang mở — tức xoá mất lựa chọn dòng của người\n * dùng. Chỉ `preventDefault()` thôi là chưa đủ: listener của `ActionBar` cũng nằm ở `document`,\n * đăng ký TRƯỚC (thanh mở trước tour), nên nó chạy trước và chưa thấy cờ. Pha capture của\n * `document` chạy trước mọi listener nổi bọt — đúng chỗ Radix `DismissableLayer` nghe Escape,\n * nên tour cư xử như một Dialog của gói. Đang gõ dở bằng bộ gõ (Telex/VNI, `isComposing`) hoặc\n * Escape đã có lớp khác dùng thì bỏ qua, cùng luật với `ActionBar`.\n */\n React.useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if (!open || event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n\n if (propsRef.current.onEscapeKeyDown) {\n propsRef.current.onEscapeKeyDown(event);\n if (event.defaultPrevented) return;\n }\n event.preventDefault();\n store.setState(\"open\", false);\n }\n\n document.addEventListener(\"keydown\", onKeyDown, { capture: true });\n return () => document.removeEventListener(\"keydown\", onKeyDown, { capture: true });\n }, [store, open, propsRef]);\n\n useIsomorphicLayoutEffect(() => {\n const wasOpen = prevOpenRef.current;\n\n if (open && !wasOpen) {\n previouslyFocusedElementRef.current = document.activeElement as HTMLElement | null;\n } else if (!open && wasOpen) {\n setTimeout(() => {\n const container = portal ?? document.body;\n const closeAutoFocusEvent = new CustomEvent(CLOSE_AUTO_FOCUS, EVENT_OPTIONS);\n\n if (propsRef.current.onCloseAutoFocus) {\n container.addEventListener(CLOSE_AUTO_FOCUS, propsRef.current.onCloseAutoFocus as EventListener, {\n once: true\n });\n }\n container.dispatchEvent(closeAutoFocusEvent);\n\n if (!closeAutoFocusEvent.defaultPrevented) {\n const elementToFocus = previouslyFocusedElementRef.current;\n if (elementToFocus && document.body.contains(elementToFocus)) {\n elementToFocus.focus({ preventScroll: true });\n }\n }\n\n previouslyFocusedElementRef.current = null;\n }, 0);\n }\n\n prevOpenRef.current = open;\n }, [open, portal, propsRef]);\n\n useIsomorphicLayoutEffect(() => {\n if (openProp !== undefined) {\n store.setState(\"open\", openProp);\n }\n }, [openProp, store]);\n\n useIsomorphicLayoutEffect(() => {\n if (valueProp !== undefined) {\n store.setState(\"value\", valueProp);\n }\n }, [valueProp, store]);\n\n const labels = React.useMemo<TourLabels>(() => ({ ...DEFAULT_TOUR_LABELS, ...labelsProp }), [labelsProp]);\n\n const contextValue = React.useMemo<TourContextValue>(\n () => ({\n dir,\n alignOffset,\n sideOffset,\n spotlightPadding,\n dismissible,\n modal,\n stepFooter,\n labels,\n onPointerDownOutside,\n onInteractOutside,\n onOpenAutoFocus,\n onCloseAutoFocus\n }),\n [\n dir,\n alignOffset,\n sideOffset,\n spotlightPadding,\n dismissible,\n modal,\n stepFooter,\n labels,\n onPointerDownOutside,\n onInteractOutside,\n onOpenAutoFocus,\n onCloseAutoFocus\n ]\n );\n\n const portalContextValue = React.useMemo<PortalContextValue>(\n () => ({\n portal,\n onPortalChange: setPortal\n }),\n [portal]\n );\n\n useScrollLock(open && modal);\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StoreContext.Provider value={store}>\n <TourContext.Provider value={contextValue}>\n <PortalContext.Provider value={portalContextValue}>\n <RootPrimitive data-slot=\"tour\" dir={dir} {...rootProps} />\n </PortalContext.Provider>\n </TourContext.Provider>\n </StoreContext.Provider>\n );\n}\n\ninterface TourStepProps extends DivProps {\n target: StepTarget;\n side?: Side;\n sideOffset?: number;\n align?: Align;\n alignOffset?: number;\n collisionBoundary?: Boundary | Boundary[];\n collisionPadding?: number | Partial<Record<Side, number>>;\n arrowPadding?: number;\n sticky?: \"partial\" | \"always\";\n hideWhenDetached?: boolean;\n avoidCollisions?: boolean;\n required?: boolean;\n forceMount?: boolean;\n onStepEnter?: () => void;\n onStepLeave?: () => void;\n}\n\nfunction TourStep(props: TourStepProps) {\n const {\n target,\n side = \"bottom\",\n sideOffset,\n align = \"center\",\n alignOffset,\n collisionBoundary = EMPTY_BOUNDARY,\n collisionPadding = 0,\n arrowPadding = 0,\n sticky = \"partial\",\n hideWhenDetached = false,\n avoidCollisions = true,\n required = false,\n forceMount = false,\n onStepEnter,\n onStepLeave,\n onPointerDownCapture: onPointerDownCaptureProp,\n onFocusCapture: onFocusCaptureProp,\n onBlurCapture: onBlurCaptureProp,\n children,\n className,\n style,\n asChild,\n ...stepProps\n } = props;\n\n const store = useStoreContext(STEP_NAME);\n\n const [arrow, setArrow] = React.useState<HTMLSpanElement | null>(null);\n const [footer, setFooter] = React.useState<FooterElement | null>(null);\n const [targetElement, setTargetElement] = React.useState<HTMLElement | null>(null);\n\n const stepRef = React.useRef<StepElement | null>(null);\n const stepIdRef = React.useRef<string>(\"\");\n const isPointerInsideReactTreeRef = React.useRef(false);\n const isFocusInsideReactTreeRef = React.useRef(false);\n\n const titleId = React.useId();\n const descriptionId = React.useId();\n\n const open = useStore((state) => state.open);\n const value = useStore((state) => state.value);\n const steps = useStore((state) => state.steps);\n const context = useTourContext(STEP_NAME);\n\n const resolvedSideOffset = sideOffset ?? context.sideOffset;\n const resolvedAlignOffset = alignOffset ?? context.alignOffset;\n\n /*\n * ⚠ Khác thượng nguồn: đăng ký bước MỘT lần khi mount, sau đó cập nhật TẠI CHỖ.\n *\n * Bản gốc gỡ rồi thêm lại bước mỗi khi một prop đổi (kể cả `onStepEnter={() => …}` viết tại\n * chỗ — tức MỖI lần render). Thêm lại là nối vào CUỐI danh sách, còn thứ tự các bước khác giữ\n * trong ref riêng không ai cập nhật: bước bị đẩy xuống cuối, và ở `value` cũ không bước nào\n * nhận mình là bước hiện tại — tour trống trơn. Chỉ cần `side={isMobile ? … : …}` là dính.\n *\n * Hai callback đi qua ref để luôn gọi bản mới nhất mà không làm dữ liệu bước \"đổi\".\n */\n const callbacksRef = useAsRef({ onStepEnter, onStepLeave });\n\n const ownStepData = React.useMemo<StepData>(\n () => ({\n target,\n align,\n alignOffset: resolvedAlignOffset,\n side,\n sideOffset: resolvedSideOffset,\n collisionBoundary,\n collisionPadding,\n arrowPadding,\n sticky,\n hideWhenDetached,\n avoidCollisions,\n onStepEnter: () => callbacksRef.current.onStepEnter?.(),\n onStepLeave: () => callbacksRef.current.onStepLeave?.(),\n required\n }),\n [\n target,\n align,\n resolvedAlignOffset,\n side,\n resolvedSideOffset,\n collisionBoundary,\n collisionPadding,\n arrowPadding,\n sticky,\n hideWhenDetached,\n avoidCollisions,\n required,\n callbacksRef\n ]\n );\n const ownStepDataRef = useAsRef(ownStepData);\n\n useIsomorphicLayoutEffect(() => {\n const { id } = store.addStep(ownStepDataRef.current);\n stepIdRef.current = id;\n\n return () => {\n store.removeStep(id);\n };\n }, [store, ownStepDataRef]);\n\n useIsomorphicLayoutEffect(() => {\n store.updateStep(stepIdRef.current, ownStepData);\n }, [store, ownStepData]);\n\n const stepData = steps[value];\n\n // ⚠ Khác thượng nguồn: vị trí đọc từ store theo id (được dồn lại khi một bước khác bị gỡ),\n // không từ một ref chụp lúc đăng ký.\n const isCurrentStep = store.getStepIndex(stepIdRef.current) === value;\n\n /*\n * ⚠ Khác thượng nguồn: bản gốc gọi `getTargetElement` — tức `document.querySelector` — NGAY\n * TRONG render. Ở đây phân giải trong layout effect: chạy sau MỖI lần render như nhịp của bản\n * gốc (đích xuất hiện muộn vẫn bắt được), vẫn trước lượt sơn, và chỉ cho bước đang hiện.\n * `setState` cùng giá trị thì React bỏ qua, nên không có vòng lặp.\n */\n useIsomorphicLayoutEffect(() => {\n setTargetElement(open && isCurrentStep && stepData ? getTargetElement(stepData.target) : null);\n });\n\n /*\n * ⚠ Khác thượng nguồn: gom điều kiện \"bước này đang HIỆN\" về một chỗ. Bản gốc bật bẫy focus\n * và bộ nghe bấm-ra-ngoài theo `open && isCurrentStep`; nếu lượt render đó chưa có đích (và\n * không `forceMount`) thì bước chưa có DOM, hiệu ứng thoát sớm và KHÔNG chạy lại khi đích xuất\n * hiện — focus không bao giờ vào được bước.\n */\n const isStepRendered = open && isCurrentStep && !!stepData && (!!targetElement || forceMount);\n\n const middleware = React.useMemo(() => {\n if (!stepData) return [];\n\n const mainAxisOffset = stepData.sideOffset ?? resolvedSideOffset;\n const crossAxisOffset = stepData.alignOffset ?? resolvedAlignOffset;\n\n const padding =\n typeof stepData.collisionPadding === \"number\"\n ? stepData.collisionPadding\n : {\n top: stepData.collisionPadding?.top ?? 0,\n right: stepData.collisionPadding?.right ?? 0,\n bottom: stepData.collisionPadding?.bottom ?? 0,\n left: stepData.collisionPadding?.left ?? 0\n };\n\n const boundary = Array.isArray(stepData.collisionBoundary)\n ? stepData.collisionBoundary\n : stepData.collisionBoundary\n ? [stepData.collisionBoundary]\n : [];\n const hasExplicitBoundaries = boundary.length > 0;\n\n const detectOverflowOptions = {\n padding,\n boundary: boundary.filter((b): b is Element => b !== null),\n altBoundary: hasExplicitBoundaries\n };\n\n return [\n offset({\n mainAxis: mainAxisOffset,\n alignmentAxis: crossAxisOffset\n }),\n stepData.avoidCollisions &&\n shift({\n mainAxis: true,\n crossAxis: false,\n limiter: stepData.sticky === \"partial\" ? limitShift() : undefined,\n ...detectOverflowOptions\n }),\n stepData.avoidCollisions && flip({ ...detectOverflowOptions }),\n arrow && onArrow({ element: arrow, padding: stepData.arrowPadding }),\n stepData.hideWhenDetached &&\n hide({\n strategy: \"referenceHidden\",\n ...detectOverflowOptions\n })\n ].filter(Boolean) as Middleware[];\n }, [stepData, resolvedSideOffset, resolvedAlignOffset, arrow]);\n\n const placement = getPlacement(stepData?.side ?? side, stepData?.align ?? align);\n\n const {\n refs,\n floatingStyles,\n placement: finalPlacement,\n middlewareData\n } = useFloating({\n placement,\n middleware,\n strategy: \"fixed\",\n whileElementsMounted: autoUpdate,\n elements: {\n reference: targetElement\n }\n });\n\n const composedRef = useComposedRefs(refs.setFloating, stepRef);\n\n const [placedSide, placedAlign] = getSideAndAlignFromPlacement(finalPlacement);\n\n const arrowX = middlewareData.arrow?.x;\n const arrowY = middlewareData.arrow?.y;\n const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;\n const isHidden = hideWhenDetached && middlewareData.hide?.referenceHidden;\n\n const stepContextValue = React.useMemo<StepContextValue>(\n () => ({\n arrowX,\n arrowY,\n placedAlign,\n placedSide,\n shouldHideArrow: cannotCenterArrow,\n titleId,\n descriptionId,\n onArrowChange: setArrow,\n onFooterChange: setFooter\n }),\n [arrowX, arrowY, placedSide, placedAlign, cannotCenterArrow, titleId, descriptionId]\n );\n\n React.useEffect(() => {\n if (open && targetElement && isCurrentStep) {\n updateMask(store, targetElement, context.spotlightPadding);\n\n let rafId: number | null = null;\n\n function onResize() {\n if (targetElement) {\n updateMask(store, targetElement, context.spotlightPadding);\n }\n }\n\n function onScroll() {\n if (rafId !== null) return;\n rafId = requestAnimationFrame(() => {\n if (targetElement) {\n updateMask(store, targetElement, context.spotlightPadding);\n }\n rafId = null;\n });\n }\n\n window.addEventListener(\"resize\", onResize);\n /*\n * ⚠ Khác thượng nguồn: nghe ở pha CAPTURE. Sự kiện `scroll` của một phần tử không nổi bọt\n * lên `window`; bản gốc chỉ bắt được khi CỬA SỔ cuộn. Trong `AppShell` chỉ `<main>` cuộn,\n * nên lỗ sáng đứng yên trong khi đích trôi đi.\n */\n window.addEventListener(\"scroll\", onScroll, { capture: true, passive: true });\n return () => {\n window.removeEventListener(\"resize\", onResize);\n window.removeEventListener(\"scroll\", onScroll, { capture: true });\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n }\n };\n }\n }, [open, targetElement, isCurrentStep, store, context.spotlightPadding]);\n\n React.useEffect(() => {\n if (!isStepRendered) return;\n\n const stepElement = stepRef.current;\n if (!stepElement) return;\n\n const ownerDocument = stepElement.ownerDocument;\n\n function onPointerDown(event: PointerEvent) {\n if (event.target && !isPointerInsideReactTreeRef.current) {\n const pointerDownOutsideEvent = new CustomEvent(POINTER_DOWN_OUTSIDE, {\n ...EVENT_OPTIONS,\n detail: { originalEvent: event }\n });\n\n context.onPointerDownOutside?.(pointerDownOutsideEvent);\n\n const interactOutsideEvent = new CustomEvent(INTERACT_OUTSIDE, {\n ...EVENT_OPTIONS,\n detail: { originalEvent: event }\n });\n context.onInteractOutside?.(interactOutsideEvent);\n\n if (\n !pointerDownOutsideEvent.defaultPrevented &&\n !interactOutsideEvent.defaultPrevented &&\n context.dismissible\n ) {\n store.setState(\"open\", false);\n }\n }\n\n isPointerInsideReactTreeRef.current = false;\n }\n\n const timerId = window.setTimeout(() => {\n ownerDocument.addEventListener(\"pointerdown\", onPointerDown);\n }, 0);\n\n return () => {\n window.clearTimeout(timerId);\n ownerDocument.removeEventListener(\"pointerdown\", onPointerDown);\n };\n }, [isStepRendered, store, context]);\n\n React.useEffect(() => {\n if (!isStepRendered) return;\n\n const stepElement = stepRef.current;\n if (!stepElement) return;\n\n const ownerDocument = stepElement.ownerDocument;\n\n function onFocusIn(event: FocusEvent) {\n const target = event.target as HTMLElement;\n\n const isFocusInStep = stepElement?.contains(target);\n const isFocusInTarget = targetElement?.contains(target);\n\n if (event.target && !isFocusInsideReactTreeRef.current && !isFocusInStep && !isFocusInTarget) {\n const interactOutsideEvent = new CustomEvent(INTERACT_OUTSIDE, {\n ...EVENT_OPTIONS,\n detail: { originalEvent: event }\n });\n\n context.onInteractOutside?.(interactOutsideEvent);\n\n if (!interactOutsideEvent.defaultPrevented && context.dismissible) {\n store.setState(\"open\", false);\n }\n }\n }\n\n ownerDocument.addEventListener(\"focusin\", onFocusIn);\n\n return () => {\n ownerDocument.removeEventListener(\"focusin\", onFocusIn);\n };\n }, [isStepRendered, store, context, targetElement]);\n\n const onPointerDownCapture = React.useCallback(\n (event: React.PointerEvent<StepElement>) => {\n onPointerDownCaptureProp?.(event);\n isPointerInsideReactTreeRef.current = true;\n },\n [onPointerDownCaptureProp]\n );\n\n const onFocusCapture = React.useCallback(\n (event: React.FocusEvent<StepElement>) => {\n onFocusCaptureProp?.(event);\n isFocusInsideReactTreeRef.current = true;\n },\n [onFocusCaptureProp]\n );\n\n const onBlurCapture = React.useCallback(\n (event: React.FocusEvent<StepElement>) => {\n onBlurCaptureProp?.(event);\n isFocusInsideReactTreeRef.current = false;\n },\n [onBlurCaptureProp]\n );\n\n React.useEffect(() => {\n if (!open || !isCurrentStep || !targetElement) return;\n\n function onTargetPointerDownCapture() {\n isPointerInsideReactTreeRef.current = true;\n }\n\n function onTargetFocusCapture() {\n isFocusInsideReactTreeRef.current = true;\n }\n\n function onTargetBlurCapture() {\n isFocusInsideReactTreeRef.current = false;\n }\n\n targetElement.addEventListener(\"pointerdown\", onTargetPointerDownCapture, true);\n targetElement.addEventListener(\"focus\", onTargetFocusCapture, true);\n targetElement.addEventListener(\"blur\", onTargetBlurCapture, true);\n\n return () => {\n targetElement.removeEventListener(\"pointerdown\", onTargetPointerDownCapture, true);\n targetElement.removeEventListener(\"focus\", onTargetFocusCapture, true);\n targetElement.removeEventListener(\"blur\", onTargetBlurCapture, true);\n };\n }, [open, isCurrentStep, targetElement]);\n\n useFocusGuards(isStepRendered);\n useFocusTrap(stepRef, isStepRendered, open, context.onOpenAutoFocus, context.onCloseAutoFocus);\n\n if (!isStepRendered) {\n return null;\n }\n\n const StepPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StepContext.Provider value={stepContextValue}>\n <StepPrimitive\n ref={composedRef}\n /*\n * ⚠ Khác thượng nguồn: bản gốc là `<div>` TRẦN, không vai trò, không tên. Bẫy focus kéo\n * người dùng vào đó và trình đọc màn hình chỉ đọc \"nút Đóng hướng dẫn\" — không nói\n * đang ở đâu, bước này nói gì. `role=\"dialog\"` + tiêu đề/mô tả làm tên và lời mô tả, như\n * `PopoverContent` của Radix. Không `aria-modal`: đích đang được soi nằm NGOÀI bước và\n * người dùng vẫn cần đọc được nó.\n */\n role=\"dialog\"\n aria-labelledby={titleId}\n aria-describedby={descriptionId}\n data-slot=\"tour-step\"\n data-side={placedSide}\n data-align={placedAlign}\n dir={context.dir}\n tabIndex={-1}\n {...stepProps}\n onPointerDownCapture={onPointerDownCapture}\n onFocusCapture={onFocusCapture}\n onBlurCapture={onBlurCapture}\n className={cn(\n // Bề mặt cùng khuôn với `PopoverContent`: nền/chữ popover, viền token, bóng tầng nổi.\n \"fixed z-50 flex w-80 flex-col gap-4 rounded-lg border bg-popover p-4 text-popover-foreground shadow-overlay outline-none\",\n className\n )}\n style={{\n ...style,\n ...floatingStyles,\n visibility: isHidden ? \"hidden\" : undefined,\n pointerEvents: isHidden ? \"none\" : undefined\n }}\n >\n {children}\n {!footer && <DefaultFooterContext.Provider value={true}>{context.stepFooter}</DefaultFooterContext.Provider>}\n </StepPrimitive>\n </StepContext.Provider>\n );\n}\n\ninterface TourSpotlightProps extends DivProps {\n forceMount?: boolean;\n}\n\nfunction TourSpotlight(props: TourSpotlightProps) {\n const { asChild, className, style, forceMount = false, ...backdropProps } = props;\n\n const open = useStore((state) => state.open);\n const maskPath = useStore((state) => state.maskPath);\n\n if (!open && !forceMount) return null;\n\n const SpotlightPrimitive = asChild ? Slot : \"div\";\n\n return (\n <SpotlightPrimitive\n data-slot=\"tour-spotlight\"\n data-state={getDataState(open)}\n {...backdropProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: `bg-black/80` (bảng màu mặc định Tailwind) → đúng màu lớp phủ của\n * `DialogOverlay`, để lớp tối của tour và của hộp thoại là MỘT thứ trong mắt người dùng.\n * Không lấy `backdrop-blur-sm` của nó: tour cần người dùng còn đọc được ngữ cảnh quanh đích.\n */\n \"fixed inset-0 z-50 bg-ink/60 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 dark:bg-ink/80\",\n className\n )}\n style={{\n clipPath: maskPath,\n ...style\n }}\n />\n );\n}\n\ninterface TourSpotlightRingProps extends DivProps {\n forceMount?: boolean;\n}\n\nfunction TourSpotlightRing(props: TourSpotlightRingProps) {\n const { asChild, className, style, forceMount = false, ...ringProps } = props;\n\n const open = useStore((state) => state.open);\n const spotlightRect = useStore((state) => state.spotlightRect);\n\n if (!open && !forceMount) return null;\n if (!spotlightRect) return null;\n\n const RingPrimitive = asChild ? Slot : \"div\";\n\n return (\n <RingPrimitive\n data-slot=\"tour-spotlight-ring\"\n data-state={getDataState(open)}\n {...ringProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: bỏ `border-ring`. Không có độ dày viền đi kèm nên nó không vẽ gì —\n * chỉ là một màu viền treo sẵn. App muốn viền thì truyền đủ cả độ dày lẫn màu\n * (`border-2 border-primary-ink`), như chính các ví dụ của thượng nguồn vẫn làm.\n */\n \"pointer-events-none fixed z-50 ring-[3px] ring-ring/50\",\n className\n )}\n style={{\n left: spotlightRect.x,\n top: spotlightRect.y,\n width: spotlightRect.width,\n height: spotlightRect.height,\n ...style\n }}\n />\n );\n}\n\ninterface TourPortalProps {\n children?: React.ReactNode;\n container?: HTMLElement | null;\n}\n\nfunction TourPortal(props: TourPortalProps) {\n const { children, container } = props;\n\n const portalContext = usePortalContext(PORTAL_NAME);\n\n /*\n * ⚠ Khác thượng nguồn: giữ NÚT gắn trong state thay cho cờ `mounted`, để lượt render không\n * phải đọc `document.body` — mọi chỗ chạm DOM đều nằm trong effect.\n */\n const [mountNode, setMountNode] = React.useState<HTMLElement | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const node = container ?? document.body;\n setMountNode(node);\n\n portalContext?.onPortalChange(node);\n return () => {\n portalContext?.onPortalChange(null);\n };\n }, [container, portalContext]);\n\n if (!mountNode) return null;\n\n const portalContainer = container ?? portalContext?.portal ?? mountNode;\n\n return ReactDOM.createPortal(children, portalContainer);\n}\n\ninterface TourArrowProps extends React.ComponentProps<\"svg\"> {\n width?: number;\n height?: number;\n asChild?: boolean;\n}\n\nfunction TourArrow(props: TourArrowProps) {\n const { width = 10, height = 5, className, children, asChild, ...arrowProps } = props;\n\n const stepContext = useStepContext(ARROW_NAME);\n const baseSide = OPPOSITE_SIDE[stepContext.placedSide];\n\n return (\n <span\n ref={stepContext.onArrowChange}\n data-slot=\"tour-arrow\"\n style={{\n position: \"absolute\",\n left: stepContext.arrowX != null ? `${stepContext.arrowX}px` : undefined,\n top: stepContext.arrowY != null ? `${stepContext.arrowY}px` : undefined,\n [baseSide]: 0,\n transformOrigin: {\n top: \"\",\n right: \"0 0\",\n bottom: \"center 0\",\n left: \"100% 0\"\n }[stepContext.placedSide],\n transform: {\n top: \"translateY(100%)\",\n right: \"translateY(50%) rotate(90deg) translateX(-50%)\",\n bottom: \"rotate(180deg)\",\n left: \"translateY(50%) rotate(-90deg) translateX(50%)\"\n }[stepContext.placedSide],\n visibility: stepContext.shouldHideArrow ? \"hidden\" : undefined\n }}\n >\n {/* ⚠ Khác thượng nguồn: `aria-hidden` — mũi tên thuần trang trí, không có gì để đọc. */}\n <svg\n viewBox=\"0 0 30 10\"\n preserveAspectRatio=\"none\"\n width={width}\n height={height}\n aria-hidden=\"true\"\n {...arrowProps}\n className={cn(\"block fill-popover stroke-border\", className)}\n >\n {asChild ? children : <polygon points=\"0,0 30,0 15,10\" />}\n </svg>\n </span>\n );\n}\n\ntype TourHeaderProps = DivProps;\n\nfunction TourHeader(props: TourHeaderProps) {\n const { asChild, className, ...headerProps } = props;\n\n const context = useTourContext(HEADER_NAME);\n\n const HeaderPrimitive = asChild ? Slot : \"div\";\n\n return (\n <HeaderPrimitive\n data-slot=\"tour-header\"\n dir={context.dir}\n {...headerProps}\n className={cn(\"flex flex-col gap-1.5 text-center sm:text-left\", className)}\n />\n );\n}\n\ntype TourTitleProps = DivProps;\n\nfunction TourTitle(props: TourTitleProps) {\n const { asChild, className, ...titleProps } = props;\n\n const context = useTourContext(TITLE_NAME);\n // ⚠ Khác thượng nguồn: nhận id từ bước chứa nó để bước trỏ `aria-labelledby` vào đây.\n const stepContext = React.useContext(StepContext);\n\n const TitlePrimitive = asChild ? Slot : \"div\";\n\n return (\n <TitlePrimitive\n id={stepContext?.titleId}\n data-slot=\"tour-title\"\n dir={context.dir}\n {...titleProps}\n className={cn(\"text-lg leading-none font-semibold tracking-tight\", className)}\n />\n );\n}\n\ntype TourDescriptionProps = DivProps;\n\nfunction TourDescription(props: TourDescriptionProps) {\n const { asChild, className, ...descriptionProps } = props;\n\n const context = useTourContext(DESCRIPTION_NAME);\n // ⚠ Khác thượng nguồn: nhận id từ bước chứa nó để bước trỏ `aria-describedby` vào đây.\n const stepContext = React.useContext(StepContext);\n\n const DescriptionPrimitive = asChild ? Slot : \"div\";\n\n return (\n <DescriptionPrimitive\n id={stepContext?.descriptionId}\n data-slot=\"tour-description\"\n dir={context.dir}\n {...descriptionProps}\n className={cn(\"text-sm text-muted-foreground\", className)}\n />\n );\n}\n\ninterface TourCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nfunction TourClose(props: TourCloseProps) {\n const { asChild, className, children, onClick: onClickProp, ...closeButtonProps } = props;\n\n const store = useStoreContext(CLOSE_NAME);\n const context = useTourContext(CLOSE_NAME);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<CloseElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n store.setState(\"open\", false);\n },\n [store, onClickProp]\n );\n\n const ClosePrimitive = asChild ? Slot : \"button\";\n\n return (\n <ClosePrimitive\n type=\"button\"\n aria-label={context.labels.close}\n data-slot=\"tour-close\"\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: khuôn nút đóng của `DialogContent` (màu chữ muted, hover nền muted,\n * vòng focus của gói) thay cho `opacity-70` — `opacity` không miễn ngưỡng tương phản cho\n * thứ còn bấm được (docs/hop-dong-mau.md). `top-3 right-3 p-1` đặt icon đúng chỗ\n * `top-4 right-4` cũ nhưng vùng bấm lên ~24px thay vì 16px (WCAG 2.5.8).\n */\n \"absolute top-3 right-3 rounded-md p-1 text-muted-foreground transition-colors duration-150 ease-out hover:bg-muted hover:text-foreground 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 className\n )}\n onClick={onClick}\n {...closeButtonProps}\n >\n {/*\n * ⚠ Khác thượng nguồn: bản gốc luôn render icon X làm con, đè mất `children` của app — và\n * với `asChild` thì Slot nhận chính cái `<svg>` làm phần tử được \"bọc\".\n */}\n {children ?? <X aria-hidden=\"true\" />}\n </ClosePrimitive>\n );\n}\n\ntype TourPrevProps = React.ComponentProps<typeof Button>;\n\n/*\n * ⚠ Khác thượng nguồn (áp cho cả `TourPrev`, `TourNext`, `TourSkip`): bỏ `aria-label` mặc định.\n * Nút đã có chữ hiện; `aria-label` cứng ĐÈ lên chữ đó — ở bước cuối `TourNext` HIỆN \"Finish\"\n * nhưng ĐỌC \"Next step\", và app truyền `children` riêng vẫn bị đọc bằng nhãn cũ. Lệch WCAG 2.5.3\n * (Label in Name): người dùng điều khiển bằng giọng nói gọi đúng chữ trên nút mà không bấm được.\n * App dùng nút chỉ-có-icon thì tự truyền `aria-label`.\n */\nfunction TourPrev(props: TourPrevProps) {\n const { children, onClick: onClickProp, ...prevButtonProps } = props;\n\n const store = useStoreContext(PREV_NAME);\n const context = useTourContext(PREV_NAME);\n const value = useStore((state) => state.value);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<PrevElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n if (value > 0) {\n store.setState(\"value\", value - 1);\n }\n },\n [value, store, onClickProp]\n );\n\n return (\n <Button\n type=\"button\"\n data-slot=\"tour-prev\"\n variant=\"outline\"\n {...prevButtonProps}\n onClick={onClick}\n disabled={value === 0}\n >\n {children ?? (\n <>\n {/* `Button` của gói không tự đặt cỡ icon (khác shadcn) → ghi `size-4` tường minh. */}\n <ChevronLeft aria-hidden=\"true\" className=\"size-4\" />\n {context.labels.prev}\n </>\n )}\n </Button>\n );\n}\n\ntype TourNextProps = React.ComponentProps<typeof Button>;\n\nfunction TourNext(props: TourNextProps) {\n const { children, onClick: onClickProp, ...nextButtonProps } = props;\n const store = useStoreContext(NEXT_NAME);\n const context = useTourContext(NEXT_NAME);\n const value = useStore((state) => state.value);\n const steps = useStore((state) => state.steps);\n\n const isLastStep = value === steps.length - 1;\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<NextElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n store.setState(\"value\", value + 1);\n },\n [value, store, onClickProp]\n );\n\n return (\n <Button type=\"button\" data-slot=\"tour-next\" {...nextButtonProps} onClick={onClick}>\n {children ?? (\n <>\n {isLastStep ? context.labels.finish : context.labels.next}\n {!isLastStep && <ChevronRight aria-hidden=\"true\" className=\"size-4\" />}\n </>\n )}\n </Button>\n );\n}\n\ntype TourSkipProps = React.ComponentProps<typeof Button>;\n\nfunction TourSkip(props: TourSkipProps) {\n const { children, onClick: onClickProp, ...skipButtonProps } = props;\n\n const store = useStoreContext(SKIP_NAME);\n const context = useTourContext(SKIP_NAME);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<SkipElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n store.setState(\"open\", false);\n },\n [store, onClickProp]\n );\n\n return (\n <Button type=\"button\" data-slot=\"tour-skip\" variant=\"outline\" {...skipButtonProps} onClick={onClick}>\n {children ?? context.labels.skip}\n </Button>\n );\n}\n\ninterface TourStepCounterProps extends DivProps {\n /** Mặc định `labels.stepCounter` của `Tour` — `Bước 2/5`. */\n format?: (current: number, total: number) => string;\n}\n\nfunction TourStepCounter(props: TourStepCounterProps) {\n const { format: formatProp, asChild, className, children, ...stepCounterProps } = props;\n\n const context = useTourContext(STEP_COUNTER_NAME);\n const format = formatProp ?? context.labels.stepCounter;\n\n const value = useStore((state) => state.value);\n const steps = useStore((state) => state.steps);\n\n const StepCounterPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StepCounterPrimitive\n data-slot=\"tour-step-counter\"\n {...stepCounterProps}\n // ⚠ Khác thượng nguồn: `whitespace-nowrap` — \"Bước 1/3\" đặt cạnh \"Quay lại\"/\"Tiếp theo\" trong\n // thẻ `w-80` bị bẻ làm hai dòng (đo trên trình duyệt); con số đếm bước không nên vỡ dòng.\n className={cn(\"whitespace-nowrap text-sm text-muted-foreground\", className)}\n >\n {children ?? format(value + 1, steps.length)}\n </StepCounterPrimitive>\n );\n}\n\ntype TourFooterProps = DivProps;\n\nfunction TourFooter(props: TourFooterProps) {\n const { asChild, className, ref, ...footerProps } = props;\n\n const stepContext = useStepContext(FOOTER_NAME);\n const hasDefaultFooter = React.useContext(DefaultFooterContext);\n const context = useTourContext(FOOTER_NAME);\n\n const composedRef = useComposedRefs(ref, hasDefaultFooter ? undefined : stepContext.onFooterChange);\n\n const FooterPrimitive = asChild ? Slot : \"div\";\n\n return (\n <FooterPrimitive\n data-slot=\"tour-footer\"\n dir={context.dir}\n {...footerProps}\n ref={composedRef}\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n />\n );\n}\n\nexport {\n Tour,\n TourArrow,\n type TourArrowProps,\n TourClose,\n type TourCloseProps,\n TourDescription,\n type TourDescriptionProps,\n TourFooter,\n type TourFooterProps,\n TourHeader,\n type TourHeaderProps,\n TourNext,\n type TourNextProps,\n TourPortal,\n type TourPortalProps,\n TourPrev,\n type TourPrevProps,\n type TourProps,\n TourSkip,\n type TourSkipProps,\n TourSpotlight,\n type TourSpotlightProps,\n TourSpotlightRing,\n type TourSpotlightRingProps,\n TourStep,\n TourStepCounter,\n type TourStepCounterProps,\n type TourStepProps,\n TourTitle,\n type TourTitleProps\n};\n"],"mappings":"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","names":["offset","onResize","onScroll","target"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/tour.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/tour) — 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 {\n autoUpdate,\n flip,\n hide,\n limitShift,\n type Middleware,\n offset,\n arrow as onArrow,\n type Placement,\n shift,\n useFloating\n} from \"@floating-ui/react-dom\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { ChevronLeft, ChevronRight, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\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 { Button } from \"./button.js\";\nimport type { TourLabels } from \"./tour-constants.js\";\n\nconst ROOT_NAME = \"Tour\";\nconst PORTAL_NAME = \"TourPortal\";\nconst STEP_NAME = \"TourStep\";\nconst ARROW_NAME = \"TourArrow\";\nconst HEADER_NAME = \"TourHeader\";\nconst TITLE_NAME = \"TourTitle\";\nconst DESCRIPTION_NAME = \"TourDescription\";\nconst CLOSE_NAME = \"TourClose\";\nconst PREV_NAME = \"TourPrev\";\nconst NEXT_NAME = \"TourNext\";\nconst SKIP_NAME = \"TourSkip\";\nconst STEP_COUNTER_NAME = \"TourStepCounter\";\nconst FOOTER_NAME = \"TourFooter\";\n\nconst POINTER_DOWN_OUTSIDE = \"tour.pointerDownOutside\";\nconst INTERACT_OUTSIDE = \"tour.interactOutside\";\nconst OPEN_AUTO_FOCUS = \"tour.openAutoFocus\";\nconst CLOSE_AUTO_FOCUS = \"tour.closeAutoFocus\";\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\nconst SIDE_OPTIONS = [\"top\", \"right\", \"bottom\", \"left\"] as const;\nconst ALIGN_OPTIONS = [\"start\", \"center\", \"end\"] as const;\n\nconst DEFAULT_ALIGN_OFFSET = 0;\nconst DEFAULT_SIDE_OFFSET = 16;\nconst DEFAULT_SPOTLIGHT_PADDING = 4;\n\ntype Side = (typeof SIDE_OPTIONS)[number];\ntype Align = (typeof ALIGN_OPTIONS)[number];\ntype Direction = \"ltr\" | \"rtl\";\n\ninterface ScrollOffset {\n top?: number;\n bottom?: number;\n left?: number;\n right?: number;\n}\n\ntype Boundary = Element | null;\n\n/*\n * ⚠ Khác thượng nguồn: mặc định `collisionBoundary` là MỘT mảng rỗng dùng chung thay vì `[]`\n * viết tại chỗ — mảng mới mỗi lần render sẽ làm dữ liệu bước \"đổi\" liên tục (xem `ownStepData`).\n */\nconst EMPTY_BOUNDARY: Boundary[] = [];\n\n/*\n * ⚠ Khác thượng nguồn: `RefObject<HTMLElement | null>` thay vì `RefObject<HTMLElement>`. Với\n * React 19, `useRef<HTMLDivElement>(null)` có kiểu `RefObject<HTMLDivElement | null>` — kiểu gốc\n * từ chối đúng cái ref mà app nào cũng tạo ra.\n */\ntype StepTarget = string | React.RefObject<HTMLElement | null> | HTMLElement;\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype StepElement = React.ComponentRef<typeof TourStep>;\ntype CloseElement = React.ComponentRef<typeof TourClose>;\ntype PrevElement = React.ComponentRef<typeof TourPrev>;\ntype NextElement = React.ComponentRef<typeof TourNext>;\ntype SkipElement = React.ComponentRef<typeof TourSkip>;\ntype FooterElement = React.ComponentRef<typeof TourFooter>;\n\nconst OPPOSITE_SIDE: Record<Side, Side> = {\n top: \"bottom\",\n right: \"left\",\n bottom: \"top\",\n left: \"right\"\n};\n\n/**\n * @see https://github.com/radix-ui/primitives/blob/main/packages/react/focus-guards/src/focus-guards.tsx\n */\nlet focusGuardCount = 0;\n\nfunction createFocusGuard() {\n const element = document.createElement(\"span\");\n element.setAttribute(\"data-tour-focus-guard\", \"\");\n element.tabIndex = 0;\n element.style.outline = \"none\";\n element.style.opacity = \"0\";\n element.style.position = \"fixed\";\n element.style.pointerEvents = \"none\";\n return element;\n}\n\n/*\n * ⚠ Khác thượng nguồn: nhận `enabled`. Bản gốc gọi hook này vô điều kiện ở MỌI `TourStep`, nên\n * chỉ cần `Tour` nằm sẵn trong layout (đúng cách dùng onboarding: gắn sẵn, đóng mặc định) là trang\n * có thêm HAI điểm dừng Tab VÔ HÌNH ở đầu và cuối `<body>` suốt vòng đời app — người dùng bàn phím\n * Tab vào một thứ không nhìn thấy. Radix chỉ cắm lính gác khi lớp modal đang MỞ.\n */\nfunction useFocusGuards(enabled: boolean) {\n React.useEffect(() => {\n if (!enabled) return;\n\n const edgeGuards = document.querySelectorAll(\"[data-tour-focus-guard]\");\n document.body.insertAdjacentElement(\"afterbegin\", edgeGuards[0] ?? createFocusGuard());\n document.body.insertAdjacentElement(\"beforeend\", edgeGuards[1] ?? createFocusGuard());\n focusGuardCount++;\n\n return () => {\n if (focusGuardCount === 1) {\n const guards = document.querySelectorAll(\"[data-tour-focus-guard]\");\n for (const node of guards) {\n node.remove();\n }\n }\n focusGuardCount--;\n };\n }, [enabled]);\n}\n\nfunction useFocusTrap(\n containerRef: React.RefObject<HTMLElement | null>,\n enabled: boolean,\n tourOpen: boolean,\n onOpenAutoFocus?: (event: OpenAutoFocusEvent) => void,\n onCloseAutoFocus?: (event: CloseAutoFocusEvent) => void\n) {\n const lastFocusedElementRef = React.useRef<HTMLElement | null>(null);\n const onOpenAutoFocusRef = useAsRef(onOpenAutoFocus);\n const onCloseAutoFocusRef = useAsRef(onCloseAutoFocus);\n const tourOpenRef = useAsRef(tourOpen);\n\n React.useEffect(() => {\n if (!enabled) return;\n\n const container = containerRef.current;\n if (!container) return;\n\n const previouslyFocusedElement = document.activeElement as HTMLElement | null;\n\n function getTabbableCandidates() {\n if (!container) return [];\n\n const nodes: HTMLElement[] = [];\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: Element) => {\n const element = node as HTMLElement;\n const isHiddenInput = element.tagName === \"INPUT\" && (element as HTMLInputElement).type === \"hidden\";\n /*\n * ⚠ Khác thượng nguồn: bỏ qua phần tử `disabled`. `TourPrev` bị khoá ở bước đầu mà\n * `tabIndex` vẫn là 0; nếu nó là ứng viên đầu tiên thì `focus()` thất bại IM LẶNG,\n * focus ở lại ngoài bước, và phím Tab kế tiếp bị tính là \"tương tác bên ngoài\" — tour\n * ĐÓNG trong tay người dùng bàn phím.\n */\n const isDisabled = (element as HTMLButtonElement).disabled === true;\n if (element.hidden || isHiddenInput || isDisabled) return NodeFilter.FILTER_SKIP;\n return element.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n }\n });\n while (walker.nextNode()) {\n nodes.push(walker.currentNode as HTMLElement);\n }\n return nodes;\n }\n\n function getTabbableEdges() {\n const candidates = getTabbableCandidates();\n const first = candidates[0];\n const last = candidates[candidates.length - 1];\n return [first, last] as const;\n }\n\n function onFocusIn(event: FocusEvent) {\n if (!container) return;\n\n const target = event.target as HTMLElement | null;\n if (container.contains(target)) {\n lastFocusedElementRef.current = target;\n } else {\n const elementToFocus = lastFocusedElementRef.current ?? getTabbableCandidates()[0];\n elementToFocus?.focus({ preventScroll: true });\n }\n }\n\n function onKeyDown(event: KeyboardEvent) {\n if (event.key !== \"Tab\" || event.altKey || event.ctrlKey || event.metaKey) return;\n\n const [first, last] = getTabbableEdges();\n const hasTabbableElements = first && last;\n\n if (!hasTabbableElements) {\n if (document.activeElement === container) event.preventDefault();\n return;\n }\n\n if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first?.focus({ preventScroll: true });\n } else if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last?.focus({ preventScroll: true });\n }\n }\n\n const openAutoFocusEvent = new CustomEvent(OPEN_AUTO_FOCUS, EVENT_OPTIONS);\n if (onOpenAutoFocusRef.current) {\n container.addEventListener(OPEN_AUTO_FOCUS, onOpenAutoFocusRef.current as EventListener, { once: true });\n }\n container.dispatchEvent(openAutoFocusEvent);\n\n if (!openAutoFocusEvent.defaultPrevented) {\n const tabbableCandidates = getTabbableCandidates();\n if (tabbableCandidates.length > 0) {\n tabbableCandidates[0]?.focus({ preventScroll: true });\n } else {\n container.focus({ preventScroll: true });\n }\n }\n\n document.addEventListener(\"focusin\", onFocusIn);\n container.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n document.removeEventListener(\"focusin\", onFocusIn);\n container.removeEventListener(\"keydown\", onKeyDown);\n\n if (!tourOpenRef.current) {\n setTimeout(() => {\n const closeAutoFocusEvent = new CustomEvent(CLOSE_AUTO_FOCUS, EVENT_OPTIONS);\n if (onCloseAutoFocusRef.current) {\n container.addEventListener(CLOSE_AUTO_FOCUS, onCloseAutoFocusRef.current as EventListener, {\n once: true\n });\n }\n container.dispatchEvent(closeAutoFocusEvent);\n\n if (!closeAutoFocusEvent.defaultPrevented) {\n if (previouslyFocusedElement && document.body.contains(previouslyFocusedElement)) {\n previouslyFocusedElement.focus({ preventScroll: true });\n }\n }\n\n if (onCloseAutoFocusRef.current) {\n container.removeEventListener(CLOSE_AUTO_FOCUS, onCloseAutoFocusRef.current as EventListener);\n }\n }, 0);\n }\n };\n }, [containerRef, enabled, onOpenAutoFocusRef, onCloseAutoFocusRef, tourOpenRef]);\n}\n\nfunction getDataState(open: boolean) {\n return open ? \"open\" : \"closed\";\n}\n\ninterface StepData {\n target: StepTarget;\n align?: Align;\n alignOffset?: number;\n side?: Side;\n sideOffset?: number;\n collisionBoundary?: Boundary | Boundary[];\n collisionPadding?: number | Partial<Record<Side, number>>;\n arrowPadding?: number;\n sticky?: \"partial\" | \"always\";\n hideWhenDetached?: boolean;\n avoidCollisions?: boolean;\n onStepEnter?: () => void;\n onStepLeave?: () => void;\n required?: boolean;\n}\n\ninterface StoreState {\n open: boolean;\n value: number;\n steps: StepData[];\n maskPath: string;\n spotlightRect: { x: number; y: number; width: number; height: number } | null;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState;\n setState: <K extends keyof StoreState>(key: K, value: StoreState[K], opts?: unknown) => void;\n notify: () => void;\n addStep: (stepData: StepData) => { id: string; index: number };\n removeStep: (id: string) => void;\n /** ⚠ Thêm so với thượng nguồn — xem chú thích ở `TourStep`. */\n updateStep: (id: string, stepData: StepData) => void;\n /** ⚠ Thêm so với thượng nguồn — vị trí HIỆN TẠI của một bước, `-1` nếu chưa đăng ký. */\n getStepIndex: (id: string) => number;\n}\n\nfunction useStore<T>(selector: (state: StoreState) => T, ogStore?: Store | null): T {\n const contextStore = React.useContext(StoreContext);\n\n const store = ogStore ?? contextStore;\n\n if (!store) {\n throw new Error(`\\`useStore\\` phải được gọi bên trong \\`${ROOT_NAME}\\``);\n }\n\n const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\nfunction getTargetElement(target: StepTarget): HTMLElement | null {\n if (typeof target === \"string\") {\n return document.querySelector(target);\n }\n if (target && \"current\" in target) {\n return target.current;\n }\n if (target instanceof HTMLElement) {\n return target;\n }\n return null;\n}\n\nfunction getDefaultScrollBehavior(): ScrollBehavior {\n if (typeof window === \"undefined\") return \"smooth\";\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ? \"auto\" : \"smooth\";\n}\n\n/*\n * ⚠ Khác thượng nguồn: tìm vùng cuộn gần nhất. Bản gốc chỉ `window.scrollTo`, nhưng khung\n * `AppShell` của Comitor là `h-dvh overflow-hidden` và nội dung cuộn trong `<main\n * overflow-y-auto>` — cửa sổ KHÔNG BAO GIỜ cuộn, nên `autoScroll` im lặng bỏ qua mọi đích nằm\n * dưới mép màn hình.\n */\nfunction getScrollParent(element: HTMLElement): HTMLElement | null {\n let parent = element.parentElement;\n while (parent && parent !== document.body && parent !== document.documentElement) {\n const { overflowY } = window.getComputedStyle(parent);\n const isScrollable = overflowY === \"auto\" || overflowY === \"scroll\" || overflowY === \"overlay\";\n if (isScrollable && parent.scrollHeight > parent.clientHeight) return parent;\n parent = parent.parentElement;\n }\n return null;\n}\n\nfunction onScrollToElement(\n element: HTMLElement,\n scrollBehavior: ScrollBehavior = getDefaultScrollBehavior(),\n scrollOffset?: ScrollOffset\n) {\n const offset: Required<ScrollOffset> = {\n top: 100,\n bottom: 100,\n left: 0,\n right: 0,\n ...scrollOffset\n };\n const rect = element.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n const viewportWidth = window.innerWidth;\n\n const isInViewport =\n rect.top >= offset.top &&\n rect.bottom <= viewportHeight - offset.bottom &&\n rect.left >= offset.left &&\n rect.right <= viewportWidth - offset.right;\n\n if (!isInViewport) {\n const scrollParent = getScrollParent(element);\n if (scrollParent) {\n // Cuộn vùng chứa đúng bấy nhiêu để đích dừng cách mép trên khung nhìn `offset.top` — cùng\n // điểm đến với nhánh `window` bên dưới.\n scrollParent.scrollBy({ top: rect.top - offset.top, behavior: scrollBehavior });\n return;\n }\n\n const elementTop = rect.top + window.scrollY;\n const scrollTop = elementTop - offset.top;\n\n window.scrollTo({\n top: Math.max(0, scrollTop),\n behavior: scrollBehavior\n });\n }\n}\n\nfunction getSideAndAlignFromPlacement(placement: Placement): [Side, Align] {\n const [side, align = \"center\"] = placement.split(\"-\") as [Side, Align?];\n return [side, align];\n}\n\nfunction getPlacement(side: Side, align: Align): Placement {\n if (align === \"center\") {\n return side as Placement;\n }\n return `${side}-${align}` as Placement;\n}\n\nfunction updateMask(store: Store, targetElement: HTMLElement, padding: number = DEFAULT_SPOTLIGHT_PADDING) {\n const clientRect = targetElement.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n const x = Math.max(0, clientRect.left - padding);\n const y = Math.max(0, clientRect.top - padding);\n const width = Math.min(viewportWidth - x, clientRect.width + padding * 2);\n const height = Math.min(viewportHeight - y, clientRect.height + padding * 2);\n\n const path = `polygon(0% 0%, 0% 100%, ${x}px 100%, ${x}px ${y}px, ${x + width}px ${y}px, ${x + width}px ${y + height}px, ${x}px ${y + height}px, ${x}px 100%, 100% 100%, 100% 0%)`;\n store.setState(\"maskPath\", path);\n store.setState(\"spotlightRect\", { x, y, width, height });\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface TourContextValue {\n dir: Direction;\n alignOffset: number;\n sideOffset: number;\n spotlightPadding: number;\n dismissible: boolean;\n modal: boolean;\n stepFooter?: React.ReactElement;\n labels: TourLabels;\n onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;\n onInteractOutside?: (event: InteractOutsideEvent) => void;\n onOpenAutoFocus?: (event: OpenAutoFocusEvent) => void;\n onCloseAutoFocus?: (event: CloseAutoFocusEvent) => void;\n}\n\nconst TourContext = React.createContext<TourContextValue | null>(null);\n\nfunction useTourContext(consumerName: string) {\n const context = React.useContext(TourContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface StepContextValue {\n arrowX?: number;\n arrowY?: number;\n placedAlign: Align;\n placedSide: Side;\n shouldHideArrow: boolean;\n /** ⚠ Thêm so với thượng nguồn — id cho `aria-labelledby`/`aria-describedby` của bước. */\n titleId: string;\n descriptionId: string;\n onArrowChange: (arrow: HTMLSpanElement | null) => void;\n onFooterChange: (footer: FooterElement | null) => void;\n}\n\nconst StepContext = React.createContext<StepContextValue | null>(null);\n\nfunction useStepContext(consumerName: string) {\n const context = React.useContext(StepContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${STEP_NAME}\\``);\n }\n return context;\n}\n\nconst DefaultFooterContext = React.createContext(false);\n\ninterface PortalContextValue {\n portal: HTMLElement | null;\n onPortalChange: (node: HTMLElement | null) => void;\n}\n\nconst PortalContext = React.createContext<PortalContextValue | null>(null);\n\nfunction usePortalContext(consumerName: string) {\n const context = React.useContext(PortalContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\nfunction useScrollLock(enabled: boolean) {\n React.useEffect(() => {\n if (!enabled) return;\n\n const originalStyle = window.getComputedStyle(document.body).overflow;\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;\n\n document.body.style.overflow = \"hidden\";\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${scrollbarWidth}px`;\n }\n\n return () => {\n document.body.style.overflow = originalStyle;\n document.body.style.paddingRight = \"\";\n };\n }, [enabled]);\n}\n\ntype PointerDownOutsideEvent = CustomEvent<{ originalEvent: PointerEvent }>;\ntype InteractOutsideEvent = CustomEvent<{\n originalEvent: PointerEvent | FocusEvent;\n}>;\ntype OpenAutoFocusEvent = CustomEvent<Record<string, never>>;\ntype CloseAutoFocusEvent = CustomEvent<Record<string, never>>;\n\ninterface TourProps extends DivProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n value?: number;\n defaultValue?: number;\n onValueChange?: (step: number) => void;\n onComplete?: () => void;\n onSkip?: () => void;\n onEscapeKeyDown?: (event: KeyboardEvent) => void;\n onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;\n onInteractOutside?: (event: InteractOutsideEvent) => void;\n onOpenAutoFocus?: (event: OpenAutoFocusEvent) => void;\n onCloseAutoFocus?: (event: CloseAutoFocusEvent) => void;\n dir?: Direction;\n alignOffset?: number;\n sideOffset?: number;\n spotlightPadding?: number;\n autoScroll?: boolean;\n /** Mặc định `\"smooth\"`, hoặc `\"auto\"` khi người dùng bật `prefers-reduced-motion`. */\n scrollBehavior?: ScrollBehavior;\n scrollOffset?: ScrollOffset;\n dismissible?: boolean;\n modal?: boolean;\n stepFooter?: React.ReactElement;\n /**\n * Ghi đè chuỗi hiển thị/a11y — xem `TourLabels`. Không truyền thì theo namespace `tour` của\n * `ComitorLocaleProvider` (mặc định tiếng Anh).\n * ⚠ Thêm so với thượng nguồn: bản gốc chôn cứng chuỗi tiếng Anh trong từng nút.\n */\n labels?: Partial<TourLabels>;\n}\n\nfunction Tour(props: TourProps) {\n const {\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n value: valueProp,\n defaultValue = 0,\n onValueChange,\n onComplete,\n onSkip,\n autoScroll = true,\n /*\n * ⚠ Khác thượng nguồn: KHÔNG có giá trị mặc định ở đây. Bản gốc viết\n * `scrollBehavior = getDefaultScrollBehavior()`, tức đọc `window.matchMedia` NGAY TRONG\n * render. Để `undefined` thì tham số mặc định của `onScrollToElement` tính đúng giá trị đó\n * vào lúc CUỘN — ngoài render, và theo cài đặt reduced-motion ở thời điểm ấy.\n */\n scrollBehavior,\n scrollOffset,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n onOpenAutoFocus,\n onCloseAutoFocus,\n dir: dirProp,\n alignOffset = DEFAULT_ALIGN_OFFSET,\n sideOffset = DEFAULT_SIDE_OFFSET,\n spotlightPadding = DEFAULT_SPOTLIGHT_PADDING,\n dismissible = true,\n modal = true,\n stepFooter,\n labels: labelsProp,\n asChild,\n ...rootProps\n } = props;\n\n const dir = useDirection(dirProp);\n\n const [portal, setPortal] = React.useState<HTMLElement | null>(null);\n const prevOpenRef = React.useRef<boolean | undefined>(undefined);\n const previouslyFocusedElementRef = React.useRef<HTMLElement | null>(null);\n\n const stateRef = useLazyRef<StoreState>(() => ({\n open: openProp ?? defaultOpen,\n value: valueProp ?? defaultValue,\n steps: [],\n maskPath: \"\",\n spotlightRect: null\n }));\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const stepIdsMapRef = useLazyRef<Map<string, number>>(() => new Map());\n const stepIdCounterRef = useLazyRef(() => ({ current: 0 }));\n const propsRef = useAsRef({\n valueProp,\n onOpenChange,\n onValueChange,\n onComplete,\n onSkip,\n onEscapeKeyDown,\n onCloseAutoFocus,\n autoScroll,\n scrollBehavior,\n scrollOffset\n });\n\n const store: Store = React.useMemo(\n () => ({\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => {\n return stateRef.current;\n },\n setState: (key, value) => {\n if (Object.is(stateRef.current[key], value)) return;\n /*\n * ⚠ Khác thượng nguồn (3.0.1): giữ giá trị CŨ trước khi ghi. Bản gốc ghi trước rồi mới đọc\n * `steps[stateRef.current.value]` làm \"bước vừa rời\" — lúc đó nó đã là bước MỚI, nên\n * `onStepLeave` chạy trên bước đang bước vào (ngay trước `onStepEnter` của chính nó) và bước\n * vừa rời thì không bao giờ nhận được.\n */\n const previousValue = stateRef.current.value;\n stateRef.current[key] = value;\n\n if (key === \"open\" && typeof value === \"boolean\") {\n propsRef.current.onOpenChange?.(value);\n\n if (value) {\n if (stateRef.current.steps.length > 0) {\n if (stateRef.current.value >= stateRef.current.steps.length) {\n store.setState(\"value\", 0);\n }\n }\n } else {\n /*\n * ⚠ Khác thượng nguồn (3.0.1): đóng ở BƯỚC CUỐI (×, Escape, bấm ra ngoài) cũng là `onSkip`.\n * Bản gốc so `< steps.length - 1`, nên đóng tại bước cuối không gọi `onSkip` lẫn\n * `onComplete` — app ghi \"đã xem tour\" ở hai callback đó sẽ bật lại tour lần sau. Đi hết\n * bằng nút Tiếp ở bước cuối vẫn là `onComplete`: khi đó `value` đã bằng `steps.length`.\n */\n if (stateRef.current.value < (stateRef.current.steps.length || 0)) {\n propsRef.current.onSkip?.();\n }\n }\n } else if (key === \"value\" && typeof value === \"number\") {\n const prevStep = stateRef.current.steps[previousValue];\n const nextStep = stateRef.current.steps[value];\n\n prevStep?.onStepLeave?.();\n nextStep?.onStepEnter?.();\n\n if (value >= stateRef.current.steps.length) {\n propsRef.current.onComplete?.();\n\n if (propsRef.current.valueProp !== undefined) {\n propsRef.current.onValueChange?.(value);\n }\n\n store.setState(\"open\", false);\n return;\n }\n\n if (propsRef.current.valueProp !== undefined) {\n propsRef.current.onValueChange?.(value);\n return;\n }\n\n propsRef.current.onValueChange?.(value);\n\n if (nextStep && propsRef.current.autoScroll) {\n const targetElement = getTargetElement(nextStep.target);\n if (targetElement) {\n onScrollToElement(targetElement, propsRef.current.scrollBehavior, propsRef.current.scrollOffset);\n }\n }\n }\n\n store.notify();\n },\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n },\n addStep: (stepData) => {\n const id = `step-${stepIdCounterRef.current.current++}`;\n const index = stateRef.current.steps.length;\n stepIdsMapRef.current.set(id, index);\n stateRef.current.steps = [...stateRef.current.steps, stepData];\n store.notify();\n return { id, index };\n },\n removeStep: (id) => {\n const index = stepIdsMapRef.current.get(id);\n if (index === undefined) return;\n\n stateRef.current.steps = stateRef.current.steps.filter((_, i) => i !== index);\n\n stepIdsMapRef.current.delete(id);\n\n for (const [stepId, stepIndex] of stepIdsMapRef.current.entries()) {\n if (stepIndex > index) {\n stepIdsMapRef.current.set(stepId, stepIndex - 1);\n }\n }\n\n store.notify();\n },\n updateStep: (id, stepData) => {\n const index = stepIdsMapRef.current.get(id);\n if (index === undefined) return;\n if (stateRef.current.steps[index] === stepData) return;\n\n const steps = [...stateRef.current.steps];\n steps[index] = stepData;\n stateRef.current.steps = steps;\n store.notify();\n },\n getStepIndex: (id) => stepIdsMapRef.current.get(id) ?? -1\n }),\n [stateRef, listenersRef, stepIdsMapRef, stepIdCounterRef, propsRef]\n );\n\n const open = useStore((state) => state.open, store);\n\n /*\n * ⚠ Khác thượng nguồn: Escape mà tour đã DÙNG thì phải đánh dấu là đã dùng, và phải nghe ở pha\n * CAPTURE. Bản gốc nghe ở pha nổi bọt và không gọi `preventDefault()`, nên đo được: MỘT lần\n * Escape vừa đóng tour vừa đóng luôn `ActionBar` đang mở — tức xoá mất lựa chọn dòng của người\n * dùng. Chỉ `preventDefault()` thôi là chưa đủ: listener của `ActionBar` cũng nằm ở `document`,\n * đăng ký TRƯỚC (thanh mở trước tour), nên nó chạy trước và chưa thấy cờ. Pha capture của\n * `document` chạy trước mọi listener nổi bọt — đúng chỗ Radix `DismissableLayer` nghe Escape,\n * nên tour cư xử như một Dialog của gói. Đang gõ dở bằng bộ gõ (Telex/VNI, `isComposing`) hoặc\n * Escape đã có lớp khác dùng thì bỏ qua, cùng luật với `ActionBar`.\n */\n React.useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if (!open || event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n\n if (propsRef.current.onEscapeKeyDown) {\n propsRef.current.onEscapeKeyDown(event);\n if (event.defaultPrevented) return;\n }\n event.preventDefault();\n store.setState(\"open\", false);\n }\n\n document.addEventListener(\"keydown\", onKeyDown, { capture: true });\n return () => document.removeEventListener(\"keydown\", onKeyDown, { capture: true });\n }, [store, open, propsRef]);\n\n useIsomorphicLayoutEffect(() => {\n const wasOpen = prevOpenRef.current;\n\n if (open && !wasOpen) {\n previouslyFocusedElementRef.current = document.activeElement as HTMLElement | null;\n } else if (!open && wasOpen) {\n setTimeout(() => {\n const container = portal ?? document.body;\n const closeAutoFocusEvent = new CustomEvent(CLOSE_AUTO_FOCUS, EVENT_OPTIONS);\n\n if (propsRef.current.onCloseAutoFocus) {\n container.addEventListener(CLOSE_AUTO_FOCUS, propsRef.current.onCloseAutoFocus as EventListener, {\n once: true\n });\n }\n container.dispatchEvent(closeAutoFocusEvent);\n\n if (!closeAutoFocusEvent.defaultPrevented) {\n const elementToFocus = previouslyFocusedElementRef.current;\n if (elementToFocus && document.body.contains(elementToFocus)) {\n elementToFocus.focus({ preventScroll: true });\n }\n }\n\n previouslyFocusedElementRef.current = null;\n }, 0);\n }\n\n prevOpenRef.current = open;\n }, [open, portal, propsRef]);\n\n useIsomorphicLayoutEffect(() => {\n if (openProp !== undefined) {\n store.setState(\"open\", openProp);\n }\n }, [openProp, store]);\n\n useIsomorphicLayoutEffect(() => {\n if (valueProp !== undefined) {\n store.setState(\"value\", valueProp);\n }\n }, [valueProp, store]);\n\n const labels = useComitorLabels(\"tour\", labelsProp);\n\n const contextValue = React.useMemo<TourContextValue>(\n () => ({\n dir,\n alignOffset,\n sideOffset,\n spotlightPadding,\n dismissible,\n modal,\n stepFooter,\n labels,\n onPointerDownOutside,\n onInteractOutside,\n onOpenAutoFocus,\n onCloseAutoFocus\n }),\n [\n dir,\n alignOffset,\n sideOffset,\n spotlightPadding,\n dismissible,\n modal,\n stepFooter,\n labels,\n onPointerDownOutside,\n onInteractOutside,\n onOpenAutoFocus,\n onCloseAutoFocus\n ]\n );\n\n const portalContextValue = React.useMemo<PortalContextValue>(\n () => ({\n portal,\n onPortalChange: setPortal\n }),\n [portal]\n );\n\n useScrollLock(open && modal);\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StoreContext.Provider value={store}>\n <TourContext.Provider value={contextValue}>\n <PortalContext.Provider value={portalContextValue}>\n <RootPrimitive data-slot=\"tour\" dir={dir} {...rootProps} />\n </PortalContext.Provider>\n </TourContext.Provider>\n </StoreContext.Provider>\n );\n}\n\ninterface TourStepProps extends DivProps {\n target: StepTarget;\n side?: Side;\n sideOffset?: number;\n align?: Align;\n alignOffset?: number;\n collisionBoundary?: Boundary | Boundary[];\n collisionPadding?: number | Partial<Record<Side, number>>;\n arrowPadding?: number;\n sticky?: \"partial\" | \"always\";\n hideWhenDetached?: boolean;\n avoidCollisions?: boolean;\n required?: boolean;\n forceMount?: boolean;\n onStepEnter?: () => void;\n onStepLeave?: () => void;\n}\n\nfunction TourStep(props: TourStepProps) {\n const {\n target,\n side = \"bottom\",\n sideOffset,\n align = \"center\",\n alignOffset,\n collisionBoundary = EMPTY_BOUNDARY,\n collisionPadding = 0,\n arrowPadding = 0,\n sticky = \"partial\",\n hideWhenDetached = false,\n avoidCollisions = true,\n required = false,\n forceMount = false,\n onStepEnter,\n onStepLeave,\n onPointerDownCapture: onPointerDownCaptureProp,\n onFocusCapture: onFocusCaptureProp,\n onBlurCapture: onBlurCaptureProp,\n children,\n className,\n style,\n asChild,\n ...stepProps\n } = props;\n\n const store = useStoreContext(STEP_NAME);\n\n const [arrow, setArrow] = React.useState<HTMLSpanElement | null>(null);\n const [footer, setFooter] = React.useState<FooterElement | null>(null);\n const [targetElement, setTargetElement] = React.useState<HTMLElement | null>(null);\n\n const stepRef = React.useRef<StepElement | null>(null);\n const stepIdRef = React.useRef<string>(\"\");\n const isPointerInsideReactTreeRef = React.useRef(false);\n const isFocusInsideReactTreeRef = React.useRef(false);\n\n const titleId = React.useId();\n const descriptionId = React.useId();\n\n const open = useStore((state) => state.open);\n const value = useStore((state) => state.value);\n const steps = useStore((state) => state.steps);\n const context = useTourContext(STEP_NAME);\n\n const resolvedSideOffset = sideOffset ?? context.sideOffset;\n const resolvedAlignOffset = alignOffset ?? context.alignOffset;\n\n /*\n * ⚠ Khác thượng nguồn: đăng ký bước MỘT lần khi mount, sau đó cập nhật TẠI CHỖ.\n *\n * Bản gốc gỡ rồi thêm lại bước mỗi khi một prop đổi (kể cả `onStepEnter={() => …}` viết tại\n * chỗ — tức MỖI lần render). Thêm lại là nối vào CUỐI danh sách, còn thứ tự các bước khác giữ\n * trong ref riêng không ai cập nhật: bước bị đẩy xuống cuối, và ở `value` cũ không bước nào\n * nhận mình là bước hiện tại — tour trống trơn. Chỉ cần `side={isMobile ? … : …}` là dính.\n *\n * Hai callback đi qua ref để luôn gọi bản mới nhất mà không làm dữ liệu bước \"đổi\".\n */\n const callbacksRef = useAsRef({ onStepEnter, onStepLeave });\n\n const ownStepData = React.useMemo<StepData>(\n () => ({\n target,\n align,\n alignOffset: resolvedAlignOffset,\n side,\n sideOffset: resolvedSideOffset,\n collisionBoundary,\n collisionPadding,\n arrowPadding,\n sticky,\n hideWhenDetached,\n avoidCollisions,\n onStepEnter: () => callbacksRef.current.onStepEnter?.(),\n onStepLeave: () => callbacksRef.current.onStepLeave?.(),\n required\n }),\n [\n target,\n align,\n resolvedAlignOffset,\n side,\n resolvedSideOffset,\n collisionBoundary,\n collisionPadding,\n arrowPadding,\n sticky,\n hideWhenDetached,\n avoidCollisions,\n required,\n callbacksRef\n ]\n );\n const ownStepDataRef = useAsRef(ownStepData);\n\n useIsomorphicLayoutEffect(() => {\n const { id } = store.addStep(ownStepDataRef.current);\n stepIdRef.current = id;\n\n return () => {\n store.removeStep(id);\n };\n }, [store, ownStepDataRef]);\n\n useIsomorphicLayoutEffect(() => {\n store.updateStep(stepIdRef.current, ownStepData);\n }, [store, ownStepData]);\n\n const stepData = steps[value];\n\n // ⚠ Khác thượng nguồn: vị trí đọc từ store theo id (được dồn lại khi một bước khác bị gỡ),\n // không từ một ref chụp lúc đăng ký.\n const isCurrentStep = store.getStepIndex(stepIdRef.current) === value;\n\n /*\n * ⚠ Khác thượng nguồn: bản gốc gọi `getTargetElement` — tức `document.querySelector` — NGAY\n * TRONG render. Ở đây phân giải trong layout effect: chạy sau MỖI lần render như nhịp của bản\n * gốc (đích xuất hiện muộn vẫn bắt được), vẫn trước lượt sơn, và chỉ cho bước đang hiện.\n * `setState` cùng giá trị thì React bỏ qua, nên không có vòng lặp.\n */\n useIsomorphicLayoutEffect(() => {\n setTargetElement(open && isCurrentStep && stepData ? getTargetElement(stepData.target) : null);\n });\n\n /*\n * ⚠ Khác thượng nguồn: gom điều kiện \"bước này đang HIỆN\" về một chỗ. Bản gốc bật bẫy focus\n * và bộ nghe bấm-ra-ngoài theo `open && isCurrentStep`; nếu lượt render đó chưa có đích (và\n * không `forceMount`) thì bước chưa có DOM, hiệu ứng thoát sớm và KHÔNG chạy lại khi đích xuất\n * hiện — focus không bao giờ vào được bước.\n */\n const isStepRendered = open && isCurrentStep && !!stepData && (!!targetElement || forceMount);\n\n const middleware = React.useMemo(() => {\n if (!stepData) return [];\n\n const mainAxisOffset = stepData.sideOffset ?? resolvedSideOffset;\n const crossAxisOffset = stepData.alignOffset ?? resolvedAlignOffset;\n\n const padding =\n typeof stepData.collisionPadding === \"number\"\n ? stepData.collisionPadding\n : {\n top: stepData.collisionPadding?.top ?? 0,\n right: stepData.collisionPadding?.right ?? 0,\n bottom: stepData.collisionPadding?.bottom ?? 0,\n left: stepData.collisionPadding?.left ?? 0\n };\n\n const boundary = Array.isArray(stepData.collisionBoundary)\n ? stepData.collisionBoundary\n : stepData.collisionBoundary\n ? [stepData.collisionBoundary]\n : [];\n const hasExplicitBoundaries = boundary.length > 0;\n\n const detectOverflowOptions = {\n padding,\n boundary: boundary.filter((b): b is Element => b !== null),\n altBoundary: hasExplicitBoundaries\n };\n\n return [\n offset({\n mainAxis: mainAxisOffset,\n alignmentAxis: crossAxisOffset\n }),\n stepData.avoidCollisions &&\n shift({\n mainAxis: true,\n crossAxis: false,\n limiter: stepData.sticky === \"partial\" ? limitShift() : undefined,\n ...detectOverflowOptions\n }),\n stepData.avoidCollisions && flip({ ...detectOverflowOptions }),\n arrow && onArrow({ element: arrow, padding: stepData.arrowPadding }),\n stepData.hideWhenDetached &&\n hide({\n strategy: \"referenceHidden\",\n ...detectOverflowOptions\n })\n ].filter(Boolean) as Middleware[];\n }, [stepData, resolvedSideOffset, resolvedAlignOffset, arrow]);\n\n const placement = getPlacement(stepData?.side ?? side, stepData?.align ?? align);\n\n const {\n refs,\n floatingStyles,\n placement: finalPlacement,\n middlewareData\n } = useFloating({\n placement,\n middleware,\n strategy: \"fixed\",\n whileElementsMounted: autoUpdate,\n elements: {\n reference: targetElement\n }\n });\n\n const composedRef = useComposedRefs(refs.setFloating, stepRef);\n\n const [placedSide, placedAlign] = getSideAndAlignFromPlacement(finalPlacement);\n\n const arrowX = middlewareData.arrow?.x;\n const arrowY = middlewareData.arrow?.y;\n const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;\n const isHidden = hideWhenDetached && middlewareData.hide?.referenceHidden;\n\n const stepContextValue = React.useMemo<StepContextValue>(\n () => ({\n arrowX,\n arrowY,\n placedAlign,\n placedSide,\n shouldHideArrow: cannotCenterArrow,\n titleId,\n descriptionId,\n onArrowChange: setArrow,\n onFooterChange: setFooter\n }),\n [arrowX, arrowY, placedSide, placedAlign, cannotCenterArrow, titleId, descriptionId]\n );\n\n React.useEffect(() => {\n if (open && targetElement && isCurrentStep) {\n updateMask(store, targetElement, context.spotlightPadding);\n\n let rafId: number | null = null;\n\n function onResize() {\n if (targetElement) {\n updateMask(store, targetElement, context.spotlightPadding);\n }\n }\n\n function onScroll() {\n if (rafId !== null) return;\n rafId = requestAnimationFrame(() => {\n if (targetElement) {\n updateMask(store, targetElement, context.spotlightPadding);\n }\n rafId = null;\n });\n }\n\n window.addEventListener(\"resize\", onResize);\n /*\n * ⚠ Khác thượng nguồn: nghe ở pha CAPTURE. Sự kiện `scroll` của một phần tử không nổi bọt\n * lên `window`; bản gốc chỉ bắt được khi CỬA SỔ cuộn. Trong `AppShell` chỉ `<main>` cuộn,\n * nên lỗ sáng đứng yên trong khi đích trôi đi.\n */\n window.addEventListener(\"scroll\", onScroll, { capture: true, passive: true });\n return () => {\n window.removeEventListener(\"resize\", onResize);\n window.removeEventListener(\"scroll\", onScroll, { capture: true });\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n }\n };\n }\n }, [open, targetElement, isCurrentStep, store, context.spotlightPadding]);\n\n React.useEffect(() => {\n if (!isStepRendered) return;\n\n const stepElement = stepRef.current;\n if (!stepElement) return;\n\n const ownerDocument = stepElement.ownerDocument;\n\n function onPointerDown(event: PointerEvent) {\n if (event.target && !isPointerInsideReactTreeRef.current) {\n const pointerDownOutsideEvent = new CustomEvent(POINTER_DOWN_OUTSIDE, {\n ...EVENT_OPTIONS,\n detail: { originalEvent: event }\n });\n\n context.onPointerDownOutside?.(pointerDownOutsideEvent);\n\n const interactOutsideEvent = new CustomEvent(INTERACT_OUTSIDE, {\n ...EVENT_OPTIONS,\n detail: { originalEvent: event }\n });\n context.onInteractOutside?.(interactOutsideEvent);\n\n if (\n !pointerDownOutsideEvent.defaultPrevented &&\n !interactOutsideEvent.defaultPrevented &&\n context.dismissible\n ) {\n store.setState(\"open\", false);\n }\n }\n\n isPointerInsideReactTreeRef.current = false;\n }\n\n const timerId = window.setTimeout(() => {\n ownerDocument.addEventListener(\"pointerdown\", onPointerDown);\n }, 0);\n\n return () => {\n window.clearTimeout(timerId);\n ownerDocument.removeEventListener(\"pointerdown\", onPointerDown);\n };\n }, [isStepRendered, store, context]);\n\n React.useEffect(() => {\n if (!isStepRendered) return;\n\n const stepElement = stepRef.current;\n if (!stepElement) return;\n\n const ownerDocument = stepElement.ownerDocument;\n\n function onFocusIn(event: FocusEvent) {\n const target = event.target as HTMLElement;\n\n const isFocusInStep = stepElement?.contains(target);\n const isFocusInTarget = targetElement?.contains(target);\n\n if (event.target && !isFocusInsideReactTreeRef.current && !isFocusInStep && !isFocusInTarget) {\n const interactOutsideEvent = new CustomEvent(INTERACT_OUTSIDE, {\n ...EVENT_OPTIONS,\n detail: { originalEvent: event }\n });\n\n context.onInteractOutside?.(interactOutsideEvent);\n\n if (!interactOutsideEvent.defaultPrevented && context.dismissible) {\n store.setState(\"open\", false);\n }\n }\n }\n\n ownerDocument.addEventListener(\"focusin\", onFocusIn);\n\n return () => {\n ownerDocument.removeEventListener(\"focusin\", onFocusIn);\n };\n }, [isStepRendered, store, context, targetElement]);\n\n const onPointerDownCapture = React.useCallback(\n (event: React.PointerEvent<StepElement>) => {\n onPointerDownCaptureProp?.(event);\n isPointerInsideReactTreeRef.current = true;\n },\n [onPointerDownCaptureProp]\n );\n\n const onFocusCapture = React.useCallback(\n (event: React.FocusEvent<StepElement>) => {\n onFocusCaptureProp?.(event);\n isFocusInsideReactTreeRef.current = true;\n },\n [onFocusCaptureProp]\n );\n\n const onBlurCapture = React.useCallback(\n (event: React.FocusEvent<StepElement>) => {\n onBlurCaptureProp?.(event);\n isFocusInsideReactTreeRef.current = false;\n },\n [onBlurCaptureProp]\n );\n\n React.useEffect(() => {\n if (!open || !isCurrentStep || !targetElement) return;\n\n function onTargetPointerDownCapture() {\n isPointerInsideReactTreeRef.current = true;\n }\n\n function onTargetFocusCapture() {\n isFocusInsideReactTreeRef.current = true;\n }\n\n function onTargetBlurCapture() {\n isFocusInsideReactTreeRef.current = false;\n }\n\n targetElement.addEventListener(\"pointerdown\", onTargetPointerDownCapture, true);\n targetElement.addEventListener(\"focus\", onTargetFocusCapture, true);\n targetElement.addEventListener(\"blur\", onTargetBlurCapture, true);\n\n return () => {\n targetElement.removeEventListener(\"pointerdown\", onTargetPointerDownCapture, true);\n targetElement.removeEventListener(\"focus\", onTargetFocusCapture, true);\n targetElement.removeEventListener(\"blur\", onTargetBlurCapture, true);\n };\n }, [open, isCurrentStep, targetElement]);\n\n useFocusGuards(isStepRendered);\n useFocusTrap(stepRef, isStepRendered, open, context.onOpenAutoFocus, context.onCloseAutoFocus);\n\n if (!isStepRendered) {\n return null;\n }\n\n const StepPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StepContext.Provider value={stepContextValue}>\n <StepPrimitive\n ref={composedRef}\n /*\n * ⚠ Khác thượng nguồn: bản gốc là `<div>` TRẦN, không vai trò, không tên. Bẫy focus kéo\n * người dùng vào đó và trình đọc màn hình chỉ đọc \"nút Đóng hướng dẫn\" — không nói\n * đang ở đâu, bước này nói gì. `role=\"dialog\"` + tiêu đề/mô tả làm tên và lời mô tả, như\n * `PopoverContent` của Radix. Không `aria-modal`: đích đang được soi nằm NGOÀI bước và\n * người dùng vẫn cần đọc được nó.\n */\n role=\"dialog\"\n aria-labelledby={titleId}\n aria-describedby={descriptionId}\n data-slot=\"tour-step\"\n data-side={placedSide}\n data-align={placedAlign}\n dir={context.dir}\n tabIndex={-1}\n {...stepProps}\n onPointerDownCapture={onPointerDownCapture}\n onFocusCapture={onFocusCapture}\n onBlurCapture={onBlurCapture}\n className={cn(\n // Bề mặt cùng khuôn với `PopoverContent`: nền/chữ popover, viền token, bóng tầng nổi.\n \"fixed z-50 flex w-80 flex-col gap-4 rounded-lg border bg-popover p-4 text-popover-foreground shadow-overlay outline-none\",\n className\n )}\n style={{\n ...style,\n ...floatingStyles,\n visibility: isHidden ? \"hidden\" : undefined,\n pointerEvents: isHidden ? \"none\" : undefined\n }}\n >\n {children}\n {!footer && <DefaultFooterContext.Provider value={true}>{context.stepFooter}</DefaultFooterContext.Provider>}\n </StepPrimitive>\n </StepContext.Provider>\n );\n}\n\ninterface TourSpotlightProps extends DivProps {\n forceMount?: boolean;\n}\n\nfunction TourSpotlight(props: TourSpotlightProps) {\n const { asChild, className, style, forceMount = false, ...backdropProps } = props;\n\n const open = useStore((state) => state.open);\n const maskPath = useStore((state) => state.maskPath);\n\n if (!open && !forceMount) return null;\n\n const SpotlightPrimitive = asChild ? Slot : \"div\";\n\n return (\n <SpotlightPrimitive\n data-slot=\"tour-spotlight\"\n data-state={getDataState(open)}\n {...backdropProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: `bg-black/80` (bảng màu mặc định Tailwind) → đúng màu lớp phủ của\n * `DialogOverlay`, để lớp tối của tour và của hộp thoại là MỘT thứ trong mắt người dùng.\n * Không lấy `backdrop-blur-sm` của nó: tour cần người dùng còn đọc được ngữ cảnh quanh đích.\n */\n \"fixed inset-0 z-50 bg-ink/60 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 dark:bg-ink/80\",\n className\n )}\n style={{\n clipPath: maskPath,\n ...style\n }}\n />\n );\n}\n\ninterface TourSpotlightRingProps extends DivProps {\n forceMount?: boolean;\n}\n\nfunction TourSpotlightRing(props: TourSpotlightRingProps) {\n const { asChild, className, style, forceMount = false, ...ringProps } = props;\n\n const open = useStore((state) => state.open);\n const spotlightRect = useStore((state) => state.spotlightRect);\n\n if (!open && !forceMount) return null;\n if (!spotlightRect) return null;\n\n const RingPrimitive = asChild ? Slot : \"div\";\n\n return (\n <RingPrimitive\n data-slot=\"tour-spotlight-ring\"\n data-state={getDataState(open)}\n {...ringProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: bỏ `border-ring`. Không có độ dày viền đi kèm nên nó không vẽ gì —\n * chỉ là một màu viền treo sẵn. App muốn viền thì truyền đủ cả độ dày lẫn màu\n * (`border-2 border-primary-ink`), như chính các ví dụ của thượng nguồn vẫn làm.\n */\n \"pointer-events-none fixed z-50 ring-[3px] ring-ring/50\",\n className\n )}\n style={{\n left: spotlightRect.x,\n top: spotlightRect.y,\n width: spotlightRect.width,\n height: spotlightRect.height,\n ...style\n }}\n />\n );\n}\n\ninterface TourPortalProps {\n children?: React.ReactNode;\n container?: HTMLElement | null;\n}\n\nfunction TourPortal(props: TourPortalProps) {\n const { children, container } = props;\n\n const portalContext = usePortalContext(PORTAL_NAME);\n\n /*\n * ⚠ Khác thượng nguồn: giữ NÚT gắn trong state thay cho cờ `mounted`, để lượt render không\n * phải đọc `document.body` — mọi chỗ chạm DOM đều nằm trong effect.\n */\n const [mountNode, setMountNode] = React.useState<HTMLElement | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const node = container ?? document.body;\n setMountNode(node);\n\n portalContext?.onPortalChange(node);\n return () => {\n portalContext?.onPortalChange(null);\n };\n }, [container, portalContext]);\n\n if (!mountNode) return null;\n\n const portalContainer = container ?? portalContext?.portal ?? mountNode;\n\n return ReactDOM.createPortal(children, portalContainer);\n}\n\ninterface TourArrowProps extends React.ComponentProps<\"svg\"> {\n width?: number;\n height?: number;\n asChild?: boolean;\n}\n\nfunction TourArrow(props: TourArrowProps) {\n const { width = 10, height = 5, className, children, asChild, ...arrowProps } = props;\n\n const stepContext = useStepContext(ARROW_NAME);\n const baseSide = OPPOSITE_SIDE[stepContext.placedSide];\n\n return (\n <span\n ref={stepContext.onArrowChange}\n data-slot=\"tour-arrow\"\n style={{\n position: \"absolute\",\n left: stepContext.arrowX != null ? `${stepContext.arrowX}px` : undefined,\n top: stepContext.arrowY != null ? `${stepContext.arrowY}px` : undefined,\n [baseSide]: 0,\n transformOrigin: {\n top: \"\",\n right: \"0 0\",\n bottom: \"center 0\",\n left: \"100% 0\"\n }[stepContext.placedSide],\n transform: {\n top: \"translateY(100%)\",\n right: \"translateY(50%) rotate(90deg) translateX(-50%)\",\n bottom: \"rotate(180deg)\",\n left: \"translateY(50%) rotate(-90deg) translateX(50%)\"\n }[stepContext.placedSide],\n visibility: stepContext.shouldHideArrow ? \"hidden\" : undefined\n }}\n >\n {/* ⚠ Khác thượng nguồn: `aria-hidden` — mũi tên thuần trang trí, không có gì để đọc. */}\n <svg\n viewBox=\"0 0 30 10\"\n preserveAspectRatio=\"none\"\n width={width}\n height={height}\n aria-hidden=\"true\"\n {...arrowProps}\n className={cn(\"block fill-popover stroke-border\", className)}\n >\n {asChild ? children : <polygon points=\"0,0 30,0 15,10\" />}\n </svg>\n </span>\n );\n}\n\ntype TourHeaderProps = DivProps;\n\nfunction TourHeader(props: TourHeaderProps) {\n const { asChild, className, ...headerProps } = props;\n\n const context = useTourContext(HEADER_NAME);\n\n const HeaderPrimitive = asChild ? Slot : \"div\";\n\n return (\n <HeaderPrimitive\n data-slot=\"tour-header\"\n dir={context.dir}\n {...headerProps}\n className={cn(\"flex flex-col gap-1.5 text-center sm:text-left\", className)}\n />\n );\n}\n\ntype TourTitleProps = DivProps;\n\nfunction TourTitle(props: TourTitleProps) {\n const { asChild, className, ...titleProps } = props;\n\n const context = useTourContext(TITLE_NAME);\n // ⚠ Khác thượng nguồn: nhận id từ bước chứa nó để bước trỏ `aria-labelledby` vào đây.\n const stepContext = React.useContext(StepContext);\n\n const TitlePrimitive = asChild ? Slot : \"div\";\n\n return (\n <TitlePrimitive\n id={stepContext?.titleId}\n data-slot=\"tour-title\"\n dir={context.dir}\n {...titleProps}\n className={cn(\"text-lg leading-none font-semibold tracking-tight\", className)}\n />\n );\n}\n\ntype TourDescriptionProps = DivProps;\n\nfunction TourDescription(props: TourDescriptionProps) {\n const { asChild, className, ...descriptionProps } = props;\n\n const context = useTourContext(DESCRIPTION_NAME);\n // ⚠ Khác thượng nguồn: nhận id từ bước chứa nó để bước trỏ `aria-describedby` vào đây.\n const stepContext = React.useContext(StepContext);\n\n const DescriptionPrimitive = asChild ? Slot : \"div\";\n\n return (\n <DescriptionPrimitive\n id={stepContext?.descriptionId}\n data-slot=\"tour-description\"\n dir={context.dir}\n {...descriptionProps}\n className={cn(\"text-sm text-muted-foreground\", className)}\n />\n );\n}\n\ninterface TourCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nfunction TourClose(props: TourCloseProps) {\n const { asChild, className, children, onClick: onClickProp, ...closeButtonProps } = props;\n\n const store = useStoreContext(CLOSE_NAME);\n const context = useTourContext(CLOSE_NAME);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<CloseElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n store.setState(\"open\", false);\n },\n [store, onClickProp]\n );\n\n const ClosePrimitive = asChild ? Slot : \"button\";\n\n return (\n <ClosePrimitive\n type=\"button\"\n aria-label={context.labels.close}\n data-slot=\"tour-close\"\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: khuôn nút đóng của `DialogContent` (màu chữ muted, hover nền muted,\n * vòng focus của gói) thay cho `opacity-70` — `opacity` không miễn ngưỡng tương phản cho\n * thứ còn bấm được (docs/hop-dong-mau.md). `top-3 right-3 p-1` đặt icon đúng chỗ\n * `top-4 right-4` cũ nhưng vùng bấm lên ~24px thay vì 16px (WCAG 2.5.8).\n */\n \"absolute top-3 right-3 rounded-md p-1 text-muted-foreground transition-colors duration-150 ease-out hover:bg-muted hover:text-foreground 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 className\n )}\n onClick={onClick}\n {...closeButtonProps}\n >\n {/*\n * ⚠ Khác thượng nguồn: bản gốc luôn render icon X làm con, đè mất `children` của app — và\n * với `asChild` thì Slot nhận chính cái `<svg>` làm phần tử được \"bọc\".\n */}\n {children ?? <X aria-hidden=\"true\" />}\n </ClosePrimitive>\n );\n}\n\ntype TourPrevProps = React.ComponentProps<typeof Button>;\n\n/*\n * ⚠ Khác thượng nguồn (áp cho cả `TourPrev`, `TourNext`, `TourSkip`): bỏ `aria-label` mặc định.\n * Nút đã có chữ hiện; `aria-label` cứng ĐÈ lên chữ đó — ở bước cuối `TourNext` HIỆN \"Finish\"\n * nhưng ĐỌC \"Next step\", và app truyền `children` riêng vẫn bị đọc bằng nhãn cũ. Lệch WCAG 2.5.3\n * (Label in Name): người dùng điều khiển bằng giọng nói gọi đúng chữ trên nút mà không bấm được.\n * App dùng nút chỉ-có-icon thì tự truyền `aria-label`.\n */\nfunction TourPrev(props: TourPrevProps) {\n const { children, onClick: onClickProp, ...prevButtonProps } = props;\n\n const store = useStoreContext(PREV_NAME);\n const context = useTourContext(PREV_NAME);\n const value = useStore((state) => state.value);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<PrevElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n if (value > 0) {\n store.setState(\"value\", value - 1);\n }\n },\n [value, store, onClickProp]\n );\n\n return (\n <Button\n type=\"button\"\n data-slot=\"tour-prev\"\n variant=\"outline\"\n {...prevButtonProps}\n onClick={onClick}\n disabled={value === 0}\n >\n {children ?? (\n <>\n {/* `Button` của gói không tự đặt cỡ icon (khác shadcn) → ghi `size-4` tường minh. */}\n <ChevronLeft aria-hidden=\"true\" className=\"size-4\" />\n {context.labels.prev}\n </>\n )}\n </Button>\n );\n}\n\ntype TourNextProps = React.ComponentProps<typeof Button>;\n\nfunction TourNext(props: TourNextProps) {\n const { children, onClick: onClickProp, ...nextButtonProps } = props;\n const store = useStoreContext(NEXT_NAME);\n const context = useTourContext(NEXT_NAME);\n const value = useStore((state) => state.value);\n const steps = useStore((state) => state.steps);\n\n const isLastStep = value === steps.length - 1;\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<NextElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n store.setState(\"value\", value + 1);\n },\n [value, store, onClickProp]\n );\n\n return (\n <Button type=\"button\" data-slot=\"tour-next\" {...nextButtonProps} onClick={onClick}>\n {children ?? (\n <>\n {isLastStep ? context.labels.finish : context.labels.next}\n {!isLastStep && <ChevronRight aria-hidden=\"true\" className=\"size-4\" />}\n </>\n )}\n </Button>\n );\n}\n\ntype TourSkipProps = React.ComponentProps<typeof Button>;\n\nfunction TourSkip(props: TourSkipProps) {\n const { children, onClick: onClickProp, ...skipButtonProps } = props;\n\n const store = useStoreContext(SKIP_NAME);\n const context = useTourContext(SKIP_NAME);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<SkipElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n store.setState(\"open\", false);\n },\n [store, onClickProp]\n );\n\n return (\n <Button type=\"button\" data-slot=\"tour-skip\" variant=\"outline\" {...skipButtonProps} onClick={onClick}>\n {children ?? context.labels.skip}\n </Button>\n );\n}\n\ninterface TourStepCounterProps extends DivProps {\n /** Mặc định `labels.stepCounter` của `Tour` — \"Step 2 of 5\". */\n format?: (current: number, total: number) => string;\n}\n\nfunction TourStepCounter(props: TourStepCounterProps) {\n const { format: formatProp, asChild, className, children, ...stepCounterProps } = props;\n\n const context = useTourContext(STEP_COUNTER_NAME);\n const format = formatProp ?? context.labels.stepCounter;\n\n const value = useStore((state) => state.value);\n const steps = useStore((state) => state.steps);\n\n const StepCounterPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StepCounterPrimitive\n data-slot=\"tour-step-counter\"\n {...stepCounterProps}\n // ⚠ Khác thượng nguồn: `whitespace-nowrap` — \"Bước 1/3\" đặt cạnh \"Quay lại\"/\"Tiếp theo\" trong\n // thẻ `w-80` bị bẻ làm hai dòng (đo trên trình duyệt); con số đếm bước không nên vỡ dòng.\n className={cn(\"whitespace-nowrap text-sm text-muted-foreground\", className)}\n >\n {children ?? format(value + 1, steps.length)}\n </StepCounterPrimitive>\n );\n}\n\ntype TourFooterProps = DivProps;\n\nfunction TourFooter(props: TourFooterProps) {\n const { asChild, className, ref, ...footerProps } = props;\n\n const stepContext = useStepContext(FOOTER_NAME);\n const hasDefaultFooter = React.useContext(DefaultFooterContext);\n const context = useTourContext(FOOTER_NAME);\n\n const composedRef = useComposedRefs(ref, hasDefaultFooter ? undefined : stepContext.onFooterChange);\n\n const FooterPrimitive = asChild ? Slot : \"div\";\n\n return (\n <FooterPrimitive\n data-slot=\"tour-footer\"\n dir={context.dir}\n {...footerProps}\n ref={composedRef}\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n />\n );\n}\n\nexport {\n Tour,\n TourArrow,\n type TourArrowProps,\n TourClose,\n type TourCloseProps,\n TourDescription,\n type TourDescriptionProps,\n TourFooter,\n type TourFooterProps,\n TourHeader,\n type TourHeaderProps,\n TourNext,\n type TourNextProps,\n TourPortal,\n type TourPortalProps,\n TourPrev,\n type TourPrevProps,\n type TourProps,\n TourSkip,\n type TourSkipProps,\n TourSpotlight,\n type TourSpotlightProps,\n TourSpotlightRing,\n type TourSpotlightRingProps,\n TourStep,\n TourStepCounter,\n type TourStepCounterProps,\n type TourStepProps,\n TourTitle,\n type TourTitleProps\n};\n"],"mappings":"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","names":["offset","onResize","onScroll","target"]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { vi } from "date-fns/locale";
|
|
4
3
|
import { Check, Upload, X } from "lucide-react";
|
|
5
4
|
import * as React from "react";
|
|
6
5
|
import { Badge, badgeVariants } from "../components/ui/badge.js";
|
|
@@ -22,7 +21,7 @@ import { Skeleton } from "../components/ui/skeleton.js";
|
|
|
22
21
|
import { Textarea } from "../components/ui/textarea.js";
|
|
23
22
|
import { cn } from "../lib/cn.js";
|
|
24
23
|
import { DataGridCellWrapper } from "./data-grid-cell-wrapper.js";
|
|
25
|
-
import { useDataGridMessages } from "./data-grid-locale.js";
|
|
24
|
+
import { useDataGridDateLocale, useDataGridMessages } from "./data-grid-locale.js";
|
|
26
25
|
import { toastError, toastMessage } from "./toast.js";
|
|
27
26
|
import { useBadgeOverflow } from "./use-badge-overflow.js";
|
|
28
27
|
import { useDebouncedCallback } from "./use-debounced-callback.js";
|
|
@@ -1080,6 +1079,7 @@ function DateCell({
|
|
|
1080
1079
|
isActiveSearchMatch,
|
|
1081
1080
|
readOnly
|
|
1082
1081
|
}) {
|
|
1082
|
+
const dateLocale = useDataGridDateLocale();
|
|
1083
1083
|
const initialValue = cell.getValue();
|
|
1084
1084
|
const [value, setValue] = React.useState(initialValue ?? "");
|
|
1085
1085
|
const containerRef = React.useRef(null);
|
|
@@ -1141,12 +1141,12 @@ function DateCell({
|
|
|
1141
1141
|
readOnly,
|
|
1142
1142
|
onKeyDown: onWrapperKeyDown,
|
|
1143
1143
|
children: /* @__PURE__ */ jsxs(Popover, { open: isEditing, onOpenChange, children: [
|
|
1144
|
-
/* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx("span", { "data-slot": "grid-cell-content", children: formatDateForDisplay(value) }) }),
|
|
1144
|
+
/* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx("span", { "data-slot": "grid-cell-content", children: formatDateForDisplay(value, dateLocale) }) }),
|
|
1145
1145
|
isEditing && /* @__PURE__ */ jsx(PopoverContent, { "data-grid-cell-editor": "", align: "start", alignOffset: -8, className: "w-auto p-0", children: /* @__PURE__ */ jsx(
|
|
1146
1146
|
Calendar,
|
|
1147
1147
|
{
|
|
1148
1148
|
mode: "single",
|
|
1149
|
-
locale:
|
|
1149
|
+
locale: dateLocale,
|
|
1150
1150
|
autoFocus: true,
|
|
1151
1151
|
defaultMonth: selectedDate ?? /* @__PURE__ */ new Date(),
|
|
1152
1152
|
selected: selectedDate,
|