From 81ebf82979f7efba460a882478284583451f2f32 Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Thu, 15 Jun 2023 00:42:01 +0800 Subject: [PATCH] feat: introduce frames (#6123) Co-authored-by: dwelle --- src/actions/actionAddToLibrary.ts | 5 +- src/actions/actionAlign.tsx | 40 +- src/actions/actionBoundText.tsx | 1 + src/actions/actionCanvas.tsx | 10 +- src/actions/actionClipboard.tsx | 25 +- src/actions/actionDeleteSelected.tsx | 14 +- src/actions/actionDistribute.tsx | 18 +- src/actions/actionDuplicateSelection.tsx | 80 +- src/actions/actionElementLock.ts | 21 +- src/actions/actionFlip.ts | 14 +- src/actions/actionFrame.ts | 140 +++ src/actions/actionGroup.tsx | 86 +- src/actions/actionLinearEditor.ts | 8 +- src/actions/actionMenu.tsx | 1 - src/actions/actionProperties.tsx | 5 +- src/actions/actionSelectAll.ts | 22 +- src/actions/actionStyles.ts | 12 +- src/actions/types.ts | 5 + src/align.ts | 4 +- src/appState.ts | 14 + src/clipboard.ts | 21 +- src/components/Actions.tsx | 150 ++- src/components/App.tsx | 1025 +++++++++++++++-- src/components/HelpDialog.tsx | 1 + src/components/ImageExportDialog.tsx | 5 +- src/components/LayerUI.tsx | 9 +- src/components/LibraryMenu.tsx | 6 +- src/components/ToolButton.tsx | 4 +- src/components/Toolbar.scss | 19 +- .../dropdownMenu/DropdownMenuTrigger.tsx | 10 +- .../hoc/withInternalFallback.test.tsx | 24 +- src/components/icons.tsx | 21 + src/components/main-menu/MainMenu.tsx | 1 + src/constants.ts | 11 + src/data/restore.ts | 6 + src/element/Hyperlink.tsx | 6 +- src/element/binding.ts | 2 +- src/element/bounds.ts | 266 ++++- src/element/collision.ts | 168 ++- src/element/dragElements.ts | 35 +- src/element/index.ts | 14 +- src/element/linearElementEditor.ts | 2 +- src/element/newElement.ts | 25 + src/element/resizeElements.ts | 82 +- src/element/textElement.ts | 10 +- src/element/transformHandles.ts | 14 +- src/element/typeChecks.ts | 7 + src/element/types.ts | 12 +- src/frame.ts | 705 ++++++++++++ src/groups.ts | 40 + src/keys.ts | 1 + src/locales/en.json | 6 +- src/math.ts | 2 +- src/packages/excalidraw/example/App.tsx | 1 + .../excalidraw/example/initialData.js | 44 + src/renderer/renderElement.ts | 163 ++- src/renderer/renderScene.ts | 203 +++- src/scene/Scene.ts | 52 +- src/scene/comparisons.ts | 3 +- src/scene/export.ts | 83 +- src/scene/selection.ts | 96 +- src/shapes.tsx | 8 + .../__snapshots__/contextmenu.test.tsx.snap | 275 +++++ .../__snapshots__/dragCreate.test.tsx.snap | 5 + src/tests/__snapshots__/export.test.tsx.snap | 3 +- src/tests/__snapshots__/move.test.tsx.snap | 6 + .../multiPointCreate.test.tsx.snap | 2 + .../regressionTests.test.tsx.snap | 642 +++++++++++ .../__snapshots__/selection.test.tsx.snap | 5 + .../data/__snapshots__/restore.test.ts.snap | 9 + src/tests/fixtures/elementFixture.ts | 1 + src/tests/helpers/api.ts | 7 +- .../packages/__snapshots__/utils.test.ts.snap | 5 + src/tests/queries/toolQueries.ts | 1 + .../scene/__snapshots__/export.test.ts.snap | 5 +- src/types.ts | 35 +- src/utils.ts | 21 +- src/zindex.ts | 133 ++- 78 files changed, 4563 insertions(+), 480 deletions(-) create mode 100644 src/actions/actionFrame.ts create mode 100644 src/frame.ts diff --git a/src/actions/actionAddToLibrary.ts b/src/actions/actionAddToLibrary.ts index a4fca560..ef69a60d 100644 --- a/src/actions/actionAddToLibrary.ts +++ b/src/actions/actionAddToLibrary.ts @@ -12,7 +12,10 @@ export const actionAddToLibrary = register({ const selectedElements = getSelectedElements( getNonDeletedElements(elements), appState, - true, + { + includeBoundTextElement: true, + includeElementsInFrames: true, + }, ); if (selectedElements.some((element) => element.type === "image")) { return { diff --git a/src/actions/actionAlign.tsx b/src/actions/actionAlign.tsx index eceb4217..d917f803 100644 --- a/src/actions/actionAlign.tsx +++ b/src/actions/actionAlign.tsx @@ -10,6 +10,7 @@ import { import { ToolButton } from "../components/ToolButton"; import { getNonDeletedElements } from "../element"; import { ExcalidrawElement } from "../element/types"; +import { updateFrameMembershipOfSelectedElements } from "../frame"; import { t } from "../i18n"; import { KEYS } from "../keys"; import { getSelectedElements, isSomeElementSelected } from "../scene"; @@ -17,10 +18,20 @@ import { AppState } from "../types"; import { arrayToMap, getShortcutKey } from "../utils"; import { register } from "./register"; -const enableActionGroup = ( +const alignActionsPredicate = ( elements: readonly ExcalidrawElement[], appState: AppState, -) => getSelectedElements(getNonDeletedElements(elements), appState).length > 1; +) => { + const selectedElements = getSelectedElements( + getNonDeletedElements(elements), + appState, + ); + return ( + selectedElements.length > 1 && + // TODO enable aligning frames when implemented properly + !selectedElements.some((el) => el.type === "frame") + ); +}; const alignSelectedElements = ( elements: readonly ExcalidrawElement[], @@ -36,14 +47,16 @@ const alignSelectedElements = ( const updatedElementsMap = arrayToMap(updatedElements); - return elements.map( - (element) => updatedElementsMap.get(element.id) || element, + return updateFrameMembershipOfSelectedElements( + elements.map((element) => updatedElementsMap.get(element.id) || element), + appState, ); }; export const actionAlignTop = register({ name: "alignTop", trackEvent: { category: "element" }, + predicate: alignActionsPredicate, perform: (elements, appState) => { return { appState, @@ -58,7 +71,7 @@ export const actionAlignTop = register({ event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_UP, PanelComponent: ({ elements, appState, updateData }) => (