UNPKG

@etsoo/materialui

Version:

TypeScript Material-UI Implementation

130 lines (129 loc) 6.58 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { Keyboard } from "@etsoo/shared"; import React from "react"; import { Utils as SharedUtils } from "@etsoo/shared"; import { ReactUtils } from "@etsoo/react"; import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank"; import CheckBoxIcon from "@mui/icons-material/CheckBox"; import { SearchField } from "./SearchField"; import { InputField } from "./InputField"; import { useAppContext } from "./app/ReactApp"; import Checkbox from "@mui/material/Checkbox"; import Autocomplete from "@mui/material/Autocomplete"; const icon = _jsx(CheckBoxOutlineBlankIcon, { fontSize: "small" }); const checkedIcon = _jsx(CheckBoxIcon, { fontSize: "small" }); /** * ComboBox multiple * @param props Props * @returns Component */ export function ComboBoxMultiple(props) { // Global app const app = useAppContext(); // Labels const labels = app?.getLabels("noOptions", "loading"); // Destruct const { search = false, autoAddBlankItem = search, idField = "id", idValue, idValues, inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputReset, inputVariant, defaultValue, label, labelField = "label", loadData, onLoadData, name, inputAutoComplete = "off", options, dataReadonly = true, readOnly, onChange, openOnFocus = true, value, disableCloseOnSelect = true, renderOption = (props, option, { selected }) => (_jsxs("li", { ...props, children: [_jsx(Checkbox, { icon: icon, checkedIcon: checkedIcon, style: { marginRight: 8 }, checked: selected }), `${option[labelField]}`] })), getOptionLabel = (option) => `${option[labelField]}`, sx = { minWidth: "150px" }, noOptionsText = labels?.noOptions, loadingText = labels?.loading, ...rest } = props; // Value input ref const inputRef = React.createRef(); // Options state const [localOptions, setOptions] = React.useState(options ?? []); const isMounted = React.useRef(true); // Local default value const localValue = idValue != null ? localOptions.filter((o) => o[idField] === idValue) : idValues != null ? localOptions.filter((o) => idValues?.includes(o[idField])) : defaultValue?.concat() ?? value?.concat(); // State // null for controlled const [stateValue, setStateValue] = React.useState(null); React.useEffect(() => { setStateValue(localValue ?? []); }, [localValue]); // When options change // [options] will cause infinite loop const propertyWay = loadData == null; React.useEffect(() => { if (propertyWay && options != null) { setOptions(options); if (stateValue != null) { if (Array.isArray(stateValue)) { const newState = stateValue.filter((item) => options.some((option) => option[idField] === item[idField])); setStateValue(newState); } else if (!options.some((option) => option[idField] === stateValue[idField])) { setStateValue(null); } } } }, [options, propertyWay]); // Add readOnly const addReadOnly = (params) => { if (readOnly != null) { Object.assign(params, { readOnly }); } Object.assign(params.inputProps, { "data-reset": inputReset }); if (dataReadonly) { params.inputProps.onKeyDown = (event) => { if (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 ""; return value.map((item) => item[idField]).join(","); }; 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 ReactUtils.triggerChange(input, newValue, false); } } }; React.useEffect(() => { if (propertyWay || loadData == null) return; loadData().then((result) => { if (result == null || !isMounted.current) return; if (onLoadData) onLoadData(result); if (autoAddBlankItem) { SharedUtils.addBlankItem(result, idField, labelField); } setOptions(result); }); }, [propertyWay, autoAddBlankItem, idField, labelField]); React.useEffect(() => { return () => { isMounted.current = false; }; }, []); // Layout return (_jsxs("div", { children: [_jsx("input", { ref: inputRef, "data-reset": inputReset ?? true, type: "text", style: { display: "none" }, name: name, value: getValue(stateValue), readOnly: true, onChange: inputOnChange }), _jsx(Autocomplete, { value: stateValue == null ? [] : Array.isArray(stateValue) ? stateValue : [stateValue], disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, multiple: true, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => { // Set value setInputValue(value.concat()); // Custom if (onChange != null) onChange(event, value, reason, details); }, openOnFocus: openOnFocus, sx: sx, renderInput: (params) => search ? (_jsx(SearchField, { ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, error: inputError, helperText: inputHelperText })) : (_jsx(InputField, { ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, error: inputError, helperText: inputHelperText })), options: localOptions, renderOption: renderOption, noOptionsText: noOptionsText, loadingText: loadingText, ...rest })] })); }