Skip to content

Commit

Permalink
feat(common): support transform z index (#817)
Browse files Browse the repository at this point in the history
  • Loading branch information
huanhuanwa authored Apr 12, 2024
1 parent 4a39aa4 commit d30361f
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .changeset/four-dodos-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@plait/common': minor
'@plait/core': minor
---

support transform z index
1 change: 1 addition & 0 deletions packages/common/src/transforms/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './property';
export * from './align';
export * from './text';
export * from './z-index';
18 changes: 18 additions & 0 deletions packages/common/src/transforms/z-index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { PlaitBoard } from '@plait/core';
import { getOneMoveOptions, moveElementsToNewPath } from '../utils';

const moveToTop = () => {};

const moveToBottom = () => {};

const moveUp = (board: PlaitBoard) => {
const moveOptions = getOneMoveOptions(board, 'up');
moveElementsToNewPath(board, moveOptions);
};

const moveDown = (board: PlaitBoard) => {
const moveOptions = getOneMoveOptions(board, 'down');
moveElementsToNewPath(board, moveOptions);
};

export const ZIndexTransforms = { moveUp, moveDown, moveToTop, moveToBottom };
1 change: 1 addition & 0 deletions packages/common/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './memorize';
export * from './vector';
export * from './math';
export * from './drawing';
export * from './z-index';
167 changes: 167 additions & 0 deletions packages/common/src/utils/z-index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import {
PlaitBoard,
getSelectedElements,
getSelectedGroups,
PlaitElement,
PlaitGroupElement,
findLastIndex,
getEditingGroup,
getGroupByElement,
PlaitGroup,
getElementsInGroup,
Transforms,
Path,
findIndex
} 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[] => {
const indicesToMove = getIndicesToMove(board);
let groupedIndices = toContiguousGroups(board, indicesToMove);
if (direction === 'up') {
groupedIndices = groupedIndices.reverse();
}
let moveContents: ZIndexMoveOption[] = [];
groupedIndices.forEach((indices, i) => {
const leadingIndex = indices[0];
const trailingIndex = indices[indices.length - 1];
const boundaryIndex = direction === 'down' ? leadingIndex : trailingIndex;
const targetIndex = getTargetIndex(board, boundaryIndex, direction);
if (targetIndex === -1 || boundaryIndex === targetIndex) {
return;
}
if (direction === 'down') {
moveContents.push(
...indices.reverse().map(path => {
return {
element: board.children[path],
newPath: [targetIndex]
};
})
);
} else {
moveContents.push(
...indices.map(path => {
return {
element: board.children[path],
newPath: [targetIndex]
};
})
);
}
});

return moveContents;
};

const getIndicesToMove = (board: PlaitBoard) => {
const selectedElements = [...getSelectedElements(board), ...getSelectedGroups(board)];
return selectedElements
.map(item => {
return board.children.indexOf(item);
})
.sort((a, b) => {
return a - b;
});
};

const toContiguousGroups = (board: PlaitBoard, array: number[]) => {
let cursor = 0;
return array.reduce((acc, value, index) => {
if (index > 0) {
const currentElement = board.children[value];
const previousElement = board.children[array[index - 1]];
const isInSameGroup = currentElement.groupId === previousElement.groupId;
const isContain = currentElement.id === previousElement.groupId || currentElement.groupId === previousElement.id;
if (array[index - 1] !== value - 1 || (!isInSameGroup && !isContain)) {
cursor = ++cursor;
}
}
(acc[cursor] || (acc[cursor] = [])).push(value);
return acc;
}, [] as number[][]);
};

/**
* Returns next candidate index that's available to be moved to. Currently that
* is a non-deleted element, and not inside a group (unless we're editing it).
*/
const getTargetIndex = (board: PlaitBoard, boundaryIndex: number, direction: 'down' | 'up') => {
if ((boundaryIndex === 0 && direction === 'down') || (boundaryIndex === board.children.length - 1 && direction === 'up')) {
return -1;
}
const indexFilter = (element: PlaitElement) => {
if (element.isDeleted || PlaitGroupElement.isGroup(element)) {
return false;
}
return true;
};
const candidateIndex =
direction === 'down'
? findLastIndex(board.children, el => indexFilter(el), Math.max(0, boundaryIndex - 1))
: findIndex(board.children, el => indexFilter(el), boundaryIndex + 1);

const nextElement = board.children[candidateIndex];
if (!nextElement) {
return -1;
}

if (!nextElement.groupId) {
return candidateIndex;
}

const elements = [...board.children];
const sourceElement = elements[boundaryIndex];
const editingGroup = getEditingGroup(board, sourceElement);
const nextElementGroups = (getGroupByElement(board, nextElement, true) || []) as PlaitGroup[];
// candidate element is a sibling in current editing group → return
if (editingGroup && sourceElement?.groupId !== nextElement?.groupId) {
// candidate element is outside current editing group → prevent
if (!(nextElementGroups as PlaitGroup[]).find(item => item.id === editingGroup.id)) {
return -1;
}
}
let siblingGroup: PlaitGroup;
if (editingGroup) {
siblingGroup = nextElementGroups[nextElementGroups.indexOf(editingGroup) - 1];
} else {
siblingGroup = nextElementGroups[nextElementGroups.length - 1];
}
if (siblingGroup) {
let elementsInSiblingGroup = getElementsInGroup(board, siblingGroup, true, true);
if (elementsInSiblingGroup.length) {
elementsInSiblingGroup = [...elementsInSiblingGroup, siblingGroup];
elementsInSiblingGroup.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;
});
// assumes getElementsInGroup() returned elements are sorted
// by zIndex (ascending)
return direction === 'down'
? elements.indexOf(elementsInSiblingGroup[0])
: elements.indexOf(elementsInSiblingGroup[elementsInSiblingGroup.length - 1]);
}
}

