mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-21 03:15:49 +08:00
add mobx
This commit is contained in:
parent
51c33c24fd
commit
c85008204a
@ -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"
|
||||
},
|
||||
|
19
packages/editor/src/EditorStore.ts
Normal file
19
packages/editor/src/EditorStore.ts
Normal file
@ -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()
|
@ -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<HTMLElement>) => {
|
||||
e.stopPropagation();
|
||||
eventBus.send(SelectComponentEvent as any, component.id);
|
||||
eventBus.send('selectComponent' as any, component.id);
|
||||
};
|
||||
const onMouseEnterWrapper = (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.stopPropagation();
|
||||
eventBus.send(HoverComponentEvent as any, component.id);
|
||||
eventBus.send('hoverComponentEvent' as any, component.id);
|
||||
};
|
||||
const onMouseLeaveWrapper = (e: React.MouseEvent<HTMLElement>) => {
|
||||
e.stopPropagation();
|
||||
eventBus.send(HoverComponentEvent as any, '');
|
||||
eventBus.send('hoverComponentEvent' as any, '');
|
||||
};
|
||||
return (
|
||||
<div
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Application } from '@sunmao-ui/core';
|
||||
import { GridCallbacks, DIALOG_CONTAINER_ID, initSunmaoUI } from '@sunmao-ui/runtime';
|
||||
import { Box, Tabs, TabList, Tab, TabPanels, TabPanel, Flex } from '@chakra-ui/react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { StructureTree } from './StructureTree';
|
||||
import { eventBus, SelectComponentEvent } from '../eventBus';
|
||||
import { eventBus } from '../eventBus';
|
||||
import { ComponentForm } from './ComponentForm';
|
||||
import { ComponentList } from './ComponentsList';
|
||||
import { useAppModel } from '../operations/useAppModel';
|
||||
@ -20,6 +21,7 @@ import {
|
||||
ReplaceAppLeafOperation,
|
||||
} from '../operations/leaf';
|
||||
import { CreateComponentBranchOperation } from '../operations/branch';
|
||||
import { editorStore } from '../EditorStore';
|
||||
|
||||
type ReturnOfInit = ReturnType<typeof initSunmaoUI>;
|
||||
|
||||
@ -32,203 +34,198 @@ type Props = {
|
||||
appStorage: AppStorage;
|
||||
};
|
||||
|
||||
export const Editor: React.FC<Props> = ({
|
||||
App,
|
||||
registry,
|
||||
stateStore,
|
||||
appModelManager,
|
||||
appStorage,
|
||||
}) => {
|
||||
const { components } = useAppModel(appModelManager);
|
||||
export const Editor: React.FC<Props> = 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<Application>(() => {
|
||||
return {
|
||||
version: 'sunmao/v1',
|
||||
kind: 'Application',
|
||||
metadata: {
|
||||
name: 'some App',
|
||||
},
|
||||
spec: {
|
||||
components,
|
||||
},
|
||||
};
|
||||
}, [components]);
|
||||
|
||||
const app = useMemo<Application>(() => {
|
||||
return {
|
||||
version: 'sunmao/v1',
|
||||
kind: 'Application',
|
||||
metadata: {
|
||||
name: 'some App',
|
||||
},
|
||||
spec: {
|
||||
components,
|
||||
},
|
||||
};
|
||||
}, [components]);
|
||||
|
||||
const appComponent = useMemo(() => {
|
||||
return (
|
||||
<App
|
||||
options={app}
|
||||
debugEvent={false}
|
||||
debugStore={false}
|
||||
gridCallbacks={gridCallbacks}
|
||||
componentWrapper={ComponentWrapper}
|
||||
/>
|
||||
);
|
||||
}, [app, gridCallbacks]);
|
||||
|
||||
const renderMain = () => {
|
||||
const appBox = (
|
||||
<Box flex="1" background="gray.50" p={4}>
|
||||
<Box
|
||||
width="100%"
|
||||
height="100%"
|
||||
background="white"
|
||||
transform={`scale(${scale / 100})`}
|
||||
>
|
||||
<Box id={DIALOG_CONTAINER_ID} width="full" height="full" position="absolute" />
|
||||
{appComponent}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
if (codeMode) {
|
||||
const appComponent = useMemo(() => {
|
||||
return (
|
||||
<Flex width="100%" height="100%">
|
||||
<Box flex="1">
|
||||
<SchemaEditor defaultCode={JSON.stringify(app, null, 2)} onChange={setCode} />
|
||||
<App
|
||||
options={app}
|
||||
debugEvent={false}
|
||||
debugStore={false}
|
||||
gridCallbacks={gridCallbacks}
|
||||
componentWrapper={ComponentWrapper}
|
||||
/>
|
||||
);
|
||||
}, [app, gridCallbacks]);
|
||||
|
||||
const renderMain = () => {
|
||||
const appBox = (
|
||||
<Box flex="1" background="gray.50" p={4}>
|
||||
<Box
|
||||
width="100%"
|
||||
height="100%"
|
||||
background="white"
|
||||
transform={`scale(${scale / 100})`}
|
||||
>
|
||||
<Box
|
||||
id={DIALOG_CONTAINER_ID}
|
||||
width="full"
|
||||
height="full"
|
||||
position="absolute"
|
||||
/>
|
||||
{appComponent}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
if (codeMode) {
|
||||
return (
|
||||
<Flex width="100%" height="100%">
|
||||
<Box flex="1">
|
||||
<SchemaEditor
|
||||
defaultCode={JSON.stringify(app, null, 2)}
|
||||
onChange={setCode}
|
||||
/>
|
||||
</Box>
|
||||
{appBox}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Box width="280px" borderRightWidth="1px" borderColor="gray.200">
|
||||
<Tabs
|
||||
align="center"
|
||||
height="100%"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
textAlign="left"
|
||||
isLazy
|
||||
>
|
||||
<TabList background="gray.50">
|
||||
<Tab>Explorer</Tab>
|
||||
<Tab>UI Tree</Tab>
|
||||
<Tab>State</Tab>
|
||||
</TabList>
|
||||
<TabPanels flex="1" overflow="auto">
|
||||
<TabPanel>
|
||||
<Explorer appStorage={appStorage} />
|
||||
</TabPanel>
|
||||
<TabPanel p={0}>
|
||||
<StructureTree
|
||||
components={components}
|
||||
selectedComponentId={editorStore.selectedComponentId}
|
||||
onSelectComponent={id => editorStore.setSelectedComponentId(id)}
|
||||
registry={registry}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel p={0} height="100%">
|
||||
<StateEditor code={JSON.stringify(stateStore, null, 2)} />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Box>
|
||||
{appBox}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Box width="280px" borderRightWidth="1px" borderColor="gray.200">
|
||||
<Tabs
|
||||
align="center"
|
||||
height="100%"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
textAlign="left"
|
||||
isLazy
|
||||
>
|
||||
<TabList background="gray.50">
|
||||
<Tab>Explorer</Tab>
|
||||
<Tab>UI Tree</Tab>
|
||||
<Tab>State</Tab>
|
||||
</TabList>
|
||||
<TabPanels flex="1" overflow="auto">
|
||||
<TabPanel>
|
||||
<Explorer appStorage={appStorage} />
|
||||
</TabPanel>
|
||||
<TabPanel p={0}>
|
||||
<StructureTree
|
||||
components={components}
|
||||
selectedComponentId={selectedComponentId}
|
||||
onSelectComponent={id => setSelectedComponentId(id)}
|
||||
registry={registry}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel p={0} height="100%">
|
||||
<StateEditor code={JSON.stringify(stateStore, null, 2)} />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Box>
|
||||
{appBox}
|
||||
<Box width="320px" borderLeftWidth="1px" borderColor="gray.200" overflow="auto">
|
||||
<Tabs
|
||||
align="center"
|
||||
textAlign="left"
|
||||
height="100%"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
>
|
||||
<TabList background="gray.50">
|
||||
<Tab>Inspect</Tab>
|
||||
<Tab>Insert</Tab>
|
||||
</TabList>
|
||||
<TabPanels flex="1" overflow="auto">
|
||||
<TabPanel p={0}>
|
||||
<ComponentForm
|
||||
app={app}
|
||||
selectedId={selectedComponentId}
|
||||
registry={registry}
|
||||
appModelManager={appModelManager}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel p={0}>
|
||||
<ComponentList registry={registry} />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<KeyboardEventWrapper selectedComponentId={selectedComponentId}>
|
||||
<Box display="flex" height="100%" width="100%" flexDirection="column">
|
||||
<EditorHeader
|
||||
scale={scale}
|
||||
setScale={setScale}
|
||||
onPreview={() => setPreview(true)}
|
||||
codeMode={codeMode}
|
||||
onCodeMode={v => {
|
||||
setCodeMode(v);
|
||||
if (!v && code) {
|
||||
eventBus.send('operation', new ReplaceAppLeafOperation(JSON.parse(code)));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Box display="flex" flex="1" overflow="auto">
|
||||
{renderMain()}
|
||||
</Box>
|
||||
</Box>
|
||||
{preview && (
|
||||
<PreviewModal onClose={() => setPreview(false)}>
|
||||
<Box width="100%" height="100%">
|
||||
<App
|
||||
options={JSON.parse(JSON.stringify(app))}
|
||||
debugEvent={false}
|
||||
debugStore={false}
|
||||
/>
|
||||
<Box width="320px" borderLeftWidth="1px" borderColor="gray.200" overflow="auto">
|
||||
<Tabs
|
||||
align="center"
|
||||
textAlign="left"
|
||||
height="100%"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
>
|
||||
<TabList background="gray.50">
|
||||
<Tab>Inspect</Tab>
|
||||
<Tab>Insert</Tab>
|
||||
</TabList>
|
||||
<TabPanels flex="1" overflow="auto">
|
||||
<TabPanel p={0}>
|
||||
<ComponentForm
|
||||
app={app}
|
||||
selectedId={editorStore.selectedComponentId}
|
||||
registry={registry}
|
||||
appModelManager={appModelManager}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel p={0}>
|
||||
<ComponentList registry={registry} />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Box>
|
||||
</PreviewModal>
|
||||
)}
|
||||
</KeyboardEventWrapper>
|
||||
);
|
||||
};
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<KeyboardEventWrapper selectedComponentId={editorStore.selectedComponentId}>
|
||||
<Box display="flex" height="100%" width="100%" flexDirection="column">
|
||||
<EditorHeader
|
||||
scale={scale}
|
||||
setScale={setScale}
|
||||
onPreview={() => setPreview(true)}
|
||||
codeMode={codeMode}
|
||||
onCodeMode={v => {
|
||||
setCodeMode(v);
|
||||
if (!v && code) {
|
||||
eventBus.send('operation', new ReplaceAppLeafOperation(JSON.parse(code)));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Box display="flex" flex="1" overflow="auto">
|
||||
{renderMain()}
|
||||
</Box>
|
||||
</Box>
|
||||
{preview && (
|
||||
<PreviewModal onClose={() => setPreview(false)}>
|
||||
<Box width="100%" height="100%">
|
||||
<App
|
||||
options={JSON.parse(JSON.stringify(app))}
|
||||
debugEvent={false}
|
||||
debugStore={false}
|
||||
/>
|
||||
</Box>
|
||||
</PreviewModal>
|
||||
)}
|
||||
</KeyboardEventWrapper>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -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<Update
|
||||
do(prev: ApplicationComponent[]): ApplicationComponent[] {
|
||||
this.prevId = this.context.componentId || prev[0].id;
|
||||
setTimeout(() => {
|
||||
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;
|
||||
}
|
||||
|
10
yarn.lock
10
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"
|
||||
|
Loading…
Reference in New Issue
Block a user