UNPKG

@etsoo/materialui

Version:

TypeScript Material-UI Implementation

139 lines (138 loc) 7.32 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ComboBox = ComboBox; const jsx_runtime_1 = require("react/jsx-runtime"); const shared_1 = require("@etsoo/shared"); const react_1 = __importDefault(require("react")); const shared_2 = require("@etsoo/shared"); const react_2 = require("@etsoo/react"); const SearchField_1 = require("./SearchField"); const InputField_1 = require("./InputField"); const ReactApp_1 = require("./app/ReactApp"); const Autocomplete_1 = __importDefault(require("@mui/material/Autocomplete")); const Stack_1 = __importDefault(require("@mui/material/Stack")); const IconButton_1 = __importDefault(require("@mui/material/IconButton")); const Add_1 = __importDefault(require("@mui/icons-material/Add")); /** * ComboBox * @param props Props * @returns Component */ function ComboBox(props) { // Global app const app = (0, ReactApp_1.useAppContext)(); // Labels const labels = app?.getLabels("noOptions", "loading", "open", "add"); // Destruct const { search = false, autoAddBlankItem = search, idField = "id", idValue, idIsString = false, inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputReset, inputVariant, defaultValue, label, labelField = "label", loadData, onLoadData, name, inputAutoComplete = "off", options, dataReadonly = true, readOnly, onChange, onValueChange, openOnFocus = true, value, disableCloseOnSelect = false, getOptionLabel = (option) => `${option[labelField]}`, sx = { minWidth: "150px", flexGrow: 2 }, noOptionsText = labels?.noOptions, loadingText = labels?.loading, openText = labels?.open, addLabel = labels?.add, onAdd, getOptionKey = (option) => `${option[idField]}`, ...rest } = props; // Value input ref const inputRef = react_1.default.createRef(); // Options state const [localOptions, setOptions] = react_1.default.useState(options ?? []); const isMounted = react_1.default.useRef(true); // When options change // [options] will cause infinite loop const propertyWay = loadData == null; react_1.default.useEffect(() => { if (propertyWay && options != null) { setOptions(options); if (stateValue != null && !options.some((option) => option[idField] === stateValue[idField])) { setStateValue(null); } } }, [options, propertyWay]); // Local default value const localValue = idValue != null ? localOptions.find((o) => o[idField] === idValue) : defaultValue ?? value; // State // null for controlled const [stateValue, setStateValue] = react_1.default.useState(null); react_1.default.useEffect(() => { if (localValue != null && localValue != stateValue) { setStateValue(localValue); if (onValueChange) onValueChange(localValue); } }, [localValue]); // Add readOnly // Before AutocompleteRenderInputParams changed, impossible to remove "InputLabelProps" and "InputProps" const addReadOnly = (params) => { if (readOnly != null) { Object.assign(params, { readOnly }); } Object.assign(params.inputProps, { "data-reset": inputReset }); if (dataReadonly) { params.inputProps.onKeyDown = (event) => { if (shared_1.Keyboard.isTypingContent(event.key)) { event.preventDefault(); } }; } // https://stackoverflow.com/questions/15738259/disabling-chrome-autofill // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html Object.assign(params.inputProps, { autoComplete: inputAutoComplete }); return params; }; const getValue = (value) => { if (value == null) return ""; if (Array.isArray(value)) return value.map((item) => item[idField]).join(","); return `${value[idField]}`; }; const setInputValue = (value) => { // Set state setStateValue(value); // Input value const input = inputRef.current; if (input) { // Update value const newValue = getValue(value); if (newValue !== input.value) { // Different value, trigger change event react_2.ReactUtils.triggerChange(input, newValue, false); } } }; const doLoadData = react_1.default.useCallback(() => { if (loadData == null) return; loadData().then((result) => { if (result == null || !isMounted.current) return; if (onLoadData) onLoadData(result); if (autoAddBlankItem) { shared_2.Utils.addBlankItem(result, idField, labelField); } setOptions(result); }); }, [loadData, autoAddBlankItem, idField, labelField]); react_1.default.useEffect(() => { if (propertyWay) return; doLoadData(); }, [propertyWay, doLoadData]); react_1.default.useEffect(() => { return () => { isMounted.current = false; }; }, []); // Layout return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: getValue(stateValue), readOnly: true, onChange: inputOnChange }), (0, jsx_runtime_1.jsxs)(Stack_1.default, { gap: 0.5, direction: "row", width: "100%", children: [(0, jsx_runtime_1.jsx)(Autocomplete_1.default, { value: stateValue, disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => { // Set value setInputValue(value); // Custom if (onChange != null) onChange(event, value, reason, details); if (onValueChange) onValueChange(value); }, openOnFocus: openOnFocus, sx: sx, renderInput: (params) => search ? ((0, jsx_runtime_1.jsx)(SearchField_1.SearchField, { ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, error: inputError, helperText: inputHelperText })) : ((0, jsx_runtime_1.jsx)(InputField_1.InputField, { ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, error: inputError, helperText: inputHelperText })), options: localOptions, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionKey: getOptionKey, ...rest }), onAdd && ((0, jsx_runtime_1.jsx)(IconButton_1.default, { size: "small", onClick: () => { onAdd(doLoadData); }, title: addLabel, children: (0, jsx_runtime_1.jsx)(Add_1.default, {}) }))] })] })); }