@etsoo/materialui 1.1.29 → 1.1.31
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.
- package/lib/CountdownButton.d.ts +1 -1
- package/lib/HiSelector.d.ts +7 -3
- package/lib/HiSelector.js +11 -12
- package/lib/HiSelectorTL.d.ts +58 -0
- package/lib/HiSelectorTL.js +49 -0
- package/lib/InputField.d.ts +4 -4
- package/lib/TextFieldEx.d.ts +4 -4
- package/lib/Tiplist.js +2 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/package.json +7 -7
- package/src/HiSelector.tsx +204 -205
- package/src/HiSelectorTL.tsx +228 -0
- package/src/Tiplist.tsx +1 -0
- package/src/index.ts +1 -0
package/lib/CountdownButton.d.ts
CHANGED
|
@@ -20,4 +20,4 @@ export type CountdownButtonProps = Omit<ButtonProps, 'endIcon' | 'disabled'> & {
|
|
|
20
20
|
* @param props Props
|
|
21
21
|
* @returns Button
|
|
22
22
|
*/
|
|
23
|
-
export declare const CountdownButton: React.ForwardRefExoticComponent<
|
|
23
|
+
export declare const CountdownButton: React.ForwardRefExoticComponent<Omit<CountdownButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
package/lib/HiSelector.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DataTypes, IdDefaultType, LabelDefaultType } from
|
|
2
|
-
import { SelectChangeEvent } from
|
|
3
|
-
import React from
|
|
1
|
+
import { DataTypes, IdDefaultType, LabelDefaultType } from "@etsoo/shared";
|
|
2
|
+
import { SelectChangeEvent } from "@mui/material";
|
|
3
|
+
import React from "react";
|
|
4
4
|
/**
|
|
5
5
|
* Hierarchy selector props
|
|
6
6
|
*/
|
|
@@ -49,6 +49,10 @@ export type HiSelectorProps<T extends object, D extends DataTypes.Keys<T> = IdDe
|
|
|
49
49
|
* Required
|
|
50
50
|
*/
|
|
51
51
|
required?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Search mode
|
|
54
|
+
*/
|
|
55
|
+
search?: boolean;
|
|
52
56
|
/**
|
|
53
57
|
* Values
|
|
54
58
|
*/
|
package/lib/HiSelector.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FormLabel, Grid } from
|
|
2
|
-
import React from
|
|
3
|
-
import { SelectEx } from
|
|
1
|
+
import { FormLabel, Grid } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { SelectEx } from "./SelectEx";
|
|
4
4
|
/**
|
|
5
5
|
* Hierarchy selector
|
|
6
6
|
* @param props Prop
|
|
@@ -8,7 +8,7 @@ import { SelectEx } from './SelectEx';
|
|
|
8
8
|
*/
|
|
9
9
|
export function HiSelector(props) {
|
|
10
10
|
// Destruct
|
|
11
|
-
const { idField =
|
|
11
|
+
const { idField = "id", error, helperText, name, label = name, 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)
|
|
@@ -16,7 +16,7 @@ export function HiSelector(props) {
|
|
|
16
16
|
};
|
|
17
17
|
const doChange = (event, index) => {
|
|
18
18
|
const value = event.target.value;
|
|
19
|
-
const itemValue = value ===
|
|
19
|
+
const itemValue = value === "" ? undefined : value;
|
|
20
20
|
updateValue(itemValue);
|
|
21
21
|
const newValues = [...localValues.slice(0, index)];
|
|
22
22
|
if (itemValue != null)
|
|
@@ -28,8 +28,7 @@ export function HiSelector(props) {
|
|
|
28
28
|
const doItemChange = (option, userAction) => {
|
|
29
29
|
if (onItemChange == null)
|
|
30
30
|
return;
|
|
31
|
-
if (!userAction &&
|
|
32
|
-
(option == null || option[idField] !== values.at(-1)))
|
|
31
|
+
if (!userAction && (option == null || option[idField] !== values.at(-1)))
|
|
33
32
|
return;
|
|
34
33
|
onItemChange(option, userAction);
|
|
35
34
|
};
|
|
@@ -43,13 +42,13 @@ export function HiSelector(props) {
|
|
|
43
42
|
return (React.createElement(React.Fragment, null,
|
|
44
43
|
React.createElement(Grid, { item: true, xs: 12 },
|
|
45
44
|
React.createElement(FormLabel, { required: required, sx: { fontSize: (theme) => theme.typography.caption } }, label),
|
|
46
|
-
React.createElement("input", { type: "hidden", name: name, value: `${currentValue !== null && currentValue !== void 0 ? currentValue :
|
|
45
|
+
React.createElement("input", { type: "hidden", name: name, value: `${currentValue !== null && currentValue !== void 0 ? currentValue : ""}` })),
|
|
47
46
|
React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
|
|
48
|
-
React.createElement(SelectEx, { idField: idField, labelField: labelField, name: "tab1", search:
|
|
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 })),
|
|
49
48
|
localValues[0] != null && (React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
|
|
50
|
-
React.createElement(SelectEx, { key: `${localValues[0]}`, idField: idField, labelField: labelField, name: "tab2", search:
|
|
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 }))),
|
|
51
50
|
localValues[1] != null && (React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
|
|
52
|
-
React.createElement(SelectEx, { key: `${localValues[1]}`, idField: idField, labelField: labelField, name: "tab3", search:
|
|
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 }))),
|
|
53
52
|
localValues[2] != null && (React.createElement(Grid, { item: true, xs: 6, md: 4, lg: 3 },
|
|
54
|
-
React.createElement(SelectEx, { key: `${localValues[2]}`, idField: idField, labelField: labelField, name: "tab4", search:
|
|
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 })))));
|
|
55
54
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { DataTypes, IdDefaultType } from "@etsoo/shared";
|
|
2
|
+
import { AutocompleteChangeReason } from "@mui/material";
|
|
3
|
+
import React from "react";
|
|
4
|
+
/**
|
|
5
|
+
* Hierarchy tiplist selector props
|
|
6
|
+
*/
|
|
7
|
+
export type HiSelectorTLProps<T extends object, D extends DataTypes.Keys<T> = IdDefaultType<T>> = {
|
|
8
|
+
/**
|
|
9
|
+
* Id field
|
|
10
|
+
*/
|
|
11
|
+
idField?: D;
|
|
12
|
+
/**
|
|
13
|
+
* Error
|
|
14
|
+
*/
|
|
15
|
+
error?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The helper text content.
|
|
18
|
+
*/
|
|
19
|
+
helperText?: React.ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* Name, also hidden input field name
|
|
22
|
+
*/
|
|
23
|
+
name: string;
|
|
24
|
+
/**
|
|
25
|
+
* Label
|
|
26
|
+
*/
|
|
27
|
+
label?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Load data callback
|
|
30
|
+
*/
|
|
31
|
+
loadData: (keyword: string | undefined, id: T[D] | undefined, maxItems: number, parent?: T[D]) => PromiseLike<T[] | null | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* On value change event
|
|
34
|
+
*/
|
|
35
|
+
onChange?: (value: unknown) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Item change callback
|
|
38
|
+
*/
|
|
39
|
+
onItemChange?: (event: React.SyntheticEvent, option: T | null, reason: AutocompleteChangeReason) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Required
|
|
42
|
+
*/
|
|
43
|
+
required?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Search mode
|
|
46
|
+
*/
|
|
47
|
+
search?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Values
|
|
50
|
+
*/
|
|
51
|
+
values?: T[D][];
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Hierarchy tiplist selector
|
|
55
|
+
* @param props Prop
|
|
56
|
+
* @returns Component
|
|
57
|
+
*/
|
|
58
|
+
export declare function HiSelectorTL<T extends object, D extends DataTypes.Keys<T> = IdDefaultType<T>>(props: HiSelectorTLProps<T, D>): JSX.Element;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { FormLabel, Grid } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Tiplist } from "./Tiplist";
|
|
4
|
+
/**
|
|
5
|
+
* Hierarchy tiplist selector
|
|
6
|
+
* @param props Prop
|
|
7
|
+
* @returns Component
|
|
8
|
+
*/
|
|
9
|
+
export function HiSelectorTL(props) {
|
|
10
|
+
// Destruct
|
|
11
|
+
const { idField = "id", error, helperText, name, label = name, loadData, onChange, onItemChange, required, search = false, values = [] } = props;
|
|
12
|
+
const [localValues, setValues] = React.useState(values);
|
|
13
|
+
const updateValue = (value) => {
|
|
14
|
+
if (onChange)
|
|
15
|
+
onChange(value);
|
|
16
|
+
};
|
|
17
|
+
const doChange = (index, event, value, reason) => {
|
|
18
|
+
if (onItemChange) {
|
|
19
|
+
onItemChange(event, value, reason);
|
|
20
|
+
if (event.isDefaultPrevented())
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const itemValue = value ? value[idField] : undefined;
|
|
24
|
+
updateValue(itemValue);
|
|
25
|
+
const newValues = [...localValues.slice(0, index)];
|
|
26
|
+
if (itemValue != null)
|
|
27
|
+
newValues.push(itemValue);
|
|
28
|
+
setValues(newValues);
|
|
29
|
+
};
|
|
30
|
+
React.useEffect(() => {
|
|
31
|
+
if (values.length > 0) {
|
|
32
|
+
setValues(values);
|
|
33
|
+
updateValue(values.at(-1));
|
|
34
|
+
}
|
|
35
|
+
}, [values.toString()]);
|
|
36
|
+
const currentValue = localValues.at(-1);
|
|
37
|
+
return (React.createElement(React.Fragment, null,
|
|
38
|
+
React.createElement(Grid, { item: true, xs: 12 },
|
|
39
|
+
React.createElement(FormLabel, { required: required, sx: { fontSize: (theme) => theme.typography.caption } }, label),
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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 },
|
|
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
|
+
}
|
package/lib/InputField.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type InputFieldProps = TextFieldProps & {
|
|
|
18
18
|
* @param props Props
|
|
19
19
|
* @returns Component
|
|
20
20
|
*/
|
|
21
|
-
export declare const InputField: React.ForwardRefExoticComponent<(
|
|
21
|
+
export declare const InputField: React.ForwardRefExoticComponent<(Omit<import("@mui/material").StandardTextFieldProps & {
|
|
22
22
|
/**
|
|
23
23
|
* Change delay (ms) to avoid repeatly dispatch onChange
|
|
24
24
|
*/
|
|
@@ -27,7 +27,7 @@ export declare const InputField: React.ForwardRefExoticComponent<(Pick<import("@
|
|
|
27
27
|
* Is the field read only?
|
|
28
28
|
*/
|
|
29
29
|
readOnly?: boolean | undefined;
|
|
30
|
-
}, "
|
|
30
|
+
}, "ref"> | Omit<import("@mui/material").FilledTextFieldProps & {
|
|
31
31
|
/**
|
|
32
32
|
* Change delay (ms) to avoid repeatly dispatch onChange
|
|
33
33
|
*/
|
|
@@ -36,7 +36,7 @@ export declare const InputField: React.ForwardRefExoticComponent<(Pick<import("@
|
|
|
36
36
|
* Is the field read only?
|
|
37
37
|
*/
|
|
38
38
|
readOnly?: boolean | undefined;
|
|
39
|
-
}, "
|
|
39
|
+
}, "ref"> | Omit<import("@mui/material").OutlinedTextFieldProps & {
|
|
40
40
|
/**
|
|
41
41
|
* Change delay (ms) to avoid repeatly dispatch onChange
|
|
42
42
|
*/
|
|
@@ -45,4 +45,4 @@ export declare const InputField: React.ForwardRefExoticComponent<(Pick<import("@
|
|
|
45
45
|
* Is the field read only?
|
|
46
46
|
*/
|
|
47
47
|
readOnly?: boolean | undefined;
|
|
48
|
-
}, "
|
|
48
|
+
}, "ref">) & React.RefAttributes<HTMLDivElement>>;
|
package/lib/TextFieldEx.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface TextFieldExMethods {
|
|
|
41
41
|
*/
|
|
42
42
|
setError(error: React.ReactNode): void;
|
|
43
43
|
}
|
|
44
|
-
export declare const TextFieldEx: React.ForwardRefExoticComponent<(
|
|
44
|
+
export declare const TextFieldEx: React.ForwardRefExoticComponent<(Omit<import("@mui/material").StandardTextFieldProps & {
|
|
45
45
|
/**
|
|
46
46
|
* Change delay (ms) to avoid repeatly dispatch onChange
|
|
47
47
|
*/
|
|
@@ -68,7 +68,7 @@ export declare const TextFieldEx: React.ForwardRefExoticComponent<(Pick<import("
|
|
|
68
68
|
* Show password button
|
|
69
69
|
*/
|
|
70
70
|
showPassword?: boolean | undefined;
|
|
71
|
-
}, "
|
|
71
|
+
}, "ref"> | Omit<import("@mui/material").FilledTextFieldProps & {
|
|
72
72
|
/**
|
|
73
73
|
* Change delay (ms) to avoid repeatly dispatch onChange
|
|
74
74
|
*/
|
|
@@ -95,7 +95,7 @@ export declare const TextFieldEx: React.ForwardRefExoticComponent<(Pick<import("
|
|
|
95
95
|
* Show password button
|
|
96
96
|
*/
|
|
97
97
|
showPassword?: boolean | undefined;
|
|
98
|
-
}, "
|
|
98
|
+
}, "ref"> | Omit<import("@mui/material").OutlinedTextFieldProps & {
|
|
99
99
|
/**
|
|
100
100
|
* Change delay (ms) to avoid repeatly dispatch onChange
|
|
101
101
|
*/
|
|
@@ -122,4 +122,4 @@ export declare const TextFieldEx: React.ForwardRefExoticComponent<(Pick<import("
|
|
|
122
122
|
* Show password button
|
|
123
123
|
*/
|
|
124
124
|
showPassword?: boolean | undefined;
|
|
125
|
-
}, "
|
|
125
|
+
}, "ref">) & React.RefAttributes<TextFieldExMethods>>;
|
package/lib/Tiplist.js
CHANGED
|
@@ -161,6 +161,8 @@ export function Tiplist(props) {
|
|
|
161
161
|
return getOptionDisabled ? getOptionDisabled(item) : false;
|
|
162
162
|
}, getOptionLabel: (item) => {
|
|
163
163
|
var _a;
|
|
164
|
+
if (typeof item !== "object")
|
|
165
|
+
return `${item}`;
|
|
164
166
|
if (item[idField] === "n/a")
|
|
165
167
|
return ((_a = labels.more) !== null && _a !== void 0 ? _a : "More") + "...";
|
|
166
168
|
return getOptionLabel
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.31",
|
|
4
4
|
"description": "TypeScript Material-UI Implementation",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -50,15 +50,15 @@
|
|
|
50
50
|
"@emotion/css": "^11.10.5",
|
|
51
51
|
"@emotion/react": "^11.10.5",
|
|
52
52
|
"@emotion/styled": "^11.10.5",
|
|
53
|
-
"@etsoo/appscript": "^1.3.
|
|
53
|
+
"@etsoo/appscript": "^1.3.63",
|
|
54
54
|
"@etsoo/notificationbase": "^1.1.23",
|
|
55
|
-
"@etsoo/react": "^1.6.
|
|
55
|
+
"@etsoo/react": "^1.6.47",
|
|
56
56
|
"@etsoo/shared": "^1.1.88",
|
|
57
57
|
"@mui/icons-material": "^5.11.0",
|
|
58
|
-
"@mui/material": "^5.11.
|
|
58
|
+
"@mui/material": "^5.11.8",
|
|
59
59
|
"@types/pica": "^9.0.1",
|
|
60
60
|
"@types/pulltorefreshjs": "^0.1.5",
|
|
61
|
-
"@types/react": "^18.0.
|
|
61
|
+
"@types/react": "^18.0.28",
|
|
62
62
|
"@types/react-avatar-editor": "^13.0.0",
|
|
63
63
|
"@types/react-dom": "^18.0.10",
|
|
64
64
|
"@types/react-input-mask": "^3.0.2",
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
"@types/jest": "^29.4.0",
|
|
87
87
|
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
|
88
88
|
"@typescript-eslint/parser": "^5.51.0",
|
|
89
|
-
"jest": "^29.4.
|
|
90
|
-
"jest-environment-jsdom": "^29.4.
|
|
89
|
+
"jest": "^29.4.2",
|
|
90
|
+
"jest-environment-jsdom": "^29.4.2",
|
|
91
91
|
"typescript": "^4.9.5"
|
|
92
92
|
}
|
|
93
93
|
}
|
package/src/HiSelector.tsx
CHANGED
|
@@ -1,75 +1,80 @@
|
|
|
1
|
-
import { DataTypes, IdDefaultType, LabelDefaultType } from
|
|
2
|
-
import { FormLabel, Grid, SelectChangeEvent } from
|
|
3
|
-
import React from
|
|
4
|
-
import { SelectEx } from
|
|
1
|
+
import { DataTypes, IdDefaultType, LabelDefaultType } from "@etsoo/shared";
|
|
2
|
+
import { FormLabel, Grid, SelectChangeEvent } from "@mui/material";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { SelectEx } from "./SelectEx";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Hierarchy selector props
|
|
8
8
|
*/
|
|
9
9
|
export type HiSelectorProps<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
T extends object,
|
|
11
|
+
D extends DataTypes.Keys<T> = IdDefaultType<T>,
|
|
12
|
+
L extends DataTypes.Keys<T, string> = LabelDefaultType<T>
|
|
13
13
|
> = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Id field
|
|
16
|
+
*/
|
|
17
|
+
idField?: D;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Error
|
|
21
|
+
*/
|
|
22
|
+
error?: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The helper text content.
|
|
26
|
+
*/
|
|
27
|
+
helperText?: React.ReactNode;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Name, also hidden input field name
|
|
31
|
+
*/
|
|
32
|
+
name: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Label
|
|
36
|
+
*/
|
|
37
|
+
label?: string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Label field
|
|
41
|
+
*/
|
|
42
|
+
labelField?: L;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Load data callback
|
|
46
|
+
*/
|
|
47
|
+
loadData: (parent?: T[D]) => PromiseLike<T[] | null | undefined>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* On value change event
|
|
51
|
+
*/
|
|
52
|
+
onChange?: (value: unknown) => void;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* On select change event
|
|
56
|
+
*/
|
|
57
|
+
onSelectChange?: (e: SelectChangeEvent<unknown>) => void;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Item change callback
|
|
61
|
+
*/
|
|
62
|
+
onItemChange?: (option: T | undefined, userAction: boolean) => void;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Required
|
|
66
|
+
*/
|
|
67
|
+
required?: boolean;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Search mode
|
|
71
|
+
*/
|
|
72
|
+
search?: boolean;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Values
|
|
76
|
+
*/
|
|
77
|
+
values?: T[D][];
|
|
73
78
|
};
|
|
74
79
|
|
|
75
80
|
/**
|
|
@@ -78,144 +83,138 @@ export type HiSelectorProps<
|
|
|
78
83
|
* @returns Component
|
|
79
84
|
*/
|
|
80
85
|
export function HiSelector<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
T extends object,
|
|
87
|
+
D extends DataTypes.Keys<T> = IdDefaultType<T>,
|
|
88
|
+
L extends DataTypes.Keys<T, string> = LabelDefaultType<T>
|
|
84
89
|
>(props: HiSelectorProps<T, D, L>) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
onItemChange={doItemChange}
|
|
216
|
-
/>
|
|
217
|
-
</Grid>
|
|
218
|
-
)}
|
|
219
|
-
</React.Fragment>
|
|
220
|
-
);
|
|
90
|
+
// Destruct
|
|
91
|
+
const {
|
|
92
|
+
idField = "id" as D,
|
|
93
|
+
error,
|
|
94
|
+
helperText,
|
|
95
|
+
name,
|
|
96
|
+
label = name,
|
|
97
|
+
labelField = "name" as L,
|
|
98
|
+
loadData,
|
|
99
|
+
onChange,
|
|
100
|
+
onSelectChange,
|
|
101
|
+
onItemChange,
|
|
102
|
+
required,
|
|
103
|
+
search = true,
|
|
104
|
+
values = []
|
|
105
|
+
} = props;
|
|
106
|
+
|
|
107
|
+
// Value type
|
|
108
|
+
type ValueType = T[D];
|
|
109
|
+
const [localValues, setValues] = React.useState<ValueType[]>(values);
|
|
110
|
+
|
|
111
|
+
const updateValue = (value?: T[D]) => {
|
|
112
|
+
if (onChange) onChange(value);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const doChange = (event: SelectChangeEvent<unknown>, index: number) => {
|
|
116
|
+
const value = event.target.value;
|
|
117
|
+
const itemValue = value === "" ? undefined : (value as T[D]);
|
|
118
|
+
updateValue(itemValue);
|
|
119
|
+
|
|
120
|
+
const newValues = [...localValues.slice(0, index)];
|
|
121
|
+
if (itemValue != null) newValues.push(itemValue);
|
|
122
|
+
setValues(newValues);
|
|
123
|
+
|
|
124
|
+
if (onSelectChange) onSelectChange(event);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const doItemChange = (option: T | undefined, userAction: boolean) => {
|
|
128
|
+
if (onItemChange == null) return;
|
|
129
|
+
if (!userAction && (option == null || option[idField] !== values.at(-1)))
|
|
130
|
+
return;
|
|
131
|
+
onItemChange(option, userAction);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
React.useEffect(() => {
|
|
135
|
+
if (values.length > 0) {
|
|
136
|
+
setValues(values);
|
|
137
|
+
updateValue(values.at(-1));
|
|
138
|
+
}
|
|
139
|
+
}, [values]);
|
|
140
|
+
|
|
141
|
+
const currentValue = localValues.at(-1);
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<React.Fragment>
|
|
145
|
+
<Grid item xs={12}>
|
|
146
|
+
<FormLabel
|
|
147
|
+
required={required}
|
|
148
|
+
sx={{ fontSize: (theme) => theme.typography.caption }}
|
|
149
|
+
>
|
|
150
|
+
{label}
|
|
151
|
+
</FormLabel>
|
|
152
|
+
<input type="hidden" name={name} value={`${currentValue ?? ""}`} />
|
|
153
|
+
</Grid>
|
|
154
|
+
<Grid item xs={6} md={4} lg={3}>
|
|
155
|
+
<SelectEx<T, D, L>
|
|
156
|
+
idField={idField}
|
|
157
|
+
labelField={labelField}
|
|
158
|
+
name="tab1"
|
|
159
|
+
search={search}
|
|
160
|
+
fullWidth
|
|
161
|
+
loadData={() => loadData()}
|
|
162
|
+
value={values[0]}
|
|
163
|
+
onChange={(event) => doChange(event, 0)}
|
|
164
|
+
onItemChange={doItemChange}
|
|
165
|
+
inputRequired={required}
|
|
166
|
+
error={error}
|
|
167
|
+
helperText={helperText}
|
|
168
|
+
/>
|
|
169
|
+
</Grid>
|
|
170
|
+
{localValues[0] != null && (
|
|
171
|
+
<Grid item xs={6} md={4} lg={3}>
|
|
172
|
+
<SelectEx<T, D, L>
|
|
173
|
+
key={`${localValues[0]}`}
|
|
174
|
+
idField={idField}
|
|
175
|
+
labelField={labelField}
|
|
176
|
+
name="tab2"
|
|
177
|
+
search={search}
|
|
178
|
+
fullWidth
|
|
179
|
+
loadData={() => loadData(localValues[0])}
|
|
180
|
+
value={values[1]}
|
|
181
|
+
onChange={(event) => doChange(event, 1)}
|
|
182
|
+
onItemChange={doItemChange}
|
|
183
|
+
/>
|
|
184
|
+
</Grid>
|
|
185
|
+
)}
|
|
186
|
+
{localValues[1] != null && (
|
|
187
|
+
<Grid item xs={6} md={4} lg={3}>
|
|
188
|
+
<SelectEx<T, D, L>
|
|
189
|
+
key={`${localValues[1]}`}
|
|
190
|
+
idField={idField}
|
|
191
|
+
labelField={labelField}
|
|
192
|
+
name="tab3"
|
|
193
|
+
search={search}
|
|
194
|
+
fullWidth
|
|
195
|
+
loadData={() => loadData(localValues[1])}
|
|
196
|
+
value={values[2]}
|
|
197
|
+
onChange={(event) => doChange(event, 2)}
|
|
198
|
+
onItemChange={doItemChange}
|
|
199
|
+
/>
|
|
200
|
+
</Grid>
|
|
201
|
+
)}
|
|
202
|
+
{localValues[2] != null && (
|
|
203
|
+
<Grid item xs={6} md={4} lg={3}>
|
|
204
|
+
<SelectEx<T, D, L>
|
|
205
|
+
key={`${localValues[2]}`}
|
|
206
|
+
idField={idField}
|
|
207
|
+
labelField={labelField}
|
|
208
|
+
name="tab4"
|
|
209
|
+
search={search}
|
|
210
|
+
fullWidth
|
|
211
|
+
loadData={() => loadData(localValues[2])}
|
|
212
|
+
value={values[3]}
|
|
213
|
+
onChange={(event) => doChange(event, 3)}
|
|
214
|
+
onItemChange={doItemChange}
|
|
215
|
+
/>
|
|
216
|
+
</Grid>
|
|
217
|
+
)}
|
|
218
|
+
</React.Fragment>
|
|
219
|
+
);
|
|
221
220
|
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { DataTypes, IdDefaultType } from "@etsoo/shared";
|
|
2
|
+
import {
|
|
3
|
+
AutocompleteChangeReason,
|
|
4
|
+
AutocompleteValue,
|
|
5
|
+
FormLabel,
|
|
6
|
+
Grid
|
|
7
|
+
} from "@mui/material";
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { Tiplist } from "./Tiplist";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Hierarchy tiplist selector props
|
|
13
|
+
*/
|
|
14
|
+
export type HiSelectorTLProps<
|
|
15
|
+
T extends object,
|
|
16
|
+
D extends DataTypes.Keys<T> = IdDefaultType<T>
|
|
17
|
+
> = {
|
|
18
|
+
/**
|
|
19
|
+
* Id field
|
|
20
|
+
*/
|
|
21
|
+
idField?: D;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Error
|
|
25
|
+
*/
|
|
26
|
+
error?: boolean;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The helper text content.
|
|
30
|
+
*/
|
|
31
|
+
helperText?: React.ReactNode;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Name, also hidden input field name
|
|
35
|
+
*/
|
|
36
|
+
name: string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Label
|
|
40
|
+
*/
|
|
41
|
+
label?: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load data callback
|
|
45
|
+
*/
|
|
46
|
+
loadData: (
|
|
47
|
+
keyword: string | undefined,
|
|
48
|
+
id: T[D] | undefined,
|
|
49
|
+
maxItems: number,
|
|
50
|
+
parent?: T[D]
|
|
51
|
+
) => PromiseLike<T[] | null | undefined>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* On value change event
|
|
55
|
+
*/
|
|
56
|
+
onChange?: (value: unknown) => void;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Item change callback
|
|
60
|
+
*/
|
|
61
|
+
onItemChange?: (
|
|
62
|
+
event: React.SyntheticEvent,
|
|
63
|
+
option: T | null,
|
|
64
|
+
reason: AutocompleteChangeReason
|
|
65
|
+
) => void;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Required
|
|
69
|
+
*/
|
|
70
|
+
required?: boolean;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Search mode
|
|
74
|
+
*/
|
|
75
|
+
search?: boolean;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Values
|
|
79
|
+
*/
|
|
80
|
+
values?: T[D][];
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Hierarchy tiplist selector
|
|
85
|
+
* @param props Prop
|
|
86
|
+
* @returns Component
|
|
87
|
+
*/
|
|
88
|
+
export function HiSelectorTL<
|
|
89
|
+
T extends object,
|
|
90
|
+
D extends DataTypes.Keys<T> = IdDefaultType<T>
|
|
91
|
+
>(props: HiSelectorTLProps<T, D>) {
|
|
92
|
+
// Destruct
|
|
93
|
+
const {
|
|
94
|
+
idField = "id" as D,
|
|
95
|
+
error,
|
|
96
|
+
helperText,
|
|
97
|
+
name,
|
|
98
|
+
label = name,
|
|
99
|
+
loadData,
|
|
100
|
+
onChange,
|
|
101
|
+
onItemChange,
|
|
102
|
+
required,
|
|
103
|
+
search = false,
|
|
104
|
+
values = []
|
|
105
|
+
} = props;
|
|
106
|
+
|
|
107
|
+
// Value type
|
|
108
|
+
type ValueType = T[D];
|
|
109
|
+
const [localValues, setValues] = React.useState<ValueType[]>(values);
|
|
110
|
+
|
|
111
|
+
const updateValue = (value?: T[D]) => {
|
|
112
|
+
if (onChange) onChange(value);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const doChange = (
|
|
116
|
+
index: number,
|
|
117
|
+
event: React.SyntheticEvent,
|
|
118
|
+
value: AutocompleteValue<T, false, false, false>,
|
|
119
|
+
reason: AutocompleteChangeReason
|
|
120
|
+
) => {
|
|
121
|
+
if (onItemChange) {
|
|
122
|
+
onItemChange(event, value, reason);
|
|
123
|
+
if (event.isDefaultPrevented()) return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const itemValue = value ? value[idField] : undefined;
|
|
127
|
+
updateValue(itemValue);
|
|
128
|
+
|
|
129
|
+
const newValues = [...localValues.slice(0, index)];
|
|
130
|
+
if (itemValue != null) newValues.push(itemValue);
|
|
131
|
+
setValues(newValues);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
React.useEffect(() => {
|
|
135
|
+
if (values.length > 0) {
|
|
136
|
+
setValues(values);
|
|
137
|
+
updateValue(values.at(-1));
|
|
138
|
+
}
|
|
139
|
+
}, [values.toString()]);
|
|
140
|
+
|
|
141
|
+
const currentValue = localValues.at(-1);
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<React.Fragment>
|
|
145
|
+
<Grid item xs={12}>
|
|
146
|
+
<FormLabel
|
|
147
|
+
required={required}
|
|
148
|
+
sx={{ fontSize: (theme) => theme.typography.caption }}
|
|
149
|
+
>
|
|
150
|
+
{label}
|
|
151
|
+
</FormLabel>
|
|
152
|
+
<input type="hidden" name={name} value={`${currentValue ?? ""}`} />
|
|
153
|
+
</Grid>
|
|
154
|
+
<Grid item xs={6} md={4} lg={3}>
|
|
155
|
+
<Tiplist<T, D>
|
|
156
|
+
idField={idField}
|
|
157
|
+
label="1"
|
|
158
|
+
name="tab1"
|
|
159
|
+
search={search}
|
|
160
|
+
fullWidth
|
|
161
|
+
idValue={values[0]}
|
|
162
|
+
loadData={(keyword, id, items) => loadData(keyword, id, items)}
|
|
163
|
+
inputRequired={required}
|
|
164
|
+
inputError={error}
|
|
165
|
+
inputHelperText={helperText}
|
|
166
|
+
onChange={(event, value, reason) => doChange(0, event, value, reason)}
|
|
167
|
+
/>
|
|
168
|
+
</Grid>
|
|
169
|
+
{localValues[0] != null && (
|
|
170
|
+
<Grid item xs={6} md={4} lg={3}>
|
|
171
|
+
<Tiplist<T, D>
|
|
172
|
+
key={`${localValues[0]}`}
|
|
173
|
+
label="2"
|
|
174
|
+
idField={idField}
|
|
175
|
+
name="tab2"
|
|
176
|
+
search={search}
|
|
177
|
+
fullWidth
|
|
178
|
+
loadData={(keyword, id, items) =>
|
|
179
|
+
loadData(keyword, id, items, localValues[0])
|
|
180
|
+
}
|
|
181
|
+
idValue={values[1]}
|
|
182
|
+
onChange={(event, value, reason) =>
|
|
183
|
+
doChange(1, event, value, reason)
|
|
184
|
+
}
|
|
185
|
+
/>
|
|
186
|
+
</Grid>
|
|
187
|
+
)}
|
|
188
|
+
{localValues[1] != null && (
|
|
189
|
+
<Grid item xs={6} md={4} lg={3}>
|
|
190
|
+
<Tiplist<T, D>
|
|
191
|
+
key={`${localValues[1]}`}
|
|
192
|
+
label="3"
|
|
193
|
+
idField={idField}
|
|
194
|
+
name="tab3"
|
|
195
|
+
search={search}
|
|
196
|
+
fullWidth
|
|
197
|
+
loadData={(keyword, id, items) =>
|
|
198
|
+
loadData(keyword, id, items, localValues[1])
|
|
199
|
+
}
|
|
200
|
+
idValue={values[2]}
|
|
201
|
+
onChange={(event, value, reason) =>
|
|
202
|
+
doChange(2, event, value, reason)
|
|
203
|
+
}
|
|
204
|
+
/>
|
|
205
|
+
</Grid>
|
|
206
|
+
)}
|
|
207
|
+
{localValues[2] != null && (
|
|
208
|
+
<Grid item xs={6} md={4} lg={3}>
|
|
209
|
+
<Tiplist<T, D>
|
|
210
|
+
key={`${localValues[2]}`}
|
|
211
|
+
label="4"
|
|
212
|
+
idField={idField}
|
|
213
|
+
name="tab4"
|
|
214
|
+
search={search}
|
|
215
|
+
fullWidth
|
|
216
|
+
loadData={(keyword, id, items) =>
|
|
217
|
+
loadData(keyword, id, items, localValues[2])
|
|
218
|
+
}
|
|
219
|
+
idValue={values[3]}
|
|
220
|
+
onChange={(event, value, reason) =>
|
|
221
|
+
doChange(3, event, value, reason)
|
|
222
|
+
}
|
|
223
|
+
/>
|
|
224
|
+
</Grid>
|
|
225
|
+
)}
|
|
226
|
+
</React.Fragment>
|
|
227
|
+
);
|
|
228
|
+
}
|
package/src/Tiplist.tsx
CHANGED
|
@@ -317,6 +317,7 @@ export function Tiplist<
|
|
|
317
317
|
return getOptionDisabled ? getOptionDisabled(item) : false;
|
|
318
318
|
}}
|
|
319
319
|
getOptionLabel={(item) => {
|
|
320
|
+
if (typeof item !== "object") return `${item}`;
|
|
320
321
|
if (item[idField] === "n/a") return (labels.more ?? "More") + "...";
|
|
321
322
|
return getOptionLabel
|
|
322
323
|
? getOptionLabel(item)
|
package/src/index.ts
CHANGED