@etsoo/materialui 1.1.45 → 1.1.47

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
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
@@ -10,6 +11,7 @@ export declare enum AddressField {
10
11
  District = "district"
11
12
  }
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];
14
+ type AddressFavorType<F extends AddressField> = F extends AddressField.Region | AddressField.State ? string : number;
13
15
  /**
14
16
  * Address selector props
15
17
  */
@@ -18,6 +20,10 @@ export type AddressSelectorProps = {
18
20
  * Address API
19
21
  */
20
22
  api: AddressApi;
23
+ /**
24
+ * Break points
25
+ */
26
+ breakPoints?: RegularBreakpoints;
21
27
  /**
22
28
  * City
23
29
  */
@@ -38,6 +44,12 @@ export type AddressSelectorProps = {
38
44
  * Error
39
45
  */
40
46
  error?: boolean;
47
+ /**
48
+ * Get favored ids
49
+ * @param field Field
50
+ * @returns Result
51
+ */
52
+ favoredIds?: <F extends AddressField>(field: F) => AddressFavorType<F>[];
41
53
  /**
42
54
  * The helper text content.
43
55
  */
@@ -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, onChange, region, regionLabel = regionDefault, required, search, state, stateLabel = stateDefault } = props;
25
+ const { api, city, cityLabel = cityDefault, district, districtLabel = districtDefault, error, favoredIds, 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);
@@ -39,8 +43,10 @@ export function AddressSelector(props) {
39
43
  if (regionState == null)
40
44
  setStates([]);
41
45
  else
42
- api.states(regionState).then((items) => {
43
- if (items == null)
46
+ api
47
+ .states(regionState, favoredIds == null ? undefined : favoredIds(AddressField.State))
48
+ .then((items) => {
49
+ if (items == null || !isMounted.current)
44
50
  return;
45
51
  setStates(items);
46
52
  });
@@ -49,8 +55,10 @@ export function AddressSelector(props) {
49
55
  if (stateState == null)
50
56
  setCities([]);
51
57
  else
52
- api.cities(stateState).then((items) => {
53
- if (items == null)
58
+ api
59
+ .cities(stateState, favoredIds == null ? undefined : favoredIds(AddressField.City))
60
+ .then((items) => {
61
+ if (items == null || !isMounted.current)
54
62
  return;
55
63
  setCities(items);
56
64
  });
@@ -59,16 +67,18 @@ export function AddressSelector(props) {
59
67
  if (cityState == null)
60
68
  setDistricts([]);
61
69
  else
62
- api.districts(cityState).then((items) => {
63
- if (items == null)
70
+ api
71
+ .districts(cityState, favoredIds == null ? undefined : favoredIds(AddressField.District))
72
+ .then((items) => {
73
+ if (items == null || !isMounted.current)
64
74
  return;
65
75
  setDistricts(items);
66
76
  });
67
77
  }, [cityState]);
68
- // Field size
69
- const fieldSize = hideRegion ? 4 : 3;
70
78
  // Handle field change
71
79
  const handleChange = (event) => {
80
+ if (!isMounted.current)
81
+ return;
72
82
  if (onChange)
73
83
  onChange(event);
74
84
  const [field, data] = event;
@@ -116,12 +126,19 @@ export function AddressSelector(props) {
116
126
  return (React.createElement(React.Fragment, null,
117
127
  label && (React.createElement(Grid, { item: true, xs: 12 },
118
128
  React.createElement(FormLabel, { required: required, sx: { fontSize: (theme) => theme.typography.caption } }, label))),
119
- !hideRegion && (React.createElement(Grid, { item: true, xs: 12, md: 6, lg: fieldSize },
120
- 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]) }))),
121
- React.createElement(Grid, { item: true, xs: 12, md: 6, lg: fieldSize },
122
- 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]) })),
123
- React.createElement(Grid, { item: true, xs: 12, md: 6, lg: fieldSize },
129
+ !hideRegion && (React.createElement(Grid, { item: true, ...breakPoints },
130
+ React.createElement(Tiplist, { label: regionLabel, name: AddressField.Region, search: search, fullWidth: true, idValue: regionState, loadData: (keyword, id, items) => api.getRegions({
131
+ keyword,
132
+ id,
133
+ items,
134
+ favoredIds: favoredIds == null
135
+ ? undefined
136
+ : favoredIds(AddressField.Region)
137
+ }), inputRequired: required, inputError: error, inputHelperText: helperText, onChange: (_event, value) => handleChange([AddressField.Region, value]) }))),
138
+ React.createElement(Grid, { item: true, ...breakPoints },
139
+ React.createElement(ComboBox, { name: AddressField.State, label: stateLabel, search: search, fullWidth: true, idValue: stateState, options: states, inputRequired: required, inputError: hideRegion ? error : undefined, inputHelperText: hideRegion ? helperText : undefined, onChange: (_event, value) => handleChange([AddressField.State, value]) })),
140
+ React.createElement(Grid, { item: true, ...breakPoints },
124
141
  React.createElement(ComboBox, { name: AddressField.City, label: cityLabel, search: search, fullWidth: true, idValue: cityState, options: cities, onChange: (_event, value) => handleChange([AddressField.City, value]) })),
125
- React.createElement(Grid, { item: true, xs: 12, md: 6, lg: fieldSize },
142
+ React.createElement(Grid, { item: true, ...breakPoints },
126
143
  React.createElement(ComboBox, { name: AddressField.District, label: districtLabel, search: search, fullWidth: true, idValue: districtState, options: districts, onChange: (_event, value) => handleChange([AddressField.District, value]) }))));
127
144
  }
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
@@ -115,5 +115,5 @@ export function ComboBox(props) {
115
115
  // Custom
116
116
  if (onChange != null)
117
117
  onChange(event, value, reason, details);
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, ...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 })));
119
119
  }
