@spaced-out/ui-design-system 0.6.36 → 0.6.37

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 (79) hide show
  1. package/.cspell/custom-words.txt +9 -0
  2. package/CHANGELOG.md +9 -0
  3. package/lib/components/ButtonDropdown/ButtonDropdown.d.ts +10 -0
  4. package/lib/components/ButtonDropdown/ButtonDropdown.d.ts.map +1 -1
  5. package/lib/components/ButtonDropdown/ButtonDropdown.js +26 -3
  6. package/lib/components/ButtonTabs/ButtonTab/ButtonTab.d.ts +9 -1
  7. package/lib/components/ButtonTabs/ButtonTab/ButtonTab.d.ts.map +1 -1
  8. package/lib/components/ButtonTabs/ButtonTab/ButtonTab.js +8 -0
  9. package/lib/components/ButtonTabs/ButtonTabDropdown.d.ts +10 -1
  10. package/lib/components/ButtonTabs/ButtonTabDropdown.d.ts.map +1 -1
  11. package/lib/components/ButtonTabs/ButtonTabDropdown.js +31 -3
  12. package/lib/components/ButtonTabs/ButtonTabs.d.ts +6 -0
  13. package/lib/components/ButtonTabs/ButtonTabs.d.ts.map +1 -1
  14. package/lib/components/ButtonTabs/ButtonTabs.js +2 -0
  15. package/lib/components/Combobox/Combobox.d.ts +9 -0
  16. package/lib/components/Combobox/Combobox.d.ts.map +1 -1
  17. package/lib/components/Combobox/Combobox.js +21 -0
  18. package/lib/components/Combobox/helper.d.ts.map +1 -1
  19. package/lib/components/Combobox/helper.js +0 -4
  20. package/lib/components/Dropdown/Dropdown.d.ts +9 -0
  21. package/lib/components/Dropdown/Dropdown.d.ts.map +1 -1
  22. package/lib/components/Dropdown/Dropdown.js +92 -24
  23. package/lib/components/Dropdown/Dropdown.module.css +24 -0
  24. package/lib/components/InlineDropdown/InlineDropdown.d.ts +10 -0
  25. package/lib/components/InlineDropdown/InlineDropdown.d.ts.map +1 -1
  26. package/lib/components/InlineDropdown/InlineDropdown.js +23 -3
  27. package/lib/components/Input/Input.module.css +38 -0
  28. package/lib/components/Menu/Menu.d.ts +99 -0
  29. package/lib/components/Menu/Menu.d.ts.map +1 -1
  30. package/lib/components/Menu/Menu.js +1152 -187
  31. package/lib/components/Menu/Menu.module.css +191 -7
  32. package/lib/components/Menu/MenuFocusScope.d.ts +21 -0
  33. package/lib/components/Menu/MenuFocusScope.d.ts.map +1 -0
  34. package/lib/components/Menu/MenuFocusScope.js +37 -0
  35. package/lib/components/Menu/MenuOptionButton.d.ts +36 -0
  36. package/lib/components/Menu/MenuOptionButton.d.ts.map +1 -1
  37. package/lib/components/Menu/MenuOptionButton.js +137 -29
  38. package/lib/components/Menu/index.d.ts +2 -0
  39. package/lib/components/Menu/index.d.ts.map +1 -1
  40. package/lib/components/Menu/index.js +22 -0
  41. package/lib/components/Menu/menuOptionId.d.ts +12 -0
  42. package/lib/components/Menu/menuOptionId.d.ts.map +1 -0
  43. package/lib/components/Menu/menuOptionId.js +18 -0
  44. package/lib/components/Menu/useMenuKeyboardNavigation.d.ts +93 -0
  45. package/lib/components/Menu/useMenuKeyboardNavigation.d.ts.map +1 -0
  46. package/lib/components/Menu/useMenuKeyboardNavigation.js +471 -0
  47. package/lib/components/Menu/useMenuTrigger.d.ts +207 -0
  48. package/lib/components/Menu/useMenuTrigger.d.ts.map +1 -0
  49. package/lib/components/Menu/useMenuTrigger.js +389 -0
  50. package/lib/components/OptionButton/OptionButton.d.ts +16 -0
  51. package/lib/components/OptionButton/OptionButton.d.ts.map +1 -1
  52. package/lib/components/OptionButton/OptionButton.js +4 -1
  53. package/lib/components/Tabs/Tab/Tab.d.ts +8 -1
  54. package/lib/components/Tabs/Tab/Tab.d.ts.map +1 -1
  55. package/lib/components/Tabs/Tab/Tab.js +8 -0
  56. package/lib/components/Tabs/TabList/TabDropdown.d.ts +10 -1
  57. package/lib/components/Tabs/TabList/TabDropdown.d.ts.map +1 -1
  58. package/lib/components/Tabs/TabList/TabDropdown.js +23 -3
  59. package/lib/components/Tabs/TabList/TabList.d.ts +14 -0
  60. package/lib/components/Tabs/TabList/TabList.d.ts.map +1 -1
  61. package/lib/components/Tabs/TabList/TabList.js +6 -0
  62. package/lib/components/Typeahead/Typeahead.d.ts +9 -0
  63. package/lib/components/Typeahead/Typeahead.d.ts.map +1 -1
  64. package/lib/components/Typeahead/Typeahead.js +107 -13
  65. package/lib/components/Typeahead/Typeahead.module.css +13 -0
  66. package/lib/hooks/index.d.ts +1 -0
  67. package/lib/hooks/index.d.ts.map +1 -1
  68. package/lib/hooks/index.js +11 -0
  69. package/lib/hooks/useInteractionModality/index.d.ts +2 -0
  70. package/lib/hooks/useInteractionModality/index.d.ts.map +1 -0
  71. package/lib/hooks/useInteractionModality/index.js +16 -0
  72. package/lib/hooks/useInteractionModality/useInteractionModality.d.ts +39 -0
  73. package/lib/hooks/useInteractionModality/useInteractionModality.d.ts.map +1 -0
  74. package/lib/hooks/useInteractionModality/useInteractionModality.js +111 -0
  75. package/lib/utils/click-away/click-away.d.ts +16 -0
  76. package/lib/utils/click-away/click-away.d.ts.map +1 -1
  77. package/lib/utils/click-away/click-away.js +49 -0
  78. package/mcp/package.json +1 -1
  79. package/package.json +1 -1
