Merge branch 'main' into feat/new-trait

* main:
  Publish
  fix: fix changing wrong tab when drag the component
  feat: add refresh button

# Conflicts:
#	packages/runtime/src/components/_internal/ImplWrapper.tsx
#	packages/runtime/src/components/core/Dummy.tsx
#	packages/runtime/src/traits/core/State.tsx
#	packages/runtime/src/types/trait.ts
This commit is contained in:
Bowen Tan 2022-04-22 16:33:36 +08:00
commit 0edbfdb6e7
13 changed files with 80 additions and 85 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@sunmao-ui/arco-lib", "name": "@sunmao-ui/arco-lib",
"version": "0.1.7", "version": "0.1.8",
"homepage": "https://github.com/webzard-io/sunmao-ui-arco-lib", "homepage": "https://github.com/webzard-io/sunmao-ui-arco-lib",
"license": "MIT", "license": "MIT",
"publishConfig": { "publishConfig": {
@ -31,8 +31,8 @@
"@arco-design/web-react": "^2.29.0", "@arco-design/web-react": "^2.29.0",
"@emotion/css": "^11.7.1", "@emotion/css": "^11.7.1",
"@sinclair/typebox": "^0.21.2", "@sinclair/typebox": "^0.21.2",
"@sunmao-ui/core": "^0.5.4", "@sunmao-ui/core": "^0.5.5",
"@sunmao-ui/runtime": "^0.5.6", "@sunmao-ui/runtime": "^0.5.7",
"eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-react-hooks": "^4.3.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
@ -42,9 +42,9 @@
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.14.170", "@types/lodash": "^4.14.170",
"@types/lodash-es": "^4.17.5", "@types/lodash-es": "^4.17.5",
"@types/react-resizable": "^1.7.4",
"@types/react": "^17.0.0", "@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^17.0.0",
"@types/react-resizable": "^1.7.4",
"@typescript-eslint/eslint-plugin": "^4.31.1", "@typescript-eslint/eslint-plugin": "^4.31.1",
"@typescript-eslint/parser": "^4.31.1", "@typescript-eslint/parser": "^4.31.1",
"@vitejs/plugin-react": "^1.0.0", "@vitejs/plugin-react": "^1.0.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "@sunmao-ui/chakra-ui-lib", "name": "@sunmao-ui/chakra-ui-lib",
"version": "0.3.7", "version": "0.3.8",
"description": "sunmao-ui chakra-ui library", "description": "sunmao-ui chakra-ui library",
"author": "sunmao-ui developers", "author": "sunmao-ui developers",
"homepage": "https://github.com/webzard-io/sunmao-ui#readme", "homepage": "https://github.com/webzard-io/sunmao-ui#readme",
@ -33,9 +33,9 @@
"@chakra-ui/react": "^1.7.1", "@chakra-ui/react": "^1.7.1",
"@emotion/styled": "^11.6.0", "@emotion/styled": "^11.6.0",
"@sinclair/typebox": "^0.21.2", "@sinclair/typebox": "^0.21.2",
"@sunmao-ui/core": "^0.5.4", "@sunmao-ui/core": "^0.5.5",
"@sunmao-ui/editor-sdk": "^0.1.8", "@sunmao-ui/editor-sdk": "^0.1.9",
"@sunmao-ui/runtime": "^0.5.6", "@sunmao-ui/runtime": "^0.5.7",
"chakra-react-select": "^1.3.2", "chakra-react-select": "^1.3.2",
"framer-motion": "^4", "framer-motion": "^4",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",

View File

@ -1,6 +1,6 @@
{ {
"name": "@sunmao-ui/core", "name": "@sunmao-ui/core",
"version": "0.5.4", "version": "0.5.5",
"description": "sunmao-ui core", "description": "sunmao-ui core",
"author": "sunmao-ui developers", "author": "sunmao-ui developers",
"homepage": "https://github.com/webzard-io/sunmao-ui#readme", "homepage": "https://github.com/webzard-io/sunmao-ui#readme",

View File

@ -1,6 +1,6 @@
{ {
"name": "@sunmao-ui/editor-sdk", "name": "@sunmao-ui/editor-sdk",
"version": "0.1.8", "version": "0.1.9",
"description": "The SDK for SunMao Editor", "description": "The SDK for SunMao Editor",
"author": "sunmao-ui developers", "author": "sunmao-ui developers",
"homepage": "https://github.com/webzard-io/sunmao-ui#readme", "homepage": "https://github.com/webzard-io/sunmao-ui#readme",
@ -29,8 +29,8 @@
"@emotion/css": "^11.7.1", "@emotion/css": "^11.7.1",
"@emotion/react": "^11.1.1", "@emotion/react": "^11.1.1",
"@sinclair/typebox": "^0.21.2", "@sinclair/typebox": "^0.21.2",
"@sunmao-ui/core": "^0.5.4", "@sunmao-ui/core": "^0.5.5",
"@sunmao-ui/runtime": "^0.5.6", "@sunmao-ui/runtime": "^0.5.7",
"codemirror": "^5.63.3", "codemirror": "^5.63.3",
"formik": "^2.2.9", "formik": "^2.2.9",
"immer": "^9.0.6", "immer": "^9.0.6",

View File

@ -1,6 +1,6 @@
{ {
"name": "@sunmao-ui/editor", "name": "@sunmao-ui/editor",
"version": "0.5.8", "version": "0.5.9",
"description": "sunmao-ui editor", "description": "sunmao-ui editor",
"author": "sunmao-ui developers", "author": "sunmao-ui developers",
"homepage": "https://github.com/webzard-io/sunmao-ui#readme", "homepage": "https://github.com/webzard-io/sunmao-ui#readme",
@ -36,11 +36,11 @@
"@emotion/css": "^11.7.1", "@emotion/css": "^11.7.1",
"@emotion/react": "^11.1.1", "@emotion/react": "^11.1.1",
"@sinclair/typebox": "^0.21.2", "@sinclair/typebox": "^0.21.2",
"@sunmao-ui/arco-lib": "^0.1.7", "@sunmao-ui/arco-lib": "^0.1.8",
"@sunmao-ui/chakra-ui-lib": "^0.3.7", "@sunmao-ui/chakra-ui-lib": "^0.3.8",
"@sunmao-ui/core": "^0.5.4", "@sunmao-ui/core": "^0.5.5",
"@sunmao-ui/editor-sdk": "^0.1.8", "@sunmao-ui/editor-sdk": "^0.1.9",
"@sunmao-ui/runtime": "^0.5.6", "@sunmao-ui/runtime": "^0.5.7",
"acorn": "^8.7.0", "acorn": "^8.7.0",
"acorn-loose": "^8.3.0", "acorn-loose": "^8.3.0",
"acorn-walk": "^8.2.0", "acorn-walk": "^8.2.0",

View File

@ -1,4 +1,4 @@
import React, { useMemo, useState, useEffect } from 'react'; import React, { useMemo, useState, useCallback, useEffect } from 'react';
import { Application } from '@sunmao-ui/core'; import { Application } from '@sunmao-ui/core';
import { import {
GridCallbacks, GridCallbacks,
@ -38,7 +38,8 @@ type Props = {
stateStore: ReturnOfInit['stateManager']['store']; stateStore: ReturnOfInit['stateManager']['store'];
services: EditorServices; services: EditorServices;
libs: SunmaoLib[]; libs: SunmaoLib[];
uiProps: UIPros onRefresh: () => void;
uiProps: UIPros;
}; };
const ApiFormStyle = css` const ApiFormStyle = css`
@ -51,7 +52,7 @@ const ApiFormStyle = css`
`; `;
export const Editor: React.FC<Props> = observer( export const Editor: React.FC<Props> = observer(
({ App, registry, stateStore, services, libs, uiProps }) => { ({ App, registry, stateStore, services, libs, uiProps, onRefresh: onRefreshApp }) => {
const { eventBus, editorStore } = services; const { eventBus, editorStore } = services;
const { const {
components, components,
@ -69,12 +70,7 @@ export const Editor: React.FC<Props> = observer(
const [preview, setPreview] = useState(false); const [preview, setPreview] = useState(false);
const [codeMode, setCodeMode] = useState(false); const [codeMode, setCodeMode] = useState(false);
const [code, setCode] = useState(''); const [code, setCode] = useState('');
const [recoverKey, setRecoverKey] = useState(0); const [isDisplayApp, setIsDisplayApp] = useState(true);
const [isError, setIsError] = useState<boolean>(false);
const onError = (err: Error | null) => {
setIsError(err !== null);
};
const gridCallbacks: GridCallbacks = useMemo(() => { const gridCallbacks: GridCallbacks = useMemo(() => {
return { return {
@ -118,8 +114,8 @@ export const Editor: React.FC<Props> = observer(
}, [components]); }, [components]);
const appComponent = useMemo(() => { const appComponent = useMemo(() => {
return ( return isDisplayApp ? (
<ErrorBoundary key={recoverKey} onError={onError}> <ErrorBoundary>
<App <App
options={app} options={app}
debugEvent={false} debugEvent={false}
@ -127,8 +123,8 @@ export const Editor: React.FC<Props> = observer(
gridCallbacks={gridCallbacks} gridCallbacks={gridCallbacks}
/> />
</ErrorBoundary> </ErrorBoundary>
); ) : null;
}, [App, app, gridCallbacks, recoverKey]); }, [App, app, gridCallbacks, isDisplayApp]);
const dataSourceForm = useMemo(() => { const dataSourceForm = useMemo(() => {
let component: React.ReactNode = <ComponentForm services={services} />; let component: React.ReactNode = <ComponentForm services={services} />;
@ -148,16 +144,29 @@ export const Editor: React.FC<Props> = observer(
return component; return component;
}, [activeDataSource, services, activeDataSourceType]); }, [activeDataSource, services, activeDataSourceType]);
useEffect(() => { const onRefresh = useCallback(()=> {
// when errors happened, `ErrorBoundary` wouldn't update until rerender services.stateManager.clear();
// so after the errors are fixed, would trigger this effect before `setError(false)` setIsDisplayApp(false);
// the process to handle the error is: onRefreshApp();
// app change -> error happen -> setError(true) -> setRecoverKey(recoverKey + 1) -> app change -> setRecoverKey(recoverKey + 1) -> setError(false) }, [services.stateManager, onRefreshApp]);
if (isError) { useEffect(()=> {
setRecoverKey(recoverKey + 1); // Wait until the app is completely unmounted before remounting it
if (isDisplayApp === false) {
setIsDisplayApp(true);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps }, [isDisplayApp]);
}, [app, isError]); // it only should depend on the app schema and `isError` to update const onCodeMode = useCallback(v => {
setCodeMode(v);
if (!v && code) {
eventBus.send(
'operation',
genOperation(registry, 'replaceApp', {
app: new AppModel(JSON.parse(code).spec.components, registry),
})
);
}
}, [code, eventBus, registry]);
const onPreview = useCallback(() => setPreview(true), []);
const renderMain = () => { const renderMain = () => {
const appBox = ( const appBox = (
@ -324,19 +333,10 @@ export const Editor: React.FC<Props> = observer(
<EditorHeader <EditorHeader
scale={scale} scale={scale}
setScale={setScale} setScale={setScale}
onPreview={() => setPreview(true)} onPreview={onPreview}
codeMode={codeMode} codeMode={codeMode}
onCodeMode={v => { onRefresh={onRefresh}
setCodeMode(v); onCodeMode={onCodeMode}
if (!v && code) {
eventBus.send(
'operation',
genOperation(registry, 'replaceApp', {
app: new AppModel(JSON.parse(code).spec.components, registry),
})
);
}
}}
/> />
<Box display="flex" flex="1" overflow="auto"> <Box display="flex" flex="1" overflow="auto">
{renderMain()} {renderMain()}

View File

@ -7,7 +7,8 @@ export const EditorHeader: React.FC<{
onPreview: () => void; onPreview: () => void;
codeMode: boolean; codeMode: boolean;
onCodeMode: (v: boolean) => void; onCodeMode: (v: boolean) => void;
}> = ({ scale, setScale, onPreview, onCodeMode, codeMode }) => { onRefresh: () => void;
}> = ({ scale, setScale, onPreview, onCodeMode, onRefresh, codeMode }) => {
return ( return (
<Flex p={2} borderBottomWidth="2px" borderColor="gray.200" align="center"> <Flex p={2} borderBottomWidth="2px" borderColor="gray.200" align="center">
<Flex flex="1"> <Flex flex="1">
@ -31,6 +32,9 @@ export const EditorHeader: React.FC<{
</Button> </Button>
</Flex> </Flex>
<Flex flex="1" justify="end"> <Flex flex="1" justify="end">
<Button colorScheme="blue" marginRight="8px" onClick={onRefresh}>
refresh
</Button>
<Button colorScheme="blue" onClick={onPreview}> <Button colorScheme="blue" onClick={onPreview}>
preview preview
</Button> </Button>

View File

@ -2,18 +2,15 @@ import React from 'react';
type Props = { type Props = {
onError?: (error: Error | null) => void; onError?: (error: Error | null) => void;
} };
class ErrorBoundary extends React.Component< class ErrorBoundary extends React.Component<Props, { error: Error | null }> {
Props,
{ error: unknown }
> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.state = { error: null }; this.state = { error: null };
} }
static getDerivedStateFromError(error: unknown) { static getDerivedStateFromError(error: Error) {
return { error }; return { error };
} }
@ -27,7 +24,7 @@ class ErrorBoundary extends React.Component<
render() { render() {
if (this.state.error) { if (this.state.error) {
return String(this.state.error); return <div style={{ whiteSpace: 'pre-line' }}>{this.state.error.stack}</div>;
} }
return this.props.children; return this.props.children;

View File

@ -1,8 +1,8 @@
enum ExplorerMenuTabs { enum ExplorerMenuTabs {
// EXPLORER = 0, EXPLORER = 0,
UI_TREE = 0, UI_TREE = 1,
DATA = 1, DATA = 2,
STATE = 2 STATE = 3
} }
enum ToolMenuTabs { enum ToolMenuTabs {

View File

@ -1,7 +1,7 @@
import { Editor as _Editor } from './components/Editor'; import { Editor as _Editor } from './components/Editor';
import { initSunmaoUI, SunmaoLib, SunmaoUIRuntimeProps } from '@sunmao-ui/runtime'; import { initSunmaoUI, SunmaoLib, SunmaoUIRuntimeProps } from '@sunmao-ui/runtime';
import { AppModelManager } from './operations/AppModelManager'; import { AppModelManager } from './operations/AppModelManager';
import React from 'react'; import React, { useState, useCallback } from 'react';
import { import {
widgets as internalWidgets, widgets as internalWidgets,
WidgetManager, WidgetManager,
@ -96,15 +96,21 @@ export function initSunmaoUIEditor(props: SunmaoUIEditorProps = {}) {
}; };
const Editor: React.FC = () => { const Editor: React.FC = () => {
const [store, setStore] = useState(stateManager.store);
const onRefresh = useCallback(()=> {
setStore(stateManager.store);
}, []);
return ( return (
<ChakraProvider theme={editorTheme}> <ChakraProvider theme={editorTheme}>
<_Editor <_Editor
App={App} App={App}
eleMap={ui.eleMap} eleMap={ui.eleMap}
registry={registry} registry={registry}
stateStore={stateManager.store} stateStore={store}
services={services} services={services}
libs={props.libs || []} libs={props.libs || []}
onRefresh={onRefresh}
uiProps={props.uiProps||{}} uiProps={props.uiProps||{}}
/> />
</ChakraProvider> </ChakraProvider>

View File

@ -1,6 +1,6 @@
{ {
"name": "@sunmao-ui/runtime", "name": "@sunmao-ui/runtime",
"version": "0.5.6", "version": "0.5.7",
"description": "sunmao-ui runtime", "description": "sunmao-ui runtime",
"author": "sunmao-ui developers", "author": "sunmao-ui developers",
"homepage": "https://github.com/webzard-io/sunmao-ui#readme", "homepage": "https://github.com/webzard-io/sunmao-ui#readme",
@ -31,7 +31,7 @@
"dependencies": { "dependencies": {
"@emotion/css": "^11.7.1", "@emotion/css": "^11.7.1",
"@sinclair/typebox": "^0.21.2", "@sinclair/typebox": "^0.21.2",
"@sunmao-ui/core": "^0.5.4", "@sunmao-ui/core": "^0.5.5",
"@vue/reactivity": "^3.1.5", "@vue/reactivity": "^3.1.5",
"@vue/shared": "^3.2.20", "@vue/shared": "^3.2.20",
"copy-to-clipboard": "^3.3.1", "copy-to-clipboard": "^3.3.1",

View File

@ -1,5 +1,4 @@
import { implementRuntimeComponent } from '../../utils/buildKit'; import { implementRuntimeComponent } from '../../utils/buildKit';
// import { useEffect } from 'react';
import { Type } from '@sinclair/typebox'; import { Type } from '@sinclair/typebox';
export default implementRuntimeComponent({ export default implementRuntimeComponent({
@ -25,21 +24,5 @@ export default implementRuntimeComponent({
events: [], events: [],
}, },
})(() => { })(() => {
// console.info('####Component Render', component.id);
// useEffect(() => {
// console.info('####Component DidMount', component.id);
// componentDidMount?.forEach(e => e());
// }, [component.id, componentDidMount]);
// useEffect(() => {
// console.info('####Component Update', component.id);
// componentDidUpdate?.forEach(e => e());
// }, [component.id, componentDidMount, componentDidUpdate]);
// useEffect(() => {
// return () => {
// console.info('Component DidUnmount', component.id, unmountHooks);
// unmountHooks?.forEach(e => e());
// };
// }, [component.id, unmountHooks]);
return null; return null;
}); });

View File

@ -32,6 +32,11 @@ const StateTraitFactory: TraitImplFactory<Static<typeof PropsSpec>> = () => {
mergeState({ [key]: initialValue }); mergeState({ [key]: initialValue });
}, },
], ],
unmountHooks: [
() => {
HasInitializedMap.delete(hashId);
},
],
}, },
}; };
}; };