@spaced-out/ui-design-system 0.1.25 → 0.1.27
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/.cspell/custom-words.txt +3 -0
- package/.storybook/main.js +40 -28
- package/.storybook/manager.js +0 -4
- package/.storybook/preview-head.html +15 -6
- package/.storybook/preview.js +0 -5
- package/CHANGELOG.md +31 -0
- package/babel.config.js +1 -0
- package/lib/components/Badge/Badge.module.css +1 -0
- package/lib/components/ButtonDropdown/ButtonDropdown.js +10 -9
- package/lib/components/ButtonDropdown/ButtonDropdown.js.flow +6 -4
- package/lib/components/ButtonTabs/ButtonTabs.js +4 -1
- package/lib/components/ButtonTabs/ButtonTabs.js.flow +2 -1
- package/lib/components/Chip/Chip.js +1 -1
- package/lib/components/Chip/Chip.js.flow +2 -2
- package/lib/components/CollapsibleCard/CollapsibleCard.js +3 -0
- package/lib/components/CollapsibleCard/CollapsibleCard.js.flow +4 -0
- package/lib/components/Dialog/Dialog.js +23 -2
- package/lib/components/Dialog/Dialog.js.flow +38 -0
- package/lib/components/Dropdown/Dropdown.js +10 -9
- package/lib/components/Dropdown/Dropdown.js.flow +6 -4
- package/lib/components/FocusManager/FocusManager.js +7 -5
- package/lib/components/FocusManager/FocusManager.js.flow +3 -3
- package/lib/components/InlineDropdown/InlineDropdown.js +10 -9
- package/lib/components/InlineDropdown/InlineDropdown.js.flow +6 -4
- package/lib/components/Menu/Menu.js +48 -12
- package/lib/components/Menu/Menu.js.flow +102 -9
- package/lib/components/Menu/Menu.module.css +10 -0
- package/lib/components/Menu/MenuOptionButton.js +21 -4
- package/lib/components/Menu/MenuOptionButton.js.flow +21 -0
- package/lib/components/Modal/Modal.js +35 -8
- package/lib/components/Modal/Modal.js.flow +52 -7
- package/lib/components/Modal/Modal.module.css +1 -3
- package/lib/components/Panel/Panel.js +21 -1
- package/lib/components/Panel/Panel.js.flow +30 -1
- package/lib/components/Panel/Panel.module.css +0 -1
- package/lib/components/Table/DefaultRow.js +5 -5
- package/lib/components/Table/DefaultRow.js.flow +14 -11
- package/lib/components/Table/StaticTable.js +5 -1
- package/lib/components/Table/StaticTable.js.flow +4 -0
- package/lib/components/Table/Table.js.flow +2 -0
- package/lib/components/Tabs/TabList/TabDropdown.js +10 -9
- package/lib/components/Tabs/TabList/TabDropdown.js.flow +6 -4
- package/lib/components/Toast/Toast.js +7 -5
- package/lib/components/Toast/Toast.js.flow +5 -3
- package/lib/components/Toggle/Toggle.js +2 -1
- package/lib/components/Toggle/Toggle.js.flow +2 -1
- package/lib/components/Tooltip/Tooltip.js +22 -25
- package/lib/components/Tooltip/Tooltip.js.flow +25 -22
- package/lib/components/Typeahead/Typeahead.js +10 -9
- package/lib/components/Typeahead/Typeahead.js.flow +6 -4
- package/lib/hooks/index.js +55 -0
- package/lib/hooks/index.js.flow +5 -0
- package/lib/hooks/useCopyToClipboard.js +31 -0
- package/lib/hooks/useCopyToClipboard.js.flow +31 -0
- package/lib/hooks/useInputState.js +23 -0
- package/lib/hooks/useInputState.js.flow +28 -0
- package/lib/hooks/useLockedBody.js +54 -0
- package/lib/hooks/useLockedBody.js.flow +55 -0
- package/lib/hooks/useToggle.js +18 -0
- package/lib/hooks/useToggle.js.flow +17 -0
- package/lib/hooks/useWindowSize.js +32 -0
- package/lib/hooks/useWindowSize.js.flow +37 -0
- package/lib/styles/typography.module.css +1 -0
- package/lib/types/common.js +0 -1
- package/lib/utils/index.js +11 -0
- package/lib/utils/index.js.flow +1 -0
- package/lib/utils/menu.js +57 -2
- package/lib/utils/menu.js.flow +109 -1
- package/lib/utils/string.js +4 -2
- package/lib/utils/string.js.flow +3 -0
- package/lib/utils/tokens.js +74 -0
- package/lib/utils/tokens.js.flow +82 -0
- package/package.json +18 -16
- package/.storybook/public/favicon.svg +0 -6
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
FloatingFocusManager,
|
|
7
7
|
// $FlowFixMe[untyped-import]
|
|
8
8
|
useFloating,
|
|
9
|
-
} from '@floating-ui/react
|
|
9
|
+
} from '@floating-ui/react';
|
|
10
10
|
|
|
11
11
|
import classify from '../../utils/classify';
|
|
12
12
|
|
|
@@ -24,7 +24,7 @@ export type FocusManagerProps = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
export const FocusManager = (props: FocusManagerProps): React.Node => {
|
|
27
|
-
const {
|
|
27
|
+
const {refs, context} = useFloating({open: true});
|
|
28
28
|
const {
|
|
29
29
|
classNames,
|
|
30
30
|
children,
|
|
@@ -39,7 +39,7 @@ export const FocusManager = (props: FocusManagerProps): React.Node => {
|
|
|
39
39
|
{...restFloatingFocusManagerProps}
|
|
40
40
|
>
|
|
41
41
|
<div
|
|
42
|
-
ref={
|
|
42
|
+
ref={refs.setFloating}
|
|
43
43
|
data-testid="FocusManager"
|
|
44
44
|
className={classify(css.wrapper, classNames?.wrapper)}
|
|
45
45
|
>
|
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.InlineDropdown = void 0;
|
|
7
7
|
var React = _interopRequireWildcard(require("react"));
|
|
8
|
-
var
|
|
8
|
+
var _react2 = require("@floating-ui/react");
|
|
9
9
|
var _space = require("../../styles/variables/_space");
|
|
10
10
|
var _classify = require("../../utils/classify");
|
|
11
11
|
var _clickAway = require("../../utils/click-away");
|
|
@@ -36,14 +36,14 @@ const InlineDropdown = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
36
36
|
const {
|
|
37
37
|
x,
|
|
38
38
|
y,
|
|
39
|
-
|
|
40
|
-
floating,
|
|
39
|
+
refs,
|
|
41
40
|
strategy
|
|
42
|
-
} = (0,
|
|
41
|
+
} = (0, _react2.useFloating)({
|
|
42
|
+
open: true,
|
|
43
43
|
strategy: 'absolute',
|
|
44
44
|
placement: anchorPosition,
|
|
45
|
-
whileElementsMounted:
|
|
46
|
-
middleware: [(0,
|
|
45
|
+
whileElementsMounted: _react2.autoUpdate,
|
|
46
|
+
middleware: [(0, _react2.shift)(), (0, _react2.flip)(), (0, _react2.offset)(parseInt(_space.spaceXXSmall))]
|
|
47
47
|
});
|
|
48
48
|
const onMenuToggle = isOpen => {
|
|
49
49
|
isOpen ? onMenuOpen && onMenuOpen() : onMenuClose && onMenuClose();
|
|
@@ -63,7 +63,7 @@ const InlineDropdown = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
63
63
|
ref: menuBtnRef
|
|
64
64
|
}, /*#__PURE__*/React.createElement(_Button.UnstyledButton, _extends({}, restButtonProps, {
|
|
65
65
|
disabled: disabled,
|
|
66
|
-
ref:
|
|
66
|
+
ref: refs.setReference,
|
|
67
67
|
onClick: e => {
|
|
68
68
|
e.stopPropagation();
|
|
69
69
|
onOpen();
|
|
@@ -80,7 +80,7 @@ const InlineDropdown = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
80
80
|
})
|
|
81
81
|
})), isOpen && menu && /*#__PURE__*/React.createElement("div", {
|
|
82
82
|
onClickCapture: cancelNext,
|
|
83
|
-
ref:
|
|
83
|
+
ref: refs.setFloating,
|
|
84
84
|
style: {
|
|
85
85
|
display: 'flex',
|
|
86
86
|
position: strategy,
|
|
@@ -94,7 +94,8 @@ const InlineDropdown = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
94
94
|
clickAway();
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
|
-
size: menu.size || 'medium'
|
|
97
|
+
size: menu.size || 'medium',
|
|
98
|
+
onTabOut: clickAway
|
|
98
99
|
}))));
|
|
99
100
|
});
|
|
100
101
|
});
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
shift,
|
|
13
13
|
// $FlowFixMe[untyped-import]
|
|
14
14
|
useFloating,
|
|
15
|
-
} from '@floating-ui/react
|
|
15
|
+
} from '@floating-ui/react';
|
|
16
16
|
|
|
17
17
|
import {spaceNone, spaceXXSmall} from '../../styles/variables/_space';
|
|
18
18
|
import {classify} from '../../utils/classify';
|
|
@@ -66,7 +66,8 @@ export const InlineDropdown: React$AbstractComponent<
|
|
|
66
66
|
): React.Node => {
|
|
67
67
|
const menuBtnRef = React.useRef(null);
|
|
68
68
|
React.useImperativeHandle(ref, () => menuBtnRef.current);
|
|
69
|
-
const {x, y,
|
|
69
|
+
const {x, y, refs, strategy} = useFloating({
|
|
70
|
+
open: true,
|
|
70
71
|
strategy: 'absolute',
|
|
71
72
|
placement: anchorPosition,
|
|
72
73
|
whileElementsMounted: autoUpdate,
|
|
@@ -91,7 +92,7 @@ export const InlineDropdown: React$AbstractComponent<
|
|
|
91
92
|
<UnstyledButton
|
|
92
93
|
{...restButtonProps}
|
|
93
94
|
disabled={disabled}
|
|
94
|
-
ref={
|
|
95
|
+
ref={refs.setReference}
|
|
95
96
|
onClick={(e) => {
|
|
96
97
|
e.stopPropagation();
|
|
97
98
|
onOpen();
|
|
@@ -118,7 +119,7 @@ export const InlineDropdown: React$AbstractComponent<
|
|
|
118
119
|
{isOpen && menu && (
|
|
119
120
|
<div
|
|
120
121
|
onClickCapture={cancelNext}
|
|
121
|
-
ref={
|
|
122
|
+
ref={refs.setFloating}
|
|
122
123
|
style={{
|
|
123
124
|
display: 'flex',
|
|
124
125
|
position: strategy,
|
|
@@ -138,6 +139,7 @@ export const InlineDropdown: React$AbstractComponent<
|
|
|
138
139
|
}
|
|
139
140
|
}}
|
|
140
141
|
size={menu.size || 'medium'}
|
|
142
|
+
onTabOut={clickAway}
|
|
141
143
|
/>
|
|
142
144
|
</div>
|
|
143
145
|
)}
|
|
@@ -6,6 +6,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.Menu = void 0;
|
|
7
7
|
var React = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _classify = require("../../utils/classify");
|
|
9
|
+
var _menu = require("../../utils/menu");
|
|
10
|
+
var _SearchInput = require("../SearchInput");
|
|
11
|
+
var _Text = require("../Text");
|
|
9
12
|
var _MenuOptionButton = require("./MenuOptionButton");
|
|
10
13
|
var _MenuModule = _interopRequireDefault(require("./Menu.module.css"));
|
|
11
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -18,44 +21,69 @@ const RenderOption = _ref => {
|
|
|
18
21
|
composeOptions,
|
|
19
22
|
groupTitleOptions,
|
|
20
23
|
classNames,
|
|
24
|
+
searchText = '',
|
|
21
25
|
...restProps
|
|
22
26
|
} = _ref;
|
|
27
|
+
const {
|
|
28
|
+
allowSearch
|
|
29
|
+
} = restProps;
|
|
23
30
|
if (options && Array.isArray(options) && options.length) {
|
|
24
|
-
|
|
31
|
+
const optionsFiltered = !allowSearch ? options : (0, _menu.getFilteredOptionsFromSearchText)(options, searchText);
|
|
32
|
+
const resultText = !allowSearch ? '' : (0, _menu.getFilteredOptionsResultText)(optionsFiltered);
|
|
33
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, allowSearch && /*#__PURE__*/React.createElement(_Text.FormLabelSmall, {
|
|
34
|
+
className: _MenuModule.default.filterOptionsResultText,
|
|
35
|
+
color: "tertiary"
|
|
36
|
+
}, resultText), optionsFiltered.map((option, idx) => /*#__PURE__*/React.createElement(React.Fragment, {
|
|
25
37
|
key: option.key
|
|
26
38
|
}, /*#__PURE__*/React.createElement(_MenuOptionButton.MenuOptionButton, _extends({
|
|
27
39
|
option: option,
|
|
28
40
|
classNames: classNames
|
|
29
|
-
}, restProps
|
|
41
|
+
}, restProps, {
|
|
42
|
+
isLastItem: idx === optionsFiltered.length - 1
|
|
43
|
+
})))));
|
|
30
44
|
}
|
|
31
45
|
if (composeOptions && Array.isArray(composeOptions) && composeOptions.length) {
|
|
32
|
-
|
|
46
|
+
const optionsFiltered = !allowSearch ? composeOptions : (0, _menu.getFilteredComposeOptionsFromSearchText)(composeOptions, searchText);
|
|
47
|
+
const resultText = !allowSearch ? '' : (0, _menu.getFilteredComposeOptionsResultText)(optionsFiltered);
|
|
48
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, allowSearch && /*#__PURE__*/React.createElement(_Text.FormLabelSmall, {
|
|
49
|
+
className: _MenuModule.default.filterOptionsResultText,
|
|
50
|
+
color: "tertiary"
|
|
51
|
+
}, resultText), optionsFiltered.map((composeMenuOptions, index) =>
|
|
33
52
|
/*#__PURE__*/
|
|
34
53
|
// eslint-disable-next-line react/no-array-index-key
|
|
35
54
|
React.createElement("span", {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}, composeMenuOptions.map(option => /*#__PURE__*/React.createElement(React.Fragment, {
|
|
55
|
+
key: index,
|
|
56
|
+
className: _MenuModule.default.menuDivider
|
|
57
|
+
}, composeMenuOptions.map((option, idx) => /*#__PURE__*/React.createElement(React.Fragment, {
|
|
39
58
|
key: option.key
|
|
40
59
|
}, /*#__PURE__*/React.createElement(_MenuOptionButton.MenuOptionButton, _extends({
|
|
41
60
|
option: option,
|
|
42
61
|
classNames: classNames
|
|
43
|
-
}, restProps
|
|
62
|
+
}, restProps, {
|
|
63
|
+
isLastItem: index === optionsFiltered.length - 1 && idx === composeMenuOptions.length - 1
|
|
64
|
+
})))))));
|
|
44
65
|
}
|
|
45
66
|
if (groupTitleOptions && Array.isArray(groupTitleOptions) && groupTitleOptions.length) {
|
|
46
|
-
|
|
67
|
+
const optionsFiltered = !allowSearch ? groupTitleOptions : (0, _menu.getFilteredGroupTitleOptionsFromSearchText)(groupTitleOptions, searchText);
|
|
68
|
+
const resultText = !allowSearch ? '' : (0, _menu.getFilteredGroupTitleOptionsResultText)(optionsFiltered);
|
|
69
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, allowSearch && /*#__PURE__*/React.createElement(_Text.FormLabelSmall, {
|
|
70
|
+
className: _MenuModule.default.filterOptionsResultText,
|
|
71
|
+
color: "tertiary"
|
|
72
|
+
}, resultText), optionsFiltered.map((optionsGroup, index) =>
|
|
47
73
|
/*#__PURE__*/
|
|
48
74
|
// eslint-disable-next-line react/no-array-index-key
|
|
49
75
|
React.createElement(React.Fragment, {
|
|
50
76
|
key: index
|
|
51
77
|
}, !!optionsGroup.groupTitle && /*#__PURE__*/React.createElement("div", {
|
|
52
78
|
className: (0, _classify.classify)(_MenuModule.default.groupTitleWrapper, classNames?.groupTitle)
|
|
53
|
-
}, optionsGroup.groupTitle), optionsGroup.options?.map(option => /*#__PURE__*/React.createElement(React.Fragment, {
|
|
79
|
+
}, optionsGroup.groupTitle), optionsGroup.options?.map((option, idx) => /*#__PURE__*/React.createElement(React.Fragment, {
|
|
54
80
|
key: option.key
|
|
55
81
|
}, /*#__PURE__*/React.createElement(_MenuOptionButton.MenuOptionButton, _extends({
|
|
56
82
|
option: option,
|
|
57
83
|
classNames: classNames
|
|
58
|
-
}, restProps
|
|
84
|
+
}, restProps, {
|
|
85
|
+
isLastItem: index === optionsFiltered.length - 1 && idx === (optionsGroup.options && optionsGroup.options.length - 1)
|
|
86
|
+
})))))));
|
|
59
87
|
}
|
|
60
88
|
return /*#__PURE__*/React.createElement(React.Fragment, null);
|
|
61
89
|
};
|
|
@@ -64,8 +92,10 @@ const Menu = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
64
92
|
classNames,
|
|
65
93
|
size = 'medium',
|
|
66
94
|
width,
|
|
67
|
-
isFluid = true
|
|
95
|
+
isFluid = true,
|
|
96
|
+
allowSearch
|
|
68
97
|
} = props;
|
|
98
|
+
const [searchText, setSearchText] = React.useState('');
|
|
69
99
|
return /*#__PURE__*/React.createElement("div", {
|
|
70
100
|
className: (0, _classify.classify)(_MenuModule.default.menuCard, {
|
|
71
101
|
[_MenuModule.default.fluid]: isFluid,
|
|
@@ -76,6 +106,12 @@ const Menu = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
76
106
|
width
|
|
77
107
|
},
|
|
78
108
|
ref: ref
|
|
79
|
-
}, /*#__PURE__*/React.createElement(
|
|
109
|
+
}, allowSearch && /*#__PURE__*/React.createElement(_SearchInput.SearchInput, {
|
|
110
|
+
value: searchText,
|
|
111
|
+
onChange: e => setSearchText(e.target.value),
|
|
112
|
+
onClear: () => setSearchText('')
|
|
113
|
+
}), /*#__PURE__*/React.createElement(RenderOption, _extends({}, props, {
|
|
114
|
+
searchText: searchText
|
|
115
|
+
})));
|
|
80
116
|
});
|
|
81
117
|
exports.Menu = Menu;
|
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
4
|
import {classify} from '../../utils/classify';
|
|
5
|
+
import {
|
|
6
|
+
getFilteredComposeOptionsFromSearchText,
|
|
7
|
+
getFilteredComposeOptionsResultText,
|
|
8
|
+
getFilteredGroupTitleOptionsFromSearchText,
|
|
9
|
+
getFilteredGroupTitleOptionsResultText,
|
|
10
|
+
getFilteredOptionsFromSearchText,
|
|
11
|
+
getFilteredOptionsResultText,
|
|
12
|
+
} from '../../utils/menu';
|
|
5
13
|
import type {IconType} from '../Icon/Icon';
|
|
14
|
+
import {SearchInput} from '../SearchInput';
|
|
15
|
+
import {FormLabelSmall} from '../Text';
|
|
6
16
|
|
|
7
17
|
import {MenuOptionButton} from './MenuOptionButton';
|
|
8
18
|
|
|
@@ -43,6 +53,10 @@ export type BaseMenuProps = {
|
|
|
43
53
|
width?: string,
|
|
44
54
|
menuDisabled?: boolean,
|
|
45
55
|
isFluid?: boolean,
|
|
56
|
+
// onTabOut is a callback function that is called when
|
|
57
|
+
// the user navigates outside of the menu using the tab key.
|
|
58
|
+
onTabOut?: () => mixed,
|
|
59
|
+
allowSearch?: boolean,
|
|
46
60
|
};
|
|
47
61
|
|
|
48
62
|
export type MenuOptionTypes = {
|
|
@@ -65,22 +79,45 @@ export type MenuProps = {
|
|
|
65
79
|
...MenuOptionTypes,
|
|
66
80
|
};
|
|
67
81
|
|
|
82
|
+
export type RenderOptionProps = {
|
|
83
|
+
...MenuProps,
|
|
84
|
+
searchText?: string,
|
|
85
|
+
};
|
|
86
|
+
|
|
68
87
|
const RenderOption = ({
|
|
69
88
|
options,
|
|
70
89
|
composeOptions,
|
|
71
90
|
groupTitleOptions,
|
|
72
91
|
classNames,
|
|
92
|
+
searchText = '',
|
|
73
93
|
...restProps
|
|
74
|
-
}:
|
|
94
|
+
}: RenderOptionProps): React.Node => {
|
|
95
|
+
const {allowSearch} = restProps;
|
|
75
96
|
if (options && Array.isArray(options) && options.length) {
|
|
97
|
+
const optionsFiltered = !allowSearch
|
|
98
|
+
? options
|
|
99
|
+
: getFilteredOptionsFromSearchText(options, searchText);
|
|
100
|
+
const resultText = !allowSearch
|
|
101
|
+
? ''
|
|
102
|
+
: getFilteredOptionsResultText(optionsFiltered);
|
|
103
|
+
|
|
76
104
|
return (
|
|
77
105
|
<>
|
|
78
|
-
{
|
|
106
|
+
{allowSearch && (
|
|
107
|
+
<FormLabelSmall
|
|
108
|
+
className={css.filterOptionsResultText}
|
|
109
|
+
color="tertiary"
|
|
110
|
+
>
|
|
111
|
+
{resultText}
|
|
112
|
+
</FormLabelSmall>
|
|
113
|
+
)}
|
|
114
|
+
{optionsFiltered.map((option, idx) => (
|
|
79
115
|
<React.Fragment key={option.key}>
|
|
80
116
|
<MenuOptionButton
|
|
81
117
|
option={option}
|
|
82
118
|
classNames={classNames}
|
|
83
119
|
{...restProps}
|
|
120
|
+
isLastItem={idx === optionsFiltered.length - 1}
|
|
84
121
|
/>
|
|
85
122
|
</React.Fragment>
|
|
86
123
|
))}
|
|
@@ -92,17 +129,36 @@ const RenderOption = ({
|
|
|
92
129
|
Array.isArray(composeOptions) &&
|
|
93
130
|
composeOptions.length
|
|
94
131
|
) {
|
|
132
|
+
const optionsFiltered = !allowSearch
|
|
133
|
+
? composeOptions
|
|
134
|
+
: getFilteredComposeOptionsFromSearchText(composeOptions, searchText);
|
|
135
|
+
const resultText = !allowSearch
|
|
136
|
+
? ''
|
|
137
|
+
: getFilteredComposeOptionsResultText(optionsFiltered);
|
|
138
|
+
|
|
95
139
|
return (
|
|
96
140
|
<>
|
|
97
|
-
{
|
|
141
|
+
{allowSearch && (
|
|
142
|
+
<FormLabelSmall
|
|
143
|
+
className={css.filterOptionsResultText}
|
|
144
|
+
color="tertiary"
|
|
145
|
+
>
|
|
146
|
+
{resultText}
|
|
147
|
+
</FormLabelSmall>
|
|
148
|
+
)}
|
|
149
|
+
{optionsFiltered.map((composeMenuOptions, index) => (
|
|
98
150
|
// eslint-disable-next-line react/no-array-index-key
|
|
99
|
-
<span className={css.menuDivider}
|
|
100
|
-
{composeMenuOptions.map((option) => (
|
|
151
|
+
<span key={index} className={css.menuDivider}>
|
|
152
|
+
{composeMenuOptions.map((option, idx) => (
|
|
101
153
|
<React.Fragment key={option.key}>
|
|
102
154
|
<MenuOptionButton
|
|
103
155
|
option={option}
|
|
104
156
|
classNames={classNames}
|
|
105
157
|
{...restProps}
|
|
158
|
+
isLastItem={
|
|
159
|
+
index === optionsFiltered.length - 1 &&
|
|
160
|
+
idx === composeMenuOptions.length - 1
|
|
161
|
+
}
|
|
106
162
|
/>
|
|
107
163
|
</React.Fragment>
|
|
108
164
|
))}
|
|
@@ -116,9 +172,27 @@ const RenderOption = ({
|
|
|
116
172
|
Array.isArray(groupTitleOptions) &&
|
|
117
173
|
groupTitleOptions.length
|
|
118
174
|
) {
|
|
175
|
+
const optionsFiltered = !allowSearch
|
|
176
|
+
? groupTitleOptions
|
|
177
|
+
: getFilteredGroupTitleOptionsFromSearchText(
|
|
178
|
+
groupTitleOptions,
|
|
179
|
+
searchText,
|
|
180
|
+
);
|
|
181
|
+
const resultText = !allowSearch
|
|
182
|
+
? ''
|
|
183
|
+
: getFilteredGroupTitleOptionsResultText(optionsFiltered);
|
|
184
|
+
|
|
119
185
|
return (
|
|
120
186
|
<>
|
|
121
|
-
{
|
|
187
|
+
{allowSearch && (
|
|
188
|
+
<FormLabelSmall
|
|
189
|
+
className={css.filterOptionsResultText}
|
|
190
|
+
color="tertiary"
|
|
191
|
+
>
|
|
192
|
+
{resultText}
|
|
193
|
+
</FormLabelSmall>
|
|
194
|
+
)}
|
|
195
|
+
{optionsFiltered.map((optionsGroup, index) => (
|
|
122
196
|
// eslint-disable-next-line react/no-array-index-key
|
|
123
197
|
<React.Fragment key={index}>
|
|
124
198
|
{!!optionsGroup.groupTitle && (
|
|
@@ -132,12 +206,17 @@ const RenderOption = ({
|
|
|
132
206
|
</div>
|
|
133
207
|
)}
|
|
134
208
|
|
|
135
|
-
{optionsGroup.options?.map((option) => (
|
|
209
|
+
{optionsGroup.options?.map((option, idx) => (
|
|
136
210
|
<React.Fragment key={option.key}>
|
|
137
211
|
<MenuOptionButton
|
|
138
212
|
option={option}
|
|
139
213
|
classNames={classNames}
|
|
140
214
|
{...restProps}
|
|
215
|
+
isLastItem={
|
|
216
|
+
index === optionsFiltered.length - 1 &&
|
|
217
|
+
idx ===
|
|
218
|
+
(optionsGroup.options && optionsGroup.options.length - 1)
|
|
219
|
+
}
|
|
141
220
|
/>
|
|
142
221
|
</React.Fragment>
|
|
143
222
|
))}
|
|
@@ -152,7 +231,14 @@ const RenderOption = ({
|
|
|
152
231
|
export const Menu: React$AbstractComponent<MenuProps, HTMLDivElement> =
|
|
153
232
|
React.forwardRef<MenuProps, HTMLDivElement>(
|
|
154
233
|
(props: MenuProps, ref): React.Node => {
|
|
155
|
-
const {
|
|
234
|
+
const {
|
|
235
|
+
classNames,
|
|
236
|
+
size = 'medium',
|
|
237
|
+
width,
|
|
238
|
+
isFluid = true,
|
|
239
|
+
allowSearch,
|
|
240
|
+
} = props;
|
|
241
|
+
const [searchText, setSearchText] = React.useState('');
|
|
156
242
|
|
|
157
243
|
return (
|
|
158
244
|
<div
|
|
@@ -168,7 +254,14 @@ export const Menu: React$AbstractComponent<MenuProps, HTMLDivElement> =
|
|
|
168
254
|
style={{width}}
|
|
169
255
|
ref={ref}
|
|
170
256
|
>
|
|
171
|
-
|
|
257
|
+
{allowSearch && (
|
|
258
|
+
<SearchInput
|
|
259
|
+
value={searchText}
|
|
260
|
+
onChange={(e) => setSearchText(e.target.value)}
|
|
261
|
+
onClear={() => setSearchText('')}
|
|
262
|
+
/>
|
|
263
|
+
)}
|
|
264
|
+
<RenderOption {...props} searchText={searchText} />
|
|
172
265
|
</div>
|
|
173
266
|
);
|
|
174
267
|
},
|
|
@@ -191,6 +191,10 @@
|
|
|
191
191
|
padding-bottom: spaceXSmall;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
.hideDivider {
|
|
195
|
+
border-top: none;
|
|
196
|
+
}
|
|
197
|
+
|
|
194
198
|
.menuDivider:first-child {
|
|
195
199
|
padding-top: spaceNone;
|
|
196
200
|
}
|
|
@@ -213,3 +217,9 @@
|
|
|
213
217
|
color: colorTextTertiary;
|
|
214
218
|
padding-top: spaceSmall;
|
|
215
219
|
}
|
|
220
|
+
|
|
221
|
+
.filterOptionsResultText {
|
|
222
|
+
margin-top: calc(spaceXSmall * 2);
|
|
223
|
+
margin-bottom: spaceXSmall;
|
|
224
|
+
margin-left: spaceSmall;
|
|
225
|
+
}
|
|
@@ -15,8 +15,9 @@ var _MenuModule = _interopRequireDefault(require("./Menu.module.css"));
|
|
|
15
15
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
16
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
17
17
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
18
|
-
|
|
18
|
+
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
19
19
|
const MenuOptionButton = props => {
|
|
20
|
+
const lastMenuItemRef = React.useRef(null);
|
|
20
21
|
const {
|
|
21
22
|
option,
|
|
22
23
|
size,
|
|
@@ -25,7 +26,9 @@ const MenuOptionButton = props => {
|
|
|
25
26
|
menuDisabled,
|
|
26
27
|
classNames,
|
|
27
28
|
optionsVariant = 'normal',
|
|
28
|
-
selectedKeys
|
|
29
|
+
selectedKeys,
|
|
30
|
+
isLastItem,
|
|
31
|
+
onTabOut
|
|
29
32
|
} = props;
|
|
30
33
|
const {
|
|
31
34
|
key,
|
|
@@ -50,7 +53,19 @@ const MenuOptionButton = props => {
|
|
|
50
53
|
React.useEffect(() => {
|
|
51
54
|
setButtonSize(optionSize || size);
|
|
52
55
|
}, [optionSize, size]);
|
|
53
|
-
|
|
56
|
+
React.useEffect(() => {
|
|
57
|
+
const handleKeyDown = event => {
|
|
58
|
+
if (event.key === 'Tab' && !event.shiftKey) {
|
|
59
|
+
// Tab pressed without shift key, calling tab out callback
|
|
60
|
+
onTabOut?.();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
lastMenuItemRef.current?.addEventListener('keydown', handleKeyDown);
|
|
64
|
+
return () => {
|
|
65
|
+
lastMenuItemRef.current?.removeEventListener('keydown', handleKeyDown);
|
|
66
|
+
};
|
|
67
|
+
}, [isLastItem]);
|
|
68
|
+
return /*#__PURE__*/React.createElement(_Button.UnstyledButton, _extends({
|
|
54
69
|
className: (0, _classify.classify)(_MenuModule.default.option, {
|
|
55
70
|
[_MenuModule.default.selected]: isSelected() || key === selectedOption?.key,
|
|
56
71
|
[_MenuModule.default.optionSmall]: buttonSize === 'small',
|
|
@@ -62,7 +77,9 @@ const MenuOptionButton = props => {
|
|
|
62
77
|
disabled: menuDisabled || disabled,
|
|
63
78
|
onClick: () => onSelect && onSelect(option),
|
|
64
79
|
autoFocus: selectedOption?.key === key
|
|
65
|
-
},
|
|
80
|
+
}, isLastItem ? {
|
|
81
|
+
ref: lastMenuItemRef
|
|
82
|
+
} : {}), optionVariant === 'checkbox' && /*#__PURE__*/React.createElement(_Checkbox.Checkbox, {
|
|
66
83
|
tabIndex: -1,
|
|
67
84
|
disabled: menuDisabled || disabled,
|
|
68
85
|
checked: isSelected()
|
|
@@ -16,9 +16,12 @@ import css from './Menu.module.css';
|
|
|
16
16
|
export type MenuOptionProps = {
|
|
17
17
|
...BaseMenuProps,
|
|
18
18
|
option: MenuOption,
|
|
19
|
+
isLastItem?: boolean,
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
export const MenuOptionButton = (props: MenuOptionProps): React.Node => {
|
|
23
|
+
const lastMenuItemRef: {current: HTMLButtonElement | null} =
|
|
24
|
+
React.useRef(null);
|
|
22
25
|
const {
|
|
23
26
|
option,
|
|
24
27
|
size,
|
|
@@ -28,6 +31,8 @@ export const MenuOptionButton = (props: MenuOptionProps): React.Node => {
|
|
|
28
31
|
classNames,
|
|
29
32
|
optionsVariant = 'normal',
|
|
30
33
|
selectedKeys,
|
|
34
|
+
isLastItem,
|
|
35
|
+
onTabOut,
|
|
31
36
|
} = props;
|
|
32
37
|
const {
|
|
33
38
|
key,
|
|
@@ -55,6 +60,21 @@ export const MenuOptionButton = (props: MenuOptionProps): React.Node => {
|
|
|
55
60
|
setButtonSize(optionSize || size);
|
|
56
61
|
}, [optionSize, size]);
|
|
57
62
|
|
|
63
|
+
React.useEffect(() => {
|
|
64
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
65
|
+
if (event.key === 'Tab' && !event.shiftKey) {
|
|
66
|
+
// Tab pressed without shift key, calling tab out callback
|
|
67
|
+
onTabOut?.();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
lastMenuItemRef.current?.addEventListener('keydown', handleKeyDown);
|
|
72
|
+
|
|
73
|
+
return () => {
|
|
74
|
+
lastMenuItemRef.current?.removeEventListener('keydown', handleKeyDown);
|
|
75
|
+
};
|
|
76
|
+
}, [isLastItem]);
|
|
77
|
+
|
|
58
78
|
return (
|
|
59
79
|
<UnstyledButton
|
|
60
80
|
className={classify(
|
|
@@ -72,6 +92,7 @@ export const MenuOptionButton = (props: MenuOptionProps): React.Node => {
|
|
|
72
92
|
disabled={menuDisabled || disabled}
|
|
73
93
|
onClick={() => onSelect && onSelect(option)}
|
|
74
94
|
autoFocus={selectedOption?.key === key}
|
|
95
|
+
{...(isLastItem ? {ref: lastMenuItemRef} : {})}
|
|
75
96
|
>
|
|
76
97
|
{optionVariant === 'checkbox' && (
|
|
77
98
|
<Checkbox
|
|
@@ -6,9 +6,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.ModalHeader = exports.ModalFooter = exports.ModalBody = exports.Modal = void 0;
|
|
7
7
|
var React = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactDom = require("react-dom");
|
|
9
|
-
var
|
|
9
|
+
var _react2 = require("@floating-ui/react");
|
|
10
10
|
var _useMountTransition = _interopRequireDefault(require("../../hooks/useMountTransition"));
|
|
11
11
|
var _motion = require("../../styles/variables/_motion");
|
|
12
|
+
var _space = require("../../styles/variables/_space");
|
|
12
13
|
var _classify = _interopRequireDefault(require("../../utils/classify"));
|
|
13
14
|
var _helpers = require("../../utils/helpers");
|
|
14
15
|
var _Button = require("../Button/Button");
|
|
@@ -20,6 +21,21 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
20
21
|
|
|
21
22
|
// $FlowFixMe[untyped-import]
|
|
22
23
|
|
|
24
|
+
const DEFAULT_MODAL_ANIMATION = {
|
|
25
|
+
duration: {
|
|
26
|
+
open: parseInt(_motion.motionDurationSlow),
|
|
27
|
+
close: parseInt(_motion.motionDurationNormal)
|
|
28
|
+
},
|
|
29
|
+
initial: {
|
|
30
|
+
transform: `translate(${_space.spaceNegHalfFluid}, ${_space.spaceNegHalfFluid}) scale(0.95)`
|
|
31
|
+
},
|
|
32
|
+
open: {
|
|
33
|
+
transform: `translate(${_space.spaceNegHalfFluid}, ${_space.spaceNegHalfFluid}) scale(1)`
|
|
34
|
+
},
|
|
35
|
+
close: {
|
|
36
|
+
transform: `translate(${_space.spaceNegHalfFluid}, ${_space.spaceNegHalfFluid}) scale(0.95)`
|
|
37
|
+
}
|
|
38
|
+
};
|
|
23
39
|
const ModalHeader = _ref => {
|
|
24
40
|
let {
|
|
25
41
|
children,
|
|
@@ -103,12 +119,19 @@ const Modal = _ref4 => {
|
|
|
103
119
|
onClose,
|
|
104
120
|
hideBackdrop = false,
|
|
105
121
|
tapOutsideToClose = true,
|
|
106
|
-
initialFocus = -1
|
|
122
|
+
initialFocus = -1,
|
|
123
|
+
customAnimation
|
|
107
124
|
} = _ref4;
|
|
108
125
|
const {
|
|
109
|
-
|
|
126
|
+
refs,
|
|
110
127
|
context
|
|
111
|
-
} = (0,
|
|
128
|
+
} = (0, _react2.useFloating)({
|
|
129
|
+
open: isOpen
|
|
130
|
+
});
|
|
131
|
+
const {
|
|
132
|
+
isMounted,
|
|
133
|
+
styles
|
|
134
|
+
} = (0, _react2.useTransitionStyles)(context, customAnimation || DEFAULT_MODAL_ANIMATION);
|
|
112
135
|
const modalId = (0, _helpers.uuid)();
|
|
113
136
|
const bodyRef = React.useRef(document.querySelector('body'));
|
|
114
137
|
const portalRootRef = React.useRef(getModalRoot(modalId) || createPortalRoot(modalId));
|
|
@@ -174,11 +197,11 @@ const Modal = _ref4 => {
|
|
|
174
197
|
onClose(e);
|
|
175
198
|
}
|
|
176
199
|
};
|
|
177
|
-
return /*#__PURE__*/(0, _reactDom.createPortal)( /*#__PURE__*/React.createElement(
|
|
200
|
+
return /*#__PURE__*/(0, _reactDom.createPortal)( /*#__PURE__*/React.createElement(_react2.FloatingFocusManager, {
|
|
178
201
|
context: context,
|
|
179
202
|
initialFocus: initialFocus
|
|
180
203
|
}, /*#__PURE__*/React.createElement("div", {
|
|
181
|
-
ref:
|
|
204
|
+
ref: refs.setFloating,
|
|
182
205
|
"aria-hidden": isOpen ? 'false' : 'true',
|
|
183
206
|
className: (0, _classify.default)(_ModalModule.default.modalContainer, {
|
|
184
207
|
[_ModalModule.default.in]: isTransitioning,
|
|
@@ -189,9 +212,13 @@ const Modal = _ref4 => {
|
|
|
189
212
|
[_ModalModule.default.darkBackdrop]: !hideBackdrop
|
|
190
213
|
}, classNames?.backdrop),
|
|
191
214
|
onClick: onBackdropClick
|
|
192
|
-
}), /*#__PURE__*/React.createElement("div", {
|
|
215
|
+
}), isMounted && /*#__PURE__*/React.createElement("div", {
|
|
193
216
|
className: (0, _classify.default)(_ModalModule.default.modal, classNames?.content),
|
|
194
|
-
role: "dialog"
|
|
217
|
+
role: "dialog",
|
|
218
|
+
style: {
|
|
219
|
+
// Transition styles
|
|
220
|
+
...styles
|
|
221
|
+
}
|
|
195
222
|
}, children))), portalRootRef.current);
|
|
196
223
|
};
|
|
197
224
|
exports.Modal = Modal;
|