mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-27 08:39:59 +08:00
commit
c2fe8d7231
@ -1,4 +1,4 @@
|
||||
import { FormControl, FormLabel, Input } from '@chakra-ui/react';
|
||||
import { FormControl, FormLabel, Input, Box } from '@chakra-ui/react';
|
||||
import { Application } from '@meta-ui/core';
|
||||
import React from 'react';
|
||||
import { eventBus } from '../../eventBus';
|
||||
@ -31,10 +31,10 @@ export const ComponentForm: React.FC<Props> = props => {
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Box p={4}>
|
||||
<div>Component Form</div>
|
||||
<div>ID: {selectedComponent?.id}</div>
|
||||
<form>{fields}</form>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -1,43 +1,82 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Tabs,
|
||||
TabList,
|
||||
Tab,
|
||||
TabPanels,
|
||||
TabPanel,
|
||||
SimpleGrid,
|
||||
Flex,
|
||||
Box,
|
||||
} from '@chakra-ui/react';
|
||||
import { registry } from '../../metaUI';
|
||||
|
||||
export const ComponentList: React.FC = () => {
|
||||
const componentList = React.useMemo(() => {
|
||||
const groups: ReactElement[] = [];
|
||||
registry.components.forEach((componentMap, version) => {
|
||||
const components: ReactElement[] = [];
|
||||
componentMap.forEach(c => {
|
||||
const onDragStart = (e: any) => {
|
||||
e.dataTransfer.setData('component', `${c.version}/${c.metadata.name}`);
|
||||
};
|
||||
const cEle = (
|
||||
<div
|
||||
key={c.metadata.name}
|
||||
className="droppable-element"
|
||||
draggable={true}
|
||||
unselectable="on"
|
||||
onDragStart={onDragStart}
|
||||
>
|
||||
{`${c.version}/${c.metadata.name}`}
|
||||
</div>
|
||||
);
|
||||
|
||||
components.push(cEle);
|
||||
});
|
||||
const cGroupEle = (
|
||||
<div key={version}>
|
||||
<div>
|
||||
<strong>{version}</strong>
|
||||
</div>
|
||||
{components}
|
||||
</div>
|
||||
);
|
||||
|
||||
groups.push(cGroupEle);
|
||||
});
|
||||
|
||||
return groups;
|
||||
}, []);
|
||||
|
||||
return <div>{componentList}</div>;
|
||||
return (
|
||||
<Tabs>
|
||||
<TabList>
|
||||
{Array.from(registry.components.keys()).map(version => (
|
||||
<Tab key={version}>{version}</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
{Array.from(registry.components.keys()).map(version => (
|
||||
<TabPanel key={version} overflow="auto">
|
||||
<SimpleGrid columns={4} spacing={1}>
|
||||
{Array.from(registry.components.get(version)!.values()).map(c => {
|
||||
const onDragStart = (e: any) => {
|
||||
e.dataTransfer.setData('component', `${c.version}/${c.metadata.name}`);
|
||||
};
|
||||
const cEle = (
|
||||
<Flex
|
||||
key={c.metadata.name}
|
||||
flexDirection="column"
|
||||
align="center"
|
||||
justify="start">
|
||||
<Flex
|
||||
className="droppable-element"
|
||||
background="gray.100"
|
||||
width="60px"
|
||||
height="60px"
|
||||
borderRadius="md"
|
||||
align="center"
|
||||
justify="center"
|
||||
transition="ease 0.2s"
|
||||
_hover={{
|
||||
transform: 'scale(1.05)',
|
||||
background: 'gray.200',
|
||||
}}
|
||||
p={2}
|
||||
draggable
|
||||
unselectable="on"
|
||||
onDragStart={onDragStart}>
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="2693"
|
||||
width="30"
|
||||
height="30">
|
||||
<path
|
||||
d="M972.09863 1016.986301H259.506849c-19.638356 0-35.068493-15.430137-35.068493-35.068493V768.70137c-8.416438 5.610959-18.235616 9.819178-28.054794 12.624657h-1.40274c-12.624658 4.208219-26.652055 5.610959-39.276712 5.610959-77.150685 0-138.871233-63.123288-138.871233-138.871233 0-77.150685 63.123288-138.871233 138.871233-138.871232 14.027397 0 28.054795 2.805479 42.082191 7.013698 1.40274 0 2.805479 1.40274 4.20822 1.40274 7.013699 2.805479 14.027397 5.610959 19.638356 8.416438l2.805479 1.40274V269.326027c0-19.638356 15.430137-35.068493 35.068493-35.068493h228.646576l-4.20822-8.416438c-7.013699-11.221918-12.624658-22.443836-15.430137-35.068493v-1.40274c-4.208219-12.624658-5.610959-26.652055-5.610958-39.276712C462.90411 72.942466 526.027397 11.221918 601.775342 11.221918c77.150685 0 138.871233 63.123288 138.871233 138.871233 0 14.027397-2.805479 28.054795-7.013698 42.082191 0 1.40274-1.40274 2.805479-1.40274 4.20822-2.805479 7.013699-5.610959 14.027397-8.416438 19.638356l-7.013699 16.832877h255.29863c19.638356 0 35.068493 15.430137 35.068493 35.068493v314.213698c0 11.221918-5.610959 22.443836-15.430137 29.457535-9.819178 7.013699-22.443836 7.013699-33.665753 2.805479L897.753425 587.747945c-1.40274 0-1.40274-1.40274-2.80548-1.40274-2.805479-1.40274-7.013699-4.208219-11.221918-5.610958h-1.402739c-7.013699-2.805479-14.027397-2.805479-21.041096-2.80548-37.873973 0-68.734247 30.860274-68.734247 68.734247s30.860274 68.734247 68.734247 68.734246c7.013699 0 14.027397-1.40274 19.638356-2.805479 7.013699-1.40274 12.624658-5.610959 18.235616-8.416439 1.40274-1.40274 2.805479-1.40274 4.20822-2.805479l53.304109-25.249315c11.221918-5.610959 23.846575-4.208219 33.665754 1.40274s16.832877 18.235616 16.832876 29.457534V981.917808c0 19.638356-15.430137 35.068493-35.068493 35.068493z m-677.523288-70.136986h642.454795v-182.356164h-1.40274c-11.221918 7.013699-22.443836 12.624658-35.068493 16.832876h-1.40274c-12.624658 4.208219-26.652055 5.610959-39.276712 5.610959-77.150685 0-138.871233-63.123288-138.871233-138.871233 0-77.150685 63.123288-138.871233 138.871233-138.871232 14.027397 0 28.054795 2.805479 42.082192 7.013698 1.40274 0 2.805479 1.40274 4.208219 1.40274 7.013699 2.805479 14.027397 5.610959 19.638356 8.416438l9.819178 4.208219v-224.438356H662.093151c-11.221918 0-22.443836-5.610959-29.457535-15.430137-7.013699-9.819178-7.013699-22.443836-2.805479-33.665753l29.457534-67.331507c0-1.40274 1.40274-1.40274 1.40274-2.805479 1.40274-2.805479 4.208219-7.013699 5.610959-11.221918v-1.40274c2.805479-7.013699 2.805479-14.027397 2.805479-21.041096 0-37.873973-30.860274-68.734247-68.734246-68.734246s-68.734247 30.860274-68.734247 68.734246c0 7.013699 1.40274 14.027397 2.80548 19.638356 1.40274 7.013699 5.610959 12.624658 8.416438 18.235617 1.40274 1.40274 1.40274 2.805479 2.805479 4.208219l28.054795 60.317808c5.610959 11.221918 4.208219 23.846575-1.40274 33.665754s-18.235616 16.832877-29.457534 16.832876H294.575342v274.936987c0 11.221918-5.610959 22.443836-15.430137 29.457534-9.819178 7.013699-22.443836 7.013699-33.665753 2.805479L192.175342 589.150685c-1.40274 0-1.40274-1.40274-2.805479-1.40274-2.805479-1.40274-7.013699-4.208219-11.221918-5.610959h-1.40274c-7.013699-2.805479-14.027397-2.805479-21.041095-2.805479-37.873973 0-68.734247 30.860274-68.734247 68.734246s30.860274 68.734247 68.734247 68.734247c7.013699 0 14.027397-1.40274 19.638356-2.805479 7.013699-1.40274 12.624658-5.610959 18.235616-8.416439 1.40274-1.40274 2.805479-1.40274 4.208219-2.805479l46.290411-22.443836c11.221918-5.610959 23.846575-4.208219 33.665754 1.40274 9.819178 7.013699 16.832877 18.235616 16.832876 29.457534v235.660274z"
|
||||
p-id="2694"></path>
|
||||
</svg>
|
||||
</Flex>
|
||||
<Box
|
||||
p={2}
|
||||
whiteSpace="pre-wrap"
|
||||
textAlign="center"
|
||||
fontSize="x-small">
|
||||
{c.metadata.displayName}
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
return cEle;
|
||||
})}
|
||||
</SimpleGrid>
|
||||
</TabPanel>
|
||||
))}
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { GridCallbacks } from '@meta-ui/runtime';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { Box, Tabs, TabList, Tab, TabPanels, TabPanel } from '@chakra-ui/react';
|
||||
import { css } from '@emotion/react';
|
||||
import { App } from '../metaUI';
|
||||
import { last } from 'lodash';
|
||||
import { App, stateStore } from '../metaUI';
|
||||
import { StructureTree } from './StructureTree';
|
||||
import {
|
||||
CreateComponentOperation,
|
||||
@ -11,35 +12,40 @@ import {
|
||||
import { eventBus } from '../eventBus';
|
||||
import { ComponentForm } from './ComponentForm';
|
||||
import { ComponentList } from './ComponentsList';
|
||||
import { EditorHeader } from './EditorHeader';
|
||||
import { PreviewModal } from './PreviewModal';
|
||||
import { useAppModel } from '../operations/useAppModel';
|
||||
import { KeyboardEventWrapper } from './KeyboardEventWrapper';
|
||||
|
||||
let count = 0;
|
||||
export const Editor = () => {
|
||||
const [selectedComponentId, setSelectedComponentId] = useState('');
|
||||
const [scale, setScale] = useState(100);
|
||||
const [preview, setPreview] = useState(false);
|
||||
const { app } = useAppModel();
|
||||
|
||||
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<HTMLElement>) => {
|
||||
e.stopPropagation();
|
||||
setSelectedComponentId(() => props.id);
|
||||
};
|
||||
return (
|
||||
<div onClick={onClick} css={style}>
|
||||
<Box
|
||||
onClick={onClick}
|
||||
css={style}
|
||||
boxShadow={props.id === selectedComponentId ? 'outline' : undefined}>
|
||||
{props.children}
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
}, [selectedComponentId]);
|
||||
|
||||
const gridCallbacks: GridCallbacks = {
|
||||
onDragStop(id, layout) {
|
||||
console.log('dragstop');
|
||||
eventBus.send(
|
||||
'operation',
|
||||
new ModifyComponentPropertyOperation(id, 'layout', layout)
|
||||
@ -47,7 +53,8 @@ export const Editor = () => {
|
||||
},
|
||||
onDrop(id, layout, item, e) {
|
||||
const component = e.dataTransfer?.getData('component') || '';
|
||||
const componentId = `component${count++}`;
|
||||
const componentName = last(component.split('/'));
|
||||
const componentId = `${componentName}_${count++}`;
|
||||
eventBus.send(
|
||||
'operation',
|
||||
new CreateComponentOperation(id, 'container', component, componentId)
|
||||
@ -71,27 +78,86 @@ export const Editor = () => {
|
||||
|
||||
return (
|
||||
<KeyboardEventWrapper selectedComponentId={selectedComponentId}>
|
||||
<Box display="flex" height="100vh" width="100vw">
|
||||
<Box flex="1">
|
||||
<StructureTree app={app} onSelectComponent={id => setSelectedComponentId(id)} />
|
||||
</Box>
|
||||
<Box flex="1">
|
||||
<strong>Drag Component to canvas!</strong>
|
||||
<ComponentList />
|
||||
</Box>
|
||||
<Box flex="3" borderRight="2px solid black">
|
||||
<App
|
||||
options={app}
|
||||
debugEvent={false}
|
||||
debugStore={false}
|
||||
gridCallbacks={gridCallbacks}
|
||||
componentWrapper={Wrapper}
|
||||
/>
|
||||
</Box>
|
||||
<Box flex="1" borderRight="2px solid black">
|
||||
<ComponentForm app={app} selectedId={selectedComponentId} />
|
||||
<Box display="flex" height="100vh" width="100vw" flexDirection="column">
|
||||
<EditorHeader
|
||||
scale={scale}
|
||||
setScale={setScale}
|
||||
onPreview={() => setPreview(true)}
|
||||
/>
|
||||
<Box display="flex" flex="1">
|
||||
<Box width="280px" borderRightWidth="1px" borderColor="gray.200">
|
||||
<Tabs
|
||||
align="center"
|
||||
height="100%"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
textAlign="left">
|
||||
<TabList background="gray.50">
|
||||
<Tab>UI Tree</Tab>
|
||||
<Tab>State</Tab>
|
||||
</TabList>
|
||||
<TabPanels flex="1" overflow="auto">
|
||||
<TabPanel p={0}>
|
||||
<StructureTree
|
||||
app={app}
|
||||
onSelectComponent={id => setSelectedComponentId(id)}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel p={0}>
|
||||
<pre>{JSON.stringify(stateStore, null, 2)}</pre>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Box>
|
||||
<Box flex="1" background="gray.50" p={4}>
|
||||
<Box
|
||||
widht="100%"
|
||||
height="100%"
|
||||
background="white"
|
||||
transform={`scale(${scale / 100})`}>
|
||||
<App
|
||||
options={app}
|
||||
debugEvent={false}
|
||||
debugStore={false}
|
||||
gridCallbacks={gridCallbacks}
|
||||
componentWrapper={Wrapper}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box width="320px" borderLeftWidth="1px" borderColor="gray.200">
|
||||
<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} />
|
||||
</TabPanel>
|
||||
<TabPanel p={0}>
|
||||
<ComponentList />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Box>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
30
packages/editor/src/components/EditorHeader/EditorHeader.tsx
Normal file
30
packages/editor/src/components/EditorHeader/EditorHeader.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { Flex, Button, Box } from '@chakra-ui/react';
|
||||
|
||||
export const EditorHeader: React.FC<{
|
||||
scale: number;
|
||||
setScale: (v: number) => void;
|
||||
onPreview: () => void;
|
||||
}> = ({ scale, setScale, onPreview }) => {
|
||||
return (
|
||||
<Flex p={2} borderBottomWidth="2px" borderColor="gray.200" align="center">
|
||||
<Flex flex="1" />
|
||||
<Flex flex="1" align="center" justify="center">
|
||||
<Button size="sm" disabled={scale <= 50} onClick={() => setScale(scale - 10)}>
|
||||
-
|
||||
</Button>
|
||||
<Box fontSize="sm" mx="2" width={10} textAlign="center">
|
||||
{scale}%
|
||||
</Box>
|
||||
<Button size="sm" disabled={scale >= 100} onClick={() => setScale(scale + 10)}>
|
||||
+
|
||||
</Button>
|
||||
</Flex>
|
||||
<Flex flex="1" justify="end">
|
||||
<Button colorScheme="blue" onClick={onPreview}>
|
||||
preview
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
1
packages/editor/src/components/EditorHeader/index.ts
Normal file
1
packages/editor/src/components/EditorHeader/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './EditorHeader';
|
25
packages/editor/src/components/PreviewModal/PreviewModal.tsx
Normal file
25
packages/editor/src/components/PreviewModal/PreviewModal.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalHeader,
|
||||
ModalContent,
|
||||
ModalCloseButton,
|
||||
ModalBody,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
export const PreviewModal: React.FC<{ onClose: () => void }> = ({
|
||||
onClose,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<Modal onClose={onClose} size="full" isOpen>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Preview App</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>{children}</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
1
packages/editor/src/components/PreviewModal/index.ts
Normal file
1
packages/editor/src/components/PreviewModal/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './PreviewModal';
|
@ -30,10 +30,10 @@ function genComponent(
|
||||
): ApplicationComponent {
|
||||
const { version, name } = parseType(type);
|
||||
const cImpl = registry.getComponent(version, name);
|
||||
const initProperties = cImpl.metadata.defaultProperties;
|
||||
const initProperties = cImpl.metadata.exampleProperties;
|
||||
count++;
|
||||
return {
|
||||
id: id || `${name}${count}`,
|
||||
id: id || `${name}_${count}`,
|
||||
type: type,
|
||||
properties: initProperties,
|
||||
traits: [genSlotTrait(parentId, slot)],
|
||||
|
@ -87,8 +87,7 @@ const FormControlImpl: ComponentImplementation<{
|
||||
<FormControl
|
||||
isRequired={isRequired}
|
||||
isInvalid={!hideInvalid && (isInvalid || (!inputValue && isRequired))}
|
||||
css={FormControlCSS}
|
||||
>
|
||||
css={FormControlCSS}>
|
||||
<div css={FormControlContentCSS}>
|
||||
<FormLabel css={FormLabelCSS}>{label}</FormLabel>
|
||||
<Slot css={FormItemCSS} slotsMap={slotsMap} slot="content" />
|
||||
@ -117,7 +116,7 @@ export default {
|
||||
name: 'formControl',
|
||||
isResizable: false,
|
||||
isDraggable: true,
|
||||
displayName: 'FormControl',
|
||||
displayName: 'Form Control',
|
||||
description: 'chakra-ui formControl',
|
||||
exampleProperties: {
|
||||
label: 'name',
|
||||
|
@ -57,8 +57,7 @@ const NumberInput: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
clampValueOnBlur={clampValueOnBlur}
|
||||
allowMouseWheel={allowMouseWheel}
|
||||
size={size}
|
||||
onChange={onChange}
|
||||
>
|
||||
onChange={onChange}>
|
||||
<NumberInputField />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper {...customerIncrement} />
|
||||
@ -106,7 +105,7 @@ export default {
|
||||
metadata: {
|
||||
name: 'number_input',
|
||||
description: 'chakra_ui number input',
|
||||
displayName: 'NumberInput',
|
||||
displayName: 'Number Input',
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
|
@ -43,8 +43,7 @@ const Radio: ComponentImplementation<Static<typeof PropsSchema>> = ({
|
||||
name={name}
|
||||
size={size}
|
||||
spacing={spacing}
|
||||
colorScheme={colorScheme}
|
||||
>
|
||||
colorScheme={colorScheme}>
|
||||
<Text value={text} />
|
||||
</BaseRadio>
|
||||
);
|
||||
|
@ -48,8 +48,10 @@ export default {
|
||||
isDraggable: true,
|
||||
isResizable: true,
|
||||
exampleProperties: {
|
||||
raw: 'confirm',
|
||||
format: 'plain',
|
||||
text: {
|
||||
raw: 'text',
|
||||
format: 'plain',
|
||||
},
|
||||
},
|
||||
exampleSize: [2, 1],
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user