123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- import {
- DEFAULT_GRID_SIZE,
- KEYS,
- arrayToMap,
- getShortcutKey,
- } from "@excalidraw/common";
- import { getNonDeletedElements } from "@excalidraw/element";
- import { LinearElementEditor } from "@excalidraw/element";
- import {
- getSelectedElements,
- getSelectionStateForElements,
- } from "@excalidraw/element";
- import { syncMovedIndices } from "@excalidraw/element";
- import { duplicateElements } from "@excalidraw/element";
- import { CaptureUpdateAction } from "@excalidraw/element";
- import { ToolButton } from "../components/ToolButton";
- import { DuplicateIcon } from "../components/icons";
- import { t } from "../i18n";
- import { isSomeElementSelected } from "../scene";
- import { register } from "./register";
- export const actionDuplicateSelection = register({
- name: "duplicateSelection",
- label: "labels.duplicateSelection",
- icon: DuplicateIcon,
- trackEvent: { category: "element" },
- perform: (elements, appState, formData, app) => {
- if (appState.selectedElementsAreBeingDragged) {
- return false;
- }
- // duplicate selected point(s) if editing a line
- if (appState.selectedLinearElement?.isEditing) {
- // TODO: Invariants should be checked here instead of duplicateSelectedPoints()
- try {
- const newAppState = LinearElementEditor.duplicateSelectedPoints(
- appState,
- app.scene,
- );
- return {
- elements,
- appState: newAppState,
- captureUpdate: CaptureUpdateAction.IMMEDIATELY,
- };
- } catch {
- return false;
- }
- }
- let { duplicatedElements, elementsWithDuplicates } = duplicateElements({
- type: "in-place",
- elements,
- idsOfElementsToDuplicate: arrayToMap(
- getSelectedElements(elements, appState, {
- includeBoundTextElement: true,
- includeElementsInFrames: true,
- }),
- ),
- appState,
- randomizeSeed: true,
- overrides: ({ origElement, origIdToDuplicateId }) => {
- const duplicateFrameId =
- origElement.frameId && origIdToDuplicateId.get(origElement.frameId);
- return {
- x: origElement.x + DEFAULT_GRID_SIZE / 2,
- y: origElement.y + DEFAULT_GRID_SIZE / 2,
- frameId: duplicateFrameId ?? origElement.frameId,
- };
- },
- });
- if (app.props.onDuplicate && elementsWithDuplicates) {
- const mappedElements = app.props.onDuplicate(
- elementsWithDuplicates,
- elements,
- );
- if (mappedElements) {
- elementsWithDuplicates = mappedElements;
- }
- }
- return {
- elements: syncMovedIndices(
- elementsWithDuplicates,
- arrayToMap(duplicatedElements),
- ),
- appState: {
- ...appState,
- ...getSelectionStateForElements(
- duplicatedElements,
- getNonDeletedElements(elementsWithDuplicates),
- appState,
- ),
- },
- captureUpdate: CaptureUpdateAction.IMMEDIATELY,
- };
- },
- keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.D,
- PanelComponent: ({ elements, appState, updateData }) => (
- <ToolButton
- type="button"
- icon={DuplicateIcon}
- title={`${t("labels.duplicateSelection")} — ${getShortcutKey(
- "CtrlOrCmd+D",
- )}`}
- aria-label={t("labels.duplicateSelection")}
- onClick={() => updateData(null)}
- visible={isSomeElementSelected(getNonDeletedElements(elements), appState)}
- />
- ),
- });
|