mirror of
https://github.com/idrawjs/idraw
synced 2026-05-24 01:58:27 +00:00
feat: add utils of group
This commit is contained in:
parent
54dcbd6653
commit
4cf487df5b
6 changed files with 169 additions and 4 deletions
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ export {
|
|||
findElementsFromListByPositions,
|
||||
findElementQueueFromListByPosition,
|
||||
getElementPositionFromList,
|
||||
getElementPositionMapFromList,
|
||||
updateElementInList,
|
||||
getGroupQueueFromList,
|
||||
getElementSize,
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
97
packages/util/src/lib/group.ts
Normal file
97
packages/util/src/lib/group.ts
Normal 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;
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue