@rh-support/components 2.5.1 → 2.5.3

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.
Files changed (72) hide show
  1. package/lib/esm/Functional/CaseContactsSelectorExternal.d.ts +12 -7
  2. package/lib/esm/Functional/CaseContactsSelectorExternal.d.ts.map +1 -1
  3. package/lib/esm/Functional/CaseContactsSelectorExternal.js +67 -60
  4. package/lib/esm/OwnerTypeaheadDropdown/OwnerTypeaheadDropdown.d.ts +17 -0
  5. package/lib/esm/OwnerTypeaheadDropdown/OwnerTypeaheadDropdown.d.ts.map +1 -0
  6. package/lib/esm/OwnerTypeaheadDropdown/OwnerTypeaheadDropdown.js +98 -0
  7. package/lib/esm/OwnerTypeaheadDropdown/index.d.ts +2 -0
  8. package/lib/esm/OwnerTypeaheadDropdown/index.d.ts.map +1 -0
  9. package/lib/esm/OwnerTypeaheadDropdown/index.js +1 -0
  10. package/lib/esm/SingleSelectDropdown/SingleSelectDropdown.d.ts +27 -0
  11. package/lib/esm/SingleSelectDropdown/SingleSelectDropdown.d.ts.map +1 -0
  12. package/lib/esm/SingleSelectDropdown/SingleSelectDropdown.js +26 -0
  13. package/lib/esm/SingleSelectDropdown/index.d.ts +2 -0
  14. package/lib/esm/SingleSelectDropdown/index.d.ts.map +1 -0
  15. package/lib/esm/SingleSelectDropdown/index.js +1 -0
  16. package/lib/esm/TypeaheadDropdown/TypeaheadDropdown.d.ts +39 -0
  17. package/lib/esm/TypeaheadDropdown/TypeaheadDropdown.d.ts.map +1 -0
  18. package/lib/esm/TypeaheadDropdown/TypeaheadDropdown.js +218 -0
  19. package/lib/esm/TypeaheadDropdown/index.d.ts +2 -0
  20. package/lib/esm/TypeaheadDropdown/index.d.ts.map +1 -0
  21. package/lib/esm/TypeaheadDropdown/index.js +1 -0
  22. package/lib/esm/hooks/index.d.ts +2 -0
  23. package/lib/esm/hooks/index.d.ts.map +1 -1
  24. package/lib/esm/hooks/index.js +2 -0
  25. package/lib/esm/hooks/usePatternFlySingleSelectToggle.d.ts +16 -0
  26. package/lib/esm/hooks/usePatternFlySingleSelectToggle.d.ts.map +1 -0
  27. package/lib/esm/hooks/usePatternFlySingleSelectToggle.js +17 -0
  28. package/lib/esm/hooks/useProgressiveLoading.d.ts +10 -0
  29. package/lib/esm/hooks/useProgressiveLoading.d.ts.map +1 -0
  30. package/lib/esm/hooks/useProgressiveLoading.js +33 -0
  31. package/lib/esm/hooks/useSelectKeyboardNavigator.d.ts +9 -1
  32. package/lib/esm/hooks/useSelectKeyboardNavigator.d.ts.map +1 -1
  33. package/lib/esm/hooks/useSelectKeyboardNavigator.js +82 -3
  34. package/lib/esm/index.d.ts +2 -1
  35. package/lib/esm/index.d.ts.map +1 -1
  36. package/lib/esm/index.js +2 -1
  37. package/package.json +6 -8
  38. package/lib/esm/DropDownList/AsyncDropDownList.d.ts +0 -6
  39. package/lib/esm/DropDownList/AsyncDropDownList.d.ts.map +0 -1
  40. package/lib/esm/DropDownList/AsyncDropDownList.js +0 -6
  41. package/lib/esm/DropDownList/DropDownList.d.ts +0 -9
  42. package/lib/esm/DropDownList/DropDownList.d.ts.map +0 -1
  43. package/lib/esm/DropDownList/DropDownList.js +0 -116
  44. package/lib/esm/DropDownList/DropdownWrapper.d.ts +0 -16
  45. package/lib/esm/DropDownList/DropdownWrapper.d.ts.map +0 -1
  46. package/lib/esm/DropDownList/DropdownWrapper.js +0 -19
  47. package/lib/esm/DropDownList/MultiSelectDropdownList.d.ts +0 -4
  48. package/lib/esm/DropDownList/MultiSelectDropdownList.d.ts.map +0 -1
  49. package/lib/esm/DropDownList/MultiSelectDropdownList.js +0 -4
  50. package/lib/esm/DropDownList/Readme.md +0 -114
  51. package/lib/esm/DropDownList/SearchableList.d.ts +0 -14
  52. package/lib/esm/DropDownList/SearchableList.d.ts.map +0 -1
  53. package/lib/esm/DropDownList/SearchableList.js +0 -98
  54. package/lib/esm/DropDownList/SelectList.d.ts +0 -11
  55. package/lib/esm/DropDownList/SelectList.d.ts.map +0 -1
  56. package/lib/esm/DropDownList/SelectList.js +0 -35
  57. package/lib/esm/DropDownList/async.d.ts +0 -9
  58. package/lib/esm/DropDownList/async.d.ts.map +0 -1
  59. package/lib/esm/DropDownList/async.js +0 -111
  60. package/lib/esm/DropDownList/dropdownList.css +0 -59
  61. package/lib/esm/DropDownList/dropdownUtils.d.ts +0 -5
  62. package/lib/esm/DropDownList/dropdownUtils.d.ts.map +0 -1
  63. package/lib/esm/DropDownList/dropdownUtils.js +0 -23
  64. package/lib/esm/DropDownList/index.d.ts +0 -4
  65. package/lib/esm/DropDownList/index.d.ts.map +0 -1
  66. package/lib/esm/DropDownList/index.js +0 -3
  67. package/lib/esm/DropDownList/types.d.ts +0 -53
  68. package/lib/esm/DropDownList/types.d.ts.map +0 -1
  69. package/lib/esm/DropDownList/types.js +0 -1
  70. package/lib/esm/DropDownList/withMulti.d.ts +0 -5
  71. package/lib/esm/DropDownList/withMulti.d.ts.map +0 -1
  72. package/lib/esm/DropDownList/withMulti.js +0 -46
