diff --git a/packages/editor/package.json b/packages/editor/package.json index a3541fbf..bc91e55a 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -42,6 +42,8 @@ "framer-motion": "^4", "immer": "^9.0.6", "lodash-es": "^4.17.21", + "mobx": "^6.3.8", + "mobx-react-lite": "^3.2.2", "react": "^17.0.2", "react-dom": "^17.0.2" }, diff --git a/packages/editor/src/EditorStore.ts b/packages/editor/src/EditorStore.ts new file mode 100644 index 00000000..f9a3db0c --- /dev/null +++ b/packages/editor/src/EditorStore.ts @@ -0,0 +1,19 @@ +import { makeAutoObservable } from 'mobx'; +import { eventBus } from './eventBus'; + +class EditorStore { + selectedComponentId = ''; + + constructor() { + eventBus.on('selectComponent', id => { + this.setSelectedComponentId(id); + }); + makeAutoObservable(this); + } + + setSelectedComponentId(val: string) { + this.selectedComponentId = val; + } +} + +export const editorStore = new EditorStore() diff --git a/packages/editor/src/components/ComponentWrapper.tsx b/packages/editor/src/components/ComponentWrapper.tsx index 06f0f37f..35f7b0e3 100644 --- a/packages/editor/src/components/ComponentWrapper.tsx +++ b/packages/editor/src/components/ComponentWrapper.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import { css } from '@emotion/react'; import { ComponentWrapperType } from '@sunmao-ui/runtime'; -import { eventBus, HoverComponentEvent, SelectComponentEvent } from '../eventBus'; +import { eventBus } from '../eventBus'; // children of components in this list should render height as 100% const fullHeightList = ['core/v1/grid_layout']; @@ -15,10 +15,10 @@ export const ComponentWrapper: ComponentWrapperType = props => { useEffect(() => { const handler = (event: string, payload: any) => { switch (event) { - case SelectComponentEvent: + case 'selectComponent': setSelectedComponentId(payload); break; - case HoverComponentEvent: + case 'hoverComponentEvent': setHoverComponentId(payload); break; } @@ -52,15 +52,15 @@ export const ComponentWrapper: ComponentWrapperType = props => { `; const onClickWrapper = (e: React.MouseEvent) => { e.stopPropagation(); - eventBus.send(SelectComponentEvent as any, component.id); + eventBus.send('selectComponent' as any, component.id); }; const onMouseEnterWrapper = (e: React.MouseEvent) => { e.stopPropagation(); - eventBus.send(HoverComponentEvent as any, component.id); + eventBus.send('hoverComponentEvent' as any, component.id); }; const onMouseLeaveWrapper = (e: React.MouseEvent) => { e.stopPropagation(); - eventBus.send(HoverComponentEvent as any, ''); + eventBus.send('hoverComponentEvent' as any, ''); }; return (
; @@ -32,203 +34,198 @@ type Props = { appStorage: AppStorage; }; -export const Editor: React.FC = ({ - App, - registry, - stateStore, - appModelManager, - appStorage, -}) => { - const { components } = useAppModel(appModelManager); +export const Editor: React.FC = observer( + ({ App, registry, stateStore, appModelManager, appStorage }) => { + const { components } = useAppModel(appModelManager); - const [selectedComponentId, setSelectedComponentId] = useState( - components?.[0]?.id || '' - ); - const [scale, setScale] = useState(100); - const [preview, setPreview] = useState(false); - const [codeMode, setCodeMode] = useState(false); - const [code, setCode] = useState(''); + const [scale, setScale] = useState(100); + const [preview, setPreview] = useState(false); + const [codeMode, setCodeMode] = useState(false); + const [code, setCode] = useState(''); - useEffect(() => { - eventBus.on(SelectComponentEvent, id => { - setSelectedComponentId(id); - }); - }, [setSelectedComponentId]); + const gridCallbacks: GridCallbacks = useMemo(() => { + return { + // drag an existing component + onDragStop(id, layout) { + eventBus.send( + 'operation', + new ModifyComponentPropertiesLeafOperation({ + componentId: id, + properties: { layout }, + }) + ); + }, + // drag a new component from tool box + onDrop(id, layout, _, e) { + const component = e.dataTransfer?.getData('component') || ''; + eventBus.send( + 'operation', + new CreateComponentBranchOperation({ + componentType: component, + parentId: id, + slot: 'content', + layout, + }) + ); + }, + }; + }, []); - const gridCallbacks: GridCallbacks = useMemo(() => { - return { - // drag an existing component - onDragStop(id, layout) { - eventBus.send( - 'operation', - new ModifyComponentPropertiesLeafOperation({ - componentId: id, - properties: { layout }, - }) - ); - }, - // drag a new component from tool box - onDrop(id, layout, _, e) { - const component = e.dataTransfer?.getData('component') || ''; - eventBus.send( - 'operation', - new CreateComponentBranchOperation({ - componentType: component, - parentId: id, - slot: 'content', - layout, - }) - ); - }, - }; - }, []); + const app = useMemo(() => { + return { + version: 'sunmao/v1', + kind: 'Application', + metadata: { + name: 'some App', + }, + spec: { + components, + }, + }; + }, [components]); - const app = useMemo(() => { - return { - version: 'sunmao/v1', - kind: 'Application', - metadata: { - name: 'some App', - }, - spec: { - components, - }, - }; - }, [components]); - - const appComponent = useMemo(() => { - return ( - - ); - }, [app, gridCallbacks]); - - const renderMain = () => { - const appBox = ( - - - - {appComponent} - - - ); - - if (codeMode) { + const appComponent = useMemo(() => { return ( - - - + + ); + }, [app, gridCallbacks]); + + const renderMain = () => { + const appBox = ( + + + + {appComponent} + + + ); + + if (codeMode) { + return ( + + + + + {appBox} + + ); + } + return ( + <> + + + + Explorer + UI Tree + State + + + + + + + editorStore.setSelectedComponentId(id)} + registry={registry} + /> + + + + + + {appBox} - - ); - } - return ( - <> - - - - Explorer - UI Tree - State - - - - - - - setSelectedComponentId(id)} - registry={registry} - /> - - - - - - - - {appBox} - - - - Inspect - Insert - - - - - - - - - - - - - ); - }; - - return ( - - - setPreview(true)} - codeMode={codeMode} - onCodeMode={v => { - setCodeMode(v); - if (!v && code) { - eventBus.send('operation', new ReplaceAppLeafOperation(JSON.parse(code))); - } - }} - /> - - {renderMain()} - - - {preview && ( - setPreview(false)}> - - + + + + Inspect + Insert + + + + + + + + + + - - )} - - ); -}; + + ); + }; + + return ( + + + setPreview(true)} + codeMode={codeMode} + onCodeMode={v => { + setCodeMode(v); + if (!v && code) { + eventBus.send('operation', new ReplaceAppLeafOperation(JSON.parse(code))); + } + }} + /> + + {renderMain()} + + + {preview && ( + setPreview(false)}> + + + + + )} + + ); + } +); diff --git a/packages/editor/src/eventBus.ts b/packages/editor/src/eventBus.ts index b2424dec..80ec9d76 100644 --- a/packages/editor/src/eventBus.ts +++ b/packages/editor/src/eventBus.ts @@ -3,17 +3,14 @@ import { Application, ApplicationComponent } from '@sunmao-ui/core'; import { IOperation } from './operations/type'; import { ImplementedRuntimeModule } from '../../runtime/lib'; -export const SelectComponentEvent = 'selectComponent'; -export const HoverComponentEvent = 'hoverComponent'; - export type EventNames = { operation: IOperation; redo: undefined; undo: undefined; componentsReload: ApplicationComponent[]; componentsChange: ApplicationComponent[]; - [SelectComponentEvent]: string; - [HoverComponentEvent]: string; + selectComponent: string; + hoverComponent: string; // for state decorators appChange: Application; diff --git a/packages/editor/src/operations/leaf/updateSelectComponentLeafOperation.ts b/packages/editor/src/operations/leaf/updateSelectComponentLeafOperation.ts index 34f130f3..b00c0b0f 100644 --- a/packages/editor/src/operations/leaf/updateSelectComponentLeafOperation.ts +++ b/packages/editor/src/operations/leaf/updateSelectComponentLeafOperation.ts @@ -1,5 +1,5 @@ import { ApplicationComponent } from '@sunmao-ui/core'; -import { eventBus, SelectComponentEvent } from '../../eventBus'; +import { eventBus } from '../../eventBus'; import { BaseLeafOperation } from '../type'; export type UpdateSelectComponentLeafOperationContext = { @@ -12,21 +12,21 @@ export class UpdateSelectComponentLeafOperation extends BaseLeafOperation { - eventBus.send(SelectComponentEvent, this.context.newId); + eventBus.send('selectComponent', this.context.newId); }); return prev; } redo(prev: ApplicationComponent[]): ApplicationComponent[] { setTimeout(() => { - eventBus.send(SelectComponentEvent, this.context.newId); + eventBus.send('selectComponent', this.context.newId); }); return prev; } undo(prev: ApplicationComponent[]): ApplicationComponent[] { setTimeout(() => { - eventBus.send(SelectComponentEvent, this.prevId); + eventBus.send('selectComponent', this.prevId); }); return prev; } diff --git a/yarn.lock b/yarn.lock index 8ae2cf16..32adfcf9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7264,6 +7264,16 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mobx-react-lite@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.2.2.tgz#cee5f09e6b0e4d2705d87276baf1de74d997fa33" + integrity sha512-FxJJMqmHcnQYOVVs2DdjNHioGlFsXF5/9VHztS9NAfIT3DYrxNZzVi119Zr/OmlWKkWNkAsssSNzPkqautfL4A== + +mobx@^6.3.8: + version "6.3.8" + resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.3.8.tgz#a4654b1b69c683237f2444cf0af39a621d4611af" + integrity sha512-RfG2y766P1o9u9xrQht/HBXEoUPIr4B3Gjri3reLW/TuHm3I/3TfBBS0OaeMbw19RIF0AymqjDNlJgakN4ZK7g== + modify-values@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"