@comitor/ui 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +14 -6
  3. package/dist/components/ui/action-bar-constants.d.ts +31 -0
  4. package/dist/components/ui/action-bar-constants.js +9 -0
  5. package/dist/components/ui/action-bar-constants.js.map +1 -0
  6. package/dist/components/ui/action-bar.d.ts +67 -0
  7. package/dist/components/ui/action-bar.js +522 -0
  8. package/dist/components/ui/action-bar.js.map +1 -0
  9. package/dist/components/ui/badge-overflow.d.ts +30 -0
  10. package/dist/components/ui/badge-overflow.js +179 -0
  11. package/dist/components/ui/badge-overflow.js.map +1 -0
  12. package/dist/components/ui/banner.d.ts +79 -0
  13. package/dist/components/ui/banner.js +518 -0
  14. package/dist/components/ui/banner.js.map +1 -0
  15. package/dist/components/ui/circular-progress.d.ts +50 -0
  16. package/dist/components/ui/circular-progress.js +266 -0
  17. package/dist/components/ui/circular-progress.js.map +1 -0
  18. package/dist/components/ui/cropper-constants.d.ts +34 -0
  19. package/dist/components/ui/cropper-constants.js +10 -0
  20. package/dist/components/ui/cropper-constants.js.map +1 -0
  21. package/dist/components/ui/cropper.d.ts +106 -0
  22. package/dist/components/ui/cropper.js +1269 -0
  23. package/dist/components/ui/cropper.js.map +1 -0
  24. package/dist/components/ui/drawer.d.ts +1 -1
  25. package/dist/components/ui/drawer.js +2 -2
  26. package/dist/components/ui/drawer.js.map +1 -1
  27. package/dist/components/ui/kanban-constants.d.ts +61 -0
  28. package/dist/components/ui/kanban-constants.js +21 -0
  29. package/dist/components/ui/kanban-constants.js.map +1 -0
  30. package/dist/components/ui/kanban.d.ts +72 -0
  31. package/dist/components/ui/kanban.js +787 -0
  32. package/dist/components/ui/kanban.js.map +1 -0
  33. package/dist/components/ui/key-value-constants.d.ts +33 -0
  34. package/dist/components/ui/key-value-constants.js +11 -0
  35. package/dist/components/ui/key-value-constants.js.map +1 -0
  36. package/dist/components/ui/key-value.d.ts +96 -0
  37. package/dist/components/ui/key-value.js +641 -0
  38. package/dist/components/ui/key-value.js.map +1 -0
  39. package/dist/components/ui/mask-input-constants.d.ts +99 -0
  40. package/dist/components/ui/mask-input-constants.js +601 -0
  41. package/dist/components/ui/mask-input-constants.js.map +1 -0
  42. package/dist/components/ui/mask-input.d.ts +36 -0
  43. package/dist/components/ui/mask-input.js +558 -0
  44. package/dist/components/ui/mask-input.js.map +1 -0
  45. package/dist/components/ui/mention.d.ts +31 -0
  46. package/dist/components/ui/mention.js +128 -0
  47. package/dist/components/ui/mention.js.map +1 -0
  48. package/dist/components/ui/phone-input-constants.d.ts +54 -0
  49. package/dist/components/ui/phone-input-constants.js +9 -0
  50. package/dist/components/ui/phone-input-constants.js.map +1 -0
  51. package/dist/components/ui/phone-input.d.ts +85 -0
  52. package/dist/components/ui/phone-input.js +743 -0
  53. package/dist/components/ui/phone-input.js.map +1 -0
  54. package/dist/components/ui/rating.d.ts +56 -0
  55. package/dist/components/ui/rating.js +646 -0
  56. package/dist/components/ui/rating.js.map +1 -0
  57. package/dist/components/ui/responsive-dialog.d.ts +35 -0
  58. package/dist/components/ui/responsive-dialog.js +193 -0
  59. package/dist/components/ui/responsive-dialog.js.map +1 -0
  60. package/dist/components/ui/scroll-spy.d.ts +42 -0
  61. package/dist/components/ui/scroll-spy.js +334 -0
  62. package/dist/components/ui/scroll-spy.js.map +1 -0
  63. package/dist/components/ui/stepper-constants.d.ts +29 -0
  64. package/dist/components/ui/stepper-constants.js +9 -0
  65. package/dist/components/ui/stepper-constants.js.map +1 -0
  66. package/dist/components/ui/stepper.d.ts +80 -0
  67. package/dist/components/ui/stepper.js +902 -0
  68. package/dist/components/ui/stepper.js.map +1 -0
  69. package/dist/components/ui/tags-input.d.ts +41 -0
  70. package/dist/components/ui/tags-input.js +127 -0
  71. package/dist/components/ui/tags-input.js.map +1 -0
  72. package/dist/components/ui/time-picker-constants.d.ts +48 -0
  73. package/dist/components/ui/time-picker-constants.js +14 -0
  74. package/dist/components/ui/time-picker-constants.js.map +1 -0
  75. package/dist/components/ui/time-picker.d.ts +115 -0
  76. package/dist/components/ui/time-picker.js +1620 -0
  77. package/dist/components/ui/time-picker.js.map +1 -0
  78. package/dist/components/ui/timeline.d.ts +32 -0
  79. package/dist/components/ui/timeline.js +559 -0
  80. package/dist/components/ui/timeline.js.map +1 -0
  81. package/dist/components/ui/tour-constants.d.ts +39 -0
  82. package/dist/components/ui/tour-constants.js +12 -0
  83. package/dist/components/ui/tour-constants.js.map +1 -0
  84. package/dist/components/ui/tour.d.ts +124 -0
  85. package/dist/components/ui/tour.js +1207 -0
  86. package/dist/components/ui/tour.js.map +1 -0
  87. package/dist/data-grid/data-grid-cell-wrapper.js +1 -1
  88. package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -1
  89. package/dist/data-grid/data-grid-context-menu.js +1 -1
  90. package/dist/data-grid/data-grid-context-menu.js.map +1 -1
  91. package/dist/data-grid/data-grid-paste-dialog.js +1 -1
  92. package/dist/data-grid/data-grid-paste-dialog.js.map +1 -1
  93. package/dist/data-grid/data-grid-row.js +1 -1
  94. package/dist/data-grid/data-grid-row.js.map +1 -1
  95. package/dist/data-grid/data-grid-search.js +1 -1
  96. package/dist/data-grid/data-grid-search.js.map +1 -1
  97. package/dist/data-grid/data-grid.js +1 -1
  98. package/dist/data-grid/data-grid.js.map +1 -1
  99. package/dist/data-grid/sortable.js +1 -1
  100. package/dist/data-grid/sortable.js.map +1 -1
  101. package/dist/data-grid/use-data-grid-undo-redo.js +2 -2
  102. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
  103. package/dist/data-grid/use-data-grid.js +3 -3
  104. package/dist/data-grid/use-data-grid.js.map +1 -1
  105. package/dist/index.d.ts +31 -0
  106. package/dist/index.js +42 -0
  107. package/dist/index.js.map +1 -1
  108. package/dist/lib/compose-refs.js.map +1 -0
  109. package/dist/lib/use-as-ref.js.map +1 -0
  110. package/dist/lib/use-is-mobile.d.ts +10 -0
  111. package/dist/lib/use-is-mobile.js +21 -0
  112. package/dist/lib/use-is-mobile.js.map +1 -0
  113. package/dist/lib/use-isomorphic-layout-effect.js.map +1 -0
  114. package/dist/lib/use-lazy-ref.js.map +1 -0
  115. package/dist/lib/visually-hidden-input.d.ts +12 -0
  116. package/dist/lib/visually-hidden-input.js +105 -0
  117. package/dist/lib/visually-hidden-input.js.map +1 -0
  118. package/package.json +5 -1
  119. package/styles.css +19 -0
  120. package/dist/data-grid/compose-refs.js.map +0 -1
  121. package/dist/data-grid/use-as-ref.js.map +0 -1
  122. package/dist/data-grid/use-isomorphic-layout-effect.js.map +0 -1
  123. package/dist/data-grid/use-lazy-ref.js.map +0 -1
  124. /package/dist/{data-grid → lib}/compose-refs.d.ts +0 -0
  125. /package/dist/{data-grid → lib}/compose-refs.js +0 -0
  126. /package/dist/{data-grid → lib}/use-as-ref.d.ts +0 -0
  127. /package/dist/{data-grid → lib}/use-as-ref.js +0 -0
  128. /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.d.ts +0 -0
  129. /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.js +0 -0
  130. /package/dist/{data-grid → lib}/use-lazy-ref.d.ts +0 -0
  131. /package/dist/{data-grid → lib}/use-lazy-ref.js +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ui/tour.tsx"],"sourcesContent":["\"use client\";\n\n/*\n * Port từ Dice UI (https://diceui.com/docs/components/tour) — MIT, © 2024 Sadman Sakib.\n * Giữ nguyên cấu trúc thượng nguồn để còn đối chiếu ngược được; mọi chỗ đổi đều có chú thích.\n */\n\nimport {\n autoUpdate,\n flip,\n hide,\n limitShift,\n type Middleware,\n offset,\n arrow as onArrow,\n type Placement,\n shift,\n useFloating\n} from \"@floating-ui/react-dom\";\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { ChevronLeft, ChevronRight, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useComposedRefs } from \"../../lib/compose-refs.js\";\nimport { useAsRef } from \"../../lib/use-as-ref.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { useLazyRef } from \"../../lib/use-lazy-ref.js\";\nimport { Button } from \"./button.js\";\nimport { DEFAULT_TOUR_LABELS, type TourLabels } from \"./tour-constants.js\";\n\nconst ROOT_NAME = \"Tour\";\nconst PORTAL_NAME = \"TourPortal\";\nconst STEP_NAME = \"TourStep\";\nconst ARROW_NAME = \"TourArrow\";\nconst HEADER_NAME = \"TourHeader\";\nconst TITLE_NAME = \"TourTitle\";\nconst DESCRIPTION_NAME = \"TourDescription\";\nconst CLOSE_NAME = \"TourClose\";\nconst PREV_NAME = \"TourPrev\";\nconst NEXT_NAME = \"TourNext\";\nconst SKIP_NAME = \"TourSkip\";\nconst STEP_COUNTER_NAME = \"TourStepCounter\";\nconst FOOTER_NAME = \"TourFooter\";\n\nconst POINTER_DOWN_OUTSIDE = \"tour.pointerDownOutside\";\nconst INTERACT_OUTSIDE = \"tour.interactOutside\";\nconst OPEN_AUTO_FOCUS = \"tour.openAutoFocus\";\nconst CLOSE_AUTO_FOCUS = \"tour.closeAutoFocus\";\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\nconst SIDE_OPTIONS = [\"top\", \"right\", \"bottom\", \"left\"] as const;\nconst ALIGN_OPTIONS = [\"start\", \"center\", \"end\"] as const;\n\nconst DEFAULT_ALIGN_OFFSET = 0;\nconst DEFAULT_SIDE_OFFSET = 16;\nconst DEFAULT_SPOTLIGHT_PADDING = 4;\n\ntype Side = (typeof SIDE_OPTIONS)[number];\ntype Align = (typeof ALIGN_OPTIONS)[number];\ntype Direction = \"ltr\" | \"rtl\";\n\ninterface ScrollOffset {\n top?: number;\n bottom?: number;\n left?: number;\n right?: number;\n}\n\ntype Boundary = Element | null;\n\n/*\n * ⚠ Khác thượng nguồn: mặc định `collisionBoundary` là MỘT mảng rỗng dùng chung thay vì `[]`\n * viết tại chỗ — mảng mới mỗi lần render sẽ làm dữ liệu bước \"đổi\" liên tục (xem `ownStepData`).\n */\nconst EMPTY_BOUNDARY: Boundary[] = [];\n\n/*\n * ⚠ Khác thượng nguồn: `RefObject<HTMLElement | null>` thay vì `RefObject<HTMLElement>`. Với\n * React 19, `useRef<HTMLDivElement>(null)` có kiểu `RefObject<HTMLDivElement | null>` — kiểu gốc\n * từ chối đúng cái ref mà app nào cũng tạo ra.\n */\ntype StepTarget = string | React.RefObject<HTMLElement | null> | HTMLElement;\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n asChild?: boolean;\n}\n\ntype StepElement = React.ComponentRef<typeof TourStep>;\ntype CloseElement = React.ComponentRef<typeof TourClose>;\ntype PrevElement = React.ComponentRef<typeof TourPrev>;\ntype NextElement = React.ComponentRef<typeof TourNext>;\ntype SkipElement = React.ComponentRef<typeof TourSkip>;\ntype FooterElement = React.ComponentRef<typeof TourFooter>;\n\nconst OPPOSITE_SIDE: Record<Side, Side> = {\n top: \"bottom\",\n right: \"left\",\n bottom: \"top\",\n left: \"right\"\n};\n\n/**\n * @see https://github.com/radix-ui/primitives/blob/main/packages/react/focus-guards/src/focus-guards.tsx\n */\nlet focusGuardCount = 0;\n\nfunction createFocusGuard() {\n const element = document.createElement(\"span\");\n element.setAttribute(\"data-tour-focus-guard\", \"\");\n element.tabIndex = 0;\n element.style.outline = \"none\";\n element.style.opacity = \"0\";\n element.style.position = \"fixed\";\n element.style.pointerEvents = \"none\";\n return element;\n}\n\n/*\n * ⚠ Khác thượng nguồn: nhận `enabled`. Bản gốc gọi hook này vô điều kiện ở MỌI `TourStep`, nên\n * chỉ cần `Tour` nằm sẵn trong layout (đúng cách dùng onboarding: gắn sẵn, đóng mặc định) là trang\n * có thêm HAI điểm dừng Tab VÔ HÌNH ở đầu và cuối `<body>` suốt vòng đời app — người dùng bàn phím\n * Tab vào một thứ không nhìn thấy. Radix chỉ cắm lính gác khi lớp modal đang MỞ.\n */\nfunction useFocusGuards(enabled: boolean) {\n React.useEffect(() => {\n if (!enabled) return;\n\n const edgeGuards = document.querySelectorAll(\"[data-tour-focus-guard]\");\n document.body.insertAdjacentElement(\"afterbegin\", edgeGuards[0] ?? createFocusGuard());\n document.body.insertAdjacentElement(\"beforeend\", edgeGuards[1] ?? createFocusGuard());\n focusGuardCount++;\n\n return () => {\n if (focusGuardCount === 1) {\n const guards = document.querySelectorAll(\"[data-tour-focus-guard]\");\n for (const node of guards) {\n node.remove();\n }\n }\n focusGuardCount--;\n };\n }, [enabled]);\n}\n\nfunction useFocusTrap(\n containerRef: React.RefObject<HTMLElement | null>,\n enabled: boolean,\n tourOpen: boolean,\n onOpenAutoFocus?: (event: OpenAutoFocusEvent) => void,\n onCloseAutoFocus?: (event: CloseAutoFocusEvent) => void\n) {\n const lastFocusedElementRef = React.useRef<HTMLElement | null>(null);\n const onOpenAutoFocusRef = useAsRef(onOpenAutoFocus);\n const onCloseAutoFocusRef = useAsRef(onCloseAutoFocus);\n const tourOpenRef = useAsRef(tourOpen);\n\n React.useEffect(() => {\n if (!enabled) return;\n\n const container = containerRef.current;\n if (!container) return;\n\n const previouslyFocusedElement = document.activeElement as HTMLElement | null;\n\n function getTabbableCandidates() {\n if (!container) return [];\n\n const nodes: HTMLElement[] = [];\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: Element) => {\n const element = node as HTMLElement;\n const isHiddenInput = element.tagName === \"INPUT\" && (element as HTMLInputElement).type === \"hidden\";\n /*\n * ⚠ Khác thượng nguồn: bỏ qua phần tử `disabled`. `TourPrev` bị khoá ở bước đầu mà\n * `tabIndex` vẫn là 0; nếu nó là ứng viên đầu tiên thì `focus()` thất bại IM LẶNG,\n * focus ở lại ngoài bước, và phím Tab kế tiếp bị tính là \"tương tác bên ngoài\" — tour\n * ĐÓNG trong tay người dùng bàn phím.\n */\n const isDisabled = (element as HTMLButtonElement).disabled === true;\n if (element.hidden || isHiddenInput || isDisabled) return NodeFilter.FILTER_SKIP;\n return element.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n }\n });\n while (walker.nextNode()) {\n nodes.push(walker.currentNode as HTMLElement);\n }\n return nodes;\n }\n\n function getTabbableEdges() {\n const candidates = getTabbableCandidates();\n const first = candidates[0];\n const last = candidates[candidates.length - 1];\n return [first, last] as const;\n }\n\n function onFocusIn(event: FocusEvent) {\n if (!container) return;\n\n const target = event.target as HTMLElement | null;\n if (container.contains(target)) {\n lastFocusedElementRef.current = target;\n } else {\n const elementToFocus = lastFocusedElementRef.current ?? getTabbableCandidates()[0];\n elementToFocus?.focus({ preventScroll: true });\n }\n }\n\n function onKeyDown(event: KeyboardEvent) {\n if (event.key !== \"Tab\" || event.altKey || event.ctrlKey || event.metaKey) return;\n\n const [first, last] = getTabbableEdges();\n const hasTabbableElements = first && last;\n\n if (!hasTabbableElements) {\n if (document.activeElement === container) event.preventDefault();\n return;\n }\n\n if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first?.focus({ preventScroll: true });\n } else if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last?.focus({ preventScroll: true });\n }\n }\n\n const openAutoFocusEvent = new CustomEvent(OPEN_AUTO_FOCUS, EVENT_OPTIONS);\n if (onOpenAutoFocusRef.current) {\n container.addEventListener(OPEN_AUTO_FOCUS, onOpenAutoFocusRef.current as EventListener, { once: true });\n }\n container.dispatchEvent(openAutoFocusEvent);\n\n if (!openAutoFocusEvent.defaultPrevented) {\n const tabbableCandidates = getTabbableCandidates();\n if (tabbableCandidates.length > 0) {\n tabbableCandidates[0]?.focus({ preventScroll: true });\n } else {\n container.focus({ preventScroll: true });\n }\n }\n\n document.addEventListener(\"focusin\", onFocusIn);\n container.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n document.removeEventListener(\"focusin\", onFocusIn);\n container.removeEventListener(\"keydown\", onKeyDown);\n\n if (!tourOpenRef.current) {\n setTimeout(() => {\n const closeAutoFocusEvent = new CustomEvent(CLOSE_AUTO_FOCUS, EVENT_OPTIONS);\n if (onCloseAutoFocusRef.current) {\n container.addEventListener(CLOSE_AUTO_FOCUS, onCloseAutoFocusRef.current as EventListener, {\n once: true\n });\n }\n container.dispatchEvent(closeAutoFocusEvent);\n\n if (!closeAutoFocusEvent.defaultPrevented) {\n if (previouslyFocusedElement && document.body.contains(previouslyFocusedElement)) {\n previouslyFocusedElement.focus({ preventScroll: true });\n }\n }\n\n if (onCloseAutoFocusRef.current) {\n container.removeEventListener(CLOSE_AUTO_FOCUS, onCloseAutoFocusRef.current as EventListener);\n }\n }, 0);\n }\n };\n }, [containerRef, enabled, onOpenAutoFocusRef, onCloseAutoFocusRef, tourOpenRef]);\n}\n\nfunction getDataState(open: boolean) {\n return open ? \"open\" : \"closed\";\n}\n\ninterface StepData {\n target: StepTarget;\n align?: Align;\n alignOffset?: number;\n side?: Side;\n sideOffset?: number;\n collisionBoundary?: Boundary | Boundary[];\n collisionPadding?: number | Partial<Record<Side, number>>;\n arrowPadding?: number;\n sticky?: \"partial\" | \"always\";\n hideWhenDetached?: boolean;\n avoidCollisions?: boolean;\n onStepEnter?: () => void;\n onStepLeave?: () => void;\n required?: boolean;\n}\n\ninterface StoreState {\n open: boolean;\n value: number;\n steps: StepData[];\n maskPath: string;\n spotlightRect: { x: number; y: number; width: number; height: number } | null;\n}\n\ninterface Store {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState;\n setState: <K extends keyof StoreState>(key: K, value: StoreState[K], opts?: unknown) => void;\n notify: () => void;\n addStep: (stepData: StepData) => { id: string; index: number };\n removeStep: (id: string) => void;\n /** ⚠ Thêm so với thượng nguồn — xem chú thích ở `TourStep`. */\n updateStep: (id: string, stepData: StepData) => void;\n /** ⚠ Thêm so với thượng nguồn — vị trí HIỆN TẠI của một bước, `-1` nếu chưa đăng ký. */\n getStepIndex: (id: string) => number;\n}\n\nfunction useStore<T>(selector: (state: StoreState) => T, ogStore?: Store | null): T {\n const contextStore = React.useContext(StoreContext);\n\n const store = ogStore ?? contextStore;\n\n if (!store) {\n throw new Error(`\\`useStore\\` phải được gọi bên trong \\`${ROOT_NAME}\\``);\n }\n\n const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\nfunction getTargetElement(target: StepTarget): HTMLElement | null {\n if (typeof target === \"string\") {\n return document.querySelector(target);\n }\n if (target && \"current\" in target) {\n return target.current;\n }\n if (target instanceof HTMLElement) {\n return target;\n }\n return null;\n}\n\nfunction getDefaultScrollBehavior(): ScrollBehavior {\n if (typeof window === \"undefined\") return \"smooth\";\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ? \"auto\" : \"smooth\";\n}\n\n/*\n * ⚠ Khác thượng nguồn: tìm vùng cuộn gần nhất. Bản gốc chỉ `window.scrollTo`, nhưng khung\n * `AppShell` của Comitor là `h-dvh overflow-hidden` và nội dung cuộn trong `<main\n * overflow-y-auto>` — cửa sổ KHÔNG BAO GIỜ cuộn, nên `autoScroll` im lặng bỏ qua mọi đích nằm\n * dưới mép màn hình.\n */\nfunction getScrollParent(element: HTMLElement): HTMLElement | null {\n let parent = element.parentElement;\n while (parent && parent !== document.body && parent !== document.documentElement) {\n const { overflowY } = window.getComputedStyle(parent);\n const isScrollable = overflowY === \"auto\" || overflowY === \"scroll\" || overflowY === \"overlay\";\n if (isScrollable && parent.scrollHeight > parent.clientHeight) return parent;\n parent = parent.parentElement;\n }\n return null;\n}\n\nfunction onScrollToElement(\n element: HTMLElement,\n scrollBehavior: ScrollBehavior = getDefaultScrollBehavior(),\n scrollOffset?: ScrollOffset\n) {\n const offset: Required<ScrollOffset> = {\n top: 100,\n bottom: 100,\n left: 0,\n right: 0,\n ...scrollOffset\n };\n const rect = element.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n const viewportWidth = window.innerWidth;\n\n const isInViewport =\n rect.top >= offset.top &&\n rect.bottom <= viewportHeight - offset.bottom &&\n rect.left >= offset.left &&\n rect.right <= viewportWidth - offset.right;\n\n if (!isInViewport) {\n const scrollParent = getScrollParent(element);\n if (scrollParent) {\n // Cuộn vùng chứa đúng bấy nhiêu để đích dừng cách mép trên khung nhìn `offset.top` — cùng\n // điểm đến với nhánh `window` bên dưới.\n scrollParent.scrollBy({ top: rect.top - offset.top, behavior: scrollBehavior });\n return;\n }\n\n const elementTop = rect.top + window.scrollY;\n const scrollTop = elementTop - offset.top;\n\n window.scrollTo({\n top: Math.max(0, scrollTop),\n behavior: scrollBehavior\n });\n }\n}\n\nfunction getSideAndAlignFromPlacement(placement: Placement): [Side, Align] {\n const [side, align = \"center\"] = placement.split(\"-\") as [Side, Align?];\n return [side, align];\n}\n\nfunction getPlacement(side: Side, align: Align): Placement {\n if (align === \"center\") {\n return side as Placement;\n }\n return `${side}-${align}` as Placement;\n}\n\nfunction updateMask(store: Store, targetElement: HTMLElement, padding: number = DEFAULT_SPOTLIGHT_PADDING) {\n const clientRect = targetElement.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n const x = Math.max(0, clientRect.left - padding);\n const y = Math.max(0, clientRect.top - padding);\n const width = Math.min(viewportWidth - x, clientRect.width + padding * 2);\n const height = Math.min(viewportHeight - y, clientRect.height + padding * 2);\n\n const path = `polygon(0% 0%, 0% 100%, ${x}px 100%, ${x}px ${y}px, ${x + width}px ${y}px, ${x + width}px ${y + height}px, ${x}px ${y + height}px, ${x}px 100%, 100% 100%, 100% 0%)`;\n store.setState(\"maskPath\", path);\n store.setState(\"spotlightRect\", { x, y, width, height });\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n const context = React.useContext(StoreContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface TourContextValue {\n dir: Direction;\n alignOffset: number;\n sideOffset: number;\n spotlightPadding: number;\n dismissible: boolean;\n modal: boolean;\n stepFooter?: React.ReactElement;\n labels: TourLabels;\n onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;\n onInteractOutside?: (event: InteractOutsideEvent) => void;\n onOpenAutoFocus?: (event: OpenAutoFocusEvent) => void;\n onCloseAutoFocus?: (event: CloseAutoFocusEvent) => void;\n}\n\nconst TourContext = React.createContext<TourContextValue | null>(null);\n\nfunction useTourContext(consumerName: string) {\n const context = React.useContext(TourContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\ninterface StepContextValue {\n arrowX?: number;\n arrowY?: number;\n placedAlign: Align;\n placedSide: Side;\n shouldHideArrow: boolean;\n /** ⚠ Thêm so với thượng nguồn — id cho `aria-labelledby`/`aria-describedby` của bước. */\n titleId: string;\n descriptionId: string;\n onArrowChange: (arrow: HTMLSpanElement | null) => void;\n onFooterChange: (footer: FooterElement | null) => void;\n}\n\nconst StepContext = React.createContext<StepContextValue | null>(null);\n\nfunction useStepContext(consumerName: string) {\n const context = React.useContext(StepContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${STEP_NAME}\\``);\n }\n return context;\n}\n\nconst DefaultFooterContext = React.createContext(false);\n\ninterface PortalContextValue {\n portal: HTMLElement | null;\n onPortalChange: (node: HTMLElement | null) => void;\n}\n\nconst PortalContext = React.createContext<PortalContextValue | null>(null);\n\nfunction usePortalContext(consumerName: string) {\n const context = React.useContext(PortalContext);\n if (!context) {\n throw new Error(`\\`${consumerName}\\` phải nằm bên trong \\`${ROOT_NAME}\\``);\n }\n return context;\n}\n\nfunction useScrollLock(enabled: boolean) {\n React.useEffect(() => {\n if (!enabled) return;\n\n const originalStyle = window.getComputedStyle(document.body).overflow;\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;\n\n document.body.style.overflow = \"hidden\";\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${scrollbarWidth}px`;\n }\n\n return () => {\n document.body.style.overflow = originalStyle;\n document.body.style.paddingRight = \"\";\n };\n }, [enabled]);\n}\n\ntype PointerDownOutsideEvent = CustomEvent<{ originalEvent: PointerEvent }>;\ntype InteractOutsideEvent = CustomEvent<{\n originalEvent: PointerEvent | FocusEvent;\n}>;\ntype OpenAutoFocusEvent = CustomEvent<Record<string, never>>;\ntype CloseAutoFocusEvent = CustomEvent<Record<string, never>>;\n\ninterface TourProps extends DivProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n value?: number;\n defaultValue?: number;\n onValueChange?: (step: number) => void;\n onComplete?: () => void;\n onSkip?: () => void;\n onEscapeKeyDown?: (event: KeyboardEvent) => void;\n onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;\n onInteractOutside?: (event: InteractOutsideEvent) => void;\n onOpenAutoFocus?: (event: OpenAutoFocusEvent) => void;\n onCloseAutoFocus?: (event: CloseAutoFocusEvent) => void;\n dir?: Direction;\n alignOffset?: number;\n sideOffset?: number;\n spotlightPadding?: number;\n autoScroll?: boolean;\n /** Mặc định `\"smooth\"`, hoặc `\"auto\"` khi người dùng bật `prefers-reduced-motion`. */\n scrollBehavior?: ScrollBehavior;\n scrollOffset?: ScrollOffset;\n dismissible?: boolean;\n modal?: boolean;\n stepFooter?: React.ReactElement;\n /**\n * Ghi đè chuỗi hiển thị/a11y (mặc định tiếng Việt) — xem `TourLabels`.\n * ⚠ Thêm so với thượng nguồn: bản gốc chôn cứng chuỗi tiếng Anh trong từng nút.\n */\n labels?: Partial<TourLabels>;\n}\n\nfunction Tour(props: TourProps) {\n const {\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n value: valueProp,\n defaultValue = 0,\n onValueChange,\n onComplete,\n onSkip,\n autoScroll = true,\n /*\n * ⚠ Khác thượng nguồn: KHÔNG có giá trị mặc định ở đây. Bản gốc viết\n * `scrollBehavior = getDefaultScrollBehavior()`, tức đọc `window.matchMedia` NGAY TRONG\n * render. Để `undefined` thì tham số mặc định của `onScrollToElement` tính đúng giá trị đó\n * vào lúc CUỘN — ngoài render, và theo cài đặt reduced-motion ở thời điểm ấy.\n */\n scrollBehavior,\n scrollOffset,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n onOpenAutoFocus,\n onCloseAutoFocus,\n dir: dirProp,\n alignOffset = DEFAULT_ALIGN_OFFSET,\n sideOffset = DEFAULT_SIDE_OFFSET,\n spotlightPadding = DEFAULT_SPOTLIGHT_PADDING,\n dismissible = true,\n modal = true,\n stepFooter,\n labels: labelsProp,\n asChild,\n ...rootProps\n } = props;\n\n const dir = useDirection(dirProp);\n\n const [portal, setPortal] = React.useState<HTMLElement | null>(null);\n const prevOpenRef = React.useRef<boolean | undefined>(undefined);\n const previouslyFocusedElementRef = React.useRef<HTMLElement | null>(null);\n\n const stateRef = useLazyRef<StoreState>(() => ({\n open: openProp ?? defaultOpen,\n value: valueProp ?? defaultValue,\n steps: [],\n maskPath: \"\",\n spotlightRect: null\n }));\n const listenersRef = useLazyRef<Set<() => void>>(() => new Set());\n const stepIdsMapRef = useLazyRef<Map<string, number>>(() => new Map());\n const stepIdCounterRef = useLazyRef(() => ({ current: 0 }));\n const propsRef = useAsRef({\n valueProp,\n onOpenChange,\n onValueChange,\n onComplete,\n onSkip,\n onEscapeKeyDown,\n onCloseAutoFocus,\n autoScroll,\n scrollBehavior,\n scrollOffset\n });\n\n const store: Store = React.useMemo(\n () => ({\n subscribe: (cb) => {\n listenersRef.current.add(cb);\n return () => listenersRef.current.delete(cb);\n },\n getState: () => {\n return stateRef.current;\n },\n setState: (key, value) => {\n if (Object.is(stateRef.current[key], value)) return;\n stateRef.current[key] = value;\n\n if (key === \"open\" && typeof value === \"boolean\") {\n propsRef.current.onOpenChange?.(value);\n\n if (value) {\n if (stateRef.current.steps.length > 0) {\n if (stateRef.current.value >= stateRef.current.steps.length) {\n store.setState(\"value\", 0);\n }\n }\n } else {\n if (stateRef.current.value < (stateRef.current.steps.length || 0) - 1) {\n propsRef.current.onSkip?.();\n }\n }\n } else if (key === \"value\" && typeof value === \"number\") {\n const prevStep = stateRef.current.steps[stateRef.current.value];\n const nextStep = stateRef.current.steps[value];\n\n prevStep?.onStepLeave?.();\n nextStep?.onStepEnter?.();\n\n if (value >= stateRef.current.steps.length) {\n propsRef.current.onComplete?.();\n\n if (propsRef.current.valueProp !== undefined) {\n propsRef.current.onValueChange?.(value);\n }\n\n store.setState(\"open\", false);\n return;\n }\n\n if (propsRef.current.valueProp !== undefined) {\n propsRef.current.onValueChange?.(value);\n return;\n }\n\n propsRef.current.onValueChange?.(value);\n\n if (nextStep && propsRef.current.autoScroll) {\n const targetElement = getTargetElement(nextStep.target);\n if (targetElement) {\n onScrollToElement(targetElement, propsRef.current.scrollBehavior, propsRef.current.scrollOffset);\n }\n }\n }\n\n store.notify();\n },\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n },\n addStep: (stepData) => {\n const id = `step-${stepIdCounterRef.current.current++}`;\n const index = stateRef.current.steps.length;\n stepIdsMapRef.current.set(id, index);\n stateRef.current.steps = [...stateRef.current.steps, stepData];\n store.notify();\n return { id, index };\n },\n removeStep: (id) => {\n const index = stepIdsMapRef.current.get(id);\n if (index === undefined) return;\n\n stateRef.current.steps = stateRef.current.steps.filter((_, i) => i !== index);\n\n stepIdsMapRef.current.delete(id);\n\n for (const [stepId, stepIndex] of stepIdsMapRef.current.entries()) {\n if (stepIndex > index) {\n stepIdsMapRef.current.set(stepId, stepIndex - 1);\n }\n }\n\n store.notify();\n },\n updateStep: (id, stepData) => {\n const index = stepIdsMapRef.current.get(id);\n if (index === undefined) return;\n if (stateRef.current.steps[index] === stepData) return;\n\n const steps = [...stateRef.current.steps];\n steps[index] = stepData;\n stateRef.current.steps = steps;\n store.notify();\n },\n getStepIndex: (id) => stepIdsMapRef.current.get(id) ?? -1\n }),\n [stateRef, listenersRef, stepIdsMapRef, stepIdCounterRef, propsRef]\n );\n\n const open = useStore((state) => state.open, store);\n\n /*\n * ⚠ Khác thượng nguồn: Escape mà tour đã DÙNG thì phải đánh dấu là đã dùng, và phải nghe ở pha\n * CAPTURE. Bản gốc nghe ở pha nổi bọt và không gọi `preventDefault()`, nên đo được: MỘT lần\n * Escape vừa đóng tour vừa đóng luôn `ActionBar` đang mở — tức xoá mất lựa chọn dòng của người\n * dùng. Chỉ `preventDefault()` thôi là chưa đủ: listener của `ActionBar` cũng nằm ở `document`,\n * đăng ký TRƯỚC (thanh mở trước tour), nên nó chạy trước và chưa thấy cờ. Pha capture của\n * `document` chạy trước mọi listener nổi bọt — đúng chỗ Radix `DismissableLayer` nghe Escape,\n * nên tour cư xử như một Dialog của gói. Đang gõ dở bằng bộ gõ (Telex/VNI, `isComposing`) hoặc\n * Escape đã có lớp khác dùng thì bỏ qua, cùng luật với `ActionBar`.\n */\n React.useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n if (!open || event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n\n if (propsRef.current.onEscapeKeyDown) {\n propsRef.current.onEscapeKeyDown(event);\n if (event.defaultPrevented) return;\n }\n event.preventDefault();\n store.setState(\"open\", false);\n }\n\n document.addEventListener(\"keydown\", onKeyDown, { capture: true });\n return () => document.removeEventListener(\"keydown\", onKeyDown, { capture: true });\n }, [store, open, propsRef]);\n\n useIsomorphicLayoutEffect(() => {\n const wasOpen = prevOpenRef.current;\n\n if (open && !wasOpen) {\n previouslyFocusedElementRef.current = document.activeElement as HTMLElement | null;\n } else if (!open && wasOpen) {\n setTimeout(() => {\n const container = portal ?? document.body;\n const closeAutoFocusEvent = new CustomEvent(CLOSE_AUTO_FOCUS, EVENT_OPTIONS);\n\n if (propsRef.current.onCloseAutoFocus) {\n container.addEventListener(CLOSE_AUTO_FOCUS, propsRef.current.onCloseAutoFocus as EventListener, {\n once: true\n });\n }\n container.dispatchEvent(closeAutoFocusEvent);\n\n if (!closeAutoFocusEvent.defaultPrevented) {\n const elementToFocus = previouslyFocusedElementRef.current;\n if (elementToFocus && document.body.contains(elementToFocus)) {\n elementToFocus.focus({ preventScroll: true });\n }\n }\n\n previouslyFocusedElementRef.current = null;\n }, 0);\n }\n\n prevOpenRef.current = open;\n }, [open, portal, propsRef]);\n\n useIsomorphicLayoutEffect(() => {\n if (openProp !== undefined) {\n store.setState(\"open\", openProp);\n }\n }, [openProp, store]);\n\n useIsomorphicLayoutEffect(() => {\n if (valueProp !== undefined) {\n store.setState(\"value\", valueProp);\n }\n }, [valueProp, store]);\n\n const labels = React.useMemo<TourLabels>(() => ({ ...DEFAULT_TOUR_LABELS, ...labelsProp }), [labelsProp]);\n\n const contextValue = React.useMemo<TourContextValue>(\n () => ({\n dir,\n alignOffset,\n sideOffset,\n spotlightPadding,\n dismissible,\n modal,\n stepFooter,\n labels,\n onPointerDownOutside,\n onInteractOutside,\n onOpenAutoFocus,\n onCloseAutoFocus\n }),\n [\n dir,\n alignOffset,\n sideOffset,\n spotlightPadding,\n dismissible,\n modal,\n stepFooter,\n labels,\n onPointerDownOutside,\n onInteractOutside,\n onOpenAutoFocus,\n onCloseAutoFocus\n ]\n );\n\n const portalContextValue = React.useMemo<PortalContextValue>(\n () => ({\n portal,\n onPortalChange: setPortal\n }),\n [portal]\n );\n\n useScrollLock(open && modal);\n\n const RootPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StoreContext.Provider value={store}>\n <TourContext.Provider value={contextValue}>\n <PortalContext.Provider value={portalContextValue}>\n <RootPrimitive data-slot=\"tour\" dir={dir} {...rootProps} />\n </PortalContext.Provider>\n </TourContext.Provider>\n </StoreContext.Provider>\n );\n}\n\ninterface TourStepProps extends DivProps {\n target: StepTarget;\n side?: Side;\n sideOffset?: number;\n align?: Align;\n alignOffset?: number;\n collisionBoundary?: Boundary | Boundary[];\n collisionPadding?: number | Partial<Record<Side, number>>;\n arrowPadding?: number;\n sticky?: \"partial\" | \"always\";\n hideWhenDetached?: boolean;\n avoidCollisions?: boolean;\n required?: boolean;\n forceMount?: boolean;\n onStepEnter?: () => void;\n onStepLeave?: () => void;\n}\n\nfunction TourStep(props: TourStepProps) {\n const {\n target,\n side = \"bottom\",\n sideOffset,\n align = \"center\",\n alignOffset,\n collisionBoundary = EMPTY_BOUNDARY,\n collisionPadding = 0,\n arrowPadding = 0,\n sticky = \"partial\",\n hideWhenDetached = false,\n avoidCollisions = true,\n required = false,\n forceMount = false,\n onStepEnter,\n onStepLeave,\n onPointerDownCapture: onPointerDownCaptureProp,\n onFocusCapture: onFocusCaptureProp,\n onBlurCapture: onBlurCaptureProp,\n children,\n className,\n style,\n asChild,\n ...stepProps\n } = props;\n\n const store = useStoreContext(STEP_NAME);\n\n const [arrow, setArrow] = React.useState<HTMLSpanElement | null>(null);\n const [footer, setFooter] = React.useState<FooterElement | null>(null);\n const [targetElement, setTargetElement] = React.useState<HTMLElement | null>(null);\n\n const stepRef = React.useRef<StepElement | null>(null);\n const stepIdRef = React.useRef<string>(\"\");\n const isPointerInsideReactTreeRef = React.useRef(false);\n const isFocusInsideReactTreeRef = React.useRef(false);\n\n const titleId = React.useId();\n const descriptionId = React.useId();\n\n const open = useStore((state) => state.open);\n const value = useStore((state) => state.value);\n const steps = useStore((state) => state.steps);\n const context = useTourContext(STEP_NAME);\n\n const resolvedSideOffset = sideOffset ?? context.sideOffset;\n const resolvedAlignOffset = alignOffset ?? context.alignOffset;\n\n /*\n * ⚠ Khác thượng nguồn: đăng ký bước MỘT lần khi mount, sau đó cập nhật TẠI CHỖ.\n *\n * Bản gốc gỡ rồi thêm lại bước mỗi khi một prop đổi (kể cả `onStepEnter={() => …}` viết tại\n * chỗ — tức MỖI lần render). Thêm lại là nối vào CUỐI danh sách, còn thứ tự các bước khác giữ\n * trong ref riêng không ai cập nhật: bước bị đẩy xuống cuối, và ở `value` cũ không bước nào\n * nhận mình là bước hiện tại — tour trống trơn. Chỉ cần `side={isMobile ? … : …}` là dính.\n *\n * Hai callback đi qua ref để luôn gọi bản mới nhất mà không làm dữ liệu bước \"đổi\".\n */\n const callbacksRef = useAsRef({ onStepEnter, onStepLeave });\n\n const ownStepData = React.useMemo<StepData>(\n () => ({\n target,\n align,\n alignOffset: resolvedAlignOffset,\n side,\n sideOffset: resolvedSideOffset,\n collisionBoundary,\n collisionPadding,\n arrowPadding,\n sticky,\n hideWhenDetached,\n avoidCollisions,\n onStepEnter: () => callbacksRef.current.onStepEnter?.(),\n onStepLeave: () => callbacksRef.current.onStepLeave?.(),\n required\n }),\n [\n target,\n align,\n resolvedAlignOffset,\n side,\n resolvedSideOffset,\n collisionBoundary,\n collisionPadding,\n arrowPadding,\n sticky,\n hideWhenDetached,\n avoidCollisions,\n required,\n callbacksRef\n ]\n );\n const ownStepDataRef = useAsRef(ownStepData);\n\n useIsomorphicLayoutEffect(() => {\n const { id } = store.addStep(ownStepDataRef.current);\n stepIdRef.current = id;\n\n return () => {\n store.removeStep(id);\n };\n }, [store, ownStepDataRef]);\n\n useIsomorphicLayoutEffect(() => {\n store.updateStep(stepIdRef.current, ownStepData);\n }, [store, ownStepData]);\n\n const stepData = steps[value];\n\n // ⚠ Khác thượng nguồn: vị trí đọc từ store theo id (được dồn lại khi một bước khác bị gỡ),\n // không từ một ref chụp lúc đăng ký.\n const isCurrentStep = store.getStepIndex(stepIdRef.current) === value;\n\n /*\n * ⚠ Khác thượng nguồn: bản gốc gọi `getTargetElement` — tức `document.querySelector` — NGAY\n * TRONG render. Ở đây phân giải trong layout effect: chạy sau MỖI lần render như nhịp của bản\n * gốc (đích xuất hiện muộn vẫn bắt được), vẫn trước lượt sơn, và chỉ cho bước đang hiện.\n * `setState` cùng giá trị thì React bỏ qua, nên không có vòng lặp.\n */\n useIsomorphicLayoutEffect(() => {\n setTargetElement(open && isCurrentStep && stepData ? getTargetElement(stepData.target) : null);\n });\n\n /*\n * ⚠ Khác thượng nguồn: gom điều kiện \"bước này đang HIỆN\" về một chỗ. Bản gốc bật bẫy focus\n * và bộ nghe bấm-ra-ngoài theo `open && isCurrentStep`; nếu lượt render đó chưa có đích (và\n * không `forceMount`) thì bước chưa có DOM, hiệu ứng thoát sớm và KHÔNG chạy lại khi đích xuất\n * hiện — focus không bao giờ vào được bước.\n */\n const isStepRendered = open && isCurrentStep && !!stepData && (!!targetElement || forceMount);\n\n const middleware = React.useMemo(() => {\n if (!stepData) return [];\n\n const mainAxisOffset = stepData.sideOffset ?? resolvedSideOffset;\n const crossAxisOffset = stepData.alignOffset ?? resolvedAlignOffset;\n\n const padding =\n typeof stepData.collisionPadding === \"number\"\n ? stepData.collisionPadding\n : {\n top: stepData.collisionPadding?.top ?? 0,\n right: stepData.collisionPadding?.right ?? 0,\n bottom: stepData.collisionPadding?.bottom ?? 0,\n left: stepData.collisionPadding?.left ?? 0\n };\n\n const boundary = Array.isArray(stepData.collisionBoundary)\n ? stepData.collisionBoundary\n : stepData.collisionBoundary\n ? [stepData.collisionBoundary]\n : [];\n const hasExplicitBoundaries = boundary.length > 0;\n\n const detectOverflowOptions = {\n padding,\n boundary: boundary.filter((b): b is Element => b !== null),\n altBoundary: hasExplicitBoundaries\n };\n\n return [\n offset({\n mainAxis: mainAxisOffset,\n alignmentAxis: crossAxisOffset\n }),\n stepData.avoidCollisions &&\n shift({\n mainAxis: true,\n crossAxis: false,\n limiter: stepData.sticky === \"partial\" ? limitShift() : undefined,\n ...detectOverflowOptions\n }),\n stepData.avoidCollisions && flip({ ...detectOverflowOptions }),\n arrow && onArrow({ element: arrow, padding: stepData.arrowPadding }),\n stepData.hideWhenDetached &&\n hide({\n strategy: \"referenceHidden\",\n ...detectOverflowOptions\n })\n ].filter(Boolean) as Middleware[];\n }, [stepData, resolvedSideOffset, resolvedAlignOffset, arrow]);\n\n const placement = getPlacement(stepData?.side ?? side, stepData?.align ?? align);\n\n const {\n refs,\n floatingStyles,\n placement: finalPlacement,\n middlewareData\n } = useFloating({\n placement,\n middleware,\n strategy: \"fixed\",\n whileElementsMounted: autoUpdate,\n elements: {\n reference: targetElement\n }\n });\n\n const composedRef = useComposedRefs(refs.setFloating, stepRef);\n\n const [placedSide, placedAlign] = getSideAndAlignFromPlacement(finalPlacement);\n\n const arrowX = middlewareData.arrow?.x;\n const arrowY = middlewareData.arrow?.y;\n const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;\n const isHidden = hideWhenDetached && middlewareData.hide?.referenceHidden;\n\n const stepContextValue = React.useMemo<StepContextValue>(\n () => ({\n arrowX,\n arrowY,\n placedAlign,\n placedSide,\n shouldHideArrow: cannotCenterArrow,\n titleId,\n descriptionId,\n onArrowChange: setArrow,\n onFooterChange: setFooter\n }),\n [arrowX, arrowY, placedSide, placedAlign, cannotCenterArrow, titleId, descriptionId]\n );\n\n React.useEffect(() => {\n if (open && targetElement && isCurrentStep) {\n updateMask(store, targetElement, context.spotlightPadding);\n\n let rafId: number | null = null;\n\n function onResize() {\n if (targetElement) {\n updateMask(store, targetElement, context.spotlightPadding);\n }\n }\n\n function onScroll() {\n if (rafId !== null) return;\n rafId = requestAnimationFrame(() => {\n if (targetElement) {\n updateMask(store, targetElement, context.spotlightPadding);\n }\n rafId = null;\n });\n }\n\n window.addEventListener(\"resize\", onResize);\n /*\n * ⚠ Khác thượng nguồn: nghe ở pha CAPTURE. Sự kiện `scroll` của một phần tử không nổi bọt\n * lên `window`; bản gốc chỉ bắt được khi CỬA SỔ cuộn. Trong `AppShell` chỉ `<main>` cuộn,\n * nên lỗ sáng đứng yên trong khi đích trôi đi.\n */\n window.addEventListener(\"scroll\", onScroll, { capture: true, passive: true });\n return () => {\n window.removeEventListener(\"resize\", onResize);\n window.removeEventListener(\"scroll\", onScroll, { capture: true });\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n }\n };\n }\n }, [open, targetElement, isCurrentStep, store, context.spotlightPadding]);\n\n React.useEffect(() => {\n if (!isStepRendered) return;\n\n const stepElement = stepRef.current;\n if (!stepElement) return;\n\n const ownerDocument = stepElement.ownerDocument;\n\n function onPointerDown(event: PointerEvent) {\n if (event.target && !isPointerInsideReactTreeRef.current) {\n const pointerDownOutsideEvent = new CustomEvent(POINTER_DOWN_OUTSIDE, {\n ...EVENT_OPTIONS,\n detail: { originalEvent: event }\n });\n\n context.onPointerDownOutside?.(pointerDownOutsideEvent);\n\n const interactOutsideEvent = new CustomEvent(INTERACT_OUTSIDE, {\n ...EVENT_OPTIONS,\n detail: { originalEvent: event }\n });\n context.onInteractOutside?.(interactOutsideEvent);\n\n if (\n !pointerDownOutsideEvent.defaultPrevented &&\n !interactOutsideEvent.defaultPrevented &&\n context.dismissible\n ) {\n store.setState(\"open\", false);\n }\n }\n\n isPointerInsideReactTreeRef.current = false;\n }\n\n const timerId = window.setTimeout(() => {\n ownerDocument.addEventListener(\"pointerdown\", onPointerDown);\n }, 0);\n\n return () => {\n window.clearTimeout(timerId);\n ownerDocument.removeEventListener(\"pointerdown\", onPointerDown);\n };\n }, [isStepRendered, store, context]);\n\n React.useEffect(() => {\n if (!isStepRendered) return;\n\n const stepElement = stepRef.current;\n if (!stepElement) return;\n\n const ownerDocument = stepElement.ownerDocument;\n\n function onFocusIn(event: FocusEvent) {\n const target = event.target as HTMLElement;\n\n const isFocusInStep = stepElement?.contains(target);\n const isFocusInTarget = targetElement?.contains(target);\n\n if (event.target && !isFocusInsideReactTreeRef.current && !isFocusInStep && !isFocusInTarget) {\n const interactOutsideEvent = new CustomEvent(INTERACT_OUTSIDE, {\n ...EVENT_OPTIONS,\n detail: { originalEvent: event }\n });\n\n context.onInteractOutside?.(interactOutsideEvent);\n\n if (!interactOutsideEvent.defaultPrevented && context.dismissible) {\n store.setState(\"open\", false);\n }\n }\n }\n\n ownerDocument.addEventListener(\"focusin\", onFocusIn);\n\n return () => {\n ownerDocument.removeEventListener(\"focusin\", onFocusIn);\n };\n }, [isStepRendered, store, context, targetElement]);\n\n const onPointerDownCapture = React.useCallback(\n (event: React.PointerEvent<StepElement>) => {\n onPointerDownCaptureProp?.(event);\n isPointerInsideReactTreeRef.current = true;\n },\n [onPointerDownCaptureProp]\n );\n\n const onFocusCapture = React.useCallback(\n (event: React.FocusEvent<StepElement>) => {\n onFocusCaptureProp?.(event);\n isFocusInsideReactTreeRef.current = true;\n },\n [onFocusCaptureProp]\n );\n\n const onBlurCapture = React.useCallback(\n (event: React.FocusEvent<StepElement>) => {\n onBlurCaptureProp?.(event);\n isFocusInsideReactTreeRef.current = false;\n },\n [onBlurCaptureProp]\n );\n\n React.useEffect(() => {\n if (!open || !isCurrentStep || !targetElement) return;\n\n function onTargetPointerDownCapture() {\n isPointerInsideReactTreeRef.current = true;\n }\n\n function onTargetFocusCapture() {\n isFocusInsideReactTreeRef.current = true;\n }\n\n function onTargetBlurCapture() {\n isFocusInsideReactTreeRef.current = false;\n }\n\n targetElement.addEventListener(\"pointerdown\", onTargetPointerDownCapture, true);\n targetElement.addEventListener(\"focus\", onTargetFocusCapture, true);\n targetElement.addEventListener(\"blur\", onTargetBlurCapture, true);\n\n return () => {\n targetElement.removeEventListener(\"pointerdown\", onTargetPointerDownCapture, true);\n targetElement.removeEventListener(\"focus\", onTargetFocusCapture, true);\n targetElement.removeEventListener(\"blur\", onTargetBlurCapture, true);\n };\n }, [open, isCurrentStep, targetElement]);\n\n useFocusGuards(isStepRendered);\n useFocusTrap(stepRef, isStepRendered, open, context.onOpenAutoFocus, context.onCloseAutoFocus);\n\n if (!isStepRendered) {\n return null;\n }\n\n const StepPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StepContext.Provider value={stepContextValue}>\n <StepPrimitive\n ref={composedRef}\n /*\n * ⚠ Khác thượng nguồn: bản gốc là `<div>` TRẦN, không vai trò, không tên. Bẫy focus kéo\n * người dùng vào đó và trình đọc màn hình chỉ đọc \"nút Đóng hướng dẫn\" — không nói\n * đang ở đâu, bước này nói gì. `role=\"dialog\"` + tiêu đề/mô tả làm tên và lời mô tả, như\n * `PopoverContent` của Radix. Không `aria-modal`: đích đang được soi nằm NGOÀI bước và\n * người dùng vẫn cần đọc được nó.\n */\n role=\"dialog\"\n aria-labelledby={titleId}\n aria-describedby={descriptionId}\n data-slot=\"tour-step\"\n data-side={placedSide}\n data-align={placedAlign}\n dir={context.dir}\n tabIndex={-1}\n {...stepProps}\n onPointerDownCapture={onPointerDownCapture}\n onFocusCapture={onFocusCapture}\n onBlurCapture={onBlurCapture}\n className={cn(\n // Bề mặt cùng khuôn với `PopoverContent`: nền/chữ popover, viền token, bóng tầng nổi.\n \"fixed z-50 flex w-80 flex-col gap-4 rounded-lg border bg-popover p-4 text-popover-foreground shadow-overlay outline-none\",\n className\n )}\n style={{\n ...style,\n ...floatingStyles,\n visibility: isHidden ? \"hidden\" : undefined,\n pointerEvents: isHidden ? \"none\" : undefined\n }}\n >\n {children}\n {!footer && <DefaultFooterContext.Provider value={true}>{context.stepFooter}</DefaultFooterContext.Provider>}\n </StepPrimitive>\n </StepContext.Provider>\n );\n}\n\ninterface TourSpotlightProps extends DivProps {\n forceMount?: boolean;\n}\n\nfunction TourSpotlight(props: TourSpotlightProps) {\n const { asChild, className, style, forceMount = false, ...backdropProps } = props;\n\n const open = useStore((state) => state.open);\n const maskPath = useStore((state) => state.maskPath);\n\n if (!open && !forceMount) return null;\n\n const SpotlightPrimitive = asChild ? Slot : \"div\";\n\n return (\n <SpotlightPrimitive\n data-slot=\"tour-spotlight\"\n data-state={getDataState(open)}\n {...backdropProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: `bg-black/80` (bảng màu mặc định Tailwind) → đúng màu lớp phủ của\n * `DialogOverlay`, để lớp tối của tour và của hộp thoại là MỘT thứ trong mắt người dùng.\n * Không lấy `backdrop-blur-sm` của nó: tour cần người dùng còn đọc được ngữ cảnh quanh đích.\n */\n \"fixed inset-0 z-50 bg-ink/60 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 dark:bg-ink/80\",\n className\n )}\n style={{\n clipPath: maskPath,\n ...style\n }}\n />\n );\n}\n\ninterface TourSpotlightRingProps extends DivProps {\n forceMount?: boolean;\n}\n\nfunction TourSpotlightRing(props: TourSpotlightRingProps) {\n const { asChild, className, style, forceMount = false, ...ringProps } = props;\n\n const open = useStore((state) => state.open);\n const spotlightRect = useStore((state) => state.spotlightRect);\n\n if (!open && !forceMount) return null;\n if (!spotlightRect) return null;\n\n const RingPrimitive = asChild ? Slot : \"div\";\n\n return (\n <RingPrimitive\n data-slot=\"tour-spotlight-ring\"\n data-state={getDataState(open)}\n {...ringProps}\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: bỏ `border-ring`. Không có độ dày viền đi kèm nên nó không vẽ gì —\n * chỉ là một màu viền treo sẵn. App muốn viền thì truyền đủ cả độ dày lẫn màu\n * (`border-2 border-primary-ink`), như chính các ví dụ của thượng nguồn vẫn làm.\n */\n \"pointer-events-none fixed z-50 ring-[3px] ring-ring/50\",\n className\n )}\n style={{\n left: spotlightRect.x,\n top: spotlightRect.y,\n width: spotlightRect.width,\n height: spotlightRect.height,\n ...style\n }}\n />\n );\n}\n\ninterface TourPortalProps {\n children?: React.ReactNode;\n container?: HTMLElement | null;\n}\n\nfunction TourPortal(props: TourPortalProps) {\n const { children, container } = props;\n\n const portalContext = usePortalContext(PORTAL_NAME);\n\n /*\n * ⚠ Khác thượng nguồn: giữ NÚT gắn trong state thay cho cờ `mounted`, để lượt render không\n * phải đọc `document.body` — mọi chỗ chạm DOM đều nằm trong effect.\n */\n const [mountNode, setMountNode] = React.useState<HTMLElement | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const node = container ?? document.body;\n setMountNode(node);\n\n portalContext?.onPortalChange(node);\n return () => {\n portalContext?.onPortalChange(null);\n };\n }, [container, portalContext]);\n\n if (!mountNode) return null;\n\n const portalContainer = container ?? portalContext?.portal ?? mountNode;\n\n return ReactDOM.createPortal(children, portalContainer);\n}\n\ninterface TourArrowProps extends React.ComponentProps<\"svg\"> {\n width?: number;\n height?: number;\n asChild?: boolean;\n}\n\nfunction TourArrow(props: TourArrowProps) {\n const { width = 10, height = 5, className, children, asChild, ...arrowProps } = props;\n\n const stepContext = useStepContext(ARROW_NAME);\n const baseSide = OPPOSITE_SIDE[stepContext.placedSide];\n\n return (\n <span\n ref={stepContext.onArrowChange}\n data-slot=\"tour-arrow\"\n style={{\n position: \"absolute\",\n left: stepContext.arrowX != null ? `${stepContext.arrowX}px` : undefined,\n top: stepContext.arrowY != null ? `${stepContext.arrowY}px` : undefined,\n [baseSide]: 0,\n transformOrigin: {\n top: \"\",\n right: \"0 0\",\n bottom: \"center 0\",\n left: \"100% 0\"\n }[stepContext.placedSide],\n transform: {\n top: \"translateY(100%)\",\n right: \"translateY(50%) rotate(90deg) translateX(-50%)\",\n bottom: \"rotate(180deg)\",\n left: \"translateY(50%) rotate(-90deg) translateX(50%)\"\n }[stepContext.placedSide],\n visibility: stepContext.shouldHideArrow ? \"hidden\" : undefined\n }}\n >\n {/* ⚠ Khác thượng nguồn: `aria-hidden` — mũi tên thuần trang trí, không có gì để đọc. */}\n <svg\n viewBox=\"0 0 30 10\"\n preserveAspectRatio=\"none\"\n width={width}\n height={height}\n aria-hidden=\"true\"\n {...arrowProps}\n className={cn(\"block fill-popover stroke-border\", className)}\n >\n {asChild ? children : <polygon points=\"0,0 30,0 15,10\" />}\n </svg>\n </span>\n );\n}\n\ntype TourHeaderProps = DivProps;\n\nfunction TourHeader(props: TourHeaderProps) {\n const { asChild, className, ...headerProps } = props;\n\n const context = useTourContext(HEADER_NAME);\n\n const HeaderPrimitive = asChild ? Slot : \"div\";\n\n return (\n <HeaderPrimitive\n data-slot=\"tour-header\"\n dir={context.dir}\n {...headerProps}\n className={cn(\"flex flex-col gap-1.5 text-center sm:text-left\", className)}\n />\n );\n}\n\ntype TourTitleProps = DivProps;\n\nfunction TourTitle(props: TourTitleProps) {\n const { asChild, className, ...titleProps } = props;\n\n const context = useTourContext(TITLE_NAME);\n // ⚠ Khác thượng nguồn: nhận id từ bước chứa nó để bước trỏ `aria-labelledby` vào đây.\n const stepContext = React.useContext(StepContext);\n\n const TitlePrimitive = asChild ? Slot : \"div\";\n\n return (\n <TitlePrimitive\n id={stepContext?.titleId}\n data-slot=\"tour-title\"\n dir={context.dir}\n {...titleProps}\n className={cn(\"text-lg leading-none font-semibold tracking-tight\", className)}\n />\n );\n}\n\ntype TourDescriptionProps = DivProps;\n\nfunction TourDescription(props: TourDescriptionProps) {\n const { asChild, className, ...descriptionProps } = props;\n\n const context = useTourContext(DESCRIPTION_NAME);\n // ⚠ Khác thượng nguồn: nhận id từ bước chứa nó để bước trỏ `aria-describedby` vào đây.\n const stepContext = React.useContext(StepContext);\n\n const DescriptionPrimitive = asChild ? Slot : \"div\";\n\n return (\n <DescriptionPrimitive\n id={stepContext?.descriptionId}\n data-slot=\"tour-description\"\n dir={context.dir}\n {...descriptionProps}\n className={cn(\"text-sm text-muted-foreground\", className)}\n />\n );\n}\n\ninterface TourCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nfunction TourClose(props: TourCloseProps) {\n const { asChild, className, children, onClick: onClickProp, ...closeButtonProps } = props;\n\n const store = useStoreContext(CLOSE_NAME);\n const context = useTourContext(CLOSE_NAME);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<CloseElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n store.setState(\"open\", false);\n },\n [store, onClickProp]\n );\n\n const ClosePrimitive = asChild ? Slot : \"button\";\n\n return (\n <ClosePrimitive\n type=\"button\"\n aria-label={context.labels.close}\n data-slot=\"tour-close\"\n className={cn(\n /*\n * ⚠ Khác thượng nguồn: khuôn nút đóng của `DialogContent` (màu chữ muted, hover nền muted,\n * vòng focus của gói) thay cho `opacity-70` — `opacity` không miễn ngưỡng tương phản cho\n * thứ còn bấm được (docs/hop-dong-mau.md). `top-3 right-3 p-1` đặt icon đúng chỗ\n * `top-4 right-4` cũ nhưng vùng bấm lên ~24px thay vì 16px (WCAG 2.5.8).\n */\n \"absolute top-3 right-3 rounded-md p-1 text-muted-foreground transition-colors duration-150 ease-out hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n onClick={onClick}\n {...closeButtonProps}\n >\n {/*\n * ⚠ Khác thượng nguồn: bản gốc luôn render icon X làm con, đè mất `children` của app — và\n * với `asChild` thì Slot nhận chính cái `<svg>` làm phần tử được \"bọc\".\n */}\n {children ?? <X aria-hidden=\"true\" />}\n </ClosePrimitive>\n );\n}\n\ntype TourPrevProps = React.ComponentProps<typeof Button>;\n\n/*\n * ⚠ Khác thượng nguồn (áp cho cả `TourPrev`, `TourNext`, `TourSkip`): bỏ `aria-label` mặc định.\n * Nút đã có chữ hiện; `aria-label` cứng ĐÈ lên chữ đó — ở bước cuối `TourNext` HIỆN \"Finish\"\n * nhưng ĐỌC \"Next step\", và app truyền `children` riêng vẫn bị đọc bằng nhãn cũ. Lệch WCAG 2.5.3\n * (Label in Name): người dùng điều khiển bằng giọng nói gọi đúng chữ trên nút mà không bấm được.\n * App dùng nút chỉ-có-icon thì tự truyền `aria-label`.\n */\nfunction TourPrev(props: TourPrevProps) {\n const { children, onClick: onClickProp, ...prevButtonProps } = props;\n\n const store = useStoreContext(PREV_NAME);\n const context = useTourContext(PREV_NAME);\n const value = useStore((state) => state.value);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<PrevElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n if (value > 0) {\n store.setState(\"value\", value - 1);\n }\n },\n [value, store, onClickProp]\n );\n\n return (\n <Button\n type=\"button\"\n data-slot=\"tour-prev\"\n variant=\"outline\"\n {...prevButtonProps}\n onClick={onClick}\n disabled={value === 0}\n >\n {children ?? (\n <>\n {/* `Button` của gói không tự đặt cỡ icon (khác shadcn) → ghi `size-4` tường minh. */}\n <ChevronLeft aria-hidden=\"true\" className=\"size-4\" />\n {context.labels.prev}\n </>\n )}\n </Button>\n );\n}\n\ntype TourNextProps = React.ComponentProps<typeof Button>;\n\nfunction TourNext(props: TourNextProps) {\n const { children, onClick: onClickProp, ...nextButtonProps } = props;\n const store = useStoreContext(NEXT_NAME);\n const context = useTourContext(NEXT_NAME);\n const value = useStore((state) => state.value);\n const steps = useStore((state) => state.steps);\n\n const isLastStep = value === steps.length - 1;\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<NextElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n store.setState(\"value\", value + 1);\n },\n [value, store, onClickProp]\n );\n\n return (\n <Button type=\"button\" data-slot=\"tour-next\" {...nextButtonProps} onClick={onClick}>\n {children ?? (\n <>\n {isLastStep ? context.labels.finish : context.labels.next}\n {!isLastStep && <ChevronRight aria-hidden=\"true\" className=\"size-4\" />}\n </>\n )}\n </Button>\n );\n}\n\ntype TourSkipProps = React.ComponentProps<typeof Button>;\n\nfunction TourSkip(props: TourSkipProps) {\n const { children, onClick: onClickProp, ...skipButtonProps } = props;\n\n const store = useStoreContext(SKIP_NAME);\n const context = useTourContext(SKIP_NAME);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<SkipElement>) => {\n onClickProp?.(event);\n if (event.defaultPrevented) return;\n\n store.setState(\"open\", false);\n },\n [store, onClickProp]\n );\n\n return (\n <Button type=\"button\" data-slot=\"tour-skip\" variant=\"outline\" {...skipButtonProps} onClick={onClick}>\n {children ?? context.labels.skip}\n </Button>\n );\n}\n\ninterface TourStepCounterProps extends DivProps {\n /** Mặc định `labels.stepCounter` của `Tour` — `Bước 2/5`. */\n format?: (current: number, total: number) => string;\n}\n\nfunction TourStepCounter(props: TourStepCounterProps) {\n const { format: formatProp, asChild, className, children, ...stepCounterProps } = props;\n\n const context = useTourContext(STEP_COUNTER_NAME);\n const format = formatProp ?? context.labels.stepCounter;\n\n const value = useStore((state) => state.value);\n const steps = useStore((state) => state.steps);\n\n const StepCounterPrimitive = asChild ? Slot : \"div\";\n\n return (\n <StepCounterPrimitive\n data-slot=\"tour-step-counter\"\n {...stepCounterProps}\n // ⚠ Khác thượng nguồn: `whitespace-nowrap` — \"Bước 1/3\" đặt cạnh \"Quay lại\"/\"Tiếp theo\" trong\n // thẻ `w-80` bị bẻ làm hai dòng (đo trên trình duyệt); con số đếm bước không nên vỡ dòng.\n className={cn(\"whitespace-nowrap text-sm text-muted-foreground\", className)}\n >\n {children ?? format(value + 1, steps.length)}\n </StepCounterPrimitive>\n );\n}\n\ntype TourFooterProps = DivProps;\n\nfunction TourFooter(props: TourFooterProps) {\n const { asChild, className, ref, ...footerProps } = props;\n\n const stepContext = useStepContext(FOOTER_NAME);\n const hasDefaultFooter = React.useContext(DefaultFooterContext);\n const context = useTourContext(FOOTER_NAME);\n\n const composedRef = useComposedRefs(ref, hasDefaultFooter ? undefined : stepContext.onFooterChange);\n\n const FooterPrimitive = asChild ? Slot : \"div\";\n\n return (\n <FooterPrimitive\n data-slot=\"tour-footer\"\n dir={context.dir}\n {...footerProps}\n ref={composedRef}\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n />\n );\n}\n\nexport {\n Tour,\n TourArrow,\n type TourArrowProps,\n TourClose,\n type TourCloseProps,\n TourDescription,\n type TourDescriptionProps,\n TourFooter,\n type TourFooterProps,\n TourHeader,\n type TourHeaderProps,\n TourNext,\n type TourNextProps,\n TourPortal,\n type TourPortalProps,\n TourPrev,\n type TourPrevProps,\n type TourProps,\n TourSkip,\n type TourSkipProps,\n TourSpotlight,\n type TourSpotlightProps,\n TourSpotlightRing,\n type TourSpotlightRingProps,\n TourStep,\n TourStepCounter,\n type TourStepCounterProps,\n type TourStepProps,\n TourTitle,\n type TourTitleProps\n};\n"],"mappings":";AA41BU,SA6wBF,UA7wBE,KA8aJ,YA9aI;AAr1BV;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA,SAAS;AAAA,EAET;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,YAAY;AACrB,SAAS,aAAa,cAAc,SAAS;AAC7C,YAAY,WAAW;AACvB,YAAY,cAAc;AAC1B,SAAS,UAAU;AACnB,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AACzB,SAAS,iCAAiC;AAC1C,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,2BAA4C;AAErD,MAAM,YAAY;AAClB,MAAM,cAAc;AACpB,MAAM,YAAY;AAClB,MAAM,aAAa;AACnB,MAAM,cAAc;AACpB,MAAM,aAAa;AACnB,MAAM,mBAAmB;AACzB,MAAM,aAAa;AACnB,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,oBAAoB;AAC1B,MAAM,cAAc;AAEpB,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AACzB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AACzB,MAAM,gBAAgB,EAAE,SAAS,OAAO,YAAY,KAAK;AAEzD,MAAM,eAAe,CAAC,OAAO,SAAS,UAAU,MAAM;AACtD,MAAM,gBAAgB,CAAC,SAAS,UAAU,KAAK;AAE/C,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;AAC5B,MAAM,4BAA4B;AAmBlC,MAAM,iBAA6B,CAAC;AAoBpC,MAAM,gBAAoC;AAAA,EACxC,KAAK;AAAA,EACL,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AACR;AAKA,IAAI,kBAAkB;AAEtB,SAAS,mBAAmB;AAC1B,QAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,UAAQ,aAAa,yBAAyB,EAAE;AAChD,UAAQ,WAAW;AACnB,UAAQ,MAAM,UAAU;AACxB,UAAQ,MAAM,UAAU;AACxB,UAAQ,MAAM,WAAW;AACzB,UAAQ,MAAM,gBAAgB;AAC9B,SAAO;AACT;AAQA,SAAS,eAAe,SAAkB;AACxC,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAS;AAEd,UAAM,aAAa,SAAS,iBAAiB,yBAAyB;AACtE,aAAS,KAAK,sBAAsB,cAAc,WAAW,CAAC,KAAK,iBAAiB,CAAC;AACrF,aAAS,KAAK,sBAAsB,aAAa,WAAW,CAAC,KAAK,iBAAiB,CAAC;AACpF;AAEA,WAAO,MAAM;AACX,UAAI,oBAAoB,GAAG;AACzB,cAAM,SAAS,SAAS,iBAAiB,yBAAyB;AAClE,mBAAW,QAAQ,QAAQ;AACzB,eAAK,OAAO;AAAA,QACd;AAAA,MACF;AACA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AACd;AAEA,SAAS,aACP,cACA,SACA,UACA,iBACA,kBACA;AACA,QAAM,wBAAwB,MAAM,OAA2B,IAAI;AACnE,QAAM,qBAAqB,SAAS,eAAe;AACnD,QAAM,sBAAsB,SAAS,gBAAgB;AACrD,QAAM,cAAc,SAAS,QAAQ;AAErC,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAS;AAEd,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,UAAW;AAEhB,UAAM,2BAA2B,SAAS;AAE1C,aAAS,wBAAwB;AAC/B,UAAI,CAAC,UAAW,QAAO,CAAC;AAExB,YAAM,QAAuB,CAAC;AAC9B,YAAM,SAAS,SAAS,iBAAiB,WAAW,WAAW,cAAc;AAAA,QAC3E,YAAY,CAAC,SAAkB;AAC7B,gBAAM,UAAU;AAChB,gBAAM,gBAAgB,QAAQ,YAAY,WAAY,QAA6B,SAAS;AAO5F,gBAAM,aAAc,QAA8B,aAAa;AAC/D,cAAI,QAAQ,UAAU,iBAAiB,WAAY,QAAO,WAAW;AACrE,iBAAO,QAAQ,YAAY,IAAI,WAAW,gBAAgB,WAAW;AAAA,QACvE;AAAA,MACF,CAAC;AACD,aAAO,OAAO,SAAS,GAAG;AACxB,cAAM,KAAK,OAAO,WAA0B;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAEA,aAAS,mBAAmB;AAC1B,YAAM,aAAa,sBAAsB;AACzC,YAAM,QAAQ,WAAW,CAAC;AAC1B,YAAM,OAAO,WAAW,WAAW,SAAS,CAAC;AAC7C,aAAO,CAAC,OAAO,IAAI;AAAA,IACrB;AAEA,aAAS,UAAU,OAAmB;AACpC,UAAI,CAAC,UAAW;AAEhB,YAAM,SAAS,MAAM;AACrB,UAAI,UAAU,SAAS,MAAM,GAAG;AAC9B,8BAAsB,UAAU;AAAA,MAClC,OAAO;AACL,cAAM,iBAAiB,sBAAsB,WAAW,sBAAsB,EAAE,CAAC;AACjF,wBAAgB,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF;AAEA,aAAS,UAAU,OAAsB;AACvC,UAAI,MAAM,QAAQ,SAAS,MAAM,UAAU,MAAM,WAAW,MAAM,QAAS;AAE3E,YAAM,CAAC,OAAO,IAAI,IAAI,iBAAiB;AACvC,YAAM,sBAAsB,SAAS;AAErC,UAAI,CAAC,qBAAqB;AACxB,YAAI,SAAS,kBAAkB,UAAW,OAAM,eAAe;AAC/D;AAAA,MACF;AAEA,UAAI,CAAC,MAAM,YAAY,SAAS,kBAAkB,MAAM;AACtD,cAAM,eAAe;AACrB,eAAO,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,MACtC,WAAW,MAAM,YAAY,SAAS,kBAAkB,OAAO;AAC7D,cAAM,eAAe;AACrB,cAAM,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,MACrC;AAAA,IACF;AAEA,UAAM,qBAAqB,IAAI,YAAY,iBAAiB,aAAa;AACzE,QAAI,mBAAmB,SAAS;AAC9B,gBAAU,iBAAiB,iBAAiB,mBAAmB,SAA0B,EAAE,MAAM,KAAK,CAAC;AAAA,IACzG;AACA,cAAU,cAAc,kBAAkB;AAE1C,QAAI,CAAC,mBAAmB,kBAAkB;AACxC,YAAM,qBAAqB,sBAAsB;AACjD,UAAI,mBAAmB,SAAS,GAAG;AACjC,2BAAmB,CAAC,GAAG,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,MACtD,OAAO;AACL,kBAAU,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,MACzC;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,SAAS;AAC9C,cAAU,iBAAiB,WAAW,SAAS;AAE/C,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,SAAS;AACjD,gBAAU,oBAAoB,WAAW,SAAS;AAElD,UAAI,CAAC,YAAY,SAAS;AACxB,mBAAW,MAAM;AACf,gBAAM,sBAAsB,IAAI,YAAY,kBAAkB,aAAa;AAC3E,cAAI,oBAAoB,SAAS;AAC/B,sBAAU,iBAAiB,kBAAkB,oBAAoB,SAA0B;AAAA,cACzF,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AACA,oBAAU,cAAc,mBAAmB;AAE3C,cAAI,CAAC,oBAAoB,kBAAkB;AACzC,gBAAI,4BAA4B,SAAS,KAAK,SAAS,wBAAwB,GAAG;AAChF,uCAAyB,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,YACxD;AAAA,UACF;AAEA,cAAI,oBAAoB,SAAS;AAC/B,sBAAU,oBAAoB,kBAAkB,oBAAoB,OAAwB;AAAA,UAC9F;AAAA,QACF,GAAG,CAAC;AAAA,MACN;AAAA,IACF;AAAA,EACF,GAAG,CAAC,cAAc,SAAS,oBAAoB,qBAAqB,WAAW,CAAC;AAClF;AAEA,SAAS,aAAa,MAAe;AACnC,SAAO,OAAO,SAAS;AACzB;AAwCA,SAAS,SAAY,UAAoC,SAA2B;AAClF,QAAM,eAAe,MAAM,WAAW,YAAY;AAElD,QAAM,QAAQ,WAAW;AAEzB,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,sEAA0C,SAAS,IAAI;AAAA,EACzE;AAEA,QAAM,cAAc,MAAM,YAAY,MAAM,SAAS,MAAM,SAAS,CAAC,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEzF,SAAO,MAAM,qBAAqB,MAAM,WAAW,aAAa,WAAW;AAC7E;AAEA,SAAS,iBAAiB,QAAwC;AAChE,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO,SAAS,cAAc,MAAM;AAAA,EACtC;AACA,MAAI,UAAU,aAAa,QAAQ;AACjC,WAAO,OAAO;AAAA,EAChB;AACA,MAAI,kBAAkB,aAAa;AACjC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,2BAA2C;AAClD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,OAAO,WAAW,kCAAkC,EAAE,UAAU,SAAS;AAClF;AAQA,SAAS,gBAAgB,SAA0C;AACjE,MAAI,SAAS,QAAQ;AACrB,SAAO,UAAU,WAAW,SAAS,QAAQ,WAAW,SAAS,iBAAiB;AAChF,UAAM,EAAE,UAAU,IAAI,OAAO,iBAAiB,MAAM;AACpD,UAAM,eAAe,cAAc,UAAU,cAAc,YAAY,cAAc;AACrF,QAAI,gBAAgB,OAAO,eAAe,OAAO,aAAc,QAAO;AACtE,aAAS,OAAO;AAAA,EAClB;AACA,SAAO;AACT;AAEA,SAAS,kBACP,SACA,iBAAiC,yBAAyB,GAC1D,cACA;AACA,QAAMA,UAAiC;AAAA,IACrC,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,GAAG;AAAA,EACL;AACA,QAAM,OAAO,QAAQ,sBAAsB;AAC3C,QAAM,iBAAiB,OAAO;AAC9B,QAAM,gBAAgB,OAAO;AAE7B,QAAM,eACJ,KAAK,OAAOA,QAAO,OACnB,KAAK,UAAU,iBAAiBA,QAAO,UACvC,KAAK,QAAQA,QAAO,QACpB,KAAK,SAAS,gBAAgBA,QAAO;AAEvC,MAAI,CAAC,cAAc;AACjB,UAAM,eAAe,gBAAgB,OAAO;AAC5C,QAAI,cAAc;AAGhB,mBAAa,SAAS,EAAE,KAAK,KAAK,MAAMA,QAAO,KAAK,UAAU,eAAe,CAAC;AAC9E;AAAA,IACF;AAEA,UAAM,aAAa,KAAK,MAAM,OAAO;AACrC,UAAM,YAAY,aAAaA,QAAO;AAEtC,WAAO,SAAS;AAAA,MACd,KAAK,KAAK,IAAI,GAAG,SAAS;AAAA,MAC1B,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AACF;AAEA,SAAS,6BAA6B,WAAqC;AACzE,QAAM,CAAC,MAAM,QAAQ,QAAQ,IAAI,UAAU,MAAM,GAAG;AACpD,SAAO,CAAC,MAAM,KAAK;AACrB;AAEA,SAAS,aAAa,MAAY,OAAyB;AACzD,MAAI,UAAU,UAAU;AACtB,WAAO;AAAA,EACT;AACA,SAAO,GAAG,IAAI,IAAI,KAAK;AACzB;AAEA,SAAS,WAAW,OAAc,eAA4B,UAAkB,2BAA2B;AACzG,QAAM,aAAa,cAAc,sBAAsB;AACvD,QAAM,gBAAgB,OAAO;AAC7B,QAAM,iBAAiB,OAAO;AAE9B,QAAM,IAAI,KAAK,IAAI,GAAG,WAAW,OAAO,OAAO;AAC/C,QAAM,IAAI,KAAK,IAAI,GAAG,WAAW,MAAM,OAAO;AAC9C,QAAM,QAAQ,KAAK,IAAI,gBAAgB,GAAG,WAAW,QAAQ,UAAU,CAAC;AACxE,QAAM,SAAS,KAAK,IAAI,iBAAiB,GAAG,WAAW,SAAS,UAAU,CAAC;AAE3E,QAAM,OAAO,2BAA2B,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,IAAI,KAAK,MAAM,CAAC,OAAO,IAAI,KAAK,MAAM,IAAI,MAAM,OAAO,CAAC,MAAM,IAAI,MAAM,OAAO,CAAC;AACpJ,QAAM,SAAS,YAAY,IAAI;AAC/B,QAAM,SAAS,iBAAiB,EAAE,GAAG,GAAG,OAAO,OAAO,CAAC;AACzD;AAEA,MAAM,eAAe,MAAM,cAA4B,IAAI;AAE3D,SAAS,gBAAgB,cAAsB;AAC7C,QAAM,UAAU,MAAM,WAAW,YAAY;AAC7C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,KAAK,YAAY,wCAA2B,SAAS,IAAI;AAAA,EAC3E;AACA,SAAO;AACT;AAiBA,MAAM,cAAc,MAAM,cAAuC,IAAI;AAErE,SAAS,eAAe,cAAsB;AAC5C,QAAM,UAAU,MAAM,WAAW,WAAW;AAC5C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,KAAK,YAAY,wCAA2B,SAAS,IAAI;AAAA,EAC3E;AACA,SAAO;AACT;AAeA,MAAM,cAAc,MAAM,cAAuC,IAAI;AAErE,SAAS,eAAe,cAAsB;AAC5C,QAAM,UAAU,MAAM,WAAW,WAAW;AAC5C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,KAAK,YAAY,wCAA2B,SAAS,IAAI;AAAA,EAC3E;AACA,SAAO;AACT;AAEA,MAAM,uBAAuB,MAAM,cAAc,KAAK;AAOtD,MAAM,gBAAgB,MAAM,cAAyC,IAAI;AAEzE,SAAS,iBAAiB,cAAsB;AAC9C,QAAM,UAAU,MAAM,WAAW,aAAa;AAC9C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,KAAK,YAAY,wCAA2B,SAAS,IAAI;AAAA,EAC3E;AACA,SAAO;AACT;AAEA,SAAS,cAAc,SAAkB;AACvC,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAS;AAEd,UAAM,gBAAgB,OAAO,iBAAiB,SAAS,IAAI,EAAE;AAC7D,UAAM,iBAAiB,OAAO,aAAa,SAAS,gBAAgB;AAEpE,aAAS,KAAK,MAAM,WAAW;AAC/B,QAAI,iBAAiB,GAAG;AACtB,eAAS,KAAK,MAAM,eAAe,GAAG,cAAc;AAAA,IACtD;AAEA,WAAO,MAAM;AACX,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,eAAe;AAAA,IACrC;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AACd;AAyCA,SAAS,KAAK,OAAkB;AAC9B,QAAM;AAAA,IACJ,MAAM;AAAA,IACN,cAAc;AAAA,IACd;AAAA,IACA,OAAO;AAAA,IACP,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK;AAAA,IACL,cAAc;AAAA,IACd,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,cAAc;AAAA,IACd,QAAQ;AAAA,IACR;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,MAAM,aAAa,OAAO;AAEhC,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA6B,IAAI;AACnE,QAAM,cAAc,MAAM,OAA4B,MAAS;AAC/D,QAAM,8BAA8B,MAAM,OAA2B,IAAI;AAEzE,QAAM,WAAW,WAAuB,OAAO;AAAA,IAC7C,MAAM,YAAY;AAAA,IAClB,OAAO,aAAa;AAAA,IACpB,OAAO,CAAC;AAAA,IACR,UAAU;AAAA,IACV,eAAe;AAAA,EACjB,EAAE;AACF,QAAM,eAAe,WAA4B,MAAM,oBAAI,IAAI,CAAC;AAChE,QAAM,gBAAgB,WAAgC,MAAM,oBAAI,IAAI,CAAC;AACrE,QAAM,mBAAmB,WAAW,OAAO,EAAE,SAAS,EAAE,EAAE;AAC1D,QAAM,WAAW,SAAS;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,QAAe,MAAM;AAAA,IACzB,OAAO;AAAA,MACL,WAAW,CAAC,OAAO;AACjB,qBAAa,QAAQ,IAAI,EAAE;AAC3B,eAAO,MAAM,aAAa,QAAQ,OAAO,EAAE;AAAA,MAC7C;AAAA,MACA,UAAU,MAAM;AACd,eAAO,SAAS;AAAA,MAClB;AAAA,MACA,UAAU,CAAC,KAAK,UAAU;AACxB,YAAI,OAAO,GAAG,SAAS,QAAQ,GAAG,GAAG,KAAK,EAAG;AAC7C,iBAAS,QAAQ,GAAG,IAAI;AAExB,YAAI,QAAQ,UAAU,OAAO,UAAU,WAAW;AAChD,mBAAS,QAAQ,eAAe,KAAK;AAErC,cAAI,OAAO;AACT,gBAAI,SAAS,QAAQ,MAAM,SAAS,GAAG;AACrC,kBAAI,SAAS,QAAQ,SAAS,SAAS,QAAQ,MAAM,QAAQ;AAC3D,sBAAM,SAAS,SAAS,CAAC;AAAA,cAC3B;AAAA,YACF;AAAA,UACF,OAAO;AACL,gBAAI,SAAS,QAAQ,SAAS,SAAS,QAAQ,MAAM,UAAU,KAAK,GAAG;AACrE,uBAAS,QAAQ,SAAS;AAAA,YAC5B;AAAA,UACF;AAAA,QACF,WAAW,QAAQ,WAAW,OAAO,UAAU,UAAU;AACvD,gBAAM,WAAW,SAAS,QAAQ,MAAM,SAAS,QAAQ,KAAK;AAC9D,gBAAM,WAAW,SAAS,QAAQ,MAAM,KAAK;AAE7C,oBAAU,cAAc;AACxB,oBAAU,cAAc;AAExB,cAAI,SAAS,SAAS,QAAQ,MAAM,QAAQ;AAC1C,qBAAS,QAAQ,aAAa;AAE9B,gBAAI,SAAS,QAAQ,cAAc,QAAW;AAC5C,uBAAS,QAAQ,gBAAgB,KAAK;AAAA,YACxC;AAEA,kBAAM,SAAS,QAAQ,KAAK;AAC5B;AAAA,UACF;AAEA,cAAI,SAAS,QAAQ,cAAc,QAAW;AAC5C,qBAAS,QAAQ,gBAAgB,KAAK;AACtC;AAAA,UACF;AAEA,mBAAS,QAAQ,gBAAgB,KAAK;AAEtC,cAAI,YAAY,SAAS,QAAQ,YAAY;AAC3C,kBAAM,gBAAgB,iBAAiB,SAAS,MAAM;AACtD,gBAAI,eAAe;AACjB,gCAAkB,eAAe,SAAS,QAAQ,gBAAgB,SAAS,QAAQ,YAAY;AAAA,YACjG;AAAA,UACF;AAAA,QACF;AAEA,cAAM,OAAO;AAAA,MACf;AAAA,MACA,QAAQ,MAAM;AACZ,mBAAW,YAAY,aAAa,SAAS;AAC3C,mBAAS;AAAA,QACX;AAAA,MACF;AAAA,MACA,SAAS,CAAC,aAAa;AACrB,cAAM,KAAK,QAAQ,iBAAiB,QAAQ,SAAS;AACrD,cAAM,QAAQ,SAAS,QAAQ,MAAM;AACrC,sBAAc,QAAQ,IAAI,IAAI,KAAK;AACnC,iBAAS,QAAQ,QAAQ,CAAC,GAAG,SAAS,QAAQ,OAAO,QAAQ;AAC7D,cAAM,OAAO;AACb,eAAO,EAAE,IAAI,MAAM;AAAA,MACrB;AAAA,MACA,YAAY,CAAC,OAAO;AAClB,cAAM,QAAQ,cAAc,QAAQ,IAAI,EAAE;AAC1C,YAAI,UAAU,OAAW;AAEzB,iBAAS,QAAQ,QAAQ,SAAS,QAAQ,MAAM,OAAO,CAAC,GAAG,MAAM,MAAM,KAAK;AAE5E,sBAAc,QAAQ,OAAO,EAAE;AAE/B,mBAAW,CAAC,QAAQ,SAAS,KAAK,cAAc,QAAQ,QAAQ,GAAG;AACjE,cAAI,YAAY,OAAO;AACrB,0BAAc,QAAQ,IAAI,QAAQ,YAAY,CAAC;AAAA,UACjD;AAAA,QACF;AAEA,cAAM,OAAO;AAAA,MACf;AAAA,MACA,YAAY,CAAC,IAAI,aAAa;AAC5B,cAAM,QAAQ,cAAc,QAAQ,IAAI,EAAE;AAC1C,YAAI,UAAU,OAAW;AACzB,YAAI,SAAS,QAAQ,MAAM,KAAK,MAAM,SAAU;AAEhD,cAAM,QAAQ,CAAC,GAAG,SAAS,QAAQ,KAAK;AACxC,cAAM,KAAK,IAAI;AACf,iBAAS,QAAQ,QAAQ;AACzB,cAAM,OAAO;AAAA,MACf;AAAA,MACA,cAAc,CAAC,OAAO,cAAc,QAAQ,IAAI,EAAE,KAAK;AAAA,IACzD;AAAA,IACA,CAAC,UAAU,cAAc,eAAe,kBAAkB,QAAQ;AAAA,EACpE;AAEA,QAAM,OAAO,SAAS,CAAC,UAAU,MAAM,MAAM,KAAK;AAYlD,QAAM,UAAU,MAAM;AACpB,aAAS,UAAU,OAAsB;AACvC,UAAI,CAAC,QAAQ,MAAM,QAAQ,YAAY,MAAM,oBAAoB,MAAM,YAAa;AAEpF,UAAI,SAAS,QAAQ,iBAAiB;AACpC,iBAAS,QAAQ,gBAAgB,KAAK;AACtC,YAAI,MAAM,iBAAkB;AAAA,MAC9B;AACA,YAAM,eAAe;AACrB,YAAM,SAAS,QAAQ,KAAK;AAAA,IAC9B;AAEA,aAAS,iBAAiB,WAAW,WAAW,EAAE,SAAS,KAAK,CAAC;AACjE,WAAO,MAAM,SAAS,oBAAoB,WAAW,WAAW,EAAE,SAAS,KAAK,CAAC;AAAA,EACnF,GAAG,CAAC,OAAO,MAAM,QAAQ,CAAC;AAE1B,4BAA0B,MAAM;AAC9B,UAAM,UAAU,YAAY;AAE5B,QAAI,QAAQ,CAAC,SAAS;AACpB,kCAA4B,UAAU,SAAS;AAAA,IACjD,WAAW,CAAC,QAAQ,SAAS;AAC3B,iBAAW,MAAM;AACf,cAAM,YAAY,UAAU,SAAS;AACrC,cAAM,sBAAsB,IAAI,YAAY,kBAAkB,aAAa;AAE3E,YAAI,SAAS,QAAQ,kBAAkB;AACrC,oBAAU,iBAAiB,kBAAkB,SAAS,QAAQ,kBAAmC;AAAA,YAC/F,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,kBAAU,cAAc,mBAAmB;AAE3C,YAAI,CAAC,oBAAoB,kBAAkB;AACzC,gBAAM,iBAAiB,4BAA4B;AACnD,cAAI,kBAAkB,SAAS,KAAK,SAAS,cAAc,GAAG;AAC5D,2BAAe,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,UAC9C;AAAA,QACF;AAEA,oCAA4B,UAAU;AAAA,MACxC,GAAG,CAAC;AAAA,IACN;AAEA,gBAAY,UAAU;AAAA,EACxB,GAAG,CAAC,MAAM,QAAQ,QAAQ,CAAC;AAE3B,4BAA0B,MAAM;AAC9B,QAAI,aAAa,QAAW;AAC1B,YAAM,SAAS,QAAQ,QAAQ;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,UAAU,KAAK,CAAC;AAEpB,4BAA0B,MAAM;AAC9B,QAAI,cAAc,QAAW;AAC3B,YAAM,SAAS,SAAS,SAAS;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,WAAW,KAAK,CAAC;AAErB,QAAM,SAAS,MAAM,QAAoB,OAAO,EAAE,GAAG,qBAAqB,GAAG,WAAW,IAAI,CAAC,UAAU,CAAC;AAExG,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,qBAAqB,MAAM;AAAA,IAC/B,OAAO;AAAA,MACL;AAAA,MACA,gBAAgB;AAAA,IAClB;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,gBAAc,QAAQ,KAAK;AAE3B,QAAM,gBAAgB,UAAU,OAAO;AAEvC,SACE,oBAAC,aAAa,UAAb,EAAsB,OAAO,OAC5B,8BAAC,YAAY,UAAZ,EAAqB,OAAO,cAC3B,8BAAC,cAAc,UAAd,EAAuB,OAAO,oBAC7B,8BAAC,iBAAc,aAAU,QAAO,KAAW,GAAG,WAAW,GAC3D,GACF,GACF;AAEJ;AAoBA,SAAS,SAAS,OAAsB;AACtC,QAAM;AAAA,IACJ;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA,oBAAoB;AAAA,IACpB,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,SAAS;AAAA,IACT,mBAAmB;AAAA,IACnB,kBAAkB;AAAA,IAClB,WAAW;AAAA,IACX,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,IACtB,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,QAAQ,gBAAgB,SAAS;AAEvC,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAiC,IAAI;AACrE,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA+B,IAAI;AACrE,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAA6B,IAAI;AAEjF,QAAM,UAAU,MAAM,OAA2B,IAAI;AACrD,QAAM,YAAY,MAAM,OAAe,EAAE;AACzC,QAAM,8BAA8B,MAAM,OAAO,KAAK;AACtD,QAAM,4BAA4B,MAAM,OAAO,KAAK;AAEpD,QAAM,UAAU,MAAM,MAAM;AAC5B,QAAM,gBAAgB,MAAM,MAAM;AAElC,QAAM,OAAO,SAAS,CAAC,UAAU,MAAM,IAAI;AAC3C,QAAM,QAAQ,SAAS,CAAC,UAAU,MAAM,KAAK;AAC7C,QAAM,QAAQ,SAAS,CAAC,UAAU,MAAM,KAAK;AAC7C,QAAM,UAAU,eAAe,SAAS;AAExC,QAAM,qBAAqB,cAAc,QAAQ;AACjD,QAAM,sBAAsB,eAAe,QAAQ;AAYnD,QAAM,eAAe,SAAS,EAAE,aAAa,YAAY,CAAC;AAE1D,QAAM,cAAc,MAAM;AAAA,IACxB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,MAAM,aAAa,QAAQ,cAAc;AAAA,MACtD,aAAa,MAAM,aAAa,QAAQ,cAAc;AAAA,MACtD;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,iBAAiB,SAAS,WAAW;AAE3C,4BAA0B,MAAM;AAC9B,UAAM,EAAE,GAAG,IAAI,MAAM,QAAQ,eAAe,OAAO;AACnD,cAAU,UAAU;AAEpB,WAAO,MAAM;AACX,YAAM,WAAW,EAAE;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,OAAO,cAAc,CAAC;AAE1B,4BAA0B,MAAM;AAC9B,UAAM,WAAW,UAAU,SAAS,WAAW;AAAA,EACjD,GAAG,CAAC,OAAO,WAAW,CAAC;AAEvB,QAAM,WAAW,MAAM,KAAK;AAI5B,QAAM,gBAAgB,MAAM,aAAa,UAAU,OAAO,MAAM;AAQhE,4BAA0B,MAAM;AAC9B,qBAAiB,QAAQ,iBAAiB,WAAW,iBAAiB,SAAS,MAAM,IAAI,IAAI;AAAA,EAC/F,CAAC;AAQD,QAAM,iBAAiB,QAAQ,iBAAiB,CAAC,CAAC,aAAa,CAAC,CAAC,iBAAiB;AAElF,QAAM,aAAa,MAAM,QAAQ,MAAM;AACrC,QAAI,CAAC,SAAU,QAAO,CAAC;AAEvB,UAAM,iBAAiB,SAAS,cAAc;AAC9C,UAAM,kBAAkB,SAAS,eAAe;AAEhD,UAAM,UACJ,OAAO,SAAS,qBAAqB,WACjC,SAAS,mBACT;AAAA,MACE,KAAK,SAAS,kBAAkB,OAAO;AAAA,MACvC,OAAO,SAAS,kBAAkB,SAAS;AAAA,MAC3C,QAAQ,SAAS,kBAAkB,UAAU;AAAA,MAC7C,MAAM,SAAS,kBAAkB,QAAQ;AAAA,IAC3C;AAEN,UAAM,WAAW,MAAM,QAAQ,SAAS,iBAAiB,IACrD,SAAS,oBACT,SAAS,oBACP,CAAC,SAAS,iBAAiB,IAC3B,CAAC;AACP,UAAM,wBAAwB,SAAS,SAAS;AAEhD,UAAM,wBAAwB;AAAA,MAC5B;AAAA,MACA,UAAU,SAAS,OAAO,CAAC,MAAoB,MAAM,IAAI;AAAA,MACzD,aAAa;AAAA,IACf;AAEA,WAAO;AAAA,MACL,OAAO;AAAA,QACL,UAAU;AAAA,QACV,eAAe;AAAA,MACjB,CAAC;AAAA,MACD,SAAS,mBACP,MAAM;AAAA,QACJ,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS,SAAS,WAAW,YAAY,WAAW,IAAI;AAAA,QACxD,GAAG;AAAA,MACL,CAAC;AAAA,MACH,SAAS,mBAAmB,KAAK,EAAE,GAAG,sBAAsB,CAAC;AAAA,MAC7D,SAAS,QAAQ,EAAE,SAAS,OAAO,SAAS,SAAS,aAAa,CAAC;AAAA,MACnE,SAAS,oBACP,KAAK;AAAA,QACH,UAAU;AAAA,QACV,GAAG;AAAA,MACL,CAAC;AAAA,IACL,EAAE,OAAO,OAAO;AAAA,EAClB,GAAG,CAAC,UAAU,oBAAoB,qBAAqB,KAAK,CAAC;AAE7D,QAAM,YAAY,aAAa,UAAU,QAAQ,MAAM,UAAU,SAAS,KAAK;AAE/E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,EACF,IAAI,YAAY;AAAA,IACd;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,sBAAsB;AAAA,IACtB,UAAU;AAAA,MACR,WAAW;AAAA,IACb;AAAA,EACF,CAAC;AAED,QAAM,cAAc,gBAAgB,KAAK,aAAa,OAAO;AAE7D,QAAM,CAAC,YAAY,WAAW,IAAI,6BAA6B,cAAc;AAE7E,QAAM,SAAS,eAAe,OAAO;AACrC,QAAM,SAAS,eAAe,OAAO;AACrC,QAAM,oBAAoB,eAAe,OAAO,iBAAiB;AACjE,QAAM,WAAW,oBAAoB,eAAe,MAAM;AAE1D,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,MACjB;AAAA,MACA;AAAA,MACA,eAAe;AAAA,MACf,gBAAgB;AAAA,IAClB;AAAA,IACA,CAAC,QAAQ,QAAQ,YAAY,aAAa,mBAAmB,SAAS,aAAa;AAAA,EACrF;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,QAAQ,iBAAiB,eAAe;AAK1C,UAASC,YAAT,WAAoB;AAClB,YAAI,eAAe;AACjB,qBAAW,OAAO,eAAe,QAAQ,gBAAgB;AAAA,QAC3D;AAAA,MACF,GAESC,YAAT,WAAoB;AAClB,YAAI,UAAU,KAAM;AACpB,gBAAQ,sBAAsB,MAAM;AAClC,cAAI,eAAe;AACjB,uBAAW,OAAO,eAAe,QAAQ,gBAAgB;AAAA,UAC3D;AACA,kBAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAdS,qBAAAD,WAMA,WAAAC;AAVT,iBAAW,OAAO,eAAe,QAAQ,gBAAgB;AAEzD,UAAI,QAAuB;AAkB3B,aAAO,iBAAiB,UAAUD,SAAQ;AAM1C,aAAO,iBAAiB,UAAUC,WAAU,EAAE,SAAS,MAAM,SAAS,KAAK,CAAC;AAC5E,aAAO,MAAM;AACX,eAAO,oBAAoB,UAAUD,SAAQ;AAC7C,eAAO,oBAAoB,UAAUC,WAAU,EAAE,SAAS,KAAK,CAAC;AAChE,YAAI,UAAU,MAAM;AAClB,+BAAqB,KAAK;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,MAAM,eAAe,eAAe,OAAO,QAAQ,gBAAgB,CAAC;AAExE,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,eAAgB;AAErB,UAAM,cAAc,QAAQ;AAC5B,QAAI,CAAC,YAAa;AAElB,UAAM,gBAAgB,YAAY;AAElC,aAAS,cAAc,OAAqB;AAC1C,UAAI,MAAM,UAAU,CAAC,4BAA4B,SAAS;AACxD,cAAM,0BAA0B,IAAI,YAAY,sBAAsB;AAAA,UACpE,GAAG;AAAA,UACH,QAAQ,EAAE,eAAe,MAAM;AAAA,QACjC,CAAC;AAED,gBAAQ,uBAAuB,uBAAuB;AAEtD,cAAM,uBAAuB,IAAI,YAAY,kBAAkB;AAAA,UAC7D,GAAG;AAAA,UACH,QAAQ,EAAE,eAAe,MAAM;AAAA,QACjC,CAAC;AACD,gBAAQ,oBAAoB,oBAAoB;AAEhD,YACE,CAAC,wBAAwB,oBACzB,CAAC,qBAAqB,oBACtB,QAAQ,aACR;AACA,gBAAM,SAAS,QAAQ,KAAK;AAAA,QAC9B;AAAA,MACF;AAEA,kCAA4B,UAAU;AAAA,IACxC;AAEA,UAAM,UAAU,OAAO,WAAW,MAAM;AACtC,oBAAc,iBAAiB,eAAe,aAAa;AAAA,IAC7D,GAAG,CAAC;AAEJ,WAAO,MAAM;AACX,aAAO,aAAa,OAAO;AAC3B,oBAAc,oBAAoB,eAAe,aAAa;AAAA,IAChE;AAAA,EACF,GAAG,CAAC,gBAAgB,OAAO,OAAO,CAAC;AAEnC,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,eAAgB;AAErB,UAAM,cAAc,QAAQ;AAC5B,QAAI,CAAC,YAAa;AAElB,UAAM,gBAAgB,YAAY;AAElC,aAAS,UAAU,OAAmB;AACpC,YAAMC,UAAS,MAAM;AAErB,YAAM,gBAAgB,aAAa,SAASA,OAAM;AAClD,YAAM,kBAAkB,eAAe,SAASA,OAAM;AAEtD,UAAI,MAAM,UAAU,CAAC,0BAA0B,WAAW,CAAC,iBAAiB,CAAC,iBAAiB;AAC5F,cAAM,uBAAuB,IAAI,YAAY,kBAAkB;AAAA,UAC7D,GAAG;AAAA,UACH,QAAQ,EAAE,eAAe,MAAM;AAAA,QACjC,CAAC;AAED,gBAAQ,oBAAoB,oBAAoB;AAEhD,YAAI,CAAC,qBAAqB,oBAAoB,QAAQ,aAAa;AACjE,gBAAM,SAAS,QAAQ,KAAK;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAEA,kBAAc,iBAAiB,WAAW,SAAS;AAEnD,WAAO,MAAM;AACX,oBAAc,oBAAoB,WAAW,SAAS;AAAA,IACxD;AAAA,EACF,GAAG,CAAC,gBAAgB,OAAO,SAAS,aAAa,CAAC;AAElD,QAAM,uBAAuB,MAAM;AAAA,IACjC,CAAC,UAA2C;AAC1C,iCAA2B,KAAK;AAChC,kCAA4B,UAAU;AAAA,IACxC;AAAA,IACA,CAAC,wBAAwB;AAAA,EAC3B;AAEA,QAAM,iBAAiB,MAAM;AAAA,IAC3B,CAAC,UAAyC;AACxC,2BAAqB,KAAK;AAC1B,gCAA0B,UAAU;AAAA,IACtC;AAAA,IACA,CAAC,kBAAkB;AAAA,EACrB;AAEA,QAAM,gBAAgB,MAAM;AAAA,IAC1B,CAAC,UAAyC;AACxC,0BAAoB,KAAK;AACzB,gCAA0B,UAAU;AAAA,IACtC;AAAA,IACA,CAAC,iBAAiB;AAAA,EACpB;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,cAAe;AAE/C,aAAS,6BAA6B;AACpC,kCAA4B,UAAU;AAAA,IACxC;AAEA,aAAS,uBAAuB;AAC9B,gCAA0B,UAAU;AAAA,IACtC;AAEA,aAAS,sBAAsB;AAC7B,gCAA0B,UAAU;AAAA,IACtC;AAEA,kBAAc,iBAAiB,eAAe,4BAA4B,IAAI;AAC9E,kBAAc,iBAAiB,SAAS,sBAAsB,IAAI;AAClE,kBAAc,iBAAiB,QAAQ,qBAAqB,IAAI;AAEhE,WAAO,MAAM;AACX,oBAAc,oBAAoB,eAAe,4BAA4B,IAAI;AACjF,oBAAc,oBAAoB,SAAS,sBAAsB,IAAI;AACrE,oBAAc,oBAAoB,QAAQ,qBAAqB,IAAI;AAAA,IACrE;AAAA,EACF,GAAG,CAAC,MAAM,eAAe,aAAa,CAAC;AAEvC,iBAAe,cAAc;AAC7B,eAAa,SAAS,gBAAgB,MAAM,QAAQ,iBAAiB,QAAQ,gBAAgB;AAE7F,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,UAAU,OAAO;AAEvC,SACE,oBAAC,YAAY,UAAZ,EAAqB,OAAO,kBAC3B;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MAQL,MAAK;AAAA,MACL,mBAAiB;AAAA,MACjB,oBAAkB;AAAA,MAClB,aAAU;AAAA,MACV,aAAW;AAAA,MACX,cAAY;AAAA,MACZ,KAAK,QAAQ;AAAA,MACb,UAAU;AAAA,MACT,GAAG;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA;AAAA,QAET;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,QACH,YAAY,WAAW,WAAW;AAAA,QAClC,eAAe,WAAW,SAAS;AAAA,MACrC;AAAA,MAEC;AAAA;AAAA,QACA,CAAC,UAAU,oBAAC,qBAAqB,UAArB,EAA8B,OAAO,MAAO,kBAAQ,YAAW;AAAA;AAAA;AAAA,EAC9E,GACF;AAEJ;AAMA,SAAS,cAAc,OAA2B;AAChD,QAAM,EAAE,SAAS,WAAW,OAAO,aAAa,OAAO,GAAG,cAAc,IAAI;AAE5E,QAAM,OAAO,SAAS,CAAC,UAAU,MAAM,IAAI;AAC3C,QAAM,WAAW,SAAS,CAAC,UAAU,MAAM,QAAQ;AAEnD,MAAI,CAAC,QAAQ,CAAC,WAAY,QAAO;AAEjC,QAAM,qBAAqB,UAAU,OAAO;AAE5C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,aAAa,IAAI;AAAA,MAC5B,GAAG;AAAA,MACJ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMT;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAO;AAAA,QACL,UAAU;AAAA,QACV,GAAG;AAAA,MACL;AAAA;AAAA,EACF;AAEJ;AAMA,SAAS,kBAAkB,OAA+B;AACxD,QAAM,EAAE,SAAS,WAAW,OAAO,aAAa,OAAO,GAAG,UAAU,IAAI;AAExE,QAAM,OAAO,SAAS,CAAC,UAAU,MAAM,IAAI;AAC3C,QAAM,gBAAgB,SAAS,CAAC,UAAU,MAAM,aAAa;AAE7D,MAAI,CAAC,QAAQ,CAAC,WAAY,QAAO;AACjC,MAAI,CAAC,cAAe,QAAO;AAE3B,QAAM,gBAAgB,UAAU,OAAO;AAEvC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,aAAa,IAAI;AAAA,MAC5B,GAAG;AAAA,MACJ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMT;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAO;AAAA,QACL,MAAM,cAAc;AAAA,QACpB,KAAK,cAAc;AAAA,QACnB,OAAO,cAAc;AAAA,QACrB,QAAQ,cAAc;AAAA,QACtB,GAAG;AAAA,MACL;AAAA;AAAA,EACF;AAEJ;AAOA,SAAS,WAAW,OAAwB;AAC1C,QAAM,EAAE,UAAU,UAAU,IAAI;AAEhC,QAAM,gBAAgB,iBAAiB,WAAW;AAMlD,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA6B,IAAI;AAEzE,4BAA0B,MAAM;AAC9B,UAAM,OAAO,aAAa,SAAS;AACnC,iBAAa,IAAI;AAEjB,mBAAe,eAAe,IAAI;AAClC,WAAO,MAAM;AACX,qBAAe,eAAe,IAAI;AAAA,IACpC;AAAA,EACF,GAAG,CAAC,WAAW,aAAa,CAAC;AAE7B,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,kBAAkB,aAAa,eAAe,UAAU;AAE9D,SAAO,SAAS,aAAa,UAAU,eAAe;AACxD;AAQA,SAAS,UAAU,OAAuB;AACxC,QAAM,EAAE,QAAQ,IAAI,SAAS,GAAG,WAAW,UAAU,SAAS,GAAG,WAAW,IAAI;AAEhF,QAAM,cAAc,eAAe,UAAU;AAC7C,QAAM,WAAW,cAAc,YAAY,UAAU;AAErD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK,YAAY;AAAA,MACjB,aAAU;AAAA,MACV,OAAO;AAAA,QACL,UAAU;AAAA,QACV,MAAM,YAAY,UAAU,OAAO,GAAG,YAAY,MAAM,OAAO;AAAA,QAC/D,KAAK,YAAY,UAAU,OAAO,GAAG,YAAY,MAAM,OAAO;AAAA,QAC9D,CAAC,QAAQ,GAAG;AAAA,QACZ,iBAAiB;AAAA,UACf,KAAK;AAAA,UACL,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,EAAE,YAAY,UAAU;AAAA,QACxB,WAAW;AAAA,UACT,KAAK;AAAA,UACL,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,EAAE,YAAY,UAAU;AAAA,QACxB,YAAY,YAAY,kBAAkB,WAAW;AAAA,MACvD;AAAA,MAGA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,qBAAoB;AAAA,UACpB;AAAA,UACA;AAAA,UACA,eAAY;AAAA,UACX,GAAG;AAAA,UACJ,WAAW,GAAG,oCAAoC,SAAS;AAAA,UAE1D,oBAAU,WAAW,oBAAC,aAAQ,QAAO,kBAAiB;AAAA;AAAA,MACzD;AAAA;AAAA,EACF;AAEJ;AAIA,SAAS,WAAW,OAAwB;AAC1C,QAAM,EAAE,SAAS,WAAW,GAAG,YAAY,IAAI;AAE/C,QAAM,UAAU,eAAe,WAAW;AAE1C,QAAM,kBAAkB,UAAU,OAAO;AAEzC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,KAAK,QAAQ;AAAA,MACZ,GAAG;AAAA,MACJ,WAAW,GAAG,kDAAkD,SAAS;AAAA;AAAA,EAC3E;AAEJ;AAIA,SAAS,UAAU,OAAuB;AACxC,QAAM,EAAE,SAAS,WAAW,GAAG,WAAW,IAAI;AAE9C,QAAM,UAAU,eAAe,UAAU;AAEzC,QAAM,cAAc,MAAM,WAAW,WAAW;AAEhD,QAAM,iBAAiB,UAAU,OAAO;AAExC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,IAAI,aAAa;AAAA,MACjB,aAAU;AAAA,MACV,KAAK,QAAQ;AAAA,MACZ,GAAG;AAAA,MACJ,WAAW,GAAG,qDAAqD,SAAS;AAAA;AAAA,EAC9E;AAEJ;AAIA,SAAS,gBAAgB,OAA6B;AACpD,QAAM,EAAE,SAAS,WAAW,GAAG,iBAAiB,IAAI;AAEpD,QAAM,UAAU,eAAe,gBAAgB;AAE/C,QAAM,cAAc,MAAM,WAAW,WAAW;AAEhD,QAAM,uBAAuB,UAAU,OAAO;AAE9C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,IAAI,aAAa;AAAA,MACjB,aAAU;AAAA,MACV,KAAK,QAAQ;AAAA,MACZ,GAAG;AAAA,MACJ,WAAW,GAAG,iCAAiC,SAAS;AAAA;AAAA,EAC1D;AAEJ;AAMA,SAAS,UAAU,OAAuB;AACxC,QAAM,EAAE,SAAS,WAAW,UAAU,SAAS,aAAa,GAAG,iBAAiB,IAAI;AAEpF,QAAM,QAAQ,gBAAgB,UAAU;AACxC,QAAM,UAAU,eAAe,UAAU;AAEzC,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,UAA0C;AACzC,oBAAc,KAAK;AACnB,UAAI,MAAM,iBAAkB;AAE5B,YAAM,SAAS,QAAQ,KAAK;AAAA,IAC9B;AAAA,IACA,CAAC,OAAO,WAAW;AAAA,EACrB;AAEA,QAAM,iBAAiB,UAAU,OAAO;AAExC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY,QAAQ,OAAO;AAAA,MAC3B,aAAU;AAAA,MACV,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOT;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAMH,sBAAY,oBAAC,KAAE,eAAY,QAAO;AAAA;AAAA,EACrC;AAEJ;AAWA,SAAS,SAAS,OAAsB;AACtC,QAAM,EAAE,UAAU,SAAS,aAAa,GAAG,gBAAgB,IAAI;AAE/D,QAAM,QAAQ,gBAAgB,SAAS;AACvC,QAAM,UAAU,eAAe,SAAS;AACxC,QAAM,QAAQ,SAAS,CAAC,UAAU,MAAM,KAAK;AAE7C,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,UAAyC;AACxC,oBAAc,KAAK;AACnB,UAAI,MAAM,iBAAkB;AAE5B,UAAI,QAAQ,GAAG;AACb,cAAM,SAAS,SAAS,QAAQ,CAAC;AAAA,MACnC;AAAA,IACF;AAAA,IACA,CAAC,OAAO,OAAO,WAAW;AAAA,EAC5B;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,aAAU;AAAA,MACV,SAAQ;AAAA,MACP,GAAG;AAAA,MACJ;AAAA,MACA,UAAU,UAAU;AAAA,MAEnB,sBACC,iCAEE;AAAA,4BAAC,eAAY,eAAY,QAAO,WAAU,UAAS;AAAA,QAClD,QAAQ,OAAO;AAAA,SAClB;AAAA;AAAA,EAEJ;AAEJ;AAIA,SAAS,SAAS,OAAsB;AACtC,QAAM,EAAE,UAAU,SAAS,aAAa,GAAG,gBAAgB,IAAI;AAC/D,QAAM,QAAQ,gBAAgB,SAAS;AACvC,QAAM,UAAU,eAAe,SAAS;AACxC,QAAM,QAAQ,SAAS,CAAC,UAAU,MAAM,KAAK;AAC7C,QAAM,QAAQ,SAAS,CAAC,UAAU,MAAM,KAAK;AAE7C,QAAM,aAAa,UAAU,MAAM,SAAS;AAE5C,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,UAAyC;AACxC,oBAAc,KAAK;AACnB,UAAI,MAAM,iBAAkB;AAE5B,YAAM,SAAS,SAAS,QAAQ,CAAC;AAAA,IACnC;AAAA,IACA,CAAC,OAAO,OAAO,WAAW;AAAA,EAC5B;AAEA,SACE,oBAAC,UAAO,MAAK,UAAS,aAAU,aAAa,GAAG,iBAAiB,SAC9D,sBACC,iCACG;AAAA,iBAAa,QAAQ,OAAO,SAAS,QAAQ,OAAO;AAAA,IACpD,CAAC,cAAc,oBAAC,gBAAa,eAAY,QAAO,WAAU,UAAS;AAAA,KACtE,GAEJ;AAEJ;AAIA,SAAS,SAAS,OAAsB;AACtC,QAAM,EAAE,UAAU,SAAS,aAAa,GAAG,gBAAgB,IAAI;AAE/D,QAAM,QAAQ,gBAAgB,SAAS;AACvC,QAAM,UAAU,eAAe,SAAS;AAExC,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,UAAyC;AACxC,oBAAc,KAAK;AACnB,UAAI,MAAM,iBAAkB;AAE5B,YAAM,SAAS,QAAQ,KAAK;AAAA,IAC9B;AAAA,IACA,CAAC,OAAO,WAAW;AAAA,EACrB;AAEA,SACE,oBAAC,UAAO,MAAK,UAAS,aAAU,aAAY,SAAQ,WAAW,GAAG,iBAAiB,SAChF,sBAAY,QAAQ,OAAO,MAC9B;AAEJ;AAOA,SAAS,gBAAgB,OAA6B;AACpD,QAAM,EAAE,QAAQ,YAAY,SAAS,WAAW,UAAU,GAAG,iBAAiB,IAAI;AAElF,QAAM,UAAU,eAAe,iBAAiB;AAChD,QAAM,SAAS,cAAc,QAAQ,OAAO;AAE5C,QAAM,QAAQ,SAAS,CAAC,UAAU,MAAM,KAAK;AAC7C,QAAM,QAAQ,SAAS,CAAC,UAAU,MAAM,KAAK;AAE7C,QAAM,uBAAuB,UAAU,OAAO;AAE9C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACT,GAAG;AAAA,MAGJ,WAAW,GAAG,mDAAmD,SAAS;AAAA,MAEzE,sBAAY,OAAO,QAAQ,GAAG,MAAM,MAAM;AAAA;AAAA,EAC7C;AAEJ;AAIA,SAAS,WAAW,OAAwB;AAC1C,QAAM,EAAE,SAAS,WAAW,KAAK,GAAG,YAAY,IAAI;AAEpD,QAAM,cAAc,eAAe,WAAW;AAC9C,QAAM,mBAAmB,MAAM,WAAW,oBAAoB;AAC9D,QAAM,UAAU,eAAe,WAAW;AAE1C,QAAM,cAAc,gBAAgB,KAAK,mBAAmB,SAAY,YAAY,cAAc;AAElG,QAAM,kBAAkB,UAAU,OAAO;AAEzC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,KAAK,QAAQ;AAAA,MACZ,GAAG;AAAA,MACJ,KAAK;AAAA,MACL,WAAW,GAAG,0DAA0D,SAAS;AAAA;AAAA,EACnF;AAEJ;","names":["offset","onResize","onScroll","target"]}
