@etsoo/materialui 1.3.65 → 1.3.67
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/__tests__/CustomFields.tsx +62 -1
- package/__tests__/SelectEx.tsx +3 -5
- package/lib/custom/CustomFieldUtils.js +8 -0
- package/lib/custom/CustomFieldWindow.d.ts +55 -0
- package/lib/custom/CustomFieldWindow.js +89 -0
- package/lib/custom/FieldSelect.js +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/package.json +7 -7
- package/src/custom/CustomFieldUtils.tsx +8 -0
- package/src/custom/CustomFieldWindow.tsx +192 -0
- package/src/custom/FieldSelect.tsx +1 -0
- package/src/index.ts +1 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
act,
|
|
4
|
+
fireEvent,
|
|
5
|
+
getByText,
|
|
6
|
+
render,
|
|
7
|
+
screen
|
|
8
|
+
} from "@testing-library/react";
|
|
3
9
|
import { CustomFieldData } from "@etsoo/appscript";
|
|
4
10
|
import { CustomFieldUtils } from "../src/custom/CustomFieldUtils";
|
|
5
11
|
import { CustomFieldReactCollection } from "@etsoo/react";
|
|
@@ -91,3 +97,58 @@ it("Render FieldCheckbox", () => {
|
|
|
91
97
|
checkboxItems[1].click();
|
|
92
98
|
});
|
|
93
99
|
});
|
|
100
|
+
|
|
101
|
+
it("Render FieldSelect", async () => {
|
|
102
|
+
// Arrange
|
|
103
|
+
const fields: CustomFieldData[] = [
|
|
104
|
+
{
|
|
105
|
+
type: "select",
|
|
106
|
+
name: "select",
|
|
107
|
+
options: [
|
|
108
|
+
{ id: "a", label: "A" },
|
|
109
|
+
{ id: "b", label: "B" },
|
|
110
|
+
{ id: "c", label: "C" }
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
const collections: CustomFieldReactCollection<CustomFieldData> = {};
|
|
116
|
+
|
|
117
|
+
act(() => {
|
|
118
|
+
render(
|
|
119
|
+
<div>
|
|
120
|
+
{CustomFieldUtils.create(
|
|
121
|
+
fields,
|
|
122
|
+
collections,
|
|
123
|
+
(field) => {
|
|
124
|
+
switch (field.type) {
|
|
125
|
+
case "select":
|
|
126
|
+
return "c";
|
|
127
|
+
default:
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
(name, value) => {
|
|
132
|
+
expect(name).toBe("checkbox");
|
|
133
|
+
expect(value).toStrictEqual(["a"]);
|
|
134
|
+
}
|
|
135
|
+
)}
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const button = screen.getByRole("combobox");
|
|
141
|
+
|
|
142
|
+
act(() => {
|
|
143
|
+
// Act, click to open the dropdown list
|
|
144
|
+
jest.useFakeTimers();
|
|
145
|
+
fireEvent.mouseDown(button);
|
|
146
|
+
jest.advanceTimersByTime(100);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const input = document.querySelector<HTMLInputElement>("input");
|
|
150
|
+
expect(input?.value).toBe("c");
|
|
151
|
+
|
|
152
|
+
const listItems = document.querySelectorAll("li");
|
|
153
|
+
expect(listItems.length).toBe(3);
|
|
154
|
+
});
|
package/__tests__/SelectEx.tsx
CHANGED
|
@@ -19,7 +19,7 @@ it("Render SelectEx", async () => {
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
// Render component
|
|
22
|
-
render(
|
|
22
|
+
const { findByText } = render(
|
|
23
23
|
<SelectEx<T>
|
|
24
24
|
options={options}
|
|
25
25
|
name="test"
|
|
@@ -44,12 +44,11 @@ it("Render SelectEx", async () => {
|
|
|
44
44
|
jest.advanceTimersByTime(100);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
/*
|
|
48
47
|
// Get list item
|
|
49
|
-
const itemName2 = await findByText(
|
|
48
|
+
const itemName2 = await findByText("Name 2");
|
|
50
49
|
expect(itemName2.nodeName).toBe("SPAN");
|
|
51
50
|
|
|
52
|
-
const itemBlank = await findByText(
|
|
51
|
+
const itemBlank = await findByText("---");
|
|
53
52
|
expect(itemBlank.nodeName).toBe("SPAN");
|
|
54
53
|
|
|
55
54
|
act(() => {
|
|
@@ -57,7 +56,6 @@ it("Render SelectEx", async () => {
|
|
|
57
56
|
});
|
|
58
57
|
|
|
59
58
|
expect(itemChangeCallback).toBeCalledTimes(2);
|
|
60
|
-
*/
|
|
61
59
|
});
|
|
62
60
|
|
|
63
61
|
it("Render multiple SelectEx", async () => {
|
|
@@ -10,6 +10,10 @@ import { FieldInput } from "./FieldInput";
|
|
|
10
10
|
import { FieldLabel } from "./FieldLabel";
|
|
11
11
|
import { FieldNumberInput } from "./FieldNumberInput";
|
|
12
12
|
import { FieldTexarea } from "./FieldTexarea";
|
|
13
|
+
import { FieldJson } from "./FieldJson";
|
|
14
|
+
import { FieldRadio } from "./FieldRadio";
|
|
15
|
+
import { FieldSelect } from "./FieldSelect";
|
|
16
|
+
import { FieldSwitch } from "./FieldSwitch";
|
|
13
17
|
/**
|
|
14
18
|
* Custom field utilities
|
|
15
19
|
*/
|
|
@@ -25,8 +29,12 @@ export var CustomFieldUtils;
|
|
|
25
29
|
date: FieldDateInput,
|
|
26
30
|
divider: FieldDivider,
|
|
27
31
|
input: FieldInput,
|
|
32
|
+
json: FieldJson,
|
|
28
33
|
label: FieldLabel,
|
|
29
34
|
number: FieldNumberInput,
|
|
35
|
+
radio: FieldRadio,
|
|
36
|
+
select: FieldSelect,
|
|
37
|
+
switch: FieldSwitch,
|
|
30
38
|
textarea: FieldTexarea
|
|
31
39
|
};
|
|
32
40
|
/**
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CustomFieldData } from "@etsoo/appscript";
|
|
2
|
+
import { GridProps } from "@mui/material";
|
|
3
|
+
import React from "react";
|
|
4
|
+
/**
|
|
5
|
+
* Custom field window props
|
|
6
|
+
* 自定义字段窗口属性
|
|
7
|
+
*/
|
|
8
|
+
export type CustomFieldWindowProps<D extends CustomFieldData> = {
|
|
9
|
+
/**
|
|
10
|
+
* Children creation callback
|
|
11
|
+
* 子元素创建回调
|
|
12
|
+
* @param open Open callback
|
|
13
|
+
* @param label Label
|
|
14
|
+
* @param pc Property count
|
|
15
|
+
* @returns Component
|
|
16
|
+
*/
|
|
17
|
+
children: (open: (customFields: D[], jsonData?: unknown) => void, label: string | undefined, pc: number) => React.ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Label
|
|
20
|
+
* 标签
|
|
21
|
+
*/
|
|
22
|
+
label?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Grid props
|
|
25
|
+
* 网格属性
|
|
26
|
+
*/
|
|
27
|
+
gridProps?: GridProps;
|
|
28
|
+
/**
|
|
29
|
+
* JSON data
|
|
30
|
+
* JSON 数据
|
|
31
|
+
*/
|
|
32
|
+
jsonData?: unknown;
|
|
33
|
+
/**
|
|
34
|
+
* Input name
|
|
35
|
+
* 输入框名称
|
|
36
|
+
*/
|
|
37
|
+
inputName?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Input ref
|
|
40
|
+
* 输入框引用
|
|
41
|
+
*/
|
|
42
|
+
inputRef?: React.MutableRefObject<HTMLInputElement | null>;
|
|
43
|
+
/**
|
|
44
|
+
* On update callback
|
|
45
|
+
* 更新回调
|
|
46
|
+
*/
|
|
47
|
+
onUpdate?: (data: Record<string, unknown>) => void;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Custom field window
|
|
51
|
+
* 自定义字段窗口
|
|
52
|
+
* @param props Props
|
|
53
|
+
* @returns Component
|
|
54
|
+
*/
|
|
55
|
+
export declare function CustomFieldWindow<D extends CustomFieldData = CustomFieldData>(props: CustomFieldWindowProps<D>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Utils } from "@etsoo/shared";
|
|
3
|
+
import { Grid, Stack } from "@mui/material";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { globalApp } from "../app/ReactApp";
|
|
6
|
+
import { MUGlobal } from "../MUGlobal";
|
|
7
|
+
import { CustomFieldUtils } from "./CustomFieldUtils";
|
|
8
|
+
function calculateKeys(data) {
|
|
9
|
+
let count = 0;
|
|
10
|
+
for (const key in data) {
|
|
11
|
+
const item = data[key];
|
|
12
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
13
|
+
count += calculateKeys(item);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
count++;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return count;
|
|
20
|
+
}
|
|
21
|
+
function parseJsonData(jsonData) {
|
|
22
|
+
let data = {};
|
|
23
|
+
if (jsonData) {
|
|
24
|
+
try {
|
|
25
|
+
data =
|
|
26
|
+
typeof jsonData === "object"
|
|
27
|
+
? jsonData
|
|
28
|
+
: typeof jsonData === "string"
|
|
29
|
+
? JSON.parse(jsonData)
|
|
30
|
+
: {};
|
|
31
|
+
}
|
|
32
|
+
catch { }
|
|
33
|
+
}
|
|
34
|
+
return [data, calculateKeys(data)];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Custom field window
|
|
38
|
+
* 自定义字段窗口
|
|
39
|
+
* @param props Props
|
|
40
|
+
* @returns Component
|
|
41
|
+
*/
|
|
42
|
+
export function CustomFieldWindow(props) {
|
|
43
|
+
// Validate app
|
|
44
|
+
const app = globalApp;
|
|
45
|
+
if (app == null) {
|
|
46
|
+
throw new Error("No globalApp");
|
|
47
|
+
}
|
|
48
|
+
const { children, label = app.get("jsonData"), gridProps, jsonData, inputName = "jsonData", inputRef, onUpdate } = props;
|
|
49
|
+
const spacing = MUGlobal.half(MUGlobal.pagePaddings);
|
|
50
|
+
const [count, setCount] = React.useState(0);
|
|
51
|
+
const [initData, propertyCount] = parseJsonData(jsonData);
|
|
52
|
+
React.useEffect(() => setCount(propertyCount), [propertyCount]);
|
|
53
|
+
return (_jsxs(React.Fragment, { children: [_jsx("input", { type: "hidden", name: inputName, ref: inputRef }), children((customFields, jsonData) => {
|
|
54
|
+
const collections = {};
|
|
55
|
+
let data = {};
|
|
56
|
+
jsonData ?? (jsonData = inputRef?.current?.value);
|
|
57
|
+
if (jsonData) {
|
|
58
|
+
const [d, pc] = parseJsonData(jsonData);
|
|
59
|
+
data = d;
|
|
60
|
+
setCount(pc);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
data = initData;
|
|
64
|
+
}
|
|
65
|
+
app.notifier.confirm(label, undefined, (value) => {
|
|
66
|
+
if (value) {
|
|
67
|
+
if (inputRef?.current) {
|
|
68
|
+
inputRef.current.value = JSON.stringify(data);
|
|
69
|
+
}
|
|
70
|
+
setCount(calculateKeys(data));
|
|
71
|
+
if (onUpdate)
|
|
72
|
+
onUpdate(data);
|
|
73
|
+
}
|
|
74
|
+
}, {
|
|
75
|
+
fullScreen: app.smDown,
|
|
76
|
+
inputs: (_jsx(Stack, { marginTop: 1.5, children: _jsx(Grid, { container: true, justifyContent: "left", spacing: spacing, sx: {
|
|
77
|
+
".MuiTypography-subtitle2": {
|
|
78
|
+
fontWeight: "bold"
|
|
79
|
+
}
|
|
80
|
+
}, ...gridProps, children: CustomFieldUtils.create(customFields, collections, (field) => {
|
|
81
|
+
if (field.name == null)
|
|
82
|
+
return;
|
|
83
|
+
return Utils.getNestedValue(data, field.name);
|
|
84
|
+
}, (name, value) => {
|
|
85
|
+
Utils.setNestedValue(data, name, value);
|
|
86
|
+
}) }) }))
|
|
87
|
+
});
|
|
88
|
+
}, label, count)] }));
|
|
89
|
+
}
|
|
@@ -37,7 +37,7 @@ export const FieldSelect = ({ field, mref, onChange, defaultValue }) => {
|
|
|
37
37
|
if (!name) {
|
|
38
38
|
return (_jsxs(Typography, { children: ["No name for FieldSelect ", JSON.stringify(field)] }));
|
|
39
39
|
}
|
|
40
|
-
return (_jsx(SelectEx, { label: field.label ?? "", helperText: field.helperText, name: name, options: field.options, fullWidth: true, onChange: (event) => {
|
|
40
|
+
return (_jsx(SelectEx, { label: field.label ?? "", helperText: field.helperText, name: name, options: field.options, fullWidth: true, value: value, onChange: (event) => {
|
|
41
41
|
const value = event.target.value;
|
|
42
42
|
const newValue = typeof value === "string" || typeof value === "number"
|
|
43
43
|
? value
|
package/lib/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./app/Labels";
|
|
|
9
9
|
export * from "./app/ReactApp";
|
|
10
10
|
export * from "./app/ServiceApp";
|
|
11
11
|
export * from "./custom/CustomFieldUtils";
|
|
12
|
+
export * from "./custom/CustomFieldWindow";
|
|
12
13
|
export * from "./messages/MessageUtils";
|
|
13
14
|
export * from "./messages/OperationMessageContainer";
|
|
14
15
|
export * from "./messages/OperationMessageDto";
|
package/lib/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./app/Labels";
|
|
|
9
9
|
export * from "./app/ReactApp";
|
|
10
10
|
export * from "./app/ServiceApp";
|
|
11
11
|
export * from "./custom/CustomFieldUtils";
|
|
12
|
+
export * from "./custom/CustomFieldWindow";
|
|
12
13
|
export * from "./messages/MessageUtils";
|
|
13
14
|
export * from "./messages/OperationMessageContainer";
|
|
14
15
|
export * from "./messages/OperationMessageDto";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/materialui",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.67",
|
|
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.11.2",
|
|
51
51
|
"@emotion/react": "^11.11.4",
|
|
52
52
|
"@emotion/styled": "^11.11.5",
|
|
53
|
-
"@etsoo/appscript": "^1.4.
|
|
53
|
+
"@etsoo/appscript": "^1.4.96",
|
|
54
54
|
"@etsoo/notificationbase": "^1.1.42",
|
|
55
|
-
"@etsoo/react": "^1.7.
|
|
56
|
-
"@etsoo/shared": "^1.2.
|
|
55
|
+
"@etsoo/react": "^1.7.54",
|
|
56
|
+
"@etsoo/shared": "^1.2.41",
|
|
57
57
|
"@mui/icons-material": "^5.15.18",
|
|
58
58
|
"@mui/material": "^5.15.18",
|
|
59
|
-
"@mui/x-data-grid": "^7.5.
|
|
59
|
+
"@mui/x-data-grid": "^7.5.1",
|
|
60
60
|
"chart.js": "^4.4.3",
|
|
61
61
|
"chartjs-plugin-datalabels": "^2.2.0",
|
|
62
62
|
"eventemitter3": "^5.0.1",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"@types/react-dom": "^18.3.0",
|
|
88
88
|
"@types/react-input-mask": "^3.0.5",
|
|
89
89
|
"@types/react-window": "^1.8.8",
|
|
90
|
-
"@typescript-eslint/eslint-plugin": "^7.
|
|
91
|
-
"@typescript-eslint/parser": "^7.
|
|
90
|
+
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
|
91
|
+
"@typescript-eslint/parser": "^7.11.0",
|
|
92
92
|
"jest": "^29.7.0",
|
|
93
93
|
"jest-environment-jsdom": "^29.7.0",
|
|
94
94
|
"typescript": "^5.4.5"
|
|
@@ -17,6 +17,10 @@ import { FieldInput } from "./FieldInput";
|
|
|
17
17
|
import { FieldLabel } from "./FieldLabel";
|
|
18
18
|
import { FieldNumberInput } from "./FieldNumberInput";
|
|
19
19
|
import { FieldTexarea } from "./FieldTexarea";
|
|
20
|
+
import { FieldJson } from "./FieldJson";
|
|
21
|
+
import { FieldRadio } from "./FieldRadio";
|
|
22
|
+
import { FieldSelect } from "./FieldSelect";
|
|
23
|
+
import { FieldSwitch } from "./FieldSwitch";
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Custom field utilities
|
|
@@ -35,8 +39,12 @@ export namespace CustomFieldUtils {
|
|
|
35
39
|
date: FieldDateInput,
|
|
36
40
|
divider: FieldDivider,
|
|
37
41
|
input: FieldInput,
|
|
42
|
+
json: FieldJson,
|
|
38
43
|
label: FieldLabel,
|
|
39
44
|
number: FieldNumberInput,
|
|
45
|
+
radio: FieldRadio,
|
|
46
|
+
select: FieldSelect,
|
|
47
|
+
switch: FieldSwitch,
|
|
40
48
|
textarea: FieldTexarea
|
|
41
49
|
};
|
|
42
50
|
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { CustomFieldData } from "@etsoo/appscript";
|
|
2
|
+
import { CustomFieldReactCollection } from "@etsoo/react";
|
|
3
|
+
import { Utils } from "@etsoo/shared";
|
|
4
|
+
import { Grid, GridProps, Stack } from "@mui/material";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { globalApp } from "../app/ReactApp";
|
|
7
|
+
import { MUGlobal } from "../MUGlobal";
|
|
8
|
+
import { CustomFieldUtils } from "./CustomFieldUtils";
|
|
9
|
+
|
|
10
|
+
function calculateKeys(data: Record<string, unknown>) {
|
|
11
|
+
let count = 0;
|
|
12
|
+
for (const key in data) {
|
|
13
|
+
const item = data[key];
|
|
14
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
15
|
+
count += calculateKeys(item as Record<string, unknown>);
|
|
16
|
+
} else {
|
|
17
|
+
count++;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return count;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseJsonData(jsonData: unknown): [Record<string, unknown>, number] {
|
|
24
|
+
let data: Record<string, unknown> = {};
|
|
25
|
+
if (jsonData) {
|
|
26
|
+
try {
|
|
27
|
+
data =
|
|
28
|
+
typeof jsonData === "object"
|
|
29
|
+
? jsonData
|
|
30
|
+
: typeof jsonData === "string"
|
|
31
|
+
? JSON.parse(jsonData)
|
|
32
|
+
: {};
|
|
33
|
+
} catch {}
|
|
34
|
+
}
|
|
35
|
+
return [data, calculateKeys(data)];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Custom field window props
|
|
40
|
+
* 自定义字段窗口属性
|
|
41
|
+
*/
|
|
42
|
+
export type CustomFieldWindowProps<D extends CustomFieldData> = {
|
|
43
|
+
/**
|
|
44
|
+
* Children creation callback
|
|
45
|
+
* 子元素创建回调
|
|
46
|
+
* @param open Open callback
|
|
47
|
+
* @param label Label
|
|
48
|
+
* @param pc Property count
|
|
49
|
+
* @returns Component
|
|
50
|
+
*/
|
|
51
|
+
children: (
|
|
52
|
+
open: (customFields: D[], jsonData?: unknown) => void,
|
|
53
|
+
label: string | undefined,
|
|
54
|
+
pc: number
|
|
55
|
+
) => React.ReactNode;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Label
|
|
59
|
+
* 标签
|
|
60
|
+
*/
|
|
61
|
+
label?: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Grid props
|
|
65
|
+
* 网格属性
|
|
66
|
+
*/
|
|
67
|
+
gridProps?: GridProps;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* JSON data
|
|
71
|
+
* JSON 数据
|
|
72
|
+
*/
|
|
73
|
+
jsonData?: unknown;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Input name
|
|
77
|
+
* 输入框名称
|
|
78
|
+
*/
|
|
79
|
+
inputName?: string;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Input ref
|
|
83
|
+
* 输入框引用
|
|
84
|
+
*/
|
|
85
|
+
inputRef?: React.MutableRefObject<HTMLInputElement | null>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* On update callback
|
|
89
|
+
* 更新回调
|
|
90
|
+
*/
|
|
91
|
+
onUpdate?: (data: Record<string, unknown>) => void;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Custom field window
|
|
96
|
+
* 自定义字段窗口
|
|
97
|
+
* @param props Props
|
|
98
|
+
* @returns Component
|
|
99
|
+
*/
|
|
100
|
+
export function CustomFieldWindow<D extends CustomFieldData = CustomFieldData>(
|
|
101
|
+
props: CustomFieldWindowProps<D>
|
|
102
|
+
) {
|
|
103
|
+
// Validate app
|
|
104
|
+
const app = globalApp;
|
|
105
|
+
if (app == null) {
|
|
106
|
+
throw new Error("No globalApp");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const {
|
|
110
|
+
children,
|
|
111
|
+
label = app.get("jsonData"),
|
|
112
|
+
gridProps,
|
|
113
|
+
jsonData,
|
|
114
|
+
inputName = "jsonData",
|
|
115
|
+
inputRef,
|
|
116
|
+
onUpdate
|
|
117
|
+
} = props;
|
|
118
|
+
|
|
119
|
+
const spacing = MUGlobal.half(MUGlobal.pagePaddings);
|
|
120
|
+
|
|
121
|
+
const [count, setCount] = React.useState(0);
|
|
122
|
+
|
|
123
|
+
const [initData, propertyCount] = parseJsonData(jsonData);
|
|
124
|
+
|
|
125
|
+
React.useEffect(() => setCount(propertyCount), [propertyCount]);
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<React.Fragment>
|
|
129
|
+
<input type="hidden" name={inputName} ref={inputRef} />
|
|
130
|
+
{children(
|
|
131
|
+
(customFields, jsonData) => {
|
|
132
|
+
const collections: CustomFieldReactCollection<D> = {};
|
|
133
|
+
let data: Record<string, unknown> = {};
|
|
134
|
+
jsonData ??= inputRef?.current?.value;
|
|
135
|
+
if (jsonData) {
|
|
136
|
+
const [d, pc] = parseJsonData(jsonData);
|
|
137
|
+
data = d;
|
|
138
|
+
setCount(pc);
|
|
139
|
+
} else {
|
|
140
|
+
data = initData;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
app.notifier.confirm(
|
|
144
|
+
label,
|
|
145
|
+
undefined,
|
|
146
|
+
(value) => {
|
|
147
|
+
if (value) {
|
|
148
|
+
if (inputRef?.current) {
|
|
149
|
+
inputRef.current.value = JSON.stringify(data);
|
|
150
|
+
}
|
|
151
|
+
setCount(calculateKeys(data));
|
|
152
|
+
if (onUpdate) onUpdate(data);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
fullScreen: app.smDown,
|
|
157
|
+
inputs: (
|
|
158
|
+
<Stack marginTop={1.5}>
|
|
159
|
+
<Grid
|
|
160
|
+
container
|
|
161
|
+
justifyContent="left"
|
|
162
|
+
spacing={spacing}
|
|
163
|
+
sx={{
|
|
164
|
+
".MuiTypography-subtitle2": {
|
|
165
|
+
fontWeight: "bold"
|
|
166
|
+
}
|
|
167
|
+
}}
|
|
168
|
+
{...gridProps}
|
|
169
|
+
>
|
|
170
|
+
{CustomFieldUtils.create(
|
|
171
|
+
customFields,
|
|
172
|
+
collections,
|
|
173
|
+
(field) => {
|
|
174
|
+
if (field.name == null) return;
|
|
175
|
+
return Utils.getNestedValue(data, field.name);
|
|
176
|
+
},
|
|
177
|
+
(name, value) => {
|
|
178
|
+
Utils.setNestedValue(data, name, value);
|
|
179
|
+
}
|
|
180
|
+
)}
|
|
181
|
+
</Grid>
|
|
182
|
+
</Stack>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
},
|
|
187
|
+
label,
|
|
188
|
+
count
|
|
189
|
+
)}
|
|
190
|
+
</React.Fragment>
|
|
191
|
+
);
|
|
192
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./app/ReactApp";
|
|
|
10
10
|
export * from "./app/ServiceApp";
|
|
11
11
|
|
|
12
12
|
export * from "./custom/CustomFieldUtils";
|
|
13
|
+
export * from "./custom/CustomFieldWindow";
|
|
13
14
|
|
|
14
15
|
export * from "./messages/MessageUtils";
|
|
15
16
|
export * from "./messages/OperationMessageContainer";
|