|
1 |
| -import React from 'react'; |
| 1 | +import React, { useState, useEffect } from 'react'; |
2 | 2 | import PropTypes from 'prop-types';
|
3 | 3 | import { useFieldApi } from '@data-driven-forms/react-form-renderer';
|
4 | 4 |
|
| 5 | +import DataDrivenSelect from '@data-driven-forms/common/src/select'; |
| 6 | +import useIsMounted from '@data-driven-forms/common/src/hooks/use-is-mounted'; |
| 7 | +import fnToString from '@data-driven-forms/common/src/utils/fn-to-string'; |
| 8 | + |
| 9 | +import { Select as CarbonSelect, MultiSelect, SelectItem } from 'carbon-components-react'; |
| 10 | +import prepareProps from '../common/prepare-props'; |
| 11 | + |
| 12 | +const ClearedMultiSelectFilterable = (props) => { |
| 13 | + return 'multi.searchable'; |
| 14 | +}; |
| 15 | + |
| 16 | +const ClearedMultiSelect = (props) => { |
| 17 | + return 'multi'; |
| 18 | +}; |
| 19 | + |
| 20 | +const ClearedSelect = ({ invalidText, hideSelectedOptions, noOptionsMessage, onInputChange, options, isFetching, invalid, ...rest }) => { |
| 21 | + return ( |
| 22 | + <CarbonSelect {...rest} if={rest.name} invalid={Boolean(invalidText)} invalidText={invalidText}> |
| 23 | + {options.map((option, index) => ( |
| 24 | + <SelectItem key={option.value || index} text={option.label} {...option} /> |
| 25 | + ))} |
| 26 | + </CarbonSelect> |
| 27 | + ); |
| 28 | +}; |
| 29 | + |
5 | 30 | const Select = (props) => {
|
6 |
| - const { input, isDisabled, options } = useFieldApi(props); |
| 31 | + const { isMulti, isSearchable, loadOptions, input, meta, ...rest } = useFieldApi(prepareProps(props)); |
| 32 | + |
| 33 | + const [isFetching, setFetching] = useState(loadOptions ? true : false); |
| 34 | + const [options, setOptions] = useState(props.options || []); |
| 35 | + const isMounted = useIsMounted(); |
| 36 | + |
| 37 | + // common select controls the string of loadOptions and if the string changed, then it reloads options |
| 38 | + // however we are enhancing the loadOptions here so the string is always the same |
| 39 | + // by increasing this counter, we can enforce the update |
| 40 | + const [loadOptionsChangeCounter, setCounter] = useState(0); |
| 41 | + |
| 42 | + const loadOptionsStr = fnToString(loadOptions); |
| 43 | + |
| 44 | + useEffect(() => { |
| 45 | + setCounter(loadOptionsChangeCounter + 1); |
| 46 | + // eslint-disable-next-line react-hooks/exhaustive-deps |
| 47 | + }, [loadOptionsStr]); |
| 48 | + |
| 49 | + const loadOptionsEnhanced = loadOptions |
| 50 | + ? (value) => { |
| 51 | + if (isMounted.current) { |
| 52 | + setFetching(true); |
| 53 | + } |
| 54 | + |
| 55 | + return loadOptions(value).then((data) => { |
| 56 | + if (isMounted.current) { |
| 57 | + setOptions([...options, ...data.filter(({ value }) => !options.find((option) => option.value === value))]); |
| 58 | + setFetching(false); |
| 59 | + } |
| 60 | + |
| 61 | + return data; |
| 62 | + }); |
| 63 | + } |
| 64 | + : undefined; |
| 65 | + |
| 66 | + const Component = isMulti && isSearchable ? ClearedMultiSelectFilterable : isMulti ? ClearedMultiSelect : ClearedSelect; |
| 67 | + |
| 68 | + const invalidText = (meta.touched && meta.error) || ''; |
7 | 69 |
|
8 | 70 | return (
|
9 |
| - <select {...input} disabled={isDisabled}> |
10 |
| - {options && |
11 |
| - options.map((option) => ( |
12 |
| - <option key={option.value} value={option.value}> |
13 |
| - {option.label} |
14 |
| - </option> |
15 |
| - ))} |
16 |
| - </select> |
| 71 | + <DataDrivenSelect |
| 72 | + SelectComponent={Component} |
| 73 | + {...rest} |
| 74 | + {...input} |
| 75 | + invalidText={invalidText} |
| 76 | + loadOptionsChangeCounter={loadOptionsChangeCounter} |
| 77 | + loadOptions={loadOptionsEnhanced} |
| 78 | + simpleValue={false} |
| 79 | + /> |
17 | 80 | );
|
18 | 81 | };
|
19 | 82 |
|
|
0 commit comments