-
Notifications
You must be signed in to change notification settings - Fork 96
Expand file tree
/
Copy pathselect.tsx
More file actions
151 lines (143 loc) · 4.61 KB
/
select.tsx
File metadata and controls
151 lines (143 loc) · 4.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useMemo } from 'react';
import { AnyObject } from "@data-driven-forms/react-form-renderer";
import clsx from 'clsx';
import useSelect from '../use-select/use-select';
import deepEqual from './deep-equal';
import { SelectOption, OptionValue, SelectValue, FlatSelectOption } from '../types/shared-types';
export interface SelectProps<T = OptionValue> {
options?: SelectOption<T>[];
onChange?: (value?: SelectValue<T>) => void;
classNamePrefix?: string;
invalid?: boolean;
simpleValue?: boolean;
isMulti?: boolean;
pluckSingleValue?: boolean;
value?: SelectValue<T>;
placeholder?: string;
loadOptionsChangeCounter?: number;
isDisabled?: boolean;
isReadOnly?: boolean;
loadOptions?: (inputValue?: string) => Promise<SelectOption<T>[]>;
loadingMessage?: React.ReactNode;
loadingProps?: AnyObject;
selectVariant?: string;
updatingMessage?: React.ReactNode;
noOptionsMessage?: React.ReactNode;
noResultsMessage?: React.ReactNode;
isSearchable?: boolean;
isClearable?: boolean;
SelectComponent?: React.ComponentType<AnyObject>;
noValueUpdates?: boolean;
optionsTransformer?: (options: AnyObject[]) => FlatSelectOption<T>[];
compareValues?: (valueA: T, valueB: T) => boolean;
menuIsPortal?: boolean;
menuPortalTarget?: Element;
showMoreLabel?: string;
showLessLabel?: string;
// Allow any additional props to be passed through to SelectComponent
// Different mappers may need different props for their SelectComponent implementations
[key: string]: any;
}
const Select = <T extends OptionValue = OptionValue>({
invalid = false,
classNamePrefix,
simpleValue = true,
isMulti,
pluckSingleValue = true,
options: propsOptions,
loadOptions,
loadingMessage,
placeholder = 'Choose...',
loadingProps,
selectVariant,
updatingMessage,
noOptionsMessage,
value,
onChange,
loadOptionsChangeCounter,
SelectComponent,
noValueUpdates,
optionsTransformer,
compareValues = deepEqual,
isSearchable = false,
isClearable = false,
...props
}: SelectProps<T>) => {
const {
state,
value: selectValue,
onChange: selectOnChange,
onInputChange,
isFetching,
} = useSelect({
loadOptions,
optionsTransformer,
options: propsOptions,
noValueUpdates,
onChange,
value,
loadOptionsChangeCounter,
isSearchable,
pluckSingleValue,
isMulti,
simpleValue,
compareValues,
});
const renderNoOptionsMessage = () => (Object.values(state.promises).some((value) => value) ? () => updatingMessage : () => noOptionsMessage);
// When isMulti is true, the "getSelect" always creates new value array, we need to memoize it to not create new array instance
// Memo is required to fix https://github.com/data-driven-forms/react-forms/issues/1366
// Keeping prev values in ref and calling lodash.isEqual is not reliable as it ca return false positive beucase it only has true/false result.
// If we have multiple updates during one reconciliation pahse the search input reset will trigger on initial key stroke
// JSON.stringify is expensive but seems to be working better.
const selectValueInternal = useMemo(() => selectValue, [JSON.stringify(selectValue)]);
if (!SelectComponent) {
return null;
}
if (state.isLoading) {
return (
<SelectComponent
isClearable={isClearable}
isSearchable={isSearchable}
{...props}
classNamePrefix={classNamePrefix}
isDisabled={true}
isFetching={true}
placeholder={placeholder}
loadingMessage={loadingMessage}
options={state.options}
onChange={() => {}}
onInputChange={onInputChange}
value={selectValueInternal}
isMulti={isMulti}
{...loadingProps}
noOptionsMessage={renderNoOptionsMessage()}
{...(state.originalOptions && { originalOptions: state.originalOptions })}
/>
);
}
return (
<SelectComponent
className={clsx(classNamePrefix, {
'has-error': invalid,
})}
{...props}
isSearchable={isSearchable}
isClearable={isClearable}
placeholder={placeholder}
isDisabled={props.isDisabled || props.isReadOnly}
options={state.options}
classNamePrefix={classNamePrefix}
isMulti={isMulti}
value={selectValueInternal}
onChange={selectOnChange}
onInputChange={onInputChange}
isFetching={isFetching}
noOptionsMessage={renderNoOptionsMessage()}
hideSelectedOptions={false}
closeMenuOnSelect={!isMulti}
{...(state.originalOptions && { originalOptions: state.originalOptions })}
/>
);
};
export default Select;