@@ -0,0 +1,218 @@
1
+ import { Button, Label, LabelGroup, MenuToggle, Select, SelectGroup, SelectList, SelectOption, Spinner, TextInputGroup, TextInputGroupMain, TextInputGroupUtilities, } from '@patternfly/react-core';
2
+ import TimesCircleIcon from '@patternfly/react-icons/dist/js/icons/times-circle-icon';
3
+ import { haltEvent } from '@rh-support/utils';
4
+ import concat from 'lodash/concat';
5
+ import filter from 'lodash/filter';
6
+ import find from 'lodash/find';
7
+ import isEmpty from 'lodash/isEmpty';
8
+ import isEqual from 'lodash/isEqual';
9
+ import isNumber from 'lodash/isNumber';
10
+ import isString from 'lodash/isString';
11
+ import React, { useEffect, useState } from 'react';
12
+ import { Highlighter } from 'react-bootstrap-typeahead';
13
+ import { useTranslation } from 'react-i18next';
14
+ import { useSelectKeyboardNavigator } from '../hooks/useSelectKeyboardNavigator';
15
+ export const VIEW_MORE_OPTION_VALUE = 'view-more-option-value';
16
+ export const ADD_NEW_OPTION_VALUE = 'new-option-value';
17
+ const VIEW_MORE_OPTION = { label: 'View more', value: VIEW_MORE_OPTION_VALUE };
18
+ const ADD_NEW_OPTION = { label: 'Create new option', value: ADD_NEW_OPTION_VALUE };
19
+ function isDropdownGroup(option) {
20
+ return option.groupLabel !== undefined;
21
+ }
22
+ function isDropdownOption(option) {
23
+ const typedOption = option;
24
+ return typedOption.value !== undefined && typedOption.label !== undefined;
25
+ }
26
+ function flattenOptions(options) {
27
+ const flattened = [];
28
+ for (const option of options) {
29
+ if (isDropdownGroup(option)) {
30
+ flattened.push(...option.options); // Only add the group's items
31
+ }
32
+ else if (isDropdownOption(option)) {
33
+ flattened.push(option); // Add standalone item
34
+ }
35
+ }
36
+ return flattened;
37
+ }
38
+ export function TypeaheadDropdown(props) {
39
+ var _a;
40
+ const { options, selected, hasClearButton, id, isDisabled, multiple, placeholder, isShowMoreOptionVisible, defaultIsOpen, inputAriaControls, status, onSelect, onBlur, onToggleClosed, onClearQuery, onChipRemoved, onQueryUpdated, onHandleLoadMore, onOpenChange, onNew, canAddNew, getCreateNewText, } = props;
41
+ const { t } = useTranslation();
42
+ const [isOpen, setIsOpen] = useState(defaultIsOpen !== null && defaultIsOpen !== void 0 ? defaultIsOpen : false);
43
+ const [query, setQuery] = useState(!multiple ? (!isEmpty(selected) ? selected[0].label : '') : '');
44
+ const [allOptions, setAllOptions] = useState((_a = flattenOptions(options)) !== null && _a !== void 0 ? _a : []);
45
+ const { focusedItemIndex, onInputKeyDown, setFocusedItemIndex } = useSelectKeyboardNavigator({
46
+ ignoreResetOnListChange: true,
47
+ isDisabled: isDisabled,
48
+ list: allOptions,
49
+ isOpen: isOpen,
50
+ setIsOpen: setIsOpen,
51
+ onSelect: (option) => onLocalSelect(undefined, option.value),
52
+ });
53
+ /**
54
+ * Allows the select toggle component to open and close itself.
55
+ */
56
+ const handleToggle = () => {
57
+ setIsOpen((open) => {
58
+ const isOpen = !open;
59
+ return isOpen;
60
+ });
61
+ };
62
+ /**
63
+ * Set the query to an empty string when the clear button is pressed.
64
+ */
65
+ const handleClearQuery = (event) => {
66
+ setQuery('');
67
+ onClearQuery && onClearQuery();
68
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
69
+ event === null || event === void 0 ? void 0 : event.preventDefault();
70
+ };
71
+ /**
72
+ * Updates the query with the new value and notifies consumers that the query has been updated.
73
+ * @param _ The unused click event
74
+ * @param v The new query value
75
+ */
76
+ const updateQuery = (event, v) => {
77
+ setQuery(v);
78
+ onQueryUpdated && onQueryUpdated(v);
79
+ if (!isOpen) {
80
+ setIsOpen(true);
81
+ }
82
+ };
83
+ /**
84
+ * Used by the Select patternfly component to change the toggle open state.
85
+ * @param isOpen The state used to change the open state of the toggle
86
+ */
87
+ const localOnOpenChange = (isOpen) => {
88
+ setIsOpen(isOpen);
89
+ !!onOpenChange && onOpenChange(isOpen);
90
+ };
91
+ const isAlreadySelected = (option) => {
92
+ return !!find(selected, (selectedOption) => selectedOption.value === option.value);
93
+ };
94
+ /**
95
+ * Handles selecting or deselecting a contact.
96
+ *
97
+ * @param {React.MouseEvent<Element, MouseEvent>} _
98
+ * @param contactLabel The value of the selected option.
99
+ * @returns
100
+ */
101
+ const onLocalSelect = (event, value) => {
102
+ if (!isString(value) && !isNumber(value)) {
103
+ return;
104
+ }
105
+ if (value === VIEW_MORE_OPTION_VALUE) {
106
+ !!onHandleLoadMore && onHandleLoadMore(event);
107
+ return;
108
+ }
109
+ if (value === ADD_NEW_OPTION_VALUE) {
110
+ !!onNew && onNew(query);
111
+ setQuery('');
112
+ return;
113
+ }
114
+ const selectedOption = find(allOptions, (o) => o.value === value);
115
+ if (!selectedOption)
116
+ return;
117
+ if (!multiple) {
118
+ if (selected[0] === selectedOption)
119
+ return;
120
+ // Single selection: replace the selection with the new option
121
+ onSelect([selectedOption]);
122
+ setQuery(selectedOption.label);
123
+ }
124
+ else {
125
+ // Multiple selection: add or remove the contact
126
+ const updatedSelection = isAlreadySelected(selectedOption)
127
+ ? filter(selected, (o) => !isEqual(selectedOption, o)) // Remove
128
+ : concat(selected, selectedOption); // Add
129
+ onSelect(updatedSelection);
130
+ setQuery('');
131
+ }
132
+ setIsOpen(false);
133
+ setFocusedItemIndex(null);
134
+ };
135
+ const toggle = (toggleRef) => (React.createElement(MenuToggle, { isFullWidth: true, variant: "typeahead", onClick: handleToggle, innerRef: toggleRef, isExpanded: isOpen, isDisabled: isDisabled, status: status },
136
+ React.createElement(TextInputGroup, { isPlain: true },
137
+ React.createElement(TextInputGroupMain, { value: query, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t(`Search by name or username`), onChange: updateQuery, onClick: handleToggle, onKeyDown: onInputKeyDown, isExpanded: isOpen, "aria-controls": inputAriaControls, role: "combobox" }, !!multiple && (React.createElement(LabelGroup, { "aria-label": "Current selections" }, selected.map((option, index) => (React.createElement(Label, { key: index, variant: "outline", onClick: haltEvent, onClose: (ev) => {
138
+ haltEvent(ev);
139
+ !!onChipRemoved && onChipRemoved(option);
140
+ } }, option.label)))))),
141
+ React.createElement(TextInputGroupUtilities, null,
142
+ query && hasClearButton && !isDisabled && (React.createElement(Button, { "aria-label": "Clear input value", variant: "plain", onClick: handleClearQuery, isDisabled: isDisabled },
143
+ React.createElement(TimesCircleIcon, null))),
144
+ isDisabled && React.createElement(Spinner, { size: "sm" })))));
145
+ /**
146
+ * Renders the options and group options.
147
+ * @returns {JSX.Element[]} The rendered options.
148
+ */
149
+ const renderOptions = () => {
150
+ let currentIndex = 0;
151
+ const optionElements = [];
152
+ for (const option of options) {
153
+ if (isDropdownOption(option)) {
154
+ optionElements.push(getOptionElement(option, currentIndex));
155
+ currentIndex++;
156
+ }
157
+ else if (isDropdownGroup(option)) {
158
+ const typedOption = option;
159
+ const groupOptionElements = [];
160
+ for (const groupOption of typedOption.options) {
161
+ groupOptionElements.push(getOptionElement(groupOption, currentIndex));
162
+ currentIndex++;
163
+ }
164
+ optionElements.push(React.createElement(SelectGroup, { label: typedOption.groupLabel }, ...groupOptionElements));
165
+ }
166
+ }
167
+ if (isShowMoreOptionVisible) {
168
+ optionElements.push(getOptionElement(VIEW_MORE_OPTION, currentIndex));
169
+ currentIndex++;
170
+ }
171
+ if (!!canAddNew && canAddNew(options, query)) {
172
+ optionElements.push(getOptionElement(ADD_NEW_OPTION, currentIndex));
173
+ }
174
+ return optionElements;
175
+ };
176
+ /**
177
+ * Renders individual options.
178
+ * @param option The option to render
179
+ * @param index The option's index within the allOptions array
180
+ * @returns The rendered option
181
+ */
182
+ const getOptionElement = (option, index) => {
183
+ if (option.value === VIEW_MORE_OPTION_VALUE) {
184
+ // You cannot use the styling applied by the "isLoadButton" prop
185
+ // because it overwrites the pf-m-focus styling. Inorder for it to visually
186
+ // look the same and be able to function with keyboard accessibility,
187
+ // the selection option text needs to be wrapped in a span with the pf link color class.
188
+ return (React.createElement(SelectOption, { key: option.value, value: option.value, isFocused: focusedItemIndex === index, isSelected: false, onClick: onHandleLoadMore },
189
+ React.createElement("span", { className: "pf-v5-u-link-color" }, "View more")));
190
+ }
191
+ else if (option.value === ADD_NEW_OPTION_VALUE) {
192
+ return (React.createElement(SelectOption, { key: option.value, value: option.value, isFocused: false, isSelected: false }, !!getCreateNewText ? getCreateNewText(query) : `${option.label}: ${query}`));
193
+ }
194
+ return (React.createElement(SelectOption, { key: option.value, value: option.value, isFocused: focusedItemIndex === index, isSelected: isAlreadySelected(option) },
195
+ React.createElement(Highlighter, { key: option.value, search: query }, option.label)));
196
+ };
197
+ useEffect(() => {
198
+ const localAllOptions = [...flattenOptions(options)];
199
+ if (isShowMoreOptionVisible) {
200
+ localAllOptions.push(VIEW_MORE_OPTION);
201
+ }
202
+ if (!!canAddNew && canAddNew(options, query)) {
203
+ localAllOptions.push(ADD_NEW_OPTION);
204
+ }
205
+ setAllOptions(localAllOptions);
206
+ // There is no need to recall this when canAddNew has been changed.
207
+ // eslint-disable-next-line react-hooks/exhaustive-deps
208
+ }, [options, isShowMoreOptionVisible, query]);
209
+ useEffect(() => {
210
+ if (!isOpen) {
211
+ setFocusedItemIndex(null);
212
+ !!onToggleClosed && onToggleClosed();
213
+ }
214
+ // eslint-disable-next-line react-hooks/exhaustive-deps
215
+ }, [isOpen]);
216
+ return (React.createElement(Select, { id: id || '', "data-tracking-id": "external-case-contact-selector", role: "menu", shouldFocusFirstItemOnOpen: false, shouldFocusToggleOnSelect: false, isOpen: isOpen, onOpenChange: localOnOpenChange, toggle: toggle, popperProps: { direction: 'down', enableFlip: false }, isScrollable: true, onBlur: onBlur, onSelect: onLocalSelect },
217
+ React.createElement(SelectList, { isAriaMultiselectable: multiple }, ...renderOptions())));
218
+ }
@@ -0,0 +1,2 @@
1
+ export * from './TypeaheadDropdown';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/TypeaheadDropdown/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './TypeaheadDropdown';
@@ -15,4 +15,6 @@ export * from './useSelectKeyboardNavigator';
15
15
  export * from './useLocalStorage';
