@spaced-out/ui-design-system 0.3.49 → 0.3.51-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/lib/components/AvatarGroup/AvatarGroup.js.flow +2 -2
  3. package/lib/components/ButtonDropdown/ButtonDropdown.js +22 -10
  4. package/lib/components/ButtonDropdown/ButtonDropdown.js.flow +58 -33
  5. package/lib/components/ButtonDropdown/ButtonDropdown.module.css +8 -1
  6. package/lib/components/ButtonDropdown/SimpleButtonDropdown.js +3 -0
  7. package/lib/components/ButtonDropdown/SimpleButtonDropdown.js.flow +6 -1
  8. package/lib/components/ButtonTabs/ButtonTabDropdown.js +13 -6
  9. package/lib/components/ButtonTabs/ButtonTabDropdown.js.flow +40 -24
  10. package/lib/components/ButtonTabs/ButtonTabDropdown.module.css +4 -0
  11. package/lib/components/ButtonTabs/ButtonTabs.js +2 -0
  12. package/lib/components/ButtonTabs/ButtonTabs.js.flow +4 -0
  13. package/lib/components/Dropdown/Dropdown.js +23 -8
  14. package/lib/components/Dropdown/Dropdown.js.flow +57 -32
  15. package/lib/components/Dropdown/Dropdown.module.css +5 -0
  16. package/lib/components/Dropdown/SimpleDropdown.js +2 -0
  17. package/lib/components/Dropdown/SimpleDropdown.js.flow +4 -0
  18. package/lib/components/FilterButtonOverlay/FilterButtonOverlay.js +23 -8
  19. package/lib/components/FilterButtonOverlay/FilterButtonOverlay.js.flow +56 -26
  20. package/lib/components/InlineDropdown/InlineDropdown.js +25 -8
  21. package/lib/components/InlineDropdown/InlineDropdown.js.flow +57 -30
  22. package/lib/components/InlineDropdown/InlineDropdown.module.css +5 -0
  23. package/lib/components/InlineDropdown/SimpleInlineDropdown.js +2 -0
  24. package/lib/components/InlineDropdown/SimpleInlineDropdown.js.flow +4 -0
  25. package/lib/components/Table/StaticTable.js +1 -1
  26. package/lib/components/Table/StaticTable.js.flow +1 -1
  27. package/lib/components/Table/Table.docs.js +1 -1
  28. package/lib/components/Table/Table.docs.js.flow +1 -1
  29. package/lib/components/Tabs/TabList/TabDropdown.js +14 -7
  30. package/lib/components/Tabs/TabList/TabDropdown.js.flow +38 -22
  31. package/lib/components/Tabs/TabList/TabDropdown.module.css +4 -0
  32. package/lib/components/Tabs/TabList/TabList.js +4 -2
  33. package/lib/components/Tabs/TabList/TabList.js.flow +4 -0
  34. package/lib/components/TokenListInput/TokenListInput.js +26 -7
  35. package/lib/components/TokenListInput/TokenListInput.js.flow +58 -32
  36. package/lib/components/TokenListInput/TokenListInput.module.css +5 -0
  37. package/lib/components/Tooltip/Tooltip.js +2 -1
  38. package/lib/components/Tooltip/Tooltip.js.flow +2 -2
  39. package/lib/components/Typeahead/SimpleTypeahead.js +3 -1
  40. package/lib/components/Typeahead/SimpleTypeahead.js.flow +4 -0
  41. package/lib/components/Typeahead/Typeahead.js +25 -8
  42. package/lib/components/Typeahead/Typeahead.js.flow +58 -30
  43. package/lib/components/Typeahead/Typeahead.module.css +5 -0
  44. package/lib/hooks/index.js +11 -0
  45. package/lib/hooks/index.js.flow +1 -0
  46. package/lib/hooks/useReferenceElementWidth/index.js +16 -0
  47. package/lib/hooks/useReferenceElementWidth/index.js.flow +3 -0
  48. package/lib/hooks/useReferenceElementWidth/useReferenceElementWidth.js +21 -0
  49. package/lib/hooks/useReferenceElementWidth/useReferenceElementWidth.js.flow +23 -0
  50. package/lib/utils/click-away/click-away.js +4 -0
  51. package/lib/utils/click-away/click-away.js.flow +34 -0
  52. package/package.json +5 -5
