add component Form

This commit is contained in:
Bowen Tan 2021-09-28 11:31:22 +08:00
parent 69955e6f62
commit 688065cb95
6 changed files with 87 additions and 19 deletions

View File

@ -0,0 +1,41 @@
import { ChakraProvider, FormControl, FormLabel, Input } from '@chakra-ui/react';
import { Application } from '@meta-ui/core';
import React from 'react';
import { eventBus } from '../../eventBus';
import { ModifyComponentPropertyOperation } from '../../operations/Operations';
type Props = { selectedId: string; app: Application };
export const ComponentForm: React.FC<Props> = props => {
const { selectedId, app } = props;
const selectedComponent = app.spec.components.find(c => c.id === selectedId);
const properties = selectedComponent?.properties;
const fields = Object.keys(properties || []).map(key => {
const value = properties![key];
const ref = React.createRef<HTMLInputElement>();
const onBlur = () => {
eventBus.send(
'operation',
new ModifyComponentPropertyOperation(selectedId, key, ref.current?.value)
);
};
return (
<FormControl key={key}>
<FormLabel>{key}</FormLabel>
<Input ref={ref} onBlur={onBlur} defaultValue={value as string} />
</FormControl>
);
});
return (
<ChakraProvider>
<div>
<div>{selectedComponent?.id}</div>
<form>{fields}</form>
</div>
</ChakraProvider>
);
};

View File

@ -0,0 +1 @@
export * from './ComponentForm';

View File

@ -7,21 +7,29 @@ import { StructureTree } from './StructureTree';
import { OperationManager } from '../operations/OperationManager';
import { CreateComponentOperation } from '../operations/Operations';
import { eventBus } from '../eventBus';
import { ComponentForm } from './ComponentForm';
const operationManager = new OperationManager(DialogFormSchema);
export const Editor = () => {
const [selectedComponent, setSelectedComponent] = useState('');
const [selectedComponentId, setSelectedComponentId] = useState('');
const [app, setApp] = useState<Application>(operationManager.getApp());
const Wrapper: React.FC<{ id: string }> = useMemo(() => {
return props => {
if (props.id === selectedComponent) {
return <div style={{ boxShadow: '0 0 1px red' }}>{props.children}</div>;
}
return <>{props.children}</>;
const style =
props.id === selectedComponentId ? { boxShadow: '0 0 1px red' } : undefined;
const onClick = (e: React.MouseEvent<HTMLElement>) => {
e.stopPropagation();
setSelectedComponentId(() => props.id);
};
return (
<div onClick={onClick} style={style}>
{props.children}
</div>
);
};
}, [selectedComponent]);
}, [selectedComponentId]);
useEffect(() => {
const onAppChange = (app: Application) => {
@ -47,18 +55,16 @@ export const Editor = () => {
return (
<Box display="flex" height="100vh">
<button onClick={onClickAdd}></button>
<button onClick={onClickUndo}></button>
<Box flex="1">
<StructureTree app={app} onSelectComponent={id => setSelectedComponent(id)} />
<button onClick={onClickAdd}></button>
<button onClick={onClickUndo}></button>
<StructureTree app={app} onSelectComponent={id => setSelectedComponentId(id)} />
</Box>
<Box flex="3" borderRight="2px solid black">
<App
debugStore={false}
debugEvent={false}
options={app}
componentWrapper={Wrapper}
/>
<App options={app} componentWrapper={Wrapper} />
</Box>
<Box flex="1" borderRight="2px solid black">
<ComponentForm app={app} selectedId={selectedComponentId} />
</Box>
</Box>
);

View File

@ -1,7 +1,6 @@
import { css } from '@emotion/react';
import { StrictMode } from 'react';
import ReactDOM from 'react-dom';
import { ComponentList } from './components/ComponentsList';
import { Editor } from './components/Editor';
export default function renderApp() {
ReactDOM.render(
@ -12,7 +11,6 @@ export default function renderApp() {
`}
>
<Editor />
<ComponentList />
</div>
</StrictMode>,
document.getElementById('root')

View File

@ -4,6 +4,7 @@ import {
Operations,
CreateComponentOperation,
RemoveComponentOperation,
ModifyComponentPropertyOperation,
} from './Operations';
import { produce } from 'immer';
import { registry } from '../metaUI';
@ -64,7 +65,6 @@ export class OperationManager {
}
apply(o: Operations, noEffect = false) {
console.log('apply');
let newApp = this.app;
switch (o.kind) {
case 'createComponent':
@ -89,6 +89,16 @@ export class OperationManager {
draft.spec.components.splice(i, 1);
});
break;
case 'modifyComponentProperty':
const mo = o as ModifyComponentPropertyOperation;
newApp = produce(this.app, draft => {
draft.spec.components.forEach(c => {
if (c.id === mo.componentId) {
c.properties[mo.propertyKey] = mo.propertyValue;
}
});
});
break;
}
this.updateApp(newApp);
}

View File

@ -1,4 +1,7 @@
export type Operations = CreateComponentOperation | RemoveComponentOperation;
export type Operations =
| CreateComponentOperation
| RemoveComponentOperation
| ModifyComponentPropertyOperation;
export class CreateComponentOperation {
kind = 'createComponent';
@ -13,3 +16,12 @@ export class RemoveComponentOperation {
kind = 'removeComponent';
constructor(public componentId: string) {}
}
export class ModifyComponentPropertyOperation {
kind = 'modifyComponentProperty';
constructor(
public componentId: string,
public propertyKey: string,
public propertyValue: unknown
) {}
}