16
16
  export * from './useSessionStorage';
17
17
  export * from './useSearchDocument';
18
+ export * from './usePatternFlySingleSelectToggle';
19
+ export * from './useProgressiveLoading';
18
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mCAAmC,CAAC;AAClD,cAAc,yBAAyB,CAAC"}
@@ -15,3 +15,5 @@ export * from './useSelectKeyboardNavigator';
15
15
  export * from './useLocalStorage';
16
16
  export * from './useSessionStorage';
17
17
  export * from './useSearchDocument';
18
+ export * from './usePatternFlySingleSelectToggle';
19
+ export * from './useProgressiveLoading';
@@ -0,0 +1,16 @@
1
+ import { MenuToggleElement } from '@patternfly/react-core';
2
+ import React, { Ref } from 'react';
3
+ interface IProps {
4
+ selected: string;
5
+ isDisabled: boolean;
6
+ isLoading?: boolean;
7
+ placeholder?: string;
8
+ isInvalid?: boolean;
9
+ }
10
+ export declare function usePatternFlySingleSelectToggle(props: IProps): {
11
+ isOpen: boolean;
12
+ setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
13
+ toggle: (toggleRef: Ref<MenuToggleElement>) => React.JSX.Element;
14
+ };
15
+ export {};
16
+ //# sourceMappingURL=usePatternFlySingleSelectToggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePatternFlySingleSelectToggle.d.ts","sourceRoot":"","sources":["../../../src/hooks/usePatternFlySingleSelectToggle.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA8B,iBAAiB,EAAW,MAAM,wBAAwB,CAAC;AAChG,OAAO,KAAK,EAAE,EAAE,GAAG,EAAY,MAAM,OAAO,CAAC;AAE7C,UAAU,MAAM;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAGD,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM;;;wBAO9B,GAAG,CAAC,iBAAiB,CAAC;EAsBpD"}
@@ -0,0 +1,17 @@
1
+ import { Flex, FlexItem, MenuToggle, Spinner } from '@patternfly/react-core';
2
+ import React, { useState } from 'react';
3
+ // The hook to manage the PatternFly Single Select component
4
+ export function usePatternFlySingleSelectToggle(props) {
5
+ const [isOpen, setIsOpen] = useState(false);
6
+ const onToggleClick = () => {
7
+ setIsOpen(!isOpen);
8
+ };
9
+ const toggle = (toggleRef) => (React.createElement(MenuToggle, { className: "single-select-toggle-text", ref: toggleRef, onClick: onToggleClick, isExpanded: isOpen, isDisabled: props.isDisabled, isFullWidth: true, status: props.isInvalid ? 'danger' : undefined },
10
+ React.createElement(Flex, { justifyContent: { default: 'justifyContentSpaceBetween' } },
11
+ React.createElement(FlexItem, null,
12
+ " ",
13
+ props.selected || props.placeholder),
14
+ props.isLoading && (React.createElement(FlexItem, null,
15
+ React.createElement(Spinner, { size: "md" }))))));
16
+ return { isOpen, setIsOpen, toggle };
17
+ }
@@ -0,0 +1,10 @@
1
+ export declare function useProgressiveLoading<T>(allItems?: T[], defaultPageSize?: number): {
2
+ pageSize: number;
3
+ visibleItems: T[];
4
+ hasMoreItems: () => boolean;
5
+ onHandleLoadMore: (event: any) => void;
6
+ setPageSize: import("react").Dispatch<import("react").SetStateAction<number>>;
7
+ setVisibleItems: import("react").Dispatch<import("react").SetStateAction<T[]>>;
8
+ resetVisibleItems: () => void;
9
+ };
10
+ //# sourceMappingURL=useProgressiveLoading.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useProgressiveLoading.d.ts","sourceRoot":"","sources":["../../../src/hooks/useProgressiveLoading.ts"],"names":[],"mappings":"AAGA,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,QAAQ,GAAE,CAAC,EAAO,EAAE,eAAe,SAAK;;;;;;;;EAoChF"}
@@ -0,0 +1,33 @@
1
+ import { haltEvent } from '@rh-support/utils';
2
+ import { useEffect, useState } from 'react';
3
+ export function useProgressiveLoading(allItems = [], defaultPageSize = 10) {
4
+ const [pageSize, setPageSize] = useState(defaultPageSize);
5
+ const [visibleItems, setVisibleItems] = useState(allItems.slice(0, pageSize));
6
+ useEffect(() => {
7
+ setVisibleItems(allItems.slice(0, pageSize));
8
+ }, [allItems, pageSize]);
9
+ /**
10
+ * Load the next "page" of items.
11
+ * @param event
12
+ */
13
+ const onHandleLoadMore = (event) => {
14
+ const newVisibleOptions = (visibleItems === null || visibleItems === void 0 ? void 0 : visibleItems.length) + pageSize;
15
+ if (newVisibleOptions < allItems.length) {
16
+ setVisibleItems(allItems.slice(0, newVisibleOptions));
17
+ }
18
+ else {
19
+ setVisibleItems(allItems);
20
+ }
21
+ haltEvent(event);
22
+ };
23
+ /**
24
+ * reset the visible items to the "first" page
25
+ */
26
+ const resetVisibleItems = () => setVisibleItems(allItems.slice(0, pageSize));
27
+ /**
28
+ *
29
+ * @returns Whether or not the number of visible items is less than the total number of items.
30
+ */
31
+ const hasMoreItems = () => visibleItems.length < allItems.length;
32
+ return { pageSize, visibleItems, hasMoreItems, onHandleLoadMore, setPageSize, setVisibleItems, resetVisibleItems };
33
+ }
@@ -2,10 +2,18 @@ import React from 'react';
2
2
  interface IProps {
3
3
  list: any[];
4
4
  isOpen: boolean;
5
+ ignoreResetOnListChange?: boolean;
6
+ resetIndexOnClose?: boolean;
7
+ isDisabled?: boolean;
5
8
  setIsOpen: (value: React.SetStateAction<boolean>) => void;
6
9
  onSelect: (value: any) => void;
7
10
  }
