@lablup/ui-common 0.1.0-alpha.2 → 0.1.0-alpha.21

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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/components/BaseCard/BaseCard.css +1 -1
  3. package/dist/assets/components/Button/Button.css +1 -1
  4. package/dist/assets/components/DataTable/DataTable.css +1 -1
  5. package/dist/assets/components/Drawer/Drawer.css +1 -1
  6. package/dist/assets/components/EmptyState/EmptyState.css +1 -1
  7. package/dist/assets/components/ErrorState/ErrorState.css +1 -1
  8. package/dist/assets/components/PageHeader/PageHeader.css +1 -1
  9. package/dist/assets/components/Select/Select.css +1 -0
  10. package/dist/assets/components/StatCard/StatCard.css +1 -1
  11. package/dist/assets/components/Tabs/Tabs.css +1 -1
  12. package/dist/assets/components/Tooltip/Tooltip.css +1 -1
  13. package/dist/components/DataTable/DataTable.d.ts +50 -6
  14. package/dist/components/DataTable/DataTable2.js +205 -133
  15. package/dist/components/DataTable/DataTable2.js.map +1 -1
  16. package/dist/components/Drawer/Drawer.d.ts +12 -1
  17. package/dist/components/Drawer/Drawer2.js +69 -51
  18. package/dist/components/Drawer/Drawer2.js.map +1 -1
  19. package/dist/components/EmptyState/EmptyState.d.ts +10 -3
  20. package/dist/components/EmptyState/EmptyState2.js +31 -48
  21. package/dist/components/EmptyState/EmptyState2.js.map +1 -1
  22. package/dist/components/EmptyState/index.d.ts +6 -1
  23. package/dist/components/ErrorState/ErrorState.d.ts +17 -4
  24. package/dist/components/ErrorState/ErrorState2.js +20 -23
  25. package/dist/components/ErrorState/ErrorState2.js.map +1 -1
  26. package/dist/components/ErrorState/index.d.ts +1 -1
  27. package/dist/components/PageHeader/PageHeader.d.ts +14 -1
  28. package/dist/components/PageHeader/PageHeader2.js +40 -24
  29. package/dist/components/PageHeader/PageHeader2.js.map +1 -1
  30. package/dist/components/Select/Select.d.ts +38 -0
  31. package/dist/components/Select/Select2.js +313 -0
  32. package/dist/components/Select/Select2.js.map +1 -0
  33. package/dist/components/Select/index.d.ts +2 -0
  34. package/dist/components/Select/index.js +2 -0
  35. package/dist/components/Skeleton/Skeleton.d.ts +11 -1
  36. package/dist/components/Skeleton/Skeleton2.js +8 -6
  37. package/dist/components/Skeleton/Skeleton2.js.map +1 -1
  38. package/dist/components/Skeleton/SkeletonCard.d.ts +3 -1
  39. package/dist/components/Skeleton/SkeletonCard2.js +13 -3
  40. package/dist/components/Skeleton/SkeletonCard2.js.map +1 -1
  41. package/dist/components/Skeleton/SkeletonChart2.js +5 -0
  42. package/dist/components/Skeleton/SkeletonChart2.js.map +1 -1
  43. package/dist/components/Skeleton/SkeletonRow.d.ts +3 -1
  44. package/dist/components/Skeleton/SkeletonRow2.js +9 -3
  45. package/dist/components/Skeleton/SkeletonRow2.js.map +1 -1
  46. package/dist/components/Skeleton/SkeletonText.d.ts +3 -1
  47. package/dist/components/Skeleton/SkeletonText2.js +7 -5
  48. package/dist/components/Skeleton/SkeletonText2.js.map +1 -1
  49. package/dist/components/StatCard/StatCard.d.ts +10 -1
  50. package/dist/components/StatCard/StatCard2.js +30 -30
  51. package/dist/components/StatCard/StatCard2.js.map +1 -1
  52. package/dist/components/Tabs/Tabs.d.ts +10 -23
  53. package/dist/components/Tabs/Tabs2.js +127 -146
  54. package/dist/components/Tabs/Tabs2.js.map +1 -1
  55. package/dist/components/Tabs/index.d.ts +2 -2
  56. package/dist/components/Tabs/index.js +2 -3
  57. package/dist/components/Tooltip/Tooltip.d.ts +15 -1
  58. package/dist/components/Tooltip/Tooltip2.js +77 -47
  59. package/dist/components/Tooltip/Tooltip2.js.map +1 -1
  60. package/dist/index.d.ts +1 -0
  61. package/dist/index.js +6 -6
  62. package/dist/styles/base.css +101 -10
  63. package/dist/styles/themes/orange-dark.css +3 -0
  64. package/dist/styles/themes/orange-light.css +6 -1
  65. package/package.json +1 -1
  66. package/dist/components/EmptyState/illustrations.d.ts +0 -51
  67. package/dist/components/EmptyState/illustrations.js +0 -571
  68. package/dist/components/EmptyState/illustrations.js.map +0 -1
  69. package/dist/components/Tabs/tabTags.d.ts +0 -9
  70. package/dist/components/Tabs/tabTags.js +0 -11
  71. package/dist/components/Tabs/tabTags.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Tabs2.js","names":[],"sources":["../../../src/components/Tabs/Tabs.tsx"],"sourcesContent":["/**\n * Tabs Component\n *\n * Unified tab navigation component with optional grouping, guide tag badges,\n * and responsive overflow handling. Follows WCAG 2.1 ARIA tab pattern.\n *\n * Features:\n * - Scroll arrows for overflow\n * - Mobile dropdown mode (overflowMode=\"dropdown\", < 480px)\n * - Overflow menu mode (overflowMode=\"menu\", < 768px)\n * - Tab grouping with separators and labels\n * - Guide tag badge support (required, recommended, beta, experimental)\n * - Touch swipe navigation (dropdown mode)\n * - Auto-scroll to active tab\n * - Full keyboard navigation (Arrow keys, Home, End)\n */\n\nimport {\n useState,\n useCallback,\n useRef,\n useEffect,\n useMemo,\n type ReactNode,\n type KeyboardEvent,\n type TouchEvent,\n} from \"react\";\nimport { Badge } from \"../Badge\";\nimport { type TabTagType, type TabTagLabels, TAG_CONFIG } from \"./tabTags\";\nimport \"./Tabs.css\";\n\n// Re-export for consumers that import from Tabs.tsx directly\nexport type { TabTagType, TabTagLabels } from \"./tabTags\";\nexport { TAG_CONFIG } from \"./tabTags\";\n\n/** English default for every {@link TabTagType} badge label. */\nconst DEFAULT_TAG_LABELS: TabTagLabels = {\n required: \"Required\",\n recommended: \"Recommended\",\n beta: \"Beta\",\n experimental: \"Experimental\",\n};\n\nexport interface TabItem {\n id: string;\n label: string;\n content: ReactNode;\n /**\n * Show a \"Required\" badge next to the tab label.\n * @deprecated Use `tag=\"required\"` instead. Kept for backward compatibility.\n */\n required?: boolean;\n /**\n * Guide tag displayed after the tab label as a small badge.\n * When set, takes precedence over the `required` boolean.\n */\n tag?: TabTagType;\n /**\n * Optional extra trailing node rendered next to the tab label (e.g. a\n * count badge). Distinct from `tag` (which uses a fixed preset). Use this\n * when migrating segmented controls that rendered their own inline count\n * pill alongside the label.\n */\n labelExtra?: ReactNode;\n /**\n * Optional leading node rendered before the tab label (e.g. an icon).\n * Useful when migrating segmented controls that prefix the label with\n * an SVG glyph (e.g. ImportExportDialog source toggle: File / Paste / URL).\n */\n labelPrefix?: ReactNode;\n /** Assign this tab to a group (used with the `groups` prop) */\n groupId?: string;\n}\n\nexport interface TabGroupMeta {\n id: string;\n label: string;\n}\n\nexport type TabOverflowMode = \"dropdown\" | \"menu\";\n\n/**\n * Visual variant of the tab bar.\n *\n * - `\"underlined\"` (default): pill container with raised active tab and scroll\n * arrows; used for primary page-level navigation that may overflow.\n * - `\"segmented\"`: gray-background segmented control with raised active tab;\n * intended for short, fixed-arity in-page mode toggles.\n * - `\"compact\"`: smaller segmented control with reduced padding; used where\n * the tab bar shares a row with other controls (e.g. StatisticsPage).\n */\nexport type TabVariant = \"underlined\" | \"segmented\" | \"compact\";\n\nexport interface TabsProps {\n tabs: TabItem[];\n /** Group definitions for organizing tabs with separators and labels */\n groups?: TabGroupMeta[];\n defaultTab?: string;\n activeTab?: string;\n onTabChange?: (tabId: string) => void;\n className?: string;\n panelClassName?: string;\n ariaLabel?: string;\n /** Show scroll arrows and overflow controls (default: true) */\n showOverflowControls?: boolean;\n /** Show group labels in the tab bar and overflow menu (default: true) */\n showGroupLabels?: boolean;\n /**\n * Overflow strategy for small screens:\n * - \"dropdown\": mobile dropdown trigger (< 480px) — default when no groups\n * - \"menu\": overflow menu button (< 768px) — default when groups are provided\n */\n overflowMode?: TabOverflowMode;\n /**\n * Visual variant. Defaults to `\"underlined\"` (the original Tabs look).\n * Use `\"segmented\"` for iOS-style pill toggles, `\"compact\"` for a smaller\n * variant suitable for in-header use alongside other controls.\n */\n variant?: TabVariant;\n /**\n * When using `variant=\"segmented\"` or `\"compact\"`, make each tab fill the\n * container evenly (flex: 1 per tab). Useful for short, equal-arity mode\n * toggles like the Models page tab bar. Defaults to false.\n */\n fillContainer?: boolean;\n /**\n * Render the tab panel (the active tab's `content`) below the tab list.\n * Defaults to true. Set to false when the consumer manages its own panel\n * rendering outside the Tabs component (e.g. pages that interleave panels\n * with other in-header elements).\n *\n * When false, each `TabItem.content` is ignored — the consumer is expected\n * to inspect `activeTab` / `onTabChange` and render its own panel.\n */\n renderPanel?: boolean;\n /** Accessible label for the overflow-menu trigger button. Default: \"More tabs\" */\n moreTabsLabel?: string;\n /** Accessible label for the mobile dropdown trigger. Default: \"Select tab\" */\n selectTabLabel?: string;\n /** Accessible label for the left scroll-arrow button. Default: \"Scroll left\" */\n scrollLeftLabel?: string;\n /** Accessible label for the right scroll-arrow button. Default: \"Scroll right\" */\n scrollRightLabel?: string;\n /**\n * English defaults for every guide-tag badge label (required / recommended\n * / beta / experimental). Override individual entries to translate them.\n */\n tagLabels?: TabTagLabels;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function Tabs({\n tabs,\n groups = [],\n defaultTab,\n activeTab: controlledTab,\n onTabChange,\n className = \"\",\n panelClassName = \"\",\n ariaLabel,\n showOverflowControls = true,\n showGroupLabels = true,\n overflowMode: explicitOverflowMode,\n variant = \"underlined\",\n fillContainer = false,\n renderPanel = true,\n moreTabsLabel = \"More tabs\",\n selectTabLabel = \"Select tab\",\n scrollLeftLabel = \"Scroll left\",\n scrollRightLabel = \"Scroll right\",\n tagLabels = DEFAULT_TAG_LABELS,\n}: TabsProps) {\n // Auto-detect overflow mode from groups presence\n const overflowMode =\n explicitOverflowMode ?? (groups.length > 0 ? \"menu\" : \"dropdown\");\n\n // ---- State ----\n const [internalActiveTab, setInternalActiveTab] = useState(\n defaultTab || tabs[0]?.id || \"\",\n );\n const [canScrollLeft, setCanScrollLeft] = useState(false);\n const [canScrollRight, setCanScrollRight] = useState(false);\n\n // Dropdown mode state (overflowMode === \"dropdown\")\n const [isMobileDropdown, setIsMobileDropdown] = useState(false);\n const [isDropdownOpen, setIsDropdownOpen] = useState(false);\n\n // Overflow menu state (overflowMode === \"menu\")\n const [showOverflowMenu, setShowOverflowMenu] = useState(false);\n const [focusedMenuIndex, setFocusedMenuIndex] = useState(-1);\n\n // ---- Refs ----\n const tabListRef = useRef<HTMLDivElement>(null);\n const tabRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n const dropdownContainerRef = useRef<HTMLDivElement>(null);\n const overflowMenuRef = useRef<HTMLDivElement>(null);\n const overflowButtonRef = useRef<HTMLButtonElement>(null);\n const menuItemRefs = useRef<HTMLButtonElement[]>([]);\n const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const touchStartX = useRef<number>(0);\n const touchEndX = useRef<number>(0);\n\n // ---- Derived state ----\n const activeTab = controlledTab ?? internalActiveTab;\n\n // Group tabs by groupId\n const groupedTabs = useMemo(() => {\n return groups.length > 0\n ? groups.map((group) => ({\n group,\n tabs: tabs.filter((tab) => tab.groupId === group.id),\n }))\n : [{ group: null as TabGroupMeta | null, tabs }];\n }, [groups, tabs]);\n\n // Flatten menu items for keyboard navigation\n const flattenedMenuTabs = useMemo(() => {\n return groupedTabs.flatMap(({ tabs: groupTabs }) => groupTabs);\n }, [groupedTabs]);\n\n // ---- Handlers ----\n\n const handleTabClick = useCallback(\n (tabId: string) => {\n if (!controlledTab) {\n setInternalActiveTab(tabId);\n }\n onTabChange?.(tabId);\n setShowOverflowMenu(false);\n },\n [controlledTab, onTabChange],\n );\n\n const handleKeyDown = useCallback(\n (\n e: KeyboardEvent<HTMLButtonElement> | KeyboardEvent<HTMLDivElement>,\n currentIndex: number,\n ) => {\n let targetIndex: number;\n\n switch (e.key) {\n case \"ArrowRight\":\n e.preventDefault();\n targetIndex = currentIndex === tabs.length - 1 ? 0 : currentIndex + 1;\n break;\n case \"ArrowLeft\":\n e.preventDefault();\n targetIndex = currentIndex === 0 ? tabs.length - 1 : currentIndex - 1;\n break;\n case \"Home\":\n e.preventDefault();\n targetIndex = 0;\n break;\n case \"End\":\n e.preventDefault();\n targetIndex = tabs.length - 1;\n break;\n default:\n return;\n }\n\n const targetTab = tabs[targetIndex];\n if (targetTab) {\n handleTabClick(targetTab.id);\n tabRefs.current.get(targetTab.id)?.focus();\n }\n },\n [tabs, handleTabClick],\n );\n\n const setTabRef = useCallback((id: string, element: HTMLButtonElement | null) => {\n if (element) {\n tabRefs.current.set(id, element);\n } else {\n tabRefs.current.delete(id);\n }\n }, []);\n\n // ---- Scroll controls ----\n\n const checkScroll = useCallback(() => {\n const container = tabListRef.current;\n if (!container || !showOverflowControls) {\n setCanScrollLeft(false);\n setCanScrollRight(false);\n return;\n }\n\n const { scrollLeft, scrollWidth, clientWidth } = container;\n setCanScrollLeft(scrollLeft > 0);\n setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 1);\n }, [showOverflowControls]);\n\n const scrollTabs = useCallback(\n (direction: \"left\" | \"right\") => {\n const container = tabListRef.current;\n if (!container) return;\n\n const scrollAmount = container.clientWidth * 0.75;\n container.scrollBy({\n left: direction === \"left\" ? -scrollAmount : scrollAmount,\n behavior: \"smooth\",\n });\n\n if (scrollTimeoutRef.current !== null) {\n clearTimeout(scrollTimeoutRef.current);\n }\n scrollTimeoutRef.current = setTimeout(checkScroll, 300);\n },\n [checkScroll],\n );\n\n // ---- Auto-scroll to active tab ----\n\n const scrollToActiveTab = useCallback(() => {\n if (!tabListRef.current || isMobileDropdown) return;\n\n const activeButton = tabRefs.current.get(activeTab);\n if (!activeButton) return;\n\n const container = tabListRef.current;\n const buttonRect = activeButton.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n\n if (buttonRect.left < containerRect.left) {\n container.scrollLeft -= containerRect.left - buttonRect.left + 8;\n } else if (buttonRect.right > containerRect.right) {\n container.scrollLeft += buttonRect.right - containerRect.right + 8;\n }\n }, [activeTab, isMobileDropdown]);\n\n // ---- Mobile dropdown mode ----\n\n const checkMobileMode = useCallback(() => {\n if (typeof window === \"undefined\" || overflowMode !== \"dropdown\") return;\n setIsMobileDropdown(window.innerWidth < 480);\n }, [overflowMode]);\n\n const toggleDropdown = useCallback(() => {\n setIsDropdownOpen((prev) => !prev);\n }, []);\n\n const handleDropdownKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === \"Escape\" && isDropdownOpen) {\n e.preventDefault();\n setIsDropdownOpen(false);\n }\n },\n [isDropdownOpen],\n );\n\n // ---- Touch swipe (dropdown mode) ----\n\n const handleTouchStart = useCallback((e: TouchEvent<HTMLDivElement>) => {\n if (e.touches[0]) {\n touchStartX.current = e.touches[0].clientX;\n }\n }, []);\n\n const handleTouchMove = useCallback((e: TouchEvent<HTMLDivElement>) => {\n if (e.touches[0]) {\n touchEndX.current = e.touches[0].clientX;\n }\n }, []);\n\n const handleTouchEnd = useCallback(() => {\n if (!touchStartX.current || !touchEndX.current) return;\n\n const diff = touchStartX.current - touchEndX.current;\n const threshold = 50;\n\n if (Math.abs(diff) > threshold) {\n const currentIndex = tabs.findIndex((tab) => tab.id === activeTab);\n const nextTab = tabs[currentIndex + 1];\n const prevTab = tabs[currentIndex - 1];\n\n if (diff > 0 && nextTab) {\n handleTabClick(nextTab.id);\n } else if (diff < 0 && prevTab) {\n handleTabClick(prevTab.id);\n }\n }\n\n touchStartX.current = 0;\n touchEndX.current = 0;\n }, [activeTab, tabs, handleTabClick]);\n\n // ---- Overflow menu (menu mode) ----\n\n const handleOverflowMenuKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n const menuItemCount = flattenedMenuTabs.length;\n\n switch (e.key) {\n case \"Escape\":\n e.preventDefault();\n setShowOverflowMenu(false);\n setFocusedMenuIndex(-1);\n overflowButtonRef.current?.focus();\n break;\n case \"ArrowDown\":\n e.preventDefault();\n setFocusedMenuIndex((prev) => {\n const next = prev >= menuItemCount - 1 ? 0 : prev + 1;\n menuItemRefs.current[next]?.focus();\n return next;\n });\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setFocusedMenuIndex((prev) => {\n const next = prev <= 0 ? menuItemCount - 1 : prev - 1;\n menuItemRefs.current[next]?.focus();\n return next;\n });\n break;\n case \"Home\":\n e.preventDefault();\n setFocusedMenuIndex(0);\n menuItemRefs.current[0]?.focus();\n break;\n case \"End\":\n e.preventDefault();\n setFocusedMenuIndex(menuItemCount - 1);\n menuItemRefs.current[menuItemCount - 1]?.focus();\n break;\n default:\n break;\n }\n },\n [flattenedMenuTabs.length],\n );\n\n const handleOverflowButtonKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === \"Escape\" && showOverflowMenu) {\n e.preventDefault();\n setShowOverflowMenu(false);\n setFocusedMenuIndex(-1);\n }\n },\n [showOverflowMenu],\n );\n\n // ---- Effects ----\n\n // Close dropdown when clicking outside\n useEffect(() => {\n if (!isDropdownOpen) return;\n\n const handleClickOutside = (event: MouseEvent) => {\n if (\n dropdownContainerRef.current &&\n !dropdownContainerRef.current.contains(event.target as Node)\n ) {\n setIsDropdownOpen(false);\n }\n };\n\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n };\n }, [isDropdownOpen]);\n\n // Close overflow menu when clicking outside\n useEffect(() => {\n if (!showOverflowMenu) return undefined;\n\n const handleClickOutside = (event: MouseEvent) => {\n if (\n overflowMenuRef.current &&\n !overflowMenuRef.current.contains(event.target as Node)\n ) {\n setShowOverflowMenu(false);\n setFocusedMenuIndex(-1);\n }\n };\n\n const handleEscapeKey = (event: globalThis.KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setShowOverflowMenu(false);\n setFocusedMenuIndex(-1);\n overflowButtonRef.current?.focus();\n }\n };\n\n document.addEventListener(\"mousedown\", handleClickOutside);\n document.addEventListener(\"keydown\", handleEscapeKey);\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n document.removeEventListener(\"keydown\", handleEscapeKey);\n };\n }, [showOverflowMenu]);\n\n // Focus menu item when overflow menu opens\n useEffect(() => {\n if (showOverflowMenu && menuItemRefs.current.length > 0) {\n const activeIndex = flattenedMenuTabs.findIndex((tab) => tab.id === activeTab);\n const indexToFocus = activeIndex >= 0 ? activeIndex : 0;\n setFocusedMenuIndex(indexToFocus);\n requestAnimationFrame(() => {\n menuItemRefs.current[indexToFocus]?.focus();\n });\n }\n }, [showOverflowMenu, flattenedMenuTabs, activeTab]);\n\n // Ensure active tab is valid\n useEffect(() => {\n if (!tabs.find((tab) => tab.id === activeTab)) {\n const firstTab = tabs[0];\n if (firstTab) {\n handleTabClick(firstTab.id);\n }\n }\n }, [tabs, activeTab, handleTabClick]);\n\n // Check mobile mode and scroll on mount/resize\n useEffect(() => {\n checkMobileMode();\n checkScroll();\n\n const handleResize = () => {\n checkMobileMode();\n checkScroll();\n };\n\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [checkMobileMode, checkScroll]);\n\n // Scroll to active tab when it changes\n useEffect(() => {\n scrollToActiveTab();\n }, [activeTab, scrollToActiveTab]);\n\n // Check scroll when tabs change\n useEffect(() => {\n checkScroll();\n }, [tabs, checkScroll]);\n\n // Cleanup scroll timeout on unmount\n useEffect(() => {\n return () => {\n if (scrollTimeoutRef.current !== null) {\n clearTimeout(scrollTimeoutRef.current);\n }\n };\n }, []);\n\n // ---- Render helpers ----\n\n const activeTabContent = tabs.find((tab) => tab.id === activeTab)?.content;\n const activeTabLabel = tabs.find((tab) => tab.id === activeTab)?.label;\n\n const hasGroups = groups.length > 0;\n\n // Reset menu item refs array each render\n menuItemRefs.current = [];\n\n /** Resolve the effective tag type for a tab (prefers `tag`, falls back to legacy `required`) */\n const resolveTabTag = (tab: TabItem): TabTagType | null => {\n if (tab.tag) return tab.tag;\n\n if (tab.required) return \"required\";\n return null;\n };\n\n /** Render a single tab button */\n const renderTabButton = (tab: TabItem, globalIndex: number) => {\n const isActive = tab.id === activeTab;\n const effectiveTag = resolveTabTag(tab);\n const tagConfig = effectiveTag ? TAG_CONFIG[effectiveTag] : null;\n const tagLabel = effectiveTag ? tagLabels[effectiveTag] : null;\n\n return (\n <button\n key={tab.id}\n ref={(el) => {\n setTabRef(tab.id, el);\n }}\n role=\"tab\"\n aria-selected={isActive}\n aria-controls={renderPanel ? `tabpanel-${tab.id}` : undefined}\n id={`tab-${tab.id}`}\n tabIndex={isActive ? 0 : -1}\n className={`tabs__tab${isActive ? \" tabs__tab--active\" : \"\"}`}\n onClick={() => {\n handleTabClick(tab.id);\n }}\n onKeyDown={(e) => {\n handleKeyDown(e, globalIndex);\n // Prevent the tablist's onKeyDown from also firing (it runs the\n // same Arrow/Home/End logic for synthetic tests / unusual focus\n // setups where focus is on the tablist itself).\n if (\n e.key === \"ArrowLeft\" ||\n e.key === \"ArrowRight\" ||\n e.key === \"Home\" ||\n e.key === \"End\"\n ) {\n e.stopPropagation();\n }\n }}\n >\n {tab.labelPrefix}\n <span className=\"tabs__tab-label\">{tab.label}</span>\n {tagConfig && (\n <Badge variant={tagConfig.variant} size=\"small\" className=\"tabs__tag-badge\">\n {tagLabel}\n </Badge>\n )}\n {tab.labelExtra}\n </button>\n );\n };\n\n /** Render the tab list content (flat or grouped) */\n const renderTabListContent = () => {\n if (hasGroups) {\n return groupedTabs.map(({ group, tabs: groupTabs }, groupIndex) => (\n <div key={group?.id || \"default\"} className=\"tabs__group\">\n {group && (\n <>\n {groupIndex > 0 && (\n <div className=\"tabs__separator\" role=\"separator\" aria-hidden=\"true\" />\n )}\n {showGroupLabels && (\n <span className=\"tabs__group-label\">{group.label}</span>\n )}\n </>\n )}\n <div className=\"tabs__group-tabs\">\n {groupTabs.map((tab) => {\n const globalIndex = tabs.findIndex((t) => t.id === tab.id);\n return renderTabButton(tab, globalIndex);\n })}\n </div>\n </div>\n ));\n }\n\n return tabs.map((tab, index) => renderTabButton(tab, index));\n };\n\n /** Render the overflow menu (menu mode) */\n const renderOverflowMenu = () => {\n if (overflowMode !== \"menu\" || !showOverflowControls) return null;\n\n return (\n <div className=\"tabs__overflow\" ref={overflowMenuRef}>\n <button\n ref={overflowButtonRef}\n type=\"button\"\n className=\"tabs__overflow-btn\"\n onClick={() => {\n setShowOverflowMenu(!showOverflowMenu);\n }}\n onKeyDown={handleOverflowButtonKeyDown}\n aria-label={moreTabsLabel}\n aria-expanded={showOverflowMenu}\n aria-haspopup=\"menu\"\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\n </svg>\n </button>\n\n {showOverflowMenu && (\n <div\n className=\"tabs__overflow-menu\"\n role=\"menu\"\n onKeyDown={handleOverflowMenuKeyDown}\n >\n {groupedTabs.map(({ group, tabs: groupTabs }) => (\n <div key={group?.id || \"default\"} className=\"tabs__overflow-group\">\n {group && showGroupLabels && (\n <div className=\"tabs__overflow-group-label\">{group.label}</div>\n )}\n {groupTabs.map((tab) => {\n const isActive = tab.id === activeTab;\n const flatIndex = flattenedMenuTabs.findIndex((t) => t.id === tab.id);\n const effectiveTag = resolveTabTag(tab);\n const tagConfig = effectiveTag ? TAG_CONFIG[effectiveTag] : null;\n const tagLabel = effectiveTag ? tagLabels[effectiveTag] : null;\n return (\n <button\n key={tab.id}\n ref={(el) => {\n if (el) {\n menuItemRefs.current[flatIndex] = el;\n }\n }}\n role=\"menuitem\"\n tabIndex={focusedMenuIndex === flatIndex ? 0 : -1}\n className={`tabs__overflow-item ${isActive ? \"tabs__overflow-item--active\" : \"\"}`}\n onClick={() => {\n handleTabClick(tab.id);\n }}\n >\n {tab.labelPrefix}\n <span>{tab.label}</span>\n {tagConfig && (\n <Badge variant={tagConfig.variant} size=\"small\">\n {tagLabel}\n </Badge>\n )}\n {tab.labelExtra}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n };\n\n // ---- Variant class helpers ----\n\n // For segmented/compact variants we always want a single horizontal pill —\n // there is no scrollable arrow affordance and the mobile dropdown trigger\n // would visually conflict with the segmented look. Underlined remains the\n // only variant that follows the historical \"scroll + dropdown\" behavior.\n const isSegmentedFamily = variant === \"segmented\" || variant === \"compact\";\n const variantClass =\n variant === \"segmented\"\n ? \"tabs--variant-segmented\"\n : variant === \"compact\"\n ? \"tabs--variant-compact\"\n : \"\";\n const fillContainerClass =\n isSegmentedFamily && fillContainer ? \"tabs--fill-container\" : \"\";\n\n // ---- Mobile dropdown render (overflowMode === \"dropdown\") ----\n\n // Segmented/compact variants always render as a single inline tab bar — we\n // skip the mobile-dropdown branch even when the viewport is narrow so the\n // visual matches the existing per-page \"segmented control\" markup the\n // migration replaces.\n if (overflowMode === \"dropdown\" && isMobileDropdown && !isSegmentedFamily) {\n return (\n <div className={`tabs tabs--mobile-dropdown ${className}`}>\n <div className=\"tabs__dropdown-container\" ref={dropdownContainerRef}>\n <button\n className=\"tabs__dropdown-trigger\"\n onClick={toggleDropdown}\n onKeyDown={handleDropdownKeyDown}\n aria-expanded={isDropdownOpen}\n aria-haspopup=\"listbox\"\n aria-label={ariaLabel || selectTabLabel}\n >\n <span className=\"tabs__dropdown-label\">{activeTabLabel}</span>\n <svg\n className={`tabs__dropdown-arrow ${isDropdownOpen ? \"tabs__dropdown-arrow--open\" : \"\"}`}\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M4 6L8 10L12 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n\n {isDropdownOpen && (\n <div className=\"tabs__dropdown-menu\" role=\"listbox\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n role=\"option\"\n aria-selected={tab.id === activeTab}\n className={`tabs__dropdown-item ${tab.id === activeTab ? \"tabs__dropdown-item--active\" : \"\"}`}\n onClick={() => {\n handleTabClick(tab.id);\n setIsDropdownOpen(false);\n }}\n >\n {tab.label}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {renderPanel && (\n <div\n id={`tabpanel-${activeTab}`}\n role=\"tabpanel\"\n aria-labelledby={`tab-${activeTab}`}\n className={`tabs__panel ${panelClassName}`.trim()}\n tabIndex={0}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n {activeTabContent}\n </div>\n )}\n </div>\n );\n }\n\n // ---- Desktop/tablet render ----\n\n const modeClass = overflowMode === \"menu\" ? \"tabs--overflow-menu\" : \"\";\n\n return (\n <div\n className={`tabs ${modeClass} ${variantClass} ${fillContainerClass} ${className}`\n .replace(/\\s+/g, \" \")\n .trim()}\n >\n <div className=\"tabs__container\">\n {/* Scroll left arrow */}\n {showOverflowControls && canScrollLeft && (\n <button\n className=\"tabs__scroll-arrow tabs__scroll-arrow--left\"\n onClick={() => {\n scrollTabs(\"left\");\n }}\n aria-label={scrollLeftLabel}\n tabIndex={-1}\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M12 15L7 10L12 5\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n )}\n\n <div\n ref={tabListRef}\n className=\"tabs__list\"\n role=\"tablist\"\n aria-label={ariaLabel}\n onScroll={checkScroll}\n onKeyDown={(e) => {\n // Mirror the per-button handler at the tablist level so synthetic\n // tests (and unusual focus setups where the tablist itself owns\n // the keyboard focus) get the same Arrow/Home/End behavior. Tab\n // buttons are still the primary handler; this only fires when\n // the event reaches the tablist without being handled by a tab.\n const currentIndex = tabs.findIndex((tab) => tab.id === activeTab);\n if (currentIndex < 0) return;\n handleKeyDown(e, currentIndex);\n }}\n onTouchStart={overflowMode === \"dropdown\" ? handleTouchStart : undefined}\n onTouchMove={overflowMode === \"dropdown\" ? handleTouchMove : undefined}\n onTouchEnd={overflowMode === \"dropdown\" ? handleTouchEnd : undefined}\n >\n {renderTabListContent()}\n </div>\n\n {/* Scroll right arrow */}\n {showOverflowControls && canScrollRight && (\n <button\n className=\"tabs__scroll-arrow tabs__scroll-arrow--right\"\n onClick={() => {\n scrollTabs(\"right\");\n }}\n aria-label={scrollRightLabel}\n tabIndex={-1}\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M8 5L13 10L8 15\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n )}\n\n {/* Overflow menu (menu mode) */}\n {renderOverflowMenu()}\n </div>\n\n {renderPanel && (\n <div\n id={`tabpanel-${activeTab}`}\n role=\"tabpanel\"\n aria-labelledby={`tab-${activeTab}`}\n className={`tabs__panel ${panelClassName}`.trim()}\n tabIndex={0}\n >\n {activeTabContent}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Tabs2.js","names":[],"sources":["../../../src/components/Tabs/Tabs.tsx"],"sourcesContent":["/**\n * Tabs Component\n *\n * Unified tab navigation component with optional grouping, guide tag badges,\n * and responsive overflow handling. Follows WCAG 2.1 ARIA tab pattern.\n *\n * Features:\n * - Scroll arrows for overflow\n * - Mobile dropdown mode (overflowMode=\"dropdown\", < 480px)\n * - Overflow menu mode (overflowMode=\"menu\", < 768px)\n * - Tab grouping with separators and labels\n * - Guide tag badge support (required, recommended, beta, experimental)\n * - Touch swipe navigation (dropdown mode)\n * - Auto-scroll to active tab\n * - Full keyboard navigation (Arrow keys, Home, End)\n */\n\nimport {\n useState,\n useCallback,\n useRef,\n useEffect,\n useMemo,\n type ReactNode,\n type KeyboardEvent,\n type TouchEvent,\n} from \"react\";\nimport \"./Tabs.css\";\n\nexport interface TabItem {\n id: string;\n label: string;\n content: ReactNode;\n /**\n * Trailing node rendered next to the tab label: a count pill, a status\n * badge, whatever the consumer's vocabulary calls for.\n *\n * This used to sit beside a `tag` prop that took one of four fixed names,\n * required / recommended / beta / experimental, and rendered a Badge for\n * it. Those are one product's guide vocabulary, and the arrangement had\n * already split across the boundary: the badge variant lived here while the\n * label text had to be passed in from the consumer's locale bundle. A\n * consumer that wants that badge renders it here, and owns both halves.\n */\n labelExtra?: ReactNode;\n /**\n * Optional leading node rendered before the tab label (e.g. an icon).\n * Useful when migrating segmented controls that prefix the label with\n * an SVG glyph (e.g. ImportExportDialog source toggle: File / Paste / URL).\n */\n labelPrefix?: ReactNode;\n /** Assign this tab to a group (used with the `groups` prop) */\n groupId?: string;\n}\n\nexport interface TabGroupMeta {\n id: string;\n label: string;\n}\n\nexport type TabOverflowMode = \"dropdown\" | \"menu\";\n\n/**\n * Visual variant of the tab bar.\n *\n * - `\"underlined\"` (default): pill container with raised active tab and scroll\n * arrows; used for primary page-level navigation that may overflow.\n * - `\"segmented\"`: gray-background segmented control with raised active tab;\n * intended for short, fixed-arity in-page mode toggles.\n * - `\"compact\"`: smaller segmented control with reduced padding; used where\n * the tab bar shares a row with other controls (e.g. StatisticsPage).\n */\nexport type TabVariant = \"underlined\" | \"segmented\" | \"compact\";\n\nexport interface TabsProps {\n tabs: TabItem[];\n /** Group definitions for organizing tabs with separators and labels */\n groups?: TabGroupMeta[];\n defaultTab?: string;\n activeTab?: string;\n onTabChange?: (tabId: string) => void;\n className?: string;\n panelClassName?: string;\n ariaLabel?: string;\n /** Show scroll arrows and overflow controls (default: true) */\n showOverflowControls?: boolean;\n /** Show group labels in the tab bar and overflow menu (default: true) */\n showGroupLabels?: boolean;\n /**\n * Overflow strategy for small screens:\n * - \"dropdown\": mobile dropdown trigger (< 480px) — default when no groups\n * - \"menu\": overflow menu button (< 768px) — default when groups are provided\n */\n overflowMode?: TabOverflowMode;\n /**\n * Visual variant. Defaults to `\"underlined\"` (the original Tabs look).\n * Use `\"segmented\"` for iOS-style pill toggles, `\"compact\"` for a smaller\n * variant suitable for in-header use alongside other controls.\n */\n variant?: TabVariant;\n /**\n * When using `variant=\"segmented\"` or `\"compact\"`, make each tab fill the\n * container evenly (flex: 1 per tab). Useful for short, equal-arity mode\n * toggles like the Models page tab bar. Defaults to false.\n */\n fillContainer?: boolean;\n /**\n * Render the tab panel (the active tab's `content`) below the tab list.\n * Defaults to true. Set to false when the consumer manages its own panel\n * rendering outside the Tabs component (e.g. pages that interleave panels\n * with other in-header elements).\n *\n * When false, each `TabItem.content` is ignored — the consumer is expected\n * to inspect `activeTab` / `onTabChange` and render its own panel.\n */\n renderPanel?: boolean;\n /** Accessible label for the overflow-menu trigger button. Default: \"More tabs\" */\n moreTabsLabel?: string;\n /** Accessible label for the mobile dropdown trigger. Default: \"Select tab\" */\n selectTabLabel?: string;\n /** Accessible label for the left scroll-arrow button. Default: \"Scroll left\" */\n scrollLeftLabel?: string;\n /** Accessible label for the right scroll-arrow button. Default: \"Scroll right\" */\n scrollRightLabel?: string;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function Tabs({\n tabs,\n groups = [],\n defaultTab,\n activeTab: controlledTab,\n onTabChange,\n className = \"\",\n panelClassName = \"\",\n ariaLabel,\n showOverflowControls = true,\n showGroupLabels = true,\n overflowMode: explicitOverflowMode,\n variant = \"underlined\",\n fillContainer = false,\n renderPanel = true,\n moreTabsLabel = \"More tabs\",\n selectTabLabel = \"Select tab\",\n scrollLeftLabel = \"Scroll left\",\n scrollRightLabel = \"Scroll right\",\n}: TabsProps) {\n // Auto-detect overflow mode from groups presence\n const overflowMode =\n explicitOverflowMode ?? (groups.length > 0 ? \"menu\" : \"dropdown\");\n\n // ---- State ----\n const [internalActiveTab, setInternalActiveTab] = useState(\n defaultTab || tabs[0]?.id || \"\",\n );\n const [canScrollLeft, setCanScrollLeft] = useState(false);\n const [canScrollRight, setCanScrollRight] = useState(false);\n\n // Dropdown mode state (overflowMode === \"dropdown\")\n const [isMobileDropdown, setIsMobileDropdown] = useState(false);\n const [isDropdownOpen, setIsDropdownOpen] = useState(false);\n\n // Overflow menu state (overflowMode === \"menu\")\n const [showOverflowMenu, setShowOverflowMenu] = useState(false);\n const [focusedMenuIndex, setFocusedMenuIndex] = useState(-1);\n\n // ---- Refs ----\n const tabListRef = useRef<HTMLDivElement>(null);\n const tabRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n const dropdownContainerRef = useRef<HTMLDivElement>(null);\n const overflowMenuRef = useRef<HTMLDivElement>(null);\n const overflowButtonRef = useRef<HTMLButtonElement>(null);\n const menuItemRefs = useRef<HTMLButtonElement[]>([]);\n const scrollTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const touchStartX = useRef<number>(0);\n const touchEndX = useRef<number>(0);\n\n // ---- Derived state ----\n const activeTab = controlledTab ?? internalActiveTab;\n\n // Group tabs by groupId\n const groupedTabs = useMemo(() => {\n return groups.length > 0\n ? groups.map((group) => ({\n group,\n tabs: tabs.filter((tab) => tab.groupId === group.id),\n }))\n : [{ group: null as TabGroupMeta | null, tabs }];\n }, [groups, tabs]);\n\n // Flatten menu items for keyboard navigation\n const flattenedMenuTabs = useMemo(() => {\n return groupedTabs.flatMap(({ tabs: groupTabs }) => groupTabs);\n }, [groupedTabs]);\n\n // ---- Handlers ----\n\n const handleTabClick = useCallback(\n (tabId: string) => {\n if (!controlledTab) {\n setInternalActiveTab(tabId);\n }\n onTabChange?.(tabId);\n setShowOverflowMenu(false);\n },\n [controlledTab, onTabChange],\n );\n\n const handleKeyDown = useCallback(\n (\n e: KeyboardEvent<HTMLButtonElement> | KeyboardEvent<HTMLDivElement>,\n currentIndex: number,\n ) => {\n let targetIndex: number;\n\n switch (e.key) {\n case \"ArrowRight\":\n e.preventDefault();\n targetIndex = currentIndex === tabs.length - 1 ? 0 : currentIndex + 1;\n break;\n case \"ArrowLeft\":\n e.preventDefault();\n targetIndex = currentIndex === 0 ? tabs.length - 1 : currentIndex - 1;\n break;\n case \"Home\":\n e.preventDefault();\n targetIndex = 0;\n break;\n case \"End\":\n e.preventDefault();\n targetIndex = tabs.length - 1;\n break;\n default:\n return;\n }\n\n const targetTab = tabs[targetIndex];\n if (targetTab) {\n handleTabClick(targetTab.id);\n tabRefs.current.get(targetTab.id)?.focus();\n }\n },\n [tabs, handleTabClick],\n );\n\n const setTabRef = useCallback((id: string, element: HTMLButtonElement | null) => {\n if (element) {\n tabRefs.current.set(id, element);\n } else {\n tabRefs.current.delete(id);\n }\n }, []);\n\n // ---- Scroll controls ----\n\n const checkScroll = useCallback(() => {\n const container = tabListRef.current;\n if (!container || !showOverflowControls) {\n setCanScrollLeft(false);\n setCanScrollRight(false);\n return;\n }\n\n const { scrollLeft, scrollWidth, clientWidth } = container;\n setCanScrollLeft(scrollLeft > 0);\n setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 1);\n }, [showOverflowControls]);\n\n const scrollTabs = useCallback(\n (direction: \"left\" | \"right\") => {\n const container = tabListRef.current;\n if (!container) return;\n\n const scrollAmount = container.clientWidth * 0.75;\n container.scrollBy({\n left: direction === \"left\" ? -scrollAmount : scrollAmount,\n behavior: \"smooth\",\n });\n\n if (scrollTimeoutRef.current !== null) {\n clearTimeout(scrollTimeoutRef.current);\n }\n scrollTimeoutRef.current = setTimeout(checkScroll, 300);\n },\n [checkScroll],\n );\n\n // ---- Auto-scroll to active tab ----\n\n const scrollToActiveTab = useCallback(() => {\n if (!tabListRef.current || isMobileDropdown) return;\n\n const activeButton = tabRefs.current.get(activeTab);\n if (!activeButton) return;\n\n const container = tabListRef.current;\n const buttonRect = activeButton.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n\n if (buttonRect.left < containerRect.left) {\n container.scrollLeft -= containerRect.left - buttonRect.left + 8;\n } else if (buttonRect.right > containerRect.right) {\n container.scrollLeft += buttonRect.right - containerRect.right + 8;\n }\n }, [activeTab, isMobileDropdown]);\n\n // ---- Mobile dropdown mode ----\n\n const checkMobileMode = useCallback(() => {\n if (typeof window === \"undefined\" || overflowMode !== \"dropdown\") return;\n setIsMobileDropdown(window.innerWidth < 480);\n }, [overflowMode]);\n\n const toggleDropdown = useCallback(() => {\n setIsDropdownOpen((prev) => !prev);\n }, []);\n\n const handleDropdownKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === \"Escape\" && isDropdownOpen) {\n e.preventDefault();\n setIsDropdownOpen(false);\n }\n },\n [isDropdownOpen],\n );\n\n // ---- Touch swipe (dropdown mode) ----\n\n const handleTouchStart = useCallback((e: TouchEvent<HTMLDivElement>) => {\n if (e.touches[0]) {\n touchStartX.current = e.touches[0].clientX;\n }\n }, []);\n\n const handleTouchMove = useCallback((e: TouchEvent<HTMLDivElement>) => {\n if (e.touches[0]) {\n touchEndX.current = e.touches[0].clientX;\n }\n }, []);\n\n const handleTouchEnd = useCallback(() => {\n if (!touchStartX.current || !touchEndX.current) return;\n\n const diff = touchStartX.current - touchEndX.current;\n const threshold = 50;\n\n if (Math.abs(diff) > threshold) {\n const currentIndex = tabs.findIndex((tab) => tab.id === activeTab);\n const nextTab = tabs[currentIndex + 1];\n const prevTab = tabs[currentIndex - 1];\n\n if (diff > 0 && nextTab) {\n handleTabClick(nextTab.id);\n } else if (diff < 0 && prevTab) {\n handleTabClick(prevTab.id);\n }\n }\n\n touchStartX.current = 0;\n touchEndX.current = 0;\n }, [activeTab, tabs, handleTabClick]);\n\n // ---- Overflow menu (menu mode) ----\n\n const handleOverflowMenuKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n const menuItemCount = flattenedMenuTabs.length;\n\n switch (e.key) {\n case \"Escape\":\n e.preventDefault();\n setShowOverflowMenu(false);\n setFocusedMenuIndex(-1);\n overflowButtonRef.current?.focus();\n break;\n case \"ArrowDown\":\n e.preventDefault();\n setFocusedMenuIndex((prev) => {\n const next = prev >= menuItemCount - 1 ? 0 : prev + 1;\n menuItemRefs.current[next]?.focus();\n return next;\n });\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setFocusedMenuIndex((prev) => {\n const next = prev <= 0 ? menuItemCount - 1 : prev - 1;\n menuItemRefs.current[next]?.focus();\n return next;\n });\n break;\n case \"Home\":\n e.preventDefault();\n setFocusedMenuIndex(0);\n menuItemRefs.current[0]?.focus();\n break;\n case \"End\":\n e.preventDefault();\n setFocusedMenuIndex(menuItemCount - 1);\n menuItemRefs.current[menuItemCount - 1]?.focus();\n break;\n default:\n break;\n }\n },\n [flattenedMenuTabs.length],\n );\n\n const handleOverflowButtonKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === \"Escape\" && showOverflowMenu) {\n e.preventDefault();\n setShowOverflowMenu(false);\n setFocusedMenuIndex(-1);\n }\n },\n [showOverflowMenu],\n );\n\n // ---- Effects ----\n\n // Close dropdown when clicking outside\n useEffect(() => {\n if (!isDropdownOpen) return;\n\n const handleClickOutside = (event: MouseEvent) => {\n if (\n dropdownContainerRef.current &&\n !dropdownContainerRef.current.contains(event.target as Node)\n ) {\n setIsDropdownOpen(false);\n }\n };\n\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n };\n }, [isDropdownOpen]);\n\n // Close overflow menu when clicking outside\n useEffect(() => {\n if (!showOverflowMenu) return undefined;\n\n const handleClickOutside = (event: MouseEvent) => {\n if (\n overflowMenuRef.current &&\n !overflowMenuRef.current.contains(event.target as Node)\n ) {\n setShowOverflowMenu(false);\n setFocusedMenuIndex(-1);\n }\n };\n\n const handleEscapeKey = (event: globalThis.KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setShowOverflowMenu(false);\n setFocusedMenuIndex(-1);\n overflowButtonRef.current?.focus();\n }\n };\n\n document.addEventListener(\"mousedown\", handleClickOutside);\n document.addEventListener(\"keydown\", handleEscapeKey);\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n document.removeEventListener(\"keydown\", handleEscapeKey);\n };\n }, [showOverflowMenu]);\n\n // Focus menu item when overflow menu opens\n useEffect(() => {\n if (showOverflowMenu && menuItemRefs.current.length > 0) {\n const activeIndex = flattenedMenuTabs.findIndex((tab) => tab.id === activeTab);\n const indexToFocus = activeIndex >= 0 ? activeIndex : 0;\n setFocusedMenuIndex(indexToFocus);\n requestAnimationFrame(() => {\n menuItemRefs.current[indexToFocus]?.focus();\n });\n }\n }, [showOverflowMenu, flattenedMenuTabs, activeTab]);\n\n // Ensure active tab is valid\n useEffect(() => {\n if (!tabs.find((tab) => tab.id === activeTab)) {\n const firstTab = tabs[0];\n if (firstTab) {\n handleTabClick(firstTab.id);\n }\n }\n }, [tabs, activeTab, handleTabClick]);\n\n // Check mobile mode and scroll on mount/resize\n useEffect(() => {\n checkMobileMode();\n checkScroll();\n\n const handleResize = () => {\n checkMobileMode();\n checkScroll();\n };\n\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [checkMobileMode, checkScroll]);\n\n // Scroll to active tab when it changes\n useEffect(() => {\n scrollToActiveTab();\n }, [activeTab, scrollToActiveTab]);\n\n // Check scroll when tabs change\n useEffect(() => {\n checkScroll();\n }, [tabs, checkScroll]);\n\n // Cleanup scroll timeout on unmount\n useEffect(() => {\n return () => {\n if (scrollTimeoutRef.current !== null) {\n clearTimeout(scrollTimeoutRef.current);\n }\n };\n }, []);\n\n // ---- Render helpers ----\n\n const activeTabContent = tabs.find((tab) => tab.id === activeTab)?.content;\n const activeTabLabel = tabs.find((tab) => tab.id === activeTab)?.label;\n\n const hasGroups = groups.length > 0;\n\n // Reset menu item refs array each render\n menuItemRefs.current = [];\n\n /** Render a single tab button */\n const renderTabButton = (tab: TabItem, globalIndex: number) => {\n const isActive = tab.id === activeTab;\n\n return (\n <button\n key={tab.id}\n ref={(el) => {\n setTabRef(tab.id, el);\n }}\n role=\"tab\"\n aria-selected={isActive}\n aria-controls={renderPanel ? `tabpanel-${tab.id}` : undefined}\n id={`tab-${tab.id}`}\n tabIndex={isActive ? 0 : -1}\n className={`tabs__tab${isActive ? \" tabs__tab--active\" : \"\"}`}\n onClick={() => {\n handleTabClick(tab.id);\n }}\n onKeyDown={(e) => {\n handleKeyDown(e, globalIndex);\n // Prevent the tablist's onKeyDown from also firing (it runs the\n // same Arrow/Home/End logic for synthetic tests / unusual focus\n // setups where focus is on the tablist itself).\n if (\n e.key === \"ArrowLeft\" ||\n e.key === \"ArrowRight\" ||\n e.key === \"Home\" ||\n e.key === \"End\"\n ) {\n e.stopPropagation();\n }\n }}\n >\n {tab.labelPrefix}\n <span className=\"tabs__tab-label\">{tab.label}</span>\n {tab.labelExtra}\n </button>\n );\n };\n\n /** Render the tab list content (flat or grouped) */\n const renderTabListContent = () => {\n if (hasGroups) {\n return groupedTabs.map(({ group, tabs: groupTabs }, groupIndex) => (\n <div key={group?.id || \"default\"} className=\"tabs__group\">\n {group && (\n <>\n {groupIndex > 0 && (\n <div className=\"tabs__separator\" role=\"separator\" aria-hidden=\"true\" />\n )}\n {showGroupLabels && (\n <span className=\"tabs__group-label\">{group.label}</span>\n )}\n </>\n )}\n <div className=\"tabs__group-tabs\">\n {groupTabs.map((tab) => {\n const globalIndex = tabs.findIndex((t) => t.id === tab.id);\n return renderTabButton(tab, globalIndex);\n })}\n </div>\n </div>\n ));\n }\n\n return tabs.map((tab, index) => renderTabButton(tab, index));\n };\n\n /** Render the overflow menu (menu mode) */\n const renderOverflowMenu = () => {\n if (overflowMode !== \"menu\" || !showOverflowControls) return null;\n\n return (\n <div className=\"tabs__overflow\" ref={overflowMenuRef}>\n <button\n ref={overflowButtonRef}\n type=\"button\"\n className=\"tabs__overflow-btn\"\n onClick={() => {\n setShowOverflowMenu(!showOverflowMenu);\n }}\n onKeyDown={handleOverflowButtonKeyDown}\n aria-label={moreTabsLabel}\n aria-expanded={showOverflowMenu}\n aria-haspopup=\"menu\"\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\n </svg>\n </button>\n\n {showOverflowMenu && (\n <div\n className=\"tabs__overflow-menu\"\n role=\"menu\"\n onKeyDown={handleOverflowMenuKeyDown}\n >\n {groupedTabs.map(({ group, tabs: groupTabs }) => (\n <div key={group?.id || \"default\"} className=\"tabs__overflow-group\">\n {group && showGroupLabels && (\n <div className=\"tabs__overflow-group-label\">{group.label}</div>\n )}\n {groupTabs.map((tab) => {\n const isActive = tab.id === activeTab;\n const flatIndex = flattenedMenuTabs.findIndex((t) => t.id === tab.id);\n return (\n <button\n key={tab.id}\n ref={(el) => {\n if (el) {\n menuItemRefs.current[flatIndex] = el;\n }\n }}\n role=\"menuitem\"\n tabIndex={focusedMenuIndex === flatIndex ? 0 : -1}\n className={`tabs__overflow-item ${isActive ? \"tabs__overflow-item--active\" : \"\"}`}\n onClick={() => {\n handleTabClick(tab.id);\n }}\n >\n {tab.labelPrefix}\n <span>{tab.label}</span>\n {tab.labelExtra}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n };\n\n // ---- Variant class helpers ----\n\n // For segmented/compact variants we always want a single horizontal pill —\n // there is no scrollable arrow affordance and the mobile dropdown trigger\n // would visually conflict with the segmented look. Underlined remains the\n // only variant that follows the historical \"scroll + dropdown\" behavior.\n const isSegmentedFamily = variant === \"segmented\" || variant === \"compact\";\n const variantClass =\n variant === \"segmented\"\n ? \"tabs--variant-segmented\"\n : variant === \"compact\"\n ? \"tabs--variant-compact\"\n : \"\";\n const fillContainerClass =\n isSegmentedFamily && fillContainer ? \"tabs--fill-container\" : \"\";\n\n // ---- Mobile dropdown render (overflowMode === \"dropdown\") ----\n\n // Segmented/compact variants always render as a single inline tab bar — we\n // skip the mobile-dropdown branch even when the viewport is narrow so the\n // visual matches the existing per-page \"segmented control\" markup the\n // migration replaces.\n if (overflowMode === \"dropdown\" && isMobileDropdown && !isSegmentedFamily) {\n return (\n <div className={`tabs tabs--mobile-dropdown ${className}`}>\n <div className=\"tabs__dropdown-container\" ref={dropdownContainerRef}>\n <button\n className=\"tabs__dropdown-trigger\"\n onClick={toggleDropdown}\n onKeyDown={handleDropdownKeyDown}\n aria-expanded={isDropdownOpen}\n aria-haspopup=\"listbox\"\n aria-label={ariaLabel || selectTabLabel}\n >\n <span className=\"tabs__dropdown-label\">{activeTabLabel}</span>\n <svg\n className={`tabs__dropdown-arrow ${isDropdownOpen ? \"tabs__dropdown-arrow--open\" : \"\"}`}\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M4 6L8 10L12 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n\n {isDropdownOpen && (\n <div className=\"tabs__dropdown-menu\" role=\"listbox\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n role=\"option\"\n aria-selected={tab.id === activeTab}\n className={`tabs__dropdown-item ${tab.id === activeTab ? \"tabs__dropdown-item--active\" : \"\"}`}\n onClick={() => {\n handleTabClick(tab.id);\n setIsDropdownOpen(false);\n }}\n >\n {tab.label}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {renderPanel && (\n <div\n id={`tabpanel-${activeTab}`}\n role=\"tabpanel\"\n aria-labelledby={`tab-${activeTab}`}\n className={`tabs__panel ${panelClassName}`.trim()}\n tabIndex={0}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n {activeTabContent}\n </div>\n )}\n </div>\n );\n }\n\n // ---- Desktop/tablet render ----\n\n const modeClass = overflowMode === \"menu\" ? \"tabs--overflow-menu\" : \"\";\n\n return (\n <div\n className={`tabs ${modeClass} ${variantClass} ${fillContainerClass} ${className}`\n .replace(/\\s+/g, \" \")\n .trim()}\n >\n <div className=\"tabs__container\">\n {/* Scroll left arrow */}\n {showOverflowControls && canScrollLeft && (\n <button\n className=\"tabs__scroll-arrow tabs__scroll-arrow--left\"\n onClick={() => {\n scrollTabs(\"left\");\n }}\n aria-label={scrollLeftLabel}\n tabIndex={-1}\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M12 15L7 10L12 5\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n )}\n\n <div\n ref={tabListRef}\n className=\"tabs__list\"\n role=\"tablist\"\n aria-label={ariaLabel}\n onScroll={checkScroll}\n onKeyDown={(e) => {\n // Mirror the per-button handler at the tablist level so synthetic\n // tests (and unusual focus setups where the tablist itself owns\n // the keyboard focus) get the same Arrow/Home/End behavior. Tab\n // buttons are still the primary handler; this only fires when\n // the event reaches the tablist without being handled by a tab.\n const currentIndex = tabs.findIndex((tab) => tab.id === activeTab);\n if (currentIndex < 0) return;\n handleKeyDown(e, currentIndex);\n }}\n onTouchStart={overflowMode === \"dropdown\" ? handleTouchStart : undefined}\n onTouchMove={overflowMode === \"dropdown\" ? handleTouchMove : undefined}\n onTouchEnd={overflowMode === \"dropdown\" ? handleTouchEnd : undefined}\n >\n {renderTabListContent()}\n </div>\n\n {/* Scroll right arrow */}\n {showOverflowControls && canScrollRight && (\n <button\n className=\"tabs__scroll-arrow tabs__scroll-arrow--right\"\n onClick={() => {\n scrollTabs(\"right\");\n }}\n aria-label={scrollRightLabel}\n tabIndex={-1}\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M8 5L13 10L8 15\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n )}\n\n {/* Overflow menu (menu mode) */}\n {renderOverflowMenu()}\n </div>\n\n {renderPanel && (\n <div\n id={`tabpanel-${activeTab}`}\n role=\"tabpanel\"\n aria-labelledby={`tab-${activeTab}`}\n className={`tabs__panel ${panelClassName}`.trim()}\n tabIndex={0}\n >\n {activeTabContent}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;AAkIA,SAAgB,EAAK,EACnB,SACA,YAAS,CAAC,GACV,gBACA,WAAW,GACX,gBACA,eAAY,IACZ,oBAAiB,IACjB,cACA,0BAAuB,IACvB,qBAAkB,IAClB,cAAc,GACd,aAAU,cACV,oBAAgB,IAChB,iBAAc,IACd,oBAAgB,aAChB,qBAAiB,cACjB,qBAAkB,eAClB,uBAAmB,kBACP;CAEZ,IAAM,IACJ,MAAyB,EAAO,SAAS,IAAI,SAAS,aAGlD,CAAC,GAAmB,MAAwB,EAChD,MAAc,EAAK,EAAE,EAAE,MAAM,EAC/B,GACM,CAAC,IAAe,KAAoB,EAAS,EAAK,GAClD,CAAC,IAAgB,KAAqB,EAAS,EAAK,GAGpD,CAAC,GAAkB,MAAuB,EAAS,EAAK,GACxD,CAAC,GAAgB,KAAqB,EAAS,EAAK,GAGpD,CAAC,GAAkB,KAAuB,EAAS,EAAK,GACxD,CAAC,IAAkB,KAAuB,EAAS,EAAE,GAGrD,IAAa,EAAuB,IAAI,GACxC,IAAU,kBAAuC,IAAI,IAAI,CAAC,GAC1D,IAAuB,EAAuB,IAAI,GAClD,IAAkB,EAAuB,IAAI,GAC7C,IAAoB,EAA0B,IAAI,GAClD,IAAe,EAA4B,CAAC,CAAC,GAC7C,IAAmB,EAA6C,IAAI,GACpE,IAAc,EAAe,CAAC,GAC9B,IAAY,EAAe,CAAC,GAG5B,IAAY,KAAiB,GAG7B,IAAc,QACX,EAAO,SAAS,IACnB,EAAO,KAAK,OAAW;EACrB;EACA,MAAM,EAAK,QAAQ,MAAQ,EAAI,YAAY,EAAM,EAAE;CACrD,EAAE,IACF,CAAC;EAAE,OAAO;EAA6B;CAAK,CAAC,GAChD,CAAC,GAAQ,CAAI,CAAC,GAGX,IAAoB,QACjB,EAAY,SAAS,EAAE,MAAM,QAAgB,CAAS,GAC5D,CAAC,CAAW,CAAC,GAIV,IAAiB,GACpB,MAAkB;EAKjB,AAJK,KACH,GAAqB,CAAK,GAE5B,IAAc,CAAK,GACnB,EAAoB,EAAK;CAC3B,GACA,CAAC,GAAe,CAAW,CAC7B,GAEM,IAAgB,GAElB,GACA,MACG;EACH,IAAI;EAEJ,QAAQ,EAAE,KAAV;GACE,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,IAAc,MAAiB,EAAK,SAAS,IAAI,IAAI,IAAe;IACpE;GACF,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,IAAc,MAAiB,IAAI,EAAK,SAAS,IAAI,IAAe;IACpE;GACF,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,IAAc;IACd;GACF,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,IAAc,EAAK,SAAS;IAC5B;GACF,SACE;EACJ;EAEA,IAAM,IAAY,EAAK;EACvB,AAAI,MACF,EAAe,EAAU,EAAE,GAC3B,EAAQ,QAAQ,IAAI,EAAU,EAAE,CAAC,EAAE,MAAM;CAE7C,GACA,CAAC,GAAM,CAAc,CACvB,GAEM,KAAY,GAAa,GAAY,MAAsC;EAC/E,AAAI,IACF,EAAQ,QAAQ,IAAI,GAAI,CAAO,IAE/B,EAAQ,QAAQ,OAAO,CAAE;CAE7B,GAAG,CAAC,CAAC,GAIC,IAAc,QAAkB;EACpC,IAAM,IAAY,EAAW;EAC7B,IAAI,CAAC,KAAa,CAAC,GAAsB;GAEvC,AADA,EAAiB,EAAK,GACtB,EAAkB,EAAK;GACvB;EACF;EAEA,IAAM,EAAE,eAAY,gBAAa,mBAAgB;EAEjD,AADA,EAAiB,IAAa,CAAC,GAC/B,EAAkB,IAAa,IAAc,IAAc,CAAC;CAC9D,GAAG,CAAC,CAAoB,CAAC,GAEnB,IAAa,GAChB,MAAgC;EAC/B,IAAM,IAAY,EAAW;EAC7B,IAAI,CAAC,GAAW;EAEhB,IAAM,IAAe,EAAU,cAAc;EAS7C,AARA,EAAU,SAAS;GACjB,MAAM,MAAc,SAAS,CAAC,IAAe;GAC7C,UAAU;EACZ,CAAC,GAEG,EAAiB,YAAY,QAC/B,aAAa,EAAiB,OAAO,GAEvC,EAAiB,UAAU,WAAW,GAAa,GAAG;CACxD,GACA,CAAC,CAAW,CACd,GAIM,IAAoB,QAAkB;EAC1C,IAAI,CAAC,EAAW,WAAW,GAAkB;EAE7C,IAAM,IAAe,EAAQ,QAAQ,IAAI,CAAS;EAClD,IAAI,CAAC,GAAc;EAEnB,IAAM,IAAY,EAAW,SACvB,IAAa,EAAa,sBAAsB,GAChD,IAAgB,EAAU,sBAAsB;EAEtD,AAAI,EAAW,OAAO,EAAc,OAClC,EAAU,cAAc,EAAc,OAAO,EAAW,OAAO,IACtD,EAAW,QAAQ,EAAc,UAC1C,EAAU,cAAc,EAAW,QAAQ,EAAc,QAAQ;CAErE,GAAG,CAAC,GAAW,CAAgB,CAAC,GAI1B,IAAkB,QAAkB;EACpC,OAAO,SAAW,OAAe,MAAiB,cACtD,GAAoB,OAAO,aAAa,GAAG;CAC7C,GAAG,CAAC,CAAY,CAAC,GAEX,KAAiB,QAAkB;EACvC,GAAmB,MAAS,CAAC,CAAI;CACnC,GAAG,CAAC,CAAC,GAEC,KAAwB,GAC3B,MAAwC;EACvC,AAAI,EAAE,QAAQ,YAAY,MACxB,EAAE,eAAe,GACjB,EAAkB,EAAK;CAE3B,GACA,CAAC,CAAc,CACjB,GAIM,IAAmB,GAAa,MAAkC;EACtE,AAAI,EAAE,QAAQ,OACZ,EAAY,UAAU,EAAE,QAAQ,EAAE,CAAC;CAEvC,GAAG,CAAC,CAAC,GAEC,IAAkB,GAAa,MAAkC;EACrE,AAAI,EAAE,QAAQ,OACZ,EAAU,UAAU,EAAE,QAAQ,EAAE,CAAC;CAErC,GAAG,CAAC,CAAC,GAEC,IAAiB,QAAkB;EACvC,IAAI,CAAC,EAAY,WAAW,CAAC,EAAU,SAAS;EAEhD,IAAM,IAAO,EAAY,UAAU,EAAU;EAG7C,IAAI,KAAK,IAAI,CAAI,IAAI,IAAW;GAC9B,IAAM,IAAe,EAAK,WAAW,MAAQ,EAAI,OAAO,CAAS,GAC3D,IAAU,EAAK,IAAe,IAC9B,IAAU,EAAK,IAAe;GAEpC,AAAI,IAAO,KAAK,IACd,EAAe,EAAQ,EAAE,IAChB,IAAO,KAAK,KACrB,EAAe,EAAQ,EAAE;EAE7B;EAGA,AADA,EAAY,UAAU,GACtB,EAAU,UAAU;CACtB,GAAG;EAAC;EAAW;EAAM;CAAc,CAAC,GAI9B,KAA4B,GAC/B,MAAqC;EACpC,IAAM,IAAgB,EAAkB;EAExC,QAAQ,EAAE,KAAV;GACE,KAAK;IAIH,AAHA,EAAE,eAAe,GACjB,EAAoB,EAAK,GACzB,EAAoB,EAAE,GACtB,EAAkB,SAAS,MAAM;IACjC;GACF,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,GAAqB,MAAS;KAC5B,IAAM,IAAO,KAAQ,IAAgB,IAAI,IAAI,IAAO;KAEpD,OADA,EAAa,QAAQ,EAAK,EAAE,MAAM,GAC3B;IACT,CAAC;IACD;GACF,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,GAAqB,MAAS;KAC5B,IAAM,IAAO,KAAQ,IAAI,IAAgB,IAAI,IAAO;KAEpD,OADA,EAAa,QAAQ,EAAK,EAAE,MAAM,GAC3B;IACT,CAAC;IACD;GACF,KAAK;IAGH,AAFA,EAAE,eAAe,GACjB,EAAoB,CAAC,GACrB,EAAa,QAAQ,EAAE,EAAE,MAAM;IAC/B;GACF,KAAK,OAGH,AAFA,EAAE,eAAe,GACjB,EAAoB,IAAgB,CAAC,GACrC,EAAa,QAAQ,IAAgB,EAAE,EAAE,MAAM;EAInD;CACF,GACA,CAAC,EAAkB,MAAM,CAC3B,GAEM,KAA8B,GACjC,MAAwC;EACvC,AAAI,EAAE,QAAQ,YAAY,MACxB,EAAE,eAAe,GACjB,EAAoB,EAAK,GACzB,EAAoB,EAAE;CAE1B,GACA,CAAC,CAAgB,CACnB;CAsGA,AAjGA,QAAgB;EACd,IAAI,CAAC,GAAgB;EAErB,IAAM,KAAsB,MAAsB;GAChD,AACE,EAAqB,WACrB,CAAC,EAAqB,QAAQ,SAAS,EAAM,MAAc,KAE3D,EAAkB,EAAK;EAE3B;EAGA,OADA,SAAS,iBAAiB,aAAa,CAAkB,SAC5C;GACX,SAAS,oBAAoB,aAAa,CAAkB;EAC9D;CACF,GAAG,CAAC,CAAc,CAAC,GAGnB,QAAgB;EACd,IAAI,CAAC,GAAkB;EAEvB,IAAM,KAAsB,MAAsB;GAChD,AACE,EAAgB,WAChB,CAAC,EAAgB,QAAQ,SAAS,EAAM,MAAc,MAEtD,EAAoB,EAAK,GACzB,EAAoB,EAAE;EAE1B,GAEM,KAAmB,MAAoC;GAC3D,AAAI,EAAM,QAAQ,aAChB,EAAoB,EAAK,GACzB,EAAoB,EAAE,GACtB,EAAkB,SAAS,MAAM;EAErC;EAIA,OAFA,SAAS,iBAAiB,aAAa,CAAkB,GACzD,SAAS,iBAAiB,WAAW,CAAe,SACvC;GAEX,AADA,SAAS,oBAAoB,aAAa,CAAkB,GAC5D,SAAS,oBAAoB,WAAW,CAAe;EACzD;CACF,GAAG,CAAC,CAAgB,CAAC,GAGrB,QAAgB;EACd,IAAI,KAAoB,EAAa,QAAQ,SAAS,GAAG;GACvD,IAAM,IAAc,EAAkB,WAAW,MAAQ,EAAI,OAAO,CAAS,GACvE,IAAe,KAAe,IAAI,IAAc;GAEtD,AADA,EAAoB,CAAY,GAChC,4BAA4B;IAC1B,EAAa,QAAQ,EAAa,EAAE,MAAM;GAC5C,CAAC;EACH;CACF,GAAG;EAAC;EAAkB;EAAmB;CAAS,CAAC,GAGnD,QAAgB;EACd,IAAI,CAAC,EAAK,MAAM,MAAQ,EAAI,OAAO,CAAS,GAAG;GAC7C,IAAM,IAAW,EAAK;GACtB,AAAI,KACF,EAAe,EAAS,EAAE;EAE9B;CACF,GAAG;EAAC;EAAM;EAAW;CAAc,CAAC,GAGpC,QAAgB;EAEd,AADA,EAAgB,GAChB,EAAY;EAEZ,IAAM,UAAqB;GAEzB,AADA,EAAgB,GAChB,EAAY;EACd;EAGA,OADA,OAAO,iBAAiB,UAAU,CAAY,SACjC;GACX,OAAO,oBAAoB,UAAU,CAAY;EACnD;CACF,GAAG,CAAC,GAAiB,CAAW,CAAC,GAGjC,QAAgB;EACd,EAAkB;CACpB,GAAG,CAAC,GAAW,CAAiB,CAAC,GAGjC,QAAgB;EACd,EAAY;CACd,GAAG,CAAC,GAAM,CAAW,CAAC,GAGtB,cACe;EACX,AAAI,EAAiB,YAAY,QAC/B,aAAa,EAAiB,OAAO;CAEzC,GACC,CAAC,CAAC;CAIL,IAAM,IAAmB,EAAK,MAAM,MAAQ,EAAI,OAAO,CAAS,CAAC,EAAE,SAC7D,KAAiB,EAAK,MAAM,MAAQ,EAAI,OAAO,CAAS,CAAC,EAAE,OAE3D,KAAY,EAAO,SAAS;CAGlC,EAAa,UAAU,CAAC;CAGxB,IAAM,KAAmB,GAAc,MAAwB;EAC7D,IAAM,IAAW,EAAI,OAAO;EAE5B,OACE,kBAAC,UAAD;GAEE,MAAM,MAAO;IACX,GAAU,EAAI,IAAI,CAAE;GACtB;GACA,MAAK;GACL,iBAAe;GACf,iBAAe,IAAc,YAAY,EAAI,OAAO,KAAA;GACpD,IAAI,OAAO,EAAI;GACf,UAAU,IAAW,IAAI;GACzB,WAAW,YAAY,IAAW,uBAAuB;GACzD,eAAe;IACb,EAAe,EAAI,EAAE;GACvB;GACA,YAAY,MAAM;IAKhB,AAJA,EAAc,GAAG,CAAW,IAK1B,EAAE,QAAQ,eACV,EAAE,QAAQ,gBACV,EAAE,QAAQ,UACV,EAAE,QAAQ,UAEV,EAAE,gBAAgB;GAEtB;GA3BF,UAAA;IA6BG,EAAI;IACL,kBAAC,QAAD;KAAM,WAAU;KAAmB,UAAA,EAAI;IAAY,CAAA;IAClD,EAAI;GACC;EA/BD,GAAA,EAAI,EA+BH;CAEZ,GAGM,WACA,KACK,EAAY,KAAK,EAAE,UAAO,MAAM,KAAa,MAClD,kBAAC,OAAD;EAAkC,WAAU;EAA5C,UAAA,CACG,KACC,kBAAA,IAAA,EAAA,UAAA,CACG,IAAa,KACZ,kBAAC,OAAD;GAAK,WAAU;GAAkB,MAAK;GAAY,eAAY;EAAQ,CAAA,GAEvE,KACC,kBAAC,QAAD;GAAM,WAAU;GAAqB,UAAA,EAAM;EAAY,CAAA,CAEzD,EAAA,CAAA,GAEJ,kBAAC,OAAD;GAAK,WAAU;GACZ,UAAA,EAAU,KAAK,MAAQ;IACtB,IAAM,IAAc,EAAK,WAAW,MAAM,EAAE,OAAO,EAAI,EAAE;IACzD,OAAO,EAAgB,GAAK,CAAW;GACzC,CAAC;EACE,CAAA,CACF;CAjBK,GAAA,GAAO,MAAM,SAiBlB,CACN,IAGI,EAAK,KAAK,GAAK,MAAU,EAAgB,GAAK,CAAK,CAAC,GAIvD,WACA,MAAiB,UAAU,CAAC,IAA6B,OAG3D,kBAAC,OAAD;EAAK,WAAU;EAAiB,KAAK;EAArC,UAAA,CACE,kBAAC,UAAD;GACE,KAAK;GACL,MAAK;GACL,WAAU;GACV,eAAe;IACb,EAAoB,CAAC,CAAgB;GACvC;GACA,WAAW;GACX,cAAY;GACZ,iBAAe;GACf,iBAAc;GAEd,UAAA,kBAAC,OAAD;IACE,OAAM;IACN,QAAO;IACP,SAAQ;IACR,MAAK;IACL,QAAO;IACP,aAAY;IACZ,eAAc;IACd,gBAAe;IARjB,UAAA;KAUE,kBAAC,UAAD;MAAQ,IAAG;MAAK,IAAG;MAAK,GAAE;KAAK,CAAA;KAC/B,kBAAC,UAAD;MAAQ,IAAG;MAAK,IAAG;MAAI,GAAE;KAAK,CAAA;KAC9B,kBAAC,UAAD;MAAQ,IAAG;MAAK,IAAG;MAAK,GAAE;KAAK,CAAA;IAC5B;;EACC,CAAA,GAEP,KACC,kBAAC,OAAD;GACE,WAAU;GACV,MAAK;GACL,WAAW;GAEV,UAAA,EAAY,KAAK,EAAE,UAAO,MAAM,QAC/B,kBAAC,OAAD;IAAkC,WAAU;IAA5C,UAAA,CACG,KAAS,KACR,kBAAC,OAAD;KAAK,WAAU;KAA8B,UAAA,EAAM;IAAW,CAAA,GAE/D,EAAU,KAAK,MAAQ;KACtB,IAAM,IAAW,EAAI,OAAO,GACtB,IAAY,EAAkB,WAAW,MAAM,EAAE,OAAO,EAAI,EAAE;KACpE,OACE,kBAAC,UAAD;MAEE,MAAM,MAAO;OACX,AAAI,MACF,EAAa,QAAQ,KAAa;MAEtC;MACA,MAAK;MACL,UAAU,OAAqB,IAAY,IAAI;MAC/C,WAAW,uBAAuB,IAAW,gCAAgC;MAC7E,eAAe;OACb,EAAe,EAAI,EAAE;MACvB;MAZF,UAAA;OAcG,EAAI;OACL,kBAAC,QAAD,EAAA,UAAO,EAAI,MAAY,CAAA;OACtB,EAAI;MACC;KAhBD,GAAA,EAAI,EAgBH;IAEZ,CAAC,CACE;GA5BK,GAAA,GAAO,MAAM,SA4BlB,CACN;EACE,CAAA,CAEJ;KAUH,IAAoB,MAAY,eAAe,MAAY;CA0FjE,OA1EI,MAAiB,cAAc,KAAoB,CAAC,IAEpD,kBAAC,OAAD;EAAK,WAAW,8BAA8B;EAA9C,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;GAA2B,KAAK;GAA/C,UAAA,CACE,kBAAC,UAAD;IACE,WAAU;IACV,SAAS;IACT,WAAW;IACX,iBAAe;IACf,iBAAc;IACd,cAAY,KAAa;IAN3B,UAAA,CAQE,kBAAC,QAAD;KAAM,WAAU;KAAwB,UAAA;IAAqB,CAAA,GAC7D,kBAAC,OAAD;KACE,WAAW,wBAAwB,IAAiB,+BAA+B;KACnF,OAAM;KACN,QAAO;KACP,SAAQ;KACR,MAAK;KACL,OAAM;KACN,eAAY;KAEZ,UAAA,kBAAC,QAAD;MACE,GAAE;MACF,QAAO;MACP,aAAY;MACZ,eAAc;MACd,gBAAe;KAChB,CAAA;IACE,CAAA,CACC;GAEP,CAAA,GAAA,KACC,kBAAC,OAAD;IAAK,WAAU;IAAsB,MAAK;IACvC,UAAA,EAAK,KAAK,MACT,kBAAC,UAAD;KAEE,MAAK;KACL,iBAAe,EAAI,OAAO;KAC1B,WAAW,uBAAuB,EAAI,OAAO,IAAY,gCAAgC;KACzF,eAAe;MAEb,AADA,EAAe,EAAI,EAAE,GACrB,EAAkB,EAAK;KACzB;KAEC,UAAA,EAAI;IACC,GAVD,EAAI,EAUH,CACT;GACE,CAAA,CAEJ;EAEJ,CAAA,GAAA,KACC,kBAAC,OAAD;GACE,IAAI,YAAY;GAChB,MAAK;GACL,mBAAiB,OAAO;GACxB,WAAW,eAAe,IAAiB,KAAK;GAChD,UAAU;GACV,cAAc;GACd,aAAa;GACb,YAAY;GAEX,UAAA;EACE,CAAA,CAEJ;MASP,kBAAC,OAAD;EACE,WAAW,QAJG,MAAiB,SAAS,wBAAwB,GAInC,GA1F/B,MAAY,cACR,4BACA,MAAY,YACV,0BACA,GAsFyC,GApF/C,KAAqB,KAAgB,yBAAyB,GAoFO,GAAG,IACnE,QAAQ,QAAQ,GAAG,CAAC,CACpB,KAAK;EAHV,UAAA,CAKE,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IAEG,KAAwB,MACvB,kBAAC,UAAD;KACE,WAAU;KACV,eAAe;MACb,EAAW,MAAM;KACnB;KACA,cAAY;KACZ,UAAU;KAEV,UAAA,kBAAC,OAAD;MACE,OAAM;MACN,QAAO;MACP,SAAQ;MACR,MAAK;MACL,OAAM;MACN,eAAY;MAEZ,UAAA,kBAAC,QAAD;OACE,GAAE;OACF,QAAO;OACP,aAAY;OACZ,eAAc;OACd,gBAAe;MAChB,CAAA;KACE,CAAA;IACC,CAAA;IAGV,kBAAC,OAAD;KACE,KAAK;KACL,WAAU;KACV,MAAK;KACL,cAAY;KACZ,UAAU;KACV,YAAY,MAAM;MAMhB,IAAM,IAAe,EAAK,WAAW,MAAQ,EAAI,OAAO,CAAS;MAC7D,IAAe,KACnB,EAAc,GAAG,CAAY;KAC/B;KACA,cAAc,MAAiB,aAAa,IAAmB,KAAA;KAC/D,aAAa,MAAiB,aAAa,IAAkB,KAAA;KAC7D,YAAY,MAAiB,aAAa,IAAiB,KAAA;KAE1D,UAAA,GAAqB;IACnB,CAAA;IAGJ,KAAwB,MACvB,kBAAC,UAAD;KACE,WAAU;KACV,eAAe;MACb,EAAW,OAAO;KACpB;KACA,cAAY;KACZ,UAAU;KAEV,UAAA,kBAAC,OAAD;MACE,OAAM;MACN,QAAO;MACP,SAAQ;MACR,MAAK;MACL,OAAM;MACN,eAAY;MAEZ,UAAA,kBAAC,QAAD;OACE,GAAE;OACF,QAAO;OACP,aAAY;OACZ,eAAc;OACd,gBAAe;MAChB,CAAA;KACE,CAAA;IACC,CAAA;IAIT,GAAmB;GACjB;EAEJ,CAAA,GAAA,KACC,kBAAC,OAAD;GACE,IAAI,YAAY;GAChB,MAAK;GACL,mBAAiB,OAAO;GACxB,WAAW,eAAe,IAAiB,KAAK;GAChD,UAAU;GAET,UAAA;EACE,CAAA,CAEJ;;AAET"}