@@ -2,7 +2,7 @@
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
- import { useComposedRefs } from "./compose-refs.js";
5
+ import { useComposedRefs } from "../lib/compose-refs.js";
6
6
  import { useDataGridPresence } from "./data-grid-presence.js";
7
7
  import { getCellKey } from "./utils.js";
8
8
  function DataGridCellWrapper({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-grid/data-grid-cell-wrapper.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { useComposedRefs } from \"./compose-refs.js\";\nimport { useDataGridPresence } from \"./data-grid-presence.js\";\nimport type { DataGridCellProps } from \"./types.js\";\nimport { getCellKey } from \"./utils.js\";\n\ninterface DataGridCellWrapperProps<TData> extends DataGridCellProps<TData>, React.ComponentProps<\"div\"> {}\n\nexport function DataGridCellWrapper<TData>({\n tableMeta,\n rowIndex,\n columnId,\n isEditing,\n isFocused,\n isSelected,\n isSearchMatch,\n isActiveSearchMatch,\n readOnly,\n rowHeight,\n className,\n onClick: onClickProp,\n onKeyDown: onKeyDownProp,\n ref,\n ...props\n}: DataGridCellWrapperProps<TData>) {\n const cellMapRef = tableMeta?.cellMapRef;\n const cellPresence = useDataGridPresence(getCellKey(rowIndex, columnId));\n\n const onCellChange = React.useCallback(\n (node: HTMLDivElement | null) => {\n if (!cellMapRef) return;\n\n const cellKey = getCellKey(rowIndex, columnId);\n\n if (node) {\n cellMapRef.current.set(cellKey, node);\n } else {\n cellMapRef.current.delete(cellKey);\n }\n },\n [rowIndex, columnId, cellMapRef]\n );\n\n const composedRef = useComposedRefs(ref, onCellChange);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!isEditing) {\n event.preventDefault();\n onClickProp?.(event);\n if (isFocused && !readOnly) {\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n } else {\n tableMeta?.onCellClick?.(rowIndex, columnId, event);\n }\n }\n },\n [tableMeta, rowIndex, columnId, isEditing, isFocused, readOnly, onClickProp]\n );\n\n const onContextMenu = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n tableMeta?.onCellContextMenu?.(rowIndex, columnId, event);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onDoubleClick = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n event.preventDefault();\n tableMeta?.onCellDoubleClick?.(rowIndex, columnId);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n onKeyDownProp?.(event);\n\n if (event.defaultPrevented) return;\n\n if (\n event.key === \"ArrowUp\" ||\n event.key === \"ArrowDown\" ||\n event.key === \"ArrowLeft\" ||\n event.key === \"ArrowRight\" ||\n event.key === \"Home\" ||\n event.key === \"End\" ||\n event.key === \"PageUp\" ||\n event.key === \"PageDown\" ||\n event.key === \"Tab\"\n ) {\n return;\n }\n\n if (isFocused && !isEditing && !readOnly) {\n if (event.key === \"F2\" || event.key === \"Enter\") {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n return;\n }\n\n if (event.key === \" \") {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n return;\n }\n\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n }\n }\n },\n [onKeyDownProp, isFocused, isEditing, readOnly, tableMeta, rowIndex, columnId]\n );\n\n const onMouseDown = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n tableMeta?.onCellMouseDown?.(rowIndex, columnId, event);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onMouseEnter = React.useCallback(() => {\n if (!isEditing) {\n tableMeta?.onCellMouseEnter?.(rowIndex, columnId);\n }\n }, [tableMeta, rowIndex, columnId, isEditing]);\n\n const onMouseUp = React.useCallback(() => {\n if (!isEditing) {\n tableMeta?.onCellMouseUp?.();\n }\n }, [tableMeta, isEditing]);\n\n return (\n <div\n role=\"button\"\n data-slot=\"grid-cell-wrapper\"\n data-editing={isEditing ? \"\" : undefined}\n data-focused={isFocused ? \"\" : undefined}\n data-selected={isSelected ? \"\" : undefined}\n tabIndex={isFocused && !isEditing ? 0 : -1}\n {...props}\n ref={composedRef}\n className={cn(\n /*\n * ─ BA TÍN HIỆU MÀU CỦA MỘT Ô, VÀ VÌ SAO CHÚNG KHÔNG PHẢI TINT CỦA `--primary`/`--warning` ─\n *\n * Bản gốc tô `bg-primary/10` cho vùng chọn và `bg-warning/20`÷`/50` cho kết quả tìm kiếm.\n * Cả hai đều là bậc TÔ NỀN pha loãng, và `pnpm lint:a11y` bác đúng chỗ: 1,05:1 (light) ·\n * 1,21:1 (dark) cho vùng chọn — dưới xa ngưỡng 3:1 của WCAG 1.4.11 cho thông tin thị giác.\n * Một dấu hiệu không nhìn thấy là một dấu hiệu GIẢ.\n *\n * Thay bằng đúng hai bậc mà hợp đồng màu cấp cho việc này:\n * • vùng chọn → `--accent`, bậc tint TRẠNG THÁI (Tabs/Switch/Progress nói \"đang bật\"\n * bằng chính nó). Là token BỀ MẶT nên nó được phép nhạt: nghĩa vụ 3:1\n * không rơi lên tint, mà rơi lên NÉT đi kèm.\n * • tìm kiếm → viền `--warning-ink` (6,51:1 light · 11,60:1 dark trên nền ô) — ĐÂY mới\n * là thứ mang thông tin; ô đang đứng thì thêm nền `--warning` đặc + chữ\n * `--warning-foreground` (12,26:1) cho khác hẳn các ô còn lại.\n *\n * `border-transparent` ở bậc nghỉ để bật/tắt viền KHÔNG xô layout 1px (cùng cách\n * `TableRow` giữ bề rộng viền không đổi giữa hai trạng thái).\n */\n // has-[[role=checkbox]] = ô kiểu checkbox (Radix render <button role=\"checkbox\">)\n \"size-full border border-transparent px-2 py-1.5 text-start text-sm outline-none focus-visible:outline-none has-[[role=checkbox]]:pt-2.5\",\n {\n \"ring-1 ring-inset\": isFocused || !!cellPresence,\n \"ring-ring\": isFocused && !cellPresence,\n \"border-warning-ink\": isSearchMatch || isActiveSearchMatch,\n \"bg-warning text-warning-foreground\": isActiveSearchMatch,\n \"bg-accent\": isSelected && !isEditing && !isActiveSearchMatch,\n \"cursor-default\": !isEditing,\n \"[&_[data-slot=grid-cell-content]]:line-clamp-1\": !isEditing && rowHeight === \"short\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-2\": !isEditing && rowHeight === \"medium\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-3\": !isEditing && rowHeight === \"tall\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-4\": !isEditing && rowHeight === \"extra-tall\"\n },\n className\n )}\n style={cellPresence ? ({ \"--tw-ring-color\": cellPresence.color } as React.CSSProperties) : undefined}\n onClick={onClick}\n onContextMenu={onContextMenu}\n onDoubleClick={onDoubleClick}\n onMouseDown={onMouseDown}\n onMouseEnter={onMouseEnter}\n onMouseUp={onMouseUp}\n onKeyDown={onKeyDown}\n />\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-cell-wrapper.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { useComposedRefs } from \"../lib/compose-refs.js\";\nimport { useDataGridPresence } from \"./data-grid-presence.js\";\nimport type { DataGridCellProps } from \"./types.js\";\nimport { getCellKey } from \"./utils.js\";\n\ninterface DataGridCellWrapperProps<TData> extends DataGridCellProps<TData>, React.ComponentProps<\"div\"> {}\n\nexport function DataGridCellWrapper<TData>({\n tableMeta,\n rowIndex,\n columnId,\n isEditing,\n isFocused,\n isSelected,\n isSearchMatch,\n isActiveSearchMatch,\n readOnly,\n rowHeight,\n className,\n onClick: onClickProp,\n onKeyDown: onKeyDownProp,\n ref,\n ...props\n}: DataGridCellWrapperProps<TData>) {\n const cellMapRef = tableMeta?.cellMapRef;\n const cellPresence = useDataGridPresence(getCellKey(rowIndex, columnId));\n\n const onCellChange = React.useCallback(\n (node: HTMLDivElement | null) => {\n if (!cellMapRef) return;\n\n const cellKey = getCellKey(rowIndex, columnId);\n\n if (node) {\n cellMapRef.current.set(cellKey, node);\n } else {\n cellMapRef.current.delete(cellKey);\n }\n },\n [rowIndex, columnId, cellMapRef]\n );\n\n const composedRef = useComposedRefs(ref, onCellChange);\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!isEditing) {\n event.preventDefault();\n onClickProp?.(event);\n if (isFocused && !readOnly) {\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n } else {\n tableMeta?.onCellClick?.(rowIndex, columnId, event);\n }\n }\n },\n [tableMeta, rowIndex, columnId, isEditing, isFocused, readOnly, onClickProp]\n );\n\n const onContextMenu = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n tableMeta?.onCellContextMenu?.(rowIndex, columnId, event);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onDoubleClick = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n event.preventDefault();\n tableMeta?.onCellDoubleClick?.(rowIndex, columnId);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n onKeyDownProp?.(event);\n\n if (event.defaultPrevented) return;\n\n if (\n event.key === \"ArrowUp\" ||\n event.key === \"ArrowDown\" ||\n event.key === \"ArrowLeft\" ||\n event.key === \"ArrowRight\" ||\n event.key === \"Home\" ||\n event.key === \"End\" ||\n event.key === \"PageUp\" ||\n event.key === \"PageDown\" ||\n event.key === \"Tab\"\n ) {\n return;\n }\n\n if (isFocused && !isEditing && !readOnly) {\n if (event.key === \"F2\" || event.key === \"Enter\") {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n return;\n }\n\n if (event.key === \" \") {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n return;\n }\n\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {\n event.preventDefault();\n event.stopPropagation();\n tableMeta?.onCellEditingStart?.(rowIndex, columnId);\n }\n }\n },\n [onKeyDownProp, isFocused, isEditing, readOnly, tableMeta, rowIndex, columnId]\n );\n\n const onMouseDown = React.useCallback(\n (event: React.MouseEvent) => {\n if (!isEditing) {\n tableMeta?.onCellMouseDown?.(rowIndex, columnId, event);\n }\n },\n [tableMeta, rowIndex, columnId, isEditing]\n );\n\n const onMouseEnter = React.useCallback(() => {\n if (!isEditing) {\n tableMeta?.onCellMouseEnter?.(rowIndex, columnId);\n }\n }, [tableMeta, rowIndex, columnId, isEditing]);\n\n const onMouseUp = React.useCallback(() => {\n if (!isEditing) {\n tableMeta?.onCellMouseUp?.();\n }\n }, [tableMeta, isEditing]);\n\n return (\n <div\n role=\"button\"\n data-slot=\"grid-cell-wrapper\"\n data-editing={isEditing ? \"\" : undefined}\n data-focused={isFocused ? \"\" : undefined}\n data-selected={isSelected ? \"\" : undefined}\n tabIndex={isFocused && !isEditing ? 0 : -1}\n {...props}\n ref={composedRef}\n className={cn(\n /*\n * ─ BA TÍN HIỆU MÀU CỦA MỘT Ô, VÀ VÌ SAO CHÚNG KHÔNG PHẢI TINT CỦA `--primary`/`--warning` ─\n *\n * Bản gốc tô `bg-primary/10` cho vùng chọn và `bg-warning/20`÷`/50` cho kết quả tìm kiếm.\n * Cả hai đều là bậc TÔ NỀN pha loãng, và `pnpm lint:a11y` bác đúng chỗ: 1,05:1 (light) ·\n * 1,21:1 (dark) cho vùng chọn — dưới xa ngưỡng 3:1 của WCAG 1.4.11 cho thông tin thị giác.\n * Một dấu hiệu không nhìn thấy là một dấu hiệu GIẢ.\n *\n * Thay bằng đúng hai bậc mà hợp đồng màu cấp cho việc này:\n * • vùng chọn → `--accent`, bậc tint TRẠNG THÁI (Tabs/Switch/Progress nói \"đang bật\"\n * bằng chính nó). Là token BỀ MẶT nên nó được phép nhạt: nghĩa vụ 3:1\n * không rơi lên tint, mà rơi lên NÉT đi kèm.\n * • tìm kiếm → viền `--warning-ink` (6,51:1 light · 11,60:1 dark trên nền ô) — ĐÂY mới\n * là thứ mang thông tin; ô đang đứng thì thêm nền `--warning` đặc + chữ\n * `--warning-foreground` (12,26:1) cho khác hẳn các ô còn lại.\n *\n * `border-transparent` ở bậc nghỉ để bật/tắt viền KHÔNG xô layout 1px (cùng cách\n * `TableRow` giữ bề rộng viền không đổi giữa hai trạng thái).\n */\n // has-[[role=checkbox]] = ô kiểu checkbox (Radix render <button role=\"checkbox\">)\n \"size-full border border-transparent px-2 py-1.5 text-start text-sm outline-none focus-visible:outline-none has-[[role=checkbox]]:pt-2.5\",\n {\n \"ring-1 ring-inset\": isFocused || !!cellPresence,\n \"ring-ring\": isFocused && !cellPresence,\n \"border-warning-ink\": isSearchMatch || isActiveSearchMatch,\n \"bg-warning text-warning-foreground\": isActiveSearchMatch,\n \"bg-accent\": isSelected && !isEditing && !isActiveSearchMatch,\n \"cursor-default\": !isEditing,\n \"[&_[data-slot=grid-cell-content]]:line-clamp-1\": !isEditing && rowHeight === \"short\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-2\": !isEditing && rowHeight === \"medium\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-3\": !isEditing && rowHeight === \"tall\",\n \"[&_[data-slot=grid-cell-content]]:line-clamp-4\": !isEditing && rowHeight === \"extra-tall\"\n },\n className\n )}\n style={cellPresence ? ({ \"--tw-ring-color\": cellPresence.color } as React.CSSProperties) : undefined}\n onClick={onClick}\n onContextMenu={onContextMenu}\n onDoubleClick={onDoubleClick}\n onMouseDown={onMouseDown}\n onMouseEnter={onMouseEnter}\n onMouseUp={onMouseUp}\n onKeyDown={onKeyDown}\n />\n );\n}\n"],"mappings":"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","names":[]}