@@ -7,13 +7,17 @@ import {
7
7
  // $FlowFixMe[untyped-import]
8
8
  flip,
9
9
  // $FlowFixMe[untyped-import]
10
+ FloatingFocusManager,
11
+ // $FlowFixMe[untyped-import]
12
+ FloatingPortal,
13
+ // $FlowFixMe[untyped-import]
10
14
  offset,
11
15
  // $FlowFixMe[untyped-import]
12
16
  useFloating,
13
17
  } from '@floating-ui/react';
14
18
  import without from 'lodash/without';
15
19
 
16
- import {sizeFluid} from '../../styles/variables/_size';
20
+ import {useReferenceElementWidth} from '../../hooks';
17
21
  import {spaceNone, spaceXXSmall} from '../../styles/variables/_space';
18
22
  import {TEXT_COLORS} from '../../types/typography';
19
23
  import classify from '../../utils/classify';
@@ -31,6 +35,7 @@ import type {InputProps} from '../Input';
31
35
  import type {BaseMenuProps} from '../Menu';
32
36
  import {Menu} from '../Menu';
33
37
  import {BodySmall} from '../Text';
38
+ import {type ElevationType, getElevationValue} from '../Tooltip';
34
39
 
35
40
  import type {ResolveTokenValueProps} from './TokenValueChips';
36
41
  import {TokenValueChips} from './TokenValueChips';
@@ -91,6 +96,7 @@ export type Props<T> = {
91
96
  onMenuClose?: () => mixed,
92
97
  resolveTokenValue?: (ResolveTokenValueProps<T>) => React.Node,
93
98
  inputProps?: InputProps,
99
+ elevation?: ElevationType,
94
100
  };
95
101
 
