feat: add `props.onDuplicate` (#9117)

* feat: add `props.onDuplicate`

* docs

* clarify docs

* fix docs
pull/9124/head
David Luzar 1 week ago committed by GitHub
parent 9e49c9254b
commit c8f4a4cb41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -69,8 +69,20 @@ export const actionDuplicateSelection = register({
} }
} }
const nextState = duplicateElements(elements, appState);
if (app.props.onDuplicate && nextState.elements) {
const mappedElements = app.props.onDuplicate(
nextState.elements,
elements,
);
if (mappedElements) {
nextState.elements = mappedElements;
}
}
return { return {
...duplicateElements(elements, appState), ...nextState,
storeAction: StoreAction.CAPTURE, storeAction: StoreAction.CAPTURE,
}; };
}, },
@ -92,7 +104,7 @@ export const actionDuplicateSelection = register({
const duplicateElements = ( const duplicateElements = (
elements: readonly ExcalidrawElement[], elements: readonly ExcalidrawElement[],
appState: AppState, appState: AppState,
): Partial<ActionResult> => { ): Partial<Exclude<ActionResult, false>> => {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const groupIdMap = new Map(); const groupIdMap = new Map();

@ -3228,7 +3228,14 @@ class App extends React.Component<AppProps, AppState> {
); );
const prevElements = this.scene.getElementsIncludingDeleted(); const prevElements = this.scene.getElementsIncludingDeleted();
const nextElements = [...prevElements, ...newElements]; let nextElements = [...prevElements, ...newElements];
const mappedNewSceneElements = this.props.onDuplicate?.(
nextElements,
prevElements,
);
nextElements = mappedNewSceneElements || nextElements;
syncMovedIndices(nextElements, arrayToMap(newElements)); syncMovedIndices(nextElements, arrayToMap(newElements));
@ -8442,7 +8449,17 @@ class App extends React.Component<AppProps, AppState> {
} }
} }
const nextSceneElements = [...nextElements, ...elementsToAppend]; let nextSceneElements: ExcalidrawElement[] = [
...nextElements,
...elementsToAppend,
];
const mappedNewSceneElements = this.props.onDuplicate?.(
nextSceneElements,
elements,
);
nextSceneElements = mappedNewSceneElements || nextSceneElements;
syncMovedIndices(nextSceneElements, arrayToMap(elementsToAppend)); syncMovedIndices(nextSceneElements, arrayToMap(elementsToAppend));

@ -46,6 +46,7 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
onPointerDown, onPointerDown,
onPointerUp, onPointerUp,
onScrollChange, onScrollChange,
onDuplicate,
children, children,
validateEmbeddable, validateEmbeddable,
renderEmbeddable, renderEmbeddable,
@ -136,6 +137,7 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
onPointerDown={onPointerDown} onPointerDown={onPointerDown}
onPointerUp={onPointerUp} onPointerUp={onPointerUp}
onScrollChange={onScrollChange} onScrollChange={onScrollChange}
onDuplicate={onDuplicate}
validateEmbeddable={validateEmbeddable} validateEmbeddable={validateEmbeddable}
renderEmbeddable={renderEmbeddable} renderEmbeddable={renderEmbeddable}
aiEnabled={aiEnabled !== false} aiEnabled={aiEnabled !== false}

@ -512,6 +512,22 @@ export interface ExcalidrawProps {
data: ClipboardData, data: ClipboardData,
event: ClipboardEvent | null, event: ClipboardEvent | null,
) => Promise<boolean> | boolean; ) => Promise<boolean> | boolean;
/**
* Called when element(s) are duplicated so you can listen or modify as
* needed.
*
* Called when duplicating via mouse-drag, keyboard, paste, library insert
* etc.
*
* Returned elements will be used in place of the next elements
* (you should return all elements, including deleted, and not mutate
* the element if changes are made)
*/
onDuplicate?: (
nextElements: readonly ExcalidrawElement[],
/** excludes the duplicated elements */
prevElements: readonly ExcalidrawElement[],
) => ExcalidrawElement[] | void;
renderTopRightUI?: ( renderTopRightUI?: (
isMobile: boolean, isMobile: boolean,
appState: UIAppState, appState: UIAppState,

Loading…
Cancel
Save