@spaced-out/ui-design-system 0.3.49 → 0.3.50
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/CHANGELOG.md +7 -0
- package/lib/components/AvatarGroup/AvatarGroup.js.flow +2 -2
- package/lib/components/ButtonDropdown/ButtonDropdown.js +22 -10
- package/lib/components/ButtonDropdown/ButtonDropdown.js.flow +58 -33
- package/lib/components/ButtonDropdown/ButtonDropdown.module.css +8 -1
- package/lib/components/ButtonDropdown/SimpleButtonDropdown.js +3 -0
- package/lib/components/ButtonDropdown/SimpleButtonDropdown.js.flow +6 -1
- package/lib/components/ButtonTabs/ButtonTabDropdown.js +13 -6
- package/lib/components/ButtonTabs/ButtonTabDropdown.js.flow +40 -24
- package/lib/components/ButtonTabs/ButtonTabDropdown.module.css +4 -0
- package/lib/components/ButtonTabs/ButtonTabs.js +2 -0
- package/lib/components/ButtonTabs/ButtonTabs.js.flow +4 -0
- package/lib/components/Dropdown/Dropdown.js +23 -8
- package/lib/components/Dropdown/Dropdown.js.flow +57 -32
- package/lib/components/Dropdown/Dropdown.module.css +5 -0
- package/lib/components/Dropdown/SimpleDropdown.js +2 -0
- package/lib/components/Dropdown/SimpleDropdown.js.flow +4 -0
- package/lib/components/FilterButtonOverlay/FilterButtonOverlay.js +23 -8
- package/lib/components/FilterButtonOverlay/FilterButtonOverlay.js.flow +56 -26
- package/lib/components/InlineDropdown/InlineDropdown.js +25 -8
- package/lib/components/InlineDropdown/InlineDropdown.js.flow +57 -30
- package/lib/components/InlineDropdown/InlineDropdown.module.css +5 -0
- package/lib/components/InlineDropdown/SimpleInlineDropdown.js +2 -0
- package/lib/components/InlineDropdown/SimpleInlineDropdown.js.flow +4 -0
- package/lib/components/Table/StaticTable.js +1 -1
- package/lib/components/Table/StaticTable.js.flow +1 -1
- package/lib/components/Table/Table.docs.js +1 -1
- package/lib/components/Table/Table.docs.js.flow +1 -1
- package/lib/components/Tabs/TabList/TabDropdown.js +14 -7
- package/lib/components/Tabs/TabList/TabDropdown.js.flow +38 -22
- package/lib/components/Tabs/TabList/TabDropdown.module.css +4 -0
- package/lib/components/Tabs/TabList/TabList.js +4 -2
- package/lib/components/Tabs/TabList/TabList.js.flow +4 -0
- package/lib/components/TokenListInput/TokenListInput.js +26 -7
- package/lib/components/TokenListInput/TokenListInput.js.flow +58 -32
- package/lib/components/TokenListInput/TokenListInput.module.css +5 -0
- package/lib/components/Tooltip/Tooltip.js +2 -1
- package/lib/components/Tooltip/Tooltip.js.flow +2 -2
- package/lib/components/Typeahead/SimpleTypeahead.js +3 -1
- package/lib/components/Typeahead/SimpleTypeahead.js.flow +4 -0
- package/lib/components/Typeahead/Typeahead.js +25 -8
- package/lib/components/Typeahead/Typeahead.js.flow +58 -30
- package/lib/components/Typeahead/Typeahead.module.css +5 -0
- package/lib/hooks/index.js +11 -0
- package/lib/hooks/index.js.flow +1 -0
- package/lib/hooks/useReferenceElementWidth/index.js +16 -0
- package/lib/hooks/useReferenceElementWidth/index.js.flow +3 -0
- package/lib/hooks/useReferenceElementWidth/useReferenceElementWidth.js +21 -0
- package/lib/hooks/useReferenceElementWidth/useReferenceElementWidth.js.flow +23 -0
- package/lib/utils/click-away/click-away.js +4 -0
- package/lib/utils/click-away/click-away.js.flow +34 -0
- package/package.json +1 -1
|
@@ -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 {
|
|
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
|
-
<
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
</>
|
|
@@ -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?:
|
|
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
|
|
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(
|
|
100
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
)}
|
package/lib/hooks/index.js
CHANGED
|
@@ -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;
|
package/lib/hooks/index.js.flow
CHANGED
|
@@ -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,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
|
}
|