@@ -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.45",
3
+ "version": "1.1.47",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,13 +50,13 @@
50
50
  "@emotion/css": "^11.10.6",
51
51
  "@emotion/react": "^11.10.6",
52
52
  "@emotion/styled": "^11.10.6",
53
- "@etsoo/appscript": "^1.3.70",
53
+ "@etsoo/appscript": "^1.3.71",
54
54
  "@etsoo/notificationbase": "^1.1.24",
55
- "@etsoo/react": "^1.6.49",
56
- "@etsoo/shared": "^1.1.89",
57
- "@mui/icons-material": "^5.11.9",
58
- "@mui/material": "^5.11.10",
59
- "@mui/x-data-grid": "^6.0.0-beta.5",
55
+ "@etsoo/react": "^1.6.50",
56
+ "@etsoo/shared": "^1.1.90",
57
+ "@mui/icons-material": "^5.11.11",
58
+ "@mui/material": "^5.11.12",
59
+ "@mui/x-data-grid": "^6.0.1",
60
60
  "@types/pica": "^9.0.1",
61
61
  "@types/pulltorefreshjs": "^0.1.5",
62
62
  "@types/react": "^18.0.28",
@@ -71,7 +71,7 @@
71
71
  "react-dom": "^18.2.0",
72
72
  "react-draggable": "^4.4.5",
73
73
  "react-imask": "^6.4.3",
74
- "react-router-dom": "^6.8.1",
74
+ "react-router-dom": "^6.8.2",
75
75
  "react-window": "^1.8.8"
76
76
  },
77
77
  "devDependencies": {
@@ -85,10 +85,10 @@
85
85
  "@testing-library/jest-dom": "^5.16.5",
86
86
  "@testing-library/react": "^14.0.0",
87
87
  "@types/jest": "^29.4.0",
88
- "@typescript-eslint/eslint-plugin": "^5.53.0",
89
- "@typescript-eslint/parser": "^5.53.0",
90
- "jest": "^29.4.3",
91
- "jest-environment-jsdom": "^29.4.3",
88
+ "@typescript-eslint/eslint-plugin": "^5.54.1",
89
+ "@typescript-eslint/parser": "^5.54.1",
90
+ "jest": "^29.5.0",
91
+ "jest-environment-jsdom": "^29.5.0",
92
92
  "typescript": "^4.9.5"
93
93
  }
