diff --git a/README.md b/README.md index f86dc58..0158e64 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,13 @@ idrawjs.com/studio/

+## Sponsors + +`@idraw/studio` is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider [sponsoring iDrawjs's development](https://opencollective.com/idrawjs). + +[![Become a Backer](https://opencollective.com/idrawjs/tiers/backers.svg?avatarHeight=48)](https://opencollective.com/idrawjs) + + ## @idraw/studio Preview @@ -25,7 +32,7 @@ The preview of `@idraw/studo`. idraw-studio-light-theme - idraw-studio-dark-theme + idraw-studio-dark-theme

diff --git a/package.json b/package.json index bd7da8b..50052f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": false, - "version": "0.4.0-alpha.12", + "version": "0.4.0-alpha.13", "workspaces": [ "packages/*" ], @@ -19,7 +19,7 @@ "upgrade:version": "vite-node ./scripts/upgrade-version.ts && pnpm i" }, "dependencies": { - "idraw": "0.4.0-beta.12", + "idraw": "0.4.0-beta.13", "antd": "5.12.1" }, "devDependencies": { diff --git a/packages/studio-base/package.json b/packages/studio-base/package.json index ea5123e..7513599 100644 --- a/packages/studio-base/package.json +++ b/packages/studio-base/package.json @@ -1,6 +1,6 @@ { "name": "@idraw/studio-base", - "version": "0.4.0-alpha.12", + "version": "0.4.0-alpha.13", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", @@ -22,7 +22,7 @@ "peerDependencies": { "antd": "^5.12.1", "classnames": "^2.3.2", - "idraw": "^0.4.0-beta.12", + "idraw": "^0.4.0-beta.13", "react": "^18.2.0", "react-color": "^2.19.3", "react-dom": "^18.2.0" diff --git a/packages/studio-base/src/css/theme/dark.less b/packages/studio-base/src/css/theme/dark.less index 516b420..70eeb4a 100644 --- a/packages/studio-base/src/css/theme/dark.less +++ b/packages/studio-base/src/css/theme/dark.less @@ -1,7 +1,7 @@ // @import '../variable.less'; &.@{prefix}-theme.@{prefix}-theme-dark { - --@{prefix}-bg-color: @gray-9; + --@{prefix}-bg-color: @gray-10; --@{prefix}-canvas-bg-color: @gray-9; --@{prefix}-text-color: @gray-4; --@{prefix}-text-hover-color: @gray-1; diff --git a/packages/studio-base/src/icons/file-copy.tsx b/packages/studio-base/src/icons/file-copy.tsx new file mode 100644 index 0000000..904c6bf --- /dev/null +++ b/packages/studio-base/src/icons/file-copy.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { IconWrapper } from './util'; +import type { IconWrapperProps } from './util'; + +const Icon = (props: IconWrapperProps) => { + return ( + + + + + + + ); +}; + +export default Icon; diff --git a/packages/studio-base/src/icons/index.ts b/packages/studio-base/src/icons/index.ts index 09de87d..df65cf8 100644 --- a/packages/studio-base/src/icons/index.ts +++ b/packages/studio-base/src/icons/index.ts @@ -28,6 +28,7 @@ import IconDrag from './drag'; import IconEdit from './edit'; import IconExperiment from './experiment'; import IconFile from './file'; +import IconFileCopy from './file-copy'; import IconFullCornerRadius from './full-corner-radius'; import IconGroup from './group'; import IconHand from './hand'; @@ -47,6 +48,7 @@ import IconPath from './path'; import IconPen from './pen'; import IconPlus from './plus'; import IconPosition from './position'; +import IconRedo from './redo'; import IconRect from './rect'; import IconResize from './resize'; import IconRotation from './rotation'; @@ -56,6 +58,7 @@ import IconSetting from './setting'; import IconSolidColor from './solid-color'; import IconStar from './star'; import IconText from './text'; +import IconUndo from './undo'; import IconUnlock from './unlock'; import IconVerticalBottom from './vertical-bottom'; import IconVerticalMiddle from './vertical-middle'; @@ -94,6 +97,7 @@ export { IconEdit, IconExperiment, IconFile, + IconFileCopy, IconFullCornerRadius, IconGroup, IconHand, @@ -113,6 +117,7 @@ export { IconPen, IconPlus, IconPosition, + IconRedo, IconRect, IconResize, IconRotation, @@ -122,6 +127,7 @@ export { IconSolidColor, IconStar, IconText, + IconUndo, IconUnlock, IconVerticalBottom, IconVerticalMiddle, diff --git a/packages/studio-base/src/icons/redo.tsx b/packages/studio-base/src/icons/redo.tsx new file mode 100644 index 0000000..368c1f9 --- /dev/null +++ b/packages/studio-base/src/icons/redo.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { IconWrapper } from './util'; +import type { IconWrapperProps } from './util'; + +const Icon = (props: IconWrapperProps) => { + return ( + + + + + + ); +}; + +export default Icon; diff --git a/packages/studio-base/src/icons/undo.tsx b/packages/studio-base/src/icons/undo.tsx new file mode 100644 index 0000000..d021f8a --- /dev/null +++ b/packages/studio-base/src/icons/undo.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { IconWrapper } from './util'; +import type { IconWrapperProps } from './util'; + +const Icon = (props: IconWrapperProps) => { + return ( + + + + + + ); +}; + +export default Icon; diff --git a/packages/studio-base/src/index.ts b/packages/studio-base/src/index.ts index 6b0ac1b..20a7c56 100644 --- a/packages/studio-base/src/index.ts +++ b/packages/studio-base/src/index.ts @@ -2,7 +2,6 @@ export * from './types'; export { ConfigContext, ConfigProvider, type ConfigContextValue } from './modules/config-provider'; export { SplitPane, SplitPaneItem } from './modules/split-pane'; -export { Toolbar, type ToolbarProps } from './modules/toolbar'; export { ThemeSwitch, type ThemeSwitchProps } from './modules/theme-switch'; export { LocaleSelector, type LocaleSelectorProps } from './modules/locale-selector'; export { ScaleSelector, type ScaleSelectorProps } from './modules/scale-selector'; @@ -45,6 +44,7 @@ export { IconEdit, IconExperiment, IconFile, + IconFileCopy, IconFullCornerRadius, IconGroup, IconHand, @@ -64,6 +64,7 @@ export { IconPen, IconPlus, IconPosition, + IconRedo, IconRect, IconResize, IconRotation, @@ -73,6 +74,7 @@ export { IconSolidColor, IconStar, IconText, + IconUndo, IconUnlock, IconVerticalBottom, IconVerticalMiddle, diff --git a/packages/studio-base/src/modules/element-detail/detail-text.tsx b/packages/studio-base/src/modules/element-detail/detail-text.tsx index 27ae22b..ae03d36 100644 --- a/packages/studio-base/src/modules/element-detail/detail-text.tsx +++ b/packages/studio-base/src/modules/element-detail/detail-text.tsx @@ -2,7 +2,7 @@ import React, { useMemo, useEffect, useContext, useRef } from 'react'; import type { CSSProperties } from 'react'; import classnames from 'classnames'; import type { Element, ElementTextDetail } from 'idraw'; -import { getElementSize } from 'idraw'; +import { getElementSize, formatNumber, is } from 'idraw'; import { Radio, Row, Col, Form, InputNumber } from 'antd'; import type { FormInstance } from 'antd'; import { ConfigContext } from '../config-provider'; @@ -17,6 +17,13 @@ import IconAlignRight from '../../icons/align-right'; const modName = 'base-element-detail-text'; const iconStyle = { fontSize: 20 }; +const formatter = (val: any) => { + if (is.number(val * 1)) { + return formatNumber(val * 1); + } + return val; +}; + export interface DetailTextProps { className?: string; style?: CSSProperties; @@ -121,7 +128,7 @@ export const DetailText = (props: DetailTextProps) => { - + @@ -131,7 +138,16 @@ export const DetailText = (props: DetailTextProps) => { - + @@ -141,7 +157,7 @@ export const DetailText = (props: DetailTextProps) => { - + diff --git a/packages/studio-base/src/modules/toolbar/index.tsx b/packages/studio-base/src/modules/toolbar/index.tsx deleted file mode 100644 index b94fe59..0000000 --- a/packages/studio-base/src/modules/toolbar/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React, { useState } from 'react'; -import type { CSSProperties } from 'react'; -import classnames from 'classnames'; -import { Radio, Button } from 'antd'; -import { createPrefixName } from '../../css'; -import IconMouse from '../../icons/mouse'; -import IconPen from '../../icons/pen'; -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; -const modName = 'base-toolbar'; - -const prefixName = createPrefixName(modName); - -export interface ToolbarProps { - className?: string; - style?: CSSProperties; - openLeftSider: boolean; - openRightSider: boolean; - onClickToggleLayer?: () => void; - onClickToggleSetting?: () => void; -} - -export const Toolbar = (props: ToolbarProps) => { - const { className, style, openLeftSider, openRightSider, onClickToggleLayer, onClickToggleSetting } = props; - const [mode, setMode] = useState('select'); - const iconStyle = { fontSize: 20 }; - - return ( -
-
-
- setMode(e.target.value)}> - - - - - - - - - - - - - - - - -
-
-
- ); -}; diff --git a/packages/studio/README.md b/packages/studio/README.md index f86dc58..0158e64 100644 --- a/packages/studio/README.md +++ b/packages/studio/README.md @@ -13,6 +13,13 @@ idrawjs.com/studio/

+## Sponsors + +`@idraw/studio` is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider [sponsoring iDrawjs's development](https://opencollective.com/idrawjs). + +[![Become a Backer](https://opencollective.com/idrawjs/tiers/backers.svg?avatarHeight=48)](https://opencollective.com/idrawjs) + + ## @idraw/studio Preview @@ -25,7 +32,7 @@ The preview of `@idraw/studo`. idraw-studio-light-theme - idraw-studio-dark-theme + idraw-studio-dark-theme

diff --git a/packages/studio/package.json b/packages/studio/package.json index 93d4fec..8205f02 100644 --- a/packages/studio/package.json +++ b/packages/studio/package.json @@ -1,6 +1,6 @@ { "name": "@idraw/studio", - "version": "0.4.0-alpha.12", + "version": "0.4.0-alpha.13", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", @@ -20,13 +20,13 @@ "author": "chenshenhai", "license": "MIT", "dependencies": { - "@idraw/studio-base": "^0.4.0-alpha.12", + "@idraw/studio-base": "^0.4.0-alpha.13", "classnames": "^2.3.2", "is-hotkey": "^0.2.0" }, "peerDependencies": { "antd": "^5.12.1", - "idraw": "^0.4.0-beta.12", + "idraw": "^0.4.0-beta.13", "react": "^18.2.0", "react-color": "^2.19.3", "react-dom": "^18.2.0" diff --git a/packages/studio/src/css/modules/dashboard.less b/packages/studio/src/css/modules/dashboard.less index 7ca47af..07170d9 100644 --- a/packages/studio/src/css/modules/dashboard.less +++ b/packages/studio/src/css/modules/dashboard.less @@ -48,9 +48,10 @@ right: 0; } .@{mod-dashboard}-left { - border-right: 1px solid; + border-right: 2px solid; border-color: ~'var(--@{prefix}-border-color)'; height: 100%; + box-sizing: border-box; } .@{mod-dashboard}-right { diff --git a/packages/studio/src/css/modules/header.less b/packages/studio/src/css/modules/header.less index cb50c39..c636f83 100644 --- a/packages/studio/src/css/modules/header.less +++ b/packages/studio/src/css/modules/header.less @@ -16,6 +16,7 @@ flex-direction: row; } .@{mod-header}-center { + width: 100%; display: flex; flex-direction: row; } diff --git a/packages/studio/src/locale/languages/en-US.ts b/packages/studio/src/locale/languages/en-US.ts index c6b3f61..db2e203 100644 --- a/packages/studio/src/locale/languages/en-US.ts +++ b/packages/studio/src/locale/languages/en-US.ts @@ -18,6 +18,12 @@ const localeValues: Locale = { group: 'Group', devicePixelRatio: 'Device pixel ratio' }, + Toolbar: { + layers: 'Layers', + ruler: 'Ruler', + attributes: 'Attributes', + hand: 'Hand tool' + }, contextMenu: { copy: 'Copy', paste: 'Paste', diff --git a/packages/studio/src/locale/languages/zh-CN.ts b/packages/studio/src/locale/languages/zh-CN.ts index c85d9c0..0edf896 100644 --- a/packages/studio/src/locale/languages/zh-CN.ts +++ b/packages/studio/src/locale/languages/zh-CN.ts @@ -18,6 +18,12 @@ const localeValues: Locale = { group: '群组', devicePixelRatio: '设备像素比例' }, + Toolbar: { + layers: '图层', + ruler: '标尺', + attributes: '属性', + hand: '拖拽工具' + }, contextMenu: { copy: '复制', paste: '粘贴', diff --git a/packages/studio/src/modules/context-menu.ts b/packages/studio/src/modules/context-menu.ts index 05e9dc2..6aa1baf 100644 --- a/packages/studio/src/modules/context-menu.ts +++ b/packages/studio/src/modules/context-menu.ts @@ -1,8 +1,8 @@ import type { MenuProps } from 'antd'; import { useLocale } from '../locale'; -import type { SharedEvent, SharedStore } from '../types'; +import type { SharedEvent, SharedStore, HookUseContextMenuOptions } from '../types'; -export function useContextMenuOptions(opts: { sharedEvent: SharedEvent; sharedStore: SharedStore }): [MenuProps['items']] { +export const useContextMenuOptions: HookUseContextMenuOptions = (opts: { sharedEvent: SharedEvent; sharedStore: SharedStore }) => { const [moduleLocale] = useLocale('contextMenu'); const { sharedEvent } = opts; const items: MenuProps['items'] = [ @@ -52,7 +52,7 @@ export function useContextMenuOptions(opts: { sharedEvent: SharedEvent; sharedSt // } ]; return [items]; -} +}; // export function createContextMenuOptions() { // const items: MenuProps['items'] = [ diff --git a/packages/studio/src/modules/dashboard/index.tsx b/packages/studio/src/modules/dashboard/index.tsx index 42a9fa1..a4fe589 100644 --- a/packages/studio/src/modules/dashboard/index.tsx +++ b/packages/studio/src/modules/dashboard/index.tsx @@ -7,8 +7,7 @@ import { PanelDetail } from '../panel-detail'; import { Header } from '../header'; import { Sketch } from '../sketch'; // import SplitPane from '../split-pane'; -import { handleHotKey } from '../hot-key'; -import type { SharedEvent, SharedStore } from '../../types'; +import type { SharedEvent, SharedStore, HookUseContextMenuOptions } from '../../types'; const modName = 'mod-dashboard'; const leftSiderDefaultWidth = 240; @@ -24,13 +23,35 @@ export interface DashboardProps { height: number; logo?: React.ReactNode; navigationMenu?: React.ReactNode; + navigationCenter?: React.ReactNode; defaultSelectedElementUUIDs?: string[]; sharedStore: SharedStore; sharedEvent: SharedEvent; + useContextMenuOptions: HookUseContextMenuOptions; + handleKeyboard: ( + e: KeyboardEvent, + opts: { + sharedEvent: SharedEvent; + sharedStore: SharedStore; + } + ) => void; } export const Dashboard = forwardRef((props: DashboardProps, ref: React.ForwardedRef) => { - const { className, style, width, height, logo, navigationMenu, defaultSelectedElementUUIDs, sharedStore, sharedEvent } = props; + const { + className, + style, + width, + height, + logo, + navigationMenu, + navigationCenter, + defaultSelectedElementUUIDs, + sharedStore, + sharedEvent, + useContextMenuOptions, + handleKeyboard + } = props; const { createPrefixName } = useContext(ConfigContext); const prefixName = createPrefixName(modName); const [openLeftSider, setOpenLeftSider] = useState(true); @@ -41,7 +62,7 @@ export const Dashboard = forwardRef((props: Dash if (['INPUT', 'TEXTAREA'].includes((e.target as any).nodeName)) { return; } - handleHotKey(e, { sharedEvent, sharedStore }); + handleKeyboard(e, { sharedEvent, sharedStore }); }; window.addEventListener('keydown', hotKeyCallback); return () => { @@ -60,8 +81,8 @@ export const Dashboard = forwardRef((props: Dash }); useEffect(() => { - const newLeftWidth = leftSiderDefaultWidth; - const newRightWidth = rightSiderDefaultWidth; + const newLeftWidth = openLeftSider ? leftSiderDefaultWidth : 0; + const newRightWidth = openRightSider ? rightSiderDefaultWidth : 0; const newCenterWidth = width - newLeftWidth - newRightWidth; setLayout({ @@ -69,7 +90,7 @@ export const Dashboard = forwardRef((props: Dash rightWidth: newRightWidth, centerWidth: newCenterWidth }); - }, [height, width]); + }, [height, width, openLeftSider, openRightSider]); return useMemo(() => { const { leftWidth, rightWidth, centerWidth } = layout; @@ -82,6 +103,7 @@ export const Dashboard = forwardRef((props: Dash sharedStore={sharedStore} logo={logo} navigationMenu={navigationMenu} + navigationCenter={navigationCenter} openLeftSider={openLeftSider} openRightSider={openRightSider} onClickToggleLayer={() => { @@ -149,11 +171,19 @@ export const Dashboard = forwardRef((props: Dash defaultSelectedElementUUIDs={defaultSelectedElementUUIDs} sharedEvent={sharedEvent} sharedStore={sharedStore} + useContextMenuOptions={useContextMenuOptions} /> )}
- +
diff --git a/packages/studio/src/modules/header/index.tsx b/packages/studio/src/modules/header/index.tsx index 990e5ab..4d2b563 100644 --- a/packages/studio/src/modules/header/index.tsx +++ b/packages/studio/src/modules/header/index.tsx @@ -16,12 +16,25 @@ export interface ModProps extends ToolbarProps { style?: CSSProperties; logo?: React.ReactNode; navigationMenu?: React.ReactNode; + navigationCenter?: React.ReactNode; sharedStore: SharedStore; sharedEvent: SharedEvent; } export const Header = (props: ModProps) => { - const { logo, navigationMenu, className, style, openLeftSider, openRightSider, onClickToggleLayer, onClickToggleSetting, sharedStore, sharedEvent } = props; + const { + logo, + navigationMenu, + navigationCenter, + className, + style, + openLeftSider, + openRightSider, + onClickToggleLayer, + onClickToggleSetting, + sharedStore, + sharedEvent + } = props; const { state, dispatch } = useContext(Context); const { createPrefixName } = useContext(ConfigContext); const generateClassName = createPrefixName(modName); @@ -42,7 +55,6 @@ export const Header = (props: ModProps) => { {logo} {navigationMenu ? navigationMenu : } { />
+ {navigationCenter &&
{navigationCenter}
} +
{ onClick: () => { resetDevicePixelRatio(3); } - }, - { - key: 'device-pixel-ratio-x4', - label: 'x4', - onClick: () => { - resetDevicePixelRatio(4); - } } ] } diff --git a/packages/studio/src/modules/panel-layer/index.tsx b/packages/studio/src/modules/panel-layer/index.tsx index c4f68d3..625f811 100644 --- a/packages/studio/src/modules/panel-layer/index.tsx +++ b/packages/studio/src/modules/panel-layer/index.tsx @@ -6,8 +6,7 @@ import { updateElementInList, moveElementPosition, getGroupQueueFromList, findEl import type { ElementPosition } from 'idraw'; import { Dropdown, Button } from 'antd'; import { Context } from '../context'; -import type { SharedEvent, SharedStore } from '../../types'; -import { useContextMenuOptions } from '../context-menu'; +import type { SharedEvent, SharedStore, HookUseContextMenuOptions } from '../../types'; const modName = 'mod-panel-layer'; @@ -18,10 +17,11 @@ export interface PanelLayerProps { defaultSelectedElementUUIDs?: string[]; sharedStore: SharedStore; sharedEvent: SharedEvent; + useContextMenuOptions: HookUseContextMenuOptions; } export const PanelLayer = (props: PanelLayerProps) => { - const { className, style, height, defaultSelectedElementUUIDs = [], sharedStore, sharedEvent } = props; + const { className, style, height, defaultSelectedElementUUIDs = [], sharedStore, sharedEvent, useContextMenuOptions } = props; const { state, dispatch } = useContext(Context); const { createPrefixName } = useContext(ConfigContext); const generateClassName = createPrefixName(modName); @@ -159,7 +159,7 @@ export const PanelLayer = (props: PanelLayerProps) => { } }} onDrop={(e) => { - const elements = moveElementPosition(editingData.elements, { + const { elements } = moveElementPosition(editingData.elements, { from: e.from, to: e.to }); diff --git a/packages/studio/src/modules/sketch/index.tsx b/packages/studio/src/modules/sketch/index.tsx index f9edb15..553b44d 100644 --- a/packages/studio/src/modules/sketch/index.tsx +++ b/packages/studio/src/modules/sketch/index.tsx @@ -6,8 +6,8 @@ import { ConfigContext, getElementTree } from '@idraw/studio-base'; import type { CSSProperties } from 'react'; import { Dropdown } from 'antd'; import { Context } from '../context'; -import type { StudioState, SharedEvent, SharedEventMap, SharedStore } from '../../types'; -import { useContextMenuOptions } from '../context-menu'; +import type { StudioState, SharedEvent, SharedEventMap, SharedStore, HookUseContextMenuOptions } from '../../types'; + import { cloneEditingDataByPosition, updateEditingDataChildrenToData } from '../../util/data'; const modName = 'mod-sketch'; @@ -17,16 +17,16 @@ export interface SketchProps { style?: CSSProperties; width: number; height: number; - sharedStore: SharedStore; sharedEvent: SharedEvent; + useContextMenuOptions: HookUseContextMenuOptions; } export const Sketch = (props: SketchProps) => { const ref = useRef(null); const refIDraw = useRef(null); const refHasFirstDraw = useRef(false); - const { className, style, width, height, sharedEvent, sharedStore } = props; + const { className, style, width, height, sharedEvent, sharedStore, useContextMenuOptions } = props; const { generateClassName } = useContext(ConfigContext); const { state, dispatch } = useContext(Context); const { editingData } = state; @@ -81,9 +81,9 @@ export const Sketch = (props: SketchProps) => { const listenDataChange = (e: { data: Data; type: string }) => { const { data, type } = e; const editingData = refEditingData.current; - if (['add-element', 'update-element', 'delete-element', 'move-element', 'drag-element', 'resize-element'].includes(type)) { + if (['addElement', 'updateElement', 'deleteElement', 'moveElement', 'dragElement', 'resizeElement'].includes(type)) { const payload: Partial = { editingData: { ...data } }; - if (['add-element', 'delete-element', 'move-element'].includes(type)) { + if (['addElement', 'deleteElement', 'moveElement'].includes(type)) { payload.treeData = getElementTree(editingData); } dispatch({ @@ -267,6 +267,19 @@ export const Sketch = (props: SketchProps) => { idraw.trigger(middlewareEventSelectClear, {}); }; + const resetEditingDataCallback = (e: SharedEventMap['resetEditingData']) => { + const { editingData } = e; + const newTreeData = getElementTree(editingData); + dispatch({ + type: 'update', + payload: { + editingData: { ...editingData }, + treeData: newTreeData + } + }); + idraw.trigger(middlewareEventSelectClear, {}); + }; + idraw.on(middlewareEventSelect, listenMiddlewareEventSelect); idraw.on('change', listenDataChange); idraw.on(middlewareEventScale, listenMiddlewareEventScale); @@ -275,6 +288,7 @@ export const Sketch = (props: SketchProps) => { sharedEvent.on('deleteElement', deleteElementCallback); sharedEvent.on('resetEditingView', resetEditingViewCallback); sharedEvent.on('resetData', resetDataCallback); + sharedEvent.on('resetEditingData', resetEditingDataCallback); sharedEvent.on('dispatch', dispatch); if (!refHasFirstDraw.current) { diff --git a/packages/studio/src/modules/toolbar/index.tsx b/packages/studio/src/modules/toolbar/index.tsx index 2ce3779..2bb553a 100644 --- a/packages/studio/src/modules/toolbar/index.tsx +++ b/packages/studio/src/modules/toolbar/index.tsx @@ -1,14 +1,18 @@ import React, { useEffect, useState, useMemo, useContext } from 'react'; import type { CSSProperties } from 'react'; import classnames from 'classnames'; -import { Button, type ButtonProps } from 'antd'; +import { Button, type ButtonProps, Tooltip } from 'antd'; import { ConfigContext, IconLayer, IconSetting, IconRuler, IconDrag } from '@idraw/studio-base'; import type { SharedEvent, SharedStore } from '../../types'; +import { useLocale } from '../../locale'; -// const RadioButton = Radio.Button; -// const RadioGroup = Radio.Group; const modName = 'mod-toolbar'; +const useModuleLocale = () => { + const [moduleLocale] = useLocale('Toolbar'); + return moduleLocale; +}; + export interface ToolbarProps { className?: string; style?: CSSProperties; @@ -34,6 +38,7 @@ export const Toolbar = (props: ToolbarProps) => { // const modeSwitchClassName = generateClassName('mode-switch'); const [toggleDrag, setToggleDrag] = useState(false); const [toggleRuler, setToggleRuler] = useState(true); + const moduleLocale = useModuleLocale(); useEffect(() => { const idraw = sharedStore.get('idraw'); @@ -54,41 +59,36 @@ export const Toolbar = (props: ToolbarProps) => { return (
- {/* setMode(e.target.value)}> - - - - - - - - - - - - - */} +
-
); - }, [openLeftSider, openRightSider, onClickToggleLayer, onClickToggleSetting, toggleDrag, toggleRuler]); + }, [openLeftSider, openRightSider, onClickToggleLayer, onClickToggleSetting, toggleDrag, toggleRuler, moduleLocale]); }; diff --git a/packages/studio/src/studio.tsx b/packages/studio/src/studio.tsx index ea87e88..e8de7b2 100644 --- a/packages/studio/src/studio.tsx +++ b/packages/studio/src/studio.tsx @@ -6,9 +6,25 @@ import { Provider, createStudioContextStateByProps, createStudioReducer } from ' import type { StudioProps, SharedEventMap, SharedStorage, SharedEvent, SharedStore, StudioImperativeHandle, StudioActionType, StudioState } from './types'; import { createSharedDefaultStorage } from './shared/store'; import { initActionEvent } from './shared/event'; +import { useContextMenuOptions as useInnerContextMenuOptions } from './modules/context-menu'; + +import { handleKeyboard as handleInnerKeyboard } from './modules/hot-key'; export const Studio = React.forwardRef((props: StudioProps, ref: any) => { - const { width = 1000, height = 600, style, className, logo, navigationMenu, defaultSelectedElementUUIDs, prefiexName, onEditGroupElement } = props; + const { + width = 1000, + height = 600, + style, + className, + logo, + navigationMenu, + navigationCenter, + defaultSelectedElementUUIDs, + prefiexName, + onEditGroupElement, + useContextMenuOptions = useInnerContextMenuOptions, + handleKeyboard = handleInnerKeyboard + } = props; const [state, dispatch] = useReducer(createStudioReducer, createStudioContextStateByProps(props)); const refDashboard = useRef(null); @@ -106,6 +122,7 @@ export const Studio = React.forwardRef((props: StudioProps, ref: any) => { { defaultSelectedElementUUIDs={defaultSelectedElementUUIDs} sharedEvent={refSharedEvent.current as SharedEvent} sharedStore={refSharedStore.current as SharedStore} + useContextMenuOptions={useContextMenuOptions} + handleKeyboard={handleKeyboard} /> diff --git a/packages/studio/src/types/index.ts b/packages/studio/src/types/index.ts index e8a46b0..9178538 100644 --- a/packages/studio/src/types/index.ts +++ b/packages/studio/src/types/index.ts @@ -2,3 +2,4 @@ export * from './lib/context'; export * from './lib/studio'; export * from './lib/shared'; export * from './lib/locale'; +export * from './lib/hooks'; diff --git a/packages/studio/src/types/lib/hooks.ts b/packages/studio/src/types/lib/hooks.ts new file mode 100644 index 0000000..5b22199 --- /dev/null +++ b/packages/studio/src/types/lib/hooks.ts @@ -0,0 +1,4 @@ +import type { MenuProps } from 'antd'; +import type { SharedEvent, SharedStore } from './shared'; + +export type HookUseContextMenuOptions = (props: { sharedEvent: SharedEvent; sharedStore: SharedStore }) => [MenuProps['items']]; diff --git a/packages/studio/src/types/lib/locale.ts b/packages/studio/src/types/lib/locale.ts index 5dbb0b1..af472fc 100644 --- a/packages/studio/src/types/lib/locale.ts +++ b/packages/studio/src/types/lib/locale.ts @@ -20,6 +20,12 @@ export interface Locale { group: string; devicePixelRatio: string; }; + Toolbar: { + layers: string; + ruler: string; + attributes: string; + hand: string; + }; contextMenu: { copy: string; paste: string; diff --git a/packages/studio/src/types/lib/shared.ts b/packages/studio/src/types/lib/shared.ts index 2bb772b..d163872 100644 --- a/packages/studio/src/types/lib/shared.ts +++ b/packages/studio/src/types/lib/shared.ts @@ -40,6 +40,9 @@ export interface SharedEventMap { resetData: { data: Data; }; + resetEditingData: { + editingData: Data; + }; copy: void; paste: void; cut: void; diff --git a/packages/studio/src/types/lib/studio.ts b/packages/studio/src/types/lib/studio.ts index 8f8438b..e519294 100644 --- a/packages/studio/src/types/lib/studio.ts +++ b/packages/studio/src/types/lib/studio.ts @@ -5,21 +5,22 @@ import type { DashboardProps } from '../../modules'; import type { SharedEvent, SharedStore } from './shared'; import type { StudioActionType, StudioState } from './context'; -export type StudioProps = Omit & { - className?: string; - style?: CSSProperties; - data: Data; - prefiexName?: string; - defaultLocale?: LocaleCode; - defaultThemeMode?: 'light' | 'dark'; - defaultScaleInfo?: { - scale: number; - offsetX: number; - offsetY: number; +export type StudioProps = Omit & + Pick, 'useContextMenuOptions' | 'handleKeyboard'> & { + className?: string; + style?: CSSProperties; + data: Data; + prefiexName?: string; + defaultLocale?: LocaleCode; + defaultThemeMode?: 'light' | 'dark'; + defaultScaleInfo?: { + scale: number; + offsetX: number; + offsetY: number; + }; + defaultEditingGroupUUID?: string; + onEditGroupElement?: (e: { uuid?: string; position: ElementPosition }) => void; }; - defaultEditingGroupUUID?: string; - onEditGroupElement?: (e: { uuid?: string; position: ElementPosition }) => void; -}; export interface StudioImperativeHandle { getSharedStore: () => SharedStore | null; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f46c84..aad333b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: 5.12.1 version: 5.12.1(react-dom@18.2.0)(react@18.2.0) idraw: - specifier: 0.4.0-beta.12 - version: 0.4.0-beta.12 + specifier: 0.4.0-beta.13 + version: 0.4.0-beta.13 devDependencies: '@ant-design/icons': specifier: ^5.2.6 @@ -160,7 +160,7 @@ importers: packages/studio: dependencies: '@idraw/studio-base': - specifier: ^0.4.0-alpha.12 + specifier: ^0.4.0-alpha.13 version: link:../studio-base antd: specifier: ^5.12.1 @@ -169,8 +169,8 @@ importers: specifier: ^2.3.2 version: 2.3.2 idraw: - specifier: ^0.4.0-beta.12 - version: 0.4.0-beta.12 + specifier: ^0.4.0-beta.13 + version: 0.4.0-beta.13 is-hotkey: specifier: ^0.2.0 version: 0.2.0 @@ -206,8 +206,8 @@ importers: specifier: ^2.3.2 version: 2.3.2 idraw: - specifier: ^0.4.0-beta.12 - version: 0.4.0-beta.12 + specifier: ^0.4.0-beta.13 + version: 0.4.0-beta.13 react: specifier: ^18.2.0 version: 18.2.0 @@ -2022,42 +2022,42 @@ packages: react: 18.2.0 dev: false - /@idraw/board@0.4.0-beta.12(@idraw/renderer@0.4.0-beta.12)(@idraw/util@0.4.0-beta.12): - resolution: {integrity: sha512-q/k96R4fiAlJQWobKCDl3RYw1PPJzCASd7sVJjwrY9HrKoDcnaxeJo45/64/bH/F+OThr15/Er3iZ6YWvZx2uw==} + /@idraw/board@0.4.0-beta.13(@idraw/renderer@0.4.0-beta.13)(@idraw/util@0.4.0-beta.13): + resolution: {integrity: sha512-ZQmPmEps33WBrlsQgP4V4nRNCynHoIrh5LTxEwRfxsrs4pXf4rStyy7wb3HwrpXUI0BO1UbzxoO6COm2cwTDzA==} peerDependencies: - '@idraw/renderer': ^0.4.0-beta.12 - '@idraw/util': ^0.4.0-beta.12 + '@idraw/renderer': ^0.4.0-beta.13 + '@idraw/util': ^0.4.0-beta.13 dependencies: - '@idraw/renderer': 0.4.0-beta.12(@idraw/util@0.4.0-beta.12) - '@idraw/util': 0.4.0-beta.12 + '@idraw/renderer': 0.4.0-beta.13(@idraw/util@0.4.0-beta.13) + '@idraw/util': 0.4.0-beta.13 dev: false - /@idraw/core@0.4.0-beta.12(@idraw/board@0.4.0-beta.12)(@idraw/renderer@0.4.0-beta.12)(@idraw/util@0.4.0-beta.12): - resolution: {integrity: sha512-Kmga+FoUw4MimrMFh/hSrozY7Bj5++Dw0pDVBs+++WIDXpls4wWW/nN/o1CjH+U22N14ltWrCEWWMj0Gg40Obw==} + /@idraw/core@0.4.0-beta.13(@idraw/board@0.4.0-beta.13)(@idraw/renderer@0.4.0-beta.13)(@idraw/util@0.4.0-beta.13): + resolution: {integrity: sha512-6fXnXMt0cjKJdDjVTVU4y5rIC1d2BJxizXgo/aOvuU3oLrajF7WFVyNormSD9JI2292vDDEtixghjaI45AmgKQ==} peerDependencies: - '@idraw/board': ^0.4.0-beta.12 - '@idraw/renderer': ^0.4.0-beta.12 - '@idraw/util': ^0.4.0-beta.12 + '@idraw/board': ^0.4.0-beta.13 + '@idraw/renderer': ^0.4.0-beta.13 + '@idraw/util': ^0.4.0-beta.13 dependencies: - '@idraw/board': 0.4.0-beta.12(@idraw/renderer@0.4.0-beta.12)(@idraw/util@0.4.0-beta.12) - '@idraw/renderer': 0.4.0-beta.12(@idraw/util@0.4.0-beta.12) - '@idraw/util': 0.4.0-beta.12 + '@idraw/board': 0.4.0-beta.13(@idraw/renderer@0.4.0-beta.13)(@idraw/util@0.4.0-beta.13) + '@idraw/renderer': 0.4.0-beta.13(@idraw/util@0.4.0-beta.13) + '@idraw/util': 0.4.0-beta.13 dev: false - /@idraw/renderer@0.4.0-beta.12(@idraw/util@0.4.0-beta.12): - resolution: {integrity: sha512-7XTW/BD5vic9GG8YlnpDoxS/VmBY5LshS/N96g8HCnCrpF327n3ViW0cUrv10bFUbm7+DZpIRAxy65SuzZE33g==} + /@idraw/renderer@0.4.0-beta.13(@idraw/util@0.4.0-beta.13): + resolution: {integrity: sha512-zOpicvhgqluGOCUvpgRLHEEIZ2ZQvl7FX+4bhZ6uTZLNj0a4xk0g9UDS6rifzidvhQJEyycKHyx4BXDe5iUK5w==} peerDependencies: - '@idraw/util': ^0.4.0-beta.12 + '@idraw/util': ^0.4.0-beta.13 dependencies: - '@idraw/util': 0.4.0-beta.12 + '@idraw/util': 0.4.0-beta.13 dev: false - /@idraw/types@0.4.0-beta.12: - resolution: {integrity: sha512-MWeLtoEciOHABaY/C0Fg0b6rn/2VtWhlduVcxzSynn4V9arEwcQJ1h2vwqSzwjMkXlAGex6T/mRLItze5vKhSA==} + /@idraw/types@0.4.0-beta.13: + resolution: {integrity: sha512-8yDOktjH57GNrSzMPbpDEchEm5G9leXq5LaGxlJ7ayUS/jJGdqjwv1IIC5J758d+s5YOixX5KDdaUFIOfcqSOg==} dev: false - /@idraw/util@0.4.0-beta.12: - resolution: {integrity: sha512-gnU3qF4iP5XXIx5WX2/2+dCQvkUiqD/btD5f30HqQ9fXiK/9Go6Ep08yfy3oxPeJ+TS2faEgXGHOA6dkbtAG8w==} + /@idraw/util@0.4.0-beta.13: + resolution: {integrity: sha512-vmHYQUlknf2cFoRV/ULuJRKLhkKKCnDB3LnlWAFkkuCeIFGgKfjZsBYVReu/Ee9mIFAl4cBfQlLwTG5Tmb7B0w==} dev: false /@isaacs/cliui@8.0.2: @@ -4877,14 +4877,14 @@ packages: safer-buffer: 2.1.2 dev: true - /idraw@0.4.0-beta.12: - resolution: {integrity: sha512-ex8DqjoqBZ/jo73+IW3NeVU1Ua4YC8CJn4CGnnXZ3fWxpWCz+/+nzKVgJP7grmlyREAH3L+HxTnDpp4aejoAdA==} + /idraw@0.4.0-beta.13: + resolution: {integrity: sha512-6ncshoiDK4SunbNQlkDZJybRRZ3u81WYJ75KWXp27pmPPsVertJl5VYb/I1eIKVIc6lhzKkhNogde3F4GuHemw==} dependencies: - '@idraw/board': 0.4.0-beta.12(@idraw/renderer@0.4.0-beta.12)(@idraw/util@0.4.0-beta.12) - '@idraw/core': 0.4.0-beta.12(@idraw/board@0.4.0-beta.12)(@idraw/renderer@0.4.0-beta.12)(@idraw/util@0.4.0-beta.12) - '@idraw/renderer': 0.4.0-beta.12(@idraw/util@0.4.0-beta.12) - '@idraw/types': 0.4.0-beta.12 - '@idraw/util': 0.4.0-beta.12 + '@idraw/board': 0.4.0-beta.13(@idraw/renderer@0.4.0-beta.13)(@idraw/util@0.4.0-beta.13) + '@idraw/core': 0.4.0-beta.13(@idraw/board@0.4.0-beta.13)(@idraw/renderer@0.4.0-beta.13)(@idraw/util@0.4.0-beta.13) + '@idraw/renderer': 0.4.0-beta.13(@idraw/util@0.4.0-beta.13) + '@idraw/types': 0.4.0-beta.13 + '@idraw/util': 0.4.0-beta.13 dev: false /ieee754@1.2.1: