@boomerang-io/carbon-addons-boomerang-react 4.6.14-beta.9 → 4.6.15-beta.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/README.md +162 -162
- package/dist/cjs/components/AboutPlatform/AboutPlatform.js +50 -50
- package/dist/cjs/components/AdvantageSideNav/AdvantageSideNav.js +221 -221
- package/dist/cjs/components/AutoSuggest/AutoSuggest.js +101 -101
- package/dist/cjs/components/Avatar/Avatar.js +18 -18
- package/dist/cjs/components/CheckboxList/CheckboxList.js +34 -34
- package/dist/cjs/components/ComboBox/ComboBox.js +103 -103
- package/dist/cjs/components/ComboBoxMultiSelect/ComboBoxMultiSelect.js +32 -32
- package/dist/cjs/components/ComboBoxMultiSelect/MultiSelect.js +208 -208
- package/dist/cjs/components/ComposedModal/ComposedModal.js +80 -80
- package/dist/cjs/components/ConfirmModal/ConfirmModal.js +43 -43
- package/dist/cjs/components/Creatable/Creatable.js +137 -137
- package/dist/cjs/components/DataDrivenInput/DataDrivenInput.js +226 -226
- package/dist/cjs/components/DateInput/DateInput.js +35 -35
- package/dist/cjs/components/DecisionButtons/DecisionButtons.js +61 -61
- package/dist/cjs/components/DelayedRender/DelayedRender.js +17 -17
- package/dist/cjs/components/DynamicFormik/DynamicFormik.js +578 -578
- package/dist/cjs/components/Error403/Error403.js +13 -13
- package/dist/cjs/components/Error403/ForbiddenErrorBackground.js +113 -113
- package/dist/cjs/components/Error403/GraphicWrangler/GraphicWrangler.js +62 -62
- package/dist/cjs/components/Error404/Error404.js +13 -13
- package/dist/cjs/components/Error404/GraphicLoch/GraphicLoch.js +17 -17
- package/dist/cjs/components/Error404/NotFoundErrorBackground.js +129 -129
- package/dist/cjs/components/ErrorBoundary/ErrorBoundary.js +32 -32
- package/dist/cjs/components/ErrorDragon/ErrorDragon.js +13 -13
- package/dist/cjs/components/ErrorDragon/assets/ErrorGraphic.js +35 -35
- package/dist/cjs/components/ErrorFullPage/ErrorFullPage.js +10 -10
- package/dist/cjs/components/ErrorMessage/ErrorMessage.js +22 -22
- package/dist/cjs/components/ErrorPage/ErrorPage.js +11 -11
- package/dist/cjs/components/ErrorPageCore/ErrorPageCore.js +15 -15
- package/dist/cjs/components/ErrorPageCore/GenericErrorBackground.js +124 -124
- package/dist/cjs/components/FeatureHeader/FeatureHeader.js +29 -29
- package/dist/cjs/components/FeatureNavTab/FeatureNavTab.js +12 -12
- package/dist/cjs/components/FeatureNavTabs/FeatureNavTabs.js +12 -12
- package/dist/cjs/components/FeatureSideNav/FeatureSideNav.js +12 -12
- package/dist/cjs/components/FeatureSideNav/FeatureSideNavFooter.js +8 -8
- package/dist/cjs/components/FeatureSideNav/FeatureSideNavHeader.js +8 -8
- package/dist/cjs/components/FeatureSideNav/FeatureSideNavLinks.js +14 -14
- package/dist/cjs/components/FeatureSideNavLink/FeatureSideNavLink.js +12 -12
- package/dist/cjs/components/Feedback/Feedback.js +38 -38
- package/dist/cjs/components/FlowModal/FlowModal.js +141 -141
- package/dist/cjs/components/FlowModal/FlowModalForm.js +12 -12
- package/dist/cjs/components/Header/Header.js +187 -187
- package/dist/cjs/components/Header/HeaderAppSwitcher.js +94 -94
- package/dist/cjs/components/Header/HeaderMenu.js +7 -7
- package/dist/cjs/components/Header/HeaderMenuItem.js +51 -51
- package/dist/cjs/components/Header/HeaderTeamSwitcher.js +329 -332
- package/dist/cjs/components/Header/UserRequests.js +29 -29
- package/dist/cjs/components/ImageModal/ImageModal.js +17 -17
- package/dist/cjs/components/Loading/Loading.js +8 -8
- package/dist/cjs/components/MemberBar/MemberBar.js +20 -20
- package/dist/cjs/components/Modal/Modal.js +20 -20
- package/dist/cjs/components/ModalConfirmEdit/ModalConfirmArray.js +14 -14
- package/dist/cjs/components/ModalConfirmEdit/ModalConfirmDetails.js +11 -11
- package/dist/cjs/components/ModalConfirmEdit/ModalConfirmEdit.js +17 -17
- package/dist/cjs/components/ModalForm/ModalForm.js +9 -9
- package/dist/cjs/components/Notifications/NotificationsContainer.js +22 -22
- package/dist/cjs/components/Notifications/ToastNotification.js +21 -21
- package/dist/cjs/components/Notifications/notify.js +17 -17
- package/dist/cjs/components/PlatformBanner/PlatformBanner.js +8 -8
- package/dist/cjs/components/PlatformNotifications/PlatformNotification.js +17 -17
- package/dist/cjs/components/PlatformNotifications/PlatformNotificationsContainer.js +136 -136
- package/dist/cjs/components/Portal/Portal.js +14 -14
- package/dist/cjs/components/PrivacyRedirect/PrivacyRedirect.js +30 -30
- package/dist/cjs/components/PrivacyStatement/PrivacyStatement.js +90 -90
- package/dist/cjs/components/ProfileSettings/ProfileSettings.js +124 -142
- package/dist/cjs/components/ProtectedRoute/ProtectedRoute.js +13 -13
- package/dist/cjs/components/RadioGroup/RadioGroup.js +33 -33
- package/dist/cjs/components/RichTextArea/RichTextArea.js +142 -142
- package/dist/cjs/components/SignOut/SignOut.js +27 -27
- package/dist/cjs/components/SupportCenter/SupportCenter.js +65 -65
- package/dist/cjs/components/TextArea/TextArea.js +16 -16
- package/dist/cjs/components/TextInput/TextInput.js +13 -13
- package/dist/cjs/components/Toggle/Toggle.js +22 -22
- package/dist/cjs/components/TooltipHover/TooltipHover.js +39 -39
- package/dist/cjs/components/UIShell/UIShell.js +95 -95
- package/dist/cjs/config/servicesConfig.js +22 -22
- package/dist/cjs/constants/DataDrivenInputTypes.js +74 -74
- package/dist/cjs/constants/UserType.js +13 -13
- package/dist/cjs/hooks/useHeaderMenu.js +49 -49
- package/dist/cjs/hooks/usePortal.js +74 -74
- package/dist/cjs/hooks/useWindowSize.js +33 -33
- package/dist/cjs/internal/ListBox/ListBox.js +41 -41
- package/dist/cjs/internal/ListBox/ListBoxField.js +19 -19
- package/dist/cjs/internal/ListBox/ListBoxMenu.js +19 -19
- package/dist/cjs/internal/ListBox/ListBoxMenuIcon.js +31 -31
- package/dist/cjs/internal/ListBox/ListBoxMenuItem.js +38 -38
- package/dist/cjs/internal/ListBox/ListBoxSelection.js +64 -64
- package/dist/cjs/internal/ListBox/index.js +10 -10
- package/dist/cjs/internal/keyboard/keys.js +16 -16
- package/dist/cjs/internal/keyboard/match.js +67 -67
- package/dist/cjs/internal/settings.js +5 -5
- package/dist/cjs/tools/accessibility.js +13 -13
- package/dist/cjs/tools/createPropAdapter.js +44 -44
- package/dist/cjs/tools/isUrl.js +39 -39
- package/dist/cjs/tools/setupGetInstanceId.js +20 -20
- package/dist/cjs/tools/useSetState.js +12 -12
- package/dist/cjs/tools/yupAst/astGenerator.js +212 -212
- package/dist/cjs/tools/yupAst/customValidators.js +17 -17
- package/dist/esm/components/AboutPlatform/AboutPlatform.js +50 -50
- package/dist/esm/components/AdvantageSideNav/AdvantageSideNav.js +221 -221
- package/dist/esm/components/AutoSuggest/AutoSuggest.js +101 -101
- package/dist/esm/components/Avatar/Avatar.js +18 -18
- package/dist/esm/components/CheckboxList/CheckboxList.js +34 -34
- package/dist/esm/components/ComboBox/ComboBox.js +103 -103
- package/dist/esm/components/ComboBoxMultiSelect/ComboBoxMultiSelect.js +32 -32
- package/dist/esm/components/ComboBoxMultiSelect/MultiSelect.js +208 -208
- package/dist/esm/components/ComposedModal/ComposedModal.js +80 -80
- package/dist/esm/components/ConfirmModal/ConfirmModal.js +43 -43
- package/dist/esm/components/Creatable/Creatable.js +137 -137
- package/dist/esm/components/DataDrivenInput/DataDrivenInput.js +226 -226
- package/dist/esm/components/DateInput/DateInput.js +35 -35
- package/dist/esm/components/DecisionButtons/DecisionButtons.js +61 -61
- package/dist/esm/components/DelayedRender/DelayedRender.js +17 -17
- package/dist/esm/components/DynamicFormik/DynamicFormik.js +578 -578
- package/dist/esm/components/Error403/Error403.js +13 -13
- package/dist/esm/components/Error403/ForbiddenErrorBackground.js +113 -113
- package/dist/esm/components/Error403/GraphicWrangler/GraphicWrangler.js +62 -62
- package/dist/esm/components/Error404/Error404.js +13 -13
- package/dist/esm/components/Error404/GraphicLoch/GraphicLoch.js +17 -17
- package/dist/esm/components/Error404/NotFoundErrorBackground.js +129 -129
- package/dist/esm/components/ErrorBoundary/ErrorBoundary.js +32 -32
- package/dist/esm/components/ErrorDragon/ErrorDragon.js +13 -13
- package/dist/esm/components/ErrorDragon/assets/ErrorGraphic.js +35 -35
- package/dist/esm/components/ErrorFullPage/ErrorFullPage.js +10 -10
- package/dist/esm/components/ErrorMessage/ErrorMessage.js +22 -22
- package/dist/esm/components/ErrorPage/ErrorPage.js +11 -11
- package/dist/esm/components/ErrorPageCore/ErrorPageCore.js +15 -15
- package/dist/esm/components/ErrorPageCore/GenericErrorBackground.js +124 -124
- package/dist/esm/components/FeatureHeader/FeatureHeader.js +29 -29
- package/dist/esm/components/FeatureNavTab/FeatureNavTab.js +12 -12
- package/dist/esm/components/FeatureNavTabs/FeatureNavTabs.js +12 -12
- package/dist/esm/components/FeatureSideNav/FeatureSideNav.js +12 -12
- package/dist/esm/components/FeatureSideNav/FeatureSideNavFooter.js +8 -8
- package/dist/esm/components/FeatureSideNav/FeatureSideNavHeader.js +8 -8
- package/dist/esm/components/FeatureSideNav/FeatureSideNavLinks.js +14 -14
- package/dist/esm/components/FeatureSideNavLink/FeatureSideNavLink.js +12 -12
- package/dist/esm/components/Feedback/Feedback.js +38 -38
- package/dist/esm/components/FlowModal/FlowModal.js +141 -141
- package/dist/esm/components/FlowModal/FlowModalForm.js +12 -12
- package/dist/esm/components/Header/Header.js +187 -187
- package/dist/esm/components/Header/HeaderAppSwitcher.js +94 -94
- package/dist/esm/components/Header/HeaderMenu.js +7 -7
- package/dist/esm/components/Header/HeaderMenuItem.js +51 -51
- package/dist/esm/components/Header/HeaderTeamSwitcher.js +329 -332
- package/dist/esm/components/Header/UserRequests.js +29 -29
- package/dist/esm/components/ImageModal/ImageModal.js +17 -17
- package/dist/esm/components/Loading/Loading.js +8 -8
- package/dist/esm/components/MemberBar/MemberBar.js +20 -20
- package/dist/esm/components/Modal/Modal.js +20 -20
- package/dist/esm/components/ModalConfirmEdit/ModalConfirmArray.js +14 -14
- package/dist/esm/components/ModalConfirmEdit/ModalConfirmDetails.js +11 -11
- package/dist/esm/components/ModalConfirmEdit/ModalConfirmEdit.js +17 -17
- package/dist/esm/components/ModalForm/ModalForm.js +9 -9
- package/dist/esm/components/Notifications/NotificationsContainer.js +22 -22
- package/dist/esm/components/Notifications/ToastNotification.js +21 -21
- package/dist/esm/components/Notifications/notify.js +17 -17
- package/dist/esm/components/PlatformBanner/PlatformBanner.js +8 -8
- package/dist/esm/components/PlatformNotifications/PlatformNotification.js +17 -17
- package/dist/esm/components/PlatformNotifications/PlatformNotificationsContainer.js +136 -136
- package/dist/esm/components/Portal/Portal.js +14 -14
- package/dist/esm/components/PrivacyRedirect/PrivacyRedirect.js +30 -30
- package/dist/esm/components/PrivacyStatement/PrivacyStatement.js +90 -90
- package/dist/esm/components/ProfileSettings/ProfileSettings.js +124 -142
- package/dist/esm/components/ProtectedRoute/ProtectedRoute.js +13 -13
- package/dist/esm/components/RadioGroup/RadioGroup.js +33 -33
- package/dist/esm/components/RichTextArea/RichTextArea.js +142 -142
- package/dist/esm/components/SignOut/SignOut.js +27 -27
- package/dist/esm/components/SupportCenter/SupportCenter.js +65 -65
- package/dist/esm/components/TextArea/TextArea.js +16 -16
- package/dist/esm/components/TextInput/TextInput.js +13 -13
- package/dist/esm/components/Toggle/Toggle.js +22 -22
- package/dist/esm/components/TooltipHover/TooltipHover.js +39 -39
- package/dist/esm/components/UIShell/UIShell.js +95 -95
- package/dist/esm/config/servicesConfig.js +22 -22
- package/dist/esm/constants/DataDrivenInputTypes.js +74 -74
- package/dist/esm/constants/UserType.js +13 -13
- package/dist/esm/hooks/useHeaderMenu.js +49 -49
- package/dist/esm/hooks/usePortal.js +74 -74
- package/dist/esm/hooks/useWindowSize.js +33 -33
- package/dist/esm/internal/ListBox/ListBox.js +41 -41
- package/dist/esm/internal/ListBox/ListBoxField.js +19 -19
- package/dist/esm/internal/ListBox/ListBoxMenu.js +19 -19
- package/dist/esm/internal/ListBox/ListBoxMenuIcon.js +31 -31
- package/dist/esm/internal/ListBox/ListBoxMenuItem.js +38 -38
- package/dist/esm/internal/ListBox/ListBoxSelection.js +64 -64
- package/dist/esm/internal/ListBox/index.js +10 -10
- package/dist/esm/internal/keyboard/keys.js +16 -16
- package/dist/esm/internal/keyboard/match.js +67 -67
- package/dist/esm/internal/settings.js +5 -5
- package/dist/esm/tools/accessibility.js +13 -13
- package/dist/esm/tools/createPropAdapter.js +44 -44
- package/dist/esm/tools/isUrl.js +39 -39
- package/dist/esm/tools/setupGetInstanceId.js +20 -20
- package/dist/esm/tools/useSetState.js +12 -12
- package/dist/esm/tools/yupAst/astGenerator.js +212 -212
- package/dist/esm/tools/yupAst/customValidators.js +17 -17
- package/dist/types/index.d.ts +1359 -1360
- package/package.json +167 -167
- package/scss/components/AboutPlatform/_aboutPlatform.scss +139 -139
- package/scss/components/AdvantageSideNav/_advantageSideNav.scss +272 -272
- package/scss/components/AutoSuggest/_autoSuggest.scss +62 -62
- package/scss/components/Avatar/_avatar.scss +32 -32
- package/scss/components/CheckboxList/_checkboxList.scss +26 -26
- package/scss/components/ComboBox/_combobox.scss +23 -23
- package/scss/components/ComboBoxMultiSelect/_comboBoxMultiSelect.scss +53 -53
- package/scss/components/ConfirmModal/_confirmModal.scss +12 -12
- package/scss/components/Creatable/_creatable.scss +48 -48
- package/scss/components/DateInput/_dateInput.scss +36 -36
- package/scss/components/DecisionButtons/_decisionButtons.scss +132 -132
- package/scss/components/DynamicFormik/_dynamicFormik.scss +17 -17
- package/scss/components/ErrorBoundary/_errorBoundary.scss +12 -12
- package/scss/components/ErrorDragon/_errorDragon.scss +55 -55
- package/scss/components/ErrorMessage/_errorMessage.scss +43 -43
- package/scss/components/ErrorPage/_errorPage.scss +66 -66
- package/scss/components/ErrorPageCore/_errorPageCore.scss +73 -73
- package/scss/components/FeatureHeader/_featureHeader.scss +67 -67
- package/scss/components/FeatureNavTab/_featureNavTab.scss +27 -27
- package/scss/components/FeatureSideNav/_featureSideNav.scss +76 -76
- package/scss/components/FeatureSideNavLink/_featureSideNavLink.scss +49 -49
- package/scss/components/Feedback/_feedback.scss +50 -50
- package/scss/components/Header/_header.scss +179 -179
- package/scss/components/Header/_headerAppSwitcher.scss +117 -117
- package/scss/components/Header/_headerMenu.scss +30 -30
- package/scss/components/Header/_headerMenuItem.scss +65 -65
- package/scss/components/Header/_headerTeamSwitcher.scss +222 -222
- package/scss/components/Header/_userRequests.scss +36 -36
- package/scss/components/ImageModal/_imageModal.scss +51 -51
- package/scss/components/MemberBar/_memberBar.scss +99 -99
- package/scss/components/Modal/_modal.scss +163 -163
- package/scss/components/ModalConfirmEdit/_modalConfirmEdit.scss +113 -113
- package/scss/components/Notifications/_notifications.scss +57 -57
- package/scss/components/PlatformBanner/_platformBanner.scss +30 -30
- package/scss/components/PlatformNotifications/_platformNotifications.scss +230 -230
- package/scss/components/PrivacyRedirect/_privacyRedirect.scss +43 -43
- package/scss/components/PrivacyStatement/_privacyStatement.scss +137 -137
- package/scss/components/ProfileSettings/_profileSettings.scss +117 -117
- package/scss/components/ProtectedRoute/_protectedRoute.scss +32 -32
- package/scss/components/RadioGroup/_radioGroup.scss +46 -46
- package/scss/components/RichTextArea/_richTextArea.scss +82 -82
- package/scss/components/SignOut/_signOut.scss +51 -51
- package/scss/components/SupportCenter/_supportCenter.scss +16 -16
- package/scss/components/TextArea/_textArea.scss +19 -19
- package/scss/components/TextInput/_textInput.scss +26 -26
- package/scss/components/Toggle/_toggle.scss +80 -80
- package/scss/components/TooltipHover/_tooltip.scss +48 -48
- package/scss/global/_config.scss +14 -14
- package/scss/global/_tippy.scss +49 -49
- package/scss/global/index.scss +401 -401
- package/scss/global/themes/_boomerang.scss +184 -184
- package/scss/global/themes/_default.scss +79 -79
- package/scss/global/themes/_shell-tokens.scss +42 -42
- package/scss/global/utils/_animations.scss +15 -15
- package/scss/global/utils/_mixins.scss +67 -67
- package/scss/global/utils/index.scss +10 -10
|
@@ -3,107 +3,107 @@ import AutoSuggest from 'react-autosuggest';
|
|
|
3
3
|
import { matchSorter } from 'match-sorter';
|
|
4
4
|
import { prefix } from '../../internal/settings.js';
|
|
5
5
|
|
|
6
|
-
/*
|
|
7
|
-
IBM Confidential
|
|
8
|
-
694970X, 69497O0
|
|
9
|
-
© Copyright IBM Corp. 2022, 2024
|
|
10
|
-
*/
|
|
11
|
-
const SELECT_METHODS = ["up", "down", "click"];
|
|
12
|
-
class AutoSuggestBmrg extends Component {
|
|
13
|
-
inputRef = React.createRef();
|
|
14
|
-
state = {
|
|
15
|
-
// Used if we want to have some of the functions external instead of
|
|
16
|
-
// the default ones in the wrapper
|
|
17
|
-
value: this.props.initialValue ?? "",
|
|
18
|
-
caretIndex: 0,
|
|
19
|
-
suggestions: [],
|
|
20
|
-
};
|
|
21
|
-
// Each time the component updates we want to refocus the input and keep the cursor in the correct place
|
|
22
|
-
// Needed for when cycling through mutliple suggestions with the arrow keys and the cursor resets to the end of the input. We don't want that.
|
|
23
|
-
componentDidUpdate(_, prevState) {
|
|
24
|
-
if (this.state.value && prevState.value !== this.state.value) {
|
|
25
|
-
// prevent it from focusing on initial render / empty
|
|
26
|
-
this.inputRef.current?.focus();
|
|
27
|
-
this.inputRef.current?.setSelectionRange(this.state.caretIndex, this.state.caretIndex);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
renderSuggestion = (suggestion) => suggestion.label;
|
|
31
|
-
onSuggestionsFetchRequested = () => {
|
|
32
|
-
this.setState(() => ({
|
|
33
|
-
suggestions: this.getSuggestions(),
|
|
34
|
-
}));
|
|
35
|
-
};
|
|
36
|
-
/**
|
|
37
|
-
* More logic here for handling a user cycling through suggestions
|
|
38
|
-
* Move the caret to the new suggestion location or use the reference to the DOM element.
|
|
39
|
-
* Shift based on the change in length of the value b/c of different length suggestions
|
|
40
|
-
*/
|
|
41
|
-
onInputChange = (event, { newValue, method }) => {
|
|
42
|
-
this.setState((prevState) => ({
|
|
43
|
-
value: newValue,
|
|
44
|
-
caretIndex: SELECT_METHODS.includes(method)
|
|
45
|
-
? prevState.caretIndex + (newValue.length - prevState.value.length)
|
|
46
|
-
: this.inputRef.current?.selectionStart ?? 0,
|
|
47
|
-
}));
|
|
48
|
-
this.props.onChange(newValue);
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* Return the new value for the input
|
|
52
|
-
* Find the current caret position
|
|
53
|
-
* get the string up to that point
|
|
54
|
-
* find the last word (space-delimited) and replace it in input
|
|
55
|
-
*/
|
|
56
|
-
getSuggestionValue = (suggestion) => {
|
|
57
|
-
const substringWordList = this.findWordsBeforeCurrentLocation();
|
|
58
|
-
/*
|
|
59
|
-
* Find the position of the caret, get the string up to that point
|
|
60
|
-
* and find the index of the last word in that substring
|
|
61
|
-
* This gives use the word to suggest matches for
|
|
62
|
-
*/
|
|
63
|
-
const closestWord = substringWordList.at(-1);
|
|
64
|
-
const position = this.state.value.slice(0, this.state.caretIndex).lastIndexOf(closestWord);
|
|
65
|
-
// Sub in the new property suggestion
|
|
66
|
-
return (this.state.value.substring(0, position) +
|
|
67
|
-
suggestion.value +
|
|
68
|
-
this.state.value.substring(position + closestWord.length));
|
|
69
|
-
};
|
|
70
|
-
getSuggestions = () => {
|
|
71
|
-
const substringWordList = this.findWordsBeforeCurrentLocation();
|
|
72
|
-
// Prevent empty string from matching everyhing
|
|
73
|
-
const closestWord = substringWordList.at(-1);
|
|
74
|
-
return !closestWord
|
|
75
|
-
? []
|
|
76
|
-
: matchSorter(this.props.autoSuggestions, closestWord, {
|
|
77
|
-
// Use match-sorter for matching inputs
|
|
78
|
-
keys: [{ key: "value" }],
|
|
79
|
-
});
|
|
80
|
-
};
|
|
81
|
-
// Get array of distinct words prior to the current location of entered text
|
|
82
|
-
// Use the inputRef instead of state becuase of asnychronous updating of state and calling of these functions :(
|
|
83
|
-
findWordsBeforeCurrentLocation = () => {
|
|
84
|
-
return this.inputRef.current?.value
|
|
85
|
-
.slice(0, this.inputRef.current?.selectionStart ?? undefined)
|
|
86
|
-
.split(" ");
|
|
87
|
-
};
|
|
88
|
-
onSuggestionsClearRequested = () => {
|
|
89
|
-
this.setState({
|
|
90
|
-
suggestions: [],
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
render() {
|
|
94
|
-
const { inputProps, children, ...rest } = this.props;
|
|
95
|
-
const finalInputProps = {
|
|
96
|
-
...inputProps,
|
|
97
|
-
onChange: this.onInputChange,
|
|
98
|
-
value: this.state.value,
|
|
99
|
-
};
|
|
100
|
-
return (React.createElement("div", { className: `${prefix}--bmrg-auto-suggest` },
|
|
101
|
-
React.createElement(AutoSuggest, { getSuggestionValue: this.getSuggestionValue, inputProps: finalInputProps, onSuggestionsClearRequested: this.onSuggestionsClearRequested, onSuggestionsFetchRequested: this.onSuggestionsFetchRequested, renderInputComponent: (props) => React.cloneElement(children, { ...props, ref: this.inputRef }), renderSuggestion: this.renderSuggestion, renderSuggestionsContainer: renderSuggestionsContainer, suggestions: this.state.suggestions, ...rest })));
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
// Needed to add aria-label for a11y
|
|
105
|
-
function renderSuggestionsContainer({ containerProps, children, ...rest }) {
|
|
106
|
-
return (React.createElement("div", { "aria-label": "AutoSuggest listbox", ...containerProps, ...rest }, children));
|
|
6
|
+
/*
|
|
7
|
+
IBM Confidential
|
|
8
|
+
694970X, 69497O0
|
|
9
|
+
© Copyright IBM Corp. 2022, 2024
|
|
10
|
+
*/
|
|
11
|
+
const SELECT_METHODS = ["up", "down", "click"];
|
|
12
|
+
class AutoSuggestBmrg extends Component {
|
|
13
|
+
inputRef = React.createRef();
|
|
14
|
+
state = {
|
|
15
|
+
// Used if we want to have some of the functions external instead of
|
|
16
|
+
// the default ones in the wrapper
|
|
17
|
+
value: this.props.initialValue ?? "",
|
|
18
|
+
caretIndex: 0,
|
|
19
|
+
suggestions: [],
|
|
20
|
+
};
|
|
21
|
+
// Each time the component updates we want to refocus the input and keep the cursor in the correct place
|
|
22
|
+
// Needed for when cycling through mutliple suggestions with the arrow keys and the cursor resets to the end of the input. We don't want that.
|
|
23
|
+
componentDidUpdate(_, prevState) {
|
|
24
|
+
if (this.state.value && prevState.value !== this.state.value) {
|
|
25
|
+
// prevent it from focusing on initial render / empty
|
|
26
|
+
this.inputRef.current?.focus();
|
|
27
|
+
this.inputRef.current?.setSelectionRange(this.state.caretIndex, this.state.caretIndex);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
renderSuggestion = (suggestion) => suggestion.label;
|
|
31
|
+
onSuggestionsFetchRequested = () => {
|
|
32
|
+
this.setState(() => ({
|
|
33
|
+
suggestions: this.getSuggestions(),
|
|
34
|
+
}));
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* More logic here for handling a user cycling through suggestions
|
|
38
|
+
* Move the caret to the new suggestion location or use the reference to the DOM element.
|
|
39
|
+
* Shift based on the change in length of the value b/c of different length suggestions
|
|
40
|
+
*/
|
|
41
|
+
onInputChange = (event, { newValue, method }) => {
|
|
42
|
+
this.setState((prevState) => ({
|
|
43
|
+
value: newValue,
|
|
44
|
+
caretIndex: SELECT_METHODS.includes(method)
|
|
45
|
+
? prevState.caretIndex + (newValue.length - prevState.value.length)
|
|
46
|
+
: this.inputRef.current?.selectionStart ?? 0,
|
|
47
|
+
}));
|
|
48
|
+
this.props.onChange(newValue);
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Return the new value for the input
|
|
52
|
+
* Find the current caret position
|
|
53
|
+
* get the string up to that point
|
|
54
|
+
* find the last word (space-delimited) and replace it in input
|
|
55
|
+
*/
|
|
56
|
+
getSuggestionValue = (suggestion) => {
|
|
57
|
+
const substringWordList = this.findWordsBeforeCurrentLocation();
|
|
58
|
+
/*
|
|
59
|
+
* Find the position of the caret, get the string up to that point
|
|
60
|
+
* and find the index of the last word in that substring
|
|
61
|
+
* This gives use the word to suggest matches for
|
|
62
|
+
*/
|
|
63
|
+
const closestWord = substringWordList.at(-1);
|
|
64
|
+
const position = this.state.value.slice(0, this.state.caretIndex).lastIndexOf(closestWord);
|
|
65
|
+
// Sub in the new property suggestion
|
|
66
|
+
return (this.state.value.substring(0, position) +
|
|
67
|
+
suggestion.value +
|
|
68
|
+
this.state.value.substring(position + closestWord.length));
|
|
69
|
+
};
|
|
70
|
+
getSuggestions = () => {
|
|
71
|
+
const substringWordList = this.findWordsBeforeCurrentLocation();
|
|
72
|
+
// Prevent empty string from matching everyhing
|
|
73
|
+
const closestWord = substringWordList.at(-1);
|
|
74
|
+
return !closestWord
|
|
75
|
+
? []
|
|
76
|
+
: matchSorter(this.props.autoSuggestions, closestWord, {
|
|
77
|
+
// Use match-sorter for matching inputs
|
|
78
|
+
keys: [{ key: "value" }],
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
// Get array of distinct words prior to the current location of entered text
|
|
82
|
+
// Use the inputRef instead of state becuase of asnychronous updating of state and calling of these functions :(
|
|
83
|
+
findWordsBeforeCurrentLocation = () => {
|
|
84
|
+
return this.inputRef.current?.value
|
|
85
|
+
.slice(0, this.inputRef.current?.selectionStart ?? undefined)
|
|
86
|
+
.split(" ");
|
|
87
|
+
};
|
|
88
|
+
onSuggestionsClearRequested = () => {
|
|
89
|
+
this.setState({
|
|
90
|
+
suggestions: [],
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
render() {
|
|
94
|
+
const { inputProps, children, ...rest } = this.props;
|
|
95
|
+
const finalInputProps = {
|
|
96
|
+
...inputProps,
|
|
97
|
+
onChange: this.onInputChange,
|
|
98
|
+
value: this.state.value,
|
|
99
|
+
};
|
|
100
|
+
return (React.createElement("div", { className: `${prefix}--bmrg-auto-suggest` },
|
|
101
|
+
React.createElement(AutoSuggest, { getSuggestionValue: this.getSuggestionValue, inputProps: finalInputProps, onSuggestionsClearRequested: this.onSuggestionsClearRequested, onSuggestionsFetchRequested: this.onSuggestionsFetchRequested, renderInputComponent: (props) => React.cloneElement(children, { ...props, ref: this.inputRef }), renderSuggestion: this.renderSuggestion, renderSuggestionsContainer: renderSuggestionsContainer, suggestions: this.state.suggestions, ...rest })));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Needed to add aria-label for a11y
|
|
105
|
+
function renderSuggestionsContainer({ containerProps, children, ...rest }) {
|
|
106
|
+
return (React.createElement("div", { "aria-label": "AutoSuggest listbox", ...containerProps, ...rest }, children));
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
export { AutoSuggestBmrg as default };
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { prefix } from '../../internal/settings.js';
|
|
3
3
|
|
|
4
|
-
/*
|
|
5
|
-
IBM Confidential
|
|
6
|
-
694970X, 69497O0
|
|
7
|
-
© Copyright IBM Corp. 2022, 2024
|
|
8
|
-
*/
|
|
9
|
-
const UserIcon = (props) => {
|
|
10
|
-
const { description, userName, plainTooltip, ...rest } = props;
|
|
11
|
-
return (React.createElement("svg", { version: "1.1", xmlns: "http://www.w3.org/2000/svg", x: "0px", y: "0px", width: "16px", height: "16px", viewBox: "0 0 16 16", role: "img", ...rest },
|
|
12
|
-
React.createElement("title", null, plainTooltip || !userName ? "Profile Image" : `User icon for ${userName}`),
|
|
13
|
-
description && React.createElement("desc", null, description),
|
|
14
|
-
React.createElement("g", null,
|
|
15
|
-
React.createElement("path", { fill: "currentColor", d: "M4,13.7C5.1,14.5,6.5,15,8,15s2.9-0.5,4-1.3v-1.2c0-0.8-0.7-1.5-1.5-1.5h-5C4.7,11,4,11.7,4,12.5V13.7\n\t\tz M3,12.9v-0.4C3,11.1,4.1,10,5.5,10h5c1.4,0,2.5,1.1,2.5,2.5v0.4c1.2-1.3,2-3,2-4.9c0-3.9-3.1-7-7-7S1,4.1,1,8\n\t\tC1,9.9,1.8,11.6,3,12.9z M8,16c-4.4,0-8-3.6-8-8s3.6-8,8-8s8,3.6,8,8S12.4,16,8,16z" }),
|
|
16
|
-
React.createElement("path", { fill: "currentColor", d: "M8,8c1.1,0,2-0.9,2-2S9.1,4,8,4S6,4.9,6,6S6.9,8,8,8z M8,9C6.3,9,5,7.7,5,6s1.3-3,3-3s3,1.3,3,3S9.7,9,8,9z" }))));
|
|
17
|
-
};
|
|
18
|
-
function Avatar({ alt, className = `${prefix}--bmrg-avatar`, size = "medium", src, style, title, userName, plainTooltip = false, ...rest }) {
|
|
19
|
-
const [error, setError] = useState(false);
|
|
20
|
-
const altText = plainTooltip || !userName ? "Profile Image" : `Avatar for ${userName}`;
|
|
21
|
-
return error || !src ? (React.createElement(UserIcon, { userName: userName, plainTooltip: plainTooltip, className: `${className} --${size || ""}`, description: altText, style: style })) : (React.createElement("img", { alt: alt ?? altText, className: `${className} --${size || ""}`, src: src, style: style, title: title ?? altText, onError: () => setError(true), ...rest }));
|
|
4
|
+
/*
|
|
5
|
+
IBM Confidential
|
|
6
|
+
694970X, 69497O0
|
|
7
|
+
© Copyright IBM Corp. 2022, 2024
|
|
8
|
+
*/
|
|
9
|
+
const UserIcon = (props) => {
|
|
10
|
+
const { description, userName, plainTooltip, ...rest } = props;
|
|
11
|
+
return (React.createElement("svg", { version: "1.1", xmlns: "http://www.w3.org/2000/svg", x: "0px", y: "0px", width: "16px", height: "16px", viewBox: "0 0 16 16", role: "img", ...rest },
|
|
12
|
+
React.createElement("title", null, plainTooltip || !userName ? "Profile Image" : `User icon for ${userName}`),
|
|
13
|
+
description && React.createElement("desc", null, description),
|
|
14
|
+
React.createElement("g", null,
|
|
15
|
+
React.createElement("path", { fill: "currentColor", d: "M4,13.7C5.1,14.5,6.5,15,8,15s2.9-0.5,4-1.3v-1.2c0-0.8-0.7-1.5-1.5-1.5h-5C4.7,11,4,11.7,4,12.5V13.7\r\n\t\tz M3,12.9v-0.4C3,11.1,4.1,10,5.5,10h5c1.4,0,2.5,1.1,2.5,2.5v0.4c1.2-1.3,2-3,2-4.9c0-3.9-3.1-7-7-7S1,4.1,1,8\r\n\t\tC1,9.9,1.8,11.6,3,12.9z M8,16c-4.4,0-8-3.6-8-8s3.6-8,8-8s8,3.6,8,8S12.4,16,8,16z" }),
|
|
16
|
+
React.createElement("path", { fill: "currentColor", d: "M8,8c1.1,0,2-0.9,2-2S9.1,4,8,4S6,4.9,6,6S6.9,8,8,8z M8,9C6.3,9,5,7.7,5,6s1.3-3,3-3s3,1.3,3,3S9.7,9,8,9z" }))));
|
|
17
|
+
};
|
|
18
|
+
function Avatar({ alt, className = `${prefix}--bmrg-avatar`, size = "medium", src, style, title, userName, plainTooltip = false, ...rest }) {
|
|
19
|
+
const [error, setError] = useState(false);
|
|
20
|
+
const altText = plainTooltip || !userName ? "Profile Image" : `Avatar for ${userName}`;
|
|
21
|
+
return error || !src ? (React.createElement(UserIcon, { userName: userName, plainTooltip: plainTooltip, className: `${className} --${size || ""}`, description: altText, style: style })) : (React.createElement("img", { alt: alt ?? altText, className: `${className} --${size || ""}`, src: src, style: style, title: title ?? altText, onError: () => setError(true), ...rest }));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export { Avatar as default };
|
|
@@ -4,40 +4,40 @@ import { Information } from '@carbon/react/icons';
|
|
|
4
4
|
import TooltipHover from '../TooltipHover/TooltipHover.js';
|
|
5
5
|
import { prefix } from '../../internal/settings.js';
|
|
6
6
|
|
|
7
|
-
/*
|
|
8
|
-
IBM Confidential
|
|
9
|
-
694970X, 69497O0
|
|
10
|
-
© Copyright IBM Corp. 2022, 2024
|
|
11
|
-
*/
|
|
12
|
-
function CheckboxListComponent({ checkboxProps, disabled, helperText, id, initialSelectedItems = [], label, labelText, onChange, options, selectedItems: propsSelectedItems, tooltipClassName = `${prefix}--bmrg-checkbox-list__tooltip`, tooltipContent, tooltipProps = { direction: "top" }, }) {
|
|
13
|
-
const [stateSelectedItems, setSelectedItems] = useState(initialSelectedItems);
|
|
14
|
-
const selectedItems = propsSelectedItems || stateSelectedItems; // Externally controlled if selectedItems props exists
|
|
15
|
-
const handleCheckboxChange = (event, { checked: value, id }) => {
|
|
16
|
-
let newSelectedItems = [...stateSelectedItems];
|
|
17
|
-
if (value) {
|
|
18
|
-
newSelectedItems.push(id);
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
newSelectedItems = newSelectedItems.filter((item) => item !== id);
|
|
22
|
-
}
|
|
23
|
-
setSelectedItems(newSelectedItems);
|
|
24
|
-
if (typeof onChange === "function") {
|
|
25
|
-
onChange(value, id, event, newSelectedItems);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
const labelValue = label || labelText;
|
|
29
|
-
const labelTextId = !labelValue ? undefined : `${id}-label`;
|
|
30
|
-
return (React.createElement("div", { key: id, className: `${prefix}--bmrg-checkbox-list` },
|
|
31
|
-
labelValue && (React.createElement("div", { className: `${prefix}--bmrg-checkbox-list__title` },
|
|
32
|
-
React.createElement("div", { id: labelTextId, className: `${prefix}--label`, style: { marginBottom: ".3125rem" } }, labelValue),
|
|
33
|
-
tooltipContent && (React.createElement("div", { className: tooltipClassName },
|
|
34
|
-
React.createElement(TooltipHover, { ...tooltipProps, tooltipText: tooltipContent },
|
|
35
|
-
React.createElement(Information, { size: 16, fill: "currentColor" })))))),
|
|
36
|
-
helperText && React.createElement("div", { className: `${prefix}--form__helper-text` }, helperText),
|
|
37
|
-
React.createElement("div", { className: `${prefix}--bmrg-checkbox-list__list` }, options.map((option) => {
|
|
38
|
-
const checked = selectedItems.some((item) => item === option.id);
|
|
39
|
-
return (React.createElement(Checkbox, { disabled: disabled, key: option.id, id: option.id, labelText: option.labelText, onChange: handleCheckboxChange, checked: checked, ...checkboxProps }));
|
|
40
|
-
}))));
|
|
7
|
+
/*
|
|
8
|
+
IBM Confidential
|
|
9
|
+
694970X, 69497O0
|
|
10
|
+
© Copyright IBM Corp. 2022, 2024
|
|
11
|
+
*/
|
|
12
|
+
function CheckboxListComponent({ checkboxProps, disabled, helperText, id, initialSelectedItems = [], label, labelText, onChange, options, selectedItems: propsSelectedItems, tooltipClassName = `${prefix}--bmrg-checkbox-list__tooltip`, tooltipContent, tooltipProps = { direction: "top" }, }) {
|
|
13
|
+
const [stateSelectedItems, setSelectedItems] = useState(initialSelectedItems);
|
|
14
|
+
const selectedItems = propsSelectedItems || stateSelectedItems; // Externally controlled if selectedItems props exists
|
|
15
|
+
const handleCheckboxChange = (event, { checked: value, id }) => {
|
|
16
|
+
let newSelectedItems = [...stateSelectedItems];
|
|
17
|
+
if (value) {
|
|
18
|
+
newSelectedItems.push(id);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
newSelectedItems = newSelectedItems.filter((item) => item !== id);
|
|
22
|
+
}
|
|
23
|
+
setSelectedItems(newSelectedItems);
|
|
24
|
+
if (typeof onChange === "function") {
|
|
25
|
+
onChange(value, id, event, newSelectedItems);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const labelValue = label || labelText;
|
|
29
|
+
const labelTextId = !labelValue ? undefined : `${id}-label`;
|
|
30
|
+
return (React.createElement("div", { key: id, className: `${prefix}--bmrg-checkbox-list` },
|
|
31
|
+
labelValue && (React.createElement("div", { className: `${prefix}--bmrg-checkbox-list__title` },
|
|
32
|
+
React.createElement("div", { id: labelTextId, className: `${prefix}--label`, style: { marginBottom: ".3125rem" } }, labelValue),
|
|
33
|
+
tooltipContent && (React.createElement("div", { className: tooltipClassName },
|
|
34
|
+
React.createElement(TooltipHover, { ...tooltipProps, tooltipText: tooltipContent },
|
|
35
|
+
React.createElement(Information, { size: 16, fill: "currentColor" })))))),
|
|
36
|
+
helperText && React.createElement("div", { className: `${prefix}--form__helper-text` }, helperText),
|
|
37
|
+
React.createElement("div", { className: `${prefix}--bmrg-checkbox-list__list` }, options.map((option) => {
|
|
38
|
+
const checked = selectedItems.some((item) => item === option.id);
|
|
39
|
+
return (React.createElement(Checkbox, { disabled: disabled, key: option.id, id: option.id, labelText: option.labelText, onChange: handleCheckboxChange, checked: checked, ...checkboxProps }));
|
|
40
|
+
}))));
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export { CheckboxListComponent as default };
|
|
@@ -5,109 +5,109 @@ import { Information } from '@carbon/react/icons';
|
|
|
5
5
|
import TooltipHover from '../TooltipHover/TooltipHover.js';
|
|
6
6
|
import { prefix } from '../../internal/settings.js';
|
|
7
7
|
|
|
8
|
-
/*
|
|
9
|
-
IBM Confidential
|
|
10
|
-
694970X, 69497O0
|
|
11
|
-
© Copyright IBM Corp. 2022, 2024
|
|
12
|
-
*/
|
|
13
|
-
function ComboBoxComponent({ disableClear = false, id, label, labelText, titleText, tooltipClassName = `${prefix}--bmrg-select__tooltip`, tooltipContent, tooltipProps = { direction: "top" }, onChange, onInputChange, shouldFilterItem, ...restComboBoxProps }) {
|
|
14
|
-
// Set the initial selected item to the label or single value passed
|
|
15
|
-
const selectedItemRef = React.useRef(restComboBoxProps.initialSelectedItem?.label ?? restComboBoxProps.initialSelectedItem);
|
|
16
|
-
const queryRef = React.useRef(selectedItemRef.current);
|
|
17
|
-
const [hasQuery, setHasQuery] = React.useState(false);
|
|
18
|
-
// Support several props for the label text
|
|
19
|
-
const labelValue = titleText || label || labelText;
|
|
20
|
-
/**
|
|
21
|
-
* The following three functions are to support a better ComboBox filtering experience
|
|
22
|
-
* than the default or passing a `shouldFilterItem` function. With the latter, if you have a selected item
|
|
23
|
-
* only that will be displayed if you do a naive filtering of the input.
|
|
24
|
-
* We want to:
|
|
25
|
-
* 1. Filter options based on the input text and plain value or label of the item
|
|
26
|
-
* 2. After selecting a value, show all options when opening the combobox without having to clear the selection
|
|
27
|
-
* 3. Filter the values when you enter a query with an item selected
|
|
28
|
-
*/
|
|
29
|
-
/**
|
|
30
|
-
* Keep track of the selected value with a ref so it doesn' re-render and to ensure that
|
|
31
|
-
* onInputChange has a fresh value. `onChange` is called, then `onInputChange` when selecting an item
|
|
32
|
-
*/
|
|
33
|
-
const defaultOnChange = React.useCallback(({ selectedItem }) => {
|
|
34
|
-
if (!selectedItem) {
|
|
35
|
-
selectedItemRef.current = selectedItem;
|
|
36
|
-
}
|
|
37
|
-
if (typeof selectedItem === "string" || typeof selectedItem === "number") {
|
|
38
|
-
selectedItemRef.current = selectedItem;
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
selectedItemRef.current = selectedItem?.label;
|
|
42
|
-
}
|
|
43
|
-
// Additional check if the onInputChange function is not called
|
|
44
|
-
// Isn't triggered if the query value matches the selected one
|
|
45
|
-
if (queryRef.current === selectedItemRef.current) {
|
|
46
|
-
setHasQuery(false);
|
|
47
|
-
}
|
|
48
|
-
// Call consumer
|
|
49
|
-
if (onChange) {
|
|
50
|
-
onChange({ selectedItem });
|
|
51
|
-
}
|
|
52
|
-
}, [onChange]);
|
|
53
|
-
/**
|
|
54
|
-
* When an item is selected, the `onInputChange` handler is called with the value selected
|
|
55
|
-
* so it is difficult to disambiguate between a keydown event and a select event
|
|
56
|
-
* Take a simple approach here. If the selectedItem and input values match, there isn't a query
|
|
57
|
-
* If they don't, there is a query. We use this to determine if we should filter the values.
|
|
58
|
-
*/
|
|
59
|
-
const defaultInputChange = (input) => {
|
|
60
|
-
queryRef.current = input;
|
|
61
|
-
if (input !== selectedItemRef.current) {
|
|
62
|
-
setHasQuery(true);
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
setHasQuery(false);
|
|
66
|
-
}
|
|
67
|
-
// Call consumer
|
|
68
|
-
if (onInputChange) {
|
|
69
|
-
onInputChange(input);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
/**
|
|
73
|
-
* Determine if I should filter the items or not
|
|
74
|
-
* Selected value and no query means show everything, otherwise filter based on the input
|
|
75
|
-
* No point in optimizing this because re-renders will only occur on query changes
|
|
76
|
-
* and we need fresh values on those events to determine how to filter
|
|
77
|
-
*/
|
|
78
|
-
const defaultShouldFilterItem = ({ item, inputValue }) => {
|
|
79
|
-
if (selectedItemRef.current && !hasQuery) {
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
if (typeof item === "string" || typeof item === "number") {
|
|
83
|
-
return String(item).toLowerCase().includes(inputValue?.toLowerCase());
|
|
84
|
-
}
|
|
85
|
-
if (item && item.label) {
|
|
86
|
-
return String(item.label).toLowerCase().includes(inputValue?.toLowerCase());
|
|
87
|
-
}
|
|
88
|
-
return item;
|
|
89
|
-
};
|
|
90
|
-
/**
|
|
91
|
-
* If a function is passed, use that
|
|
92
|
-
* If a false or null value is explicitely passed, then use default filtering behavior in component
|
|
93
|
-
* Otherwise use our filtering logic as the new default
|
|
94
|
-
*/
|
|
95
|
-
let finalShouldFilterItem;
|
|
96
|
-
if (typeof shouldFilterItem === "function") {
|
|
97
|
-
finalShouldFilterItem = shouldFilterItem;
|
|
98
|
-
}
|
|
99
|
-
else if (shouldFilterItem === false || shouldFilterItem === null) {
|
|
100
|
-
finalShouldFilterItem = undefined;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
finalShouldFilterItem = defaultShouldFilterItem;
|
|
104
|
-
}
|
|
105
|
-
return (React.createElement("div", { key: id, className: cx(`${prefix}--bmrg-select`, { "--disableClear": disableClear }) },
|
|
106
|
-
React.createElement(ComboBox, { id: id, titleText: labelValue && (React.createElement("div", { style: { display: "flex" } },
|
|
107
|
-
React.createElement("div", null, titleText || labelText || label),
|
|
108
|
-
tooltipContent && (React.createElement("div", { className: tooltipClassName },
|
|
109
|
-
React.createElement(TooltipHover, { ...tooltipProps, tooltipText: tooltipContent },
|
|
110
|
-
React.createElement(Information, { size: 16, fill: "currentColor" })))))), onChange: defaultOnChange, onInputChange: defaultInputChange, shouldFilterItem: finalShouldFilterItem, ...restComboBoxProps })));
|
|
8
|
+
/*
|
|
9
|
+
IBM Confidential
|
|
10
|
+
694970X, 69497O0
|
|
11
|
+
© Copyright IBM Corp. 2022, 2024
|
|
12
|
+
*/
|
|
13
|
+
function ComboBoxComponent({ disableClear = false, id, label, labelText, titleText, tooltipClassName = `${prefix}--bmrg-select__tooltip`, tooltipContent, tooltipProps = { direction: "top" }, onChange, onInputChange, shouldFilterItem, ...restComboBoxProps }) {
|
|
14
|
+
// Set the initial selected item to the label or single value passed
|
|
15
|
+
const selectedItemRef = React.useRef(restComboBoxProps.initialSelectedItem?.label ?? restComboBoxProps.initialSelectedItem);
|
|
16
|
+
const queryRef = React.useRef(selectedItemRef.current);
|
|
17
|
+
const [hasQuery, setHasQuery] = React.useState(false);
|
|
18
|
+
// Support several props for the label text
|
|
19
|
+
const labelValue = titleText || label || labelText;
|
|
20
|
+
/**
|
|
21
|
+
* The following three functions are to support a better ComboBox filtering experience
|
|
22
|
+
* than the default or passing a `shouldFilterItem` function. With the latter, if you have a selected item
|
|
23
|
+
* only that will be displayed if you do a naive filtering of the input.
|
|
24
|
+
* We want to:
|
|
25
|
+
* 1. Filter options based on the input text and plain value or label of the item
|
|
26
|
+
* 2. After selecting a value, show all options when opening the combobox without having to clear the selection
|
|
27
|
+
* 3. Filter the values when you enter a query with an item selected
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Keep track of the selected value with a ref so it doesn' re-render and to ensure that
|
|
31
|
+
* onInputChange has a fresh value. `onChange` is called, then `onInputChange` when selecting an item
|
|
32
|
+
*/
|
|
33
|
+
const defaultOnChange = React.useCallback(({ selectedItem }) => {
|
|
34
|
+
if (!selectedItem) {
|
|
35
|
+
selectedItemRef.current = selectedItem;
|
|
36
|
+
}
|
|
37
|
+
if (typeof selectedItem === "string" || typeof selectedItem === "number") {
|
|
38
|
+
selectedItemRef.current = selectedItem;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
selectedItemRef.current = selectedItem?.label;
|
|
42
|
+
}
|
|
43
|
+
// Additional check if the onInputChange function is not called
|
|
44
|
+
// Isn't triggered if the query value matches the selected one
|
|
45
|
+
if (queryRef.current === selectedItemRef.current) {
|
|
46
|
+
setHasQuery(false);
|
|
47
|
+
}
|
|
48
|
+
// Call consumer
|
|
49
|
+
if (onChange) {
|
|
50
|
+
onChange({ selectedItem });
|
|
51
|
+
}
|
|
52
|
+
}, [onChange]);
|
|
53
|
+
/**
|
|
54
|
+
* When an item is selected, the `onInputChange` handler is called with the value selected
|
|
55
|
+
* so it is difficult to disambiguate between a keydown event and a select event
|
|
56
|
+
* Take a simple approach here. If the selectedItem and input values match, there isn't a query
|
|
57
|
+
* If they don't, there is a query. We use this to determine if we should filter the values.
|
|
58
|
+
*/
|
|
59
|
+
const defaultInputChange = (input) => {
|
|
60
|
+
queryRef.current = input;
|
|
61
|
+
if (input !== selectedItemRef.current) {
|
|
62
|
+
setHasQuery(true);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
setHasQuery(false);
|
|
66
|
+
}
|
|
67
|
+
// Call consumer
|
|
68
|
+
if (onInputChange) {
|
|
69
|
+
onInputChange(input);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Determine if I should filter the items or not
|
|
74
|
+
* Selected value and no query means show everything, otherwise filter based on the input
|
|
75
|
+
* No point in optimizing this because re-renders will only occur on query changes
|
|
76
|
+
* and we need fresh values on those events to determine how to filter
|
|
77
|
+
*/
|
|
78
|
+
const defaultShouldFilterItem = ({ item, inputValue }) => {
|
|
79
|
+
if (selectedItemRef.current && !hasQuery) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (typeof item === "string" || typeof item === "number") {
|
|
83
|
+
return String(item).toLowerCase().includes(inputValue?.toLowerCase());
|
|
84
|
+
}
|
|
85
|
+
if (item && item.label) {
|
|
86
|
+
return String(item.label).toLowerCase().includes(inputValue?.toLowerCase());
|
|
87
|
+
}
|
|
88
|
+
return item;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* If a function is passed, use that
|
|
92
|
+
* If a false or null value is explicitely passed, then use default filtering behavior in component
|
|
93
|
+
* Otherwise use our filtering logic as the new default
|
|
94
|
+
*/
|
|
95
|
+
let finalShouldFilterItem;
|
|
96
|
+
if (typeof shouldFilterItem === "function") {
|
|
97
|
+
finalShouldFilterItem = shouldFilterItem;
|
|
98
|
+
}
|
|
99
|
+
else if (shouldFilterItem === false || shouldFilterItem === null) {
|
|
100
|
+
finalShouldFilterItem = undefined;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
finalShouldFilterItem = defaultShouldFilterItem;
|
|
104
|
+
}
|
|
105
|
+
return (React.createElement("div", { key: id, className: cx(`${prefix}--bmrg-select`, { "--disableClear": disableClear }) },
|
|
106
|
+
React.createElement(ComboBox, { id: id, titleText: labelValue && (React.createElement("div", { style: { display: "flex" } },
|
|
107
|
+
React.createElement("div", null, titleText || labelText || label),
|
|
108
|
+
tooltipContent && (React.createElement("div", { className: tooltipClassName },
|
|
109
|
+
React.createElement(TooltipHover, { ...tooltipProps, tooltipText: tooltipContent },
|
|
110
|
+
React.createElement(Information, { size: 16, fill: "currentColor" })))))), onChange: defaultOnChange, onInputChange: defaultInputChange, shouldFilterItem: finalShouldFilterItem, ...restComboBoxProps })));
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
export { ComboBoxComponent as default };
|
|
@@ -5,38 +5,38 @@ import MultiSelectComboBox from './MultiSelect.js';
|
|
|
5
5
|
import TooltipHover from '../TooltipHover/TooltipHover.js';
|
|
6
6
|
import { prefix } from '../../internal/settings.js';
|
|
7
7
|
|
|
8
|
-
/*
|
|
9
|
-
IBM Confidential
|
|
10
|
-
694970X, 69497O0
|
|
11
|
-
© Copyright IBM Corp. 2022, 2024
|
|
12
|
-
*/
|
|
13
|
-
/**
|
|
14
|
-
* For now we expect that if the prop value is a csv string,
|
|
15
|
-
* then the items would be either in the key:value or value:label format.
|
|
16
|
-
* The prop value would contain either the keys in the key:value or values in the value:label.
|
|
17
|
-
*/
|
|
18
|
-
function getFilteredItems({ items, selectedItems }) {
|
|
19
|
-
return items.filter((item) => selectedItems.some((selectedItem) => selectedItem === item.key || selectedItem === item.value));
|
|
20
|
-
}
|
|
21
|
-
function ComboBoxMultiSelect({ disableClear = false, id, initialSelectedItems, items, label, labelText, selectedItems, titleText, tooltipClassName = `${prefix}--bmrg-multi-select__tooltip`, tooltipContent, tooltipProps = { direction: "top" }, ...multiSelectProps }) {
|
|
22
|
-
const labelValue = titleText || label || labelText;
|
|
23
|
-
let finalInitialSelectedItems = initialSelectedItems;
|
|
24
|
-
let finalSelectedItems = selectedItems;
|
|
25
|
-
/** Add support for csv strings */
|
|
26
|
-
if (typeof finalInitialSelectedItems === "string") {
|
|
27
|
-
const initialSelectedItemsArray = finalInitialSelectedItems.split(",");
|
|
28
|
-
finalInitialSelectedItems = getFilteredItems({ items, selectedItems: initialSelectedItemsArray });
|
|
29
|
-
}
|
|
30
|
-
if (typeof finalSelectedItems === "string") {
|
|
31
|
-
const selectedItemsArray = finalSelectedItems.split(",");
|
|
32
|
-
finalSelectedItems = getFilteredItems({ items, selectedItems: selectedItemsArray });
|
|
33
|
-
}
|
|
34
|
-
return (React.createElement("div", { key: id, className: cx(`${prefix}--bmrg-multi-select`, { "--disableClear": disableClear }) },
|
|
35
|
-
React.createElement(MultiSelectComboBox, { id: id, titleText: labelValue && (React.createElement("div", { style: { display: "flex" } },
|
|
36
|
-
React.createElement("div", null, titleText || labelText || label),
|
|
37
|
-
tooltipContent && (React.createElement("div", { className: tooltipClassName },
|
|
38
|
-
React.createElement(TooltipHover, { ...tooltipProps, tooltipText: tooltipContent },
|
|
39
|
-
React.createElement(Information, { size: 16, fill: "currentColor" })))))), initialSelectedItems: finalInitialSelectedItems, selectedItems: finalSelectedItems, items: items, ...multiSelectProps })));
|
|
8
|
+
/*
|
|
9
|
+
IBM Confidential
|
|
10
|
+
694970X, 69497O0
|
|
11
|
+
© Copyright IBM Corp. 2022, 2024
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* For now we expect that if the prop value is a csv string,
|
|
15
|
+
* then the items would be either in the key:value or value:label format.
|
|
16
|
+
* The prop value would contain either the keys in the key:value or values in the value:label.
|
|
17
|
+
*/
|
|
18
|
+
function getFilteredItems({ items, selectedItems }) {
|
|
19
|
+
return items.filter((item) => selectedItems.some((selectedItem) => selectedItem === item.key || selectedItem === item.value));
|
|
20
|
+
}
|
|
21
|
+
function ComboBoxMultiSelect({ disableClear = false, id, initialSelectedItems, items, label, labelText, selectedItems, titleText, tooltipClassName = `${prefix}--bmrg-multi-select__tooltip`, tooltipContent, tooltipProps = { direction: "top" }, ...multiSelectProps }) {
|
|
22
|
+
const labelValue = titleText || label || labelText;
|
|
23
|
+
let finalInitialSelectedItems = initialSelectedItems;
|
|
24
|
+
let finalSelectedItems = selectedItems;
|
|
25
|
+
/** Add support for csv strings */
|
|
26
|
+
if (typeof finalInitialSelectedItems === "string") {
|
|
27
|
+
const initialSelectedItemsArray = finalInitialSelectedItems.split(",");
|
|
28
|
+
finalInitialSelectedItems = getFilteredItems({ items, selectedItems: initialSelectedItemsArray });
|
|
29
|
+
}
|
|
30
|
+
if (typeof finalSelectedItems === "string") {
|
|
31
|
+
const selectedItemsArray = finalSelectedItems.split(",");
|
|
32
|
+
finalSelectedItems = getFilteredItems({ items, selectedItems: selectedItemsArray });
|
|
33
|
+
}
|
|
34
|
+
return (React.createElement("div", { key: id, className: cx(`${prefix}--bmrg-multi-select`, { "--disableClear": disableClear }) },
|
|
35
|
+
React.createElement(MultiSelectComboBox, { id: id, titleText: labelValue && (React.createElement("div", { style: { display: "flex" } },
|
|
36
|
+
React.createElement("div", null, titleText || labelText || label),
|
|
37
|
+
tooltipContent && (React.createElement("div", { className: tooltipClassName },
|
|
38
|
+
React.createElement(TooltipHover, { ...tooltipProps, tooltipText: tooltipContent },
|
|
39
|
+
React.createElement(Information, { size: 16, fill: "currentColor" })))))), initialSelectedItems: finalInitialSelectedItems, selectedItems: finalSelectedItems, items: items, ...multiSelectProps })));
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export { ComboBoxMultiSelect as default };
|