@etsoo/materialui 1.1.44 → 1.1.46

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.
@@ -1,4 +1,5 @@
1
- import { AddressApi } from "@etsoo/appscript";
1
+ import { AddressApi, AddressCity, AddressDistrict, AddressRegionDb, AddressState } from "@etsoo/appscript";
2
+ import { RegularBreakpoints } from "@mui/material";
2
3
  import React from "react";
3
4
  /**
4
5
  * Address field
@@ -9,6 +10,7 @@ export declare enum AddressField {
9
10
  City = "city",
10
11
  District = "district"
11
12
  }
13
+ type AddressFieldType<F extends AddressField> = F extends AddressField.Region ? [F, AddressRegionDb | null] : F extends AddressField.State ? [F, AddressState | null] : F extends AddressField.City ? [F, AddressCity | null] : [F, AddressDistrict | null];
12
14
  /**
13
15
  * Address selector props
14
16
  */
@@ -17,6 +19,10 @@ export type AddressSelectorProps = {
17
19
  * Address API
18
20
  */
19
21
  api: AddressApi;
22
+ /**
23
+ * Break points
24
+ */
25
+ breakPoints?: RegularBreakpoints;
20
26
  /**
21
27
  * City
22
28
  */
@@ -49,6 +55,11 @@ export type AddressSelectorProps = {
49
55
  * Label
50
56
  */
51
57
  label?: string;
58
+ /**
59
+ * Onchange hanlder
60
+ * @param event Event
61
+ */
62
+ onChange?: <F extends AddressField>(event: AddressFieldType<F>) => void;
52
63
  /**
53
64
  * Country or region
54
65
  */
@@ -79,3 +90,4 @@ export type AddressSelectorProps = {
79
90
  * @param props Props
80
91
  */
81
92
  export declare function AddressSelector(props: AddressSelectorProps): JSX.Element;
93
+ export {};
@@ -22,7 +22,11 @@ export function AddressSelector(props) {
22
22
  // Labels
23
23
  const { city: cityDefault = "City", district: districtDefault = "District", region: regionDefault = "Region", state: stateDefault = "State" } = (_a = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("region", "state", "city", "district")) !== null && _a !== void 0 ? _a : {};
24
24
  // Destruct
25
- const { api, city, cityLabel = cityDefault, district, districtLabel = districtDefault, error, helperText, hideRegion, label, region, regionLabel = regionDefault, required, search, state, stateLabel = stateDefault } = props;
25
+ const { api, city, cityLabel = cityDefault, district, districtLabel = districtDefault, error, helperText, hideRegion, label, onChange, region, regionLabel = regionDefault, required, search, state, stateLabel = stateDefault, breakPoints = { xs: 12, md: 6, lg: hideRegion ? 4 : 3 } } = props;
26
+ const isMounted = React.useRef(true);
27
+ React.useEffect(() => () => {
28
+ isMounted.current = false;
29
+ }, []);
26
30
  // States
27
31
  const [regionState, setRegion] = React.useState(region);
28
32
  const [stateState, setState] = React.useState(state);
@@ -40,7 +44,7 @@ export function AddressSelector(props) {
40
44
  setStates([]);
41
45
  else
42
46
  api.states(regionState).then((items) => {
43
- if (items == null)
47
+ if (items == null || !isMounted.current)
44
48
  return;
45
49
  setStates(items);
46
50
  });
@@ -50,7 +54,7 @@ export function AddressSelector(props) {
50
54
  setCities([]);
51
55
  else
52
56
  api.cities(stateState).then((items) => {
53
- if (items == null)
57
+ if (items == null || !isMounted.current)
54
58
  return;
55
59
  setCities(items);
56
60
  });
@@ -60,21 +64,24 @@ export function AddressSelector(props) {
60
64
  setDistricts([]);
61
65
  else
62
66
  api.districts(cityState).then((items) => {
63
- if (items == null)
67
+ if (items == null || !isMounted.current)
64
68
  return;
65
69
  setDistricts(items);
66
70
  });
67
71
  }, [cityState]);
68
- // Field size
69
- const fieldSize = hideRegion ? 4 : 3;
70
72
  // Handle field change
71
- const handleChange = (field, value) => {
73
+ const handleChange = (event) => {
74
+ if (!isMounted.current)
75
+ return;
76
+ if (onChange)
77
+ onChange(event);
78
+ const [field, data] = event;
72
79
  if (field === AddressField.Region) {
73
- if (value == null) {
80
+ if (data == null) {
74
81
  setRegion(undefined);
75
82
  }
76
83
  else {
77
- setRegion(value);
84
+ setRegion(data.id);
78
85
  }
79
86
  setState(undefined);
80
87
  setCity(undefined);
@@ -82,50 +89,43 @@ export function AddressSelector(props) {
82
89
  return;
83
90
  }
84
91
  if (field === AddressField.State) {
85
- if (value == null) {
92
+ if (data == null) {
86
93
  setState(undefined);
87
94
  }
88
95
  else {
89
- setState(value);
96
+ setState(data.id);
90
97
  }
91
98
  setCity(undefined);
92
99
  setDistrict(undefined);
93
100
  return;
94
101
  }
95
102
  if (field === AddressField.City) {
96
- if (value == null) {
103
+ if (data == null) {
97
104
  setCity(undefined);
98
105
  }
99
- else if (typeof value === "number") {
100
- setCity(value);
101
- }
102
106
  else {
103
- setCity(parseInt(`${value}`));
107
+ setCity(data.id);
104
108
  }
105
109
  setDistrict(undefined);
106
110
  return;
107
111
  }
108
- if (value == null) {
112
+ if (data == null) {
109
113
  setDistrict(undefined);
110
114
  }
111
- else if (typeof value === "number") {
112
- setDistrict(value);
113
- }
114
115
  else {
115
- setDistrict(parseInt(`${value}`));
116
+ setDistrict(data.id);
116
117
  }
117
118
  };
118
- console.log(regionState, stateState, cityState, districtState);
119
119
  // Layout
120
120
  return (React.createElement(React.Fragment, null,
121
121
  label && (React.createElement(Grid, { item: true, xs: 12 },
122
122
  React.createElement(FormLabel, { required: required, sx: { fontSize: (theme) => theme.typography.caption } }, label))),
123
- !hideRegion && (React.createElement(Grid, { item: true, xs: 12, md: 6, lg: fieldSize },
124
- React.createElement(Tiplist, { label: regionLabel, name: AddressField.Region, search: search, fullWidth: true, idValue: regionState, loadData: (keyword, id, items) => api.getRegions({ keyword, id, items }), inputRequired: required, inputError: error, inputHelperText: helperText, onChange: (_event, value) => handleChange(AddressField.Region, value === null || value === void 0 ? void 0 : value.id) }))),
125
- React.createElement(Grid, { item: true, xs: 12, md: 6, lg: fieldSize },
126
- React.createElement(ComboBox, { name: AddressField.State, label: stateLabel, search: search, fullWidth: true, idValue: stateState, options: states, inputRequired: hideRegion ? required : undefined, inputError: hideRegion ? error : undefined, inputHelperText: hideRegion ? helperText : undefined, onChange: (_event, value) => handleChange(AddressField.State, value === null || value === void 0 ? void 0 : value.id) })),
127
- React.createElement(Grid, { item: true, xs: 12, md: 6, lg: fieldSize },
128
- React.createElement(ComboBox, { name: AddressField.City, label: cityLabel, search: search, fullWidth: true, idValue: cityState, options: cities, onChange: (_event, value) => handleChange(AddressField.City, value === null || value === void 0 ? void 0 : value.id) })),
129
- React.createElement(Grid, { item: true, xs: 12, md: 6, lg: fieldSize },
130
- React.createElement(ComboBox, { name: AddressField.District, label: districtLabel, search: search, fullWidth: true, idValue: districtState, options: districts, onChange: (_event, value) => handleChange(AddressField.District, value === null || value === void 0 ? void 0 : value.id) }))));
123
+ !hideRegion && (React.createElement(Grid, { item: true, ...breakPoints },
124
+ React.createElement(Tiplist, { label: regionLabel, name: AddressField.Region, search: search, fullWidth: true, idValue: regionState, loadData: (keyword, id, items) => api.getRegions({ keyword, id, items }), inputRequired: required, inputError: error, inputHelperText: helperText, onChange: (_event, value) => handleChange([AddressField.Region, value]) }))),
125
+ React.createElement(Grid, { item: true, ...breakPoints },
126
+ React.createElement(ComboBox, { name: AddressField.State, label: stateLabel, search: search, fullWidth: true, idValue: stateState, options: states, inputRequired: hideRegion ? required : undefined, inputError: hideRegion ? error : undefined, inputHelperText: hideRegion ? helperText : undefined, onChange: (_event, value) => handleChange([AddressField.State, value]) })),
127
+ React.createElement(Grid, { item: true, ...breakPoints },
128
+ React.createElement(ComboBox, { name: AddressField.City, label: cityLabel, search: search, fullWidth: true, idValue: cityState, options: cities, onChange: (_event, value) => handleChange([AddressField.City, value]) })),
129
+ React.createElement(Grid, { item: true, ...breakPoints },
130
+ React.createElement(ComboBox, { name: AddressField.District, label: districtLabel, search: search, fullWidth: true, idValue: districtState, options: districts, onChange: (_event, value) => handleChange([AddressField.District, value]) }))));
131
131
  }
package/lib/ComboBox.js CHANGED
@@ -13,10 +13,10 @@ import { globalApp } from "./app/ReactApp";
13
13
  */
14
14
  export function ComboBox(props) {
15
15
  // Labels
16
- const labels = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("noOptions", "loading");
16
+ const labels = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("noOptions", "loading", "open");
17
17
  // Destruct
18
18
  const { search = false, autoAddBlankItem = search, idField = "id", idValue, inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, defaultValue, label, labelField = "label", loadData, onLoadData, name, inputAutoComplete = "new-password", // disable autocomplete and autofill, 'off' does not work
19
- options, dataReadonly = true, readOnly, onChange, openOnFocus = true, value, disableCloseOnSelect = false, getOptionLabel = (option) => `${option[labelField]}`, sx = { minWidth: "150px" }, noOptionsText = labels === null || labels === void 0 ? void 0 : labels.noOptions, loadingText = labels === null || labels === void 0 ? void 0 : labels.loading, ...rest } = props;
19
+ options, dataReadonly = true, readOnly, onChange, openOnFocus = true, value, disableCloseOnSelect = false, getOptionLabel = (option) => `${option[labelField]}`, sx = { minWidth: "150px" }, noOptionsText = labels === null || labels === void 0 ? void 0 : labels.noOptions, loadingText = labels === null || labels === void 0 ? void 0 : labels.loading, openText = labels === null || labels === void 0 ? void 0 : labels.open, ...rest } = props;
20
20
  // Value input ref
21
21
  const inputRef = React.createRef();
22
22
  // Options state
@@ -26,8 +26,13 @@ export function ComboBox(props) {
26
26
  // [options] will cause infinite loop
27
27
  const propertyWay = loadData == null;
28
28
  React.useEffect(() => {
29
- if (propertyWay && options != null)
29
+ if (propertyWay && options != null) {
30
30
  setOptions(options);
31
+ if (stateValue != null &&
32
+ !options.some((option) => option[idField] === stateValue[idField])) {
33
+ setStateValue(null);
34
+ }
35
+ }
31
36
  }, [options, propertyWay]);
32
37
  // Local default value
33
38
  const localValue = idValue != null
@@ -110,5 +115,5 @@ export function ComboBox(props) {
110
115
  // Custom
111
116
  if (onChange != null)
112
117
  onChange(event, value, reason, details);
113
- }, openOnFocus: openOnFocus, sx: sx, renderInput: (params) => search ? (React.createElement(SearchField, { ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, error: inputError, helperText: inputHelperText })) : (React.createElement(InputField, { ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, error: inputError, helperText: inputHelperText })), options: localOptions, noOptionsText: noOptionsText, loadingText: loadingText, ...rest })));
118
+ }, openOnFocus: openOnFocus, sx: sx, renderInput: (params) => search ? (React.createElement(SearchField, { ...addReadOnly(params), label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, error: inputError, helperText: inputHelperText })) : (React.createElement(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, ...rest })));
114
119
  }
@@ -33,8 +33,18 @@ export function ComboBoxMultiple(props) {
33
33
  // [options] will cause infinite loop
34
34
  const propertyWay = loadData == null;
35
35
  React.useEffect(() => {
36
- if (propertyWay && options != null)
36
+ if (propertyWay && options != null) {
37
37
  setOptions(options);
38
+ if (stateValue != null) {
39
+ if (Array.isArray(stateValue)) {
40
+ const newState = stateValue.filter((item) => options.some((option) => option[idField] === item[idField]));
41
+ setStateValue(newState);
42
+ }
43
+ else if (!options.some((option) => option[idField] === stateValue[idField])) {
44
+ setStateValue(null);
45
+ }
46
+ }
47
+ }
38
48
  }, [options, propertyWay]);
39
49
  // Local default value
40
50
  const localValue = idValue != null
@@ -1,10 +1,14 @@
1
1
  import { DataTypes, IdDefaultType, LabelDefaultType } from "@etsoo/shared";
2
- import { SelectChangeEvent } from "@mui/material";
2
+ import { RegularBreakpoints, SelectChangeEvent } from "@mui/material";
3
3
  import React from "react";
4
4
  /**
5
5
  * Hierarchy selector props
6
6
  */
7
7
  export type HiSelectorProps<T extends object, D extends DataTypes.Keys<T> = IdDefaultType<T>, L extends DataTypes.Keys<T, string> = LabelDefaultType<T>> = {
8
+ /**
9
+ * Break points
10
+ */
11
+ breakPoints?: RegularBreakpoints;
8
12
  /**
9
13
  * Id field
10
14
  */
package/lib/HiSelector.js CHANGED
@@ -8,7 +8,7 @@ import { SelectEx } from "./SelectEx";
8
8
  */
9
9
  export function HiSelector(props) {
10
10
  // Destruct
11
- const { idField = "id", error, helperText, name, label, labelField = "name", loadData, onChange, onSelectChange, onItemChange, required, search = true, values = [] } = props;
11
+ const { breakPoints = { xs: 6, md: 4, lg: 3 }, idField = "id", error, helperText, name, label, labelField = "name", loadData, onChange, onSelectChange, onItemChange, required, search = true, values = [] } = props;
12
12
  const [localValues, setValues] = React.useState(values);
13
13
  const updateValue = (value) => {
14
14
  if (onChange)
@@ -43,12 +43,12 @@ export function HiSelector(props) {
43
43
  label && (React.createElement(Grid, { item: true, xs: 12 },
44
44
  React.createElement(FormLabel, { required: required, sx: { fontSize: (theme) => theme.typography.caption } }, label))),
45
45
  React.createElement("input", { type: "hidden", name: name, value: `${currentValue !== null && currentValue !== void 0 ? currentValue : ""}` }),
46
- React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
46
+ React.createElement(Grid, { item: true, ...breakPoints },
47
47
  React.createElement(SelectEx, { idField: idField, labelField: labelField, name: "tab1", search: search, fullWidth: true, loadData: () => loadData(), value: values[0], onChange: (event) => doChange(event, 0), onItemChange: doItemChange, inputRequired: required, error: error, helperText: helperText })),
48
- localValues[0] != null && (React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
48
+ localValues[0] != null && (React.createElement(Grid, { item: true, ...breakPoints },
49
49
  React.createElement(SelectEx, { key: `${localValues[0]}`, idField: idField, labelField: labelField, name: "tab2", search: search, fullWidth: true, loadData: () => loadData(localValues[0]), value: values[1], onChange: (event) => doChange(event, 1), onItemChange: doItemChange }))),
50
- localValues[1] != null && (React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
50
+ localValues[1] != null && (React.createElement(Grid, { item: true, ...breakPoints },
51
51
  React.createElement(SelectEx, { key: `${localValues[1]}`, idField: idField, labelField: labelField, name: "tab3", search: search, fullWidth: true, loadData: () => loadData(localValues[1]), value: values[2], onChange: (event) => doChange(event, 2), onItemChange: doItemChange }))),
52
- localValues[2] != null && (React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
52
+ localValues[2] != null && (React.createElement(Grid, { item: true, ...breakPoints },
53
53
  React.createElement(SelectEx, { key: `${localValues[2]}`, idField: idField, labelField: labelField, name: "tab4", search: search, fullWidth: true, loadData: () => loadData(localValues[2]), value: values[3], onChange: (event) => doChange(event, 3), onItemChange: doItemChange })))));
54
54
  }
@@ -1,10 +1,14 @@
1
1
  import { DataTypes, IdDefaultType } from "@etsoo/shared";
2
- import { AutocompleteChangeReason } from "@mui/material";
2
+ import { AutocompleteChangeReason, RegularBreakpoints } from "@mui/material";
3
3
  import React from "react";
4
4
  /**
5
5
  * Hierarchy tiplist selector props
6
6
  */
7
7
  export type HiSelectorTLProps<T extends object, D extends DataTypes.Keys<T> = IdDefaultType<T>> = {
8
+ /**
9
+ * Break points
10
+ */
11
+ breakPoints?: RegularBreakpoints;
8
12
  /**
9
13
  * Id field
10
14
  */
@@ -8,7 +8,7 @@ import { Tiplist } from "./Tiplist";
8
8
  */
9
9
  export function HiSelectorTL(props) {
10
10
  // Destruct
11
- const { idField = "id", error, helperText, name, label = name, loadData, onChange, onItemChange, required, search = false, values = [] } = props;
11
+ const { breakPoints = { xs: 6, md: 4, lg: 3 }, idField = "id", error, helperText, name, label = name, loadData, onChange, onItemChange, required, search = false, values = [] } = props;
12
12
  const [localValues, setValues] = React.useState(values);
13
13
  const updateValue = (value) => {
14
14
  if (onChange)
@@ -38,12 +38,12 @@ export function HiSelectorTL(props) {
38
38
  label && (React.createElement(Grid, { item: true, xs: 12 },
39
39
  React.createElement(FormLabel, { required: required, sx: { fontSize: (theme) => theme.typography.caption } }, label))),
40
40
  React.createElement("input", { type: "hidden", name: name, value: `${currentValue !== null && currentValue !== void 0 ? currentValue : ""}` }),
41
- React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
41
+ React.createElement(Grid, { item: true, ...breakPoints },
42
42
  React.createElement(Tiplist, { idField: idField, label: "1", name: "tab1", search: search, fullWidth: true, idValue: values[0], loadData: (keyword, id, items) => loadData(keyword, id, items), inputRequired: required, inputError: error, inputHelperText: helperText, onChange: (event, value, reason) => doChange(0, event, value, reason) })),
43
- localValues[0] != null && (React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
43
+ localValues[0] != null && (React.createElement(Grid, { item: true, ...breakPoints },
44
44
  React.createElement(Tiplist, { key: `${localValues[0]}`, label: "2", idField: idField, name: "tab2", search: search, fullWidth: true, loadData: (keyword, id, items) => loadData(keyword, id, items, localValues[0]), idValue: values[1], onChange: (event, value, reason) => doChange(1, event, value, reason) }))),
45
- localValues[1] != null && (React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
45
+ localValues[1] != null && (React.createElement(Grid, { item: true, ...breakPoints },
46
46
  React.createElement(Tiplist, { key: `${localValues[1]}`, label: "3", idField: idField, name: "tab3", search: search, fullWidth: true, loadData: (keyword, id, items) => loadData(keyword, id, items, localValues[1]), idValue: values[2], onChange: (event, value, reason) => doChange(2, event, value, reason) }))),
47
- localValues[2] != null && (React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
47
+ localValues[2] != null && (React.createElement(Grid, { item: true, ...breakPoints },
48
48
  React.createElement(Tiplist, { key: `${localValues[2]}`, label: "4", idField: idField, name: "tab4", search: search, fullWidth: true, loadData: (keyword, id, items) => loadData(keyword, id, items, localValues[2]), idValue: values[3], onChange: (event, value, reason) => doChange(3, event, value, reason) })))));
49
49
  }
package/lib/Tiplist.js CHANGED
@@ -13,9 +13,9 @@ import { SearchField } from "./SearchField";
13
13
  export function Tiplist(props) {
14
14
  var _a;
15
15
  // Labels
16
- const { noOptions, loading, more } = (_a = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("noOptions", "loading", "more")) !== null && _a !== void 0 ? _a : {};
16
+ const { noOptions, loading, more, open: openDefault } = (_a = globalApp === null || globalApp === void 0 ? void 0 : globalApp.getLabels("noOptions", "loading", "more", "open")) !== null && _a !== void 0 ? _a : {};
17
17
  // Destruct
18
- const { search = false, idField = "id", idValue, inputAutoComplete = "new-password", inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, label, loadData, defaultValue, value, maxItems = 16, name, readOnly, onChange, openOnFocus = true, sx = { minWidth: "180px" }, noOptionsText = noOptions, loadingText = loading, getOptionLabel, getOptionDisabled, ...rest } = props;
18
+ const { search = false, idField = "id", idValue, inputAutoComplete = "new-password", inputError, inputHelperText, inputMargin, inputOnChange, inputRequired, inputVariant, label, loadData, defaultValue, value, maxItems = 16, name, readOnly, onChange, openOnFocus = true, sx = { minWidth: "180px" }, noOptionsText = noOptions, loadingText = loading, openText = openDefault, getOptionLabel, getOptionDisabled, ...rest } = props;
19
19
  // Value input ref
20
20
  const inputRef = React.createRef();
21
21
  // Local value
@@ -156,7 +156,7 @@ export function Tiplist(props) {
156
156
  open: false,
157
157
  ...(!states.value && { options: [] })
158
158
  });
159
- }, loading: states.loading, sx: sx, renderInput: (params) => search ? (React.createElement(SearchField, { onChange: changeHandle, ...addReadOnly(params), readOnly: readOnly, label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })) : (React.createElement(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], noOptionsText: noOptionsText, loadingText: loadingText, getOptionDisabled: (item) => {
159
+ }, loading: states.loading, sx: sx, renderInput: (params) => search ? (React.createElement(SearchField, { onChange: changeHandle, ...addReadOnly(params), readOnly: readOnly, label: label, name: name + "Input", margin: inputMargin, variant: inputVariant, required: inputRequired, autoComplete: inputAutoComplete, error: inputError, helperText: inputHelperText })) : (React.createElement(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], noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, getOptionDisabled: (item) => {
160
160
  if (item[idField] === "n/a")
161
161
  return true;
162
162
  return getOptionDisabled ? getOptionDisabled(item) : false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.1.44",
3
+ "version": "1.1.46",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -5,7 +5,7 @@ import {
5
5
  AddressRegionDb,
6
6
  AddressState
7
7
  } from "@etsoo/appscript";
8
- import { FormLabel, Grid } from "@mui/material";
8
+ import { FormLabel, Grid, RegularBreakpoints } from "@mui/material";
9
9
  import React from "react";
10
10
  import { globalApp } from "./app/ReactApp";
11
11
  import { ComboBox } from "./ComboBox";
@@ -21,6 +21,14 @@ export enum AddressField {
21
21
  District = "district"
22
22
  }
23
23
 
24
+ type AddressFieldType<F extends AddressField> = F extends AddressField.Region
25
+ ? [F, AddressRegionDb | null]
26
+ : F extends AddressField.State
27
+ ? [F, AddressState | null]
28
+ : F extends AddressField.City
29
+ ? [F, AddressCity | null]
30
+ : [F, AddressDistrict | null];
31
+
24
32
  /**
25
33
  * Address selector props
26
34
  */
@@ -30,6 +38,11 @@ export type AddressSelectorProps = {
30
38
  */
31
39
  api: AddressApi;
32
40
 
41
+ /**
42
+ * Break points
43
+ */
44
+ breakPoints?: RegularBreakpoints;
45
+
33
46
  /**
34
47
  * City
35
48
  */
@@ -70,6 +83,12 @@ export type AddressSelectorProps = {
70
83
  */
71
84
  label?: string;
72
85
 
86
+ /**
87
+ * Onchange hanlder
88
+ * @param event Event
89
+ */
90
+ onChange?: <F extends AddressField>(event: AddressFieldType<F>) => void;
91
+
73
92
  /**
74
93
  * Country or region
75
94
  */
@@ -125,14 +144,24 @@ export function AddressSelector(props: AddressSelectorProps) {
125
144
  helperText,
126
145
  hideRegion,
127
146
  label,
147
+ onChange,
128
148
  region,
129
149
  regionLabel = regionDefault,
130
150
  required,
131
151
  search,
132
152
  state,
133
- stateLabel = stateDefault
153
+ stateLabel = stateDefault,
154
+ breakPoints = { xs: 12, md: 6, lg: hideRegion ? 4 : 3 }
134
155
  } = props;
135
156
 
157
+ const isMounted = React.useRef(true);
158
+ React.useEffect(
159
+ () => () => {
160
+ isMounted.current = false;
161
+ },
162
+ []
163
+ );
164
+
136
165
  // States
137
166
  const [regionState, setRegion] = React.useState(region);
138
167
  const [stateState, setState] = React.useState(state);
@@ -152,7 +181,7 @@ export function AddressSelector(props: AddressSelectorProps) {
152
181
  if (regionState == null) setStates([]);
153
182
  else
154
183
  api.states(regionState).then((items) => {
155
- if (items == null) return;
184
+ if (items == null || !isMounted.current) return;
156
185
  setStates(items);
157
186
  });
158
187
  }, [regionState]);
@@ -160,7 +189,7 @@ export function AddressSelector(props: AddressSelectorProps) {
160
189
  if (stateState == null) setCities([]);
161
190
  else
162
191
  api.cities(stateState).then((items) => {
163
- if (items == null) return;
192
+ if (items == null || !isMounted.current) return;
164
193
  setCities(items);
165
194
  });
166
195
  }, [stateState]);
@@ -168,21 +197,24 @@ export function AddressSelector(props: AddressSelectorProps) {
168
197
  if (cityState == null) setDistricts([]);
169
198
  else
170
199
  api.districts(cityState).then((items) => {
171
- if (items == null) return;
200
+ if (items == null || !isMounted.current) return;
172
201
  setDistricts(items);
173
202
  });
174
203
  }, [cityState]);
175
204
 
176
- // Field size
177
- const fieldSize = hideRegion ? 4 : 3;
178
-
179
205
  // Handle field change
180
- const handleChange = (field: AddressField, value: unknown) => {
206
+ const handleChange = <F extends AddressField>(event: AddressFieldType<F>) => {
207
+ if (!isMounted.current) return;
208
+
209
+ if (onChange) onChange(event);
210
+
211
+ const [field, data] = event;
212
+
181
213
  if (field === AddressField.Region) {
182
- if (value == null) {
214
+ if (data == null) {
183
215
  setRegion(undefined);
184
216
  } else {
185
- setRegion(value as string);
217
+ setRegion(data.id);
186
218
  }
187
219
  setState(undefined);
188
220
  setCity(undefined);
@@ -192,10 +224,10 @@ export function AddressSelector(props: AddressSelectorProps) {
192
224
  }
193
225
 
194
226
  if (field === AddressField.State) {
195
- if (value == null) {
227
+ if (data == null) {
196
228
  setState(undefined);
197
229
  } else {
198
- setState(value as string);
230
+ setState(data.id);
199
231
  }
200
232
  setCity(undefined);
201
233
  setDistrict(undefined);
@@ -204,29 +236,23 @@ export function AddressSelector(props: AddressSelectorProps) {
204
236
  }
205
237
 
206
238
  if (field === AddressField.City) {
207
- if (value == null) {
239
+ if (data == null) {
208
240
  setCity(undefined);
209
- } else if (typeof value === "number") {
210
- setCity(value);
211
241
  } else {
212
- setCity(parseInt(`${value}`));
242
+ setCity(data.id);
213
243
  }
214
244
  setDistrict(undefined);
215
245
 
216
246
  return;
217
247
  }
218
248
 
219
- if (value == null) {
249
+ if (data == null) {
220
250
  setDistrict(undefined);
221
- } else if (typeof value === "number") {
222
- setDistrict(value);
223
251
  } else {
224
- setDistrict(parseInt(`${value}`));
252
+ setDistrict(data.id);
225
253
  }
226
254
  };
227
255
 
228
- console.log(regionState, stateState, cityState, districtState);
229
-
230
256
  // Layout
231
257
  return (
232
258
  <React.Fragment>
@@ -241,7 +267,7 @@ export function AddressSelector(props: AddressSelectorProps) {
241
267
  </Grid>
242
268
  )}
243
269
  {!hideRegion && (
244
- <Grid item xs={12} md={6} lg={fieldSize}>
270
+ <Grid item {...breakPoints}>
245
271
  <Tiplist<AddressRegionDb>
246
272
  label={regionLabel}
247
273
  name={AddressField.Region}
@@ -255,12 +281,12 @@ export function AddressSelector(props: AddressSelectorProps) {
255
281
  inputError={error}
256
282
  inputHelperText={helperText}
257
283
  onChange={(_event, value) =>
258
- handleChange(AddressField.Region, value?.id)
284
+ handleChange([AddressField.Region, value])
259
285
  }
260
286
  />
261
287
  </Grid>
262
288
  )}
263
- <Grid item xs={12} md={6} lg={fieldSize}>
289
+ <Grid item {...breakPoints}>
264
290
  <ComboBox<AddressState>
265
291
  name={AddressField.State}
266
292
  label={stateLabel}
@@ -272,11 +298,11 @@ export function AddressSelector(props: AddressSelectorProps) {
272
298
  inputError={hideRegion ? error : undefined}
273
299
  inputHelperText={hideRegion ? helperText : undefined}
274
300
  onChange={(_event, value) =>
275
- handleChange(AddressField.State, value?.id)
301
+ handleChange([AddressField.State, value])
276
302
  }
277
303
  />
278
304
  </Grid>
279
- <Grid item xs={12} md={6} lg={fieldSize}>
305
+ <Grid item {...breakPoints}>
280
306
  <ComboBox<AddressCity>
281
307
  name={AddressField.City}
282
308
  label={cityLabel}
@@ -284,12 +310,10 @@ export function AddressSelector(props: AddressSelectorProps) {
284
310
  fullWidth
285
311
  idValue={cityState}
286
312
  options={cities}
287
- onChange={(_event, value) =>
288
- handleChange(AddressField.City, value?.id)
289
- }
313
+ onChange={(_event, value) => handleChange([AddressField.City, value])}
290
314
  />
291
315
  </Grid>
292
- <Grid item xs={12} md={6} lg={fieldSize}>
316
+ <Grid item {...breakPoints}>
293
317
  <ComboBox<AddressDistrict>
294
318
  name={AddressField.District}
295
319
  label={districtLabel}
@@ -298,7 +322,7 @@ export function AddressSelector(props: AddressSelectorProps) {
298
322
  idValue={districtState}
299
323
  options={districts}
300
324
  onChange={(_event, value) =>
301
- handleChange(AddressField.District, value?.id)
325
+ handleChange([AddressField.District, value])
302
326
  }
303
327
  />
304
328
  </Grid>
package/src/ComboBox.tsx CHANGED
@@ -64,7 +64,7 @@ export function ComboBox<
64
64
  L extends DataTypes.Keys<T, string> = LabelDefaultType<T>
65
65
  >(props: ComboBoxProps<T, D, L>) {
66
66
  // Labels
67
- const labels = globalApp?.getLabels("noOptions", "loading");
67
+ const labels = globalApp?.getLabels("noOptions", "loading", "open");
68
68
 
69
69
  // Destruct
70
70
  const {
@@ -96,6 +96,7 @@ export function ComboBox<
96
96
  sx = { minWidth: "150px" },
97
97
  noOptionsText = labels?.noOptions,
98
98
  loadingText = labels?.loading,
99
+ openText = labels?.open,
99
100
  ...rest
100
101
  } = props;
101
102
 
@@ -110,7 +111,15 @@ export function ComboBox<
110
111
  // [options] will cause infinite loop
111
112
  const propertyWay = loadData == null;
112
113
  React.useEffect(() => {
113
- if (propertyWay && options != null) setOptions(options);
114
+ if (propertyWay && options != null) {
115
+ setOptions(options);
116
+ if (
117
+ stateValue != null &&
118
+ !options.some((option) => option[idField] === stateValue[idField])
119
+ ) {
120
+ setStateValue(null);
121
+ }
122
+ }
114
123
  }, [options, propertyWay]);
115
124
 
116
125
  // Local default value
@@ -254,6 +263,7 @@ export function ComboBox<
254
263
  options={localOptions}
255
264
  noOptionsText={noOptionsText}
256
265
  loadingText={loadingText}
266
+ openText={openText}
257
267
  {...rest}
258
268
  />
259
269
  </div>
@@ -139,7 +139,22 @@ export function ComboBoxMultiple<
139
139
  // [options] will cause infinite loop
140
140
  const propertyWay = loadData == null;
141
141
  React.useEffect(() => {
142
- if (propertyWay && options != null) setOptions(options);
142
+ if (propertyWay && options != null) {
143
+ setOptions(options);
144
+
145
+ if (stateValue != null) {
146
+ if (Array.isArray(stateValue)) {
147
+ const newState = stateValue.filter((item) =>
148
+ options.some((option) => option[idField] === item[idField])
149
+ );
150
+ setStateValue(newState);
151
+ } else if (
152
+ !options.some((option) => option[idField] === stateValue[idField])
153
+ ) {
154
+ setStateValue(null);
155
+ }
156
+ }
157
+ }
143
158
  }, [options, propertyWay]);
144
159
 
145
160
  // Local default value
@@ -1,5 +1,10 @@
1
1
  import { DataTypes, IdDefaultType, LabelDefaultType } from "@etsoo/shared";
2
- import { FormLabel, Grid, SelectChangeEvent } from "@mui/material";
2
+ import {
3
+ FormLabel,
4
+ Grid,
5
+ RegularBreakpoints,
6
+ SelectChangeEvent
7
+ } from "@mui/material";
3
8
  import React from "react";
4
9
  import { SelectEx } from "./SelectEx";
5
10
 
@@ -11,6 +16,11 @@ export type HiSelectorProps<
11
16
  D extends DataTypes.Keys<T> = IdDefaultType<T>,
12
17
  L extends DataTypes.Keys<T, string> = LabelDefaultType<T>
13
18
  > = {
19
+ /**
20
+ * Break points
21
+ */
22
+ breakPoints?: RegularBreakpoints;
23
+
14
24
  /**
15
25
  * Id field
16
26
  */
@@ -89,6 +99,7 @@ export function HiSelector<
89
99
  >(props: HiSelectorProps<T, D, L>) {
90
100
  // Destruct
91
101
  const {
102
+ breakPoints = { xs: 6, md: 4, lg: 3 },
92
103
  idField = "id" as D,
93
104
  error,
94
105
  helperText,
@@ -153,7 +164,7 @@ export function HiSelector<
153
164
  </Grid>
154
165
  )}
155
166
  <input type="hidden" name={name} value={`${currentValue ?? ""}`} />
156
- <Grid item xs={6} md={4} lg={3}>
167
+ <Grid item {...breakPoints}>
157
168
  <SelectEx<T, D, L>
158
169
  idField={idField}
159
170
  labelField={labelField}
@@ -170,7 +181,7 @@ export function HiSelector<
170
181
  />
171
182
  </Grid>
172
183
  {localValues[0] != null && (
173
- <Grid item xs={6} md={4} lg={3}>
184
+ <Grid item {...breakPoints}>
174
185
  <SelectEx<T, D, L>
175
186
  key={`${localValues[0]}`}
176
187
  idField={idField}
@@ -186,7 +197,7 @@ export function HiSelector<
186
197
  </Grid>
187
198
  )}
188
199
  {localValues[1] != null && (
189
- <Grid item xs={6} md={4} lg={3}>
200
+ <Grid item {...breakPoints}>
190
201
  <SelectEx<T, D, L>
191
202
  key={`${localValues[1]}`}
192
203
  idField={idField}
@@ -202,7 +213,7 @@ export function HiSelector<
202
213
  </Grid>
203
214
  )}
204
215
  {localValues[2] != null && (
205
- <Grid item xs={6} md={4} lg={3}>
216
+ <Grid item {...breakPoints}>
206
217
  <SelectEx<T, D, L>
207
218
  key={`${localValues[2]}`}
208
219
  idField={idField}
@@ -3,7 +3,8 @@ import {
3
3
  AutocompleteChangeReason,
4
4
  AutocompleteValue,
5
5
  FormLabel,
6
- Grid
6
+ Grid,
7
+ RegularBreakpoints
7
8
  } from "@mui/material";
8
9
  import React from "react";
9
10
  import { Tiplist } from "./Tiplist";
@@ -15,6 +16,11 @@ export type HiSelectorTLProps<
15
16
  T extends object,
16
17
  D extends DataTypes.Keys<T> = IdDefaultType<T>
17
18
  > = {
19
+ /**
20
+ * Break points
21
+ */
22
+ breakPoints?: RegularBreakpoints;
23
+
18
24
  /**
19
25
  * Id field
20
26
  */
@@ -91,6 +97,7 @@ export function HiSelectorTL<
91
97
  >(props: HiSelectorTLProps<T, D>) {
92
98
  // Destruct
93
99
  const {
100
+ breakPoints = { xs: 6, md: 4, lg: 3 },
94
101
  idField = "id" as D,
95
102
  error,
96
103
  helperText,
@@ -153,7 +160,7 @@ export function HiSelectorTL<
153
160
  </Grid>
154
161
  )}
155
162
  <input type="hidden" name={name} value={`${currentValue ?? ""}`} />
156
- <Grid item xs={6} md={4} lg={3}>
163
+ <Grid item {...breakPoints}>
157
164
  <Tiplist<T, D>
158
165
  idField={idField}
159
166
  label="1"
@@ -169,7 +176,7 @@ export function HiSelectorTL<
169
176
  />
170
177
  </Grid>
171
178
  {localValues[0] != null && (
172
- <Grid item xs={6} md={4} lg={3}>
179
+ <Grid item {...breakPoints}>
173
180
  <Tiplist<T, D>
174
181
  key={`${localValues[0]}`}
175
182
  label="2"
@@ -188,7 +195,7 @@ export function HiSelectorTL<
188
195
  </Grid>
189
196
  )}
190
197
  {localValues[1] != null && (
191
- <Grid item xs={6} md={4} lg={3}>
198
+ <Grid item {...breakPoints}>
192
199
  <Tiplist<T, D>
193
200
  key={`${localValues[1]}`}
194
201
  label="3"
@@ -207,7 +214,7 @@ export function HiSelectorTL<
207
214
  </Grid>
208
215
  )}
209
216
  {localValues[2] != null && (
210
- <Grid item xs={6} md={4} lg={3}>
217
+ <Grid item {...breakPoints}>
211
218
  <Tiplist<T, D>
212
219
  key={`${localValues[2]}`}
213
220
  label="4"
package/src/Tiplist.tsx CHANGED
@@ -47,8 +47,12 @@ export function Tiplist<
47
47
  D extends DataTypes.Keys<T> = IdDefaultType<T>
48
48
  >(props: TiplistProps<T, D>) {
49
49
  // Labels
50
- const { noOptions, loading, more } =
51
- globalApp?.getLabels("noOptions", "loading", "more") ?? {};
50
+ const {
51
+ noOptions,
52
+ loading,
53
+ more,
54
+ open: openDefault
55
+ } = globalApp?.getLabels("noOptions", "loading", "more", "open") ?? {};
52
56
 
53
57
  // Destruct
54
58
  const {
@@ -74,6 +78,7 @@ export function Tiplist<
74
78
  sx = { minWidth: "180px" },
75
79
  noOptionsText = noOptions,
76
80
  loadingText = loading,
81
+ openText = openDefault,
77
82
  getOptionLabel,
78
83
  getOptionDisabled,
79
84
  ...rest
@@ -313,6 +318,7 @@ export function Tiplist<
313
318
  }
314
319
  noOptionsText={noOptionsText}
315
320
  loadingText={loadingText}
321
+ openText={openText}
316
322
  getOptionDisabled={(item) => {
317
323
  if (item[idField] === "n/a") return true;
318
324
  return getOptionDisabled ? getOptionDisabled(item) : false;