diff --git a/.changeset/hip-apricots-crash.md b/.changeset/hip-apricots-crash.md new file mode 100644 index 000000000..75f8a3571 --- /dev/null +++ b/.changeset/hip-apricots-crash.md @@ -0,0 +1,6 @@ +--- +'@plait/common': patch +'@plait/core': patch +--- + +ensuring consistent hierarchy of all selected elements when add group diff --git a/packages/common/src/transforms/z-index.ts b/packages/common/src/transforms/z-index.ts index 308a70c43..5097ea171 100644 --- a/packages/common/src/transforms/z-index.ts +++ b/packages/common/src/transforms/z-index.ts @@ -1,5 +1,5 @@ -import { PlaitBoard } from '@plait/core'; -import { getOneMoveOptions, moveElementsToNewPath, getAllMoveOptions } from '../utils'; +import { moveElementsToNewPath, PlaitBoard } from '@plait/core'; +import { getOneMoveOptions, getAllMoveOptions } from '../utils'; const moveToTop = (board: PlaitBoard) => { const moveOptions = getAllMoveOptions(board, 'up'); diff --git a/packages/common/src/utils/z-index.ts b/packages/common/src/utils/z-index.ts index 6514d6ceb..31bbd75fd 100644 --- a/packages/common/src/utils/z-index.ts +++ b/packages/common/src/utils/z-index.ts @@ -9,38 +9,19 @@ import { getGroupByElement, PlaitGroup, getElementsInGroup, - Transforms, - Path, - findIndex + findIndex, + getElementsIndices, + MoveNodeOption, + sortElements, } from '@plait/core'; -export interface ZIndexMoveOption { - element: PlaitElement; - newPath: Path; -} - -export const moveElementsToNewPath = (board: PlaitBoard, zIndexMoveOption: ZIndexMoveOption[]) => { - zIndexMoveOption - .map(item => { - const path = PlaitBoard.findPath(board, item.element); - const ref = board.pathRef(path); - return () => { - ref.current && Transforms.moveNode(board, ref.current, item.newPath); - ref.unref(); - }; - }) - .forEach(action => { - action(); - }); -}; - -export const getOneMoveOptions = (board: PlaitBoard, direction: 'down' | 'up'): ZIndexMoveOption[] => { +export const getOneMoveOptions = (board: PlaitBoard, direction: 'down' | 'up'): MoveNodeOption[] => { const indicesToMove = getIndicesToMove(board); let groupedIndices = toContiguousGroups(board, indicesToMove); if (direction === 'up') { groupedIndices = groupedIndices.reverse(); } - let moveContents: ZIndexMoveOption[] = []; + let moveContents: MoveNodeOption[] = []; groupedIndices.forEach((indices, i) => { const leadingIndex = indices[0]; const trailingIndex = indices[indices.length - 1]; @@ -65,10 +46,10 @@ export const getOneMoveOptions = (board: PlaitBoard, direction: 'down' | 'up'): return moveContents; }; -export const getAllMoveOptions = (board: PlaitBoard, direction: 'down' | 'up'): ZIndexMoveOption[] => { +export const getAllMoveOptions = (board: PlaitBoard, direction: 'down' | 'up'): MoveNodeOption[] => { const indicesToMove = getIndicesToMove(board); let groupedIndices = toContiguousGroups(board, indicesToMove); - let moveContents: ZIndexMoveOption[] = []; + let moveContents: MoveNodeOption[] = []; if (direction === 'up') { groupedIndices = groupedIndices.reverse(); } @@ -80,7 +61,7 @@ export const getAllMoveOptions = (board: PlaitBoard, direction: 'down' | 'up'): const editingGroup = getEditingGroup(board, sourceElement); let targetIndex = direction === 'down' ? 0 : board.children.length - 1; if (editingGroup) { - const elementsInGroup = ascendingSortElements(board, getElementsInGroup(board, editingGroup, true, true)); + const elementsInGroup = sortElements(board, getElementsInGroup(board, editingGroup, true, true)); targetIndex = direction === 'down' ? board.children.indexOf(elementsInGroup[0]) @@ -190,20 +171,6 @@ const getTargetIndex = (board: PlaitBoard, boundaryIndex: number, direction: 'do }; const getIndicesToMove = (board: PlaitBoard) => { - const selectedElements = [...getSelectedElements(board), ...getSelectedGroups(board)].filter(item => board.canSetZIndex(item)); - return selectedElements - .map(item => { - return board.children.indexOf(item); - }) - .sort((a, b) => { - return a - b; - }); -}; - -const ascendingSortElements = (board: PlaitBoard, elements: PlaitElement[] = []) => { - return elements.sort((a, b) => { - const indexA = board.children.findIndex(child => child.id === a.id); - const indexB = board.children.findIndex(child => child.id === b.id); - return indexA - indexB; - }); + const selectedElements = [...getSelectedElements(board), ...getSelectedGroups(board)]; + return getElementsIndices(board, selectedElements); }; diff --git a/packages/core/src/transforms/group.ts b/packages/core/src/transforms/group.ts index 2bb6038a7..7f603f013 100644 --- a/packages/core/src/transforms/group.ts +++ b/packages/core/src/transforms/group.ts @@ -6,7 +6,12 @@ import { canAddGroup, hasSelectedElementsInSameGroup, canRemoveGroup, - findElements + findElements, + getElementsIndices, + isIndicesContinuous, + getSelectedElements, + getHighestIndexOfElement, + moveElementsToNewPathAfterAddGroup } from '../utils'; import { NodeTransforms } from './node'; @@ -20,6 +25,14 @@ export const addGroup = (board: PlaitBoard, elements?: PlaitElement[]) => { const path = PlaitBoard.findPath(board, item); NodeTransforms.setNode(board, { groupId: group.id }, path); }); + const selectedElements = getSelectedElements(board); + const highestIndexOfSelectedElement = getHighestIndexOfElement(board, [...selectedElements, ...selectedGroups]); + const indices = getElementsIndices(board, highestSelectedElements); + const isContinuous = isIndicesContinuous(indices); + if (!isContinuous) { + moveElementsToNewPathAfterAddGroup(board, [...selectedElements, ...selectedGroups], [highestIndexOfSelectedElement - 1]); + } + const groupPath = [highestIndexOfSelectedElement + 1]; if (hasSelectedElementsInSameGroup(highestSelectedElements)) { const newGroupId = selectedIsolatedElements[0].groupId; NodeTransforms.insertNode( @@ -28,10 +41,10 @@ export const addGroup = (board: PlaitBoard, elements?: PlaitElement[]) => { ...group, groupId: newGroupId }, - [board.children.length] + groupPath ); } else { - NodeTransforms.insertNode(board, group, [board.children.length]); + NodeTransforms.insertNode(board, group, groupPath); } } }; diff --git a/packages/core/src/utils/common.ts b/packages/core/src/utils/common.ts index 41eb16d4c..2bc9e42b6 100644 --- a/packages/core/src/utils/common.ts +++ b/packages/core/src/utils/common.ts @@ -1,8 +1,16 @@ +import { Path, PlaitElement } from '../interfaces'; import { PlaitBoard } from '../interfaces/board'; import { Subscription, timer } from 'rxjs'; +import { NodeTransforms } from '../transforms/node'; +import { sortElements } from './position'; const BOARD_TO_RAF = new WeakMap(); +export interface MoveNodeOption { + element: PlaitElement; + newPath: Path; +} + const getTimerId = (board: PlaitBoard, key: string) => { const state = getRAFState(board); return state[key] || null; @@ -49,3 +57,30 @@ export const debounce = (func: () => void, wait: number, options?: { leading: bo } }; }; + +export const getElementsIndices = (board: PlaitBoard, elements: PlaitElement[]): number[] => { + sortElements(board, elements); + return elements.map(item => { + return board.children.map(item => item.id).indexOf(item.id); + }); +}; + +export const getHighestIndexOfElement = (board: PlaitBoard, elements: PlaitElement[]) => { + const indices = getElementsIndices(board, elements); + return indices[indices.length-1]; +}; + +export const moveElementsToNewPath = (board: PlaitBoard, moveOptions: MoveNodeOption[]) => { + moveOptions + .map(item => { + const path = PlaitBoard.findPath(board, item.element); + const ref = board.pathRef(path); + return () => { + ref.current && NodeTransforms.moveNode(board, ref.current, item.newPath); + ref.unref(); + }; + }) + .forEach(action => { + action(); + }); +}; diff --git a/packages/core/src/utils/group.ts b/packages/core/src/utils/group.ts index 902ff8243..201222c7c 100644 --- a/packages/core/src/utils/group.ts +++ b/packages/core/src/utils/group.ts @@ -1,5 +1,5 @@ import { ACTIVE_STROKE_WIDTH } from '../constants'; -import { PlaitBoard, PlaitElement, PlaitGroup, PlaitGroupElement, RectangleClient, SELECTION_BORDER_COLOR } from '../interfaces'; +import { Path, PlaitBoard, PlaitElement, PlaitGroup, PlaitGroupElement, RectangleClient, SELECTION_BORDER_COLOR } from '../interfaces'; import { getSelectionAngle } from './angle'; import { createG, setAngleForG } from './dom'; import { drawRectangle } from './drawing/rectangle'; @@ -8,6 +8,8 @@ import { idCreator } from './id-creator'; import { getSelectedElements } from './selected-element'; import { isSelectionMoving } from './selection'; import { depthFirstRecursion } from './tree'; +import { moveElementsToNewPath } from './common'; +import { sortElements } from './position'; export const getElementsInGroup = (board: PlaitBoard, group: PlaitGroup, recursion?: boolean, includeGroup?: boolean) => { let result: PlaitElement[] = []; @@ -234,7 +236,6 @@ export const canRemoveGroup = (board: PlaitBoard, elements?: PlaitElement[]) => return selectedElements.length > 0 && selectedGroups.length > 0; }; - export const getEditingGroup = (board: PlaitBoard, element: PlaitElement) => { const groups = getGroupByElement(board, element, true) as PlaitGroup[]; let editingGroup = null; @@ -247,4 +248,18 @@ export const getEditingGroup = (board: PlaitBoard, element: PlaitElement) => { } } return editingGroup; -}; \ No newline at end of file +}; + +export const moveElementsToNewPathAfterAddGroup = (board: PlaitBoard, moveElements: PlaitElement[], newPath: Path) => { + sortElements(board, moveElements); + moveElements.pop(); + moveElementsToNewPath( + board, + moveElements.map(element => { + return { + element, + newPath + }; + }) + ); +}; diff --git a/packages/core/src/utils/helper.ts b/packages/core/src/utils/helper.ts index cebf1ff71..3836dcece 100644 --- a/packages/core/src/utils/helper.ts +++ b/packages/core/src/utils/helper.ts @@ -74,3 +74,13 @@ export const findIndex = ( } return -1; }; + +export const isIndicesContinuous = (indexes: number[]): boolean => { + indexes.sort((a, b) => a - b); + for (let i = 1; i < indexes.length; i++) { + if (indexes[i] !== indexes[i - 1] + 1) { + return false; + } + } + return true; +}; diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index a0e4107e1..9a70ec53e 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -29,4 +29,5 @@ export * from './group'; export * from './selection'; export * from './angle'; export * from './fragment'; -export * from './snap/snap'; \ No newline at end of file +export * from './snap/snap'; +export * from './position'; diff --git a/packages/core/src/utils/position.ts b/packages/core/src/utils/position.ts index 97b3c5bab..8b74aa2ca 100644 --- a/packages/core/src/utils/position.ts +++ b/packages/core/src/utils/position.ts @@ -1,9 +1,9 @@ import { PlaitBoard, PlaitElement } from '../interfaces'; -export const sortElements = (board: PlaitBoard, elements: PlaitElement[]) => { +export const sortElements = (board: PlaitBoard, elements: PlaitElement[], ascendingOrder = true) => { return [...elements].sort((a: PlaitElement, b: PlaitElement) => { const pathA = PlaitBoard.findPath(board, a); const pathB = PlaitBoard.findPath(board, b); - return pathA[0] - pathB[0]; + return ascendingOrder ? pathA[0] - pathB[0] : pathB[0] - pathA[0]; }); };