96
102
  export function TokenListInput<T>(props: Props<T>): React.Node {
@@ -120,11 +126,11 @@ export function TokenListInput<T>(props: Props<T>): React.Node {
120
126
  values,
121
127
  resolveTokenValue,
122
128
  inputProps,
129
+ elevation = 'modal',
123
130
  } = props;
124
-
125
131
  const menuRef = React.useRef<HTMLDivElement | null>(null);
126
132
 
127
- const {x, y, refs, strategy} = useFloating({
133
+ const {x, y, refs, strategy, context} = useFloating({
128
134
  open: true,
129
135
  strategy: STRATEGY_TYPE.absolute,
130
136
  placement: ANCHOR_POSITION_TYPE.bottomStart,
@@ -133,6 +139,7 @@ export function TokenListInput<T>(props: Props<T>): React.Node {
133
139
  });
134
140
 
135
141
  const inputRef = React.useRef<?HTMLInputElement>();
142
+ const dropdownWidth = useReferenceElementWidth(refs.reference?.current);
136
143
 
137
144
  const onOpenToggle = (isOpen) => {
138
145
  if (isOpen) {
@@ -222,8 +229,9 @@ export function TokenListInput<T>(props: Props<T>): React.Node {
222
229
  {({isOpen, onOpen, clickAway, boundaryRef, triggerRef}) => (
223
230
  <>
224
231
  <div
225
- className={classify(css.tokenListContainer, classNames?.wrapper)}
226
232
  ref={menuRef}
233
+ className={classify(css.tokenListContainer, classNames?.wrapper)}
234
+ data-testid="TokenListInput"
227
235
  >
228
236
  <div
229
237
  onClick={() => {
@@ -324,34 +332,52 @@ export function TokenListInput<T>(props: Props<T>): React.Node {
324
332
  </div>
325
333
  )}
326
334
  {!locked && isOpen && menu && (
327
- <div
328
- ref={mergeRefs([refs.setFloating, boundaryRef])}
329
- style={{
330
- position: strategy,
331
- top: y ?? spaceNone,
332
- left: x ?? spaceNone,
333
- width: sizeFluid,
334
- }}
335
- >
336
- {/* $FlowFixMe[incompatible-type] Menu expects MenuOption but receives T */}
337
- {/* $FlowFixMe[prop-missing] MenuOption properties are missing in T */}
338
- <Menu
339
- {...menu}
340
- onSelect={(option) => {
341
- if (values.length >= limit) {
342
- return;
343
- }
344
- // $FlowFixMe[incompatible-call] option from Menu is MenuOption but addValue expects T
345
- // $FlowFixMe[prop-missing] MenuOption properties are missing in T
346
- addValue(option);
347
- clickAway();
348
- inputRef.current?.focus();
349
- }}
350
- size={menu.size || size}
351
- onTabOut={clickAway}
352
- ref={menuRef}
353
- />
354
- </div>
335
+ <FloatingPortal>
336
+ <FloatingFocusManager
337
+ modal={false}
338
+ context={context}
339
+ returnFocus={false}
340
+ initialFocus={refs.reference}
341
+ >
342
+ <div
343
+ className={css.menuWrapper}
344
+ ref={mergeRefs([refs.setFloating, boundaryRef])}
345
+ style={{
346
+ position: strategy,
347
+ top: y ?? spaceNone,
348
+ left: x ?? spaceNone,
349
+ /* NOTE(Sharad): The FloatingPortal renders the menu outside the normal DOM structure,
350
+ so its parent is effectively the <body> element. This means the menu
351
+ would otherwise default to the body's width. To support fluid width,
352
+ we must manually set the dropdown width here; otherwise, it uses a fixed width.
353
+ Also, Only treat menu as non-fluid if isFluid is strictly false, since default is true in menu and undefined means fluid. */ ...(menu.isFluid !==
354
+ false && {
355
+ '--dropdown-width': dropdownWidth,
356
+ }),
357
+ '--menu-elevation': getElevationValue(elevation),
358
+ }}
359
+ >
360
+ {/* $FlowFixMe[incompatible-type] Menu expects MenuOption but receives T */}
361
+ {/* $FlowFixMe[prop-missing] MenuOption properties are missing in T */}
362
+ <Menu
363
+ {...menu}
364
+ onSelect={(option) => {
365
+ if (values.length >= limit) {
366
+ return;
367
+ }
368
+ // $FlowFixMe[incompatible-call] option from Menu is MenuOption but addValue expects T
369
+ // $FlowFixMe[prop-missing] MenuOption properties are missing in T
370
+ addValue(option);
371
+ clickAway();
372
+ inputRef.current?.focus();
373
+ }}
374
+ size={menu.size || size}
375
+ onTabOut={clickAway}
376
+ ref={menuRef}
377
+ />
378
+ </div>
379
+ </FloatingFocusManager>
380
+ </FloatingPortal>
355
381
  )}
356
382
  </div>
357
383
  </>
@@ -150,3 +150,8 @@
150
150
  height: size26;
151
151
  margin-left: auto;
152
152
  }
153
+
154
+ .menuWrapper {
155
+ width: var(--dropdown-width);
156
+ z-index: var(--menu-elevation);
157
+ }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.Tooltip = exports.ELEVATION_TYPES = exports.DELAY_MOTION_DURATION_TYPES = void 0;
6
+ exports.getElevationValue = exports.Tooltip = exports.ELEVATION_TYPES = exports.DELAY_MOTION_DURATION_TYPES = void 0;
7
7
  var React = _interopRequireWildcard(require("react"));
8
8
  var _react2 = require("@floating-ui/react");
9
9
  var ELEVATION = _interopRequireWildcard(require("../../styles/variables/_elevation"));
@@ -39,6 +39,7 @@ const getElevationValue = elevation => {
39
39
  const elevationKey = 'elevation' + (0, _string.capitalize)(elevation);
40
40
  return ELEVATION[elevationKey] || elevation;
41
41
  };
42
+ exports.getElevationValue = getElevationValue;
42
43
  const Tooltip = _ref => {
43
44
  let {
44
45
  classNames,
@@ -84,7 +84,7 @@ export type BaseTooltipProps = {
84
84
  titleMaxLines?: number,
85
85
  delayMotionDuration?: DelayMotionDurationType,
86
86
  hidden?: boolean,
87
- elevation?: string,
87
+ elevation?: ElevationType,
88
88
  };
89
89
 
90
90
  export type TooltipProps = {
@@ -94,7 +94,7 @@ export type TooltipProps = {
94
94
  children: any,
95
95
  };
96
96
 
97
- const getElevationValue = (elevation: string): string => {
97
+ export const getElevationValue = (elevation: string): string => {
98
98
  const elevationKey = 'elevation' + capitalize(elevation);
99
99
  return ELEVATION[elevationKey] || elevation;
100
100
  };
@@ -31,6 +31,7 @@ const SimpleTypeaheadBase = (props, ref) => {
31
31
  showLabelTooltip,
32
32
  allowInternalFilter = true,
33
33
  allowWrap = false,
34
+ elevation = 'modal',
34
35
  ...inputProps
35
36
  } = props;
36
37
  const [typeaheadInputText, setTypeaheadInputText] = React.useState('');
@@ -84,7 +85,8 @@ const SimpleTypeaheadBase = (props, ref) => {
84
85
  showLabelTooltip,
85
86
  allowWrap
86
87
  },
87
- clickAwayRef: clickAwayRef
88
+ clickAwayRef: clickAwayRef,
89
+ elevation: elevation
88
90
  }));
89
91
  };
90
92
  const SimpleTypeahead = exports.SimpleTypeahead = /*#__PURE__*/React.forwardRef(SimpleTypeaheadBase);
@@ -7,6 +7,7 @@ import type {ClickAwayRefType} from '../../utils/click-away';
7
7
  import {getTextLabelFromSelectedKeys} from '../../utils/menu';
8
8
  import type {InputProps} from '../Input';
9
9
  import type {MenuOption, Virtualization} from '../Menu';
10
+ import type {ElevationType} from '../Tooltip';
10
11
 
11
12
  import {Typeahead} from './Typeahead';
12
13
 
@@ -38,6 +39,7 @@ export type SimpleTypeaheadProps = {
38
39
  menuClassNames?: MenuClassNames,
39
40
  showLabelTooltip?: MenuLabelTooltip,
40
41
  allowWrap?: boolean,
42
+ elevation?: ElevationType,
41
43
 
42
44
  // events
43
45
  onSelect?: (option: MenuOption, ?SyntheticEvent<HTMLElement>) => mixed,
@@ -76,6 +78,7 @@ const SimpleTypeaheadBase = (props: SimpleTypeaheadProps, ref) => {
76
78
  showLabelTooltip,
77
79
  allowInternalFilter = true,
78
80
  allowWrap = false,
81
+ elevation = 'modal',
79
82
  ...inputProps
80
83
  } = props;
81
84
 
@@ -148,6 +151,7 @@ const SimpleTypeaheadBase = (props: SimpleTypeaheadProps, ref) => {
148
151
  allowWrap,
149
152
  }}
150
153
  clickAwayRef={clickAwayRef}
154
+ elevation={elevation}
151
155
  />
152
156
  );
153
157
  };
@@ -6,13 +6,14 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.Typeahead = void 0;
7
7
  var React = _interopRequireWildcard(require("react"));
8
8
  var _react2 = require("@floating-ui/react");
9
- var _size = require("../../styles/variables/_size");
9
+ var _hooks = require("../../hooks");
10
10
  var _space = require("../../styles/variables/_space");
11
11
  var _classify = require("../../utils/classify");
12
12
  var _clickAway = require("../../utils/click-away");
13
13
  var _mergeRefs = require("../../utils/merge-refs");
14
14
  var _Menu = require("../Menu");
15
15
  var _SearchInput = require("../SearchInput");
16
+ var _Tooltip = require("../Tooltip");
16
17
  var _TypeaheadModule = _interopRequireDefault(require("./Typeahead.module.css"));
17
18
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
19
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
@@ -33,6 +34,7 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
33
34
  menuOpenOffset = 1,
34
35
  onFocus,
35
36
  clickAwayRef,
37
+ elevation = 'modal',
36
38
  ...inputProps
37
39
  } = _ref;
38
40
  const menuOptions = menu?.options;
@@ -40,7 +42,8 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
40
42
  x,
41
43
  y,
42
44
  refs,
43
- strategy
45
+ strategy,
46
+ context
44
47
  } = (0, _react2.useFloating)({
45
48
  open: true,
46
49
  strategy: 'absolute',
@@ -48,6 +51,7 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
48
51
  whileElementsMounted: _react2.autoUpdate,
49
52
  middleware: [(0, _react2.flip)(), (0, _react2.offset)(parseInt(_space.spaceXXSmall))]
50
53
  });
54
+ const dropdownWidth = (0, _hooks.useReferenceElementWidth)(refs.reference?.current);
51
55
  const onMenuToggle = isOpen => {
52
56
  isOpen ? onMenuOpen && onMenuOpen() : onMenuClose && onMenuClose();
53
57
  };
@@ -64,8 +68,7 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
64
68
  } = _ref2;
65
69
  return /*#__PURE__*/React.createElement("div", {
66
70
  "data-testid": "Typeahead",
67
- className: (0, _classify.classify)(_TypeaheadModule.default.typeaheadContainer, classNames?.wrapper),
68
- ref: boundaryRef
71
+ className: (0, _classify.classify)(_TypeaheadModule.default.typeaheadContainer, classNames?.wrapper)
69
72
  }, /*#__PURE__*/React.createElement(_SearchInput.SearchInput, _extends({}, inputProps, {
70
73
  ref: ref,
71
74
  boxRef: (0, _mergeRefs.mergeRefs)([refs.setReference, triggerRef]),
@@ -96,13 +99,27 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
96
99
  onClear: _e => {
97
100
  onClear?.();
98
101
  }
99
- })), isOpen && !isLoading && menu && menuOptions && !!menuOptions.length && /*#__PURE__*/React.createElement("div", {
100
- ref: refs.setFloating,
102
+ })), isOpen && !isLoading && menu && menuOptions && !!menuOptions.length && /*#__PURE__*/React.createElement(_react2.FloatingPortal, null, /*#__PURE__*/React.createElement(_react2.FloatingFocusManager, {
103
+ modal: false,
104
+ context: context,
105
+ returnFocus: false,
106
+ initialFocus: refs.reference
107
+ }, /*#__PURE__*/React.createElement("div", {
108
+ ref: (0, _mergeRefs.mergeRefs)([refs.setFloating, boundaryRef]),
109
+ className: _TypeaheadModule.default.menuWrapper,
101
110
  style: {
102
111
  position: strategy,
103
112
  top: y ?? _space.spaceNone,
104
113
  left: x ?? _space.spaceNone,
105
- width: _size.sizeFluid
114
+ /* NOTE(Sharad): The FloatingPortal renders the menu outside the normal DOM structure,
115
+ so its parent is effectively the <body> element. This means the menu
116
+ would otherwise default to the body's width. To support fluid width,
117
+ we must manually set the dropdown width here; otherwise, it uses a fixed width.
118
+ Also, Only treat menu as non-fluid if isFluid is strictly false, since default is true in menu and undefined means fluid. */
119
+ ...(menu.isFluid !== false && {
120
+ '--dropdown-width': dropdownWidth
121
+ }),
122
+ '--menu-elevation': (0, _Tooltip.getElevationValue)(elevation)
106
123
  }
107
124
  }, /*#__PURE__*/React.createElement(_Menu.Menu, _extends({}, menu, {
108
125
  options: menuOptions,
@@ -116,7 +133,7 @@ const BaseTypeahead = /*#__PURE__*/React.forwardRef((_ref, ref) => {
116
133
  },
117
134
  size: menu.size || size,
118
135
  onTabOut: clickAway
119
- }))));
136
+ }))))));
120
137
  });
