feat: update component rendering for @idraw/design

This commit is contained in:
chenshenhai 2023-05-28 21:11:17 +08:00
parent 2a40bb624e
commit b23aeae6f1
10 changed files with 105 additions and 96 deletions

View file

@ -1,11 +1,12 @@
import { createUUID } from '@idraw/util';
import type { DesignData } from '../src';
const data: DesignData = {
components: [
{
uuid: 'demo-xxx-001',
uuid: createUUID(),
type: 'component',
name: 'demo',
name: 'Button default',
x: 50,
y: 50,
w: 100,
@ -14,7 +15,7 @@ const data: DesignData = {
bgColor: '#1f1f1f',
children: [
{
uuid: 'group-001-0014',
uuid: createUUID(),
type: 'circle',
x: -40,
y: 0,
@ -25,7 +26,7 @@ const data: DesignData = {
}
},
{
uuid: 'group-001-0015',
uuid: createUUID(),
type: 'circle',
x: -20,
y: 0,
@ -36,7 +37,7 @@ const data: DesignData = {
}
},
{
uuid: 'group-001-0016',
uuid: createUUID(),
type: 'circle',
x: 0,
y: 0,
@ -47,7 +48,7 @@ const data: DesignData = {
}
},
{
uuid: 'group-001-0017',
uuid: createUUID(),
type: 'circle',
x: 20,
y: 0,
@ -58,7 +59,7 @@ const data: DesignData = {
}
},
{
uuid: 'group-001-0018',
uuid: createUUID(),
type: 'circle',
x: 40,
y: 0,
@ -72,9 +73,9 @@ const data: DesignData = {
}
},
{
uuid: 'demo-xxx-002',
uuid: createUUID(),
type: 'component',
name: 'demo',
name: 'Button primary',
x: 50,
y: 50,
w: 100,
@ -83,7 +84,7 @@ const data: DesignData = {
bgColor: '#f0f0f0',
children: [
{
uuid: 'group-001-0014',
uuid: createUUID(),
type: 'circle',
x: -40,
y: 0,
@ -94,7 +95,7 @@ const data: DesignData = {
}
},
{
uuid: 'group-001-0015',
uuid: createUUID(),
type: 'circle',
x: -20,
y: 0,
@ -105,7 +106,7 @@ const data: DesignData = {
}
},
{
uuid: 'group-001-0016',
uuid: createUUID(),
type: 'circle',
x: 0,
y: 0,
@ -116,7 +117,7 @@ const data: DesignData = {
}
},
{
uuid: 'group-001-0017',
uuid: createUUID(),
type: 'circle',
x: 20,
y: 0,
@ -127,7 +128,7 @@ const data: DesignData = {
}
},
{
uuid: 'group-001-0018',
uuid: createUUID(),
type: 'circle',
x: 40,
y: 0,

View file

@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
import data from './data';
import { Design } from '../src/index';
const dom = document.querySelector('#lab') as HTMLDivElement;
@ -20,7 +21,7 @@ const App = () => {
const width = 800;
const height = 600;
return <Design width={width} height={height} mode={'dark'} style={style} />;
return <Design width={width} height={height} style={style} designData={data} />;
};
root.render(<App />);

View file

@ -1,13 +1,16 @@
import { createContext } from 'react';
import type { Dispatch } from 'react';
import type { Data } from '@idraw/types';
import { DesignData } from './types';
export interface DesignState {
data: DesignData;
designData: DesignData | null;
viewDrawData: Data | null;
viewDrawUUID: string | null;
themeMode: 'light' | 'dark';
}
export type DesignActionType = 'updateThemeMode' | 'updateData';
export type DesignActionType = 'updateThemeMode' | 'updateDesignData';
export type DesignAction = {
type: DesignActionType;
@ -42,14 +45,14 @@ export function createDesignReducer(state: DesignState, action: DesignAction): D
}
};
}
case 'updateData': {
if (!action?.payload?.data) {
case 'updateDesignData': {
if (!action?.payload?.designData) {
return state;
}
return {
...state,
...{
data: action?.payload?.data
data: action?.payload?.designData
}
};
}
@ -60,8 +63,10 @@ export function createDesignReducer(state: DesignState, action: DesignAction): D
export function createDesignContextState(opts?: Partial<DesignState>): DesignState {
return {
data: opts?.data || createDesignData(),
themeMode: opts?.themeMode || 'light'
designData: opts?.designData || createDesignData(),
themeMode: opts?.themeMode || 'light',
viewDrawData: null,
viewDrawUUID: null
};
}

View file

@ -13,24 +13,24 @@ const themeName = 'theme';
const themePrefixName = createPrefixName(themeName);
export type DesignProps = SketchProps & {
data?: DesignData;
designData?: DesignData;
locale?: string; // TODO
themeMode?: 'light' | 'dark';
};
export const Design = (props: DesignProps) => {
const { width = 1000, height = 600, style, className, data, themeMode } = props;
const { width = 1000, height = 600, style, className, designData, themeMode } = props;
const [state, dispatch] = useReducer(createDesignReducer, createDesignContextState({ data, themeMode }));
const [state, dispatch] = useReducer(createDesignReducer, createDesignContextState({ designData, themeMode }));
useEffect(() => {
if (data) {
if (designData) {
dispatch({
type: 'updateData',
payload: { data }
type: 'updateDesignData',
payload: { designData }
});
}
}, [data]);
}, [designData]);
return (
<Provider value={{ state, dispatch }}>

View file

@ -7,27 +7,35 @@ import { prefixName } from './config';
import { LayerTree } from './layer-tree';
import FileOutlined from '@ant-design/icons/FileOutlined';
import AppstoreOutlined from '@ant-design/icons/AppstoreOutlined';
import ProjectOutlined from '@ant-design/icons/ProjectOutlined';
import CalculatorOutlined from '@ant-design/icons/CalculatorOutlined';
const items: TabsProps['items'] = [
{
key: '1',
key: 'page',
label: <FileOutlined className={prefixName('tab', 'title')} />,
children: (
<div style={{ width: '100%', overflow: 'auto' }}>
<LayerTree />
<LayerTree type="page" />
</div>
)
},
{
key: '2',
key: 'module',
label: <AppstoreOutlined className={prefixName('tab', 'title')} />,
children: `Content of Tab Pane 2`
children: (
<div style={{ width: '100%', overflow: 'auto' }}>
<LayerTree type="module" />
</div>
)
},
{
key: '3',
label: <ProjectOutlined className={prefixName('tab', 'title')} />,
children: `Content of Tab Pane 3`
key: 'component',
label: <CalculatorOutlined className={prefixName('tab', 'title')} />,
children: (
<div style={{ width: '100%', overflow: 'auto' }}>
<LayerTree type="component" />
</div>
)
}
];
@ -38,11 +46,13 @@ export interface PanelLayerProps {
export const PanelLayer = (props: PanelLayerProps) => {
const { className, style } = props;
const defaultTabKey = items[2].key;
return (
<div style={style} className={classnames(prefixName(), className)}>
{/* <div className={prefixName('header')}>header</div> */}
<div className={prefixName('content')}>
<Tabs className={prefixName('tabs')} defaultActiveKey="1" centered items={items} size="small" />
<Tabs className={prefixName('tabs')} defaultActiveKey={defaultTabKey} centered items={items} size="small" />
</div>
{/* <div className={prefixName('footer')}>footer</div> */}
</div>

View file

@ -1,78 +1,38 @@
import React from 'react';
import type { CSSProperties } from 'react';
import React, { useEffect, useContext } from 'react';
import classnames from 'classnames';
import Tree from 'antd/es/tree';
import DownOutlined from '@ant-design/icons/DownOutlined';
import { prefixName } from './config';
import { Context } from '../../context';
import { parseComponentViewTree } from '../../util/component';
import type { CSSProperties } from 'react';
import type { DataNode, TreeProps } from 'antd/es/tree';
import type { DesignItemType } from '../../types';
const { DirectoryTree } = Tree;
const treeData: DataNode[] = [0, 1].map((i) => {
return {
title: 'design-layer-data parent 1',
key: `${i}-0`,
children: [
{
title: 'design-layer-data parent 1-0',
key: `${i}-0-0`,
children: [
{
title: 'design-layer-data leaf',
key: `${i}-0-0-0`
},
{
title: 'design-layer-data leaf',
key: `${i}-0-0-1`
},
{
title: 'design-layer-data leaf',
key: `${i}-0-0-2`
}
]
},
{
title: 'design-layer-data parent 1-1',
key: `${i}-0-1`,
children: [
{
title: 'design-layer-data leaf',
key: `${i}-0-1-0`
}
]
},
{
title: 'design-layer-data parent 1-2',
key: `${i}-0-2`,
children: [
{
title: 'design-layer-data leaf',
key: `${i}-0-2-0`
},
{
title: 'design-layer-data leaf',
key: `${i}-0-2-1`
}
]
}
]
};
});
const baseName = 'layer-tree';
export interface LayerTreeProps {
className?: string;
style?: CSSProperties;
type: DesignItemType;
}
export const LayerTree = (props: LayerTreeProps) => {
const { className, style } = props;
const { className, style, type } = props;
const { state } = useContext(Context);
const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
console.log('selected', selectedKeys, info);
};
let treeData: DataNode[] = [];
if (type === 'component') {
treeData = parseComponentViewTree(state?.designData || null);
}
return (
<div style={style} className={classnames(prefixName(baseName), className)}>
<DirectoryTree showLine blockNode switcherIcon={<DownOutlined />} icon={null} defaultExpandedKeys={['0-0-0']} onSelect={onSelect} treeData={treeData} />

View file

@ -1,9 +1,11 @@
import type { Element, ElementType, ElementSize, ElementBaseDesc } from '@idraw/types';
export type DesignItemType = 'component' | 'module' | 'page';
export type DesignComponent = Omit<ElementSize, 'angle'> & {
uuid: string;
type: 'component';
name?: string;
name: string;
desc?: ElementBaseDesc & {
children: Array<Element<ElementType> | DesignComponent>;
};
@ -12,7 +14,7 @@ export type DesignComponent = Omit<ElementSize, 'angle'> & {
export type DesignModule = Omit<ElementSize, 'angle'> & {
uuid: string;
type: 'module';
name?: string;
name: string;
desc?: ElementBaseDesc & {
children: Array<DesignComponent>;
};
@ -21,7 +23,7 @@ export type DesignModule = Omit<ElementSize, 'angle'> & {
export type DesignPage = Omit<ElementSize, 'angle'> & {
uuid: string;
type: 'page';
name?: string;
name: string;
desc: ElementBaseDesc & {
children: Array<DesignModule>;
};

View file

@ -1 +1,2 @@
export * from './data';
export * from './view';

View file

@ -0,0 +1,5 @@
export interface ViewTreeNode {
title: string;
key: string;
children?: ViewTreeNode[];
}

View file

@ -0,0 +1,24 @@
import { Data } from '@idraw/types';
import { ViewTreeNode, DesignData, DesignComponent } from '../types';
export function parseComponentViewTree(designData: DesignData | null): ViewTreeNode[] {
const list: ViewTreeNode[] = [];
designData?.components?.forEach((comp: DesignComponent) => {
const children: ViewTreeNode[] = [];
if (Array.isArray(comp?.desc?.children)) {
comp?.desc?.children?.forEach((child) => {
children.push({
key: child.uuid,
title: child.name || 'Unamed',
children: []
});
});
}
list.push({
key: comp.uuid,
title: comp.name || 'Unamed',
children
});
});
return list;
}