@appbuckets/react-ui
Version:
Just Another React UI Framework
441 lines (438 loc) • 12.6 kB
JavaScript
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 };