@@ -12,7 +12,14 @@ export declare const TAB_SIZE: Readonly<{
12
12
  }>;
13
13
  export declare const tabSizeOptions: Array<unknown>;
14
14
  export type TabSize = keyof typeof TAB_SIZE;
15
- export interface TabProps {
15
+ /**
16
+ * Attributes forwarded straight to the underlying `<button>`. Declared rather
17
+ * than relied on: `Tab` spreads its rest props, so these reached the DOM
18
+ * whether or not the type mentioned them — which meant a future refactor could
19
+ * drop a popup trigger's ARIA with no type error anywhere.
20
+ */
21
+ type ForwardedButtonProps = Pick<React.ButtonHTMLAttributes<HTMLButtonElement>, 'id' | 'aria-label' | 'aria-haspopup' | 'aria-expanded' | 'aria-controls' | 'onKeyDown'>;
22
+ export interface TabProps extends ForwardedButtonProps {
16
23
  classNames?: ClassNames;
17
24
  onSelect?: (arg1: {
18
25
  tabId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Tab.d.ts","sourceRoot":"","sources":["../../../../src/components/Tabs/Tab/Tab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,4BAA4B,CAAC;AAMrD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,qBAAqB,CAAC;AAGlD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAM1D,KAAK,UAAU,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC,CAAC;AAEH,eAAO,MAAM,QAAQ;;;EAGnB,CAAC;AAEH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,OAAO,CAA8B,CAAC;AAEzE,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,QAAQ,CAAC;AAE5C,MAAM,WAAW,QAAQ;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC;IAC/D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,GAAG,EAAE,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,CA6HjE,CAAC"}
1
+ {"version":3,"file":"Tab.d.ts","sourceRoot":"","sources":["../../../../src/components/Tabs/Tab/Tab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,4BAA4B,CAAC;AAMrD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,qBAAqB,CAAC;AAGlD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAM1D,KAAK,UAAU,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC,CAAC;AAEH,eAAO,MAAM,QAAQ;;;EAGnB,CAAC;AAEH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,OAAO,CAA8B,CAAC;AAEzE,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,QAAQ,CAAC;AAE5C;;;;;GAKG;AACH,KAAK,oBAAoB,GAAG,IAAI,CAC9B,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAC3C,IAAI,GACJ,YAAY,GACZ,eAAe,GACf,eAAe,GACf,eAAe,GACf,WAAW,CACd,CAAC;AAEF,MAAM,WAAW,QAAS,SAAQ,oBAAoB;IACpD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC;IAC/D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,GAAG,EAAE,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,CA6HjE,CAAC"}
@@ -20,6 +20,14 @@ const TAB_SIZE = exports.TAB_SIZE = Object.freeze({
20
20
  medium: 'medium'
21
21
  });
22
22
  const tabSizeOptions = exports.tabSizeOptions = [...Object.keys(TAB_SIZE)];
23
+
24
+ /**
25
+ * Attributes forwarded straight to the underlying `<button>`. Declared rather
26
+ * than relied on: `Tab` spreads its rest props, so these reached the DOM
27
+ * whether or not the type mentioned them — which meant a future refactor could
28
+ * drop a popup trigger's ARIA with no type error anywhere.
29
+ */
30
+
23
31
  const Tab = exports.Tab = /*#__PURE__*/React.forwardRef((_ref, forwardRef) => {
24
32
  let {
25
33
  classNames,
@@ -17,9 +17,18 @@ export interface TabDropdownProps {
17
17
  classNames?: ClassNames;
18
18
  selected?: boolean;
19
19
  disabled?: boolean;
20
+ /**
21
+ * Opt in to the accessibility behavior: `aria-haspopup` / `aria-expanded` /
22
+ * `aria-controls` on the tab, the arrow key that opens the popup, and focus
23
+ * moving into the menu and back. Forwarded to the `Menu` this opens.
24
+ *
25
+ * Off by default, and off is this component exactly as it behaved before any
26
+ * of it existed.
27
+ */
28
+ enableA11y?: boolean;
20
29
  elevation?: ElevationType;
21
30
  testId?: string;
22
31
  }
23
- export declare const TabDropdown: ({ anchorPosition, size, onOptionSelect, props, elevation, classNames, testId, }: TabDropdownProps) => React.JSX.Element;
32
+ export declare const TabDropdown: ({ anchorPosition, size, onOptionSelect, props, elevation, classNames, enableA11y, testId, }: TabDropdownProps) => React.JSX.Element;
24
33
  export {};
25
34
  //# sourceMappingURL=TabDropdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TabDropdown.d.ts","sourceRoot":"","sources":["../../../../src/components/Tabs/TabList/TabDropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAkB/B,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAE/D,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAM1D,KAAK,UAAU,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,QAAQ,CAAC;QACd,IAAI,EAAE,SAAS,CAAC;KACjB,CAAC;IACF,cAAc,CAAC,EAAE,CACf,MAAM,EAAE,UAAU,EAClB,IAAI,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,KACxD,OAAO,CAAC;IACb,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,WAAW,GAAI,iFAQzB,gBAAgB,sBAsElB,CAAC"}
1
+ {"version":3,"file":"TabDropdown.d.ts","sourceRoot":"","sources":["../../../../src/components/Tabs/TabList/TabDropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAiB/B,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAI/D,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAM1D,KAAK,UAAU,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,QAAQ,CAAC;QACd,IAAI,EAAE,SAAS,CAAC;KACjB,CAAC;IACF,cAAc,CAAC,EAAE,CACf,MAAM,EAAE,UAAU,EAClB,IAAI,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,KACxD,OAAO,CAAC;IACb,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,WAAW,GAAI,6FASzB,gBAAgB,sBAwFlB,CAAC"}
@@ -12,6 +12,8 @@ var _clickAway = require("../../../utils/click-away");
12
12
  var _mergeRefs = require("../../../utils/merge-refs");
13
13
  var _qa = require("../../../utils/qa");
14
14
  var _Menu = require("../../Menu");
15
+ var _MenuFocusScope = require("../../Menu/MenuFocusScope");
16
+ var _useMenuTrigger = require("../../Menu/useMenuTrigger");
15
17
  var _Tab = require("../Tab");
16
18
  var _Tooltip = require("../../Tooltip");
17
19
  var _TabDropdownModule = _interopRequireDefault(require("./TabDropdown.module.css"));
@@ -26,8 +28,17 @@ const TabDropdown = _ref => {
26
28
  props,
27
29
  elevation = 'modal',
28
30
  classNames,
31
+ enableA11y = false,
29
32
  testId
30
33
  } = _ref;
34
+ const menuTrigger = (0, _useMenuTrigger.useMenuTrigger)({
35
+ enabled: enableA11y,
36
+ // The consumer's own id and menu naming win — see ButtonDropdown.
37
+ id: props?.tab?.id,
38
+ menuId: props?.menu?.menuId,
39
+ ariaLabel: props?.menu?.ariaLabel,
40
+ ariaLabelledBy: props?.menu?.ariaLabelledBy
41
+ });
31
42
  const {
32
43
  x,
33
44
  y,
@@ -42,6 +53,7 @@ const TabDropdown = _ref => {
42
53
  middleware: [(0, _react2.shift)(), (0, _react2.flip)(), (0, _react2.offset)(parseInt(_space.spaceXXSmall))]
43
54
  });
44
55
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_clickAway.ClickAway, {
56
+ restoreFocusOnClose: enableA11y,
45
57
  children: _ref2 => {
46
58
  let {
47
59
  isOpen,
@@ -58,6 +70,12 @@ const TabDropdown = _ref => {
58
70
  className: (0, _classify.classify)(_TabDropdownModule.default.tabDropdownContainer, classNames?.wrapper),
59
71
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tab.Tab, {
60
72
  ...props?.tab,
73
+ ...menuTrigger.getTriggerProps({
74
+ // With no `menu` there is no popup to advertise or to open.
75
+ isOpen: isOpen && Boolean(props?.menu),
76
+ onOpen,
77
+ onKeyDown: props?.tab?.onKeyDown
78
+ }),
61
79
  size: size,
62
80
  ref: (0, _mergeRefs.mergeRefs)([refs.setReference, triggerRef]),
63
81
  onClick: e => {
@@ -73,8 +91,8 @@ const TabDropdown = _ref => {
73
91
  slot: 'dropdown-tab'
74
92
  })
75
93
  }), isOpen && props?.menu && /*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.FloatingPortal, {
76
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.FloatingFocusManager, {
77
- modal: false,
94
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MenuFocusScope.MenuFocusScope, {
95
+ enabled: !enableA11y,
78
96
  context: context,
79
97
  initialFocus: refs.reference,
80
98
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
@@ -93,6 +111,7 @@ const TabDropdown = _ref => {
93
111
  },
94
112
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Menu.Menu, {
95
113
  ...props.menu,
114
+ ...menuTrigger.menuProps,
96
115
  onSelect: (option, e) => {
97
116
  onOptionSelect?.(option, e);
98
117
  clickAway();
@@ -102,7 +121,8 @@ const TabDropdown = _ref => {
102
121
  testId: (0, _qa.generateTestId)({
103
122
  base: testId,
104
123
  slot: 'menu'
105
- })
124
+ }),
125
+ enableA11y: enableA11y
106
126
  })
107
127
  })
108
128
  })
@@ -19,6 +19,20 @@ export interface TabListProps {
19
19
  menuWidth?: string;
20
20
  elevation?: ElevationType;
21
21
  maxTabWidth?: string;
22
+ /**
23
+ * Accessible name for the overflow tab, which is icon-only and therefore has
24
+ * no text of its own — it also names the listbox that tab opens. Override it
25
+ * for another language, as `ButtonTabs` does with `wrapTabTitle`.
26
+ */
27
+ overflowTabLabel?: string;
28
+ /**
29
+ * Opt in to the accessibility behavior for the overflow tab and the menu it
30
+ * opens, and give that icon-only tab an accessible name.
31
+ *
32
+ * Off by default, and off is this component exactly as it behaved before any
33
+ * of it existed.
34
+ */
35
+ enableA11y?: boolean;
22
36
  testId?: string;
23
37
  }
24
38
  export declare const TabList: Flow.AbstractComponent<TabListProps, HTMLDivElement>;
@@ -1 +1 @@
1
- {"version":3,"file":"TabList.d.ts","sourceRoot":"","sources":["../../../../src/components/Tabs/TabList/TabList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,4BAA4B,CAAC;AAWrD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAuB1D,KAAK,UAAU,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,OAAO,EAAE,IAAI,CAAC,iBAAiB,CAAC,YAAY,EAAE,cAAc,CA8JtE,CAAC"}
1
+ {"version":3,"file":"TabList.d.ts","sourceRoot":"","sources":["../../../../src/components/Tabs/TabList/TabList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,4BAA4B,CAAC;AAWrD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAuB1D,KAAK,UAAU,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,OAAO,EAAE,IAAI,CAAC,iBAAiB,CAAC,YAAY,EAAE,cAAc,CAoKtE,CAAC"}
@@ -29,6 +29,8 @@ const TabList = exports.TabList = /*#__PURE__*/React.forwardRef((_ref, forwardRe
29
29
  // tooltips may appear over the modal. In such cases, handle it by passing an appropriate elevation.
30
30
  elevation = 'modal',
31
31
  maxTabWidth = _size.size120,
32
+ overflowTabLabel = 'More tabs',
33
+ enableA11y = false,
32
34
  testId
33
35
  } = _ref;
34
36
  const ref = React.useRef(null);
@@ -107,6 +109,9 @@ const TabList = exports.TabList = /*#__PURE__*/React.forwardRef((_ref, forwardRe
107
109
  props: {
108
110
  tab: {
109
111
  iconName: 'ellipsis',
112
+ // Also names the listbox this tab opens, through the trigger
113
+ // idref the hook sets up.
114
+ 'aria-label': enableA11y ? overflowTabLabel : undefined,
110
115
  tabId: 'tab-dropdown',
111
116
  selectedTab: {
112
117
  tabId: isMenuOptionSelected ? 'tab-dropdown' : ''
@@ -121,6 +126,7 @@ const TabList = exports.TabList = /*#__PURE__*/React.forwardRef((_ref, forwardRe
121
126
  }
122
127
  },
123
128
  elevation: elevation,
129
+ enableA11y: enableA11y,
124
130
  testId: (0, _qa.generateTestId)({
125
131
  base: testId,
126
132
  slot: 'dropdown'
@@ -21,6 +21,15 @@ interface BaseTypeaheadProps extends Omit<InputProps, 'classNames' | 'onSelect'
21
21
  menuOpenOffset?: number;
22
22
  clickAwayRef?: React.RefObject<ClickAwayRefType | null>;
23
23
  elevation?: ElevationType;
24
+ /**
25
+ * Opt in to the accessibility behavior: the `combobox` role and its ARIA,
26
+ * arrow-key navigation of the suggestions from the input, and focus handling
27
+ * on close. Forwarded to the `Menu` this opens.
28
+ *
29
+ * Off by default, and off is this component exactly as it behaved before any
30
+ * of it existed.
31
+ */
32
+ enableA11y?: boolean;
24
33
  testId?: string;
25
34
  rightSlot?: React.ReactNode;
26
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Typeahead.d.ts","sourceRoot":"","sources":["../../../src/components/Typeahead/Typeahead.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,4BAA4B,CAAC;AAKrD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AAK3D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAG/D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAM1D,KAAK,UAAU,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAEH,UAAU,kBACR,SAAQ,IAAI,CACV,UAAU,EACR,YAAY,GACZ,UAAU,GACV,UAAU,GACV,YAAY,GACZ,aAAa,GACb,oBAAoB,GACpB,MAAM,GACN,SAAS,GACT,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,WAAW,CACd;IACD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,UAAU,EAClB,IAAI,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,KACxD,OAAO,CAAC;IACb,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC;IACjE,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACxD,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,kBAAkB,EAAE,qBAAqB,CAAC;IACvD,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AA6OD,eAAO,MAAM,SAAS,EAAE,IAAI,CAAC,iBAAiB,CAC5C,cAAc,EACd,gBAAgB,CASjB,CAAC"}
1
+ {"version":3,"file":"Typeahead.d.ts","sourceRoot":"","sources":["../../../src/components/Typeahead/Typeahead.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,4BAA4B,CAAC;AAKrD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AAK3D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAK/D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAM1D,KAAK,UAAU,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAEH,UAAU,kBACR,SAAQ,IAAI,CACV,UAAU,EACR,YAAY,GACZ,UAAU,GACV,UAAU,GACV,YAAY,GACZ,aAAa,GACb,oBAAoB,GACpB,MAAM,GACN,SAAS,GACT,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,WAAW,CACd;IACD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,UAAU,EAClB,IAAI,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,KACxD,OAAO,CAAC;IACb,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,KAAK,OAAO,CAAC;IACjE,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACxD,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,kBAAkB,EAAE,qBAAqB,CAAC;IACvD,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAuXD,eAAO,MAAM,SAAS,EAAE,IAAI,CAAC,iBAAiB,CAC5C,cAAc,EACd,gBAAgB,CASjB,CAAC"}
@@ -13,6 +13,8 @@ var _clickAway = require("../../utils/click-away");
13
13
  var _mergeRefs = require("../../utils/merge-refs");
14
14
  var _qa = require("../../utils/qa");
15
15
  var _Menu = require("../Menu");
16
+ var _MenuFocusScope = require("../Menu/MenuFocusScope");
17
+ var _useMenuTrigger = require("../Menu/useMenuTrigger");
16
18
  var _SearchInput = require("../SearchInput");
17
19
  var _Tooltip = require("../Tooltip");
18
20
  var _TypeaheadModule = _interopRequireDefault(require("./Typeahead.module.css"));
@@ -36,6 +38,7 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
36
38
  onFocus,
37
39
  clickAwayRef,
38
40
  elevation = 'modal',
41
+ enableA11y = false,
39
42
  testId,
40
43
  rightSlot,
41
44
  ...inputProps
@@ -44,6 +47,12 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
44
47
  const groupTitleOptions = menu?.groupTitleOptions;
45
48
  const isMenuOptionsPresent = menuOptions && menuOptions.length > 0;
46
49
  const isMenuGroupPresent = groupTitleOptions && groupTitleOptions.length > 0;
50
+ const menuListRef = React.useRef(null);
51
+ const clickAwayFnRef = React.useRef(null);
52
+ // Set when we close the menu ourselves after a selection, so the focus
53
+ // restoration that follows does not read as the user entering the field.
54
+ const skipNextFocusOpenRef = React.useRef(false);
55
+ const [isMenuOpen, setIsMenuOpen] = React.useState(false);
47
56
  const {
48
57
  x,
49
58
  y,
@@ -58,7 +67,58 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
58
67
  middleware: [(0, _react2.flip)(), (0, _react2.offset)(parseInt(_space.spaceXXSmall))]
59
68
  });
60
69
  const dropdownWidth = (0, _hooks.useReferenceElementWidth)(refs.reference?.current);
70
+
71
+ // The options Menu will actually render, in its own precedence: it returns
72
+ // on the first non-empty source, so concatenating both would count and
73
+ // navigate rows that are not on screen.
74
+ const flatOptions = React.useMemo(() => (0, _Menu.getFlatOptions)({
75
+ options: menuOptions,
76
+ groupTitleOptions
77
+ }), [menuOptions, groupTitleOptions]);
78
+ const focusSearchInput = () => {
79
+ refs.reference?.current?.querySelector('input')?.focus();
80
+ };
81
+ const handleOptionSelect = (option, e) => {
82
+ onSelect?.(option, e ?? null);
83
+ if (
84
+ // keepMenuOpenOnOptionSelect lets the menu persist on select in the normal variant
85
+ !option.keepMenuOpenOnOptionSelect && (!menu?.optionsVariant || menu.optionsVariant === 'normal')) {
86
+ // Swallow the focus event that ClickAway's focus restoration will fire
87
+ // on the input, so closing here actually sticks — but only when focus is
88
+ // inside the menu and a restoration is therefore coming. Selecting with
89
+ // Enter never moves focus off the input, so nothing would consume the
90
+ // flag and it would swallow the user's next genuine focus instead.
91
+ // Only with the opt-in: without it ClickAway restores no focus, so
92
+ // there is no restoration event to swallow.
93
+ skipNextFocusOpenRef.current = Boolean(enableA11y && typeof document !== 'undefined' && menuListRef.current?.contains(document.activeElement));
94
+ clickAwayFnRef.current?.();
95
+ }
96
+ };
97
+
98
+ // Editable combobox: focus stays on the SearchInput, arrows move the active
99
+ // descendant in the portaled listbox, Enter selects, printable keys fall
100
+ // through to the input (which filters the options). The shared hook owns
101
+ // that wiring, the highlight and the trigger's ARIA.
102
+ const comboboxTrigger = (0, _useMenuTrigger.useComboboxMenuTrigger)({
103
+ enabled: enableA11y,
104
+ pattern: 'autocomplete',
105
+ menu,
106
+ // This component owns filtering, so it hands over the options it will
107
+ // actually render rather than letting the hook read them from `menu`.
108
+ options: flatOptions,
109
+ menuRef: menuListRef,
110
+ isOpen: isMenuOpen,
111
+ onSelectOption: option => handleOptionSelect(option, null),
112
+ onClose: () => clickAwayFnRef.current?.(),
113
+ focusTrigger: focusSearchInput,
114
+ canOpen: () => typeaheadInputText.length >= menuOpenOffset,
115
+ triggerAriaLabelledBy: inputProps['aria-labelledby'],
116
+ triggerAriaLabel: inputProps['aria-label'],
117
+ // These are suggestions, not a fixed set of options.
118
+ defaultAriaLabel: 'Suggestions'
119
+ });
61
120
  const onMenuToggle = isOpen => {
121
+ setIsMenuOpen(isOpen);
62
122
  if (isOpen) {
63
123
  onMenuOpen?.();
64
124
  } else {
@@ -68,6 +128,7 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
68
128
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_clickAway.ClickAway, {
69
129
  onChange: onMenuToggle,
70
130
  clickAwayRef: clickAwayRef,
131
+ restoreFocusOnClose: enableA11y,
71
132
  children: _ref2 => {
72
133
  let {
73
134
  isOpen,
@@ -76,6 +137,13 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
76
137
  boundaryRef,
77
138
  triggerRef
78
139
  } = _ref2;
140
+ // Capture the latest close handler for keyboard selection (wired at
141
+ // the component top level).
142
+ clickAwayFnRef.current = clickAway;
143
+ // The single source of truth for "the listbox exists right now", used
144
+ // both to render the portal and to drive the trigger's ARIA state so
145
+ // the two can never disagree.
146
+ const isMenuRendered = Boolean(isOpen && !isLoading && menu && (isMenuOptionsPresent || isMenuGroupPresent));
79
147
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
80
148
  "data-testid": (0, _qa.generateTestId)({
81
149
  base: testId,
@@ -85,6 +153,21 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
85
153
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_SearchInput.SearchInput, {
86
154
  ...inputProps,
87
155
  ref: ref,
156
+ ...comboboxTrigger.getTriggerProps({
157
+ // The popup's own state routes the keys: `onOpen` TOGGLES, so
158
+ // treating an open-but-withheld menu (loading, no matches) as
159
+ // closed would make ArrowDown close it.
160
+ isOpen,
161
+ // ...while the ARIA describes what is actually rendered, since
162
+ // the popup is withheld while loading and when nothing
163
+ // matches. Claiming aria-expanded with an aria-controls IDREF
164
+ // that resolves to nothing is both a lie to the user and an
165
+ // invalid value.
166
+ isExpanded: isMenuRendered,
167
+ onOpen,
168
+ onKeyDown: inputProps.onKeyDown
169
+ }),
170
+ "aria-busy": enableA11y && isLoading || undefined,
88
171
  boxRef: (0, _mergeRefs.mergeRefs)([refs.setReference, triggerRef]),
89
172
  size: size,
90
173
  placeholder: placeholder,
@@ -106,7 +189,13 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
106
189
  }
107
190
  },
108
191
  onFocus: _e => {
109
- if (typeaheadInputText.length >= menuOpenOffset) {
192
+ // Picking an option with the pointer unmounts the row that had
193
+ // focus, so ClickAway hands focus back to this input. That is a
194
+ // restoration, not the user entering the field, and treating it
195
+ // as one would immediately reopen the menu we just closed.
196
+ if (skipNextFocusOpenRef.current) {
197
+ skipNextFocusOpenRef.current = false;
198
+ } else if (typeaheadInputText.length >= menuOpenOffset) {
110
199
  if (!isOpen) {
111
200
  onOpen();
112
201
  }
@@ -122,9 +211,18 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
122
211
  base: testId,
123
212
  slot: 'search-input'
124
213
  })
125
- }), isOpen && !isLoading && menu && (isMenuOptionsPresent || isMenuGroupPresent) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.FloatingPortal, {
126
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.FloatingFocusManager, {
127
- modal: false,
214
+ }), enableA11y && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
215
+ role: "status",
216
+ "aria-live": "polite",
217
+ className: _TypeaheadModule.default.visuallyHidden,
218
+ "data-testid": (0, _qa.generateTestId)({
219
+ base: testId,
220
+ slot: 'live-status'
221
+ }),
222
+ children: isLoading ? menu?.staticLabels?.LOADING_RESULTS ?? 'Loading results' : isOpen ? flatOptions.length === 0 ? menu?.staticLabels?.NO_RESULTS ?? 'No results' : `${flatOptions.length} ${flatOptions.length === 1 ? menu?.staticLabels?.RESULT ?? 'result' : menu?.staticLabels?.RESULTS ?? 'results'}` : ''
223
+ }), isMenuRendered && menu && /*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.FloatingPortal, {
224
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MenuFocusScope.MenuFocusScope, {
225
+ enabled: !enableA11y,
128
226
  context: context,
129
227
  returnFocus: false,
130
228
  initialFocus: refs.reference,
@@ -151,22 +249,18 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
151
249
  },
152
250
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Menu.Menu, {
153
251
  ...menu,
252
+ ref: menuListRef,
253
+ ...comboboxTrigger.menuProps,
154
254
  groupTitleOptions: groupTitleOptions,
155
255
  options: menuOptions,
156
256
  testId: (0, _qa.generateTestId)({
157
257
  base: testId,
158
258
  slot: 'menu'
159
259
  }),
160
- onSelect: (option, e) => {
161
- onSelect?.(option, e);
162
- if (
163
- // option.keepMenuOpenOnOptionSelect - to allow the menu persist its open stat upon option selection in normal variant
164
- !option.keepMenuOpenOnOptionSelect && (!menu.optionsVariant || menu.optionsVariant === 'normal')) {
165
- clickAway();
166
- }
167
- },
260
+ onSelect: handleOptionSelect,
168
261
  size: menu.size || size,
169
- onTabOut: clickAway
262
+ onTabOut: clickAway,
263
+ enableA11y: enableA11y
170
264
  })
171
265
  })
172
266
  })
@@ -14,3 +14,16 @@
14
14
  width: var(--dropdown-width);
15
15
  z-index: var(--menu-elevation);
16
16
  }
17
+
18
+ /* Announced by screen readers (loading / result count) but visually hidden. */
19
+ .visuallyHidden {
20
+ position: absolute;
21
+ width: 1px;
22
+ height: 1px;
23
+ margin: -1px;
24
+ padding: 0;
25
+ border: 0;
26
+ overflow: hidden;
27
+ clip: rect(0, 0, 0, 0);
28
+ white-space: nowrap;
29
+ }
@@ -4,6 +4,7 @@ export * from '../hooks/useDebounce';
4
4
  export * from '../hooks/useFileUpload';
5
5
  export * from '../hooks/useFilteredOptions';
6
6
  export * from '../hooks/useInfiniteScroll';
7
+ export * from '../hooks/useInteractionModality';
7
8
  export * from '../hooks/useInputState';
8
9
  export * from '../hooks/useLockedBody';
9
10
  export * from '../hooks/useModal';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,sCAAsC,CAAC;AACrD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yBAAyB,CAAC;AACxC,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,sCAAsC,CAAC;AACrD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kCAAkC,CAAC;AACjD,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yBAAyB,CAAC;AACxC,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC"}
@@ -69,6 +69,17 @@ Object.keys(_useInfiniteScroll).forEach(function (key) {
69
69
  }
70
70
  });
71
71
  });
72
+ var _useInteractionModality = require("./useInteractionModality");
73
+ Object.keys(_useInteractionModality).forEach(function (key) {
74
+ if (key === "default" || key === "__esModule") return;
75
+ if (key in exports && exports[key] === _useInteractionModality[key]) return;
76
+ Object.defineProperty(exports, key, {
77
+ enumerable: true,
78
+ get: function () {
79
+ return _useInteractionModality[key];
80
+ }
81
+ });
82
+ });
72
83
  var _useInputState = require("./useInputState");
73
84
  Object.keys(_useInputState).forEach(function (key) {
74
85
  if (key === "default" || key === "__esModule") return;
@@ -0,0 +1,2 @@
1
+ export * from '../../hooks/useInteractionModality/useInteractionModality';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/useInteractionModality/index.ts"],"names":[],"mappings":"AAAA,cAAc,yDAAyD,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _useInteractionModality = require("./useInteractionModality");
7
+ Object.keys(_useInteractionModality).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _useInteractionModality[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _useInteractionModality[key];
14
+ }
15
+ });
16
+ });
@@ -0,0 +1,39 @@
1
+ export type InteractionModality = 'keyboard' | 'pointer';
2
+ /**
3
+ * Begin tracking. Idempotent, and session-scoped by design: the listeners live
4
+ * for the life of the page, so there is nothing to tear down.
5
+ *
6
+ * Tracking has to start BEFORE the thing that wants the answer exists. The
7
+ * interesting question is "how was the popup that just mounted opened?", and the
8
+ * keypress that opened it happened while the popup did not yet exist — so a
9
+ * component reading this on mount is always too late to have attached the
10
+ * listener itself. Whatever renders earlier and knows a popup may follow — a
11
+ * menu trigger — primes it.
12
+ *
13
+ * It is not primed on import. Attaching document listeners is a side effect, and
14
+ * a consumer that has not opted into any of this should get none: an app merely
15
+ * importing a component from this package must not start listening to its
16
+ * keyboard. Capture phase, so the modality is recorded before any component
17
+ * handler reacts to the same event.
18
+ */
19
+ export declare const startInteractionModalityTracking: () => void;
20
+ /**
21
+ * How the user last interacted with the page, read imperatively.
22
+ *
23
+ * Use this when the answer is needed during render or in an effect rather than
24
+ * as reactive state — for example a popup deciding on mount whether it was opened
25
+ * by keyboard, where re-rendering on later modality changes would be wrong.
26
+ */
27
+ export declare const getInteractionModality: () => InteractionModality;
28
+ /**
29
+ * How the user last interacted with the page, as reactive state. Re-renders the
30
+ * caller when the user switches between keyboard and pointer.
31
+ */
32
+ export declare const useInteractionModality: (
33
+ /**
34
+ * Subscribe at all. False leaves the caller on the default and attaches
35
+ * nothing — for a component whose consumer has not opted into the behavior
36
+ * this modality serves.
37
+ */
38
+ enabled?: boolean) => InteractionModality;
39
+ //# sourceMappingURL=useInteractionModality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInteractionModality.d.ts","sourceRoot":"","sources":["../../../src/hooks/useInteractionModality/useInteractionModality.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,SAAS,CAAC;AAgEzD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gCAAgC,QAAO,IAOnD,CAAC;AAUF;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,QAAO,mBACzB,CAAC;AAElB;;;GAGG;AACH,eAAO,MAAM,sBAAsB;AACjC;;;;GAIG;AACH,iBAAc,KACb,mBAWF,CAAC"}