Skip to content

Commit

Permalink
feat(core): support duplicate elements #WIK-15134 (#828)
Browse files Browse the repository at this point in the history
  • Loading branch information
huanhuanwa authored Apr 17, 2024
1 parent 43e1220 commit 2b08378
Show file tree
Hide file tree
Showing 19 changed files with 157 additions and 79 deletions.
12 changes: 12 additions & 0 deletions .changeset/rich-ads-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@plait/common': minor
'@plait/core': minor
'@plait/draw': minor
'@plait/mind': minor
---

rename setFragment to buildFragment

remove DataTransfer param of setFragment and insertFragment

support duplicate elements
4 changes: 2 additions & 2 deletions packages/common/src/plugins/with-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export function withGroup(board: PlaitBoard) {
return getRelatedFragment([...elements, ...groups], originData);
};

board.insertFragment = (data: DataTransfer | null, clipboardData: ClipboardData | null, targetPoint: Point) => {
board.insertFragment = (clipboardData: ClipboardData | null, targetPoint: Point) => {
let elements: PlaitElement[] = [];
if (clipboardData?.elements?.length) {
elements = new Array(clipboardData?.elements?.length);
Expand All @@ -94,7 +94,7 @@ export function withGroup(board: PlaitBoard) {
}
clipboardData.elements = elements;
}
insertFragment(data, clipboardData, targetPoint);
insertFragment(clipboardData, targetPoint);
const groupElements = elements?.filter(value => PlaitGroupElement.isGroup(value)) as PlaitElement[];
groupElements.forEach(element => {
Transforms.insertNode(board, element, [board.children.length]);
Expand Down
12 changes: 5 additions & 7 deletions packages/core/src/board/board.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import {
getClipboardData,
getRectangleByElements,
getSelectedElements,
setClipboardData,
setFragment,
toHostPoint,
toViewBoxPoint
} from '../utils';
Expand Down Expand Up @@ -398,10 +400,8 @@ export class PlaitBoardComponent implements BoardComponentInterface, OnInit, OnC
filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board))
)
.subscribe((event: ClipboardEvent) => {
const selectedElements = getSelectedElements(this.board);
event.preventDefault();
const rectangle = getRectangleByElements(this.board, selectedElements, false);
this.board.setFragment(event.clipboardData, null, rectangle, 'copy');
setFragment(this.board, 'copy', event.clipboardData);
});

fromEvent<ClipboardEvent>(document, 'paste')
Expand All @@ -414,7 +414,7 @@ export class PlaitBoardComponent implements BoardComponentInterface, OnInit, OnC
if (mousePoint) {
const targetPoint = toViewBoxPoint(this.board, toHostPoint(this.board, mousePoint[0], mousePoint[1]));
const clipboardData = await getClipboardData(clipboardEvent.clipboardData);
this.board.insertFragment(clipboardEvent.clipboardData, clipboardData, targetPoint);
this.board.insertFragment(clipboardData, targetPoint);
}
});

