feat: add utils of group

This commit is contained in:
chenshenhai 2024-07-27 21:38:50 +08:00
parent 54dcbd6653
commit 4cf487df5b
6 changed files with 169 additions and 4 deletions

View file

@ -10,6 +10,7 @@ import {
findElementsFromList,
findElementsFromListByPositions,
getElementPositionFromList,
getElementPositionMapFromList,
deepResizeGroupElement,
getElementSize
} from '@idraw/util';
@ -99,6 +100,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
let moveOriginalStartPoint: Point | null = null;
let moveOriginalStartElementSize: ElementSize | null = null;
let inBusyMode: 'resize' | 'drag' | 'drag-list' | 'area' | null = null;
let hasChangedData: boolean | null = null;
sharer.setSharedStorage(keyActionType, null);
sharer.setSharedStorage(keyEnableSnapToGrid, true);
@ -154,7 +156,10 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
}
if (opts?.triggerEvent === true) {
eventHub.trigger(coreEventKeys.SELECT, { uuids: list.map((elem) => elem.uuid), positions: [] });
const uuids = list.map((elem) => elem.uuid);
const data = sharer.getActiveStorage('data');
const positionMap = getElementPositionMapFromList(uuids, data?.elements || []);
eventHub.trigger(coreEventKeys.SELECT, { uuids, positions: list.map((elem) => [...positionMap[elem.uuid]]) });
}
};
@ -471,6 +476,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
const enableSnapToGrid = sharer.getSharedStorage(keyEnableSnapToGrid);
if (actionType === 'drag') {
hasChangedData = true;
inBusyMode = 'drag';
if (data && elems?.length === 1 && moveOriginalStartElementSize && originalStart && end && elems[0]?.operations?.locked !== true) {
const { moveX, moveY } = calcMoveInGroup(originalStart, end, groupQueue);
@ -518,6 +524,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
}
viewer.drawFrame();
} else if (actionType === 'drag-list') {
hasChangedData = true;
inBusyMode = 'drag-list';
if (data && originalStart && start && end && elems?.length > 1) {
const moveX = (end.x - start.x) / scale;
@ -546,6 +553,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
viewer.drawFrame();
} else if (actionType === 'resize') {
if (data && elems?.length === 1 && originalStart && moveOriginalStartElementSize && resizeType?.startsWith('resize-')) {
hasChangedData = true;
inBusyMode = 'resize';
const pointGroupQueue: Element<'group'>[] = [];
groupQueue.forEach((group) => {
@ -700,7 +708,10 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (type === 'resize') {
type = 'resizeElement';
}
eventHub.trigger(coreEventKeys.CHANGE, { data, type, selectedElements, hoverElement });
if (hasChangedData) {
eventHub.trigger(coreEventKeys.CHANGE, { data, type, selectedElements, hoverElement });
hasChangedData = false;
}
}
viewer.drawFrame();
};

View file

@ -66,6 +66,7 @@ export {
findElementsFromListByPositions,
findElementQueueFromListByPosition,
getElementPositionFromList,
getElementPositionMapFromList,
updateElementInList,
getGroupQueueFromList,
getElementSize,

View file

@ -43,6 +43,7 @@ export {
filterElementAsset,
isResourceElement,
getElementPositionFromList,
getElementPositionMapFromList,
calcElementListSize
} from './lib/element';
export { checkRectIntersect } from './lib/rect';
@ -89,3 +90,4 @@ export { modifyElement, getModifiedElement } from './lib/modify';
// export { ModifyRecorder } from './lib/modify-recorder';
export { enhanceFontFamliy } from './lib/text';
export { flatElementList } from './lib/flat';
export { groupElementsByPosition, ungroupElementsByPosition } from './lib/group';

View file

@ -465,3 +465,42 @@ export function getElementPositionFromList(uuid: string, elements: Element<Eleme
_loop(elements);
return result;
}
export function getElementPositionMapFromList(
uuids: string[],
elements: Element<ElementType>[]
): {
[uuid: string]: ElementPosition;
} {
const currentPosition: ElementPosition = [];
const positionMap: {
[uuid: string]: ElementPosition;
} = {};
let over = false;
const _loop = (list: Element<ElementType>[]) => {
for (let i = 0; i < list.length; i++) {
if (over === true) {
break;
}
currentPosition.push(i);
const elem = list[i];
if (uuids.includes(elem.uuid)) {
positionMap[elem.uuid] = [...currentPosition];
if (Object.keys(positionMap).length === uuids.length) {
over = true;
break;
}
} else if (elem.type === 'group') {
_loop((elem as Element<'group'>)?.detail?.children || []);
}
if (over) {
break;
}
currentPosition.pop();
}
};
_loop(elements);
return positionMap;
}

View file

@ -0,0 +1,97 @@
import type { Elements, Element, ElementPosition } from '@idraw/types';
import { findElementFromListByPosition, calcElementListSize } from './element';
import { deleteElementInListByPosition, insertElementToListByPosition } from './handle-element';
import { createUUID } from './uuid';
export function groupElementsByPosition(list: Elements, positions: ElementPosition[]): Elements {
if (positions.length > 1) {
let isValidPositions: boolean = true;
let lastIndexs: number[] = [];
for (let i = 1; i < positions.length; i++) {
const prevPosition = positions[i - 1];
const position = positions[i];
if (!(prevPosition.length > 0 && position.length > 0)) {
isValidPositions = false;
break;
}
if (prevPosition.length !== position.length) {
isValidPositions = false;
break;
}
const temp1 = [...prevPosition];
const temp2 = [...position];
const lastIndex1 = temp1.pop();
const lastIndex2 = temp2.pop();
if (i === 1 && typeof lastIndex1 === 'number' && lastIndex1 >= 0) {
lastIndexs.push(lastIndex1 as number);
}
if (typeof lastIndex2 === 'number' && lastIndex2 >= 0) {
lastIndexs.push(lastIndex2 as number);
}
}
if (isValidPositions !== true) {
console.error('[idraw]: The grouped elements are not siblings!');
return list;
}
lastIndexs.sort((a, b) => a - b);
const groupParentPosition = [...positions[0]].splice(0, positions[0].length - 1);
const groupChildren: Elements = [];
const groupPosition = [...groupParentPosition, lastIndexs[0]];
for (let i = 0; i < lastIndexs.length; i++) {
const position = [...groupParentPosition, lastIndexs[i]];
const elem = findElementFromListByPosition(position, list);
if (elem) {
groupChildren.push(elem);
}
}
const groupSize = calcElementListSize(groupChildren);
for (let i = 0; i < groupChildren.length; i++) {
const elem = groupChildren[i];
if (elem) {
elem.x -= groupSize.x;
elem.y -= groupSize.y;
}
}
for (let i = lastIndexs.length - 1; i >= 0; i--) {
const position = [...groupParentPosition, lastIndexs[i]];
deleteElementInListByPosition(position, list);
}
const group: Element<'group'> = {
name: 'Group',
uuid: createUUID(),
type: 'group',
...groupSize,
detail: {
children: groupChildren
}
};
insertElementToListByPosition(group, groupPosition, list);
}
return list;
}
export function ungroupElementsByPosition(list: Elements, position: ElementPosition): Elements {
const elem = findElementFromListByPosition(position, list) as Element<'group'>;
if (!(elem && elem?.type === 'group' && Array.isArray(elem?.detail?.children))) {
console.error('[idraw]: The ungrouped element is not a group element!');
}
const groupParentPosition = [...position].splice(0, position.length - 1);
const groupLastIndex = position[position.length - 1];
const { x, y } = elem;
deleteElementInListByPosition(position, list);
elem.detail.children.forEach((child, i) => {
child.x += x;
child.y += y;
const elemPosition = [...groupParentPosition, groupLastIndex + i];
insertElementToListByPosition(child, elemPosition, list);
});
return list;
}

View file

@ -1,12 +1,18 @@
import type { RecursivePartial } from '@idraw/types';
import { deepClone } from './data';
export class Store<T extends Record<string | symbol, any> = Record<string | symbol, any>> {
export class Store<
T extends Record<string | symbol, any> = Record<string | symbol, any>,
S extends Record<string | symbol, any> = Record<string | symbol, any>
> {
#temp: T;
#backUpDefaultStorage: T;
#static: RecursivePartial<S>;
constructor(opts: { defaultStorage: T }) {
constructor(opts: { defaultStorage: T; defaultStatic?: S }) {
this.#backUpDefaultStorage = deepClone(opts.defaultStorage);
this.#temp = this.#createTempStorage();
this.#static = opts.defaultStatic || {};
}
set<K extends keyof T>(name: K, value: T[K]) {
@ -17,6 +23,14 @@ export class Store<T extends Record<string | symbol, any> = Record<string | symb
return this.#temp[name];
}
setStatic<K extends keyof S>(name: K, value: S[K]) {
this.#static[name] = value;
}
getStatic<K extends keyof S>(name: K): S[K] | undefined {
return this.#static[name] as S[K] | undefined;
}
getSnapshot(opts?: { deepClone?: boolean }): T {
if (opts?.deepClone === true) {
return deepClone(this.#temp);
@ -30,6 +44,7 @@ export class Store<T extends Record<string | symbol, any> = Record<string | symb
destroy() {
this.#temp = null as any;
this.#static = null as any;
}
#createTempStorage() {