@@ -9,9 +9,9 @@ import {
9
9
  DropdownMenuSeparator,
10
10
  DropdownMenuTrigger
11
11
  } from "../components/ui/dropdown-menu.js";
12
+ import { useAsRef } from "../lib/use-as-ref.js";
12
13
  import { useDataGridMessages } from "./data-grid-locale.js";
13
14
  import { toastSuccess } from "./toast.js";
14
- import { useAsRef } from "./use-as-ref.js";
15
15
  import { getEmptyCellValue, parseCellKey } from "./utils.js";
16
16
  function DataGridContextMenu({ tableMeta, columns, contextMenu }) {
17
17
  const onContextMenuOpenChange = tableMeta?.onContextMenuOpenChange;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-grid/data-grid-context-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ColumnDef, TableMeta } from \"@tanstack/react-table\";\nimport { CopyIcon, EraserIcon, ScissorsIcon, Trash2Icon } from \"lucide-react\";\nimport * as React from \"react\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger\n} from \"../components/ui/dropdown-menu.js\";\nimport { useDataGridMessages } from \"./data-grid-locale.js\";\nimport { toastSuccess } from \"./toast.js\";\nimport type { CellUpdate, ContextMenuState } from \"./types.js\";\nimport { useAsRef } from \"./use-as-ref.js\";\nimport { getEmptyCellValue, parseCellKey } from \"./utils.js\";\n\ninterface DataGridContextMenuProps<TData> {\n tableMeta: TableMeta<TData>;\n columns: Array<ColumnDef<TData>>;\n contextMenu: ContextMenuState;\n}\n\nexport function DataGridContextMenu<TData>({ tableMeta, columns, contextMenu }: DataGridContextMenuProps<TData>) {\n const onContextMenuOpenChange = tableMeta?.onContextMenuOpenChange;\n const selectionState = tableMeta?.selectionState;\n const dataGridRef = tableMeta?.dataGridRef;\n const onDataUpdate = tableMeta?.onDataUpdate;\n const onRowsDelete = tableMeta?.onRowsDelete;\n const onCellsCopy = tableMeta?.onCellsCopy;\n const onCellsCut = tableMeta?.onCellsCut;\n\n if (!contextMenu.open) return null;\n\n return (\n <ContextMenu\n tableMeta={tableMeta}\n columns={columns}\n dataGridRef={dataGridRef}\n contextMenu={contextMenu}\n onContextMenuOpenChange={onContextMenuOpenChange}\n selectionState={selectionState}\n onDataUpdate={onDataUpdate}\n onRowsDelete={onRowsDelete}\n onCellsCopy={onCellsCopy}\n onCellsCut={onCellsCut}\n />\n );\n}\n\ninterface ContextMenuProps<TData>\n extends Pick<\n TableMeta<TData>,\n | \"dataGridRef\"\n | \"onContextMenuOpenChange\"\n | \"selectionState\"\n | \"onDataUpdate\"\n | \"onRowsDelete\"\n | \"onCellsCopy\"\n | \"onCellsCut\"\n | \"readOnly\"\n >,\n Required<Pick<TableMeta<TData>, \"contextMenu\">> {\n tableMeta: TableMeta<TData>;\n columns: Array<ColumnDef<TData>>;\n}\n\nconst ContextMenu = React.memo(ContextMenuImpl, (prev, next) => {\n if (prev.contextMenu.open !== next.contextMenu.open) return false;\n if (!next.contextMenu.open) return true;\n if (prev.contextMenu.x !== next.contextMenu.x) return false;\n if (prev.contextMenu.y !== next.contextMenu.y) return false;\n\n const prevSize = prev.selectionState?.selectedCells?.size ?? 0;\n const nextSize = next.selectionState?.selectedCells?.size ?? 0;\n if (prevSize !== nextSize) return false;\n\n return true;\n}) as typeof ContextMenuImpl;\n\nfunction ContextMenuImpl<TData>({\n tableMeta,\n columns,\n dataGridRef,\n contextMenu,\n onContextMenuOpenChange,\n selectionState,\n onDataUpdate,\n onRowsDelete,\n onCellsCopy,\n onCellsCut\n}: ContextMenuProps<TData>) {\n const msg = useDataGridMessages();\n\n const propsRef = useAsRef({\n dataGridRef,\n selectionState,\n onDataUpdate,\n onRowsDelete,\n onCellsCopy,\n onCellsCut,\n columns\n });\n\n const triggerStyle = React.useMemo<React.CSSProperties>(\n () => ({\n position: \"fixed\",\n left: `${contextMenu.x}px`,\n top: `${contextMenu.y}px`,\n width: \"1px\",\n height: \"1px\",\n padding: 0,\n margin: 0,\n border: \"none\",\n background: \"transparent\",\n pointerEvents: \"none\",\n opacity: 0\n }),\n [contextMenu.x, contextMenu.y]\n );\n\n const onCloseAutoFocus: NonNullable<React.ComponentProps<typeof DropdownMenuContent>[\"onCloseAutoFocus\"]> =\n React.useCallback(\n (event) => {\n event.preventDefault();\n propsRef.current.dataGridRef?.current?.focus();\n },\n [propsRef]\n );\n\n const onCopy = React.useCallback(() => {\n propsRef.current.onCellsCopy?.();\n }, [propsRef]);\n\n const onCut = React.useCallback(() => {\n propsRef.current.onCellsCut?.();\n }, [propsRef]);\n\n const onClear = React.useCallback(() => {\n const { selectionState, columns, onDataUpdate } = propsRef.current;\n\n if (!selectionState?.selectedCells || selectionState.selectedCells.size === 0) return;\n\n const updates: Array<CellUpdate> = [];\n\n for (const cellKey of selectionState.selectedCells) {\n const { rowIndex, columnId } = parseCellKey(cellKey);\n\n // Get column from columns array\n const column = columns.find((col) => {\n if (col.id) return col.id === columnId;\n if (\"accessorKey\" in col) return col.accessorKey === columnId;\n return false;\n });\n const cellVariant = column?.meta?.cell?.variant;\n\n const emptyValue = getEmptyCellValue(cellVariant);\n\n updates.push({ rowIndex, columnId, value: emptyValue });\n }\n\n onDataUpdate?.(updates);\n\n toastSuccess(msg.toast.cellsCleared(updates.length));\n }, [propsRef, msg]);\n\n const onDelete = React.useCallback(async () => {\n const { selectionState, onRowsDelete } = propsRef.current;\n\n if (!selectionState?.selectedCells || selectionState.selectedCells.size === 0) return;\n\n const rowIndices = new Set<number>();\n for (const cellKey of selectionState.selectedCells) {\n const { rowIndex } = parseCellKey(cellKey);\n rowIndices.add(rowIndex);\n }\n\n const rowIndicesArray = Array.from(rowIndices).sort((a, b) => a - b);\n const rowCount = rowIndicesArray.length;\n\n await onRowsDelete?.(rowIndicesArray);\n\n toastSuccess(msg.toast.rowsDeleted(rowCount));\n }, [propsRef, msg]);\n\n return (\n <DropdownMenu open={contextMenu.open} onOpenChange={onContextMenuOpenChange}>\n <DropdownMenuTrigger style={triggerStyle} />\n <DropdownMenuContent data-grid-popover=\"\" align=\"start\" className=\"w-48\" onCloseAutoFocus={onCloseAutoFocus}>\n <DropdownMenuItem onSelect={onCopy}>\n <CopyIcon />\n {msg.contextMenu.copy}\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={onCut} disabled={tableMeta?.readOnly}>\n <ScissorsIcon />\n {msg.contextMenu.cut}\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={onClear} disabled={tableMeta?.readOnly}>\n <EraserIcon />\n {msg.contextMenu.clear}\n </DropdownMenuItem>\n {onRowsDelete && (\n <>\n <DropdownMenuSeparator />\n {/* `variant=\"destructive\"` của gói đã đi qua `--destructive-ink` (bậc CHỮ) cho cả\n chữ, icon và nền focus — đừng tự viết lại bằng `text-destructive` (bậc TÔ NỀN). */}\n <DropdownMenuItem variant=\"destructive\" onSelect={onDelete}>\n <Trash2Icon />\n {msg.contextMenu.deleteRows}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":"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","names":["selectionState","columns","onDataUpdate","onRowsDelete"]}
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-context-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ColumnDef, TableMeta } from \"@tanstack/react-table\";\nimport { CopyIcon, EraserIcon, ScissorsIcon, Trash2Icon } from \"lucide-react\";\nimport * as React from \"react\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger\n} from \"../components/ui/dropdown-menu.js\";\nimport { useAsRef } from \"../lib/use-as-ref.js\";\nimport { useDataGridMessages } from \"./data-grid-locale.js\";\nimport { toastSuccess } from \"./toast.js\";\nimport type { CellUpdate, ContextMenuState } from \"./types.js\";\nimport { getEmptyCellValue, parseCellKey } from \"./utils.js\";\n\ninterface DataGridContextMenuProps<TData> {\n tableMeta: TableMeta<TData>;\n columns: Array<ColumnDef<TData>>;\n contextMenu: ContextMenuState;\n}\n\nexport function DataGridContextMenu<TData>({ tableMeta, columns, contextMenu }: DataGridContextMenuProps<TData>) {\n const onContextMenuOpenChange = tableMeta?.onContextMenuOpenChange;\n const selectionState = tableMeta?.selectionState;\n const dataGridRef = tableMeta?.dataGridRef;\n const onDataUpdate = tableMeta?.onDataUpdate;\n const onRowsDelete = tableMeta?.onRowsDelete;\n const onCellsCopy = tableMeta?.onCellsCopy;\n const onCellsCut = tableMeta?.onCellsCut;\n\n if (!contextMenu.open) return null;\n\n return (\n <ContextMenu\n tableMeta={tableMeta}\n columns={columns}\n dataGridRef={dataGridRef}\n contextMenu={contextMenu}\n onContextMenuOpenChange={onContextMenuOpenChange}\n selectionState={selectionState}\n onDataUpdate={onDataUpdate}\n onRowsDelete={onRowsDelete}\n onCellsCopy={onCellsCopy}\n onCellsCut={onCellsCut}\n />\n );\n}\n\ninterface ContextMenuProps<TData>\n extends Pick<\n TableMeta<TData>,\n | \"dataGridRef\"\n | \"onContextMenuOpenChange\"\n | \"selectionState\"\n | \"onDataUpdate\"\n | \"onRowsDelete\"\n | \"onCellsCopy\"\n | \"onCellsCut\"\n | \"readOnly\"\n >,\n Required<Pick<TableMeta<TData>, \"contextMenu\">> {\n tableMeta: TableMeta<TData>;\n columns: Array<ColumnDef<TData>>;\n}\n\nconst ContextMenu = React.memo(ContextMenuImpl, (prev, next) => {\n if (prev.contextMenu.open !== next.contextMenu.open) return false;\n if (!next.contextMenu.open) return true;\n if (prev.contextMenu.x !== next.contextMenu.x) return false;\n if (prev.contextMenu.y !== next.contextMenu.y) return false;\n\n const prevSize = prev.selectionState?.selectedCells?.size ?? 0;\n const nextSize = next.selectionState?.selectedCells?.size ?? 0;\n if (prevSize !== nextSize) return false;\n\n return true;\n}) as typeof ContextMenuImpl;\n\nfunction ContextMenuImpl<TData>({\n tableMeta,\n columns,\n dataGridRef,\n contextMenu,\n onContextMenuOpenChange,\n selectionState,\n onDataUpdate,\n onRowsDelete,\n onCellsCopy,\n onCellsCut\n}: ContextMenuProps<TData>) {\n const msg = useDataGridMessages();\n\n const propsRef = useAsRef({\n dataGridRef,\n selectionState,\n onDataUpdate,\n onRowsDelete,\n onCellsCopy,\n onCellsCut,\n columns\n });\n\n const triggerStyle = React.useMemo<React.CSSProperties>(\n () => ({\n position: \"fixed\",\n left: `${contextMenu.x}px`,\n top: `${contextMenu.y}px`,\n width: \"1px\",\n height: \"1px\",\n padding: 0,\n margin: 0,\n border: \"none\",\n background: \"transparent\",\n pointerEvents: \"none\",\n opacity: 0\n }),\n [contextMenu.x, contextMenu.y]\n );\n\n const onCloseAutoFocus: NonNullable<React.ComponentProps<typeof DropdownMenuContent>[\"onCloseAutoFocus\"]> =\n React.useCallback(\n (event) => {\n event.preventDefault();\n propsRef.current.dataGridRef?.current?.focus();\n },\n [propsRef]\n );\n\n const onCopy = React.useCallback(() => {\n propsRef.current.onCellsCopy?.();\n }, [propsRef]);\n\n const onCut = React.useCallback(() => {\n propsRef.current.onCellsCut?.();\n }, [propsRef]);\n\n const onClear = React.useCallback(() => {\n const { selectionState, columns, onDataUpdate } = propsRef.current;\n\n if (!selectionState?.selectedCells || selectionState.selectedCells.size === 0) return;\n\n const updates: Array<CellUpdate> = [];\n\n for (const cellKey of selectionState.selectedCells) {\n const { rowIndex, columnId } = parseCellKey(cellKey);\n\n // Get column from columns array\n const column = columns.find((col) => {\n if (col.id) return col.id === columnId;\n if (\"accessorKey\" in col) return col.accessorKey === columnId;\n return false;\n });\n const cellVariant = column?.meta?.cell?.variant;\n\n const emptyValue = getEmptyCellValue(cellVariant);\n\n updates.push({ rowIndex, columnId, value: emptyValue });\n }\n\n onDataUpdate?.(updates);\n\n toastSuccess(msg.toast.cellsCleared(updates.length));\n }, [propsRef, msg]);\n\n const onDelete = React.useCallback(async () => {\n const { selectionState, onRowsDelete } = propsRef.current;\n\n if (!selectionState?.selectedCells || selectionState.selectedCells.size === 0) return;\n\n const rowIndices = new Set<number>();\n for (const cellKey of selectionState.selectedCells) {\n const { rowIndex } = parseCellKey(cellKey);\n rowIndices.add(rowIndex);\n }\n\n const rowIndicesArray = Array.from(rowIndices).sort((a, b) => a - b);\n const rowCount = rowIndicesArray.length;\n\n await onRowsDelete?.(rowIndicesArray);\n\n toastSuccess(msg.toast.rowsDeleted(rowCount));\n }, [propsRef, msg]);\n\n return (\n <DropdownMenu open={contextMenu.open} onOpenChange={onContextMenuOpenChange}>\n <DropdownMenuTrigger style={triggerStyle} />\n <DropdownMenuContent data-grid-popover=\"\" align=\"start\" className=\"w-48\" onCloseAutoFocus={onCloseAutoFocus}>\n <DropdownMenuItem onSelect={onCopy}>\n <CopyIcon />\n {msg.contextMenu.copy}\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={onCut} disabled={tableMeta?.readOnly}>\n <ScissorsIcon />\n {msg.contextMenu.cut}\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={onClear} disabled={tableMeta?.readOnly}>\n <EraserIcon />\n {msg.contextMenu.clear}\n </DropdownMenuItem>\n {onRowsDelete && (\n <>\n <DropdownMenuSeparator />\n {/* `variant=\"destructive\"` của gói đã đi qua `--destructive-ink` (bậc CHỮ) cho cả\n chữ, icon và nền focus — đừng tự viết lại bằng `text-destructive` (bậc TÔ NỀN). */}\n <DropdownMenuItem variant=\"destructive\" onSelect={onDelete}>\n <Trash2Icon />\n {msg.contextMenu.deleteRows}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":"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","names":["selectionState","columns","onDataUpdate","onRowsDelete"]}
@@ -11,8 +11,8 @@ import {
11
11
  DialogTitle
12
12
  } from "../components/ui/dialog.js";