121
138
  });
122
139
  const StatefulTypeahead = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
@@ -7,12 +7,16 @@ import {
7
7
  // $FlowFixMe[untyped-import]
8
8
  flip,
9
9
  // $FlowFixMe[untyped-import]
10
+ FloatingFocusManager,
11
+ // $FlowFixMe[untyped-import]
12
+ FloatingPortal,
13
+ // $FlowFixMe[untyped-import]
10
14
  offset,
11
15
  // $FlowFixMe[untyped-import]
12
16
  useFloating,
13
17
  } from '@floating-ui/react';
14
18
 
15
- import {sizeFluid} from '../../styles/variables/_size';
19
+ import {useReferenceElementWidth} from '../../hooks';
16
20
  import {spaceNone, spaceXXSmall} from '../../styles/variables/_space';
17
21
  import {classify} from '../../utils/classify';
18
22
  import {type ClickAwayRefType, ClickAway} from '../../utils/click-away';
@@ -21,6 +25,8 @@ import type {InputProps} from '../Input';
21
25
  import type {MenuOption, MenuProps} from '../Menu';
22
26
  import {Menu} from '../Menu';
23
27
  import {SearchInput} from '../SearchInput';
28
+ import type {ElevationType} from '../Tooltip';
29
+ import {getElevationValue} from '../Tooltip';
24
30
 