@@ -1,2 +1,2 @@
1
- export { Tabs, TAG_CONFIG } from './Tabs';
2
- export type { TabsProps, TabItem, TabGroupMeta, TabOverflowMode, TabTagType, TabTagLabels, TabVariant, } from './Tabs';
1
+ export { Tabs } from './Tabs';
2
+ export type { TabsProps, TabItem, TabGroupMeta, TabOverflowMode, TabVariant, } from './Tabs';
@@ -1,3 +1,2 @@
1
- import { TAG_CONFIG as e } from "./tabTags.js";
2
- import { Tabs as t } from "./Tabs2.js";
3
- export { e as TAG_CONFIG, t as Tabs };
1
+ import { Tabs as e } from "./Tabs2.js";
2
+ export { e as Tabs };
@@ -12,5 +12,19 @@ export interface TooltipProps {
12
12
  tooltipId?: string;
13
13
  /** Tab index for the wrapper. Use -1 when wrapping already-focusable elements like buttons. Defaults to 0. */
14
14
  tabIndex?: number;
15
+ /**
16
+ * Announce the trigger as a button carrying `aria-expanded`, and let Enter
17
+ * and Space toggle the content.
18
+ *
19
+ * Off by default: a trigger whose content only supplements what is already
20
+ * on screen is not a control and should not claim to be one. Turn it on
21
+ * where the content is something a reader summons deliberately. A glossary
22
+ * term is the case this exists for, and two products had independently built
23
+ * the same trigger contract around one.
24
+ *
25
+ * Do not combine with `tabIndex={-1}`, which is for wrapping an element that
26
+ * is already focusable and already owns its role.
27
+ */
28
+ toggleable?: boolean;
15
29
  }
