mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 15:14:52 +09:00
Split up BaseConfigTree component
Pull out the computation of the tree data into a separate function to reduce the complexity of the BaseConfigTree component.
This commit is contained in:
1 parent
707c0bb453
commit
790f880e98
1 file changed
+119
-96
@@ -1,7 +1,9 @@
|
||||
import * as lib from "@clusterio/lib";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Button, Card, Checkbox, Form, Input, InputNumber, Space, Spin, Tooltip, Tree, Typography } from "antd";
|
||||
import {
|
||||
Button, Card, Checkbox, Form, FormInstance, Input, InputNumber, Space, Spin, Tooltip, Tree, Typography,
|
||||
} from "antd";
|
||||
import ReloadOutlined from "@ant-design/icons/ReloadOutlined";
|
||||
|
||||
const { Title } = Typography;
|
||||
@@ -25,6 +27,20 @@ function getInitialValues(config: lib.Config<any>, props: BaseConfigTreeProps) {
|
||||
return initialValues;
|
||||
}
|
||||
|
||||
function renderInput(def: lib.FieldDefinition) {
|
||||
if (def.type === "boolean") {
|
||||
return <Checkbox/>;
|
||||
}
|
||||
if (def.type === "string") {
|
||||
return <Input/>;
|
||||
}
|
||||
if (def.type === "number") {
|
||||
return <InputNumber/>;
|
||||
}
|
||||
|
||||
return `Unknown type ${def.type}`;
|
||||
}
|
||||
|
||||
type BaseConfigTreeProps = {
|
||||
ConfigClass: typeof lib.ControllerConfig | typeof lib.HostConfig | typeof lib.InstanceConfig;
|
||||
retrieveConfig: () => Promise<lib.ConfigSchema>;
|
||||
@@ -38,7 +54,7 @@ export default function BaseConfigTree(props: BaseConfigTreeProps) {
|
||||
let [config, setConfig] = useState<lib.Config<any>|null>(null);
|
||||
let [form] = Form.useForm();
|
||||
let [changedFields, setChangedFields] = useState<Set<string>>(new Set());
|
||||
let [errorFields, setErrorFields] = useState(new Map());
|
||||
let [errorFields, setErrorFields] = useState(new Map<string, string>());
|
||||
let [applying, setApplying] = useState(false);
|
||||
const available = props.available ?? true;
|
||||
|
||||
@@ -56,20 +72,6 @@ export default function BaseConfigTree(props: BaseConfigTreeProps) {
|
||||
}
|
||||
}, [props.id, available]);
|
||||
|
||||
function renderInput(def: lib.FieldDefinition) {
|
||||
if (def.type === "boolean") {
|
||||
return <Checkbox/>;
|
||||
}
|
||||
if (def.type === "string") {
|
||||
return <Input/>;
|
||||
}
|
||||
if (def.type === "number") {
|
||||
return <InputNumber/>;
|
||||
}
|
||||
|
||||
return `Unknown type ${def.type}`;
|
||||
}
|
||||
|
||||
if (!available) {
|
||||
return <>
|
||||
<Title level={5} style={{ marginTop: 16 }}>Config</Title>
|
||||
@@ -109,12 +111,109 @@ export default function BaseConfigTree(props: BaseConfigTreeProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const [propsMap, treeData] = computeTreeData(
|
||||
form,
|
||||
initialValues,
|
||||
errorFields,
|
||||
config,
|
||||
setConfig,
|
||||
props.ConfigClass,
|
||||
onValuesChange,
|
||||
);
|
||||
|
||||
return <>
|
||||
<Title level={5} style={{ marginTop: 16 }}>
|
||||
Config
|
||||
<Space style={{ float: "right" }}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
loading={applying}
|
||||
onClick={async () => {
|
||||
setApplying(true);
|
||||
for (let field of changedFields) {
|
||||
let value = form.getFieldValue(field);
|
||||
let request;
|
||||
if (propsMap.has(field)) {
|
||||
let [fieldName, prop] = propsMap.get(field);
|
||||
request = props.setProp(fieldName, prop, value);
|
||||
|
||||
} else {
|
||||
request = props.setField(field, value === null ? "" : String(value));
|
||||
}
|
||||
|
||||
try {
|
||||
await request;
|
||||
setChangedFields(changed => {
|
||||
let newChanged = new Set(changed);
|
||||
newChanged.delete(field);
|
||||
return newChanged;
|
||||
});
|
||||
|
||||
} catch (err: any) {
|
||||
setErrorFields(errors => {
|
||||
let newErrors = new Map(errors);
|
||||
newErrors.set(field, err.message);
|
||||
return newErrors;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await updateConfig();
|
||||
setApplying(false);
|
||||
|
||||
if (props.onApply) {
|
||||
props.onApply();
|
||||
}
|
||||
}}
|
||||
disabled={changedFields.size === 0}
|
||||
>
|
||||
Apply
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => {
|
||||
form.resetFields();
|
||||
setChangedFields(new Set());
|
||||
setErrorFields(new Map());
|
||||
}}
|
||||
>
|
||||
Revert
|
||||
</Button>
|
||||
</Space>
|
||||
</Title>
|
||||
<Card size="small" className="config-container">
|
||||
<Form
|
||||
size="small"
|
||||
form={form}
|
||||
initialValues={initialValues}
|
||||
onValuesChange={onValuesChange}
|
||||
>
|
||||
<Tree
|
||||
treeData={treeData}
|
||||
selectable={false}
|
||||
defaultExpandAll={true}
|
||||
filterTreeNode={(node) => changedFields.has(node.key)}
|
||||
/>
|
||||
</Form>
|
||||
</Card>
|
||||
</>;
|
||||
}
|
||||
|
||||
function computeTreeData(
|
||||
form: FormInstance<any>,
|
||||
initialValues: any,
|
||||
errorFields: Map<string, string>,
|
||||
config: lib.Config<any>,
|
||||
setConfig: (newConfig: lib.Config<any>) => void,
|
||||
ConfigClass: BaseConfigTreeProps["ConfigClass"],
|
||||
onValuesChange: (changedValues: object) => void,
|
||||
) {
|
||||
let restartTip = <Tooltip
|
||||
className="ant-form-item-tooltip"
|
||||
title="A restart is required for this setting to take effect"
|
||||
><ReloadOutlined/></Tooltip>;
|
||||
|
||||
|
||||
type ChildNode = {
|
||||
key: string;
|
||||
children: any[];
|
||||
@@ -129,7 +228,7 @@ export default function BaseConfigTree(props: BaseConfigTreeProps) {
|
||||
let treeData = [];
|
||||
let propsMap = new Map();
|
||||
const groups = new Map<string, { [name: string]: lib.FieldDefinition }>();
|
||||
for (let [fieldName, def] of Object.entries(props.ConfigClass.fieldDefinitions)) {
|
||||
for (let [fieldName, def] of Object.entries(ConfigClass.fieldDefinitions)) {
|
||||
const [groupName, field] = lib.splitOn(".", fieldName);
|
||||
let group = groups.get(groupName);
|
||||
if (!group) {
|
||||
@@ -221,7 +320,7 @@ export default function BaseConfigTree(props: BaseConfigTreeProps) {
|
||||
let propName = form.getFieldValue(`${newPropPath}.name`);
|
||||
let propValue = form.getFieldValue(`${newPropPath}.value`);
|
||||
if (!Object.prototype.hasOwnProperty.call(value, propName)) {
|
||||
let newConfig = props.ConfigClass.fromJSON(config!.toJSON(), "control");
|
||||
let newConfig = ConfigClass.fromJSON(config!.toJSON(), "control");
|
||||
(newConfig as lib.Config<any>).setProp(fieldName, propName, null);
|
||||
setConfig(newConfig);
|
||||
}
|
||||
@@ -264,81 +363,5 @@ export default function BaseConfigTree(props: BaseConfigTreeProps) {
|
||||
treeData.push(treeNode);
|
||||
}
|
||||
|
||||
return <>
|
||||
<Title level={5} style={{ marginTop: 16 }}>
|
||||
Config
|
||||
<Space style={{ float: "right" }}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
loading={applying}
|
||||
onClick={async () => {
|
||||
setApplying(true);
|
||||
for (let field of changedFields) {
|
||||
let value = form.getFieldValue(field);
|
||||
let request;
|
||||
if (propsMap.has(field)) {
|
||||
let [fieldName, prop] = propsMap.get(field);
|
||||
request = props.setProp(fieldName, prop, value);
|
||||
|
||||
} else {
|
||||
request = props.setField(field, value === null ? "" : String(value));
|
||||
}
|
||||
|
||||
try {
|
||||
await request;
|
||||
setChangedFields(changed => {
|
||||
let newChanged = new Set(changed);
|
||||
newChanged.delete(field);
|
||||
return newChanged;
|
||||
});
|
||||
|
||||
} catch (err: any) {
|
||||
setErrorFields(errors => {
|
||||
let newErrors = new Map(errors);
|
||||
newErrors.set(field, err.message);
|
||||
return newErrors;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await updateConfig();
|
||||
setApplying(false);
|
||||
|
||||
if (props.onApply) {
|
||||
props.onApply();
|
||||
}
|
||||
}}
|
||||
disabled={changedFields.size === 0}
|
||||
>
|
||||
Apply
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => {
|
||||
form.resetFields();
|
||||
setChangedFields(new Set());
|
||||
setErrorFields(new Map());
|
||||
}}
|
||||
>
|
||||
Revert
|
||||
</Button>
|
||||
</Space>
|
||||
</Title>
|
||||
<Card size="small" className="config-container">
|
||||
<Form
|
||||
size="small"
|
||||
form={form}
|
||||
initialValues={initialValues}
|
||||
onValuesChange={onValuesChange}
|
||||
>
|
||||
<Tree
|
||||
treeData={treeData}
|
||||
selectable={false}
|
||||
defaultExpandAll={true}
|
||||
filterTreeNode={(node) => changedFields.has(node.key)}
|
||||
/>
|
||||
</Form>
|
||||
</Card>
|
||||
</>;
|
||||
return [propsMap, treeData] as [typeof propsMap, typeof treeData];
|
||||
}
|
||||
Reference in new issue
Block a user