return candidateIndex;
};
15 changes: 15 additions & 0 deletions packages/core/src/utils/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,18 @@ export const canRemoveGroup = (board: PlaitBoard, elements?: PlaitElement[]) =>
const selectedElements = elements || getSelectedElements(board);
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;
if (groups?.length) {
for (let i = 0; i < groups?.length; i++) {
if (!isSelectedAllElementsInGroup(board, groups[i])) {
editingGroup = groups[i];
break;
}
}
}
return editingGroup;
};
39 changes: 38 additions & 1 deletion packages/core/src/utils/helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PlaitElement } from "../interfaces";
import { PlaitElement } from '../interfaces';

export function isNullOrUndefined(value: any) {
return value === null || value === undefined;
Expand Down Expand Up @@ -37,3 +37,40 @@ export function uniqueById(elements: PlaitElement[]) {

return Array.from(uniqueMap.values());
}

export const findLastIndex = <T>(
array: readonly T[],
cb: (element: T, index: number, array: readonly T[]) => boolean,
fromIndex: number = array.length - 1
) => {
if (fromIndex < 0) {
fromIndex = array.length + fromIndex;
}
fromIndex = Math.min(array.length - 1, Math.max(fromIndex, 0));
let index = fromIndex + 1;
while (--index > -1) {
if (cb(array[index], index, array)) {
return index;
}
}
return -1;
};

export const findIndex = <T>(
array: readonly T[],
cb: (element: T, index: number, array: readonly T[]) => boolean,
fromIndex: number = 0
) => {
// fromIndex = 2
if (fromIndex < 0) {
fromIndex = array.length + fromIndex;
}
fromIndex = Math.min(array.length, Math.max(fromIndex, 0));
let index = fromIndex - 1;
while (++index < array.length) {
if (cb(array[index], index, array)) {
return index;
}
}
return -1;
};
2 changes: 2 additions & 0 deletions src/app/editor/editor.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
</plait-board>

<div #contextMenu class="context-menu">
<div class="context-menu-item" (pointerup)="moveUp($event)">上移一层</div>
<div class="context-menu-item" (pointerup)="moveDown($event)">下移一层</div>
<div class="context-menu-item" *ngIf="showAddGroup" (pointerup)="addGroup($event)">成组</div>
<div class="context-menu-item" *ngIf="showRemoveGroup" (pointerup)="removeGroup($event)">取消成组</div>
<div class="context-menu-item" (pointerup)="copy($event)">复制</div>
Expand Down
14 changes: 13 additions & 1 deletion src/app/editor/editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { withCommonPlugin } from '../plugins/with-common';
import { AppMenuComponent } from '../components/menu/menu.component';
import { NgIf } from '@angular/common';
import { mockTurningPointData } from './mock-turning-point-data';
import { withGroup } from '@plait/common';
import { withGroup, ZIndexTransforms } from '@plait/common';

const LOCAL_STORAGE_KEY = 'plait-board-data';

Expand Down Expand Up @@ -169,6 +169,18 @@ export class BasicEditorComponent implements OnInit {
BoardTransforms.updateThemeColor(this.board, value as ThemeColorMode);
}

moveUp(event: MouseEvent) {
event.stopPropagation();
event.preventDefault();
ZIndexTransforms.moveUp(this.board);
}

moveDown(event: MouseEvent) {
event.stopPropagation();
event.preventDefault();
ZIndexTransforms.moveDown(this.board);
}

copy(event: MouseEvent) {
event.stopPropagation();
event.preventDefault();
Expand Down

0 comments on commit d30361f

Please sign in to comment.