diff --git a/packages/editor/src/components/Editor.tsx b/packages/editor/src/components/Editor.tsx index c64297b9..b1aa76c3 100644 --- a/packages/editor/src/components/Editor.tsx +++ b/packages/editor/src/components/Editor.tsx @@ -1,7 +1,7 @@ import { useMemo, useState } from 'react'; import { GridCallbacks } from '@meta-ui/runtime'; import { Box } from '@chakra-ui/react'; -import { css } from '@emotion/react'; +import produce from 'immer'; import { App } from '../metaUI'; import { StructureTree } from './StructureTree'; import { @@ -15,7 +15,6 @@ import { useAppModel } from '../operations/useAppModel'; import { KeyboardEventWrapper } from './KeyboardEventWrapper'; import { genComponentWrapper } from './ComponentWrapper'; -let count = 0; export const Editor = () => { const [selectedComponentId, setSelectedComponentId] = useState(''); const [hoverComponentId, setHoverComponentId] = useState(''); @@ -47,55 +46,38 @@ export const Editor = () => { setHoverComponentId, ]); - // const Wrapper: React.FC<{ id: string }> = useMemo(() => { - // return props => { - // const style = css` - // height: 100%; - // box-shadow: 0 0 ${props.id === selectedComponentId ? 1 : 0}px red; - // `; - // const onClick = (e: React.MouseEvent) => { - // e.stopPropagation(); - // setSelectedComponentId(() => props.id); - // }; - // return ( - //
- // {props.children} - //
- // ); - // }; - // }, [selectedComponentId]); + const gridCallbacks: GridCallbacks = useMemo(() => { + return { + onDragStop(id, layout) { + console.log('dragstop'); + eventBus.send( + 'operation', + new ModifyComponentPropertyOperation(id, 'layout', layout) + ); + }, + onDrop(id, layout, _, e) { + const component = e.dataTransfer?.getData('component') || ''; + const componentId = `component${layout.length++}`; + eventBus.send( + 'operation', + new CreateComponentOperation(id, 'container', component, componentId) + ); - const gridCallbacks: GridCallbacks = { - onDragStop(id, layout) { - console.log('dragstop'); - eventBus.send( - 'operation', - new ModifyComponentPropertyOperation(id, 'layout', layout) - ); - }, - onDrop(id, layout, item, e) { - const component = e.dataTransfer?.getData('component') || ''; - const componentId = `component${count++}`; - eventBus.send( - 'operation', - new CreateComponentOperation(id, 'container', component, componentId) - ); + const newLayout = produce(layout, draft => { + draft.forEach(l => { + if (l.i === '__dropping-elem__') { + l.i = componentId; + } + }); + }).filter(v => !!v); // there is unknown empty in array - const newLayout = [ - ...layout, - { - ...item, - w: 3, - i: componentId, - }, - ]; - - eventBus.send( - 'operation', - new ModifyComponentPropertyOperation(id, 'layout', newLayout) - ); - }, - }; + eventBus.send( + 'operation', + new ModifyComponentPropertyOperation(id, 'layout', newLayout) + ); + }, + }; + }, []); return ( diff --git a/packages/editor/src/main.tsx b/packages/editor/src/main.tsx index f9bea502..62211a0c 100644 --- a/packages/editor/src/main.tsx +++ b/packages/editor/src/main.tsx @@ -2,7 +2,11 @@ import { ChakraProvider } from '@chakra-ui/react'; import { css } from '@emotion/react'; import { StrictMode } from 'react'; import ReactDOM from 'react-dom'; +import 'react-grid-layout/css/styles.css'; +import 'react-resizable/css/styles.css'; + import { Editor } from './components/Editor'; + export default function renderApp() { ReactDOM.render( diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 6415297d..4e0673cf 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -34,6 +34,7 @@ "react-dom": "^17.0.0", "react-grid-layout": "^1.3.0", "react-markdown": "^6.0.2", + "react-resize-detector": "^6.7.6", "react-simple-code-editor": "^0.11.0", "wouter": "^2.7.4" }, diff --git a/packages/runtime/src/components/_internal/GridLayout.tsx b/packages/runtime/src/components/_internal/GridLayout.tsx index f0f9fd3a..a90aced6 100644 --- a/packages/runtime/src/components/_internal/GridLayout.tsx +++ b/packages/runtime/src/components/_internal/GridLayout.tsx @@ -1,41 +1,41 @@ -import RGL, { WidthProvider } from 'react-grid-layout'; -import { Static, Type } from '@sinclair/typebox'; +import RGL from 'react-grid-layout'; +import { css } from '@emotion/react'; +import { useResizeDetector } from 'react-resize-detector'; import 'react-grid-layout/css/styles.css'; import 'react-resizable/css/styles.css'; import { GRID_HEIGHT } from '../../constants'; -const ReactGridLayout = WidthProvider(RGL); +const GridLayout: React.FC = props => { + const { children } = props; + const spacing = 10; + const { width, ref } = useResizeDetector(); -export const LayoutPropertySchema = Type.Array( - Type.Object({ - x: Type.Number(), - y: Type.Number(), - w: Type.Number(), - h: Type.Number(), - i: Type.String(), - isResizable: Type.Optional(Type.Boolean()), - }) -); + const bgCss = css` + height: 100%; + background: white; + background-image: linear-gradient(#eee 1px, transparent 0), + linear-gradient(90deg, #eee 1px, transparent 0); + background-size: ${(width || 0) / 12}px ${GRID_HEIGHT + spacing}px; + background-position: 0px ${spacing / 2}px; + `; -const GridLayout: React.FC<{ - layout: Static; - onDragStop?: (layout: RGL.Layout[]) => void; - onDrop?: (layout: RGL.Layout[], item: RGL.Layout, event: DragEvent) => void; -}> = ({ children, layout, onDragStop, onDrop }) => { return ( - - {children} - +
+ + {children} + +
); }; diff --git a/packages/runtime/src/components/core/GridLayout.tsx b/packages/runtime/src/components/core/GridLayout.tsx index 592805fa..4364de0c 100644 --- a/packages/runtime/src/components/core/GridLayout.tsx +++ b/packages/runtime/src/components/core/GridLayout.tsx @@ -1,10 +1,9 @@ import React, { Suspense } from 'react'; -import RGL from 'react-grid-layout'; import { ComponentImplementation } from '../../services/registry'; import { createComponent } from '@meta-ui/core'; import { getSlots } from '../_internal/Slot'; -import { LayoutPropertySchema } from '../../components/_internal/GridLayout'; import { Static, Type } from '@sinclair/typebox'; +import { partial } from 'lodash'; const BaseGridLayout = React.lazy(() => import('../../components/_internal/GridLayout')); @@ -14,25 +13,33 @@ const GridLayout: ComponentImplementation> = ({ gridCallbacks, component, }) => { - const onDragStop = (layout: RGL.Layout[]) => { - gridCallbacks?.onDragStop && gridCallbacks?.onDragStop(component.id, layout); - }; - const onDrop = (layout: RGL.Layout[], item: RGL.Layout, e: DragEvent) => { - gridCallbacks?.onDrop && gridCallbacks?.onDrop(component.id, layout, item, e); - }; + const onDragStop = gridCallbacks?.onDragStop + ? partial(gridCallbacks.onDragStop, component.id) + : undefined; + const onDrop = gridCallbacks?.onDrop + ? partial(gridCallbacks.onDrop, component.id) + : undefined; + return ( -
- - {getSlots(slotsMap, 'container')} - -
+ + {getSlots(slotsMap, 'container')} +
); }; const PropsSchema = Type.Object({ - layout: LayoutPropertySchema, + layout: Type.Array( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + w: Type.Number(), + h: Type.Number(), + i: Type.String(), + isResizable: Type.Optional(Type.Boolean()), + }) + ), }); export default { diff --git a/yarn.lock b/yarn.lock index e3dc951b..42c0d302 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3016,6 +3016,11 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/resize-observer-browser@^0.1.6": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.6.tgz#d8e6c2f830e2650dc06fe74464472ff64b54a302" + integrity sha512-61IfTac0s9jvNtBCpyo86QeaN8qqpMGHdK0uGKCCIy2dt5/Yk84VduHIdWAcmkC5QvdkPL0p5eWYgUZtHKKUVg== + "@types/scheduler@*": version "0.16.2" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" @@ -6379,6 +6384,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" @@ -7684,6 +7694,16 @@ react-resizable@^3.0.4: prop-types "15.x" react-draggable "^4.0.3" +react-resize-detector@^6.7.6: + version "6.7.6" + resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-6.7.6.tgz#4416994e5ead7eba76606e3a248a1dfca49b67a3" + integrity sha512-/6RZlul1yePSoYJxWxmmgjO320moeLC/khrwpEVIL+D2EjLKhqOwzFv+H8laMbImVj7Zu4FlMa0oA7au3/ChjQ== + dependencies: + "@types/resize-observer-browser" "^0.1.6" + lodash.debounce "^4.0.8" + lodash.throttle "^4.1.1" + resize-observer-polyfill "^1.5.1" + react-simple-code-editor@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.11.0.tgz#bb57c7c29b570f2ab229872599eac184f5bc673c" @@ -7952,6 +7972,11 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"