@comitor/ui 3.0.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 +5 -1
- package/dist/components/composite/toast.d.ts +2 -1
- package/dist/components/composite/toast.js +3 -9
- package/dist/components/composite/toast.js.map +1 -1
- package/dist/components/ui/action-bar.js +45 -1
- package/dist/components/ui/action-bar.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/kanban.js +9 -3
- package/dist/components/ui/kanban.js.map +1 -1
- package/dist/components/ui/key-value.js +70 -76
- package/dist/components/ui/key-value.js.map +1 -1
- package/dist/components/ui/mask-input-constants.js +31 -71
- package/dist/components/ui/mask-input-constants.js.map +1 -1
- package/dist/components/ui/mask-input.js +18 -20
- package/dist/components/ui/mask-input.js.map +1 -1
- package/dist/components/ui/phone-input.js +5 -2
- package/dist/components/ui/phone-input.js.map +1 -1
- package/dist/components/ui/rating.js +4 -2
- package/dist/components/ui/rating.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/toggle-group.js +12 -1
- package/dist/components/ui/toggle-group.js.map +1 -1
- package/dist/components/ui/tour.js +3 -2
- package/dist/components/ui/tour.js.map +1 -1
- package/dist/data-grid/use-data-grid.d.ts +2 -2
- package/dist/i18n/locale-provider.d.ts +18 -0
- package/dist/i18n/locale-provider.js.map +1 -1
- package/dist/i18n/namespaces/rating.js +2 -1
- package/dist/i18n/namespaces/rating.js.map +1 -1
- package/dist/i18n/namespaces/shell.d.ts +4 -0
- package/dist/i18n/namespaces/shell.js +1 -1
- package/dist/i18n/namespaces/shell.js.map +1 -1
- package/dist/i18n/namespaces/toast.d.ts +3 -2
- package/dist/i18n/namespaces/toast.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/display-axis-provider.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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 { 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 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 = 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
|
+
{"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"]}
|
|
@@ -106,7 +106,7 @@ declare function useDataGrid<TData>({ data, columns, rowHeight: rowHeightProp, o
|
|
|
106
106
|
pasteDialog: PasteDialogState;
|
|
107
107
|
onRowAdd: ((event?: React.MouseEvent<HTMLDivElement>) => Promise<void>) | undefined;
|
|
108
108
|
adjustLayout: boolean;
|
|
109
|
-
locale: {
|
|
109
|
+
locale: "en" | "vi" | {
|
|
110
110
|
grid: {
|
|
111
111
|
label: string;
|
|
112
112
|
addRow: string;
|
|
@@ -381,7 +381,7 @@ declare function useDataGrid<TData>({ data, columns, rowHeight: rowHeightProp, o
|
|
|
381
381
|
shortcuts: string;
|
|
382
382
|
};
|
|
383
383
|
};
|
|
384
|
-
} |
|
|
384
|
+
} | undefined;
|
|
385
385
|
};
|
|
386
386
|
|
|
387
387
|
export { type UseDataGridProps, useDataGrid };
|
|
@@ -86,11 +86,29 @@ interface ComitorLocaleProviderProps {
|
|
|
86
86
|
* Ghi đè một phần bảng chữ (theo namespace), hoặc cấp nguyên một bảng cho ngôn ngữ không dựng sẵn.
|
|
87
87
|
* ⚠ Giữ tham chiếu ỔN ĐỊNH (hằng module hoặc `useMemo`) — object mới mỗi lượt render làm mọi
|
|
88
88
|
* component đọc chữ render lại.
|
|
89
|
+
* ⚠ Có khoá là HÀM (vd `tablePagination.range`) thì object này không đi qua ranh giới server →
|
|
90
|
+
* client được: khai nó trong một component `"use client"` của app, đừng truyền từ root layout.
|
|
89
91
|
*/
|
|
90
92
|
messages?: ComitorMessagesOverride;
|
|
91
93
|
/**
|
|
92
94
|
* Locale date-fns thay cho mặc định (`enUS` cho mọi mã không phải tiếng Việt). Cần khi app muốn
|
|
93
95
|
* định dạng kiểu Anh (`enGB`) hay một ngôn ngữ date-fns có sẵn khác.
|
|
96
|
+
*
|
|
97
|
+
* ⚠ Locale date-fns là object toàn HÀM — root layout của Next là Server Component, và truyền nó
|
|
98
|
+
* thẳng từ đó làm trang lỗi 500 ("Functions cannot be passed directly to Client Components"; đo
|
|
99
|
+
* trên Next 16 ở comitor-ds, 3.0.0). Đặt provider trong một component `"use client"` của app rồi
|
|
100
|
+
* render component đó từ layout:
|
|
101
|
+
*
|
|
102
|
+
* ```tsx
|
|
103
|
+
* "use client";
|
|
104
|
+
* export function ComitorLocale({ locale, children }: { locale: string; children: React.ReactNode }) {
|
|
105
|
+
* return (
|
|
106
|
+
* <ComitorLocaleProvider locale={locale} dateFnsLocale={locale === "en-GB" ? enGB : undefined}>
|
|
107
|
+
* {children}
|
|
108
|
+
* </ComitorLocaleProvider>
|
|
109
|
+
* );
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
94
112
|
*/
|
|
95
113
|
dateFnsLocale?: Locale;
|
|
96
114
|
children: React.ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/i18n/locale-provider.tsx"],"sourcesContent":["\"use client\";\n\nimport type { Locale as DateFnsLocale } from \"date-fns\";\nimport { enUS, vi } from \"date-fns/locale\";\nimport * as React from \"react\";\nimport { type ComitorLanguage, DEFAULT_COMITOR_LOCALE, getComitorLanguage } from \"./locale.js\";\nimport { type ComitorMessages, type ComitorMessagesOverride, getComitorMessages } from \"./messages.js\";\n\n/**\n * Ngôn ngữ của CẢ gói, đi xuống cây bằng context — một lần ở root, mọi component đọc theo.\n *\n * ─ THỨ TỰ ƯU TIÊN, cho mọi chuỗi và mọi định dạng ─\n * prop của chính component (`labels`, `placeholder`, `locale`…) → provider gần nhất → `\"en\"`.\n * Không có provider thì gói chạy tiếng Anh — đúng cho app quốc tế, và đúng cho Storybook/test.\n *\n * ─ APP COMITOR: LUÔN truyền ngôn ngữ từ thiết lập của NGƯỜI DÙNG ─\n * Ngôn ngữ của app Comitor sống ở hồ sơ Account (gieo vào cookie, đọc bằng `next-intl`). Bọc một\n * lần ở root layout, TRONG `NextIntlClientProvider`:\n *\n * ```tsx\n * const locale = await getLocale(); // next-intl/server\n * <ComitorLocaleProvider locale={locale}>{children}</ComitorLocaleProvider>\n * ```\n *\n * ⚠ Đừng để mặc định: từ `3.0.0` mặc định là tiếng Anh, nên một app Comitor quên dòng này sẽ hiện\n * nút \"Cancel\" giữa màn hình tiếng Việt — không lỗi, không cảnh báo.\n */\n\ninterface ComitorLocaleContextValue {\n /** Mã BCP 47 app truyền vào — dùng cho mọi phép định dạng `Intl`. */\n locale: string;\n /** Bảng chữ dựng sẵn được chọn từ `locale`. */\n language: ComitorLanguage;\n /** Bảng chữ đầy đủ đang có hiệu lực (đã trộn phần ghi đè của app). */\n messages: ComitorMessages;\n /** Locale của date-fns cho các component định dạng ngày bằng date-fns. */\n dateFnsLocale: DateFnsLocale;\n}\n\nconst DATE_FNS_LOCALES: Record<ComitorLanguage, DateFnsLocale> = { en: enUS, vi };\n\nfunction buildContextValue(\n locale: string,\n override?: ComitorMessagesOverride,\n dateFnsLocale?: DateFnsLocale\n): ComitorLocaleContextValue {\n const language = getComitorLanguage(locale);\n return {\n locale,\n language,\n messages: getComitorMessages(locale, override),\n dateFnsLocale: dateFnsLocale ?? DATE_FNS_LOCALES[language]\n };\n}\n\nconst DEFAULT_CONTEXT_VALUE = buildContextValue(DEFAULT_COMITOR_LOCALE);\n\nconst ComitorLocaleContext = React.createContext<ComitorLocaleContextValue>(DEFAULT_CONTEXT_VALUE);\n\nexport interface ComitorLocaleProviderProps {\n /**\n * Mã BCP 47: `\"vi\"`, `\"en\"`, `\"en-GB\"`… Chọn bảng chữ dựng sẵn (xem `getComitorLanguage`) VÀ\n * quyết định định dạng ngày/giờ/số. App Comitor truyền `getLocale()` / `useLocale()` của next-intl.\n */\n locale?: string;\n /**\n * Ghi đè một phần bảng chữ (theo namespace), hoặc cấp nguyên một bảng cho ngôn ngữ không dựng sẵn.\n * ⚠ Giữ tham chiếu ỔN ĐỊNH (hằng module hoặc `useMemo`) — object mới mỗi lượt render làm mọi\n * component đọc chữ render lại.\n */\n messages?: ComitorMessagesOverride;\n /**\n * Locale date-fns thay cho mặc định (`enUS` cho mọi mã không phải tiếng Việt). Cần khi app muốn\n * định dạng kiểu Anh (`enGB`) hay một ngôn ngữ date-fns có sẵn khác.\n */\n dateFnsLocale?: DateFnsLocale;\n children: React.ReactNode;\n}\n\nexport function ComitorLocaleProvider({\n locale = DEFAULT_COMITOR_LOCALE,\n messages,\n dateFnsLocale,\n children\n}: ComitorLocaleProviderProps) {\n const value = React.useMemo(\n () => buildContextValue(locale, messages, dateFnsLocale),\n [locale, messages, dateFnsLocale]\n );\n return <ComitorLocaleContext.Provider value={value}>{children}</ComitorLocaleContext.Provider>;\n}\n\n/** Ngôn ngữ đang có hiệu lực: `locale` (cho `Intl`), `language`, bảng chữ, locale date-fns. */\nexport function useComitorLocale(): ComitorLocaleContextValue {\n return React.useContext(ComitorLocaleContext);\n}\n\n/** Chữ của MỘT namespace theo provider gần nhất (hoặc tiếng Anh khi không có provider). */\nexport function useComitorMessages<K extends keyof ComitorMessages>(namespace: K): ComitorMessages[K] {\n return React.useContext(ComitorLocaleContext).messages[namespace];\n}\n\n/**\n * Chữ của một namespace, đã trộn `labels` của chính component lên trên — đúng thứ tự ưu tiên\n * prop → provider → `\"en\"`. Dành cho component có prop `labels?: Partial<…>`.\n */\nexport function useComitorLabels<K extends keyof ComitorMessages>(\n namespace: K,\n labels?: Partial<ComitorMessages[K]>\n): ComitorMessages[K] {\n const base = useComitorMessages(namespace);\n return React.useMemo(() => (labels ? { ...base, ...labels } : base), [base, labels]);\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../src/i18n/locale-provider.tsx"],"sourcesContent":["\"use client\";\n\nimport type { Locale as DateFnsLocale } from \"date-fns\";\nimport { enUS, vi } from \"date-fns/locale\";\nimport * as React from \"react\";\nimport { type ComitorLanguage, DEFAULT_COMITOR_LOCALE, getComitorLanguage } from \"./locale.js\";\nimport { type ComitorMessages, type ComitorMessagesOverride, getComitorMessages } from \"./messages.js\";\n\n/**\n * Ngôn ngữ của CẢ gói, đi xuống cây bằng context — một lần ở root, mọi component đọc theo.\n *\n * ─ THỨ TỰ ƯU TIÊN, cho mọi chuỗi và mọi định dạng ─\n * prop của chính component (`labels`, `placeholder`, `locale`…) → provider gần nhất → `\"en\"`.\n * Không có provider thì gói chạy tiếng Anh — đúng cho app quốc tế, và đúng cho Storybook/test.\n *\n * ─ APP COMITOR: LUÔN truyền ngôn ngữ từ thiết lập của NGƯỜI DÙNG ─\n * Ngôn ngữ của app Comitor sống ở hồ sơ Account (gieo vào cookie, đọc bằng `next-intl`). Bọc một\n * lần ở root layout, TRONG `NextIntlClientProvider`:\n *\n * ```tsx\n * const locale = await getLocale(); // next-intl/server\n * <ComitorLocaleProvider locale={locale}>{children}</ComitorLocaleProvider>\n * ```\n *\n * ⚠ Đừng để mặc định: từ `3.0.0` mặc định là tiếng Anh, nên một app Comitor quên dòng này sẽ hiện\n * nút \"Cancel\" giữa màn hình tiếng Việt — không lỗi, không cảnh báo.\n */\n\ninterface ComitorLocaleContextValue {\n /** Mã BCP 47 app truyền vào — dùng cho mọi phép định dạng `Intl`. */\n locale: string;\n /** Bảng chữ dựng sẵn được chọn từ `locale`. */\n language: ComitorLanguage;\n /** Bảng chữ đầy đủ đang có hiệu lực (đã trộn phần ghi đè của app). */\n messages: ComitorMessages;\n /** Locale của date-fns cho các component định dạng ngày bằng date-fns. */\n dateFnsLocale: DateFnsLocale;\n}\n\nconst DATE_FNS_LOCALES: Record<ComitorLanguage, DateFnsLocale> = { en: enUS, vi };\n\nfunction buildContextValue(\n locale: string,\n override?: ComitorMessagesOverride,\n dateFnsLocale?: DateFnsLocale\n): ComitorLocaleContextValue {\n const language = getComitorLanguage(locale);\n return {\n locale,\n language,\n messages: getComitorMessages(locale, override),\n dateFnsLocale: dateFnsLocale ?? DATE_FNS_LOCALES[language]\n };\n}\n\nconst DEFAULT_CONTEXT_VALUE = buildContextValue(DEFAULT_COMITOR_LOCALE);\n\nconst ComitorLocaleContext = React.createContext<ComitorLocaleContextValue>(DEFAULT_CONTEXT_VALUE);\n\nexport interface ComitorLocaleProviderProps {\n /**\n * Mã BCP 47: `\"vi\"`, `\"en\"`, `\"en-GB\"`… Chọn bảng chữ dựng sẵn (xem `getComitorLanguage`) VÀ\n * quyết định định dạng ngày/giờ/số. App Comitor truyền `getLocale()` / `useLocale()` của next-intl.\n */\n locale?: string;\n /**\n * Ghi đè một phần bảng chữ (theo namespace), hoặc cấp nguyên một bảng cho ngôn ngữ không dựng sẵn.\n * ⚠ Giữ tham chiếu ỔN ĐỊNH (hằng module hoặc `useMemo`) — object mới mỗi lượt render làm mọi\n * component đọc chữ render lại.\n * ⚠ Có khoá là HÀM (vd `tablePagination.range`) thì object này không đi qua ranh giới server →\n * client được: khai nó trong một component `\"use client\"` của app, đừng truyền từ root layout.\n */\n messages?: ComitorMessagesOverride;\n /**\n * Locale date-fns thay cho mặc định (`enUS` cho mọi mã không phải tiếng Việt). Cần khi app muốn\n * định dạng kiểu Anh (`enGB`) hay một ngôn ngữ date-fns có sẵn khác.\n *\n * ⚠ Locale date-fns là object toàn HÀM — root layout của Next là Server Component, và truyền nó\n * thẳng từ đó làm trang lỗi 500 (\"Functions cannot be passed directly to Client Components\"; đo\n * trên Next 16 ở comitor-ds, 3.0.0). Đặt provider trong một component `\"use client\"` của app rồi\n * render component đó từ layout:\n *\n * ```tsx\n * \"use client\";\n * export function ComitorLocale({ locale, children }: { locale: string; children: React.ReactNode }) {\n * return (\n * <ComitorLocaleProvider locale={locale} dateFnsLocale={locale === \"en-GB\" ? enGB : undefined}>\n * {children}\n * </ComitorLocaleProvider>\n * );\n * }\n * ```\n */\n dateFnsLocale?: DateFnsLocale;\n children: React.ReactNode;\n}\n\nexport function ComitorLocaleProvider({\n locale = DEFAULT_COMITOR_LOCALE,\n messages,\n dateFnsLocale,\n children\n}: ComitorLocaleProviderProps) {\n const value = React.useMemo(\n () => buildContextValue(locale, messages, dateFnsLocale),\n [locale, messages, dateFnsLocale]\n );\n return <ComitorLocaleContext.Provider value={value}>{children}</ComitorLocaleContext.Provider>;\n}\n\n/** Ngôn ngữ đang có hiệu lực: `locale` (cho `Intl`), `language`, bảng chữ, locale date-fns. */\nexport function useComitorLocale(): ComitorLocaleContextValue {\n return React.useContext(ComitorLocaleContext);\n}\n\n/** Chữ của MỘT namespace theo provider gần nhất (hoặc tiếng Anh khi không có provider). */\nexport function useComitorMessages<K extends keyof ComitorMessages>(namespace: K): ComitorMessages[K] {\n return React.useContext(ComitorLocaleContext).messages[namespace];\n}\n\n/**\n * Chữ của một namespace, đã trộn `labels` của chính component lên trên — đúng thứ tự ưu tiên\n * prop → provider → `\"en\"`. Dành cho component có prop `labels?: Partial<…>`.\n */\nexport function useComitorLabels<K extends keyof ComitorMessages>(\n namespace: K,\n labels?: Partial<ComitorMessages[K]>\n): ComitorMessages[K] {\n const base = useComitorMessages(namespace);\n return React.useMemo(() => (labels ? { ...base, ...labels } : base), [base, labels]);\n}\n"],"mappings":";AA2GS;AAxGT,SAAS,MAAM,UAAU;AACzB,YAAY,WAAW;AACvB,SAA+B,wBAAwB,0BAA0B;AACjF,SAA6D,0BAA0B;AAiCvF,MAAM,mBAA2D,EAAE,IAAI,MAAM,GAAG;AAEhF,SAAS,kBACP,QACA,UACA,eAC2B;AAC3B,QAAM,WAAW,mBAAmB,MAAM;AAC1C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,UAAU,mBAAmB,QAAQ,QAAQ;AAAA,IAC7C,eAAe,iBAAiB,iBAAiB,QAAQ;AAAA,EAC3D;AACF;AAEA,MAAM,wBAAwB,kBAAkB,sBAAsB;AAEtE,MAAM,uBAAuB,MAAM,cAAyC,qBAAqB;AAwC1F,SAAS,sBAAsB;AAAA,EACpC,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AACF,GAA+B;AAC7B,QAAM,QAAQ,MAAM;AAAA,IAClB,MAAM,kBAAkB,QAAQ,UAAU,aAAa;AAAA,IACvD,CAAC,QAAQ,UAAU,aAAa;AAAA,EAClC;AACA,SAAO,oBAAC,qBAAqB,UAArB,EAA8B,OAAe,UAAS;AAChE;AAGO,SAAS,mBAA8C;AAC5D,SAAO,MAAM,WAAW,oBAAoB;AAC9C;AAGO,SAAS,mBAAoD,WAAkC;AACpG,SAAO,MAAM,WAAW,oBAAoB,EAAE,SAAS,SAAS;AAClE;AAMO,SAAS,iBACd,WACA,QACoB;AACpB,QAAM,OAAO,mBAAmB,SAAS;AACzC,SAAO,MAAM,QAAQ,MAAO,SAAS,EAAE,GAAG,MAAM,GAAG,OAAO,IAAI,MAAO,CAAC,MAAM,MAAM,CAAC;AACrF;","names":[]}
|
|
@@ -2,7 +2,8 @@ const ratingEn = {
|
|
|
2
2
|
itemLabel: (value) => value === 1 ? "1 star" : `${value} stars`
|
|
3
3
|
};
|
|
4
4
|
const ratingVi = {
|
|
5
|
-
|
|
5
|
+
// Từ 3.0.1 mục đang chọn của giá trị lẻ mang tên theo giá trị thật (4,5) — viết dấu phẩy thập phân.
|
|
6
|
+
itemLabel: (value) => `${value.toLocaleString("vi-VN")} sao`
|
|
6
7
|
};
|
|
7
8
|
export {
|
|
8
9
|
ratingEn,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/i18n/namespaces/rating.ts"],"sourcesContent":["/** Chữ của `Rating` / `RatingItem`. */\nexport interface RatingMessages {\n /**\n * Tên của TỪNG ngôi sao cho trình đọc màn hình — prop `itemLabel` của `Rating`.\n * HÀM vì có số chèn vào giữa câu (và tiếng Anh còn phải chia số ít/số nhiều).\n */\n itemLabel: (value: number, max: number) => string;\n}\n\nexport const ratingEn: RatingMessages = {\n itemLabel: (value) => (value === 1 ? \"1 star\" : `${value} stars`)\n};\n\nexport const ratingVi: RatingMessages = {\n itemLabel: (value) => `${value} sao`\n};\n"],"mappings":"AASO,MAAM,WAA2B;AAAA,EACtC,WAAW,CAAC,UAAW,UAAU,IAAI,WAAW,GAAG,KAAK;AAC1D;AAEO,MAAM,WAA2B;AAAA,
|
|
1
|
+
{"version":3,"sources":["../../../src/i18n/namespaces/rating.ts"],"sourcesContent":["/** Chữ của `Rating` / `RatingItem`. */\nexport interface RatingMessages {\n /**\n * Tên của TỪNG ngôi sao cho trình đọc màn hình — prop `itemLabel` của `Rating`.\n * HÀM vì có số chèn vào giữa câu (và tiếng Anh còn phải chia số ít/số nhiều).\n */\n itemLabel: (value: number, max: number) => string;\n}\n\nexport const ratingEn: RatingMessages = {\n itemLabel: (value) => (value === 1 ? \"1 star\" : `${value} stars`)\n};\n\nexport const ratingVi: RatingMessages = {\n // Từ 3.0.1 mục đang chọn của giá trị lẻ mang tên theo giá trị thật (4,5) — viết dấu phẩy thập phân.\n itemLabel: (value) => `${value.toLocaleString(\"vi-VN\")} sao`\n};\n"],"mappings":"AASO,MAAM,WAA2B;AAAA,EACtC,WAAW,CAAC,UAAW,UAAU,IAAI,WAAW,GAAG,KAAK;AAC1D;AAEO,MAAM,WAA2B;AAAA;AAAA,EAEtC,WAAW,CAAC,UAAU,GAAG,MAAM,eAAe,OAAO,CAAC;AACxD;","names":[]}
|
|
@@ -10,6 +10,10 @@ import 'react';
|
|
|
10
10
|
*
|
|
11
11
|
* ⚠ `collapseSidebarShort` PHẢI là một phần của `collapseSidebar` ở MỌI ngôn ngữ (WCAG 2.5.3):
|
|
12
12
|
* "Collapse" ⊂ "Collapse sidebar", "Thu gọn" ⊂ "Thu gọn thanh bên".
|
|
13
|
+
* ⚠ Cùng luật cho cặp `apps` (tooltip HIỆN RA của nút mở launcher) ⊂ `appLauncherLabel` (tên cho
|
|
14
|
+
* trình đọc màn hình): "Ứng dụng" ⊂ "Mở danh sách ứng dụng". Bảng tiếng Anh của `3.0.0` hỏng đúng
|
|
15
|
+
* chỗ này — "Apps" không nằm trong "Open app launcher", nên người điều khiển bằng giọng nói đọc
|
|
16
|
+
* chữ họ THẤY ("click Apps") mà không khớp nút nào. Sửa ở `3.0.1`.
|
|
13
17
|
*/
|
|
14
18
|
declare const shellEn: ShellLabels;
|
|
15
19
|
declare const shellVi: ShellLabels;
|
|
@@ -17,7 +17,7 @@ const shellEn = {
|
|
|
17
17
|
inviteMembers: "Invite members",
|
|
18
18
|
members: (count) => `${count} ${count === 1 ? "member" : "members"}`,
|
|
19
19
|
apps: "Apps",
|
|
20
|
-
appLauncherLabel: "Open
|
|
20
|
+
appLauncherLabel: "Open apps list",
|
|
21
21
|
myApps: "Your apps",
|
|
22
22
|
discoverApps: "Discover more",
|
|
23
23
|
locked: "Locked",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/i18n/namespaces/shell.ts"],"sourcesContent":["import type { ShellLabels } from \"../../shell/types.js\";\n\n/**\n * Chữ của cả tầng shell — `ShellProvider`/`AppShell` và mọi thứ đọc `useShell().labels`\n * (`Sidebar`, `AppHeader`, `MobileMenu`, `WorkspaceSwitcher`, `AppLauncher`, `UserMenu`), cộng\n * `ThemeToggle` và `NotificationsButton` khi đứng ngoài shell. Hình dạng + chú thích từng khoá:\n * `ShellLabels`.\n *\n * ⚠ `collapseSidebarShort` PHẢI là một phần của `collapseSidebar` ở MỌI ngôn ngữ (WCAG 2.5.3):\n * \"Collapse\" ⊂ \"Collapse sidebar\", \"Thu gọn\" ⊂ \"Thu gọn thanh bên\".\n */\nexport const shellEn: ShellLabels = {\n collapseSidebar: \"Collapse sidebar\",\n collapseSidebarShort: \"Collapse\",\n expandSidebar: \"Expand sidebar\",\n openMenu: \"Open menu\",\n closeMenu: \"Close menu\",\n search: \"Search\",\n searchPlaceholder: \"Search…\",\n notifications: \"Notifications\",\n navigationLabel: \"Main navigation\",\n workspace: \"Workspace\",\n workspaceSwitcherLabel: \"Switch workspace\",\n workspaceSearchPlaceholder: \"Search workspaces…\",\n workspaceEmpty: \"No workspaces found\",\n createWorkspace: \"Create workspace\",\n workspaceSettings: \"Workspace settings\",\n inviteMembers: \"Invite members\",\n members: (count) => `${count} ${count === 1 ? \"member\" : \"members\"}`,\n apps: \"Apps\",\n appLauncherLabel: \"Open
|
|
1
|
+
{"version":3,"sources":["../../../src/i18n/namespaces/shell.ts"],"sourcesContent":["import type { ShellLabels } from \"../../shell/types.js\";\n\n/**\n * Chữ của cả tầng shell — `ShellProvider`/`AppShell` và mọi thứ đọc `useShell().labels`\n * (`Sidebar`, `AppHeader`, `MobileMenu`, `WorkspaceSwitcher`, `AppLauncher`, `UserMenu`), cộng\n * `ThemeToggle` và `NotificationsButton` khi đứng ngoài shell. Hình dạng + chú thích từng khoá:\n * `ShellLabels`.\n *\n * ⚠ `collapseSidebarShort` PHẢI là một phần của `collapseSidebar` ở MỌI ngôn ngữ (WCAG 2.5.3):\n * \"Collapse\" ⊂ \"Collapse sidebar\", \"Thu gọn\" ⊂ \"Thu gọn thanh bên\".\n * ⚠ Cùng luật cho cặp `apps` (tooltip HIỆN RA của nút mở launcher) ⊂ `appLauncherLabel` (tên cho\n * trình đọc màn hình): \"Ứng dụng\" ⊂ \"Mở danh sách ứng dụng\". Bảng tiếng Anh của `3.0.0` hỏng đúng\n * chỗ này — \"Apps\" không nằm trong \"Open app launcher\", nên người điều khiển bằng giọng nói đọc\n * chữ họ THẤY (\"click Apps\") mà không khớp nút nào. Sửa ở `3.0.1`.\n */\nexport const shellEn: ShellLabels = {\n collapseSidebar: \"Collapse sidebar\",\n collapseSidebarShort: \"Collapse\",\n expandSidebar: \"Expand sidebar\",\n openMenu: \"Open menu\",\n closeMenu: \"Close menu\",\n search: \"Search\",\n searchPlaceholder: \"Search…\",\n notifications: \"Notifications\",\n navigationLabel: \"Main navigation\",\n workspace: \"Workspace\",\n workspaceSwitcherLabel: \"Switch workspace\",\n workspaceSearchPlaceholder: \"Search workspaces…\",\n workspaceEmpty: \"No workspaces found\",\n createWorkspace: \"Create workspace\",\n workspaceSettings: \"Workspace settings\",\n inviteMembers: \"Invite members\",\n members: (count) => `${count} ${count === 1 ? \"member\" : \"members\"}`,\n apps: \"Apps\",\n appLauncherLabel: \"Open apps list\",\n myApps: \"Your apps\",\n discoverApps: \"Discover more\",\n locked: \"Locked\",\n lockedHint: \"Not included in your current plan\",\n account: \"Account\",\n profile: \"Profile\",\n settings: \"Settings\",\n appearance: \"Appearance\",\n help: \"Help\",\n logout: \"Log out\",\n themeLight: \"Light\",\n themeDark: \"Dark\",\n themeSystem: \"System\",\n toggleTheme: \"Toggle theme\"\n};\n\nexport const shellVi: ShellLabels = {\n collapseSidebar: \"Thu gọn thanh bên\",\n collapseSidebarShort: \"Thu gọn\",\n expandSidebar: \"Mở rộng thanh bên\",\n openMenu: \"Mở menu\",\n closeMenu: \"Đóng menu\",\n search: \"Tìm kiếm\",\n searchPlaceholder: \"Tìm kiếm…\",\n notifications: \"Thông báo\",\n navigationLabel: \"Điều hướng chính\",\n workspace: \"Không gian làm việc\",\n workspaceSwitcherLabel: \"Đổi không gian làm việc\",\n workspaceSearchPlaceholder: \"Tìm không gian làm việc…\",\n workspaceEmpty: \"Không tìm thấy không gian làm việc\",\n createWorkspace: \"Tạo không gian làm việc\",\n workspaceSettings: \"Cài đặt không gian làm việc\",\n inviteMembers: \"Mời thành viên\",\n members: (count) => `${count} thành viên`,\n apps: \"Ứng dụng\",\n appLauncherLabel: \"Mở danh sách ứng dụng\",\n myApps: \"Ứng dụng của bạn\",\n discoverApps: \"Khám phá thêm\",\n locked: \"Chưa mở khoá\",\n lockedHint: \"Ứng dụng chưa có trong gói hiện tại\",\n account: \"Tài khoản\",\n profile: \"Hồ sơ\",\n settings: \"Cài đặt\",\n appearance: \"Giao diện\",\n help: \"Trợ giúp\",\n logout: \"Đăng xuất\",\n themeLight: \"Sáng\",\n themeDark: \"Tối\",\n themeSystem: \"Hệ thống\",\n toggleTheme: \"Đổi giao diện\"\n};\n"],"mappings":"AAeO,MAAM,UAAuB;AAAA,EAClC,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,UAAU;AAAA,EACV,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,wBAAwB;AAAA,EACxB,4BAA4B;AAAA,EAC5B,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,SAAS,CAAC,UAAU,GAAG,KAAK,IAAI,UAAU,IAAI,WAAW,SAAS;AAAA,EAClE,MAAM;AAAA,EACN,kBAAkB;AAAA,EAClB,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,aAAa;AAAA,EACb,aAAa;AACf;AAEO,MAAM,UAAuB;AAAA,EAClC,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,UAAU;AAAA,EACV,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,wBAAwB;AAAA,EACxB,4BAA4B;AAAA,EAC5B,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,SAAS,CAAC,UAAU,GAAG,KAAK;AAAA,EAC5B,MAAM;AAAA,EACN,kBAAkB;AAAA,EAClB,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,aAAa;AAAA,EACb,aAAa;AACf;","names":[]}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Chữ của `toastPromise`.
|
|
3
3
|
*
|
|
4
4
|
* `toastPromise` là hàm THƯỜNG gọi từ event handler, ngoài lượt render — nó không tự đọc được
|
|
5
|
-
* `ComitorLocaleProvider`.
|
|
6
|
-
* ngôn ngữ hiện hành cho nó dùng; xem
|
|
5
|
+
* `ComitorLocaleProvider`. Toaster (luôn nằm trong cây, dưới provider — `Toaster` của entry chính
|
|
6
|
+
* hay `SonnerToaster` của `/shell`) ghi bảng này theo ngôn ngữ hiện hành cho nó dùng; xem
|
|
7
|
+
* `lib/toast-language.ts`.
|
|
7
8
|
*/
|
|
8
9
|
interface ToastMessages {
|
|
9
10
|
/** Chữ của toast lúc Promise còn chạy — `messages.loading` của `toastPromise` */
|