@rhc-shared-components/form-multi-select-component 0.1.3 → 0.2.1
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/dist/FormMultiSelectInput.d.ts +4 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.js +283 -95
- package/dist/index.modern.js +245 -81
- package/package.json +3 -3
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import '
|
|
3
|
-
export interface IMultiSelectItem {
|
|
4
|
-
value: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
isDisabled?: boolean;
|
|
7
|
-
extraProps?: any;
|
|
8
|
-
}
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SelectOptionProps } from '@patternfly/react-core';
|
|
9
3
|
export interface FormMultiSelectInputProps {
|
|
10
4
|
name: string;
|
|
11
5
|
label: string;
|
|
12
6
|
isRequired?: boolean;
|
|
13
7
|
placeholder?: string;
|
|
14
|
-
|
|
8
|
+
selectMenuOptions: SelectOptionProps[];
|
|
15
9
|
ariaLabel?: string;
|
|
16
10
|
helperText?: string;
|
|
17
11
|
maxHeight?: string | number;
|
|
@@ -20,5 +14,5 @@ export interface FormMultiSelectInputProps {
|
|
|
20
14
|
menuAppendTo?: HTMLElement | (() => HTMLElement) | 'inline' | 'parent';
|
|
21
15
|
extraProps?: any;
|
|
22
16
|
}
|
|
23
|
-
declare const FormMultiSelectInput: React.
|
|
17
|
+
declare const FormMultiSelectInput: React.FunctionComponent<FormMultiSelectInputProps>;
|
|
24
18
|
export default FormMultiSelectInput;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import FormMultiSelectInput
|
|
2
|
-
export { FormMultiSelectInput
|
|
1
|
+
import FormMultiSelectInput from './FormMultiSelectInput';
|
|
2
|
+
export { FormMultiSelectInput };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
var React = require('react');
|
|
2
|
-
var formik = require('formik');
|
|
3
2
|
var reactCore = require('@patternfly/react-core');
|
|
4
3
|
var formGroupContainer = require('@rhc-shared-components/form-group-container');
|
|
5
|
-
var
|
|
4
|
+
var formik = require('formik');
|
|
5
|
+
|
|
6
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
6
7
|
|
|
7
8
|
function _interopNamespace(e) {
|
|
8
9
|
if (e && e.__esModule) return e;
|
|
@@ -23,24 +24,7 @@ function _interopNamespace(e) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
26
|
-
|
|
27
|
-
function _extends() {
|
|
28
|
-
_extends = Object.assign || function (target) {
|
|
29
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
30
|
-
var source = arguments[i];
|
|
31
|
-
|
|
32
|
-
for (var key in source) {
|
|
33
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
34
|
-
target[key] = source[key];
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return target;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
return _extends.apply(this, arguments);
|
|
43
|
-
}
|
|
27
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
44
28
|
|
|
45
29
|
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
46
30
|
if (source == null) return {};
|
|
@@ -57,121 +41,325 @@ function _objectWithoutPropertiesLoose(source, excluded) {
|
|
|
57
41
|
return target;
|
|
58
42
|
}
|
|
59
43
|
|
|
60
|
-
|
|
44
|
+
/******************************************************************************
|
|
45
|
+
Copyright (c) Microsoft Corporation.
|
|
46
|
+
|
|
47
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
48
|
+
purpose with or without fee is hereby granted.
|
|
49
|
+
|
|
50
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
51
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
52
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
53
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
54
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
55
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
56
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
57
|
+
***************************************************************************** */
|
|
58
|
+
|
|
59
|
+
function __rest(s, e) {
|
|
60
|
+
var t = {};
|
|
61
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
62
|
+
t[p] = s[p];
|
|
63
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
64
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
65
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
66
|
+
t[p[i]] = s[p[i]];
|
|
67
|
+
}
|
|
68
|
+
return t;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
72
|
+
var e = new Error(message);
|
|
73
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
let currentId = 0;
|
|
77
|
+
/**
|
|
78
|
+
* Factory to create Icon class components for consumers
|
|
79
|
+
*/
|
|
80
|
+
function createIcon({ name, xOffset = 0, yOffset = 0, width, height, svgPath }) {
|
|
81
|
+
var _a;
|
|
82
|
+
return _a = class SVGIcon extends React__namespace.Component {
|
|
83
|
+
constructor() {
|
|
84
|
+
super(...arguments);
|
|
85
|
+
this.id = `icon-title-${currentId++}`;
|
|
86
|
+
}
|
|
87
|
+
render() {
|
|
88
|
+
const _a = this.props, { title, className } = _a, props = __rest(_a, ["title", "className"]);
|
|
89
|
+
const classes = className ? `pf-v5-svg ${className}` : 'pf-v5-svg';
|
|
90
|
+
const hasTitle = Boolean(title);
|
|
91
|
+
const viewBox = [xOffset, yOffset, width, height].join(' ');
|
|
92
|
+
return (React__namespace.createElement("svg", Object.assign({ className: classes, viewBox: viewBox, fill: "currentColor", "aria-labelledby": hasTitle ? this.id : null, "aria-hidden": hasTitle ? null : true, role: "img", width: "1em", height: "1em" }, props),
|
|
93
|
+
hasTitle && React__namespace.createElement("title", { id: this.id }, title),
|
|
94
|
+
React__namespace.createElement("path", { d: svgPath })));
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
_a.displayName = name,
|
|
98
|
+
_a;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const TimesIconConfig = {
|
|
102
|
+
name: 'TimesIcon',
|
|
103
|
+
height: 512,
|
|
104
|
+
width: 352,
|
|
105
|
+
svgPath: 'M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z',
|
|
106
|
+
yOffset: 0,
|
|
107
|
+
xOffset: 0,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const TimesIcon = createIcon(TimesIconConfig);
|
|
111
|
+
|
|
112
|
+
var TimesIcon$1 = TimesIcon;
|
|
113
|
+
|
|
114
|
+
var _excluded = ["label", "isRequired", "children", "selectMenuOptions", "ariaLabel", "placeholder", "helperText", "maxHeight", "isDisabled", "classNames", "menuAppendTo", "extraProps"];
|
|
61
115
|
|
|
62
116
|
var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
|
|
63
117
|
var label = _ref.label,
|
|
64
118
|
isRequired = _ref.isRequired,
|
|
65
|
-
|
|
66
|
-
_ref$ariaLabel = _ref.ariaLabel,
|
|
67
|
-
ariaLabel = _ref$ariaLabel === void 0 ? 'Select Input' : _ref$ariaLabel,
|
|
119
|
+
selectMenuOptions = _ref.selectMenuOptions,
|
|
68
120
|
placeholder = _ref.placeholder,
|
|
69
121
|
helperText = _ref.helperText,
|
|
70
|
-
maxHeight = _ref.maxHeight,
|
|
71
122
|
isDisabled = _ref.isDisabled,
|
|
72
|
-
classNames = _ref.classNames,
|
|
73
123
|
menuAppendTo = _ref.menuAppendTo,
|
|
74
|
-
extraProps = _ref.extraProps,
|
|
75
124
|
rest = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
76
125
|
|
|
77
126
|
var _useField = formik.useField(rest),
|
|
78
127
|
meta = _useField[1];
|
|
79
128
|
|
|
80
|
-
var value = meta.value;
|
|
81
|
-
|
|
82
129
|
var _useFormikContext = formik.useFormikContext(),
|
|
83
130
|
isSubmitting = _useFormikContext.isSubmitting,
|
|
84
131
|
setFieldValue = _useFormikContext.setFieldValue,
|
|
85
132
|
setFieldTouched = _useFormikContext.setFieldTouched;
|
|
86
133
|
|
|
87
|
-
var _React$useState =
|
|
134
|
+
var _React$useState = React__default["default"].useState(false),
|
|
88
135
|
isOpen = _React$useState[0],
|
|
89
136
|
setIsOpen = _React$useState[1];
|
|
90
137
|
|
|
91
|
-
var
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
138
|
+
var _React$useState2 = React__default["default"].useState(''),
|
|
139
|
+
inputValue = _React$useState2[0],
|
|
140
|
+
setInputValue = _React$useState2[1];
|
|
141
|
+
|
|
142
|
+
var _React$useState3 = React__default["default"].useState([]),
|
|
143
|
+
selected = _React$useState3[0],
|
|
144
|
+
setSelected = _React$useState3[1];
|
|
145
|
+
|
|
146
|
+
var _React$useState4 = React__default["default"].useState(selectMenuOptions),
|
|
147
|
+
selectOptions = _React$useState4[0],
|
|
148
|
+
setSelectOptions = _React$useState4[1];
|
|
149
|
+
|
|
150
|
+
var _React$useState5 = React__default["default"].useState(null),
|
|
151
|
+
focusedItemIndex = _React$useState5[0],
|
|
152
|
+
setFocusedItemIndex = _React$useState5[1];
|
|
153
|
+
|
|
154
|
+
var _React$useState6 = React__default["default"].useState(null),
|
|
155
|
+
activeItem = _React$useState6[0],
|
|
156
|
+
setActiveItem = _React$useState6[1];
|
|
157
|
+
|
|
158
|
+
var textInputRef = React__default["default"].useRef();
|
|
159
|
+
React__default["default"].useEffect(function () {
|
|
160
|
+
var newSelectOptions = selectMenuOptions; // Filter menu items based on the text input value when one exists
|
|
161
|
+
|
|
162
|
+
if (inputValue) {
|
|
163
|
+
newSelectOptions = selectMenuOptions.filter(function (menuItem) {
|
|
164
|
+
return String(menuItem.children).toLowerCase().includes(inputValue.toLowerCase());
|
|
165
|
+
}); // When no options are found after filtering, display 'No results found'
|
|
166
|
+
|
|
167
|
+
if (!newSelectOptions.length) {
|
|
168
|
+
newSelectOptions = [{
|
|
169
|
+
isDisabled: false,
|
|
170
|
+
children: "No results found for \"" + inputValue + "\"",
|
|
171
|
+
value: 'no results'
|
|
172
|
+
}];
|
|
173
|
+
} // Open the menu when the input value changes and the new value is not empty
|
|
98
174
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
175
|
+
|
|
176
|
+
if (!isOpen) {
|
|
177
|
+
setIsOpen(true);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
setSelectOptions(newSelectOptions);
|
|
182
|
+
setFocusedItemIndex(null);
|
|
183
|
+
setActiveItem(null);
|
|
184
|
+
}, [inputValue]);
|
|
185
|
+
|
|
186
|
+
var handleMenuArrowKeys = function handleMenuArrowKeys(key) {
|
|
187
|
+
var indexToFocus = 0;
|
|
188
|
+
|
|
189
|
+
if (isOpen) {
|
|
190
|
+
if (key === 'ArrowUp') {
|
|
191
|
+
// When no index is set or at the first index, focus to the last, otherwise decrement focus index
|
|
192
|
+
if (focusedItemIndex === null || focusedItemIndex === 0) {
|
|
193
|
+
indexToFocus = selectOptions.length - 1;
|
|
194
|
+
} else {
|
|
195
|
+
indexToFocus = focusedItemIndex - 1;
|
|
114
196
|
}
|
|
115
|
-
}
|
|
116
|
-
}));
|
|
117
|
-
};
|
|
197
|
+
}
|
|
118
198
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
199
|
+
if (key === 'ArrowDown') {
|
|
200
|
+
// When no index is set or at the last index, focus to the first, otherwise increment focus index
|
|
201
|
+
if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
|
|
202
|
+
indexToFocus = 0;
|
|
203
|
+
} else {
|
|
204
|
+
indexToFocus = focusedItemIndex + 1;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
setFocusedItemIndex(indexToFocus);
|
|
209
|
+
var focusedItem = selectOptions.filter(function (option) {
|
|
210
|
+
return !option.isDisabled;
|
|
211
|
+
})[indexToFocus];
|
|
212
|
+
setActiveItem("select-multi-typeahead-" + focusedItem.value.replace(' ', '-'));
|
|
213
|
+
}
|
|
214
|
+
};
|
|
122
215
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return (selectItem == null ? void 0 : selectItem.isDisabled) || (selectItem == null ? void 0 : (_selectItem$extraProp2 = selectItem.extraProps) == null ? void 0 : _selectItem$extraProp2.isDisabled);
|
|
216
|
+
var onInputKeyDown = function onInputKeyDown(event) {
|
|
217
|
+
var enabledMenuItems = selectOptions.filter(function (menuItem) {
|
|
218
|
+
return !menuItem.isDisabled;
|
|
127
219
|
});
|
|
128
|
-
|
|
129
|
-
|
|
220
|
+
var firstMenuItem = enabledMenuItems[0];
|
|
221
|
+
var focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
|
|
222
|
+
|
|
223
|
+
switch (event.key) {
|
|
224
|
+
// Select the first available option
|
|
225
|
+
case 'Enter':
|
|
226
|
+
if (!isOpen) {
|
|
227
|
+
setIsOpen(function (prevIsOpen) {
|
|
228
|
+
return !prevIsOpen;
|
|
229
|
+
});
|
|
230
|
+
} else if (isOpen && focusedItem.value !== 'no results') {
|
|
231
|
+
_onSelect(focusedItem.value);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
break;
|
|
235
|
+
|
|
236
|
+
case 'Tab':
|
|
237
|
+
case 'Escape':
|
|
238
|
+
setIsOpen(false);
|
|
239
|
+
setActiveItem(null);
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
case 'ArrowUp':
|
|
243
|
+
case 'ArrowDown':
|
|
244
|
+
event.preventDefault();
|
|
245
|
+
handleMenuArrowKeys(event.key);
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
130
248
|
};
|
|
131
249
|
|
|
132
|
-
var
|
|
250
|
+
var onToggleClick = function onToggleClick() {
|
|
133
251
|
setIsOpen(!isOpen);
|
|
134
252
|
};
|
|
135
253
|
|
|
136
|
-
|
|
254
|
+
var onTextInputChange = function onTextInputChange(_event, value) {
|
|
255
|
+
setInputValue(value);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
var _onSelect = function onSelect(value) {
|
|
259
|
+
var _textInputRef$current;
|
|
260
|
+
|
|
261
|
+
// eslint-disable-next-line no-console
|
|
262
|
+
console.log('selected', value);
|
|
263
|
+
|
|
264
|
+
if (value && value !== 'no results') {
|
|
265
|
+
var selectedValues = selected.includes(value) ? selected.filter(function (selection) {
|
|
266
|
+
return selection !== value;
|
|
267
|
+
}) : [].concat(selected, [value]);
|
|
268
|
+
setSelected(selectedValues);
|
|
269
|
+
setFieldTouched(rest.name, true, false);
|
|
270
|
+
setFieldValue(rest.name, selectedValues, true);
|
|
271
|
+
} // eslint-disable-next-line no-unused-expressions
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
textInputRef == null ? void 0 : (_textInputRef$current = textInputRef.current) == null ? void 0 : _textInputRef$current.focus();
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
var toggle = function toggle(toggleRef) {
|
|
278
|
+
return React__default["default"].createElement(reactCore.MenuToggle, {
|
|
279
|
+
variant: 'typeahead',
|
|
280
|
+
onClick: onToggleClick,
|
|
281
|
+
innerRef: toggleRef,
|
|
282
|
+
isExpanded: isOpen,
|
|
283
|
+
isDisabled: isSubmitting || isDisabled,
|
|
284
|
+
isFullWidth: true
|
|
285
|
+
}, React__default["default"].createElement(reactCore.TextInputGroup, {
|
|
286
|
+
isPlain: true
|
|
287
|
+
}, React__default["default"].createElement(reactCore.TextInputGroupMain, Object.assign({
|
|
288
|
+
value: inputValue,
|
|
289
|
+
onClick: onToggleClick,
|
|
290
|
+
onChange: onTextInputChange,
|
|
291
|
+
onKeyDown: onInputKeyDown,
|
|
292
|
+
id: 'multi-typeahead-select-input',
|
|
293
|
+
autoComplete: 'off',
|
|
294
|
+
innerRef: textInputRef,
|
|
295
|
+
placeholder: placeholder
|
|
296
|
+
}, activeItem && {
|
|
297
|
+
'aria-activedescendant': activeItem
|
|
298
|
+
}, {
|
|
299
|
+
role: 'combobox',
|
|
300
|
+
isExpanded: isOpen,
|
|
301
|
+
"aria-controls": 'select-multi-typeahead-listbox'
|
|
302
|
+
}), React__default["default"].createElement(reactCore.ChipGroup, {
|
|
303
|
+
"aria-label": 'Current selections'
|
|
304
|
+
}, selected.map(function (selection, index) {
|
|
305
|
+
return React__default["default"].createElement(reactCore.Chip, {
|
|
306
|
+
key: index,
|
|
307
|
+
onClick: function onClick(ev) {
|
|
308
|
+
ev.stopPropagation();
|
|
309
|
+
|
|
310
|
+
_onSelect(selection);
|
|
311
|
+
}
|
|
312
|
+
}, selection);
|
|
313
|
+
}))), React__default["default"].createElement(reactCore.TextInputGroupUtilities, null, selected.length > 0 && React__default["default"].createElement(reactCore.Button, {
|
|
314
|
+
variant: 'plain',
|
|
315
|
+
onClick: function onClick() {
|
|
316
|
+
var _textInputRef$current2;
|
|
317
|
+
|
|
318
|
+
setInputValue('');
|
|
319
|
+
setSelected([]); // eslint-disable-next-line no-unused-expressions
|
|
320
|
+
|
|
321
|
+
textInputRef == null ? void 0 : (_textInputRef$current2 = textInputRef.current) == null ? void 0 : _textInputRef$current2.focus();
|
|
322
|
+
},
|
|
323
|
+
"aria-label": 'Clear input value'
|
|
324
|
+
}, React__default["default"].createElement(TimesIcon$1, {
|
|
325
|
+
"aria-hidden": true
|
|
326
|
+
})))));
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
return React__default["default"].createElement(React__default["default"].Fragment, null, React__default["default"].createElement(formGroupContainer.FormGroupContainer, {
|
|
137
330
|
validated: meta.touched && meta.error ? reactCore.ValidatedOptions.error : reactCore.ValidatedOptions["default"],
|
|
138
331
|
helperTextInvalid: meta.error,
|
|
139
332
|
isRequired: isRequired,
|
|
140
333
|
fieldId: rest.name,
|
|
141
334
|
label: label,
|
|
142
335
|
helperText: helperText
|
|
143
|
-
},
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
maxHeight: maxHeight
|
|
155
|
-
}, {
|
|
156
|
-
isDisabled: isSubmitting || isDisabled,
|
|
157
|
-
"aria-label": ariaLabel
|
|
158
|
-
}, disabledItems && {
|
|
159
|
-
chipGroupComponent: React__namespace.createElement(CustomChipGroup, null)
|
|
160
|
-
}, classNames && {
|
|
161
|
-
className: classNames
|
|
336
|
+
}, React__default["default"].createElement(reactCore.Select, Object.assign({
|
|
337
|
+
id: 'multi-typeahead-select',
|
|
338
|
+
isOpen: isOpen,
|
|
339
|
+
selected: selected,
|
|
340
|
+
onSelect: function onSelect(_ev, selection) {
|
|
341
|
+
return _onSelect(selection);
|
|
342
|
+
},
|
|
343
|
+
onOpenChange: function onOpenChange() {
|
|
344
|
+
return setIsOpen(false);
|
|
345
|
+
},
|
|
346
|
+
toggle: toggle
|
|
162
347
|
}, menuAppendTo && {
|
|
163
348
|
menuAppendTo: menuAppendTo
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
349
|
+
}), React__default["default"].createElement(reactCore.SelectList, {
|
|
350
|
+
isAriaMultiselectable: true,
|
|
351
|
+
id: 'select-multi-typeahead-listbox'
|
|
352
|
+
}, selectOptions.map(function (option, index) {
|
|
353
|
+
return React__default["default"].createElement(reactCore.SelectOption, Object.assign({
|
|
354
|
+
key: option.value || option.children,
|
|
355
|
+
isFocused: focusedItemIndex === index,
|
|
356
|
+
className: option.className,
|
|
357
|
+
id: "select-multi-typeahead-" + option.value.replace(' ', '-'),
|
|
172
358
|
isDisabled: option.isDisabled
|
|
173
|
-
}, option
|
|
174
|
-
|
|
359
|
+
}, option, {
|
|
360
|
+
ref: null
|
|
361
|
+
}));
|
|
362
|
+
})))));
|
|
175
363
|
};
|
|
176
364
|
|
|
177
365
|
exports.FormMultiSelectInput = FormMultiSelectInput;
|
package/dist/index.modern.js
CHANGED
|
@@ -1,26 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import { ValidatedOptions, Select,
|
|
2
|
+
import React__default from 'react';
|
|
3
|
+
import { ValidatedOptions, Select, SelectList, SelectOption, MenuToggle, TextInputGroup, TextInputGroupMain, ChipGroup, Chip, TextInputGroupUtilities, Button } from '@patternfly/react-core';
|
|
4
4
|
import { FormGroupContainer } from '@rhc-shared-components/form-group-container';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
function _extends() {
|
|
8
|
-
_extends = Object.assign || function (target) {
|
|
9
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
10
|
-
var source = arguments[i];
|
|
11
|
-
|
|
12
|
-
for (var key in source) {
|
|
13
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
14
|
-
target[key] = source[key];
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return target;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
return _extends.apply(this, arguments);
|
|
23
|
-
}
|
|
5
|
+
import { useField, useFormikContext } from 'formik';
|
|
24
6
|
|
|
25
7
|
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
26
8
|
if (source == null) return {};
|
|
@@ -37,105 +19,287 @@ function _objectWithoutPropertiesLoose(source, excluded) {
|
|
|
37
19
|
return target;
|
|
38
20
|
}
|
|
39
21
|
|
|
40
|
-
|
|
22
|
+
/******************************************************************************
|
|
23
|
+
Copyright (c) Microsoft Corporation.
|
|
24
|
+
|
|
25
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
26
|
+
purpose with or without fee is hereby granted.
|
|
27
|
+
|
|
28
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
29
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
30
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
31
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
32
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
33
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
34
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
35
|
+
***************************************************************************** */
|
|
36
|
+
|
|
37
|
+
function __rest(s, e) {
|
|
38
|
+
var t = {};
|
|
39
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
40
|
+
t[p] = s[p];
|
|
41
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
42
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
43
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
44
|
+
t[p[i]] = s[p[i]];
|
|
45
|
+
}
|
|
46
|
+
return t;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let currentId = 0;
|
|
55
|
+
/**
|
|
56
|
+
* Factory to create Icon class components for consumers
|
|
57
|
+
*/
|
|
58
|
+
function createIcon({ name, xOffset = 0, yOffset = 0, width, height, svgPath }) {
|
|
59
|
+
var _a;
|
|
60
|
+
return _a = class SVGIcon extends React.Component {
|
|
61
|
+
constructor() {
|
|
62
|
+
super(...arguments);
|
|
63
|
+
this.id = `icon-title-${currentId++}`;
|
|
64
|
+
}
|
|
65
|
+
render() {
|
|
66
|
+
const _a = this.props, { title, className } = _a, props = __rest(_a, ["title", "className"]);
|
|
67
|
+
const classes = className ? `pf-v5-svg ${className}` : 'pf-v5-svg';
|
|
68
|
+
const hasTitle = Boolean(title);
|
|
69
|
+
const viewBox = [xOffset, yOffset, width, height].join(' ');
|
|
70
|
+
return (React.createElement("svg", Object.assign({ className: classes, viewBox: viewBox, fill: "currentColor", "aria-labelledby": hasTitle ? this.id : null, "aria-hidden": hasTitle ? null : true, role: "img", width: "1em", height: "1em" }, props),
|
|
71
|
+
hasTitle && React.createElement("title", { id: this.id }, title),
|
|
72
|
+
React.createElement("path", { d: svgPath })));
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
_a.displayName = name,
|
|
76
|
+
_a;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const TimesIconConfig = {
|
|
80
|
+
name: 'TimesIcon',
|
|
81
|
+
height: 512,
|
|
82
|
+
width: 352,
|
|
83
|
+
svgPath: 'M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z',
|
|
84
|
+
yOffset: 0,
|
|
85
|
+
xOffset: 0,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const TimesIcon = createIcon(TimesIconConfig);
|
|
89
|
+
|
|
90
|
+
var TimesIcon$1 = TimesIcon;
|
|
91
|
+
|
|
92
|
+
const _excluded = ["label", "isRequired", "children", "selectMenuOptions", "ariaLabel", "placeholder", "helperText", "maxHeight", "isDisabled", "classNames", "menuAppendTo", "extraProps"];
|
|
41
93
|
|
|
42
94
|
const FormMultiSelectInput = _ref => {
|
|
43
95
|
let {
|
|
44
96
|
label,
|
|
45
97
|
isRequired,
|
|
46
|
-
|
|
98
|
+
selectMenuOptions,
|
|
47
99
|
ariaLabel = 'Select Input',
|
|
48
100
|
placeholder,
|
|
49
101
|
helperText,
|
|
50
|
-
maxHeight,
|
|
51
102
|
isDisabled,
|
|
52
|
-
|
|
53
|
-
menuAppendTo,
|
|
54
|
-
extraProps
|
|
103
|
+
menuAppendTo
|
|
55
104
|
} = _ref,
|
|
56
105
|
rest = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
57
106
|
|
|
58
107
|
const [, meta] = useField(rest);
|
|
59
|
-
const {
|
|
60
|
-
value
|
|
61
|
-
} = meta;
|
|
62
108
|
const {
|
|
63
109
|
isSubmitting,
|
|
64
110
|
setFieldValue,
|
|
65
111
|
setFieldTouched
|
|
66
112
|
} = useFormikContext();
|
|
67
|
-
const [isOpen, setIsOpen] =
|
|
113
|
+
const [isOpen, setIsOpen] = React__default.useState(false);
|
|
114
|
+
const [inputValue, setInputValue] = React__default.useState('');
|
|
115
|
+
const [selected, setSelected] = React__default.useState([]);
|
|
116
|
+
const [selectOptions, setSelectOptions] = React__default.useState(selectMenuOptions);
|
|
117
|
+
const [focusedItemIndex, setFocusedItemIndex] = React__default.useState(null);
|
|
118
|
+
const [activeItem, setActiveItem] = React__default.useState(null);
|
|
119
|
+
const textInputRef = React__default.useRef();
|
|
120
|
+
React__default.useEffect(() => {
|
|
121
|
+
let newSelectOptions = selectMenuOptions; // Filter menu items based on the text input value when one exists
|
|
122
|
+
|
|
123
|
+
if (inputValue) {
|
|
124
|
+
newSelectOptions = selectMenuOptions.filter(menuItem => String(menuItem.children).toLowerCase().includes(inputValue.toLowerCase())); // When no options are found after filtering, display 'No results found'
|
|
125
|
+
|
|
126
|
+
if (!newSelectOptions.length) {
|
|
127
|
+
newSelectOptions = [{
|
|
128
|
+
isDisabled: false,
|
|
129
|
+
children: `No results found for "${inputValue}"`,
|
|
130
|
+
value: 'no results'
|
|
131
|
+
}];
|
|
132
|
+
} // Open the menu when the input value changes and the new value is not empty
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if (!isOpen) {
|
|
136
|
+
setIsOpen(true);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
setSelectOptions(newSelectOptions);
|
|
141
|
+
setFocusedItemIndex(null);
|
|
142
|
+
setActiveItem(null);
|
|
143
|
+
}, [inputValue]);
|
|
144
|
+
|
|
145
|
+
const handleMenuArrowKeys = key => {
|
|
146
|
+
let indexToFocus = 0;
|
|
147
|
+
|
|
148
|
+
if (isOpen) {
|
|
149
|
+
if (key === 'ArrowUp') {
|
|
150
|
+
// When no index is set or at the first index, focus to the last, otherwise decrement focus index
|
|
151
|
+
if (focusedItemIndex === null || focusedItemIndex === 0) {
|
|
152
|
+
indexToFocus = selectOptions.length - 1;
|
|
153
|
+
} else {
|
|
154
|
+
indexToFocus = focusedItemIndex - 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (key === 'ArrowDown') {
|
|
159
|
+
// When no index is set or at the last index, focus to the first, otherwise increment focus index
|
|
160
|
+
if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
|
|
161
|
+
indexToFocus = 0;
|
|
162
|
+
} else {
|
|
163
|
+
indexToFocus = focusedItemIndex + 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
68
166
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
167
|
+
setFocusedItemIndex(indexToFocus);
|
|
168
|
+
const focusedItem = selectOptions.filter(option => !option.isDisabled)[indexToFocus];
|
|
169
|
+
setActiveItem(`select-multi-typeahead-${focusedItem.value.replace(' ', '-')}`);
|
|
170
|
+
}
|
|
73
171
|
};
|
|
74
172
|
|
|
75
|
-
const
|
|
76
|
-
|
|
173
|
+
const onInputKeyDown = event => {
|
|
174
|
+
const enabledMenuItems = selectOptions.filter(menuItem => !menuItem.isDisabled);
|
|
175
|
+
const [firstMenuItem] = enabledMenuItems;
|
|
176
|
+
const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
|
|
77
177
|
|
|
78
|
-
|
|
79
|
-
|
|
178
|
+
switch (event.key) {
|
|
179
|
+
// Select the first available option
|
|
180
|
+
case 'Enter':
|
|
181
|
+
if (!isOpen) {
|
|
182
|
+
setIsOpen(prevIsOpen => !prevIsOpen);
|
|
183
|
+
} else if (isOpen && focusedItem.value !== 'no results') {
|
|
184
|
+
onSelect(focusedItem.value);
|
|
185
|
+
}
|
|
80
186
|
|
|
81
|
-
|
|
82
|
-
isReadOnly: !!disabledItems.find(item => item.value === currentValue),
|
|
83
|
-
key: currentValue,
|
|
84
|
-
onClick: event => onSelect(event, currentValue)
|
|
85
|
-
}, currentValue)));
|
|
187
|
+
break;
|
|
86
188
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
189
|
+
case 'Tab':
|
|
190
|
+
case 'Escape':
|
|
191
|
+
setIsOpen(false);
|
|
192
|
+
setActiveItem(null);
|
|
193
|
+
break;
|
|
90
194
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
195
|
+
case 'ArrowUp':
|
|
196
|
+
case 'ArrowDown':
|
|
197
|
+
event.preventDefault();
|
|
198
|
+
handleMenuArrowKeys(event.key);
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
96
201
|
};
|
|
97
202
|
|
|
98
|
-
const
|
|
203
|
+
const onToggleClick = () => {
|
|
99
204
|
setIsOpen(!isOpen);
|
|
100
205
|
};
|
|
101
206
|
|
|
102
|
-
|
|
207
|
+
const onTextInputChange = (_event, value) => {
|
|
208
|
+
setInputValue(value);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const onSelect = value => {
|
|
212
|
+
var _textInputRef$current;
|
|
213
|
+
|
|
214
|
+
// eslint-disable-next-line no-console
|
|
215
|
+
console.log('selected', value);
|
|
216
|
+
|
|
217
|
+
if (value && value !== 'no results') {
|
|
218
|
+
const selectedValues = selected.includes(value) ? selected.filter(selection => selection !== value) : [...selected, value];
|
|
219
|
+
setSelected(selectedValues);
|
|
220
|
+
setFieldTouched(rest.name, true, false);
|
|
221
|
+
setFieldValue(rest.name, selectedValues, true);
|
|
222
|
+
} // eslint-disable-next-line no-unused-expressions
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
textInputRef == null ? void 0 : (_textInputRef$current = textInputRef.current) == null ? void 0 : _textInputRef$current.focus();
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const toggle = toggleRef => React__default.createElement(MenuToggle, {
|
|
229
|
+
variant: 'typeahead',
|
|
230
|
+
onClick: onToggleClick,
|
|
231
|
+
innerRef: toggleRef,
|
|
232
|
+
isExpanded: isOpen,
|
|
233
|
+
isDisabled: isSubmitting || isDisabled,
|
|
234
|
+
isFullWidth: true
|
|
235
|
+
}, React__default.createElement(TextInputGroup, {
|
|
236
|
+
isPlain: true
|
|
237
|
+
}, React__default.createElement(TextInputGroupMain, Object.assign({
|
|
238
|
+
value: inputValue,
|
|
239
|
+
onClick: onToggleClick,
|
|
240
|
+
onChange: onTextInputChange,
|
|
241
|
+
onKeyDown: onInputKeyDown,
|
|
242
|
+
id: 'multi-typeahead-select-input',
|
|
243
|
+
autoComplete: 'off',
|
|
244
|
+
innerRef: textInputRef,
|
|
245
|
+
placeholder: placeholder
|
|
246
|
+
}, activeItem && {
|
|
247
|
+
'aria-activedescendant': activeItem
|
|
248
|
+
}, {
|
|
249
|
+
role: 'combobox',
|
|
250
|
+
isExpanded: isOpen,
|
|
251
|
+
"aria-controls": 'select-multi-typeahead-listbox'
|
|
252
|
+
}), React__default.createElement(ChipGroup, {
|
|
253
|
+
"aria-label": 'Current selections'
|
|
254
|
+
}, selected.map((selection, index) => React__default.createElement(Chip, {
|
|
255
|
+
key: index,
|
|
256
|
+
onClick: ev => {
|
|
257
|
+
ev.stopPropagation();
|
|
258
|
+
onSelect(selection);
|
|
259
|
+
}
|
|
260
|
+
}, selection)))), React__default.createElement(TextInputGroupUtilities, null, selected.length > 0 && React__default.createElement(Button, {
|
|
261
|
+
variant: 'plain',
|
|
262
|
+
onClick: () => {
|
|
263
|
+
var _textInputRef$current2;
|
|
264
|
+
|
|
265
|
+
setInputValue('');
|
|
266
|
+
setSelected([]); // eslint-disable-next-line no-unused-expressions
|
|
267
|
+
|
|
268
|
+
textInputRef == null ? void 0 : (_textInputRef$current2 = textInputRef.current) == null ? void 0 : _textInputRef$current2.focus();
|
|
269
|
+
},
|
|
270
|
+
"aria-label": 'Clear input value'
|
|
271
|
+
}, React__default.createElement(TimesIcon$1, {
|
|
272
|
+
"aria-hidden": true
|
|
273
|
+
})))));
|
|
274
|
+
|
|
275
|
+
return React__default.createElement(React__default.Fragment, null, React__default.createElement(FormGroupContainer, {
|
|
103
276
|
validated: meta.touched && meta.error ? ValidatedOptions.error : ValidatedOptions.default,
|
|
104
277
|
helperTextInvalid: meta.error,
|
|
105
278
|
isRequired: isRequired,
|
|
106
279
|
fieldId: rest.name,
|
|
107
280
|
label: label,
|
|
108
281
|
helperText: helperText
|
|
109
|
-
},
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
onClear: onClear,
|
|
117
|
-
selections: value,
|
|
118
|
-
isOpen: isOpen
|
|
119
|
-
}, maxHeight && {
|
|
120
|
-
maxHeight
|
|
121
|
-
}, {
|
|
122
|
-
isDisabled: isSubmitting || isDisabled,
|
|
123
|
-
"aria-label": ariaLabel
|
|
124
|
-
}, disabledItems && {
|
|
125
|
-
chipGroupComponent: React.createElement(CustomChipGroup, null)
|
|
126
|
-
}, classNames && {
|
|
127
|
-
className: classNames
|
|
282
|
+
}, React__default.createElement(Select, Object.assign({
|
|
283
|
+
id: 'multi-typeahead-select',
|
|
284
|
+
isOpen: isOpen,
|
|
285
|
+
selected: selected,
|
|
286
|
+
onSelect: (_ev, selection) => onSelect(selection),
|
|
287
|
+
onOpenChange: () => setIsOpen(false),
|
|
288
|
+
toggle: toggle
|
|
128
289
|
}, menuAppendTo && {
|
|
129
290
|
menuAppendTo
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
291
|
+
}), React__default.createElement(SelectList, {
|
|
292
|
+
isAriaMultiselectable: true,
|
|
293
|
+
id: 'select-multi-typeahead-listbox'
|
|
294
|
+
}, selectOptions.map((option, index) => React__default.createElement(SelectOption, Object.assign({
|
|
295
|
+
key: option.value || option.children,
|
|
296
|
+
isFocused: focusedItemIndex === index,
|
|
297
|
+
className: option.className,
|
|
298
|
+
id: `select-multi-typeahead-${option.value.replace(' ', '-')}`,
|
|
137
299
|
isDisabled: option.isDisabled
|
|
138
|
-
}, option
|
|
300
|
+
}, option, {
|
|
301
|
+
ref: null
|
|
302
|
+
})))))));
|
|
139
303
|
};
|
|
140
304
|
|
|
141
305
|
export { FormMultiSelectInput };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rhc-shared-components/form-multi-select-component",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "project description",
|
|
5
5
|
"author": "shkale",
|
|
6
6
|
"license": "MIT",
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"typescript": "^3.7.5"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@patternfly/react-core": "
|
|
66
|
-
"@rhc-shared-components/form-group-container": "^0.
|
|
65
|
+
"@patternfly/react-core": "5.3.0",
|
|
66
|
+
"@rhc-shared-components/form-group-container": "^0.4.0",
|
|
67
67
|
"@types/lodash": "^4.14.177",
|
|
68
68
|
"formik": "^2.1.4",
|
|
69
69
|
"lodash": "^4.17.21",
|