Expand All @@ -424,10 +424,8 @@ export class PlaitBoardComponent implements BoardComponentInterface, OnInit, OnC
filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board))
)
.subscribe((event: ClipboardEvent) => {
const selectedElements = getSelectedElements(this.board);
event.preventDefault();
const rectangle = getRectangleByElements(this.board, selectedElements, false);
this.board.setFragment(event.clipboardData, null, rectangle, 'cut');
setFragment(this.board, 'cut', event.clipboardData);
deleteFragment(this.board);
});
}
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/interfaces/board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,12 @@ export interface PlaitBoard {
keyDown: (event: KeyboardEvent) => void;
globalKeyDown: (event: KeyboardEvent) => void;
keyUp: (event: KeyboardEvent) => void;
setFragment: (
data: DataTransfer | null,
buildFragment: (
clipboardContext: WritableClipboardContext | null,
rectangle: RectangleClient | null,
type: 'copy' | 'cut'
) => void;
insertFragment: (data: DataTransfer | null, clipboardData: ClipboardData | null, targetPoint: Point) => void;
) => WritableClipboardContext | null;
insertFragment: (clipboardData: ClipboardData | null, targetPoint: Point) => void;
deleteFragment: (data: PlaitElement[]) => void;
getDeletedFragment: (data: PlaitElement[]) => PlaitElement[];
getRelatedFragment: (data: PlaitElement[], originData?: PlaitElement[]) => PlaitElement[];
Expand Down
6 changes: 2 additions & 4 deletions packages/core/src/plugins/create-board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,8 @@ export function createBoard(children: PlaitElement[], options?: PlaitBoardOption
globalKeyDown: (event: KeyboardEvent) => {},
keyUp: (event: KeyboardEvent) => {},
dblClick: (event: MouseEvent) => {},
setFragment: (data: DataTransfer | null, clipboardContext: WritableClipboardContext | null) => {
setClipboardData(data, clipboardContext);
},
insertFragment: (data: DataTransfer | null) => {},
buildFragment: (clipboardContext: WritableClipboardContext | null) => clipboardContext,
insertFragment: () => {},
deleteFragment: (elements: PlaitElement[]) => {
CoreTransforms.removeElements(board, elements);
},
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/plugins/with-hotkey.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isHotkey, isKeyHotkey } from 'is-hotkey';
import { Ancestor, PlaitBoard, PlaitElement, PlaitPluginKey } from '../interfaces';
import { BoardTransforms, Transforms } from '../transforms';
import { deleteFragment, depthFirstRecursion, getSelectedElements, hotkeys } from '../utils';
import { deleteFragment, depthFirstRecursion, duplicateElements, getSelectedElements, hotkeys } from '../utils';
import { PlaitOptionsBoard } from './with-options';
import { WithPluginOptions } from './with-selection';

Expand Down Expand Up @@ -57,6 +57,13 @@ export const withHotkey = (board: PlaitBoard) => {
}

const selectedElements = getSelectedElements(board);
if (!PlaitBoard.isReadonly(board) && selectedElements.length > 0) {
if (isKeyHotkey('mod+d', event)) {
event.preventDefault();
duplicateElements(board, selectedElements);
return;
}
}
if (
!PlaitBoard.isReadonly(board) &&
selectedElements.length > 0 &&
Expand Down
9 changes: 4 additions & 5 deletions packages/core/src/plugins/with-related-fragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import {
} from '../utils';

export function withRelatedFragment(board: PlaitBoard) {
const { setFragment } = board;
const { buildFragment } = board;

board.setFragment = (
data: DataTransfer | null,
board.buildFragment = (
clipboardContext: WritableClipboardContext | null,
rectangle: RectangleClient | null,
type: 'copy' | 'cut'
Expand All @@ -22,10 +21,10 @@ export function withRelatedFragment(board: PlaitBoard) {
clipboardContext = addClipboardContext(clipboardContext, {
text: '',
type: WritableClipboardType.elements,
data: relatedFragment
elements: relatedFragment
});
}
setFragment(data, clipboardContext, rectangle, type);
return buildFragment(clipboardContext, rectangle, type);
};

return board;
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/utils/clipboard/clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@ export const setClipboardData = async (dataTransfer: DataTransfer | null, clipbo
if (!clipboardContext) {
return;
}
const { type, data, text } = clipboardContext;
const { type, elements, text } = clipboardContext;

if (getProbablySupportsClipboardWrite()) {
return await setNavigatorClipboard(type, data, text);
return await setNavigatorClipboard(type, elements, text);
}

if (dataTransfer) {
setDataTransferClipboard(dataTransfer, type, data);
setDataTransferClipboard(dataTransfer, type, elements);
setDataTransferClipboardText(dataTransfer, text);
return;
}

// Compatible with situations where navigator.clipboard.write is not supported and dataTransfer is empty
// Such as contextmenu copy in Firefox.
if (getProbablySupportsClipboardWriteText()) {
return await navigator.clipboard.writeText(buildPlaitHtml(type, data));
return await navigator.clipboard.writeText(buildPlaitHtml(type, elements));
}
};
8 changes: 4 additions & 4 deletions packages/core/src/utils/clipboard/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ export const getProbablySupportsClipboardRead = () => {

export const createClipboardContext = (
type: WritableClipboardType,
data: WritableClipboardData,
elements: WritableClipboardData,
text: string
): WritableClipboardContext => {
return {
type,
data,
elements,
text
};
};
Expand All @@ -67,11 +67,11 @@ export const addClipboardContext = (
clipboardContext: WritableClipboardContext,
addition: WritableClipboardContext
): WritableClipboardContext => {
const { type, data, text } = clipboardContext;
const { type, elements, text } = clipboardContext;
if (type === addition.type) {
return {
type,
data: data.concat(addition.data),
elements: elements.concat(addition.elements),
text: text + ' ' + addition.text
};
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/utils/clipboard/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export type WritableClipboardData = PlaitElement[] | any[];
export interface WritableClipboardContext {
text: string;
type: WritableClipboardType;
data: WritableClipboardData;
elements: WritableClipboardData;
}

export interface ClipboardData {
Expand Down
29 changes: 27 additions & 2 deletions packages/core/src/utils/fragment.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
import { PlaitBoard } from "../interfaces";
import { PlaitBoard, PlaitElement } from '../interfaces';
import { setClipboardData } from './clipboard/clipboard';
import { getRectangleByElements } from './element';
import { getSelectedElements } from './selected-element';

export const deleteFragment = (board: PlaitBoard) => {
const elements = board.getDeletedFragment([]);
board.deleteFragment(elements);
}
};

export const setFragment = (board: PlaitBoard, type: 'copy' | 'cut', clipboardData: DataTransfer | null) => {
const selectedElements = getSelectedElements(board);
const rectangle = getRectangleByElements(board, selectedElements, false);
const clipboardContext = board.buildFragment(null, rectangle, type);
clipboardContext && setClipboardData(clipboardData, clipboardContext);
};


export const duplicateElements = (board: PlaitBoard, elements?: PlaitElement[]) => {
const selectedElements = elements || getSelectedElements(board);
const rectangle = getRectangleByElements(board, selectedElements, false);
const clipboardContext = board.buildFragment(null, rectangle, 'copy');
clipboardContext &&
board.insertFragment(
{
...clipboardContext,
text: undefined
},
[rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2]
);
};
2 changes: 1 addition & 1 deletion packages/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ export * from './angle';
export * from './fragment';
export * from './snap/snap';
export * from './z-index';
export * from './position';
export * from './position';
13 changes: 6 additions & 7 deletions packages/draw/src/plugins/with-draw-fragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { DEFAULT_IMAGE_WIDTH } from '../constants';

export const withDrawFragment = (baseBoard: PlaitBoard) => {
const board = baseBoard as PlaitBoard;
const { getDeletedFragment, setFragment, insertFragment } = board;
const { getDeletedFragment, buildFragment, insertFragment } = board;

board.getDeletedFragment = (data: PlaitElement[]) => {
const drawElements = getSelectedDrawElements(board);
Expand All @@ -47,8 +47,7 @@ export const withDrawFragment = (baseBoard: PlaitBoard) => {
return getDeletedFragment(data);
};

board.setFragment = (
data: DataTransfer | null,
board.buildFragment = (
clipboardContext: WritableClipboardContext | null,
rectangle: RectangleClient | null,
type: 'copy' | 'cut'
Expand All @@ -70,14 +69,14 @@ export const withDrawFragment = (baseBoard: PlaitBoard) => {
clipboardContext = addClipboardContext(clipboardContext, {
text,
type: WritableClipboardType.elements,
data: elements
elements
});
}
}
setFragment(data, clipboardContext, rectangle, type);
return buildFragment(clipboardContext, rectangle, type);
};

board.insertFragment = (data: DataTransfer | null, clipboardData: ClipboardData | null, targetPoint: Point) => {
board.insertFragment = (clipboardData: ClipboardData | null, targetPoint: Point) => {
const selectedElements = getSelectedElements(board);
if (clipboardData?.files?.length) {
const acceptImageArray = acceptImageTypes.map(type => 'image/' + type);
Expand Down Expand Up @@ -111,7 +110,7 @@ export const withDrawFragment = (baseBoard: PlaitBoard) => {
}
}

insertFragment(data, clipboardData, targetPoint);
insertFragment(clipboardData, targetPoint);
};

return board;
Expand Down
1 change: 0 additions & 1 deletion packages/draw/src/plugins/with-draw-hotkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { PlaitBoard, PlaitElement, getHitElementByPoint, getSelectedElements, to
import { isVirtualKey, isSpaceHotkey, isDelete } from '@plait/common';
import { GeometryComponent } from '../geometry.component';
import { PlaitDrawElement } from '../interfaces';
import { getSelectedGeometryElements } from '../utils';

export const withDrawHotkey = (board: PlaitBoard) => {
const { keyDown, dblClick } = board;
Expand Down
13 changes: 6 additions & 7 deletions packages/mind/src/plugins/with-mind-fragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { getElementsText } from '@plait/common';
export const withMindFragment = (baseBoard: PlaitBoard) => {
const board = baseBoard as PlaitBoard & PlaitMindBoard;
let firstLevelElements: MindElement[] | null;
const { getDeletedFragment, insertFragment, setFragment, deleteFragment } = board;
const { getDeletedFragment, insertFragment, buildFragment, deleteFragment } = board;

board.getDeletedFragment = (data: PlaitElement[]) => {
const targetMindElements = getSelectedMindElements(board);
Expand All @@ -54,8 +54,7 @@ export const withMindFragment = (baseBoard: PlaitBoard) => {
}
};

board.setFragment = (
data: DataTransfer | null,
board.buildFragment = (
clipboardContext: WritableClipboardContext | null,
rectangle: RectangleClient | null,
type: 'copy' | 'cut'
Expand All @@ -71,14 +70,14 @@ export const withMindFragment = (baseBoard: PlaitBoard) => {
clipboardContext = addClipboardContext(clipboardContext, {
text,
type: WritableClipboardType.elements,
data: elements
elements
});
}
}
setFragment(data, clipboardContext, rectangle, type);
return buildFragment(clipboardContext, rectangle, type);
};

board.insertFragment = (data: DataTransfer | null, clipboardData: ClipboardData | null, targetPoint: Point) => {
board.insertFragment = (clipboardData: ClipboardData | null, targetPoint: Point) => {
if (clipboardData?.elements?.length) {
const mindElements = clipboardData.elements?.filter(value => MindElement.isMindElement(board, value));
if (mindElements && mindElements.length > 0) {
Expand All @@ -93,7 +92,7 @@ export const withMindFragment = (baseBoard: PlaitBoard) => {
}
}

insertFragment(data, clipboardData, targetPoint);
insertFragment(clipboardData, targetPoint);
};

return board;
Expand Down
Loading

0 comments on commit 2b08378

Please sign in to comment.