修复bug
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import React from "react";
|
||||
import {Checkbox, ColorPicker, Input, InputNumber, Select, Space} from "antd";
|
||||
|
||||
export type Backend_Field_Descriptor = {
|
||||
name: string
|
||||
value_type?: string
|
||||
readable?: boolean
|
||||
editable?: boolean
|
||||
children?: Backend_Field_Descriptor[]
|
||||
presentation?: {
|
||||
label?: string
|
||||
control?: string
|
||||
options?: {label: string, value: unknown}[]
|
||||
}
|
||||
}
|
||||
|
||||
export type Backend_Form_Section = {
|
||||
title?: string
|
||||
object_path?: string
|
||||
descriptor_path?: string
|
||||
fields: string[]
|
||||
}
|
||||
|
||||
export type Backend_Disabled_Rule = {
|
||||
field: string
|
||||
disabled_when: {
|
||||
field: string
|
||||
equals: unknown
|
||||
}
|
||||
}
|
||||
|
||||
type Json_Object = Record<string, any>
|
||||
|
||||
export function get_path(root: Json_Object, path = ""): any {
|
||||
if (!path) return root;
|
||||
return path.split(".").reduce((value: any, key) => value?.[key], root);
|
||||
}
|
||||
|
||||
export function fields_at_path(fields: Backend_Field_Descriptor[], path = ""): Backend_Field_Descriptor[] {
|
||||
if (!path) return fields;
|
||||
let current = fields;
|
||||
for (const name of path.split(".")) {
|
||||
const field = current.find(item => item.name === name);
|
||||
if (!field) return [];
|
||||
current = field.children || [];
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function field_disabled(root: Json_Object, field: Backend_Field_Descriptor, rules: Backend_Disabled_Rule[]): boolean {
|
||||
if (field.editable === false) return true;
|
||||
const rule = rules.find(item => item.field === field.name);
|
||||
return Boolean(rule && get_path(root, rule.disabled_when.field) === rule.disabled_when.equals);
|
||||
}
|
||||
|
||||
function Backend_Field_Control(props: {
|
||||
descriptor: Backend_Field_Descriptor
|
||||
value: any
|
||||
disabled: boolean
|
||||
onChange: (value: any) => void
|
||||
}) {
|
||||
const {descriptor, value, disabled, onChange} = props;
|
||||
const control = descriptor.presentation?.control || descriptor.value_type || "text";
|
||||
const label = descriptor.presentation?.label || descriptor.name;
|
||||
if (control === "boolean" || descriptor.value_type === "boolean") {
|
||||
return <Checkbox checked={Boolean(value)} disabled={disabled}
|
||||
onChange={event => onChange(event.target.checked)}>{label}</Checkbox>;
|
||||
}
|
||||
if (control === "color") {
|
||||
return <Space><span>{label}</span><ColorPicker value={typeof value === "string" ? value : undefined}
|
||||
disabled={disabled}
|
||||
onChange={color => onChange(color.toHexString())}/></Space>;
|
||||
}
|
||||
if (control === "select") {
|
||||
return <Select value={value} disabled={disabled} options={descriptor.presentation?.options || []}
|
||||
onChange={onChange} style={{width: 260}}/>;
|
||||
}
|
||||
if (control === "number" || descriptor.value_type === "integer" || descriptor.value_type === "number") {
|
||||
return <InputNumber addonBefore={label} value={value ?? null} disabled={disabled}
|
||||
precision={descriptor.value_type === "integer" ? 0 : undefined}
|
||||
onChange={onChange} style={{width: 280}}/>;
|
||||
}
|
||||
return <Input addonBefore={label} value={value == null ? "" : String(value)} readOnly={disabled}
|
||||
onChange={event => onChange(event.target.value)} style={{width: 420, maxWidth: "100%"}}/>;
|
||||
}
|
||||
|
||||
export function Backend_Fields(props: {
|
||||
root: Json_Object
|
||||
descriptor_fields: Backend_Field_Descriptor[]
|
||||
section: Backend_Form_Section
|
||||
rules?: Backend_Disabled_Rule[]
|
||||
onChange?: (path: string, value: unknown, descriptor: Backend_Field_Descriptor) => void
|
||||
}) {
|
||||
const object = get_path(props.root, props.section.object_path) as Json_Object | undefined;
|
||||
const descriptor_path = props.section.descriptor_path ?? props.section.object_path;
|
||||
const available_fields = fields_at_path(props.descriptor_fields, descriptor_path);
|
||||
const selected = props.section.fields
|
||||
.map(name => available_fields.find(field => field.name === name))
|
||||
.filter((field): field is Backend_Field_Descriptor => Boolean(field && field.readable !== false));
|
||||
const update = (field: Backend_Field_Descriptor, value: unknown) => {
|
||||
if (!object) return;
|
||||
object[field.name] = value;
|
||||
const path = [props.section.object_path, field.name].filter(Boolean).join(".");
|
||||
props.onChange?.(path, value, field);
|
||||
};
|
||||
const booleans = selected.filter(field => (field.presentation?.control || field.value_type) === "boolean");
|
||||
const controls = selected.filter(field => !booleans.includes(field));
|
||||
return (
|
||||
<Space direction="vertical" size={8} style={{width: "100%"}}>
|
||||
{booleans.length > 0 && <Space wrap>
|
||||
{booleans.map(field => <Backend_Field_Control key={field.name} descriptor={field}
|
||||
value={object?.[field.name]}
|
||||
disabled={field_disabled(props.root, field, props.rules || [])}
|
||||
onChange={value => update(field, value)}/>) }
|
||||
</Space>}
|
||||
{controls.map(field => <Backend_Field_Control key={field.name} descriptor={field}
|
||||
value={object?.[field.name]}
|
||||
disabled={field_disabled(props.root, field, props.rules || [])}
|
||||
onChange={value => update(field, value)}/>) }
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user