Merge pull request #85 from webzard-io/feat/create-component

support drag component to tree to create
This commit is contained in:
yz-yu 2021-10-14 09:43:49 +08:00 committed by GitHub
commit 99234c2cf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 53 additions and 29 deletions

View File

@ -14,7 +14,7 @@ type ApplicationSpec = {
components: ApplicationComponent[];
};
type ApplicationComponent = {
export type ApplicationComponent = {
id: string;
type: string;
// do runtime type check

View File

@ -1,12 +1,9 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Application } from '@meta-ui/core';
import { useCallback, useMemo, useState } from 'react';
import { GridCallbacks } from '@meta-ui/runtime';
import { Box, Button } from '@chakra-ui/react';
import { css } from '@emotion/react';
import { DefaultAppSchema } from '../constants';
import { App } from '../metaUI';
import { StructureTree } from './StructureTree';
import { OperationManager } from '../operations/OperationManager';
import {
CreateComponentOperation,
ModifyComponentPropertyOperation,
@ -14,12 +11,12 @@ import {
import { eventBus } from '../eventBus';
import { ComponentForm } from './ComponentForm';
import { ComponentList } from './ComponentsList';
import { useAppModel } from '../operations/useAppModel';
const operationManager = new OperationManager(DefaultAppSchema);
let count = 0;
export const Editor = () => {
const [selectedComponentId, setSelectedComponentId] = useState('');
const [app, setApp] = useState<Application>(operationManager.getApp());
const { app } = useAppModel();
const Wrapper: React.FC<{ id: string }> = useMemo(() => {
return props => {
@ -39,20 +36,9 @@ export const Editor = () => {
};
}, [selectedComponentId]);
useEffect(() => {
const onAppChange = (app: Application) => {
setApp(() => app);
};
eventBus.on('appChange', onAppChange);
return () => {
eventBus.off('appChange', onAppChange);
};
}, []);
const onClickUndo = useCallback(() => {
eventBus.send('undo');
}, [app, setApp]);
}, []);
const gridCallbacks: GridCallbacks = {
onDragStop(id, layout) {

View File

@ -1,9 +1,12 @@
import React from 'react';
import React, { DragEvent } from 'react';
import { Application } from '@meta-ui/core';
import { IconButton } from '@chakra-ui/react';
import { DeleteIcon } from '@chakra-ui/icons';
import { eventBus } from '../../eventBus';
import { RemoveComponentOperation } from '../../operations/Operations';
import {
CreateComponentOperation,
RemoveComponentOperation,
} from '../../operations/Operations';
type ChildrenMap = Map<string, SlotsMap>;
type SlotsMap = Map<string, Array<Application['spec']['components'][0]>>;
@ -19,6 +22,7 @@ export const StructureTree: React.FC<Props> = props => {
const topLevelComponents: Component[] = [];
const childrenMap: ChildrenMap = new Map();
// parse components array to slotsMap
app.spec.components.forEach(c => {
const slotTrait = c.traits.find(t => t.type === 'core/v1/slot');
if (slotTrait) {
@ -42,9 +46,17 @@ export const StructureTree: React.FC<Props> = props => {
if (slots) {
slotsEle = Array.from(slots.keys()).map(slot => {
const children = slots.get(slot)!.map(genTreeItem);
const onDrop = (e: DragEvent) => {
const creatingComponent = e.dataTransfer?.getData('component') || '';
console.log('createComponent', component.id, slot, creatingComponent);
eventBus.send(
'operation',
new CreateComponentOperation(component.id, slot, creatingComponent)
);
};
return (
<div key={slot}>
<div>slot: {slot}</div>
<div onDrop={onDrop}>slot: {slot}</div>
{children}
</div>
);

View File

@ -40,7 +40,7 @@ function genComponent(
};
}
export class OperationManager {
export class AppModelManager {
private undoStack: Operations[] = [];
private app: Application;

View File

@ -0,0 +1,26 @@
import { Application } from '@meta-ui/core';
import { useEffect, useState } from 'react';
import { DefaultAppSchema } from '../constants';
import { eventBus } from '../eventBus';
import { AppModelManager } from './AppModelManager';
const appModelManager = new AppModelManager(DefaultAppSchema);
export function useAppModel() {
const [app, setApp] = useState<Application>(appModelManager.getApp());
useEffect(() => {
const onAppChange = (app: Application) => {
setApp(() => app);
};
eventBus.on('appChange', onAppChange);
return () => {
eventBus.off('appChange', onAppChange);
};
}, []);
return {
app,
};
}

View File

@ -7,11 +7,11 @@ import { LIST_ITEM_EXP, LIST_ITEM_INDEX_EXP } from '../../constants';
import { parseType } from '../../utils/parseType';
import { ImplWrapper } from '../../services/ImplWrapper';
import { resolveAppComponents } from '../../services/resolveAppComponents';
import { ApplicationComponent } from 'src/types/RuntimeSchema';
import { RuntimeApplicationComponent } from 'src/types/RuntimeSchema';
export function parseTypeComponents(
c: Application['spec']['components'][0]
): ApplicationComponent {
): RuntimeApplicationComponent {
return {
...c,
parsedType: parseType(c.type),

View File

@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { watch } from '@vue-reactivity/watch';
import { merge } from 'lodash';
import {
ApplicationComponent,
RuntimeApplicationComponent,
ImplWrapperProps,
TraitResult,
} from '../types/RuntimeSchema';
@ -10,7 +10,7 @@ import {
type ArrayElement<ArrayType extends readonly unknown[]> =
ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
type ApplicationTrait = ArrayElement<ApplicationComponent['traits']>;
type ApplicationTrait = ArrayElement<RuntimeApplicationComponent['traits']>;
export const ImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>(
(props, ref) => {

View File

@ -5,7 +5,7 @@ import { Registry } from 'src/services/registry';
import { StateManager } from 'src/services/stateStore';
import { Application, RuntimeApplication } from '@meta-ui/core';
export type ApplicationComponent = RuntimeApplication['spec']['components'][0];
export type RuntimeApplicationComponent = RuntimeApplication['spec']['components'][0];
export type MetaUIServices = {
registry: Registry;
@ -34,7 +34,7 @@ export type AppProps = {
} & ComponentParamsFromApp;
export type ImplWrapperProps = {
component: ApplicationComponent;
component: RuntimeApplicationComponent;
slotsMap: SlotsMap | undefined;
targetSlot: { id: string; slot: string } | null;
services: MetaUIServices;