diff --git a/.vscode/settings.json b/.vscode/settings.json index db4afa1..d95dedb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,8 @@ }, "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[less]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" } } \ No newline at end of file diff --git a/packages/design/dev/data.ts b/packages/design/dev/data.ts new file mode 100644 index 0000000..fb49006 --- /dev/null +++ b/packages/design/dev/data.ts @@ -0,0 +1,148 @@ +import type { DesignData } from '../src'; + +const data: DesignData = { + components: [ + { + uuid: 'demo-xxx-001', + type: 'component', + name: 'demo', + x: 50, + y: 50, + w: 100, + h: 100, + desc: { + bgColor: '#1f1f1f', + children: [ + { + uuid: 'group-001-0014', + type: 'circle', + x: -40, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#f44336' + } + }, + { + uuid: 'group-001-0015', + type: 'circle', + x: -20, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#ff9800' + } + }, + { + uuid: 'group-001-0016', + type: 'circle', + x: 0, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#ffc106' + } + }, + { + uuid: 'group-001-0017', + type: 'circle', + x: 20, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#cddc39' + } + }, + { + uuid: 'group-001-0018', + type: 'circle', + x: 40, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#4caf50' + } + } + ] + } + }, + { + uuid: 'demo-xxx-002', + type: 'component', + name: 'demo', + x: 50, + y: 50, + w: 100, + h: 100, + desc: { + bgColor: '#f0f0f0', + children: [ + { + uuid: 'group-001-0014', + type: 'circle', + x: -40, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#f44336' + } + }, + { + uuid: 'group-001-0015', + type: 'circle', + x: -20, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#ff9800' + } + }, + { + uuid: 'group-001-0016', + type: 'circle', + x: 0, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#ffc106' + } + }, + { + uuid: 'group-001-0017', + type: 'circle', + x: 20, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#cddc39' + } + }, + { + uuid: 'group-001-0018', + type: 'circle', + x: 40, + y: 0, + w: 100, + h: 100, + desc: { + bgColor: '#4caf50' + } + } + ] + } + } + ], + modules: [], + pages: [] +}; + +export default data; diff --git a/packages/design/dev/main.tsx b/packages/design/dev/main.tsx index 6d7aa6e..03d3e9a 100644 --- a/packages/design/dev/main.tsx +++ b/packages/design/dev/main.tsx @@ -1,8 +1,26 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { createRoot } from 'react-dom/client'; import { Design } from '../src/index'; const dom = document.querySelector('#lab') as HTMLDivElement; const root = createRoot(dom); -root.render(); +const App = () => { + // const style = { margin: 0, padding: 0 } + // const [width, setWidth] = useState(window.innerWidth); + // const [height, setHeight] = useState(window.innerHeight); + // useEffect(() => { + // window.addEventListener('resize', () => { + // setWidth(window.innerWidth); + // setHeight(window.innerHeight); + // }); + // }, []); + + const style = { margin: 40 }; + const width = 1000; + const height = 600; + + return ; +}; + +root.render(); diff --git a/packages/design/src/context.ts b/packages/design/src/context.ts new file mode 100644 index 0000000..57e4d50 --- /dev/null +++ b/packages/design/src/context.ts @@ -0,0 +1,24 @@ +import { createContext } from 'react'; +import { DesignData } from './types'; + +export interface DesignContext { + data: DesignData; +} + +export function createDesignData(): DesignData { + return { + components: [], + modules: [], + pages: [] + }; +} + +export function createDesignContextValue(opts?: { data?: DesignData }): DesignContext { + return { + data: opts?.data || createDesignData() + }; +} + +const Context = createContext(createDesignContextValue()); + +export const Provider = Context.Provider; diff --git a/packages/design/src/css/icons/index.less b/packages/design/src/css/icons/index.less index 13ed6e2..5b724ff 100644 --- a/packages/design/src/css/icons/index.less +++ b/packages/design/src/css/icons/index.less @@ -1,11 +1,19 @@ -@import "../../css/variable.less"; +@import '../../css/variable.less'; @mod-name: ~'@{prefix}-icon'; .@{mod-name} { - display: flex; - text-align: center; + font-size: 16; + display: inline-flex; align-items: center; justify-content: center; - font-size: 16; -} \ No newline at end of file + color: inherit; + font-style: normal; + line-height: 0; + text-align: center; + text-transform: none; + + svg { + justify-content: center; + } +} diff --git a/packages/design/src/css/index.less b/packages/design/src/css/index.less index 8ae0a91..3454289 100644 --- a/packages/design/src/css/index.less +++ b/packages/design/src/css/index.less @@ -6,3 +6,4 @@ @import "./modules/header.less"; @import "./modules/sketch.less"; @import "./modules/toolbar.less"; +@import "./modules/panel-layer.less"; diff --git a/packages/design/src/css/modules/header.less b/packages/design/src/css/modules/header.less index 66cd0be..19cf8d0 100644 --- a/packages/design/src/css/modules/header.less +++ b/packages/design/src/css/modules/header.less @@ -1 +1,16 @@ -@import "../variable.less"; \ No newline at end of file +@import '../variable.less'; + +@mod-header: ~'@{prefix}-mod-header'; + +.@{mod-header} { + display: flex; + align-items: center; + justify-content: space-between; + height: 100%; + box-sizing: border-box; + padding: 0 20px; + + .@{mod-header}-theme-switch { + display: flex; + } +} diff --git a/packages/design/src/css/modules/panel-layer.less b/packages/design/src/css/modules/panel-layer.less new file mode 100644 index 0000000..6eeabff --- /dev/null +++ b/packages/design/src/css/modules/panel-layer.less @@ -0,0 +1,23 @@ +@import "../variable.less"; + +@mod-panel-layer: ~'@{prefix}-mod-panel-layer'; + +.@{mod-panel-layer} { + height: 100%; + width: 100%; + display: flex; + flex-flow: column; + + .@{mod-panel-layer}-header { + height: 40px; + } + + .@{mod-panel-layer}-content { + flex: 1; + } + + .@{mod-panel-layer}-footer { + height: 40px; + } + +} \ No newline at end of file diff --git a/packages/design/src/css/modules/sketch.less b/packages/design/src/css/modules/sketch.less index 15a55db..151761e 100644 --- a/packages/design/src/css/modules/sketch.less +++ b/packages/design/src/css/modules/sketch.less @@ -2,7 +2,10 @@ @mod-sketch: ~'@{prefix}-mod-sketch'; +// @mod-sketch-header-height: 36px; + .@{mod-sketch} { + display: flex; position: relative; overflow: hidden; box-shadow: 0 0 0 1px #0000001a,0px 0px .5px #0000002e, 0px 3px 8px #0000001a, 0px 1px 3px #0000001a; @@ -15,4 +18,20 @@ min-width: 400px; transform: translateX(-50%); } + + .@{mod-sketch}-header { + position: absolute; + top: 0; + left: 0; + right: 0; + // height: @mod-sketch-header-height; + } + + .@{mod-sketch}-content { + position: absolute; + // top: @mod-sketch-header-height; + bottom: 0; + left: 0; + right: 0; + } } \ No newline at end of file diff --git a/packages/design/src/icons/dark.tsx b/packages/design/src/icons/dark.tsx new file mode 100644 index 0000000..b1a35f9 --- /dev/null +++ b/packages/design/src/icons/dark.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import classnames from 'classnames'; +import { iconClassName } from './util'; +import type { IconProps } from './util'; + +const Dark = (props: IconProps) => { + const { className, style } = props; + return ( + + + + + + ); +}; + +export default Dark; diff --git a/packages/design/src/icons/light.tsx b/packages/design/src/icons/light.tsx new file mode 100644 index 0000000..07cbe80 --- /dev/null +++ b/packages/design/src/icons/light.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import classnames from 'classnames'; +import { iconClassName } from './util'; +import type { IconProps } from './util'; + +const Light = (props: IconProps) => { + const { className, style } = props; + return ( + + + + + + ); +}; + +export default Light; diff --git a/packages/design/src/icons/more.tsx b/packages/design/src/icons/more.tsx new file mode 100644 index 0000000..96625c6 --- /dev/null +++ b/packages/design/src/icons/more.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import classnames from 'classnames'; +import { iconClassName } from './util'; +import type { IconProps } from './util'; + +const More = (props: IconProps) => { + const { className, style } = props; + return ( + + + + + + + + + ); +}; + +export default More; diff --git a/packages/design/src/index.tsx b/packages/design/src/index.tsx index 901d6f2..9d96d4f 100644 --- a/packages/design/src/index.tsx +++ b/packages/design/src/index.tsx @@ -1,25 +1,31 @@ import React, { useEffect, useState } from 'react'; import { Sketch } from './modules'; +import type { SketchProps } from './modules'; +import { Provider, createDesignContextValue } from './context'; +import type { DesignContext } from './context'; +import type { DesignData } from './types'; import './css/index.less'; -export const Design = () => { - const [width, setWidth] = useState(1000); - const [height, setHeight] = useState(600); +export type DesignProps = SketchProps & { + data?: DesignData; + locale?: string; // TODO +}; - // const [width, setWidth] = useState(window.innerWidth); - // const [height, setHeight] = useState(window.innerHeight); - // useEffect(() => { - // window.addEventListener('resize', () => { - // const width = window.innerWidth; - // const height = window.innerHeight; - // setWidth(width); - // setHeight(height); - // }); - // }, []); +export const Design = (props: DesignProps) => { + const { width = 1000, height = 600, style, className, data } = props; + const [contextValue, setContextValue] = useState(createDesignContextValue({ data })); + + useEffect(() => { + if (data) { + setContextValue({ ...contextValue, ...{ data } }); + } + }, [data]); return ( -
- -
+ + + ); }; + +export * from './types'; diff --git a/packages/design/src/modules/_mod/index.tsx b/packages/design/src/modules/_mod/index.tsx index e70bd92..3062a8f 100644 --- a/packages/design/src/modules/_mod/index.tsx +++ b/packages/design/src/modules/_mod/index.tsx @@ -2,9 +2,8 @@ import React from 'react'; import type { CSSProperties } from 'react'; import classnames from 'classnames'; import { createPrefixName } from '../../css'; -import './index.less'; -const modName = 'mod-sketch'; +const modName = 'mod-xxx'; const prefixName = createPrefixName(modName); diff --git a/packages/design/src/modules/header/index.tsx b/packages/design/src/modules/header/index.tsx index e69de29..0429d0d 100644 --- a/packages/design/src/modules/header/index.tsx +++ b/packages/design/src/modules/header/index.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import type { CSSProperties } from 'react'; +import classnames from 'classnames'; +import Switch from 'antd/es/switch'; +import { createPrefixName } from '../../css'; +import IconDark from '../../icons/dark'; +import IconLight from '../../icons/light'; + +const modName = 'mod-header'; + +const prefixName = createPrefixName(modName); + +export interface ModProps { + className?: string; + style?: CSSProperties; +} + +export const Header = (props: ModProps) => { + const { className, style } = props; + return ( +
+ Header + } + unCheckedChildren={} + defaultChecked + /> +
+ ); +}; diff --git a/packages/design/src/modules/panel-layer/index.tsx b/packages/design/src/modules/panel-layer/index.tsx new file mode 100644 index 0000000..ebb78f5 --- /dev/null +++ b/packages/design/src/modules/panel-layer/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import type { CSSProperties } from 'react'; +import classnames from 'classnames'; +import { createPrefixName } from '../../css'; + +const modName = 'mod-panel-layer'; + +const prefixName = createPrefixName(modName); + +export interface PanelLayerProps { + className?: string; + style?: CSSProperties; +} + +export const PanelLayer = (props: PanelLayerProps) => { + const { className, style } = props; + return ( +
+
header
+
Panel Layer
+
footer
+
+ ); +}; diff --git a/packages/design/src/modules/sketch/index.tsx b/packages/design/src/modules/sketch/index.tsx index b4a7e70..3ce9c5e 100644 --- a/packages/design/src/modules/sketch/index.tsx +++ b/packages/design/src/modules/sketch/index.tsx @@ -5,8 +5,11 @@ import { calcElementsContextSize } from '@idraw/util'; import Drawer from 'antd/es/drawer'; import { getData } from '../../data'; import { Toolbar } from '../toolbar'; +import { PanelLayer } from '../panel-layer'; +import { Header } from '../header'; import type { CSSProperties } from 'react'; import { createPrefixName } from '../../css'; +import { HEADER_HEIGHT } from './layout'; const modName = 'mod-sketch'; const siderWidth = 200; @@ -24,18 +27,19 @@ export const Sketch = (props: SketchProps) => { const ref = useRef(null); const refCore = useRef(null); const refLeftDOM = useRef(null); + const refRighttDOM = useRef(null); const { className, style, width, height } = props; const data = getData(); const devicePixelRatio = window.devicePixelRatio; - const [openLayer, setOpenLayer] = useState(false); - const [openSetting, setOpenSetting] = useState(false); + const [openLeftSider, setOpenLeftSider] = useState(false); + const [openRightSider, setOpenRightSider] = useState(false); useEffect(() => { if (ref?.current) { const options = { width, - height, + height: height - HEADER_HEIGHT, devicePixelRatio }; const core = new Core(ref.current, options); @@ -55,40 +59,48 @@ export const Sketch = (props: SketchProps) => { const contextSize = calcElementsContextSize(data.elements, { viewWidth: width, viewHeight: height }); core.resize({ width, - height, + height: height - HEADER_HEIGHT, devicePixelRatio, ...contextSize }); }, [height, width]); return ( -
-
+
+
+
+
+
+
+
{ - setOpenLayer(openLayer ? false : true); + setOpenLeftSider(openLeftSider ? false : true); }} onClickToggleSetting={() => { - setOpenSetting(openSetting ? false : true); + setOpenRightSider(openRightSider ? false : true); }} /> -
{ console.log('on close left'); - setOpenLayer(false); + setOpenLeftSider(false); }} mask={false} - open={openLayer} + open={openLeftSider} getContainer={() => { return refLeftDOM.current as HTMLDivElement; }} width={siderWidth} + bodyStyle={{ + padding: 0 + }} rootStyle={{ position: 'absolute', top: 0, @@ -96,21 +108,19 @@ export const Sketch = (props: SketchProps) => { left: 0 }} > -

Some contents...

-

Some contents...

-

Some contents...

+
{ console.log('on close right'); - setOpenSetting(false); + setOpenRightSider(false); }} mask={false} - open={openSetting} + open={openRightSider} getContainer={() => { - return refLeftDOM.current as HTMLDivElement; + return refRighttDOM.current as HTMLDivElement; }} width={siderWidth} rootStyle={{ diff --git a/packages/design/src/modules/sketch/layout.ts b/packages/design/src/modules/sketch/layout.ts new file mode 100644 index 0000000..25d7a30 --- /dev/null +++ b/packages/design/src/modules/sketch/layout.ts @@ -0,0 +1 @@ +export const HEADER_HEIGHT = 36; diff --git a/packages/design/src/modules/toolbar/index.tsx b/packages/design/src/modules/toolbar/index.tsx index b90829e..97ef2ac 100644 --- a/packages/design/src/modules/toolbar/index.tsx +++ b/packages/design/src/modules/toolbar/index.tsx @@ -10,6 +10,7 @@ import IconHand from '../../icons/hand'; import IconScale from '../../icons/scale'; import IconLayer from '../../icons/layer'; import IconSetting from '../../icons/setting'; +import IconMore from '../../icons/more'; const RadioButton = Radio.Button; const RadioGroup = Radio.Group; @@ -20,21 +21,21 @@ const prefixName = createPrefixName(modName); export interface ToolbarProps { className?: string; style?: CSSProperties; - openLayer: boolean; - openSetting: boolean; + openLeftSider: boolean; + openRightSider: boolean; onClickToggleLayer?: () => void; onClickToggleSetting?: () => void; } export const Toolbar = (props: ToolbarProps) => { - const { className, style, openLayer, openSetting, onClickToggleLayer, onClickToggleSetting } = props; + const { className, style, openLeftSider, openRightSider, onClickToggleLayer, onClickToggleSetting } = props; const [mode, setMode] = useState('select'); const iconStyle = { fontSize: 20 }; return (
-
setMode(e.target.value)}> @@ -49,9 +50,12 @@ export const Toolbar = (props: ToolbarProps) => { + + +
-
); diff --git a/packages/design/src/types/data.ts b/packages/design/src/types/data.ts new file mode 100644 index 0000000..634e6d4 --- /dev/null +++ b/packages/design/src/types/data.ts @@ -0,0 +1,34 @@ +import type { Element, ElementType, ElementSize, ElementBaseDesc } from '@idraw/types'; + +export type DesignComponent = Omit & { + uuid: string; + type: 'component'; + name?: string; + desc?: ElementBaseDesc & { + children: Array | DesignComponent>; + }; +}; + +export type DesignModule = Omit & { + uuid: string; + type: 'module'; + name?: string; + desc?: ElementBaseDesc & { + children: Array; + }; +}; + +export type DesignPage = Omit & { + uuid: string; + type: 'page'; + name?: string; + desc: ElementBaseDesc & { + children: Array; + }; +}; + +export interface DesignData { + components: DesignComponent[]; + modules: DesignModule[]; + pages: DesignPage[]; +} diff --git a/packages/design/src/types/index.ts b/packages/design/src/types/index.ts new file mode 100644 index 0000000..3707679 --- /dev/null +++ b/packages/design/src/types/index.ts @@ -0,0 +1 @@ +export * from './data'; diff --git a/packages/types/src/lib/element.ts b/packages/types/src/lib/element.ts index 26ef01e..9b8abba 100644 --- a/packages/types/src/lib/element.ts +++ b/packages/types/src/lib/element.ts @@ -6,7 +6,7 @@ export interface ElementSize { angle?: number; } -interface ElementBaseDesc { +export interface ElementBaseDesc { borderWidth?: number; borderColor?: string; borderRadius?: number; @@ -14,13 +14,17 @@ interface ElementBaseDesc { shadowOffsetX?: number; shadowOffsetY?: number; shadowBlur?: number; -} - -interface ElementRectDesc extends ElementBaseDesc { color?: string; bgColor?: string; } +// interface ElementRectDesc extends ElementBaseDesc { +// // color?: string; +// // bgColor?: string; +// } + +type ElementRectDesc = ElementBaseDesc; + interface ElemenTextDesc extends ElementBaseDesc { text: string; color: string;