From d8289d4d8308e469767791d9d1702c93a0aca6df Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sat, 31 May 2025 13:57:09 +0800 Subject: [PATCH 1/2] feat: add features of undo and redo --- package.json | 2 +- .../core/src/{lib => cursor}/cursor-image.ts | 0 packages/core/src/{lib => cursor}/cursor.ts | 0 packages/core/src/index.ts | 20 ++++++------- .../dragger/index.ts | 0 .../info/config.ts | 0 .../info/draw-info.ts | 0 .../{middleware => middlewares}/info/index.ts | 0 .../{middleware => middlewares}/info/types.ts | 0 .../layout-selector/config.ts | 0 .../layout-selector/index.ts | 2 +- .../layout-selector/types.ts | 0 .../layout-selector/util.ts | 0 .../pointer/index.ts | 0 .../pointer/types.ts | 0 .../ruler/config.ts | 0 .../ruler/index.ts | 0 .../ruler/types.ts | 0 .../{middleware => middlewares}/ruler/util.ts | 0 .../scaler/index.ts | 0 .../scroller/config.ts | 0 .../scroller/index.ts | 0 .../scroller/types.ts | 0 .../scroller/util.ts | 0 .../selector/config.ts | 0 .../selector/draw-auxiliary.ts | 0 .../selector/draw-base.ts | 0 .../selector/draw-debug.ts | 0 .../selector/draw-reference.ts | 0 .../selector/draw-wrapper.ts | 0 .../selector/index.ts | 29 +++++++++++++++---- .../selector/pattern/icon-rotate.ts | 0 .../selector/pattern/index.ts | 0 .../selector/reference.ts | 0 .../selector/types.ts | 0 .../selector/util.ts | 0 .../text-editor/index.ts | 0 packages/idraw/src/idraw.ts | 18 ++++++------ packages/idraw/src/middlewares/use-history.ts | 8 ++--- packages/renderer/src/draw/text.ts | 7 ++++- packages/types/src/lib/config.ts | 11 +++++-- packages/types/src/lib/core.ts | 2 +- packages/types/src/lib/modify.ts | 28 +++++++++--------- packages/util/src/view/config.ts | 2 +- packages/util/src/view/resize-element.ts | 26 ++++++++--------- 45 files changed, 93 insertions(+), 62 deletions(-) rename packages/core/src/{lib => cursor}/cursor-image.ts (100%) rename packages/core/src/{lib => cursor}/cursor.ts (100%) rename packages/core/src/{middleware => middlewares}/dragger/index.ts (100%) rename packages/core/src/{middleware => middlewares}/info/config.ts (100%) rename packages/core/src/{middleware => middlewares}/info/draw-info.ts (100%) rename packages/core/src/{middleware => middlewares}/info/index.ts (100%) rename packages/core/src/{middleware => middlewares}/info/types.ts (100%) rename packages/core/src/{middleware => middlewares}/layout-selector/config.ts (100%) rename packages/core/src/{middleware => middlewares}/layout-selector/index.ts (99%) rename packages/core/src/{middleware => middlewares}/layout-selector/types.ts (100%) rename packages/core/src/{middleware => middlewares}/layout-selector/util.ts (100%) rename packages/core/src/{middleware => middlewares}/pointer/index.ts (100%) rename packages/core/src/{middleware => middlewares}/pointer/types.ts (100%) rename packages/core/src/{middleware => middlewares}/ruler/config.ts (100%) rename packages/core/src/{middleware => middlewares}/ruler/index.ts (100%) rename packages/core/src/{middleware => middlewares}/ruler/types.ts (100%) rename packages/core/src/{middleware => middlewares}/ruler/util.ts (100%) rename packages/core/src/{middleware => middlewares}/scaler/index.ts (100%) rename packages/core/src/{middleware => middlewares}/scroller/config.ts (100%) rename packages/core/src/{middleware => middlewares}/scroller/index.ts (100%) rename packages/core/src/{middleware => middlewares}/scroller/types.ts (100%) rename packages/core/src/{middleware => middlewares}/scroller/util.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/config.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/draw-auxiliary.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/draw-base.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/draw-debug.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/draw-reference.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/draw-wrapper.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/index.ts (97%) rename packages/core/src/{middleware => middlewares}/selector/pattern/icon-rotate.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/pattern/index.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/reference.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/types.ts (100%) rename packages/core/src/{middleware => middlewares}/selector/util.ts (100%) rename packages/core/src/{middleware => middlewares}/text-editor/index.ts (100%) diff --git a/package.json b/package.json index 7a56256..cbc1c04 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": false, - "version": "0.4.0-beta.45", + "version": "0.4.0-rc.0", "workspaces": [ "packages/*" ], diff --git a/packages/core/src/lib/cursor-image.ts b/packages/core/src/cursor/cursor-image.ts similarity index 100% rename from packages/core/src/lib/cursor-image.ts rename to packages/core/src/cursor/cursor-image.ts diff --git a/packages/core/src/lib/cursor.ts b/packages/core/src/cursor/cursor.ts similarity index 100% rename from packages/core/src/lib/cursor.ts rename to packages/core/src/cursor/cursor.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 8e99249..95c8c46 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -34,7 +34,7 @@ import { } from '@idraw/util'; import { Board, Sharer, Calculator } from './board'; import { createBoardContent, validateElements } from '@idraw/util'; -import { Cursor } from './lib/cursor'; +import { Cursor } from './cursor/cursor'; import { getModifyElementRecord } from './record'; export { coreEventKeys } from './config'; @@ -43,15 +43,15 @@ export type { CoreEventKeys } from './config'; export { Board, Sharer, Calculator }; // export { MiddlewareSelector } from './middleware/selector'; -export { MiddlewareSelector } from './middleware/selector'; -export { MiddlewareScroller } from './middleware/scroller'; -export { MiddlewareScaler } from './middleware/scaler'; -export { MiddlewareRuler } from './middleware/ruler'; -export { MiddlewareTextEditor } from './middleware/text-editor'; -export { MiddlewareDragger } from './middleware/dragger'; -export { MiddlewareInfo } from './middleware/info'; -export { MiddlewareLayoutSelector } from './middleware/layout-selector'; -export { MiddlewarePointer } from './middleware/pointer'; +export { MiddlewareSelector } from './middlewares/selector'; +export { MiddlewareScroller } from './middlewares/scroller'; +export { MiddlewareScaler } from './middlewares/scaler'; +export { MiddlewareRuler } from './middlewares/ruler'; +export { MiddlewareTextEditor } from './middlewares/text-editor'; +export { MiddlewareDragger } from './middlewares/dragger'; +export { MiddlewareInfo } from './middlewares/info'; +export { MiddlewareLayoutSelector } from './middlewares/layout-selector'; +export { MiddlewarePointer } from './middlewares/pointer'; export class Core { #board: Board; diff --git a/packages/core/src/middleware/dragger/index.ts b/packages/core/src/middlewares/dragger/index.ts similarity index 100% rename from packages/core/src/middleware/dragger/index.ts rename to packages/core/src/middlewares/dragger/index.ts diff --git a/packages/core/src/middleware/info/config.ts b/packages/core/src/middlewares/info/config.ts similarity index 100% rename from packages/core/src/middleware/info/config.ts rename to packages/core/src/middlewares/info/config.ts diff --git a/packages/core/src/middleware/info/draw-info.ts b/packages/core/src/middlewares/info/draw-info.ts similarity index 100% rename from packages/core/src/middleware/info/draw-info.ts rename to packages/core/src/middlewares/info/draw-info.ts diff --git a/packages/core/src/middleware/info/index.ts b/packages/core/src/middlewares/info/index.ts similarity index 100% rename from packages/core/src/middleware/info/index.ts rename to packages/core/src/middlewares/info/index.ts diff --git a/packages/core/src/middleware/info/types.ts b/packages/core/src/middlewares/info/types.ts similarity index 100% rename from packages/core/src/middleware/info/types.ts rename to packages/core/src/middlewares/info/types.ts diff --git a/packages/core/src/middleware/layout-selector/config.ts b/packages/core/src/middlewares/layout-selector/config.ts similarity index 100% rename from packages/core/src/middleware/layout-selector/config.ts rename to packages/core/src/middlewares/layout-selector/config.ts diff --git a/packages/core/src/middleware/layout-selector/index.ts b/packages/core/src/middlewares/layout-selector/index.ts similarity index 99% rename from packages/core/src/middleware/layout-selector/index.ts rename to packages/core/src/middlewares/layout-selector/index.ts index 2c20639..a845885 100644 --- a/packages/core/src/middleware/layout-selector/index.ts +++ b/packages/core/src/middlewares/layout-selector/index.ts @@ -391,7 +391,7 @@ export const MiddlewareLayoutSelector: Middleware< }; } eventHub.trigger(coreEventKeys.CHANGE, { - type: 'dragLayout', + type: 'resizeLayout', data, modifyRecord }); diff --git a/packages/core/src/middleware/layout-selector/types.ts b/packages/core/src/middlewares/layout-selector/types.ts similarity index 100% rename from packages/core/src/middleware/layout-selector/types.ts rename to packages/core/src/middlewares/layout-selector/types.ts diff --git a/packages/core/src/middleware/layout-selector/util.ts b/packages/core/src/middlewares/layout-selector/util.ts similarity index 100% rename from packages/core/src/middleware/layout-selector/util.ts rename to packages/core/src/middlewares/layout-selector/util.ts diff --git a/packages/core/src/middleware/pointer/index.ts b/packages/core/src/middlewares/pointer/index.ts similarity index 100% rename from packages/core/src/middleware/pointer/index.ts rename to packages/core/src/middlewares/pointer/index.ts diff --git a/packages/core/src/middleware/pointer/types.ts b/packages/core/src/middlewares/pointer/types.ts similarity index 100% rename from packages/core/src/middleware/pointer/types.ts rename to packages/core/src/middlewares/pointer/types.ts diff --git a/packages/core/src/middleware/ruler/config.ts b/packages/core/src/middlewares/ruler/config.ts similarity index 100% rename from packages/core/src/middleware/ruler/config.ts rename to packages/core/src/middlewares/ruler/config.ts diff --git a/packages/core/src/middleware/ruler/index.ts b/packages/core/src/middlewares/ruler/index.ts similarity index 100% rename from packages/core/src/middleware/ruler/index.ts rename to packages/core/src/middlewares/ruler/index.ts diff --git a/packages/core/src/middleware/ruler/types.ts b/packages/core/src/middlewares/ruler/types.ts similarity index 100% rename from packages/core/src/middleware/ruler/types.ts rename to packages/core/src/middlewares/ruler/types.ts diff --git a/packages/core/src/middleware/ruler/util.ts b/packages/core/src/middlewares/ruler/util.ts similarity index 100% rename from packages/core/src/middleware/ruler/util.ts rename to packages/core/src/middlewares/ruler/util.ts diff --git a/packages/core/src/middleware/scaler/index.ts b/packages/core/src/middlewares/scaler/index.ts similarity index 100% rename from packages/core/src/middleware/scaler/index.ts rename to packages/core/src/middlewares/scaler/index.ts diff --git a/packages/core/src/middleware/scroller/config.ts b/packages/core/src/middlewares/scroller/config.ts similarity index 100% rename from packages/core/src/middleware/scroller/config.ts rename to packages/core/src/middlewares/scroller/config.ts diff --git a/packages/core/src/middleware/scroller/index.ts b/packages/core/src/middlewares/scroller/index.ts similarity index 100% rename from packages/core/src/middleware/scroller/index.ts rename to packages/core/src/middlewares/scroller/index.ts diff --git a/packages/core/src/middleware/scroller/types.ts b/packages/core/src/middlewares/scroller/types.ts similarity index 100% rename from packages/core/src/middleware/scroller/types.ts rename to packages/core/src/middlewares/scroller/types.ts diff --git a/packages/core/src/middleware/scroller/util.ts b/packages/core/src/middlewares/scroller/util.ts similarity index 100% rename from packages/core/src/middleware/scroller/util.ts rename to packages/core/src/middlewares/scroller/util.ts diff --git a/packages/core/src/middleware/selector/config.ts b/packages/core/src/middlewares/selector/config.ts similarity index 100% rename from packages/core/src/middleware/selector/config.ts rename to packages/core/src/middlewares/selector/config.ts diff --git a/packages/core/src/middleware/selector/draw-auxiliary.ts b/packages/core/src/middlewares/selector/draw-auxiliary.ts similarity index 100% rename from packages/core/src/middleware/selector/draw-auxiliary.ts rename to packages/core/src/middlewares/selector/draw-auxiliary.ts diff --git a/packages/core/src/middleware/selector/draw-base.ts b/packages/core/src/middlewares/selector/draw-base.ts similarity index 100% rename from packages/core/src/middleware/selector/draw-base.ts rename to packages/core/src/middlewares/selector/draw-base.ts diff --git a/packages/core/src/middleware/selector/draw-debug.ts b/packages/core/src/middlewares/selector/draw-debug.ts similarity index 100% rename from packages/core/src/middleware/selector/draw-debug.ts rename to packages/core/src/middlewares/selector/draw-debug.ts diff --git a/packages/core/src/middleware/selector/draw-reference.ts b/packages/core/src/middlewares/selector/draw-reference.ts similarity index 100% rename from packages/core/src/middleware/selector/draw-reference.ts rename to packages/core/src/middlewares/selector/draw-reference.ts diff --git a/packages/core/src/middleware/selector/draw-wrapper.ts b/packages/core/src/middlewares/selector/draw-wrapper.ts similarity index 100% rename from packages/core/src/middleware/selector/draw-wrapper.ts rename to packages/core/src/middlewares/selector/draw-wrapper.ts diff --git a/packages/core/src/middleware/selector/index.ts b/packages/core/src/middlewares/selector/index.ts similarity index 97% rename from packages/core/src/middleware/selector/index.ts rename to packages/core/src/middlewares/selector/index.ts index a0229f5..f9f3166 100644 --- a/packages/core/src/middleware/selector/index.ts +++ b/packages/core/src/middlewares/selector/index.ts @@ -120,6 +120,9 @@ export const MiddlewareSelector: Middleware< devicePixelRatio: sharer.getActiveViewSizeInfo().devicePixelRatio }); + let startResizeGroupRecord: ModifyRecord<'resizeElements'> | null = null; + let endResizeGroupRecord: ModifyRecord<'resizeElements'> | null = null; + sharer.setSharedStorage(keyActionType, null); sharer.setSharedStorage(keyEnableSnapToGrid, true); @@ -213,6 +216,8 @@ export const MiddlewareSelector: Middleware< }; const clear = () => { + startResizeGroupRecord = null; + endResizeGroupRecord = null; sharer.setSharedStorage(keyActionType, null); sharer.setSharedStorage(keyResizeType, null); sharer.setSharedStorage(keyAreaStart, null); @@ -419,6 +424,8 @@ export const MiddlewareSelector: Middleware< prevPoint = e.point; moveOriginalStartPoint = e.point; + startResizeGroupRecord = null; + endResizeGroupRecord = null; sharer.setSharedStorage(keyActionType, null); sharer.setSharedStorage(keyResizeType, null); sharer.setSharedStorage(keyAreaStart, null); @@ -674,7 +681,7 @@ export const MiddlewareSelector: Middleware< const gridW = calculator.toGridNum(resizedElemSize.w, calcOpts); const gridH = calculator.toGridNum(resizedElemSize.h, calcOpts); if (elems[0].type === 'group') { - resizeEffectGroupElement( + endResizeGroupRecord = resizeEffectGroupElement( elems[0] as Element<'group'>, { x: gridX, @@ -684,6 +691,9 @@ export const MiddlewareSelector: Middleware< }, { resizeEffect: elems[0].operations?.resizeEffect } ); + if (!startResizeGroupRecord) { + startResizeGroupRecord = endResizeGroupRecord; + } elems[0].x = gridX; elems[0].y = gridY; } else { @@ -791,16 +801,16 @@ export const MiddlewareSelector: Middleware< } if (data && (['drag', 'drag-list', 'drag-list-end', 'resize'] as ActionType[]).includes(actionType)) { - let type: any = 'dragElement'; + let type: any = 'resizeElement'; if (type === 'resize') { type = 'resizeElement'; } if (hasChangedData) { const startSize = pointStartElementSizeList[0] as ElementSize & { uuid: string }; - let modifyRecord: ModifyRecord | undefined = undefined; + let modifyRecord: ModifyRecord | null | undefined = null; if (selectedElements.length === 1) { modifyRecord = { - type: 'dragElement', + type: 'resizeElement', time: 0, content: { method: 'modifyElement', @@ -809,9 +819,18 @@ export const MiddlewareSelector: Middleware< after: toFlattenElement(getElementSize(selectedElements[0])) } }; + if (selectedElements[0].type === 'group' && startResizeGroupRecord && endResizeGroupRecord) { + modifyRecord = { + ...endResizeGroupRecord, + content: { + ...endResizeGroupRecord.content, + before: startResizeGroupRecord.content.before + } + }; + } } else if (selectedElements.length > 1) { modifyRecord = { - type: 'dragElements', + type: 'resizeElements', time: 0, content: { method: 'modifyElements', diff --git a/packages/core/src/middleware/selector/pattern/icon-rotate.ts b/packages/core/src/middlewares/selector/pattern/icon-rotate.ts similarity index 100% rename from packages/core/src/middleware/selector/pattern/icon-rotate.ts rename to packages/core/src/middlewares/selector/pattern/icon-rotate.ts diff --git a/packages/core/src/middleware/selector/pattern/index.ts b/packages/core/src/middlewares/selector/pattern/index.ts similarity index 100% rename from packages/core/src/middleware/selector/pattern/index.ts rename to packages/core/src/middlewares/selector/pattern/index.ts diff --git a/packages/core/src/middleware/selector/reference.ts b/packages/core/src/middlewares/selector/reference.ts similarity index 100% rename from packages/core/src/middleware/selector/reference.ts rename to packages/core/src/middlewares/selector/reference.ts diff --git a/packages/core/src/middleware/selector/types.ts b/packages/core/src/middlewares/selector/types.ts similarity index 100% rename from packages/core/src/middleware/selector/types.ts rename to packages/core/src/middlewares/selector/types.ts diff --git a/packages/core/src/middleware/selector/util.ts b/packages/core/src/middlewares/selector/util.ts similarity index 100% rename from packages/core/src/middleware/selector/util.ts rename to packages/core/src/middlewares/selector/util.ts diff --git a/packages/core/src/middleware/text-editor/index.ts b/packages/core/src/middlewares/text-editor/index.ts similarity index 100% rename from packages/core/src/middleware/text-editor/index.ts rename to packages/core/src/middlewares/text-editor/index.ts diff --git a/packages/idraw/src/idraw.ts b/packages/idraw/src/idraw.ts index 831097f..7d779cc 100644 --- a/packages/idraw/src/idraw.ts +++ b/packages/idraw/src/idraw.ts @@ -238,15 +238,15 @@ export class iDraw { return this.#core.forceRender(); } - // getHistoryHandler() { - // return this.#historyHandler; - // } + getHistoryHandler() { + return this.#historyHandler; + } - // redo() { - // this.#historyHandler?.redo(); - // } + redo() { + this.#historyHandler?.redo(); + } - // undo() { - // this.#historyHandler?.undo(); - // } + undo() { + this.#historyHandler?.undo(); + } } diff --git a/packages/idraw/src/middlewares/use-history.ts b/packages/idraw/src/middlewares/use-history.ts index b0d6379..9e4d3ce 100644 --- a/packages/idraw/src/middlewares/use-history.ts +++ b/packages/idraw/src/middlewares/use-history.ts @@ -18,9 +18,9 @@ const supportRecordTypes = [ 'deleteElement', 'moveElement', 'addElement', - 'dragElement', 'resizeElement', - 'dragLayout', + 'resizeElements', + 'resizeLayout', 'modifyLayout', 'modifyGlobal' ]; @@ -90,7 +90,7 @@ export const useHistory = (opts: { core: Core; limit?: number }) => { undoRecord = core.modifyGlobal(info) as ModifyRecord; } else if (record.content.method === 'modifyElements') { undoRecord = core.modifyElements( - record.content.before.forEach((item) => unflatObject(item)) as unknown as Array< + record.content.before.map((item) => unflatObject(item)) as unknown as Array< RecursivePartial> & Pick > ) as ModifyRecord; @@ -153,7 +153,7 @@ export const useHistory = (opts: { core: Core; limit?: number }) => { redoRecord = core.modifyGlobal(info) as ModifyRecord; } else if (record.content.method === 'modifyElements') { redoRecord = core.modifyElements( - record.content.before.forEach((item) => unflatObject(item)) as unknown as Array< + record.content.before.map((item) => unflatObject(item)) as unknown as Array< RecursivePartial> & Pick > ) as ModifyRecord; diff --git a/packages/renderer/src/draw/text.ts b/packages/renderer/src/draw/text.ts index 66191e0..a220d98 100644 --- a/packages/renderer/src/draw/text.ts +++ b/packages/renderer/src/draw/text.ts @@ -1,7 +1,7 @@ import type { Element, RendererDrawElementOptions, ViewContext2D } from '@idraw/types'; import { rotateElement, calcViewElementSize, enhanceFontFamliy } from '@idraw/util'; import { is, isColorStr, getDefaultElementDetailConfig } from '@idraw/util'; -import { drawBox, drawBoxShadow } from './box'; +import { drawBox, drawBoxShadow, getOpacity } from './box'; const detailConfig = getDefaultElementDetailConfig(); @@ -35,6 +35,9 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render return; } + const { parentOpacity } = opts; + const opacity = getOpacity(elem) * parentOpacity; + ctx.globalAlpha = opacity; ctx.fillStyle = elem.detail.color || detailConfig.color; ctx.textBaseline = 'top'; ctx.$setFont({ @@ -64,6 +67,8 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render }); } } + + ctx.globalAlpha = parentOpacity; } }); } diff --git a/packages/types/src/lib/config.ts b/packages/types/src/lib/config.ts index fb02734..ed25ff4 100644 --- a/packages/types/src/lib/config.ts +++ b/packages/types/src/lib/config.ts @@ -1,5 +1,12 @@ import type { ElementBaseDetail, ElementTextDetail, ElementGroupDetail } from './element'; -export type DefaultElementDetailConfig = Required> & - Required> & +export type DefaultElementDetailConfig = Required< + Omit +> & + Required< + Pick< + ElementTextDetail, + 'color' | 'textAlign' | 'verticalAlign' | 'fontSize' | 'fontFamily' | 'fontWeight' | 'minInlineSize' | 'wordBreak' + > + > & Required>; diff --git a/packages/types/src/lib/core.ts b/packages/types/src/lib/core.ts index 9ca9cd4..62c3038 100644 --- a/packages/types/src/lib/core.ts +++ b/packages/types/src/lib/core.ts @@ -39,7 +39,7 @@ export interface CoreEventChange { type: T | 'setData' | 'other' | string; selectedElements?: Element[] | null; hoverElement?: Element | null; - modifyRecord?: ModifyRecord; + modifyRecord?: ModifyRecord | null; } export interface CoreEventScale { scale: number; diff --git a/packages/types/src/lib/modify.ts b/packages/types/src/lib/modify.ts index fb83a15..ccf0286 100644 --- a/packages/types/src/lib/modify.ts +++ b/packages/types/src/lib/modify.ts @@ -7,10 +7,10 @@ export type ModifyMethod = | 'deleteElement' | 'moveElement' | 'addElement' - | 'dragElement' - | 'dragElements' + | 'resizeElement' + | 'resizeElements' | 'modifyElements' - | 'dragLayout' + | 'resizeLayout' | 'modifyLayout' | 'modifyGlobal'; @@ -92,19 +92,19 @@ export interface ModifyContentMap { from: ElementPosition; to: ElementPosition; }; - dragElement: { + resizeElement: { method: 'modifyElement'; uuid: string; before: FlattenElement | null; after: FlattenElement | null; }; - dragElements: { + resizeElements: { method: 'modifyElements'; before: (FlattenLayout & { uuid: string })[]; after: (FlattenLayout & { uuid: string })[]; }; - dragLayout: { - method: 'modifyElement'; + resizeLayout: { + method: 'modifyLayout'; before: FlattenLayout; after: FlattenLayout; }; @@ -151,13 +151,13 @@ export interface ModifyRecordMap { time: number; content: ModifyContentMap['moveElement']; }; - dragElement: { - type: 'dragElement'; + resizeElement: { + type: 'resizeElement'; time: number; content: ModifyContentMap['modifyElement']; }; - dragElements: { - type: 'dragElements'; + resizeElements: { + type: 'resizeElements'; time: number; content: ModifyContentMap['modifyElements']; }; @@ -166,10 +166,10 @@ export interface ModifyRecordMap { time: number; content: ModifyContentMap['modifyElements']; }; - dragLayout: { - type: 'dragLayout'; + resizeLayout: { + type: 'resizeLayout'; time: number; - content: ModifyContentMap['dragLayout']; + content: ModifyContentMap['resizeLayout']; }; modifyLayout: { type: 'modifyLayout'; diff --git a/packages/util/src/view/config.ts b/packages/util/src/view/config.ts index 0da1f7a..4b0001f 100644 --- a/packages/util/src/view/config.ts +++ b/packages/util/src/view/config.ts @@ -61,7 +61,7 @@ export function getDefaultElementTextDetail(elementSize: ElementSize): ElementTe color: detailConfig.color, fontFamily: detailConfig.fontFamily, fontWeight: detailConfig.fontWeight, - lineHeight: elementSize.w / defaultText.length, + // lineHeight: elementSize.w / defaultText.length, fontSize: elementSize.w / defaultText.length, textAlign: 'center', verticalAlign: 'middle' diff --git a/packages/util/src/view/resize-element.ts b/packages/util/src/view/resize-element.ts index 53ac686..aaef555 100644 --- a/packages/util/src/view/resize-element.ts +++ b/packages/util/src/view/resize-element.ts @@ -105,6 +105,10 @@ function resizeElementBaseDetailByRatio(elem: Element, opts: DeepResizeRatioOpti function resizeElementBaseByRatio(elem: Element, opts: DeepResizeRatioOptions): ModifyRecord<'modifyElement'> { const { xRatio, yRatio } = opts; const { uuid, x, y, w, h } = elem; + elem.x = doNum(x * xRatio); + elem.y = doNum(y * yRatio); + elem.w = doNum(w * xRatio); + elem.h = doNum(h * yRatio); const record: ModifyRecord<'modifyElement'> = { type: 'modifyElement', time: Date.now(), @@ -112,13 +116,10 @@ function resizeElementBaseByRatio(elem: Element, opts: DeepResizeRatioOptions): method: 'modifyElement', uuid: uuid, before: { x, y, w, h }, - after: { x, y, w, h } + after: { x: elem.x, y: elem.y, w: elem.w, h: elem.h } } }; - elem.x = doNum(x * xRatio); - elem.y = doNum(y * yRatio); - elem.w = doNum(w * xRatio); - elem.h = doNum(h * yRatio); + const detailRecord = resizeElementBaseDetailByRatio(elem, opts); record.content.before = { ...record.content.before, @@ -170,7 +171,7 @@ function resizeTextElementDetailByRatio( function deepResizeElementByRatio( elem: Element, opts: DeepResizeRatioOptions, - record?: ModifyRecord<'modifyElements'> + record?: ModifyRecord<'resizeElements'> ) { const { type, uuid } = elem; @@ -210,7 +211,7 @@ function deepResizeElementByRatio( function fixedResizeGroupElementChildren( elem: Element<'group'>, opts: FixedResizeOptions, - record?: ModifyRecord<'modifyElements'> + record?: ModifyRecord<'resizeElements'> ) { if (!(elem.type === 'group' && Array.isArray(elem.detail.children))) { return; @@ -250,12 +251,9 @@ export function resizeEffectGroupElement( opts?: { resizeEffect?: ElementOperations['resizeEffect']; } -): ModifyRecord<'modifyElements'> | null { - if (!istype.number(size.x) && !istype.number(size.y)) { - return null; - } - const record: ModifyRecord<'modifyElements'> = { - type: 'modifyElements', +): ModifyRecord<'resizeElements'> | null { + const record: ModifyRecord<'resizeElements'> = { + type: 'resizeElements', time: Date.now(), content: { method: 'modifyElements', @@ -279,6 +277,8 @@ export function resizeEffectGroupElement( const afterGroupElem: FlattenLayout & { uuid: string } = { uuid, x: resizeX, y: resizeY, w: resizeW, h: resizeH }; if (opts?.resizeEffect === 'deepResize') { + record.content.before.push(beforeGroupElem); + record.content.after.push(afterGroupElem); const xRatio = resizeW / elem.w; const yRatio = resizeH / elem.h; if (xRatio === yRatio && xRatio === 1) { From 1e38d92575392297ff711c8bc12374f9e8ffc215 Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sat, 31 May 2025 14:03:15 +0800 Subject: [PATCH 2/2] test(util): add unit test of resizeEffectGroupElement --- .../util/__tests__/lib/resize-element.test.ts | 292 ++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 packages/util/__tests__/lib/resize-element.test.ts diff --git a/packages/util/__tests__/lib/resize-element.test.ts b/packages/util/__tests__/lib/resize-element.test.ts new file mode 100644 index 0000000..41ac045 --- /dev/null +++ b/packages/util/__tests__/lib/resize-element.test.ts @@ -0,0 +1,292 @@ +import { createElement, resizeEffectGroupElement } from '@idraw/util'; +import type { Element } from '@idraw/types'; + +const createGroupByRatio = (opts?: { xRatio?: number; yRatio?: number }) => { + const { xRatio = 1, yRatio = 1 } = opts || {}; + const minRatio = Math.min(xRatio, yRatio); + const maxRatio = Math.max(xRatio, yRatio); + const midRatio = (minRatio + maxRatio) / 2; + + const group: Element<'group'> = createElement('group', { + uuid: 'test-001', + x: 10, + y: 10, + w: 2000 * xRatio, + h: 2000 * yRatio, + detail: { + children: [ + createElement('rect', { uuid: 'test-002', x: 20 * xRatio, y: 20 * yRatio, w: 20 * xRatio, h: 20 * yRatio }), + createElement('circle', { uuid: 'test-003', x: 40 * xRatio, y: 40 * yRatio, w: 40 * xRatio, h: 40 * yRatio }), + createElement('text', { + uuid: 'test-004', + x: 60 * xRatio, + y: 60 * yRatio, + w: 60 * xRatio, + h: 60 * yRatio, + detail: { + fontSize: 16 * midRatio, + // lineHeight: 32 * midRatio, + text: 'Text in Group' + } + }), + createElement('image', { + uuid: 'test-005', + x: 80 * xRatio, + y: 80 * yRatio, + w: 80 * xRatio, + h: 80 * yRatio, + detail: { src: 'https://example.com/002.png' } + }), + createElement('group', { + uuid: 'test-100', + x: 500 * xRatio, + y: 500 * yRatio, + w: 1000 * xRatio, + h: 1000 * yRatio, + detail: { + children: [ + createElement('rect', { + uuid: 'test-101', + x: 20 * xRatio, + y: 20 * yRatio, + w: 20 * xRatio, + h: 20 * yRatio + }), + createElement('circle', { + uuid: 'test-102', + x: 40 * xRatio, + y: 40 * yRatio, + w: 40 * xRatio, + h: 40 * yRatio + }), + createElement('text', { + uuid: 'test-103', + x: 60 * xRatio, + y: 60 * yRatio, + w: 60 * xRatio, + h: 60 * yRatio, + detail: { + fontSize: 16 * midRatio, + text: 'Text in Group' + } + }), + createElement('image', { + uuid: 'test-104', + x: 80 * xRatio, + y: 80 * yRatio, + w: 80 * xRatio, + h: 80 * yRatio, + detail: { src: 'https://example.com/002.png' } + }) + ] + } + }) + ] + }, + operations: { + resizeEffect: 'deepResize' + } + }); + return group; +}; + +const createGroupByFixed = (opts: { moveX: number; moveY: number; moveW: number; moveH: number }) => { + const { moveX, moveY, moveW, moveH } = opts || {}; + + const group: Element<'group'> = createElement('group', { + uuid: 'test-001', + x: 10 + moveX, + y: 10 + moveY, + w: 2000 + moveW, + h: 2000 + moveH, + detail: { + children: [ + createElement('rect', { uuid: 'test-002', x: 20 - moveX, y: 20 - moveY, w: 20, h: 20 }), + createElement('circle', { uuid: 'test-003', x: 40 - moveX, y: 40 - moveY, w: 40, h: 40 }), + createElement('text', { + uuid: 'test-004', + x: 60 - moveX, + y: 60 - moveY, + w: 60, + h: 60, + detail: { + fontSize: 16, + text: 'Text in Group' + } + }), + createElement('image', { + uuid: 'test-005', + x: 80 - moveX, + y: 80 - moveY, + w: 80, + h: 80, + detail: { src: 'https://example.com/002.png' } + }), + createElement('group', { + uuid: 'test-100', + x: 500 - moveX, + y: 500 - moveY, + w: 1000, + h: 1000, + detail: { + children: [ + createElement('rect', { + uuid: 'test-101', + x: 20, + y: 20, + w: 20, + h: 20 + }), + createElement('circle', { + uuid: 'test-102', + x: 40, + y: 40, + w: 40, + h: 40 + }), + createElement('text', { + uuid: 'test-103', + x: 60, + y: 60, + w: 60, + h: 60, + detail: { + fontSize: 16, + text: 'Text in Group' + } + }), + createElement('image', { + uuid: 'test-104', + x: 80, + y: 80, + w: 80, + h: 80, + detail: { src: 'https://example.com/002.png' } + }) + ] + } + }) + ] + }, + operations: { + resizeEffect: 'deepResize' + } + }); + return group; +}; + +describe('resizeEffectGroupElement', () => { + beforeEach(() => { + jest.useFakeTimers().setSystemTime(new Date('2025-01-01')); + }); + + test('deepSize', () => { + const group = createGroupByRatio(); + const xRatio = 2; + const yRatio = 3; + + const record = resizeEffectGroupElement( + group, + { + w: group.w * xRatio, + h: group.h * yRatio + }, + { + resizeEffect: 'deepResize' + } + ); + + expect(group).toStrictEqual( + createGroupByRatio({ + xRatio, + yRatio + }) + ); + + expect(record).toStrictEqual({ + type: 'resizeElements', + time: 1735689600000, + content: { + method: 'modifyElements', + before: [ + { uuid: 'test-001', x: 10, y: 10, w: 2000, h: 2000 }, + { x: 20, y: 20, w: 20, h: 20, uuid: 'test-002' }, + { x: 40, y: 40, w: 40, h: 40, uuid: 'test-003' }, + { x: 60, y: 60, w: 60, h: 60, uuid: 'test-004', 'detail.fontSize': 16 }, + { x: 80, y: 80, w: 80, h: 80, uuid: 'test-005' }, + { x: 500, y: 500, w: 1000, h: 1000, uuid: 'test-100' }, + { x: 20, y: 20, w: 20, h: 20, uuid: 'test-101' }, + { x: 40, y: 40, w: 40, h: 40, uuid: 'test-102' }, + { x: 60, y: 60, w: 60, h: 60, uuid: 'test-103', 'detail.fontSize': 16 }, + { x: 80, y: 80, w: 80, h: 80, uuid: 'test-104' } + ], + after: [ + { uuid: 'test-001', x: 10, y: 10, w: 4000, h: 6000 }, + { x: 40, y: 60, w: 40, h: 60, uuid: 'test-002' }, + { x: 80, y: 120, w: 80, h: 120, uuid: 'test-003' }, + { x: 120, y: 180, w: 120, h: 180, uuid: 'test-004', 'detail.fontSize': 40 }, + { x: 160, y: 240, w: 160, h: 240, uuid: 'test-005' }, + { x: 1000, y: 1500, w: 2000, h: 3000, uuid: 'test-100' }, + { x: 40, y: 60, w: 40, h: 60, uuid: 'test-101' }, + { x: 80, y: 120, w: 80, h: 120, uuid: 'test-102' }, + { x: 120, y: 180, w: 120, h: 180, uuid: 'test-103', 'detail.fontSize': 40 }, + { x: 160, y: 240, w: 160, h: 240, uuid: 'test-104' } + ] + } + }); + }); + + test('fixed', () => { + const group = createGroupByRatio(); + const moveX = 99; + const moveY = 88; + const moveW = 77; + const moveH = 66; + + const record = resizeEffectGroupElement( + group, + { + x: group.x + moveX, + y: group.y + moveY, + w: group.w + moveW, + h: group.h + moveH + }, + { + resizeEffect: 'fixed' + } + ); + + expect(group).toStrictEqual( + createGroupByFixed({ + moveX, + moveY, + moveW, + moveH + }) + ); + + expect(record).toStrictEqual({ + type: 'resizeElements', + time: 1735689600000, + content: { + method: 'modifyElements', + before: [ + { uuid: 'test-001', x: 10, y: 10, w: 2000, h: 2000 }, + { uuid: 'test-002', x: 20, y: 20 }, + { uuid: 'test-003', x: 40, y: 40 }, + { uuid: 'test-004', x: 60, y: 60 }, + { uuid: 'test-005', x: 80, y: 80 }, + { uuid: 'test-100', x: 500, y: 500 } + ], + after: [ + { uuid: 'test-001', x: 109, y: 98, w: 2077, h: 2066 }, + { uuid: 'test-002', x: -79, y: -68 }, + { uuid: 'test-003', x: -59, y: -48 }, + { uuid: 'test-004', x: -39, y: -28 }, + { uuid: 'test-005', x: -19, y: -8 }, + { uuid: 'test-100', x: 401, y: 412 } + ] + } + }); + }); +});