@etsoo/materialui
Version:
TypeScript Material-UI Implementation
200 lines (199 loc) • 10.1 kB
JavaScript
;
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))] }));
}