13
13
  import { cn } from "../lib/cn.js";
14
+ import { useAsRef } from "../lib/use-as-ref.js";
14
15
  import { useDataGridMessages } from "./data-grid-locale.js";
15
- import { useAsRef } from "./use-as-ref.js";
16
16
  function DataGridPasteDialog({ tableMeta, pasteDialog }) {
17
17
  const onPasteDialogOpenChange = tableMeta?.onPasteDialogOpenChange;
18
18
  const onCellsPaste = tableMeta?.onCellsPaste;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-grid/data-grid-paste-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport type { TableMeta } from \"@tanstack/react-table\";\nimport * as React from \"react\";\nimport { Button } from \"../components/ui/button.js\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle\n} from \"../components/ui/dialog.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { useDataGridMessages } from \"./data-grid-locale.js\";\nimport type { PasteDialogState } from \"./types.js\";\nimport { useAsRef } from \"./use-as-ref.js\";\n\ninterface DataGridPasteDialogProps<TData> {\n tableMeta: TableMeta<TData>;\n pasteDialog: PasteDialogState;\n}\n\nexport function DataGridPasteDialog<TData>({ tableMeta, pasteDialog }: DataGridPasteDialogProps<TData>) {\n const onPasteDialogOpenChange = tableMeta?.onPasteDialogOpenChange;\n const onCellsPaste = tableMeta?.onCellsPaste;\n\n if (!pasteDialog.open) return null;\n\n return (\n <PasteDialog\n pasteDialog={pasteDialog}\n onPasteDialogOpenChange={onPasteDialogOpenChange}\n onCellsPaste={onCellsPaste}\n />\n );\n}\n\ninterface PasteDialogProps\n extends Pick<TableMeta<unknown>, \"onPasteDialogOpenChange\" | \"onCellsPaste\">,\n Required<Pick<TableMeta<unknown>, \"pasteDialog\">> {}\n\nconst PasteDialog = React.memo(PasteDialogImpl, (prev, next) => {\n if (prev.pasteDialog.open !== next.pasteDialog.open) return false;\n if (!next.pasteDialog.open) return true;\n if (prev.pasteDialog.rowsNeeded !== next.pasteDialog.rowsNeeded) return false;\n\n return true;\n});\n\nfunction PasteDialogImpl({ pasteDialog, onPasteDialogOpenChange, onCellsPaste }: PasteDialogProps) {\n const msg = useDataGridMessages();\n\n const propsRef = useAsRef({\n onPasteDialogOpenChange,\n onCellsPaste\n });\n\n const expandRadioRef = React.useRef<HTMLInputElement | null>(null);\n\n const onOpenChange = React.useCallback(\n (open: boolean) => {\n propsRef.current.onPasteDialogOpenChange?.(open);\n },\n [propsRef]\n );\n\n const onCancel = React.useCallback(() => {\n propsRef.current.onPasteDialogOpenChange?.(false);\n }, [propsRef]);\n\n const onContinue = React.useCallback(() => {\n propsRef.current.onCellsPaste?.(expandRadioRef.current?.checked ?? false);\n }, [propsRef]);\n\n return (\n <Dialog open={pasteDialog.open} onOpenChange={onOpenChange}>\n <DialogContent data-grid-popover=\"\">\n <DialogHeader>\n <DialogTitle>{msg.pasteDialog.title}</DialogTitle>\n <DialogDescription>{msg.pasteDialog.description(pasteDialog.rowsNeeded)}</DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-3 py-1\">\n {/* biome-ignore lint/a11y/noLabelWithoutControl: <RadioItem> bọc sẵn <input type=\"radio\"> bên trong */}\n <label className=\"flex cursor-pointer items-start gap-3\">\n <RadioItem ref={expandRadioRef} name=\"expand-option\" value=\"expand\" defaultChecked />\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-medium text-sm leading-none\">{msg.pasteDialog.expandTitle}</span>\n <span className=\"text-muted-foreground text-sm\">\n {msg.pasteDialog.expandDescription(pasteDialog.rowsNeeded)}\n </span>\n </div>\n </label>\n {/* biome-ignore lint/a11y/noLabelWithoutControl: <RadioItem> bọc sẵn <input type=\"radio\"> bên trong */}\n <label className=\"flex cursor-pointer items-start gap-3\">\n <RadioItem name=\"expand-option\" value=\"no-expand\" />\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-medium text-sm leading-none\">{msg.pasteDialog.keepTitle}</span>\n <span className=\"text-muted-foreground text-sm\">{msg.pasteDialog.keepDescription}</span>\n </div>\n </label>\n </div>\n <DialogFooter className=\"gap-2\">\n <Button variant=\"outline\" onClick={onCancel}>\n {msg.pasteDialog.cancel}\n </Button>\n <Button onClick={onContinue}>{msg.pasteDialog.confirm}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * Radio thuần HTML (không dùng RadioGroup của Radix) để bấm vào cả khối `<label>`\n * là chọn được, và để đọc trạng thái qua ref lúc bấm \"Tiếp tục\".\n */\nfunction RadioItem({ className, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type=\"radio\"\n className={cn(\n // Cùng bộ token với `RadioGroupItem` của gói (`--choice-edge` cho viền nghỉ,\n // `--control-on` cho viền + chấm khi chọn) — đừng quay lại `--primary`/`--ring`: đó là\n // bậc tô NỀN, đem vẽ NÉT thì ở light chỉ 1,73:1.\n \"relative mt-0.5 size-4 shrink-0 appearance-none rounded-full border border-choice-edge outline-none transition-colors dark:bg-input-fill\",\n \"hover:border-choice-edge-strong checked:border-control-on\",\n \"focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"checked:before:absolute checked:before:start-1/2 checked:before:top-1/2 checked:before:size-2 checked:before:-translate-x-1/2 checked:before:-translate-y-1/2 checked:before:rounded-full checked:before:bg-control-on checked:before:content-['']\",\n className\n )}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-paste-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport type { TableMeta } from \"@tanstack/react-table\";\nimport * as React from \"react\";\nimport { Button } from \"../components/ui/button.js\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle\n} from \"../components/ui/dialog.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { useAsRef } from \"../lib/use-as-ref.js\";\nimport { useDataGridMessages } from \"./data-grid-locale.js\";\nimport type { PasteDialogState } from \"./types.js\";\n\ninterface DataGridPasteDialogProps<TData> {\n tableMeta: TableMeta<TData>;\n pasteDialog: PasteDialogState;\n}\n\nexport function DataGridPasteDialog<TData>({ tableMeta, pasteDialog }: DataGridPasteDialogProps<TData>) {\n const onPasteDialogOpenChange = tableMeta?.onPasteDialogOpenChange;\n const onCellsPaste = tableMeta?.onCellsPaste;\n\n if (!pasteDialog.open) return null;\n\n return (\n <PasteDialog\n pasteDialog={pasteDialog}\n onPasteDialogOpenChange={onPasteDialogOpenChange}\n onCellsPaste={onCellsPaste}\n />\n );\n}\n\ninterface PasteDialogProps\n extends Pick<TableMeta<unknown>, \"onPasteDialogOpenChange\" | \"onCellsPaste\">,\n Required<Pick<TableMeta<unknown>, \"pasteDialog\">> {}\n\nconst PasteDialog = React.memo(PasteDialogImpl, (prev, next) => {\n if (prev.pasteDialog.open !== next.pasteDialog.open) return false;\n if (!next.pasteDialog.open) return true;\n if (prev.pasteDialog.rowsNeeded !== next.pasteDialog.rowsNeeded) return false;\n\n return true;\n});\n\nfunction PasteDialogImpl({ pasteDialog, onPasteDialogOpenChange, onCellsPaste }: PasteDialogProps) {\n const msg = useDataGridMessages();\n\n const propsRef = useAsRef({\n onPasteDialogOpenChange,\n onCellsPaste\n });\n\n const expandRadioRef = React.useRef<HTMLInputElement | null>(null);\n\n const onOpenChange = React.useCallback(\n (open: boolean) => {\n propsRef.current.onPasteDialogOpenChange?.(open);\n },\n [propsRef]\n );\n\n const onCancel = React.useCallback(() => {\n propsRef.current.onPasteDialogOpenChange?.(false);\n }, [propsRef]);\n\n const onContinue = React.useCallback(() => {\n propsRef.current.onCellsPaste?.(expandRadioRef.current?.checked ?? false);\n }, [propsRef]);\n\n return (\n <Dialog open={pasteDialog.open} onOpenChange={onOpenChange}>\n <DialogContent data-grid-popover=\"\">\n <DialogHeader>\n <DialogTitle>{msg.pasteDialog.title}</DialogTitle>\n <DialogDescription>{msg.pasteDialog.description(pasteDialog.rowsNeeded)}</DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-3 py-1\">\n {/* biome-ignore lint/a11y/noLabelWithoutControl: <RadioItem> bọc sẵn <input type=\"radio\"> bên trong */}\n <label className=\"flex cursor-pointer items-start gap-3\">\n <RadioItem ref={expandRadioRef} name=\"expand-option\" value=\"expand\" defaultChecked />\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-medium text-sm leading-none\">{msg.pasteDialog.expandTitle}</span>\n <span className=\"text-muted-foreground text-sm\">\n {msg.pasteDialog.expandDescription(pasteDialog.rowsNeeded)}\n </span>\n </div>\n </label>\n {/* biome-ignore lint/a11y/noLabelWithoutControl: <RadioItem> bọc sẵn <input type=\"radio\"> bên trong */}\n <label className=\"flex cursor-pointer items-start gap-3\">\n <RadioItem name=\"expand-option\" value=\"no-expand\" />\n <div className=\"flex flex-col gap-1\">\n <span className=\"font-medium text-sm leading-none\">{msg.pasteDialog.keepTitle}</span>\n <span className=\"text-muted-foreground text-sm\">{msg.pasteDialog.keepDescription}</span>\n </div>\n </label>\n </div>\n <DialogFooter className=\"gap-2\">\n <Button variant=\"outline\" onClick={onCancel}>\n {msg.pasteDialog.cancel}\n </Button>\n <Button onClick={onContinue}>{msg.pasteDialog.confirm}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * Radio thuần HTML (không dùng RadioGroup của Radix) để bấm vào cả khối `<label>`\n * là chọn được, và để đọc trạng thái qua ref lúc bấm \"Tiếp tục\".\n */\nfunction RadioItem({ className, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type=\"radio\"\n className={cn(\n // Cùng bộ token với `RadioGroupItem` của gói (`--choice-edge` cho viền nghỉ,\n // `--control-on` cho viền + chấm khi chọn) — đừng quay lại `--primary`/`--ring`: đó là\n // bậc tô NỀN, đem vẽ NÉT thì ở light chỉ 1,73:1.\n \"relative mt-0.5 size-4 shrink-0 appearance-none rounded-full border border-choice-edge outline-none transition-colors dark:bg-input-fill\",\n \"hover:border-choice-edge-strong checked:border-control-on\",\n \"focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"checked:before:absolute checked:before:start-1/2 checked:before:top-1/2 checked:before:size-2 checked:before:-translate-x-1/2 checked:before:-translate-y-1/2 checked:before:rounded-full checked:before:bg-control-on checked:before:content-['']\",\n className\n )}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
- import { useComposedRefs } from "./compose-refs.js";
5
+ import { useComposedRefs } from "../lib/compose-refs.js";
6
6
  import { DataGridCell } from "./data-grid-cell.js";
7
7
  import {
8
8
  flexRender,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-grid/data-grid-row.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ColumnPinningState, Row, TableMeta, VisibilityState } from \"@tanstack/react-table\";\nimport type { VirtualItem } from \"@tanstack/react-virtual\";\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { useComposedRefs } from \"./compose-refs.js\";\nimport { DataGridCell } from \"./data-grid-cell.js\";\nimport type { CellPosition, Direction, RowHeightValue } from \"./types.js\";\nimport {\n flexRender,\n getCellKey,\n getColumnBorderVisibility,\n getColumnPinningStyle,\n getRowHeightValue\n} from \"./utils.js\";\n\ninterface DataGridRowProps<TData> extends React.ComponentProps<\"div\"> {\n row: Row<TData>;\n tableMeta: TableMeta<TData>;\n virtualItem: VirtualItem;\n measureElement: (node: Element | null) => void;\n rowMapRef: React.RefObject<Map<number, HTMLDivElement>>;\n rowHeight: RowHeightValue;\n columnVisibility: VisibilityState;\n columnPinning: ColumnPinningState;\n focusedCell: CellPosition | null;\n editingCell: CellPosition | null;\n cellSelectionKeys: Set<string>;\n searchMatchColumns: Set<string> | null;\n activeSearchMatch: CellPosition | null;\n dir: Direction;\n readOnly: boolean;\n stretchColumns: boolean;\n adjustLayout: boolean;\n}\n\nexport const DataGridRow = React.memo(DataGridRowImpl, (prev, next) => {\n const prevRowIndex = prev.virtualItem.index;\n const nextRowIndex = next.virtualItem.index;\n\n // Re-render if row identity changed\n if (prev.row.id !== next.row.id) {\n return false;\n }\n\n // Re-render if row data (original) reference changed\n if (prev.row.original !== next.row.original) {\n return false;\n }\n\n // Re-render if virtual position changed (handles transform updates)\n if (prev.virtualItem.start !== next.virtualItem.start) {\n return false;\n }\n\n // Re-render if focus state changed for this row\n const prevHasFocus = prev.focusedCell?.rowIndex === prevRowIndex;\n const nextHasFocus = next.focusedCell?.rowIndex === nextRowIndex;\n\n if (prevHasFocus !== nextHasFocus) {\n return false;\n }\n\n // Re-render if focused column changed within this row\n if (nextHasFocus && prevHasFocus) {\n if (prev.focusedCell?.columnId !== next.focusedCell?.columnId) {\n return false;\n }\n }\n\n // Re-render if editing state changed for this row\n const prevHasEditing = prev.editingCell?.rowIndex === prevRowIndex;\n const nextHasEditing = next.editingCell?.rowIndex === nextRowIndex;\n\n if (prevHasEditing !== nextHasEditing) {\n return false;\n }\n\n // Re-render if editing column changed within this row\n if (nextHasEditing && prevHasEditing) {\n if (prev.editingCell?.columnId !== next.editingCell?.columnId) {\n return false;\n }\n }\n\n // Re-render if this row's selected cells changed\n // Using stable Set reference that only includes this row's cells\n if (prev.cellSelectionKeys !== next.cellSelectionKeys) {\n return false;\n }\n\n // Re-render if column visibility changed\n if (prev.columnVisibility !== next.columnVisibility) {\n return false;\n }\n\n // Re-render if row height changed\n if (prev.rowHeight !== next.rowHeight) {\n return false;\n }\n\n // Re-render if column pinning state changed\n if (prev.columnPinning !== next.columnPinning) {\n return false;\n }\n\n // Re-render if readOnly changed\n if (prev.readOnly !== next.readOnly) {\n return false;\n }\n\n // Re-render if search match columns changed for this row\n if (prev.searchMatchColumns !== next.searchMatchColumns) {\n return false;\n }\n\n // Re-render if active search match changed for this row\n if (prev.activeSearchMatch?.columnId !== next.activeSearchMatch?.columnId) {\n return false;\n }\n\n // Re-render if direction changed\n if (prev.dir !== next.dir) {\n return false;\n }\n\n // Re-render if adjustLayout state changed\n if (prev.adjustLayout !== next.adjustLayout) {\n return false;\n }\n\n // Re-render if stretchColumns changed\n if (prev.stretchColumns !== next.stretchColumns) {\n return false;\n }\n\n // Skip re-render - props are equal\n return true;\n}) as typeof DataGridRowImpl;\n\nfunction DataGridRowImpl<TData>({\n row,\n tableMeta,\n virtualItem,\n measureElement,\n rowMapRef,\n rowHeight,\n columnVisibility,\n columnPinning,\n focusedCell,\n editingCell,\n cellSelectionKeys,\n searchMatchColumns,\n activeSearchMatch,\n dir,\n readOnly,\n stretchColumns,\n adjustLayout,\n className,\n style,\n ref,\n ...props\n}: DataGridRowProps<TData>) {\n const virtualRowIndex = virtualItem.index;\n\n const onRowChange = React.useCallback(\n (node: HTMLDivElement | null) => {\n if (typeof virtualRowIndex === \"undefined\") return;\n\n if (node) {\n measureElement(node);\n rowMapRef.current?.set(virtualRowIndex, node);\n } else {\n rowMapRef.current?.delete(virtualRowIndex);\n }\n },\n [virtualRowIndex, measureElement, rowMapRef]\n );\n\n const rowRef = useComposedRefs(ref, onRowChange);\n\n const isRowSelected = row.getIsSelected();\n\n // Memoize visible cells to avoid recreating cell array on every render\n // Though TanStack returns new Cell wrappers, memoizing the array helps React's reconciliation\n // biome-ignore lint/correctness/useExhaustiveDependencies: columnVisibility and columnPinning are used for calculating the visible cells\n const visibleCells = React.useMemo(() => row.getVisibleCells(), [row, columnVisibility, columnPinning]);\n\n return (\n <div\n key={row.id}\n role=\"row\"\n aria-rowindex={virtualRowIndex + 2}\n aria-selected={isRowSelected}\n data-index={virtualRowIndex}\n data-slot=\"grid-row\"\n tabIndex={-1}\n {...props}\n ref={rowRef}\n className={cn(\n \"absolute flex w-full border-b [content-visibility:auto]\",\n !adjustLayout && \"will-change-transform\",\n className\n )}\n style={{\n height: `${getRowHeightValue(rowHeight)}px`,\n ...(adjustLayout ? { top: `${virtualItem.start}px` } : { transform: `translateY(${virtualItem.start}px)` }),\n ...style\n }}\n >\n {visibleCells.map((cell, colIndex) => {\n const columnId = cell.column.id;\n\n const isCellFocused = focusedCell?.rowIndex === virtualRowIndex && focusedCell?.columnId === columnId;\n const isCellEditing = editingCell?.rowIndex === virtualRowIndex && editingCell?.columnId === columnId;\n const isCellSelected = cellSelectionKeys?.has(getCellKey(virtualRowIndex, columnId)) ?? false;\n\n const isSearchMatch = searchMatchColumns?.has(columnId) ?? false;\n const isActiveSearchMatch = activeSearchMatch?.columnId === columnId;\n\n const nextCell = visibleCells[colIndex + 1];\n const isLastColumn = colIndex === visibleCells.length - 1;\n const { showEndBorder, showStartBorder } = getColumnBorderVisibility({\n column: cell.column,\n nextColumn: nextCell?.column,\n isLastColumn\n });\n\n return (\n <div\n key={cell.id}\n role=\"gridcell\"\n aria-colindex={colIndex + 1}\n data-highlighted={isCellFocused ? \"\" : undefined}\n data-slot=\"grid-cell\"\n tabIndex={-1}\n className={cn({\n grow: stretchColumns && columnId !== \"select\",\n \"border-e\": showEndBorder && columnId !== \"select\",\n \"border-s\": showStartBorder && columnId !== \"select\"\n })}\n style={{\n ...getColumnPinningStyle({ column: cell.column, dir }),\n width: `calc(var(--col-${columnId}-size) * 1px)`\n }}\n >\n {typeof cell.column.columnDef.header === \"function\" ? (\n <div\n className={cn(\"size-full px-3 py-1.5\", {\n // `--accent` là bậc tint TRẠNG THÁI của gói (Tabs/Switch/Progress dùng đúng nó\n // để nói \"đang bật\"). `bg-primary/10` là gold pha 10% — 1,05:1, một dấu hiệu giả.\n \"bg-accent\": isRowSelected\n })}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </div>\n ) : (\n <DataGridCell\n cell={cell}\n tableMeta={tableMeta}\n rowIndex={virtualRowIndex}\n columnId={columnId}\n rowHeight={rowHeight}\n isFocused={isCellFocused}\n isEditing={isCellEditing}\n isSelected={isCellSelected}\n isSearchMatch={isSearchMatch}\n isActiveSearchMatch={isActiveSearchMatch}\n readOnly={readOnly}\n />\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-row.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ColumnPinningState, Row, TableMeta, VisibilityState } from \"@tanstack/react-table\";\nimport type { VirtualItem } from \"@tanstack/react-virtual\";\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { useComposedRefs } from \"../lib/compose-refs.js\";\nimport { DataGridCell } from \"./data-grid-cell.js\";\nimport type { CellPosition, Direction, RowHeightValue } from \"./types.js\";\nimport {\n flexRender,\n getCellKey,\n getColumnBorderVisibility,\n getColumnPinningStyle,\n getRowHeightValue\n} from \"./utils.js\";\n\ninterface DataGridRowProps<TData> extends React.ComponentProps<\"div\"> {\n row: Row<TData>;\n tableMeta: TableMeta<TData>;\n virtualItem: VirtualItem;\n measureElement: (node: Element | null) => void;\n rowMapRef: React.RefObject<Map<number, HTMLDivElement>>;\n rowHeight: RowHeightValue;\n columnVisibility: VisibilityState;\n columnPinning: ColumnPinningState;\n focusedCell: CellPosition | null;\n editingCell: CellPosition | null;\n cellSelectionKeys: Set<string>;\n searchMatchColumns: Set<string> | null;\n activeSearchMatch: CellPosition | null;\n dir: Direction;\n readOnly: boolean;\n stretchColumns: boolean;\n adjustLayout: boolean;\n}\n\nexport const DataGridRow = React.memo(DataGridRowImpl, (prev, next) => {\n const prevRowIndex = prev.virtualItem.index;\n const nextRowIndex = next.virtualItem.index;\n\n // Re-render if row identity changed\n if (prev.row.id !== next.row.id) {\n return false;\n }\n\n // Re-render if row data (original) reference changed\n if (prev.row.original !== next.row.original) {\n return false;\n }\n\n // Re-render if virtual position changed (handles transform updates)\n if (prev.virtualItem.start !== next.virtualItem.start) {\n return false;\n }\n\n // Re-render if focus state changed for this row\n const prevHasFocus = prev.focusedCell?.rowIndex === prevRowIndex;\n const nextHasFocus = next.focusedCell?.rowIndex === nextRowIndex;\n\n if (prevHasFocus !== nextHasFocus) {\n return false;\n }\n\n // Re-render if focused column changed within this row\n if (nextHasFocus && prevHasFocus) {\n if (prev.focusedCell?.columnId !== next.focusedCell?.columnId) {\n return false;\n }\n }\n\n // Re-render if editing state changed for this row\n const prevHasEditing = prev.editingCell?.rowIndex === prevRowIndex;\n const nextHasEditing = next.editingCell?.rowIndex === nextRowIndex;\n\n if (prevHasEditing !== nextHasEditing) {\n return false;\n }\n\n // Re-render if editing column changed within this row\n if (nextHasEditing && prevHasEditing) {\n if (prev.editingCell?.columnId !== next.editingCell?.columnId) {\n return false;\n }\n }\n\n // Re-render if this row's selected cells changed\n // Using stable Set reference that only includes this row's cells\n if (prev.cellSelectionKeys !== next.cellSelectionKeys) {\n return false;\n }\n\n // Re-render if column visibility changed\n if (prev.columnVisibility !== next.columnVisibility) {\n return false;\n }\n\n // Re-render if row height changed\n if (prev.rowHeight !== next.rowHeight) {\n return false;\n }\n\n // Re-render if column pinning state changed\n if (prev.columnPinning !== next.columnPinning) {\n return false;\n }\n\n // Re-render if readOnly changed\n if (prev.readOnly !== next.readOnly) {\n return false;\n }\n\n // Re-render if search match columns changed for this row\n if (prev.searchMatchColumns !== next.searchMatchColumns) {\n return false;\n }\n\n // Re-render if active search match changed for this row\n if (prev.activeSearchMatch?.columnId !== next.activeSearchMatch?.columnId) {\n return false;\n }\n\n // Re-render if direction changed\n if (prev.dir !== next.dir) {\n return false;\n }\n\n // Re-render if adjustLayout state changed\n if (prev.adjustLayout !== next.adjustLayout) {\n return false;\n }\n\n // Re-render if stretchColumns changed\n if (prev.stretchColumns !== next.stretchColumns) {\n return false;\n }\n\n // Skip re-render - props are equal\n return true;\n}) as typeof DataGridRowImpl;\n\nfunction DataGridRowImpl<TData>({\n row,\n tableMeta,\n virtualItem,\n measureElement,\n rowMapRef,\n rowHeight,\n columnVisibility,\n columnPinning,\n focusedCell,\n editingCell,\n cellSelectionKeys,\n searchMatchColumns,\n activeSearchMatch,\n dir,\n readOnly,\n stretchColumns,\n adjustLayout,\n className,\n style,\n ref,\n ...props\n}: DataGridRowProps<TData>) {\n const virtualRowIndex = virtualItem.index;\n\n const onRowChange = React.useCallback(\n (node: HTMLDivElement | null) => {\n if (typeof virtualRowIndex === \"undefined\") return;\n\n if (node) {\n measureElement(node);\n rowMapRef.current?.set(virtualRowIndex, node);\n } else {\n rowMapRef.current?.delete(virtualRowIndex);\n }\n },\n [virtualRowIndex, measureElement, rowMapRef]\n );\n\n const rowRef = useComposedRefs(ref, onRowChange);\n\n const isRowSelected = row.getIsSelected();\n\n // Memoize visible cells to avoid recreating cell array on every render\n // Though TanStack returns new Cell wrappers, memoizing the array helps React's reconciliation\n // biome-ignore lint/correctness/useExhaustiveDependencies: columnVisibility and columnPinning are used for calculating the visible cells\n const visibleCells = React.useMemo(() => row.getVisibleCells(), [row, columnVisibility, columnPinning]);\n\n return (\n <div\n key={row.id}\n role=\"row\"\n aria-rowindex={virtualRowIndex + 2}\n aria-selected={isRowSelected}\n data-index={virtualRowIndex}\n data-slot=\"grid-row\"\n tabIndex={-1}\n {...props}\n ref={rowRef}\n className={cn(\n \"absolute flex w-full border-b [content-visibility:auto]\",\n !adjustLayout && \"will-change-transform\",\n className\n )}\n style={{\n height: `${getRowHeightValue(rowHeight)}px`,\n ...(adjustLayout ? { top: `${virtualItem.start}px` } : { transform: `translateY(${virtualItem.start}px)` }),\n ...style\n }}\n >\n {visibleCells.map((cell, colIndex) => {\n const columnId = cell.column.id;\n\n const isCellFocused = focusedCell?.rowIndex === virtualRowIndex && focusedCell?.columnId === columnId;\n const isCellEditing = editingCell?.rowIndex === virtualRowIndex && editingCell?.columnId === columnId;\n const isCellSelected = cellSelectionKeys?.has(getCellKey(virtualRowIndex, columnId)) ?? false;\n\n const isSearchMatch = searchMatchColumns?.has(columnId) ?? false;\n const isActiveSearchMatch = activeSearchMatch?.columnId === columnId;\n\n const nextCell = visibleCells[colIndex + 1];\n const isLastColumn = colIndex === visibleCells.length - 1;\n const { showEndBorder, showStartBorder } = getColumnBorderVisibility({\n column: cell.column,\n nextColumn: nextCell?.column,\n isLastColumn\n });\n\n return (\n <div\n key={cell.id}\n role=\"gridcell\"\n aria-colindex={colIndex + 1}\n data-highlighted={isCellFocused ? \"\" : undefined}\n data-slot=\"grid-cell\"\n tabIndex={-1}\n className={cn({\n grow: stretchColumns && columnId !== \"select\",\n \"border-e\": showEndBorder && columnId !== \"select\",\n \"border-s\": showStartBorder && columnId !== \"select\"\n })}\n style={{\n ...getColumnPinningStyle({ column: cell.column, dir }),\n width: `calc(var(--col-${columnId}-size) * 1px)`\n }}\n >\n {typeof cell.column.columnDef.header === \"function\" ? (\n <div\n className={cn(\"size-full px-3 py-1.5\", {\n // `--accent` là bậc tint TRẠNG THÁI của gói (Tabs/Switch/Progress dùng đúng nó\n // để nói \"đang bật\"). `bg-primary/10` là gold pha 10% — 1,05:1, một dấu hiệu giả.\n \"bg-accent\": isRowSelected\n })}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </div>\n ) : (\n <DataGridCell\n cell={cell}\n tableMeta={tableMeta}\n rowIndex={virtualRowIndex}\n columnId={columnId}\n rowHeight={rowHeight}\n isFocused={isCellFocused}\n isEditing={isCellEditing}\n isSelected={isCellSelected}\n isSearchMatch={isSearchMatch}\n isActiveSearchMatch={isActiveSearchMatch}\n readOnly={readOnly}\n />\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -4,8 +4,8 @@ import { ChevronDown, ChevronUp, X } from "lucide-react";
4
4
  import * as React from "react";
5
5
  import { Button } from "../components/ui/button.js";
6
6
  import { Input } from "../components/ui/input.js";
7
+ import { useAsRef } from "../lib/use-as-ref.js";
7
8
  import { useDataGridMessages } from "./data-grid-locale.js";
8
- import { useAsRef } from "./use-as-ref.js";
9
9
  import { useDebouncedCallback } from "./use-debounced-callback.js";
10
10
  function onTriggerPointerDown(event) {
11
11
  const target = event.target;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-grid/data-grid-search.tsx"],"sourcesContent":["\"use client\";\n\nimport { ChevronDown, ChevronUp, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button } from \"../components/ui/button.js\";\nimport { Input } from \"../components/ui/input.js\";\nimport { useDataGridMessages } from \"./data-grid-locale.js\";\nimport type { SearchState } from \"./types.js\";\nimport { useAsRef } from \"./use-as-ref.js\";\nimport { useDebouncedCallback } from \"./use-debounced-callback.js\";\n\nfunction onTriggerPointerDown(event: React.PointerEvent<HTMLButtonElement>) {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n if (target.hasPointerCapture(event.pointerId)) {\n target.releasePointerCapture(event.pointerId);\n }\n\n // Prevent the trigger from stealing focus away from the input\n if (\n event.button === 0 &&\n event.ctrlKey === false &&\n event.pointerType === \"mouse\" &&\n !(event.target instanceof HTMLInputElement)\n ) {\n event.preventDefault();\n }\n}\n\ninterface DataGridSearchProps extends SearchState {}\n\nexport const DataGridSearch = React.memo(DataGridSearchImpl, (prev, next) => {\n if (prev.searchOpen !== next.searchOpen) return false;\n\n if (!next.searchOpen) return true;\n\n // Exclude searchQuery because the input is uncontrolled, and hasQuery state handles the status text\n if (prev.matchIndex !== next.matchIndex) return false;\n\n if (prev.searchMatches.length !== next.searchMatches.length) return false;\n\n for (let i = 0; i < prev.searchMatches.length; i++) {\n const prevMatch = prev.searchMatches[i];\n const nextMatch = next.searchMatches[i];\n\n if (!prevMatch || !nextMatch) return false;\n\n if (prevMatch.rowIndex !== nextMatch.rowIndex || prevMatch.columnId !== nextMatch.columnId) {\n return false;\n }\n }\n\n return true;\n});\n\nfunction DataGridSearchImpl({\n searchMatches,\n matchIndex,\n searchOpen,\n onSearchOpenChange,\n searchQuery,\n onSearchQueryChange,\n onSearch,\n onNavigateToNextMatch,\n onNavigateToPrevMatch\n}: DataGridSearchProps) {\n const msg = useDataGridMessages();\n\n const propsRef = useAsRef({\n onSearchOpenChange,\n onSearchQueryChange,\n onSearch,\n onNavigateToNextMatch,\n onNavigateToPrevMatch\n });\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const isComposingRef = React.useRef(false);\n const [hasQuery, setHasQuery] = React.useState(searchQuery.length > 0);\n\n React.useEffect(() => {\n if (searchOpen) {\n requestAnimationFrame(() => {\n inputRef.current?.focus();\n });\n return;\n }\n\n isComposingRef.current = false;\n setHasQuery(false);\n }, [searchOpen]);\n\n React.useEffect(() => {\n if (!searchOpen) return;\n\n function onEscape(event: KeyboardEvent) {\n if (event.key === \"Escape\") {\n event.preventDefault();\n propsRef.current.onSearchOpenChange(false);\n }\n }\n\n document.addEventListener(\"keydown\", onEscape);\n return () => document.removeEventListener(\"keydown\", onEscape);\n }, [searchOpen, propsRef]);\n\n const debouncedSearch = useDebouncedCallback((query: string) => {\n propsRef.current.onSearch(query);\n }, 150);\n\n function onCompositionStart() {\n isComposingRef.current = true;\n }\n\n function onCompositionEnd(event: React.CompositionEvent<HTMLInputElement>) {\n isComposingRef.current = false;\n const value = event.currentTarget.value;\n setHasQuery(value.length > 0);\n propsRef.current.onSearchQueryChange(value);\n debouncedSearch(value);\n }\n\n function onKeyDown(event: React.KeyboardEvent) {\n event.stopPropagation();\n\n if (event.key === \"Enter\") {\n if (event.nativeEvent.isComposing) return;\n event.preventDefault();\n if (event.shiftKey) {\n propsRef.current.onNavigateToPrevMatch();\n } else {\n propsRef.current.onNavigateToNextMatch();\n }\n }\n }\n\n function onChange(event: React.ChangeEvent<HTMLInputElement>) {\n if (isComposingRef.current) return;\n const value = event.target.value;\n setHasQuery(value.length > 0);\n propsRef.current.onSearchQueryChange(value);\n debouncedSearch(value);\n }\n\n function onClose() {\n propsRef.current.onSearchOpenChange(false);\n }\n\n function onPrevMatch() {\n propsRef.current.onNavigateToPrevMatch();\n }\n\n function onNextMatch() {\n propsRef.current.onNavigateToNextMatch();\n }\n\n if (!searchOpen) return null;\n\n return (\n <div\n role=\"search\"\n data-slot=\"grid-search\"\n // `shadow-overlay`, KHÔNG `shadow-lg`: bóng của gói đi qua `--elevation-*` nên nó đổi\n // theo sáng/tối; `shadow-lg` của Tailwind là rgba đen cố định, ở dark gần như vô hình.\n className=\"fade-in-0 slide-in-from-top-2 absolute end-4 top-4 z-50 flex animate-in flex-col gap-2 rounded-lg border bg-popover p-2 shadow-overlay\"\n >\n <div className=\"flex items-center gap-2\">\n <Input\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n spellCheck={false}\n placeholder={msg.search.placeholder}\n // placeholder biến mất ngay khi người dùng gõ → vẫn phải có nhãn riêng\n aria-label={msg.search.label}\n className=\"h-8 w-64\"\n ref={inputRef}\n defaultValue={searchQuery}\n onChange={onChange}\n onKeyDown={onKeyDown}\n onCompositionStart={onCompositionStart}\n onCompositionEnd={onCompositionEnd}\n />\n <div className=\"flex items-center gap-1\">\n <Button\n aria-label={msg.search.prevMatch}\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-7\"\n onClick={onPrevMatch}\n onPointerDown={onTriggerPointerDown}\n disabled={searchMatches.length === 0}\n >\n <ChevronUp className=\"size-3.5\" />\n </Button>\n <Button\n aria-label={msg.search.nextMatch}\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-7\"\n onClick={onNextMatch}\n onPointerDown={onTriggerPointerDown}\n disabled={searchMatches.length === 0}\n >\n <ChevronDown className=\"size-3.5\" />\n </Button>\n <Button aria-label={msg.search.close} variant=\"ghost\" size=\"icon\" className=\"size-7\" onClick={onClose}>\n <X className=\"size-3.5\" />\n </Button>\n </div>\n </div>\n <div role=\"status\" className=\"flex items-center gap-1 whitespace-nowrap text-muted-foreground text-xs\">\n {searchMatches.length > 0 ? (\n <span>{msg.search.matchPosition(matchIndex + 1, searchMatches.length)}</span>\n ) : hasQuery ? (\n <span>{msg.search.noResults}</span>\n ) : (\n <span>{msg.search.typeToSearch}</span>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-search.tsx"],"sourcesContent":["\"use client\";\n\nimport { ChevronDown, ChevronUp, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button } from \"../components/ui/button.js\";\nimport { Input } from \"../components/ui/input.js\";\nimport { useAsRef } from \"../lib/use-as-ref.js\";\nimport { useDataGridMessages } from \"./data-grid-locale.js\";\nimport type { SearchState } from \"./types.js\";\nimport { useDebouncedCallback } from \"./use-debounced-callback.js\";\n\nfunction onTriggerPointerDown(event: React.PointerEvent<HTMLButtonElement>) {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n if (target.hasPointerCapture(event.pointerId)) {\n target.releasePointerCapture(event.pointerId);\n }\n\n // Prevent the trigger from stealing focus away from the input\n if (\n event.button === 0 &&\n event.ctrlKey === false &&\n event.pointerType === \"mouse\" &&\n !(event.target instanceof HTMLInputElement)\n ) {\n event.preventDefault();\n }\n}\n\ninterface DataGridSearchProps extends SearchState {}\n\nexport const DataGridSearch = React.memo(DataGridSearchImpl, (prev, next) => {\n if (prev.searchOpen !== next.searchOpen) return false;\n\n if (!next.searchOpen) return true;\n\n // Exclude searchQuery because the input is uncontrolled, and hasQuery state handles the status text\n if (prev.matchIndex !== next.matchIndex) return false;\n\n if (prev.searchMatches.length !== next.searchMatches.length) return false;\n\n for (let i = 0; i < prev.searchMatches.length; i++) {\n const prevMatch = prev.searchMatches[i];\n const nextMatch = next.searchMatches[i];\n\n if (!prevMatch || !nextMatch) return false;\n\n if (prevMatch.rowIndex !== nextMatch.rowIndex || prevMatch.columnId !== nextMatch.columnId) {\n return false;\n }\n }\n\n return true;\n});\n\nfunction DataGridSearchImpl({\n searchMatches,\n matchIndex,\n searchOpen,\n onSearchOpenChange,\n searchQuery,\n onSearchQueryChange,\n onSearch,\n onNavigateToNextMatch,\n onNavigateToPrevMatch\n}: DataGridSearchProps) {\n const msg = useDataGridMessages();\n\n const propsRef = useAsRef({\n onSearchOpenChange,\n onSearchQueryChange,\n onSearch,\n onNavigateToNextMatch,\n onNavigateToPrevMatch\n });\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const isComposingRef = React.useRef(false);\n const [hasQuery, setHasQuery] = React.useState(searchQuery.length > 0);\n\n React.useEffect(() => {\n if (searchOpen) {\n requestAnimationFrame(() => {\n inputRef.current?.focus();\n });\n return;\n }\n\n isComposingRef.current = false;\n setHasQuery(false);\n }, [searchOpen]);\n\n React.useEffect(() => {\n if (!searchOpen) return;\n\n function onEscape(event: KeyboardEvent) {\n if (event.key === \"Escape\") {\n event.preventDefault();\n propsRef.current.onSearchOpenChange(false);\n }\n }\n\n document.addEventListener(\"keydown\", onEscape);\n return () => document.removeEventListener(\"keydown\", onEscape);\n }, [searchOpen, propsRef]);\n\n const debouncedSearch = useDebouncedCallback((query: string) => {\n propsRef.current.onSearch(query);\n }, 150);\n\n function onCompositionStart() {\n isComposingRef.current = true;\n }\n\n function onCompositionEnd(event: React.CompositionEvent<HTMLInputElement>) {\n isComposingRef.current = false;\n const value = event.currentTarget.value;\n setHasQuery(value.length > 0);\n propsRef.current.onSearchQueryChange(value);\n debouncedSearch(value);\n }\n\n function onKeyDown(event: React.KeyboardEvent) {\n event.stopPropagation();\n\n if (event.key === \"Enter\") {\n if (event.nativeEvent.isComposing) return;\n event.preventDefault();\n if (event.shiftKey) {\n propsRef.current.onNavigateToPrevMatch();\n } else {\n propsRef.current.onNavigateToNextMatch();\n }\n }\n }\n\n function onChange(event: React.ChangeEvent<HTMLInputElement>) {\n if (isComposingRef.current) return;\n const value = event.target.value;\n setHasQuery(value.length > 0);\n propsRef.current.onSearchQueryChange(value);\n debouncedSearch(value);\n }\n\n function onClose() {\n propsRef.current.onSearchOpenChange(false);\n }\n\n function onPrevMatch() {\n propsRef.current.onNavigateToPrevMatch();\n }\n\n function onNextMatch() {\n propsRef.current.onNavigateToNextMatch();\n }\n\n if (!searchOpen) return null;\n\n return (\n <div\n role=\"search\"\n data-slot=\"grid-search\"\n // `shadow-overlay`, KHÔNG `shadow-lg`: bóng của gói đi qua `--elevation-*` nên nó đổi\n // theo sáng/tối; `shadow-lg` của Tailwind là rgba đen cố định, ở dark gần như vô hình.\n className=\"fade-in-0 slide-in-from-top-2 absolute end-4 top-4 z-50 flex animate-in flex-col gap-2 rounded-lg border bg-popover p-2 shadow-overlay\"\n >\n <div className=\"flex items-center gap-2\">\n <Input\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n spellCheck={false}\n placeholder={msg.search.placeholder}\n // placeholder biến mất ngay khi người dùng gõ → vẫn phải có nhãn riêng\n aria-label={msg.search.label}\n className=\"h-8 w-64\"\n ref={inputRef}\n defaultValue={searchQuery}\n onChange={onChange}\n onKeyDown={onKeyDown}\n onCompositionStart={onCompositionStart}\n onCompositionEnd={onCompositionEnd}\n />\n <div className=\"flex items-center gap-1\">\n <Button\n aria-label={msg.search.prevMatch}\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-7\"\n onClick={onPrevMatch}\n onPointerDown={onTriggerPointerDown}\n disabled={searchMatches.length === 0}\n >\n <ChevronUp className=\"size-3.5\" />\n </Button>\n <Button\n aria-label={msg.search.nextMatch}\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-7\"\n onClick={onNextMatch}\n onPointerDown={onTriggerPointerDown}\n disabled={searchMatches.length === 0}\n >\n <ChevronDown className=\"size-3.5\" />\n </Button>\n <Button aria-label={msg.search.close} variant=\"ghost\" size=\"icon\" className=\"size-7\" onClick={onClose}>\n <X className=\"size-3.5\" />\n </Button>\n </div>\n </div>\n <div role=\"status\" className=\"flex items-center gap-1 whitespace-nowrap text-muted-foreground text-xs\">\n {searchMatches.length > 0 ? (\n <span>{msg.search.matchPosition(matchIndex + 1, searchMatches.length)}</span>\n ) : hasQuery ? (\n <span>{msg.search.noResults}</span>\n ) : (\n <span>{msg.search.typeToSearch}</span>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -4,6 +4,7 @@ import { Plus } from "lucide-react";
4
4
  import * as React from "react";
5
5
  import { TooltipProvider } from "../components/ui/tooltip.js";
6
6
  import { cn } from "../lib/cn.js";
7
+ import { useAsRef } from "../lib/use-as-ref.js";
7
8
  import { DataGridColumnHeader } from "./data-grid-column-header.js";
8
9
  import { DataGridContextMenu } from "./data-grid-context-menu.js";
9
10
  import { DataGridLocaleProvider } from "./data-grid-locale.js";
@@ -11,7 +12,6 @@ import { DataGridPasteDialog } from "./data-grid-paste-dialog.js";
11
12
  import { DataGridRow } from "./data-grid-row.js";
12
13
  import { DataGridSearch } from "./data-grid-search.js";
13
14
  import { getDataGridMessages } from "./messages.js";
14
- import { useAsRef } from "./use-as-ref.js";
15
15
  import { flexRender, getColumnBorderVisibility, getColumnPinningStyle } from "./utils.js";
16
16
  const EMPTY_CELL_SELECTION_SET = /* @__PURE__ */ new Set();
17
17
  function DataGrid({