@mtes-mct/monitor-ui 6.3.2 → 6.4.0
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/CHANGELOG.md +15 -0
- package/fields/MultiSelect.d.ts +3 -1
- package/fields/Search.d.ts +5 -1
- package/fields/Select.d.ts +3 -1
- package/index.js +80 -109
- package/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [6.3.3](https://github.com/MTES-MCT/monitor-ui/compare/v6.3.2...v6.3.3) (2023-05-25)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **fields:** control options internally with customSearch in Select & MultiSelect ([251ad18](https://github.com/MTES-MCT/monitor-ui/commit/251ad188dbc388c6efcaa1e2bf0afdd0ba343a65))
|
|
7
|
+
* **fields:** reset controlled rsuite data on change with customSearch in MultiSelect ([b96b6d5](https://github.com/MTES-MCT/monitor-ui/commit/b96b6d5eb0e3e00170449ec5df0615ee7c6cea96))
|
|
8
|
+
|
|
9
|
+
## [6.3.2](https://github.com/MTES-MCT/monitor-ui/compare/v6.3.1...v6.3.2) (2023-05-25)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **libs:** make CustomSearch fully functional ([931dda0](https://github.com/MTES-MCT/monitor-ui/commit/931dda03e79ba3710d9ae0d14a45305e0c57b819))
|
|
15
|
+
|
|
1
16
|
## [6.3.1](https://github.com/MTES-MCT/monitor-ui/compare/v6.3.0...v6.3.1) (2023-05-24)
|
|
2
17
|
|
|
3
18
|
|
package/fields/MultiSelect.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export type MultiSelectProps<OptionValue extends OptionValueType = string> = Omi
|
|
|
6
6
|
/** Used to pass something else than `window.document` as a base container to attach global events listeners. */
|
|
7
7
|
baseContainer?: Document | HTMLDivElement | null | undefined;
|
|
8
8
|
customSearch?: CustomSearch<Option<OptionValue>> | undefined;
|
|
9
|
+
/** Minimum search query length required to trigger custom search filtering. */
|
|
10
|
+
customSearchMinQueryLength?: number | undefined;
|
|
9
11
|
error?: string | undefined;
|
|
10
12
|
isErrorMessageHidden?: boolean | undefined;
|
|
11
13
|
isLabelHidden?: boolean | undefined;
|
|
@@ -18,4 +20,4 @@ export type MultiSelectProps<OptionValue extends OptionValueType = string> = Omi
|
|
|
18
20
|
options: Option<OptionValue>[];
|
|
19
21
|
value?: OptionValue[] | undefined;
|
|
20
22
|
};
|
|
21
|
-
export declare function MultiSelect<OptionValue extends OptionValueType = string>({ baseContainer, customSearch, disabled, error, isErrorMessageHidden, isLabelHidden, isLight, isUndefinedWhenDisabled, label, onChange, options, optionValueKey, searchable, value, ...originalProps }: MultiSelectProps<OptionValue>): JSX.Element;
|
|
23
|
+
export declare function MultiSelect<OptionValue extends OptionValueType = string>({ baseContainer, customSearch, customSearchMinQueryLength, disabled, error, isErrorMessageHidden, isLabelHidden, isLight, isUndefinedWhenDisabled, label, onChange, options, optionValueKey, searchable, value, ...originalProps }: MultiSelectProps<OptionValue>): JSX.Element;
|
package/fields/Search.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ElementType } from 'react';
|
|
2
|
+
import type { CustomSearch } from '../libs/CustomSearch';
|
|
2
3
|
import type { Option, OptionValueType } from '../types';
|
|
3
4
|
import type { AutoCompleteProps as RsuiteAutoCompleteProps } from 'rsuite';
|
|
4
5
|
import type { Promisable } from 'type-fest';
|
|
@@ -6,6 +7,9 @@ export type SearchProps<OptionValue extends OptionValueType = string> = Omit<Rsu
|
|
|
6
7
|
MenuItem?: ElementType | undefined;
|
|
7
8
|
/** Used to pass something else than `window.document` as a base container to attach global events listeners. */
|
|
8
9
|
baseContainer?: Document | HTMLDivElement | null | undefined;
|
|
10
|
+
customSearch?: CustomSearch<Option<OptionValue>> | undefined;
|
|
11
|
+
/** Minimum search query length required to trigger custom search filtering. */
|
|
12
|
+
customSearchMinQueryLength?: number | undefined;
|
|
9
13
|
error?: string | undefined;
|
|
10
14
|
isErrorMessageHidden?: boolean | undefined;
|
|
11
15
|
isLabelHidden?: boolean | undefined;
|
|
@@ -19,4 +23,4 @@ export type SearchProps<OptionValue extends OptionValueType = string> = Omit<Rsu
|
|
|
19
23
|
options?: Option<OptionValue>[];
|
|
20
24
|
value?: OptionValue | undefined;
|
|
21
25
|
};
|
|
22
|
-
export declare function Search<OptionValue extends OptionValueType = string>({ baseContainer, className, error, isErrorMessageHidden, isLabelHidden, isLight, isSearchIconVisible, label, MenuItem, onChange, onQuery, options, optionValueKey, value, ...originalProps }: SearchProps<OptionValue>): JSX.Element;
|
|
26
|
+
export declare function Search<OptionValue extends OptionValueType = string>({ baseContainer, className, customSearch, customSearchMinQueryLength, error, isErrorMessageHidden, isLabelHidden, isLight, isSearchIconVisible, label, MenuItem, onChange, onQuery, options, optionValueKey, value, ...originalProps }: SearchProps<OptionValue>): JSX.Element;
|
package/fields/Select.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export type SelectProps<OptionValue extends OptionValueType = string> = Omit<Sel
|
|
|
6
6
|
/** Used to pass something else than `window.document` as a base container to attach global events listeners. */
|
|
7
7
|
baseContainer?: Document | HTMLDivElement | null | undefined;
|
|
8
8
|
customSearch?: CustomSearch<Option<OptionValue>> | undefined;
|
|
9
|
+
/** Minimum search query length required to trigger custom search filtering. */
|
|
10
|
+
customSearchMinQueryLength?: number | undefined;
|
|
9
11
|
error?: string | undefined;
|
|
10
12
|
isCleanable?: boolean | undefined;
|
|
11
13
|
isErrorMessageHidden?: boolean | undefined;
|
|
@@ -19,4 +21,4 @@ export type SelectProps<OptionValue extends OptionValueType = string> = Omit<Sel
|
|
|
19
21
|
options: Option<OptionValue>[];
|
|
20
22
|
value?: OptionValue | undefined;
|
|
21
23
|
};
|
|
22
|
-
export declare function Select<OptionValue extends OptionValueType = string>({ baseContainer, customSearch, disabled, error, isCleanable, isErrorMessageHidden, isLabelHidden, isLight, isUndefinedWhenDisabled, label, onChange, options, optionValueKey, searchable, value, ...originalProps }: SelectProps<OptionValue>): JSX.Element;
|
|
24
|
+
export declare function Select<OptionValue extends OptionValueType = string>({ baseContainer, customSearch, customSearchMinQueryLength, disabled, error, isCleanable, isErrorMessageHidden, isLabelHidden, isLight, isUndefinedWhenDisabled, label, onChange, options, optionValueKey, searchable, value, ...originalProps }: SelectProps<OptionValue>): JSX.Element;
|
package/index.js
CHANGED
|
@@ -20739,9 +20739,11 @@ function normalizeString(text) {
|
|
|
20739
20739
|
return cleanText.length > 0 ? cleanText : undefined;
|
|
20740
20740
|
}
|
|
20741
20741
|
|
|
20742
|
-
function Search({ baseContainer, className, error, isErrorMessageHidden = false, isLabelHidden, isLight = false, isSearchIconVisible = true, label, MenuItem, onChange, onQuery, options = [], optionValueKey, value, ...originalProps }) {
|
|
20742
|
+
function Search({ baseContainer, className, customSearch = undefined, customSearchMinQueryLength = 1, error, isErrorMessageHidden = false, isLabelHidden, isLight = false, isSearchIconVisible = true, label, MenuItem, onChange, onQuery, options = [], optionValueKey, value, ...originalProps }) {
|
|
20743
20743
|
// eslint-disable-next-line no-null/no-null
|
|
20744
20744
|
const boxRef = useRef(null);
|
|
20745
|
+
/** Instance of `CustomSearch` */
|
|
20746
|
+
const customSearchRef = useRef(customSearch);
|
|
20745
20747
|
const queryRef = useRef(undefined);
|
|
20746
20748
|
const data = useMemo(() => getRsuiteDataFromOptions(options, optionValueKey), [options, optionValueKey]);
|
|
20747
20749
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -20752,6 +20754,8 @@ function Search({ baseContainer, className, error, isErrorMessageHidden = false,
|
|
|
20752
20754
|
const key = useKey([value, originalProps.disabled, originalProps.name]);
|
|
20753
20755
|
const rsuiteValue = useMemo(() => getRsuiteValueFromOptionValue(value, optionValueKey), [value, optionValueKey]);
|
|
20754
20756
|
const [inputValue, setInputValue] = useState(rsuiteValue);
|
|
20757
|
+
// Only used when `customSearch` prop is set
|
|
20758
|
+
const [controlledRsuiteData, setControlledRsuiteData] = useState(customSearch ? data : undefined);
|
|
20755
20759
|
const close = useCallback(() => {
|
|
20756
20760
|
setIsOpen(false);
|
|
20757
20761
|
}, []);
|
|
@@ -20763,6 +20767,12 @@ function Search({ baseContainer, className, error, isErrorMessageHidden = false,
|
|
|
20763
20767
|
if (!(typeof nextQuery === 'string')) {
|
|
20764
20768
|
return;
|
|
20765
20769
|
}
|
|
20770
|
+
if (customSearch && customSearchRef.current) {
|
|
20771
|
+
const nextControlledRsuiteData = nextQuery.trim().length >= customSearchMinQueryLength
|
|
20772
|
+
? getRsuiteDataFromOptions(customSearchRef.current.find(nextQuery), optionValueKey)
|
|
20773
|
+
: data;
|
|
20774
|
+
setControlledRsuiteData(nextControlledRsuiteData);
|
|
20775
|
+
}
|
|
20766
20776
|
queryRef.current = normalizeString(nextQuery);
|
|
20767
20777
|
if (event.type === 'change') {
|
|
20768
20778
|
setInputValue(nextQuery);
|
|
@@ -20777,7 +20787,7 @@ function Search({ baseContainer, className, error, isErrorMessageHidden = false,
|
|
|
20777
20787
|
if (onQuery) {
|
|
20778
20788
|
onQuery(queryRef.current);
|
|
20779
20789
|
}
|
|
20780
|
-
}, [onChange, onQuery]);
|
|
20790
|
+
}, [customSearch, onChange, onQuery, data, customSearchMinQueryLength, optionValueKey]);
|
|
20781
20791
|
const handleSelect = useCallback((_, item) => {
|
|
20782
20792
|
if (onChange) {
|
|
20783
20793
|
onChange(item.optionValue);
|
|
@@ -20789,7 +20799,13 @@ function Search({ baseContainer, className, error, isErrorMessageHidden = false,
|
|
|
20789
20799
|
useEffect(() => {
|
|
20790
20800
|
forceUpdate();
|
|
20791
20801
|
}, [forceUpdate]);
|
|
20792
|
-
return (jsxs(Field$2, { className: controlledClassName, children: [jsx(Label, { disabled: originalProps.disabled, hasError: hasError, htmlFor: originalProps.name, isHidden: isLabelHidden, children: label }), jsxs(Box$c, { ref: boxRef, isLight: isLight, children: [boxRef.current && (jsx(StyledAutoComplete, { "$isLight": isLight, container: boxRef.current,
|
|
20802
|
+
return (jsxs(Field$2, { className: controlledClassName, children: [jsx(Label, { disabled: originalProps.disabled, hasError: hasError, htmlFor: originalProps.name, isHidden: isLabelHidden, children: label }), jsxs(Box$c, { ref: boxRef, isLight: isLight, children: [boxRef.current && (jsx(StyledAutoComplete, { "$isLight": isLight, container: boxRef.current,
|
|
20803
|
+
// When we use a custom search, we use `controlledRsuiteData` to provide the matching options (data),
|
|
20804
|
+
// when we don't, we don't need to control that and just pass the non-internally-controlled `rsuiteData`
|
|
20805
|
+
data: controlledRsuiteData || data,
|
|
20806
|
+
// When we use a custom search, we use `controlledRsuiteData` to provide the matching options (data),
|
|
20807
|
+
// that's why we send this "always true" filter to disable Rsuite SelectPicker internal search filtering
|
|
20808
|
+
filterBy: (customSearch ? () => true : undefined), id: originalProps.name, onChange: handleChange, onSelect: handleSelect, open: isOpen, renderMenuItem: (itemLabel, item) => MenuItem ? jsx(MenuItem, { item: item.value }) : itemLabel, value: inputValue, ...originalProps }, key)), inputValue && (jsxs(Fragment, { children: [jsx(StyledCloseButton, { accent: Accent.TERTIARY, color: THEME.color.slateGray, Icon: Close, isSearchIconVisible: isSearchIconVisible, onClick: clean, size: Size.SMALL }), isSearchIconVisible && jsx(Separator, { children: "|" })] })), isSearchIconVisible && jsx(StyledIconSearch, { color: THEME.color.slateGray, size: 20 })] }), !isErrorMessageHidden && hasError && jsx(FieldError, { children: controlledError })] }));
|
|
20793
20809
|
}
|
|
20794
20810
|
const StyledCloseButton = styled(IconButton) `
|
|
20795
20811
|
cursor: pointer;
|
|
@@ -20812,7 +20828,7 @@ const StyledAutoComplete = styled(AutoComplete) `
|
|
|
20812
20828
|
font-size: 13px;
|
|
20813
20829
|
flex-grow: 1;
|
|
20814
20830
|
|
|
20815
|
-
|
|
20831
|
+
.rs-input {
|
|
20816
20832
|
background-color: ${p => (p.$isLight ? p.theme.color.white : p.theme.color.gainsboro)};
|
|
20817
20833
|
border-width: 0 0 1px;
|
|
20818
20834
|
border-color: ${p => (p.$isLight ? p.theme.color.white : p.theme.color.gainsboro)};
|
|
@@ -20824,7 +20840,11 @@ const StyledAutoComplete = styled(AutoComplete) `
|
|
|
20824
20840
|
|
|
20825
20841
|
:focus {
|
|
20826
20842
|
outline: unset;
|
|
20827
|
-
border-color:
|
|
20843
|
+
border-color: transparent;
|
|
20844
|
+
}
|
|
20845
|
+
:hover {
|
|
20846
|
+
outline: unset;
|
|
20847
|
+
border-color: transparent;
|
|
20828
20848
|
}
|
|
20829
20849
|
}
|
|
20830
20850
|
`;
|
|
@@ -25681,47 +25701,19 @@ class CustomSearch {
|
|
|
25681
25701
|
}
|
|
25682
25702
|
}
|
|
25683
25703
|
|
|
25684
|
-
function MultiSelect({ baseContainer, customSearch, disabled = false, error, isErrorMessageHidden = false, isLabelHidden = false, isLight = false, isUndefinedWhenDisabled = false, label, onChange, options, optionValueKey, searchable = false, value, ...originalProps }) {
|
|
25704
|
+
function MultiSelect({ baseContainer, customSearch, customSearchMinQueryLength = 1, disabled = false, error, isErrorMessageHidden = false, isLabelHidden = false, isLight = false, isUndefinedWhenDisabled = false, label, onChange, options, optionValueKey, searchable = false, value, ...originalProps }) {
|
|
25685
25705
|
// eslint-disable-next-line no-null/no-null
|
|
25686
25706
|
const boxRef = useRef(null);
|
|
25687
|
-
/**
|
|
25688
|
-
* Current list of option labels found by `CustomSearch.find()` for the current select search query
|
|
25689
|
-
*
|
|
25690
|
-
* @description
|
|
25691
|
-
* `undefined` means that search query is empty, thus all labels should be a match.
|
|
25692
|
-
*/
|
|
25693
|
-
const customSearchLabelMatchesRef = useRef(undefined);
|
|
25694
25707
|
/** Instance of `CustomSearch` */
|
|
25695
25708
|
const customSearchRef = useRef(customSearch);
|
|
25696
|
-
/** Last search query (only used when `customSearch` prop is set) */
|
|
25697
|
-
const previousSearchQueryRef = useRef('');
|
|
25698
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
25699
25709
|
const controlledError = useMemo(() => normalizeString(error), [error]);
|
|
25700
25710
|
const rsuiteData = useMemo(() => getRsuiteDataFromOptions(options, optionValueKey), [options, optionValueKey]);
|
|
25701
25711
|
const hasError = useMemo(() => Boolean(controlledError), [controlledError]);
|
|
25702
25712
|
const key = useKey([disabled, originalProps.name, value]);
|
|
25703
25713
|
const selectedRsuiteValue = useMemo(() => (value || []).map(valueItem => getRsuiteValueFromOptionValue(valueItem, optionValueKey)), [optionValueKey, value]);
|
|
25704
|
-
|
|
25705
|
-
|
|
25706
|
-
|
|
25707
|
-
// and we use the `customSearchLabelMatches` ref-stored results, in the form of option labels,
|
|
25708
|
-
// to check if the current option label is part of these results.
|
|
25709
|
-
// Note that options label are expected to be unique in order for this pattern to work.
|
|
25710
|
-
customSearchRef.current
|
|
25711
|
-
? (query, _label, item) => {
|
|
25712
|
-
if (!customSearchRef.current) {
|
|
25713
|
-
throw new Error('`customSearchRef.current` is undefined.');
|
|
25714
|
-
}
|
|
25715
|
-
// Since this function will be called xN times, N being the number of options,
|
|
25716
|
-
// we only want to update found option labels once each time the search query changes.
|
|
25717
|
-
if (query !== previousSearchQueryRef.current) {
|
|
25718
|
-
const nextCustomSearchLabelMatches = query.trim().length > 0 ? customSearchRef.current.find(query).map(option => option.label) : undefined;
|
|
25719
|
-
customSearchLabelMatchesRef.current = nextCustomSearchLabelMatches;
|
|
25720
|
-
previousSearchQueryRef.current = query;
|
|
25721
|
-
}
|
|
25722
|
-
return customSearchLabelMatchesRef.current ? customSearchLabelMatchesRef.current.includes(item.label) : true;
|
|
25723
|
-
}
|
|
25724
|
-
: undefined, []);
|
|
25714
|
+
// Only used when `customSearch` prop is set
|
|
25715
|
+
const [controlledRsuiteData, setControlledRsuiteData] = useState(customSearch ? rsuiteData : undefined);
|
|
25716
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
25725
25717
|
const { forceUpdate } = useForceUpdate();
|
|
25726
25718
|
const close = useCallback(() => {
|
|
25727
25719
|
setIsOpen(false);
|
|
@@ -25738,8 +25730,18 @@ function MultiSelect({ baseContainer, customSearch, disabled = false, error, isE
|
|
|
25738
25730
|
}
|
|
25739
25731
|
const nextValue = nextOptionRsuiteValues ? getOptionValuesFromRsuiteDataValues(nextOptionRsuiteValues) : [];
|
|
25740
25732
|
const normalizedNextValue = nextValue.length > 0 ? nextValue : undefined;
|
|
25733
|
+
setControlledRsuiteData(rsuiteData);
|
|
25741
25734
|
onChange(normalizedNextValue);
|
|
25742
|
-
}, [getOptionValuesFromRsuiteDataValues, onChange]);
|
|
25735
|
+
}, [getOptionValuesFromRsuiteDataValues, onChange, rsuiteData]);
|
|
25736
|
+
const handleSearch = useCallback((nextQuery) => {
|
|
25737
|
+
if (!customSearchRef.current || nextQuery.trim().length < customSearchMinQueryLength) {
|
|
25738
|
+
return;
|
|
25739
|
+
}
|
|
25740
|
+
const nextControlledRsuiteData = nextQuery.trim().length >= customSearchMinQueryLength
|
|
25741
|
+
? getRsuiteDataFromOptions(customSearchRef.current.find(nextQuery), optionValueKey)
|
|
25742
|
+
: rsuiteData;
|
|
25743
|
+
setControlledRsuiteData(nextControlledRsuiteData);
|
|
25744
|
+
}, [customSearchMinQueryLength, optionValueKey, rsuiteData]);
|
|
25743
25745
|
const renderMenuItem = useCallback((node) => jsx("span", { title: String(node), children: String(node) }), []);
|
|
25744
25746
|
const toggle = useCallback((event) => {
|
|
25745
25747
|
let targetElement = event.target;
|
|
@@ -25759,7 +25761,13 @@ function MultiSelect({ baseContainer, customSearch, disabled = false, error, isE
|
|
|
25759
25761
|
useEffect(() => {
|
|
25760
25762
|
forceUpdate();
|
|
25761
25763
|
}, [forceUpdate]);
|
|
25762
|
-
return (jsxs(Field$2, { className: "Field-MultiSelect", children: [jsx(Label, { disabled: disabled, hasError: hasError, htmlFor: originalProps.name, isHidden: isLabelHidden, children: label }), jsx(Box$4, { ref: boxRef, "$hasError": hasError, "$isActive": isOpen, "$isLight": isLight, onClick: toggle, children: boxRef.current && (jsx(TagPicker, { container: boxRef.current,
|
|
25764
|
+
return (jsxs(Field$2, { className: "Field-MultiSelect", children: [jsx(Label, { disabled: disabled, hasError: hasError, htmlFor: originalProps.name, isHidden: isLabelHidden, children: label }), jsx(Box$4, { ref: boxRef, "$hasError": hasError, "$isActive": isOpen, "$isLight": isLight, onClick: toggle, children: boxRef.current && (jsx(TagPicker, { container: boxRef.current,
|
|
25765
|
+
// When we use a custom search, we use `controlledRsuiteData` to provide the matching options (data),
|
|
25766
|
+
// when we don't, we don't need to control that and just pass the non-internally-controlled `rsuiteData`
|
|
25767
|
+
data: controlledRsuiteData || rsuiteData, disabled: disabled, id: originalProps.name, onChange: handleChange, onClick: toggle, onSearch: handleSearch, open: isOpen, renderMenuItem: renderMenuItem, searchable: !!customSearch || searchable,
|
|
25768
|
+
// When we use a custom search, we use `controlledRsuiteData` to provide the matching options (data),
|
|
25769
|
+
// that's why we send this "always true" filter to disable Rsuite TagPicker internal search filtering
|
|
25770
|
+
searchBy: (customSearch ? () => true : undefined), value: selectedRsuiteValue, ...originalProps }, key)) }), !isErrorMessageHidden && hasError && jsx(FieldError, { children: controlledError })] }));
|
|
25763
25771
|
}
|
|
25764
25772
|
const Box$4 = styled.div `
|
|
25765
25773
|
position: relative;
|
|
@@ -34943,48 +34951,20 @@ const StyledFieldset = styled(Fieldset) `
|
|
|
34943
34951
|
}
|
|
34944
34952
|
`;
|
|
34945
34953
|
|
|
34946
|
-
function Select({ baseContainer, customSearch, disabled = false, error, isCleanable = true, isErrorMessageHidden = false, isLabelHidden = false, isLight = false, isUndefinedWhenDisabled = false, label, onChange, options, optionValueKey, searchable = false, value, ...originalProps }) {
|
|
34954
|
+
function Select({ baseContainer, customSearch, customSearchMinQueryLength = 1, disabled = false, error, isCleanable = true, isErrorMessageHidden = false, isLabelHidden = false, isLight = false, isUndefinedWhenDisabled = false, label, onChange, options, optionValueKey, searchable = false, value, ...originalProps }) {
|
|
34947
34955
|
// eslint-disable-next-line no-null/no-null
|
|
34948
34956
|
const boxRef = useRef(null);
|
|
34949
|
-
/**
|
|
34950
|
-
* Current list of option labels found by `CustomSearch.find()` for the current select search query
|
|
34951
|
-
*
|
|
34952
|
-
* @description
|
|
34953
|
-
* `undefined` means that search query is empty, thus all labels should be a match.
|
|
34954
|
-
*/
|
|
34955
|
-
const customSearchLabelMatchesRef = useRef(undefined);
|
|
34956
34957
|
/** Instance of `CustomSearch` */
|
|
34957
34958
|
const customSearchRef = useRef(customSearch);
|
|
34958
|
-
/** Last search query (only used when `customSearch` prop is set) */
|
|
34959
|
-
const previousSearchQueryRef = useRef('');
|
|
34960
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
34961
34959
|
const { forceUpdate } = useForceUpdate();
|
|
34962
34960
|
const controlledError = useMemo(() => normalizeString(error), [error]);
|
|
34963
34961
|
const rsuiteData = useMemo(() => getRsuiteDataFromOptions(options, optionValueKey), [options, optionValueKey]);
|
|
34964
34962
|
const hasError = useMemo(() => Boolean(controlledError), [controlledError]);
|
|
34965
34963
|
const key = useKey([disabled, originalProps.name, value]);
|
|
34966
34964
|
const selectedRsuiteValue = useMemo(() => getRsuiteValueFromOptionValue(value, optionValueKey), [value, optionValueKey]);
|
|
34967
|
-
|
|
34968
|
-
|
|
34969
|
-
|
|
34970
|
-
// and we use the `customSearchLabelMatches` ref-stored results, in the form of option labels,
|
|
34971
|
-
// to check if the current option label is part of these results.
|
|
34972
|
-
// Note that options label are expected to be unique in order for this pattern to work.
|
|
34973
|
-
customSearchRef.current
|
|
34974
|
-
? (query, _label, item) => {
|
|
34975
|
-
if (!customSearchRef.current) {
|
|
34976
|
-
throw new Error('`customSearchRef.current` is undefined.');
|
|
34977
|
-
}
|
|
34978
|
-
// Since this function will be called xN times, N being the number of options,
|
|
34979
|
-
// we only want to update found option labels once each time the search query changes.
|
|
34980
|
-
if (query !== previousSearchQueryRef.current) {
|
|
34981
|
-
const nextCustomSearchLabelMatches = query.trim().length > 0 ? customSearchRef.current.find(query).map(option => option.label) : undefined;
|
|
34982
|
-
customSearchLabelMatchesRef.current = nextCustomSearchLabelMatches;
|
|
34983
|
-
previousSearchQueryRef.current = query;
|
|
34984
|
-
}
|
|
34985
|
-
return customSearchLabelMatchesRef.current ? customSearchLabelMatchesRef.current.includes(item.label) : true;
|
|
34986
|
-
}
|
|
34987
|
-
: undefined, []);
|
|
34965
|
+
// Only used when `customSearch` prop is set
|
|
34966
|
+
const [controlledRsuiteData, setControlledRsuiteData] = useState(customSearch ? rsuiteData : undefined);
|
|
34967
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
34988
34968
|
const close = useCallback(() => {
|
|
34989
34969
|
setIsOpen(false);
|
|
34990
34970
|
}, []);
|
|
@@ -34994,6 +34974,15 @@ function Select({ baseContainer, customSearch, disabled = false, error, isCleana
|
|
|
34994
34974
|
}
|
|
34995
34975
|
onChange(undefined);
|
|
34996
34976
|
}, [onChange]);
|
|
34977
|
+
const handleSearch = useCallback((nextQuery) => {
|
|
34978
|
+
if (!customSearchRef.current || nextQuery.trim().length < customSearchMinQueryLength) {
|
|
34979
|
+
return;
|
|
34980
|
+
}
|
|
34981
|
+
const nextControlledRsuiteData = nextQuery.trim().length >= customSearchMinQueryLength
|
|
34982
|
+
? getRsuiteDataFromOptions(customSearchRef.current.find(nextQuery), optionValueKey)
|
|
34983
|
+
: rsuiteData;
|
|
34984
|
+
setControlledRsuiteData(nextControlledRsuiteData);
|
|
34985
|
+
}, [customSearchMinQueryLength, optionValueKey, rsuiteData]);
|
|
34997
34986
|
const handleSelect = useCallback((_, selectedItem) => {
|
|
34998
34987
|
close();
|
|
34999
34988
|
if (onChange) {
|
|
@@ -35021,10 +35010,16 @@ function Select({ baseContainer, customSearch, disabled = false, error, isCleana
|
|
|
35021
35010
|
useEffect(() => {
|
|
35022
35011
|
forceUpdate();
|
|
35023
35012
|
}, [forceUpdate]);
|
|
35024
|
-
return (jsxs(Field$2, { className: "Field-Select", children: [jsx(Label, { disabled: disabled, hasError: hasError, htmlFor: originalProps.name, isHidden: isLabelHidden, children: label }), jsx(Box, { ref: boxRef, onClick: toggle, children: boxRef.current && (jsx(StyledSelectPicker, { "$isLight": isLight, cleanable: isCleanable, container: boxRef.current,
|
|
35013
|
+
return (jsxs(Field$2, { className: "Field-Select", children: [jsx(Label, { disabled: disabled, hasError: hasError, htmlFor: originalProps.name, isHidden: isLabelHidden, children: label }), jsx(Box, { ref: boxRef, onClick: toggle, children: boxRef.current && (jsx(StyledSelectPicker, { "$isLight": isLight, cleanable: isCleanable, container: boxRef.current,
|
|
35014
|
+
// When we use a custom search, we use `controlledRsuiteData` to provide the matching options (data),
|
|
35015
|
+
// when we don't, we don't need to control that and just pass the non-internally-controlled `rsuiteData`
|
|
35016
|
+
data: controlledRsuiteData || rsuiteData, disabled: disabled, id: originalProps.name, onClean: handleClean, onSearch: handleSearch,
|
|
35025
35017
|
// `as any` because we customized `ItemDataType` type by adding `optionValue`,
|
|
35026
35018
|
// which generates an optional vs required type conflict
|
|
35027
|
-
onSelect: handleSelect, open: isOpen, renderMenuItem: renderMenuItem, searchable: !!customSearch || searchable,
|
|
35019
|
+
onSelect: handleSelect, open: isOpen, renderMenuItem: renderMenuItem, searchable: !!customSearch || searchable,
|
|
35020
|
+
// When we use a custom search, we use `controlledRsuiteData` to provide the matching options (data),
|
|
35021
|
+
// that's why we send this "always true" filter to disable Rsuite SelectPicker internal search filtering
|
|
35022
|
+
searchBy: (customSearch ? () => true : undefined), value: selectedRsuiteValue, ...originalProps }, key)) }), !isErrorMessageHidden && hasError && jsx(FieldError, { children: controlledError })] }));
|
|
35028
35023
|
}
|
|
35029
35024
|
const StyledSelectPicker = styled(SelectPicker) `
|
|
35030
35025
|
> .rs-picker-toggle {
|
|
@@ -35190,20 +35185,17 @@ const InputBox = styled.div `
|
|
|
35190
35185
|
|
|
35191
35186
|
function FormikSearch({ name, ...originalProps }) {
|
|
35192
35187
|
const [field, meta, helpers] = useField(name);
|
|
35193
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35194
35188
|
const handleChange = useMemo(() => value => {
|
|
35195
|
-
helpers.setTouched(true);
|
|
35196
35189
|
helpers.setValue(value);
|
|
35197
35190
|
},
|
|
35198
35191
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35199
35192
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35200
35193
|
[]);
|
|
35201
|
-
return jsx(Search, { error: error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35194
|
+
return jsx(Search, { error: meta.error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35202
35195
|
}
|
|
35203
35196
|
|
|
35204
35197
|
function FormikCheckbox({ name, ...originalProps }) {
|
|
35205
35198
|
const [field, meta, helpers] = useField(name);
|
|
35206
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35207
35199
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35208
35200
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35209
35201
|
const handleChange = useMemo(() => helpers.setValue, []);
|
|
@@ -35211,56 +35203,49 @@ function FormikCheckbox({ name, ...originalProps }) {
|
|
|
35211
35203
|
// A checkbox must initialize its Formik value on mount:
|
|
35212
35204
|
// it wouldn't make sense to keep it as `undefined` since `undefined` means `false` in the case of a checkbox
|
|
35213
35205
|
useEffect(() => {
|
|
35214
|
-
helpers.setTouched(true);
|
|
35215
35206
|
helpers.setValue(isChecked);
|
|
35216
35207
|
},
|
|
35217
35208
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35218
35209
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35219
35210
|
[]);
|
|
35220
|
-
return jsx(Checkbox, { checked: isChecked, error: error, name: name, onChange: handleChange, ...originalProps });
|
|
35211
|
+
return jsx(Checkbox, { checked: isChecked, error: meta.error, name: name, onChange: handleChange, ...originalProps });
|
|
35221
35212
|
}
|
|
35222
35213
|
|
|
35223
35214
|
function FormikCoordinatesInput({ name, ...originalProps }) {
|
|
35224
35215
|
const [field, meta, helpers] = useField(name);
|
|
35225
35216
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35226
35217
|
const defaultValue = useMemo(() => field.value, []);
|
|
35227
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35228
35218
|
const handleChange = useMemo(() => value => {
|
|
35229
|
-
helpers.setTouched(true);
|
|
35230
35219
|
helpers.setValue(value);
|
|
35231
35220
|
},
|
|
35232
35221
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35233
35222
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35234
35223
|
[]);
|
|
35235
|
-
return jsx(CoordinatesInput, { defaultValue: defaultValue, error: error, onChange: handleChange, ...originalProps });
|
|
35224
|
+
return jsx(CoordinatesInput, { defaultValue: defaultValue, error: meta.error, onChange: handleChange, ...originalProps });
|
|
35236
35225
|
}
|
|
35237
35226
|
|
|
35238
35227
|
const UntypedDatePicker = DatePicker;
|
|
35239
35228
|
function FormikDatePicker({ name, ...originalProps }) {
|
|
35240
35229
|
const [field, meta, helpers] = useField(name);
|
|
35241
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35242
35230
|
const handleChange = useMemo(() => value => {
|
|
35243
|
-
helpers.setTouched(true);
|
|
35244
35231
|
helpers.setValue(value);
|
|
35245
35232
|
},
|
|
35246
35233
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35247
35234
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35248
35235
|
[]);
|
|
35249
|
-
return jsx(UntypedDatePicker, { defaultValue: field.value, error: error, onChange: handleChange, ...originalProps });
|
|
35236
|
+
return jsx(UntypedDatePicker, { defaultValue: field.value, error: meta.error, onChange: handleChange, ...originalProps });
|
|
35250
35237
|
}
|
|
35251
35238
|
|
|
35252
35239
|
const UntypedDateRangePicker = DateRangePicker;
|
|
35253
35240
|
function FormikDateRangePicker({ name, ...originalProps }) {
|
|
35254
35241
|
const [field, meta, helpers] = useField(name);
|
|
35255
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35256
35242
|
const handleChange = useMemo(() => value => {
|
|
35257
|
-
helpers.setTouched(true);
|
|
35258
35243
|
helpers.setValue(value);
|
|
35259
35244
|
},
|
|
35260
35245
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35261
35246
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35262
35247
|
[]);
|
|
35263
|
-
return jsx(UntypedDateRangePicker, { defaultValue: field.value, error: error, onChange: handleChange, ...originalProps });
|
|
35248
|
+
return (jsx(UntypedDateRangePicker, { defaultValue: field.value, error: meta.error, onChange: handleChange, ...originalProps }));
|
|
35264
35249
|
}
|
|
35265
35250
|
|
|
35266
35251
|
function FormikEffect({ onChange }) {
|
|
@@ -35273,93 +35258,79 @@ function FormikEffect({ onChange }) {
|
|
|
35273
35258
|
|
|
35274
35259
|
function FormikMultiCheckbox({ name, ...originalProps }) {
|
|
35275
35260
|
const [field, meta, helpers] = useField(name);
|
|
35276
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35277
35261
|
const handleChange = useMemo(() => value => {
|
|
35278
|
-
helpers.setTouched(true);
|
|
35279
35262
|
helpers.setValue(value);
|
|
35280
35263
|
},
|
|
35281
35264
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35282
35265
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35283
35266
|
[]);
|
|
35284
|
-
return jsx(MultiCheckbox, { error: error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35267
|
+
return jsx(MultiCheckbox, { error: meta.error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35285
35268
|
}
|
|
35286
35269
|
|
|
35287
35270
|
function FormikMultiSelect({ name, ...originalProps }) {
|
|
35288
35271
|
const [field, meta, helpers] = useField(name);
|
|
35289
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35290
35272
|
const handleChange = useMemo(() => value => {
|
|
35291
|
-
helpers.setTouched(true);
|
|
35292
35273
|
helpers.setValue(value);
|
|
35293
35274
|
},
|
|
35294
35275
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35295
35276
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35296
35277
|
[]);
|
|
35297
|
-
return jsx(MultiSelect, { error: error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35278
|
+
return jsx(MultiSelect, { error: meta.error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35298
35279
|
}
|
|
35299
35280
|
|
|
35300
35281
|
function FormikMultiRadio({ name, ...originalProps }) {
|
|
35301
35282
|
const [field, meta, helpers] = useField(name);
|
|
35302
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35303
35283
|
const handleChange = useMemo(() => value => {
|
|
35304
|
-
helpers.setTouched(true);
|
|
35305
35284
|
helpers.setValue(value);
|
|
35306
35285
|
},
|
|
35307
35286
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35308
35287
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35309
35288
|
[]);
|
|
35310
|
-
return jsx(MultiRadio, { error: error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35289
|
+
return jsx(MultiRadio, { error: meta.error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35311
35290
|
}
|
|
35312
35291
|
|
|
35313
35292
|
function FormikNumberInput({ name, ...originalProps }) {
|
|
35314
35293
|
const [field, meta, helpers] = useField(name);
|
|
35315
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35316
35294
|
const handleChange = useMemo(() => value => {
|
|
35317
|
-
helpers.setTouched(true);
|
|
35318
35295
|
helpers.setValue(value);
|
|
35319
35296
|
},
|
|
35320
35297
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35321
35298
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35322
35299
|
[]);
|
|
35323
|
-
return jsx(NumberInput, { error: error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35300
|
+
return jsx(NumberInput, { error: meta.error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35324
35301
|
}
|
|
35325
35302
|
|
|
35326
35303
|
function FormikSelect({ name, ...originalProps }) {
|
|
35327
35304
|
const [field, meta, helpers] = useField(name);
|
|
35328
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35329
35305
|
const handleChange = useMemo(() => value => {
|
|
35330
|
-
helpers.setTouched(true);
|
|
35331
35306
|
helpers.setValue(value);
|
|
35332
35307
|
},
|
|
35333
35308
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35334
35309
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35335
35310
|
[]);
|
|
35336
|
-
return jsx(Select, { error: error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35311
|
+
return jsx(Select, { error: meta.error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35337
35312
|
}
|
|
35338
35313
|
|
|
35339
35314
|
function FormikTextarea({ name, ...originalProps }) {
|
|
35340
35315
|
const [field, meta, helpers] = useField(name);
|
|
35341
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35342
35316
|
const handleChange = useMemo(() => value => {
|
|
35343
|
-
helpers.setTouched(true);
|
|
35344
35317
|
helpers.setValue(value);
|
|
35345
35318
|
},
|
|
35346
35319
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35347
35320
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35348
35321
|
[]);
|
|
35349
|
-
return jsx(Textarea, { error: error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35322
|
+
return jsx(Textarea, { error: meta.error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35350
35323
|
}
|
|
35351
35324
|
|
|
35352
35325
|
function FormikTextInput({ name, ...originalProps }) {
|
|
35353
35326
|
const [field, meta, helpers] = useField(name);
|
|
35354
|
-
const error = meta.touched ? meta.error : undefined;
|
|
35355
35327
|
const handleChange = useMemo(() => value => {
|
|
35356
|
-
helpers.setTouched(true);
|
|
35357
35328
|
helpers.setValue(value);
|
|
35358
35329
|
},
|
|
35359
35330
|
// We don't want to trigger infinite re-rendering since `helpers.setValue` changes after each rendering
|
|
35360
35331
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35361
35332
|
[]);
|
|
35362
|
-
return jsx(TextInput, { error: error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35333
|
+
return jsx(TextInput, { error: meta.error, name: name, onChange: handleChange, value: field.value, ...originalProps });
|
|
35363
35334
|
}
|
|
35364
35335
|
|
|
35365
35336
|
function useFieldControl(value, onChange, defaultValueWhenUndefined) {
|