@etsoo/materialui 1.5.98 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,7 +27,7 @@ function ComboBox(props) {
27
27
  // Labels
28
28
  const labels = app?.getLabels("noOptions", "loading", "open", "add");
29
29
  // Destruct
30
- 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;
30
+ const { disabled, 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;
31
31
  // Value input ref
32
32
  const inputRef = react_1.default.createRef();
33
33
  // Options state
@@ -124,7 +124,7 @@ function ComboBox(props) {
124
124
  };
125
125
  }, []);
126
126
  // Layout
127
- return ((0, jsx_runtime_1.jsxs)("div", { style: { flex: 2 }, 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) => {
127
+ return ((0, jsx_runtime_1.jsxs)("div", { style: { flex: 2 }, 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, disabled: disabled }), (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, disabled: disabled, disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => {
128
128
  // Set value
129
129
  setInputValue(value);
130
130
  // Custom
@@ -29,7 +29,7 @@ function ComboBoxMultiple(props) {
29
29
  // Labels
30
30
  const labels = app?.getLabels("noOptions", "loading");
31
31
  // Destruct
32
- 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 }) => ((0, jsx_runtime_1.jsxs)("li", { ...props, children: [(0, jsx_runtime_1.jsx)(Checkbox_1.default, { icon: icon, checkedIcon: checkedIcon, style: { marginRight: 8 }, checked: selected }), `${option[labelField]}`] })), getOptionLabel = (option) => `${option[labelField]}`, getOptionKey = (option) => `${option[idField]}`, sx = { minWidth: "150px" }, noOptionsText = labels?.noOptions, loadingText = labels?.loading, ...rest } = props;
32
+ 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 }) => ((0, jsx_runtime_1.jsxs)("li", { ...props, children: [(0, jsx_runtime_1.jsx)(Checkbox_1.default, { icon: icon, checkedIcon: checkedIcon, style: { marginRight: 8 }, checked: selected }), `${option[labelField]}`] })), getOptionLabel = (option) => `${option[labelField]}`, getOptionKey = (option) => `${option[idField]}`, sx = { minWidth: "150px" }, noOptionsText = labels?.noOptions, loadingText = labels?.loading, disabled, ...rest } = props;
33
33
  // Value input ref
34
34
  const inputRef = react_1.default.createRef();
35
35
  // Options state
@@ -121,11 +121,11 @@ function ComboBoxMultiple(props) {
121
121
  };
122
122
  }, []);
123
123
  // Layout
