@spaced-out/ui-design-system 0.1.36 → 0.1.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.
- package/.github/workflows/publish_to_npm.yml +2 -2
- package/CHANGELOG.md +27 -0
- package/lib/components/ButtonDropdown/SimpleButtonDropdown.js +75 -0
- package/lib/components/ButtonDropdown/SimpleButtonDropdown.js.flow +142 -0
- package/lib/components/ButtonDropdown/index.js +21 -12
- package/lib/components/ButtonDropdown/index.js.flow +2 -2
- package/lib/components/Dropdown/Dropdown.js +1 -1
- package/lib/components/Dropdown/Dropdown.js.flow +1 -1
- package/lib/components/Dropdown/SimpleDropdown.js +74 -0
- package/lib/components/Dropdown/SimpleDropdown.js.flow +134 -0
- package/lib/components/Dropdown/index.js +11 -0
- package/lib/components/Dropdown/index.js.flow +1 -0
- package/lib/components/FocusManager/FocusManager.js +7 -1
- package/lib/components/FocusManager/FocusManager.js.flow +7 -0
- package/lib/components/FocusManagerWithArrowKeyNavigation/FocusManagerWithArrowKeyNavigation.js +102 -0
- package/lib/components/FocusManagerWithArrowKeyNavigation/FocusManagerWithArrowKeyNavigation.js.flow +118 -0
- package/lib/components/FocusManagerWithArrowKeyNavigation/FocusManagerWithArrowKeyNavigation.module.css +7 -0
- package/lib/components/FocusManagerWithArrowKeyNavigation/index.js +16 -0
- package/lib/components/FocusManagerWithArrowKeyNavigation/index.js.flow +3 -0
- package/lib/components/InlineDropdown/SimpleInlineDropdown.js +75 -0
- package/lib/components/InlineDropdown/SimpleInlineDropdown.js.flow +139 -0
- package/lib/components/InlineDropdown/index.js +11 -0
- package/lib/components/InlineDropdown/index.js.flow +1 -0
- package/lib/components/Menu/Menu.js +2 -1
- package/lib/components/Menu/Menu.js.flow +12 -0
- package/lib/components/Menu/MenuOptionButton.js +9 -4
- package/lib/components/Menu/MenuOptionButton.js.flow +10 -2
- package/lib/components/OptionButton/SimpleOptionButton.js +74 -0
- package/lib/components/OptionButton/SimpleOptionButton.js.flow +139 -0
- package/lib/components/OptionButton/index.js +21 -6
- package/lib/components/OptionButton/index.js.flow +3 -2
- package/lib/components/RadioButton/RadioButton.js +2 -1
- package/lib/components/RadioButton/RadioButton.js.flow +1 -0
- package/lib/components/RadioButton/RadioButton.module.css +4 -0
- package/lib/components/Typeahead/SimpleTypeahead.js +76 -0
- package/lib/components/Typeahead/SimpleTypeahead.js.flow +134 -0
- package/lib/components/Typeahead/Typeahead.js +3 -4
- package/lib/components/Typeahead/Typeahead.js.flow +123 -117
- package/lib/components/Typeahead/index.js +21 -6
- package/lib/components/Typeahead/index.js.flow +2 -2
- package/lib/components/index.js +11 -0
- package/lib/components/index.js.flow +1 -0
- package/package.json +1 -1
package/lib/components/FocusManagerWithArrowKeyNavigation/FocusManagerWithArrowKeyNavigation.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.SkipElementFromNavigation = exports.FocusManagerWithArrowKeyNavigation = void 0;
|
|
7
|
+
var React = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _react2 = require("@floating-ui/react");
|
|
9
|
+
var _classify = _interopRequireDefault(require("../../utils/classify"));
|
|
10
|
+
var _makeClassNameComponent = require("../../utils/makeClassNameComponent");
|
|
11
|
+
var _FocusManagerWithArrowKeyNavigationModule = _interopRequireDefault(require("./FocusManagerWithArrowKeyNavigation.module.css"));
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
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); }
|
|
14
|
+
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; }
|
|
15
|
+
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); }
|
|
16
|
+
const SKIP_ELEMENT_DISPLAY_NAME = 'SkipElementFromNavigation';
|
|
17
|
+
const SkipElementFromNavigation = (0, _makeClassNameComponent.makeClassNameComponent)(_FocusManagerWithArrowKeyNavigationModule.default.skipElementWrapper);
|
|
18
|
+
exports.SkipElementFromNavigation = SkipElementFromNavigation;
|
|
19
|
+
SkipElementFromNavigation.displayName = SKIP_ELEMENT_DISPLAY_NAME;
|
|
20
|
+
const FocusManagerWithArrowKeyNavigation = props => {
|
|
21
|
+
const {
|
|
22
|
+
classNames,
|
|
23
|
+
children,
|
|
24
|
+
initialFocus = -1,
|
|
25
|
+
orientation = 'vertical',
|
|
26
|
+
modal = false,
|
|
27
|
+
cols = 1,
|
|
28
|
+
...restFloatingFocusManagerProps
|
|
29
|
+
} = props;
|
|
30
|
+
const {
|
|
31
|
+
refs,
|
|
32
|
+
context
|
|
33
|
+
} = (0, _react2.useFloating)({
|
|
34
|
+
open: true
|
|
35
|
+
});
|
|
36
|
+
const [activeIndex, setActiveIndex] = React.useState(null);
|
|
37
|
+
const listRef = React.useRef([]);
|
|
38
|
+
const childrenArray = React.Children.toArray(children).filter(Boolean);
|
|
39
|
+
|
|
40
|
+
// Note(Nishant): This is to correctly call the onClick handler which could have been on child
|
|
41
|
+
// we also need to set the active index correctly on click for list navigation to work
|
|
42
|
+
const childOnClickPassthrough = function (childOnClickHandler, index) {
|
|
43
|
+
if (childOnClickHandler) {
|
|
44
|
+
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
|
|
45
|
+
args[_key - 2] = arguments[_key];
|
|
46
|
+
}
|
|
47
|
+
childOnClickHandler(...args);
|
|
48
|
+
}
|
|
49
|
+
setActiveIndex(index);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Note(Nishant): Force the list navigation props onto the children
|
|
53
|
+
// while using this component make sure your all the passed children have a focus state
|
|
54
|
+
|
|
55
|
+
let skippedChildrenCount = 0;
|
|
56
|
+
const clonedChildren = childrenArray.map((child, index) => {
|
|
57
|
+
const {
|
|
58
|
+
onClick: childClickHandler
|
|
59
|
+
} = child.props;
|
|
60
|
+
let adjustedIndex = index - skippedChildrenCount;
|
|
61
|
+
if (child?.type?.displayName === SKIP_ELEMENT_DISPLAY_NAME) {
|
|
62
|
+
skippedChildrenCount++;
|
|
63
|
+
adjustedIndex = null;
|
|
64
|
+
}
|
|
65
|
+
return /*#__PURE__*/React.cloneElement(child, {
|
|
66
|
+
...child.props,
|
|
67
|
+
tabIndex: activeIndex === index ? 0 : -1,
|
|
68
|
+
ref: node => {
|
|
69
|
+
if (adjustedIndex !== null) {
|
|
70
|
+
listRef.current[adjustedIndex] = node;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
onClick: function () {
|
|
74
|
+
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
75
|
+
args[_key2] = arguments[_key2];
|
|
76
|
+
}
|
|
77
|
+
childOnClickPassthrough(childClickHandler, adjustedIndex, ...args);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
const listNavigation = (0, _react2.useListNavigation)(context, {
|
|
82
|
+
orientation,
|
|
83
|
+
cols,
|
|
84
|
+
listRef,
|
|
85
|
+
activeIndex,
|
|
86
|
+
onNavigate: setActiveIndex
|
|
87
|
+
});
|
|
88
|
+
const {
|
|
89
|
+
getFloatingProps
|
|
90
|
+
} = (0, _react2.useInteractions)([listNavigation]);
|
|
91
|
+
return /*#__PURE__*/React.createElement(_react2.FloatingFocusManager, _extends({
|
|
92
|
+
context: context,
|
|
93
|
+
modal: modal,
|
|
94
|
+
initialFocus: initialFocus
|
|
95
|
+
}, restFloatingFocusManagerProps), /*#__PURE__*/React.createElement("div", _extends({
|
|
96
|
+
ref: refs.setFloating,
|
|
97
|
+
"data-testid": "FocusManagerWithArrowKeyNavigation"
|
|
98
|
+
}, getFloatingProps(), {
|
|
99
|
+
className: (0, _classify.default)(_FocusManagerWithArrowKeyNavigationModule.default.wrapper, classNames?.wrapper)
|
|
100
|
+
}), clonedChildren));
|
|
101
|
+
};
|
|
102
|
+
exports.FocusManagerWithArrowKeyNavigation = FocusManagerWithArrowKeyNavigation;
|
package/lib/components/FocusManagerWithArrowKeyNavigation/FocusManagerWithArrowKeyNavigation.js.flow
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import {
|
|
5
|
+
// $FlowFixMe[untyped-import]
|
|
6
|
+
FloatingFocusManager,
|
|
7
|
+
// $FlowFixMe[untyped-import]
|
|
8
|
+
useFloating,
|
|
9
|
+
// $FlowFixMe[untyped-import]
|
|
10
|
+
useInteractions,
|
|
11
|
+
// $FlowFixMe[untyped-import]
|
|
12
|
+
useListNavigation,
|
|
13
|
+
} from '@floating-ui/react';
|
|
14
|
+
|
|
15
|
+
import classify from '../../utils/classify';
|
|
16
|
+
import type {ClassNameComponent} from '../../utils/makeClassNameComponent';
|
|
17
|
+
import {makeClassNameComponent} from '../../utils/makeClassNameComponent';
|
|
18
|
+
import type {FocusManagerProps} from '../FocusManager';
|
|
19
|
+
|
|
20
|
+
import css from './FocusManagerWithArrowKeyNavigation.module.css';
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
const SKIP_ELEMENT_DISPLAY_NAME = 'SkipElementFromNavigation';
|
|
24
|
+
|
|
25
|
+
export const SkipElementFromNavigation: ClassNameComponent<'div'> =
|
|
26
|
+
makeClassNameComponent(css.skipElementWrapper);
|
|
27
|
+
|
|
28
|
+
SkipElementFromNavigation.displayName = SKIP_ELEMENT_DISPLAY_NAME;
|
|
29
|
+
|
|
30
|
+
export type FocusManagerWithListNavigationProps = {
|
|
31
|
+
...FocusManagerProps,
|
|
32
|
+
cols?: number,
|
|
33
|
+
orientation?: 'horizontal' | 'vertical',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const FocusManagerWithArrowKeyNavigation = (
|
|
37
|
+
props: FocusManagerWithListNavigationProps,
|
|
38
|
+
): React.Node => {
|
|
39
|
+
const {
|
|
40
|
+
classNames,
|
|
41
|
+
children,
|
|
42
|
+
initialFocus = -1,
|
|
43
|
+
orientation = 'vertical',
|
|
44
|
+
modal = false,
|
|
45
|
+
cols = 1,
|
|
46
|
+
...restFloatingFocusManagerProps
|
|
47
|
+
} = props;
|
|
48
|
+
|
|
49
|
+
const {refs, context} = useFloating({open: true});
|
|
50
|
+
const [activeIndex, setActiveIndex] = React.useState(null);
|
|
51
|
+
const listRef = React.useRef([]);
|
|
52
|
+
const childrenArray = React.Children.toArray(children).filter(Boolean);
|
|
53
|
+
|
|
54
|
+
// Note(Nishant): This is to correctly call the onClick handler which could have been on child
|
|
55
|
+
// we also need to set the active index correctly on click for list navigation to work
|
|
56
|
+
const childOnClickPassthrough = (childOnClickHandler, index, ...args) => {
|
|
57
|
+
if (childOnClickHandler) {
|
|
58
|
+
childOnClickHandler(...args);
|
|
59
|
+
}
|
|
60
|
+
setActiveIndex(index);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Note(Nishant): Force the list navigation props onto the children
|
|
64
|
+
// while using this component make sure your all the passed children have a focus state
|
|
65
|
+
|
|
66
|
+
let skippedChildrenCount = 0;
|
|
67
|
+
|
|
68
|
+
const clonedChildren = childrenArray.map((child, index) => {
|
|
69
|
+
const {onClick: childClickHandler} = child.props;
|
|
70
|
+
|
|
71
|
+
let adjustedIndex = index - skippedChildrenCount;
|
|
72
|
+
|
|
73
|
+
if (child?.type?.displayName === SKIP_ELEMENT_DISPLAY_NAME) {
|
|
74
|
+
skippedChildrenCount++;
|
|
75
|
+
adjustedIndex = null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return React.cloneElement(child, {
|
|
79
|
+
...child.props,
|
|
80
|
+
tabIndex: activeIndex === index ? 0 : -1,
|
|
81
|
+
ref: (node) => {
|
|
82
|
+
if (adjustedIndex !== null) {
|
|
83
|
+
listRef.current[adjustedIndex] = node;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
onClick: (...args) => {
|
|
87
|
+
childOnClickPassthrough(childClickHandler, adjustedIndex, ...args);
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const listNavigation = useListNavigation(context, {
|
|
93
|
+
orientation,
|
|
94
|
+
cols,
|
|
95
|
+
listRef,
|
|
96
|
+
activeIndex,
|
|
97
|
+
onNavigate: setActiveIndex,
|
|
98
|
+
});
|
|
99
|
+
const {getFloatingProps} = useInteractions([listNavigation]);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<FloatingFocusManager
|
|
103
|
+
context={context}
|
|
104
|
+
modal={modal}
|
|
105
|
+
initialFocus={initialFocus}
|
|
106
|
+
{...restFloatingFocusManagerProps}
|
|
107
|
+
>
|
|
108
|
+
<div
|
|
109
|
+
ref={refs.setFloating}
|
|
110
|
+
data-testid="FocusManagerWithArrowKeyNavigation"
|
|
111
|
+
{...getFloatingProps()}
|
|
112
|
+
className={classify(css.wrapper, classNames?.wrapper)}
|
|
113
|
+
>
|
|
114
|
+
{clonedChildren}
|
|
115
|
+
</div>
|
|
116
|
+
</FloatingFocusManager>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _FocusManagerWithArrowKeyNavigation = require("./FocusManagerWithArrowKeyNavigation");
|
|
7
|
+
Object.keys(_FocusManagerWithArrowKeyNavigation).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _FocusManagerWithArrowKeyNavigation[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () {
|
|
13
|
+
return _FocusManagerWithArrowKeyNavigation[key];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.SimpleInlineDropdown = void 0;
|
|
7
|
+
var React = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _menu = require("../../utils/menu");
|
|
9
|
+
var _InlineDropdown = require("./InlineDropdown");
|
|
10
|
+
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); }
|
|
11
|
+
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; }
|
|
12
|
+
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); }
|
|
13
|
+
const SimpleInlineDropdownBase = (props, ref) => {
|
|
14
|
+
const {
|
|
15
|
+
size = 'medium',
|
|
16
|
+
classNames,
|
|
17
|
+
anchorPosition,
|
|
18
|
+
options,
|
|
19
|
+
optionsVariant,
|
|
20
|
+
allowSearch,
|
|
21
|
+
selectedKeys,
|
|
22
|
+
onOptionSelect,
|
|
23
|
+
onMenuOpen,
|
|
24
|
+
onMenuClose,
|
|
25
|
+
resolveLabel,
|
|
26
|
+
resolveSecondaryLabel,
|
|
27
|
+
children,
|
|
28
|
+
isFluid,
|
|
29
|
+
...buttonProps
|
|
30
|
+
} = props;
|
|
31
|
+
const [btnText, setBtnText] = React.useState('');
|
|
32
|
+
const [inlineDropdownSelectedKeys, setInlineDropdownSelectedKeys] = React.useState(selectedKeys);
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
const newBtnText = (0, _menu.getButtonLabelFromSelectedKeys)(selectedKeys, children);
|
|
35
|
+
setInlineDropdownSelectedKeys(selectedKeys);
|
|
36
|
+
setBtnText(newBtnText);
|
|
37
|
+
}, [selectedKeys]);
|
|
38
|
+
const handleOptionChange = selectedOption => {
|
|
39
|
+
let newSelectedKeys = [];
|
|
40
|
+
if (optionsVariant === 'checkbox') {
|
|
41
|
+
newSelectedKeys = (0, _menu.getSelectedKeysFromSelectedOption)(selectedOption, inlineDropdownSelectedKeys);
|
|
42
|
+
} else {
|
|
43
|
+
newSelectedKeys = [selectedOption.key];
|
|
44
|
+
}
|
|
45
|
+
const newBtnText = (0, _menu.getButtonLabelFromSelectedKeys)(newSelectedKeys, children);
|
|
46
|
+
setInlineDropdownSelectedKeys(newSelectedKeys);
|
|
47
|
+
setBtnText(newBtnText);
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
onOptionSelect?.(selectedOption);
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
React.useImperativeHandle(ref, () => ({
|
|
53
|
+
selectedKeys: inlineDropdownSelectedKeys
|
|
54
|
+
}));
|
|
55
|
+
return /*#__PURE__*/React.createElement(_InlineDropdown.InlineDropdown, _extends({}, buttonProps, {
|
|
56
|
+
anchorPosition: anchorPosition,
|
|
57
|
+
classNames: classNames,
|
|
58
|
+
size: size,
|
|
59
|
+
onOptionSelect: handleOptionChange,
|
|
60
|
+
onMenuOpen: onMenuOpen,
|
|
61
|
+
onMenuClose: onMenuClose,
|
|
62
|
+
menu: {
|
|
63
|
+
isFluid,
|
|
64
|
+
options,
|
|
65
|
+
selectedKeys: inlineDropdownSelectedKeys,
|
|
66
|
+
optionsVariant,
|
|
67
|
+
allowSearch,
|
|
68
|
+
resolveLabel,
|
|
69
|
+
resolveSecondaryLabel,
|
|
70
|
+
size: size === 'extraSmall' ? 'small' : size
|
|
71
|
+
}
|
|
72
|
+
}), optionsVariant === 'checkbox' ? btnText : children);
|
|
73
|
+
};
|
|
74
|
+
const SimpleInlineDropdown = /*#__PURE__*/React.forwardRef(SimpleInlineDropdownBase);
|
|
75
|
+
exports.SimpleInlineDropdown = SimpleInlineDropdown;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
getButtonLabelFromSelectedKeys,
|
|
7
|
+
getSelectedKeysFromSelectedOption,
|
|
8
|
+
} from '../../utils/menu';
|
|
9
|
+
import type {ButtonProps} from '../Button';
|
|
10
|
+
import type {AnchorType} from '../ButtonDropdown';
|
|
11
|
+
import type {MenuOption, MenuOptionsVariant} from '../Menu';
|
|
12
|
+
|
|
13
|
+
import {InlineDropdown} from './InlineDropdown';
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
type ClassNames = $ReadOnly<{
|
|
17
|
+
buttonWrapper?: string,
|
|
18
|
+
dropdownContainer?: string,
|
|
19
|
+
}>;
|
|
20
|
+
|
|
21
|
+
export type SimpleInlineDropdownRef = {
|
|
22
|
+
selectedKeys?: Array<string>,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type SimpleInlineDropdownProps = {
|
|
26
|
+
// Input props
|
|
27
|
+
...ButtonProps,
|
|
28
|
+
|
|
29
|
+
classNames?: ClassNames,
|
|
30
|
+
anchorPosition?: AnchorType,
|
|
31
|
+
|
|
32
|
+
// Menu props
|
|
33
|
+
options?: Array<MenuOption>,
|
|
34
|
+
|
|
35
|
+
optionsVariant?: MenuOptionsVariant,
|
|
36
|
+
allowSearch?: boolean,
|
|
37
|
+
selectedKeys?: Array<string>,
|
|
38
|
+
|
|
39
|
+
// events
|
|
40
|
+
onOptionSelect?: (option: MenuOption) => mixed,
|
|
41
|
+
onMenuOpen?: () => mixed,
|
|
42
|
+
onMenuClose?: () => mixed,
|
|
43
|
+
|
|
44
|
+
// Resolvers
|
|
45
|
+
resolveLabel?: (option: MenuOption) => string | React.Node,
|
|
46
|
+
resolveSecondaryLabel?: (option: MenuOption) => string | React.Node,
|
|
47
|
+
...
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const SimpleInlineDropdownBase = (props: SimpleInlineDropdownProps, ref) => {
|
|
51
|
+
const {
|
|
52
|
+
size = 'medium',
|
|
53
|
+
classNames,
|
|
54
|
+
anchorPosition,
|
|
55
|
+
options,
|
|
56
|
+
optionsVariant,
|
|
57
|
+
allowSearch,
|
|
58
|
+
selectedKeys,
|
|
59
|
+
onOptionSelect,
|
|
60
|
+
onMenuOpen,
|
|
61
|
+
onMenuClose,
|
|
62
|
+
resolveLabel,
|
|
63
|
+
resolveSecondaryLabel,
|
|
64
|
+
children,
|
|
65
|
+
isFluid,
|
|
66
|
+
...buttonProps
|
|
67
|
+
} = props;
|
|
68
|
+
|
|
69
|
+
const [btnText, setBtnText] = React.useState('');
|
|
70
|
+
const [inlineDropdownSelectedKeys, setInlineDropdownSelectedKeys] =
|
|
71
|
+
React.useState(selectedKeys);
|
|
72
|
+
|
|
73
|
+
React.useEffect(() => {
|
|
74
|
+
const newBtnText = getButtonLabelFromSelectedKeys(selectedKeys, children);
|
|
75
|
+
|
|
76
|
+
setInlineDropdownSelectedKeys(selectedKeys);
|
|
77
|
+
setBtnText(newBtnText);
|
|
78
|
+
}, [selectedKeys]);
|
|
79
|
+
|
|
80
|
+
const handleOptionChange = (selectedOption: MenuOption) => {
|
|
81
|
+
let newSelectedKeys = [];
|
|
82
|
+
|
|
83
|
+
if (optionsVariant === 'checkbox') {
|
|
84
|
+
newSelectedKeys = getSelectedKeysFromSelectedOption(
|
|
85
|
+
selectedOption,
|
|
86
|
+
inlineDropdownSelectedKeys,
|
|
87
|
+
);
|
|
88
|
+
} else {
|
|
89
|
+
newSelectedKeys = [selectedOption.key];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const newBtnText = getButtonLabelFromSelectedKeys(
|
|
93
|
+
newSelectedKeys,
|
|
94
|
+
children,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
setInlineDropdownSelectedKeys(newSelectedKeys);
|
|
98
|
+
setBtnText(newBtnText);
|
|
99
|
+
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
onOptionSelect?.(selectedOption);
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
React.useImperativeHandle(ref, () => ({
|
|
106
|
+
selectedKeys: inlineDropdownSelectedKeys,
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<InlineDropdown
|
|
111
|
+
{...buttonProps}
|
|
112
|
+
anchorPosition={anchorPosition}
|
|
113
|
+
classNames={classNames}
|
|
114
|
+
size={size}
|
|
115
|
+
onOptionSelect={handleOptionChange}
|
|
116
|
+
onMenuOpen={onMenuOpen}
|
|
117
|
+
onMenuClose={onMenuClose}
|
|
118
|
+
menu={{
|
|
119
|
+
isFluid,
|
|
120
|
+
options,
|
|
121
|
+
selectedKeys: inlineDropdownSelectedKeys,
|
|
122
|
+
optionsVariant,
|
|
123
|
+
allowSearch,
|
|
124
|
+
resolveLabel,
|
|
125
|
+
resolveSecondaryLabel,
|
|
126
|
+
size: size === 'extraSmall' ? 'small' : size,
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
{optionsVariant === 'checkbox' ? btnText : children}
|
|
130
|
+
</InlineDropdown>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const SimpleInlineDropdown: React.AbstractComponent<
|
|
135
|
+
SimpleInlineDropdownProps,
|
|
136
|
+
SimpleInlineDropdownRef,
|
|
137
|
+
> = React.forwardRef<SimpleInlineDropdownProps, SimpleInlineDropdownRef>(
|
|
138
|
+
SimpleInlineDropdownBase,
|
|
139
|
+
);
|
|
@@ -13,4 +13,15 @@ Object.keys(_InlineDropdown).forEach(function (key) {
|
|
|
13
13
|
return _InlineDropdown[key];
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
|
+
});
|
|
17
|
+
var _SimpleInlineDropdown = require("./SimpleInlineDropdown");
|
|
18
|
+
Object.keys(_SimpleInlineDropdown).forEach(function (key) {
|
|
19
|
+
if (key === "default" || key === "__esModule") return;
|
|
20
|
+
if (key in exports && exports[key] === _SimpleInlineDropdown[key]) return;
|
|
21
|
+
Object.defineProperty(exports, key, {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () {
|
|
24
|
+
return _SimpleInlineDropdown[key];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
16
27
|
});
|
|
@@ -109,7 +109,8 @@ const Menu = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
109
109
|
}, allowSearch && /*#__PURE__*/React.createElement(_SearchInput.SearchInput, {
|
|
110
110
|
value: searchText,
|
|
111
111
|
onChange: e => setSearchText(e.target.value),
|
|
112
|
-
onClear: () => setSearchText('')
|
|
112
|
+
onClear: () => setSearchText(''),
|
|
113
|
+
size: size
|
|
113
114
|
}), /*#__PURE__*/React.createElement(RenderOption, _extends({}, props, {
|
|
114
115
|
searchText: searchText
|
|
115
116
|
})));
|
|
@@ -27,8 +27,13 @@ type ClassNames = $ReadOnly<{
|
|
|
27
27
|
optionTextLabel?: string,
|
|
28
28
|
}>;
|
|
29
29
|
|
|
30
|
+
type OptionClassNames = $ReadOnly<{
|
|
31
|
+
wrapper?: string,
|
|
32
|
+
}>;
|
|
33
|
+
|
|
30
34
|
export type MenuOption = {
|
|
31
35
|
key: string,
|
|
36
|
+
classNames?: OptionClassNames,
|
|
32
37
|
label?: string,
|
|
33
38
|
secondaryLabel?: string,
|
|
34
39
|
customComponent?: React.Node,
|
|
@@ -57,6 +62,12 @@ export type BaseMenuProps = {
|
|
|
57
62
|
// the user navigates outside of the menu using the tab key.
|
|
58
63
|
onTabOut?: () => mixed,
|
|
59
64
|
allowSearch?: boolean,
|
|
65
|
+
// A function that resolves the label for a MenuOption.
|
|
66
|
+
// It takes a MenuOption as a parameter and returns either a string or a React Node.
|
|
67
|
+
resolveLabel?: (option: MenuOption) => string | React.Node,
|
|
68
|
+
// A function that resolves the secondaryLabel for a MenuOption.
|
|
69
|
+
// It takes a MenuOption as a parameter and returns either a string or a React Node.
|
|
70
|
+
resolveSecondaryLabel?: (option: MenuOption) => string | React.Node,
|
|
60
71
|
};
|
|
61
72
|
|
|
62
73
|
export type MenuOptionTypes = {
|
|
@@ -259,6 +270,7 @@ export const Menu: React$AbstractComponent<MenuProps, HTMLDivElement> =
|
|
|
259
270
|
value={searchText}
|
|
260
271
|
onChange={(e) => setSearchText(e.target.value)}
|
|
261
272
|
onClear={() => setSearchText('')}
|
|
273
|
+
size={size}
|
|
262
274
|
/>
|
|
263
275
|
)}
|
|
264
276
|
<RenderOption {...props} searchText={searchText} />
|
|
@@ -28,7 +28,9 @@ const MenuOptionButton = props => {
|
|
|
28
28
|
optionsVariant = 'normal',
|
|
29
29
|
selectedKeys,
|
|
30
30
|
isLastItem,
|
|
31
|
-
onTabOut
|
|
31
|
+
onTabOut,
|
|
32
|
+
resolveLabel,
|
|
33
|
+
resolveSecondaryLabel
|
|
32
34
|
} = props;
|
|
33
35
|
const {
|
|
34
36
|
key,
|
|
@@ -37,6 +39,7 @@ const MenuOptionButton = props => {
|
|
|
37
39
|
customComponent,
|
|
38
40
|
iconLeft,
|
|
39
41
|
iconLeftType,
|
|
42
|
+
classNames: optionClassNames,
|
|
40
43
|
iconRight,
|
|
41
44
|
iconRightType,
|
|
42
45
|
disabled,
|
|
@@ -44,6 +47,8 @@ const MenuOptionButton = props => {
|
|
|
44
47
|
optionVariant = optionsVariant
|
|
45
48
|
} = option;
|
|
46
49
|
const [buttonSize, setButtonSize] = React.useState(optionSize || size);
|
|
50
|
+
const resolvedLabel = resolveLabel ? resolveLabel(option) : label;
|
|
51
|
+
const resolvedSecondaryLabel = resolveSecondaryLabel ? resolveSecondaryLabel(option) : secondaryLabel;
|
|
47
52
|
const isSelected = () => {
|
|
48
53
|
if (!selectedKeys || !Array.isArray(selectedKeys) || !selectedKeys.length) {
|
|
49
54
|
return false;
|
|
@@ -73,7 +78,7 @@ const MenuOptionButton = props => {
|
|
|
73
78
|
[_MenuModule.default.disabled]: menuDisabled || disabled,
|
|
74
79
|
[_MenuModule.default.withIconLeft]: !!iconLeft,
|
|
75
80
|
[_MenuModule.default.withIconRight]: !!iconRight
|
|
76
|
-
}, classNames?.option),
|
|
81
|
+
}, classNames?.option, optionClassNames?.wrapper),
|
|
77
82
|
disabled: menuDisabled || disabled,
|
|
78
83
|
onClick: () => onSelect && onSelect(option),
|
|
79
84
|
autoFocus: selectedOption?.key === key
|
|
@@ -97,9 +102,9 @@ const MenuOptionButton = props => {
|
|
|
97
102
|
className: (0, _classify.classify)(_MenuModule.default.optionTextContainer, classNames?.optionTextContainer)
|
|
98
103
|
}, /*#__PURE__*/React.isValidElement(customComponent) ? customComponent : /*#__PURE__*/React.createElement("div", {
|
|
99
104
|
className: (0, _classify.classify)(_MenuModule.default.optionTextLabel, classNames?.optionTextLabel)
|
|
100
|
-
}, /*#__PURE__*/React.createElement(_Truncate.Truncate, null,
|
|
105
|
+
}, /*#__PURE__*/React.createElement(_Truncate.Truncate, null, resolvedLabel)), !!secondaryLabel && /*#__PURE__*/React.createElement("div", {
|
|
101
106
|
className: _MenuModule.default.optionTextSecondaryLabel
|
|
102
|
-
}, /*#__PURE__*/React.createElement(_Truncate.Truncate, null,
|
|
107
|
+
}, /*#__PURE__*/React.createElement(_Truncate.Truncate, null, resolvedSecondaryLabel))), !!iconRight && /*#__PURE__*/React.createElement(_Icon.Icon, {
|
|
103
108
|
name: iconRight,
|
|
104
109
|
type: iconRightType,
|
|
105
110
|
size: "small",
|
|
@@ -33,6 +33,8 @@ export const MenuOptionButton = (props: MenuOptionProps): React.Node => {
|
|
|
33
33
|
selectedKeys,
|
|
34
34
|
isLastItem,
|
|
35
35
|
onTabOut,
|
|
36
|
+
resolveLabel,
|
|
37
|
+
resolveSecondaryLabel,
|
|
36
38
|
} = props;
|
|
37
39
|
const {
|
|
38
40
|
key,
|
|
@@ -41,6 +43,7 @@ export const MenuOptionButton = (props: MenuOptionProps): React.Node => {
|
|
|
41
43
|
customComponent,
|
|
42
44
|
iconLeft,
|
|
43
45
|
iconLeftType,
|
|
46
|
+
classNames: optionClassNames,
|
|
44
47
|
iconRight,
|
|
45
48
|
iconRightType,
|
|
46
49
|
disabled,
|
|
@@ -48,6 +51,10 @@ export const MenuOptionButton = (props: MenuOptionProps): React.Node => {
|
|
|
48
51
|
optionVariant = optionsVariant,
|
|
49
52
|
} = option;
|
|
50
53
|
const [buttonSize, setButtonSize] = React.useState(optionSize || size);
|
|
54
|
+
const resolvedLabel = resolveLabel ? resolveLabel(option) : label;
|
|
55
|
+
const resolvedSecondaryLabel = resolveSecondaryLabel
|
|
56
|
+
? resolveSecondaryLabel(option)
|
|
57
|
+
: secondaryLabel;
|
|
51
58
|
const isSelected = () => {
|
|
52
59
|
if (!selectedKeys || !Array.isArray(selectedKeys) || !selectedKeys.length) {
|
|
53
60
|
return false;
|
|
@@ -88,6 +95,7 @@ export const MenuOptionButton = (props: MenuOptionProps): React.Node => {
|
|
|
88
95
|
[css.withIconRight]: !!iconRight,
|
|
89
96
|
},
|
|
90
97
|
classNames?.option,
|
|
98
|
+
optionClassNames?.wrapper,
|
|
91
99
|
)}
|
|
92
100
|
disabled={menuDisabled || disabled}
|
|
93
101
|
onClick={() => onSelect && onSelect(option)}
|
|
@@ -132,13 +140,13 @@ export const MenuOptionButton = (props: MenuOptionProps): React.Node => {
|
|
|
132
140
|
classNames?.optionTextLabel,
|
|
133
141
|
)}
|
|
134
142
|
>
|
|
135
|
-
<Truncate>{
|
|
143
|
+
<Truncate>{resolvedLabel}</Truncate>
|
|
136
144
|
</div>
|
|
137
145
|
)}
|
|
138
146
|
|
|
139
147
|
{!!secondaryLabel && (
|
|
140
148
|
<div className={css.optionTextSecondaryLabel}>
|
|
141
|
-
<Truncate>{
|
|
149
|
+
<Truncate>{resolvedSecondaryLabel}</Truncate>
|
|
142
150
|
</div>
|
|
143
151
|
)}
|
|
144
152
|
</div>
|