From 17b3bc23873a10f4cb3eb5dbc197b03bf26601e9 Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sun, 4 Aug 2024 10:12:35 +0800 Subject: [PATCH 1/2] deps: upgrade idraw to 0.4.0-beta.36 --- package.json | 2 +- packages/studio-base/package.json | 2 +- packages/studio/package.json | 2 +- pnpm-lock.yaml | 82 +++++++++++++++---------------- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index 81361dc..7583888 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "upgrade:version": "npm run version:reset && pnpm i" }, "dependencies": { - "idraw": "0.4.0-beta.34", + "idraw": "0.4.0-beta.36", "antd": "5.19.3" }, "devDependencies": { diff --git a/packages/studio-base/package.json b/packages/studio-base/package.json index 0ea44d2..74a201e 100644 --- a/packages/studio-base/package.json +++ b/packages/studio-base/package.json @@ -24,7 +24,7 @@ "@rc-component/color-picker": "^1.5.3", "antd": "^5.19.3", "classnames": "^2.3.2", - "idraw": "^0.4.0-beta.34", + "idraw": "^0.4.0-beta.36", "react": "^18.3.1", "react-dom": "^18.3.1" }, diff --git a/packages/studio/package.json b/packages/studio/package.json index 65bd4e3..f2f1fd2 100644 --- a/packages/studio/package.json +++ b/packages/studio/package.json @@ -27,7 +27,7 @@ }, "peerDependencies": { "antd": "^5.19.3", - "idraw": "^0.4.0-beta.34", + "idraw": "^0.4.0-beta.36", "react": "^18.3.1", "react-dom": "^18.3.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48bdd9d..d7c7167 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: 5.19.3 version: 5.19.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) idraw: - specifier: 0.4.0-beta.34 - version: 0.4.0-beta.34 + specifier: 0.4.0-beta.36 + version: 0.4.0-beta.36 devDependencies: '@babel/core': specifier: ^7.24.8 @@ -172,8 +172,8 @@ importers: specifier: ^2.5.1 version: 2.5.1 idraw: - specifier: ^0.4.0-beta.34 - version: 0.4.0-beta.34 + specifier: ^0.4.0-beta.36 + version: 0.4.0-beta.36 is-hotkey: specifier: ^0.2.0 version: 0.2.0 @@ -206,8 +206,8 @@ importers: specifier: ^2.3.2 version: 2.5.1 idraw: - specifier: ^0.4.0-beta.34 - version: 0.4.0-beta.34 + specifier: ^0.4.0-beta.36 + version: 0.4.0-beta.36 react: specifier: ^18.3.1 version: 18.3.1 @@ -1287,29 +1287,29 @@ packages: resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} engines: {node: '>=18.18'} - '@idraw/board@0.4.0-beta.34': - resolution: {integrity: sha512-CWxzmV3BLwO7UP+h6Gvt8rsqwbBAklfxEtsRlB3rCUROJ8/n9Ovy67A/hLoLyNdvIKBDQtKLEdp2Dxig9yAFYA==} + '@idraw/board@0.4.0-beta.36': + resolution: {integrity: sha512-lyHYGhaKBvbpKmJSJ3YsG3W/GtzcEHTVLiLLBd43W6w4f3hF4kAtmSw27NAjc/MIMVrCb/6Mowy2eAfbGJDHjA==} peerDependencies: - '@idraw/renderer': ^0.4.0-beta.34 - '@idraw/util': ^0.4.0-beta.34 + '@idraw/renderer': ^0.4.0-beta.36 + '@idraw/util': ^0.4.0-beta.36 - '@idraw/core@0.4.0-beta.34': - resolution: {integrity: sha512-hYGNqT40DLoqtHH4JA0Hg7UbziWa60Jx0YszxSNlOP7tIzBhcNlWjVR8ZNpourEw4ibVniE5IsuGiSWgP/BPHw==} + '@idraw/core@0.4.0-beta.36': + resolution: {integrity: sha512-sv+sntEh4AJaeQzDG0jgODV/nq6MDH/mFIwptd/jq+QJ1NtgdTCgX7gN5TZ3LGtlcdeg+JFTyvxE9xpWqi1KKg==} peerDependencies: - '@idraw/board': ^0.4.0-beta.34 - '@idraw/renderer': ^0.4.0-beta.34 - '@idraw/util': ^0.4.0-beta.34 + '@idraw/board': ^0.4.0-beta.36 + '@idraw/renderer': ^0.4.0-beta.36 + '@idraw/util': ^0.4.0-beta.36 - '@idraw/renderer@0.4.0-beta.34': - resolution: {integrity: sha512-Kiv365GmxZYl0kJRJUhdmOZOW7SjI/GDi91Hj4/2ZZs8s6JmPbf8Ux25/7BfrAlb698gtTfZKwy+TNzVIy0a0A==} + '@idraw/renderer@0.4.0-beta.36': + resolution: {integrity: sha512-SWsFu1SmTZ3v+ycQO+kyZszVf38LwW4mIs85FqnU9nDhSv8pUJJQnPh/WeSdx9E+NYljxY2FhUHt5Xc38LYCbA==} peerDependencies: - '@idraw/util': ^0.4.0-beta.34 + '@idraw/util': ^0.4.0-beta.36 - '@idraw/types@0.4.0-beta.34': - resolution: {integrity: sha512-Mg39tX2uEkehbvc3qoNp0OgpCz1QN8R2tmZytFXQrfIequpu88Flxw89ERUlwNVP/0IV2noLlxaa1WXRkzGmZw==} + '@idraw/types@0.4.0-beta.36': + resolution: {integrity: sha512-EujQAhacIiZ72CjJXrXwhQiIQuUuHBp3ZdmkpcpaujIPmeAWIheJ1njI26kdGlGJThBbDAcH4FAhB9367WN95g==} - '@idraw/util@0.4.0-beta.34': - resolution: {integrity: sha512-FzNUa3hikjm2jKZA6ZO/vYe7QRQJgm5/QC6xKlpsnRiO008vmO0bizrFo2Lty7nzzQhUnZNZppmUQWoaoRODFw==} + '@idraw/util@0.4.0-beta.36': + resolution: {integrity: sha512-88mCqpCqebD2/h3DwDbmA+YDe5HP2tCMdgm6YPK7P3wAv+WUUfU0VrgP7vQrdU0ukiHzsnRDfHJHJjgkrOdE8g==} '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} @@ -2855,8 +2855,8 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - idraw@0.4.0-beta.34: - resolution: {integrity: sha512-Lra/2kD//UbIFVm7UzLrrqIhmbvZhVuKJbBnXE3kvaxxgU/vm3m/HcHtYIRJR2JzvJep/ifpxgazplutfoGcXg==} + idraw@0.4.0-beta.36: + resolution: {integrity: sha512-AdOTuCC28Yt/qrjkA73LwTWdtGTbzo2mlzMg5W0+X/9BEj1Bsex+n6N/J4qcshPvLO/+LlIugYG8aS8k4IqseA==} ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -5555,24 +5555,24 @@ snapshots: '@humanwhocodes/retry@0.3.0': {} - '@idraw/board@0.4.0-beta.34(@idraw/renderer@0.4.0-beta.34(@idraw/util@0.4.0-beta.34))(@idraw/util@0.4.0-beta.34)': + '@idraw/board@0.4.0-beta.36(@idraw/renderer@0.4.0-beta.36(@idraw/util@0.4.0-beta.36))(@idraw/util@0.4.0-beta.36)': dependencies: - '@idraw/renderer': 0.4.0-beta.34(@idraw/util@0.4.0-beta.34) - '@idraw/util': 0.4.0-beta.34 + '@idraw/renderer': 0.4.0-beta.36(@idraw/util@0.4.0-beta.36) + '@idraw/util': 0.4.0-beta.36 - '@idraw/core@0.4.0-beta.34(@idraw/board@0.4.0-beta.34(@idraw/renderer@0.4.0-beta.34(@idraw/util@0.4.0-beta.34))(@idraw/util@0.4.0-beta.34))(@idraw/renderer@0.4.0-beta.34(@idraw/util@0.4.0-beta.34))(@idraw/util@0.4.0-beta.34)': + '@idraw/core@0.4.0-beta.36(@idraw/board@0.4.0-beta.36(@idraw/renderer@0.4.0-beta.36(@idraw/util@0.4.0-beta.36))(@idraw/util@0.4.0-beta.36))(@idraw/renderer@0.4.0-beta.36(@idraw/util@0.4.0-beta.36))(@idraw/util@0.4.0-beta.36)': dependencies: - '@idraw/board': 0.4.0-beta.34(@idraw/renderer@0.4.0-beta.34(@idraw/util@0.4.0-beta.34))(@idraw/util@0.4.0-beta.34) - '@idraw/renderer': 0.4.0-beta.34(@idraw/util@0.4.0-beta.34) - '@idraw/util': 0.4.0-beta.34 + '@idraw/board': 0.4.0-beta.36(@idraw/renderer@0.4.0-beta.36(@idraw/util@0.4.0-beta.36))(@idraw/util@0.4.0-beta.36) + '@idraw/renderer': 0.4.0-beta.36(@idraw/util@0.4.0-beta.36) + '@idraw/util': 0.4.0-beta.36 - '@idraw/renderer@0.4.0-beta.34(@idraw/util@0.4.0-beta.34)': + '@idraw/renderer@0.4.0-beta.36(@idraw/util@0.4.0-beta.36)': dependencies: - '@idraw/util': 0.4.0-beta.34 + '@idraw/util': 0.4.0-beta.36 - '@idraw/types@0.4.0-beta.34': {} + '@idraw/types@0.4.0-beta.36': {} - '@idraw/util@0.4.0-beta.34': {} + '@idraw/util@0.4.0-beta.36': {} '@isaacs/cliui@8.0.2': dependencies: @@ -7400,13 +7400,13 @@ snapshots: dependencies: safer-buffer: 2.1.2 - idraw@0.4.0-beta.34: + idraw@0.4.0-beta.36: dependencies: - '@idraw/board': 0.4.0-beta.34(@idraw/renderer@0.4.0-beta.34(@idraw/util@0.4.0-beta.34))(@idraw/util@0.4.0-beta.34) - '@idraw/core': 0.4.0-beta.34(@idraw/board@0.4.0-beta.34(@idraw/renderer@0.4.0-beta.34(@idraw/util@0.4.0-beta.34))(@idraw/util@0.4.0-beta.34))(@idraw/renderer@0.4.0-beta.34(@idraw/util@0.4.0-beta.34))(@idraw/util@0.4.0-beta.34) - '@idraw/renderer': 0.4.0-beta.34(@idraw/util@0.4.0-beta.34) - '@idraw/types': 0.4.0-beta.34 - '@idraw/util': 0.4.0-beta.34 + '@idraw/board': 0.4.0-beta.36(@idraw/renderer@0.4.0-beta.36(@idraw/util@0.4.0-beta.36))(@idraw/util@0.4.0-beta.36) + '@idraw/core': 0.4.0-beta.36(@idraw/board@0.4.0-beta.36(@idraw/renderer@0.4.0-beta.36(@idraw/util@0.4.0-beta.36))(@idraw/util@0.4.0-beta.36))(@idraw/renderer@0.4.0-beta.36(@idraw/util@0.4.0-beta.36))(@idraw/util@0.4.0-beta.36) + '@idraw/renderer': 0.4.0-beta.36(@idraw/util@0.4.0-beta.36) + '@idraw/types': 0.4.0-beta.36 + '@idraw/util': 0.4.0-beta.36 ieee754@1.2.1: {} From 62cbc2c45d3f5f1c852fc1fc17c29401bd5df39c Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sun, 4 Aug 2024 10:43:54 +0800 Subject: [PATCH 2/2] feat: improve element tree, redo/undo logic --- .../src/css/modules/element-tree.less | 26 +++-- .../src/css/modules/page-tree.less | 19 +++- .../solid-color-picker.tsx | 5 +- .../src/modules/element-tree/index.tsx | 53 +++++++-- .../src/modules/element-tree/tree-node.tsx | 4 +- .../src/modules/element-tree/wrap.tsx | 41 +++++-- .../src/modules/page-tree/index.tsx | 59 +++++++++- .../src/modules/page-tree/tree-node.tsx | 4 +- .../src/modules/page-tree/wrap.tsx | 22 +++- packages/studio/src/modules/context-menu.tsx | 104 +++++++----------- .../src/modules/export-image-file/index.tsx | 2 +- .../studio/src/modules/nav-menu/index.tsx | 6 +- .../studio/src/modules/panel-layer/index.tsx | 11 +- .../modules/panel-page/add-page-button.tsx | 2 +- .../studio/src/modules/panel-page/index.tsx | 26 ++--- packages/studio/src/modules/sketch/index.tsx | 87 ++++++--------- .../src/modules/toolbar/action-menu.tsx | 10 +- packages/studio/src/modules/toolbar/index.tsx | 6 +- packages/studio/src/shared/event.ts | 20 ++-- .../studio/src/shared/snapshot-recorder.ts | 4 + packages/studio/src/shared/store.ts | 4 +- packages/studio/src/studio.tsx | 4 +- packages/studio/src/types/lib/shared.ts | 9 +- 23 files changed, 317 insertions(+), 211 deletions(-) diff --git a/packages/studio-base/src/css/modules/element-tree.less b/packages/studio-base/src/css/modules/element-tree.less index 2ada46d..3fa3771 100644 --- a/packages/studio-base/src/css/modules/element-tree.less +++ b/packages/studio-base/src/css/modules/element-tree.less @@ -5,10 +5,21 @@ .@{base-element-tree} { // fix antd style &.ant-tree { + // padding: 10px 0; + + &.ant-tree-directory { + .ant-tree-treenode:before { + bottom: 0; + } + } .ant-tree-iconEle { display: none; } + .ant-tree-treenode { + padding-bottom: 0; + } + .ant-tree-title { display: flex; width: 100%; @@ -33,9 +44,10 @@ position: relative; display: -webkit-box; float: inline-start; + height: 28px; + line-height: 28px; + padding-left: 28px; overflow: hidden; - height: 24px; - padding-left: 24px; white-space: initial; text-overflow: ellipsis; -webkit-box-orient: vertical; @@ -50,20 +62,12 @@ left: 0; right: 0; display: inline-flex; - // display: none; float: inline-start; - // overflow: hidden; - // white-space: nowrap; - // text-overflow: ellipsis; - - // &.@{base-element-tree}-node-title-input-active { - // display: flex; - // } } .@{base-element-tree}-node-title-icon { position: absolute; - top: 6px; + top: 8px; left: 6px; font-size: 12px; display: inline-flex; diff --git a/packages/studio-base/src/css/modules/page-tree.less b/packages/studio-base/src/css/modules/page-tree.less index 0944548..e6fb82c 100644 --- a/packages/studio-base/src/css/modules/page-tree.less +++ b/packages/studio-base/src/css/modules/page-tree.less @@ -5,10 +5,22 @@ .@{base-page-tree} { // fix antd style &.ant-tree { + // padding: 10px 0; + + &.ant-tree-directory { + .ant-tree-treenode:before { + bottom: 0; + } + } + .ant-tree-iconEle { display: none; } + .ant-tree-treenode { + padding-bottom: 0; + } + .ant-tree-title { display: flex; width: 100%; @@ -34,8 +46,9 @@ display: -webkit-box; float: inline-start; overflow: hidden; - height: 24px; - padding-left: 24px; + height: 28px; + line-height: 28px; + padding-left: 28px; white-space: initial; text-overflow: ellipsis; -webkit-box-orient: vertical; @@ -63,7 +76,7 @@ .@{base-page-tree}-node-title-icon { position: absolute; - top: 6px; + top: 8px; left: 6px; font-size: 14px; display: inline-flex; diff --git a/packages/studio-base/src/modules/element-detail/field-item/multiple-color-picker/solid-color-picker.tsx b/packages/studio-base/src/modules/element-detail/field-item/multiple-color-picker/solid-color-picker.tsx index 2b3e927..86d0363 100644 --- a/packages/studio-base/src/modules/element-detail/field-item/multiple-color-picker/solid-color-picker.tsx +++ b/packages/studio-base/src/modules/element-detail/field-item/multiple-color-picker/solid-color-picker.tsx @@ -111,9 +111,8 @@ export const SolidColorPicker: React.FC = ({ value = '', onCha className={className} value={new Color(value)} onChange={(data) => { - const hex8 = `#${data.toHex8().toUpperCase()}`; - const hex = hex8.endsWith('FF') ? `#${data.toHex().toUpperCase()}` : hex8; - + const hex8 = `${data.toHexString().toUpperCase()}`; + const hex = hex8.length === 9 && hex8.endsWith('FF') ? `${data.toHexString().toUpperCase()}` : hex8; onChange?.(hex); }} /> diff --git a/packages/studio-base/src/modules/element-tree/index.tsx b/packages/studio-base/src/modules/element-tree/index.tsx index 2712412..d248bdb 100644 --- a/packages/studio-base/src/modules/element-tree/index.tsx +++ b/packages/studio-base/src/modules/element-tree/index.tsx @@ -6,13 +6,38 @@ import type { TreeProps, TreeDataNode } from 'antd'; import type { ElementPosition } from 'idraw'; import { wrapTreeViewData } from './wrap'; import type { TreeNodeProps } from './tree-node'; -import type { ElementTreeData } from '../../types'; +import type { ElementTreeData, ElementTreeNode } from '../../types'; import { IconDown } from '../../icons'; import { generateClassName } from '../../css'; const { DirectoryTree } = Tree; const modName = 'base-element-tree'; +function reverseElementPosition(position: ElementPosition, treeData: ElementTreeData, opts?: { isToPosition: boolean }): ElementPosition { + const newPosition: ElementPosition = []; + let targetNodeList: ElementTreeNode[] = treeData; + for (let i = 0; i < position.length; i++) { + const index = position[i]; + let reverseIndex = -1; + if (index === 0) { + reverseIndex = targetNodeList.length - 1; + } else if (index >= targetNodeList.length - 1) { + reverseIndex = 0; + } else { + reverseIndex = targetNodeList.length - 1 - index; + } + newPosition.push(reverseIndex); + targetNodeList = targetNodeList[reverseIndex]?.children as ElementTreeNode[]; + } + + if (opts?.isToPosition === true) { + if (newPosition.length > 0) { + newPosition[newPosition.length - 1] += 1; + } + } + return newPosition; +} + export type ElementTreeProps = Pick & { height: number; className?: string; @@ -27,6 +52,7 @@ export type ElementTreeProps = Pick void; onDelete?: (e: { uuid: string }) => void; onGoToGroup?: (e: { uuid: string; position: ElementPosition }) => void; + reverse?: boolean; }; const treePosToElementPosition = (pos: string) => { @@ -50,13 +76,18 @@ export const ElementTree = React.forwardRef((props: ElementTreeProps, ref: any) defaultExpandedKeys, onDelete, onExpand, - onGoToGroup + onGoToGroup, + // reverse // TODO + reverse = true } = props; - const onSelectNode: TreeProps['onSelect'] = (selectedKeys, info) => { + const onSelectNode: TreeProps['onSelect'] = (skeys, info) => { const pos = treePosToElementPosition(info.node.pos); - const positions: ElementPosition[] = [pos]; - const uuids = [selectedKeys[0]] as string[]; + let positions: ElementPosition[] = [pos]; + if (reverse === true) { + positions = [reverseElementPosition(pos, treeData || [])]; + } + const uuids = [skeys[0]] as string[]; onSelect?.({ uuids, positions }); }; @@ -77,7 +108,8 @@ export const ElementTree = React.forwardRef((props: ElementTreeProps, ref: any) onDelete: onElementDelete, onGoToGroup, position: [], - selectedKeys: selectedKeys || [] + selectedKeys: selectedKeys || [], + reverse: !!reverse }); return ( @@ -113,7 +145,14 @@ export const ElementTree = React.forwardRef((props: ElementTreeProps, ref: any) } else if (node.dragOver === true) { to.push(0); } - onDrop?.({ from, to }); + let resultFrom = from; + let resultTo = to; + if (reverse === true) { + resultFrom = reverseElementPosition(from, treeData || []); + resultTo = reverseElementPosition(to, treeData || [], { isToPosition: true }); + } + + onDrop?.({ from: resultFrom, to: resultTo }); }} /> ); diff --git a/packages/studio-base/src/modules/element-tree/tree-node.tsx b/packages/studio-base/src/modules/element-tree/tree-node.tsx index 4310d4e..f014cc9 100644 --- a/packages/studio-base/src/modules/element-tree/tree-node.tsx +++ b/packages/studio-base/src/modules/element-tree/tree-node.tsx @@ -231,8 +231,8 @@ export const TreeNode = (props: TreeNodeProps) => { style={style} className={classnames(rootClassName, className, isSelected ? selectedClassName : null)} onClick={onClickTitle} - onMouseOver={onNodeMouseOver} - onMouseLeave={onNodeMouseLeave} + // onMouseOver={onNodeMouseOver} + // onMouseLeave={onNodeMouseLeave} onContextMenu={onNodeContextMenu} > diff --git a/packages/studio-base/src/modules/element-tree/wrap.tsx b/packages/studio-base/src/modules/element-tree/wrap.tsx index 43524da..0802365 100644 --- a/packages/studio-base/src/modules/element-tree/wrap.tsx +++ b/packages/studio-base/src/modules/element-tree/wrap.tsx @@ -11,18 +11,28 @@ type WrapOptions = Pick void; onContextMenu?: (e: { uuids: string[]; positions: ElementPosition[] }) => void; selectedKeys: string[]; + reverse: boolean; }; export function wrapTreeViewData(elementTree: ElementTreeData, opts: WrapOptions): ElementTreeViewData { const tree: ElementTreeViewData = []; - const { position } = opts; + const { position, reverse } = opts; if (Array.isArray(elementTree)) { const pos = [...position]; - elementTree.forEach((node, i) => { - pos.push(i); - tree.push(wrapTreeViewNode(node, { ...opts, ...{ position: [...pos] } })); - pos.pop(); - }); + if (reverse === true) { + for (let i = elementTree.length - 1; i >= 0; i--) { + const node = elementTree[i]; + pos.push(i); + tree.push(wrapTreeViewNode(node, { ...opts, ...{ position: [...pos] } })); + pos.pop(); + } + } else { + elementTree.forEach((node, i) => { + pos.push(i); + tree.push(wrapTreeViewNode(node, { ...opts, ...{ position: [...pos] } })); + pos.pop(); + }); + } } return tree; } @@ -55,11 +65,20 @@ const wrapTreeViewNode = (treeNode: ElementTreeNode, opts: WrapOptions) => { }; if (Array.isArray(treeNode.children)) { const pos = [...position]; - treeNode.children.forEach((child, i) => { - pos.push(i); - node.children.push(wrapTreeViewNode(child, { ...opts, ...{ position: [...pos] } })); - pos.pop(); - }); + if (opts.reverse === true) { + for (let i = treeNode.children.length - 1; i >= 0; i--) { + const child = treeNode.children[i]; + pos.push(i); + node.children.push(wrapTreeViewNode(child, { ...opts, ...{ position: [...pos] } })); + pos.pop(); + } + } else { + treeNode.children.forEach((child, i) => { + pos.push(i); + node.children.push(wrapTreeViewNode(child, { ...opts, ...{ position: [...pos] } })); + pos.pop(); + }); + } } return node; }; diff --git a/packages/studio-base/src/modules/page-tree/index.tsx b/packages/studio-base/src/modules/page-tree/index.tsx index 428e126..c240374 100644 --- a/packages/studio-base/src/modules/page-tree/index.tsx +++ b/packages/studio-base/src/modules/page-tree/index.tsx @@ -24,6 +24,7 @@ export type PageTreeProps = Pick void; onDrop?: (e: { from: ElementPosition; to: ElementPosition }) => void; onDelete?: (e: { uuid: string }) => void; + reverse?: boolean; }; const treePosToElementPosition = (pos: string) => { @@ -32,12 +33,52 @@ const treePosToElementPosition = (pos: string) => { return elemPos; }; +function reverseElementPosition(position: ElementPosition, treeData: PageTreeData, opts?: { isToPosition: boolean }): ElementPosition { + const newPosition: ElementPosition = []; + for (let i = 0; i < position.length; i++) { + const index = position[i]; + let reverseIndex = -1; + if (index === 0) { + reverseIndex = treeData.length - 1; + } else if (index >= treeData.length - 1) { + reverseIndex = 0; + } else { + reverseIndex = treeData.length - 1 - index; + } + newPosition.push(reverseIndex); + } + + if (opts?.isToPosition === true) { + if (newPosition.length > 0) { + newPosition[newPosition.length - 1] += 1; + } + } + return newPosition; +} + export const PageTree = React.forwardRef((props: PageTreeProps, ref: any) => { - const { height, className, style, treeData, onTitleChange, onOperationToggle, onSelect, selectedKeys, onDrop, defaultExpandedKeys, expandedKeys, onDelete } = - props; + const { + height, + className, + style, + treeData, + onTitleChange, + onOperationToggle, + onSelect, + selectedKeys, + onDrop, + defaultExpandedKeys, + expandedKeys, + onDelete, + // reverse // TODO + reverse = true + } = props; const onSelectNode: TreeProps['onSelect'] = (selectedKeys, info) => { const pos = treePosToElementPosition(info.node.pos); - const positions: ElementPosition[] = [pos]; + let positions: ElementPosition[] = [pos]; + if (reverse === true) { + positions = [reverseElementPosition(pos, treeData || [])]; + } const uuids = [selectedKeys[0]] as string[]; onSelect?.({ uuids, positions }); }; @@ -54,7 +95,8 @@ export const PageTree = React.forwardRef((props: PageTreeProps, ref: any) => { onOperationToggle, onDelete: onElementDelete, position: [], - selectedKeys: selectedKeys || [] + selectedKeys: selectedKeys || [], + reverse: !!reverse }); return ( @@ -94,7 +136,14 @@ export const PageTree = React.forwardRef((props: PageTreeProps, ref: any) => { } else if (node.dragOver === true) { to.push(0); } - onDrop?.({ from, to }); + let resultFrom = from; + let resultTo = to; + if (reverse === true) { + resultFrom = reverseElementPosition(from, treeData || []); + resultTo = reverseElementPosition(to, treeData || [], { isToPosition: true }); + } + + onDrop?.({ from: resultFrom, to: resultTo }); }} /> ); diff --git a/packages/studio-base/src/modules/page-tree/tree-node.tsx b/packages/studio-base/src/modules/page-tree/tree-node.tsx index 425909e..08c5cd8 100644 --- a/packages/studio-base/src/modules/page-tree/tree-node.tsx +++ b/packages/studio-base/src/modules/page-tree/tree-node.tsx @@ -185,8 +185,8 @@ export const TreeNode = (props: TreeNodeProps) => { style={style} className={classnames(rootClassName, className, isSelected ? selectedClassName : null)} onClick={onClickTitle} - onMouseOver={onNodeMouseOver} - onMouseLeave={onNodeMouseLeave} + // onMouseOver={onNodeMouseOver} + // onMouseLeave={onNodeMouseLeave} > diff --git a/packages/studio-base/src/modules/page-tree/wrap.tsx b/packages/studio-base/src/modules/page-tree/wrap.tsx index 6f9f01f..a9a2d5e 100644 --- a/packages/studio-base/src/modules/page-tree/wrap.tsx +++ b/packages/studio-base/src/modules/page-tree/wrap.tsx @@ -10,18 +10,28 @@ type WrapOptions = Pick void; selectedKeys: string[]; + reverse: boolean; }; export function wrapTreeViewData(elementTree: PageTreeData, opts: WrapOptions): PageTreeViewData { const tree: PageTreeViewData = []; - const { position } = opts; + const { position, reverse } = opts; if (Array.isArray(elementTree)) { const pos = [...position]; - elementTree.forEach((node, i) => { - pos.push(i); - tree.push(wrapTreeViewNode(node, { ...opts, ...{ position: [...pos] } })); - pos.pop(); - }); + if (reverse === true) { + for (let i = elementTree.length - 1; i >= 0; i--) { + const node = elementTree[i]; + pos.push(i); + tree.push(wrapTreeViewNode(node, { ...opts, ...{ position: [...pos] } })); + pos.pop(); + } + } else { + elementTree.forEach((node, i) => { + pos.push(i); + tree.push(wrapTreeViewNode(node, { ...opts, ...{ position: [...pos] } })); + pos.pop(); + }); + } } return tree; } diff --git a/packages/studio/src/modules/context-menu.tsx b/packages/studio/src/modules/context-menu.tsx index f28873b..694a7dd 100644 --- a/packages/studio/src/modules/context-menu.tsx +++ b/packages/studio/src/modules/context-menu.tsx @@ -1,5 +1,5 @@ import type { MenuProps } from 'antd'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useLocale } from '../locale'; import type { SharedEvent, SharedStore, HookUseContextMenuOptions, UpdateContextMenuOptions } from '../types'; @@ -13,12 +13,21 @@ const MenuItem = (props: { label: React.ReactNode; suffix?: React.ReactNode }) = ); }; +type InnerContextMenuItem = { + name: string; + hotKey: string; + key: string; + disabled: boolean; + onClick: () => void; +}; + export const useContextMenuOptions: HookUseContextMenuOptions = (opts: { sharedEvent: SharedEvent; sharedStore: SharedStore }) => { const [moduleLocale] = useLocale('contextMenu'); const { sharedEvent } = opts; - const defaultItems: MenuProps['items'] = [ + const defaultInnerItems: InnerContextMenuItem[] = [ { - label: Ctrl+C} />, + name: moduleLocale.copy, + hotKey: 'Ctrl+C', key: 'copy', disabled: true, onClick: () => { @@ -26,7 +35,8 @@ export const useContextMenuOptions: HookUseContextMenuOptions = (opts: { sharedE } }, { - label: Ctrl+V} />, + name: moduleLocale.paste, + hotKey: 'Ctrl+V', key: 'paste', disabled: true, onClick: () => { @@ -34,7 +44,8 @@ export const useContextMenuOptions: HookUseContextMenuOptions = (opts: { sharedE } }, { - label: Ctrl+X} />, + name: moduleLocale.cut, + hotKey: 'Ctrl+X', key: 'cut', disabled: true, onClick: () => { @@ -42,79 +53,46 @@ export const useContextMenuOptions: HookUseContextMenuOptions = (opts: { sharedE } }, { - label: Del} />, + name: moduleLocale.delete, + hotKey: 'Del', key: 'delete', disabled: true, onClick: () => { sharedEvent.trigger('delete'); } } - // { - // label: moduleLocale.bringToFront, - // key: 'bring-to-front', - // disabled: true, - // onClick: () => { - // console.log('bring-to-front'); - // } - // }, - // { - // label: moduleLocale.sendToBack, - // key: 'send-to-back', - // disabled: true, - // onClick: () => { - // console.log('send-to-back'); - // } - // } ]; - const [items, setItems] = useState['items']>(defaultItems); + + const innerItemsToViewItems = (list: InnerContextMenuItem[]) => { + const newItems = list.map((innerItem) => { + const name = (moduleLocale as any)[innerItem?.key as any] || innerItem.name; + const newItem = { + label: {innerItem.hotKey}} />, + key: innerItem.key, + disabled: innerItem.disabled, + onClick: innerItem.onClick + }; + return newItem; + }); + return newItems; + }; + + const [innerItems, setInnerItems] = useState(defaultInnerItems); + const [items, setItems] = useState['items']>(innerItemsToViewItems(innerItems)); + + useEffect(() => { + setItems(innerItemsToViewItems(innerItems)); + }, [innerItems, moduleLocale]); const updateContextMenuOptions: UpdateContextMenuOptions = (opts) => { const { selectedElements } = opts; - items?.forEach((item) => { + innerItems?.forEach((item) => { if (item && ['copy', 'paste', 'cut', 'delete'].includes(item?.key as string)) { (item as any).disabled = selectedElements?.length > 0 ? false : true; } }); - setItems([...items]); + setInnerItems([...innerItems]); }; return [items, updateContextMenuOptions]; }; - -// export function createContextMenuOptions() { -// const items: MenuProps['items'] = [ -// { -// label: 'Copy', -// key: 'copy', -// disabled: true, -// onClick: () => { -// console.log('copy'); -// } -// }, -// { -// label: 'Paste here', -// key: 'paste-here', -// disabled: true, -// onClick: () => { -// console.log('paste-here'); -// } -// }, -// { -// label: 'Bring to front', -// key: 'bring-to-front', -// disabled: true, -// onClick: () => { -// console.log('bring-to-front'); -// } -// }, -// { -// label: 'Send to back', -// key: 'send-to-back', -// disabled: true, -// onClick: () => { -// console.log('send-to-back'); -// } -// } -// ]; -// return items; -// } diff --git a/packages/studio/src/modules/export-image-file/index.tsx b/packages/studio/src/modules/export-image-file/index.tsx index 146ec9d..c445672 100644 --- a/packages/studio/src/modules/export-image-file/index.tsx +++ b/packages/studio/src/modules/export-image-file/index.tsx @@ -45,7 +45,7 @@ export const ExportFile = (props: ExportFileProps) => { const [form] = Form.useForm(); const resetImage = useCallback((pixelRatio: number) => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); if (!idraw) { return; } diff --git a/packages/studio/src/modules/nav-menu/index.tsx b/packages/studio/src/modules/nav-menu/index.tsx index b0478cf..9eff2fe 100644 --- a/packages/studio/src/modules/nav-menu/index.tsx +++ b/packages/studio/src/modules/nav-menu/index.tsx @@ -60,7 +60,7 @@ export const NavMenu = (props: NavMenuProps) => { const disabledAddElement = !!(editMode === 'page' && (pageTree.length === 0 || isPageOverview)); const resetDevicePixelRatio = (radio: number) => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); idraw?.resize({ devicePixelRatio: radio }); @@ -114,7 +114,7 @@ export const NavMenu = (props: NavMenuProps) => { key: 'export-json-file', label: moduleLocale.exportJSONFile, onClick: () => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); const data = idraw?.getData({ compact: true }); if (data) { const text = JSON.stringify(data); @@ -234,7 +234,7 @@ export const NavMenu = (props: NavMenuProps) => { ]; const refreshSelectedKeys = () => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); const newSelectedKeys: string[] = []; if (idraw) { const { viewSizeInfo } = idraw.getViewInfo(); diff --git a/packages/studio/src/modules/panel-layer/index.tsx b/packages/studio/src/modules/panel-layer/index.tsx index 83a7ba2..1cd73c8 100644 --- a/packages/studio/src/modules/panel-layer/index.tsx +++ b/packages/studio/src/modules/panel-layer/index.tsx @@ -45,7 +45,7 @@ export const PanelLayer = (props: PanelLayerProps) => { }; const selectElementsByPositions = (positions: ElementPosition[]) => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); idraw?.selectElementsByPositions(positions); }; @@ -168,8 +168,13 @@ export const PanelLayer = (props: PanelLayerProps) => { const targetElem = findElementFromListByPosition(e.to, editingData.elements); if (targetElem) { - targetElem.x = 0; - targetElem.y = 0; + // TODO + if (e.to.length === e.to.length) { + // TODO + } else { + targetElem.x = 0; + targetElem.y = 0; + } } const elementTree = getElementTree(editingData); diff --git a/packages/studio/src/modules/panel-page/add-page-button.tsx b/packages/studio/src/modules/panel-page/add-page-button.tsx index 1d29126..81e7086 100644 --- a/packages/studio/src/modules/panel-page/add-page-button.tsx +++ b/packages/studio/src/modules/panel-page/add-page-button.tsx @@ -64,7 +64,7 @@ export const AddPageButton = (props: AddPageButtonProps) => { return; } const elem = deepCloneElement(element); - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); const data = idraw?.getData(); const start: PointSize = { x: 0, y: 0 }; if (idraw && Array.isArray(data?.elements)) { diff --git a/packages/studio/src/modules/panel-page/index.tsx b/packages/studio/src/modules/panel-page/index.tsx index f9bdb57..1beac34 100644 --- a/packages/studio/src/modules/panel-page/index.tsx +++ b/packages/studio/src/modules/panel-page/index.tsx @@ -71,17 +71,17 @@ export const PanelPage = (props: PanelPageProps) => { const [selectedPageUUIDs, setSelectedPageUUIDs] = useState(getSelectedPageKeys()); useEffect(() => { - const idraw = sharedStore.get('idraw'); - const listenSelectedPage = (e: { uuids: string[] }) => { + const idraw = sharedStore.getStatic('idraw'); + const listenSelectedPage = (e: { uuids?: string[] }) => { if (refInPageOverview.current === true) { const { uuids = [] } = e; setSelectedPageUUIDs([...uuids]); } }; - idraw?.on(eventKeys.select, listenSelectedPage); + idraw?.on(eventKeys.SELECT, listenSelectedPage); return () => { - idraw?.off(eventKeys.select, listenSelectedPage); + idraw?.off(eventKeys.SELECT, listenSelectedPage); }; }, []); @@ -90,12 +90,6 @@ export const PanelPage = (props: PanelPageProps) => { const pageUUID = pageTree[editingDataPosition[0]]?.uuid; if (pageUUID && !selectedPageUUIDs.includes(pageUUID)) { setSelectedPageUUIDs([pageUUID]); - // setTimeout(() => { - // // TODO - // refPageTree.current?.scrollTo({ - // key: pageUUID - // }); - // }, 300); } } }, [editingDataPosition]); @@ -121,7 +115,7 @@ export const PanelPage = (props: PanelPageProps) => { }; useEffect(() => { - const idraw = sharedStore?.get('idraw'); + const idraw = sharedStore?.getStatic('idraw'); let currentKeys: string[] = []; if (inPageOverview === true) { sharedEvent.trigger('resetEditingView', { type: 'back-root', position: null }); @@ -157,7 +151,7 @@ export const PanelPage = (props: PanelPageProps) => { }; const selectElementsByPositions = (positions: ElementPosition[]) => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); idraw?.selectElementsByPositions(positions); }; @@ -281,9 +275,9 @@ export const PanelPage = (props: PanelPageProps) => { }} onSelect={(e) => { if (e?.positions.length === 1) { - const idraw = sharedStore.get('idraw'); - idraw?.trigger(eventKeys.select, { uuids: [] }); - idraw?.trigger(eventKeys.clearSelect); + const idraw = sharedStore.getStatic('idraw'); + idraw?.trigger(eventKeys.SELECT, { uuids: [] }); + idraw?.trigger(eventKeys.CLEAR_SELECT); if (inPageOverview) { if (!selectedPageUUIDs?.includes(e.uuids[0])) { selectElementsByPositions(e.positions); @@ -379,7 +373,7 @@ export const PanelPage = (props: PanelPageProps) => { type: 'update', payload: { editingData: { ...editingData }, elementTree } }); - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); if (operations.locked === true) { idraw?.cancelElements(); } diff --git a/packages/studio/src/modules/sketch/index.tsx b/packages/studio/src/modules/sketch/index.tsx index 6e76c9e..20fa3ca 100644 --- a/packages/studio/src/modules/sketch/index.tsx +++ b/packages/studio/src/modules/sketch/index.tsx @@ -61,7 +61,7 @@ export const Sketch = (props: SketchProps) => { idraw.enable('ruler'); refIDraw.current = idraw; - const listenMiddlewareEventSelect = (e: { uuids: string[]; positions: ElementPosition[] }) => { + const listenMiddlewareEventSelect = (e: { uuids?: string[]; positions?: ElementPosition[] }) => { const editingData = refEditingData.current; let { uuids } = e; if (uuids?.length === 1 && refSelectedUUIDs.current?.length === 1 && uuids[0] === refSelectedUUIDs.current[0]) { @@ -69,7 +69,7 @@ export const Sketch = (props: SketchProps) => { } const { positions } = e; - if (positions && Array.isArray(positions)) { + if (positions && Array.isArray(positions) && positions.length > 0) { const elems = findElementsFromListByPositions(positions, editingData.elements); uuids = elems.map((e: { uuid: any }) => e.uuid); } @@ -83,7 +83,7 @@ export const Sketch = (props: SketchProps) => { const listenDataChange = (e: { data: Data; type: string }) => { const { data, type } = e; - const snapshotRecorder = sharedStore.get('snapshotRecorder'); + const snapshotRecorder = sharedStore.getStatic('snapshotRecorder'); // recording snapshot if (snapshotRecorder) { if (!snapshotRecorder.hasBeforeSnapshot()) { @@ -279,18 +279,19 @@ export const Sketch = (props: SketchProps) => { } const elementTree = getElementTree(editingData); - idraw.cancelElements(); - idraw.centerContent({ data: editingData }); + // idraw.cancelElements(); + // idraw.centerContent({ data: editingData }); dispatch({ type: 'update', payload: { data: { ...data }, - editingData: editingData, + // editingData: editingData, elementTree, pageTree, editingDataPosition } }); + sharedEvent.trigger('resetEditingView', { type: 'go-to-page', position: [pageTree.length - 1] }); }; const deletePageCallback = (e: SharedEventMap['deletePage']) => { @@ -315,54 +316,15 @@ export const Sketch = (props: SketchProps) => { const pageTree = getElementTree(data); payload.data = { ...data }; payload.pageTree = pageTree; - - if (refEditingDataPosition.current.length === 1 && index === refEditingDataPosition.current[0]) { - let nextPage = data?.elements?.[index]; - let pageIndex = index; - while (!nextPage?.extends?.isPage && pageIndex > 0) { - pageIndex--; - nextPage = data?.elements?.[pageIndex]; - } - if (nextPage) { - const { children, ...restDetail } = (nextPage as Element<'group'>).detail; - const newEditingData: Data = { - elements: children || [], - layout: { - x: 0, - y: 0, - w: nextPage.w, - h: nextPage.h, - detail: restDetail, - operations: { - position: 'relative' - } - } - }; - - const elementTree = getElementTree(newEditingData); - payload.editingData = newEditingData; - payload.editingDataPosition = [pageIndex]; - payload.elementTree = elementTree; - idraw.centerContent({ data: newEditingData }); - } else { - payload.editingData = { elements: [] }; - payload.editingDataPosition = []; - payload.elementTree = []; - } - } else if (refEditingDataPosition.current.length === 0) { - payload.editingData = { ...data }; - } - - if (pageTree.length === 0) { - payload.elementTree = []; - payload.editingDataPosition = []; - payload.editingData = { elements: [] }; - } dispatch({ type: 'update', payload }); - // idraw.trigger(eventKeys.CLEAR_SELECT, {}); + let selectPageIndex = refEditingDataPosition.current[0] || 0; + if (selectPageIndex >= index) { + selectPageIndex -= 1; + sharedEvent.trigger('resetEditingView', { type: 'go-to-page', position: [selectPageIndex || 0] }); + } }; const resetEditingViewCallback = (e: SharedEventMap['resetEditingView']) => { @@ -400,6 +362,9 @@ export const Sketch = (props: SketchProps) => { }); idraw.centerContent({ data: newEditingData }); idraw.trigger(eventKeys.CLEAR_SELECT, {}); + const snapshotRecorder = sharedStore.getStatic('snapshotRecorder'); + snapshotRecorder?.clear(); + snapshotRecorder?.setBeforeData(newEditingData); } else if (type === 'go-to-group' && position) { // update new editing data const newEditingDataPosition = [...position]; @@ -417,6 +382,10 @@ export const Sketch = (props: SketchProps) => { }); idraw.centerContent({ data: newEditingData }); idraw.trigger(eventKeys.CLEAR_SELECT, {}); + + const snapshotRecorder = sharedStore.getStatic('snapshotRecorder'); + snapshotRecorder?.clear(); + snapshotRecorder?.setBeforeData(newEditingData); } else if (type === 'go-to-next-group' && position) { // update new editing data const newEditingDataPosition = [...editingDataPosition, ...position]; @@ -434,6 +403,10 @@ export const Sketch = (props: SketchProps) => { }); idraw.centerContent({ data: newEditingData }); idraw.trigger(eventKeys.CLEAR_SELECT, {}); + + const snapshotRecorder = sharedStore.getStatic('snapshotRecorder'); + snapshotRecorder?.clear(); + snapshotRecorder?.setBeforeData(newEditingData); } else if (type === 'back-one' && editingDataPosition.length > 0) { const newEditingDataPosition = [...editingDataPosition]; newEditingDataPosition.pop(); @@ -456,6 +429,10 @@ export const Sketch = (props: SketchProps) => { // }); idraw.centerContent({ data: newEditingData }); idraw.trigger(eventKeys.CLEAR_SELECT, {}); + + const snapshotRecorder = sharedStore.getStatic('snapshotRecorder'); + snapshotRecorder?.clear(); + snapshotRecorder?.setBeforeData(newEditingData); } else if (type === 'back-root') { // update new editing data const newEditingDataPosition: ElementPosition = []; @@ -477,6 +454,10 @@ export const Sketch = (props: SketchProps) => { // }); idraw.centerContent({ data: newEditingData }); idraw.trigger(eventKeys.CLEAR_SELECT, {}); + + const snapshotRecorder = sharedStore.getStatic('snapshotRecorder'); + snapshotRecorder?.clear(); + snapshotRecorder?.setBeforeData(newEditingData); } }; @@ -561,7 +542,7 @@ export const Sketch = (props: SketchProps) => { refHasFirstDraw.current = true; } - sharedStore.set('idraw', idraw); + sharedStore.setStatic('idraw', idraw); return () => { refHasFirstDraw.current = false; @@ -575,8 +556,8 @@ export const Sketch = (props: SketchProps) => { // sharedEvent.off('deleteElement', deleteElementCallback); // sharedEvent.off('resetEditingView', resetEditingViewCallback); // sharedEvent.off('resetData', resetDataCallback); - sharedStore.set('idraw', null); - sharedStore.set('snapshotRecorder', null); + sharedStore.setStatic('idraw', null); + sharedStore.setStatic('snapshotRecorder', null); }; }, []); diff --git a/packages/studio/src/modules/toolbar/action-menu.tsx b/packages/studio/src/modules/toolbar/action-menu.tsx index df91d84..7ea0cae 100644 --- a/packages/studio/src/modules/toolbar/action-menu.tsx +++ b/packages/studio/src/modules/toolbar/action-menu.tsx @@ -31,7 +31,7 @@ export const ActionMenu = (props: ActionMenuProps) => { }; useEffect(() => { - const snapshotRecorder = sharedStore.get('snapshotRecorder'); + const snapshotRecorder = sharedStore.getStatic('snapshotRecorder'); const doCallback = (e: { canRedo: boolean; canUndo: boolean }) => { setCanRedo(e.canRedo); setCanUndo(e.canUndo); @@ -44,10 +44,17 @@ export const ActionMenu = (props: ActionMenuProps) => { setCanRedo(e.canRedo); setCanUndo(e.canUndo); }; + + const clearCallback = () => { + setCanRedo(false); + setCanUndo(false); + }; + if (snapshotRecorder) { snapshotRecorder.on('do', doCallback); snapshotRecorder.on('undo', undoCallback); snapshotRecorder.on('redo', redoCallback); + snapshotRecorder.on('clear', clearCallback); } return () => { @@ -55,6 +62,7 @@ export const ActionMenu = (props: ActionMenuProps) => { snapshotRecorder.off('do', doCallback); snapshotRecorder.off('undo', undoCallback); snapshotRecorder.off('redo', redoCallback); + snapshotRecorder.off('clear', clearCallback); } }; }, []); diff --git a/packages/studio/src/modules/toolbar/index.tsx b/packages/studio/src/modules/toolbar/index.tsx index 385bc46..c658c69 100644 --- a/packages/studio/src/modules/toolbar/index.tsx +++ b/packages/studio/src/modules/toolbar/index.tsx @@ -40,7 +40,7 @@ export const Toolbar = (props: ToolbarProps) => { const moduleLocale = useModuleLocale(); useEffect(() => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); if (toggleRuler === true) { idraw?.enable('ruler'); } else { @@ -49,7 +49,7 @@ export const Toolbar = (props: ToolbarProps) => { }, [toggleRuler]); useEffect(() => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); idraw?.setMode(toggleDrag ? 'drag' : 'select'); }, [toggleDrag]); @@ -97,7 +97,7 @@ export const Toolbar = (props: ToolbarProps) => { type="text" icon={} onClick={() => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); idraw?.centerContent(); }} /> diff --git a/packages/studio/src/shared/event.ts b/packages/studio/src/shared/event.ts index 66b5551..566a65a 100644 --- a/packages/studio/src/shared/event.ts +++ b/packages/studio/src/shared/event.ts @@ -7,7 +7,7 @@ export function initActionEvent(opts: { sharedEvent: SharedEvent; sharedStore: S const { sharedEvent, sharedStore } = opts; sharedEvent.on('copy', () => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); const selectedUUIDs = sharedStore.get('selectedUUIDs'); const data = idraw?.getData(); if (data && Array.isArray(selectedUUIDs) && selectedUUIDs.length > 0) { @@ -22,7 +22,7 @@ export function initActionEvent(opts: { sharedEvent: SharedEvent; sharedStore: S }); sharedEvent.on('paste', () => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); const clipboardCache = sharedStore.get('clipboard'); const selectedUUIDs = sharedStore.get('selectedUUIDs'); @@ -78,7 +78,7 @@ export function initActionEvent(opts: { sharedEvent: SharedEvent; sharedStore: S }); sharedEvent.on('cut', () => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); const selectedUUIDs = sharedStore.get('selectedUUIDs'); const data = idraw?.getData(); if (data && Array.isArray(selectedUUIDs) && selectedUUIDs.length > 0) { @@ -106,11 +106,11 @@ export function initActionEvent(opts: { sharedEvent: SharedEvent; sharedStore: S elementTree } }); - idraw?.trigger(eventKeys.clearSelect); + idraw?.trigger(eventKeys.CLEAR_SELECT); }); sharedEvent.on('delete', () => { - const idraw = sharedStore.get('idraw'); + const idraw = sharedStore.getStatic('idraw'); const selectedUUIDs = sharedStore.get('selectedUUIDs'); const data = idraw?.getData(); if (data && Array.isArray(selectedUUIDs) && selectedUUIDs.length > 0) { @@ -128,14 +128,14 @@ export function initActionEvent(opts: { sharedEvent: SharedEvent; sharedStore: S elementTree } }); - idraw?.trigger(eventKeys.clearSelect); + idraw?.trigger(eventKeys.CLEAR_SELECT); } } }); sharedEvent.on('undo', () => { - const idraw = sharedStore?.get('idraw'); - const snapshotRecorder = sharedStore.get('snapshotRecorder'); + const idraw = sharedStore?.getStatic('idraw'); + const snapshotRecorder = sharedStore.getStatic('snapshotRecorder'); const data = snapshotRecorder?.undo(idraw?.getData()); if (data) { sharedEvent?.trigger('resetEditingData', { @@ -148,8 +148,8 @@ export function initActionEvent(opts: { sharedEvent: SharedEvent; sharedStore: S } }); sharedEvent.on('redo', () => { - const idraw = sharedStore?.get('idraw'); - const snapshotRecorder = sharedStore.get('snapshotRecorder'); + const idraw = sharedStore?.getStatic('idraw'); + const snapshotRecorder = sharedStore.getStatic('snapshotRecorder'); const data = snapshotRecorder?.redo(idraw?.getData()); if (data) { sharedEvent?.trigger('resetEditingData', { diff --git a/packages/studio/src/shared/snapshot-recorder.ts b/packages/studio/src/shared/snapshot-recorder.ts index 6262173..79b2ddf 100644 --- a/packages/studio/src/shared/snapshot-recorder.ts +++ b/packages/studio/src/shared/snapshot-recorder.ts @@ -75,6 +75,7 @@ interface SnapshotRecorderEventMap { do: { canUndo: boolean; canRedo: boolean }; undo: { canUndo: boolean; canRedo: boolean }; redo: { canUndo: boolean; canRedo: boolean }; + clear: void; } export class SnapshotRecorder { @@ -120,12 +121,15 @@ export class SnapshotRecorder { clear() { this.#doStack = []; this.#undoStack = []; + this.#beforeSnapshot = null; + this.#event.trigger('clear'); } destroy() { this.clear(); this.#doStack = null as any; this.#undoStack = null as any; + this.#beforeSnapshot = null; this.#event.clear(); this.#event.destroy(); this.#event = null as any; diff --git a/packages/studio/src/shared/store.ts b/packages/studio/src/shared/store.ts index 39be8dc..76cfde2 100644 --- a/packages/studio/src/shared/store.ts +++ b/packages/studio/src/shared/store.ts @@ -2,12 +2,10 @@ import type { SharedStorage } from '../types'; export function createSharedDefaultStorage(): SharedStorage { return { - idraw: null, clipboard: { type: 'default', data: null }, - selectedUUIDs: [], - snapshotRecorder: null + selectedUUIDs: [] }; } diff --git a/packages/studio/src/studio.tsx b/packages/studio/src/studio.tsx index 2272ecb..536a400 100644 --- a/packages/studio/src/studio.tsx +++ b/packages/studio/src/studio.tsx @@ -38,8 +38,8 @@ export const Studio = React.forwardRef((props: StudioProps, ref: any) => { }) ); const refSnapshotRecorder = useRef(new SnapshotRecorder()); - if (!refSharedStore.current?.get('snapshotRecorder')) { - refSharedStore.current?.set('snapshotRecorder', refSnapshotRecorder.current); + if (!refSharedStore.current?.getStatic('snapshotRecorder')) { + refSharedStore.current?.setStatic('snapshotRecorder', refSnapshotRecorder.current); } useEffect(() => { diff --git a/packages/studio/src/types/lib/shared.ts b/packages/studio/src/types/lib/shared.ts index c64bac0..3933176 100644 --- a/packages/studio/src/types/lib/shared.ts +++ b/packages/studio/src/types/lib/shared.ts @@ -11,16 +11,21 @@ interface clipboardMap { } export interface SharedStorage { - idraw: IDraw | null; + // idraw: IDraw | null; clipboard: { type: keyof clipboardMap | string; data: clipboardMap[keyof clipboardMap] | any; }; selectedUUIDs: string[]; + // snapshotRecorder: SnapshotRecorder | null; +} + +export interface SharedStatic { + idraw: IDraw | null; snapshotRecorder: SnapshotRecorder | null; } -export type SharedStore = Store; +export type SharedStore = Store; export interface SharedEventMap { dispatch: { type: StudioActionType; payload: Partial };