8
- export declare function useSelectKeyboardNavigator({ list, isOpen, setIsOpen, onSelect }: IProps): {
11
+ export declare function useSelectKeyboardNavigator({ list, isOpen, ignoreResetOnListChange, resetIndexOnClose, isDisabled, setIsOpen, onSelect, }: IProps): {
12
+ focusedItemIndex: number;
13
+ onInputKeyDown: (event: any) => void;
14
+ setFocusedItemIndex: React.Dispatch<React.SetStateAction<number>>;
15
+ };
16
+ export declare function useSelectKeyboardNavigatorModified({ list, isOpen, setIsOpen, onSelect }: IProps): {
9
17
  onInputKeyDown: (event: any) => void;
10
18
  focusedItemIndex: number;
11
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useSelectKeyboardNavigator.d.ts","sourceRoot":"","sources":["../../../src/hooks/useSelectKeyboardNavigator.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,UAAU,MAAM;IACZ,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC1D,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,0BAA0B,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM;;;EAwDvF"}
1
+ {"version":3,"file":"useSelectKeyboardNavigator.d.ts","sourceRoot":"","sources":["../../../src/hooks/useSelectKeyboardNavigator.tsx"],"names":[],"mappings":"AACA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,UAAU,MAAM;IACZ,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC1D,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,0BAA0B,CAAC,EACvC,IAAI,EACJ,MAAM,EACN,uBAAuB,EACvB,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,QAAQ,GACX,EAAE,MAAM;;;;EAqFR;AAED,wBAAgB,kCAAkC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM;;;EAqD/F"}
@@ -1,9 +1,29 @@
1
+ import { haltEvent } from '@rh-support/utils';
1
2
  import { useEffect, useState } from 'react';
2
- export function useSelectKeyboardNavigator({ list, isOpen, setIsOpen, onSelect }) {
3
+ export function useSelectKeyboardNavigator({ list, isOpen, ignoreResetOnListChange, resetIndexOnClose, isDisabled, setIsOpen, onSelect, }) {
3
4
  const [focusedItemIndex, setFocusedItemIndex] = useState(null);
5
+ /**
6
+ * Reset the item index whenever the list changes if it is not specified to be ignored.
7
+ */
4
8
  useEffect(() => {
5
- setFocusedItemIndex(null);
9
+ !ignoreResetOnListChange && setFocusedItemIndex(null);
10
+ // This hook should only run when the list has changed.
11
+ // eslint-disable-next-line react-hooks/exhaustive-deps
6
12
  }, [list.length]);
13
+ /**
14
+ * Reset the item index whenever isOpen is false and the focused index is not null.
15
+ * Should only be called when isOpen is changed
16
+ */
17
+ useEffect(() => {
18
+ if (resetIndexOnClose && !isOpen && focusedItemIndex !== null) {
19
+ setFocusedItemIndex(null);
20
+ }
21
+ // eslint-disable-next-line react-hooks/exhaustive-deps
22
+ }, [isOpen]);
23
+ /**
24
+ * Handles transitioning the focus to the next item in the list.
25
+ * @param key The stringified version of the arrow keys
26
+ */
7
27
  const handleMenuArrowKeys = (key) => {
8
28
  let indexToFocus;
9
29
  if (isOpen) {
@@ -28,12 +48,18 @@ export function useSelectKeyboardNavigator({ list, isOpen, setIsOpen, onSelect }
28
48
  setFocusedItemIndex(indexToFocus);
29
49
  }
30
50
  };
51
+ /**
52
+ * Listens for keyboard events to handle the opening/closing of the menu,
53
+ * the selection of a focused menu item and transitioning the focus to the next item.
54
+ *
55
+ * @param event The key down event
56
+ */
31
57
  const onInputKeyDown = (event) => {
32
58
  const focusedItem = focusedItemIndex !== null ? list[focusedItemIndex] : null;
33
59
  switch (event.key) {
34
60
  // Select the first available option
35
61
  case 'Enter':
36
- if (!isOpen) {
62
+ if (!isOpen && !isDisabled) {
37
63
  setIsOpen((pre) => !pre);
38
64
  }
39
65
  else if (isOpen && focusedItem !== null) {
@@ -41,6 +67,7 @@ export function useSelectKeyboardNavigator({ list, isOpen, setIsOpen, onSelect }
41
67
  }
42
68
  break;
43
69
  case 'Escape':
70
+ resetIndexOnClose && setFocusedItemIndex(null);
44
71
  setIsOpen(false);
45
72
  break;
46
73
  case 'ArrowUp':
@@ -50,5 +77,57 @@ export function useSelectKeyboardNavigator({ list, isOpen, setIsOpen, onSelect }
50
77
  break;
51
78
  }
52
79
  };
80
+ return { focusedItemIndex, onInputKeyDown, setFocusedItemIndex };
81
+ }
82
+ export function useSelectKeyboardNavigatorModified({ list, isOpen, setIsOpen, onSelect }) {
83
+ const [focusedItemIndex, setFocusedItemIndex] = useState(0);
84
+ const handleMenuArrowKeys = (key) => {
85
+ if (isOpen) {
86
+ setFocusedItemIndex((localIndex) => {
87
+ let indexToFocus;
88
+ if (key === 'ArrowUp') {
89
+ // When no index is set or at the first index, focus to the last, otherwise decrement focus index
90
+ if (localIndex === 0) {
91
+ indexToFocus = list.length - 1;
92
+ }
93
+ else {
94
+ indexToFocus = localIndex - 1;
95
+ }
96
+ }
97
+ if (key === 'ArrowDown') {
98
+ // When no index is set or at the last index, focus to the first, otherwise increment focus index
99
+ if (localIndex === list.length - 1) {
100
+ indexToFocus = 0;
101
+ }
102
+ else {
103
+ indexToFocus = localIndex + 1;
104
+ }
105
+ }
106
+ return indexToFocus;
107
+ });
108
+ }
109
+ };
110
+ const onInputKeyDown = (event) => {
111
+ switch (event.key) {
112
+ // Select the first available option
113
+ case 'Enter':
114
+ const focusedItem = focusedItemIndex !== null ? list[focusedItemIndex] : null;
115
+ if (!isOpen) {
116
+ setIsOpen((pre) => !pre);
117
+ }
118
+ else if (isOpen && focusedItem !== null) {
119
+ onSelect(focusedItem);
120
+ }
121
+ break;
122
+ case 'Escape':
123
+ setIsOpen(false);
124
+ break;
125
+ case 'ArrowUp':
126
+ case 'ArrowDown':
127
+ haltEvent(event);
128
+ handleMenuArrowKeys(event.key);
129
+ break;
130
+ }
131
+ };
53
132
  return { onInputKeyDown, focusedItemIndex };
54
133
  }
@@ -1,7 +1,6 @@
1
1
  export * from './LoadingIndicator';
2
2
  export * from './AlertMessage';
3
3
  export * from './ProgressBar';
4
- export * from './DropDownList';
5
4
  export * from './ErrorBoundary';
6
5
  export * from './ErrorPages';
7
6
  export * from './LoginModal';
@@ -32,4 +31,6 @@ export * from './TextAreaResizable';
32
31
  export * from './CustomTextInput';
33
32
  export * from './PhoneInput';
34
33
  export * from './TagsSelector';
34
+ export * from './SingleSelectDropdown';
35
+ export * from './OwnerTypeaheadDropdown';
35
36
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC"}
package/lib/esm/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  export * from './LoadingIndicator';
2
2
  export * from './AlertMessage';
3
3
  export * from './ProgressBar';
4
- export * from './DropDownList';
5
4
  export * from './ErrorBoundary';
6
5
  export * from './ErrorPages';
7
6
  export * from './LoginModal';
@@ -32,3 +31,5 @@ export * from './TextAreaResizable';
32
31
  export * from './CustomTextInput';
33
32
  export * from './PhoneInput';
34
33
  export * from './TagsSelector';
34
+ export * from './SingleSelectDropdown';
35
+ export * from './OwnerTypeaheadDropdown';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rh-support/components",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "Contains all reusabel components for support app",
5
5
  "author": "Vikas Rathee <vrathee@redhat.com>",
6
6
  "license": "ISC",
@@ -44,13 +44,12 @@
44
44
  "prepublishOnly": "npm run build"
45
45
  },
46
46
  "peerDependencies": {
47
- "@cee-eng/hydrajs": "4.17.36",
47
+ "@cee-eng/hydrajs": "4.18.0",
48
48
  "@cee-eng/ui-toolkit": "1.1.8",
49
49
  "@patternfly/patternfly": "5.4.0",
50
50
  "@patternfly/react-core": "5.4.0",
51
51
  "@patternfly/react-table": "5.4.0",
52
52
  "dompurify": "^2.2.6",
53
- "downshift": "^6.0.5",
54
53
  "js-worker-search": "^1.4.1",
55
54
  "lazysizes": "^5.3.2",
56
55
  "lodash": "^4.17.21",
@@ -62,7 +61,7 @@
62
61
  "use-deep-compare-effect": "^1.6.1"
63
62
  },
64
63
  "dependencies": {
65
- "@cee-eng/hydrajs": "4.17.36",
64
+ "@cee-eng/hydrajs": "4.18.0",
66
65
  "@cee-eng/ui-toolkit": "1.1.8",
67
66
  "@patternfly/patternfly": "5.4.0",
68
67
  "@patternfly/react-core": "5.4.0",
@@ -70,10 +69,9 @@
70
69
  "@patternfly/react-table": "5.1.1",
71
70
  "@patternfly/react-tokens": "^5.4.0",
72
71
  "@rh-support/types": "2.0.5",
73
- "@rh-support/user-permissions": "2.5.0",
74
- "@rh-support/utils": "2.5.0",
72
+ "@rh-support/user-permissions": "2.5.1",
73
+ "@rh-support/utils": "2.5.1",
75
74
  "dompurify": "^2.2.6",
76
- "downshift": "^6.0.5",
77
75
  "js-worker-search": "^1.4.1",
78
76
  "lazysizes": "^5.3.2",
79
77
  "lodash": "^4.17.21",
@@ -111,5 +109,5 @@
111
109
  "defaults and supports es6-module",
112
110
  "maintained node versions"
113
111
  ],
114
- "gitHead": "78f51b2cfd60107a49503d13542ba46d29df4d04"
112
+ "gitHead": "92c1c95e58dc8e6d2df7fd21b3815da9b7d713c9"
115
113
  }
@@ -1,6 +0,0 @@
1
- import { MultiSelectDropdownProps } from './MultiSelectDropdownList';
2
- import { AsyncDropdownProps, IAsyncOnlyProps, IDropdownListProps } from './types';
3
- declare const AsyncDropDownList: <T extends {}>(props: IAsyncOnlyProps<T> & IDropdownListProps<T>) => JSX.Element;
4
- declare const AsyncMultiDropDownList: <T extends {}>(props: IAsyncOnlyProps<T> & MultiSelectDropdownProps<T>) => JSX.Element;
5
- export { AsyncDropDownList, AsyncMultiDropDownList, AsyncDropdownProps };
6
- //# sourceMappingURL=AsyncDropDownList.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AsyncDropDownList.d.ts","sourceRoot":"","sources":["../../../src/DropDownList/AsyncDropDownList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA2B,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElF,QAAA,MAAM,iBAAiB,EAA0B,CAAC,CAAY,SAAF,EAAE,EAC1D,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,KAChD,GAAG,CAAC,OAAO,CAAC;AAEjB,QAAA,MAAM,sBAAsB,EAAyC,CAAC,CAAY,SAAF,EAAE,EAC9E,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,wBAAwB,CAAC,CAAC,CAAC,KACtD,GAAG,CAAC,OAAO,CAAC;AAEjB,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,CAAC"}
@@ -1,6 +0,0 @@
1
- import { withAsync } from './async';
2
- import { Dropdown } from './DropDownList';
3
- import { MultiSelectDropDownList } from './MultiSelectDropdownList';
4
- const AsyncDropDownList = withAsync(Dropdown);
5
- const AsyncMultiDropDownList = withAsync(MultiSelectDropDownList);
6
- export { AsyncDropDownList, AsyncMultiDropDownList };
@@ -1,9 +0,0 @@
1
- import './dropdownList.css';
2
- import React from 'react';
3
- import { IDropdownListProps } from './types';
4
- declare function Dropdown<T>(props: IDropdownListProps<T>): React.JSX.Element;
5
- declare namespace Dropdown {
6
- var defaultProps: Partial<IDropdownListProps<any>>;
7
- }
8
- export { Dropdown };
9
- //# sourceMappingURL=DropDownList.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DropDownList.d.ts","sourceRoot":"","sources":["../../../src/DropDownList/DropDownList.tsx"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC;AAO5B,OAAO,KAAuC,MAAM,OAAO,CAAC;AAM5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAuB7C,iBAAS,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,qBAoJhD;kBApJQ,QAAQ;;;AAsJjB,OAAO,EAAE,QAAQ,EAAE,CAAC"}