@comitor/ui 1.11.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +35 -0
- package/README.md +23 -7
- package/dist/components/ui/action-bar-constants.d.ts +31 -0
- package/dist/components/ui/action-bar-constants.js +9 -0
- package/dist/components/ui/action-bar-constants.js.map +1 -0
- package/dist/components/ui/action-bar.d.ts +67 -0
- package/dist/components/ui/action-bar.js +522 -0
- package/dist/components/ui/action-bar.js.map +1 -0
- package/dist/components/ui/badge-overflow.d.ts +30 -0
- package/dist/components/ui/badge-overflow.js +179 -0
- package/dist/components/ui/badge-overflow.js.map +1 -0
- package/dist/components/ui/banner.d.ts +79 -0
- package/dist/components/ui/banner.js +518 -0
- package/dist/components/ui/banner.js.map +1 -0
- package/dist/components/ui/circular-progress.d.ts +50 -0
- package/dist/components/ui/circular-progress.js +266 -0
- package/dist/components/ui/circular-progress.js.map +1 -0
- package/dist/components/ui/cropper-constants.d.ts +34 -0
- package/dist/components/ui/cropper-constants.js +10 -0
- package/dist/components/ui/cropper-constants.js.map +1 -0
- package/dist/components/ui/cropper.d.ts +106 -0
- package/dist/components/ui/cropper.js +1269 -0
- package/dist/components/ui/cropper.js.map +1 -0
- package/dist/components/ui/drawer.d.ts +1 -1
- package/dist/components/ui/drawer.js +2 -2
- package/dist/components/ui/drawer.js.map +1 -1
- package/dist/components/ui/kanban-constants.d.ts +61 -0
- package/dist/components/ui/kanban-constants.js +21 -0
- package/dist/components/ui/kanban-constants.js.map +1 -0
- package/dist/components/ui/kanban.d.ts +72 -0
- package/dist/components/ui/kanban.js +787 -0
- package/dist/components/ui/kanban.js.map +1 -0
- package/dist/components/ui/key-value-constants.d.ts +33 -0
- package/dist/components/ui/key-value-constants.js +11 -0
- package/dist/components/ui/key-value-constants.js.map +1 -0
- package/dist/components/ui/key-value.d.ts +96 -0
- package/dist/components/ui/key-value.js +641 -0
- package/dist/components/ui/key-value.js.map +1 -0
- package/dist/components/ui/mask-input-constants.d.ts +99 -0
- package/dist/components/ui/mask-input-constants.js +601 -0
- package/dist/components/ui/mask-input-constants.js.map +1 -0
- package/dist/components/ui/mask-input.d.ts +36 -0
- package/dist/components/ui/mask-input.js +558 -0
- package/dist/components/ui/mask-input.js.map +1 -0
- package/dist/components/ui/mention.d.ts +31 -0
- package/dist/components/ui/mention.js +128 -0
- package/dist/components/ui/mention.js.map +1 -0
- package/dist/components/ui/phone-input-constants.d.ts +54 -0
- package/dist/components/ui/phone-input-constants.js +9 -0
- package/dist/components/ui/phone-input-constants.js.map +1 -0
- package/dist/components/ui/phone-input.d.ts +85 -0
- package/dist/components/ui/phone-input.js +743 -0
- package/dist/components/ui/phone-input.js.map +1 -0
- package/dist/components/ui/rating.d.ts +56 -0
- package/dist/components/ui/rating.js +646 -0
- package/dist/components/ui/rating.js.map +1 -0
- package/dist/components/ui/responsive-dialog.d.ts +35 -0
- package/dist/components/ui/responsive-dialog.js +193 -0
- package/dist/components/ui/responsive-dialog.js.map +1 -0
- package/dist/components/ui/scroll-spy.d.ts +42 -0
- package/dist/components/ui/scroll-spy.js +334 -0
- package/dist/components/ui/scroll-spy.js.map +1 -0
- package/dist/components/ui/stepper-constants.d.ts +29 -0
- package/dist/components/ui/stepper-constants.js +9 -0
- package/dist/components/ui/stepper-constants.js.map +1 -0
- package/dist/components/ui/stepper.d.ts +80 -0
- package/dist/components/ui/stepper.js +902 -0
- package/dist/components/ui/stepper.js.map +1 -0
- package/dist/components/ui/tags-input.d.ts +41 -0
- package/dist/components/ui/tags-input.js +127 -0
- package/dist/components/ui/tags-input.js.map +1 -0
- package/dist/components/ui/time-picker-constants.d.ts +48 -0
- package/dist/components/ui/time-picker-constants.js +14 -0
- package/dist/components/ui/time-picker-constants.js.map +1 -0
- package/dist/components/ui/time-picker.d.ts +115 -0
- package/dist/components/ui/time-picker.js +1620 -0
- package/dist/components/ui/time-picker.js.map +1 -0
- package/dist/components/ui/timeline.d.ts +32 -0
- package/dist/components/ui/timeline.js +559 -0
- package/dist/components/ui/timeline.js.map +1 -0
- package/dist/components/ui/tour-constants.d.ts +39 -0
- package/dist/components/ui/tour-constants.js +12 -0
- package/dist/components/ui/tour-constants.js.map +1 -0
- package/dist/components/ui/tour.d.ts +124 -0
- package/dist/components/ui/tour.js +1207 -0
- package/dist/components/ui/tour.js.map +1 -0
- package/dist/data-grid/data-grid-cell-variants.js +10 -6
- package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
- package/dist/data-grid/data-grid-cell-wrapper.js +1 -1
- package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -1
- package/dist/data-grid/data-grid-column-header.js +4 -2
- package/dist/data-grid/data-grid-column-header.js.map +1 -1
- package/dist/data-grid/data-grid-context-menu.js +5 -4
- package/dist/data-grid/data-grid-context-menu.js.map +1 -1
- package/dist/data-grid/data-grid-filter-menu.d.ts +12 -1
- package/dist/data-grid/data-grid-filter-menu.js +28 -17
- package/dist/data-grid/data-grid-filter-menu.js.map +1 -1
- package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +12 -1
- package/dist/data-grid/data-grid-keyboard-shortcuts.js +7 -5
- package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -1
- package/dist/data-grid/data-grid-locale.d.ts +24 -0
- package/dist/data-grid/data-grid-locale.js +25 -0
- package/dist/data-grid/data-grid-locale.js.map +1 -0
- package/dist/data-grid/data-grid-paste-dialog.js +3 -2
- package/dist/data-grid/data-grid-paste-dialog.js.map +1 -1
- package/dist/data-grid/data-grid-row-height-menu.d.ts +11 -1
- package/dist/data-grid/data-grid-row-height-menu.js +25 -28
- package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
- package/dist/data-grid/data-grid-row.js +1 -1
- package/dist/data-grid/data-grid-row.js.map +1 -1
- package/dist/data-grid/data-grid-search.js +6 -5
- package/dist/data-grid/data-grid-search.js.map +1 -1
- package/dist/data-grid/data-grid-select-column.js +3 -1
- package/dist/data-grid/data-grid-select-column.js.map +1 -1
- package/dist/data-grid/data-grid-sort-menu.d.ts +12 -1
- package/dist/data-grid/data-grid-sort-menu.js +25 -13
- package/dist/data-grid/data-grid-sort-menu.js.map +1 -1
- package/dist/data-grid/data-grid-view-menu.d.ts +12 -1
- package/dist/data-grid/data-grid-view-menu.js +12 -4
- package/dist/data-grid/data-grid-view-menu.js.map +1 -1
- package/dist/data-grid/data-grid.d.ts +14 -2
- package/dist/data-grid/data-grid.js +7 -4
- package/dist/data-grid/data-grid.js.map +1 -1
- package/dist/data-grid/filters.d.ts +17 -21
- package/dist/data-grid/filters.js +73 -52
- package/dist/data-grid/filters.js.map +1 -1
- package/dist/data-grid/index.d.ts +4 -1
- package/dist/data-grid/index.js +20 -2
- package/dist/data-grid/index.js.map +1 -1
- package/dist/data-grid/messages-en.d.ts +294 -0
- package/dist/data-grid/messages-en.js +279 -0
- package/dist/data-grid/messages-en.js.map +1 -0
- package/dist/data-grid/messages-vi.d.ts +12 -0
- package/dist/data-grid/messages-vi.js +279 -0
- package/dist/data-grid/messages-vi.js.map +1 -0
- package/dist/data-grid/messages.d.ts +549 -262
- package/dist/data-grid/messages.js +16 -275
- package/dist/data-grid/messages.js.map +1 -1
- package/dist/data-grid/sortable.js +1 -1
- package/dist/data-grid/sortable.js.map +1 -1
- package/dist/data-grid/use-data-grid-undo-redo.d.ts +13 -1
- package/dist/data-grid/use-data-grid-undo-redo.js +13 -10
- package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
- package/dist/data-grid/use-data-grid.d.ts +281 -1
- package/dist/data-grid/use-data-grid.js +24 -19
- package/dist/data-grid/use-data-grid.js.map +1 -1
- package/dist/data-grid/utils.d.ts +9 -1
- package/dist/data-grid/utils.js +3 -2
- package/dist/data-grid/utils.js.map +1 -1
- package/dist/index.d.ts +31 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/compose-refs.js.map +1 -0
- package/dist/lib/use-as-ref.js.map +1 -0
- package/dist/lib/use-is-mobile.d.ts +10 -0
- package/dist/lib/use-is-mobile.js +21 -0
- package/dist/lib/use-is-mobile.js.map +1 -0
- package/dist/lib/use-isomorphic-layout-effect.js.map +1 -0
- package/dist/lib/use-lazy-ref.js.map +1 -0
- package/dist/lib/visually-hidden-input.d.ts +12 -0
- package/dist/lib/visually-hidden-input.js +105 -0
- package/dist/lib/visually-hidden-input.js.map +1 -0
- package/package.json +5 -1
- package/styles.css +19 -0
- package/dist/data-grid/compose-refs.js.map +0 -1
- package/dist/data-grid/use-as-ref.js.map +0 -1
- package/dist/data-grid/use-isomorphic-layout-effect.js.map +0 -1
- package/dist/data-grid/use-lazy-ref.js.map +0 -1
- /package/dist/{data-grid → lib}/compose-refs.d.ts +0 -0
- /package/dist/{data-grid → lib}/compose-refs.js +0 -0
- /package/dist/{data-grid → lib}/use-as-ref.d.ts +0 -0
- /package/dist/{data-grid → lib}/use-as-ref.js +0 -0
- /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.d.ts +0 -0
- /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.js +0 -0
- /package/dist/{data-grid → lib}/use-lazy-ref.d.ts +0 -0
- /package/dist/{data-grid → lib}/use-lazy-ref.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/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"]}
|
|
@@ -22,7 +22,7 @@ import { Skeleton } from "../components/ui/skeleton.js";
|
|
|
22
22
|
import { Textarea } from "../components/ui/textarea.js";
|
|
23
23
|
import { cn } from "../lib/cn.js";
|
|
24
24
|
import { DataGridCellWrapper } from "./data-grid-cell-wrapper.js";
|
|
25
|
-
import {
|
|
25
|
+
import { useDataGridMessages } from "./data-grid-locale.js";
|
|
26
26
|
import { toastError, toastMessage } from "./toast.js";
|
|
27
27
|
import { useBadgeOverflow } from "./use-badge-overflow.js";
|
|
28
28
|
import { useDebouncedCallback } from "./use-debounced-callback.js";
|
|
@@ -191,6 +191,7 @@ function LongTextCell({
|
|
|
191
191
|
isActiveSearchMatch,
|
|
192
192
|
readOnly
|
|
193
193
|
}) {
|
|
194
|
+
const msg = useDataGridMessages();
|
|
194
195
|
const initialValue = cell.getValue();
|
|
195
196
|
const [value, setValue] = React.useState(initialValue ?? "");
|
|
196
197
|
const textareaRef = React.useRef(null);
|
|
@@ -468,6 +469,7 @@ function UrlCell({
|
|
|
468
469
|
isActiveSearchMatch,
|
|
469
470
|
readOnly
|
|
470
471
|
}) {
|
|
472
|
+
const msg = useDataGridMessages();
|
|
471
473
|
const initialValue = cell.getValue();
|
|
472
474
|
const [value, setValue] = React.useState(initialValue ?? "");
|
|
473
475
|
const cellRef = React.useRef(null);
|
|
@@ -559,7 +561,7 @@ function UrlCell({
|
|
|
559
561
|
}
|
|
560
562
|
event.stopPropagation();
|
|
561
563
|
},
|
|
562
|
-
[isEditing, value]
|
|
564
|
+
[isEditing, value, msg]
|
|
563
565
|
);
|
|
564
566
|
React.useEffect(() => {
|
|
565
567
|
const wasEditing = prevIsEditingRef.current;
|
|
@@ -839,6 +841,7 @@ function MultiSelectCell({
|
|
|
839
841
|
isActiveSearchMatch,
|
|
840
842
|
readOnly
|
|
841
843
|
}) {
|
|
844
|
+
const msg = useDataGridMessages();
|
|
842
845
|
const cellValue = React.useMemo(() => {
|
|
843
846
|
const value = cell.getValue();
|
|
844
847
|
return value ?? [];
|
|
@@ -1169,6 +1172,7 @@ function FileCell({
|
|
|
1169
1172
|
isActiveSearchMatch,
|
|
1170
1173
|
readOnly
|
|
1171
1174
|
}) {
|
|
1175
|
+
const msg = useDataGridMessages();
|
|
1172
1176
|
const cellValue = React.useMemo(() => cell.getValue() ?? [], [cell]);
|
|
1173
1177
|
const cellKey = getCellKey(rowIndex, columnId);
|
|
1174
1178
|
const prevCellKeyRef = React.useRef(cellKey);
|
|
@@ -1232,7 +1236,7 @@ function FileCell({
|
|
|
1232
1236
|
}
|
|
1233
1237
|
return null;
|
|
1234
1238
|
},
|
|
1235
|
-
[maxFileSize, acceptedTypes]
|
|
1239
|
+
[maxFileSize, acceptedTypes, msg]
|
|
1236
1240
|
);
|
|
1237
1241
|
const addFiles = React.useCallback(
|
|
1238
1242
|
async (newFiles, skipUpload = false) => {
|
|
@@ -1335,7 +1339,7 @@ function FileCell({
|
|
|
1335
1339
|
}
|
|
1336
1340
|
}
|
|
1337
1341
|
},
|
|
1338
|
-
[files, maxFiles, validateFile, tableMeta, rowIndex, columnId, readOnly, isPending]
|
|
1342
|
+
[files, maxFiles, validateFile, tableMeta, rowIndex, columnId, readOnly, isPending, msg]
|
|
1339
1343
|
);
|
|
1340
1344
|
const removeFile = React.useCallback(
|
|
1341
1345
|
async (fileId) => {
|
|
@@ -1373,7 +1377,7 @@ function FileCell({
|
|
|
1373
1377
|
});
|
|
1374
1378
|
tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: updatedFiles });
|
|
1375
1379
|
},
|
|
1376
|
-
[files, tableMeta, rowIndex, columnId, readOnly, isPending]
|
|
1380
|
+
[files, tableMeta, rowIndex, columnId, readOnly, isPending, msg]
|
|
1377
1381
|
);
|
|
1378
1382
|
const clearAll = React.useCallback(async () => {
|
|
1379
1383
|
if (readOnly || isPending) return;
|
|
@@ -1401,7 +1405,7 @@ function FileCell({
|
|
|
1401
1405
|
setFiles([]);
|
|
1402
1406
|
setDeletingFiles(/* @__PURE__ */ new Set());
|
|
1403
1407
|
tableMeta?.onDataUpdate?.({ rowIndex, columnId, value: [] });
|
|
1404
|
-
}, [files, tableMeta, rowIndex, columnId, readOnly, isPending]);
|
|
1408
|
+
}, [files, tableMeta, rowIndex, columnId, readOnly, isPending, msg]);
|
|
1405
1409
|
const onCellDragEnter = React.useCallback((event) => {
|
|
1406
1410
|
event.preventDefault();
|
|
1407
1411
|
event.stopPropagation();
|