UNPKG

@appbuckets/react-ui

Version:
441 lines (438 loc) 12.6 kB
import { __rest, __read, __spreadArray, __assign } from 'tslib'; import * as React from 'react'; import clsx from 'clsx'; import { createShorthandFactory, useAutoControlledValue, useForkRef, } from '@appbuckets/react-ui-core'; import ReactSelect from 'react-select'; import CreatableReactSelect from 'react-select/creatable'; import '../BucketTheme/BucketTheme.js'; import { useWithDefaultProps } from '../BucketTheme/BucketContext.js'; import { useSharedClassName, useSplitStateClassName, } from '../utils/customHook.js'; import splitFieldProps from '../utils/splitFieldProps.js'; import Field from '../Field/Field.js'; /* -------- * Component Render * -------- */ var SelectRender = function (receivedProps, ref) { var props = useWithDefaultProps('select', receivedProps); /** Split props from className */ var _a = useSharedClassName(props), className = _a.className, _b = _a.rest, /** Strict Select Component Props */ creatable = _b.creatable, userDefinedGetOptionValue = _b.getOptionValue, getNewOptionData = _b.getNewOptionData, loading = _b.loading, userDefinedOptions = _b.options, userDefinedTabIndex = _b.tabIndex, /** Select Event Handler */ userDefinedOnBlur = _b.onBlur, userDefinedOnChange = _b.onChange, userDefinedOnFocus = _b.onFocus, userDefinedOnInputChange = _b.onInputChange, userDefinedOnMenuClose = _b.onMenuClose, userDefinedOnMenuOpen = _b.onMenuOpen, userDefinedOnMenuScrollToBottom = _b.onMenuScrollToBottom, userDefinedOnMenuScrollToTop = _b.onMenuScrollToTop, /** React Select Props */ userDefinedInputValue = _b.inputValue, userDefinedDefaultInputValue = _b.defaultInputValue, userDefinedValue = _b.value, userDefinedDefaultValue = _b.defaultValue, /** All others prop */ rawRest = __rest(_b, [ 'creatable', 'getOptionValue', 'getNewOptionData', 'loading', 'options', 'tabIndex', 'onBlur', 'onChange', 'onFocus', 'onInputChange', 'onMenuClose', 'onMenuOpen', 'onMenuScrollToBottom', 'onMenuScrollToTop', 'inputValue', 'defaultInputValue', 'value', 'defaultValue', ]); // ---- // Split Props // ---- var _c = __read(useSplitStateClassName(rawRest), 2), stateClasses = _c[0], compoundProps = _c[1]; var _d = __read(splitFieldProps(compoundProps), 2), fieldProps = _d[0], rest = _d[1]; // ---- // Define Internal State and Variables // ---- var _e = __read( useAutoControlledValue('', { prop: userDefinedInputValue, defaultProp: userDefinedDefaultInputValue, }), 2 ), inputValue = _e[0], trySetInputValue = _e[1]; var _f = __read( useAutoControlledValue(null, { prop: userDefinedValue, defaultProp: userDefinedDefaultValue, }), 2 ), value = _f[0], trySetValue = _f[1]; var _g = __read(React.useState([]), 2), createdOptions = _g[0], setCreatedOptions = _g[1]; var fieldRef = React.useRef(null); var selectRef = React.useRef(null); var handleRef = useForkRef(ref, selectRef); // ---- // Merge UserDefined Options and newly created Options // ---- var options = React.useMemo( function () { return __spreadArray( __spreadArray([], __read(userDefinedOptions), false), __read(createdOptions), false ); }, [userDefinedOptions, createdOptions] ); // ---- // Define the Element and its computed props // ---- var ElementType = creatable ? CreatableReactSelect : ReactSelect; var tabIndex = React.useMemo( function () { if (fieldProps.disabled || fieldProps.readOnly) { return '-1'; } if (userDefinedTabIndex !== undefined && userDefinedTabIndex !== null) { return userDefinedTabIndex.toString(); } return undefined; }, [fieldProps.disabled, fieldProps.readOnly, userDefinedTabIndex] ); var classes = clsx( { required: fieldProps.required, 'read-only': fieldProps.readOnly, disabled: fieldProps.disabled, }, 'react-select', stateClasses, className ); // ---- // Get the Current Select Value using its ref // ---- var getOptionValue = React.useCallback( function (option) { var _a; /** If function has not be defined, return as is */ if (!userDefinedGetOptionValue) { return (_a = option === null || option === void 0 ? void 0 : option.value) !== null && _a !== void 0 ? _a : ''; } return userDefinedGetOptionValue(option); }, [userDefinedGetOptionValue] ); var getOptionValueAsString = React.useCallback( function (option) { var optionValue = getOptionValue(option); if (optionValue === undefined || optionValue === null) { return ''; } if (typeof optionValue !== 'string') { return optionValue.toString(); } return optionValue; }, [getOptionValue] ); var createNewOption = React.useCallback( function (newOptionInputValue) { /** Assert the getNewOptionData function exists */ if (typeof getNewOptionData !== 'function') { throw new Error( 'Creatable Select must have the getNewOptionData function' ); } /** Transform newOptionInputValue into a valid select option */ var newOption = getNewOptionData(newOptionInputValue); /** Get the new option value */ var newOptionValue = getOptionValue(newOption); /** Add the option to new option array only if value could not be found */ if ( !createdOptions.find(function (option) { return getOptionValue(option) === newOptionValue; }) ) { setCreatedOptions( __spreadArray( __spreadArray([], __read(createdOptions), false), [newOption], false ) ); } }, [getNewOptionData, createdOptions, getOptionValue] ); var selectedOption = React.useMemo( function () { var _a; /** Return default with no Options */ if (!options || !Array.isArray(options)) { return rest.isMulti ? [] : null; } /** On single select, find the Option */ if (!rest.isMulti) { return (_a = options.find(function (option) { return getOptionValue(option) === value; })) !== null && _a !== void 0 ? _a : null; } /** Return filtered options */ if (Array.isArray(value)) { return options.filter(function (option) { return value.includes(getOptionValue(option)); }); } /** Fallback to Empty Array */ return []; }, [value, options, getOptionValue, rest.isMulti] ); var getSelectedValueFromSelectRef = function () { var _a; /** Get the Select State */ var state = ((_a = selectRef.current) !== null && _a !== void 0 ? _a : {}) .state; if (!state) { return props.isMulti ? [] : null; } var selectedValue = state.value; if (props.isMulti || Array.isArray(selectedValue)) { return Array.isArray(selectedValue) ? selectedValue.map(function (selected) { return getOptionValue(selected); }) : []; } return selectedValue ? getOptionValue(selectedValue) : null; }; // ---- // Component Handlers // ---- var handleSelectBlur = function (e) { var _a; /** Abort if Disabled or ReadOnly */ if (fieldProps.disabled || fieldProps.readOnly) { return; } /** Remove focused class from field */ (_a = fieldRef.current) === null || _a === void 0 ? void 0 : _a.classList.remove('focused'); /** Get the selected value */ if (userDefinedOnBlur) { userDefinedOnBlur( e, __assign(__assign({}, props), { inputValue: inputValue, value: getSelectedValueFromSelectRef(), action: null, }) ); } }; var handleSelectChange = function (selected, action) { var _a; /** Set field as Dirty */ (_a = fieldRef.current) === null || _a === void 0 ? void 0 : _a.classList.add('dirty'); /** If a new option has been created, append to created options array */ if (action.action === 'create-option') { createNewOption(inputValue); } var selectedValue = props.isMulti ? Array.isArray(selected) ? selected.map(function (option) { return getOptionValue(option); }) : [] : selected ? getOptionValue(selected) : null; if (userDefinedOnChange) { userDefinedOnChange( null, __assign(__assign({}, props), { action: action, inputValue: inputValue, value: selectedValue, }) ); } trySetValue(selectedValue); }; var handleSelectFocus = function (e) { var _a, _b; /** Abort if Disabled or ReadOnly */ if (fieldProps.disabled || fieldProps.readOnly) { return; } /** Remove focused class from field */ (_a = fieldRef.current) === null || _a === void 0 ? void 0 : _a.classList.add('focused'); (_b = fieldRef.current) === null || _b === void 0 ? void 0 : _b.classList.add('touched'); /** Get the selected value */ if (userDefinedOnFocus) { userDefinedOnFocus( e, __assign(__assign({}, props), { inputValue: inputValue, value: getSelectedValueFromSelectRef(), action: null, }) ); } }; var handleInputChange = function (newInputValue) { if (userDefinedOnInputChange) { userDefinedOnInputChange( null, __assign(__assign({}, props), { inputValue: newInputValue, value: getSelectedValueFromSelectRef(), action: null, }) ); } trySetInputValue(newInputValue); }; var handleMenuOpen = function () { if (userDefinedOnMenuOpen) { userDefinedOnMenuOpen( null, __assign(__assign({}, props), { inputValue: inputValue, value: getSelectedValueFromSelectRef(), action: null, }) ); } }; var handleMenuClose = function () { if (userDefinedOnMenuClose) { userDefinedOnMenuClose( null, __assign(__assign({}, props), { inputValue: inputValue, value: getSelectedValueFromSelectRef(), action: null, }) ); } }; var handleMenuScrollToBottom = function (e) { if (userDefinedOnMenuScrollToBottom) { userDefinedOnMenuScrollToBottom( e, __assign(__assign({}, props), { inputValue: inputValue, value: getSelectedValueFromSelectRef(), action: null, }) ); } }; var handleMenuScrollToTop = function (e) { if (userDefinedOnMenuScrollToTop) { userDefinedOnMenuScrollToTop( e, __assign(__assign({}, props), { inputValue: inputValue, value: getSelectedValueFromSelectRef(), action: null, }) ); } }; // ---- // Render the Component // ---- return React.createElement( Field, __assign({ ref: fieldRef }, fieldProps, { appearance: rawRest.appearance, danger: rawRest.danger, info: rawRest.info, primary: rawRest.primary, secondary: rawRest.secondary, success: rawRest.success, warning: rawRest.warning, contentType: 'select input', }), React.createElement( ElementType, __assign({}, rest, { ref: handleRef, className: classes, classNamePrefix: ' ', getOptionValue: getOptionValueAsString, isDisabled: fieldProps.disabled, isLoading: loading, tabIndex: tabIndex, inputValue: inputValue, options: options, value: selectedOption, onBlur: handleSelectBlur, onChange: handleSelectChange, onFocus: handleSelectFocus, onMenuClose: handleMenuClose, onMenuOpen: handleMenuOpen, onMenuScrollToBottom: handleMenuScrollToBottom, onMenuScrollToTop: handleMenuScrollToTop, onInputChange: handleInputChange, }) ) ); }; var Select = React.forwardRef(SelectRender); Select.displayName = 'Select'; Select.create = createShorthandFactory(Select, function (options) { return { options: options, }; }); export { Select as default };