94
94
  }
@@ -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";
@@ -29,6 +29,12 @@ type AddressFieldType<F extends AddressField> = F extends AddressField.Region
29
29
  ? [F, AddressCity | null]
30
30
  : [F, AddressDistrict | null];
31
31
 
32
+ type AddressFavorType<F extends AddressField> = F extends
33
+ | AddressField.Region
34
+ | AddressField.State
35
+ ? string
36
+ : number;
37
+
32
38
  /**
33
39
  * Address selector props
34
40
  */
@@ -38,6 +44,11 @@ export type AddressSelectorProps = {
38
44
  */
39
45
  api: AddressApi;
40
46
 
47
+ /**
48
+ * Break points
49
+ */
50
+ breakPoints?: RegularBreakpoints;
51
+
41
52
  /**
42
53
  * City
43
54
  */
@@ -63,6 +74,13 @@ export type AddressSelectorProps = {
63
74
  */
64
75
  error?: boolean;
65
76
 
77
+ /**
78
+ * Get favored ids
79
+ * @param field Field
80
+ * @returns Result
81
+ */
82
+ favoredIds?: <F extends AddressField>(field: F) => AddressFavorType<F>[];
83
+
66
84
  /**
67
85
  * The helper text content.
68
86
  */
@@ -136,6 +154,7 @@ export function AddressSelector(props: AddressSelectorProps) {
136
154
  district,
137
155
  districtLabel = districtDefault,
138
156
  error,
157
+ favoredIds,
139
158
  helperText,
140
159
  hideRegion,
141
160
  label,
@@ -145,9 +164,18 @@ export function AddressSelector(props: AddressSelectorProps) {
145
164
  required,
146
165
  search,
147
166
  state,
148
- stateLabel = stateDefault
167
+ stateLabel = stateDefault,
168
+ breakPoints = { xs: 12, md: 6, lg: hideRegion ? 4 : 3 }
149
169
  } = props;
150
170
 
171
+ const isMounted = React.useRef(true);
172
+ React.useEffect(
173
+ () => () => {
174
+ isMounted.current = false;
175
+ },
176
+ []
177
+ );
178
+
151
179
  // States
152
180
  const [regionState, setRegion] = React.useState(region);
153
181
  const [stateState, setState] = React.useState(state);
@@ -166,33 +194,47 @@ export function AddressSelector(props: AddressSelectorProps) {
166
194
  React.useEffect(() => {
167
195
  if (regionState == null) setStates([]);
168
196
  else
169
- api.states(regionState).then((items) => {
170
- if (items == null) return;
171
- setStates(items);
172
- });
197
+ api
198
+ .states(
199
+ regionState,
200
+ favoredIds == null ? undefined : favoredIds(AddressField.State)
201
+ )
202
+ .then((items) => {
203
+ if (items == null || !isMounted.current) return;
204
+ setStates(items);
205
+ });
173
206
  }, [regionState]);
174
207
  React.useEffect(() => {
175
208
  if (stateState == null) setCities([]);
176
209
  else
177
- api.cities(stateState).then((items) => {
178
- if (items == null) return;
179
- setCities(items);
180
- });
210
+ api
211
+ .cities(
212
+ stateState,
213
+ favoredIds == null ? undefined : favoredIds(AddressField.City)
214
+ )
215
+ .then((items) => {
216
+ if (items == null || !isMounted.current) return;
217
+ setCities(items);
218
+ });
181
219
  }, [stateState]);
182
220
  React.useEffect(() => {
183
221
  if (cityState == null) setDistricts([]);
184
222
  else
185
- api.districts(cityState).then((items) => {
186
- if (items == null) return;
187
- setDistricts(items);
188
- });
223
+ api
224
+ .districts(
225
+ cityState,
226
+ favoredIds == null ? undefined : favoredIds(AddressField.District)
227
+ )
228
+ .then((items) => {
229
+ if (items == null || !isMounted.current) return;
230
+ setDistricts(items);
231
+ });
189
232
  }, [cityState]);
190
233
 
191
- // Field size
192
- const fieldSize = hideRegion ? 4 : 3;
193
-
194
234
  // Handle field change
195
235
  const handleChange = <F extends AddressField>(event: AddressFieldType<F>) => {
236
+ if (!isMounted.current) return;
237
+
196
238
  if (onChange) onChange(event);
197
239
 
198
240
  const [field, data] = event;
@@ -254,7 +296,7 @@ export function AddressSelector(props: AddressSelectorProps) {
254
296
  </Grid>
255
297
  )}
256
298
  {!hideRegion && (
257
- <Grid item xs={12} md={6} lg={fieldSize}>
299
+ <Grid item {...breakPoints}>
258
300
  <Tiplist<AddressRegionDb>
259
301
  label={regionLabel}
260
302
  name={AddressField.Region}
@@ -262,7 +304,15 @@ export function AddressSelector(props: AddressSelectorProps) {
262
304
  fullWidth
263
305
  idValue={regionState}
264
306
  loadData={(keyword, id, items) =>
265
- api.getRegions({ keyword, id, items })
307
+ api.getRegions({
308
+ keyword,
309
+ id,
310
+ items,
311
+ favoredIds:
312
+ favoredIds == null
313
+ ? undefined
314
+ : favoredIds(AddressField.Region)
315
+ })
266
316
  }
267
317
  inputRequired={required}
268
318
  inputError={error}
@@ -273,7 +323,7 @@ export function AddressSelector(props: AddressSelectorProps) {
273
323
  />
274
324
  </Grid>
275
325
  )}
276
- <Grid item xs={12} md={6} lg={fieldSize}>
326
+ <Grid item {...breakPoints}>
277
327
  <ComboBox<AddressState>
278
328
  name={AddressField.State}
279
329
  label={stateLabel}
@@ -281,7 +331,7 @@ export function AddressSelector(props: AddressSelectorProps) {
281
331
  fullWidth
282
332
  idValue={stateState}
283
333
  options={states}
284
- inputRequired={hideRegion ? required : undefined}
334
+ inputRequired={required}
285
335
  inputError={hideRegion ? error : undefined}
286
336
  inputHelperText={hideRegion ? helperText : undefined}
287
337
  onChange={(_event, value) =>
@@ -289,7 +339,7 @@ export function AddressSelector(props: AddressSelectorProps) {
289
339
  }
290
340
  />
291
341
  </Grid>
292
- <Grid item xs={12} md={6} lg={fieldSize}>
342
+ <Grid item {...breakPoints}>
293
343
  <ComboBox<AddressCity>
294
344
  name={AddressField.City}
295
345
  label={cityLabel}
@@ -300,7 +350,7 @@ export function AddressSelector(props: AddressSelectorProps) {
300
350
  onChange={(_event, value) => handleChange([AddressField.City, value])}
301
351
  />
302
352
  </Grid>
303
- <Grid item xs={12} md={6} lg={fieldSize}>
353
+ <Grid item {...breakPoints}>
304
354
  <ComboBox<AddressDistrict>
305
355
  name={AddressField.District}
306
356
  label={districtLabel}
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
 
@@ -262,6 +263,7 @@ export function ComboBox<
262
263
  options={localOptions}
263
264
  noOptionsText={noOptionsText}
264
265
  loadingText={loadingText}
266
+ openText={openText}
265
267
  {...rest}
266
268
  />
267
269
  </div>
@@ -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;