16
- export declare function Tooltip({ content, children, className, contentClassName, tooltipId, tabIndex, }: TooltipProps): import("react").JSX.Element;
30
+ export declare function Tooltip({ content, children, className, contentClassName, tooltipId, tabIndex, toggleable, }: TooltipProps): import("react").JSX.Element;
@@ -1,68 +1,98 @@
1
1
  /* empty css */
2
- import { useCallback as e, useEffect as t, useId as n, useRef as r, useState as i } from "react";
3
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
4
- import { createPortal as s } from "react-dom";
2
+ import { useCallback as e, useEffect as t, useId as n, useLayoutEffect as r, useRef as i, useState as a } from "react";
3
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
4
+ import { createPortal as c } from "react-dom";
5
5
  //#region src/components/Tooltip/Tooltip.tsx
6
- function c({ content: c, children: l, className: u, contentClassName: d, tooltipId: f, tabIndex: p = 0 }) {
7
- let [m, h] = i(!1), [g, _] = i(null), v = r(null), y = r(null), b = r(!0), x = n(), S = f ?? `tooltip-${x}`;
8
- t(() => (b.current = !0, () => {
9
- b.current = !1;
6
+ var l = 120;
7
+ function u({ content: u, children: d, className: f, contentClassName: p, tooltipId: m, tabIndex: h = 0, toggleable: g = !1 }) {
8
+ let [_, v] = a(!1), [y, b] = a(null), x = i(null), S = i(null), C = i(!0), w = i(null), T = n(), E = m ?? `tooltip-${T}`;
9
+ t(() => (C.current = !0, () => {
10
+ C.current = !1, w.current !== null && clearTimeout(w.current);
10
11
  }), []), t(() => {
11
- let e = y.current;
12
+ let e = S.current;
12
13
  if (!e) return;
13
14
  let t = () => {
14
- b.current && h(!0);
15
+ C.current && v(!0);
15
16
  }, n = () => {
16
- b.current && (h(!1), _(null));
17
+ C.current && (v(!1), b(null));
17
18
  };
18
19
  return e.addEventListener("focusin", t), e.addEventListener("focusout", n), () => {
19
20
  e.removeEventListener("focusin", t), e.removeEventListener("focusout", n);
20
21
  };
21
- }, []), t(() => {
22
- if (!m || !y.current || !b.current) return;
23
- let e = y.current.getBoundingClientRect();
24
- requestAnimationFrame(() => {
25
- if (!b.current || !v.current) return;
26
- let t = v.current.getBoundingClientRect(), n = e.top, r = window.innerHeight - e.bottom, i = t.height, a = n < i + 10 && r > n ? "bottom" : "top", o = e.left + e.width / 2, s = t.width, c = o - s / 2, l = window.innerWidth;
27
- c + s > l - 16 && (c = l - s - 16), c < 16 && (c = 16);
28
- let u;
29
- u = a === "top" ? e.top - i - 8 : e.bottom + 8, _({
30
- top: u,
31
- left: c,
32
- placement: a
33
- });
22
+ }, []);
23
+ let D = e(() => {
24
+ let e = S.current, t = x.current;
25
+ if (!C.current || !e || !t) return;
26
+ let n = e.getBoundingClientRect(), r = t.getBoundingClientRect(), i = n.top, a = window.innerHeight - n.bottom, o = r.height, s = i < o + 10 && a > i ? "bottom" : "top", c = n.left + n.width / 2, l = r.width, u = c - l / 2, d = window.innerWidth;
27
+ u + l > d - 16 && (u = d - l - 16), u < 16 && (u = 16);
28
+ let f = s === "top" ? n.top - o - 8 : n.bottom + 8;
29
+ b({
30
+ top: f,
31
+ left: u,
32
+ placement: s
34
33
  });
35
- }, [m]);
36
- let C = e(() => {
37
- h(!0);
38
- }, []), w = e(() => {
39
- h(!1), _(null);
40
- }, []), T = m && /* @__PURE__ */ a("div", {
41
- ref: v,
42
- id: S,
43
- className: `tooltip__content tooltip__content--${g?.placement ?? "top"} ${d ?? ""}`,
44
- style: g ? {
45
- top: `${String(g.top)}px`,
46
- left: `${String(g.left)}px`
34
+ }, []);
35
+ r(() => {
36
+ if (_) return D(), window.addEventListener("scroll", D, {
37
+ capture: !0,
38
+ passive: !0
39
+ }), window.addEventListener("resize", D), () => {
40
+ window.removeEventListener("scroll", D, { capture: !0 }), window.removeEventListener("resize", D);
41
+ };
42
+ }, [_, D]);
43
+ let O = e(() => {
44
+ w.current !== null && (clearTimeout(w.current), w.current = null), v(!0);
45
+ }, []), k = e(() => {
46
+ w.current !== null && (clearTimeout(w.current), w.current = null), v(!1), b(null);
47
+ }, []), A = e(() => {
48
+ w.current !== null && clearTimeout(w.current), w.current = setTimeout(() => {
49
+ w.current = null, C.current && (v(!1), b(null));
50
+ }, l);
51
+ }, []);
52
+ t(() => {
53
+ if (!_) return;
54
+ let e = (e) => {
55
+ e.key === "Escape" && k();
56
+ };
57
+ return document.addEventListener("keydown", e), () => {
58
+ document.removeEventListener("keydown", e);
59
+ };
60
+ }, [_, k]);
61
+ let j = e((e) => {
62
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), _ ? k() : O());
63
+ }, [
64
+ _,
65
+ k,
66
+ O
67
+ ]), M = _ && /* @__PURE__ */ o("div", {
68
+ ref: x,
69
+ id: E,
70
+ className: `tooltip__content tooltip__content--${y?.placement ?? "top"} ${p ?? ""}`,
71
+ style: y ? {
72
+ top: `${String(y.top)}px`,
73
+ left: `${String(y.left)}px`
47
74
  } : { visibility: "hidden" },
48
75
  role: "tooltip",
49
- children: typeof c == "string" ? /* @__PURE__ */ a("p", {
76
+ children: typeof u == "string" ? /* @__PURE__ */ o("p", {
50
77
  className: "tooltip__text",
51
- children: c
52
- }) : c
78
+ children: u
79
+ }) : u
53
80
  });
54
- return /* @__PURE__ */ o("div", {
55
- ref: y,
56
- className: `tooltip__wrapper ${u ?? ""}`,
57
- onMouseEnter: C,
58
- onMouseLeave: w,
59
- tabIndex: p,
60
- "aria-describedby": m ? S : void 0,
61
- children: [l, T && s(T, document.body)]
81
+ return /* @__PURE__ */ s("div", {
82
+ ref: S,
83
+ className: `tooltip__wrapper ${f ?? ""}`,
84
+ onMouseEnter: O,
85
+ onMouseLeave: A,
86
+ onKeyDown: g ? j : void 0,
87
+ tabIndex: h,
88
+ role: g ? "button" : void 0,
89
+ "aria-expanded": g ? _ : void 0,
90
+ "aria-describedby": _ ? E : void 0,
91
+ children: [d, M && c(M, document.body)]
62
92
  });
63
93
  }
64
94
  //#endregion
65
- export { c as Tooltip };
95
+ export { u as Tooltip };
66
96
 
67
97
  //# sourceMappingURL=Tooltip2.js.map
68
98
  import "../../assets/components/Tooltip/Tooltip.css";
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip2.js","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/**\n * Tooltip Component\n *\n * A reusable tooltip component that displays content on hover.\n * Supports both simple text and complex ReactNode content.\n *\n * Accessibility:\n * - Shows on mouse hover and keyboard focus (WCAG 1.4.13)\n * - Uses role=\"tooltip\" for screen readers\n * - Accessible via keyboard navigation\n */\n\nimport { useState, useRef, useEffect, useCallback, useId, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport \"./Tooltip.css\";\n\nexport interface TooltipProps {\n /** Tooltip content - can be string or ReactNode */\n content: ReactNode;\n /** Children to wrap with tooltip (trigger element) */\n children: ReactNode;\n /** Additional class name for the wrapper */\n className?: string;\n /** Additional class name for the tooltip content */\n contentClassName?: string;\n /** Custom ID for the tooltip (for aria-describedby association) */\n tooltipId?: string;\n /** Tab index for the wrapper. Use -1 when wrapping already-focusable elements like buttons. Defaults to 0. */\n tabIndex?: number;\n}\n\ninterface TooltipPosition {\n top: number;\n left: number;\n placement: \"top\" | \"bottom\";\n}\n\nexport function Tooltip({\n content,\n children,\n className,\n contentClassName,\n tooltipId,\n tabIndex = 0,\n}: TooltipProps) {\n const [isVisible, setIsVisible] = useState(false);\n const [tooltipPosition, setTooltipPosition] = useState<TooltipPosition | null>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const isMountedRef = useRef(true);\n const generatedId = useId();\n const effectiveTooltipId = tooltipId ?? `tooltip-${generatedId}`;\n\n // Track mount state\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n // Handle focus events using focusin/focusout which bubble from child elements\n // This ensures tooltips show when keyboard focus moves to children (WCAG 1.4.13)\n useEffect(() => {\n const wrapper = wrapperRef.current;\n if (!wrapper) return;\n\n // focusin/focusout bubble from child elements, unlike focus/blur\n const handleFocusIn = () => {\n if (isMountedRef.current) {\n setIsVisible(true);\n }\n };\n\n const handleFocusOut = () => {\n if (isMountedRef.current) {\n setIsVisible(false);\n setTooltipPosition(null);\n }\n };\n\n wrapper.addEventListener(\"focusin\", handleFocusIn);\n wrapper.addEventListener(\"focusout\", handleFocusOut);\n\n return () => {\n wrapper.removeEventListener(\"focusin\", handleFocusIn);\n wrapper.removeEventListener(\"focusout\", handleFocusOut);\n };\n }, []);\n\n // Calculate tooltip position based on available space using fixed positioning\n useEffect(() => {\n if (!isVisible || !wrapperRef.current) return;\n if (!isMountedRef.current) return;\n\n const wrapperRect = wrapperRef.current.getBoundingClientRect();\n\n // Use requestAnimationFrame to ensure DOM is updated\n requestAnimationFrame(() => {\n if (!isMountedRef.current || !tooltipRef.current) return;\n\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\n const spaceAbove = wrapperRect.top;\n const spaceBelow = window.innerHeight - wrapperRect.bottom;\n\n const tooltipHeight = tooltipRect.height;\n const margin = 10;\n\n // Determine placement\n const placement: \"top\" | \"bottom\" =\n spaceAbove < tooltipHeight + margin && spaceBelow > spaceAbove\n ? \"bottom\"\n : \"top\";\n\n // Calculate left position (center on wrapper, but ensure it stays within viewport)\n const wrapperCenter = wrapperRect.left + wrapperRect.width / 2;\n const tooltipWidth = tooltipRect.width;\n let left = wrapperCenter - tooltipWidth / 2;\n const viewportWidth = window.innerWidth;\n\n // Keep tooltip within viewport bounds\n if (left + tooltipWidth > viewportWidth - 16) {\n left = viewportWidth - tooltipWidth - 16;\n }\n if (left < 16) {\n left = 16;\n }\n\n // Calculate top position\n let top: number;\n if (placement === \"top\") {\n top = wrapperRect.top - tooltipHeight - 8;\n } else {\n top = wrapperRect.bottom + 8;\n }\n\n setTooltipPosition({ top, left, placement });\n });\n }, [isVisible]);\n\n const handleShow = useCallback(() => {\n setIsVisible(true);\n }, []);\n\n const handleHide = useCallback(() => {\n setIsVisible(false);\n setTooltipPosition(null);\n }, []);\n\n const tooltipElement = isVisible && (\n <div\n ref={tooltipRef}\n id={effectiveTooltipId}\n className={`tooltip__content tooltip__content--${tooltipPosition?.placement ?? \"top\"} ${contentClassName ?? \"\"}`}\n style={\n tooltipPosition\n ? {\n top: `${String(tooltipPosition.top)}px`,\n left: `${String(tooltipPosition.left)}px`,\n }\n : { visibility: \"hidden\" }\n }\n role=\"tooltip\"\n >\n {typeof content === \"string\" ? (\n <p className=\"tooltip__text\">{content}</p>\n ) : (\n content\n )}\n </div>\n );\n\n return (\n <div\n ref={wrapperRef}\n className={`tooltip__wrapper ${className ?? \"\"}`}\n onMouseEnter={handleShow}\n onMouseLeave={handleHide}\n tabIndex={tabIndex}\n aria-describedby={isVisible ? effectiveTooltipId : undefined}\n >\n {children}\n {tooltipElement && createPortal(tooltipElement, document.body)}\n </div>\n );\n}\n"],"mappings":";;;;;AAqCA,SAAgB,EAAQ,EACtB,YACA,aACA,cACA,qBACA,cACA,cAAW,KACI;CACf,IAAM,CAAC,GAAW,KAAgB,EAAS,EAAK,GAC1C,CAAC,GAAiB,KAAsB,EAAiC,IAAI,GAC7E,IAAa,EAAuB,IAAI,GACxC,IAAa,EAAuB,IAAI,GACxC,IAAe,EAAO,EAAI,GAC1B,IAAc,EAAM,GACpB,IAAqB,KAAa,WAAW;CAwCnD,AArCA,SACE,EAAa,UAAU,UACV;EACX,EAAa,UAAU;CACzB,IACC,CAAC,CAAC,GAIL,QAAgB;EACd,IAAM,IAAU,EAAW;EAC3B,IAAI,CAAC,GAAS;EAGd,IAAM,UAAsB;GAC1B,AAAI,EAAa,WACf,EAAa,EAAI;EAErB,GAEM,UAAuB;GAC3B,AAAI,EAAa,YACf,EAAa,EAAK,GAClB,EAAmB,IAAI;EAE3B;EAKA,OAHA,EAAQ,iBAAiB,WAAW,CAAa,GACjD,EAAQ,iBAAiB,YAAY,CAAc,SAEtC;GAEX,AADA,EAAQ,oBAAoB,WAAW,CAAa,GACpD,EAAQ,oBAAoB,YAAY,CAAc;EACxD;CACF,GAAG,CAAC,CAAC,GAGL,QAAgB;EAEd,IADI,CAAC,KAAa,CAAC,EAAW,WAC1B,CAAC,EAAa,SAAS;EAE3B,IAAM,IAAc,EAAW,QAAQ,sBAAsB;EAG7D,4BAA4B;GAC1B,IAAI,CAAC,EAAa,WAAW,CAAC,EAAW,SAAS;GAElD,IAAM,IAAc,EAAW,QAAQ,sBAAsB,GACvD,IAAa,EAAY,KACzB,IAAa,OAAO,cAAc,EAAY,QAE9C,IAAgB,EAAY,QAI5B,IACJ,IAAa,IAAgB,MAAU,IAAa,IAChD,WACA,OAGA,IAAgB,EAAY,OAAO,EAAY,QAAQ,GACvD,IAAe,EAAY,OAC7B,IAAO,IAAgB,IAAe,GACpC,IAAgB,OAAO;GAM7B,AAHI,IAAO,IAAe,IAAgB,OACxC,IAAO,IAAgB,IAAe,KAEpC,IAAO,OACT,IAAO;GAIT,IAAI;GAOJ,AANA,AAGE,IAHE,MAAc,QACV,EAAY,MAAM,IAAgB,IAElC,EAAY,SAAS,GAG7B,EAAmB;IAAE;IAAK;IAAM;GAAU,CAAC;EAC7C,CAAC;CACH,GAAG,CAAC,CAAS,CAAC;CAEd,IAAM,IAAa,QAAkB;EACnC,EAAa,EAAI;CACnB,GAAG,CAAC,CAAC,GAEC,IAAa,QAAkB;EAEnC,AADA,EAAa,EAAK,GAClB,EAAmB,IAAI;CACzB,GAAG,CAAC,CAAC,GAEC,IAAiB,KACrB,kBAAC,OAAD;EACE,KAAK;EACL,IAAI;EACJ,WAAW,sCAAsC,GAAiB,aAAa,MAAM,GAAG,KAAoB;EAC5G,OACE,IACI;GACE,KAAK,GAAG,OAAO,EAAgB,GAAG,EAAE;GACpC,MAAM,GAAG,OAAO,EAAgB,IAAI,EAAE;EACxC,IACA,EAAE,YAAY,SAAS;EAE7B,MAAK;EAEJ,UAAA,OAAO,KAAY,WAClB,kBAAC,KAAD;GAAG,WAAU;GAAiB,UAAA;EAAW,CAAA,IAEzC;CAEC,CAAA;CAGP,OACE,kBAAC,OAAD;EACE,KAAK;EACL,WAAW,oBAAoB,KAAa;EAC5C,cAAc;EACd,cAAc;EACJ;EACV,oBAAkB,IAAY,IAAqB,KAAA;EANrD,UAAA,CAQG,GACA,KAAkB,EAAa,GAAgB,SAAS,IAAI,CAC1D;;AAET"}
1
+ {"version":3,"file":"Tooltip2.js","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/**\n * Tooltip Component\n *\n * A reusable tooltip component that displays content on hover.\n * Supports both simple text and complex ReactNode content.\n *\n * Accessibility, against the three parts of WCAG 2.1 SC 1.4.13:\n * - Hoverable: the pointer can move onto the tooltip itself without it\n * disappearing. The content sits 8px away from the trigger, so the pointer\n * crosses a gap to get there and the hide is deferred over that crossing.\n * - Dismissible: Escape closes it without moving the pointer or focus.\n * - Persistent: it stays while the trigger is hovered or focused.\n *\n * Also uses role=\"tooltip\" and aria-describedby so a screen reader announces\n * the content with the trigger. `toggleable` additionally makes the trigger a\n * button with aria-expanded that Enter and Space operate, for content a reader\n * summons rather than passes over.\n */\n\nimport {\n useState,\n useRef,\n useEffect,\n useLayoutEffect,\n useCallback,\n useId,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport \"./Tooltip.css\";\n\nexport interface TooltipProps {\n /** Tooltip content - can be string or ReactNode */\n content: ReactNode;\n /** Children to wrap with tooltip (trigger element) */\n children: ReactNode;\n /** Additional class name for the wrapper */\n className?: string;\n /** Additional class name for the tooltip content */\n contentClassName?: string;\n /** Custom ID for the tooltip (for aria-describedby association) */\n tooltipId?: string;\n /** Tab index for the wrapper. Use -1 when wrapping already-focusable elements like buttons. Defaults to 0. */\n tabIndex?: number;\n /**\n * Announce the trigger as a button carrying `aria-expanded`, and let Enter\n * and Space toggle the content.\n *\n * Off by default: a trigger whose content only supplements what is already\n * on screen is not a control and should not claim to be one. Turn it on\n * where the content is something a reader summons deliberately. A glossary\n * term is the case this exists for, and two products had independently built\n * the same trigger contract around one.\n *\n * Do not combine with `tabIndex={-1}`, which is for wrapping an element that\n * is already focusable and already owns its role.\n */\n toggleable?: boolean;\n}\n\n/** Long enough to cross the 8px gap to the tooltip, short enough not to linger. */\nconst HIDE_GRACE_MS = 120;\n\ninterface TooltipPosition {\n top: number;\n left: number;\n placement: \"top\" | \"bottom\";\n}\n\nexport function Tooltip({\n content,\n children,\n className,\n contentClassName,\n tooltipId,\n tabIndex = 0,\n toggleable = false,\n}: TooltipProps) {\n const [isVisible, setIsVisible] = useState(false);\n const [tooltipPosition, setTooltipPosition] = useState<TooltipPosition | null>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const isMountedRef = useRef(true);\n const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const generatedId = useId();\n const effectiveTooltipId = tooltipId ?? `tooltip-${generatedId}`;\n\n // Track mount state\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n };\n }, []);\n\n // Handle focus events using focusin/focusout which bubble from child elements\n // This ensures tooltips show when keyboard focus moves to children (WCAG 1.4.13)\n useEffect(() => {\n const wrapper = wrapperRef.current;\n if (!wrapper) return;\n\n // focusin/focusout bubble from child elements, unlike focus/blur\n const handleFocusIn = () => {\n if (isMountedRef.current) {\n setIsVisible(true);\n }\n };\n\n const handleFocusOut = () => {\n if (isMountedRef.current) {\n setIsVisible(false);\n setTooltipPosition(null);\n }\n };\n\n wrapper.addEventListener(\"focusin\", handleFocusIn);\n wrapper.addEventListener(\"focusout\", handleFocusOut);\n\n return () => {\n wrapper.removeEventListener(\"focusin\", handleFocusIn);\n wrapper.removeEventListener(\"focusout\", handleFocusOut);\n };\n }, []);\n\n /**\n * Place the tooltip beside the trigger, in whichever direction has room.\n *\n * Reads both boxes at call time rather than closing over them, because this\n * runs again every time the trigger moves under the viewport.\n */\n const measure = useCallback(() => {\n const wrapper = wrapperRef.current;\n const tooltip = tooltipRef.current;\n if (!isMountedRef.current || !wrapper || !tooltip) return;\n\n const wrapperRect = wrapper.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n const spaceAbove = wrapperRect.top;\n const spaceBelow = window.innerHeight - wrapperRect.bottom;\n\n const tooltipHeight = tooltipRect.height;\n const margin = 10;\n\n // Determine placement\n const placement: \"top\" | \"bottom\" =\n spaceAbove < tooltipHeight + margin && spaceBelow > spaceAbove ? \"bottom\" : \"top\";\n\n // Calculate left position (center on wrapper, but ensure it stays within viewport)\n const wrapperCenter = wrapperRect.left + wrapperRect.width / 2;\n const tooltipWidth = tooltipRect.width;\n let left = wrapperCenter - tooltipWidth / 2;\n const viewportWidth = window.innerWidth;\n\n // Keep tooltip within viewport bounds\n if (left + tooltipWidth > viewportWidth - 16) {\n left = viewportWidth - tooltipWidth - 16;\n }\n if (left < 16) {\n left = 16;\n }\n\n // Calculate top position\n const top =\n placement === \"top\"\n ? wrapperRect.top - tooltipHeight - 8\n : wrapperRect.bottom + 8;\n\n setTooltipPosition({ top, left, placement });\n }, []);\n\n /**\n * The content is `position: fixed` and lives in a portal, so nothing moves it\n * when the trigger moves. Any scroll between the two, on the page or on a\n * container in between, leaves it pointing at where the trigger used to be,\n * and a resize can leave it off the edge. Scroll events from a container do\n * not bubble, hence the capture phase.\n *\n * A layout effect, not an effect plus a frame. The content is in the DOM by\n * the time this runs and can be measured, and the state it sets is flushed\n * before the browser paints, so the tooltip is never painted unplaced. The\n * frame it used to wait for was a frame the tooltip spent mounted and\n * `visibility: hidden`, which is out of the accessibility tree: present but\n * unreadable, and unfindable by any query that respects that tree.\n */\n useLayoutEffect(() => {\n if (!isVisible) return;\n\n measure();\n window.addEventListener(\"scroll\", measure, { capture: true, passive: true });\n window.addEventListener(\"resize\", measure);\n\n return () => {\n window.removeEventListener(\"scroll\", measure, { capture: true });\n window.removeEventListener(\"resize\", measure);\n };\n }, [isVisible, measure]);\n\n const handleShow = useCallback(() => {\n if (hideTimerRef.current !== null) {\n clearTimeout(hideTimerRef.current);\n hideTimerRef.current = null;\n }\n setIsVisible(true);\n }, []);\n\n const hideNow = useCallback(() => {\n if (hideTimerRef.current !== null) {\n clearTimeout(hideTimerRef.current);\n hideTimerRef.current = null;\n }\n setIsVisible(false);\n setTooltipPosition(null);\n }, []);\n\n /**\n * Deferred so the pointer can cross the 8px gap between the trigger and the\n * tooltip. React propagates enter and leave through the portal, so a direct\n * hop onto the content never fires this; the gap does, because the pointer\n * passes over a node that belongs to neither element. Hiding on that would\n * put the content out of reach (WCAG 2.1 SC 1.4.13, Hoverable). Arriving on\n * either element cancels the pending hide.\n */\n const handleHide = useCallback(() => {\n if (hideTimerRef.current !== null) clearTimeout(hideTimerRef.current);\n hideTimerRef.current = setTimeout(() => {\n hideTimerRef.current = null;\n if (!isMountedRef.current) return;\n setIsVisible(false);\n setTooltipPosition(null);\n }, HIDE_GRACE_MS);\n }, []);\n\n // Dismissible: Escape closes without moving the pointer or focus. Bound to\n // the document because focus may sit on a child of the trigger, or nowhere\n // at all when the tooltip was opened by hover.\n useEffect(() => {\n if (!isVisible) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") hideNow();\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [isVisible, hideNow]);\n\n /**\n * Enter and Space toggle, for a trigger that is a control rather than\n * incidental hover help. Both default to something else, Space scrolling the\n * page and Enter submitting an enclosing form, so both are prevented.\n */\n const handleKeyDown = useCallback(\n (e: ReactKeyboardEvent<HTMLDivElement>) => {\n if (e.key !== \"Enter\" && e.key !== \" \") return;\n e.preventDefault();\n if (isVisible) {\n hideNow();\n } else {\n handleShow();\n }\n },\n [isVisible, hideNow, handleShow],\n );\n\n const tooltipElement = isVisible && (\n <div\n ref={tooltipRef}\n id={effectiveTooltipId}\n className={`tooltip__content tooltip__content--${tooltipPosition?.placement ?? \"top\"} ${contentClassName ?? \"\"}`}\n style={\n tooltipPosition\n ? {\n top: `${String(tooltipPosition.top)}px`,\n left: `${String(tooltipPosition.left)}px`,\n }\n : { visibility: \"hidden\" }\n }\n role=\"tooltip\"\n >\n {typeof content === \"string\" ? (\n <p className=\"tooltip__text\">{content}</p>\n ) : (\n content\n )}\n </div>\n );\n\n return (\n <div\n ref={wrapperRef}\n className={`tooltip__wrapper ${className ?? \"\"}`}\n onMouseEnter={handleShow}\n onMouseLeave={handleHide}\n onKeyDown={toggleable ? handleKeyDown : undefined}\n tabIndex={tabIndex}\n role={toggleable ? \"button\" : undefined}\n aria-expanded={toggleable ? isVisible : undefined}\n aria-describedby={isVisible ? effectiveTooltipId : undefined}\n >\n {children}\n {tooltipElement && createPortal(tooltipElement, document.body)}\n </div>\n );\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -24,6 +24,7 @@ export * from './components/PageLayout';
24
24
  export * from './components/ProgressBar';
25
25
  export * from './components/Skeleton';
26
26
  export * from './components/SmoothHeight';
27
+ export * from './components/Select';
27
28
  export * from './components/StatCard';
28
29
  export * from './components/StatusTag';
29
30
  export * from './components/Tabs';
package/dist/index.js CHANGED
@@ -14,11 +14,11 @@ import { SkeletonText as f } from "./components/Skeleton/SkeletonText2.js";
14
14
  import { SkeletonChart as p } from "./components/Skeleton/SkeletonChart2.js";
15
15
  import { SkeletonRow as m } from "./components/Skeleton/SkeletonRow2.js";
16
16
  import { SmoothHeight as h } from "./components/SmoothHeight/SmoothHeight2.js";
17
- import { usePrefersReducedMotion as g } from "./hooks/usePrefersReducedMotion.js";
18
- import { StatCard as _ } from "./components/StatCard/StatCard2.js";
19
- import { formatCompactNumber as v } from "./components/StatCard/formatters.js";
20
- import { StatusTag as y } from "./components/StatusTag/StatusTag2.js";
21
- import { TAG_CONFIG as b } from "./components/Tabs/tabTags.js";
17
+ import { Select as g } from "./components/Select/Select2.js";
18
+ import { usePrefersReducedMotion as _ } from "./hooks/usePrefersReducedMotion.js";
19
+ import { StatCard as v } from "./components/StatCard/StatCard2.js";
20
+ import { formatCompactNumber as y } from "./components/StatCard/formatters.js";
21
+ import { StatusTag as b } from "./components/StatusTag/StatusTag2.js";
22
22
  import { Tabs as x } from "./components/Tabs/Tabs2.js";
23
23
  import { Tooltip as S } from "./components/Tooltip/Tooltip2.js";
24
- export { t as Badge, e as BaseCard, n as Button, r as DataTable, i as Drawer, a as EmptyState, o as ErrorState, s as PageHeader, c as PageLayout, l as ProgressBar, u as Skeleton, d as SkeletonCard, p as SkeletonChart, m as SkeletonRow, f as SkeletonText, h as SmoothHeight, _ as StatCard, y as StatusTag, b as TAG_CONFIG, x as Tabs, S as Tooltip, v as formatCompactNumber, g as usePrefersReducedMotion };
24
+ export { t as Badge, e as BaseCard, n as Button, r as DataTable, i as Drawer, a as EmptyState, o as ErrorState, s as PageHeader, c as PageLayout, l as ProgressBar, g as Select, u as Skeleton, d as SkeletonCard, p as SkeletonChart, m as SkeletonRow, f as SkeletonText, h as SmoothHeight, v as StatCard, b as StatusTag, x as Tabs, S as Tooltip, y as formatCompactNumber, _ as usePrefersReducedMotion };