124
- return ((0, jsx_runtime_1.jsxs)("div", { style: { flex: 2 }, children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, "data-reset": inputReset ?? true, type: "text", style: { display: "none" }, name: name, value: getValue(stateValue), readOnly: true, onChange: inputOnChange }), (0, jsx_runtime_1.jsx)(Autocomplete_1.default, { value: stateValue == null
124
+ return ((0, jsx_runtime_1.jsxs)("div", { style: { flex: 2 }, children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, "data-reset": inputReset ?? true, type: "text", style: { display: "none" }, name: name, value: getValue(stateValue), readOnly: true, onChange: inputOnChange, disabled: disabled }), (0, jsx_runtime_1.jsx)(Autocomplete_1.default, { value: stateValue == null
125
125
  ? []
126
126
  : Array.isArray(stateValue)
127
127
  ? stateValue
128
- : [stateValue], disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, getOptionKey: getOptionKey, multiple: true, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => {
128
+ : [stateValue], disabled: disabled, disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, getOptionKey: getOptionKey, multiple: true, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => {
129
129
  // Set value
130
130
  setInputValue(value.concat());
131
131
  // Custom
@@ -5,21 +5,17 @@ import { TextFieldProps } from "@mui/material/TextField";
5
5
  */
6
6
  export type InputFieldProps = TextFieldProps & {
7
7
  /**
8
- * Change delay (ms) to avoid repeatly dispatch onChange
8
+ * Change [delay (ms), Minimum characters] to avoid repeatly dispatch
9
9
  */
10
- changeDelay?: number;
10
+ changeDelay?: [number, number?];
11
11
  /**
12
- * Change delay handler, without it onChange will be applied
12
+ * Change delay handler
13
13
  */
14
14
  onChangeDelay?: React.ChangeEventHandler<HTMLTextAreaElement | HTMLInputElement>;
15
15
  /**
16
16
  * Is the field read only?
17
17
  */
18
18
  readOnly?: boolean;
19
- /**
20
- * Minimum characters to trigger the change event
21
- */
22
- minChars?: number;
23
19
  };
24
20
  /**
25
21
  * Input field
@@ -16,31 +16,29 @@ const TextField_1 = __importDefault(require("@mui/material/TextField"));
16
16
  */
17
17
  function InputField(props) {
18
18
  // Destruct
19
- const { InputProps = {}, inputProps = {}, slotProps, changeDelay, onChange, onChangeDelay, readOnly, size = MUGlobal_1.MUGlobal.inputFieldSize, variant = MUGlobal_1.MUGlobal.inputFieldVariant, minChars = 0, ...rest } = props;
19
+ const { InputProps = {}, inputProps = {}, slotProps, onChange, onChangeDelay, changeDelay = onChangeDelay ? [480] : undefined, readOnly, size = MUGlobal_1.MUGlobal.inputFieldSize, variant = MUGlobal_1.MUGlobal.inputFieldVariant, ...rest } = props;
20
20
  // Slot props
21
21
  const { htmlInput, input, inputLabel, ...restSlotProps } = slotProps ?? {};
22
22
  const isMounted = react_2.default.useRef(true);
23
23
  const createDelayed = () => {
24
- if (changeDelay != null && changeDelay >= 1) {
25
- const changeHandler = onChangeDelay ?? onChange;
26
- if (changeHandler)
27
- return (0, react_1.useDelayedExecutor)(changeHandler, changeDelay);
24
+ if (onChangeDelay && changeDelay && changeDelay[0] >= 1) {
25
+ return (0, react_1.useDelayedExecutor)(onChangeDelay, changeDelay[0]);
28
26
  }
29
27
  return undefined;
30
28
  };
31
29
  const delayed = createDelayed();
32
30
  const onChangeEx = (event) => {
33
- // Min characters check
34
- const len = event.target.value.length;
35
- if (len > 0 && len < minChars) {
36
- // Avoid to trigger the form change event
37
- event.stopPropagation();
38
- event.preventDefault();
39
- return;
31
+ // Change handler
32
+ onChange?.(event);
33
+ if (onChangeDelay && changeDelay && delayed) {
34
+ const [_, minChars = 0] = changeDelay;
35
+ if (minChars > 0) {
36
+ const len = event.target.value.length;
37
+ if (len < minChars)
38
+ return;
39
+ }
40
+ delayed.call(undefined, event);
40
41
  }
41
- if (onChange && (delayed == null || onChangeDelay != null))
42
- onChange(event);
43
- delayed?.call(undefined, event);
44
42
  };
45
43
  react_2.default.useEffect(() => {
46
44
  return () => {
@@ -51,7 +49,7 @@ function InputField(props) {
51
49
  // Layout
52
50
  return ((0, jsx_runtime_1.jsx)(TextField_1.default, { slotProps: {
53
51
  htmlInput: {
54
- ["data-min-chars"]: minChars,
52
+ ["data-min-chars"]: changeDelay?.[1],
55
53
  ...htmlInput,
56
54
  ...inputProps
57
55
  },
@@ -35,7 +35,7 @@ function InputTipField(props) {
35
35
  const [anchorEl, setAnchorEl] = react_1.default.useState();
36
36
  const [data, setData] = react_1.default.useState();
37
37
  // Destruct
38
- const { component = "input", componentProps, changeDelay = 480, onChangeDelay, fullWidth = true, slotProps = {}, ...rest } = props;
38
+ const { component = "input", componentProps, changeDelay, onChangeDelay, fullWidth = true, slotProps = {}, ...rest } = props;
39
39
  const { labelProps = {
40
40
  title: app?.get("clickForDetails"),
41
41
  sx: { color: (theme) => theme.palette.error.main, cursor: "pointer" }
@@ -18,7 +18,7 @@ const IconButton_1 = __importDefault(require("@mui/material/IconButton"));
18
18
  */
19
19
  function IntInputField(props) {
20
20
  // Destruct
21
- const { min = 0, step = 1, max = 9999999, inputStyle = { textAlign: "right" }, boxProps, buttons, endSymbol, symbol, value, changeDelay = 600, onChangeDelay, onChange, onFocus = (event) => event.currentTarget.select(), onValueChange, required, ...rest } = props;
21
+ const { min = 0, step = 1, max = 9999999, inputStyle = { textAlign: "right" }, boxProps, buttons, endSymbol, symbol, value, changeDelay = [600], onChangeDelay, onChange, onFocus = (event) => event.currentTarget.select(), onValueChange, required, ...rest } = props;
22
22
  // State
23
23
  const [localValue, setLocalValue] = react_1.default.useState();
24
24
  const setValue = (value, source, init = false) => {
@@ -40,7 +40,7 @@ function QuickList(props) {
40
40
  loadDataLocal();
41
41
  }, []);
42
42
  // Layout
43
- return ((0, jsx_runtime_1.jsxs)(FlexBox_1.VBox, { gap: gap, height: height, ...rest, children: [(0, jsx_runtime_1.jsx)(InputField_1.InputField, { label: label, changeDelay: 480, onChangeDelay: (event) => {
43
+ return ((0, jsx_runtime_1.jsxs)(FlexBox_1.VBox, { gap: gap, height: height, ...rest, children: [(0, jsx_runtime_1.jsx)(InputField_1.InputField, { label: label, onChangeDelay: (event) => {
44
44
  // Stop bubble
45
45
  event.preventDefault();
46
46
  event.stopPropagation();
@@ -12,7 +12,7 @@ export type TagListProps = Omit<AutocompleteProps<string, true, false, true>, "o
12
12
  /**
13
13
  * Input props
14
14
  */
15
- inputProps?: Omit<InputFieldProps, "onChange">;
15
+ inputProps?: Omit<InputFieldProps, "onChangeDelay">;
16
16
  /**
17
17
  * Max items
18
18
  */
@@ -20,7 +20,7 @@ function TagList(props) {
20
20
  const { noOptions, loading: loadingLabel, more = "More", open: openDefault } = app?.getLabels("noOptions", "loading", "more", "open") ?? {};
21
21
  const moreLabel = more + "...";
22
22
  // Destruct
23
- const { renderOption = ({ key, ...props }, option, { selected }) => ((0, jsx_runtime_1.jsxs)("li", { ...props, children: [(0, jsx_runtime_1.jsx)(Checkbox_1.default, { icon: (0, jsx_runtime_1.jsx)(CheckBoxOutlineBlank_1.default, { fontSize: "small" }), checkedIcon: (0, jsx_runtime_1.jsx)(CheckBox_1.default, { fontSize: "small" }), style: { marginRight: 8 }, checked: selected }), option] }, key)), renderTags = (value, getTagProps) => value.map((option, index) => {
23
+ const { renderOption = ({ key, ...props }, option, { selected }) => ((0, jsx_runtime_1.jsxs)("li", { ...props, children: [(0, jsx_runtime_1.jsx)(Checkbox_1.default, { icon: (0, jsx_runtime_1.jsx)(CheckBoxOutlineBlank_1.default, { fontSize: "small" }), checkedIcon: (0, jsx_runtime_1.jsx)(CheckBox_1.default, { fontSize: "small" }), style: { marginRight: 8 }, checked: selected }), option] }, key)), renderValue = (value, getTagProps) => value.map((option, index) => {
24
24
  const { key, ...rest } = getTagProps({ index });
25
25
  return (0, jsx_runtime_1.jsx)(Chip_1.default, { variant: "outlined", label: option, ...rest }, key);
26
26
  }), noOptionsText = noOptions, loadingText = loadingLabel, openText = openDefault, loadData, maxItems = 16, disableCloseOnSelect = true, openOnFocus = true, label, inputProps, onChange, value, ...rest } = props;
@@ -54,7 +54,7 @@ function TagList(props) {
54
54
  }
55
55
  }, onClose: () => {
56
56
  setOpen(false);
57
- }, options: options, loading: loading, disableCloseOnSelect: disableCloseOnSelect, clearOnBlur: true, openOnFocus: openOnFocus, renderOption: renderOption, renderTags: renderTags, renderInput: (params) => ((0, jsx_runtime_1.jsx)(InputField_1.InputField, { label: label, changeDelay: 480, onChange: async (event) => {
57
+ }, options: options, loading: loading, disableCloseOnSelect: disableCloseOnSelect, clearOnBlur: true, openOnFocus: openOnFocus, renderOption: renderOption, renderValue: renderValue, renderInput: (params) => ((0, jsx_runtime_1.jsx)(InputField_1.InputField, { label: label, onChangeDelay: async (event) => {
58
58
  // Stop bubble
59
59
  event.preventDefault();
60
60
  event.stopPropagation();
@@ -13,7 +13,7 @@ export type TagListProProps<D extends ListType2 = ListType2> = Omit<Autocomplete
13
13
  /**
14
14
  * Input props
15
15
  */
16
- inputProps?: Omit<InputFieldProps, "onChange">;
16
+ inputProps?: Omit<InputFieldProps, "onChangeDelay">;
17
17
  /**
18
18
  * Max items
19
19
  */
@@ -56,7 +56,7 @@ function TagListPro(props) {
56
56
  }
57
57
  }, onClose: () => {
58
58
  setOpen(false);
59
- }, options: options, loading: loading, disableCloseOnSelect: disableCloseOnSelect, openOnFocus: openOnFocus, renderOption: renderOption, renderValue: renderValue, renderInput: (params) => ((0, jsx_runtime_1.jsx)(InputField_1.InputField, { label: label, changeDelay: 480, onChange: async (event) => {
59
+ }, options: options, loading: loading, disableCloseOnSelect: disableCloseOnSelect, openOnFocus: openOnFocus, renderOption: renderOption, renderValue: renderValue, renderInput: (params) => ((0, jsx_runtime_1.jsx)(InputField_1.InputField, { label: label, onChangeDelay: async (event) => {
60
60
  // Stop bubble
61
61
  event.preventDefault();
62
62
  event.stopPropagation();
@@ -23,7 +23,7 @@ function Tiplist(props) {
23
23
  // Labels
24
24
  const { noOptions, loading, more1 = "More", open: openDefault } = app?.getLabels("noOptions", "loading", "more1", "open") ?? {};
25
25
  // Destruct
26
- const { search = false, idField = "id", idValue, idIsString = false, inputAutoComplete = "off", inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputReset, inputVariant, label, loadData, defaultValue, value, maxItems = 16, width = search ? 160 : undefined, name, readOnly, onChange, onValueChange, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionKey = (option) => `${option[idField]}`, getOptionLabel, getOptionDisabled, sx = {}, minChars, ...rest } = props;
26
+ const { search = false, idField = "id", idValue, idIsString = false, inputAutoComplete = "off", inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputReset, inputVariant, label, loadData, defaultValue, value, maxItems = 16, width = search ? 160 : undefined, name, readOnly, onChange, onValueChange, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionKey = (option) => `${option[idField]}`, getOptionLabel, getOptionDisabled, sx = {}, minChars, disabled, ...rest } = props;
27
27
  if (width && sx)
28
28
  Object.assign(sx, { width: `${width}px` });
29
29
  // Value input ref
@@ -77,8 +77,12 @@ function Tiplist(props) {
77
77
  }
78
78
  // Stop bubble
79
79
  event.stopPropagation();
80
+ const value = event.currentTarget.value;
81
+ if (minChars && minChars > 0 && value.length < minChars) {
82
+ return;
83
+ }
80
84
  // Call with delay
81
- delayed.call(undefined, event.currentTarget.value);
85
+ delayed.call(undefined, value);
82
86
  };
83
87
  // Directly load data
84
88
  const loadDataDirect = (keyword, id) => {
@@ -172,7 +176,7 @@ function Tiplist(props) {
172
176
  };
173
177
  }, []);
174
178
  // Layout
175
- return ((0, jsx_runtime_1.jsxs)("div", { style: { flex: 2 }, children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: `${inputValue ?? (state.current.idSet ? "" : localIdValue ?? "")}`, readOnly: true, onChange: inputOnChange }), (0, jsx_runtime_1.jsx)(Autocomplete_1.default, { filterOptions: (options, _state) => options, value: states.value, options: states.options, onChange: (event, value, reason, details) => {
179
+ return ((0, jsx_runtime_1.jsxs)("div", { style: { flex: 2 }, children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: `${inputValue ?? (state.current.idSet ? "" : localIdValue ?? "")}`, readOnly: true, onChange: inputOnChange, disabled: disabled }), (0, jsx_runtime_1.jsx)(Autocomplete_1.default, { filterOptions: (options, _state) => options, disabled: disabled, value: states.value, options: states.options, onChange: (event, value, reason, details) => {
176
180
  // Set value
177
181
  setInputValue(value);
178
182
  // Custom
@@ -197,7 +201,7 @@ function Tiplist(props) {
197
201
  open: false,
198
202
  ...(!states.value && { options: [] })
199
203
  });
200
- }, loading: states.loading, renderInput: (params) => search ? ((0, jsx_runtime_1.jsx)(SearchField_1.SearchField, { onChange: changeHandle, ...addReadOnly(params), readOnly: readOnly, label: label, name: name + "Input", margin: inputMargin, minChars: minChars, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })) : ((0, jsx_runtime_1.jsx)(InputField_1.InputField, { onChange: changeHandle, ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, minChars: minChars, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })), isOptionEqualToValue: (option, value) => option[idField] === value[idField], sx: sx, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionDisabled: (item) => {
204
+ }, loading: states.loading, renderInput: (params) => search ? ((0, jsx_runtime_1.jsx)(SearchField_1.SearchField, { onChange: changeHandle, ...addReadOnly(params), readOnly: readOnly, label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })) : ((0, jsx_runtime_1.jsx)(InputField_1.InputField, { onChange: changeHandle, ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })), isOptionEqualToValue: (option, value) => option[idField] === value[idField], sx: sx, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionDisabled: (item) => {
201
205
  if (item[idField] === "n/a")
202
206
  return true;
203
207
  return getOptionDisabled ? getOptionDisabled(item) : false;
@@ -22,7 +22,7 @@ function TiplistPro(props) {
22
22
  // Labels
23
23
  const { noOptions, loading, more, open: openDefault } = app?.getLabels("noOptions", "loading", "more", "open") ?? {};
24
24
  // Destruct
25
- const { label, loadData, defaultValue, value, idValue, idIsString = false, maxItems = 16, width, name, inputOnChange, inputProps, inputReset, sx, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionDisabled, getOptionLabel, getOptionKey = (option) => typeof option === "string" ? option : option.id, onChange, onValueChange, minChars, ...rest } = props;
25
+ const { label, loadData, defaultValue, value, idValue, idIsString = false, maxItems = 16, width, name, inputOnChange, inputProps, inputReset, sx, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionDisabled, getOptionLabel, getOptionKey = (option) => typeof option === "string" ? option : option.id, onChange, onValueChange, minChars, disabled, ...rest } = props;
26
26
  if (width && sx)
27
27
  Object.assign(sx, { width: `${width}px` });
28
28
  // Value input ref
@@ -66,8 +66,12 @@ function TiplistPro(props) {
66
66
  }
67
67
  // Stop bubble
68
68
  event.stopPropagation();
69
+ const value = event.currentTarget.value;
70
+ if (minChars && minChars > 0 && value.length < minChars) {
71
+ return;
72
+ }
69
73
  // Call with delay
70
- delayed.call(undefined, event.currentTarget.value);
74
+ delayed.call(undefined, value);
71
75
  };
72
76
  // Directly load data
73
77
  const loadDataDirect = (keyword, id) => {
@@ -158,7 +162,7 @@ function TiplistPro(props) {
158
162
  };
159
163
  }, []);
160
164
  // Layout
161
- return ((0, jsx_runtime_1.jsxs)("div", { style: { flex: 2 }, children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: inputValue ?? (state.current.idSet ? "" : localIdValue ?? ""), readOnly: true, onChange: inputOnChange }), (0, jsx_runtime_1.jsx)(Autocomplete_1.default, { filterOptions: (options, _state) => options, value: states.value, options: states.options, freeSolo: true, clearOnBlur: false, onChange: (event, value, reason, details) => {
165
+ return ((0, jsx_runtime_1.jsxs)("div", { style: { flex: 2 }, children: [(0, jsx_runtime_1.jsx)("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: inputValue ?? (state.current.idSet ? "" : localIdValue ?? ""), readOnly: true, onChange: inputOnChange, disabled: disabled }), (0, jsx_runtime_1.jsx)(Autocomplete_1.default, { filterOptions: (options, _state) => options, value: states.value, options: states.options, disabled: disabled, freeSolo: true, clearOnBlur: false, onChange: (event, value, reason, details) => {
162
166
  if (typeof value === "object") {
163
167
  // Set value
164
168
  setInputValue(value);
@@ -189,7 +193,7 @@ function TiplistPro(props) {
189
193
  open: false,
190
194
  ...(!states.value && { options: [] })
191
195
  });
192
- }, loading: states.loading, renderInput: (params) => ((0, jsx_runtime_1.jsx)(InputField_1.InputField, { minChars: minChars, ...inputProps, ...params, onChange: changeHandle, label: label, name: name + "Input", onBlur: (event) => {
196
+ }, loading: states.loading, renderInput: (params) => ((0, jsx_runtime_1.jsx)(InputField_1.InputField, { ...inputProps, ...params, onChange: changeHandle, label: label, name: name + "Input", onBlur: (event) => {
193
197
  if (states.value == null && onChange)
194
198
  onChange(event, event.target.value, "blur", undefined);
195
199
  }, "data-reset": inputReset })), isOptionEqualToValue: (option, value) => option.id === value.id, sx: sx, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionKey: getOptionKey, getOptionDisabled: (item) => {
@@ -21,7 +21,7 @@ export function ComboBox(props) {
21
21
  // Labels
22
22
  const labels = app?.getLabels("noOptions", "loading", "open", "add");
23
23
  // Destruct
24
- 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;
24
+ const { disabled, 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;
25
25
  // Value input ref
26
26
  const inputRef = React.createRef();
27
27
  // Options state
@@ -118,7 +118,7 @@ export function ComboBox(props) {
118
118
  };
119
119
  }, []);
120
120
  // Layout
121
- return (_jsxs("div", { style: { flex: 2 }, children: [_jsx("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: getValue(stateValue), readOnly: true, onChange: inputOnChange }), _jsxs(Stack, { gap: 0.5, direction: "row", width: "100%", children: [_jsx(Autocomplete, { value: stateValue, disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => {
121
+ return (_jsxs("div", { style: { flex: 2 }, children: [_jsx("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: getValue(stateValue), readOnly: true, onChange: inputOnChange, disabled: disabled }), _jsxs(Stack, { gap: 0.5, direction: "row", width: "100%", children: [_jsx(Autocomplete, { value: stateValue, disabled: disabled, disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => {
122
122
  // Set value
123
123
  setInputValue(value);
124
124
  // Custom
@@ -23,7 +23,7 @@ export function ComboBoxMultiple(props) {
23
23
  // Labels
24
24
  const labels = app?.getLabels("noOptions", "loading");
25
25
  // Destruct
26
- 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]}`, getOptionKey = (option) => `${option[idField]}`, sx = { minWidth: "150px" }, noOptionsText = labels?.noOptions, loadingText = labels?.loading, ...rest } = props;
26
+ 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]}`, getOptionKey = (option) => `${option[idField]}`, sx = { minWidth: "150px" }, noOptionsText = labels?.noOptions, loadingText = labels?.loading, disabled, ...rest } = props;
27
27
  // Value input ref
28
28
  const inputRef = React.createRef();
29
29
  // Options state
@@ -115,11 +115,11 @@ export function ComboBoxMultiple(props) {
115
115
  };
116
116
  }, []);
117
117
  // Layout
118
- return (_jsxs("div", { style: { flex: 2 }, 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
118
+ return (_jsxs("div", { style: { flex: 2 }, children: [_jsx("input", { ref: inputRef, "data-reset": inputReset ?? true, type: "text", style: { display: "none" }, name: name, value: getValue(stateValue), readOnly: true, onChange: inputOnChange, disabled: disabled }), _jsx(Autocomplete, { value: stateValue == null
119
119
  ? []
120
120
  : Array.isArray(stateValue)
121
121
  ? stateValue
122
- : [stateValue], disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, getOptionKey: getOptionKey, multiple: true, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => {
122
+ : [stateValue], disabled: disabled, disableCloseOnSelect: disableCloseOnSelect, getOptionLabel: getOptionLabel, getOptionKey: getOptionKey, multiple: true, isOptionEqualToValue: (option, value) => option[idField] === value[idField], onChange: (event, value, reason, details) => {
123
123
  // Set value
124
124
  setInputValue(value.concat());
125
125
  // Custom
@@ -5,21 +5,17 @@ import { TextFieldProps } from "@mui/material/TextField";
5
5
  */
6
6
  export type InputFieldProps = TextFieldProps & {
7
7
  /**
8
- * Change delay (ms) to avoid repeatly dispatch onChange
8
+ * Change [delay (ms), Minimum characters] to avoid repeatly dispatch
9
9
  */
10
- changeDelay?: number;
10
+ changeDelay?: [number, number?];
11
11
  /**
12
- * Change delay handler, without it onChange will be applied
12
+ * Change delay handler
13
13
  */
14
14
  onChangeDelay?: React.ChangeEventHandler<HTMLTextAreaElement | HTMLInputElement>;
15
15
  /**
16
16
  * Is the field read only?
17
17
  */
18
18
  readOnly?: boolean;
19
- /**
20
- * Minimum characters to trigger the change event
21
- */
22
- minChars?: number;
23
19
  };
24
20
  /**
25
21
  * Input field
@@ -10,31 +10,29 @@ import TextField from "@mui/material/TextField";
10
10
  */
11
11
  export function InputField(props) {
12
12
  // Destruct
13
- const { InputProps = {}, inputProps = {}, slotProps, changeDelay, onChange, onChangeDelay, readOnly, size = MUGlobal.inputFieldSize, variant = MUGlobal.inputFieldVariant, minChars = 0, ...rest } = props;
13
+ const { InputProps = {}, inputProps = {}, slotProps, onChange, onChangeDelay, changeDelay = onChangeDelay ? [480] : undefined, readOnly, size = MUGlobal.inputFieldSize, variant = MUGlobal.inputFieldVariant, ...rest } = props;
14
14
  // Slot props
15
15
  const { htmlInput, input, inputLabel, ...restSlotProps } = slotProps ?? {};
16
16
  const isMounted = React.useRef(true);
17
17
  const createDelayed = () => {
18
- if (changeDelay != null && changeDelay >= 1) {
19
- const changeHandler = onChangeDelay ?? onChange;
20
- if (changeHandler)
21
- return useDelayedExecutor(changeHandler, changeDelay);
18
+ if (onChangeDelay && changeDelay && changeDelay[0] >= 1) {
19
+ return useDelayedExecutor(onChangeDelay, changeDelay[0]);
22
20
  }
23
21
  return undefined;
24
22
  };
25
23
  const delayed = createDelayed();
26
24
  const onChangeEx = (event) => {
27
- // Min characters check
28
- const len = event.target.value.length;
29
- if (len > 0 && len < minChars) {
30
- // Avoid to trigger the form change event
31
- event.stopPropagation();
32
- event.preventDefault();
33
- return;
25
+ // Change handler
26
+ onChange?.(event);
27
+ if (onChangeDelay && changeDelay && delayed) {
28
+ const [_, minChars = 0] = changeDelay;
29
+ if (minChars > 0) {
30
+ const len = event.target.value.length;
31
+ if (len < minChars)
32
+ return;
33
+ }
34
+ delayed.call(undefined, event);
34
35
  }
35
- if (onChange && (delayed == null || onChangeDelay != null))
36
- onChange(event);
37
- delayed?.call(undefined, event);
38
36
  };
39
37
  React.useEffect(() => {
40
38
  return () => {
@@ -45,7 +43,7 @@ export function InputField(props) {
45
43
  // Layout
46
44
  return (_jsx(TextField, { slotProps: {
47
45
  htmlInput: {
48
- ["data-min-chars"]: minChars,
46
+ ["data-min-chars"]: changeDelay?.[1],
49
47
  ...htmlInput,
50
48
  ...inputProps
51
49
  },
@@ -29,7 +29,7 @@ export function InputTipField(props) {
29
29
  const [anchorEl, setAnchorEl] = React.useState();
30
30
  const [data, setData] = React.useState();
31
31
  // Destruct
32
- const { component = "input", componentProps, changeDelay = 480, onChangeDelay, fullWidth = true, slotProps = {}, ...rest } = props;
32
+ const { component = "input", componentProps, changeDelay, onChangeDelay, fullWidth = true, slotProps = {}, ...rest } = props;
33
33
  const { labelProps = {
34
34
  title: app?.get("clickForDetails"),
35
35
  sx: { color: (theme) => theme.palette.error.main, cursor: "pointer" }
@@ -12,7 +12,7 @@ import IconButton from "@mui/material/IconButton";
12
12
  */
13
13
  export function IntInputField(props) {
14
14
  // Destruct
15
- const { min = 0, step = 1, max = 9999999, inputStyle = { textAlign: "right" }, boxProps, buttons, endSymbol, symbol, value, changeDelay = 600, onChangeDelay, onChange, onFocus = (event) => event.currentTarget.select(), onValueChange, required, ...rest } = props;
15
+ const { min = 0, step = 1, max = 9999999, inputStyle = { textAlign: "right" }, boxProps, buttons, endSymbol, symbol, value, changeDelay = [600], onChangeDelay, onChange, onFocus = (event) => event.currentTarget.select(), onValueChange, required, ...rest } = props;
16
16
  // State
17
17
  const [localValue, setLocalValue] = React.useState();
18
18
  const setValue = (value, source, init = false) => {
@@ -34,7 +34,7 @@ export function QuickList(props) {
34
34
  loadDataLocal();
35
35
  }, []);
36
36
  // Layout
37
- return (_jsxs(VBox, { gap: gap, height: height, ...rest, children: [_jsx(InputField, { label: label, changeDelay: 480, onChangeDelay: (event) => {
37
+ return (_jsxs(VBox, { gap: gap, height: height, ...rest, children: [_jsx(InputField, { label: label, onChangeDelay: (event) => {
38
38
  // Stop bubble
39
39
  event.preventDefault();
40
40
  event.stopPropagation();
@@ -12,7 +12,7 @@ export type TagListProps = Omit<AutocompleteProps<string, true, false, true>, "o
12
12
  /**
13
13
  * Input props
14
14
  */
15
- inputProps?: Omit<InputFieldProps, "onChange">;
15
+ inputProps?: Omit<InputFieldProps, "onChangeDelay">;
16
16
  /**
17
17
  * Max items
18
18
  */
@@ -14,7 +14,7 @@ export function TagList(props) {
14
14
  const { noOptions, loading: loadingLabel, more = "More", open: openDefault } = app?.getLabels("noOptions", "loading", "more", "open") ?? {};
15
15
  const moreLabel = more + "...";
16
16
  // Destruct
17
- const { renderOption = ({ key, ...props }, option, { selected }) => (_jsxs("li", { ...props, children: [_jsx(Checkbox, { icon: _jsx(CheckBoxOutlineBlankIcon, { fontSize: "small" }), checkedIcon: _jsx(CheckBoxIcon, { fontSize: "small" }), style: { marginRight: 8 }, checked: selected }), option] }, key)), renderTags = (value, getTagProps) => value.map((option, index) => {
17
+ const { renderOption = ({ key, ...props }, option, { selected }) => (_jsxs("li", { ...props, children: [_jsx(Checkbox, { icon: _jsx(CheckBoxOutlineBlankIcon, { fontSize: "small" }), checkedIcon: _jsx(CheckBoxIcon, { fontSize: "small" }), style: { marginRight: 8 }, checked: selected }), option] }, key)), renderValue = (value, getTagProps) => value.map((option, index) => {
18
18
  const { key, ...rest } = getTagProps({ index });
19
19
  return _jsx(Chip, { variant: "outlined", label: option, ...rest }, key);
20
20
  }), noOptionsText = noOptions, loadingText = loadingLabel, openText = openDefault, loadData, maxItems = 16, disableCloseOnSelect = true, openOnFocus = true, label, inputProps, onChange, value, ...rest } = props;
@@ -48,7 +48,7 @@ export function TagList(props) {
48
48
  }
49
49
  }, onClose: () => {
50
50
  setOpen(false);
51
- }, options: options, loading: loading, disableCloseOnSelect: disableCloseOnSelect, clearOnBlur: true, openOnFocus: openOnFocus, renderOption: renderOption, renderTags: renderTags, renderInput: (params) => (_jsx(InputField, { label: label, changeDelay: 480, onChange: async (event) => {
51
+ }, options: options, loading: loading, disableCloseOnSelect: disableCloseOnSelect, clearOnBlur: true, openOnFocus: openOnFocus, renderOption: renderOption, renderValue: renderValue, renderInput: (params) => (_jsx(InputField, { label: label, onChangeDelay: async (event) => {
52
52
  // Stop bubble
53
53
  event.preventDefault();
54
54
  event.stopPropagation();
@@ -13,7 +13,7 @@ export type TagListProProps<D extends ListType2 = ListType2> = Omit<Autocomplete
13
13
  /**
14
14
  * Input props
15
15
  */
16
- inputProps?: Omit<InputFieldProps, "onChange">;
16
+ inputProps?: Omit<InputFieldProps, "onChangeDelay">;
17
17
  /**
18
18
  * Max items
19
19
  */
@@ -50,7 +50,7 @@ export function TagListPro(props) {
50
50
  }
51
51
  }, onClose: () => {
52
52
  setOpen(false);
53
- }, options: options, loading: loading, disableCloseOnSelect: disableCloseOnSelect, openOnFocus: openOnFocus, renderOption: renderOption, renderValue: renderValue, renderInput: (params) => (_jsx(InputField, { label: label, changeDelay: 480, onChange: async (event) => {
53
+ }, options: options, loading: loading, disableCloseOnSelect: disableCloseOnSelect, openOnFocus: openOnFocus, renderOption: renderOption, renderValue: renderValue, renderInput: (params) => (_jsx(InputField, { label: label, onChangeDelay: async (event) => {
54
54
  // Stop bubble
55
55
  event.preventDefault();
56
56
  event.stopPropagation();
@@ -17,7 +17,7 @@ export function Tiplist(props) {
17
17
  // Labels
18
18
  const { noOptions, loading, more1 = "More", open: openDefault } = app?.getLabels("noOptions", "loading", "more1", "open") ?? {};
19
19
  // Destruct
20
- const { search = false, idField = "id", idValue, idIsString = false, inputAutoComplete = "off", inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputReset, inputVariant, label, loadData, defaultValue, value, maxItems = 16, width = search ? 160 : undefined, name, readOnly, onChange, onValueChange, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionKey = (option) => `${option[idField]}`, getOptionLabel, getOptionDisabled, sx = {}, minChars, ...rest } = props;
20
+ const { search = false, idField = "id", idValue, idIsString = false, inputAutoComplete = "off", inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputReset, inputVariant, label, loadData, defaultValue, value, maxItems = 16, width = search ? 160 : undefined, name, readOnly, onChange, onValueChange, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionKey = (option) => `${option[idField]}`, getOptionLabel, getOptionDisabled, sx = {}, minChars, disabled, ...rest } = props;
21
21
  if (width && sx)
22
22
  Object.assign(sx, { width: `${width}px` });
23
23
  // Value input ref
@@ -71,8 +71,12 @@ export function Tiplist(props) {
71
71
  }
72
72
  // Stop bubble
73
73
  event.stopPropagation();
74
+ const value = event.currentTarget.value;
75
+ if (minChars && minChars > 0 && value.length < minChars) {
76
+ return;
77
+ }
74
78
  // Call with delay
75
- delayed.call(undefined, event.currentTarget.value);
79
+ delayed.call(undefined, value);
76
80
  };
77
81
  // Directly load data
78
82
  const loadDataDirect = (keyword, id) => {
@@ -166,7 +170,7 @@ export function Tiplist(props) {
166
170
  };
167
171
  }, []);
168
172
  // Layout
169
- return (_jsxs("div", { style: { flex: 2 }, children: [_jsx("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: `${inputValue ?? (state.current.idSet ? "" : localIdValue ?? "")}`, readOnly: true, onChange: inputOnChange }), _jsx(Autocomplete, { filterOptions: (options, _state) => options, value: states.value, options: states.options, onChange: (event, value, reason, details) => {
173
+ return (_jsxs("div", { style: { flex: 2 }, children: [_jsx("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: `${inputValue ?? (state.current.idSet ? "" : localIdValue ?? "")}`, readOnly: true, onChange: inputOnChange, disabled: disabled }), _jsx(Autocomplete, { filterOptions: (options, _state) => options, disabled: disabled, value: states.value, options: states.options, onChange: (event, value, reason, details) => {
170
174
  // Set value
171
175
  setInputValue(value);
172
176
  // Custom
@@ -191,7 +195,7 @@ export function Tiplist(props) {
191
195
  open: false,
192
196
  ...(!states.value && { options: [] })
193
197
  });
194
- }, loading: states.loading, renderInput: (params) => search ? (_jsx(SearchField, { onChange: changeHandle, ...addReadOnly(params), readOnly: readOnly, label: label, name: name + "Input", margin: inputMargin, minChars: minChars, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })) : (_jsx(InputField, { onChange: changeHandle, ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, minChars: minChars, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })), isOptionEqualToValue: (option, value) => option[idField] === value[idField], sx: sx, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionDisabled: (item) => {
198
+ }, loading: states.loading, renderInput: (params) => search ? (_jsx(SearchField, { onChange: changeHandle, ...addReadOnly(params), readOnly: readOnly, label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })) : (_jsx(InputField, { onChange: changeHandle, ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })), isOptionEqualToValue: (option, value) => option[idField] === value[idField], sx: sx, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionDisabled: (item) => {
195
199
  if (item[idField] === "n/a")
196
200
  return true;
197
201
  return getOptionDisabled ? getOptionDisabled(item) : false;
@@ -16,7 +16,7 @@ export function TiplistPro(props) {
16
16
  // Labels
17
17
  const { noOptions, loading, more, open: openDefault } = app?.getLabels("noOptions", "loading", "more", "open") ?? {};
18
18
  // Destruct
19
- const { label, loadData, defaultValue, value, idValue, idIsString = false, maxItems = 16, width, name, inputOnChange, inputProps, inputReset, sx, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionDisabled, getOptionLabel, getOptionKey = (option) => typeof option === "string" ? option : option.id, onChange, onValueChange, minChars, ...rest } = props;
19
+ const { label, loadData, defaultValue, value, idValue, idIsString = false, maxItems = 16, width, name, inputOnChange, inputProps, inputReset, sx, openOnFocus = true, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionDisabled, getOptionLabel, getOptionKey = (option) => typeof option === "string" ? option : option.id, onChange, onValueChange, minChars, disabled, ...rest } = props;
20
20
  if (width && sx)
21
21
  Object.assign(sx, { width: `${width}px` });
22
22
  // Value input ref
@@ -60,8 +60,12 @@ export function TiplistPro(props) {
60
60
  }
61
61
  // Stop bubble
62
62
  event.stopPropagation();
63
+ const value = event.currentTarget.value;
64
+ if (minChars && minChars > 0 && value.length < minChars) {
65
+ return;
66
+ }
63
67
  // Call with delay
64
- delayed.call(undefined, event.currentTarget.value);
68
+ delayed.call(undefined, value);
65
69
  };
66
70
  // Directly load data
67
71
  const loadDataDirect = (keyword, id) => {
@@ -152,7 +156,7 @@ export function TiplistPro(props) {
152
156
  };
153
157
  }, []);
154
158
  // Layout
155
- return (_jsxs("div", { style: { flex: 2 }, children: [_jsx("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: inputValue ?? (state.current.idSet ? "" : localIdValue ?? ""), readOnly: true, onChange: inputOnChange }), _jsx(Autocomplete, { filterOptions: (options, _state) => options, value: states.value, options: states.options, freeSolo: true, clearOnBlur: false, onChange: (event, value, reason, details) => {
159
+ return (_jsxs("div", { style: { flex: 2 }, children: [_jsx("input", { ref: inputRef, "data-reset": inputReset ?? true, type: idIsString ? "text" : "number", style: { display: "none" }, name: name, value: inputValue ?? (state.current.idSet ? "" : localIdValue ?? ""), readOnly: true, onChange: inputOnChange, disabled: disabled }), _jsx(Autocomplete, { filterOptions: (options, _state) => options, value: states.value, options: states.options, disabled: disabled, freeSolo: true, clearOnBlur: false, onChange: (event, value, reason, details) => {
156
160
  if (typeof value === "object") {
157
161
  // Set value
158
162
  setInputValue(value);
@@ -183,7 +187,7 @@ export function TiplistPro(props) {
183
187
  open: false,
184
188
  ...(!states.value && { options: [] })
185
189
  });
186
- }, loading: states.loading, renderInput: (params) => (_jsx(InputField, { minChars: minChars, ...inputProps, ...params, onChange: changeHandle, label: label, name: name + "Input", onBlur: (event) => {
190
+ }, loading: states.loading, renderInput: (params) => (_jsx(InputField, { ...inputProps, ...params, onChange: changeHandle, label: label, name: name + "Input", onBlur: (event) => {
187
191
  if (states.value == null && onChange)
188
192
  onChange(event, event.target.value, "blur", undefined);
189
193
  }, "data-reset": inputReset })), isOptionEqualToValue: (option, value) => option.id === value.id, sx: sx, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionKey: getOptionKey, getOptionDisabled: (item) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.5.98",
3
+ "version": "1.6.0",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -44,9 +44,9 @@
44
44
  "@etsoo/notificationbase": "^1.1.66",
45
45
  "@etsoo/react": "^1.8.68",
46
46
  "@etsoo/shared": "^1.2.80",
47
- "@mui/icons-material": "^7.3.6",
48
- "@mui/material": "^7.3.6",
49
- "@mui/x-data-grid": "^8.23.0",
47
+ "@mui/icons-material": "^7.3.7",
48
+ "@mui/material": "^7.3.7",
49
+ "@mui/x-data-grid": "^8.24.0",
50
50
  "chart.js": "^4.5.1",
51
51
  "chartjs-plugin-datalabels": "^2.2.0",
52
52
  "dompurify": "^3.3.1",
package/src/ComboBox.tsx CHANGED
@@ -86,6 +86,7 @@ export function ComboBox<
86
86
 
87
87
  // Destruct
88
88
  const {
89
+ disabled,
89
90
  search = false,
90
91
  autoAddBlankItem = search,
91
92
  idField = "id" as D,
@@ -246,11 +247,13 @@ export function ComboBox<
246
247
  value={getValue(stateValue)}
247
248
  readOnly
248
249
  onChange={inputOnChange}
250
+ disabled={disabled}
249
251
  />
250
252
  {/* Previous input will reset first with "disableClearable = false", next input trigger change works */}
251
253
  <Stack gap={0.5} direction="row" width="100%">
252
254
  <Autocomplete<T, false, false, false>
253
255
  value={stateValue}
256
+ disabled={disabled}
254
257
  disableCloseOnSelect={disableCloseOnSelect}
255
258
  getOptionLabel={getOptionLabel}
256
259
  isOptionEqualToValue={(option: T, value: T) =>
@@ -126,6 +126,7 @@ export function ComboBoxMultiple<
126
126
  sx = { minWidth: "150px" },
127
127
  noOptionsText = labels?.noOptions,
128
128
  loadingText = labels?.loading,
129
+ disabled,
129
130
  ...rest
130
131
  } = props;
131
132
 
@@ -249,6 +250,7 @@ export function ComboBoxMultiple<
249
250
  value={getValue(stateValue)}
250
251
  readOnly
251
252
  onChange={inputOnChange}
253
+ disabled={disabled}
252
254
  />
253
255
  {/* Previous input will reset first with "disableClearable = false", next input trigger change works */}
254
256
  <Autocomplete<T, true, false, false>
@@ -259,6 +261,7 @@ export function ComboBoxMultiple<
259
261
  ? stateValue
260
262
  : [stateValue]
261
263
  }
264
+ disabled={disabled}
262
265
  disableCloseOnSelect={disableCloseOnSelect}
263
266
  getOptionLabel={getOptionLabel}
264
267
  getOptionKey={getOptionKey}
@@ -8,12 +8,12 @@ import TextField, { TextFieldProps } from "@mui/material/TextField";
8
8
  */
9
9
  export type InputFieldProps = TextFieldProps & {
10
10
  /**
11
- * Change delay (ms) to avoid repeatly dispatch onChange
11
+ * Change [delay (ms), Minimum characters] to avoid repeatly dispatch
12
12
  */
13
- changeDelay?: number;
13
+ changeDelay?: [number, number?];
14
14
 
15
15
  /**
16
- * Change delay handler, without it onChange will be applied
16
+ * Change delay handler
17
17
  */
18
18
  onChangeDelay?: React.ChangeEventHandler<
19
19
  HTMLTextAreaElement | HTMLInputElement
@@ -23,11 +23,6 @@ export type InputFieldProps = TextFieldProps & {
23
23
  * Is the field read only?
24
24
  */
25
25
  readOnly?: boolean;
26
-
27
- /**
28
- * Minimum characters to trigger the change event
29
- */
30
- minChars?: number;
31
26
  };
32
27
 
33
28
  /**
@@ -41,13 +36,12 @@ export function InputField(props: InputFieldProps) {
41
36
  InputProps = {},
42
37
  inputProps = {},
43
38
  slotProps,
44
- changeDelay,
45
39
  onChange,
46
40
  onChangeDelay,
41
+ changeDelay = onChangeDelay ? [480] : undefined,
47
42
  readOnly,
48
43
  size = MUGlobal.inputFieldSize,
49
44
  variant = MUGlobal.inputFieldVariant,
50
- minChars = 0,
51
45
  ...rest
52
46
  } = props;
53
47
 
@@ -56,9 +50,8 @@ export function InputField(props: InputFieldProps) {
56
50
 
57
51
  const isMounted = React.useRef(true);
58
52
  const createDelayed = () => {
59
- if (changeDelay != null && changeDelay >= 1) {
60
- const changeHandler = onChangeDelay ?? onChange;
61
- if (changeHandler) return useDelayedExecutor(changeHandler, changeDelay);
53
+ if (onChangeDelay && changeDelay && changeDelay[0] >= 1) {
54
+ return useDelayedExecutor(onChangeDelay, changeDelay[0]);
62
55
  }
63
56
  return undefined;
64
57
  };
@@ -67,17 +60,19 @@ export function InputField(props: InputFieldProps) {
67
60
  const onChangeEx = (
68
61
  event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
69
62
  ) => {
70
- // Min characters check
71
- const len = event.target.value.length;
72
- if (len > 0 && len < minChars) {
73
- // Avoid to trigger the form change event
74
- event.stopPropagation();
75
- event.preventDefault();
76
- return;
77
- }
63
+ // Change handler
64
+ onChange?.(event);
78
65
 
79
- if (onChange && (delayed == null || onChangeDelay != null)) onChange(event);
80
- delayed?.call(undefined, event);
66
+ if (onChangeDelay && changeDelay && delayed) {
67
+ const [_, minChars = 0] = changeDelay;
68
+
69
+ if (minChars > 0) {
70
+ const len = event.target.value.length;
71
+ if (len < minChars) return;
72
+ }
73
+
74
+ delayed.call(undefined, event);
75
+ }
81
76
  };
82
77
 
83
78
  React.useEffect(() => {
@@ -92,7 +87,7 @@ export function InputField(props: InputFieldProps) {
92
87
  <TextField
93
88
  slotProps={{
94
89
  htmlInput: {
95
- ["data-min-chars"]: minChars,
90
+ ["data-min-chars"]: changeDelay?.[1],
96
91
  ...htmlInput,
97
92
  ...inputProps
98
93
  },
@@ -96,7 +96,7 @@ export function InputTipField<T extends ItemType = ItemType>(
96
96
  const {
97
97
  component = "input",
98
98
  componentProps,
99
- changeDelay = 480,
99
+ changeDelay,
100
100
  onChangeDelay,
101
101
  fullWidth = true,
102
102
  slotProps = {},
@@ -87,7 +87,7 @@ export function IntInputField(props: IntInputFieldProps) {
87
87
  endSymbol,
88
88
  symbol,
89
89
  value,
90
- changeDelay = 600,
90
+ changeDelay = [600],
91
91
  onChangeDelay,
92
92
  onChange,
93
93
  onFocus = (event) => event.currentTarget.select(),
package/src/QuickList.tsx CHANGED
@@ -118,11 +118,11 @@ export function QuickList<T extends ListType2 = ListType2>(
118
118
  <VBox gap={gap} height={height} {...rest}>
119
119
  <InputField
120
120
  label={label}
121
- changeDelay={480}
122
121
  onChangeDelay={(event) => {
123
122
  // Stop bubble
124
123
  event.preventDefault();
125
124
  event.stopPropagation();
125
+
126
126
  loadDataLocal(event.target.value);
127
127
  }}
128
128
  fullWidth
package/src/TagList.tsx CHANGED
@@ -27,7 +27,7 @@ export type TagListProps = Omit<
27
27
  /**
28
28
  * Input props
29
29
  */
30
- inputProps?: Omit<InputFieldProps, "onChange">;
30
+ inputProps?: Omit<InputFieldProps, "onChangeDelay">;
31
31
 
32
32
  /**
33
33
  * Max items
@@ -62,7 +62,7 @@ export function TagList(props: TagListProps) {
62
62
  {option}
63
63
  </li>
64
64
  ),
65
- renderTags = (value: readonly string[], getTagProps) =>
65
+ renderValue = (value: readonly string[], getTagProps) =>
66
66
  value.map((option, index) => {
67
67
  const { key, ...rest } = getTagProps({ index });
68
68
  return <Chip variant="outlined" key={key} label={option} {...rest} />;
@@ -129,12 +129,11 @@ export function TagList(props: TagListProps) {
129
129
  clearOnBlur
130
130
  openOnFocus={openOnFocus}
131
131
  renderOption={renderOption}
132
- renderTags={renderTags}
132
+ renderValue={renderValue}
133
133
  renderInput={(params) => (
134
134
  <InputField
135
135
  label={label}
136
- changeDelay={480}
137
- onChange={async (event) => {
136
+ onChangeDelay={async (event) => {
138
137
  // Stop bubble
139
138
  event.preventDefault();
140
139
  event.stopPropagation();
@@ -28,7 +28,7 @@ export type TagListProProps<D extends ListType2 = ListType2> = Omit<
28
28
  /**
29
29
  * Input props
30
30
  */
31
- inputProps?: Omit<InputFieldProps, "onChange">;
31
+ inputProps?: Omit<InputFieldProps, "onChangeDelay">;
32
32
 
33
33
  /**
34
34
  * Max items
@@ -147,8 +147,7 @@ export function TagListPro<D extends ListType2 = ListType2>(
147
147
  renderInput={(params) => (
148
148
  <InputField
149
149
  label={label}
150
- changeDelay={480}
151
- onChange={async (event) => {
150
+ onChangeDelay={async (event) => {
152
151
  // Stop bubble
153
152
  event.preventDefault();
154
153
  event.stopPropagation();
package/src/Tiplist.tsx CHANGED
@@ -102,6 +102,7 @@ export function Tiplist<
102
102
  getOptionDisabled,
103
103
  sx = {},
104
104
  minChars,
105
+ disabled,
105
106
  ...rest
106
107
  } = props;
107
108
 
@@ -177,8 +178,13 @@ export function Tiplist<
177
178
  // Stop bubble
178
179
  event.stopPropagation();
179
180
 
181
+ const value = event.currentTarget.value;
182
+ if (minChars && minChars > 0 && value.length < minChars) {
183
+ return;
184
+ }
185
+
180
186
  // Call with delay
181
- delayed.call(undefined, event.currentTarget.value);
187
+ delayed.call(undefined, value);
182
188
  };
183
189
 
184
190
  // Directly load data
@@ -297,10 +303,12 @@ export function Tiplist<
297
303
  }`}
298
304
  readOnly
299
305
  onChange={inputOnChange}
306
+ disabled={disabled}
300
307
  />
301
308
  {/* Previous input will reset first with "disableClearable = false", next input trigger change works */}
302
309
  <Autocomplete<T, undefined, false, false>
303
310
  filterOptions={(options, _state) => options}
311
+ disabled={disabled}
304
312
  value={states.value}
305
313
  options={states.options}
306
314
  onChange={(event, value, reason, details) => {
@@ -349,7 +357,6 @@ export function Tiplist<
349
357
  label={label}
350
358
  name={name + "Input"}
351
359
  margin={inputMargin}
352
- minChars={minChars}
353
360
  variant={inputVariant}
354
361
  required={inputRequired}
355
362
  autoComplete={inputAutoComplete}
@@ -363,7 +370,6 @@ export function Tiplist<
363
370
  label={label}
364
371
  name={name + "Input"}
365
372
  margin={inputMargin}
366
- minChars={minChars}
367
373
  variant={inputVariant}
368
374
  required={inputRequired}
369
375
  autoComplete={inputAutoComplete}
@@ -132,6 +132,7 @@ export function TiplistPro<T extends ListType2 = ListType2>(
132
132
  onChange,
133
133
  onValueChange,
134
134
  minChars,
135
+ disabled,
135
136
  ...rest
136
137
  } = props;
137
138
 
@@ -194,8 +195,13 @@ export function TiplistPro<T extends ListType2 = ListType2>(
194
195
  // Stop bubble
195
196
  event.stopPropagation();
196
197
 
198
+ const value = event.currentTarget.value;
199
+ if (minChars && minChars > 0 && value.length < minChars) {
200
+ return;
201
+ }
202
+
197
203
  // Call with delay
198
- delayed.call(undefined, event.currentTarget.value);
204
+ delayed.call(undefined, value);
199
205
  };
200
206
 
201
207
  // Directly load data
@@ -309,12 +315,14 @@ export function TiplistPro<T extends ListType2 = ListType2>(
309
315
  value={inputValue ?? (state.current.idSet ? "" : localIdValue ?? "")}
310
316
  readOnly
311
317
  onChange={inputOnChange}
318
+ disabled={disabled}
312
319
  />
313
320
  {/* Previous input will reset first with "disableClearable = false", next input trigger change works */}
314
321
  <Autocomplete<T, false, false, true>
315
322
  filterOptions={(options, _state) => options}
316
323
  value={states.value}
317
324
  options={states.options}
325
+ disabled={disabled}
318
326
  freeSolo
319
327
  clearOnBlur={false}
320
328
  onChange={(event, value, reason, details) => {
@@ -363,7 +371,6 @@ export function TiplistPro<T extends ListType2 = ListType2>(
363
371
  loading={states.loading}
364
372
  renderInput={(params) => (
365
373
  <InputField
366
- minChars={minChars}
367
374
  {...inputProps}
368
375
  {...params}
369
376
  onChange={changeHandle}