25
31
  import css from './Typeahead.module.css';
26
32
 
@@ -40,6 +46,7 @@ type BaseTypeaheadProps = {
40
46
  isLoading?: boolean,
41
47
  menuOpenOffset?: number,
42
48
  clickAwayRef?: ClickAwayRefType,
49
+ elevation?: ElevationType,
43
50
  ...
44
51
  };
45
52
 
@@ -69,12 +76,14 @@ const BaseTypeahead: React$AbstractComponent<
69
76
  menuOpenOffset = 1,
70
77
  onFocus,
71
78
  clickAwayRef,
79
+ elevation = 'modal',
72
80
  ...inputProps
73
81
  }: BaseTypeaheadProps,
74
82
  ref,
75
83
  ): React.Node => {
76
84
  const menuOptions = menu?.options;
77
- const {x, y, refs, strategy} = useFloating({
85
+
86
+ const {x, y, refs, strategy, context} = useFloating({
78
87
  open: true,
79
88
  strategy: 'absolute',
80
89
  placement: 'bottom-start',
@@ -82,6 +91,8 @@ const BaseTypeahead: React$AbstractComponent<
82
91
  middleware: [flip(), offset(parseInt(spaceXXSmall))],
83
92
  });
84
93
 
94
+ const dropdownWidth = useReferenceElementWidth(refs.reference?.current);
95
+
85
96
  const onMenuToggle = (isOpen: boolean) => {
86
97
  isOpen ? onMenuOpen && onMenuOpen() : onMenuClose && onMenuClose();
87
98
  };
@@ -92,7 +103,6 @@ const BaseTypeahead: React$AbstractComponent<
92
103
  <div
93
104
  data-testid="Typeahead"
94
105
  className={classify(css.typeaheadContainer, classNames?.wrapper)}
95
- ref={boundaryRef}
96
106
  >
97
107
  <SearchInput
98
108
  {...inputProps}
@@ -129,33 +139,51 @@ const BaseTypeahead: React$AbstractComponent<
129
139
  menu &&
130
140
  menuOptions &&
131
141
  !!menuOptions.length && (
132
- <div
133
- ref={refs.setFloating}
134
- style={{
135
- position: strategy,
136
- top: y ?? spaceNone,
137
- left: x ?? spaceNone,
138
- width: sizeFluid,
139
- }}
140
- >
141
- <Menu
142
- {...menu}
143
- options={menuOptions}
144
- onSelect={(option, e) => {
145
- onSelect && onSelect(option, e);
146
- if (
147
- // option.keepMenuOpenOnOptionSelect - to allow the menu persist its open stat upon option selection in normal variant
148
- !option.keepMenuOpenOnOptionSelect &&
149
- (!menu.optionsVariant ||
150
- menu.optionsVariant === 'normal')
151
- ) {
152
- clickAway();
153
- }
154
- }}
155
- size={menu.size || size}
156
- onTabOut={clickAway}
157
- />
158
- </div>
142
+ <FloatingPortal>
143
+ <FloatingFocusManager
144
+ modal={false}
145
+ context={context}
146
+ returnFocus={false}
147
+ initialFocus={refs.reference}
148
+ >
149
+ <div
150
+ ref={mergeRefs([refs.setFloating, boundaryRef])}
151
+ className={css.menuWrapper}
152
+ style={{
153
+ position: strategy,
154
+ top: y ?? spaceNone,
155
+ left: x ?? spaceNone,
156
+ /* NOTE(Sharad): The FloatingPortal renders the menu outside the normal DOM structure,
157
+ so its parent is effectively the <body> element. This means the menu
158
+ would otherwise default to the body's width. To support fluid width,
159
+ we must manually set the dropdown width here; otherwise, it uses a fixed width.
160
+ Also, Only treat menu as non-fluid if isFluid is strictly false, since default is true in menu and undefined means fluid. */
161
+ ...(menu.isFluid !== false && {
162
+ '--dropdown-width': dropdownWidth,
163
+ }),
164
+ '--menu-elevation': getElevationValue(elevation),
165
+ }}
166
+ >
167
+ <Menu
168
+ {...menu}
169
+ options={menuOptions}
170
+ onSelect={(option, e) => {
171
+ onSelect && onSelect(option, e);
172
+ if (
173
+ // option.keepMenuOpenOnOptionSelect - to allow the menu persist its open stat upon option selection in normal variant
174
+ !option.keepMenuOpenOnOptionSelect &&
175
+ (!menu.optionsVariant ||
176
+ menu.optionsVariant === 'normal')
177
+ ) {
178
+ clickAway();
179
+ }
180
+ }}
181
+ size={menu.size || size}
182
+ onTabOut={clickAway}
183
+ />
184
+ </div>
185
+ </FloatingFocusManager>
186
+ </FloatingPortal>
159
187
  )}
160
188
  </div>
161
189
  )}
