se-design 1.0.83-dev.2 → 1.0.83-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index29.js CHANGED
@@ -1,5 +1,5 @@
1
- import r, { useState as $, useMemo as j, useRef as F, useEffect as B } from "react";
2
- import { Icon as L } from "./index6.js";
1
+ import r, { useState as $, useMemo as j, useRef as L, useEffect as B } from "react";
2
+ import { Icon as O } from "./index6.js";
3
3
  import { Popover as z } from "./index19.js";
4
4
  import { MenuList as H } from "./index18.js";
5
5
  import { useStableId as U } from "./index205.js";
@@ -21,19 +21,19 @@ const re = ({
21
21
  onTabChange: m,
22
22
  primaryTabCount: u,
23
23
  headingLevel: g,
24
- id: O,
25
- ariaLabel: A = "Tabs",
24
+ id: A,
25
+ ariaLabel: F = "Tabs",
26
26
  ariaLabelledBy: M,
27
27
  panelId: p
28
28
  }) => {
29
- const K = i || (o[0] ? o[0].id : ""), [b, h] = $(K), [w, y] = $(!1), T = u ?? o?.length, f = o.slice(0, T), c = o.slice(T), s = c.find((e) => e.id === b), E = o.some((e) => e.renderTabContent), k = U(O, "tabs"), R = j(() => {
30
- const e = f.filter((l) => !l.disabled), t = c.filter((l) => !l.disabled), n = e.map((l) => l.id);
29
+ const K = i || (o[0] ? o[0].id : ""), [b, h] = $(K), [w, y] = $(!1), T = u ?? o?.length, f = o.slice(0, T), c = o.slice(T), l = c.find((e) => e.id === b), E = o.some((e) => e.renderTabContent), k = U(A, "tabs"), R = j(() => {
30
+ const e = f.filter((s) => !s.disabled), t = c.filter((s) => !s.disabled), n = e.map((s) => s.id);
31
31
  return t.length > 0 ? [...n, "overflow"] : n;
32
32
  }, [f, c]), {
33
- getTabProps: I,
33
+ getTabProps: x,
34
34
  getPanelProps: S,
35
35
  getTabListProps: _,
36
- handleKeyDown: x,
36
+ handleKeyDown: I,
37
37
  setFocusedTabId: C
38
38
  } = V({
39
39
  itemIds: R,
@@ -42,11 +42,11 @@ const re = ({
42
42
  idBase: k,
43
43
  includePanelLinks: E || !!p,
44
44
  externalPanelId: p
45
- }), P = F(null), v = F(!1);
45
+ }), N = L(null), v = L(!1);
46
46
  B(() => {
47
47
  h(i || "");
48
48
  }, [i]);
49
- const N = (e) => {
49
+ const P = (e) => {
50
50
  h(e?.id), m?.(e?.id);
51
51
  }, D = (e, t) => e.id === t;
52
52
  if (g && o.length === 1) {
@@ -63,19 +63,19 @@ const re = ({
63
63
  className: "se-design-tabs flex items-stretch relative border rounded-md border-[var(--color-gray-400)] w-fit",
64
64
  "data-automation-id": "tabs-container"
65
65
  }, _({
66
- ariaLabel: A,
66
+ ariaLabel: F,
67
67
  ariaLabelledBy: M
68
68
  })), f.map((e) => {
69
- const t = b === e.id, n = !!e.disabled, l = I(e.id);
69
+ const t = b === e.id, n = !!e.disabled, s = x(e.id);
70
70
  return /* @__PURE__ */ r.createElement("button", d({
71
71
  type: "button",
72
72
  key: e.id
73
- }, l, {
73
+ }, s, {
74
74
  disabled: n,
75
75
  "aria-label": e.ariaLabel,
76
76
  onFocus: () => !n && C(e.id),
77
- onKeyDown: x,
78
- onClick: () => N(e),
77
+ onKeyDown: I,
78
+ onClick: () => P(e),
79
79
  className: `se-design-tab-item flex items-center text-base px-3 py-0.5 min-w-0 border-l first:border-l-0 first:rounded-l-[0.3rem] last:rounded-r-[0.3rem] border-[var(--color-gray-400)] ${n ? "" : "focus-outline"} ${n ? "text-[var(--color-gray-400)] cursor-not-allowed" : t ? "se-design-tab-item-active text-[var(--color-white)] bg-[var(--color-blue-500)] font-medium hover:cursor-pointer" : "text-[var(--color-gray-700)] font-normal hover:bg-[var(--color-gray-50)] hover:text-[var(--color-gray-900)] cursor-pointer transition-all duration-200 ease-in-out"}`,
80
80
  "data-automation-id": `tab-item-${e.automationId || e.id || ""}`
81
81
  }), e.label);
@@ -85,20 +85,20 @@ const re = ({
85
85
  noBorder: !0,
86
86
  disableClickToggle: !0,
87
87
  onPopoverToggle: (e) => {
88
- e || (v.current && P.current?.focus(), v.current = !1);
88
+ e || (v.current && N.current?.focus(), v.current = !1);
89
89
  },
90
90
  renderPopoverSrcElement: ({
91
91
  displayPopover: e,
92
92
  togglePopover: t
93
93
  }) => {
94
- const n = I("overflow"), l = s ? q(s.id, k) : void 0;
94
+ const n = x("overflow"), s = l ? q(l.id, k) : void 0;
95
95
  return /* @__PURE__ */ r.createElement("div", d({}, n, {
96
96
  ref: (a) => {
97
- P.current = a, n.ref(a);
97
+ N.current = a, n.ref(a);
98
98
  },
99
- "aria-selected": !!s,
100
- "aria-label": s?.label ? void 0 : `More tabs (${c.length})`,
101
- "aria-controls": l,
99
+ "aria-selected": !!l,
100
+ "aria-label": l?.label ? void 0 : `More tabs (${c.length})`,
101
+ "aria-controls": s,
102
102
  "aria-haspopup": "true",
103
103
  "aria-expanded": e,
104
104
  onClick: () => {
@@ -107,21 +107,25 @@ const re = ({
107
107
  onFocus: () => C("overflow"),
108
108
  "data-automation-id": "tabs-kebab-menu",
109
109
  onKeyDown: (a) => {
110
- a.key === "Enter" || a.key === " " || a.key === "ArrowDown" ? (a.preventDefault(), v.current = !0, t(!0)) : a.key === "ArrowUp" ? (a.preventDefault(), v.current = !0, t("last")) : x(a);
110
+ a.key === "Enter" || a.key === " " || a.key === "ArrowDown" ? (a.preventDefault(), v.current = !0, t(!0)) : a.key === "ArrowUp" ? (a.preventDefault(), v.current = !0, t("last")) : I(a);
111
111
  },
112
- className: `overflow-tabs-src-element focus-outline border-l px-3 py-0.5 rounded-r-md font-medium hover:cursor-pointer transition-all duration-200 ease-in-out ${e ? "bg-[var(--color-blue-100)]" : ""} ${s?.label ? "bg-[var(--color-blue-500)]" : "hover:bg-[var(--color-gray-50)]"}`,
112
+ className: `overflow-tabs-src-element focus-outline border-l px-3 py-0.5 rounded-r-md font-medium hover:cursor-pointer transition-all duration-200 ease-in-out ${e ? "bg-[var(--color-blue-100)]" : ""} ${l?.label ? "bg-[var(--color-blue-500)]" : "hover:bg-[var(--color-gray-50)]"}`,
113
113
  onMouseEnter: () => y(!0),
114
114
  onMouseLeave: () => y(!1)
115
- }), s?.label ? /* @__PURE__ */ r.createElement(r.Fragment, null, /* @__PURE__ */ r.createElement("span", {
115
+ }), l?.label ? /* @__PURE__ */ r.createElement("span", {
116
+ className: "pointer-events-none flex items-center gap-1"
117
+ }, /* @__PURE__ */ r.createElement("span", {
116
118
  className: "text-[var(--color-white)]"
117
- }, s?.label), /* @__PURE__ */ r.createElement(L, {
119
+ }, l?.label), /* @__PURE__ */ r.createElement(O, {
118
120
  name: "chevron",
119
121
  rotation: e ? "180" : "0",
120
122
  stroke: w ? "var(--color-gray-100)" : "var(--color-white)"
121
- })) : /* @__PURE__ */ r.createElement(L, {
123
+ })) : /* @__PURE__ */ r.createElement("span", {
124
+ className: "pointer-events-none"
125
+ }, /* @__PURE__ */ r.createElement(O, {
122
126
  name: "kebab-menu",
123
127
  fill: w ? "var(--color-gray-900)" : "var(--color-gray-700)"
124
- }));
128
+ })));
125
129
  },
126
130
  renderPopoverContents: ({
127
131
  closePopoverCb: e
@@ -131,7 +135,7 @@ const re = ({
131
135
  label: t?.label,
132
136
  automationId: t?.automationId || t?.id,
133
137
  onClick: () => {
134
- N(t), e();
138
+ P(t), e();
135
139
  }
136
140
  }))
137
141
  })
@@ -1 +1 @@
1
- {"version":3,"file":"index29.js","sources":["../src/components/Tabs/index.tsx"],"sourcesContent":["import React, { FC, ReactNode, useState, useEffect, useMemo, useRef } from 'react';\nimport { Icon } from '../Icon';\nimport { Popover } from '../Popover';\nimport { MenuList } from '../MenuList';\nimport { useStableId } from '../../utils/useStableId';\nimport { useTabsA11y, getPanelId } from '../../utils/a11y';\n\nimport './style.scss';\n\ninterface TabProps {\n label: string;\n id: string;\n disabled?: boolean;\n renderTabContent?: (tab: TabProps) => ReactNode;\n automationId?: string;\n ariaLabel?: string;\n}\ninterface TabsProps {\n defaultActiveTab?: string;\n tabs: TabProps[];\n onTabChange?: (tabId: string) => void;\n primaryTabCount?: number; // prop for controlling primary tabs\n headingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'span'; // heading tag to render single tab as a heading element when only one tab is present\n automationId?: string;\n id?: string; // custom ID for baseId (passed to useStableId)\n ariaLabel?: string; // Accessible name when no visible label exists\n ariaLabelledBy?: string; // ID of element that labels this tablist (preferred over ariaLabel)\n panelId?: string; // When consumer manages a single external panel, pass its id here\n}\n\nexport const Tabs: FC<TabsProps> = ({\n defaultActiveTab,\n tabs,\n onTabChange,\n primaryTabCount,\n headingLevel: HeadingTag,\n id,\n ariaLabel = 'Tabs',\n ariaLabelledBy,\n panelId\n}) => {\n const defaultTabId = defaultActiveTab || (tabs[0] ? tabs[0].id : '');\n const [activeTab, setActiveTab] = useState(defaultTabId);\n const [isIconTabHover, setIsIconTabHover] = useState(false);\n\n const visibleTabCount = primaryTabCount ?? tabs?.length;\n const visibleTabs = tabs.slice(0, visibleTabCount);\n const overflowTabs = tabs.slice(visibleTabCount);\n const activeOverflowTab = overflowTabs.find((tab) => tab.id === activeTab);\n\n // Only render internal panels if at least one tab provides content via renderTabContent\n const hasPanelContent = tabs.some(t => t.renderTabContent);\n\n // A11y setup\n const baseId = useStableId(id, 'tabs');\n const itemIds = useMemo(() => {\n const enabledVisibleTabs = visibleTabs.filter(t => !t.disabled);\n const enabledOverflowTabs = overflowTabs.filter(t => !t.disabled);\n const visibleIds = enabledVisibleTabs.map(t => t.id);\n return enabledOverflowTabs.length > 0 ? [...visibleIds, 'overflow'] : visibleIds;\n }, [visibleTabs, overflowTabs]);\n\n const { getTabProps, getPanelProps, getTabListProps, handleKeyDown, setFocusedTabId } = useTabsA11y({\n itemIds,\n activeItem: activeTab,\n orientation: 'horizontal',\n idBase: baseId,\n includePanelLinks: hasPanelContent || !!panelId,\n externalPanelId: panelId\n });\n\n // Ref for the overflow button div — needed to return focus after menu closes\n const overflowDivRef = useRef<HTMLDivElement>(null);\n // Track whether the overflow popover was opened via keyboard — only return focus\n // programmatically in that case (mouse-opened popovers should not trigger :focus-visible on close)\n const wasKeyboardOpenedRef = useRef(false);\n\n useEffect(() => {\n setActiveTab(defaultActiveTab || '');\n }, [defaultActiveTab]);\n\n const handleTabClick = (tab: TabProps) => {\n setActiveTab(tab?.id);\n onTabChange?.(tab?.id);\n };\n\n const isTabActive = (tab: TabProps, activeTab: string) => {\n return tab.id === activeTab;\n };\n\n if (HeadingTag && tabs.length === 1) {\n const singleTab = tabs[0];\n return (\n <>\n <HeadingTag className=\"se-design-tabs se-design-tabs-heading\">\n {singleTab.label}\n </HeadingTag>\n <div className=\"se-design-tabs-content\">\n <div className=\"tab-content block\">\n {singleTab?.renderTabContent && singleTab?.renderTabContent(singleTab)}\n </div>\n </div>\n </>\n );\n }\n\n return (\n <>\n <div\n className=\"se-design-tabs flex items-stretch relative border rounded-md border-[var(--color-gray-400)] w-fit\"\n data-automation-id=\"tabs-container\"\n {...getTabListProps({ ariaLabel, ariaLabelledBy })}\n >\n {visibleTabs.map((tab) => {\n const isActive = activeTab === tab.id;\n const isDisabled = !!tab.disabled;\n const tabProps = getTabProps(tab.id);\n return (\n <button\n type=\"button\"\n key={tab.id}\n {...tabProps}\n disabled={isDisabled}\n aria-label={tab.ariaLabel}\n onFocus={() => !isDisabled && setFocusedTabId(tab.id)}\n onKeyDown={handleKeyDown}\n onClick={() => handleTabClick(tab)}\n className={`se-design-tab-item flex items-center text-base px-3 py-0.5 min-w-0 border-l first:border-l-0 first:rounded-l-[0.3rem] last:rounded-r-[0.3rem] border-[var(--color-gray-400)] ${!isDisabled ? 'focus-outline' : ''} ${\n isDisabled\n ? 'text-[var(--color-gray-400)] cursor-not-allowed'\n : isActive\n ? 'se-design-tab-item-active text-[var(--color-white)] bg-[var(--color-blue-500)] font-medium hover:cursor-pointer'\n : 'text-[var(--color-gray-700)] font-normal hover:bg-[var(--color-gray-50)] hover:text-[var(--color-gray-900)] cursor-pointer transition-all duration-200 ease-in-out'\n }`}\n data-automation-id={`tab-item-${tab.automationId || tab.id || ''}`}\n >\n {tab.label}\n </button>\n );\n })}\n\n {overflowTabs.length > 0 && (\n <Popover\n className=\"se-design-overflow-tabs\"\n position=\"bottom-left\"\n noBorder\n disableClickToggle={true}\n onPopoverToggle={(isOpen) => {\n if (!isOpen) {\n // Only return focus programmatically when the popover was opened via keyboard.\n // Mouse-opened popovers must not call .focus() — programmatic focus after a\n // pointer interaction triggers :focus-visible intermittently across browsers.\n if (wasKeyboardOpenedRef.current) overflowDivRef.current?.focus();\n wasKeyboardOpenedRef.current = false;\n }\n }}\n renderPopoverSrcElement={({ displayPopover, togglePopover }) => {\n const overflowTabProps = getTabProps('overflow');\n const overflowAriaControls = activeOverflowTab\n ? getPanelId(activeOverflowTab.id, baseId)\n : undefined;\n\n return (\n <div\n {...overflowTabProps}\n ref={(el) => { overflowDivRef.current = el; overflowTabProps.ref(el); }}\n aria-selected={!!activeOverflowTab}\n aria-label={activeOverflowTab?.label ? undefined : `More tabs (${overflowTabs.length})`}\n aria-controls={overflowAriaControls}\n aria-haspopup=\"true\"\n aria-expanded={displayPopover}\n onClick={() => { wasKeyboardOpenedRef.current = false; togglePopover(true); }}\n onFocus={() => setFocusedTabId('overflow')}\n data-automation-id=\"tabs-kebab-menu\"\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {\n e.preventDefault();\n wasKeyboardOpenedRef.current = true;\n togglePopover(true);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n wasKeyboardOpenedRef.current = true;\n togglePopover('last');\n } else {\n handleKeyDown(e);\n }\n }}\n className={`overflow-tabs-src-element focus-outline border-l px-3 py-0.5 rounded-r-md font-medium hover:cursor-pointer transition-all duration-200 ease-in-out ${\n displayPopover ? 'bg-[var(--color-blue-100)]' : ''\n } ${activeOverflowTab?.label ? 'bg-[var(--color-blue-500)]' : 'hover:bg-[var(--color-gray-50)]'}`}\n onMouseEnter={() => setIsIconTabHover(true)}\n onMouseLeave={() => setIsIconTabHover(false)}\n >\n {activeOverflowTab?.label ? (\n <>\n <span className=\"text-[var(--color-white)]\">{activeOverflowTab?.label}</span>\n <Icon\n name=\"chevron\"\n rotation={displayPopover ? '180' : '0'}\n stroke={isIconTabHover ? 'var(--color-gray-100)' : 'var(--color-white)'}\n />\n </>\n ) : (\n <Icon name=\"kebab-menu\" fill={isIconTabHover ? 'var(--color-gray-900)' : 'var(--color-gray-700)'} />\n )}\n </div>\n );\n }}\n renderPopoverContents={({ closePopoverCb }) => (\n <MenuList\n items={overflowTabs.map((tab) => ({\n id: tab?.id,\n label: tab?.label,\n automationId: tab?.automationId || tab?.id,\n onClick: () => {\n handleTabClick(tab);\n closePopoverCb();\n }\n }))}\n />\n )}\n />\n )}\n </div>\n\n {hasPanelContent && (\n <div className=\"se-design-tabs-content\">\n {tabs.map((tab) => (\n <div\n key={tab.id}\n {...getPanelProps(tab.id)}\n className={`tab-content ${isTabActive(tab, activeTab) ? 'block' : 'hidden'}`}\n >\n {isTabActive(tab, activeTab) && tab.renderTabContent?.(tab)}\n </div>\n ))}\n </div>\n )}\n </>\n );\n};\n"],"names":["Tabs","defaultActiveTab","tabs","onTabChange","primaryTabCount","headingLevel","HeadingTag","id","ariaLabel","ariaLabelledBy","panelId","defaultTabId","activeTab","setActiveTab","useState","isIconTabHover","setIsIconTabHover","visibleTabCount","length","visibleTabs","slice","overflowTabs","activeOverflowTab","find","tab","hasPanelContent","some","t","renderTabContent","baseId","useStableId","itemIds","useMemo","enabledVisibleTabs","filter","disabled","enabledOverflowTabs","visibleIds","map","getTabProps","getPanelProps","getTabListProps","handleKeyDown","setFocusedTabId","useTabsA11y","activeItem","orientation","idBase","includePanelLinks","externalPanelId","overflowDivRef","useRef","wasKeyboardOpenedRef","useEffect","handleTabClick","isTabActive","singleTab","createElement","React","Fragment","className","label","_extends","isActive","isDisabled","tabProps","type","key","onFocus","onKeyDown","onClick","automationId","Popover","position","noBorder","disableClickToggle","onPopoverToggle","isOpen","current","focus","renderPopoverSrcElement","displayPopover","togglePopover","overflowTabProps","overflowAriaControls","getPanelId","undefined","ref","el","e","preventDefault","onMouseEnter","onMouseLeave","Icon","name","rotation","stroke","fill","renderPopoverContents","closePopoverCb","MenuList","items"],"mappings":"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"}
1
+ {"version":3,"file":"index29.js","sources":["../src/components/Tabs/index.tsx"],"sourcesContent":["import React, { FC, ReactNode, useState, useEffect, useMemo, useRef } from 'react';\nimport { Icon } from '../Icon';\nimport { Popover } from '../Popover';\nimport { MenuList } from '../MenuList';\nimport { useStableId } from '../../utils/useStableId';\nimport { useTabsA11y, getPanelId } from '../../utils/a11y';\n\nimport './style.scss';\n\ninterface TabProps {\n label: string;\n id: string;\n disabled?: boolean;\n renderTabContent?: (tab: TabProps) => ReactNode;\n automationId?: string;\n ariaLabel?: string;\n}\ninterface TabsProps {\n defaultActiveTab?: string;\n tabs: TabProps[];\n onTabChange?: (tabId: string) => void;\n primaryTabCount?: number; // prop for controlling primary tabs\n headingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'span'; // heading tag to render single tab as a heading element when only one tab is present\n automationId?: string;\n id?: string; // custom ID for baseId (passed to useStableId)\n ariaLabel?: string; // Accessible name when no visible label exists\n ariaLabelledBy?: string; // ID of element that labels this tablist (preferred over ariaLabel)\n panelId?: string; // When consumer manages a single external panel, pass its id here\n}\n\nexport const Tabs: FC<TabsProps> = ({\n defaultActiveTab,\n tabs,\n onTabChange,\n primaryTabCount,\n headingLevel: HeadingTag,\n id,\n ariaLabel = 'Tabs',\n ariaLabelledBy,\n panelId\n}) => {\n const defaultTabId = defaultActiveTab || (tabs[0] ? tabs[0].id : '');\n const [activeTab, setActiveTab] = useState(defaultTabId);\n const [isIconTabHover, setIsIconTabHover] = useState(false);\n\n const visibleTabCount = primaryTabCount ?? tabs?.length;\n const visibleTabs = tabs.slice(0, visibleTabCount);\n const overflowTabs = tabs.slice(visibleTabCount);\n const activeOverflowTab = overflowTabs.find((tab) => tab.id === activeTab);\n\n // Only render internal panels if at least one tab provides content via renderTabContent\n const hasPanelContent = tabs.some(t => t.renderTabContent);\n\n // A11y setup\n const baseId = useStableId(id, 'tabs');\n const itemIds = useMemo(() => {\n const enabledVisibleTabs = visibleTabs.filter(t => !t.disabled);\n const enabledOverflowTabs = overflowTabs.filter(t => !t.disabled);\n const visibleIds = enabledVisibleTabs.map(t => t.id);\n return enabledOverflowTabs.length > 0 ? [...visibleIds, 'overflow'] : visibleIds;\n }, [visibleTabs, overflowTabs]);\n\n const { getTabProps, getPanelProps, getTabListProps, handleKeyDown, setFocusedTabId } = useTabsA11y({\n itemIds,\n activeItem: activeTab,\n orientation: 'horizontal',\n idBase: baseId,\n includePanelLinks: hasPanelContent || !!panelId,\n externalPanelId: panelId\n });\n\n // Ref for the overflow button div — needed to return focus after menu closes\n const overflowDivRef = useRef<HTMLDivElement>(null);\n // Track whether the overflow popover was opened via keyboard — only return focus\n // programmatically in that case (mouse-opened popovers should not trigger :focus-visible on close)\n const wasKeyboardOpenedRef = useRef(false);\n\n useEffect(() => {\n setActiveTab(defaultActiveTab || '');\n }, [defaultActiveTab]);\n\n const handleTabClick = (tab: TabProps) => {\n setActiveTab(tab?.id);\n onTabChange?.(tab?.id);\n };\n\n const isTabActive = (tab: TabProps, activeTab: string) => {\n return tab.id === activeTab;\n };\n\n if (HeadingTag && tabs.length === 1) {\n const singleTab = tabs[0];\n return (\n <>\n <HeadingTag className=\"se-design-tabs se-design-tabs-heading\">\n {singleTab.label}\n </HeadingTag>\n <div className=\"se-design-tabs-content\">\n <div className=\"tab-content block\">\n {singleTab?.renderTabContent && singleTab?.renderTabContent(singleTab)}\n </div>\n </div>\n </>\n );\n }\n\n return (\n <>\n <div\n className=\"se-design-tabs flex items-stretch relative border rounded-md border-[var(--color-gray-400)] w-fit\"\n data-automation-id=\"tabs-container\"\n {...getTabListProps({ ariaLabel, ariaLabelledBy })}\n >\n {visibleTabs.map((tab) => {\n const isActive = activeTab === tab.id;\n const isDisabled = !!tab.disabled;\n const tabProps = getTabProps(tab.id);\n return (\n <button\n type=\"button\"\n key={tab.id}\n {...tabProps}\n disabled={isDisabled}\n aria-label={tab.ariaLabel}\n onFocus={() => !isDisabled && setFocusedTabId(tab.id)}\n onKeyDown={handleKeyDown}\n onClick={() => handleTabClick(tab)}\n className={`se-design-tab-item flex items-center text-base px-3 py-0.5 min-w-0 border-l first:border-l-0 first:rounded-l-[0.3rem] last:rounded-r-[0.3rem] border-[var(--color-gray-400)] ${!isDisabled ? 'focus-outline' : ''} ${\n isDisabled\n ? 'text-[var(--color-gray-400)] cursor-not-allowed'\n : isActive\n ? 'se-design-tab-item-active text-[var(--color-white)] bg-[var(--color-blue-500)] font-medium hover:cursor-pointer'\n : 'text-[var(--color-gray-700)] font-normal hover:bg-[var(--color-gray-50)] hover:text-[var(--color-gray-900)] cursor-pointer transition-all duration-200 ease-in-out'\n }`}\n data-automation-id={`tab-item-${tab.automationId || tab.id || ''}`}\n >\n {tab.label}\n </button>\n );\n })}\n\n {overflowTabs.length > 0 && (\n <Popover\n className=\"se-design-overflow-tabs\"\n position=\"bottom-left\"\n noBorder\n disableClickToggle={true}\n onPopoverToggle={(isOpen) => {\n if (!isOpen) {\n if (wasKeyboardOpenedRef.current) overflowDivRef.current?.focus();\n wasKeyboardOpenedRef.current = false;\n }\n }}\n renderPopoverSrcElement={({ displayPopover, togglePopover }) => {\n const overflowTabProps = getTabProps('overflow');\n const overflowAriaControls = activeOverflowTab\n ? getPanelId(activeOverflowTab.id, baseId)\n : undefined;\n\n return (\n <div\n {...overflowTabProps}\n ref={(el) => { overflowDivRef.current = el; overflowTabProps.ref(el); }}\n aria-selected={!!activeOverflowTab}\n aria-label={activeOverflowTab?.label ? undefined : `More tabs (${overflowTabs.length})`}\n aria-controls={overflowAriaControls}\n aria-haspopup=\"true\"\n aria-expanded={displayPopover}\n onClick={() => { wasKeyboardOpenedRef.current = false; togglePopover(true); }}\n onFocus={() => setFocusedTabId('overflow')}\n data-automation-id=\"tabs-kebab-menu\"\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {\n e.preventDefault();\n wasKeyboardOpenedRef.current = true;\n togglePopover(true);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n wasKeyboardOpenedRef.current = true;\n togglePopover('last');\n } else {\n handleKeyDown(e);\n }\n }}\n className={`overflow-tabs-src-element focus-outline border-l px-3 py-0.5 rounded-r-md font-medium hover:cursor-pointer transition-all duration-200 ease-in-out ${\n displayPopover ? 'bg-[var(--color-blue-100)]' : ''\n } ${activeOverflowTab?.label ? 'bg-[var(--color-blue-500)]' : 'hover:bg-[var(--color-gray-50)]'}`}\n onMouseEnter={() => setIsIconTabHover(true)}\n onMouseLeave={() => setIsIconTabHover(false)}\n >\n {activeOverflowTab?.label ? (\n <span className=\"pointer-events-none flex items-center gap-1\">\n <span className=\"text-[var(--color-white)]\">{activeOverflowTab?.label}</span>\n <Icon\n name=\"chevron\"\n rotation={displayPopover ? '180' : '0'}\n stroke={isIconTabHover ? 'var(--color-gray-100)' : 'var(--color-white)'}\n />\n </span>\n ) : (\n <span className=\"pointer-events-none\">\n <Icon name=\"kebab-menu\" fill={isIconTabHover ? 'var(--color-gray-900)' : 'var(--color-gray-700)'} />\n </span>\n )}\n </div>\n );\n }}\n renderPopoverContents={({ closePopoverCb }) => (\n <MenuList\n items={overflowTabs.map((tab) => ({\n id: tab?.id,\n label: tab?.label,\n automationId: tab?.automationId || tab?.id,\n onClick: () => {\n handleTabClick(tab);\n closePopoverCb();\n }\n }))}\n />\n )}\n />\n )}\n </div>\n\n {hasPanelContent && (\n <div className=\"se-design-tabs-content\">\n {tabs.map((tab) => (\n <div\n key={tab.id}\n {...getPanelProps(tab.id)}\n className={`tab-content ${isTabActive(tab, activeTab) ? 'block' : 'hidden'}`}\n >\n {isTabActive(tab, activeTab) && tab.renderTabContent?.(tab)}\n </div>\n ))}\n </div>\n )}\n </>\n );\n};\n"],"names":["Tabs","defaultActiveTab","tabs","onTabChange","primaryTabCount","headingLevel","HeadingTag","id","ariaLabel","ariaLabelledBy","panelId","defaultTabId","activeTab","setActiveTab","useState","isIconTabHover","setIsIconTabHover","visibleTabCount","length","visibleTabs","slice","overflowTabs","activeOverflowTab","find","tab","hasPanelContent","some","t","renderTabContent","baseId","useStableId","itemIds","useMemo","enabledVisibleTabs","filter","disabled","enabledOverflowTabs","visibleIds","map","getTabProps","getPanelProps","getTabListProps","handleKeyDown","setFocusedTabId","useTabsA11y","activeItem","orientation","idBase","includePanelLinks","externalPanelId","overflowDivRef","useRef","wasKeyboardOpenedRef","useEffect","handleTabClick","isTabActive","singleTab","createElement","React","Fragment","className","label","_extends","isActive","isDisabled","tabProps","type","key","onFocus","onKeyDown","onClick","automationId","Popover","position","noBorder","disableClickToggle","onPopoverToggle","isOpen","current","focus","renderPopoverSrcElement","displayPopover","togglePopover","overflowTabProps","overflowAriaControls","getPanelId","undefined","ref","el","e","preventDefault","onMouseEnter","onMouseLeave","Icon","name","rotation","stroke","fill","renderPopoverContents","closePopoverCb","MenuList","items"],"mappings":"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"}
package/dist/index70.js CHANGED
@@ -13,20 +13,20 @@ function M({
13
13
  const [c, b] = n.useState(u || r[0] || ""), g = n.useRef({});
14
14
  n.useEffect(() => {
15
15
  if (r.length > 0 && !r.includes(c)) {
16
- const e = u || r[0] || "";
17
- b(e);
16
+ const t = u || r[0] || "";
17
+ b(t);
18
18
  }
19
19
  }, [r, c, u]);
20
- const x = n.useCallback((e) => {
21
- b(e), f?.(e);
22
- }, [f]), y = n.useCallback((e) => {
23
- const t = g.current[e];
24
- t ? t.focus() : (b(e), f?.(e));
25
- }, [f]), p = n.useCallback((e, t = 1) => {
20
+ const x = n.useCallback((t) => {
21
+ b((e) => e === t ? e : (f?.(t), t));
22
+ }, [f]), y = n.useCallback((t) => {
23
+ const e = g.current[t];
24
+ e ? e.focus() : (b(t), f?.(t));
25
+ }, [f]), p = n.useCallback((t, e = 1) => {
26
26
  if (r.length === 0) return;
27
27
  const o = r.indexOf(c), l = Math.max(0, o);
28
28
  let s = l;
29
- switch (e) {
29
+ switch (t) {
30
30
  case "first":
31
31
  s = 0;
32
32
  break;
@@ -34,26 +34,26 @@ function M({
34
34
  s = r.length - 1;
35
35
  break;
36
36
  case "prev":
37
- k ? s = (l - t + r.length) % r.length : s = Math.max(0, l - t);
37
+ k ? s = (l - e + r.length) % r.length : s = Math.max(0, l - e);
38
38
  break;
39
39
  case "next":
40
- k ? s = (l + t) % r.length : s = Math.min(r.length - 1, l + t);
40
+ k ? s = (l + e) % r.length : s = Math.min(r.length - 1, l + e);
41
41
  break;
42
42
  }
43
43
  const w = r[s];
44
44
  w && g.current[w]?.focus();
45
- }, [r, c, k]), A = n.useCallback((e) => ({
46
- ref: (t) => {
47
- g.current[e] = t;
45
+ }, [r, c, k]), A = n.useCallback((t) => ({
46
+ ref: (e) => {
47
+ g.current[t] = e;
48
48
  },
49
- tabIndex: c === e ? v : -1,
50
- onFocus: () => x(e)
51
- }), [c, v, x]), R = n.useCallback((e) => {
52
- let t = null, o = 1;
53
- e.key === "Home" ? t = "first" : e.key === "End" ? t = "last" : (a !== "vertical" && (e.key === "ArrowLeft" ? t = "prev" : e.key === "ArrowRight" && (t = "next")), !t && a !== "horizontal" && (e.key === "ArrowUp" ? t = "prev" : e.key === "ArrowDown" && (t = "next"), t && i && (o = i))), t && (e.preventDefault(), e.stopPropagation(), p(t, o));
54
- }, [a, i, p]), z = n.useCallback((e) => {
55
- const t = {};
56
- return h && (t.role = h), a === "horizontal" ? t["aria-orientation"] = "horizontal" : a === "vertical" && (t["aria-orientation"] = "vertical"), e && Object.assign(t, C(e)), t;
49
+ tabIndex: c === t ? v : -1,
50
+ onFocus: () => x(t)
51
+ }), [c, v, x]), R = n.useCallback((t) => {
52
+ let e = null, o = 1;
53
+ t.key === "Home" ? e = "first" : t.key === "End" ? e = "last" : (a !== "vertical" && (t.key === "ArrowLeft" ? e = "prev" : t.key === "ArrowRight" && (e = "next")), !e && a !== "horizontal" && (t.key === "ArrowUp" ? e = "prev" : t.key === "ArrowDown" && (e = "next"), e && i && (o = i))), e && (t.preventDefault(), t.stopPropagation(), p(e, o));
54
+ }, [a, i, p]), z = n.useCallback((t) => {
55
+ const e = {};
56
+ return h && (e.role = h), a === "horizontal" ? e["aria-orientation"] = "horizontal" : a === "vertical" && (e["aria-orientation"] = "vertical"), t && Object.assign(e, C(t)), e;
57
57
  }, [h, a]);
58
58
  return {
59
59
  setFocusedId: x,
@@ -1 +1 @@
1
- {"version":3,"file":"index70.js","sources":["../src/utils/a11y/useRovingFocus.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { getA11yNameAttributes, AccessibleNameInput } from './accessibleName';\nexport type { AccessibleNameInput };\n\nexport type RovingDirection = 'prev' | 'next' | 'first' | 'last';\n\nexport interface UseRovingFocusOptions {\n /**\n * Array of item IDs in order\n */\n itemIds: string[];\n\n /**\n * The tabIndex to apply to the currently focused item in the roving group.\n * Defaults to 0 \n */\n tabIndex?: number;\n\n /**\n * Initial focused item ID. Defaults to first item.\n */\n defaultFocusedId?: string;\n\n /**\n * Orientation for arrow key mapping.\n * - horizontal: ArrowLeft/ArrowRight\n * - vertical: ArrowUp/ArrowDown\n * - grid: all four arrow keys (Left/Right move by 1, Up/Down move by `cols`)\n */\n orientation?: 'horizontal' | 'vertical' | 'grid';\n\n /**\n * Number of columns in the grid. Required when orientation is 'grid'.\n * ArrowUp/ArrowDown navigate by this many items in the flat itemIds array.\n */\n cols?: number;\n\n /**\n * Whether navigation wraps around at ends. Defaults to true.\n */\n loop?: boolean;\n\n /**\n * Callback when focus changes\n */\n onFocusChange?: (id: string) => void;\n\n /**\n * ARIA role for the container element (e.g. 'grid', 'menu', 'tablist', 'toolbar').\n * Returned via getContainerProps().\n */\n role?: React.AriaRole;\n}\n\nexport interface RovingItemProps {\n ref: (el: HTMLElement | null) => void;\n tabIndex: number;\n onFocus: () => void;\n}\n\nexport interface RovingContainerProps {\n role?: React.AriaRole;\n 'aria-orientation'?: 'horizontal' | 'vertical';\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n}\n\nexport interface UseRovingFocusReturn {\n /**\n * Set focused item ID manually\n */\n setFocusedId: (id: string) => void;\n\n /**\n * Imperatively focus a DOM element by id.\n * If the element exists in refs, focus it immediately.\n * If not yet in DOM, update state so it gets focus once rendered.\n */\n focusItem: (id: string) => void;\n\n /**\n * Keyboard handler for arrow/Home/End navigation.\n * Attach to each item's onKeyDown.\n */\n handleKeyDown: (e: React.KeyboardEvent) => void;\n\n /**\n * Get props for an item in the roving focus group (ref + tabIndex)\n */\n getRovingItemProps: (id: string) => RovingItemProps;\n\n /**\n * Props to spread on the container element.\n * Returns role (if provided), aria-orientation (derived from orientation; omitted for grid),\n * and any accessible name/description attributes passed in.\n */\n getContainerProps: (nameInput?: AccessibleNameInput) => RovingContainerProps;\n}\n\n/**\n * Hook for managing roving focus pattern (roving tabindex).\n * Reusable for composite widgets: tabs, toolbars, menus, listboxes, radio groups, grids.\n *\n * @example\n * // 1D (toolbar/tabs):\n * const { getRovingItemProps, handleKeyDown } = useRovingFocus({\n * itemIds: ['tab1', 'tab2', 'tab3'],\n * orientation: 'horizontal'\n * });\n *\n * // 2D (grid — flat itemIds, cols for row-jump math):\n * const { getRovingItemProps, handleKeyDown } = useRovingFocus({\n * itemIds: ['r0c0', 'r0c1', 'r0c2', 'r1c0', 'r1c1', 'r1c2'],\n * orientation: 'grid',\n * cols: 3\n * });\n *\n * // In render:\n * <button {...getRovingItemProps('tab1')} onKeyDown={handleKeyDown}>Tab 1</button>\n */\nexport function useRovingFocus({\n itemIds,\n tabIndex = 0,\n defaultFocusedId,\n orientation = 'horizontal',\n cols,\n loop = true,\n onFocusChange,\n role\n}: UseRovingFocusOptions): UseRovingFocusReturn {\n const [focusedId, setFocusedIdState] = React.useState<string>(\n defaultFocusedId || itemIds[0] || ''\n );\n\n const itemRefs = React.useRef<Record<string, HTMLElement | null>>({});\n\n // Sync focusedId if itemIds change and current focusedId is no longer valid\n React.useEffect(() => {\n if (itemIds.length > 0 && !itemIds.includes(focusedId)) {\n const newFocusedId = defaultFocusedId || itemIds[0] || '';\n setFocusedIdState(newFocusedId);\n }\n }, [itemIds, focusedId, defaultFocusedId]);\n\n const setFocusedId = React.useCallback(\n (id: string) => {\n setFocusedIdState(id);\n onFocusChange?.(id);\n },\n [onFocusChange]\n );\n\n const focusItem = React.useCallback(\n (id: string) => {\n const el = itemRefs.current[id];\n if (el) {\n el.focus();\n } else {\n // element not yet in DOM (e.g. month just changed) — update state so it gets focus once rendered\n setFocusedIdState(id);\n onFocusChange?.(id);\n }\n },\n [onFocusChange]\n );\n\n const moveFocus = React.useCallback(\n (direction: RovingDirection, step: number = 1) => {\n if (itemIds.length === 0) return;\n\n const currentIdx = itemIds.indexOf(focusedId);\n const safeIdx = Math.max(0, currentIdx);\n let nextIdx = safeIdx;\n\n switch (direction) {\n case 'first':\n nextIdx = 0;\n break;\n case 'last':\n nextIdx = itemIds.length - 1;\n break;\n case 'prev':\n if (loop) {\n nextIdx = (safeIdx - step + itemIds.length) % itemIds.length;\n } else {\n nextIdx = Math.max(0, safeIdx - step);\n }\n break;\n case 'next':\n if (loop) {\n nextIdx = (safeIdx + step) % itemIds.length;\n } else {\n nextIdx = Math.min(itemIds.length - 1, safeIdx + step);\n }\n break;\n }\n\n const nextId = itemIds[nextIdx];\n if (!nextId) return;\n\n itemRefs.current[nextId]?.focus();\n },\n [itemIds, focusedId, loop]\n );\n\n const getRovingItemProps = React.useCallback(\n (id: string): RovingItemProps => ({\n ref: (el: HTMLElement | null) => {\n itemRefs.current[id] = el;\n },\n tabIndex: focusedId === id ? tabIndex : -1,\n onFocus: () => setFocusedId(id)\n }),\n [focusedId, tabIndex, setFocusedId]\n );\n\n // Keyboard handler for arrow/Home/End navigation\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent) => {\n let direction: RovingDirection | null = null;\n let step = 1;\n\n if (e.key === 'Home') {\n direction = 'first';\n } else if (e.key === 'End') {\n direction = 'last';\n } else {\n // Horizontal axis (Left/Right) — active for 'horizontal' and 'grid'\n if (orientation !== 'vertical') {\n if (e.key === 'ArrowLeft') direction = 'prev';\n else if (e.key === 'ArrowRight') direction = 'next';\n }\n // Vertical axis (Up/Down) — active for 'vertical' and 'grid'\n if (!direction && orientation !== 'horizontal') {\n if (e.key === 'ArrowUp') direction = 'prev';\n else if (e.key === 'ArrowDown') direction = 'next';\n if (direction && cols) step = cols;\n }\n }\n\n if (direction) {\n e.preventDefault();\n e.stopPropagation();\n moveFocus(direction, step);\n }\n },\n [orientation, cols, moveFocus]\n );\n\n const getContainerProps = React.useCallback((nameInput?: AccessibleNameInput): RovingContainerProps => {\n const props: RovingContainerProps = {};\n if (role) props.role = role;\n if (orientation === 'horizontal') props['aria-orientation'] = 'horizontal';\n else if (orientation === 'vertical') props['aria-orientation'] = 'vertical';\n // grid: aria-orientation omitted — not applicable for role=\"grid\"\n if (nameInput) Object.assign(props, getA11yNameAttributes(nameInput));\n return props;\n }, [role, orientation]);\n\n return {\n setFocusedId,\n focusItem,\n handleKeyDown,\n getRovingItemProps,\n getContainerProps\n };\n}\n"],"names":["React","getA11yNameAttributes","useRovingFocus","itemIds","tabIndex","defaultFocusedId","orientation","cols","loop","onFocusChange","role","focusedId","setFocusedIdState","useState","itemRefs","useRef","useEffect","length","includes","newFocusedId","setFocusedId","useCallback","id","focusItem","el","current","focus","moveFocus","direction","step","currentIdx","indexOf","safeIdx","Math","max","nextIdx","min","nextId","getRovingItemProps","ref","onFocus","handleKeyDown","e","key","preventDefault","stopPropagation","getContainerProps","nameInput","props","Object","assign"],"mappings":"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"}
1
+ {"version":3,"file":"index70.js","sources":["../src/utils/a11y/useRovingFocus.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { getA11yNameAttributes, AccessibleNameInput } from './accessibleName';\nexport type { AccessibleNameInput };\n\nexport type RovingDirection = 'prev' | 'next' | 'first' | 'last';\n\nexport interface UseRovingFocusOptions {\n /**\n * Array of item IDs in order\n */\n itemIds: string[];\n\n /**\n * The tabIndex to apply to the currently focused item in the roving group.\n * Defaults to 0 \n */\n tabIndex?: number;\n\n /**\n * Initial focused item ID. Defaults to first item.\n */\n defaultFocusedId?: string;\n\n /**\n * Orientation for arrow key mapping.\n * - horizontal: ArrowLeft/ArrowRight\n * - vertical: ArrowUp/ArrowDown\n * - grid: all four arrow keys (Left/Right move by 1, Up/Down move by `cols`)\n */\n orientation?: 'horizontal' | 'vertical' | 'grid';\n\n /**\n * Number of columns in the grid. Required when orientation is 'grid'.\n * ArrowUp/ArrowDown navigate by this many items in the flat itemIds array.\n */\n cols?: number;\n\n /**\n * Whether navigation wraps around at ends. Defaults to true.\n */\n loop?: boolean;\n\n /**\n * Callback when focus changes\n */\n onFocusChange?: (id: string) => void;\n\n /**\n * ARIA role for the container element (e.g. 'grid', 'menu', 'tablist', 'toolbar').\n * Returned via getContainerProps().\n */\n role?: React.AriaRole;\n}\n\nexport interface RovingItemProps {\n ref: (el: HTMLElement | null) => void;\n tabIndex: number;\n onFocus: () => void;\n}\n\nexport interface RovingContainerProps {\n role?: React.AriaRole;\n 'aria-orientation'?: 'horizontal' | 'vertical';\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n}\n\nexport interface UseRovingFocusReturn {\n /**\n * Set focused item ID manually\n */\n setFocusedId: (id: string) => void;\n\n /**\n * Imperatively focus a DOM element by id.\n * If the element exists in refs, focus it immediately.\n * If not yet in DOM, update state so it gets focus once rendered.\n */\n focusItem: (id: string) => void;\n\n /**\n * Keyboard handler for arrow/Home/End navigation.\n * Attach to each item's onKeyDown.\n */\n handleKeyDown: (e: React.KeyboardEvent) => void;\n\n /**\n * Get props for an item in the roving focus group (ref + tabIndex)\n */\n getRovingItemProps: (id: string) => RovingItemProps;\n\n /**\n * Props to spread on the container element.\n * Returns role (if provided), aria-orientation (derived from orientation; omitted for grid),\n * and any accessible name/description attributes passed in.\n */\n getContainerProps: (nameInput?: AccessibleNameInput) => RovingContainerProps;\n}\n\n/**\n * Hook for managing roving focus pattern (roving tabindex).\n * Reusable for composite widgets: tabs, toolbars, menus, listboxes, radio groups, grids.\n *\n * @example\n * // 1D (toolbar/tabs):\n * const { getRovingItemProps, handleKeyDown } = useRovingFocus({\n * itemIds: ['tab1', 'tab2', 'tab3'],\n * orientation: 'horizontal'\n * });\n *\n * // 2D (grid — flat itemIds, cols for row-jump math):\n * const { getRovingItemProps, handleKeyDown } = useRovingFocus({\n * itemIds: ['r0c0', 'r0c1', 'r0c2', 'r1c0', 'r1c1', 'r1c2'],\n * orientation: 'grid',\n * cols: 3\n * });\n *\n * // In render:\n * <button {...getRovingItemProps('tab1')} onKeyDown={handleKeyDown}>Tab 1</button>\n */\nexport function useRovingFocus({\n itemIds,\n tabIndex = 0,\n defaultFocusedId,\n orientation = 'horizontal',\n cols,\n loop = true,\n onFocusChange,\n role\n}: UseRovingFocusOptions): UseRovingFocusReturn {\n const [focusedId, setFocusedIdState] = React.useState<string>(\n defaultFocusedId || itemIds[0] || ''\n );\n\n const itemRefs = React.useRef<Record<string, HTMLElement | null>>({});\n\n // Sync focusedId if itemIds change and current focusedId is no longer valid\n React.useEffect(() => {\n if (itemIds.length > 0 && !itemIds.includes(focusedId)) {\n const newFocusedId = defaultFocusedId || itemIds[0] || '';\n setFocusedIdState(newFocusedId);\n }\n }, [itemIds, focusedId, defaultFocusedId]);\n\n const setFocusedId = React.useCallback(\n (id: string) => {\n setFocusedIdState((prev) => {\n if (prev === id) return prev;\n onFocusChange?.(id);\n return id;\n });\n },\n [onFocusChange]\n );\n\n const focusItem = React.useCallback(\n (id: string) => {\n const el = itemRefs.current[id];\n if (el) {\n el.focus();\n } else {\n // element not yet in DOM (e.g. month just changed) — update state so it gets focus once rendered\n setFocusedIdState(id);\n onFocusChange?.(id);\n }\n },\n [onFocusChange]\n );\n\n const moveFocus = React.useCallback(\n (direction: RovingDirection, step: number = 1) => {\n if (itemIds.length === 0) return;\n\n const currentIdx = itemIds.indexOf(focusedId);\n const safeIdx = Math.max(0, currentIdx);\n let nextIdx = safeIdx;\n\n switch (direction) {\n case 'first':\n nextIdx = 0;\n break;\n case 'last':\n nextIdx = itemIds.length - 1;\n break;\n case 'prev':\n if (loop) {\n nextIdx = (safeIdx - step + itemIds.length) % itemIds.length;\n } else {\n nextIdx = Math.max(0, safeIdx - step);\n }\n break;\n case 'next':\n if (loop) {\n nextIdx = (safeIdx + step) % itemIds.length;\n } else {\n nextIdx = Math.min(itemIds.length - 1, safeIdx + step);\n }\n break;\n }\n\n const nextId = itemIds[nextIdx];\n if (!nextId) return;\n\n itemRefs.current[nextId]?.focus();\n },\n [itemIds, focusedId, loop]\n );\n\n const getRovingItemProps = React.useCallback(\n (id: string): RovingItemProps => ({\n ref: (el: HTMLElement | null) => {\n itemRefs.current[id] = el;\n },\n tabIndex: focusedId === id ? tabIndex : -1,\n onFocus: () => setFocusedId(id)\n }),\n [focusedId, tabIndex, setFocusedId]\n );\n\n // Keyboard handler for arrow/Home/End navigation\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent) => {\n let direction: RovingDirection | null = null;\n let step = 1;\n\n if (e.key === 'Home') {\n direction = 'first';\n } else if (e.key === 'End') {\n direction = 'last';\n } else {\n // Horizontal axis (Left/Right) — active for 'horizontal' and 'grid'\n if (orientation !== 'vertical') {\n if (e.key === 'ArrowLeft') direction = 'prev';\n else if (e.key === 'ArrowRight') direction = 'next';\n }\n // Vertical axis (Up/Down) — active for 'vertical' and 'grid'\n if (!direction && orientation !== 'horizontal') {\n if (e.key === 'ArrowUp') direction = 'prev';\n else if (e.key === 'ArrowDown') direction = 'next';\n if (direction && cols) step = cols;\n }\n }\n\n if (direction) {\n e.preventDefault();\n e.stopPropagation();\n moveFocus(direction, step);\n }\n },\n [orientation, cols, moveFocus]\n );\n\n const getContainerProps = React.useCallback((nameInput?: AccessibleNameInput): RovingContainerProps => {\n const props: RovingContainerProps = {};\n if (role) props.role = role;\n if (orientation === 'horizontal') props['aria-orientation'] = 'horizontal';\n else if (orientation === 'vertical') props['aria-orientation'] = 'vertical';\n // grid: aria-orientation omitted — not applicable for role=\"grid\"\n if (nameInput) Object.assign(props, getA11yNameAttributes(nameInput));\n return props;\n }, [role, orientation]);\n\n return {\n setFocusedId,\n focusItem,\n handleKeyDown,\n getRovingItemProps,\n getContainerProps\n };\n}\n"],"names":["React","getA11yNameAttributes","useRovingFocus","itemIds","tabIndex","defaultFocusedId","orientation","cols","loop","onFocusChange","role","focusedId","setFocusedIdState","useState","itemRefs","useRef","useEffect","length","includes","newFocusedId","setFocusedId","useCallback","id","prev","focusItem","el","current","focus","moveFocus","direction","step","currentIdx","indexOf","safeIdx","Math","max","nextIdx","min","nextId","getRovingItemProps","ref","onFocus","handleKeyDown","e","key","preventDefault","stopPropagation","getContainerProps","nameInput","props","Object","assign"],"mappings":"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"}
package/dist/index79.js CHANGED
@@ -1,59 +1,56 @@
1
- import { useState as g, useEffect as s, useCallback as d } from "react";
2
- import { useScrollActiveIntoView as I } from "./index247.js";
1
+ import { useState as w, useEffect as x, useCallback as k } from "react";
2
+ import { useScrollActiveIntoView as v } from "./index247.js";
3
3
  function y({
4
4
  items: u,
5
5
  isOpen: t,
6
- onSelect: n,
6
+ onSelect: l,
7
7
  onClose: i,
8
- onOpen: c,
9
- loop: b = !0,
10
- disabled: D = !1,
11
- listboxRef: h,
12
- optionSelector: v = '[role="option"]',
13
- keepHighlightOnSelect: l = !1,
14
- closeOnTab: k = !0
8
+ onOpen: n,
9
+ loop: c = !0,
10
+ disabled: b = !1,
11
+ listboxRef: D,
12
+ optionSelector: d = '[role="option"]',
13
+ keepHighlightOnSelect: h = !1,
14
+ closeOnTab: g = !0
15
15
  }) {
16
- const [a, e] = g(-1);
17
- s(() => {
18
- t || e(-1);
19
- }, [t]), I({
20
- containerRef: h,
21
- activeIndex: a,
22
- itemSelector: v,
16
+ const [f, a] = w(-1);
17
+ x(() => {
18
+ t || a(-1);
19
+ }, [t]), v({
20
+ containerRef: D,
21
+ activeIndex: f,
22
+ itemSelector: d,
23
23
  enabled: t
24
24
  });
25
- const w = d((r) => {
26
- if (D) {
25
+ const m = k((r) => {
26
+ if (b) {
27
27
  r.key === "Escape" && t && (r.preventDefault(), i());
28
28
  return;
29
29
  }
30
- const f = u.length;
30
+ const e = u.length;
31
31
  switch (r.key) {
32
32
  case "ArrowDown":
33
- r.preventDefault(), r.stopPropagation(), !t && f > 0 ? (c?.(), e(0)) : t && f > 0 && e((o) => b ? (o + 1) % f : Math.min(o + 1, f - 1));
33
+ r.preventDefault(), r.stopPropagation(), !t && e > 0 ? (n?.(), a(0)) : t && e > 0 && a((o) => c ? (o + 1) % e : Math.min(o + 1, e - 1));
34
34
  break;
35
35
  case "ArrowUp":
36
- r.preventDefault(), r.stopPropagation(), t && f > 0 && e((o) => o === -1 ? f - 1 : b ? (o - 1 + f) % f : Math.max(o - 1, 0));
36
+ r.preventDefault(), r.stopPropagation(), t && e > 0 && a((o) => o === -1 ? e - 1 : c ? (o - 1 + e) % e : Math.max(o - 1, 0));
37
37
  break;
38
38
  case "Enter":
39
- r.preventDefault(), t ? a >= 0 && u[a] && (n(u[a], a), l || e(-1)) : (c?.(), e(0));
40
- break;
41
- case " ":
42
- r.preventDefault(), t ? a >= 0 && u[a] && (n(u[a], a), l || e(-1)) : (c?.(), e(0));
39
+ r.preventDefault(), t ? f >= 0 && u[f] && (l(u[f], f), h || a(-1)) : (n?.(), a(0));
43
40
  break;
44
41
  case "Escape":
45
- t && (r.preventDefault(), i(), e(-1));
42
+ t && (r.preventDefault(), i(), a(-1));
46
43
  break;
47
44
  case "Tab":
48
- t && k && (i(), e(-1));
45
+ t && g && (i(), a(-1));
49
46
  break;
50
47
  }
51
- }, [D, u, t, a, n, i, c, b, l, k]), x = d((r, f) => `${r}-option-${f}`, []);
48
+ }, [b, u, t, f, l, i, n, c, h, g]), s = k((r, e) => `${r}-option-${e}`, []);
52
49
  return {
53
- highlightedIndex: a,
54
- setHighlightedIndex: e,
55
- handleKeyDown: w,
56
- getOptionId: x
50
+ highlightedIndex: f,
51
+ setHighlightedIndex: a,
52
+ handleKeyDown: m,
53
+ getOptionId: s
57
54
  };
58
55
  }
59
56
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"index79.js","sources":["../src/utils/a11y/useComboboxNavigation.ts"],"sourcesContent":["import { useCallback, useState, useEffect } from 'react';\nimport type { RefObject } from 'react';\nimport { useScrollActiveIntoView } from './useScrollActiveIntoView';\n\nexport interface UseComboboxNavigationOptions<T = any> {\n /**\n * Array of items to navigate through\n */\n items: T[];\n \n /**\n * Whether the dropdown is currently open\n */\n isOpen: boolean;\n \n /**\n * Callback when an item is selected (Enter key)\n */\n onSelect: (item: T, index: number) => void;\n \n /**\n * Callback to close the dropdown\n */\n onClose: () => void;\n \n /**\n * Optional: Callback to open the dropdown\n */\n onOpen?: () => void;\n \n /**\n * Whether to wrap around at the ends of the list.\n * Default: true\n */\n loop?: boolean;\n \n /**\n * Whether keyboard navigation is disabled\n * (e.g., for custom rendered content)\n */\n disabled?: boolean;\n\n /**\n * Whether to keep the highlighted index after selecting an item.\n * Useful for multi-select where the dropdown stays open after selection.\n * Default: false\n */\n keepHighlightOnSelect?: boolean;\n\n /**\n * Whether Tab should close the dropdown.\n * Set to false when Tab should move focus to elements within the popup (e.g. CTAs).\n * Default: true\n */\n closeOnTab?: boolean;\n\n /**\n * Ref to the listbox container for scroll management\n */\n listboxRef?: RefObject<HTMLElement | null>;\n \n /**\n * CSS selector for option elements (default: '[role=\"option\"]')\n */\n optionSelector?: string;\n}\n\nexport interface UseComboboxNavigationReturn {\n /**\n * Currently highlighted index (-1 if none)\n */\n highlightedIndex: number;\n \n /**\n * Set the highlighted index manually\n */\n setHighlightedIndex: (index: number | ((prev: number) => number)) => void;\n \n /**\n * Keyboard event handler for the combobox input\n */\n handleKeyDown: (e: React.KeyboardEvent) => void;\n \n /**\n * Generate stable ID for an option\n */\n getOptionId: (listboxId: string, index: number) => string;\n \n}\n\n/**\n * Hook for managing combobox keyboard navigation with aria-activedescendant.\n * \n * Implements WAI-ARIA 1.2 Combobox pattern:\n * - Arrow Up/Down to navigate options\n * - Enter to select highlighted option\n * - Escape to close dropdown\n * - Tab to close and move focus\n * - Auto-scrolls highlighted option into view\n * \n * @example\n * ```tsx\n * const { \n * highlightedIndex, \n * handleKeyDown, \n * getOptionId \n * } = useComboboxNavigation({\n * items: suggestions,\n * isOpen: isDropdownOpen,\n * onSelect: (item, idx) => handleSelect(item),\n * onClose: () => setIsOpen(false),\n * listboxRef\n * });\n * \n * <input\n * role=\"combobox\"\n * onKeyDown={handleKeyDown}\n * aria-activedescendant={highlightedIndex >= 0 ? getOptionId(listboxId, highlightedIndex) : undefined}\n * />\n * ```\n */\nexport function useComboboxNavigation<T = any>({\n items,\n isOpen,\n onSelect,\n onClose,\n onOpen,\n loop = true,\n disabled = false,\n listboxRef,\n optionSelector = '[role=\"option\"]',\n keepHighlightOnSelect = false,\n closeOnTab = true\n}: UseComboboxNavigationOptions<T>): UseComboboxNavigationReturn {\n const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);\n \n // Reset highlighted index when dropdown closes\n useEffect(() => {\n if (!isOpen) {\n setHighlightedIndex(-1);\n }\n }, [isOpen]);\n \n // Auto-scroll highlighted item into view\n useScrollActiveIntoView({\n containerRef: listboxRef,\n activeIndex: highlightedIndex,\n itemSelector: optionSelector,\n enabled: isOpen\n });\n \n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (disabled) {\n // For disabled navigation, still handle Escape\n if (e.key === 'Escape' && isOpen) {\n e.preventDefault();\n onClose();\n }\n return;\n }\n \n const itemCount = items.length;\n \n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n e.stopPropagation();\n if (!isOpen && itemCount > 0) {\n // Open dropdown and highlight first item\n onOpen?.();\n setHighlightedIndex(0);\n } else if (isOpen && itemCount > 0) {\n // Navigate down\n setHighlightedIndex((prev) => {\n if (loop) {\n return (prev + 1) % itemCount;\n }\n return Math.min(prev + 1, itemCount - 1);\n });\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n e.stopPropagation();\n if (isOpen && itemCount > 0) {\n // Navigate up\n setHighlightedIndex((prev) => {\n // If nothing highlighted, go to last item\n if (prev === -1) {\n return itemCount - 1;\n }\n if (loop) {\n return (prev - 1 + itemCount) % itemCount;\n }\n return Math.max(prev - 1, 0);\n });\n }\n break;\n \n case 'Enter':\n e.preventDefault();\n if (!isOpen) {\n onOpen?.();\n setHighlightedIndex(0);\n } else if (highlightedIndex >= 0 && items[highlightedIndex]) {\n onSelect(items[highlightedIndex], highlightedIndex);\n if (!keepHighlightOnSelect) {\n setHighlightedIndex(-1);\n }\n }\n break;\n\n case ' ':\n e.preventDefault();\n if (!isOpen) {\n onOpen?.();\n setHighlightedIndex(0);\n } else if (highlightedIndex >= 0 && items[highlightedIndex]) {\n onSelect(items[highlightedIndex], highlightedIndex);\n if (!keepHighlightOnSelect) {\n setHighlightedIndex(-1);\n }\n }\n break;\n case 'Escape':\n if (isOpen) {\n e.preventDefault();\n onClose();\n setHighlightedIndex(-1);\n }\n break;\n \n case 'Tab':\n // closeOnTab=true (default): close dropdown and let focus move to next element\n // closeOnTab=false: keep dropdown open so Tab reaches CTAs (Clear/Apply)\n if (isOpen && closeOnTab) {\n onClose();\n setHighlightedIndex(-1);\n }\n break;\n }\n },\n [disabled, items, isOpen, highlightedIndex, onSelect, onClose, onOpen, loop, keepHighlightOnSelect, closeOnTab]\n );\n \n const getOptionId = useCallback(\n (listboxId: string, index: number) => `${listboxId}-option-${index}`,\n []\n );\n \n return {\n highlightedIndex,\n setHighlightedIndex,\n handleKeyDown,\n getOptionId\n };\n}\n"],"names":["useState","useEffect","useCallback","useScrollActiveIntoView","useComboboxNavigation","items","isOpen","onSelect","onClose","onOpen","loop","disabled","listboxRef","optionSelector","keepHighlightOnSelect","closeOnTab","highlightedIndex","setHighlightedIndex","containerRef","activeIndex","itemSelector","enabled","handleKeyDown","e","key","preventDefault","itemCount","length","stopPropagation","prev","Math","min","max","getOptionId","listboxId","index"],"mappings":"AAyHO,SAAA,YAAAA,GAAA,aAAAC,GAAA,eAAAC,SAAA;AAAA,SAAA,2BAAAC,SAAA;AAAA,SAASC,EAA+B;AAAA,EAC7CC,OAAAA;AAAAA,EACAC,QAAAA;AAAAA,EACAC,UAAAA;AAAAA,EACAC,SAAAA;AAAAA,EACAC,QAAAA;AAAAA,EACAC,MAAAA,IAAO;AAAA,EACPC,UAAAA,IAAW;AAAA,EACXC,YAAAA;AAAAA,EACAC,gBAAAA,IAAiB;AAAA,EACjBC,uBAAAA,IAAwB;AAAA,EACxBC,YAAAA,IAAa;AACkB,GAAgC;AAC/D,QAAM,CAACC,GAAkBC,CAAmB,IAAIjB,EAAiB,EAAE;AAGnEC,EAAAA,EAAU,MAAM;AACd,IAAKK,KACHW,EAAoB,EAAE;AAAA,EAE1B,GAAG,CAACX,CAAM,CAAC,GAGXH,EAAwB;AAAA,IACtBe,cAAcN;AAAAA,IACdO,aAAaH;AAAAA,IACbI,cAAcP;AAAAA,IACdQ,SAASf;AAAAA,EAAAA,CACV;AAED,QAAMgB,IAAgBpB,EACpB,CAACqB,MAA2B;AAC1B,QAAIZ,GAAU;AAEZ,MAAIY,EAAEC,QAAQ,YAAYlB,MACxBiB,EAAEE,eAAAA,GACFjB,EAAAA;AAEF;AAAA,IACF;AAEA,UAAMkB,IAAYrB,EAAMsB;AAExB,YAAQJ,EAAEC,KAAAA;AAAAA,MACR,KAAK;AACHD,QAAAA,EAAEE,eAAAA,GACFF,EAAEK,gBAAAA,GACE,CAACtB,KAAUoB,IAAY,KAEzBjB,IAAAA,GACAQ,EAAoB,CAAC,KACZX,KAAUoB,IAAY,KAE/BT,EAAqBY,CAAAA,MACfnB,KACMmB,IAAO,KAAKH,IAEfI,KAAKC,IAAIF,IAAO,GAAGH,IAAY,CAAC,CACxC;AAEH;AAAA,MAEF,KAAK;AACHH,QAAAA,EAAEE,eAAAA,GACFF,EAAEK,gBAAAA,GACEtB,KAAUoB,IAAY,KAExBT,EAAqBY,CAAAA,MAEfA,MAAS,KACJH,IAAY,IAEjBhB,KACMmB,IAAO,IAAIH,KAAaA,IAE3BI,KAAKE,IAAIH,IAAO,GAAG,CAAC,CAC5B;AAEH;AAAA,MAEF,KAAK;AACHN,QAAAA,EAAEE,eAAAA,GACGnB,IAGMU,KAAoB,KAAKX,EAAMW,CAAgB,MACxDT,EAASF,EAAMW,CAAgB,GAAGA,CAAgB,GAC7CF,KACHG,EAAoB,EAAE,MALxBR,IAAAA,GACAQ,EAAoB,CAAC;AAOvB;AAAA,MAEF,KAAK;AACHM,QAAAA,EAAEE,eAAAA,GACGnB,IAGMU,KAAoB,KAAKX,EAAMW,CAAgB,MACxDT,EAASF,EAAMW,CAAgB,GAAGA,CAAgB,GAC7CF,KACHG,EAAoB,EAAE,MALxBR,IAAAA,GACAQ,EAAoB,CAAC;AAOvB;AAAA,MACF,KAAK;AACH,QAAIX,MACFiB,EAAEE,eAAAA,GACFjB,EAAAA,GACAS,EAAoB,EAAE;AAExB;AAAA,MAEF,KAAK;AAGH,QAAIX,KAAUS,MACZP,EAAAA,GACAS,EAAoB,EAAE;AAExB;AAAA,IAAA;AAAA,EAEN,GACA,CAACN,GAAUN,GAAOC,GAAQU,GAAkBT,GAAUC,GAASC,GAAQC,GAAMI,GAAuBC,CAAU,CAChH,GAEMkB,IAAc/B,EAClB,CAACgC,GAAmBC,MAAkB,GAAGD,CAAS,WAAWC,CAAK,IAClE,CAAA,CACF;AAEA,SAAO;AAAA,IACLnB,kBAAAA;AAAAA,IACAC,qBAAAA;AAAAA,IACAK,eAAAA;AAAAA,IACAW,aAAAA;AAAAA,EAAAA;AAEJ;"}
1
+ {"version":3,"file":"index79.js","sources":["../src/utils/a11y/useComboboxNavigation.ts"],"sourcesContent":["import { useCallback, useState, useEffect } from 'react';\nimport type { RefObject } from 'react';\nimport { useScrollActiveIntoView } from './useScrollActiveIntoView';\n\nexport interface UseComboboxNavigationOptions<T = any> {\n /**\n * Array of items to navigate through\n */\n items: T[];\n \n /**\n * Whether the dropdown is currently open\n */\n isOpen: boolean;\n \n /**\n * Callback when an item is selected (Enter key)\n */\n onSelect: (item: T, index: number) => void;\n \n /**\n * Callback to close the dropdown\n */\n onClose: () => void;\n \n /**\n * Optional: Callback to open the dropdown\n */\n onOpen?: () => void;\n \n /**\n * Whether to wrap around at the ends of the list.\n * Default: true\n */\n loop?: boolean;\n \n /**\n * Whether keyboard navigation is disabled\n * (e.g., for custom rendered content)\n */\n disabled?: boolean;\n\n /**\n * Whether to keep the highlighted index after selecting an item.\n * Useful for multi-select where the dropdown stays open after selection.\n * Default: false\n */\n keepHighlightOnSelect?: boolean;\n\n /**\n * Whether Tab should close the dropdown.\n * Set to false when Tab should move focus to elements within the popup (e.g. CTAs).\n * Default: true\n */\n closeOnTab?: boolean;\n\n /**\n * Ref to the listbox container for scroll management\n */\n listboxRef?: RefObject<HTMLElement | null>;\n \n /**\n * CSS selector for option elements (default: '[role=\"option\"]')\n */\n optionSelector?: string;\n}\n\nexport interface UseComboboxNavigationReturn {\n /**\n * Currently highlighted index (-1 if none)\n */\n highlightedIndex: number;\n \n /**\n * Set the highlighted index manually\n */\n setHighlightedIndex: (index: number | ((prev: number) => number)) => void;\n \n /**\n * Keyboard event handler for the combobox input\n */\n handleKeyDown: (e: React.KeyboardEvent) => void;\n \n /**\n * Generate stable ID for an option\n */\n getOptionId: (listboxId: string, index: number) => string;\n \n}\n\n/**\n * Hook for managing combobox keyboard navigation with aria-activedescendant.\n * \n * Implements WAI-ARIA 1.2 Combobox pattern:\n * - Arrow Up/Down to navigate options\n * - Enter to select highlighted option\n * - Escape to close dropdown\n * - Tab to close and move focus\n * - Auto-scrolls highlighted option into view\n * \n * @example\n * ```tsx\n * const { \n * highlightedIndex, \n * handleKeyDown, \n * getOptionId \n * } = useComboboxNavigation({\n * items: suggestions,\n * isOpen: isDropdownOpen,\n * onSelect: (item, idx) => handleSelect(item),\n * onClose: () => setIsOpen(false),\n * listboxRef\n * });\n * \n * <input\n * role=\"combobox\"\n * onKeyDown={handleKeyDown}\n * aria-activedescendant={highlightedIndex >= 0 ? getOptionId(listboxId, highlightedIndex) : undefined}\n * />\n * ```\n */\nexport function useComboboxNavigation<T = any>({\n items,\n isOpen,\n onSelect,\n onClose,\n onOpen,\n loop = true,\n disabled = false,\n listboxRef,\n optionSelector = '[role=\"option\"]',\n keepHighlightOnSelect = false,\n closeOnTab = true\n}: UseComboboxNavigationOptions<T>): UseComboboxNavigationReturn {\n const [highlightedIndex, setHighlightedIndex] = useState<number>(-1);\n \n // Reset highlighted index when dropdown closes\n useEffect(() => {\n if (!isOpen) {\n setHighlightedIndex(-1);\n }\n }, [isOpen]);\n \n // Auto-scroll highlighted item into view\n useScrollActiveIntoView({\n containerRef: listboxRef,\n activeIndex: highlightedIndex,\n itemSelector: optionSelector,\n enabled: isOpen\n });\n \n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (disabled) {\n // For disabled navigation, still handle Escape\n if (e.key === 'Escape' && isOpen) {\n e.preventDefault();\n onClose();\n }\n return;\n }\n \n const itemCount = items.length;\n \n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n e.stopPropagation();\n if (!isOpen && itemCount > 0) {\n // Open dropdown and highlight first item\n onOpen?.();\n setHighlightedIndex(0);\n } else if (isOpen && itemCount > 0) {\n // Navigate down\n setHighlightedIndex((prev) => {\n if (loop) {\n return (prev + 1) % itemCount;\n }\n return Math.min(prev + 1, itemCount - 1);\n });\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n e.stopPropagation();\n if (isOpen && itemCount > 0) {\n // Navigate up\n setHighlightedIndex((prev) => {\n // If nothing highlighted, go to last item\n if (prev === -1) {\n return itemCount - 1;\n }\n if (loop) {\n return (prev - 1 + itemCount) % itemCount;\n }\n return Math.max(prev - 1, 0);\n });\n }\n break;\n \n case 'Enter':\n e.preventDefault();\n if (!isOpen) {\n onOpen?.();\n setHighlightedIndex(0);\n } else if (highlightedIndex >= 0 && items[highlightedIndex]) {\n onSelect(items[highlightedIndex], highlightedIndex);\n if (!keepHighlightOnSelect) {\n setHighlightedIndex(-1);\n }\n }\n break;\n\n case 'Escape':\n if (isOpen) {\n e.preventDefault();\n onClose();\n setHighlightedIndex(-1);\n }\n break;\n \n case 'Tab':\n // closeOnTab=true (default): close dropdown and let focus move to next element\n // closeOnTab=false: keep dropdown open so Tab reaches CTAs (Clear/Apply)\n if (isOpen && closeOnTab) {\n onClose();\n setHighlightedIndex(-1);\n }\n break;\n }\n },\n [disabled, items, isOpen, highlightedIndex, onSelect, onClose, onOpen, loop, keepHighlightOnSelect, closeOnTab]\n );\n \n const getOptionId = useCallback(\n (listboxId: string, index: number) => `${listboxId}-option-${index}`,\n []\n );\n \n return {\n highlightedIndex,\n setHighlightedIndex,\n handleKeyDown,\n getOptionId\n };\n}\n"],"names":["useState","useEffect","useCallback","useScrollActiveIntoView","useComboboxNavigation","items","isOpen","onSelect","onClose","onOpen","loop","disabled","listboxRef","optionSelector","keepHighlightOnSelect","closeOnTab","highlightedIndex","setHighlightedIndex","containerRef","activeIndex","itemSelector","enabled","handleKeyDown","e","key","preventDefault","itemCount","length","stopPropagation","prev","Math","min","max","getOptionId","listboxId","index"],"mappings":"AAyHO,SAAA,YAAAA,GAAA,aAAAC,GAAA,eAAAC,SAAA;AAAA,SAAA,2BAAAC,SAAA;AAAA,SAASC,EAA+B;AAAA,EAC7CC,OAAAA;AAAAA,EACAC,QAAAA;AAAAA,EACAC,UAAAA;AAAAA,EACAC,SAAAA;AAAAA,EACAC,QAAAA;AAAAA,EACAC,MAAAA,IAAO;AAAA,EACPC,UAAAA,IAAW;AAAA,EACXC,YAAAA;AAAAA,EACAC,gBAAAA,IAAiB;AAAA,EACjBC,uBAAAA,IAAwB;AAAA,EACxBC,YAAAA,IAAa;AACkB,GAAgC;AAC/D,QAAM,CAACC,GAAkBC,CAAmB,IAAIjB,EAAiB,EAAE;AAGnEC,EAAAA,EAAU,MAAM;AACd,IAAKK,KACHW,EAAoB,EAAE;AAAA,EAE1B,GAAG,CAACX,CAAM,CAAC,GAGXH,EAAwB;AAAA,IACtBe,cAAcN;AAAAA,IACdO,aAAaH;AAAAA,IACbI,cAAcP;AAAAA,IACdQ,SAASf;AAAAA,EAAAA,CACV;AAED,QAAMgB,IAAgBpB,EACpB,CAACqB,MAA2B;AAC1B,QAAIZ,GAAU;AAEZ,MAAIY,EAAEC,QAAQ,YAAYlB,MACxBiB,EAAEE,eAAAA,GACFjB,EAAAA;AAEF;AAAA,IACF;AAEA,UAAMkB,IAAYrB,EAAMsB;AAExB,YAAQJ,EAAEC,KAAAA;AAAAA,MACR,KAAK;AACHD,QAAAA,EAAEE,eAAAA,GACFF,EAAEK,gBAAAA,GACE,CAACtB,KAAUoB,IAAY,KAEzBjB,IAAAA,GACAQ,EAAoB,CAAC,KACZX,KAAUoB,IAAY,KAE/BT,EAAqBY,CAAAA,MACfnB,KACMmB,IAAO,KAAKH,IAEfI,KAAKC,IAAIF,IAAO,GAAGH,IAAY,CAAC,CACxC;AAEH;AAAA,MAEF,KAAK;AACHH,QAAAA,EAAEE,eAAAA,GACFF,EAAEK,gBAAAA,GACEtB,KAAUoB,IAAY,KAExBT,EAAqBY,CAAAA,MAEfA,MAAS,KACJH,IAAY,IAEjBhB,KACMmB,IAAO,IAAIH,KAAaA,IAE3BI,KAAKE,IAAIH,IAAO,GAAG,CAAC,CAC5B;AAEH;AAAA,MAEF,KAAK;AACHN,QAAAA,EAAEE,eAAAA,GACGnB,IAGMU,KAAoB,KAAKX,EAAMW,CAAgB,MACxDT,EAASF,EAAMW,CAAgB,GAAGA,CAAgB,GAC7CF,KACHG,EAAoB,EAAE,MALxBR,IAAAA,GACAQ,EAAoB,CAAC;AAOvB;AAAA,MAEF,KAAK;AACH,QAAIX,MACFiB,EAAEE,eAAAA,GACFjB,EAAAA,GACAS,EAAoB,EAAE;AAExB;AAAA,MAEF,KAAK;AAGH,QAAIX,KAAUS,MACZP,EAAAA,GACAS,EAAoB,EAAE;AAExB;AAAA,IAAA;AAAA,EAEN,GACA,CAACN,GAAUN,GAAOC,GAAQU,GAAkBT,GAAUC,GAASC,GAAQC,GAAMI,GAAuBC,CAAU,CAChH,GAEMkB,IAAc/B,EAClB,CAACgC,GAAmBC,MAAkB,GAAGD,CAAS,WAAWC,CAAK,IAClE,CAAA,CACF;AAEA,SAAO;AAAA,IACLnB,kBAAAA;AAAAA,IACAC,qBAAAA;AAAAA,IACAK,eAAAA;AAAAA,IACAW,aAAAA;AAAAA,EAAAA;AAEJ;"}
package/dist/index80.js CHANGED
@@ -1,27 +1,30 @@
1
1
  import * as e from "react";
2
- function p({
3
- disabled: t = !1,
4
- onFocusIn: u,
5
- onFocusOut: n,
6
- onEscape: a,
7
- closeOnEscape: c = !0
2
+ function m({
3
+ disabled: r = !1,
4
+ onFocusIn: n,
5
+ onFocusOut: u,
6
+ onEscape: c,
7
+ closeOnEscape: a = !0
8
8
  }) {
9
- const f = e.useCallback((r) => {
10
- t || u?.();
11
- }, [t, u]), s = e.useCallback((r) => {
12
- if (t) return;
13
- const o = r.relatedTarget;
14
- o && r.currentTarget.contains(o) || n?.();
15
- }, [t, n]), C = e.useCallback((r) => {
16
- t || !c || r.key !== "Escape" || (r.preventDefault(), a?.());
17
- }, [t, c, a]);
9
+ const f = e.useCallback((t) => {
10
+ r || n?.();
11
+ }, [r, n]), o = e.useRef(null), i = e.useCallback((t) => {
12
+ if (r) return;
13
+ o.current = t.currentTarget;
14
+ const s = t.relatedTarget;
15
+ s && t.currentTarget.contains(s) || requestAnimationFrame(() => {
16
+ o.current?.contains(document.activeElement) || u?.();
17
+ });
18
+ }, [r, u]), l = e.useCallback((t) => {
19
+ r || !a || t.key !== "Escape" || (t.preventDefault(), c?.());
20
+ }, [r, a, c]);
18
21
  return {
19
22
  onFocusCapture: f,
20
- onBlurCapture: s,
21
- onKeyDownCapture: C
23
+ onBlurCapture: i,
24
+ onKeyDownCapture: l
22
25
  };
23
26
  }
24
27
  export {
25
- p as useDismissOnFocusOut
28
+ m as useDismissOnFocusOut
26
29
  };
27
30
  //# sourceMappingURL=index80.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index80.js","sources":["../src/utils/a11y/useDismissOnFocusOut.ts"],"sourcesContent":["import * as React from 'react';\n\nexport type UseDismissOnFocusOutOptions = {\n /**\n * Disable all handlers (no-ops). Useful when a component provides alternate\n * focus/keyboard behavior (e.g., disabled-trigger tooltip wrapper).\n */\n disabled?: boolean;\n /**\n * Called when focus enters anywhere within the target.\n */\n onFocusIn?: () => void;\n /**\n * Called when focus leaves the target (i.e., focus moves to an element\n * outside the currentTarget).\n */\n onFocusOut?: () => void;\n /**\n * Called when Escape is pressed while focus is within the target.\n */\n onEscape?: () => void;\n /**\n * Whether Escape should trigger dismissal. Default: true.\n */\n closeOnEscape?: boolean;\n};\n\nexport type UseDismissOnFocusOutReturn<T extends HTMLElement = HTMLElement> = {\n onFocusCapture: (e: React.FocusEvent<T>) => void;\n onBlurCapture: (e: React.FocusEvent<T>) => void;\n onKeyDownCapture: (e: React.KeyboardEvent<T>) => void;\n};\n\n/**\n * Returns capture-phase handlers to \"show on focus within\" and \"dismiss on focus out\",\n * with optional Escape-to-dismiss.\n *\n * Intended for non-interactive surfaces like tooltips where content should remain\n * visible while focus stays within the wrapper.\n */\nexport function useDismissOnFocusOut<T extends HTMLElement = HTMLElement>({\n disabled = false,\n onFocusIn,\n onFocusOut,\n onEscape,\n closeOnEscape = true\n}: UseDismissOnFocusOutOptions): UseDismissOnFocusOutReturn<T> {\n const onFocusCapture = React.useCallback(\n (_e: React.FocusEvent<T>) => {\n if (disabled) return;\n onFocusIn?.();\n },\n [disabled, onFocusIn]\n );\n\n const onBlurCapture = React.useCallback(\n (e: React.FocusEvent<T>) => {\n if (disabled) return;\n\n const nextFocused = e.relatedTarget as Node | null;\n if (nextFocused && e.currentTarget.contains(nextFocused)) return;\n\n onFocusOut?.();\n },\n [disabled, onFocusOut]\n );\n\n const onKeyDownCapture = React.useCallback(\n (e: React.KeyboardEvent<T>) => {\n if (disabled || !closeOnEscape || e.key !== 'Escape') return;\n\n e.preventDefault();\n onEscape?.();\n },\n [disabled, closeOnEscape, onEscape]\n );\n\n return { onFocusCapture, onBlurCapture, onKeyDownCapture };\n}\n\n"],"names":["React","useDismissOnFocusOut","disabled","onFocusIn","onFocusOut","onEscape","closeOnEscape","onFocusCapture","useCallback","_e","onBlurCapture","e","nextFocused","relatedTarget","currentTarget","contains","onKeyDownCapture","key","preventDefault"],"mappings":"AAwCO,YAAAA,OAAA;AAAA,SAASC,EAA0D;AAAA,EACxEC,UAAAA,IAAW;AAAA,EACXC,WAAAA;AAAAA,EACAC,YAAAA;AAAAA,EACAC,UAAAA;AAAAA,EACAC,eAAAA,IAAgB;AACW,GAAkC;AAC7D,QAAMC,IAAiBP,EAAMQ,YAC3B,CAACC,MAA4B;AAC3B,IAAIP,KACJC,IAAAA;AAAAA,EACF,GACA,CAACD,GAAUC,CAAS,CACtB,GAEMO,IAAgBV,EAAMQ,YAC1B,CAACG,MAA2B;AAC1B,QAAIT,EAAU;AAEd,UAAMU,IAAcD,EAAEE;AACtB,IAAID,KAAeD,EAAEG,cAAcC,SAASH,CAAW,KAEvDR,IAAAA;AAAAA,EACF,GACA,CAACF,GAAUE,CAAU,CACvB,GAEMY,IAAmBhB,EAAMQ,YAC7B,CAACG,MAA8B;AAC7B,IAAIT,KAAY,CAACI,KAAiBK,EAAEM,QAAQ,aAE5CN,EAAEO,eAAAA,GACFb,IAAAA;AAAAA,EACF,GACA,CAACH,GAAUI,GAAeD,CAAQ,CACpC;AAEA,SAAO;AAAA,IAAEE,gBAAAA;AAAAA,IAAgBG,eAAAA;AAAAA,IAAeM,kBAAAA;AAAAA,EAAAA;AAC1C;"}
1
+ {"version":3,"file":"index80.js","sources":["../src/utils/a11y/useDismissOnFocusOut.ts"],"sourcesContent":["import * as React from 'react';\n\nexport type UseDismissOnFocusOutOptions = {\n /**\n * Disable all handlers (no-ops). Useful when a component provides alternate\n * focus/keyboard behavior (e.g., disabled-trigger tooltip wrapper).\n */\n disabled?: boolean;\n /**\n * Called when focus enters anywhere within the target.\n */\n onFocusIn?: () => void;\n /**\n * Called when focus leaves the target (i.e., focus moves to an element\n * outside the currentTarget).\n */\n onFocusOut?: () => void;\n /**\n * Called when Escape is pressed while focus is within the target.\n */\n onEscape?: () => void;\n /**\n * Whether Escape should trigger dismissal. Default: true.\n */\n closeOnEscape?: boolean;\n};\n\nexport type UseDismissOnFocusOutReturn<T extends HTMLElement = HTMLElement> = {\n onFocusCapture: (e: React.FocusEvent<T>) => void;\n onBlurCapture: (e: React.FocusEvent<T>) => void;\n onKeyDownCapture: (e: React.KeyboardEvent<T>) => void;\n};\n\n/**\n * Returns capture-phase handlers to \"show on focus within\" and \"dismiss on focus out\",\n * with optional Escape-to-dismiss.\n *\n * Intended for non-interactive surfaces like tooltips where content should remain\n * visible while focus stays within the wrapper.\n */\nexport function useDismissOnFocusOut<T extends HTMLElement = HTMLElement>({\n disabled = false,\n onFocusIn,\n onFocusOut,\n onEscape,\n closeOnEscape = true\n}: UseDismissOnFocusOutOptions): UseDismissOnFocusOutReturn<T> {\n const onFocusCapture = React.useCallback(\n (_e: React.FocusEvent<T>) => {\n if (disabled) return;\n onFocusIn?.();\n },\n [disabled, onFocusIn]\n );\n\n const containerRef = React.useRef<T | null>(null);\n\n const onBlurCapture = React.useCallback(\n (e: React.FocusEvent<T>) => {\n if (disabled) return;\n\n // Capture the container element for the async check — e.currentTarget\n // is only valid synchronously during this event handler.\n containerRef.current = e.currentTarget;\n\n const nextFocused = e.relatedTarget as Node | null;\n if (nextFocused && e.currentTarget.contains(nextFocused)) return;\n\n // Defer the close check: when focus transitions between elements inside\n // the container (e.g. a React re-render replaces a focused DOM node),\n // relatedTarget can be null transiently. By the next frame the browser\n // has settled activeElement, so we re-check containment before closing.\n requestAnimationFrame(() => {\n if (containerRef.current?.contains(document.activeElement)) return;\n onFocusOut?.();\n });\n },\n [disabled, onFocusOut]\n );\n\n const onKeyDownCapture = React.useCallback(\n (e: React.KeyboardEvent<T>) => {\n if (disabled || !closeOnEscape || e.key !== 'Escape') return;\n\n e.preventDefault();\n onEscape?.();\n },\n [disabled, closeOnEscape, onEscape]\n );\n\n return { onFocusCapture, onBlurCapture, onKeyDownCapture };\n}\n\n"],"names":["React","useDismissOnFocusOut","disabled","onFocusIn","onFocusOut","onEscape","closeOnEscape","onFocusCapture","useCallback","_e","containerRef","useRef","onBlurCapture","e","current","currentTarget","nextFocused","relatedTarget","contains","requestAnimationFrame","document","activeElement","onKeyDownCapture","key","preventDefault"],"mappings":"AAwCO,YAAAA,OAAA;AAAA,SAASC,EAA0D;AAAA,EACxEC,UAAAA,IAAW;AAAA,EACXC,WAAAA;AAAAA,EACAC,YAAAA;AAAAA,EACAC,UAAAA;AAAAA,EACAC,eAAAA,IAAgB;AACW,GAAkC;AAC7D,QAAMC,IAAiBP,EAAMQ,YAC3B,CAACC,MAA4B;AAC3B,IAAIP,KACJC,IAAAA;AAAAA,EACF,GACA,CAACD,GAAUC,CAAS,CACtB,GAEMO,IAAeV,EAAMW,OAAiB,IAAI,GAE1CC,IAAgBZ,EAAMQ,YAC1B,CAACK,MAA2B;AAC1B,QAAIX,EAAU;AAIdQ,IAAAA,EAAaI,UAAUD,EAAEE;AAEzB,UAAMC,IAAcH,EAAEI;AACtB,IAAID,KAAeH,EAAEE,cAAcG,SAASF,CAAW,KAMvDG,sBAAsB,MAAM;AAC1B,MAAIT,EAAaI,SAASI,SAASE,SAASC,aAAa,KACzDjB,IAAAA;AAAAA,IACF,CAAC;AAAA,EACH,GACA,CAACF,GAAUE,CAAU,CACvB,GAEMkB,IAAmBtB,EAAMQ,YAC7B,CAACK,MAA8B;AAC7B,IAAIX,KAAY,CAACI,KAAiBO,EAAEU,QAAQ,aAE5CV,EAAEW,eAAAA,GACFnB,IAAAA;AAAAA,EACF,GACA,CAACH,GAAUI,GAAeD,CAAQ,CACpC;AAEA,SAAO;AAAA,IAAEE,gBAAAA;AAAAA,IAAgBK,eAAAA;AAAAA,IAAeU,kBAAAA;AAAAA,EAAAA;AAC1C;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "se-design",
3
- "version": "1.0.83-dev.2",
3
+ "version": "1.0.83-dev.4",
4
4
  "type": "module",
5
5
  "module": "dist/index.js",
6
6
  "exports": {