@rhc-shared-components/form-multi-select-component 0.1.2 → 0.1.4
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 +5 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +290 -94
- package/dist/index.modern.js +252 -79
- 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.
|
|
24
|
-
export
|
|
17
|
+
declare const FormMultiSelectInput: React.FunctionComponent<FormMultiSelectInputProps>;
|
|
18
|
+
export { FormMultiSelectInput, SelectOptionProps as IMultiSelectInputOptionProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import FormMultiSelectInput,
|
|
2
|
-
export { FormMultiSelectInput,
|
|
1
|
+
import { FormMultiSelectInput, IMultiSelectInputOptionProps } from './FormMultiSelectInput';
|
|
2
|
+
export { FormMultiSelectInput, IMultiSelectInputOptionProps };
|
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,21 +41,86 @@ 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),
|
|
@@ -84,94 +133,241 @@ var FormMultiSelectInput = function FormMultiSelectInput(_ref) {
|
|
|
84
133
|
setFieldValue = _useFormikContext.setFieldValue,
|
|
85
134
|
setFieldTouched = _useFormikContext.setFieldTouched;
|
|
86
135
|
|
|
87
|
-
var _React$useState =
|
|
136
|
+
var _React$useState = React__default["default"].useState(false),
|
|
88
137
|
isOpen = _React$useState[0],
|
|
89
138
|
setIsOpen = _React$useState[1];
|
|
90
139
|
|
|
91
|
-
var
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
140
|
+
var _React$useState2 = React__default["default"].useState(''),
|
|
141
|
+
inputValue = _React$useState2[0],
|
|
142
|
+
setInputValue = _React$useState2[1];
|
|
143
|
+
|
|
144
|
+
var _React$useState3 = React__default["default"].useState([]),
|
|
145
|
+
selected = _React$useState3[0],
|
|
146
|
+
setSelected = _React$useState3[1];
|
|
147
|
+
|
|
148
|
+
var _React$useState4 = React__default["default"].useState(selectMenuOptions),
|
|
149
|
+
selectOptions = _React$useState4[0],
|
|
150
|
+
setSelectOptions = _React$useState4[1];
|
|
151
|
+
|
|
152
|
+
var _React$useState5 = React__default["default"].useState(null),
|
|
153
|
+
focusedItemIndex = _React$useState5[0],
|
|
154
|
+
setFocusedItemIndex = _React$useState5[1];
|
|
155
|
+
|
|
156
|
+
var _React$useState6 = React__default["default"].useState(null),
|
|
157
|
+
activeItem = _React$useState6[0],
|
|
158
|
+
setActiveItem = _React$useState6[1];
|
|
159
|
+
|
|
160
|
+
var textInputRef = React__default["default"].useRef();
|
|
161
|
+
React__default["default"].useEffect(function () {
|
|
162
|
+
var newSelectOptions = selectMenuOptions; // Filter menu items based on the text input value when one exists
|
|
163
|
+
|
|
164
|
+
if (inputValue) {
|
|
165
|
+
newSelectOptions = selectMenuOptions.filter(function (menuItem) {
|
|
166
|
+
return String(menuItem.children).toLowerCase().includes(inputValue.toLowerCase());
|
|
167
|
+
}); // When no options are found after filtering, display 'No results found'
|
|
168
|
+
|
|
169
|
+
if (!newSelectOptions.length) {
|
|
170
|
+
newSelectOptions = [{
|
|
171
|
+
isDisabled: false,
|
|
172
|
+
children: "No results found for \"" + inputValue + "\"",
|
|
173
|
+
value: 'no results'
|
|
174
|
+
}];
|
|
175
|
+
} // Open the menu when the input value changes and the new value is not empty
|
|
176
|
+
|
|
98
177
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
178
|
+
if (!isOpen) {
|
|
179
|
+
setIsOpen(true);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
setSelectOptions(newSelectOptions);
|
|
184
|
+
setFocusedItemIndex(null);
|
|
185
|
+
setActiveItem(null);
|
|
186
|
+
}, [inputValue]);
|
|
187
|
+
React__default["default"].useEffect(function () {
|
|
188
|
+
if ((value == null ? void 0 : value.length) > 0) {
|
|
189
|
+
setFieldValue(rest.name, value, true);
|
|
190
|
+
setSelected(value);
|
|
191
|
+
}
|
|
192
|
+
}, []);
|
|
193
|
+
|
|
194
|
+
var handleMenuArrowKeys = function handleMenuArrowKeys(key) {
|
|
195
|
+
var indexToFocus = 0;
|
|
196
|
+
|
|
197
|
+
if (isOpen) {
|
|
198
|
+
if (key === 'ArrowUp') {
|
|
199
|
+
// When no index is set or at the first index, focus to the last, otherwise decrement focus index
|
|
200
|
+
if (focusedItemIndex === null || focusedItemIndex === 0) {
|
|
201
|
+
indexToFocus = selectOptions.length - 1;
|
|
202
|
+
} else {
|
|
203
|
+
indexToFocus = focusedItemIndex - 1;
|
|
114
204
|
}
|
|
115
|
-
}
|
|
116
|
-
}));
|
|
117
|
-
};
|
|
205
|
+
}
|
|
118
206
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
207
|
+
if (key === 'ArrowDown') {
|
|
208
|
+
// When no index is set or at the last index, focus to the first, otherwise increment focus index
|
|
209
|
+
if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
|
|
210
|
+
indexToFocus = 0;
|
|
211
|
+
} else {
|
|
212
|
+
indexToFocus = focusedItemIndex + 1;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
setFocusedItemIndex(indexToFocus);
|
|
217
|
+
var focusedItem = selectOptions.filter(function (option) {
|
|
218
|
+
return !option.isDisabled;
|
|
219
|
+
})[indexToFocus];
|
|
220
|
+
setActiveItem("select-multi-typeahead-" + focusedItem.value.replace(' ', '-'));
|
|
221
|
+
}
|
|
222
|
+
};
|
|
122
223
|
|
|
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);
|
|
224
|
+
var onInputKeyDown = function onInputKeyDown(event) {
|
|
225
|
+
var enabledMenuItems = selectOptions.filter(function (menuItem) {
|
|
226
|
+
return !menuItem.isDisabled;
|
|
127
227
|
});
|
|
128
|
-
|
|
129
|
-
|
|
228
|
+
var firstMenuItem = enabledMenuItems[0];
|
|
229
|
+
var focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
|
|
230
|
+
|
|
231
|
+
switch (event.key) {
|
|
232
|
+
// Select the first available option
|
|
233
|
+
case 'Enter':
|
|
234
|
+
if (!isOpen) {
|
|
235
|
+
setIsOpen(function (prevIsOpen) {
|
|
236
|
+
return !prevIsOpen;
|
|
237
|
+
});
|
|
238
|
+
} else if (isOpen && focusedItem.value !== 'no results') {
|
|
239
|
+
_onSelect(focusedItem.value);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
break;
|
|
243
|
+
|
|
244
|
+
case 'Tab':
|
|
245
|
+
case 'Escape':
|
|
246
|
+
setIsOpen(false);
|
|
247
|
+
setActiveItem(null);
|
|
248
|
+
break;
|
|
249
|
+
|
|
250
|
+
case 'ArrowUp':
|
|
251
|
+
case 'ArrowDown':
|
|
252
|
+
event.preventDefault();
|
|
253
|
+
handleMenuArrowKeys(event.key);
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
130
256
|
};
|
|
131
257
|
|
|
132
|
-
var
|
|
258
|
+
var onToggleClick = function onToggleClick() {
|
|
133
259
|
setIsOpen(!isOpen);
|
|
134
260
|
};
|
|
135
261
|
|
|
136
|
-
|
|
262
|
+
var onTextInputChange = function onTextInputChange(_event, value) {
|
|
263
|
+
setInputValue(value);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
var _onSelect = function onSelect(value) {
|
|
267
|
+
var _textInputRef$current;
|
|
268
|
+
|
|
269
|
+
// eslint-disable-next-line no-console
|
|
270
|
+
console.log('selected', value);
|
|
271
|
+
|
|
272
|
+
if (value && value !== 'no results') {
|
|
273
|
+
var selectedValues = selected.includes(value) ? selected.filter(function (selection) {
|
|
274
|
+
return selection !== value;
|
|
275
|
+
}) : [].concat(selected, [value]);
|
|
276
|
+
setSelected(selectedValues);
|
|
277
|
+
setFieldTouched(rest.name, true, false);
|
|
278
|
+
setFieldValue(rest.name, selectedValues, true);
|
|
279
|
+
} // eslint-disable-next-line no-unused-expressions
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
textInputRef == null ? void 0 : (_textInputRef$current = textInputRef.current) == null ? void 0 : _textInputRef$current.focus();
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
var toggle = function toggle(toggleRef) {
|
|
286
|
+
return React__default["default"].createElement(reactCore.MenuToggle, {
|
|
287
|
+
variant: 'typeahead',
|
|
288
|
+
onClick: onToggleClick,
|
|
289
|
+
innerRef: toggleRef,
|
|
290
|
+
isExpanded: isOpen,
|
|
291
|
+
isDisabled: isSubmitting || isDisabled,
|
|
292
|
+
isFullWidth: true
|
|
293
|
+
}, React__default["default"].createElement(reactCore.TextInputGroup, {
|
|
294
|
+
isPlain: true
|
|
295
|
+
}, React__default["default"].createElement(reactCore.TextInputGroupMain, Object.assign({
|
|
296
|
+
value: inputValue,
|
|
297
|
+
onClick: onToggleClick,
|
|
298
|
+
onChange: onTextInputChange,
|
|
299
|
+
onKeyDown: onInputKeyDown,
|
|
300
|
+
id: 'multi-typeahead-select-input',
|
|
301
|
+
autoComplete: 'off',
|
|
302
|
+
innerRef: textInputRef,
|
|
303
|
+
placeholder: placeholder
|
|
304
|
+
}, activeItem && {
|
|
305
|
+
'aria-activedescendant': activeItem
|
|
306
|
+
}, {
|
|
307
|
+
role: 'combobox',
|
|
308
|
+
isExpanded: isOpen,
|
|
309
|
+
"aria-controls": 'select-multi-typeahead-listbox'
|
|
310
|
+
}), React__default["default"].createElement(reactCore.ChipGroup, {
|
|
311
|
+
"aria-label": 'Current selections'
|
|
312
|
+
}, selected.map(function (selection, index) {
|
|
313
|
+
return React__default["default"].createElement(reactCore.Chip, {
|
|
314
|
+
key: index,
|
|
315
|
+
onClick: function onClick(ev) {
|
|
316
|
+
ev.stopPropagation();
|
|
317
|
+
|
|
318
|
+
_onSelect(selection);
|
|
319
|
+
}
|
|
320
|
+
}, selection);
|
|
321
|
+
}))), React__default["default"].createElement(reactCore.TextInputGroupUtilities, null, selected.length > 0 && React__default["default"].createElement(reactCore.Button, {
|
|
322
|
+
variant: 'plain',
|
|
323
|
+
onClick: function onClick() {
|
|
324
|
+
var _textInputRef$current2;
|
|
325
|
+
|
|
326
|
+
setInputValue('');
|
|
327
|
+
setSelected([]); // eslint-disable-next-line no-unused-expressions
|
|
328
|
+
|
|
329
|
+
textInputRef == null ? void 0 : (_textInputRef$current2 = textInputRef.current) == null ? void 0 : _textInputRef$current2.focus();
|
|
330
|
+
},
|
|
331
|
+
"aria-label": 'Clear input value'
|
|
332
|
+
}, React__default["default"].createElement(TimesIcon$1, {
|
|
333
|
+
"aria-hidden": true
|
|
334
|
+
})))));
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
return React__default["default"].createElement(React__default["default"].Fragment, null, React__default["default"].createElement(formGroupContainer.FormGroupContainer, {
|
|
137
338
|
validated: meta.touched && meta.error ? reactCore.ValidatedOptions.error : reactCore.ValidatedOptions["default"],
|
|
138
339
|
helperTextInvalid: meta.error,
|
|
139
340
|
isRequired: isRequired,
|
|
140
341
|
fieldId: rest.name,
|
|
141
342
|
label: label,
|
|
142
343
|
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
|
|
344
|
+
}, React__default["default"].createElement(reactCore.Select, Object.assign({
|
|
345
|
+
id: 'multi-typeahead-select',
|
|
346
|
+
isOpen: isOpen,
|
|
347
|
+
selected: selected,
|
|
348
|
+
onSelect: function onSelect(_ev, selection) {
|
|
349
|
+
return _onSelect(selection);
|
|
350
|
+
},
|
|
351
|
+
onOpenChange: function onOpenChange() {
|
|
352
|
+
return setIsOpen(false);
|
|
353
|
+
},
|
|
354
|
+
toggle: toggle
|
|
162
355
|
}, menuAppendTo && {
|
|
163
356
|
menuAppendTo: menuAppendTo
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
357
|
+
}), React__default["default"].createElement(reactCore.SelectList, {
|
|
358
|
+
isAriaMultiselectable: true,
|
|
359
|
+
id: 'select-multi-typeahead-listbox'
|
|
360
|
+
}, selectOptions.map(function (option, index) {
|
|
361
|
+
return React__default["default"].createElement(reactCore.SelectOption, Object.assign({
|
|
362
|
+
key: option.value || option.children,
|
|
363
|
+
isFocused: focusedItemIndex === index,
|
|
364
|
+
className: option.className,
|
|
365
|
+
id: "select-multi-typeahead-" + option.value.replace(' ', '-'),
|
|
366
|
+
isDisabled: option.isDisabled || isSubmitting
|
|
367
|
+
}, option, {
|
|
368
|
+
ref: null
|
|
369
|
+
}));
|
|
370
|
+
})))));
|
|
175
371
|
};
|
|
176
372
|
|
|
177
373
|
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,21 +19,88 @@ 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
|
|
|
@@ -64,78 +113,202 @@ const FormMultiSelectInput = _ref => {
|
|
|
64
113
|
setFieldValue,
|
|
65
114
|
setFieldTouched
|
|
66
115
|
} = useFormikContext();
|
|
67
|
-
const [isOpen, setIsOpen] =
|
|
116
|
+
const [isOpen, setIsOpen] = React__default.useState(false);
|
|
117
|
+
const [inputValue, setInputValue] = React__default.useState('');
|
|
118
|
+
const [selected, setSelected] = React__default.useState([]);
|
|
119
|
+
const [selectOptions, setSelectOptions] = React__default.useState(selectMenuOptions);
|
|
120
|
+
const [focusedItemIndex, setFocusedItemIndex] = React__default.useState(null);
|
|
121
|
+
const [activeItem, setActiveItem] = React__default.useState(null);
|
|
122
|
+
const textInputRef = React__default.useRef();
|
|
123
|
+
React__default.useEffect(() => {
|
|
124
|
+
let newSelectOptions = selectMenuOptions; // Filter menu items based on the text input value when one exists
|
|
125
|
+
|
|
126
|
+
if (inputValue) {
|
|
127
|
+
newSelectOptions = selectMenuOptions.filter(menuItem => String(menuItem.children).toLowerCase().includes(inputValue.toLowerCase())); // When no options are found after filtering, display 'No results found'
|
|
128
|
+
|
|
129
|
+
if (!newSelectOptions.length) {
|
|
130
|
+
newSelectOptions = [{
|
|
131
|
+
isDisabled: false,
|
|
132
|
+
children: `No results found for "${inputValue}"`,
|
|
133
|
+
value: 'no results'
|
|
134
|
+
}];
|
|
135
|
+
} // Open the menu when the input value changes and the new value is not empty
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if (!isOpen) {
|
|
139
|
+
setIsOpen(true);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
setSelectOptions(newSelectOptions);
|
|
144
|
+
setFocusedItemIndex(null);
|
|
145
|
+
setActiveItem(null);
|
|
146
|
+
}, [inputValue]);
|
|
147
|
+
React__default.useEffect(() => {
|
|
148
|
+
if ((value == null ? void 0 : value.length) > 0) {
|
|
149
|
+
setFieldValue(rest.name, value, true);
|
|
150
|
+
setSelected(value);
|
|
151
|
+
}
|
|
152
|
+
}, []);
|
|
153
|
+
|
|
154
|
+
const handleMenuArrowKeys = key => {
|
|
155
|
+
let indexToFocus = 0;
|
|
156
|
+
|
|
157
|
+
if (isOpen) {
|
|
158
|
+
if (key === 'ArrowUp') {
|
|
159
|
+
// When no index is set or at the first index, focus to the last, otherwise decrement focus index
|
|
160
|
+
if (focusedItemIndex === null || focusedItemIndex === 0) {
|
|
161
|
+
indexToFocus = selectOptions.length - 1;
|
|
162
|
+
} else {
|
|
163
|
+
indexToFocus = focusedItemIndex - 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (key === 'ArrowDown') {
|
|
168
|
+
// When no index is set or at the last index, focus to the first, otherwise increment focus index
|
|
169
|
+
if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
|
|
170
|
+
indexToFocus = 0;
|
|
171
|
+
} else {
|
|
172
|
+
indexToFocus = focusedItemIndex + 1;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
68
175
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
176
|
+
setFocusedItemIndex(indexToFocus);
|
|
177
|
+
const focusedItem = selectOptions.filter(option => !option.isDisabled)[indexToFocus];
|
|
178
|
+
setActiveItem(`select-multi-typeahead-${focusedItem.value.replace(' ', '-')}`);
|
|
179
|
+
}
|
|
73
180
|
};
|
|
74
181
|
|
|
75
|
-
const
|
|
76
|
-
|
|
182
|
+
const onInputKeyDown = event => {
|
|
183
|
+
const enabledMenuItems = selectOptions.filter(menuItem => !menuItem.isDisabled);
|
|
184
|
+
const [firstMenuItem] = enabledMenuItems;
|
|
185
|
+
const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
|
|
77
186
|
|
|
78
|
-
|
|
79
|
-
|
|
187
|
+
switch (event.key) {
|
|
188
|
+
// Select the first available option
|
|
189
|
+
case 'Enter':
|
|
190
|
+
if (!isOpen) {
|
|
191
|
+
setIsOpen(prevIsOpen => !prevIsOpen);
|
|
192
|
+
} else if (isOpen && focusedItem.value !== 'no results') {
|
|
193
|
+
onSelect(focusedItem.value);
|
|
194
|
+
}
|
|
80
195
|
|
|
81
|
-
|
|
82
|
-
isReadOnly: !!disabledItems.find(item => item.value === currentValue),
|
|
83
|
-
key: currentValue,
|
|
84
|
-
onClick: event => onSelect(event, currentValue)
|
|
85
|
-
}, currentValue)));
|
|
196
|
+
break;
|
|
86
197
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
198
|
+
case 'Tab':
|
|
199
|
+
case 'Escape':
|
|
200
|
+
setIsOpen(false);
|
|
201
|
+
setActiveItem(null);
|
|
202
|
+
break;
|
|
90
203
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
204
|
+
case 'ArrowUp':
|
|
205
|
+
case 'ArrowDown':
|
|
206
|
+
event.preventDefault();
|
|
207
|
+
handleMenuArrowKeys(event.key);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
96
210
|
};
|
|
97
211
|
|
|
98
|
-
const
|
|
212
|
+
const onToggleClick = () => {
|
|
99
213
|
setIsOpen(!isOpen);
|
|
100
214
|
};
|
|
101
215
|
|
|
102
|
-
|
|
216
|
+
const onTextInputChange = (_event, value) => {
|
|
217
|
+
setInputValue(value);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const onSelect = value => {
|
|
221
|
+
var _textInputRef$current;
|
|
222
|
+
|
|
223
|
+
// eslint-disable-next-line no-console
|
|
224
|
+
console.log('selected', value);
|
|
225
|
+
|
|
226
|
+
if (value && value !== 'no results') {
|
|
227
|
+
const selectedValues = selected.includes(value) ? selected.filter(selection => selection !== value) : [...selected, value];
|
|
228
|
+
setSelected(selectedValues);
|
|
229
|
+
setFieldTouched(rest.name, true, false);
|
|
230
|
+
setFieldValue(rest.name, selectedValues, true);
|
|
231
|
+
} // eslint-disable-next-line no-unused-expressions
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
textInputRef == null ? void 0 : (_textInputRef$current = textInputRef.current) == null ? void 0 : _textInputRef$current.focus();
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const toggle = toggleRef => React__default.createElement(MenuToggle, {
|
|
238
|
+
variant: 'typeahead',
|
|
239
|
+
onClick: onToggleClick,
|
|
240
|
+
innerRef: toggleRef,
|
|
241
|
+
isExpanded: isOpen,
|
|
242
|
+
isDisabled: isSubmitting || isDisabled,
|
|
243
|
+
isFullWidth: true
|
|
244
|
+
}, React__default.createElement(TextInputGroup, {
|
|
245
|
+
isPlain: true
|
|
246
|
+
}, React__default.createElement(TextInputGroupMain, Object.assign({
|
|
247
|
+
value: inputValue,
|
|
248
|
+
onClick: onToggleClick,
|
|
249
|
+
onChange: onTextInputChange,
|
|
250
|
+
onKeyDown: onInputKeyDown,
|
|
251
|
+
id: 'multi-typeahead-select-input',
|
|
252
|
+
autoComplete: 'off',
|
|
253
|
+
innerRef: textInputRef,
|
|
254
|
+
placeholder: placeholder
|
|
255
|
+
}, activeItem && {
|
|
256
|
+
'aria-activedescendant': activeItem
|
|
257
|
+
}, {
|
|
258
|
+
role: 'combobox',
|
|
259
|
+
isExpanded: isOpen,
|
|
260
|
+
"aria-controls": 'select-multi-typeahead-listbox'
|
|
261
|
+
}), React__default.createElement(ChipGroup, {
|
|
262
|
+
"aria-label": 'Current selections'
|
|
263
|
+
}, selected.map((selection, index) => React__default.createElement(Chip, {
|
|
264
|
+
key: index,
|
|
265
|
+
onClick: ev => {
|
|
266
|
+
ev.stopPropagation();
|
|
267
|
+
onSelect(selection);
|
|
268
|
+
}
|
|
269
|
+
}, selection)))), React__default.createElement(TextInputGroupUtilities, null, selected.length > 0 && React__default.createElement(Button, {
|
|
270
|
+
variant: 'plain',
|
|
271
|
+
onClick: () => {
|
|
272
|
+
var _textInputRef$current2;
|
|
273
|
+
|
|
274
|
+
setInputValue('');
|
|
275
|
+
setSelected([]); // eslint-disable-next-line no-unused-expressions
|
|
276
|
+
|
|
277
|
+
textInputRef == null ? void 0 : (_textInputRef$current2 = textInputRef.current) == null ? void 0 : _textInputRef$current2.focus();
|
|
278
|
+
},
|
|
279
|
+
"aria-label": 'Clear input value'
|
|
280
|
+
}, React__default.createElement(TimesIcon$1, {
|
|
281
|
+
"aria-hidden": true
|
|
282
|
+
})))));
|
|
283
|
+
|
|
284
|
+
return React__default.createElement(React__default.Fragment, null, React__default.createElement(FormGroupContainer, {
|
|
103
285
|
validated: meta.touched && meta.error ? ValidatedOptions.error : ValidatedOptions.default,
|
|
104
286
|
helperTextInvalid: meta.error,
|
|
105
287
|
isRequired: isRequired,
|
|
106
288
|
fieldId: rest.name,
|
|
107
289
|
label: label,
|
|
108
290
|
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
|
|
291
|
+
}, React__default.createElement(Select, Object.assign({
|
|
292
|
+
id: 'multi-typeahead-select',
|
|
293
|
+
isOpen: isOpen,
|
|
294
|
+
selected: selected,
|
|
295
|
+
onSelect: (_ev, selection) => onSelect(selection),
|
|
296
|
+
onOpenChange: () => setIsOpen(false),
|
|
297
|
+
toggle: toggle
|
|
128
298
|
}, menuAppendTo && {
|
|
129
299
|
menuAppendTo
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
300
|
+
}), React__default.createElement(SelectList, {
|
|
301
|
+
isAriaMultiselectable: true,
|
|
302
|
+
id: 'select-multi-typeahead-listbox'
|
|
303
|
+
}, selectOptions.map((option, index) => React__default.createElement(SelectOption, Object.assign({
|
|
304
|
+
key: option.value || option.children,
|
|
305
|
+
isFocused: focusedItemIndex === index,
|
|
306
|
+
className: option.className,
|
|
307
|
+
id: `select-multi-typeahead-${option.value.replace(' ', '-')}`,
|
|
308
|
+
isDisabled: option.isDisabled || isSubmitting
|
|
309
|
+
}, option, {
|
|
310
|
+
ref: null
|
|
311
|
+
})))))));
|
|
139
312
|
};
|
|
140
313
|
|
|
141
314
|
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.1.4",
|
|
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",
|