UNPKG

@etsoo/materialui

Version:

TypeScript Material-UI Implementation

200 lines (199 loc) 10.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SelectEx = SelectEx; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importDefault(require("react")); const MUGlobal_1 = require("./MUGlobal"); const ListItemRightIcon_1 = require("./ListItemRightIcon"); const shared_1 = require("@etsoo/shared"); const react_2 = require("@etsoo/react"); const Select_1 = __importDefault(require("@mui/material/Select")); const Stack_1 = __importDefault(require("@mui/material/Stack")); const FormControl_1 = __importDefault(require("@mui/material/FormControl")); const InputLabel_1 = __importDefault(require("@mui/material/InputLabel")); const OutlinedInput_1 = __importDefault(require("@mui/material/OutlinedInput")); const MenuItem_1 = __importDefault(require("@mui/material/MenuItem")); const Checkbox_1 = __importDefault(require("@mui/material/Checkbox")); const ListItemText_1 = __importDefault(require("@mui/material/ListItemText")); const IconButton_1 = __importDefault(require("@mui/material/IconButton")); const FormHelperText_1 = __importDefault(require("@mui/material/FormHelperText")); const Refresh_1 = __importDefault(require("@mui/icons-material/Refresh")); /** * Extended select component * @param props Props * @returns Component */ function SelectEx(props) { // Destruct const { defaultValue, idField = "id", error, helperText, inputReset, itemIconRenderer, itemStyle, label, labelField = "label", loadData, mRef, onItemChange, onItemClick, onLoadData, multiple = false, name, options, refresh, search = false, autoAddBlankItem = search, value, onChange, fullWidth, required, variant = "outlined", ...rest } = props; // Options state const [localOptions, setOptions] = react_1.default.useState([]); const isMounted = react_1.default.useRef(false); const doItemChange = (options, value, userAction) => { if (onItemChange == null) return; let option; if (multiple && Array.isArray(value)) { option = options.find((option) => value.includes(option[idField])); } else if (value == null || value === "") { option = undefined; } else { option = options.find((option) => option[idField] === value); } onItemChange(option, userAction); }; // Local value const v = defaultValue ?? value; const valueSource = react_1.default.useMemo(() => (multiple ? (v ? (Array.isArray(v) ? v : [v]) : []) : v ?? ""), [multiple, v]); const setOptionsAdd = react_1.default.useCallback((options) => { const localOptions = [...options]; if (autoAddBlankItem) { shared_1.Utils.addBlankItem(localOptions, idField, labelField); } setOptions(localOptions); if (valueSource != null) doItemChange(options, valueSource, false); }, [valueSource]); // When options change // [options] will cause infinite loop const propertyWay = loadData == null; react_1.default.useEffect(() => { if (options == null || !propertyWay) return; setOptionsAdd(options); }, [options, propertyWay, setOptionsAdd]); // Value state const [valueState, setValueStateBase] = react_1.default.useState(valueSource); const valueRef = react_1.default.useRef(); const setValueState = (newValue) => { valueRef.current = newValue; setValueStateBase(newValue); }; const [open, setOpen] = react_1.default.useState(false); react_1.default.useImperativeHandle(mRef, () => ({ setOpen: (isOpen) => { setOpen(isOpen); } }), []); react_1.default.useEffect(() => { if (valueSource != null) setValueState(valueSource); }, [valueSource]); // Label id const labelId = `selectex-label-${name}`; // Set item const setItemValue = (id) => { if (id !== valueRef.current) { // Difference const diff = multiple ? shared_1.ArrayUtils.differences(id, valueRef.current) : id; setValueState(id); const input = divRef.current?.querySelector("input"); if (input) { // Different value, trigger change event react_2.ReactUtils.triggerChange(input, id, false); } return diff; } return undefined; }; // Get option id const getId = (option) => { return option[idField]; }; // Get option label const getLabel = (option) => { return typeof labelField === "function" ? labelField(option) : option[labelField]; }; // Refs const divRef = react_1.default.useRef(); // Refresh list data const refreshData = () => { if (loadData == null) return; loadData().then((result) => { if (result == null || !isMounted.current) return; if (onLoadData) onLoadData(result); setOptionsAdd(result); }); }; // When value change react_1.default.useEffect(() => { refreshData(); }, [valueSource]); // When layout ready react_1.default.useEffect(() => { const input = divRef.current?.querySelector("input"); const inputChange = (event) => { // Reset case if (event.cancelable) setValueState(multiple ? [] : ""); }; input?.addEventListener("change", inputChange); isMounted.current = true; return () => { isMounted.current = false; input?.removeEventListener("change", inputChange); }; }, [multiple]); // Layout return ((0, jsx_runtime_1.jsxs)(Stack_1.default, { direction: "row", children: [(0, jsx_runtime_1.jsxs)(FormControl_1.default, { size: search ? MUGlobal_1.MUGlobal.searchFieldSize : MUGlobal_1.MUGlobal.inputFieldSize, fullWidth: fullWidth, error: error, children: [(0, jsx_runtime_1.jsx)(InputLabel_1.default, { id: labelId, variant: variant, shrink: search ? MUGlobal_1.MUGlobal.searchFieldShrink : MUGlobal_1.MUGlobal.inputFieldShrink, required: required, children: label }), (0, jsx_runtime_1.jsx)(Select_1.default, { ref: divRef, value: multiple ? valueState : localOptions.some((o) => o[idField] === valueState) ? valueState : "", input: (0, jsx_runtime_1.jsx)(OutlinedInput_1.default, { notched: true, label: label, required: required, inputProps: { "aria-hidden": false, "data-reset": inputReset } }), open: open, onClose: () => setOpen(false), onOpen: () => setOpen(true), labelId: labelId, name: name, multiple: multiple, onChange: (event, child) => { if (onChange) { onChange(event, child); // event.preventDefault() will block executing if (event.defaultPrevented) return; } // Set item value const value = event.target.value; if (multiple && !Array.isArray(value)) return; const diff = setItemValue(value); if (diff != null) { doItemChange(localOptions, diff, true); } }, renderValue: (selected) => { // The text shows up return localOptions .filter((option) => { const id = getId(option); return Array.isArray(selected) ? selected.indexOf(id) !== -1 : selected === id; }) .map((option) => getLabel(option)) .join(", "); }, sx: { minWidth: "150px" }, fullWidth: fullWidth, required: required, variant: variant, ...rest, children: localOptions.map((option) => { // Option id const id = getId(option); // Option label const label = getLabel(option); // Option return ((0, jsx_runtime_1.jsxs)(MenuItem_1.default, { value: id, onClick: (event) => { if (onItemClick) { onItemClick(event, option); } }, style: itemStyle == null ? undefined : itemStyle(option), children: [multiple && ((0, jsx_runtime_1.jsx)(Checkbox_1.default, { checked: Array.isArray(valueState) ? valueState.includes(id) : valueState === id })), (0, jsx_runtime_1.jsx)(ListItemText_1.default, { primary: label }), itemIconRenderer && ((0, jsx_runtime_1.jsx)(ListItemRightIcon_1.ListItemRightIcon, { children: itemIconRenderer(option[idField]) }))] }, id)); }) }), helperText != null && (0, jsx_runtime_1.jsx)(FormHelperText_1.default, { children: helperText })] }), refresh != null && loadData != null && (typeof refresh === "string" ? ((0, jsx_runtime_1.jsx)(IconButton_1.default, { size: "small", title: refresh, onClick: refreshData, children: (0, jsx_runtime_1.jsx)(Refresh_1.default, {}) })) : (refresh))] })); }