Skip to content

Commit c8f4a4c

Browse files
authored
feat: add props.onDuplicate (#9117)
* feat: add `props.onDuplicate` * docs * clarify docs * fix docs
1 parent 9e49c92 commit c8f4a4c

File tree

4 files changed

+51
-4
lines changed

4 files changed

+51
-4
lines changed

packages/excalidraw/actions/actionDuplicateSelection.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,20 @@ export const actionDuplicateSelection = register({
6969
}
7070
}
7171

72+
const nextState = duplicateElements(elements, appState);
73+
74+
if (app.props.onDuplicate && nextState.elements) {
75+
const mappedElements = app.props.onDuplicate(
76+
nextState.elements,
77+
elements,
78+
);
79+
if (mappedElements) {
80+
nextState.elements = mappedElements;
81+
}
82+
}
83+
7284
return {
73-
...duplicateElements(elements, appState),
85+
...nextState,
7486
storeAction: StoreAction.CAPTURE,
7587
};
7688
},
@@ -92,7 +104,7 @@ export const actionDuplicateSelection = register({
92104
const duplicateElements = (
93105
elements: readonly ExcalidrawElement[],
94106
appState: AppState,
95-
): Partial<ActionResult> => {
107+
): Partial<Exclude<ActionResult, false>> => {
96108
// ---------------------------------------------------------------------------
97109

98110
const groupIdMap = new Map();

packages/excalidraw/components/App.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3228,7 +3228,14 @@ class App extends React.Component<AppProps, AppState> {
32283228
);
32293229

32303230
const prevElements = this.scene.getElementsIncludingDeleted();
3231-
const nextElements = [...prevElements, ...newElements];
3231+
let nextElements = [...prevElements, ...newElements];
3232+
3233+
const mappedNewSceneElements = this.props.onDuplicate?.(
3234+
nextElements,
3235+
prevElements,
3236+
);
3237+
3238+
nextElements = mappedNewSceneElements || nextElements;
32323239

32333240
syncMovedIndices(nextElements, arrayToMap(newElements));
32343241

@@ -8442,7 +8449,17 @@ class App extends React.Component<AppProps, AppState> {
84428449
}
84438450
}
84448451

8445-
const nextSceneElements = [...nextElements, ...elementsToAppend];
8452+
let nextSceneElements: ExcalidrawElement[] = [
8453+
...nextElements,
8454+
...elementsToAppend,
8455+
];
8456+
8457+
const mappedNewSceneElements = this.props.onDuplicate?.(
8458+
nextSceneElements,
8459+
elements,
8460+
);
8461+
8462+
nextSceneElements = mappedNewSceneElements || nextSceneElements;
84468463

84478464
syncMovedIndices(nextSceneElements, arrayToMap(elementsToAppend));
84488465

packages/excalidraw/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
4646
onPointerDown,
4747
onPointerUp,
4848
onScrollChange,
49+
onDuplicate,
4950
children,
5051
validateEmbeddable,
5152
renderEmbeddable,
@@ -136,6 +137,7 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
136137
onPointerDown={onPointerDown}
137138
onPointerUp={onPointerUp}
138139
onScrollChange={onScrollChange}
140+
onDuplicate={onDuplicate}
139141
validateEmbeddable={validateEmbeddable}
140142
renderEmbeddable={renderEmbeddable}
141143
aiEnabled={aiEnabled !== false}

packages/excalidraw/types.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,22 @@ export interface ExcalidrawProps {
512512
data: ClipboardData,
513513
event: ClipboardEvent | null,
514514
) => Promise<boolean> | boolean;
515+
/**
516+
* Called when element(s) are duplicated so you can listen or modify as
517+
* needed.
518+
*
519+
* Called when duplicating via mouse-drag, keyboard, paste, library insert
520+
* etc.
521+
*
522+
* Returned elements will be used in place of the next elements
523+
* (you should return all elements, including deleted, and not mutate
524+
* the element if changes are made)
525+
*/
526+
onDuplicate?: (
527+
nextElements: readonly ExcalidrawElement[],
528+
/** excludes the duplicated elements */
529+
prevElements: readonly ExcalidrawElement[],
530+
) => ExcalidrawElement[] | void;
515531
renderTopRightUI?: (
516532
isMobile: boolean,
517533
appState: UIAppState,

0 commit comments

Comments
 (0)