@@ -9,3 +9,8 @@
9
9
  width: sizeFluid;
10
10
  height: fit-content;
11
11
  }
12
+
13
+ .menuWrapper {
14
+ width: var(--dropdown-width);
15
+ z-index: var(--menu-elevation);
16
+ }
@@ -124,6 +124,17 @@ Object.keys(_usePagination).forEach(function (key) {
124
124
  }
125
125
  });
126
126
  });
127
+ var _useReferenceElementWidth = require("./useReferenceElementWidth");
128
+ Object.keys(_useReferenceElementWidth).forEach(function (key) {
129
+ if (key === "default" || key === "__esModule") return;
130
+ if (key in exports && exports[key] === _useReferenceElementWidth[key]) return;
131
+ Object.defineProperty(exports, key, {
132
+ enumerable: true,
133
+ get: function () {
134
+ return _useReferenceElementWidth[key];
135
+ }
136
+ });
137
+ });
127
138
  var _useResizeObserver = require("./useResizeObserver");
128
139
  Object.keys(_useResizeObserver).forEach(function (key) {
129
140
  if (key === "default" || key === "__esModule") return;
@@ -11,6 +11,7 @@ export * from './useLockedBody';
11
11
  export * from './useModal';
12
12
  export * from './useMountTransition';
13
13
  export * from './usePagination';
14
+ export * from './useReferenceElementWidth';
14
15
  export * from './useResizeObserver';
15
16
  export * from './useToastPortal';
16
17
  export * from './useToggle';
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _useReferenceElementWidth = require("./useReferenceElementWidth");
7
+ Object.keys(_useReferenceElementWidth).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _useReferenceElementWidth[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _useReferenceElementWidth[key];
14
+ }
15
+ });
16
+ });
@@ -0,0 +1,3 @@
1
+ // @flow strict
2
+
3
+ export * from './useReferenceElementWidth';
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useReferenceElementWidth = useReferenceElementWidth;
7
+ var React = _interopRequireWildcard(require("react"));
8
+ var _size = require("../../styles/variables/_size");
9
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
10
+ const MIN_FLUID_WIDTH = 160;
11
+ function useReferenceElementWidth(ref) {
12
+ let minWidth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : MIN_FLUID_WIDTH;
13
+ const [width, setWidth] = React.useState(_size.sizeFluid);
14
+ const refWidth = ref?.offsetWidth;
15
+ React.useLayoutEffect(() => {
16
+ if (refWidth) {
17
+ setWidth(Math.max(refWidth, minWidth) + 'px');
18
+ }
19
+ }, [refWidth, minWidth]);
20
+ return width;
21
+ }
@@ -0,0 +1,23 @@
1
+ // @flow strict
2
+ import * as React from 'react';
3
+
4
+ import {sizeFluid} from '../../styles/variables/_size';
5
+
6
+
7
+ const MIN_FLUID_WIDTH = 160;
8
+
9
+ export function useReferenceElementWidth(
10
+ ref: ?HTMLElement,
11
+ minWidth: number = MIN_FLUID_WIDTH,
12
+ ): string {
13
+ const [width, setWidth] = React.useState(sizeFluid);
14
+
15
+ const refWidth = ref?.offsetWidth;
16
+ React.useLayoutEffect(() => {
17
+ if (refWidth) {
18
+ setWidth(Math.max(refWidth, minWidth) + 'px');
19
+ }
20
+ }, [refWidth, minWidth]);
21
+
22
+ return width;
23
+ }
@@ -12,6 +12,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
12
12
  // TODO(Nishant): Make this a functional component
13
13
  class ClickAway extends React.Component {
14
14
  static defaultProps = {
15
+ containsNestedFloatingPortals: false,
15
16
  closeOnEscapeKeypress: true
16
17
  };
17
18
  state = {
@@ -96,6 +97,9 @@ class ClickAway extends React.Component {
96
97
  }, this.handleOnChange);
97
98
  };
98
99
  handleCloseClick = evt => {
100
+ if (evt.target instanceof Node && this.props.containsNestedFloatingPortals && this.boundaryRef.current?.parentElement && (this.boundaryRef.current.parentElement === evt.target || this.boundaryRef.current.parentElement.contains(evt.target))) {
101
+ return;
102
+ }
99
103
  if (evt.target instanceof Node && this.boundaryRef && (this.boundaryRef === evt.target || this.boundaryRef.current?.contains(evt.target))) {
100
104
  return;
101
105
  }