mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2025-01-30 17:09:35 +08:00
fix(editor): make the form update correctly
This commit is contained in:
parent
940cdb619c
commit
f96dc66881
@ -5,18 +5,18 @@ import { simple as simpleWalk } from 'acorn-walk';
|
||||
import { flattenDeep, isArray, isObject } from 'lodash-es';
|
||||
import { ComponentId, IFieldModel, ModuleId } from './IAppModel';
|
||||
|
||||
const regExp = new RegExp('.*{{.*}}.*');
|
||||
const regExp = /.*{{.*}}.*/;
|
||||
|
||||
export class FieldModel implements IFieldModel {
|
||||
isDynamic = false;
|
||||
refs: Record<ComponentId | ModuleId, string[]> = {};
|
||||
private value: unknown | Array<IFieldModel> | Record<string, IFieldModel>;
|
||||
|
||||
constructor(value: unknown) {
|
||||
constructor (value: unknown) {
|
||||
this.update(value);
|
||||
}
|
||||
|
||||
get rawValue() {
|
||||
get rawValue () {
|
||||
if (isObject(this.value)) {
|
||||
if (isArray(this.value)) {
|
||||
return this.value.map(field => field.rawValue);
|
||||
@ -32,21 +32,30 @@ export class FieldModel implements IFieldModel {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
update(value: unknown) {
|
||||
update (value: unknown, shouldExtendValues = true) {
|
||||
if (isObject(value)) {
|
||||
if (!isObject(this.value)) {
|
||||
this.value = isArray(value) ? [] : {};
|
||||
}
|
||||
const isArrayValue = isArray(value);
|
||||
const isOldValueObject = isObject(this.value);
|
||||
|
||||
for (const key in value) {
|
||||
const val = (value as Record<string, unknown>)[key];
|
||||
const _thisValue = this.value as Record<string, IFieldModel>;
|
||||
if (!_thisValue[key]) {
|
||||
_thisValue[key] = new FieldModel(val);
|
||||
this.value = (Object.keys(value) as Array<keyof typeof value>).reduce((result, key) => {
|
||||
const oldValue: IFieldModel | null = isObject(this.value) ? this.value[key] : null;
|
||||
let newValue: FieldModel;
|
||||
|
||||
if (oldValue) {
|
||||
(oldValue as IFieldModel).update(value[key], false);
|
||||
newValue = oldValue;
|
||||
} else {
|
||||
_thisValue[key].update(val);
|
||||
newValue = new FieldModel(value[key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isArray(result)) {
|
||||
result.push(newValue);
|
||||
} else {
|
||||
result[key] = newValue;
|
||||
}
|
||||
|
||||
return result;
|
||||
}, (isArrayValue ? [] : (shouldExtendValues && isOldValueObject ? this.value : {})) as Record<string, IFieldModel>);
|
||||
} else {
|
||||
this.value = value;
|
||||
}
|
||||
@ -54,19 +63,19 @@ export class FieldModel implements IFieldModel {
|
||||
this.parseReferences();
|
||||
}
|
||||
|
||||
getProperty(key: string | number): FieldModel | undefined {
|
||||
getProperty (key: string | number): FieldModel | undefined {
|
||||
if (typeof this.value === 'object') {
|
||||
return (this.value as any)[key];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.value
|
||||
getValue () {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
traverse(cb: (f: IFieldModel, key: string) => void) {
|
||||
function _traverse(field: FieldModel, key: string) {
|
||||
traverse (cb: (f: IFieldModel, key: string) => void) {
|
||||
function _traverse (field: FieldModel, key: string) {
|
||||
if (isObject(field.value)) {
|
||||
for (const _key in field.value) {
|
||||
const val = field.getProperty(_key);
|
||||
@ -81,7 +90,7 @@ export class FieldModel implements IFieldModel {
|
||||
_traverse(this, '');
|
||||
}
|
||||
|
||||
private parseReferences() {
|
||||
private parseReferences () {
|
||||
if (!this.isDynamic || typeof this.value !== 'string') return;
|
||||
|
||||
const exps = flattenDeep(
|
||||
@ -102,13 +111,13 @@ export class FieldModel implements IFieldModel {
|
||||
const str = exp.slice(node.start, node.end);
|
||||
let path = str.replace(lastIdentifier, '');
|
||||
if (path.startsWith('.')) {
|
||||
path = path.slice(1, path.length)
|
||||
path = path.slice(1, path.length);
|
||||
}
|
||||
this.refs[lastIdentifier].push(path);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import {
|
||||
ComponentSchema,
|
||||
TraitSchema,
|
||||
MethodSchema,
|
||||
RuntimeTrait,
|
||||
RuntimeTrait
|
||||
} from '@sunmao-ui/core';
|
||||
|
||||
export type ComponentId = string & {
|
||||
@ -116,7 +116,7 @@ export interface ITraitModel {
|
||||
export interface IFieldModel {
|
||||
// value: any;
|
||||
isDynamic: boolean;
|
||||
update: (value: unknown) => void;
|
||||
update: (value: unknown, shouldExtendValues: boolean) => void;
|
||||
getProperty: (key: string) => IFieldModel | void;
|
||||
getValue: () => unknown | void | IFieldModel;
|
||||
traverse: (cb: (f: IFieldModel, key: string) => void) => void;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { flatten } from 'lodash-es';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { FormControl, FormLabel, Input, Textarea, VStack } from '@chakra-ui/react';
|
||||
@ -29,30 +29,49 @@ export const renderField = (properties: {
|
||||
services: EditorServices;
|
||||
}) => {
|
||||
const { value, type, fullKey, selectedComponentId, index, services } = properties;
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [textareaValue, setTextareaValue] = useState(value as string);
|
||||
const { eventBus, registry } = services;
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useEffect(() => {
|
||||
if (typeof value !== 'object') {
|
||||
setTextareaValue(value as string);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
if (typeof value !== 'object') {
|
||||
const ref = React.createRef<HTMLTextAreaElement>();
|
||||
const onBlur = () => {
|
||||
const operation = type
|
||||
? genOperation(registry, 'modifyTraitProperty', {
|
||||
componentId: selectedComponentId,
|
||||
traitIndex: index,
|
||||
properties: {
|
||||
[fullKey]: ref.current?.value,
|
||||
},
|
||||
})
|
||||
componentId: selectedComponentId,
|
||||
traitIndex: index,
|
||||
properties: {
|
||||
[fullKey]: ref.current?.value,
|
||||
},
|
||||
})
|
||||
: genOperation(registry, 'modifyComponentProperty', {
|
||||
componentId: selectedComponentId,
|
||||
properties: {
|
||||
[fullKey]: ref.current?.value,
|
||||
},
|
||||
});
|
||||
componentId: selectedComponentId,
|
||||
properties: {
|
||||
[fullKey]: ref.current?.value,
|
||||
},
|
||||
});
|
||||
eventBus.send('operation', operation);
|
||||
};
|
||||
const onChange = (event: any) => {
|
||||
setTextareaValue(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControl key={`${selectedComponentId}-${fullKey}`}>
|
||||
<FormLabel>{fullKey}</FormLabel>
|
||||
<Textarea ref={ref} onBlur={onBlur} defaultValue={value as string} />
|
||||
<Textarea
|
||||
ref={ref}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
value={textareaValue}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
} else {
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
Input,
|
||||
Select,
|
||||
Switch,
|
||||
VStack,
|
||||
VStack
|
||||
} from '@chakra-ui/react';
|
||||
import { Static } from '@sinclair/typebox';
|
||||
import { CloseIcon } from '@chakra-ui/icons';
|
||||
@ -33,16 +33,24 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
const { components } = editorStore;
|
||||
const [methods, setMethods] = useState<string[]>([]);
|
||||
|
||||
const updateMethods = useCallback(
|
||||
(componentId: string) => {
|
||||
const type = components.find(c => c.id === componentId)?.type;
|
||||
if (type) {
|
||||
const componentSpec = registry.getComponentByType(type).spec;
|
||||
setMethods(Object.keys(componentSpec.methods));
|
||||
}
|
||||
},
|
||||
[components, registry]
|
||||
);
|
||||
const formik = useFormik({
|
||||
initialValues: handler,
|
||||
onSubmit: values => {
|
||||
onChange(values);
|
||||
}
|
||||
});
|
||||
|
||||
const updateMethods = useCallback((componentId: string) => {
|
||||
const type = components.find(c => c.id === componentId)?.type;
|
||||
if (type) {
|
||||
const componentSpec = registry.getComponentByType(type).spec;
|
||||
setMethods(Object.keys(componentSpec.methods));
|
||||
}
|
||||
}, [components, registry]);
|
||||
|
||||
useEffect(() => {
|
||||
formik.setValues(handler);
|
||||
}, [handler]);
|
||||
|
||||
useEffect(() => {
|
||||
if (handler.componentId) {
|
||||
@ -54,25 +62,19 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
updateMethods(e.target.value);
|
||||
};
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: handler,
|
||||
onSubmit: values => {
|
||||
onChange(values);
|
||||
},
|
||||
});
|
||||
|
||||
const typeField = (
|
||||
<FormControl>
|
||||
<FormLabel>Event Type</FormLabel>
|
||||
<Select
|
||||
name="type"
|
||||
placeholder="Select Event Type"
|
||||
onChange={formik.handleChange}
|
||||
onBlur={() => formik.submitForm()}
|
||||
onChange={formik.handleChange}
|
||||
placeholder="Select Event Type"
|
||||
value={formik.values.type}
|
||||
>
|
||||
{eventTypes.map(e => (
|
||||
<option key={e} value={e}>
|
||||
<option key={e}
|
||||
value={e}>
|
||||
{e}
|
||||
</option>
|
||||
))}
|
||||
@ -84,16 +86,17 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
<FormLabel>Target Component</FormLabel>
|
||||
<Select
|
||||
name="componentId"
|
||||
placeholder="Select Target Component"
|
||||
onBlur={() => formik.submitForm()}
|
||||
onChange={e => {
|
||||
onTargetComponentChange(e);
|
||||
formik.handleChange(e);
|
||||
}}
|
||||
onBlur={() => formik.submitForm()}
|
||||
placeholder="Select Target Component"
|
||||
value={formik.values.componentId}
|
||||
>
|
||||
{components.map(c => (
|
||||
<option key={c.id} value={c.id}>
|
||||
<option key={c.id}
|
||||
value={c.id}>
|
||||
{c.id}
|
||||
</option>
|
||||
))}
|
||||
@ -105,13 +108,14 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
<FormLabel>Method</FormLabel>
|
||||
<Select
|
||||
name="method.name"
|
||||
placeholder="Select Method"
|
||||
onChange={formik.handleChange}
|
||||
onBlur={() => formik.submitForm()}
|
||||
onChange={formik.handleChange}
|
||||
placeholder="Select Method"
|
||||
value={formik.values.method.name}
|
||||
>
|
||||
{methods.map(m => (
|
||||
<option key={m} value={m}>
|
||||
<option key={m}
|
||||
value={m}>
|
||||
{m}
|
||||
</option>
|
||||
))}
|
||||
@ -123,7 +127,7 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
<FormControl>
|
||||
<FormLabel>Parameters</FormLabel>
|
||||
<KeyValueEditor
|
||||
initValue={formik.values.method.parameters}
|
||||
value={formik.values.method.parameters}
|
||||
onChange={json => {
|
||||
formik.setFieldValue('method.parameters', json);
|
||||
formik.submitForm();
|
||||
@ -137,8 +141,8 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
<FormLabel>Wait Type</FormLabel>
|
||||
<Select
|
||||
name="wait.type"
|
||||
onChange={formik.handleChange}
|
||||
onBlur={() => formik.submitForm()}
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.wait?.type}
|
||||
>
|
||||
<option value="delay">delay</option>
|
||||
@ -153,8 +157,8 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
<FormLabel>Wait Time</FormLabel>
|
||||
<Input
|
||||
name="wait.time"
|
||||
onChange={formik.handleChange}
|
||||
onBlur={() => formik.submitForm()}
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.wait?.time}
|
||||
/>
|
||||
</FormControl>
|
||||
@ -164,16 +168,17 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
<FormControl>
|
||||
<FormLabel>Disabled</FormLabel>
|
||||
<Switch
|
||||
name="disabled"
|
||||
isChecked={formik.values.disabled}
|
||||
onChange={formik.handleChange}
|
||||
name="disabled"
|
||||
onBlur={() => formik.submitForm()}
|
||||
onChange={formik.handleChange}
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box position="relative" width="100%">
|
||||
<Box position="relative"
|
||||
width="100%">
|
||||
<VStack className={formWrapperCSS}>
|
||||
{hideEventType ? null : typeField}
|
||||
{targetField}
|
||||
@ -184,15 +189,15 @@ export const EventHandlerForm: React.FC<Props> = observer(props => {
|
||||
{disabledField}
|
||||
</VStack>
|
||||
<IconButton
|
||||
position="absolute"
|
||||
right="4"
|
||||
top="4"
|
||||
aria-label="remove event handler"
|
||||
variant="ghost"
|
||||
colorScheme="red"
|
||||
size="xs"
|
||||
icon={<CloseIcon />}
|
||||
onClick={onRemove}
|
||||
position="absolute"
|
||||
right="4"
|
||||
size="xs"
|
||||
top="4"
|
||||
variant="ghost"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
@ -92,7 +92,7 @@ export const FetchTraitForm: React.FC<Props> = props => {
|
||||
<FormControl>
|
||||
<FormLabel>Body</FormLabel>
|
||||
<KeyValueEditor
|
||||
initValue={formik.values.body}
|
||||
value={formik.values.body}
|
||||
onChange={json => {
|
||||
formik.setFieldValue('body', json);
|
||||
formik.submitForm();
|
||||
@ -105,7 +105,7 @@ export const FetchTraitForm: React.FC<Props> = props => {
|
||||
<FormControl>
|
||||
<FormLabel>Headers</FormLabel>
|
||||
<KeyValueEditor
|
||||
initValue={formik.values.headers}
|
||||
value={formik.values.headers}
|
||||
onChange={json => {
|
||||
formik.setFieldValue('headers', json);
|
||||
formik.submitForm();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { FieldProps } from './fields';
|
||||
import {
|
||||
NumberInput,
|
||||
@ -14,6 +14,10 @@ const NumberField: React.FC<Props> = props => {
|
||||
const { formData, onChange } = props;
|
||||
const [value, setValue] = useState(String(formData));
|
||||
|
||||
useEffect(() => {
|
||||
setValue(String(formData));
|
||||
}, [formData]);
|
||||
|
||||
return (
|
||||
<NumberInput
|
||||
value={value}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { FieldProps } from './fields';
|
||||
import { Input, Select } from '@chakra-ui/react';
|
||||
|
||||
@ -8,6 +8,10 @@ const StringField: React.FC<Props> = props => {
|
||||
const { schema, formData, onChange } = props;
|
||||
const [value, setValue] = useState(formData);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(formData);
|
||||
}, [formData]);
|
||||
|
||||
// enum
|
||||
if (Array.isArray(schema.enum)) {
|
||||
return (
|
||||
|
@ -52,7 +52,7 @@ export const ModuleMetaDataForm: React.FC<ModuleMetaDataFormProps> = observer(
|
||||
<FormControl>
|
||||
<FormLabel>Module StateMap</FormLabel>
|
||||
<KeyValueEditor
|
||||
initValue={formik.values.stateMap}
|
||||
value={formik.values.stateMap}
|
||||
onChange={json => {
|
||||
formik.setFieldValue('stateMap', json);
|
||||
formik.submitForm();
|
||||
|
@ -2,18 +2,22 @@ import { CloseIcon } from '@chakra-ui/icons';
|
||||
import { Button, HStack, IconButton, Input, VStack } from '@chakra-ui/react';
|
||||
import produce from 'immer';
|
||||
import { fromPairs, toPairs } from 'lodash-es';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
type Props = {
|
||||
onChange: (json: Record<string, string>) => void;
|
||||
initValue?: Record<string, string>;
|
||||
value?: Record<string, string>;
|
||||
};
|
||||
|
||||
export const KeyValueEditor: React.FC<Props> = props => {
|
||||
const [rows, setRows] = useState<Array<[string, string]>>(() => {
|
||||
return toPairs(props.initValue);
|
||||
return toPairs(props.value);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setRows(toPairs(props.value));
|
||||
}, [props.value]);
|
||||
|
||||
const emitDataChange = (newRows: Array<[string, string]>) => {
|
||||
const json = fromPairs(newRows);
|
||||
props.onChange(json);
|
||||
|
Loading…
Reference in New Issue
Block a user