From 82b9a6b464941d1afbb2396e77e1bbfc407a0c60 Mon Sep 17 00:00:00 2001 From: Marcel Mraz Date: Tue, 11 Mar 2025 23:18:15 +0100 Subject: [PATCH 01/12] =?UTF-8?q?docs:=20CHANGELOG=20typos=20=F0=9F=99=8F?= =?UTF-8?q?=20(#9250)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/excalidraw/CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/excalidraw/CHANGELOG.md b/packages/excalidraw/CHANGELOG.md index 568492e83..e4a98d19e 100644 --- a/packages/excalidraw/CHANGELOG.md +++ b/packages/excalidraw/CHANGELOG.md @@ -45,9 +45,9 @@ Please add the latest change on the top under the correct section. #### Deprecated UMD bundle in favor of ES modules [#7441](https://github.com/excalidraw/excalidraw/pull/7441), [#9127](https://github.com/excalidraw/excalidraw/pull/9127) -We've transitioned from `UMD` to `ESM` bundle format. Our new `dist` bundles inside `@excalidraw/excalidraw` package now contain only bundled source files, making any dependencies tree-shakable. The npm package comes with the following structure: +We've transitioned from `UMD` to `ESM` bundle format. Our new `dist` folder inside `@excalidraw/excalidraw` package now contains only bundled source files, making any dependencies tree-shakable. The package comes with the following structure: -> **Note**: The structure is simplified for the sake of brevity, omitting lazy-loadable modules, including locales (previously treated as json assets) and source maps in the development bundle. +> **Note**: The structure is simplified for the sake of brevity, omitting lazy-loadable modules, including locales (previously treated as JSON assets) and source maps in the development bundle. ``` @excalidraw/excalidraw/ @@ -72,7 +72,7 @@ Since `"node"` and `"node10"` do not support `package.json` `"exports"` fields, ##### ESM strict resolution -Due to ESM strict resolution, if you're using Webpack or other bundler that expects import paths to be fully specified, you'll need to explicitly disable this feature. +Due to ESM's strict resolution, if you're using Webpack or other bundler that expects import paths to be fully specified, you'll need to disable this feature explicitly. For example in Webpack, you should set [`resolve.fullySpecified`](https://webpack.js.org/configuration/resolve/#resolvefullyspecified) to `false`. @@ -80,7 +80,7 @@ For this reason, CRA will no longer work unless you eject or use a workaround su ##### New structure of the imports -Dependening on the environment, this is how imports should look like with the `ESM`: +Depending on the environment, this is how imports should look like with the `ESM`: **With bundler (Vite, Next.js, etc.)** @@ -128,7 +128,7 @@ The `excalidraw-assets` and `excalidraw-assets-dev` folders, which contained loc ##### Locales -Locales are no longer treated as static `.json` assets, but are transpiled with `esbuild` dirrectly to the `.js` as ES modules. Note that some build tools (i.e. Vite) may require setting `es2022` as a build target, in order to support "Arbitrary module namespace identifier names", e.g. `export { english as "en-us" } )`. +Locales are no longer treated as static `.json` assets but are transpiled with `esbuild` directly to the `.js` as ES modules. Note that some build tools (i.e. Vite) may require setting `es2022` as a build target, in order to support "Arbitrary module namespace identifier names", e.g. `export { english as "en-us" } )`. ```js // vite.config.js @@ -145,7 +145,7 @@ optimizeDeps: { ##### Fonts -New fonts, which we've added, are automatically loaded from the CDN. For self-hosting purposes, you'll have to copy the content of the folder `node_modules/@excalidraw/excalidraw/dist/prod/fonts` to the path where your assets should be served from (i.e. `public/` directory in your project). In that case, you should also set `window.EXCALIDRAW_ASSET_PATH` to the very same path, i.e. `/` in case it's in the root: +All fonts are automatically loaded from the [esm.run](https://esm.run/) CDN. For self-hosting purposes, you'll have to copy the content of the folder `node_modules/@excalidraw/excalidraw/dist/prod/fonts` to the path where your assets should be served from (i.e. `public/` directory in your project). In that case, you should also set `window.EXCALIDRAW_ASSET_PATH` to the very same path, i.e. `/` in case it's in the root: ```js @@ -159,7 +159,7 @@ or, if you serve your assets from the root of your CDN, you would do: ``` -or, if you prefer the path to be dynamicly set based on the `location.origin`, you could do the following: +or, if you prefer the path to be dynamically set based on the `location.origin`, you could do the following: ```jsx // Next.js @@ -189,7 +189,7 @@ updateScene({ }); // B ``` -The `updateScene` API has changed due to the added `Store` component, as part of multiplayer undo / redo initiative. Specifically, optional `sceneData` parameter `commitToHistory: boolean` was replaced with optional `captureUpdate: CaptureUpdateActionType` parameter. Therefore, make sure to update all instances of `updateScene`, which use `commitToHistory` parameter according to the _before / after_ table below. +The `updateScene` API has changed due to the added `Store` component, as part of the multiplayer undo / redo initiative. Specifically, optional `sceneData` parameter `commitToHistory: boolean` was replaced with optional `captureUpdate: CaptureUpdateActionType` parameter. Therefore, make sure to update all instances of `updateScene`, which use `commitToHistory` parameter according to the _before / after_ table below. > **Note**: Some updates are not observed by the store / history - i.e. updates to `collaborators` object or parts of `AppState` which are not observed (not `ObservedAppState`). Such updates will never make it to the undo / redo stacks, regardless of the passed `captureUpdate` value. @@ -203,7 +203,7 @@ The `updateScene` API has changed due to the added `Store` component, as part of - `ExcalidrawTextElement.baseline` was removed and replaced with a vertical offset computation based on font metrics, performed on each text element re-render. In case of custom font usage, extend the `FONT_METRICS` object with the related properties. [#7693](https://github.com/excalidraw/excalidraw/pull/7693) -- `ExcalidrawEmbeddableElement.validated` was removed and moved to private editor state. This should largely not affect your apps unless you were reading from this attribute. We keep validating embeddable urls internally, and the public [`props.validateEmbeddable`](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/props#validateembeddable) still applies. [#7539](https://github.com/excalidraw/excalidraw/pull/7539) +- `ExcalidrawEmbeddableElement.validated` was removed and moved to the private editor state. This should largely not affect your apps unless you were reading from this attribute. We keep validating embeddable urls internally, and the public [`props.validateEmbeddable`](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/props#validateembeddable) still applies. [#7539](https://github.com/excalidraw/excalidraw/pull/7539) - Stats container CSS has changed, so if you're using `renderCustomStats`, you may need to adjust your styles to retain the same layout. [#8361](https://github.com/excalidraw/excalidraw/pull/8361) @@ -487,7 +487,7 @@ The `updateScene` API has changed due to the added `Store` component, as part of - Linear element complete button disabled [#8492](https://github.com/excalidraw/excalidraw/pull/8492) -- Aspect ratio of distorted images are not preserved in SVG exports [#8061](https://github.com/excalidraw/excalidraw/pull/8061) +- Aspect ratios of distorted images are not preserved in SVG exports [#8061](https://github.com/excalidraw/excalidraw/pull/8061) - WYSIWYG editor padding is not normalized with zoom.value [#8481](https://github.com/excalidraw/excalidraw/pull/8481) @@ -517,7 +517,7 @@ The `updateScene` API has changed due to the added `Store` component, as part of - Round coordinates and sizes for rectangle intersection [#8366](https://github.com/excalidraw/excalidraw/pull/8366) -- Text content with tab characters act different in view/edit [#8336](https://github.com/excalidraw/excalidraw/pull/8336) +- Text content with tab characters act differently in view/edit [#8336](https://github.com/excalidraw/excalidraw/pull/8336) - Drawing from 0-dimension canvas [#8356](https://github.com/excalidraw/excalidraw/pull/8356) From 21ffaf4d76bf8554c3ba5561ab8ff41ce89b55fa Mon Sep 17 00:00:00 2001 From: Marcel Mraz Date: Wed, 12 Mar 2025 15:23:31 +0100 Subject: [PATCH 02/12] refactor: auto ordered imports (#9163) --- .eslintrc.json | 15 ++ dev-docs/src/components/Homepage/index.js | 3 +- dev-docs/src/components/Homepage/index.tsx | 3 +- dev-docs/src/pages/index.tsx | 9 +- dev-docs/src/theme/MDXComponents.js | 2 +- examples/with-nextjs/src/app/page.tsx | 1 + .../with-nextjs/src/excalidrawWrapper.tsx | 3 +- .../src/pages/excalidraw-in-pages.tsx | 1 + .../components/CustomFooter.tsx | 1 + .../components/ExampleApp.tsx | 39 ++- .../components/MobileFooter.tsx | 6 +- .../components/sidebar/ExampleSidebar.tsx | 1 + examples/with-script-in-browser/index.tsx | 5 +- examples/with-script-in-browser/utils.ts | 4 +- excalidraw-app/App.tsx | 202 +++++++-------- excalidraw-app/CustomStats.tsx | 16 +- excalidraw-app/ExcalidrawPlusIframeExport.tsx | 10 +- excalidraw-app/app-language/LanguageList.tsx | 4 +- .../app-language/language-detector.ts | 2 +- excalidraw-app/app-language/language-state.ts | 2 + excalidraw-app/collab/Collab.tsx | 102 ++++---- excalidraw-app/collab/CollabError.tsx | 1 + excalidraw-app/collab/Portal.tsx | 31 +-- excalidraw-app/components/AI.tsx | 3 +- excalidraw-app/components/AppFooter.tsx | 8 +- excalidraw-app/components/AppMainMenu.tsx | 9 +- .../components/AppWelcomeScreen.tsx | 5 +- excalidraw-app/components/DebugCanvas.tsx | 23 +- excalidraw-app/components/EncryptedIcon.tsx | 2 +- .../components/ExportToExcalidrawPlus.tsx | 30 +-- excalidraw-app/components/GitHubCorner.tsx | 3 +- .../components/TopErrorBoundary.tsx | 6 +- excalidraw-app/data/FileManager.ts | 3 +- excalidraw-app/data/LocalData.ts | 17 +- excalidraw-app/data/firebase.ts | 42 ++-- excalidraw-app/data/index.ts | 16 +- excalidraw-app/data/localStorage.ts | 6 +- excalidraw-app/index.tsx | 4 +- excalidraw-app/share/ShareDialog.tsx | 20 +- excalidraw-app/tests/MobileMenu.test.tsx | 4 +- excalidraw-app/tests/collab.test.tsx | 13 +- excalidraw-app/useHandleAppTheme.ts | 6 +- package.json | 1 + .../excalidraw/actions/actionAddToLibrary.ts | 9 +- packages/excalidraw/actions/actionAlign.tsx | 10 +- .../excalidraw/actions/actionBoundText.tsx | 22 +- packages/excalidraw/actions/actionCanvas.tsx | 35 +-- .../excalidraw/actions/actionClipboard.tsx | 11 +- .../excalidraw/actions/actionCropEditor.tsx | 10 +- .../actions/actionDeleteSelected.test.tsx | 4 +- .../actions/actionDeleteSelected.tsx | 28 ++- .../excalidraw/actions/actionDistribute.tsx | 10 +- .../actions/actionDuplicateSelection.test.tsx | 8 +- .../actions/actionDuplicateSelection.tsx | 48 ++-- .../excalidraw/actions/actionElementLink.ts | 1 + .../actions/actionElementLock.test.tsx | 9 +- .../excalidraw/actions/actionElementLock.ts | 4 +- packages/excalidraw/actions/actionExport.tsx | 30 +-- .../excalidraw/actions/actionFinalize.tsx | 25 +- .../excalidraw/actions/actionFlip.test.tsx | 10 +- packages/excalidraw/actions/actionFlip.ts | 42 ++-- packages/excalidraw/actions/actionFrame.ts | 26 +- packages/excalidraw/actions/actionGroup.tsx | 52 ++-- packages/excalidraw/actions/actionHistory.tsx | 23 +- .../excalidraw/actions/actionLinearEditor.tsx | 12 +- packages/excalidraw/actions/actionLink.tsx | 3 +- packages/excalidraw/actions/actionMenu.tsx | 7 +- .../excalidraw/actions/actionNavigate.tsx | 9 +- .../actions/actionProperties.test.tsx | 11 +- .../excalidraw/actions/actionProperties.tsx | 60 ++--- .../excalidraw/actions/actionSelectAll.ts | 16 +- packages/excalidraw/actions/actionStyles.ts | 28 ++- .../actions/actionTextAutoResize.ts | 4 +- .../actions/actionToggleGridMode.tsx | 8 +- .../actions/actionToggleObjectsSnapMode.tsx | 1 + .../actions/actionToggleSearchMenu.ts | 10 +- .../excalidraw/actions/actionToggleStats.tsx | 5 +- .../actions/actionToggleViewMode.tsx | 1 + .../actions/actionToggleZenMode.tsx | 1 + packages/excalidraw/actions/actionZindex.tsx | 21 +- packages/excalidraw/actions/manager.tsx | 16 +- packages/excalidraw/actions/shortcuts.ts | 3 +- packages/excalidraw/actions/types.ts | 4 +- packages/excalidraw/align.ts | 11 +- packages/excalidraw/animated-trail.ts | 13 +- packages/excalidraw/appState.ts | 1 + packages/excalidraw/change.ts | 25 +- packages/excalidraw/charts.test.ts | 3 +- packages/excalidraw/charts.ts | 7 +- packages/excalidraw/clients.ts | 1 + packages/excalidraw/clipboard.ts | 21 +- packages/excalidraw/colors.ts | 1 + packages/excalidraw/components/Actions.tsx | 68 ++--- .../components/ActiveConfirmDialog.tsx | 3 +- packages/excalidraw/components/App.tsx | 233 +++++++++--------- packages/excalidraw/components/Avatar.tsx | 9 +- packages/excalidraw/components/Button.tsx | 2 + packages/excalidraw/components/ButtonIcon.tsx | 5 +- .../excalidraw/components/ButtonIconCycle.tsx | 3 +- .../components/ButtonIconSelect.tsx | 4 +- .../excalidraw/components/CheckboxItem.tsx | 3 +- .../components/ColorPicker/ColorInput.tsx | 21 +- .../components/ColorPicker/ColorPicker.tsx | 31 +-- .../ColorPicker/CustomColorList.tsx | 6 +- .../components/ColorPicker/HotkeyLabel.tsx | 1 + .../components/ColorPicker/Picker.tsx | 27 +- .../ColorPicker/PickerColorList.tsx | 9 +- .../components/ColorPicker/ShadeList.tsx | 9 +- .../components/ColorPicker/TopPicks.tsx | 4 +- .../ColorPicker/colorPickerUtils.ts | 5 +- .../ColorPicker/keyboardNavHandlers.ts | 14 +- .../CommandPalette/CommandPalette.tsx | 59 +++-- .../defaultCommandPaletteItems.ts | 1 + .../excalidraw/components/ConfirmDialog.tsx | 13 +- .../excalidraw/components/ContextMenu.tsx | 21 +- .../excalidraw/components/DarkModeToggle.tsx | 8 +- .../components/DefaultSidebar.test.tsx | 2 + .../excalidraw/components/DefaultSidebar.tsx | 16 +- .../DiagramToCodePlugin.tsx | 2 + packages/excalidraw/components/Dialog.tsx | 15 +- .../components/DialogActionButton.tsx | 7 +- .../components/ElementLinkDialog.tsx | 19 +- .../excalidraw/components/ErrorDialog.tsx | 3 +- packages/excalidraw/components/EyeDropper.tsx | 10 +- .../excalidraw/components/FilledButton.tsx | 12 +- .../components/FixedSideContainer.tsx | 4 +- .../components/FollowMode/FollowMode.tsx | 4 +- .../components/FontPicker/FontPicker.tsx | 16 +- .../components/FontPicker/FontPickerList.tsx | 17 +- .../FontPicker/FontPickerTrigger.tsx | 7 +- .../FontPicker/keyboardNavHandlers.ts | 4 +- packages/excalidraw/components/HandButton.tsx | 8 +- packages/excalidraw/components/HelpButton.tsx | 1 + packages/excalidraw/components/HelpDialog.tsx | 20 +- packages/excalidraw/components/HintViewer.tsx | 13 +- packages/excalidraw/components/IconPicker.tsx | 13 +- .../components/ImageExportDialog.tsx | 20 +- .../excalidraw/components/InitializeApp.tsx | 6 +- packages/excalidraw/components/Island.tsx | 4 +- .../components/JSONExportDialog.tsx | 21 +- .../components/LaserPointerButton.tsx | 6 +- packages/excalidraw/components/LayerUI.tsx | 95 +++---- .../excalidraw/components/LibraryMenu.tsx | 47 ++-- .../components/LibraryMenuBrowseButton.tsx | 1 + .../components/LibraryMenuControlButtons.tsx | 6 +- .../components/LibraryMenuHeaderContent.tsx | 37 +-- .../components/LibraryMenuItems.tsx | 29 ++- .../components/LibraryMenuSection.tsx | 9 +- .../excalidraw/components/LibraryUnit.tsx | 10 +- .../excalidraw/components/LoadingMessage.tsx | 9 +- packages/excalidraw/components/LockButton.tsx | 6 +- .../excalidraw/components/MagicButton.tsx | 5 +- packages/excalidraw/components/MobileMenu.tsx | 39 +-- packages/excalidraw/components/Modal.tsx | 12 +- .../OverwriteConfirm/OverwriteConfirm.tsx | 7 +- .../OverwriteConfirmActions.tsx | 7 +- .../OverwriteConfirm/OverwriteConfirmState.ts | 1 + .../components/PasteChartDialog.tsx | 9 +- .../excalidraw/components/PenModeButton.tsx | 6 +- packages/excalidraw/components/Popover.tsx | 6 +- .../excalidraw/components/ProjectName.tsx | 11 +- .../components/PropertiesPopover.tsx | 7 +- .../excalidraw/components/PublishLibrary.tsx | 31 +-- .../excalidraw/components/QuickSearch.tsx | 1 + packages/excalidraw/components/RadioGroup.tsx | 1 + packages/excalidraw/components/Range.tsx | 2 + packages/excalidraw/components/SVGLayer.tsx | 3 +- packages/excalidraw/components/SearchMenu.tsx | 38 +-- packages/excalidraw/components/Section.tsx | 2 + .../components/ShareableLinkDialog.tsx | 4 +- .../components/Sidebar/Sidebar.test.tsx | 4 +- .../excalidraw/components/Sidebar/Sidebar.tsx | 29 ++- .../components/Sidebar/SidebarHeader.tsx | 8 +- .../components/Sidebar/SidebarTab.tsx | 1 + .../components/Sidebar/SidebarTabTrigger.tsx | 1 + .../components/Sidebar/SidebarTabs.tsx | 1 + .../components/Sidebar/SidebarTrigger.tsx | 8 +- .../excalidraw/components/Sidebar/common.ts | 3 +- .../Sidebar/siderbar.test.helpers.tsx | 1 + packages/excalidraw/components/Stack.tsx | 4 +- .../excalidraw/components/Stats/Angle.tsx | 12 +- .../components/Stats/CanvasGrid.tsx | 6 +- .../excalidraw/components/Stats/Dimension.tsx | 21 +- .../excalidraw/components/Stats/DragInput.tsx | 21 +- .../excalidraw/components/Stats/FontSize.tsx | 18 +- .../components/Stats/MultiAngle.tsx | 14 +- .../components/Stats/MultiDimension.tsx | 17 +- .../components/Stats/MultiFontSize.tsx | 14 +- .../components/Stats/MultiPosition.tsx | 19 +- .../excalidraw/components/Stats/Position.tsx | 17 +- .../excalidraw/components/Stats/index.tsx | 55 +++-- .../components/Stats/stats.test.tsx | 32 +-- packages/excalidraw/components/Stats/utils.ts | 15 +- .../TTDDialog/MermaidToExcalidraw.tsx | 28 ++- .../components/TTDDialog/TTDDialog.tsx | 34 +-- .../components/TTDDialog/TTDDialogInput.tsx | 4 +- .../components/TTDDialog/TTDDialogPanel.tsx | 6 +- .../components/TTDDialog/TTDDialogTabs.tsx | 6 +- .../components/TTDDialog/TTDDialogTrigger.tsx | 15 +- .../excalidraw/components/TTDDialog/common.ts | 8 +- packages/excalidraw/components/TextField.tsx | 8 +- packages/excalidraw/components/Toast.tsx | 7 +- packages/excalidraw/components/ToolButton.tsx | 15 +- packages/excalidraw/components/Tooltip.tsx | 4 +- packages/excalidraw/components/Trans.test.tsx | 3 +- packages/excalidraw/components/Trans.tsx | 3 +- packages/excalidraw/components/UserList.tsx | 28 ++- .../components/canvases/InteractiveCanvas.tsx | 20 +- .../components/canvases/NewElementCanvas.tsx | 8 +- .../components/canvases/StaticCanvas.tsx | 16 +- .../dropdownMenu/DropdownMenu.test.tsx | 1 + .../components/dropdownMenu/DropdownMenu.tsx | 11 +- .../dropdownMenu/DropdownMenuContent.tsx | 14 +- .../dropdownMenu/DropdownMenuItem.tsx | 11 +- .../dropdownMenu/DropdownMenuItemContent.tsx | 3 +- .../dropdownMenu/DropdownMenuItemLink.tsx | 6 +- .../dropdownMenu/DropdownMenuTrigger.tsx | 1 + .../components/dropdownMenu/common.ts | 1 + .../excalidraw/components/footer/Footer.tsx | 6 +- .../components/footer/FooterCenter.tsx | 4 +- .../hoc/withInternalFallback.test.tsx | 3 +- .../components/hoc/withInternalFallback.tsx | 1 + .../components/hyperlink/Hyperlink.tsx | 60 ++--- .../components/hyperlink/helpers.ts | 9 +- packages/excalidraw/components/icons.tsx | 7 +- .../LiveCollaborationTrigger.tsx | 10 +- .../components/main-menu/DefaultItems.tsx | 45 ++-- .../components/main-menu/MainMenu.tsx | 18 +- .../welcome-screen/WelcomeScreen.Center.tsx | 9 +- .../welcome-screen/WelcomeScreen.Hints.tsx | 2 +- packages/excalidraw/constants.ts | 5 +- packages/excalidraw/context/tunnels.ts | 2 +- packages/excalidraw/context/ui-appState.ts | 1 + packages/excalidraw/cursor.ts | 6 +- packages/excalidraw/data/blob.ts | 11 +- packages/excalidraw/data/encode.ts | 1 + packages/excalidraw/data/encryption.ts | 1 + packages/excalidraw/data/filesystem.ts | 4 +- packages/excalidraw/data/image.ts | 6 +- packages/excalidraw/data/index.ts | 20 +- packages/excalidraw/data/json.ts | 7 +- packages/excalidraw/data/library.ts | 41 +-- packages/excalidraw/data/reconcile.ts | 6 +- packages/excalidraw/data/resave.ts | 6 +- packages/excalidraw/data/restore.ts | 104 ++++---- packages/excalidraw/data/transform.test.ts | 10 +- packages/excalidraw/data/transform.ts | 34 +-- packages/excalidraw/data/types.ts | 4 +- packages/excalidraw/data/url.ts | 1 + packages/excalidraw/distribute.ts | 3 +- .../element/ElementCanvasButtons.tsx | 11 +- packages/excalidraw/element/binding.ts | 123 ++++----- packages/excalidraw/element/bounds.test.ts | 6 +- packages/excalidraw/element/bounds.ts | 70 +++--- packages/excalidraw/element/collision.ts | 60 ++--- packages/excalidraw/element/cropElement.ts | 12 +- packages/excalidraw/element/distance.ts | 13 +- packages/excalidraw/element/dragElements.ts | 28 ++- .../excalidraw/element/elbowArrow.test.tsx | 18 +- packages/excalidraw/element/elbowArrow.ts | 11 +- packages/excalidraw/element/elementLink.ts | 1 + packages/excalidraw/element/embeddable.ts | 10 +- .../excalidraw/element/flowchart.test.tsx | 8 +- packages/excalidraw/element/flowchart.ts | 31 +-- packages/excalidraw/element/heading.ts | 17 +- packages/excalidraw/element/image.ts | 4 +- packages/excalidraw/element/index.ts | 5 +- .../excalidraw/element/linearElementEditor.ts | 90 +++---- packages/excalidraw/element/mutateElement.ts | 15 +- .../excalidraw/element/newElement.test.ts | 18 +- packages/excalidraw/element/newElement.ts | 54 ++-- packages/excalidraw/element/resizeElements.ts | 110 +++++---- packages/excalidraw/element/resizeTest.ts | 51 ++-- .../element/showSelectedShapeActions.ts | 3 +- .../excalidraw/element/sizeHelpers.test.ts | 4 +- packages/excalidraw/element/sizeHelpers.ts | 10 +- .../excalidraw/element/sortElements.test.ts | 2 + packages/excalidraw/element/sortElements.ts | 1 + .../excalidraw/element/textElement.test.ts | 2 + packages/excalidraw/element/textElement.ts | 41 +-- .../excalidraw/element/textMeasurements.ts | 1 + .../excalidraw/element/textWrapping.test.ts | 1 + packages/excalidraw/element/textWrapping.ts | 2 + .../excalidraw/element/textWysiwyg.test.tsx | 18 +- packages/excalidraw/element/textWysiwyg.tsx | 62 ++--- .../excalidraw/element/transformHandles.ts | 33 +-- .../excalidraw/element/typeChecks.test.ts | 1 + packages/excalidraw/element/typeChecks.ts | 3 +- packages/excalidraw/element/types.ts | 1 + packages/excalidraw/element/utils.ts | 8 +- packages/excalidraw/fonts/Cascadia/index.ts | 4 +- .../excalidraw/fonts/ComicShanns/index.ts | 10 +- packages/excalidraw/fonts/Emoji/index.ts | 1 - .../excalidraw/fonts/ExcalidrawFontFace.ts | 5 +- packages/excalidraw/fonts/Excalifont/index.ts | 16 +- packages/excalidraw/fonts/FontMetadata.ts | 3 +- packages/excalidraw/fonts/Fonts.ts | 6 +- packages/excalidraw/fonts/Helvetica/index.ts | 1 - packages/excalidraw/fonts/Liberation/index.ts | 4 +- packages/excalidraw/fonts/Lilita/index.ts | 7 +- packages/excalidraw/fonts/Nunito/index.ts | 13 +- packages/excalidraw/fonts/Virgil/index.ts | 4 +- packages/excalidraw/fonts/Xiaolai/index.ts | 40 +-- packages/excalidraw/fractionalIndex.ts | 10 +- packages/excalidraw/frame.test.tsx | 6 +- packages/excalidraw/frame.ts | 34 +-- packages/excalidraw/groups.ts | 7 +- packages/excalidraw/history.ts | 3 +- .../hooks/useCreatePortalContainer.ts | 1 + packages/excalidraw/hooks/useEmitter.ts | 1 + .../excalidraw/hooks/useLibraryItemSvg.ts | 4 +- packages/excalidraw/hooks/useOutsideClick.ts | 1 + .../excalidraw/hooks/useScrollPosition.ts | 5 +- packages/excalidraw/i18n.ts | 3 +- packages/excalidraw/index-node.ts | 5 +- packages/excalidraw/index.tsx | 19 +- packages/excalidraw/keys.ts | 1 + packages/excalidraw/laser-trails.ts | 10 +- packages/excalidraw/queue.ts | 3 +- packages/excalidraw/random.ts | 3 +- packages/excalidraw/reactUtils.ts | 3 +- packages/excalidraw/renderer/helpers.ts | 5 +- .../excalidraw/renderer/interactiveScene.ts | 100 ++++---- packages/excalidraw/renderer/renderElement.ts | 82 +++--- .../renderer/renderNewElementScene.ts | 4 +- packages/excalidraw/renderer/renderSnaps.ts | 2 + packages/excalidraw/renderer/staticScene.ts | 35 +-- .../excalidraw/renderer/staticSvgScene.ts | 22 +- packages/excalidraw/scene/Renderer.ts | 12 +- packages/excalidraw/scene/Scene.ts | 31 +-- packages/excalidraw/scene/Shape.ts | 39 +-- packages/excalidraw/scene/ShapeCache.ts | 13 +- packages/excalidraw/scene/export.ts | 44 ++-- packages/excalidraw/scene/normalize.ts | 2 + packages/excalidraw/scene/scroll.ts | 6 +- packages/excalidraw/scene/scrollbars.ts | 7 +- packages/excalidraw/scene/selection.ts | 17 +- packages/excalidraw/scene/types.ts | 6 +- packages/excalidraw/shapes.tsx | 10 +- packages/excalidraw/snapping.ts | 19 +- packages/excalidraw/store.ts | 5 +- .../subset/harfbuzz/harfbuzz-loader.ts | 2 +- packages/excalidraw/subset/subset-main.ts | 4 +- .../excalidraw/subset/subset-shared.chunk.ts | 2 +- .../excalidraw/subset/woff2/woff2-loader.ts | 2 +- packages/excalidraw/tests/App.test.tsx | 8 +- .../tests/MermaidToExcalidraw.test.tsx | 8 +- .../excalidraw/tests/actionStyles.test.tsx | 3 +- packages/excalidraw/tests/align.test.tsx | 14 +- packages/excalidraw/tests/appState.test.tsx | 9 +- packages/excalidraw/tests/binding.test.tsx | 20 +- packages/excalidraw/tests/clipboard.test.tsx | 23 +- .../excalidraw/tests/contextmenu.test.tsx | 21 +- .../excalidraw/tests/cropElement.test.tsx | 19 +- .../excalidraw/tests/data/reconcile.test.ts | 9 +- .../excalidraw/tests/data/restore.test.ts | 18 +- packages/excalidraw/tests/dragCreate.test.tsx | 11 +- .../excalidraw/tests/elementLocking.test.tsx | 11 +- packages/excalidraw/tests/excalidraw.test.tsx | 10 +- packages/excalidraw/tests/export.test.tsx | 15 +- .../excalidraw/tests/fitToContent.test.tsx | 7 +- .../tests/fixtures/diagramFixture.ts | 1 + .../tests/fixtures/elementFixture.ts | 2 + packages/excalidraw/tests/flip.test.tsx | 32 +-- .../excalidraw/tests/fractionalIndex.test.ts | 11 +- packages/excalidraw/tests/helpers/api.ts | 55 +++-- packages/excalidraw/tests/helpers/mocks.ts | 5 +- packages/excalidraw/tests/helpers/ui.ts | 56 +++-- packages/excalidraw/tests/history.test.tsx | 69 +++--- packages/excalidraw/tests/library.test.tsx | 18 +- .../tests/linearElementEditor.test.tsx | 47 ++-- packages/excalidraw/tests/move.test.tsx | 19 +- .../tests/multiPointCreate.test.tsx | 15 +- .../excalidraw/tests/packages/events.test.tsx | 8 +- .../excalidraw/tests/queries/toolQueries.ts | 4 +- .../excalidraw/tests/regressionTests.test.tsx | 11 +- packages/excalidraw/tests/resize.test.tsx | 30 ++- packages/excalidraw/tests/rotate.test.tsx | 10 +- .../excalidraw/tests/scene/export.test.ts | 18 +- packages/excalidraw/tests/scroll.test.tsx | 10 +- packages/excalidraw/tests/search.test.tsx | 11 +- packages/excalidraw/tests/selection.test.tsx | 20 +- packages/excalidraw/tests/shortcuts.test.tsx | 4 +- packages/excalidraw/tests/test-utils.ts | 20 +- packages/excalidraw/tests/tool.test.tsx | 7 +- packages/excalidraw/tests/viewMode.test.tsx | 8 +- packages/excalidraw/tests/zindex.test.tsx | 13 +- packages/excalidraw/types.ts | 38 +-- packages/excalidraw/utils.ts | 6 +- packages/excalidraw/visualdebug.ts | 7 +- packages/excalidraw/zindex.ts | 11 +- packages/math/angle.ts | 3 +- packages/math/curve.test.ts | 1 + packages/math/curve.ts | 3 +- packages/math/ellipse.test.ts | 1 + packages/math/ellipse.ts | 15 +- packages/math/line.ts | 1 + packages/math/point.test.ts | 1 + packages/math/point.ts | 5 +- packages/math/polygon.ts | 3 +- packages/math/range.ts | 1 + packages/math/rectangle.ts | 1 + packages/math/segment.ts | 3 +- packages/utils/bbox.ts | 1 + packages/utils/collision.test.ts | 5 +- packages/utils/collision.ts | 17 +- packages/utils/export.test.ts | 6 +- packages/utils/export.ts | 29 +-- packages/utils/geometry/geometry.test.ts | 14 +- packages/utils/geometry/shape.ts | 10 +- packages/utils/utils.unmocked.test.ts | 10 +- packages/utils/withinBounds.test.ts | 4 +- packages/utils/withinBounds.ts | 19 +- scripts/autorelease.js | 3 +- scripts/buildMath.js | 1 + scripts/buildPackage.js | 2 + scripts/buildUtils.js | 2 + scripts/prerelease.js | 1 + scripts/woff2/woff2-esbuild-plugins.js | 7 +- setupTests.ts | 6 +- yarn.lock | 2 +- 421 files changed, 3532 insertions(+), 2763 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 095543a85..8263b08a9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,21 @@ { "extends": ["@excalidraw/eslint-config", "react-app"], "rules": { + "import/order": [ + "warn", + { + "groups": ["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"], + "pathGroups": [ + { + "pattern": "@excalidraw/**", + "group": "external", + "position": "after" + } + ], + "newlines-between": "always-and-inside-groups", + "warnOnUnassignedImports": true + } + ], "import/no-anonymous-default-export": "off", "no-restricted-globals": "off", "@typescript-eslint/consistent-type-imports": [ diff --git a/dev-docs/src/components/Homepage/index.js b/dev-docs/src/components/Homepage/index.js index e350e03a2..3c1c25f4f 100644 --- a/dev-docs/src/components/Homepage/index.js +++ b/dev-docs/src/components/Homepage/index.js @@ -1,5 +1,6 @@ -import React from "react"; import clsx from "clsx"; +import React from "react"; + import styles from "./styles.module.css"; const FeatureList = [ diff --git a/dev-docs/src/components/Homepage/index.tsx b/dev-docs/src/components/Homepage/index.tsx index ee86e9195..613eaa556 100644 --- a/dev-docs/src/components/Homepage/index.tsx +++ b/dev-docs/src/components/Homepage/index.tsx @@ -1,5 +1,6 @@ -import React from "react"; import clsx from "clsx"; +import React from "react"; + import styles from "./styles.module.css"; type FeatureItem = { diff --git a/dev-docs/src/pages/index.tsx b/dev-docs/src/pages/index.tsx index f35c2e4a7..39b66bb79 100644 --- a/dev-docs/src/pages/index.tsx +++ b/dev-docs/src/pages/index.tsx @@ -1,10 +1,11 @@ -import React from "react"; -import clsx from "clsx"; -import Layout from "@theme/Layout"; import Link from "@docusaurus/Link"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; -import styles from "./index.module.css"; import HomepageFeatures from "@site/src/components/Homepage"; +import Layout from "@theme/Layout"; +import clsx from "clsx"; +import React from "react"; + +import styles from "./index.module.css"; function HomepageHeader() { const { siteConfig } = useDocusaurusContext(); diff --git a/dev-docs/src/theme/MDXComponents.js b/dev-docs/src/theme/MDXComponents.js index 35234301f..97ff4d054 100644 --- a/dev-docs/src/theme/MDXComponents.js +++ b/dev-docs/src/theme/MDXComponents.js @@ -1,6 +1,6 @@ // Import the original mapper -import MDXComponents from "@theme-original/MDXComponents"; import Highlight from "@site/src/components/Highlight"; +import MDXComponents from "@theme-original/MDXComponents"; export default { // Re-use the default mapping diff --git a/examples/with-nextjs/src/app/page.tsx b/examples/with-nextjs/src/app/page.tsx index 191aca120..e23255237 100644 --- a/examples/with-nextjs/src/app/page.tsx +++ b/examples/with-nextjs/src/app/page.tsx @@ -1,5 +1,6 @@ import dynamic from "next/dynamic"; import Script from "next/script"; + import "../common.scss"; // Since client components get prerenderd on server as well hence importing the excalidraw stuff dynamically diff --git a/examples/with-nextjs/src/excalidrawWrapper.tsx b/examples/with-nextjs/src/excalidrawWrapper.tsx index b4c45fa7d..fab4df651 100644 --- a/examples/with-nextjs/src/excalidrawWrapper.tsx +++ b/examples/with-nextjs/src/excalidrawWrapper.tsx @@ -1,10 +1,11 @@ "use client"; import * as excalidrawLib from "@excalidraw/excalidraw"; import { Excalidraw } from "@excalidraw/excalidraw"; -import App from "../../with-script-in-browser/components/ExampleApp"; import "@excalidraw/excalidraw/index.css"; +import App from "../../with-script-in-browser/components/ExampleApp"; + const ExcalidrawWrapper: React.FC = () => { return ( <> diff --git a/examples/with-nextjs/src/pages/excalidraw-in-pages.tsx b/examples/with-nextjs/src/pages/excalidraw-in-pages.tsx index 527a346b9..116bb96ce 100644 --- a/examples/with-nextjs/src/pages/excalidraw-in-pages.tsx +++ b/examples/with-nextjs/src/pages/excalidraw-in-pages.tsx @@ -1,4 +1,5 @@ import dynamic from "next/dynamic"; + import "../common.scss"; // Since client components get prerenderd on server as well hence importing the excalidraw stuff dynamically diff --git a/examples/with-script-in-browser/components/CustomFooter.tsx b/examples/with-script-in-browser/components/CustomFooter.tsx index 72fd199f6..a9c902ade 100644 --- a/examples/with-script-in-browser/components/CustomFooter.tsx +++ b/examples/with-script-in-browser/components/CustomFooter.tsx @@ -1,4 +1,5 @@ import React from "react"; + import type * as TExcalidraw from "@excalidraw/excalidraw"; import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types"; diff --git a/examples/with-script-in-browser/components/ExampleApp.tsx b/examples/with-script-in-browser/components/ExampleApp.tsx index 08c8032d5..976004f04 100644 --- a/examples/with-script-in-browser/components/ExampleApp.tsx +++ b/examples/with-script-in-browser/components/ExampleApp.tsx @@ -1,3 +1,4 @@ +import { nanoid } from "nanoid"; import React, { useEffect, useState, @@ -6,13 +7,24 @@ import React, { Children, cloneElement, } from "react"; -import ExampleSidebar from "./sidebar/ExampleSidebar"; import type * as TExcalidraw from "@excalidraw/excalidraw"; +import type { ImportedLibraryData } from "@excalidraw/excalidraw/data/types"; +import type { + NonDeletedExcalidrawElement, + Theme, +} from "@excalidraw/excalidraw/element/types"; +import type { + AppState, + BinaryFileData, + ExcalidrawImperativeAPI, + ExcalidrawInitialDataState, + Gesture, + LibraryItems, + PointerDownState as ExcalidrawPointerDownState, +} from "@excalidraw/excalidraw/types"; -import { nanoid } from "nanoid"; - -import type { ResolvablePromise } from "../utils"; +import initialData from "../initialData"; import { resolvablePromise, distance2d, @@ -23,25 +35,12 @@ import { import CustomFooter from "./CustomFooter"; import MobileFooter from "./MobileFooter"; -import initialData from "../initialData"; - -import type { - AppState, - BinaryFileData, - ExcalidrawImperativeAPI, - ExcalidrawInitialDataState, - Gesture, - LibraryItems, - PointerDownState as ExcalidrawPointerDownState, -} from "@excalidraw/excalidraw/types"; -import type { - NonDeletedExcalidrawElement, - Theme, -} from "@excalidraw/excalidraw/element/types"; -import type { ImportedLibraryData } from "@excalidraw/excalidraw/data/types"; +import ExampleSidebar from "./sidebar/ExampleSidebar"; import "./ExampleApp.scss"; +import type { ResolvablePromise } from "../utils"; + type Comment = { x: number; y: number; diff --git a/examples/with-script-in-browser/components/MobileFooter.tsx b/examples/with-script-in-browser/components/MobileFooter.tsx index c8fc0f113..a6e1fa1b2 100644 --- a/examples/with-script-in-browser/components/MobileFooter.tsx +++ b/examples/with-script-in-browser/components/MobileFooter.tsx @@ -1,7 +1,9 @@ import React from "react"; -import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types"; -import CustomFooter from "./CustomFooter"; + import type * as TExcalidraw from "@excalidraw/excalidraw"; +import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types"; + +import CustomFooter from "./CustomFooter"; const MobileFooter = ({ excalidrawAPI, diff --git a/examples/with-script-in-browser/components/sidebar/ExampleSidebar.tsx b/examples/with-script-in-browser/components/sidebar/ExampleSidebar.tsx index 193913410..88950b34b 100644 --- a/examples/with-script-in-browser/components/sidebar/ExampleSidebar.tsx +++ b/examples/with-script-in-browser/components/sidebar/ExampleSidebar.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; + import "./ExampleSidebar.scss"; export default function Sidebar({ children }: { children: React.ReactNode }) { diff --git a/examples/with-script-in-browser/index.tsx b/examples/with-script-in-browser/index.tsx index 25ad96e37..8f92e7cc5 100644 --- a/examples/with-script-in-browser/index.tsx +++ b/examples/with-script-in-browser/index.tsx @@ -1,10 +1,11 @@ -import App from "./components/ExampleApp"; import React, { StrictMode } from "react"; import { createRoot } from "react-dom/client"; +import "@excalidraw/excalidraw/index.css"; + import type * as TExcalidraw from "@excalidraw/excalidraw"; -import "@excalidraw/excalidraw/index.css"; +import App from "./components/ExampleApp"; declare global { interface Window { diff --git a/examples/with-script-in-browser/utils.ts b/examples/with-script-in-browser/utils.ts index a77b93fb0..285e9652d 100644 --- a/examples/with-script-in-browser/utils.ts +++ b/examples/with-script-in-browser/utils.ts @@ -1,6 +1,6 @@ -import { unstable_batchedUpdates } from "react-dom"; -import { fileOpen as _fileOpen } from "browser-fs-access"; import { MIME_TYPES } from "@excalidraw/excalidraw"; +import { fileOpen as _fileOpen } from "browser-fs-access"; +import { unstable_batchedUpdates } from "react-dom"; type FILE_EXTENSION = Exclude; diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx index 2cb6e7715..dc399a7d0 100644 --- a/excalidraw-app/App.tsx +++ b/excalidraw-app/App.tsx @@ -1,24 +1,3 @@ -import polyfill from "@excalidraw/excalidraw/polyfill"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { trackEvent } from "@excalidraw/excalidraw/analytics"; -import { getDefaultAppState } from "@excalidraw/excalidraw/appState"; -import { ErrorDialog } from "@excalidraw/excalidraw/components/ErrorDialog"; -import { TopErrorBoundary } from "./components/TopErrorBoundary"; -import { - APP_NAME, - EVENT, - THEME, - TITLE_TIMEOUT, - VERSION_TIMEOUT, -} from "@excalidraw/excalidraw/constants"; -import { loadFromBlob } from "@excalidraw/excalidraw/data/blob"; -import type { - FileId, - NonDeletedExcalidrawElement, - OrderedExcalidrawElement, -} from "@excalidraw/excalidraw/element/types"; -import { useCallbackRefState } from "@excalidraw/excalidraw/hooks/useCallbackRefState"; -import { t } from "@excalidraw/excalidraw/i18n"; import { Excalidraw, LiveCollaborationTrigger, @@ -26,14 +5,29 @@ import { CaptureUpdateAction, reconcileElements, } from "@excalidraw/excalidraw"; -import type { - AppState, - ExcalidrawImperativeAPI, - BinaryFiles, - ExcalidrawInitialDataState, - UIAppState, -} from "@excalidraw/excalidraw/types"; -import type { ResolvablePromise } from "@excalidraw/excalidraw/utils"; +import { trackEvent } from "@excalidraw/excalidraw/analytics"; +import { getDefaultAppState } from "@excalidraw/excalidraw/appState"; +import { + CommandPalette, + DEFAULT_CATEGORIES, +} from "@excalidraw/excalidraw/components/CommandPalette/CommandPalette"; +import { ErrorDialog } from "@excalidraw/excalidraw/components/ErrorDialog"; +import { OverwriteConfirmDialog } from "@excalidraw/excalidraw/components/OverwriteConfirm/OverwriteConfirm"; +import { openConfirmModal } from "@excalidraw/excalidraw/components/OverwriteConfirm/OverwriteConfirmState"; +import { ShareableLinkDialog } from "@excalidraw/excalidraw/components/ShareableLinkDialog"; +import Trans from "@excalidraw/excalidraw/components/Trans"; +import { + APP_NAME, + EVENT, + THEME, + TITLE_TIMEOUT, + VERSION_TIMEOUT, +} from "@excalidraw/excalidraw/constants"; +import polyfill from "@excalidraw/excalidraw/polyfill"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { loadFromBlob } from "@excalidraw/excalidraw/data/blob"; +import { useCallbackRefState } from "@excalidraw/excalidraw/hooks/useCallbackRefState"; +import { t } from "@excalidraw/excalidraw/i18n"; import { debounce, getVersion, @@ -43,74 +37,6 @@ import { resolvablePromise, isRunningInIframe, } from "@excalidraw/excalidraw/utils"; -import { - FIREBASE_STORAGE_PREFIXES, - isExcalidrawPlusSignedUser, - STORAGE_KEYS, - SYNC_BROWSER_TABS_TIMEOUT, -} from "./app_constants"; -import type { CollabAPI } from "./collab/Collab"; -import Collab, { - collabAPIAtom, - isCollaboratingAtom, - isOfflineAtom, -} from "./collab/Collab"; -import { - exportToBackend, - getCollaborationLinkData, - isCollaborationLink, - loadScene, -} from "./data"; -import { - importFromLocalStorage, - importUsernameFromLocalStorage, -} from "./data/localStorage"; -import CustomStats from "./CustomStats"; -import type { RestoredDataState } from "@excalidraw/excalidraw/data/restore"; -import { restore, restoreAppState } from "@excalidraw/excalidraw/data/restore"; -import { - ExportToExcalidrawPlus, - exportToExcalidrawPlus, -} from "./components/ExportToExcalidrawPlus"; -import { updateStaleImageStatuses } from "./data/FileManager"; -import { newElementWith } from "@excalidraw/excalidraw/element/mutateElement"; -import { isInitializedImageElement } from "@excalidraw/excalidraw/element/typeChecks"; -import { loadFilesFromFirebase } from "./data/firebase"; -import { - LibraryIndexedDBAdapter, - LibraryLocalStorageMigrationAdapter, - LocalData, -} from "./data/LocalData"; -import { isBrowserStorageStateNewer } from "./data/tabSync"; -import clsx from "clsx"; -import { - parseLibraryTokensFromUrl, - useHandleLibrary, -} from "@excalidraw/excalidraw/data/library"; -import { AppMainMenu } from "./components/AppMainMenu"; -import { AppWelcomeScreen } from "./components/AppWelcomeScreen"; -import { AppFooter } from "./components/AppFooter"; -import { - Provider, - useAtom, - useAtomValue, - useAtomWithInitialValue, - appJotaiStore, -} from "./app-jotai"; - -import "./index.scss"; -import type { ResolutionType } from "@excalidraw/excalidraw/utility-types"; -import { ShareableLinkDialog } from "@excalidraw/excalidraw/components/ShareableLinkDialog"; -import { openConfirmModal } from "@excalidraw/excalidraw/components/OverwriteConfirm/OverwriteConfirmState"; -import { OverwriteConfirmDialog } from "@excalidraw/excalidraw/components/OverwriteConfirm/OverwriteConfirm"; -import Trans from "@excalidraw/excalidraw/components/Trans"; -import { ShareDialog, shareDialogStateAtom } from "./share/ShareDialog"; -import CollabError, { collabErrorIndicatorAtom } from "./collab/CollabError"; -import type { RemoteExcalidrawElement } from "@excalidraw/excalidraw/data/reconcile"; -import { - CommandPalette, - DEFAULT_CATEGORIES, -} from "@excalidraw/excalidraw/components/CommandPalette/CommandPalette"; import { GithubIcon, XBrandIcon, @@ -121,6 +47,83 @@ import { share, youtubeIcon, } from "@excalidraw/excalidraw/components/icons"; +import { isElementLink } from "@excalidraw/excalidraw/element/elementLink"; +import { restore, restoreAppState } from "@excalidraw/excalidraw/data/restore"; +import { newElementWith } from "@excalidraw/excalidraw/element/mutateElement"; +import { isInitializedImageElement } from "@excalidraw/excalidraw/element/typeChecks"; +import clsx from "clsx"; +import { + parseLibraryTokensFromUrl, + useHandleLibrary, +} from "@excalidraw/excalidraw/data/library"; + +import type { RemoteExcalidrawElement } from "@excalidraw/excalidraw/data/reconcile"; +import type { RestoredDataState } from "@excalidraw/excalidraw/data/restore"; +import type { + FileId, + NonDeletedExcalidrawElement, + OrderedExcalidrawElement, +} from "@excalidraw/excalidraw/element/types"; +import type { + AppState, + ExcalidrawImperativeAPI, + BinaryFiles, + ExcalidrawInitialDataState, + UIAppState, +} from "@excalidraw/excalidraw/types"; +import type { ResolutionType } from "@excalidraw/excalidraw/utility-types"; +import type { ResolvablePromise } from "@excalidraw/excalidraw/utils"; + +import CustomStats from "./CustomStats"; +import { + Provider, + useAtom, + useAtomValue, + useAtomWithInitialValue, + appJotaiStore, +} from "./app-jotai"; +import { + FIREBASE_STORAGE_PREFIXES, + isExcalidrawPlusSignedUser, + STORAGE_KEYS, + SYNC_BROWSER_TABS_TIMEOUT, +} from "./app_constants"; +import Collab, { + collabAPIAtom, + isCollaboratingAtom, + isOfflineAtom, +} from "./collab/Collab"; +import { AppFooter } from "./components/AppFooter"; +import { AppMainMenu } from "./components/AppMainMenu"; +import { AppWelcomeScreen } from "./components/AppWelcomeScreen"; +import { + ExportToExcalidrawPlus, + exportToExcalidrawPlus, +} from "./components/ExportToExcalidrawPlus"; +import { TopErrorBoundary } from "./components/TopErrorBoundary"; + +import { + exportToBackend, + getCollaborationLinkData, + isCollaborationLink, + loadScene, +} from "./data"; + +import { updateStaleImageStatuses } from "./data/FileManager"; +import { + importFromLocalStorage, + importUsernameFromLocalStorage, +} from "./data/localStorage"; + +import { loadFilesFromFirebase } from "./data/firebase"; +import { + LibraryIndexedDBAdapter, + LibraryLocalStorageMigrationAdapter, + LocalData, +} from "./data/LocalData"; +import { isBrowserStorageStateNewer } from "./data/tabSync"; +import { ShareDialog, shareDialogStateAtom } from "./share/ShareDialog"; +import CollabError, { collabErrorIndicatorAtom } from "./collab/CollabError"; import { useHandleAppTheme } from "./useHandleAppTheme"; import { getPreferredLanguage } from "./app-language/language-detector"; import { useAppLangCode } from "./app-language/language-state"; @@ -131,7 +134,10 @@ import DebugCanvas, { } from "./components/DebugCanvas"; import { AIComponents } from "./components/AI"; import { ExcalidrawPlusIframeExport } from "./ExcalidrawPlusIframeExport"; -import { isElementLink } from "@excalidraw/excalidraw/element/elementLink"; + +import "./index.scss"; + +import type { CollabAPI } from "./collab/Collab"; polyfill(); diff --git a/excalidraw-app/CustomStats.tsx b/excalidraw-app/CustomStats.tsx index 96ca55d67..c57f297fe 100644 --- a/excalidraw-app/CustomStats.tsx +++ b/excalidraw-app/CustomStats.tsx @@ -1,15 +1,17 @@ -import { useEffect, useState } from "react"; +import { Stats } from "@excalidraw/excalidraw"; +import { copyTextToSystemClipboard } from "@excalidraw/excalidraw/clipboard"; +import { DEFAULT_VERSION } from "@excalidraw/excalidraw/constants"; +import { t } from "@excalidraw/excalidraw/i18n"; import { debounce, getVersion, nFormatter } from "@excalidraw/excalidraw/utils"; +import { useEffect, useState } from "react"; + +import type { NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types"; +import type { UIAppState } from "@excalidraw/excalidraw/types"; + import { getElementsStorageSize, getTotalStorageSize, } from "./data/localStorage"; -import { DEFAULT_VERSION } from "@excalidraw/excalidraw/constants"; -import { t } from "@excalidraw/excalidraw/i18n"; -import { copyTextToSystemClipboard } from "@excalidraw/excalidraw/clipboard"; -import type { NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types"; -import type { UIAppState } from "@excalidraw/excalidraw/types"; -import { Stats } from "@excalidraw/excalidraw"; type StorageSizes = { scene: number; total: number }; diff --git a/excalidraw-app/ExcalidrawPlusIframeExport.tsx b/excalidraw-app/ExcalidrawPlusIframeExport.tsx index 1f9cd631c..be046f795 100644 --- a/excalidraw-app/ExcalidrawPlusIframeExport.tsx +++ b/excalidraw-app/ExcalidrawPlusIframeExport.tsx @@ -1,13 +1,15 @@ +import { base64urlToString } from "@excalidraw/excalidraw/data/encode"; +import { ExcalidrawError } from "@excalidraw/excalidraw/errors"; import { useLayoutEffect, useRef } from "react"; -import { STORAGE_KEYS } from "./app_constants"; -import { LocalData } from "./data/LocalData"; + import type { FileId, OrderedExcalidrawElement, } from "@excalidraw/excalidraw/element/types"; import type { AppState, BinaryFileData } from "@excalidraw/excalidraw/types"; -import { ExcalidrawError } from "@excalidraw/excalidraw/errors"; -import { base64urlToString } from "@excalidraw/excalidraw/data/encode"; + +import { STORAGE_KEYS } from "./app_constants"; +import { LocalData } from "./data/LocalData"; const EVENT_REQUEST_SCENE = "REQUEST_SCENE"; diff --git a/excalidraw-app/app-language/LanguageList.tsx b/excalidraw-app/app-language/LanguageList.tsx index 001d4ea17..b4682f353 100644 --- a/excalidraw-app/app-language/LanguageList.tsx +++ b/excalidraw-app/app-language/LanguageList.tsx @@ -1,6 +1,8 @@ -import React from "react"; import { useI18n, languages } from "@excalidraw/excalidraw/i18n"; +import React from "react"; + import { useSetAtom } from "../app-jotai"; + import { appLangCodeAtom } from "./language-state"; export const LanguageList = ({ style }: { style?: React.CSSProperties }) => { diff --git a/excalidraw-app/app-language/language-detector.ts b/excalidraw-app/app-language/language-detector.ts index 52da4908f..535189d0a 100644 --- a/excalidraw-app/app-language/language-detector.ts +++ b/excalidraw-app/app-language/language-detector.ts @@ -1,5 +1,5 @@ -import LanguageDetector from "i18next-browser-languagedetector"; import { defaultLang, languages } from "@excalidraw/excalidraw"; +import LanguageDetector from "i18next-browser-languagedetector"; export const languageDetector = new LanguageDetector(); diff --git a/excalidraw-app/app-language/language-state.ts b/excalidraw-app/app-language/language-state.ts index f491c2215..d9ec90322 100644 --- a/excalidraw-app/app-language/language-state.ts +++ b/excalidraw-app/app-language/language-state.ts @@ -1,5 +1,7 @@ import { useEffect } from "react"; + import { atom, useAtom } from "../app-jotai"; + import { getPreferredLanguage, languageDetector } from "./language-detector"; export const appLangCodeAtom = atom(getPreferredLanguage()); diff --git a/excalidraw-app/collab/Collab.tsx b/excalidraw-app/collab/Collab.tsx index 9a16e0569..0964a7365 100644 --- a/excalidraw-app/collab/Collab.tsx +++ b/excalidraw-app/collab/Collab.tsx @@ -1,21 +1,3 @@ -import throttle from "lodash.throttle"; -import { PureComponent } from "react"; -import type { - BinaryFileData, - ExcalidrawImperativeAPI, - SocketId, - Collaborator, - Gesture, -} from "@excalidraw/excalidraw/types"; -import { ErrorDialog } from "@excalidraw/excalidraw/components/ErrorDialog"; -import { APP_NAME, ENV, EVENT } from "@excalidraw/excalidraw/constants"; -import type { ImportedDataState } from "@excalidraw/excalidraw/data/types"; -import type { - ExcalidrawElement, - FileId, - InitializedExcalidrawImageElement, - OrderedExcalidrawElement, -} from "@excalidraw/excalidraw/element/types"; import { CaptureUpdateAction, getSceneVersion, @@ -23,12 +5,53 @@ import { zoomToFitBounds, reconcileElements, } from "@excalidraw/excalidraw"; +import { ErrorDialog } from "@excalidraw/excalidraw/components/ErrorDialog"; +import { APP_NAME, ENV, EVENT } from "@excalidraw/excalidraw/constants"; +import { + IDLE_THRESHOLD, + ACTIVE_THRESHOLD, + UserIdleState, +} from "@excalidraw/excalidraw/constants"; +import { decryptData } from "@excalidraw/excalidraw/data/encryption"; +import { getVisibleSceneBounds } from "@excalidraw/excalidraw/element/bounds"; +import { newElementWith } from "@excalidraw/excalidraw/element/mutateElement"; +import { + isImageElement, + isInitializedImageElement, +} from "@excalidraw/excalidraw/element/typeChecks"; +import { AbortError } from "@excalidraw/excalidraw/errors"; +import { t } from "@excalidraw/excalidraw/i18n"; +import { withBatchedUpdates } from "@excalidraw/excalidraw/reactUtils"; import { assertNever, preventUnload, resolvablePromise, throttleRAF, } from "@excalidraw/excalidraw/utils"; +import throttle from "lodash.throttle"; +import { PureComponent } from "react"; + +import type { + ReconciledExcalidrawElement, + RemoteExcalidrawElement, +} from "@excalidraw/excalidraw/data/reconcile"; +import type { ImportedDataState } from "@excalidraw/excalidraw/data/types"; +import type { + ExcalidrawElement, + FileId, + InitializedExcalidrawImageElement, + OrderedExcalidrawElement, +} from "@excalidraw/excalidraw/element/types"; +import type { + BinaryFileData, + ExcalidrawImperativeAPI, + SocketId, + Collaborator, + Gesture, +} from "@excalidraw/excalidraw/types"; +import type { Mutable, ValueOf } from "@excalidraw/excalidraw/utility-types"; + +import { appJotaiStore, atom } from "../app-jotai"; import { CURSOR_SYNC_TIMEOUT, FILE_UPLOAD_MAX_BYTES, @@ -39,15 +62,17 @@ import { SYNC_FULL_SCENE_INTERVAL_MS, WS_EVENTS, } from "../app_constants"; -import type { - SocketUpdateDataSource, - SyncableExcalidrawElement, -} from "../data"; import { generateCollaborationLinkData, getCollaborationLink, getSyncableElements, } from "../data"; +import { + encodeFilesForUpload, + FileManager, + updateStaleImageStatuses, +} from "../data/FileManager"; +import { LocalData } from "../data/LocalData"; import { isSavedToFirebase, loadFilesFromFirebase, @@ -59,36 +84,15 @@ import { importUsernameFromLocalStorage, saveUsernameToLocalStorage, } from "../data/localStorage"; -import Portal from "./Portal"; -import { t } from "@excalidraw/excalidraw/i18n"; -import { - IDLE_THRESHOLD, - ACTIVE_THRESHOLD, - UserIdleState, -} from "@excalidraw/excalidraw/constants"; -import { - encodeFilesForUpload, - FileManager, - updateStaleImageStatuses, -} from "../data/FileManager"; -import { AbortError } from "@excalidraw/excalidraw/errors"; -import { - isImageElement, - isInitializedImageElement, -} from "@excalidraw/excalidraw/element/typeChecks"; -import { newElementWith } from "@excalidraw/excalidraw/element/mutateElement"; -import { decryptData } from "@excalidraw/excalidraw/data/encryption"; import { resetBrowserStateVersions } from "../data/tabSync"; -import { LocalData } from "../data/LocalData"; -import { appJotaiStore, atom } from "../app-jotai"; -import type { Mutable, ValueOf } from "@excalidraw/excalidraw/utility-types"; -import { getVisibleSceneBounds } from "@excalidraw/excalidraw/element/bounds"; -import { withBatchedUpdates } from "@excalidraw/excalidraw/reactUtils"; + import { collabErrorIndicatorAtom } from "./CollabError"; +import Portal from "./Portal"; + import type { - ReconciledExcalidrawElement, - RemoteExcalidrawElement, -} from "@excalidraw/excalidraw/data/reconcile"; + SocketUpdateDataSource, + SyncableExcalidrawElement, +} from "../data"; export const collabAPIAtom = atom(null); export const isCollaboratingAtom = atom(false); diff --git a/excalidraw-app/collab/CollabError.tsx b/excalidraw-app/collab/CollabError.tsx index 76828cf1a..eb6f391bc 100644 --- a/excalidraw-app/collab/CollabError.tsx +++ b/excalidraw-app/collab/CollabError.tsx @@ -2,6 +2,7 @@ import { Tooltip } from "@excalidraw/excalidraw/components/Tooltip"; import { warning } from "@excalidraw/excalidraw/components/icons"; import clsx from "clsx"; import { useEffect, useRef, useState } from "react"; + import { atom } from "../app-jotai"; import "./CollabError.scss"; diff --git a/excalidraw-app/collab/Portal.tsx b/excalidraw-app/collab/Portal.tsx index 1788d39f7..037bca314 100644 --- a/excalidraw-app/collab/Portal.tsx +++ b/excalidraw-app/collab/Portal.tsx @@ -1,25 +1,26 @@ +import { CaptureUpdateAction } from "@excalidraw/excalidraw"; +import { trackEvent } from "@excalidraw/excalidraw/analytics"; +import { encryptData } from "@excalidraw/excalidraw/data/encryption"; +import { newElementWith } from "@excalidraw/excalidraw/element/mutateElement"; +import throttle from "lodash.throttle"; + +import type { UserIdleState } from "@excalidraw/excalidraw/constants"; +import type { OrderedExcalidrawElement } from "@excalidraw/excalidraw/element/types"; +import type { + OnUserFollowedPayload, + SocketId, +} from "@excalidraw/excalidraw/types"; + +import { WS_EVENTS, FILE_UPLOAD_TIMEOUT, WS_SUBTYPES } from "../app_constants"; +import { isSyncableElement } from "../data"; + import type { SocketUpdateData, SocketUpdateDataSource, SyncableExcalidrawElement, } from "../data"; -import { isSyncableElement } from "../data"; - import type { TCollabClass } from "./Collab"; - -import type { OrderedExcalidrawElement } from "@excalidraw/excalidraw/element/types"; -import { WS_EVENTS, FILE_UPLOAD_TIMEOUT, WS_SUBTYPES } from "../app_constants"; -import type { - OnUserFollowedPayload, - SocketId, -} from "@excalidraw/excalidraw/types"; -import type { UserIdleState } from "@excalidraw/excalidraw/constants"; -import { trackEvent } from "@excalidraw/excalidraw/analytics"; -import throttle from "lodash.throttle"; -import { newElementWith } from "@excalidraw/excalidraw/element/mutateElement"; -import { encryptData } from "@excalidraw/excalidraw/data/encryption"; import type { Socket } from "socket.io-client"; -import { CaptureUpdateAction } from "@excalidraw/excalidraw"; class Portal { collab: TCollabClass; diff --git a/excalidraw-app/components/AI.tsx b/excalidraw-app/components/AI.tsx index fa5d12fa2..c9b6684b3 100644 --- a/excalidraw-app/components/AI.tsx +++ b/excalidraw-app/components/AI.tsx @@ -1,4 +1,3 @@ -import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types"; import { DiagramToCodePlugin, exportToBlob, @@ -9,6 +8,8 @@ import { import { getDataURL } from "@excalidraw/excalidraw/data/blob"; import { safelyParseJSON } from "@excalidraw/excalidraw/utils"; +import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types"; + export const AIComponents = ({ excalidrawAPI, }: { diff --git a/excalidraw-app/components/AppFooter.tsx b/excalidraw-app/components/AppFooter.tsx index e81df7c41..7d42d46bf 100644 --- a/excalidraw-app/components/AppFooter.tsx +++ b/excalidraw-app/components/AppFooter.tsx @@ -1,9 +1,11 @@ -import React from "react"; import { Footer } from "@excalidraw/excalidraw/index"; +import React from "react"; + +import { isExcalidrawPlusSignedUser } from "../app_constants"; + +import { DebugFooter, isVisualDebuggerEnabled } from "./DebugCanvas"; import { EncryptedIcon } from "./EncryptedIcon"; import { ExcalidrawPlusAppLink } from "./ExcalidrawPlusAppLink"; -import { isExcalidrawPlusSignedUser } from "../app_constants"; -import { DebugFooter, isVisualDebuggerEnabled } from "./DebugCanvas"; export const AppFooter = React.memo( ({ onChange }: { onChange: () => void }) => { diff --git a/excalidraw-app/components/AppMainMenu.tsx b/excalidraw-app/components/AppMainMenu.tsx index cb741cf25..996d6912d 100644 --- a/excalidraw-app/components/AppMainMenu.tsx +++ b/excalidraw-app/components/AppMainMenu.tsx @@ -1,13 +1,16 @@ -import React from "react"; import { loginIcon, ExcalLogo, eyeIcon, } from "@excalidraw/excalidraw/components/icons"; -import type { Theme } from "@excalidraw/excalidraw/element/types"; import { MainMenu } from "@excalidraw/excalidraw/index"; -import { isExcalidrawPlusSignedUser } from "../app_constants"; +import React from "react"; + +import type { Theme } from "@excalidraw/excalidraw/element/types"; + import { LanguageList } from "../app-language/LanguageList"; +import { isExcalidrawPlusSignedUser } from "../app_constants"; + import { saveDebugState } from "./DebugCanvas"; export const AppMainMenu: React.FC<{ diff --git a/excalidraw-app/components/AppWelcomeScreen.tsx b/excalidraw-app/components/AppWelcomeScreen.tsx index bdc9859f8..c33de092a 100644 --- a/excalidraw-app/components/AppWelcomeScreen.tsx +++ b/excalidraw-app/components/AppWelcomeScreen.tsx @@ -1,9 +1,10 @@ -import React from "react"; import { loginIcon } from "@excalidraw/excalidraw/components/icons"; +import { POINTER_EVENTS } from "@excalidraw/excalidraw/constants"; import { useI18n } from "@excalidraw/excalidraw/i18n"; import { WelcomeScreen } from "@excalidraw/excalidraw/index"; +import React from "react"; + import { isExcalidrawPlusSignedUser } from "../app_constants"; -import { POINTER_EVENTS } from "@excalidraw/excalidraw/constants"; export const AppWelcomeScreen: React.FC<{ onCollabDialogOpen: () => any; diff --git a/excalidraw-app/components/DebugCanvas.tsx b/excalidraw-app/components/DebugCanvas.tsx index 31898ba57..af1a2b140 100644 --- a/excalidraw-app/components/DebugCanvas.tsx +++ b/excalidraw-app/components/DebugCanvas.tsx @@ -1,24 +1,27 @@ -import { useCallback, useImperativeHandle, useRef } from "react"; -import { type AppState } from "@excalidraw/excalidraw/types"; -import { throttleRAF } from "@excalidraw/excalidraw/utils"; -import { - bootstrapCanvas, - getNormalizedCanvasDimensions, -} from "@excalidraw/excalidraw/renderer/helpers"; -import type { DebugElement } from "@excalidraw/excalidraw/visualdebug"; import { ArrowheadArrowIcon, CloseIcon, TrashIcon, } from "@excalidraw/excalidraw/components/icons"; -import { STORAGE_KEYS } from "../app_constants"; -import type { Curve } from "../../packages/math"; +import { + bootstrapCanvas, + getNormalizedCanvasDimensions, +} from "@excalidraw/excalidraw/renderer/helpers"; +import { type AppState } from "@excalidraw/excalidraw/types"; +import { throttleRAF } from "@excalidraw/excalidraw/utils"; +import { useCallback, useImperativeHandle, useRef } from "react"; + +import type { DebugElement } from "@excalidraw/excalidraw/visualdebug"; + import { isLineSegment, type GlobalPoint, type LineSegment, } from "../../packages/math"; import { isCurve } from "../../packages/math/curve"; +import { STORAGE_KEYS } from "../app_constants"; + +import type { Curve } from "../../packages/math"; const renderLine = ( context: CanvasRenderingContext2D, diff --git a/excalidraw-app/components/EncryptedIcon.tsx b/excalidraw-app/components/EncryptedIcon.tsx index a3a841783..552e7ca82 100644 --- a/excalidraw-app/components/EncryptedIcon.tsx +++ b/excalidraw-app/components/EncryptedIcon.tsx @@ -1,5 +1,5 @@ -import { shield } from "@excalidraw/excalidraw/components/icons"; import { Tooltip } from "@excalidraw/excalidraw/components/Tooltip"; +import { shield } from "@excalidraw/excalidraw/components/icons"; import { useI18n } from "@excalidraw/excalidraw/i18n"; export const EncryptedIcon = () => { diff --git a/excalidraw-app/components/ExportToExcalidrawPlus.tsx b/excalidraw-app/components/ExportToExcalidrawPlus.tsx index 782ecd963..a630f804f 100644 --- a/excalidraw-app/components/ExportToExcalidrawPlus.tsx +++ b/excalidraw-app/components/ExportToExcalidrawPlus.tsx @@ -1,8 +1,20 @@ -import React from "react"; +import { trackEvent } from "@excalidraw/excalidraw/analytics"; import { Card } from "@excalidraw/excalidraw/components/Card"; +import { ExcalidrawLogo } from "@excalidraw/excalidraw/components/ExcalidrawLogo"; import { ToolButton } from "@excalidraw/excalidraw/components/ToolButton"; +import { MIME_TYPES } from "@excalidraw/excalidraw/constants"; +import { + encryptData, + generateEncryptionKey, +} from "@excalidraw/excalidraw/data/encryption"; import { serializeAsJSON } from "@excalidraw/excalidraw/data/json"; -import { loadFirebaseStorage, saveFilesToFirebase } from "../data/firebase"; +import { isInitializedImageElement } from "@excalidraw/excalidraw/element/typeChecks"; +import { useI18n } from "@excalidraw/excalidraw/i18n"; +import { getFrame } from "@excalidraw/excalidraw/utils"; +import { uploadBytes, ref } from "firebase/storage"; +import { nanoid } from "nanoid"; +import React from "react"; + import type { FileId, NonDeletedExcalidrawElement, @@ -12,20 +24,10 @@ import type { BinaryFileData, BinaryFiles, } from "@excalidraw/excalidraw/types"; -import { nanoid } from "nanoid"; -import { useI18n } from "@excalidraw/excalidraw/i18n"; -import { - encryptData, - generateEncryptionKey, -} from "@excalidraw/excalidraw/data/encryption"; -import { isInitializedImageElement } from "@excalidraw/excalidraw/element/typeChecks"; + import { FILE_UPLOAD_MAX_BYTES } from "../app_constants"; import { encodeFilesForUpload } from "../data/FileManager"; -import { uploadBytes, ref } from "firebase/storage"; -import { MIME_TYPES } from "@excalidraw/excalidraw/constants"; -import { trackEvent } from "@excalidraw/excalidraw/analytics"; -import { getFrame } from "@excalidraw/excalidraw/utils"; -import { ExcalidrawLogo } from "@excalidraw/excalidraw/components/ExcalidrawLogo"; +import { loadFirebaseStorage, saveFilesToFirebase } from "../data/firebase"; export const exportToExcalidrawPlus = async ( elements: readonly NonDeletedExcalidrawElement[], diff --git a/excalidraw-app/components/GitHubCorner.tsx b/excalidraw-app/components/GitHubCorner.tsx index 4d74242d1..b1c17f29e 100644 --- a/excalidraw-app/components/GitHubCorner.tsx +++ b/excalidraw-app/components/GitHubCorner.tsx @@ -1,6 +1,7 @@ +import { THEME } from "@excalidraw/excalidraw/constants"; import oc from "open-color"; import React from "react"; -import { THEME } from "@excalidraw/excalidraw/constants"; + import type { Theme } from "@excalidraw/excalidraw/element/types"; // https://github.com/tholman/github-corners diff --git a/excalidraw-app/components/TopErrorBoundary.tsx b/excalidraw-app/components/TopErrorBoundary.tsx index e7e00be3b..77d9e5f4f 100644 --- a/excalidraw-app/components/TopErrorBoundary.tsx +++ b/excalidraw-app/components/TopErrorBoundary.tsx @@ -1,7 +1,7 @@ -import React from "react"; -import * as Sentry from "@sentry/browser"; -import { t } from "@excalidraw/excalidraw/i18n"; import Trans from "@excalidraw/excalidraw/components/Trans"; +import { t } from "@excalidraw/excalidraw/i18n"; +import * as Sentry from "@sentry/browser"; +import React from "react"; interface TopErrorBoundaryState { hasError: boolean; diff --git a/excalidraw-app/data/FileManager.ts b/excalidraw-app/data/FileManager.ts index f5f4eaca9..93d7ddec8 100644 --- a/excalidraw-app/data/FileManager.ts +++ b/excalidraw-app/data/FileManager.ts @@ -2,13 +2,14 @@ import { CaptureUpdateAction } from "@excalidraw/excalidraw"; import { compressData } from "@excalidraw/excalidraw/data/encode"; import { newElementWith } from "@excalidraw/excalidraw/element/mutateElement"; import { isInitializedImageElement } from "@excalidraw/excalidraw/element/typeChecks"; +import { t } from "@excalidraw/excalidraw/i18n"; + import type { ExcalidrawElement, ExcalidrawImageElement, FileId, InitializedExcalidrawImageElement, } from "@excalidraw/excalidraw/element/types"; -import { t } from "@excalidraw/excalidraw/i18n"; import type { BinaryFileData, BinaryFileMetadata, diff --git a/excalidraw-app/data/LocalData.ts b/excalidraw-app/data/LocalData.ts index 3ab0ad931..6b53cc482 100644 --- a/excalidraw-app/data/LocalData.ts +++ b/excalidraw-app/data/LocalData.ts @@ -10,6 +10,13 @@ * (localStorage, indexedDB). */ +import { clearAppStateForLocalStorage } from "@excalidraw/excalidraw/appState"; +import { + CANVAS_SEARCH_TAB, + DEFAULT_SIDEBAR, +} from "@excalidraw/excalidraw/constants"; +import { clearElementsForLocalStorage } from "@excalidraw/excalidraw/element"; +import { debounce } from "@excalidraw/excalidraw/utils"; import { createStore, entries, @@ -19,14 +26,9 @@ import { setMany, get, } from "idb-keyval"; -import { clearAppStateForLocalStorage } from "@excalidraw/excalidraw/appState"; -import { - CANVAS_SEARCH_TAB, - DEFAULT_SIDEBAR, -} from "@excalidraw/excalidraw/constants"; + import type { LibraryPersistedData } from "@excalidraw/excalidraw/data/library"; import type { ImportedDataState } from "@excalidraw/excalidraw/data/types"; -import { clearElementsForLocalStorage } from "@excalidraw/excalidraw/element"; import type { ExcalidrawElement, FileId, @@ -37,8 +39,9 @@ import type { BinaryFiles, } from "@excalidraw/excalidraw/types"; import type { MaybePromise } from "@excalidraw/excalidraw/utility-types"; -import { debounce } from "@excalidraw/excalidraw/utils"; + import { SAVE_TO_LOCAL_STORAGE_TIMEOUT, STORAGE_KEYS } from "../app_constants"; + import { FileManager } from "./FileManager"; import { Locker } from "./Locker"; import { updateBrowserStateVersion } from "./tabSync"; diff --git a/excalidraw-app/data/firebase.ts b/excalidraw-app/data/firebase.ts index a871b4895..66893b513 100644 --- a/excalidraw-app/data/firebase.ts +++ b/excalidraw-app/data/firebase.ts @@ -1,27 +1,12 @@ import { reconcileElements } from "@excalidraw/excalidraw"; -import type { - ExcalidrawElement, - FileId, - OrderedExcalidrawElement, -} from "@excalidraw/excalidraw/element/types"; -import { getSceneVersion } from "@excalidraw/excalidraw/element"; -import type Portal from "../collab/Portal"; -import { restoreElements } from "@excalidraw/excalidraw/data/restore"; -import type { - AppState, - BinaryFileData, - BinaryFileMetadata, - DataURL, -} from "@excalidraw/excalidraw/types"; -import { FILE_CACHE_MAX_AGE_SEC } from "../app_constants"; +import { MIME_TYPES } from "@excalidraw/excalidraw/constants"; import { decompressData } from "@excalidraw/excalidraw/data/encode"; import { encryptData, decryptData, } from "@excalidraw/excalidraw/data/encryption"; -import { MIME_TYPES } from "@excalidraw/excalidraw/constants"; -import type { SyncableExcalidrawElement } from "."; -import { getSyncableElements } from "."; +import { restoreElements } from "@excalidraw/excalidraw/data/restore"; +import { getSceneVersion } from "@excalidraw/excalidraw/element"; import { initializeApp } from "firebase/app"; import { getFirestore, @@ -31,8 +16,27 @@ import { Bytes, } from "firebase/firestore"; import { getStorage, ref, uploadBytes } from "firebase/storage"; -import type { Socket } from "socket.io-client"; + import type { RemoteExcalidrawElement } from "@excalidraw/excalidraw/data/reconcile"; +import type { + ExcalidrawElement, + FileId, + OrderedExcalidrawElement, +} from "@excalidraw/excalidraw/element/types"; +import type { + AppState, + BinaryFileData, + BinaryFileMetadata, + DataURL, +} from "@excalidraw/excalidraw/types"; + +import { FILE_CACHE_MAX_AGE_SEC } from "../app_constants"; + +import { getSyncableElements } from "."; + +import type { SyncableExcalidrawElement } from "."; +import type Portal from "../collab/Portal"; +import type { Socket } from "socket.io-client"; // private // ----------------------------------------------------------------------------- diff --git a/excalidraw-app/data/index.ts b/excalidraw-app/data/index.ts index 6ecb98f77..e8fbc1f4d 100644 --- a/excalidraw-app/data/index.ts +++ b/excalidraw-app/data/index.ts @@ -9,34 +9,38 @@ import { } from "@excalidraw/excalidraw/data/encryption"; import { serializeAsJSON } from "@excalidraw/excalidraw/data/json"; import { restore } from "@excalidraw/excalidraw/data/restore"; -import type { ImportedDataState } from "@excalidraw/excalidraw/data/types"; -import type { SceneBounds } from "@excalidraw/excalidraw/element/bounds"; import { isInvisiblySmallElement } from "@excalidraw/excalidraw/element/sizeHelpers"; import { isInitializedImageElement } from "@excalidraw/excalidraw/element/typeChecks"; +import { t } from "@excalidraw/excalidraw/i18n"; +import { bytesToHexString } from "@excalidraw/excalidraw/utils"; + +import type { UserIdleState } from "@excalidraw/excalidraw/constants"; +import type { ImportedDataState } from "@excalidraw/excalidraw/data/types"; +import type { SceneBounds } from "@excalidraw/excalidraw/element/bounds"; import type { ExcalidrawElement, FileId, OrderedExcalidrawElement, } from "@excalidraw/excalidraw/element/types"; -import { t } from "@excalidraw/excalidraw/i18n"; import type { AppState, BinaryFileData, BinaryFiles, SocketId, } from "@excalidraw/excalidraw/types"; -import type { UserIdleState } from "@excalidraw/excalidraw/constants"; import type { MakeBrand } from "@excalidraw/excalidraw/utility-types"; -import { bytesToHexString } from "@excalidraw/excalidraw/utils"; -import type { WS_SUBTYPES } from "../app_constants"; + import { DELETED_ELEMENT_TIMEOUT, FILE_UPLOAD_MAX_BYTES, ROOM_ID_BYTES, } from "../app_constants"; + import { encodeFilesForUpload } from "./FileManager"; import { saveFilesToFirebase } from "./firebase"; +import type { WS_SUBTYPES } from "../app_constants"; + export type SyncableExcalidrawElement = OrderedExcalidrawElement & MakeBrand<"SyncableExcalidrawElement">; diff --git a/excalidraw-app/data/localStorage.ts b/excalidraw-app/data/localStorage.ts index 640dfdb60..ac1206498 100644 --- a/excalidraw-app/data/localStorage.ts +++ b/excalidraw-app/data/localStorage.ts @@ -1,10 +1,12 @@ -import type { ExcalidrawElement } from "@excalidraw/excalidraw/element/types"; -import type { AppState } from "@excalidraw/excalidraw/types"; import { clearAppStateForLocalStorage, getDefaultAppState, } from "@excalidraw/excalidraw/appState"; import { clearElementsForLocalStorage } from "@excalidraw/excalidraw/element"; + +import type { ExcalidrawElement } from "@excalidraw/excalidraw/element/types"; +import type { AppState } from "@excalidraw/excalidraw/types"; + import { STORAGE_KEYS } from "../app_constants"; export const saveUsernameToLocalStorage = (username: string) => { diff --git a/excalidraw-app/index.tsx b/excalidraw-app/index.tsx index a28bd9e49..98c902e36 100644 --- a/excalidraw-app/index.tsx +++ b/excalidraw-app/index.tsx @@ -1,9 +1,11 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import ExcalidrawApp from "./App"; import { registerSW } from "virtual:pwa-register"; import "../excalidraw-app/sentry"; + +import ExcalidrawApp from "./App"; + window.__EXCALIDRAW_SHA__ = import.meta.env.VITE_APP_GIT_SHA; const rootElement = document.getElementById("root")!; const root = createRoot(rootElement); diff --git a/excalidraw-app/share/ShareDialog.tsx b/excalidraw-app/share/ShareDialog.tsx index bba7e3678..de77c6079 100644 --- a/excalidraw-app/share/ShareDialog.tsx +++ b/excalidraw-app/share/ShareDialog.tsx @@ -1,10 +1,8 @@ -import { useEffect, useRef, useState } from "react"; -import { copyTextToSystemClipboard } from "@excalidraw/excalidraw/clipboard"; import { trackEvent } from "@excalidraw/excalidraw/analytics"; -import { getFrame } from "@excalidraw/excalidraw/utils"; -import { useI18n } from "@excalidraw/excalidraw/i18n"; -import { KEYS } from "@excalidraw/excalidraw/keys"; +import { copyTextToSystemClipboard } from "@excalidraw/excalidraw/clipboard"; import { Dialog } from "@excalidraw/excalidraw/components/Dialog"; +import { FilledButton } from "@excalidraw/excalidraw/components/FilledButton"; +import { TextField } from "@excalidraw/excalidraw/components/TextField"; import { copyIcon, LinkIcon, @@ -14,16 +12,20 @@ import { shareIOS, shareWindows, } from "@excalidraw/excalidraw/components/icons"; -import { TextField } from "@excalidraw/excalidraw/components/TextField"; -import { FilledButton } from "@excalidraw/excalidraw/components/FilledButton"; -import type { CollabAPI } from "../collab/Collab"; -import { activeRoomLinkAtom } from "../collab/Collab"; import { useUIAppState } from "@excalidraw/excalidraw/context/ui-appState"; import { useCopyStatus } from "@excalidraw/excalidraw/hooks/useCopiedIndicator"; +import { useI18n } from "@excalidraw/excalidraw/i18n"; +import { KEYS } from "@excalidraw/excalidraw/keys"; +import { getFrame } from "@excalidraw/excalidraw/utils"; +import { useEffect, useRef, useState } from "react"; + import { atom, useAtom, useAtomValue } from "../app-jotai"; +import { activeRoomLinkAtom } from "../collab/Collab"; import "./ShareDialog.scss"; +import type { CollabAPI } from "../collab/Collab"; + type OnExportToBackend = () => void; type ShareDialogType = "share" | "collaborationOnly"; diff --git a/excalidraw-app/tests/MobileMenu.test.tsx b/excalidraw-app/tests/MobileMenu.test.tsx index a6eef3b51..3b05e7296 100644 --- a/excalidraw-app/tests/MobileMenu.test.tsx +++ b/excalidraw-app/tests/MobileMenu.test.tsx @@ -1,11 +1,11 @@ -import ExcalidrawApp from "../App"; +import { UI } from "@excalidraw/excalidraw/tests/helpers/ui"; import { mockBoundingClientRect, render, restoreOriginalGetBoundingClientRect, } from "@excalidraw/excalidraw/tests/test-utils"; -import { UI } from "@excalidraw/excalidraw/tests/helpers/ui"; +import ExcalidrawApp from "../App"; describe("Test MobileMenu", () => { const { h } = window; diff --git a/excalidraw-app/tests/collab.test.tsx b/excalidraw-app/tests/collab.test.tsx index 80cf4b0b9..f6cd68fab 100644 --- a/excalidraw-app/tests/collab.test.tsx +++ b/excalidraw-app/tests/collab.test.tsx @@ -1,13 +1,14 @@ -import { vi } from "vitest"; -import { act, render, waitFor } from "@excalidraw/excalidraw/tests/test-utils"; -import ExcalidrawApp from "../App"; -import { API } from "@excalidraw/excalidraw/tests/helpers/api"; -import { syncInvalidIndices } from "@excalidraw/excalidraw/fractionalIndex"; +import { CaptureUpdateAction, newElementWith } from "@excalidraw/excalidraw"; import { createRedoAction, createUndoAction, } from "@excalidraw/excalidraw/actions/actionHistory"; -import { CaptureUpdateAction, newElementWith } from "@excalidraw/excalidraw"; +import { syncInvalidIndices } from "@excalidraw/excalidraw/fractionalIndex"; +import { API } from "@excalidraw/excalidraw/tests/helpers/api"; +import { act, render, waitFor } from "@excalidraw/excalidraw/tests/test-utils"; +import { vi } from "vitest"; + +import ExcalidrawApp from "../App"; const { h } = window; diff --git a/excalidraw-app/useHandleAppTheme.ts b/excalidraw-app/useHandleAppTheme.ts index e31961174..6fc2ff54f 100644 --- a/excalidraw-app/useHandleAppTheme.ts +++ b/excalidraw-app/useHandleAppTheme.ts @@ -1,8 +1,10 @@ -import { useEffect, useLayoutEffect, useState } from "react"; import { THEME } from "@excalidraw/excalidraw"; import { EVENT } from "@excalidraw/excalidraw/constants"; -import type { Theme } from "@excalidraw/excalidraw/element/types"; import { CODES, KEYS } from "@excalidraw/excalidraw/keys"; +import { useEffect, useLayoutEffect, useState } from "react"; + +import type { Theme } from "@excalidraw/excalidraw/element/types"; + import { STORAGE_KEYS } from "./app_constants"; const getDarkThemeMediaQuery = (): MediaQueryList | undefined => diff --git a/package.json b/package.json index aaa8027a8..9ff4379d1 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "dotenv": "16.0.1", "eslint-config-prettier": "8.5.0", "eslint-config-react-app": "7.0.1", + "eslint-plugin-import": "2.31.0", "eslint-plugin-prettier": "3.3.1", "http-server": "14.1.1", "husky": "7.0.4", diff --git a/packages/excalidraw/actions/actionAddToLibrary.ts b/packages/excalidraw/actions/actionAddToLibrary.ts index 3186e3bcb..b246918a0 100644 --- a/packages/excalidraw/actions/actionAddToLibrary.ts +++ b/packages/excalidraw/actions/actionAddToLibrary.ts @@ -1,10 +1,11 @@ -import { register } from "./register"; -import { deepCopyElement } from "../element/newElement"; -import { randomId } from "../random"; -import { t } from "../i18n"; import { LIBRARY_DISABLED_TYPES } from "../constants"; +import { deepCopyElement } from "../element/newElement"; +import { t } from "../i18n"; +import { randomId } from "../random"; import { CaptureUpdateAction } from "../store"; +import { register } from "./register"; + export const actionAddToLibrary = register({ name: "addToLibrary", trackEvent: { category: "element" }, diff --git a/packages/excalidraw/actions/actionAlign.tsx b/packages/excalidraw/actions/actionAlign.tsx index 53e8e618b..b42122a50 100644 --- a/packages/excalidraw/actions/actionAlign.tsx +++ b/packages/excalidraw/actions/actionAlign.tsx @@ -1,5 +1,5 @@ -import type { Alignment } from "../align"; import { alignElements } from "../align"; +import { ToolButton } from "../components/ToolButton"; import { AlignBottomIcon, AlignLeftIcon, @@ -8,19 +8,21 @@ import { CenterHorizontallyIcon, CenterVerticallyIcon, } from "../components/icons"; -import { ToolButton } from "../components/ToolButton"; import { getNonDeletedElements } from "../element"; import { isFrameLikeElement } from "../element/typeChecks"; -import type { ExcalidrawElement } from "../element/types"; import { updateFrameMembershipOfSelectedElements } from "../frame"; import { t } from "../i18n"; import { KEYS } from "../keys"; import { isSomeElementSelected } from "../scene"; import { CaptureUpdateAction } from "../store"; -import type { AppClassProperties, AppState, UIAppState } from "../types"; import { arrayToMap, getShortcutKey } from "../utils"; + import { register } from "./register"; +import type { Alignment } from "../align"; +import type { ExcalidrawElement } from "../element/types"; +import type { AppClassProperties, AppState, UIAppState } from "../types"; + export const alignActionsPredicate = ( appState: UIAppState, app: AppClassProperties, diff --git a/packages/excalidraw/actions/actionBoundText.tsx b/packages/excalidraw/actions/actionBoundText.tsx index b72ddee64..4da3f99a1 100644 --- a/packages/excalidraw/actions/actionBoundText.tsx +++ b/packages/excalidraw/actions/actionBoundText.tsx @@ -5,6 +5,11 @@ import { VERTICAL_ALIGN, } from "../constants"; import { isTextElement, newElement } from "../element"; +import { + getOriginalContainerHeightFromCache, + resetOriginalContainerCache, + updateOriginalContainerCache, +} from "../element/containerCache"; import { mutateElement } from "../element/mutateElement"; import { computeBoundTextPosition, @@ -12,16 +17,18 @@ import { getBoundTextElement, redrawTextBoundingBox, } from "../element/textElement"; -import { - getOriginalContainerHeightFromCache, - resetOriginalContainerCache, - updateOriginalContainerCache, -} from "../element/containerCache"; +import { measureText } from "../element/textMeasurements"; import { hasBoundTextElement, isTextBindableContainer, isUsingAdaptiveRadius, } from "../element/typeChecks"; +import { syncMovedIndices } from "../fractionalIndex"; +import { CaptureUpdateAction } from "../store"; +import { arrayToMap, getFontString } from "../utils"; + +import { register } from "./register"; + import type { ExcalidrawElement, ExcalidrawLinearElement, @@ -30,11 +37,6 @@ import type { } from "../element/types"; import type { AppState } from "../types"; import type { Mutable } from "../utility-types"; -import { arrayToMap, getFontString } from "../utils"; -import { register } from "./register"; -import { syncMovedIndices } from "../fractionalIndex"; -import { CaptureUpdateAction } from "../store"; -import { measureText } from "../element/textMeasurements"; export const actionUnbindText = register({ name: "unbindText", diff --git a/packages/excalidraw/actions/actionCanvas.tsx b/packages/excalidraw/actions/actionCanvas.tsx index 903a6d84a..f3e0a8a32 100644 --- a/packages/excalidraw/actions/actionCanvas.tsx +++ b/packages/excalidraw/actions/actionCanvas.tsx @@ -1,4 +1,14 @@ +import { clamp, roundToStep } from "@excalidraw/math"; + +import { + getDefaultAppState, + isEraserActive, + isHandToolActive, +} from "../appState"; +import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors"; import { ColorPicker } from "../components/ColorPicker/ColorPicker"; +import { ToolButton } from "../components/ToolButton"; +import { Tooltip } from "../components/Tooltip"; import { handIcon, MoonIcon, @@ -9,7 +19,6 @@ import { ZoomOutIcon, ZoomResetIcon, } from "../components/icons"; -import { ToolButton } from "../components/ToolButton"; import { CURSOR_TYPE, MAX_ZOOM, @@ -17,28 +26,22 @@ import { THEME, ZOOM_STEP, } from "../constants"; +import { setCursor } from "../cursor"; import { getCommonBounds, getNonDeletedElements } from "../element"; -import type { ExcalidrawElement } from "../element/types"; +import { newElementWith } from "../element/mutateElement"; import { t } from "../i18n"; import { CODES, KEYS } from "../keys"; import { getNormalizedZoom } from "../scene"; import { centerScrollOn } from "../scene/scroll"; import { getStateForZoom } from "../scene/zoom"; -import type { AppState, Offsets } from "../types"; -import { getShortcutKey, updateActiveTool } from "../utils"; -import { register } from "./register"; -import { Tooltip } from "../components/Tooltip"; -import { newElementWith } from "../element/mutateElement"; -import { - getDefaultAppState, - isEraserActive, - isHandToolActive, -} from "../appState"; -import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors"; -import type { SceneBounds } from "../element/bounds"; -import { setCursor } from "../cursor"; import { CaptureUpdateAction } from "../store"; -import { clamp, roundToStep } from "@excalidraw/math"; +import { getShortcutKey, updateActiveTool } from "../utils"; + +import { register } from "./register"; + +import type { SceneBounds } from "../element/bounds"; +import type { ExcalidrawElement } from "../element/types"; +import type { AppState, Offsets } from "../types"; export const actionChangeViewBackgroundColor = register({ name: "changeViewBackgroundColor", diff --git a/packages/excalidraw/actions/actionClipboard.tsx b/packages/excalidraw/actions/actionClipboard.tsx index fffe7b377..4ce3c39dd 100644 --- a/packages/excalidraw/actions/actionClipboard.tsx +++ b/packages/excalidraw/actions/actionClipboard.tsx @@ -1,5 +1,3 @@ -import { CODES, KEYS } from "../keys"; -import { register } from "./register"; import { copyTextToSystemClipboard, copyToClipboard, @@ -8,14 +6,17 @@ import { probablySupportsClipboardWriteText, readSystemClipboard, } from "../clipboard"; -import { actionDeleteSelected } from "./actionDeleteSelected"; +import { DuplicateIcon, cutIcon, pngIcon, svgIcon } from "../components/icons"; +import { isFirefox } from "../constants"; import { exportCanvas, prepareElementsForExport } from "../data/index"; import { getTextFromElements, isTextElement } from "../element"; import { t } from "../i18n"; -import { isFirefox } from "../constants"; -import { DuplicateIcon, cutIcon, pngIcon, svgIcon } from "../components/icons"; +import { CODES, KEYS } from "../keys"; import { CaptureUpdateAction } from "../store"; +import { actionDeleteSelected } from "./actionDeleteSelected"; +import { register } from "./register"; + export const actionCopy = register({ name: "copy", label: "labels.copy", diff --git a/packages/excalidraw/actions/actionCropEditor.tsx b/packages/excalidraw/actions/actionCropEditor.tsx index 643f666ba..c377b3a02 100644 --- a/packages/excalidraw/actions/actionCropEditor.tsx +++ b/packages/excalidraw/actions/actionCropEditor.tsx @@ -1,9 +1,11 @@ -import { register } from "./register"; -import { cropIcon } from "../components/icons"; -import { CaptureUpdateAction } from "../store"; import { ToolButton } from "../components/ToolButton"; -import { t } from "../i18n"; +import { cropIcon } from "../components/icons"; import { isImageElement } from "../element/typeChecks"; +import { t } from "../i18n"; +import { CaptureUpdateAction } from "../store"; + +import { register } from "./register"; + import type { ExcalidrawImageElement } from "../element/types"; export const actionToggleCropEditor = register({ diff --git a/packages/excalidraw/actions/actionDeleteSelected.test.tsx b/packages/excalidraw/actions/actionDeleteSelected.test.tsx index d48646fac..090c81941 100644 --- a/packages/excalidraw/actions/actionDeleteSelected.test.tsx +++ b/packages/excalidraw/actions/actionDeleteSelected.test.tsx @@ -1,7 +1,9 @@ import React from "react"; + import { Excalidraw, mutateElement } from "../index"; -import { act, assertElements, render } from "../tests/test-utils"; import { API } from "../tests/helpers/api"; +import { act, assertElements, render } from "../tests/test-utils"; + import { actionDeleteSelected } from "./actionDeleteSelected"; const { h } = window; diff --git a/packages/excalidraw/actions/actionDeleteSelected.tsx b/packages/excalidraw/actions/actionDeleteSelected.tsx index c640f9256..fe3f8c5d7 100644 --- a/packages/excalidraw/actions/actionDeleteSelected.tsx +++ b/packages/excalidraw/actions/actionDeleteSelected.tsx @@ -1,25 +1,27 @@ -import { getSelectedElements, isSomeElementSelected } from "../scene"; -import { KEYS } from "../keys"; import { ToolButton } from "../components/ToolButton"; -import { t } from "../i18n"; -import { register } from "./register"; +import { TrashIcon } from "../components/icons"; import { getNonDeletedElements } from "../element"; -import type { ExcalidrawElement } from "../element/types"; -import type { AppClassProperties, AppState } from "../types"; -import { mutateElement, newElementWith } from "../element/mutateElement"; -import { getElementsInGroup, selectGroupsForSelectedElements } from "../groups"; -import { LinearElementEditor } from "../element/linearElementEditor"; import { fixBindingsAfterDeletion } from "../element/binding"; +import { LinearElementEditor } from "../element/linearElementEditor"; +import { mutateElement, newElementWith } from "../element/mutateElement"; +import { getContainerElement } from "../element/textElement"; import { isBoundToContainer, isElbowArrow, isFrameLikeElement, } from "../element/typeChecks"; -import { updateActiveTool } from "../utils"; -import { TrashIcon } from "../components/icons"; -import { CaptureUpdateAction } from "../store"; -import { getContainerElement } from "../element/textElement"; import { getFrameChildren } from "../frame"; +import { getElementsInGroup, selectGroupsForSelectedElements } from "../groups"; +import { t } from "../i18n"; +import { KEYS } from "../keys"; +import { getSelectedElements, isSomeElementSelected } from "../scene"; +import { CaptureUpdateAction } from "../store"; +import { updateActiveTool } from "../utils"; + +import { register } from "./register"; + +import type { ExcalidrawElement } from "../element/types"; +import type { AppClassProperties, AppState } from "../types"; const deleteSelectedElements = ( elements: readonly ExcalidrawElement[], diff --git a/packages/excalidraw/actions/actionDistribute.tsx b/packages/excalidraw/actions/actionDistribute.tsx index 15d33b1f1..c96bdaa3e 100644 --- a/packages/excalidraw/actions/actionDistribute.tsx +++ b/packages/excalidraw/actions/actionDistribute.tsx @@ -1,22 +1,24 @@ +import { ToolButton } from "../components/ToolButton"; import { DistributeHorizontallyIcon, DistributeVerticallyIcon, } from "../components/icons"; -import { ToolButton } from "../components/ToolButton"; -import type { Distribution } from "../distribute"; import { distributeElements } from "../distribute"; import { getNonDeletedElements } from "../element"; import { isFrameLikeElement } from "../element/typeChecks"; -import type { ExcalidrawElement } from "../element/types"; import { updateFrameMembershipOfSelectedElements } from "../frame"; import { t } from "../i18n"; import { CODES, KEYS } from "../keys"; import { isSomeElementSelected } from "../scene"; import { CaptureUpdateAction } from "../store"; -import type { AppClassProperties, AppState } from "../types"; import { arrayToMap, getShortcutKey } from "../utils"; + import { register } from "./register"; +import type { Distribution } from "../distribute"; +import type { ExcalidrawElement } from "../element/types"; +import type { AppClassProperties, AppState } from "../types"; + const enableActionGroup = (appState: AppState, app: AppClassProperties) => { const selectedElements = app.scene.getSelectedElements(appState); return ( diff --git a/packages/excalidraw/actions/actionDuplicateSelection.test.tsx b/packages/excalidraw/actions/actionDuplicateSelection.test.tsx index dc471df37..2c1d44e92 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.test.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.test.tsx @@ -1,14 +1,16 @@ +import React from "react"; + +import { ORIG_ID } from "../constants"; import { Excalidraw } from "../index"; +import { API } from "../tests/helpers/api"; import { act, assertElements, getCloneByOrigId, render, } from "../tests/test-utils"; -import { API } from "../tests/helpers/api"; + import { actionDuplicateSelection } from "./actionDuplicateSelection"; -import React from "react"; -import { ORIG_ID } from "../constants"; const { h } = window; diff --git a/packages/excalidraw/actions/actionDuplicateSelection.tsx b/packages/excalidraw/actions/actionDuplicateSelection.tsx index b28e831c8..ce225a1d6 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.tsx @@ -1,27 +1,8 @@ -import { KEYS } from "../keys"; -import { register } from "./register"; -import type { ExcalidrawElement } from "../element/types"; -import { duplicateElement, getNonDeletedElements } from "../element"; -import { isSomeElementSelected } from "../scene"; import { ToolButton } from "../components/ToolButton"; -import { t } from "../i18n"; -import { - arrayToMap, - castArray, - findLastIndex, - getShortcutKey, - invariant, -} from "../utils"; -import { LinearElementEditor } from "../element/linearElementEditor"; -import { - selectGroupsForSelectedElements, - getSelectedGroupForElement, - getElementsInGroup, -} from "../groups"; -import type { AppState } from "../types"; -import { fixBindingsAfterDuplication } from "../element/binding"; -import type { ActionResult } from "./types"; +import { DuplicateIcon } from "../components/icons"; import { DEFAULT_GRID_SIZE } from "../constants"; +import { duplicateElement, getNonDeletedElements } from "../element"; +import { fixBindingsAfterDuplication } from "../element/binding"; import { bindTextToShapeAfterDuplication, getBoundTextElement, @@ -33,16 +14,37 @@ import { isFrameLikeElement, } from "../element/typeChecks"; import { normalizeElementOrder } from "../element/sortElements"; -import { DuplicateIcon } from "../components/icons"; +import { LinearElementEditor } from "../element/linearElementEditor"; import { bindElementsToFramesAfterDuplication, getFrameChildren, } from "../frame"; +import { + selectGroupsForSelectedElements, + getSelectedGroupForElement, + getElementsInGroup, +} from "../groups"; +import { t } from "../i18n"; +import { KEYS } from "../keys"; +import { isSomeElementSelected } from "../scene"; import { excludeElementsInFramesFromSelection, getSelectedElements, } from "../scene/selection"; import { CaptureUpdateAction } from "../store"; +import { + arrayToMap, + castArray, + findLastIndex, + getShortcutKey, + invariant, +} from "../utils"; + +import { register } from "./register"; + +import type { ActionResult } from "./types"; +import type { ExcalidrawElement } from "../element/types"; +import type { AppState } from "../types"; export const actionDuplicateSelection = register({ name: "duplicateSelection", diff --git a/packages/excalidraw/actions/actionElementLink.ts b/packages/excalidraw/actions/actionElementLink.ts index 91469fd63..69fc67dd3 100644 --- a/packages/excalidraw/actions/actionElementLink.ts +++ b/packages/excalidraw/actions/actionElementLink.ts @@ -8,6 +8,7 @@ import { import { t } from "../i18n"; import { getSelectedElements } from "../scene"; import { CaptureUpdateAction } from "../store"; + import { register } from "./register"; export const actionCopyElementLink = register({ diff --git a/packages/excalidraw/actions/actionElementLock.test.tsx b/packages/excalidraw/actions/actionElementLock.test.tsx index d6dd3f7ac..ec45ab9b2 100644 --- a/packages/excalidraw/actions/actionElementLock.test.tsx +++ b/packages/excalidraw/actions/actionElementLock.test.tsx @@ -1,9 +1,10 @@ -import React from "react"; -import { Excalidraw } from "../index"; import { queryByTestId, fireEvent } from "@testing-library/react"; -import { render } from "../tests/test-utils"; -import { Pointer, UI } from "../tests/helpers/ui"; +import React from "react"; + +import { Excalidraw } from "../index"; import { API } from "../tests/helpers/api"; +import { Pointer, UI } from "../tests/helpers/ui"; +import { render } from "../tests/test-utils"; const { h } = window; const mouse = new Pointer("mouse"); diff --git a/packages/excalidraw/actions/actionElementLock.ts b/packages/excalidraw/actions/actionElementLock.ts index eba21f240..57ca1dded 100644 --- a/packages/excalidraw/actions/actionElementLock.ts +++ b/packages/excalidraw/actions/actionElementLock.ts @@ -1,13 +1,15 @@ import { LockedIcon, UnlockedIcon } from "../components/icons"; import { newElementWith } from "../element/mutateElement"; import { isFrameLikeElement } from "../element/typeChecks"; -import type { ExcalidrawElement } from "../element/types"; import { KEYS } from "../keys"; import { getSelectedElements } from "../scene"; import { CaptureUpdateAction } from "../store"; import { arrayToMap } from "../utils"; + import { register } from "./register"; +import type { ExcalidrawElement } from "../element/types"; + const shouldLock = (elements: readonly ExcalidrawElement[]) => elements.every((el) => !el.locked); diff --git a/packages/excalidraw/actions/actionExport.tsx b/packages/excalidraw/actions/actionExport.tsx index 8d18acd89..c9d70fc46 100644 --- a/packages/excalidraw/actions/actionExport.tsx +++ b/packages/excalidraw/actions/actionExport.tsx @@ -1,25 +1,27 @@ -import { ExportIcon, questionCircle, saveAs } from "../components/icons"; +import { useDevice } from "../components/App"; +import { CheckboxItem } from "../components/CheckboxItem"; +import { DarkModeToggle } from "../components/DarkModeToggle"; import { ProjectName } from "../components/ProjectName"; import { ToolButton } from "../components/ToolButton"; import { Tooltip } from "../components/Tooltip"; -import { DarkModeToggle } from "../components/DarkModeToggle"; -import { loadFromJSON, saveAsJSON } from "../data"; -import { resaveAsImageWithScene } from "../data/resave"; -import { t } from "../i18n"; -import { useDevice } from "../components/App"; -import { KEYS } from "../keys"; -import { register } from "./register"; -import { CheckboxItem } from "../components/CheckboxItem"; -import { getExportSize } from "../scene/export"; +import { ExportIcon, questionCircle, saveAs } from "../components/icons"; import { DEFAULT_EXPORT_PADDING, EXPORT_SCALES, THEME } from "../constants"; -import { getSelectedElements, isSomeElementSelected } from "../scene"; -import { getNonDeletedElements } from "../element"; +import { loadFromJSON, saveAsJSON } from "../data"; import { isImageFileHandle } from "../data/blob"; import { nativeFileSystemSupported } from "../data/filesystem"; -import type { Theme } from "../element/types"; +import { resaveAsImageWithScene } from "../data/resave"; +import { getNonDeletedElements } from "../element"; +import { t } from "../i18n"; +import { KEYS } from "../keys"; +import { getSelectedElements, isSomeElementSelected } from "../scene"; +import { getExportSize } from "../scene/export"; +import { CaptureUpdateAction } from "../store"; import "../components/ToolIcon.scss"; -import { CaptureUpdateAction } from "../store"; + +import { register } from "./register"; + +import type { Theme } from "../element/types"; export const actionChangeProjectName = register({ name: "changeProjectName", diff --git a/packages/excalidraw/actions/actionFinalize.tsx b/packages/excalidraw/actions/actionFinalize.tsx index f2d8c0158..ac8a4e036 100644 --- a/packages/excalidraw/actions/actionFinalize.tsx +++ b/packages/excalidraw/actions/actionFinalize.tsx @@ -1,22 +1,25 @@ -import { KEYS } from "../keys"; -import { isInvisiblySmallElement } from "../element"; -import { arrayToMap, updateActiveTool } from "../utils"; +import { pointFrom } from "@excalidraw/math"; + import { ToolButton } from "../components/ToolButton"; import { done } from "../components/icons"; -import { t } from "../i18n"; -import { register } from "./register"; -import { mutateElement } from "../element/mutateElement"; -import { LinearElementEditor } from "../element/linearElementEditor"; +import { resetCursor } from "../cursor"; +import { isInvisiblySmallElement } from "../element"; import { maybeBindLinearElement, bindOrUnbindLinearElement, } from "../element/binding"; +import { LinearElementEditor } from "../element/linearElementEditor"; +import { mutateElement } from "../element/mutateElement"; import { isBindingElement, isLinearElement } from "../element/typeChecks"; -import type { AppState } from "../types"; -import { resetCursor } from "../cursor"; -import { CaptureUpdateAction } from "../store"; -import { pointFrom } from "@excalidraw/math"; +import { t } from "../i18n"; +import { KEYS } from "../keys"; import { isPathALoop } from "../shapes"; +import { CaptureUpdateAction } from "../store"; +import { arrayToMap, updateActiveTool } from "../utils"; + +import { register } from "./register"; + +import type { AppState } from "../types"; export const actionFinalize = register({ name: "finalize", diff --git a/packages/excalidraw/actions/actionFlip.test.tsx b/packages/excalidraw/actions/actionFlip.test.tsx index 94e4721dd..e9953e02d 100644 --- a/packages/excalidraw/actions/actionFlip.test.tsx +++ b/packages/excalidraw/actions/actionFlip.test.tsx @@ -1,8 +1,10 @@ -import React from "react"; -import { Excalidraw } from "../index"; -import { render } from "../tests/test-utils"; -import { API } from "../tests/helpers/api"; import { pointFrom } from "@excalidraw/math"; +import React from "react"; + +import { Excalidraw } from "../index"; +import { API } from "../tests/helpers/api"; +import { render } from "../tests/test-utils"; + import { actionFlipHorizontal, actionFlipVertical } from "./actionFlip"; const { h } = window; diff --git a/packages/excalidraw/actions/actionFlip.ts b/packages/excalidraw/actions/actionFlip.ts index 80149b877..bfc7fb721 100644 --- a/packages/excalidraw/actions/actionFlip.ts +++ b/packages/excalidraw/actions/actionFlip.ts @@ -1,6 +1,26 @@ -import { register } from "./register"; -import { getSelectedElements } from "../scene"; +import { flipHorizontal, flipVertical } from "../components/icons"; import { getNonDeletedElements } from "../element"; +import { + bindOrUnbindLinearElements, + isBindingEnabled, +} from "../element/binding"; +import { getCommonBoundingBox } from "../element/bounds"; +import { mutateElement, newElementWith } from "../element/mutateElement"; +import { deepCopyElement } from "../element/newElement"; +import { resizeMultipleElements } from "../element/resizeElements"; +import { + isArrowElement, + isElbowArrow, + isLinearElement, +} from "../element/typeChecks"; +import { updateFrameMembershipOfSelectedElements } from "../frame"; +import { CODES, KEYS } from "../keys"; +import { getSelectedElements } from "../scene"; +import { CaptureUpdateAction } from "../store"; +import { arrayToMap } from "../utils"; + +import { register } from "./register"; + import type { ExcalidrawArrowElement, ExcalidrawElbowArrowElement, @@ -8,25 +28,7 @@ import type { NonDeleted, NonDeletedSceneElementsMap, } from "../element/types"; -import { resizeMultipleElements } from "../element/resizeElements"; import type { AppClassProperties, AppState } from "../types"; -import { arrayToMap } from "../utils"; -import { CODES, KEYS } from "../keys"; -import { - bindOrUnbindLinearElements, - isBindingEnabled, -} from "../element/binding"; -import { updateFrameMembershipOfSelectedElements } from "../frame"; -import { flipHorizontal, flipVertical } from "../components/icons"; -import { CaptureUpdateAction } from "../store"; -import { - isArrowElement, - isElbowArrow, - isLinearElement, -} from "../element/typeChecks"; -import { mutateElement, newElementWith } from "../element/mutateElement"; -import { deepCopyElement } from "../element/newElement"; -import { getCommonBoundingBox } from "../element/bounds"; export const actionFlipHorizontal = register({ name: "flipHorizontal", diff --git a/packages/excalidraw/actions/actionFrame.ts b/packages/excalidraw/actions/actionFrame.ts index ce05b533b..198994b29 100644 --- a/packages/excalidraw/actions/actionFrame.ts +++ b/packages/excalidraw/actions/actionFrame.ts @@ -1,19 +1,21 @@ +import { frameToolIcon } from "../components/icons"; +import { setCursorForShape } from "../cursor"; import { getCommonBounds, getNonDeletedElements } from "../element"; -import type { ExcalidrawElement } from "../element/types"; +import { mutateElement } from "../element/mutateElement"; +import { newFrameElement } from "../element/newElement"; +import { isFrameLikeElement } from "../element/typeChecks"; import { addElementsToFrame, removeAllElementsFromFrame } from "../frame"; import { getFrameChildren } from "../frame"; -import { KEYS } from "../keys"; -import type { AppClassProperties, AppState, UIAppState } from "../types"; -import { updateActiveTool } from "../utils"; -import { setCursorForShape } from "../cursor"; -import { register } from "./register"; -import { isFrameLikeElement } from "../element/typeChecks"; -import { frameToolIcon } from "../components/icons"; -import { CaptureUpdateAction } from "../store"; -import { getSelectedElements } from "../scene"; -import { newFrameElement } from "../element/newElement"; import { getElementsInGroup } from "../groups"; -import { mutateElement } from "../element/mutateElement"; +import { KEYS } from "../keys"; +import { getSelectedElements } from "../scene"; +import { CaptureUpdateAction } from "../store"; +import { updateActiveTool } from "../utils"; + +import { register } from "./register"; + +import type { ExcalidrawElement } from "../element/types"; +import type { AppClassProperties, AppState, UIAppState } from "../types"; const isSingleFrameSelected = ( appState: UIAppState, diff --git a/packages/excalidraw/actions/actionGroup.tsx b/packages/excalidraw/actions/actionGroup.tsx index 4e98c59c7..ded2f5b72 100644 --- a/packages/excalidraw/actions/actionGroup.tsx +++ b/packages/excalidraw/actions/actionGroup.tsx @@ -1,29 +1,9 @@ -import { KEYS } from "../keys"; -import { t } from "../i18n"; -import { arrayToMap, getShortcutKey } from "../utils"; -import { register } from "./register"; -import { UngroupIcon, GroupIcon } from "../components/icons"; -import { newElementWith } from "../element/mutateElement"; -import { isSomeElementSelected } from "../scene"; -import { - getSelectedGroupIds, - selectGroup, - selectGroupsForSelectedElements, - getElementsInGroup, - addToGroup, - removeFromSelectedGroups, - isElementInGroup, -} from "../groups"; -import { getNonDeletedElements } from "../element"; -import { randomId } from "../random"; import { ToolButton } from "../components/ToolButton"; -import type { - ExcalidrawElement, - ExcalidrawTextElement, - OrderedExcalidrawElement, -} from "../element/types"; -import type { AppClassProperties, AppState } from "../types"; +import { UngroupIcon, GroupIcon } from "../components/icons"; +import { getNonDeletedElements } from "../element"; +import { newElementWith } from "../element/mutateElement"; import { isBoundToContainer } from "../element/typeChecks"; +import { syncMovedIndices } from "../fractionalIndex"; import { frameAndChildrenSelectedTogether, getElementsInResizingFrame, @@ -33,8 +13,30 @@ import { removeElementsFromFrame, replaceAllElementsInFrame, } from "../frame"; -import { syncMovedIndices } from "../fractionalIndex"; +import { + getSelectedGroupIds, + selectGroup, + selectGroupsForSelectedElements, + getElementsInGroup, + addToGroup, + removeFromSelectedGroups, + isElementInGroup, +} from "../groups"; +import { t } from "../i18n"; +import { KEYS } from "../keys"; +import { randomId } from "../random"; +import { isSomeElementSelected } from "../scene"; import { CaptureUpdateAction } from "../store"; +import { arrayToMap, getShortcutKey } from "../utils"; + +import { register } from "./register"; + +import type { + ExcalidrawElement, + ExcalidrawTextElement, + OrderedExcalidrawElement, +} from "../element/types"; +import type { AppClassProperties, AppState } from "../types"; const allElementsInSameGroup = (elements: readonly ExcalidrawElement[]) => { if (elements.length >= 2) { diff --git a/packages/excalidraw/actions/actionHistory.tsx b/packages/excalidraw/actions/actionHistory.tsx index 299a09f3d..da4f80219 100644 --- a/packages/excalidraw/actions/actionHistory.tsx +++ b/packages/excalidraw/actions/actionHistory.tsx @@ -1,17 +1,18 @@ -import type { Action, ActionResult } from "./types"; -import { UndoIcon, RedoIcon } from "../components/icons"; import { ToolButton } from "../components/ToolButton"; -import { t } from "../i18n"; -import type { History } from "../history"; -import { HistoryChangedEvent } from "../history"; -import type { AppClassProperties, AppState } from "../types"; -import { KEYS, matchKey } from "../keys"; -import { arrayToMap } from "../utils"; +import { UndoIcon, RedoIcon } from "../components/icons"; import { isWindows } from "../constants"; -import type { SceneElementsMap } from "../element/types"; -import type { Store } from "../store"; -import { CaptureUpdateAction } from "../store"; +import { HistoryChangedEvent } from "../history"; import { useEmitter } from "../hooks/useEmitter"; +import { t } from "../i18n"; +import { KEYS, matchKey } from "../keys"; +import { CaptureUpdateAction } from "../store"; +import { arrayToMap } from "../utils"; + +import type { SceneElementsMap } from "../element/types"; +import type { History } from "../history"; +import type { Store } from "../store"; +import type { AppClassProperties, AppState } from "../types"; +import type { Action, ActionResult } from "./types"; const executeHistoryAction = ( app: AppClassProperties, diff --git a/packages/excalidraw/actions/actionLinearEditor.tsx b/packages/excalidraw/actions/actionLinearEditor.tsx index 1f05755b5..f17f0e565 100644 --- a/packages/excalidraw/actions/actionLinearEditor.tsx +++ b/packages/excalidraw/actions/actionLinearEditor.tsx @@ -1,12 +1,14 @@ import { DEFAULT_CATEGORIES } from "../components/CommandPalette/CommandPalette"; +import { ToolButton } from "../components/ToolButton"; +import { lineEditorIcon } from "../components/icons"; import { LinearElementEditor } from "../element/linearElementEditor"; import { isElbowArrow, isLinearElement } from "../element/typeChecks"; -import type { ExcalidrawLinearElement } from "../element/types"; -import { CaptureUpdateAction } from "../store"; -import { register } from "./register"; -import { ToolButton } from "../components/ToolButton"; import { t } from "../i18n"; -import { lineEditorIcon } from "../components/icons"; +import { CaptureUpdateAction } from "../store"; + +import { register } from "./register"; + +import type { ExcalidrawLinearElement } from "../element/types"; export const actionToggleLinearEditor = register({ name: "toggleLinearEditor", diff --git a/packages/excalidraw/actions/actionLink.tsx b/packages/excalidraw/actions/actionLink.tsx index beb95d2c1..91d01b7a6 100644 --- a/packages/excalidraw/actions/actionLink.tsx +++ b/packages/excalidraw/actions/actionLink.tsx @@ -1,12 +1,13 @@ +import { ToolButton } from "../components/ToolButton"; import { getContextMenuLabel } from "../components/hyperlink/Hyperlink"; import { LinkIcon } from "../components/icons"; -import { ToolButton } from "../components/ToolButton"; import { isEmbeddableElement } from "../element/typeChecks"; import { t } from "../i18n"; import { KEYS } from "../keys"; import { getSelectedElements } from "../scene"; import { CaptureUpdateAction } from "../store"; import { getShortcutKey } from "../utils"; + import { register } from "./register"; export const actionLink = register({ diff --git a/packages/excalidraw/actions/actionMenu.tsx b/packages/excalidraw/actions/actionMenu.tsx index 9e71fe255..15b763cb9 100644 --- a/packages/excalidraw/actions/actionMenu.tsx +++ b/packages/excalidraw/actions/actionMenu.tsx @@ -1,11 +1,12 @@ -import { HamburgerMenuIcon, HelpIconThin, palette } from "../components/icons"; import { ToolButton } from "../components/ToolButton"; -import { t } from "../i18n"; +import { HamburgerMenuIcon, HelpIconThin, palette } from "../components/icons"; import { showSelectedShapeActions, getNonDeletedElements } from "../element"; -import { register } from "./register"; +import { t } from "../i18n"; import { KEYS } from "../keys"; import { CaptureUpdateAction } from "../store"; +import { register } from "./register"; + export const actionToggleCanvasMenu = register({ name: "toggleCanvasMenu", label: "buttons.menu", diff --git a/packages/excalidraw/actions/actionNavigate.tsx b/packages/excalidraw/actions/actionNavigate.tsx index 48037200c..738386839 100644 --- a/packages/excalidraw/actions/actionNavigate.tsx +++ b/packages/excalidraw/actions/actionNavigate.tsx @@ -1,6 +1,7 @@ +import clsx from "clsx"; + import { getClientColor } from "../clients"; import { Avatar } from "../components/Avatar"; -import type { GoToCollaboratorComponentProps } from "../components/UserList"; import { eyeIcon, microphoneIcon, @@ -8,9 +9,11 @@ import { } from "../components/icons"; import { t } from "../i18n"; import { CaptureUpdateAction } from "../store"; -import type { Collaborator } from "../types"; + import { register } from "./register"; -import clsx from "clsx"; + +import type { GoToCollaboratorComponentProps } from "../components/UserList"; +import type { Collaborator } from "../types"; export const actionGoToCollaborator = register({ name: "goToCollaborator", diff --git a/packages/excalidraw/actions/actionProperties.test.tsx b/packages/excalidraw/actions/actionProperties.test.tsx index 80d2ca257..c5467bb94 100644 --- a/packages/excalidraw/actions/actionProperties.test.tsx +++ b/packages/excalidraw/actions/actionProperties.test.tsx @@ -1,11 +1,12 @@ -import React from "react"; -import { Excalidraw } from "../index"; import { queryByTestId } from "@testing-library/react"; -import { render } from "../tests/test-utils"; -import { UI } from "../tests/helpers/ui"; -import { API } from "../tests/helpers/api"; +import React from "react"; + import { COLOR_PALETTE, DEFAULT_ELEMENT_BACKGROUND_PICKS } from "../colors"; import { FONT_FAMILY, STROKE_WIDTH } from "../constants"; +import { Excalidraw } from "../index"; +import { API } from "../tests/helpers/api"; +import { UI } from "../tests/helpers/ui"; +import { render } from "../tests/test-utils"; describe("element locking", () => { beforeEach(async () => { diff --git a/packages/excalidraw/actions/actionProperties.tsx b/packages/excalidraw/actions/actionProperties.tsx index 0c2ad3bab..1e24d9bed 100644 --- a/packages/excalidraw/actions/actionProperties.tsx +++ b/packages/excalidraw/actions/actionProperties.tsx @@ -1,20 +1,23 @@ +import { pointFrom } from "@excalidraw/math"; import { useEffect, useMemo, useRef, useState } from "react"; -import type { AppClassProperties, AppState, Primitive } from "../types"; -import type { CaptureUpdateActionType } from "../store"; + +import type { LocalPoint } from "@excalidraw/math"; + +import { trackEvent } from "../analytics"; import { DEFAULT_ELEMENT_BACKGROUND_COLOR_PALETTE, DEFAULT_ELEMENT_BACKGROUND_PICKS, DEFAULT_ELEMENT_STROKE_COLOR_PALETTE, DEFAULT_ELEMENT_STROKE_PICKS, } from "../colors"; -import { trackEvent } from "../analytics"; import { ButtonIconSelect } from "../components/ButtonIconSelect"; import { ColorPicker } from "../components/ColorPicker/ColorPicker"; -import { IconPicker } from "../components/IconPicker"; import { FontPicker } from "../components/FontPicker/FontPicker"; +import { IconPicker } from "../components/IconPicker"; // TODO barnabasmolnar/editor-redesign // TextAlignTopIcon, TextAlignBottomIcon,TextAlignMiddleIcon, // ArrowHead icons +import { Range } from "../components/Range"; import { ArrowheadArrowIcon, ArrowheadBarIcon, @@ -71,6 +74,14 @@ import { isTextElement, redrawTextBoundingBox, } from "../element"; +import { + bindLinearElement, + bindPointToSnapToElementOutline, + calculateFixedPointForElbowArrowBinding, + getHoveredElementForBinding, + updateBoundElements, +} from "../element/binding"; +import { LinearElementEditor } from "../element/linearElementEditor"; import { mutateElement, newElementWith } from "../element/mutateElement"; import { getBoundTextElement } from "../element/textElement"; import { @@ -80,17 +91,7 @@ import { isLinearElement, isUsingAdaptiveRadius, } from "../element/typeChecks"; -import type { - Arrowhead, - ExcalidrawBindableElement, - ExcalidrawElement, - ExcalidrawLinearElement, - ExcalidrawTextElement, - FontFamilyValues, - TextAlign, - VerticalAlign, - NonDeletedSceneElementsMap, -} from "../element/types"; +import { Fonts, getLineHeight } from "../fonts"; import { getLanguage, t } from "../i18n"; import { KEYS } from "../keys"; import { randomInteger } from "../random"; @@ -102,26 +103,29 @@ import { isSomeElementSelected, } from "../scene"; import { hasStrokeColor } from "../scene/comparisons"; +import { CaptureUpdateAction } from "../store"; import { arrayToMap, getFontFamilyString, getShortcutKey, tupleToCoors, } from "../utils"; + import { register } from "./register"; -import { CaptureUpdateAction } from "../store"; -import { Fonts, getLineHeight } from "../fonts"; -import { - bindLinearElement, - bindPointToSnapToElementOutline, - calculateFixedPointForElbowArrowBinding, - getHoveredElementForBinding, - updateBoundElements, -} from "../element/binding"; -import { LinearElementEditor } from "../element/linearElementEditor"; -import type { LocalPoint } from "@excalidraw/math"; -import { pointFrom } from "@excalidraw/math"; -import { Range } from "../components/Range"; + +import type { + Arrowhead, + ExcalidrawBindableElement, + ExcalidrawElement, + ExcalidrawLinearElement, + ExcalidrawTextElement, + FontFamilyValues, + TextAlign, + VerticalAlign, + NonDeletedSceneElementsMap, +} from "../element/types"; +import type { CaptureUpdateActionType } from "../store"; +import type { AppClassProperties, AppState, Primitive } from "../types"; const FONT_SIZE_RELATIVE_INCREASE_STEP = 0.1; diff --git a/packages/excalidraw/actions/actionSelectAll.ts b/packages/excalidraw/actions/actionSelectAll.ts index 61c278053..5f6d7eabc 100644 --- a/packages/excalidraw/actions/actionSelectAll.ts +++ b/packages/excalidraw/actions/actionSelectAll.ts @@ -1,13 +1,15 @@ -import { KEYS } from "../keys"; -import { register } from "./register"; -import { selectGroupsForSelectedElements } from "../groups"; -import { getNonDeletedElements, isTextElement } from "../element"; -import type { ExcalidrawElement } from "../element/types"; -import { isLinearElement } from "../element/typeChecks"; -import { LinearElementEditor } from "../element/linearElementEditor"; import { selectAllIcon } from "../components/icons"; +import { getNonDeletedElements, isTextElement } from "../element"; +import { LinearElementEditor } from "../element/linearElementEditor"; +import { isLinearElement } from "../element/typeChecks"; +import { selectGroupsForSelectedElements } from "../groups"; +import { KEYS } from "../keys"; import { CaptureUpdateAction } from "../store"; +import { register } from "./register"; + +import type { ExcalidrawElement } from "../element/types"; + export const actionSelectAll = register({ name: "selectAll", label: "labels.selectAll", diff --git a/packages/excalidraw/actions/actionStyles.ts b/packages/excalidraw/actions/actionStyles.ts index 01f804062..ea517c442 100644 --- a/packages/excalidraw/actions/actionStyles.ts +++ b/packages/excalidraw/actions/actionStyles.ts @@ -1,17 +1,15 @@ -import { - isTextElement, - isExcalidrawElement, - redrawTextBoundingBox, -} from "../element"; -import { CODES, KEYS } from "../keys"; -import { t } from "../i18n"; -import { register } from "./register"; -import { newElementWith } from "../element/mutateElement"; +import { paintIcon } from "../components/icons"; import { DEFAULT_FONT_SIZE, DEFAULT_FONT_FAMILY, DEFAULT_TEXT_ALIGN, } from "../constants"; +import { + isTextElement, + isExcalidrawElement, + redrawTextBoundingBox, +} from "../element"; +import { newElementWith } from "../element/mutateElement"; import { getBoundTextElement } from "../element/textElement"; import { hasBoundTextElement, @@ -20,11 +18,15 @@ import { isFrameLikeElement, isArrowElement, } from "../element/typeChecks"; -import { getSelectedElements } from "../scene"; -import type { ExcalidrawTextElement } from "../element/types"; -import { paintIcon } from "../components/icons"; -import { CaptureUpdateAction } from "../store"; import { getLineHeight } from "../fonts"; +import { t } from "../i18n"; +import { CODES, KEYS } from "../keys"; +import { getSelectedElements } from "../scene"; +import { CaptureUpdateAction } from "../store"; + +import { register } from "./register"; + +import type { ExcalidrawTextElement } from "../element/types"; // `copiedStyles` is exported only for tests. export let copiedStyles: string = "{}"; diff --git a/packages/excalidraw/actions/actionTextAutoResize.ts b/packages/excalidraw/actions/actionTextAutoResize.ts index 29f54c9a7..b9ad7783f 100644 --- a/packages/excalidraw/actions/actionTextAutoResize.ts +++ b/packages/excalidraw/actions/actionTextAutoResize.ts @@ -3,10 +3,12 @@ import { newElementWith } from "../element/mutateElement"; import { measureText } from "../element/textMeasurements"; import { getSelectedElements } from "../scene"; import { CaptureUpdateAction } from "../store"; -import type { AppClassProperties } from "../types"; import { getFontString } from "../utils"; + import { register } from "./register"; +import type { AppClassProperties } from "../types"; + export const actionTextAutoResize = register({ name: "autoResize", label: "labels.autoResize", diff --git a/packages/excalidraw/actions/actionToggleGridMode.tsx b/packages/excalidraw/actions/actionToggleGridMode.tsx index 69d657057..98360e843 100644 --- a/packages/excalidraw/actions/actionToggleGridMode.tsx +++ b/packages/excalidraw/actions/actionToggleGridMode.tsx @@ -1,9 +1,11 @@ -import { CODES, KEYS } from "../keys"; -import { register } from "./register"; -import type { AppState } from "../types"; import { gridIcon } from "../components/icons"; +import { CODES, KEYS } from "../keys"; import { CaptureUpdateAction } from "../store"; +import { register } from "./register"; + +import type { AppState } from "../types"; + export const actionToggleGridMode = register({ name: "gridMode", icon: gridIcon, diff --git a/packages/excalidraw/actions/actionToggleObjectsSnapMode.tsx b/packages/excalidraw/actions/actionToggleObjectsSnapMode.tsx index 1ae3cbe0b..d51124132 100644 --- a/packages/excalidraw/actions/actionToggleObjectsSnapMode.tsx +++ b/packages/excalidraw/actions/actionToggleObjectsSnapMode.tsx @@ -1,6 +1,7 @@ import { magnetIcon } from "../components/icons"; import { CODES, KEYS } from "../keys"; import { CaptureUpdateAction } from "../store"; + import { register } from "./register"; export const actionToggleObjectsSnapMode = register({ diff --git a/packages/excalidraw/actions/actionToggleSearchMenu.ts b/packages/excalidraw/actions/actionToggleSearchMenu.ts index 9261e79c4..75d9074c8 100644 --- a/packages/excalidraw/actions/actionToggleSearchMenu.ts +++ b/packages/excalidraw/actions/actionToggleSearchMenu.ts @@ -1,9 +1,11 @@ -import { KEYS } from "../keys"; -import { register } from "./register"; -import type { AppState } from "../types"; import { searchIcon } from "../components/icons"; -import { CaptureUpdateAction } from "../store"; import { CANVAS_SEARCH_TAB, CLASSES, DEFAULT_SIDEBAR } from "../constants"; +import { KEYS } from "../keys"; +import { CaptureUpdateAction } from "../store"; + +import { register } from "./register"; + +import type { AppState } from "../types"; export const actionToggleSearchMenu = register({ name: "searchMenu", diff --git a/packages/excalidraw/actions/actionToggleStats.tsx b/packages/excalidraw/actions/actionToggleStats.tsx index e28d099ae..d0cdc64a3 100644 --- a/packages/excalidraw/actions/actionToggleStats.tsx +++ b/packages/excalidraw/actions/actionToggleStats.tsx @@ -1,8 +1,9 @@ -import { register } from "./register"; -import { CODES, KEYS } from "../keys"; import { abacusIcon } from "../components/icons"; +import { CODES, KEYS } from "../keys"; import { CaptureUpdateAction } from "../store"; +import { register } from "./register"; + export const actionToggleStats = register({ name: "stats", label: "stats.fullTitle", diff --git a/packages/excalidraw/actions/actionToggleViewMode.tsx b/packages/excalidraw/actions/actionToggleViewMode.tsx index cae3b095e..055135263 100644 --- a/packages/excalidraw/actions/actionToggleViewMode.tsx +++ b/packages/excalidraw/actions/actionToggleViewMode.tsx @@ -1,6 +1,7 @@ import { eyeIcon } from "../components/icons"; import { CODES, KEYS } from "../keys"; import { CaptureUpdateAction } from "../store"; + import { register } from "./register"; export const actionToggleViewMode = register({ diff --git a/packages/excalidraw/actions/actionToggleZenMode.tsx b/packages/excalidraw/actions/actionToggleZenMode.tsx index 31afd3f3a..31c72bf95 100644 --- a/packages/excalidraw/actions/actionToggleZenMode.tsx +++ b/packages/excalidraw/actions/actionToggleZenMode.tsx @@ -1,6 +1,7 @@ import { coffeeIcon } from "../components/icons"; import { CODES, KEYS } from "../keys"; import { CaptureUpdateAction } from "../store"; + import { register } from "./register"; export const actionToggleZenMode = register({ diff --git a/packages/excalidraw/actions/actionZindex.tsx b/packages/excalidraw/actions/actionZindex.tsx index 4097b2df1..82d3d77da 100644 --- a/packages/excalidraw/actions/actionZindex.tsx +++ b/packages/excalidraw/actions/actionZindex.tsx @@ -1,13 +1,3 @@ -import { - moveOneLeft, - moveOneRight, - moveAllLeft, - moveAllRight, -} from "../zindex"; -import { KEYS, CODES } from "../keys"; -import { t } from "../i18n"; -import { getShortcutKey } from "../utils"; -import { register } from "./register"; import { BringForwardIcon, BringToFrontIcon, @@ -15,7 +5,18 @@ import { SendToBackIcon, } from "../components/icons"; import { isDarwin } from "../constants"; +import { t } from "../i18n"; +import { KEYS, CODES } from "../keys"; import { CaptureUpdateAction } from "../store"; +import { getShortcutKey } from "../utils"; +import { + moveOneLeft, + moveOneRight, + moveAllLeft, + moveAllRight, +} from "../zindex"; + +import { register } from "./register"; export const actionSendBackward = register({ name: "sendBackward", diff --git a/packages/excalidraw/actions/manager.tsx b/packages/excalidraw/actions/manager.tsx index f37843800..d3609640e 100644 --- a/packages/excalidraw/actions/manager.tsx +++ b/packages/excalidraw/actions/manager.tsx @@ -1,4 +1,13 @@ import React from "react"; + +import { trackEvent } from "../analytics"; +import { isPromiseLike } from "../utils"; + +import type { + ExcalidrawElement, + OrderedExcalidrawElement, +} from "../element/types"; +import type { AppClassProperties, AppState } from "../types"; import type { Action, UpdaterFn, @@ -7,13 +16,6 @@ import type { PanelComponentProps, ActionSource, } from "./types"; -import type { - ExcalidrawElement, - OrderedExcalidrawElement, -} from "../element/types"; -import type { AppClassProperties, AppState } from "../types"; -import { trackEvent } from "../analytics"; -import { isPromiseLike } from "../utils"; const trackAction = ( action: Action, diff --git a/packages/excalidraw/actions/shortcuts.ts b/packages/excalidraw/actions/shortcuts.ts index 451609dff..89a7c5ae2 100644 --- a/packages/excalidraw/actions/shortcuts.ts +++ b/packages/excalidraw/actions/shortcuts.ts @@ -1,7 +1,8 @@ import { isDarwin } from "../constants"; import { t } from "../i18n"; -import type { SubtypeOf } from "../utility-types"; import { getShortcutKey } from "../utils"; + +import type { SubtypeOf } from "../utility-types"; import type { ActionName } from "./types"; export type ShortcutName = diff --git a/packages/excalidraw/actions/types.ts b/packages/excalidraw/actions/types.ts index 71ac9f4ab..c30f53f5e 100644 --- a/packages/excalidraw/actions/types.ts +++ b/packages/excalidraw/actions/types.ts @@ -1,8 +1,8 @@ -import type React from "react"; import type { ExcalidrawElement, OrderedExcalidrawElement, } from "../element/types"; +import type { CaptureUpdateActionType } from "../store"; import type { AppClassProperties, AppState, @@ -10,7 +10,7 @@ import type { BinaryFiles, UIAppState, } from "../types"; -import type { CaptureUpdateActionType } from "../store"; +import type React from "react"; export type ActionSource = | "ui" diff --git a/packages/excalidraw/align.ts b/packages/excalidraw/align.ts index 113ba274c..36e5d6603 100644 --- a/packages/excalidraw/align.ts +++ b/packages/excalidraw/align.ts @@ -1,9 +1,10 @@ -import type { ElementsMap, ExcalidrawElement } from "./element/types"; -import { mutateElement } from "./element/mutateElement"; -import type { BoundingBox } from "./element/bounds"; -import { getCommonBoundingBox } from "./element/bounds"; -import { getMaximumGroups } from "./groups"; import { updateBoundElements } from "./element/binding"; +import { getCommonBoundingBox } from "./element/bounds"; +import { mutateElement } from "./element/mutateElement"; +import { getMaximumGroups } from "./groups"; + +import type { BoundingBox } from "./element/bounds"; +import type { ElementsMap, ExcalidrawElement } from "./element/types"; import type Scene from "./scene/Scene"; export interface Alignment { diff --git a/packages/excalidraw/animated-trail.ts b/packages/excalidraw/animated-trail.ts index 97a005461..a20460771 100644 --- a/packages/excalidraw/animated-trail.ts +++ b/packages/excalidraw/animated-trail.ts @@ -1,10 +1,13 @@ -import type { LaserPointerOptions } from "@excalidraw/laser-pointer"; import { LaserPointer } from "@excalidraw/laser-pointer"; -import type { AnimationFrameHandler } from "./animation-frame-handler"; -import type { AppState } from "./types"; -import { getSvgPathFromStroke, sceneCoordsToViewportCoords } from "./utils"; -import type App from "./components/App"; + +import type { LaserPointerOptions } from "@excalidraw/laser-pointer"; + import { SVG_NS } from "./constants"; +import { getSvgPathFromStroke, sceneCoordsToViewportCoords } from "./utils"; + +import type { AnimationFrameHandler } from "./animation-frame-handler"; +import type App from "./components/App"; +import type { AppState } from "./types"; export interface Trail { start(container: SVGSVGElement): void; diff --git a/packages/excalidraw/appState.ts b/packages/excalidraw/appState.ts index 644949e7c..acda8468b 100644 --- a/packages/excalidraw/appState.ts +++ b/packages/excalidraw/appState.ts @@ -11,6 +11,7 @@ import { THEME, DEFAULT_GRID_STEP, } from "./constants"; + import type { AppState, NormalizedZoomValue } from "./types"; const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio) diff --git a/packages/excalidraw/change.ts b/packages/excalidraw/change.ts index 1cbf39e89..206f3dcc7 100644 --- a/packages/excalidraw/change.ts +++ b/packages/excalidraw/change.ts @@ -1,5 +1,4 @@ import { ENV } from "./constants"; -import type { BindableProp, BindingProp } from "./element/binding"; import { BoundElement, BindableElement, @@ -7,7 +6,6 @@ import { updateBoundElements, } from "./element/binding"; import { LinearElementEditor } from "./element/linearElementEditor"; -import type { ElementUpdate } from "./element/mutateElement"; import { mutateElement, newElementWith } from "./element/mutateElement"; import { getBoundTextElementId, @@ -20,6 +18,19 @@ import { isImageElement, isTextElement, } from "./element/typeChecks"; +import { orderByFractionalIndex, syncMovedIndices } from "./fractionalIndex"; +import { getNonDeletedGroupIds } from "./groups"; +import { getObservedAppState } from "./store"; +import { + arrayToMap, + arrayToObject, + assertNever, + isShallowEqual, + toBrandedType, +} from "./utils"; + +import type { BindableProp, BindingProp } from "./element/binding"; +import type { ElementUpdate } from "./element/mutateElement"; import type { ExcalidrawElement, ExcalidrawImageElement, @@ -30,9 +41,6 @@ import type { OrderedExcalidrawElement, SceneElementsMap, } from "./element/types"; -import { orderByFractionalIndex, syncMovedIndices } from "./fractionalIndex"; -import { getNonDeletedGroupIds } from "./groups"; -import { getObservedAppState } from "./store"; import type { AppState, ObservedAppState, @@ -40,13 +48,6 @@ import type { ObservedStandaloneAppState, } from "./types"; import type { SubtypeOf, ValueOf } from "./utility-types"; -import { - arrayToMap, - arrayToObject, - assertNever, - isShallowEqual, - toBrandedType, -} from "./utils"; /** * Represents the difference between two objects of the same type. diff --git a/packages/excalidraw/charts.test.ts b/packages/excalidraw/charts.test.ts index fcd8823a9..94fa92fa0 100644 --- a/packages/excalidraw/charts.test.ts +++ b/packages/excalidraw/charts.test.ts @@ -1,6 +1,7 @@ -import type { Spreadsheet } from "./charts"; import { tryParseCells, tryParseNumber, VALID_SPREADSHEET } from "./charts"; +import type { Spreadsheet } from "./charts"; + describe("charts", () => { describe("tryParseNumber", () => { it.each<[string, number]>([ diff --git a/packages/excalidraw/charts.ts b/packages/excalidraw/charts.ts index 65d17e228..e54c55f28 100644 --- a/packages/excalidraw/charts.ts +++ b/packages/excalidraw/charts.ts @@ -1,5 +1,7 @@ -import type { Radians } from "@excalidraw/math"; import { pointFrom } from "@excalidraw/math"; + +import type { Radians } from "@excalidraw/math"; + import { COLOR_PALETTE, DEFAULT_CHART_COLOR_INDEX, @@ -11,9 +13,10 @@ import { VERTICAL_ALIGN, } from "./constants"; import { newElement, newLinearElement, newTextElement } from "./element"; -import type { NonDeletedExcalidrawElement } from "./element/types"; import { randomId } from "./random"; +import type { NonDeletedExcalidrawElement } from "./element/types"; + export type ChartElements = readonly NonDeletedExcalidrawElement[]; const BAR_WIDTH = 32; diff --git a/packages/excalidraw/clients.ts b/packages/excalidraw/clients.ts index 29d240011..6cf4613a8 100644 --- a/packages/excalidraw/clients.ts +++ b/packages/excalidraw/clients.ts @@ -6,6 +6,7 @@ import { UserIdleState, } from "./constants"; import { roundRect } from "./renderer/roundRect"; + import type { InteractiveCanvasRenderConfig } from "./scene/types"; import type { Collaborator, diff --git a/packages/excalidraw/clipboard.ts b/packages/excalidraw/clipboard.ts index 296a3415a..1b0e9942a 100644 --- a/packages/excalidraw/clipboard.ts +++ b/packages/excalidraw/clipboard.ts @@ -1,25 +1,26 @@ -import type { - ExcalidrawElement, - NonDeletedExcalidrawElement, -} from "./element/types"; -import type { BinaryFiles } from "./types"; -import type { Spreadsheet } from "./charts"; import { tryParseSpreadsheet, VALID_SPREADSHEET } from "./charts"; import { ALLOWED_PASTE_MIME_TYPES, EXPORT_DATA_TYPES, MIME_TYPES, } from "./constants"; +import { createFile, isSupportedImageFileType } from "./data/blob"; +import { mutateElement } from "./element/mutateElement"; +import { deepCopyElement } from "./element/newElement"; import { isFrameLikeElement, isInitializedImageElement, } from "./element/typeChecks"; -import { deepCopyElement } from "./element/newElement"; -import { mutateElement } from "./element/mutateElement"; +import { ExcalidrawError } from "./errors"; import { getContainingFrame } from "./frame"; import { arrayToMap, isMemberOf, isPromiseLike } from "./utils"; -import { createFile, isSupportedImageFileType } from "./data/blob"; -import { ExcalidrawError } from "./errors"; + +import type { Spreadsheet } from "./charts"; +import type { + ExcalidrawElement, + NonDeletedExcalidrawElement, +} from "./element/types"; +import type { BinaryFiles } from "./types"; type ElementsClipboard = { type: typeof EXPORT_DATA_TYPES.excalidrawClipboard; diff --git a/packages/excalidraw/colors.ts b/packages/excalidraw/colors.ts index e4cd67a94..84d04bcf4 100644 --- a/packages/excalidraw/colors.ts +++ b/packages/excalidraw/colors.ts @@ -1,4 +1,5 @@ import oc from "open-color"; + import type { Merge } from "./utility-types"; // FIXME can't put to utils.ts rn because of circular dependency diff --git a/packages/excalidraw/components/Actions.tsx b/packages/excalidraw/components/Actions.tsx index cd9120fbc..c74c1ad85 100644 --- a/packages/excalidraw/components/Actions.tsx +++ b/packages/excalidraw/components/Actions.tsx @@ -1,13 +1,25 @@ +import clsx from "clsx"; import { useState } from "react"; -import type { ActionManager } from "../actions/manager"; -import type { - ExcalidrawElement, - ExcalidrawElementType, - NonDeletedElementsMap, - NonDeletedSceneElementsMap, -} from "../element/types"; + +import { actionToggleZenMode } from "../actions"; + +import { KEYS } from "../keys"; +import { CLASSES } from "../constants"; +import { alignActionsPredicate } from "../actions/actionAlign"; +import { trackEvent } from "../analytics"; +import { useTunnels } from "../context/tunnels"; +import { + shouldAllowVerticalAlign, + suppportsHorizontalAlign, +} from "../element/textElement"; +import { + hasBoundTextElement, + isElbowArrow, + isImageElement, + isLinearElement, + isTextElement, +} from "../element/typeChecks"; import { t } from "../i18n"; -import { useDevice } from "./App"; import { canChangeRoundness, canHaveArrowheads, @@ -16,29 +28,16 @@ import { hasStrokeStyle, hasStrokeWidth, } from "../scene"; -import { SHAPES } from "../shapes"; -import type { AppClassProperties, AppProps, UIAppState, Zoom } from "../types"; -import { capitalizeString, isTransparent } from "../utils"; -import Stack from "./Stack"; -import { ToolButton } from "./ToolButton"; import { hasStrokeColor, toolIsArrow } from "../scene/comparisons"; -import { trackEvent } from "../analytics"; -import { - hasBoundTextElement, - isElbowArrow, - isImageElement, - isLinearElement, - isTextElement, -} from "../element/typeChecks"; -import clsx from "clsx"; -import { actionToggleZenMode } from "../actions"; -import { Tooltip } from "./Tooltip"; -import { - shouldAllowVerticalAlign, - suppportsHorizontalAlign, -} from "../element/textElement"; +import { SHAPES } from "../shapes"; +import { capitalizeString, isTransparent } from "../utils"; import "./Actions.scss"; + +import { useDevice } from "./App"; +import Stack from "./Stack"; +import { ToolButton } from "./ToolButton"; +import { Tooltip } from "./Tooltip"; import DropdownMenu from "./dropdownMenu/DropdownMenu"; import { EmbedIcon, @@ -48,10 +47,15 @@ import { laserPointerToolIcon, MagicIcon, } from "./icons"; -import { KEYS } from "../keys"; -import { useTunnels } from "../context/tunnels"; -import { CLASSES } from "../constants"; -import { alignActionsPredicate } from "../actions/actionAlign"; + +import type { + ExcalidrawElement, + ExcalidrawElementType, + NonDeletedElementsMap, + NonDeletedSceneElementsMap, +} from "../element/types"; +import type { AppClassProperties, AppProps, UIAppState, Zoom } from "../types"; +import type { ActionManager } from "../actions/manager"; export const canChangeStrokeColor = ( appState: UIAppState, diff --git a/packages/excalidraw/components/ActiveConfirmDialog.tsx b/packages/excalidraw/components/ActiveConfirmDialog.tsx index 699fbc61f..00c6ad579 100644 --- a/packages/excalidraw/components/ActiveConfirmDialog.tsx +++ b/packages/excalidraw/components/ActiveConfirmDialog.tsx @@ -1,6 +1,7 @@ import { actionClearCanvas } from "../actions"; -import { t } from "../i18n"; import { atom, useAtom } from "../editor-jotai"; +import { t } from "../i18n"; + import { useExcalidrawActionManager } from "./App"; import ConfirmDialog from "./ConfirmDialog"; diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index dc6d28782..6017b73b7 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -1,10 +1,26 @@ +import { + clamp, + pointFrom, + pointDistance, + vector, + pointRotateRads, + vectorScale, + vectorFromPoint, + vectorSubtract, + vectorDot, + vectorNormalize, +} from "@excalidraw/math"; +import { isPointInShape } from "@excalidraw/utils/collision"; +import { getSelectionBoxShape } from "@excalidraw/utils/geometry/shape"; +import clsx from "clsx"; +import throttle from "lodash.throttle"; +import { nanoid } from "nanoid"; import React, { useContext } from "react"; import { flushSync } from "react-dom"; - -import type { RoughCanvas } from "roughjs/bin/canvas"; import rough from "roughjs/bin/rough"; -import clsx from "clsx"; -import { nanoid } from "nanoid"; + +import type { LocalPoint, Radians } from "@excalidraw/math"; + import { actionAddToLibrary, actionBringForward, @@ -37,17 +53,29 @@ import { actionToggleObjectsSnapMode, actionToggleCropEditor, } from "../actions"; +import { actionWrapTextInContainer } from "../actions/actionBoundText"; +import { actionToggleHandTool, zoomToFit } from "../actions/actionCanvas"; +import { actionPaste } from "../actions/actionClipboard"; +import { actionCopyElementLink } from "../actions/actionElementLink"; +import { actionUnlockAllElements } from "../actions/actionElementLock"; +import { + actionRemoveAllElementsFromFrame, + actionSelectAllElementsInFrame, + actionWrapSelectionInFrame, +} from "../actions/actionFrame"; import { createRedoAction, createUndoAction } from "../actions/actionHistory"; +import { actionTextAutoResize } from "../actions/actionTextAutoResize"; +import { actionToggleViewMode } from "../actions/actionToggleViewMode"; import { ActionManager } from "../actions/manager"; import { actions } from "../actions/register"; -import type { Action, ActionResult } from "../actions/types"; +import { getShortcutFromShortcutName } from "../actions/shortcuts"; import { trackEvent } from "../analytics"; +import { AnimationFrameHandler } from "../animation-frame-handler"; import { getDefaultAppState, isEraserActive, isHandToolActive, } from "../appState"; -import type { PastedMixedContent } from "../clipboard"; import { copyTextToSystemClipboard, parseClipboard } from "../clipboard"; import { APP_NAME, @@ -92,7 +120,6 @@ import { isSafari, type EXPORT_IMAGE_TYPES, } from "../constants"; -import type { ExportedElements } from "../data"; import { exportCanvas, loadFromBlob } from "../data"; import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library"; import { restore, restoreElements } from "../data/restore"; @@ -167,30 +194,6 @@ import { isFlowchartNodeElement, isBindableElement, } from "../element/typeChecks"; -import type { - ExcalidrawBindableElement, - ExcalidrawElement, - ExcalidrawFreeDrawElement, - ExcalidrawGenericElement, - ExcalidrawLinearElement, - ExcalidrawTextElement, - NonDeleted, - InitializedExcalidrawImageElement, - ExcalidrawImageElement, - FileId, - NonDeletedExcalidrawElement, - ExcalidrawTextContainer, - ExcalidrawFrameLikeElement, - ExcalidrawMagicFrameElement, - ExcalidrawIframeLikeElement, - IframeData, - ExcalidrawIframeElement, - ExcalidrawEmbeddableElement, - Ordered, - MagicGenerationData, - ExcalidrawNonSelectionElement, - ExcalidrawArrowElement, -} from "../element/types"; import { getCenter, getDistance } from "../gesture"; import { editGroupForSelectedElement, @@ -224,10 +227,6 @@ import { isSomeElementSelected, } from "../scene"; import Scene from "../scene/Scene"; -import type { - RenderInteractiveSceneCallback, - ScrollBars, -} from "../scene/types"; import { getStateForZoom } from "../scene/zoom"; import { findShapeByKey, @@ -236,36 +235,6 @@ import { getElementShape, isPathALoop, } from "../shapes"; -import { getSelectionBoxShape } from "@excalidraw/utils/geometry/shape"; -import { isPointInShape } from "@excalidraw/utils/collision"; -import type { - AppClassProperties, - AppProps, - AppState, - BinaryFileData, - DataURL, - ExcalidrawImperativeAPI, - BinaryFiles, - Gesture, - GestureEvent, - LibraryItems, - PointerDownState, - SceneData, - Device, - FrameNameBoundsCache, - SidebarName, - SidebarTabName, - KeyboardModifiersObject, - CollaboratorPointer, - ToolType, - OnUserFollowedPayload, - UnsubscribeCallback, - EmbedsValidationStatus, - ElementsPendingErasure, - GenerateDiagramToCode, - NullableGridSize, - Offsets, -} from "../types"; import { debounce, distance, @@ -299,11 +268,6 @@ import { maybeParseEmbedSrc, getEmbedLink, } from "../element/embeddable"; -import type { ContextMenuItems } from "./ContextMenu"; -import { ContextMenu, CONTEXT_MENU_SEPARATOR } from "./ContextMenu"; -import LayerUI from "./LayerUI"; -import { Toast } from "./Toast"; -import { actionToggleViewMode } from "../actions/actionToggleViewMode"; import { dataURLToFile, dataURLToString, @@ -326,8 +290,6 @@ import { normalizeSVG, updateImageCache as _updateImageCache, } from "../element/image"; -import throttle from "lodash.throttle"; -import type { FileSystemHandle } from "../data/filesystem"; import { fileOpen } from "../data/filesystem"; import { bindTextToShapeAfterDuplication, @@ -343,7 +305,6 @@ import { } from "../components/hyperlink/Hyperlink"; import { isLocalLink, normalizeLink, toValidURL } from "../data/url"; import { shouldShowBoundingBox } from "../element/transformHandles"; -import { actionUnlockAllElements } from "../actions/actionElementLock"; import { Fonts, getLineHeight } from "../fonts"; import { getFrameChildren, @@ -366,15 +327,7 @@ import { excludeElementsInFramesFromSelection, makeNextSelectedElementIds, } from "../scene/selection"; -import { actionPaste } from "../actions/actionClipboard"; -import { - actionRemoveAllElementsFromFrame, - actionSelectAllElementsInFrame, - actionWrapSelectionInFrame, -} from "../actions/actionFrame"; -import { actionToggleHandTool, zoomToFit } from "../actions/actionCanvas"; import { editorJotaiStore } from "../editor-jotai"; -import { activeConfirmDialogAtom } from "./ActiveConfirmDialog"; import { ImageSceneDataError } from "../errors"; import { getSnapLinesAtPointer, @@ -389,17 +342,9 @@ import { isGridModeEnabled, getGridPoint, } from "../snapping"; -import { actionWrapTextInContainer } from "../actions/actionBoundText"; -import BraveMeasureTextError from "./BraveMeasureTextError"; -import { activeEyeDropperAtom } from "./EyeDropper"; -import type { ExcalidrawElementSkeleton } from "../data/transform"; import { convertToExcalidrawElements } from "../data/transform"; -import type { ValueOf } from "../utility-types"; -import { isSidebarDockedAtom } from "./Sidebar/Sidebar"; -import { StaticCanvas, InteractiveCanvas } from "./canvases"; import { Renderer } from "../scene/Renderer"; import { ShapeCache } from "../scene/ShapeCache"; -import { SVGLayer } from "./SVGLayer"; import { setEraserCursor, setCursor, @@ -409,11 +354,7 @@ import { import { Emitter } from "../emitter"; import { ElementCanvasButtons } from "../element/ElementCanvasButtons"; import { COLOR_PALETTE } from "../colors"; -import { ElementCanvasButton } from "./MagicButton"; -import { MagicIcon, copyIcon, fullscreenIcon } from "./icons"; -import FollowMode from "./FollowMode/FollowMode"; import { Store, CaptureUpdateAction } from "../store"; -import { AnimationFrameHandler } from "../animation-frame-handler"; import { AnimatedTrail } from "../animated-trail"; import { LaserTrails } from "../laser-trails"; import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils"; @@ -426,37 +367,15 @@ import { import { textWysiwyg } from "../element/textWysiwyg"; import { isOverScrollBars } from "../scene/scrollbars"; import { syncInvalidIndices, syncMovedIndices } from "../fractionalIndex"; -import { - isPointHittingLink, - isPointHittingLinkIcon, -} from "./hyperlink/helpers"; -import { getShortcutFromShortcutName } from "../actions/shortcuts"; -import { actionTextAutoResize } from "../actions/actionTextAutoResize"; import { getVisibleSceneBounds } from "../element/bounds"; import { isMaybeMermaidDefinition } from "../mermaid"; -import NewElementCanvas from "./canvases/NewElementCanvas"; import { FlowChartCreator, FlowChartNavigator, getLinkDirectionFromKey, } from "../element/flowchart"; -import { searchItemInFocusAtom } from "./SearchMenu"; -import type { LocalPoint, Radians } from "@excalidraw/math"; -import { - clamp, - pointFrom, - pointDistance, - vector, - pointRotateRads, - vectorScale, - vectorFromPoint, - vectorSubtract, - vectorDot, - vectorNormalize, -} from "@excalidraw/math"; import { cropElement } from "../element/cropElement"; import { wrapText } from "../element/textWrapping"; -import { actionCopyElementLink } from "../actions/actionElementLink"; import { isElementLink, parseElementLinkFromURL } from "../element/elementLink"; import { isMeasureTextSupported, @@ -468,6 +387,90 @@ import { getMinTextElementWidth, } from "../element/textMeasurements"; +import { activeConfirmDialogAtom } from "./ActiveConfirmDialog"; +import BraveMeasureTextError from "./BraveMeasureTextError"; +import { ContextMenu, CONTEXT_MENU_SEPARATOR } from "./ContextMenu"; +import { activeEyeDropperAtom } from "./EyeDropper"; +import FollowMode from "./FollowMode/FollowMode"; +import LayerUI from "./LayerUI"; +import { ElementCanvasButton } from "./MagicButton"; +import { SVGLayer } from "./SVGLayer"; +import { searchItemInFocusAtom } from "./SearchMenu"; +import { isSidebarDockedAtom } from "./Sidebar/Sidebar"; +import { StaticCanvas, InteractiveCanvas } from "./canvases"; +import NewElementCanvas from "./canvases/NewElementCanvas"; +import { + isPointHittingLink, + isPointHittingLinkIcon, +} from "./hyperlink/helpers"; +import { MagicIcon, copyIcon, fullscreenIcon } from "./icons"; +import { Toast } from "./Toast"; + +import type { Action, ActionResult } from "../actions/types"; +import type { PastedMixedContent } from "../clipboard"; +import type { ExportedElements } from "../data"; +import type { ContextMenuItems } from "./ContextMenu"; +import type { FileSystemHandle } from "../data/filesystem"; +import type { ExcalidrawElementSkeleton } from "../data/transform"; +import type { + ExcalidrawBindableElement, + ExcalidrawElement, + ExcalidrawFreeDrawElement, + ExcalidrawGenericElement, + ExcalidrawLinearElement, + ExcalidrawTextElement, + NonDeleted, + InitializedExcalidrawImageElement, + ExcalidrawImageElement, + FileId, + NonDeletedExcalidrawElement, + ExcalidrawTextContainer, + ExcalidrawFrameLikeElement, + ExcalidrawMagicFrameElement, + ExcalidrawIframeLikeElement, + IframeData, + ExcalidrawIframeElement, + ExcalidrawEmbeddableElement, + Ordered, + MagicGenerationData, + ExcalidrawNonSelectionElement, + ExcalidrawArrowElement, +} from "../element/types"; +import type { + RenderInteractiveSceneCallback, + ScrollBars, +} from "../scene/types"; +import type { + AppClassProperties, + AppProps, + AppState, + BinaryFileData, + DataURL, + ExcalidrawImperativeAPI, + BinaryFiles, + Gesture, + GestureEvent, + LibraryItems, + PointerDownState, + SceneData, + Device, + FrameNameBoundsCache, + SidebarName, + SidebarTabName, + KeyboardModifiersObject, + CollaboratorPointer, + ToolType, + OnUserFollowedPayload, + UnsubscribeCallback, + EmbedsValidationStatus, + ElementsPendingErasure, + GenerateDiagramToCode, + NullableGridSize, + Offsets, +} from "../types"; +import type { ValueOf } from "../utility-types"; +import type { RoughCanvas } from "roughjs/bin/canvas"; + const AppContext = React.createContext(null!); const AppPropsContext = React.createContext(null!); diff --git a/packages/excalidraw/components/Avatar.tsx b/packages/excalidraw/components/Avatar.tsx index 9ddc319c6..648fb5fce 100644 --- a/packages/excalidraw/components/Avatar.tsx +++ b/packages/excalidraw/components/Avatar.tsx @@ -1,8 +1,9 @@ -import "./Avatar.scss"; - -import React, { useState } from "react"; -import { getNameInitial } from "../clients"; import clsx from "clsx"; +import React, { useState } from "react"; + +import { getNameInitial } from "../clients"; + +import "./Avatar.scss"; type AvatarProps = { onClick: (e: React.MouseEvent) => void; diff --git a/packages/excalidraw/components/Button.tsx b/packages/excalidraw/components/Button.tsx index 779cee582..9512d607f 100644 --- a/packages/excalidraw/components/Button.tsx +++ b/packages/excalidraw/components/Button.tsx @@ -1,6 +1,8 @@ import clsx from "clsx"; import React from "react"; + import { composeEventHandlers } from "../utils"; + import "./Button.scss"; interface ButtonProps diff --git a/packages/excalidraw/components/ButtonIcon.tsx b/packages/excalidraw/components/ButtonIcon.tsx index cbbd73df9..27b4dc3d8 100644 --- a/packages/excalidraw/components/ButtonIcon.tsx +++ b/packages/excalidraw/components/ButtonIcon.tsx @@ -1,9 +1,10 @@ -import { forwardRef } from "react"; -import type { JSX } from "react"; import clsx from "clsx"; +import { forwardRef } from "react"; import "./ButtonIcon.scss"; +import type { JSX } from "react"; + interface ButtonIconProps { icon: JSX.Element; title: string; diff --git a/packages/excalidraw/components/ButtonIconCycle.tsx b/packages/excalidraw/components/ButtonIconCycle.tsx index b73a8ec5a..53c5e12bd 100644 --- a/packages/excalidraw/components/ButtonIconCycle.tsx +++ b/packages/excalidraw/components/ButtonIconCycle.tsx @@ -1,6 +1,7 @@ -import type { JSX } from "react"; import clsx from "clsx"; +import type { JSX } from "react"; + export const ButtonIconCycle = ({ options, value, diff --git a/packages/excalidraw/components/ButtonIconSelect.tsx b/packages/excalidraw/components/ButtonIconSelect.tsx index 26191b50b..45665e4ca 100644 --- a/packages/excalidraw/components/ButtonIconSelect.tsx +++ b/packages/excalidraw/components/ButtonIconSelect.tsx @@ -1,7 +1,9 @@ -import type { JSX } from "react"; import clsx from "clsx"; + import { ButtonIcon } from "./ButtonIcon"; +import type { JSX } from "react"; + // TODO: It might be "clever" to add option.icon to the existing component export const ButtonIconSelect = ( props: { diff --git a/packages/excalidraw/components/CheckboxItem.tsx b/packages/excalidraw/components/CheckboxItem.tsx index 21c5cfafd..9c757eab5 100644 --- a/packages/excalidraw/components/CheckboxItem.tsx +++ b/packages/excalidraw/components/CheckboxItem.tsx @@ -1,5 +1,6 @@ -import React from "react"; import clsx from "clsx"; +import React from "react"; + import { checkIcon } from "./icons"; import "./CheckboxItem.scss"; diff --git a/packages/excalidraw/components/ColorPicker/ColorInput.tsx b/packages/excalidraw/components/ColorPicker/ColorInput.tsx index 837c88709..dc7d572a3 100644 --- a/packages/excalidraw/components/ColorPicker/ColorInput.tsx +++ b/packages/excalidraw/components/ColorPicker/ColorInput.tsx @@ -1,15 +1,18 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import { getColor } from "./ColorPicker"; -import type { ColorPickerType } from "./colorPickerUtils"; -import { activeColorPickerSectionAtom } from "./colorPickerUtils"; -import { eyeDropperIcon } from "../icons"; -import { useAtom } from "../../editor-jotai"; -import { KEYS } from "../../keys"; -import { activeEyeDropperAtom } from "../EyeDropper"; import clsx from "clsx"; +import { useCallback, useEffect, useRef, useState } from "react"; + +import { useAtom } from "../../editor-jotai"; import { t } from "../../i18n"; -import { useDevice } from "../App"; +import { KEYS } from "../../keys"; import { getShortcutKey } from "../../utils"; +import { useDevice } from "../App"; +import { activeEyeDropperAtom } from "../EyeDropper"; +import { eyeDropperIcon } from "../icons"; + +import { getColor } from "./ColorPicker"; +import { activeColorPickerSectionAtom } from "./colorPickerUtils"; + +import type { ColorPickerType } from "./colorPickerUtils"; interface ColorInputProps { color: string; diff --git a/packages/excalidraw/components/ColorPicker/ColorPicker.tsx b/packages/excalidraw/components/ColorPicker/ColorPicker.tsx index 74d552701..7f6ee8a85 100644 --- a/packages/excalidraw/components/ColorPicker/ColorPicker.tsx +++ b/packages/excalidraw/components/ColorPicker/ColorPicker.tsx @@ -1,26 +1,29 @@ -import { isTransparent } from "../../utils"; -import type { ExcalidrawElement } from "../../element/types"; -import type { AppState } from "../../types"; -import { TopPicks } from "./TopPicks"; -import { ButtonSeparator } from "../ButtonSeparator"; -import { Picker } from "./Picker"; import * as Popover from "@radix-ui/react-popover"; -import type { ColorPickerType } from "./colorPickerUtils"; -import { activeColorPickerSectionAtom } from "./colorPickerUtils"; -import { useExcalidrawContainer } from "../App"; -import type { ColorTuple, ColorPaletteCustom } from "../../colors"; -import { COLOR_PALETTE } from "../../colors"; -import PickerHeading from "./PickerHeading"; -import { t } from "../../i18n"; import clsx from "clsx"; import { useRef } from "react"; + +import { COLOR_PALETTE } from "../../colors"; import { useAtom } from "../../editor-jotai"; -import { ColorInput } from "./ColorInput"; +import { t } from "../../i18n"; +import { isTransparent } from "../../utils"; +import { useExcalidrawContainer } from "../App"; +import { ButtonSeparator } from "../ButtonSeparator"; import { activeEyeDropperAtom } from "../EyeDropper"; import { PropertiesPopover } from "../PropertiesPopover"; +import { ColorInput } from "./ColorInput"; +import { Picker } from "./Picker"; +import PickerHeading from "./PickerHeading"; +import { TopPicks } from "./TopPicks"; +import { activeColorPickerSectionAtom } from "./colorPickerUtils"; + import "./ColorPicker.scss"; +import type { ColorPickerType } from "./colorPickerUtils"; +import type { ColorTuple, ColorPaletteCustom } from "../../colors"; +import type { ExcalidrawElement } from "../../element/types"; +import type { AppState } from "../../types"; + const isValidColor = (color: string) => { const style = new Option().style; style.color = color; diff --git a/packages/excalidraw/components/ColorPicker/CustomColorList.tsx b/packages/excalidraw/components/ColorPicker/CustomColorList.tsx index 5fe1e3e53..2c735102a 100644 --- a/packages/excalidraw/components/ColorPicker/CustomColorList.tsx +++ b/packages/excalidraw/components/ColorPicker/CustomColorList.tsx @@ -1,8 +1,10 @@ import clsx from "clsx"; -import { useAtom } from "../../editor-jotai"; import { useEffect, useRef } from "react"; -import { activeColorPickerSectionAtom } from "./colorPickerUtils"; + +import { useAtom } from "../../editor-jotai"; + import HotkeyLabel from "./HotkeyLabel"; +import { activeColorPickerSectionAtom } from "./colorPickerUtils"; interface CustomColorListProps { colors: string[]; diff --git a/packages/excalidraw/components/ColorPicker/HotkeyLabel.tsx b/packages/excalidraw/components/ColorPicker/HotkeyLabel.tsx index 145060d19..6e4d5e39c 100644 --- a/packages/excalidraw/components/ColorPicker/HotkeyLabel.tsx +++ b/packages/excalidraw/components/ColorPicker/HotkeyLabel.tsx @@ -1,4 +1,5 @@ import React from "react"; + import { getContrastYIQ } from "./colorPickerUtils"; interface HotkeyLabelProps { diff --git a/packages/excalidraw/components/ColorPicker/Picker.tsx b/packages/excalidraw/components/ColorPicker/Picker.tsx index 88d687670..9f311e9c1 100644 --- a/packages/excalidraw/components/ColorPicker/Picker.tsx +++ b/packages/excalidraw/components/ColorPicker/Picker.tsx @@ -1,28 +1,29 @@ import React, { useEffect, useState } from "react"; -import { t } from "../../i18n"; -import type { ExcalidrawElement } from "../../element/types"; -import { ShadeList } from "./ShadeList"; - -import PickerColorList from "./PickerColorList"; +import { + DEFAULT_ELEMENT_BACKGROUND_COLOR_INDEX, + DEFAULT_ELEMENT_STROKE_COLOR_INDEX, +} from "../../colors"; +import { EVENT } from "../../constants"; import { useAtom } from "../../editor-jotai"; +import { t } from "../../i18n"; +import { KEYS } from "../../keys"; + import { CustomColorList } from "./CustomColorList"; -import { colorPickerKeyNavHandler } from "./keyboardNavHandlers"; +import PickerColorList from "./PickerColorList"; import PickerHeading from "./PickerHeading"; -import type { ColorPickerType } from "./colorPickerUtils"; +import { ShadeList } from "./ShadeList"; import { activeColorPickerSectionAtom, getColorNameAndShadeFromColor, getMostUsedCustomColors, isCustomColor, } from "./colorPickerUtils"; +import { colorPickerKeyNavHandler } from "./keyboardNavHandlers"; + +import type { ColorPickerType } from "./colorPickerUtils"; import type { ColorPaletteCustom } from "../../colors"; -import { - DEFAULT_ELEMENT_BACKGROUND_COLOR_INDEX, - DEFAULT_ELEMENT_STROKE_COLOR_INDEX, -} from "../../colors"; -import { KEYS } from "../../keys"; -import { EVENT } from "../../constants"; +import type { ExcalidrawElement } from "../../element/types"; interface PickerProps { color: string; diff --git a/packages/excalidraw/components/ColorPicker/PickerColorList.tsx b/packages/excalidraw/components/ColorPicker/PickerColorList.tsx index f43559d95..b355228f1 100644 --- a/packages/excalidraw/components/ColorPicker/PickerColorList.tsx +++ b/packages/excalidraw/components/ColorPicker/PickerColorList.tsx @@ -1,15 +1,18 @@ import clsx from "clsx"; -import { useAtom } from "../../editor-jotai"; import { useEffect, useRef } from "react"; + +import { useAtom } from "../../editor-jotai"; +import { t } from "../../i18n"; + +import HotkeyLabel from "./HotkeyLabel"; import { activeColorPickerSectionAtom, colorPickerHotkeyBindings, getColorNameAndShadeFromColor, } from "./colorPickerUtils"; -import HotkeyLabel from "./HotkeyLabel"; + import type { ColorPaletteCustom } from "../../colors"; import type { TranslationKeys } from "../../i18n"; -import { t } from "../../i18n"; interface PickerColorListProps { palette: ColorPaletteCustom; diff --git a/packages/excalidraw/components/ColorPicker/ShadeList.tsx b/packages/excalidraw/components/ColorPicker/ShadeList.tsx index 8d3d4cc2a..35d89ea80 100644 --- a/packages/excalidraw/components/ColorPicker/ShadeList.tsx +++ b/packages/excalidraw/components/ColorPicker/ShadeList.tsx @@ -1,12 +1,15 @@ import clsx from "clsx"; -import { useAtom } from "../../editor-jotai"; import { useEffect, useRef } from "react"; + +import { useAtom } from "../../editor-jotai"; +import { t } from "../../i18n"; + +import HotkeyLabel from "./HotkeyLabel"; import { activeColorPickerSectionAtom, getColorNameAndShadeFromColor, } from "./colorPickerUtils"; -import HotkeyLabel from "./HotkeyLabel"; -import { t } from "../../i18n"; + import type { ColorPaletteCustom } from "../../colors"; interface ShadeListProps { diff --git a/packages/excalidraw/components/ColorPicker/TopPicks.tsx b/packages/excalidraw/components/ColorPicker/TopPicks.tsx index 5c69d1e43..9bd8fdb9a 100644 --- a/packages/excalidraw/components/ColorPicker/TopPicks.tsx +++ b/packages/excalidraw/components/ColorPicker/TopPicks.tsx @@ -1,11 +1,13 @@ import clsx from "clsx"; -import type { ColorPickerType } from "./colorPickerUtils"; + import { DEFAULT_CANVAS_BACKGROUND_PICKS, DEFAULT_ELEMENT_BACKGROUND_PICKS, DEFAULT_ELEMENT_STROKE_PICKS, } from "../../colors"; +import type { ColorPickerType } from "./colorPickerUtils"; + interface TopPicksProps { onChange: (color: string) => void; type: ColorPickerType; diff --git a/packages/excalidraw/components/ColorPicker/colorPickerUtils.ts b/packages/excalidraw/components/ColorPicker/colorPickerUtils.ts index 2733b7aba..bbb4e587d 100644 --- a/packages/excalidraw/components/ColorPicker/colorPickerUtils.ts +++ b/packages/excalidraw/components/ColorPicker/colorPickerUtils.ts @@ -1,8 +1,9 @@ -import type { ExcalidrawElement } from "../../element/types"; -import type { ColorPickerColor, ColorPaletteCustom } from "../../colors"; import { MAX_CUSTOM_COLORS_USED_IN_CANVAS } from "../../colors"; import { atom } from "../../editor-jotai"; +import type { ColorPickerColor, ColorPaletteCustom } from "../../colors"; +import type { ExcalidrawElement } from "../../element/types"; + export const getColorNameAndShadeFromColor = ({ palette, color, diff --git a/packages/excalidraw/components/ColorPicker/keyboardNavHandlers.ts b/packages/excalidraw/components/ColorPicker/keyboardNavHandlers.ts index 7767692ed..c4e321700 100644 --- a/packages/excalidraw/components/ColorPicker/keyboardNavHandlers.ts +++ b/packages/excalidraw/components/ColorPicker/keyboardNavHandlers.ts @@ -1,16 +1,18 @@ +import { COLORS_PER_ROW, COLOR_PALETTE } from "../../colors"; import { KEYS } from "../../keys"; + +import { + colorPickerHotkeyBindings, + getColorNameAndShadeFromColor, +} from "./colorPickerUtils"; + +import type { ActiveColorPickerSectionAtomType } from "./colorPickerUtils"; import type { ColorPickerColor, ColorPalette, ColorPaletteCustom, } from "../../colors"; -import { COLORS_PER_ROW, COLOR_PALETTE } from "../../colors"; import type { ValueOf } from "../../utility-types"; -import type { ActiveColorPickerSectionAtomType } from "./colorPickerUtils"; -import { - colorPickerHotkeyBindings, - getColorNameAndShadeFromColor, -} from "./colorPickerUtils"; const arrowHandler = ( eventKey: string, diff --git a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx index cc3c782c4..7febb61c9 100644 --- a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx +++ b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx @@ -1,21 +1,34 @@ +import clsx from "clsx"; +import fuzzy from "fuzzy"; import { useEffect, useRef, useState } from "react"; + +import { + actionClearCanvas, + actionLink, + actionToggleSearchMenu, +} from "../../actions"; +import { + actionCopyElementLink, + actionLinkToElement, +} from "../../actions/actionElementLink"; +import { getShortcutFromShortcutName } from "../../actions/shortcuts"; +import { trackEvent } from "../../analytics"; +import { DEFAULT_SIDEBAR, EVENT } from "../../constants"; +import { useUIAppState } from "../../context/ui-appState"; +import { deburr } from "../../deburr"; +import { atom, useAtom, editorJotaiStore } from "../../editor-jotai"; +import { t } from "../../i18n"; +import { KEYS } from "../../keys"; import { useApp, useAppProps, useExcalidrawActionManager, useExcalidrawSetAppState, } from "../App"; -import { KEYS } from "../../keys"; import { Dialog } from "../Dialog"; +import { InlineIcon } from "../InlineIcon"; import { TextField } from "../TextField"; -import clsx from "clsx"; import { getSelectedElements } from "../../scene"; -import type { Action } from "../../actions/types"; -import type { TranslationKeys } from "../../i18n"; -import { t } from "../../i18n"; -import type { ShortcutName } from "../../actions/shortcuts"; -import { getShortcutFromShortcutName } from "../../actions/shortcuts"; -import { DEFAULT_SIDEBAR, EVENT } from "../../constants"; import { LockedIcon, UnlockedIcon, @@ -28,37 +41,29 @@ import { brainIconThin, LibraryIcon, } from "../icons"; -import fuzzy from "fuzzy"; -import { useUIAppState } from "../../context/ui-appState"; -import type { AppProps, AppState, UIAppState } from "../../types"; + import { capitalizeString, getShortcutKey, isWritableElement, } from "../../utils"; -import { atom, useAtom, editorJotaiStore } from "../../editor-jotai"; -import { deburr } from "../../deburr"; -import type { MarkRequired } from "../../utility-types"; -import { InlineIcon } from "../InlineIcon"; + import { SHAPES } from "../../shapes"; import { canChangeBackgroundColor, canChangeStrokeColor } from "../Actions"; import { useStableCallback } from "../../hooks/useStableCallback"; -import { - actionClearCanvas, - actionLink, - actionToggleSearchMenu, -} from "../../actions"; import { activeConfirmDialogAtom } from "../ActiveConfirmDialog"; -import type { CommandPaletteItem } from "./types"; -import * as defaultItems from "./defaultCommandPaletteItems"; -import { trackEvent } from "../../analytics"; import { useStable } from "../../hooks/useStable"; +import * as defaultItems from "./defaultCommandPaletteItems"; + import "./CommandPalette.scss"; -import { - actionCopyElementLink, - actionLinkToElement, -} from "../../actions/actionElementLink"; + +import type { CommandPaletteItem } from "./types"; +import type { AppProps, AppState, UIAppState } from "../../types"; +import type { MarkRequired } from "../../utility-types"; +import type { ShortcutName } from "../../actions/shortcuts"; +import type { TranslationKeys } from "../../i18n"; +import type { Action } from "../../actions/types"; const lastUsedPaletteItem = atom(null); diff --git a/packages/excalidraw/components/CommandPalette/defaultCommandPaletteItems.ts b/packages/excalidraw/components/CommandPalette/defaultCommandPaletteItems.ts index dea14ff26..485e1767c 100644 --- a/packages/excalidraw/components/CommandPalette/defaultCommandPaletteItems.ts +++ b/packages/excalidraw/components/CommandPalette/defaultCommandPaletteItems.ts @@ -1,4 +1,5 @@ import { actionToggleTheme } from "../../actions"; + import type { CommandPaletteItem } from "./types"; export const toggleTheme: CommandPaletteItem = { diff --git a/packages/excalidraw/components/ConfirmDialog.tsx b/packages/excalidraw/components/ConfirmDialog.tsx index 81073ecdb..cb402dd97 100644 --- a/packages/excalidraw/components/ConfirmDialog.tsx +++ b/packages/excalidraw/components/ConfirmDialog.tsx @@ -1,13 +1,16 @@ import { flushSync } from "react-dom"; -import { t } from "../i18n"; -import type { DialogProps } from "./Dialog"; -import { Dialog } from "./Dialog"; -import "./ConfirmDialog.scss"; +import { useSetAtom } from "../editor-jotai"; +import { t } from "../i18n"; + +import { Dialog } from "./Dialog"; import DialogActionButton from "./DialogActionButton"; import { isLibraryMenuOpenAtom } from "./LibraryMenu"; import { useExcalidrawContainer, useExcalidrawSetAppState } from "./App"; -import { useSetAtom } from "../editor-jotai"; + +import "./ConfirmDialog.scss"; + +import type { DialogProps } from "./Dialog"; interface Props extends Omit { onConfirm: () => void; diff --git a/packages/excalidraw/components/ContextMenu.tsx b/packages/excalidraw/components/ContextMenu.tsx index 517e5faed..3295d1d09 100644 --- a/packages/excalidraw/components/ContextMenu.tsx +++ b/packages/excalidraw/components/ContextMenu.tsx @@ -1,15 +1,20 @@ import clsx from "clsx"; -import { Popover } from "./Popover"; -import type { TranslationKeys } from "../i18n"; +import React from "react"; + +import { getShortcutFromShortcutName } from "../actions/shortcuts"; import { t } from "../i18n"; -import "./ContextMenu.scss"; -import type { ShortcutName } from "../actions/shortcuts"; -import { getShortcutFromShortcutName } from "../actions/shortcuts"; -import type { Action } from "../actions/types"; -import type { ActionManager } from "../actions/manager"; import { useExcalidrawAppState, useExcalidrawElements } from "./App"; -import React from "react"; + +import { Popover } from "./Popover"; + +import "./ContextMenu.scss"; + +import type { ActionManager } from "../actions/manager"; +import type { ShortcutName } from "../actions/shortcuts"; +import type { Action } from "../actions/types"; + +import type { TranslationKeys } from "../i18n"; export type ContextMenuItem = typeof CONTEXT_MENU_SEPARATOR | Action; diff --git a/packages/excalidraw/components/DarkModeToggle.tsx b/packages/excalidraw/components/DarkModeToggle.tsx index 6292ba5ed..f04712944 100644 --- a/packages/excalidraw/components/DarkModeToggle.tsx +++ b/packages/excalidraw/components/DarkModeToggle.tsx @@ -1,8 +1,10 @@ +import { THEME } from "../constants"; +import { t } from "../i18n"; + +import { ToolButton } from "./ToolButton"; + import "./ToolIcon.scss"; -import { t } from "../i18n"; -import { ToolButton } from "./ToolButton"; -import { THEME } from "../constants"; import type { Theme } from "../element/types"; // We chose to use only explicit toggle and not a third option for system value, diff --git a/packages/excalidraw/components/DefaultSidebar.test.tsx b/packages/excalidraw/components/DefaultSidebar.test.tsx index ac1e2961c..f8bc0dbbb 100644 --- a/packages/excalidraw/components/DefaultSidebar.test.tsx +++ b/packages/excalidraw/components/DefaultSidebar.test.tsx @@ -1,4 +1,5 @@ import React from "react"; + import { DEFAULT_SIDEBAR } from "../constants"; import { DefaultSidebar } from "../index"; import { @@ -6,6 +7,7 @@ import { waitFor, withExcalidrawDimensions, } from "../tests/test-utils"; + import { assertExcalidrawWithSidebar, assertSidebarDockButton, diff --git a/packages/excalidraw/components/DefaultSidebar.tsx b/packages/excalidraw/components/DefaultSidebar.tsx index 70b0c2d6c..cd9683c60 100644 --- a/packages/excalidraw/components/DefaultSidebar.tsx +++ b/packages/excalidraw/components/DefaultSidebar.tsx @@ -1,4 +1,5 @@ import clsx from "clsx"; + import { CANVAS_SEARCH_TAB, DEFAULT_SIDEBAR, @@ -6,17 +7,20 @@ import { } from "../constants"; import { useTunnels } from "../context/tunnels"; import { useUIAppState } from "../context/ui-appState"; -import type { MarkOptional, Merge } from "../utility-types"; import { composeEventHandlers } from "../utils"; -import { useExcalidrawSetAppState } from "./App"; -import { withInternalFallback } from "./hoc/withInternalFallback"; -import { LibraryMenu } from "./LibraryMenu"; -import type { SidebarProps, SidebarTriggerProps } from "./Sidebar/common"; -import { Sidebar } from "./Sidebar/Sidebar"; + import "../components/dropdownMenu/DropdownMenu.scss"; + +import { useExcalidrawSetAppState } from "./App"; +import { LibraryMenu } from "./LibraryMenu"; import { SearchMenu } from "./SearchMenu"; +import { Sidebar } from "./Sidebar/Sidebar"; +import { withInternalFallback } from "./hoc/withInternalFallback"; import { LibraryIcon, searchIcon } from "./icons"; +import type { MarkOptional, Merge } from "../utility-types"; +import type { SidebarProps, SidebarTriggerProps } from "./Sidebar/common"; + const DefaultSidebarTrigger = withInternalFallback( "DefaultSidebarTrigger", ( diff --git a/packages/excalidraw/components/DiagramToCodePlugin/DiagramToCodePlugin.tsx b/packages/excalidraw/components/DiagramToCodePlugin/DiagramToCodePlugin.tsx index 950599963..e225ca193 100644 --- a/packages/excalidraw/components/DiagramToCodePlugin/DiagramToCodePlugin.tsx +++ b/packages/excalidraw/components/DiagramToCodePlugin/DiagramToCodePlugin.tsx @@ -1,5 +1,7 @@ import { useLayoutEffect } from "react"; + import { useApp } from "../App"; + import type { GenerateDiagramToCode } from "../../types"; export const DiagramToCodePlugin = (props: { diff --git a/packages/excalidraw/components/Dialog.tsx b/packages/excalidraw/components/Dialog.tsx index 0a105cf8d..cf6b1254a 100644 --- a/packages/excalidraw/components/Dialog.tsx +++ b/packages/excalidraw/components/Dialog.tsx @@ -1,21 +1,24 @@ import clsx from "clsx"; import React, { useEffect, useState } from "react"; + +import { useSetAtom } from "../editor-jotai"; import { useCallbackRefState } from "../hooks/useCallbackRefState"; +import { t } from "../i18n"; +import { KEYS } from "../keys"; +import { queryFocusableElements } from "../utils"; + import { useExcalidrawContainer, useDevice, useExcalidrawSetAppState, } from "./App"; -import { KEYS } from "../keys"; -import "./Dialog.scss"; import { Island } from "./Island"; -import { Modal } from "./Modal"; -import { queryFocusableElements } from "../utils"; import { isLibraryMenuOpenAtom } from "./LibraryMenu"; -import { useSetAtom } from "../editor-jotai"; -import { t } from "../i18n"; +import { Modal } from "./Modal"; import { CloseIcon } from "./icons"; +import "./Dialog.scss"; + export type DialogSize = number | "small" | "regular" | "wide" | undefined; export interface DialogProps { diff --git a/packages/excalidraw/components/DialogActionButton.tsx b/packages/excalidraw/components/DialogActionButton.tsx index 0c4f9d589..1637fc31a 100644 --- a/packages/excalidraw/components/DialogActionButton.tsx +++ b/packages/excalidraw/components/DialogActionButton.tsx @@ -1,8 +1,11 @@ import clsx from "clsx"; -import type { ReactNode } from "react"; -import "./DialogActionButton.scss"; + import Spinner from "./Spinner"; +import "./DialogActionButton.scss"; + +import type { ReactNode } from "react"; + interface DialogActionButtonProps { label: string; children?: ReactNode; diff --git a/packages/excalidraw/components/ElementLinkDialog.tsx b/packages/excalidraw/components/ElementLinkDialog.tsx index 2ec3eaa0b..7cf329633 100644 --- a/packages/excalidraw/components/ElementLinkDialog.tsx +++ b/packages/excalidraw/components/ElementLinkDialog.tsx @@ -1,21 +1,24 @@ -import { TextField } from "./TextField"; -import type { AppProps, AppState, UIAppState } from "../types"; -import DialogActionButton from "./DialogActionButton"; -import { getSelectedElements } from "../scene"; +import { useCallback, useEffect, useState } from "react"; + +import { normalizeLink } from "../data/url"; import { defaultGetElementLinkFromSelection, getLinkIdAndTypeFromSelection, } from "../element/elementLink"; import { mutateElement } from "../element/mutateElement"; -import { useCallback, useEffect, useState } from "react"; import { t } from "../i18n"; -import type { ElementsMap, ExcalidrawElement } from "../element/types"; +import { KEYS } from "../keys"; +import { getSelectedElements } from "../scene"; + +import DialogActionButton from "./DialogActionButton"; +import { TextField } from "./TextField"; import { ToolButton } from "./ToolButton"; import { TrashIcon } from "./icons"; -import { KEYS } from "../keys"; import "./ElementLinkDialog.scss"; -import { normalizeLink } from "../data/url"; + +import type { ElementsMap, ExcalidrawElement } from "../element/types"; +import type { AppProps, AppState, UIAppState } from "../types"; const ElementLinkDialog = ({ sourceElementId, diff --git a/packages/excalidraw/components/ErrorDialog.tsx b/packages/excalidraw/components/ErrorDialog.tsx index 74d265f73..132b292b9 100644 --- a/packages/excalidraw/components/ErrorDialog.tsx +++ b/packages/excalidraw/components/ErrorDialog.tsx @@ -1,8 +1,9 @@ import React, { useState } from "react"; + import { t } from "../i18n"; -import { Dialog } from "./Dialog"; import { useExcalidrawContainer } from "./App"; +import { Dialog } from "./Dialog"; export const ErrorDialog = ({ children, diff --git a/packages/excalidraw/components/EyeDropper.tsx b/packages/excalidraw/components/EyeDropper.tsx index 429c68a4a..8c4a73e32 100644 --- a/packages/excalidraw/components/EyeDropper.tsx +++ b/packages/excalidraw/components/EyeDropper.tsx @@ -1,19 +1,23 @@ import { useEffect, useRef } from "react"; import { createPortal } from "react-dom"; + import { rgbToHex } from "../colors"; import { EVENT } from "../constants"; import { useUIAppState } from "../context/ui-appState"; +import { atom } from "../editor-jotai"; import { useCreatePortalContainer } from "../hooks/useCreatePortalContainer"; import { useOutsideClick } from "../hooks/useOutsideClick"; +import { useStable } from "../hooks/useStable"; import { KEYS } from "../keys"; import { getSelectedElements } from "../scene"; + import { useApp, useExcalidrawContainer, useExcalidrawElements } from "./App"; -import { useStable } from "../hooks/useStable"; import "./EyeDropper.scss"; -import type { ColorPickerType } from "./ColorPicker/colorPickerUtils"; + import type { ExcalidrawElement } from "../element/types"; -import { atom } from "../editor-jotai"; + +import type { ColorPickerType } from "./ColorPicker/colorPickerUtils"; export type EyeDropperProperties = { keepOpenOnAlt: boolean; diff --git a/packages/excalidraw/components/FilledButton.tsx b/packages/excalidraw/components/FilledButton.tsx index 136090848..4e5b3e3ba 100644 --- a/packages/excalidraw/components/FilledButton.tsx +++ b/packages/excalidraw/components/FilledButton.tsx @@ -1,11 +1,13 @@ -import React, { forwardRef, useState } from "react"; import clsx from "clsx"; +import React, { forwardRef, useState } from "react"; + +import { AbortError } from "../errors"; +import { isPromiseLike } from "../utils"; + +import Spinner from "./Spinner"; +import { tablerCheckIcon } from "./icons"; import "./FilledButton.scss"; -import { AbortError } from "../errors"; -import Spinner from "./Spinner"; -import { isPromiseLike } from "../utils"; -import { tablerCheckIcon } from "./icons"; export type ButtonVariant = "filled" | "outlined" | "icon"; export type ButtonColor = diff --git a/packages/excalidraw/components/FixedSideContainer.tsx b/packages/excalidraw/components/FixedSideContainer.tsx index 76aee9989..3130d78e9 100644 --- a/packages/excalidraw/components/FixedSideContainer.tsx +++ b/packages/excalidraw/components/FixedSideContainer.tsx @@ -1,8 +1,8 @@ -import "./FixedSideContainer.scss"; - import React from "react"; import clsx from "clsx"; +import "./FixedSideContainer.scss"; + type FixedSideContainerProps = { children: React.ReactNode; side: "top" | "left" | "right"; diff --git a/packages/excalidraw/components/FollowMode/FollowMode.tsx b/packages/excalidraw/components/FollowMode/FollowMode.tsx index 89581bfa4..e862cb4ea 100644 --- a/packages/excalidraw/components/FollowMode/FollowMode.tsx +++ b/packages/excalidraw/components/FollowMode/FollowMode.tsx @@ -1,7 +1,9 @@ -import type { UserToFollow } from "../../types"; import { CloseIcon } from "../icons"; + import "./FollowMode.scss"; +import type { UserToFollow } from "../../types"; + interface FollowModeProps { width: number; height: number; diff --git a/packages/excalidraw/components/FontPicker/FontPicker.tsx b/packages/excalidraw/components/FontPicker/FontPicker.tsx index 4585c5841..4018ad243 100644 --- a/packages/excalidraw/components/FontPicker/FontPicker.tsx +++ b/packages/excalidraw/components/FontPicker/FontPicker.tsx @@ -1,21 +1,23 @@ -import React, { useCallback, useMemo } from "react"; import * as Popover from "@radix-ui/react-popover"; +import React, { useCallback, useMemo } from "react"; -import { FontPickerList } from "./FontPickerList"; -import { FontPickerTrigger } from "./FontPickerTrigger"; +import { FONT_FAMILY } from "../../constants"; +import { t } from "../../i18n"; import { ButtonIconSelect } from "../ButtonIconSelect"; +import { ButtonSeparator } from "../ButtonSeparator"; import { FontFamilyCodeIcon, FontFamilyNormalIcon, FreedrawIcon, } from "../icons"; -import { ButtonSeparator } from "../ButtonSeparator"; -import type { FontFamilyValues } from "../../element/types"; -import { FONT_FAMILY } from "../../constants"; -import { t } from "../../i18n"; + +import { FontPickerList } from "./FontPickerList"; +import { FontPickerTrigger } from "./FontPickerTrigger"; import "./FontPicker.scss"; +import type { FontFamilyValues } from "../../element/types"; + export const DEFAULT_FONTS = [ { value: FONT_FAMILY.Excalifont, diff --git a/packages/excalidraw/components/FontPicker/FontPickerList.tsx b/packages/excalidraw/components/FontPicker/FontPickerList.tsx index 3a680b8fe..d93b08695 100644 --- a/packages/excalidraw/components/FontPicker/FontPickerList.tsx +++ b/packages/excalidraw/components/FontPicker/FontPickerList.tsx @@ -1,4 +1,3 @@ -import type { JSX } from "react"; import React, { useMemo, useState, @@ -7,6 +6,11 @@ import React, { useCallback, type KeyboardEventHandler, } from "react"; + +import { type FontFamilyValues } from "../../element/types"; +import { Fonts } from "../../fonts"; +import { t } from "../../i18n"; +import { arrayToList, debounce, getFontFamilyString } from "../../utils"; import { useApp, useAppProps, useExcalidrawContainer } from "../App"; import { PropertiesPopover } from "../PropertiesPopover"; import { QuickSearch } from "../QuickSearch"; @@ -16,14 +20,13 @@ import DropdownMenuItem, { DropDownMenuItemBadgeType, DropDownMenuItemBadge, } from "../dropdownMenu/DropdownMenuItem"; -import { type FontFamilyValues } from "../../element/types"; -import { arrayToList, debounce, getFontFamilyString } from "../../utils"; -import { t } from "../../i18n"; -import { fontPickerKeyHandler } from "./keyboardNavHandlers"; -import { Fonts } from "../../fonts"; -import type { ValueOf } from "../../utility-types"; import { FontFamilyNormalIcon } from "../icons"; +import { fontPickerKeyHandler } from "./keyboardNavHandlers"; + +import type { ValueOf } from "../../utility-types"; +import type { JSX } from "react"; + export interface FontDescriptor { value: number; icon: JSX.Element; diff --git a/packages/excalidraw/components/FontPicker/FontPickerTrigger.tsx b/packages/excalidraw/components/FontPicker/FontPickerTrigger.tsx index 8652dabcd..d83dda0fd 100644 --- a/packages/excalidraw/components/FontPicker/FontPickerTrigger.tsx +++ b/packages/excalidraw/components/FontPicker/FontPickerTrigger.tsx @@ -1,11 +1,14 @@ import * as Popover from "@radix-ui/react-popover"; import { useMemo } from "react"; + +import { t } from "../../i18n"; import { ButtonIcon } from "../ButtonIcon"; import { TextIcon } from "../icons"; -import type { FontFamilyValues } from "../../element/types"; -import { t } from "../../i18n"; + import { isDefaultFont } from "./FontPicker"; +import type { FontFamilyValues } from "../../element/types"; + interface FontPickerTriggerProps { selectedFontFamily: FontFamilyValues | null; } diff --git a/packages/excalidraw/components/FontPicker/keyboardNavHandlers.ts b/packages/excalidraw/components/FontPicker/keyboardNavHandlers.ts index 19c4adccc..b0ecdf371 100644 --- a/packages/excalidraw/components/FontPicker/keyboardNavHandlers.ts +++ b/packages/excalidraw/components/FontPicker/keyboardNavHandlers.ts @@ -1,7 +1,9 @@ -import type { Node } from "../../utils"; import { KEYS } from "../../keys"; + import { type FontDescriptor } from "./FontPickerList"; +import type { Node } from "../../utils"; + interface FontPickerKeyNavHandlerProps { event: React.KeyboardEvent; inputRef: React.RefObject; diff --git a/packages/excalidraw/components/HandButton.tsx b/packages/excalidraw/components/HandButton.tsx index ce63791e1..4bffb1000 100644 --- a/packages/excalidraw/components/HandButton.tsx +++ b/packages/excalidraw/components/HandButton.tsx @@ -1,9 +1,11 @@ -import "./ToolIcon.scss"; - import clsx from "clsx"; + +import { KEYS } from "../keys"; + import { ToolButton } from "./ToolButton"; import { handIcon } from "./icons"; -import { KEYS } from "../keys"; + +import "./ToolIcon.scss"; type LockIconProps = { title?: string; diff --git a/packages/excalidraw/components/HelpButton.tsx b/packages/excalidraw/components/HelpButton.tsx index ce387244c..39c867cde 100644 --- a/packages/excalidraw/components/HelpButton.tsx +++ b/packages/excalidraw/components/HelpButton.tsx @@ -1,4 +1,5 @@ import { t } from "../i18n"; + import { HelpIcon } from "./icons"; type HelpButtonProps = { diff --git a/packages/excalidraw/components/HelpDialog.tsx b/packages/excalidraw/components/HelpDialog.tsx index 926096c69..19ecaa57e 100644 --- a/packages/excalidraw/components/HelpDialog.tsx +++ b/packages/excalidraw/components/HelpDialog.tsx @@ -1,14 +1,18 @@ -import type { JSX } from "react"; import React from "react"; -import { t } from "../i18n"; -import { KEYS } from "../keys"; -import { Dialog } from "./Dialog"; -import { getShortcutKey } from "../utils"; -import "./HelpDialog.scss"; -import { ExternalLinkIcon, GithubIcon, youtubeIcon } from "./icons"; + +import { getShortcutFromShortcutName } from "../actions/shortcuts"; import { probablySupportsClipboardBlob } from "../clipboard"; import { isDarwin, isFirefox, isWindows } from "../constants"; -import { getShortcutFromShortcutName } from "../actions/shortcuts"; +import { t } from "../i18n"; +import { KEYS } from "../keys"; +import { getShortcutKey } from "../utils"; + +import { Dialog } from "./Dialog"; +import { ExternalLinkIcon, GithubIcon, youtubeIcon } from "./icons"; + +import "./HelpDialog.scss"; + +import type { JSX } from "react"; const Header = () => (
diff --git a/packages/excalidraw/components/HintViewer.tsx b/packages/excalidraw/components/HintViewer.tsx index f09f65852..c285e361b 100644 --- a/packages/excalidraw/components/HintViewer.tsx +++ b/packages/excalidraw/components/HintViewer.tsx @@ -1,5 +1,4 @@ -import { t } from "../i18n"; -import type { AppClassProperties, Device, UIAppState } from "../types"; +import { isEraserActive } from "../appState"; import { isFlowchartNodeElement, isImageElement, @@ -7,14 +6,18 @@ import { isTextBindableContainer, isTextElement, } from "../element/typeChecks"; -import { getShortcutKey } from "../utils"; -import { isEraserActive } from "../appState"; +import { t } from "../i18n"; + +import { getShortcutKey } from "../utils"; -import "./HintViewer.scss"; import { isNodeInFlowchart } from "../element/flowchart"; import { isGridModeEnabled } from "../snapping"; import { CANVAS_SEARCH_TAB, DEFAULT_SIDEBAR } from "../constants"; +import "./HintViewer.scss"; + +import type { AppClassProperties, Device, UIAppState } from "../types"; + interface HintViewerProps { appState: UIAppState; isMobile: boolean; diff --git a/packages/excalidraw/components/IconPicker.tsx b/packages/excalidraw/components/IconPicker.tsx index ee83f905a..b91d37c23 100644 --- a/packages/excalidraw/components/IconPicker.tsx +++ b/packages/excalidraw/components/IconPicker.tsx @@ -1,15 +1,18 @@ -import type { JSX } from "react"; -import React, { useEffect } from "react"; import * as Popover from "@radix-ui/react-popover"; -import { isArrowKey, KEYS } from "../keys"; -import { getLanguage, t } from "../i18n"; import clsx from "clsx"; -import Collapsible from "./Stats/Collapsible"; +import React, { useEffect } from "react"; + import { atom, useAtom } from "../editor-jotai"; +import { getLanguage, t } from "../i18n"; +import { isArrowKey, KEYS } from "../keys"; + +import Collapsible from "./Stats/Collapsible"; import { useDevice } from "./App"; import "./IconPicker.scss"; +import type { JSX } from "react"; + const moreOptionsAtom = atom(false); type Option = { diff --git a/packages/excalidraw/components/ImageExportDialog.tsx b/packages/excalidraw/components/ImageExportDialog.tsx index d06f4a842..0d19f5b5d 100644 --- a/packages/excalidraw/components/ImageExportDialog.tsx +++ b/packages/excalidraw/components/ImageExportDialog.tsx @@ -1,8 +1,6 @@ +import { exportToCanvas } from "@excalidraw/utils/export"; import React, { useEffect, useRef, useState } from "react"; -import type { ActionManager } from "../actions/manager"; -import type { AppClassProperties, BinaryFiles, UIAppState } from "../types"; - import { actionExportWithDarkMode, actionChangeExportBackground, @@ -17,25 +15,27 @@ import { isFirefox, EXPORT_SCALES, } from "../constants"; - +import { prepareElementsForExport } from "../data"; import { canvasToBlob } from "../data/blob"; import { nativeFileSystemSupported } from "../data/filesystem"; -import type { NonDeletedExcalidrawElement } from "../element/types"; +import { useCopyStatus } from "../hooks/useCopiedIndicator"; + import { t } from "../i18n"; import { isSomeElementSelected } from "../scene"; -import { exportToCanvas } from "@excalidraw/utils/export"; +import { cloneJSON } from "../utils"; import { copyIcon, downloadIcon, helpIcon } from "./icons"; import { Dialog } from "./Dialog"; import { RadioGroup } from "./RadioGroup"; import { Switch } from "./Switch"; import { Tooltip } from "./Tooltip"; +import { FilledButton } from "./FilledButton"; import "./ImageExportDialog.scss"; -import { FilledButton } from "./FilledButton"; -import { cloneJSON } from "../utils"; -import { prepareElementsForExport } from "../data"; -import { useCopyStatus } from "../hooks/useCopiedIndicator"; + +import type { ActionManager } from "../actions/manager"; +import type { NonDeletedExcalidrawElement } from "../element/types"; +import type { AppClassProperties, BinaryFiles, UIAppState } from "../types"; const supportsContextFilters = "filter" in document.createElement("canvas").getContext("2d")!; diff --git a/packages/excalidraw/components/InitializeApp.tsx b/packages/excalidraw/components/InitializeApp.tsx index 41f90ceeb..efc83a55b 100644 --- a/packages/excalidraw/components/InitializeApp.tsx +++ b/packages/excalidraw/components/InitializeApp.tsx @@ -1,9 +1,11 @@ import React, { useEffect, useState } from "react"; -import { LoadingMessage } from "./LoadingMessage"; -import type { Language } from "../i18n"; import { defaultLang, languages, setLanguage } from "../i18n"; + +import { LoadingMessage } from "./LoadingMessage"; + import type { Theme } from "../element/types"; +import type { Language } from "../i18n"; interface Props { langCode: Language["code"]; diff --git a/packages/excalidraw/components/Island.tsx b/packages/excalidraw/components/Island.tsx index 7baf307a0..8219e5fc7 100644 --- a/packages/excalidraw/components/Island.tsx +++ b/packages/excalidraw/components/Island.tsx @@ -1,8 +1,8 @@ -import "./Island.scss"; - import React from "react"; import clsx from "clsx"; +import "./Island.scss"; + type IslandProps = { children: React.ReactNode; padding?: number; diff --git a/packages/excalidraw/components/JSONExportDialog.tsx b/packages/excalidraw/components/JSONExportDialog.tsx index 527ad5746..ae203d3e4 100644 --- a/packages/excalidraw/components/JSONExportDialog.tsx +++ b/packages/excalidraw/components/JSONExportDialog.tsx @@ -1,19 +1,22 @@ import React from "react"; -import type { NonDeletedExcalidrawElement } from "../element/types"; -import { t } from "../i18n"; -import type { ExportOpts, BinaryFiles, UIAppState } from "../types"; -import { Dialog } from "./Dialog"; -import { exportToFileIcon, LinkIcon } from "./icons"; -import { ToolButton } from "./ToolButton"; import { actionSaveFileToDisk } from "../actions/actionExport"; + +import { trackEvent } from "../analytics"; +import { nativeFileSystemSupported } from "../data/filesystem"; +import { t } from "../i18n"; +import { getFrame } from "../utils"; + import { Card } from "./Card"; +import { Dialog } from "./Dialog"; +import { ToolButton } from "./ToolButton"; +import { exportToFileIcon, LinkIcon } from "./icons"; import "./ExportDialog.scss"; -import { nativeFileSystemSupported } from "../data/filesystem"; -import { trackEvent } from "../analytics"; + import type { ActionManager } from "../actions/manager"; -import { getFrame } from "../utils"; +import type { NonDeletedExcalidrawElement } from "../element/types"; +import type { ExportOpts, BinaryFiles, UIAppState } from "../types"; export type ExportCB = ( elements: readonly NonDeletedExcalidrawElement[], diff --git a/packages/excalidraw/components/LaserPointerButton.tsx b/packages/excalidraw/components/LaserPointerButton.tsx index 35470f6e7..237d0831c 100644 --- a/packages/excalidraw/components/LaserPointerButton.tsx +++ b/packages/excalidraw/components/LaserPointerButton.tsx @@ -1,9 +1,11 @@ +import clsx from "clsx"; + import "./ToolIcon.scss"; -import clsx from "clsx"; -import type { ToolButtonSize } from "./ToolButton"; import { laserPointerToolIcon } from "./icons"; +import type { ToolButtonSize } from "./ToolButton"; + type LaserPointerIconProps = { title?: string; name?: string; diff --git a/packages/excalidraw/components/LayerUI.tsx b/packages/excalidraw/components/LayerUI.tsx index 11914d09a..83ac7ae9c 100644 --- a/packages/excalidraw/components/LayerUI.tsx +++ b/packages/excalidraw/components/LayerUI.tsx @@ -1,12 +1,58 @@ import clsx from "clsx"; import React from "react"; -import type { ActionManager } from "../actions/manager"; + +import { mutateElement } from "../element/mutateElement"; +import { ShapeCache } from "../scene/ShapeCache"; +import Scene from "../scene/Scene"; +import { actionToggleStats } from "../actions"; +import { trackEvent } from "../analytics"; +import { isHandToolActive } from "../appState"; import { CLASSES, DEFAULT_SIDEBAR, TOOL_TYPE } from "../constants"; +import { TunnelsContext, useInitializeTunnels } from "../context/tunnels"; +import { UIAppStateContext } from "../context/ui-appState"; +import { useAtom, useAtomValue } from "../editor-jotai"; import { showSelectedShapeActions } from "../element"; -import type { NonDeletedExcalidrawElement } from "../element/types"; -import type { Language } from "../i18n"; import { t } from "../i18n"; import { calculateScrollCenter } from "../scene"; +import { capitalizeString, isShallowEqual } from "../utils"; + +import { SelectedShapeActions, ShapesSwitcher } from "./Actions"; +import { LoadingMessage } from "./LoadingMessage"; +import { LockButton } from "./LockButton"; +import { MobileMenu } from "./MobileMenu"; +import { PasteChartDialog } from "./PasteChartDialog"; +import { Section } from "./Section"; +import Stack from "./Stack"; +import { UserList } from "./UserList"; +import { PenModeButton } from "./PenModeButton"; +import Footer from "./footer/Footer"; +import { isSidebarDockedAtom } from "./Sidebar/Sidebar"; +import MainMenu from "./main-menu/MainMenu"; +import { ActiveConfirmDialog } from "./ActiveConfirmDialog"; +import { useDevice } from "./App"; +import { OverwriteConfirmDialog } from "./OverwriteConfirm/OverwriteConfirm"; +import { LibraryIcon } from "./icons"; +import { DefaultSidebar } from "./DefaultSidebar"; +import { TTDDialog } from "./TTDDialog/TTDDialog"; +import { Stats } from "./Stats"; +import ElementLinkDialog from "./ElementLinkDialog"; +import { ErrorDialog } from "./ErrorDialog"; +import { EyeDropper, activeEyeDropperAtom } from "./EyeDropper"; +import { FixedSideContainer } from "./FixedSideContainer"; +import { HandButton } from "./HandButton"; +import { HelpDialog } from "./HelpDialog"; +import { HintViewer } from "./HintViewer"; +import { ImageExportDialog } from "./ImageExportDialog"; +import { Island } from "./Island"; +import { JSONExportDialog } from "./JSONExportDialog"; +import { LaserPointerButton } from "./LaserPointerButton"; + +import "./LayerUI.scss"; +import "./Toolbar.scss"; + +import type { ActionManager } from "../actions/manager"; +import type { NonDeletedExcalidrawElement } from "../element/types"; +import type { Language } from "../i18n"; import type { AppProps, AppState, @@ -15,49 +61,6 @@ import type { UIAppState, AppClassProperties, } from "../types"; -import { capitalizeString, isShallowEqual } from "../utils"; -import { SelectedShapeActions, ShapesSwitcher } from "./Actions"; -import { ErrorDialog } from "./ErrorDialog"; -import { ImageExportDialog } from "./ImageExportDialog"; -import { FixedSideContainer } from "./FixedSideContainer"; -import { HintViewer } from "./HintViewer"; -import { Island } from "./Island"; -import { LoadingMessage } from "./LoadingMessage"; -import { LockButton } from "./LockButton"; -import { MobileMenu } from "./MobileMenu"; -import { PasteChartDialog } from "./PasteChartDialog"; -import { Section } from "./Section"; -import { HelpDialog } from "./HelpDialog"; -import Stack from "./Stack"; -import { UserList } from "./UserList"; -import { JSONExportDialog } from "./JSONExportDialog"; -import { PenModeButton } from "./PenModeButton"; -import { trackEvent } from "../analytics"; -import { useDevice } from "./App"; -import Footer from "./footer/Footer"; -import { isSidebarDockedAtom } from "./Sidebar/Sidebar"; -import { useAtom, useAtomValue } from "../editor-jotai"; -import MainMenu from "./main-menu/MainMenu"; -import { ActiveConfirmDialog } from "./ActiveConfirmDialog"; -import { OverwriteConfirmDialog } from "./OverwriteConfirm/OverwriteConfirm"; -import { HandButton } from "./HandButton"; -import { isHandToolActive } from "../appState"; -import { TunnelsContext, useInitializeTunnels } from "../context/tunnels"; -import { LibraryIcon } from "./icons"; -import { UIAppStateContext } from "../context/ui-appState"; -import { DefaultSidebar } from "./DefaultSidebar"; -import { EyeDropper, activeEyeDropperAtom } from "./EyeDropper"; -import { mutateElement } from "../element/mutateElement"; -import { ShapeCache } from "../scene/ShapeCache"; -import Scene from "../scene/Scene"; -import { LaserPointerButton } from "./LaserPointerButton"; -import { TTDDialog } from "./TTDDialog/TTDDialog"; -import { Stats } from "./Stats"; -import { actionToggleStats } from "../actions"; -import ElementLinkDialog from "./ElementLinkDialog"; - -import "./LayerUI.scss"; -import "./Toolbar.scss"; interface LayerUIProps { actionManager: ActionManager; diff --git a/packages/excalidraw/components/LibraryMenu.tsx b/packages/excalidraw/components/LibraryMenu.tsx index 0162d93a0..03c476e47 100644 --- a/packages/excalidraw/components/LibraryMenu.tsx +++ b/packages/excalidraw/components/LibraryMenu.tsx @@ -6,13 +6,36 @@ import React, { memo, useRef, } from "react"; -import type Library from "../data/library"; + +import { trackEvent } from "../analytics"; +import { LIBRARY_DISABLED_TYPES } from "../constants"; +import { useUIAppState } from "../context/ui-appState"; import { distributeLibraryItemsOnSquareGrid, libraryItemsAtom, } from "../data/library"; +import { atom, useAtom } from "../editor-jotai"; import { t } from "../i18n"; import { randomId } from "../random"; +import { getSelectedElements } from "../scene"; +import { isShallowEqual } from "../utils"; + +import { + useApp, + useAppProps, + useExcalidrawElements, + useExcalidrawSetAppState, +} from "./App"; +import { LibraryMenuControlButtons } from "./LibraryMenuControlButtons"; +import LibraryMenuItems from "./LibraryMenuItems"; +import Spinner from "./Spinner"; + +import "./LibraryMenu.scss"; + +import type { + ExcalidrawElement, + NonDeletedExcalidrawElement, +} from "../element/types"; import type { LibraryItems, LibraryItem, @@ -20,27 +43,7 @@ import type { UIAppState, AppClassProperties, } from "../types"; -import LibraryMenuItems from "./LibraryMenuItems"; -import { trackEvent } from "../analytics"; -import { atom, useAtom } from "../editor-jotai"; -import Spinner from "./Spinner"; -import { - useApp, - useAppProps, - useExcalidrawElements, - useExcalidrawSetAppState, -} from "./App"; -import { getSelectedElements } from "../scene"; -import { useUIAppState } from "../context/ui-appState"; - -import "./LibraryMenu.scss"; -import { LibraryMenuControlButtons } from "./LibraryMenuControlButtons"; -import type { - ExcalidrawElement, - NonDeletedExcalidrawElement, -} from "../element/types"; -import { LIBRARY_DISABLED_TYPES } from "../constants"; -import { isShallowEqual } from "../utils"; +import type Library from "../data/library"; export const isLibraryMenuOpenAtom = atom(false); diff --git a/packages/excalidraw/components/LibraryMenuBrowseButton.tsx b/packages/excalidraw/components/LibraryMenuBrowseButton.tsx index 43fbedd77..2b2623ed1 100644 --- a/packages/excalidraw/components/LibraryMenuBrowseButton.tsx +++ b/packages/excalidraw/components/LibraryMenuBrowseButton.tsx @@ -1,5 +1,6 @@ import { VERSIONS } from "../constants"; import { t } from "../i18n"; + import type { ExcalidrawProps, UIAppState } from "../types"; const LibraryMenuBrowseButton = ({ diff --git a/packages/excalidraw/components/LibraryMenuControlButtons.tsx b/packages/excalidraw/components/LibraryMenuControlButtons.tsx index b467ca39c..1ea722168 100644 --- a/packages/excalidraw/components/LibraryMenuControlButtons.tsx +++ b/packages/excalidraw/components/LibraryMenuControlButtons.tsx @@ -1,7 +1,9 @@ -import type { ExcalidrawProps, UIAppState } from "../types"; -import LibraryMenuBrowseButton from "./LibraryMenuBrowseButton"; import clsx from "clsx"; +import LibraryMenuBrowseButton from "./LibraryMenuBrowseButton"; + +import type { ExcalidrawProps, UIAppState } from "../types"; + export const LibraryMenuControlButtons = ({ libraryReturnUrl, theme, diff --git a/packages/excalidraw/components/LibraryMenuHeaderContent.tsx b/packages/excalidraw/components/LibraryMenuHeaderContent.tsx index bec31aa36..cc6c942ae 100644 --- a/packages/excalidraw/components/LibraryMenuHeaderContent.tsx +++ b/packages/excalidraw/components/LibraryMenuHeaderContent.tsx @@ -1,12 +1,23 @@ +import clsx from "clsx"; import { useCallback, useState } from "react"; -import { t } from "../i18n"; -import Trans from "./Trans"; -import { useAtom } from "../editor-jotai"; -import type { LibraryItem, LibraryItems, UIAppState } from "../types"; -import { useApp, useExcalidrawSetAppState } from "./App"; + +import { useUIAppState } from "../context/ui-appState"; +import { fileOpen } from "../data/filesystem"; import { saveLibraryAsJSON } from "../data/json"; -import type Library from "../data/library"; import { libraryItemsAtom } from "../data/library"; +import { useAtom } from "../editor-jotai"; +import { useLibraryCache } from "../hooks/useLibraryItemSvg"; +import { t } from "../i18n"; +import { muteFSAbortError } from "../utils"; + +import { useApp, useExcalidrawSetAppState } from "./App"; +import ConfirmDialog from "./ConfirmDialog"; +import { Dialog } from "./Dialog"; +import { isLibraryMenuOpenAtom } from "./LibraryMenu"; +import PublishLibrary from "./PublishLibrary"; +import { ToolButton } from "./ToolButton"; +import Trans from "./Trans"; +import DropdownMenu from "./dropdownMenu/DropdownMenu"; import { DotsIcon, ExportIcon, @@ -14,17 +25,9 @@ import { publishIcon, TrashIcon, } from "./icons"; -import { ToolButton } from "./ToolButton"; -import { fileOpen } from "../data/filesystem"; -import { muteFSAbortError } from "../utils"; -import ConfirmDialog from "./ConfirmDialog"; -import PublishLibrary from "./PublishLibrary"; -import { Dialog } from "./Dialog"; -import DropdownMenu from "./dropdownMenu/DropdownMenu"; -import { isLibraryMenuOpenAtom } from "./LibraryMenu"; -import { useUIAppState } from "../context/ui-appState"; -import clsx from "clsx"; -import { useLibraryCache } from "../hooks/useLibraryItemSvg"; + +import type Library from "../data/library"; +import type { LibraryItem, LibraryItems, UIAppState } from "../types"; const getSelectedItems = ( libraryItems: LibraryItems, diff --git a/packages/excalidraw/components/LibraryMenuItems.tsx b/packages/excalidraw/components/LibraryMenuItems.tsx index aa2c3e68e..af5b9d3e6 100644 --- a/packages/excalidraw/components/LibraryMenuItems.tsx +++ b/packages/excalidraw/components/LibraryMenuItems.tsx @@ -5,30 +5,33 @@ import React, { useRef, useState, } from "react"; -import { serializeLibraryAsJSON } from "../data/json"; -import { t } from "../i18n"; -import type { - ExcalidrawProps, - LibraryItem, - LibraryItems, - UIAppState, -} from "../types"; -import { arrayToMap } from "../utils"; -import Stack from "./Stack"; + import { MIME_TYPES } from "../constants"; -import Spinner from "./Spinner"; +import { serializeLibraryAsJSON } from "../data/json"; import { duplicateElements } from "../element/newElement"; +import { useLibraryCache } from "../hooks/useLibraryItemSvg"; +import { useScrollPosition } from "../hooks/useScrollPosition"; +import { t } from "../i18n"; +import { arrayToMap } from "../utils"; + import { LibraryMenuControlButtons } from "./LibraryMenuControlButtons"; import { LibraryDropdownMenu } from "./LibraryMenuHeaderContent"; import { LibraryMenuSection, LibraryMenuSectionGrid, } from "./LibraryMenuSection"; -import { useScrollPosition } from "../hooks/useScrollPosition"; -import { useLibraryCache } from "../hooks/useLibraryItemSvg"; +import Spinner from "./Spinner"; +import Stack from "./Stack"; import "./LibraryMenuItems.scss"; +import type { + ExcalidrawProps, + LibraryItem, + LibraryItems, + UIAppState, +} from "../types"; + // using an odd number of items per batch so the rendering creates an irregular // pattern which looks more organic const ITEMS_RENDERED_PER_BATCH = 17; diff --git a/packages/excalidraw/components/LibraryMenuSection.tsx b/packages/excalidraw/components/LibraryMenuSection.tsx index b07d6b1cc..5f7de2b09 100644 --- a/packages/excalidraw/components/LibraryMenuSection.tsx +++ b/packages/excalidraw/components/LibraryMenuSection.tsx @@ -1,10 +1,13 @@ -import type { ReactNode } from "react"; import React, { memo, useEffect, useState } from "react"; + +import { useTransition } from "../hooks/useTransition"; + import { EmptyLibraryUnit, LibraryUnit } from "./LibraryUnit"; -import type { LibraryItem } from "../types"; + import type { ExcalidrawElement, NonDeleted } from "../element/types"; import type { SvgCache } from "../hooks/useLibraryItemSvg"; -import { useTransition } from "../hooks/useTransition"; +import type { LibraryItem } from "../types"; +import type { ReactNode } from "react"; type LibraryOrPendingItem = ( | LibraryItem diff --git a/packages/excalidraw/components/LibraryUnit.tsx b/packages/excalidraw/components/LibraryUnit.tsx index 71e1a00f5..9cd891715 100644 --- a/packages/excalidraw/components/LibraryUnit.tsx +++ b/packages/excalidraw/components/LibraryUnit.tsx @@ -1,12 +1,16 @@ import clsx from "clsx"; import { memo, useEffect, useRef, useState } from "react"; + +import { useLibraryItemSvg } from "../hooks/useLibraryItemSvg"; + import { useDevice } from "./App"; -import type { LibraryItem } from "../types"; -import "./LibraryUnit.scss"; import { CheckboxItem } from "./CheckboxItem"; import { PlusIcon } from "./icons"; + +import "./LibraryUnit.scss"; + +import type { LibraryItem } from "../types"; import type { SvgCache } from "../hooks/useLibraryItemSvg"; -import { useLibraryItemSvg } from "../hooks/useLibraryItemSvg"; export const LibraryUnit = memo( ({ diff --git a/packages/excalidraw/components/LoadingMessage.tsx b/packages/excalidraw/components/LoadingMessage.tsx index b00725257..bdcc5a341 100644 --- a/packages/excalidraw/components/LoadingMessage.tsx +++ b/packages/excalidraw/components/LoadingMessage.tsx @@ -1,8 +1,11 @@ -import { t } from "../i18n"; -import { useState, useEffect } from "react"; -import Spinner from "./Spinner"; import clsx from "clsx"; +import { useState, useEffect } from "react"; + import { THEME } from "../constants"; +import { t } from "../i18n"; + +import Spinner from "./Spinner"; + import type { Theme } from "../element/types"; export const LoadingMessage: React.FC<{ delay?: number; theme?: Theme }> = ({ diff --git a/packages/excalidraw/components/LockButton.tsx b/packages/excalidraw/components/LockButton.tsx index f7913d12d..8363ffa0c 100644 --- a/packages/excalidraw/components/LockButton.tsx +++ b/packages/excalidraw/components/LockButton.tsx @@ -1,9 +1,11 @@ +import clsx from "clsx"; + import "./ToolIcon.scss"; -import clsx from "clsx"; -import type { ToolButtonSize } from "./ToolButton"; import { LockedIcon, UnlockedIcon } from "./icons"; +import type { ToolButtonSize } from "./ToolButton"; + type LockIconProps = { title?: string; name?: string; diff --git a/packages/excalidraw/components/MagicButton.tsx b/packages/excalidraw/components/MagicButton.tsx index eedc63b3d..b2bbd03e2 100644 --- a/packages/excalidraw/components/MagicButton.tsx +++ b/packages/excalidraw/components/MagicButton.tsx @@ -1,9 +1,10 @@ -import type { JSX } from "react"; import clsx from "clsx"; -import type { ToolButtonSize } from "./ToolButton"; import "./ToolIcon.scss"; +import type { ToolButtonSize } from "./ToolButton"; +import type { JSX } from "react"; + const DEFAULT_SIZE: ToolButtonSize = "small"; export const ElementCanvasButton = (props: { diff --git a/packages/excalidraw/components/MobileMenu.tsx b/packages/excalidraw/components/MobileMenu.tsx index bef99a91a..08a20174c 100644 --- a/packages/excalidraw/components/MobileMenu.tsx +++ b/packages/excalidraw/components/MobileMenu.tsx @@ -1,5 +1,24 @@ -import type { JSX } from "react"; import React from "react"; + +import { isHandToolActive } from "../appState"; +import { useTunnels } from "../context/tunnels"; +import { showSelectedShapeActions } from "../element"; +import { t } from "../i18n"; +import { calculateScrollCenter } from "../scene"; +import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars"; + +import { SelectedShapeActions, ShapesSwitcher } from "./Actions"; +import { FixedSideContainer } from "./FixedSideContainer"; +import { HandButton } from "./HandButton"; +import { HintViewer } from "./HintViewer"; +import { Island } from "./Island"; +import { LockButton } from "./LockButton"; +import { PenModeButton } from "./PenModeButton"; +import { Section } from "./Section"; +import Stack from "./Stack"; + +import type { ActionManager } from "../actions/manager"; +import type { NonDeletedExcalidrawElement } from "../element/types"; import type { AppClassProperties, AppProps, @@ -8,23 +27,7 @@ import type { ExcalidrawProps, UIAppState, } from "../types"; -import type { ActionManager } from "../actions/manager"; -import { t } from "../i18n"; -import Stack from "./Stack"; -import { showSelectedShapeActions } from "../element"; -import type { NonDeletedExcalidrawElement } from "../element/types"; -import { FixedSideContainer } from "./FixedSideContainer"; -import { Island } from "./Island"; -import { HintViewer } from "./HintViewer"; -import { calculateScrollCenter } from "../scene"; -import { SelectedShapeActions, ShapesSwitcher } from "./Actions"; -import { Section } from "./Section"; -import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars"; -import { LockButton } from "./LockButton"; -import { PenModeButton } from "./PenModeButton"; -import { HandButton } from "./HandButton"; -import { isHandToolActive } from "../appState"; -import { useTunnels } from "../context/tunnels"; +import type { JSX } from "react"; type MobileMenuProps = { appState: UIAppState; diff --git a/packages/excalidraw/components/Modal.tsx b/packages/excalidraw/components/Modal.tsx index e8fff195b..a5ade9efa 100644 --- a/packages/excalidraw/components/Modal.tsx +++ b/packages/excalidraw/components/Modal.tsx @@ -1,11 +1,13 @@ +import clsx from "clsx"; +import { useRef } from "react"; +import { createPortal } from "react-dom"; + +import { useCreatePortalContainer } from "../hooks/useCreatePortalContainer"; +import { KEYS } from "../keys"; + import "./Modal.scss"; -import { createPortal } from "react-dom"; -import clsx from "clsx"; -import { KEYS } from "../keys"; import type { AppState } from "../types"; -import { useCreatePortalContainer } from "../hooks/useCreatePortalContainer"; -import { useRef } from "react"; export const Modal: React.FC<{ className?: string; diff --git a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.tsx b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.tsx index 4bf8d6717..f2de877e0 100644 --- a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.tsx +++ b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.tsx @@ -3,12 +3,13 @@ import React from "react"; import { useTunnels } from "../../context/tunnels"; import { useAtom } from "../../editor-jotai"; import { Dialog } from "../Dialog"; +import { FilledButton } from "../FilledButton"; import { withInternalFallback } from "../hoc/withInternalFallback"; +import { alertTriangleIcon } from "../icons"; + +import { Actions, Action } from "./OverwriteConfirmActions"; import { overwriteConfirmStateAtom } from "./OverwriteConfirmState"; -import { FilledButton } from "../FilledButton"; -import { alertTriangleIcon } from "../icons"; -import { Actions, Action } from "./OverwriteConfirmActions"; import "./OverwriteConfirm.scss"; export type OverwriteConfirmDialogProps = { diff --git a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmActions.tsx b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmActions.tsx index 5da0a08ef..e5fb56d85 100644 --- a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmActions.tsx +++ b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmActions.tsx @@ -1,9 +1,10 @@ import React from "react"; -import { FilledButton } from "../FilledButton"; -import { useExcalidrawActionManager, useExcalidrawSetAppState } from "../App"; + import { actionSaveFileToDisk } from "../../actions"; -import { useI18n } from "../../i18n"; import { actionChangeExportEmbedScene } from "../../actions/actionExport"; +import { useI18n } from "../../i18n"; +import { useExcalidrawActionManager, useExcalidrawSetAppState } from "../App"; +import { FilledButton } from "../FilledButton"; export type ActionProps = { title: string; diff --git a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.ts b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.ts index 04616aa8d..cefb2153a 100644 --- a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.ts +++ b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.ts @@ -1,4 +1,5 @@ import { atom, editorJotaiStore } from "../../editor-jotai"; + import type React from "react"; export type OverwriteConfirmState = diff --git a/packages/excalidraw/components/PasteChartDialog.tsx b/packages/excalidraw/components/PasteChartDialog.tsx index 08a5634a9..58bd5a385 100644 --- a/packages/excalidraw/components/PasteChartDialog.tsx +++ b/packages/excalidraw/components/PasteChartDialog.tsx @@ -1,17 +1,20 @@ import oc from "open-color"; import React, { useLayoutEffect, useRef, useState } from "react"; + import { trackEvent } from "../analytics"; -import type { ChartElements, Spreadsheet } from "../charts"; import { renderSpreadsheet } from "../charts"; -import type { ChartType } from "../element/types"; import { t } from "../i18n"; import { exportToSvg } from "../scene/export"; -import type { UIAppState } from "../types"; + import { useApp } from "./App"; import { Dialog } from "./Dialog"; import "./PasteChartDialog.scss"; +import type { ChartElements, Spreadsheet } from "../charts"; +import type { ChartType } from "../element/types"; +import type { UIAppState } from "../types"; + type OnInsertChart = (chartType: ChartType, elements: ChartElements) => void; const ChartPreviewBtn = (props: { diff --git a/packages/excalidraw/components/PenModeButton.tsx b/packages/excalidraw/components/PenModeButton.tsx index ca5ceb2b6..e5df8ac9b 100644 --- a/packages/excalidraw/components/PenModeButton.tsx +++ b/packages/excalidraw/components/PenModeButton.tsx @@ -1,9 +1,11 @@ +import clsx from "clsx"; + import "./ToolIcon.scss"; -import clsx from "clsx"; -import type { ToolButtonSize } from "./ToolButton"; import { PenModeIcon } from "./icons"; +import type { ToolButtonSize } from "./ToolButton"; + type PenModeIconProps = { title?: string; name?: string; diff --git a/packages/excalidraw/components/Popover.tsx b/packages/excalidraw/components/Popover.tsx index 987e9fb91..2bd72b20b 100644 --- a/packages/excalidraw/components/Popover.tsx +++ b/packages/excalidraw/components/Popover.tsx @@ -1,8 +1,10 @@ import React, { useLayoutEffect, useRef, useEffect } from "react"; -import "./Popover.scss"; import { unstable_batchedUpdates } from "react-dom"; -import { queryFocusableElements } from "../utils"; + import { KEYS } from "../keys"; +import { queryFocusableElements } from "../utils"; + +import "./Popover.scss"; type Props = { top?: number; diff --git a/packages/excalidraw/components/ProjectName.tsx b/packages/excalidraw/components/ProjectName.tsx index 592961793..0d60ddd66 100644 --- a/packages/excalidraw/components/ProjectName.tsx +++ b/packages/excalidraw/components/ProjectName.tsx @@ -1,12 +1,13 @@ -import "./TextInput.scss"; - import React, { useState } from "react"; -import { focusNearestParent } from "../utils"; -import "./ProjectName.scss"; -import { useExcalidrawContainer } from "./App"; +import { focusNearestParent } from "../utils"; import { KEYS } from "../keys"; +import { useExcalidrawContainer } from "./App"; + +import "./TextInput.scss"; +import "./ProjectName.scss"; + type Props = { value: string; onChange: (value: string) => void; diff --git a/packages/excalidraw/components/PropertiesPopover.tsx b/packages/excalidraw/components/PropertiesPopover.tsx index 455af1184..a30f1374d 100644 --- a/packages/excalidraw/components/PropertiesPopover.tsx +++ b/packages/excalidraw/components/PropertiesPopover.tsx @@ -1,10 +1,11 @@ -import React, { type ReactNode } from "react"; -import clsx from "clsx"; import * as Popover from "@radix-ui/react-popover"; +import clsx from "clsx"; +import React, { type ReactNode } from "react"; + +import { isInteractive } from "../utils"; import { useDevice } from "./App"; import { Island } from "./Island"; -import { isInteractive } from "../utils"; interface PropertiesPopoverProps { className?: string; diff --git a/packages/excalidraw/components/PublishLibrary.tsx b/packages/excalidraw/components/PublishLibrary.tsx index fe68f880d..8a7188b63 100644 --- a/packages/excalidraw/components/PublishLibrary.tsx +++ b/packages/excalidraw/components/PublishLibrary.tsx @@ -1,13 +1,7 @@ -import type { ReactNode } from "react"; -import { useCallback, useEffect, useRef, useState } from "react"; -import OpenColor from "open-color"; - -import { Dialog } from "./Dialog"; -import { t } from "../i18n"; -import Trans from "./Trans"; - -import type { LibraryItems, LibraryItem, UIAppState } from "../types"; import { exportToCanvas, exportToSvg } from "@excalidraw/utils/export"; +import OpenColor from "open-color"; +import { useCallback, useEffect, useRef, useState } from "react"; + import { EDITOR_LS_KEYS, EXPORT_DATA_TYPES, @@ -15,16 +9,23 @@ import { MIME_TYPES, VERSIONS, } from "../constants"; -import type { ExportedLibraryData } from "../data/types"; -import { canvasToBlob, resizeImageFile } from "../data/blob"; -import { chunk } from "../utils"; -import DialogActionButton from "./DialogActionButton"; -import { CloseIcon } from "./icons"; -import { ToolButton } from "./ToolButton"; import { EditorLocalStorage } from "../data/EditorLocalStorage"; +import { canvasToBlob, resizeImageFile } from "../data/blob"; +import { t } from "../i18n"; +import { chunk } from "../utils"; + +import { Dialog } from "./Dialog"; +import DialogActionButton from "./DialogActionButton"; +import { ToolButton } from "./ToolButton"; +import Trans from "./Trans"; +import { CloseIcon } from "./icons"; import "./PublishLibrary.scss"; +import type { ReactNode } from "react"; +import type { ExportedLibraryData } from "../data/types"; +import type { LibraryItems, LibraryItem, UIAppState } from "../types"; + interface PublishLibraryDataParams { authorName: string; githubHandle: string; diff --git a/packages/excalidraw/components/QuickSearch.tsx b/packages/excalidraw/components/QuickSearch.tsx index c7ec78b50..83b65183f 100644 --- a/packages/excalidraw/components/QuickSearch.tsx +++ b/packages/excalidraw/components/QuickSearch.tsx @@ -1,5 +1,6 @@ import clsx from "clsx"; import React from "react"; + import { searchIcon } from "./icons"; import "./QuickSearch.scss"; diff --git a/packages/excalidraw/components/RadioGroup.tsx b/packages/excalidraw/components/RadioGroup.tsx index 64d4d587d..c08355aa2 100644 --- a/packages/excalidraw/components/RadioGroup.tsx +++ b/packages/excalidraw/components/RadioGroup.tsx @@ -1,4 +1,5 @@ import clsx from "clsx"; + import "./RadioGroup.scss"; export type RadioGroupChoice = { diff --git a/packages/excalidraw/components/Range.tsx b/packages/excalidraw/components/Range.tsx index cd1869cc0..3ab9ede15 100644 --- a/packages/excalidraw/components/Range.tsx +++ b/packages/excalidraw/components/Range.tsx @@ -1,6 +1,8 @@ import React, { useEffect } from "react"; + import { getFormValue } from "../actions/actionProperties"; import { t } from "../i18n"; + import "./Range.scss"; export type RangeProps = { diff --git a/packages/excalidraw/components/SVGLayer.tsx b/packages/excalidraw/components/SVGLayer.tsx index 667b89ab7..815d463a3 100644 --- a/packages/excalidraw/components/SVGLayer.tsx +++ b/packages/excalidraw/components/SVGLayer.tsx @@ -1,8 +1,9 @@ import { useEffect, useRef } from "react"; -import type { Trail } from "../animated-trail"; import "./SVGLayer.scss"; +import type { Trail } from "../animated-trail"; + type SVGLayerProps = { trails: Trail[]; }; diff --git a/packages/excalidraw/components/SearchMenu.tsx b/packages/excalidraw/components/SearchMenu.tsx index 873de5adb..70d3f4f27 100644 --- a/packages/excalidraw/components/SearchMenu.tsx +++ b/packages/excalidraw/components/SearchMenu.tsx @@ -1,25 +1,29 @@ -import { Fragment, memo, useEffect, useRef, useState } from "react"; -import { collapseDownIcon, upIcon, searchIcon } from "./icons"; -import { TextField } from "./TextField"; -import { Button } from "./Button"; -import { useApp, useExcalidrawSetAppState } from "./App"; -import debounce from "lodash.debounce"; -import type { AppClassProperties } from "../types"; -import { isTextElement, newTextElement } from "../element"; -import type { ExcalidrawTextElement } from "../element/types"; -import { addEventListener, getFontString } from "../utils"; -import { KEYS } from "../keys"; +import { round } from "@excalidraw/math"; import clsx from "clsx"; -import { atom, useAtom } from "../editor-jotai"; -import { t } from "../i18n"; -import { isElementCompletelyInViewport } from "../element/sizeHelpers"; -import { randomInteger } from "../random"; +import debounce from "lodash.debounce"; +import { Fragment, memo, useEffect, useRef, useState } from "react"; + import { CLASSES, EVENT } from "../constants"; +import { atom, useAtom } from "../editor-jotai"; +import { isTextElement, newTextElement } from "../element"; +import { isElementCompletelyInViewport } from "../element/sizeHelpers"; + +import { measureText } from "../element/textMeasurements"; import { useStable } from "../hooks/useStable"; +import { t } from "../i18n"; +import { KEYS } from "../keys"; +import { randomInteger } from "../random"; +import { addEventListener, getFontString } from "../utils"; + +import { useApp, useExcalidrawSetAppState } from "./App"; +import { Button } from "./Button"; +import { TextField } from "./TextField"; +import { collapseDownIcon, upIcon, searchIcon } from "./icons"; import "./SearchMenu.scss"; -import { round } from "@excalidraw/math"; -import { measureText } from "../element/textMeasurements"; + +import type { ExcalidrawTextElement } from "../element/types"; +import type { AppClassProperties } from "../types"; const searchQueryAtom = atom(""); export const searchItemInFocusAtom = atom(null); diff --git a/packages/excalidraw/components/Section.tsx b/packages/excalidraw/components/Section.tsx index 8cd0aaab5..e7e357bb5 100644 --- a/packages/excalidraw/components/Section.tsx +++ b/packages/excalidraw/components/Section.tsx @@ -1,5 +1,7 @@ import React from "react"; + import { t } from "../i18n"; + import { useExcalidrawContainer } from "./App"; export const Section: React.FC<{ diff --git a/packages/excalidraw/components/ShareableLinkDialog.tsx b/packages/excalidraw/components/ShareableLinkDialog.tsx index 987c7bf61..514a77632 100644 --- a/packages/excalidraw/components/ShareableLinkDialog.tsx +++ b/packages/excalidraw/components/ShareableLinkDialog.tsx @@ -1,12 +1,12 @@ import { useRef, useState } from "react"; import { copyTextToSystemClipboard } from "../clipboard"; +import { useCopyStatus } from "../hooks/useCopiedIndicator"; import { useI18n } from "../i18n"; import { Dialog } from "./Dialog"; -import { TextField } from "./TextField"; import { FilledButton } from "./FilledButton"; -import { useCopyStatus } from "../hooks/useCopiedIndicator"; +import { TextField } from "./TextField"; import { copyIcon } from "./icons"; import "./ShareableLinkDialog.scss"; diff --git a/packages/excalidraw/components/Sidebar/Sidebar.test.tsx b/packages/excalidraw/components/Sidebar/Sidebar.test.tsx index b61529d9e..39a1cbc63 100644 --- a/packages/excalidraw/components/Sidebar/Sidebar.test.tsx +++ b/packages/excalidraw/components/Sidebar/Sidebar.test.tsx @@ -1,4 +1,6 @@ import React from "react"; +import { vi } from "vitest"; + import { DEFAULT_SIDEBAR } from "../../constants"; import { Excalidraw, Sidebar } from "../../index"; import { @@ -10,7 +12,7 @@ import { waitFor, withExcalidrawDimensions, } from "../../tests/test-utils"; -import { vi } from "vitest"; + import { assertExcalidrawWithSidebar, assertSidebarDockButton, diff --git a/packages/excalidraw/components/Sidebar/Sidebar.tsx b/packages/excalidraw/components/Sidebar/Sidebar.tsx index 7c747d2f4..8a008fd9f 100644 --- a/packages/excalidraw/components/Sidebar/Sidebar.tsx +++ b/packages/excalidraw/components/Sidebar/Sidebar.tsx @@ -1,3 +1,4 @@ +import clsx from "clsx"; import React, { useEffect, useLayoutEffect, @@ -7,26 +8,28 @@ import React, { useImperativeHandle, useCallback, } from "react"; -import { Island } from "../Island"; -import { atom, useSetAtom } from "../../editor-jotai"; -import type { SidebarProps, SidebarPropsContextValue } from "./common"; -import { SidebarPropsContext } from "./common"; -import { SidebarHeader } from "./SidebarHeader"; -import clsx from "clsx"; -import { useDevice, useExcalidrawSetAppState } from "../App"; -import { updateObject } from "../../utils"; -import { KEYS } from "../../keys"; + import { EVENT } from "../../constants"; -import { SidebarTrigger } from "./SidebarTrigger"; -import { SidebarTabTriggers } from "./SidebarTabTriggers"; +import { useUIAppState } from "../../context/ui-appState"; +import { atom, useSetAtom } from "../../editor-jotai"; +import { useOutsideClick } from "../../hooks/useOutsideClick"; +import { KEYS } from "../../keys"; +import { updateObject } from "../../utils"; +import { useDevice, useExcalidrawSetAppState } from "../App"; +import { Island } from "../Island"; + +import { SidebarHeader } from "./SidebarHeader"; import { SidebarTabTrigger } from "./SidebarTabTrigger"; +import { SidebarTabTriggers } from "./SidebarTabTriggers"; +import { SidebarTrigger } from "./SidebarTrigger"; +import { SidebarPropsContext } from "./common"; import { SidebarTabs } from "./SidebarTabs"; import { SidebarTab } from "./SidebarTab"; -import { useUIAppState } from "../../context/ui-appState"; -import { useOutsideClick } from "../../hooks/useOutsideClick"; import "./Sidebar.scss"; +import type { SidebarProps, SidebarPropsContextValue } from "./common"; + /** * Flags whether the currently rendered Sidebar is docked or not, for use * in upstream components that need to act on this (e.g. LayerUI to shift the diff --git a/packages/excalidraw/components/Sidebar/SidebarHeader.tsx b/packages/excalidraw/components/Sidebar/SidebarHeader.tsx index 6d046aba3..9c9e51cf5 100644 --- a/packages/excalidraw/components/Sidebar/SidebarHeader.tsx +++ b/packages/excalidraw/components/Sidebar/SidebarHeader.tsx @@ -1,11 +1,13 @@ import clsx from "clsx"; import { useContext } from "react"; + import { t } from "../../i18n"; import { useDevice } from "../App"; -import { SidebarPropsContext } from "./common"; -import { CloseIcon, PinIcon } from "../icons"; -import { Tooltip } from "../Tooltip"; import { Button } from "../Button"; +import { Tooltip } from "../Tooltip"; +import { CloseIcon, PinIcon } from "../icons"; + +import { SidebarPropsContext } from "./common"; export const SidebarHeader = ({ children, diff --git a/packages/excalidraw/components/Sidebar/SidebarTab.tsx b/packages/excalidraw/components/Sidebar/SidebarTab.tsx index 6fddab0d6..0525662f7 100644 --- a/packages/excalidraw/components/Sidebar/SidebarTab.tsx +++ b/packages/excalidraw/components/Sidebar/SidebarTab.tsx @@ -1,4 +1,5 @@ import * as RadixTabs from "@radix-ui/react-tabs"; + import type { SidebarTabName } from "../../types"; export const SidebarTab = ({ diff --git a/packages/excalidraw/components/Sidebar/SidebarTabTrigger.tsx b/packages/excalidraw/components/Sidebar/SidebarTabTrigger.tsx index 8509ef23d..9f2c09bcc 100644 --- a/packages/excalidraw/components/Sidebar/SidebarTabTrigger.tsx +++ b/packages/excalidraw/components/Sidebar/SidebarTabTrigger.tsx @@ -1,4 +1,5 @@ import * as RadixTabs from "@radix-ui/react-tabs"; + import type { SidebarTabName } from "../../types"; export const SidebarTabTrigger = ({ diff --git a/packages/excalidraw/components/Sidebar/SidebarTabs.tsx b/packages/excalidraw/components/Sidebar/SidebarTabs.tsx index a681b5ee5..448840c4a 100644 --- a/packages/excalidraw/components/Sidebar/SidebarTabs.tsx +++ b/packages/excalidraw/components/Sidebar/SidebarTabs.tsx @@ -1,4 +1,5 @@ import * as RadixTabs from "@radix-ui/react-tabs"; + import { useUIAppState } from "../../context/ui-appState"; import { useExcalidrawSetAppState } from "../App"; diff --git a/packages/excalidraw/components/Sidebar/SidebarTrigger.tsx b/packages/excalidraw/components/Sidebar/SidebarTrigger.tsx index a26e52d23..6e8bf374c 100644 --- a/packages/excalidraw/components/Sidebar/SidebarTrigger.tsx +++ b/packages/excalidraw/components/Sidebar/SidebarTrigger.tsx @@ -1,10 +1,12 @@ -import { useExcalidrawSetAppState } from "../App"; -import type { SidebarTriggerProps } from "./common"; -import { useUIAppState } from "../../context/ui-appState"; import clsx from "clsx"; +import { useUIAppState } from "../../context/ui-appState"; +import { useExcalidrawSetAppState } from "../App"; + import "./SidebarTrigger.scss"; +import type { SidebarTriggerProps } from "./common"; + export const SidebarTrigger = ({ name, tab, diff --git a/packages/excalidraw/components/Sidebar/common.ts b/packages/excalidraw/components/Sidebar/common.ts index 35c0c8bb1..239b16fc9 100644 --- a/packages/excalidraw/components/Sidebar/common.ts +++ b/packages/excalidraw/components/Sidebar/common.ts @@ -1,6 +1,7 @@ -import type { JSX } from "react"; import React from "react"; + import type { AppState, SidebarName, SidebarTabName } from "../../types"; +import type { JSX } from "react"; export type SidebarTriggerProps = { name: SidebarName; diff --git a/packages/excalidraw/components/Sidebar/siderbar.test.helpers.tsx b/packages/excalidraw/components/Sidebar/siderbar.test.helpers.tsx index c2a37431b..650eca6b0 100644 --- a/packages/excalidraw/components/Sidebar/siderbar.test.helpers.tsx +++ b/packages/excalidraw/components/Sidebar/siderbar.test.helpers.tsx @@ -1,4 +1,5 @@ import React from "react"; + import { Excalidraw } from "../.."; import { GlobalTestState, diff --git a/packages/excalidraw/components/Stack.tsx b/packages/excalidraw/components/Stack.tsx index f45d47118..75e559aa3 100644 --- a/packages/excalidraw/components/Stack.tsx +++ b/packages/excalidraw/components/Stack.tsx @@ -1,8 +1,8 @@ -import "./Stack.scss"; - import React, { forwardRef } from "react"; import clsx from "clsx"; +import "./Stack.scss"; + type StackProps = { children: React.ReactNode; gap?: number; diff --git a/packages/excalidraw/components/Stats/Angle.tsx b/packages/excalidraw/components/Stats/Angle.tsx index 409476ada..10c76d519 100644 --- a/packages/excalidraw/components/Stats/Angle.tsx +++ b/packages/excalidraw/components/Stats/Angle.tsx @@ -1,15 +1,19 @@ +import { degreesToRadians, radiansToDegrees } from "@excalidraw/math"; + +import type { Degrees } from "@excalidraw/math"; + import { mutateElement } from "../../element/mutateElement"; import { getBoundTextElement } from "../../element/textElement"; import { isArrowElement, isElbowArrow } from "../../element/typeChecks"; -import type { ExcalidrawElement } from "../../element/types"; import { angleIcon } from "../icons"; + import DragInput from "./DragInput"; -import type { DragInputCallbackType } from "./DragInput"; import { getStepSizedValue, isPropertyEditable, updateBindings } from "./utils"; + +import type { DragInputCallbackType } from "./DragInput"; +import type { ExcalidrawElement } from "../../element/types"; import type Scene from "../../scene/Scene"; import type { AppState } from "../../types"; -import type { Degrees } from "@excalidraw/math"; -import { degreesToRadians, radiansToDegrees } from "@excalidraw/math"; interface AngleProps { element: ExcalidrawElement; diff --git a/packages/excalidraw/components/Stats/CanvasGrid.tsx b/packages/excalidraw/components/Stats/CanvasGrid.tsx index a08f709ae..4611365f4 100644 --- a/packages/excalidraw/components/Stats/CanvasGrid.tsx +++ b/packages/excalidraw/components/Stats/CanvasGrid.tsx @@ -1,8 +1,10 @@ +import { getNormalizedGridStep } from "../../scene"; + import StatsDragInput from "./DragInput"; +import { getStepSizedValue } from "./utils"; + import type Scene from "../../scene/Scene"; import type { AppState } from "../../types"; -import { getStepSizedValue } from "./utils"; -import { getNormalizedGridStep } from "../../scene"; interface PositionProps { property: "gridStep"; diff --git a/packages/excalidraw/components/Stats/Dimension.tsx b/packages/excalidraw/components/Stats/Dimension.tsx index ce096d280..f58c06acc 100644 --- a/packages/excalidraw/components/Stats/Dimension.tsx +++ b/packages/excalidraw/components/Stats/Dimension.tsx @@ -1,18 +1,21 @@ -import type { ExcalidrawElement } from "../../element/types"; -import DragInput from "./DragInput"; -import type { DragInputCallbackType } from "./DragInput"; -import { getStepSizedValue, isPropertyEditable } from "./utils"; +import { clamp, round } from "@excalidraw/math"; + import { MIN_WIDTH_OR_HEIGHT } from "../../constants"; -import { resizeSingleElement } from "../../element/resizeElements"; -import type Scene from "../../scene/Scene"; -import type { AppState } from "../../types"; -import { isImageElement } from "../../element/typeChecks"; import { MINIMAL_CROP_SIZE, getUncroppedWidthAndHeight, } from "../../element/cropElement"; import { mutateElement } from "../../element/mutateElement"; -import { clamp, round } from "@excalidraw/math"; +import { resizeSingleElement } from "../../element/resizeElements"; +import { isImageElement } from "../../element/typeChecks"; + +import DragInput from "./DragInput"; +import { getStepSizedValue, isPropertyEditable } from "./utils"; + +import type { DragInputCallbackType } from "./DragInput"; +import type { ExcalidrawElement } from "../../element/types"; +import type Scene from "../../scene/Scene"; +import type { AppState } from "../../types"; interface DimensionDragInputProps { property: "width" | "height"; diff --git a/packages/excalidraw/components/Stats/DragInput.tsx b/packages/excalidraw/components/Stats/DragInput.tsx index 82d6419c0..fbea33e55 100644 --- a/packages/excalidraw/components/Stats/DragInput.tsx +++ b/packages/excalidraw/components/Stats/DragInput.tsx @@ -1,19 +1,22 @@ -import { useEffect, useRef, useState } from "react"; -import { EVENT } from "../../constants"; -import { KEYS } from "../../keys"; -import type { ElementsMap, ExcalidrawElement } from "../../element/types"; -import { deepCopyElement } from "../../element/newElement"; import clsx from "clsx"; +import { useEffect, useRef, useState } from "react"; + +import { EVENT } from "../../constants"; +import { deepCopyElement } from "../../element/newElement"; +import { KEYS } from "../../keys"; +import { CaptureUpdateAction } from "../../store"; +import { cloneJSON } from "../../utils"; import { useApp } from "../App"; import { InlineIcon } from "../InlineIcon"; -import type { StatsInputProperty } from "./utils"; + import { SMALLEST_DELTA } from "./utils"; -import { CaptureUpdateAction } from "../../store"; -import type Scene from "../../scene/Scene"; import "./DragInput.scss"; + +import type { StatsInputProperty } from "./utils"; +import type { ElementsMap, ExcalidrawElement } from "../../element/types"; +import type Scene from "../../scene/Scene"; import type { AppState } from "../../types"; -import { cloneJSON } from "../../utils"; export type DragInputCallbackType< P extends StatsInputProperty, diff --git a/packages/excalidraw/components/Stats/FontSize.tsx b/packages/excalidraw/components/Stats/FontSize.tsx index 13dc6dbee..4fbdab833 100644 --- a/packages/excalidraw/components/Stats/FontSize.tsx +++ b/packages/excalidraw/components/Stats/FontSize.tsx @@ -1,17 +1,19 @@ +import { isTextElement, redrawTextBoundingBox } from "../../element"; +import { mutateElement } from "../../element/mutateElement"; +import { getBoundTextElement } from "../../element/textElement"; +import { hasBoundTextElement } from "../../element/typeChecks"; +import { fontSizeIcon } from "../icons"; + +import StatsDragInput from "./DragInput"; +import { getStepSizedValue } from "./utils"; + +import type { DragInputCallbackType } from "./DragInput"; import type { ExcalidrawElement, ExcalidrawTextElement, } from "../../element/types"; -import StatsDragInput from "./DragInput"; -import type { DragInputCallbackType } from "./DragInput"; -import { mutateElement } from "../../element/mutateElement"; -import { getStepSizedValue } from "./utils"; -import { fontSizeIcon } from "../icons"; import type Scene from "../../scene/Scene"; import type { AppState } from "../../types"; -import { isTextElement, redrawTextBoundingBox } from "../../element"; -import { hasBoundTextElement } from "../../element/typeChecks"; -import { getBoundTextElement } from "../../element/textElement"; interface FontSizeProps { element: ExcalidrawElement; diff --git a/packages/excalidraw/components/Stats/MultiAngle.tsx b/packages/excalidraw/components/Stats/MultiAngle.tsx index d9a78825a..ec314c183 100644 --- a/packages/excalidraw/components/Stats/MultiAngle.tsx +++ b/packages/excalidraw/components/Stats/MultiAngle.tsx @@ -1,16 +1,20 @@ +import { degreesToRadians, radiansToDegrees } from "@excalidraw/math"; + +import type { Degrees } from "@excalidraw/math"; + import { mutateElement } from "../../element/mutateElement"; import { getBoundTextElement } from "../../element/textElement"; import { isArrowElement } from "../../element/typeChecks"; -import type { ExcalidrawElement } from "../../element/types"; import { isInGroup } from "../../groups"; -import type Scene from "../../scene/Scene"; import { angleIcon } from "../icons"; + import DragInput from "./DragInput"; -import type { DragInputCallbackType } from "./DragInput"; import { getStepSizedValue, isPropertyEditable } from "./utils"; + +import type { DragInputCallbackType } from "./DragInput"; +import type { ExcalidrawElement } from "../../element/types"; +import type Scene from "../../scene/Scene"; import type { AppState } from "../../types"; -import type { Degrees } from "@excalidraw/math"; -import { degreesToRadians, radiansToDegrees } from "@excalidraw/math"; interface MultiAngleProps { elements: readonly ExcalidrawElement[]; diff --git a/packages/excalidraw/components/Stats/MultiDimension.tsx b/packages/excalidraw/components/Stats/MultiDimension.tsx index 04f193747..fb9cea942 100644 --- a/packages/excalidraw/components/Stats/MultiDimension.tsx +++ b/packages/excalidraw/components/Stats/MultiDimension.tsx @@ -1,4 +1,7 @@ +import { pointFrom, type GlobalPoint } from "@excalidraw/math"; import { useMemo } from "react"; + +import { MIN_WIDTH_OR_HEIGHT } from "../../constants"; import { getCommonBounds, isTextElement } from "../../element"; import { updateBoundElements } from "../../element/binding"; import { mutateElement } from "../../element/mutateElement"; @@ -10,6 +13,13 @@ import { getBoundTextElement, handleBindTextResize, } from "../../element/textElement"; + +import DragInput from "./DragInput"; +import { getAtomicUnits, getStepSizedValue, isPropertyEditable } from "./utils"; +import { getElementsInAtomicUnit } from "./utils"; + +import type { DragInputCallbackType } from "./DragInput"; +import type { AtomicUnit } from "./utils"; import type { ElementsMap, ExcalidrawElement, @@ -17,13 +27,6 @@ import type { } from "../../element/types"; import type Scene from "../../scene/Scene"; import type { AppState } from "../../types"; -import DragInput from "./DragInput"; -import type { DragInputCallbackType } from "./DragInput"; -import { getAtomicUnits, getStepSizedValue, isPropertyEditable } from "./utils"; -import { getElementsInAtomicUnit } from "./utils"; -import type { AtomicUnit } from "./utils"; -import { MIN_WIDTH_OR_HEIGHT } from "../../constants"; -import { pointFrom, type GlobalPoint } from "@excalidraw/math"; interface MultiDimensionProps { property: "width" | "height"; diff --git a/packages/excalidraw/components/Stats/MultiFontSize.tsx b/packages/excalidraw/components/Stats/MultiFontSize.tsx index 419bbbc36..8335399ef 100644 --- a/packages/excalidraw/components/Stats/MultiFontSize.tsx +++ b/packages/excalidraw/components/Stats/MultiFontSize.tsx @@ -1,19 +1,21 @@ import { isTextElement, redrawTextBoundingBox } from "../../element"; import { mutateElement } from "../../element/mutateElement"; +import { getBoundTextElement } from "../../element/textElement"; import { hasBoundTextElement } from "../../element/typeChecks"; +import { isInGroup } from "../../groups"; +import { fontSizeIcon } from "../icons"; + +import StatsDragInput from "./DragInput"; +import { getStepSizedValue } from "./utils"; + +import type { DragInputCallbackType } from "./DragInput"; import type { ExcalidrawElement, ExcalidrawTextElement, NonDeletedSceneElementsMap, } from "../../element/types"; -import { isInGroup } from "../../groups"; import type Scene from "../../scene/Scene"; -import { fontSizeIcon } from "../icons"; -import StatsDragInput from "./DragInput"; -import type { DragInputCallbackType } from "./DragInput"; -import { getStepSizedValue } from "./utils"; import type { AppState } from "../../types"; -import { getBoundTextElement } from "../../element/textElement"; interface MultiFontSizeProps { elements: readonly ExcalidrawElement[]; diff --git a/packages/excalidraw/components/Stats/MultiPosition.tsx b/packages/excalidraw/components/Stats/MultiPosition.tsx index 8a29aa66d..a8fdacf29 100644 --- a/packages/excalidraw/components/Stats/MultiPosition.tsx +++ b/packages/excalidraw/components/Stats/MultiPosition.tsx @@ -1,3 +1,14 @@ +import { pointFrom, pointRotateRads } from "@excalidraw/math"; +import { useMemo } from "react"; + +import { getCommonBounds, isTextElement } from "../../element"; + +import StatsDragInput from "./DragInput"; +import { getAtomicUnits, getStepSizedValue, isPropertyEditable } from "./utils"; +import { getElementsInAtomicUnit, moveElement } from "./utils"; + +import type { DragInputCallbackType } from "./DragInput"; +import type { AtomicUnit } from "./utils"; import type { ElementsMap, ExcalidrawElement, @@ -5,15 +16,7 @@ import type { NonDeletedSceneElementsMap, } from "../../element/types"; import type Scene from "../../scene/Scene"; -import StatsDragInput from "./DragInput"; -import type { DragInputCallbackType } from "./DragInput"; -import { getAtomicUnits, getStepSizedValue, isPropertyEditable } from "./utils"; -import { getCommonBounds, isTextElement } from "../../element"; -import { useMemo } from "react"; -import { getElementsInAtomicUnit, moveElement } from "./utils"; -import type { AtomicUnit } from "./utils"; import type { AppState } from "../../types"; -import { pointFrom, pointRotateRads } from "@excalidraw/math"; interface MultiPositionProps { property: "x" | "y"; diff --git a/packages/excalidraw/components/Stats/Position.tsx b/packages/excalidraw/components/Stats/Position.tsx index 038e58ebc..08d0a8830 100644 --- a/packages/excalidraw/components/Stats/Position.tsx +++ b/packages/excalidraw/components/Stats/Position.tsx @@ -1,16 +1,19 @@ -import type { ElementsMap, ExcalidrawElement } from "../../element/types"; -import StatsDragInput from "./DragInput"; -import type { DragInputCallbackType } from "./DragInput"; -import { getStepSizedValue, moveElement } from "./utils"; -import type Scene from "../../scene/Scene"; -import type { AppState } from "../../types"; import { clamp, pointFrom, pointRotateRads, round } from "@excalidraw/math"; -import { isImageElement } from "../../element/typeChecks"; + import { getFlipAdjustedCropPosition, getUncroppedWidthAndHeight, } from "../../element/cropElement"; import { mutateElement } from "../../element/mutateElement"; +import { isImageElement } from "../../element/typeChecks"; + +import StatsDragInput from "./DragInput"; +import { getStepSizedValue, moveElement } from "./utils"; + +import type { DragInputCallbackType } from "./DragInput"; +import type { ElementsMap, ExcalidrawElement } from "../../element/types"; +import type Scene from "../../scene/Scene"; +import type { AppState } from "../../types"; interface PositionProps { property: "x" | "y"; diff --git a/packages/excalidraw/components/Stats/index.tsx b/packages/excalidraw/components/Stats/index.tsx index 2ccd93ca2..56764fefb 100644 --- a/packages/excalidraw/components/Stats/index.tsx +++ b/packages/excalidraw/components/Stats/index.tsx @@ -1,37 +1,40 @@ +import { round } from "@excalidraw/math"; +import clsx from "clsx"; +import throttle from "lodash.throttle"; import { useEffect, useMemo, useState, memo } from "react"; + +import { STATS_PANELS } from "../../constants"; import { getCommonBounds } from "../../element/bounds"; -import type { NonDeletedExcalidrawElement } from "../../element/types"; +import { getUncroppedWidthAndHeight } from "../../element/cropElement"; +import { isElbowArrow, isImageElement } from "../../element/typeChecks"; +import { frameAndChildrenSelectedTogether } from "../../frame"; +import { elementsAreInSameGroup } from "../../groups"; import { t } from "../../i18n"; +import { isGridModeEnabled } from "../../snapping"; +import { useExcalidrawAppState, useExcalidrawSetAppState } from "../App"; +import { Island } from "../Island"; +import { CloseIcon } from "../icons"; + +import Angle from "./Angle"; +import CanvasGrid from "./CanvasGrid"; +import Collapsible from "./Collapsible"; +import Dimension from "./Dimension"; +import FontSize from "./FontSize"; +import MultiAngle from "./MultiAngle"; +import MultiDimension from "./MultiDimension"; +import MultiFontSize from "./MultiFontSize"; +import MultiPosition from "./MultiPosition"; +import Position from "./Position"; +import { getAtomicUnits } from "./utils"; + +import "./Stats.scss"; + +import type { NonDeletedExcalidrawElement } from "../../element/types"; import type { AppClassProperties, AppState, ExcalidrawProps, } from "../../types"; -import { CloseIcon } from "../icons"; -import { Island } from "../Island"; -import throttle from "lodash.throttle"; -import Dimension from "./Dimension"; -import Angle from "./Angle"; -import FontSize from "./FontSize"; -import MultiDimension from "./MultiDimension"; -import { elementsAreInSameGroup } from "../../groups"; -import MultiAngle from "./MultiAngle"; -import MultiFontSize from "./MultiFontSize"; -import Position from "./Position"; -import MultiPosition from "./MultiPosition"; -import Collapsible from "./Collapsible"; -import { useExcalidrawAppState, useExcalidrawSetAppState } from "../App"; -import { getAtomicUnits } from "./utils"; -import { STATS_PANELS } from "../../constants"; -import { isElbowArrow, isImageElement } from "../../element/typeChecks"; -import CanvasGrid from "./CanvasGrid"; -import clsx from "clsx"; - -import "./Stats.scss"; -import { isGridModeEnabled } from "../../snapping"; -import { getUncroppedWidthAndHeight } from "../../element/cropElement"; -import { round } from "@excalidraw/math"; -import { frameAndChildrenSelectedTogether } from "../../frame"; interface StatsProps { app: AppClassProperties; diff --git a/packages/excalidraw/components/Stats/stats.test.tsx b/packages/excalidraw/components/Stats/stats.test.tsx index 49d6a625f..b1fe0d685 100644 --- a/packages/excalidraw/components/Stats/stats.test.tsx +++ b/packages/excalidraw/components/Stats/stats.test.tsx @@ -1,31 +1,35 @@ -import React from "react"; +import { degreesToRadians, pointFrom, pointRotateRads } from "@excalidraw/math"; import { act, fireEvent, queryByTestId } from "@testing-library/react"; +import React from "react"; +import { vi } from "vitest"; + +import type { Degrees } from "@excalidraw/math"; + +import { Excalidraw, mutateElement } from "../.."; +import { actionGroup } from "../../actions"; +import { getCommonBounds, isTextElement } from "../../element"; +import { isInGroup } from "../../groups"; +import { t } from "../../i18n"; +import { reseed } from "../../random"; +import * as StaticScene from "../../renderer/staticScene"; +import { API } from "../../tests/helpers/api"; import { Keyboard, Pointer, UI } from "../../tests/helpers/ui"; -import { getStepSizedValue } from "./utils"; +import { getTextEditor, updateTextEditor } from "../../tests/queries/dom"; import { GlobalTestState, mockBoundingClientRect, render, restoreOriginalGetBoundingClientRect, } from "../../tests/test-utils"; -import * as StaticScene from "../../renderer/staticScene"; -import { vi } from "vitest"; -import { reseed } from "../../random"; import { setDateTimeForTests } from "../../utils"; -import { Excalidraw, mutateElement } from "../.."; -import { t } from "../../i18n"; + +import { getStepSizedValue } from "./utils"; + import type { ExcalidrawElement, ExcalidrawLinearElement, ExcalidrawTextElement, } from "../../element/types"; -import { getTextEditor, updateTextEditor } from "../../tests/queries/dom"; -import { getCommonBounds, isTextElement } from "../../element"; -import { API } from "../../tests/helpers/api"; -import { actionGroup } from "../../actions"; -import { isInGroup } from "../../groups"; -import type { Degrees } from "@excalidraw/math"; -import { degreesToRadians, pointFrom, pointRotateRads } from "@excalidraw/math"; const { h } = window; const mouse = new Pointer("mouse"); diff --git a/packages/excalidraw/components/Stats/utils.ts b/packages/excalidraw/components/Stats/utils.ts index 61555baeb..219e02fa5 100644 --- a/packages/excalidraw/components/Stats/utils.ts +++ b/packages/excalidraw/components/Stats/utils.ts @@ -1,5 +1,7 @@ -import type { Radians } from "@excalidraw/math"; import { pointFrom, pointRotateRads } from "@excalidraw/math"; + +import type { Radians } from "@excalidraw/math"; + import { bindOrUnbindLinearElements, updateBoundElements, @@ -11,17 +13,18 @@ import { isLinearElement, isTextElement, } from "../../element/typeChecks"; +import { + getSelectedGroupIds, + getElementsInGroup, + isInGroup, +} from "../../groups"; + import type { ElementsMap, ExcalidrawElement, NonDeletedExcalidrawElement, NonDeletedSceneElementsMap, } from "../../element/types"; -import { - getSelectedGroupIds, - getElementsInGroup, - isInGroup, -} from "../../groups"; import type Scene from "../../scene/Scene"; import type { AppState } from "../../types"; diff --git a/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx b/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx index 83fb91d0e..b8cd48dfa 100644 --- a/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx +++ b/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx @@ -1,25 +1,29 @@ import { useState, useRef, useEffect, useDeferredValue } from "react"; -import type { BinaryFiles } from "../../types"; + import { useApp } from "../App"; -import type { NonDeletedExcalidrawElement } from "../../element/types"; import { ArrowRightIcon } from "../icons"; -import "./MermaidToExcalidraw.scss"; +import { EDITOR_LS_KEYS } from "../../constants"; +import { EditorLocalStorage } from "../../data/EditorLocalStorage"; import { t } from "../../i18n"; +import { debounce, isDevEnv } from "../../utils"; import Trans from "../Trans"; -import type { MermaidToExcalidrawLibProps } from "./common"; + +import { TTDDialogInput } from "./TTDDialogInput"; +import { TTDDialogOutput } from "./TTDDialogOutput"; +import { TTDDialogPanel } from "./TTDDialogPanel"; +import { TTDDialogPanels } from "./TTDDialogPanels"; +import { TTDDialogSubmitShortcut } from "./TTDDialogSubmitShortcut"; import { convertMermaidToExcalidraw, insertToEditor, saveMermaidDataToStorage, } from "./common"; -import { TTDDialogPanels } from "./TTDDialogPanels"; -import { TTDDialogPanel } from "./TTDDialogPanel"; -import { TTDDialogInput } from "./TTDDialogInput"; -import { TTDDialogOutput } from "./TTDDialogOutput"; -import { EditorLocalStorage } from "../../data/EditorLocalStorage"; -import { EDITOR_LS_KEYS } from "../../constants"; -import { debounce, isDevEnv } from "../../utils"; -import { TTDDialogSubmitShortcut } from "./TTDDialogSubmitShortcut"; + +import "./MermaidToExcalidraw.scss"; + +import type { BinaryFiles } from "../../types"; +import type { MermaidToExcalidrawLibProps } from "./common"; +import type { NonDeletedExcalidrawElement } from "../../element/types"; const MERMAID_EXAMPLE = "flowchart TD\n A[Christmas] -->|Get money| B(Go shopping)\n B --> C{Let me think}\n C -->|One| D[Laptop]\n C -->|Two| E[iPhone]\n C -->|Three| F[Car]"; diff --git a/packages/excalidraw/components/TTDDialog/TTDDialog.tsx b/packages/excalidraw/components/TTDDialog/TTDDialog.tsx index c77dfbf68..3b9b868f7 100644 --- a/packages/excalidraw/components/TTDDialog/TTDDialog.tsx +++ b/packages/excalidraw/components/TTDDialog/TTDDialog.tsx @@ -1,35 +1,39 @@ -import { Dialog } from "../Dialog"; +import { isFiniteNumber } from "@excalidraw/math"; +import { useEffect, useRef, useState } from "react"; + +import { trackEvent } from "../../analytics"; +import { useUIAppState } from "../../context/ui-appState"; +import { atom, useAtom } from "../../editor-jotai"; +import { t } from "../../i18n"; import { useApp, useExcalidrawSetAppState } from "../App"; +import { Dialog } from "../Dialog"; +import { InlineIcon } from "../InlineIcon"; +import { withInternalFallback } from "../hoc/withInternalFallback"; +import { ArrowRightIcon } from "../icons"; + import MermaidToExcalidraw from "./MermaidToExcalidraw"; import TTDDialogTabs from "./TTDDialogTabs"; -import type { ChangeEventHandler } from "react"; -import { useEffect, useRef, useState } from "react"; -import { useUIAppState } from "../../context/ui-appState"; -import { withInternalFallback } from "../hoc/withInternalFallback"; import { TTDDialogTabTriggers } from "./TTDDialogTabTriggers"; import { TTDDialogTabTrigger } from "./TTDDialogTabTrigger"; import { TTDDialogTab } from "./TTDDialogTab"; -import { t } from "../../i18n"; import { TTDDialogInput } from "./TTDDialogInput"; import { TTDDialogOutput } from "./TTDDialogOutput"; import { TTDDialogPanel } from "./TTDDialogPanel"; import { TTDDialogPanels } from "./TTDDialogPanels"; -import type { MermaidToExcalidrawLibProps } from "./common"; + import { convertMermaidToExcalidraw, insertToEditor, saveMermaidDataToStorage, } from "./common"; -import type { NonDeletedExcalidrawElement } from "../../element/types"; -import type { BinaryFiles } from "../../types"; -import { ArrowRightIcon } from "../icons"; +import { TTDDialogSubmitShortcut } from "./TTDDialogSubmitShortcut"; import "./TTDDialog.scss"; -import { atom, useAtom } from "../../editor-jotai"; -import { trackEvent } from "../../analytics"; -import { InlineIcon } from "../InlineIcon"; -import { TTDDialogSubmitShortcut } from "./TTDDialogSubmitShortcut"; -import { isFiniteNumber } from "@excalidraw/math"; + +import type { ChangeEventHandler } from "react"; +import type { MermaidToExcalidrawLibProps } from "./common"; +import type { NonDeletedExcalidrawElement } from "../../element/types"; +import type { BinaryFiles } from "../../types"; const MIN_PROMPT_LENGTH = 3; const MAX_PROMPT_LENGTH = 1000; diff --git a/packages/excalidraw/components/TTDDialog/TTDDialogInput.tsx b/packages/excalidraw/components/TTDDialog/TTDDialogInput.tsx index e11d0dc3f..8e349643e 100644 --- a/packages/excalidraw/components/TTDDialog/TTDDialogInput.tsx +++ b/packages/excalidraw/components/TTDDialog/TTDDialogInput.tsx @@ -1,8 +1,10 @@ -import type { ChangeEventHandler } from "react"; import { useEffect, useRef } from "react"; + import { EVENT } from "../../constants"; import { KEYS } from "../../keys"; +import type { ChangeEventHandler } from "react"; + interface TTDDialogInputProps { input: string; placeholder: string; diff --git a/packages/excalidraw/components/TTDDialog/TTDDialogPanel.tsx b/packages/excalidraw/components/TTDDialog/TTDDialogPanel.tsx index 0a78e4969..82aede8ae 100644 --- a/packages/excalidraw/components/TTDDialog/TTDDialogPanel.tsx +++ b/packages/excalidraw/components/TTDDialog/TTDDialogPanel.tsx @@ -1,8 +1,10 @@ -import type { ReactNode } from "react"; -import { Button } from "../Button"; import clsx from "clsx"; + +import { Button } from "../Button"; import Spinner from "../Spinner"; +import type { ReactNode } from "react"; + interface TTDDialogPanelProps { label: string; children: ReactNode; diff --git a/packages/excalidraw/components/TTDDialog/TTDDialogTabs.tsx b/packages/excalidraw/components/TTDDialog/TTDDialogTabs.tsx index 439f92844..1c9075e7d 100644 --- a/packages/excalidraw/components/TTDDialog/TTDDialogTabs.tsx +++ b/packages/excalidraw/components/TTDDialog/TTDDialogTabs.tsx @@ -1,8 +1,10 @@ import * as RadixTabs from "@radix-ui/react-tabs"; -import type { ReactNode } from "react"; import { useRef } from "react"; -import { useExcalidrawSetAppState } from "../App"; + import { isMemberOf } from "../../utils"; +import { useExcalidrawSetAppState } from "../App"; + +import type { ReactNode } from "react"; const TTDDialogTabs = ( props: { diff --git a/packages/excalidraw/components/TTDDialog/TTDDialogTrigger.tsx b/packages/excalidraw/components/TTDDialog/TTDDialogTrigger.tsx index a73f31a68..833b659fe 100644 --- a/packages/excalidraw/components/TTDDialog/TTDDialogTrigger.tsx +++ b/packages/excalidraw/components/TTDDialog/TTDDialogTrigger.tsx @@ -1,11 +1,12 @@ -import type { JSX } from "react"; -import type { ReactNode } from "react"; -import { useTunnels } from "../../context/tunnels"; -import DropdownMenu from "../dropdownMenu/DropdownMenu"; -import { useExcalidrawSetAppState } from "../App"; -import { brainIcon } from "../icons"; -import { t } from "../../i18n"; import { trackEvent } from "../../analytics"; +import { useTunnels } from "../../context/tunnels"; +import { t } from "../../i18n"; +import { useExcalidrawSetAppState } from "../App"; +import DropdownMenu from "../dropdownMenu/DropdownMenu"; +import { brainIcon } from "../icons"; + +import type { ReactNode } from "react"; +import type { JSX } from "react"; export const TTDDialogTrigger = ({ children, diff --git a/packages/excalidraw/components/TTDDialog/common.ts b/packages/excalidraw/components/TTDDialog/common.ts index 4191126a4..89c342c65 100644 --- a/packages/excalidraw/components/TTDDialog/common.ts +++ b/packages/excalidraw/components/TTDDialog/common.ts @@ -1,12 +1,14 @@ import type { MermaidConfig } from "@excalidraw/mermaid-to-excalidraw"; import type { MermaidToExcalidrawResult } from "@excalidraw/mermaid-to-excalidraw/dist/interfaces"; + import { DEFAULT_EXPORT_PADDING, EDITOR_LS_KEYS } from "../../constants"; +import { EditorLocalStorage } from "../../data/EditorLocalStorage"; +import { canvasToBlob } from "../../data/blob"; +import { t } from "../../i18n"; import { convertToExcalidrawElements, exportToCanvas } from "../../index"; + import type { NonDeletedExcalidrawElement } from "../../element/types"; import type { AppClassProperties, BinaryFiles } from "../../types"; -import { canvasToBlob } from "../../data/blob"; -import { EditorLocalStorage } from "../../data/EditorLocalStorage"; -import { t } from "../../i18n"; const resetPreview = ({ canvasRef, diff --git a/packages/excalidraw/components/TextField.tsx b/packages/excalidraw/components/TextField.tsx index c5bdc8260..d6bc315b1 100644 --- a/packages/excalidraw/components/TextField.tsx +++ b/packages/excalidraw/components/TextField.tsx @@ -1,4 +1,4 @@ -import type { KeyboardEvent } from "react"; +import clsx from "clsx"; import { forwardRef, useRef, @@ -6,12 +6,14 @@ import { useLayoutEffect, useState, } from "react"; -import clsx from "clsx"; -import "./TextField.scss"; import { Button } from "./Button"; import { eyeIcon, eyeClosedIcon } from "./icons"; +import "./TextField.scss"; + +import type { KeyboardEvent } from "react"; + type TextFieldProps = { onChange?: (value: string) => void; onClick?: () => void; diff --git a/packages/excalidraw/components/Toast.tsx b/packages/excalidraw/components/Toast.tsx index d99c9f648..a7167c00f 100644 --- a/packages/excalidraw/components/Toast.tsx +++ b/packages/excalidraw/components/Toast.tsx @@ -1,9 +1,12 @@ -import type { CSSProperties } from "react"; import { useCallback, useEffect, useRef } from "react"; + import { CloseIcon } from "./icons"; -import "./Toast.scss"; import { ToolButton } from "./ToolButton"; +import "./Toast.scss"; + +import type { CSSProperties } from "react"; + const DEFAULT_TOAST_TIMEOUT = 5000; export const Toast = ({ diff --git a/packages/excalidraw/components/ToolButton.tsx b/packages/excalidraw/components/ToolButton.tsx index 09b0b6cf8..fb76731b0 100644 --- a/packages/excalidraw/components/ToolButton.tsx +++ b/packages/excalidraw/components/ToolButton.tsx @@ -1,13 +1,16 @@ +import clsx from "clsx"; +import React, { useEffect, useRef, useState } from "react"; + +import { AbortError } from "../errors"; +import { isPromiseLike } from "../utils"; + import "./ToolIcon.scss"; -import type { CSSProperties } from "react"; -import React, { useEffect, useRef, useState } from "react"; -import clsx from "clsx"; -import { useExcalidrawContainer } from "./App"; -import { AbortError } from "../errors"; import Spinner from "./Spinner"; +import { useExcalidrawContainer } from "./App"; + import type { PointerType } from "../element/types"; -import { isPromiseLike } from "../utils"; +import type { CSSProperties } from "react"; export type ToolButtonSize = "small" | "medium"; diff --git a/packages/excalidraw/components/Tooltip.tsx b/packages/excalidraw/components/Tooltip.tsx index 38c04ef23..9c7c7ea0c 100644 --- a/packages/excalidraw/components/Tooltip.tsx +++ b/packages/excalidraw/components/Tooltip.tsx @@ -1,7 +1,7 @@ -import "./Tooltip.scss"; - import React, { useEffect } from "react"; +import "./Tooltip.scss"; + export const getTooltipDiv = () => { const existingDiv = document.querySelector( ".excalidraw-tooltip", diff --git a/packages/excalidraw/components/Trans.test.tsx b/packages/excalidraw/components/Trans.test.tsx index c331796bd..077fcb88c 100644 --- a/packages/excalidraw/components/Trans.test.tsx +++ b/packages/excalidraw/components/Trans.test.tsx @@ -1,10 +1,11 @@ import { render } from "@testing-library/react"; +import { EditorJotaiProvider } from "../editor-jotai"; import fallbackLangData from "../locales/en.json"; import Trans from "./Trans"; + import type { TranslationKeys } from "../i18n"; -import { EditorJotaiProvider } from "../editor-jotai"; describe("Test ", () => { it("should translate the the strings correctly", () => { diff --git a/packages/excalidraw/components/Trans.tsx b/packages/excalidraw/components/Trans.tsx index 0cb0f78cb..7a6215c89 100644 --- a/packages/excalidraw/components/Trans.tsx +++ b/packages/excalidraw/components/Trans.tsx @@ -1,8 +1,9 @@ import React from "react"; -import type { TranslationKeys } from "../i18n"; import { useI18n } from "../i18n"; +import type { TranslationKeys } from "../i18n"; + // Used for splitting i18nKey into tokens in Trans component // Example: // "Please click {{location}} to continue.".split(SPLIT_REGEX).filter(Boolean) diff --git a/packages/excalidraw/components/UserList.tsx b/packages/excalidraw/components/UserList.tsx index 9fbfc1aed..6c74411a1 100644 --- a/packages/excalidraw/components/UserList.tsx +++ b/packages/excalidraw/components/UserList.tsx @@ -1,20 +1,22 @@ -import "./UserList.scss"; - -import React, { useLayoutEffect } from "react"; -import clsx from "clsx"; -import type { Collaborator, SocketId } from "../types"; -import { Tooltip } from "./Tooltip"; -import { useExcalidrawActionManager } from "./App"; -import type { ActionManager } from "../actions/manager"; - import * as Popover from "@radix-ui/react-popover"; -import { Island } from "./Island"; -import { QuickSearch } from "./QuickSearch"; +import clsx from "clsx"; +import React, { useLayoutEffect } from "react"; + +import { supportsResizeObserver } from "../constants"; import { t } from "../i18n"; import { isShallowEqual } from "../utils"; -import { supportsResizeObserver } from "../constants"; -import type { MarkRequired } from "../utility-types"; + +import { useExcalidrawActionManager } from "./App"; +import { Island } from "./Island"; +import { QuickSearch } from "./QuickSearch"; import { ScrollableList } from "./ScrollableList"; +import { Tooltip } from "./Tooltip"; + +import "./UserList.scss"; + +import type { ActionManager } from "../actions/manager"; +import type { Collaborator, SocketId } from "../types"; +import type { MarkRequired } from "../utility-types"; export type GoToCollaboratorComponentProps = { socketId: SocketId; diff --git a/packages/excalidraw/components/canvases/InteractiveCanvas.tsx b/packages/excalidraw/components/canvases/InteractiveCanvas.tsx index 03ae0f6b6..8388e5e6c 100644 --- a/packages/excalidraw/components/canvases/InteractiveCanvas.tsx +++ b/packages/excalidraw/components/canvases/InteractiveCanvas.tsx @@ -1,20 +1,22 @@ import React, { useEffect, useRef } from "react"; -import { isShallowEqual, sceneCoordsToViewportCoords } from "../../utils"; + import { CURSOR_TYPE } from "../../constants"; import { t } from "../../i18n"; -import type { DOMAttributes } from "react"; -import type { AppState, Device, InteractiveCanvasAppState } from "../../types"; +import { isRenderThrottlingEnabled } from "../../reactUtils"; +import { renderInteractiveScene } from "../../renderer/interactiveScene"; +import { isShallowEqual, sceneCoordsToViewportCoords } from "../../utils"; + +import type { + NonDeletedExcalidrawElement, + NonDeletedSceneElementsMap, +} from "../../element/types"; import type { InteractiveCanvasRenderConfig, RenderableElementsMap, RenderInteractiveSceneCallback, } from "../../scene/types"; -import type { - NonDeletedExcalidrawElement, - NonDeletedSceneElementsMap, -} from "../../element/types"; -import { isRenderThrottlingEnabled } from "../../reactUtils"; -import { renderInteractiveScene } from "../../renderer/interactiveScene"; +import type { AppState, Device, InteractiveCanvasAppState } from "../../types"; +import type { DOMAttributes } from "react"; type InteractiveCanvasProps = { containerRef: React.RefObject; diff --git a/packages/excalidraw/components/canvases/NewElementCanvas.tsx b/packages/excalidraw/components/canvases/NewElementCanvas.tsx index 181567110..524d6af34 100644 --- a/packages/excalidraw/components/canvases/NewElementCanvas.tsx +++ b/packages/excalidraw/components/canvases/NewElementCanvas.tsx @@ -1,13 +1,15 @@ import { useEffect, useRef } from "react"; + +import { isRenderThrottlingEnabled } from "../../reactUtils"; +import { renderNewElementScene } from "../../renderer/renderNewElementScene"; + import type { NonDeletedSceneElementsMap } from "../../element/types"; -import type { AppState } from "../../types"; import type { RenderableElementsMap, StaticCanvasRenderConfig, } from "../../scene/types"; +import type { AppState } from "../../types"; import type { RoughCanvas } from "roughjs/bin/canvas"; -import { renderNewElementScene } from "../../renderer/renderNewElementScene"; -import { isRenderThrottlingEnabled } from "../../reactUtils"; interface NewElementCanvasProps { appState: AppState; diff --git a/packages/excalidraw/components/canvases/StaticCanvas.tsx b/packages/excalidraw/components/canvases/StaticCanvas.tsx index 9185bdd5c..6e2df9537 100644 --- a/packages/excalidraw/components/canvases/StaticCanvas.tsx +++ b/packages/excalidraw/components/canvases/StaticCanvas.tsx @@ -1,17 +1,19 @@ import React, { useEffect, useRef } from "react"; -import type { RoughCanvas } from "roughjs/bin/canvas"; + +import { isRenderThrottlingEnabled } from "../../reactUtils"; import { renderStaticScene } from "../../renderer/staticScene"; import { isShallowEqual } from "../../utils"; -import type { AppState, StaticCanvasAppState } from "../../types"; -import type { - RenderableElementsMap, - StaticCanvasRenderConfig, -} from "../../scene/types"; + import type { NonDeletedExcalidrawElement, NonDeletedSceneElementsMap, } from "../../element/types"; -import { isRenderThrottlingEnabled } from "../../reactUtils"; +import type { + RenderableElementsMap, + StaticCanvasRenderConfig, +} from "../../scene/types"; +import type { AppState, StaticCanvasAppState } from "../../types"; +import type { RoughCanvas } from "roughjs/bin/canvas"; type StaticCanvasProps = { canvas: HTMLCanvasElement; diff --git a/packages/excalidraw/components/dropdownMenu/DropdownMenu.test.tsx b/packages/excalidraw/components/dropdownMenu/DropdownMenu.test.tsx index 0c8deec87..d4c0b7a8d 100644 --- a/packages/excalidraw/components/dropdownMenu/DropdownMenu.test.tsx +++ b/packages/excalidraw/components/dropdownMenu/DropdownMenu.test.tsx @@ -1,4 +1,5 @@ import React from "react"; + import { Excalidraw } from "../../index"; import { KEYS } from "../../keys"; import { Keyboard } from "../../tests/helpers/ui"; diff --git a/packages/excalidraw/components/dropdownMenu/DropdownMenu.tsx b/packages/excalidraw/components/dropdownMenu/DropdownMenu.tsx index 8f4ce435b..e1412e20b 100644 --- a/packages/excalidraw/components/dropdownMenu/DropdownMenu.tsx +++ b/packages/excalidraw/components/dropdownMenu/DropdownMenu.tsx @@ -1,11 +1,12 @@ import React from "react"; -import DropdownMenuTrigger from "./DropdownMenuTrigger"; -import DropdownMenuItem from "./DropdownMenuItem"; -import MenuSeparator from "./DropdownMenuSeparator"; -import DropdownMenuGroup from "./DropdownMenuGroup"; + import DropdownMenuContent from "./DropdownMenuContent"; -import DropdownMenuItemLink from "./DropdownMenuItemLink"; +import DropdownMenuGroup from "./DropdownMenuGroup"; +import DropdownMenuItem from "./DropdownMenuItem"; import DropdownMenuItemCustom from "./DropdownMenuItemCustom"; +import DropdownMenuItemLink from "./DropdownMenuItemLink"; +import MenuSeparator from "./DropdownMenuSeparator"; +import DropdownMenuTrigger from "./DropdownMenuTrigger"; import { getMenuContentComponent, getMenuTriggerComponent, diff --git a/packages/excalidraw/components/dropdownMenu/DropdownMenuContent.tsx b/packages/excalidraw/components/dropdownMenu/DropdownMenuContent.tsx index a203124d6..24a0a4d27 100644 --- a/packages/excalidraw/components/dropdownMenu/DropdownMenuContent.tsx +++ b/packages/excalidraw/components/dropdownMenu/DropdownMenuContent.tsx @@ -1,13 +1,15 @@ -import { Island } from "../Island"; -import { useDevice } from "../App"; import clsx from "clsx"; -import Stack from "../Stack"; import React, { useEffect, useRef } from "react"; -import { DropdownMenuContentPropsContext } from "./common"; -import { useOutsideClick } from "../../hooks/useOutsideClick"; -import { KEYS } from "../../keys"; + import { EVENT } from "../../constants"; +import { useOutsideClick } from "../../hooks/useOutsideClick"; import { useStable } from "../../hooks/useStable"; +import { KEYS } from "../../keys"; +import { useDevice } from "../App"; +import { Island } from "../Island"; +import Stack from "../Stack"; + +import { DropdownMenuContentPropsContext } from "./common"; const MenuContent = ({ children, diff --git a/packages/excalidraw/components/dropdownMenu/DropdownMenuItem.tsx b/packages/excalidraw/components/dropdownMenu/DropdownMenuItem.tsx index 1ff53f8af..fc19bd1bf 100644 --- a/packages/excalidraw/components/dropdownMenu/DropdownMenuItem.tsx +++ b/packages/excalidraw/components/dropdownMenu/DropdownMenuItem.tsx @@ -1,13 +1,16 @@ -import type { JSX } from "react"; import React, { useEffect, useRef } from "react"; + +import { THEME } from "../../constants"; +import { useExcalidrawAppState } from "../App"; + +import MenuItemContent from "./DropdownMenuItemContent"; import { getDropdownMenuItemClassName, useHandleDropdownMenuItemClick, } from "./common"; -import MenuItemContent from "./DropdownMenuItemContent"; -import { useExcalidrawAppState } from "../App"; -import { THEME } from "../../constants"; + import type { ValueOf } from "../../utility-types"; +import type { JSX } from "react"; const DropdownMenuItem = ({ icon, diff --git a/packages/excalidraw/components/dropdownMenu/DropdownMenuItemContent.tsx b/packages/excalidraw/components/dropdownMenu/DropdownMenuItemContent.tsx index 000b8c344..b2f9e7e0a 100644 --- a/packages/excalidraw/components/dropdownMenu/DropdownMenuItemContent.tsx +++ b/packages/excalidraw/components/dropdownMenu/DropdownMenuItemContent.tsx @@ -1,6 +1,7 @@ -import type { JSX } from "react"; import { useDevice } from "../App"; +import type { JSX } from "react"; + const MenuItemContent = ({ textStyle, icon, diff --git a/packages/excalidraw/components/dropdownMenu/DropdownMenuItemLink.tsx b/packages/excalidraw/components/dropdownMenu/DropdownMenuItemLink.tsx index 2dbee7584..1457f5ae7 100644 --- a/packages/excalidraw/components/dropdownMenu/DropdownMenuItemLink.tsx +++ b/packages/excalidraw/components/dropdownMenu/DropdownMenuItemLink.tsx @@ -1,11 +1,13 @@ -import MenuItemContent from "./DropdownMenuItemContent"; -import type { JSX } from "react"; import React from "react"; + +import MenuItemContent from "./DropdownMenuItemContent"; import { getDropdownMenuItemClassName, useHandleDropdownMenuItemClick, } from "./common"; +import type { JSX } from "react"; + const DropdownMenuItemLink = ({ icon, shortcut, diff --git a/packages/excalidraw/components/dropdownMenu/DropdownMenuTrigger.tsx b/packages/excalidraw/components/dropdownMenu/DropdownMenuTrigger.tsx index e7369ba57..a7301fb44 100644 --- a/packages/excalidraw/components/dropdownMenu/DropdownMenuTrigger.tsx +++ b/packages/excalidraw/components/dropdownMenu/DropdownMenuTrigger.tsx @@ -1,4 +1,5 @@ import clsx from "clsx"; + import { useDevice } from "../App"; const MenuTrigger = ({ diff --git a/packages/excalidraw/components/dropdownMenu/common.ts b/packages/excalidraw/components/dropdownMenu/common.ts index a2a46fc93..312b78aae 100644 --- a/packages/excalidraw/components/dropdownMenu/common.ts +++ b/packages/excalidraw/components/dropdownMenu/common.ts @@ -1,4 +1,5 @@ import React, { useContext } from "react"; + import { EVENT } from "../../constants"; import { composeEventHandlers } from "../../utils"; diff --git a/packages/excalidraw/components/footer/Footer.tsx b/packages/excalidraw/components/footer/Footer.tsx index ccbe9eaee..427628e7c 100644 --- a/packages/excalidraw/components/footer/Footer.tsx +++ b/packages/excalidraw/components/footer/Footer.tsx @@ -1,6 +1,7 @@ import clsx from "clsx"; + import { actionShortcuts } from "../../actions"; -import type { ActionManager } from "../../actions/manager"; +import { useTunnels } from "../../context/tunnels"; import { ExitZenModeAction, FinalizeAction, @@ -8,10 +9,11 @@ import { ZoomActions, } from "../Actions"; import { useDevice } from "../App"; -import { useTunnels } from "../../context/tunnels"; import { HelpButton } from "../HelpButton"; import { Section } from "../Section"; import Stack from "../Stack"; + +import type { ActionManager } from "../../actions/manager"; import type { UIAppState } from "../../types"; const Footer = ({ diff --git a/packages/excalidraw/components/footer/FooterCenter.tsx b/packages/excalidraw/components/footer/FooterCenter.tsx index 55a0fb998..6a44581de 100644 --- a/packages/excalidraw/components/footer/FooterCenter.tsx +++ b/packages/excalidraw/components/footer/FooterCenter.tsx @@ -1,8 +1,10 @@ import clsx from "clsx"; + import { useTunnels } from "../../context/tunnels"; -import "./FooterCenter.scss"; import { useUIAppState } from "../../context/ui-appState"; +import "./FooterCenter.scss"; + const FooterCenter = ({ children }: { children?: React.ReactNode }) => { const { FooterCenterTunnel } = useTunnels(); const appState = useUIAppState(); diff --git a/packages/excalidraw/components/hoc/withInternalFallback.test.tsx b/packages/excalidraw/components/hoc/withInternalFallback.test.tsx index 554313377..1f30a64c4 100644 --- a/packages/excalidraw/components/hoc/withInternalFallback.test.tsx +++ b/packages/excalidraw/components/hoc/withInternalFallback.test.tsx @@ -1,6 +1,7 @@ import React from "react"; -import { render, queryAllByTestId } from "../../tests/test-utils"; + import { Excalidraw, MainMenu } from "../../index"; +import { render, queryAllByTestId } from "../../tests/test-utils"; describe("Test internal component fallback rendering", () => { it("should render only one menu per excalidraw instance (custom menu first scenario)", async () => { diff --git a/packages/excalidraw/components/hoc/withInternalFallback.tsx b/packages/excalidraw/components/hoc/withInternalFallback.tsx index 5906b30f5..298f58dd9 100644 --- a/packages/excalidraw/components/hoc/withInternalFallback.tsx +++ b/packages/excalidraw/components/hoc/withInternalFallback.tsx @@ -1,4 +1,5 @@ import React, { useLayoutEffect, useRef } from "react"; + import { useTunnels } from "../../context/tunnels"; import { atom } from "../../editor-jotai"; diff --git a/packages/excalidraw/components/hyperlink/Hyperlink.tsx b/packages/excalidraw/components/hyperlink/Hyperlink.tsx index 4d3dce640..88983aeab 100644 --- a/packages/excalidraw/components/hyperlink/Hyperlink.tsx +++ b/packages/excalidraw/components/hyperlink/Hyperlink.tsx @@ -1,20 +1,5 @@ -import type { AppState, ExcalidrawProps, UIAppState } from "../../types"; -import { - sceneCoordsToViewportCoords, - viewportCoordsToSceneCoords, - wrapEvent, -} from "../../utils"; -import { getEmbedLink, embeddableURLValidator } from "../../element/embeddable"; -import { mutateElement } from "../../element/mutateElement"; -import type { - ElementsMap, - ExcalidrawEmbeddableElement, - NonDeletedExcalidrawElement, -} from "../../element/types"; - -import { ToolButton } from "../ToolButton"; -import { FreedrawIcon, TrashIcon, elementLinkIcon } from "../icons"; -import { t } from "../../i18n"; +import { pointFrom, type GlobalPoint } from "@excalidraw/math"; +import clsx from "clsx"; import { useCallback, useEffect, @@ -22,23 +7,40 @@ import { useRef, useState, } from "react"; -import clsx from "clsx"; -import { KEYS } from "../../keys"; -import { EVENT, HYPERLINK_TOOLTIP_DELAY } from "../../constants"; -import { getElementAbsoluteCoords } from "../../element/bounds"; -import { getTooltipDiv, updateTooltipPosition } from "../../components/Tooltip"; -import { getSelectedElements } from "../../scene"; -import { hitElementBoundingBox } from "../../element/collision"; -import { isLocalLink, normalizeLink } from "../../data/url"; + import { trackEvent } from "../../analytics"; -import { useAppProps, useDevice, useExcalidrawAppState } from "../App"; -import { isEmbeddableElement } from "../../element/typeChecks"; -import { getLinkHandleFromCoords } from "./helpers"; -import { pointFrom, type GlobalPoint } from "@excalidraw/math"; +import { getTooltipDiv, updateTooltipPosition } from "../../components/Tooltip"; +import { EVENT, HYPERLINK_TOOLTIP_DELAY } from "../../constants"; +import { isLocalLink, normalizeLink } from "../../data/url"; +import { getElementAbsoluteCoords } from "../../element/bounds"; +import { hitElementBoundingBox } from "../../element/collision"; import { isElementLink } from "../../element/elementLink"; +import { getEmbedLink, embeddableURLValidator } from "../../element/embeddable"; +import { mutateElement } from "../../element/mutateElement"; +import { t } from "../../i18n"; +import { + sceneCoordsToViewportCoords, + viewportCoordsToSceneCoords, + wrapEvent, +} from "../../utils"; +import { useAppProps, useDevice, useExcalidrawAppState } from "../App"; +import { ToolButton } from "../ToolButton"; +import { FreedrawIcon, TrashIcon, elementLinkIcon } from "../icons"; +import { KEYS } from "../../keys"; +import { getSelectedElements } from "../../scene"; +import { isEmbeddableElement } from "../../element/typeChecks"; + +import { getLinkHandleFromCoords } from "./helpers"; import "./Hyperlink.scss"; +import type { + ElementsMap, + ExcalidrawEmbeddableElement, + NonDeletedExcalidrawElement, +} from "../../element/types"; +import type { AppState, ExcalidrawProps, UIAppState } from "../../types"; + const POPUP_WIDTH = 380; const POPUP_HEIGHT = 42; const POPUP_PADDING = 5; diff --git a/packages/excalidraw/components/hyperlink/helpers.ts b/packages/excalidraw/components/hyperlink/helpers.ts index 75c5dad9c..3d39e2ebe 100644 --- a/packages/excalidraw/components/hyperlink/helpers.ts +++ b/packages/excalidraw/components/hyperlink/helpers.ts @@ -1,14 +1,17 @@ -import type { GlobalPoint, Radians } from "@excalidraw/math"; import { pointFrom, pointRotateRads } from "@excalidraw/math"; + +import type { GlobalPoint, Radians } from "@excalidraw/math"; + import { MIME_TYPES } from "../../constants"; -import type { Bounds } from "../../element/bounds"; import { getElementAbsoluteCoords } from "../../element/bounds"; import { hitElementBoundingBox } from "../../element/collision"; +import { DEFAULT_LINK_SIZE } from "../../renderer/renderElement"; + +import type { Bounds } from "../../element/bounds"; import type { ElementsMap, NonDeletedExcalidrawElement, } from "../../element/types"; -import { DEFAULT_LINK_SIZE } from "../../renderer/renderElement"; import type { AppState, UIAppState } from "../../types"; export const EXTERNAL_LINK_IMG = document.createElement("img"); diff --git a/packages/excalidraw/components/icons.tsx b/packages/excalidraw/components/icons.tsx index 073d7a9f7..03e6ca2bb 100644 --- a/packages/excalidraw/components/icons.tsx +++ b/packages/excalidraw/components/icons.tsx @@ -6,13 +6,14 @@ // Note: when adding new icons, review https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/RTL_Guidelines // to determine whether or not the icons should be mirrored in right-to-left languages. +import clsx from "clsx"; +import oc from "open-color"; import React from "react"; -import oc from "open-color"; -import clsx from "clsx"; -import type { Theme } from "../element/types"; import { THEME } from "../constants"; +import type { Theme } from "../element/types"; + export const iconFillColor = (theme: Theme) => "var(--icon-fill-color)"; const handlerColor = (theme: Theme) => diff --git a/packages/excalidraw/components/live-collaboration/LiveCollaborationTrigger.tsx b/packages/excalidraw/components/live-collaboration/LiveCollaborationTrigger.tsx index a22bc523a..1aa187167 100644 --- a/packages/excalidraw/components/live-collaboration/LiveCollaborationTrigger.tsx +++ b/packages/excalidraw/components/live-collaboration/LiveCollaborationTrigger.tsx @@ -1,12 +1,12 @@ -import { t } from "../../i18n"; -import { share } from "../icons"; -import { Button } from "../Button"; - import clsx from "clsx"; -import "./LiveCollaborationTrigger.scss"; +import { t } from "../../i18n"; +import { Button } from "../Button"; +import { share } from "../icons"; import { useUIAppState } from "../../context/ui-appState"; +import "./LiveCollaborationTrigger.scss"; + const LiveCollaborationTrigger = ({ isCollaborating, onSelect, diff --git a/packages/excalidraw/components/main-menu/DefaultItems.tsx b/packages/excalidraw/components/main-menu/DefaultItems.tsx index 632ea4ffa..c2307d521 100644 --- a/packages/excalidraw/components/main-menu/DefaultItems.tsx +++ b/packages/excalidraw/components/main-menu/DefaultItems.tsx @@ -1,11 +1,32 @@ +import clsx from "clsx"; + +import { + actionClearCanvas, + actionLoadScene, + actionSaveToActiveFile, + actionShortcuts, + actionToggleSearchMenu, + actionToggleTheme, +} from "../../actions"; import { getShortcutFromShortcutName } from "../../actions/shortcuts"; +import { trackEvent } from "../../analytics"; +import { THEME } from "../../constants"; +import { useUIAppState } from "../../context/ui-appState"; +import { useSetAtom } from "../../editor-jotai"; import { useI18n } from "../../i18n"; +import { activeConfirmDialogAtom } from "../ActiveConfirmDialog"; import { useExcalidrawSetAppState, useExcalidrawActionManager, useExcalidrawElements, useAppProps, } from "../App"; +import { openConfirmModal } from "../OverwriteConfirm/OverwriteConfirmState"; +import Trans from "../Trans"; +import DropdownMenuItem from "../dropdownMenu/DropdownMenuItem"; +import DropdownMenuItemContentRadio from "../dropdownMenu/DropdownMenuItemContentRadio"; +import DropdownMenuItemLink from "../dropdownMenu/DropdownMenuItemLink"; +import { GithubIcon, DiscordIcon, XBrandIcon } from "../icons"; import { boltIcon, DeviceDesktopIcon, @@ -20,29 +41,11 @@ import { TrashIcon, usersIcon, } from "../icons"; -import { GithubIcon, DiscordIcon, XBrandIcon } from "../icons"; -import DropdownMenuItem from "../dropdownMenu/DropdownMenuItem"; -import DropdownMenuItemLink from "../dropdownMenu/DropdownMenuItemLink"; -import { - actionClearCanvas, - actionLoadScene, - actionSaveToActiveFile, - actionShortcuts, - actionToggleSearchMenu, - actionToggleTheme, -} from "../../actions"; -import clsx from "clsx"; -import { activeConfirmDialogAtom } from "../ActiveConfirmDialog"; -import { useSetAtom } from "../../editor-jotai"; -import { useUIAppState } from "../../context/ui-appState"; -import { openConfirmModal } from "../OverwriteConfirm/OverwriteConfirmState"; -import Trans from "../Trans"; -import DropdownMenuItemContentRadio from "../dropdownMenu/DropdownMenuItemContentRadio"; -import { THEME } from "../../constants"; -import type { Theme } from "../../element/types"; -import { trackEvent } from "../../analytics"; + import "./DefaultItems.scss"; +import type { Theme } from "../../element/types"; + export const LoadScene = () => { const { t } = useI18n(); const actionManager = useExcalidrawActionManager(); diff --git a/packages/excalidraw/components/main-menu/MainMenu.tsx b/packages/excalidraw/components/main-menu/MainMenu.tsx index 07afd3c1d..54f60a364 100644 --- a/packages/excalidraw/components/main-menu/MainMenu.tsx +++ b/packages/excalidraw/components/main-menu/MainMenu.tsx @@ -1,16 +1,16 @@ import React from "react"; -import { useDevice, useExcalidrawSetAppState } from "../App"; -import DropdownMenu from "../dropdownMenu/DropdownMenu"; -import * as DefaultItems from "./DefaultItems"; - -import { UserList } from "../UserList"; -import { t } from "../../i18n"; -import { HamburgerMenuIcon } from "../icons"; -import { withInternalFallback } from "../hoc/withInternalFallback"; -import { composeEventHandlers } from "../../utils"; import { useTunnels } from "../../context/tunnels"; import { useUIAppState } from "../../context/ui-appState"; +import { t } from "../../i18n"; +import { composeEventHandlers } from "../../utils"; +import { useDevice, useExcalidrawSetAppState } from "../App"; +import { UserList } from "../UserList"; +import DropdownMenu from "../dropdownMenu/DropdownMenu"; +import { withInternalFallback } from "../hoc/withInternalFallback"; +import { HamburgerMenuIcon } from "../icons"; + +import * as DefaultItems from "./DefaultItems"; const MainMenu = Object.assign( withInternalFallback( diff --git a/packages/excalidraw/components/welcome-screen/WelcomeScreen.Center.tsx b/packages/excalidraw/components/welcome-screen/WelcomeScreen.Center.tsx index 4faa41bf7..bd9b9e55b 100644 --- a/packages/excalidraw/components/welcome-screen/WelcomeScreen.Center.tsx +++ b/packages/excalidraw/components/welcome-screen/WelcomeScreen.Center.tsx @@ -1,12 +1,13 @@ -import type { JSX } from "react"; import { actionLoadScene, actionShortcuts } from "../../actions"; import { getShortcutFromShortcutName } from "../../actions/shortcuts"; +import { useTunnels } from "../../context/tunnels"; +import { useUIAppState } from "../../context/ui-appState"; import { t, useI18n } from "../../i18n"; import { useDevice, useExcalidrawActionManager } from "../App"; -import { useTunnels } from "../../context/tunnels"; -import { HelpIcon, LoadIcon, usersIcon } from "../icons"; -import { useUIAppState } from "../../context/ui-appState"; import { ExcalidrawLogo } from "../ExcalidrawLogo"; +import { HelpIcon, LoadIcon, usersIcon } from "../icons"; + +import type { JSX } from "react"; const WelcomeScreenMenuItemContent = ({ icon, diff --git a/packages/excalidraw/components/welcome-screen/WelcomeScreen.Hints.tsx b/packages/excalidraw/components/welcome-screen/WelcomeScreen.Hints.tsx index 896f4014e..e47a5a1d3 100644 --- a/packages/excalidraw/components/welcome-screen/WelcomeScreen.Hints.tsx +++ b/packages/excalidraw/components/welcome-screen/WelcomeScreen.Hints.tsx @@ -1,5 +1,5 @@ -import { t } from "../../i18n"; import { useTunnels } from "../../context/tunnels"; +import { t } from "../../i18n"; import { WelcomeScreenHelpArrow, WelcomeScreenMenuArrow, diff --git a/packages/excalidraw/constants.ts b/packages/excalidraw/constants.ts index 616ced1a7..88cddd5c5 100644 --- a/packages/excalidraw/constants.ts +++ b/packages/excalidraw/constants.ts @@ -1,7 +1,8 @@ -import type { AppProps, AppState } from "./types"; -import type { ExcalidrawElement, FontFamilyValues } from "./element/types"; import { COLOR_PALETTE } from "./colors"; +import type { ExcalidrawElement, FontFamilyValues } from "./element/types"; +import type { AppProps, AppState } from "./types"; + export const isDarwin = /Mac|iPod|iPhone|iPad/.test(navigator.platform); export const isWindows = /^Win/.test(navigator.platform); export const isAndroid = /\b(android)\b/i.test(navigator.userAgent); diff --git a/packages/excalidraw/context/tunnels.ts b/packages/excalidraw/context/tunnels.ts index 73b85ba6a..370a0f2bb 100644 --- a/packages/excalidraw/context/tunnels.ts +++ b/packages/excalidraw/context/tunnels.ts @@ -1,6 +1,6 @@ +import { createIsolation } from "jotai-scope"; import React from "react"; import tunnel from "tunnel-rat"; -import { createIsolation } from "jotai-scope"; export type Tunnel = ReturnType; diff --git a/packages/excalidraw/context/ui-appState.ts b/packages/excalidraw/context/ui-appState.ts index f94ce28c3..174b6e89c 100644 --- a/packages/excalidraw/context/ui-appState.ts +++ b/packages/excalidraw/context/ui-appState.ts @@ -1,4 +1,5 @@ import React from "react"; + import type { UIAppState } from "../types"; export const UIAppStateContext = React.createContext(null!); diff --git a/packages/excalidraw/cursor.ts b/packages/excalidraw/cursor.ts index 759b7a4ae..1aa6e52d2 100644 --- a/packages/excalidraw/cursor.ts +++ b/packages/excalidraw/cursor.ts @@ -1,7 +1,9 @@ -import { CURSOR_TYPE, MIME_TYPES, THEME } from "./constants"; import OpenColor from "open-color"; -import type { AppState, DataURL } from "./types"; + import { isHandToolActive, isEraserActive } from "./appState"; +import { CURSOR_TYPE, MIME_TYPES, THEME } from "./constants"; + +import type { AppState, DataURL } from "./types"; const laserPointerCursorSVG_tag = ``; const laserPointerCursorBackgroundSVG = ``; diff --git a/packages/excalidraw/data/blob.ts b/packages/excalidraw/data/blob.ts index 2293f860c..54505068f 100644 --- a/packages/excalidraw/data/blob.ts +++ b/packages/excalidraw/data/blob.ts @@ -1,19 +1,22 @@ import { nanoid } from "nanoid"; + import { cleanAppStateForExport } from "../appState"; import { IMAGE_MIME_TYPES, MIME_TYPES } from "../constants"; import { clearElementsForExport } from "../element"; -import type { ExcalidrawElement, FileId } from "../element/types"; import { CanvasError, ImageSceneDataError } from "../errors"; import { calculateScrollCenter } from "../scene"; import { decodeSvgBase64Payload } from "../scene/export"; -import type { AppState, DataURL, LibraryItem } from "../types"; -import type { ValueOf } from "../utility-types"; import { bytesToHexString, isPromiseLike } from "../utils"; + import { base64ToString, stringToBase64, toByteString } from "./encode"; -import type { FileSystemHandle } from "./filesystem"; import { nativeFileSystemSupported } from "./filesystem"; import { isValidExcalidrawData, isValidLibrary } from "./json"; import { restore, restoreLibraryItems } from "./restore"; + +import type { FileSystemHandle } from "./filesystem"; +import type { ExcalidrawElement, FileId } from "../element/types"; +import type { AppState, DataURL, LibraryItem } from "../types"; +import type { ValueOf } from "../utility-types"; import type { ImportedLibraryData } from "./types"; const parseFileContents = async (blob: Blob | File): Promise => { diff --git a/packages/excalidraw/data/encode.ts b/packages/excalidraw/data/encode.ts index 15dfdb2c0..31d7a5bc1 100644 --- a/packages/excalidraw/data/encode.ts +++ b/packages/excalidraw/data/encode.ts @@ -1,4 +1,5 @@ import { deflate, inflate } from "pako"; + import { encryptData, decryptData } from "./encryption"; // ----------------------------------------------------------------------------- diff --git a/packages/excalidraw/data/encryption.ts b/packages/excalidraw/data/encryption.ts index 33e689977..a796d05b4 100644 --- a/packages/excalidraw/data/encryption.ts +++ b/packages/excalidraw/data/encryption.ts @@ -1,4 +1,5 @@ import { ENCRYPTION_KEY_BITS } from "../constants"; + import { blobToArrayBuffer } from "./blob"; export const IV_LENGTH_BYTES = 12; diff --git a/packages/excalidraw/data/filesystem.ts b/packages/excalidraw/data/filesystem.ts index 186d58734..0bdab68fd 100644 --- a/packages/excalidraw/data/filesystem.ts +++ b/packages/excalidraw/data/filesystem.ts @@ -1,13 +1,15 @@ -import type { FileSystemHandle } from "browser-fs-access"; import { fileOpen as _fileOpen, fileSave as _fileSave, supported as nativeFileSystemSupported, } from "browser-fs-access"; + import { EVENT, MIME_TYPES } from "../constants"; import { AbortError } from "../errors"; import { debounce } from "../utils"; +import type { FileSystemHandle } from "browser-fs-access"; + type FILE_EXTENSION = Exclude; const INPUT_CHANGE_INTERVAL_MS = 500; diff --git a/packages/excalidraw/data/image.ts b/packages/excalidraw/data/image.ts index 0359a9c32..e54f7bab5 100644 --- a/packages/excalidraw/data/image.ts +++ b/packages/excalidraw/data/image.ts @@ -1,9 +1,11 @@ -import decodePng from "png-chunks-extract"; import tEXt from "png-chunk-text"; import encodePng from "png-chunks-encode"; -import { encode, decode } from "./encode"; +import decodePng from "png-chunks-extract"; + import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants"; + import { blobToArrayBuffer } from "./blob"; +import { encode, decode } from "./encode"; // ----------------------------------------------------------------------------- // PNG diff --git a/packages/excalidraw/data/index.ts b/packages/excalidraw/data/index.ts index 04cdcaf95..013ac5b75 100644 --- a/packages/excalidraw/data/index.ts +++ b/packages/excalidraw/data/index.ts @@ -11,22 +11,24 @@ import { } from "../constants"; import { getNonDeletedElements } from "../element"; import { isFrameLikeElement } from "../element/typeChecks"; +import { getElementsOverlappingFrame } from "../frame"; +import { t } from "../i18n"; +import { getSelectedElements, isSomeElementSelected } from "../scene"; +import { exportToCanvas, exportToSvg } from "../scene/export"; +import { cloneJSON } from "../utils"; + +import { canvasToBlob } from "./blob"; +import { fileSave } from "./filesystem"; +import { serializeAsJSON } from "./json"; + +import type { FileSystemHandle } from "./filesystem"; import type { ExcalidrawElement, ExcalidrawFrameLikeElement, NonDeletedExcalidrawElement, } from "../element/types"; -import { getElementsOverlappingFrame } from "../frame"; -import { t } from "../i18n"; -import { getSelectedElements, isSomeElementSelected } from "../scene"; -import { exportToCanvas, exportToSvg } from "../scene/export"; import type { ExportType } from "../scene/types"; import type { AppState, BinaryFiles } from "../types"; -import { cloneJSON } from "../utils"; -import { canvasToBlob } from "./blob"; -import type { FileSystemHandle } from "./filesystem"; -import { fileSave } from "./filesystem"; -import { serializeAsJSON } from "./json"; export { loadFromBlob } from "./blob"; export { loadFromJSON, saveAsJSON } from "./json"; diff --git a/packages/excalidraw/data/json.ts b/packages/excalidraw/data/json.ts index 1270fd1b4..c1cdd4f92 100644 --- a/packages/excalidraw/data/json.ts +++ b/packages/excalidraw/data/json.ts @@ -1,4 +1,3 @@ -import { fileOpen, fileSave } from "./filesystem"; import { cleanAppStateForExport, clearAppStateForDatabase } from "../appState"; import { DEFAULT_FILENAME, @@ -8,10 +7,12 @@ import { VERSIONS, } from "../constants"; import { clearElementsForDatabase, clearElementsForExport } from "../element"; + +import { isImageFileHandle, loadFromBlob, normalizeFile } from "./blob"; +import { fileOpen, fileSave } from "./filesystem"; + import type { ExcalidrawElement } from "../element/types"; import type { AppState, BinaryFiles, LibraryItems } from "../types"; -import { isImageFileHandle, loadFromBlob, normalizeFile } from "./blob"; - import type { ExportedDataState, ImportedDataState, diff --git a/packages/excalidraw/data/library.ts b/packages/excalidraw/data/library.ts index 1c23edcf0..76424f54a 100644 --- a/packages/excalidraw/data/library.ts +++ b/packages/excalidraw/data/library.ts @@ -1,19 +1,5 @@ -import { loadLibraryFromBlob } from "./blob"; -import type { - LibraryItems, - LibraryItem, - ExcalidrawImperativeAPI, - LibraryItemsSource, - LibraryItems_anyVersion, -} from "../types"; -import { restoreLibraryItems } from "./restore"; -import type App from "../components/App"; -import { atom, editorJotaiStore } from "../editor-jotai"; -import type { ExcalidrawElement } from "../element/types"; -import { getCommonBoundingBox } from "../element/bounds"; -import { AbortError } from "../errors"; -import { t } from "../i18n"; import { useEffect, useRef } from "react"; + import { URL_HASH_KEYS, URL_QUERY_KEYS, @@ -22,7 +8,14 @@ import { DEFAULT_SIDEBAR, LIBRARY_SIDEBAR_TAB, } from "../constants"; +import { atom, editorJotaiStore } from "../editor-jotai"; +import { hashElementsVersion, hashString } from "../element"; +import { getCommonBoundingBox } from "../element/bounds"; +import { Emitter } from "../emitter"; +import { AbortError } from "../errors"; import { libraryItemSvgsCache } from "../hooks/useLibraryItemSvg"; +import { t } from "../i18n"; +import { Queue } from "../queue"; import { arrayToMap, cloneJSON, @@ -30,12 +23,22 @@ import { promiseTry, resolvablePromise, } from "../utils"; -import type { MaybePromise } from "../utility-types"; -import { Emitter } from "../emitter"; -import { Queue } from "../queue"; -import { hashElementsVersion, hashString } from "../element"; + +import { loadLibraryFromBlob } from "./blob"; +import { restoreLibraryItems } from "./restore"; import { toValidURL } from "./url"; +import type App from "../components/App"; +import type { ExcalidrawElement } from "../element/types"; +import type { + LibraryItems, + LibraryItem, + ExcalidrawImperativeAPI, + LibraryItemsSource, + LibraryItems_anyVersion, +} from "../types"; +import type { MaybePromise } from "../utility-types"; + /** * format: hostname or hostname/pathname * diff --git a/packages/excalidraw/data/reconcile.ts b/packages/excalidraw/data/reconcile.ts index fa4cff8d1..ef644c3aa 100644 --- a/packages/excalidraw/data/reconcile.ts +++ b/packages/excalidraw/data/reconcile.ts @@ -1,14 +1,16 @@ import throttle from "lodash.throttle"; + import { ENV } from "../constants"; -import type { OrderedExcalidrawElement } from "../element/types"; import { orderByFractionalIndex, syncInvalidIndices, validateFractionalIndices, } from "../fractionalIndex"; +import { arrayToMap } from "../utils"; + +import type { OrderedExcalidrawElement } from "../element/types"; import type { AppState } from "../types"; import type { MakeBrand } from "../utility-types"; -import { arrayToMap } from "../utils"; export type ReconciledExcalidrawElement = OrderedExcalidrawElement & MakeBrand<"ReconciledElement">; diff --git a/packages/excalidraw/data/resave.ts b/packages/excalidraw/data/resave.ts index 624918425..2c448429a 100644 --- a/packages/excalidraw/data/resave.ts +++ b/packages/excalidraw/data/resave.ts @@ -1,7 +1,9 @@ +import { getFileHandleType, isImageFileHandleType } from "./blob"; + +import { exportCanvas, prepareElementsForExport } from "."; + import type { ExcalidrawElement } from "../element/types"; import type { AppState, BinaryFiles } from "../types"; -import { exportCanvas, prepareElementsForExport } from "."; -import { getFileHandleType, isImageFileHandleType } from "./blob"; export const resaveAsImageWithScene = async ( elements: readonly ExcalidrawElement[], diff --git a/packages/excalidraw/data/restore.ts b/packages/excalidraw/data/restore.ts index c4e45b025..df1a64621 100644 --- a/packages/excalidraw/data/restore.ts +++ b/packages/excalidraw/data/restore.ts @@ -1,3 +1,56 @@ +import { isFiniteNumber, pointFrom } from "@excalidraw/math"; + +import type { LocalPoint, Radians } from "@excalidraw/math"; + +import { getDefaultAppState } from "../appState"; +import { + DEFAULT_FONT_FAMILY, + DEFAULT_TEXT_ALIGN, + DEFAULT_VERTICAL_ALIGN, + FONT_FAMILY, + ROUNDNESS, + DEFAULT_SIDEBAR, + DEFAULT_ELEMENT_PROPS, + DEFAULT_GRID_SIZE, + DEFAULT_GRID_STEP, +} from "../constants"; +import { + getNonDeletedElements, + getNormalizedDimensions, + isInvisiblySmallElement, + refreshTextDimensions, +} from "../element"; +import { normalizeFixedPoint } from "../element/binding"; +import { + updateElbowArrowPoints, + validateElbowPoints, +} from "../element/elbowArrow"; +import { LinearElementEditor } from "../element/linearElementEditor"; +import { bumpVersion } from "../element/mutateElement"; +import { getContainerElement } from "../element/textElement"; +import { detectLineHeight } from "../element/textMeasurements"; +import { + isArrowElement, + isElbowArrow, + isFixedPointBinding, + isLinearElement, + isTextElement, + isUsingAdaptiveRadius, +} from "../element/typeChecks"; +import { getLineHeight } from "../fonts"; +import { syncInvalidIndices } from "../fractionalIndex"; +import { randomId } from "../random"; +import { + getNormalizedGridSize, + getNormalizedGridStep, + getNormalizedZoom, +} from "../scene"; +import { getUpdatedTimestamp, updateActiveTool } from "../utils"; +import { arrayToMap } from "../utils"; +import { getSizeFromPoints } from "../points"; + +import { normalizeLink } from "./url"; + import type { ExcalidrawArrowElement, ExcalidrawElbowArrowElement, @@ -14,57 +67,8 @@ import type { StrokeRoundness, } from "../element/types"; import type { AppState, BinaryFiles, LibraryItem } from "../types"; -import type { ImportedDataState, LegacyAppState } from "./types"; -import { - getNonDeletedElements, - getNormalizedDimensions, - isInvisiblySmallElement, - refreshTextDimensions, -} from "../element"; -import { - isArrowElement, - isElbowArrow, - isFixedPointBinding, - isLinearElement, - isTextElement, - isUsingAdaptiveRadius, -} from "../element/typeChecks"; -import { randomId } from "../random"; -import { - DEFAULT_FONT_FAMILY, - DEFAULT_TEXT_ALIGN, - DEFAULT_VERTICAL_ALIGN, - FONT_FAMILY, - ROUNDNESS, - DEFAULT_SIDEBAR, - DEFAULT_ELEMENT_PROPS, - DEFAULT_GRID_SIZE, - DEFAULT_GRID_STEP, -} from "../constants"; -import { getDefaultAppState } from "../appState"; -import { LinearElementEditor } from "../element/linearElementEditor"; -import { bumpVersion } from "../element/mutateElement"; -import { getUpdatedTimestamp, updateActiveTool } from "../utils"; -import { arrayToMap } from "../utils"; import type { MarkOptional, Mutable } from "../utility-types"; -import { getContainerElement } from "../element/textElement"; -import { normalizeLink } from "./url"; -import { syncInvalidIndices } from "../fractionalIndex"; -import { getSizeFromPoints } from "../points"; -import { getLineHeight } from "../fonts"; -import { normalizeFixedPoint } from "../element/binding"; -import { - getNormalizedGridSize, - getNormalizedGridStep, - getNormalizedZoom, -} from "../scene"; -import type { LocalPoint, Radians } from "@excalidraw/math"; -import { isFiniteNumber, pointFrom } from "@excalidraw/math"; -import { detectLineHeight } from "../element/textMeasurements"; -import { - updateElbowArrowPoints, - validateElbowPoints, -} from "../element/elbowArrow"; +import type { ImportedDataState, LegacyAppState } from "./types"; type RestoredAppState = Omit< AppState, diff --git a/packages/excalidraw/data/transform.test.ts b/packages/excalidraw/data/transform.test.ts index a36af0c35..94f7b6c82 100644 --- a/packages/excalidraw/data/transform.test.ts +++ b/packages/excalidraw/data/transform.test.ts @@ -1,8 +1,10 @@ -import { vi } from "vitest"; -import type { ExcalidrawElementSkeleton } from "./transform"; -import { convertToExcalidrawElements } from "./transform"; -import type { ExcalidrawArrowElement } from "../element/types"; import { pointFrom } from "@excalidraw/math"; +import { vi } from "vitest"; + +import { convertToExcalidrawElements } from "./transform"; + +import type { ExcalidrawElementSkeleton } from "./transform"; +import type { ExcalidrawArrowElement } from "../element/types"; const opts = { regenerateIds: false }; diff --git a/packages/excalidraw/data/transform.ts b/packages/excalidraw/data/transform.ts index 023740cf1..704b63cb5 100644 --- a/packages/excalidraw/data/transform.ts +++ b/packages/excalidraw/data/transform.ts @@ -1,3 +1,5 @@ +import { pointFrom, type LocalPoint } from "@excalidraw/math"; + import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, @@ -11,7 +13,6 @@ import { redrawTextBoundingBox, } from "../element"; import { bindLinearElement } from "../element/binding"; -import type { ElementConstructorOpts } from "../element/newElement"; import { newArrowElement, newFrameElement, @@ -19,6 +20,22 @@ import { newMagicFrameElement, newTextElement, } from "../element/newElement"; +import { measureText, normalizeText } from "../element/textMeasurements"; +import { isArrowElement } from "../element/typeChecks"; +import { getLineHeight } from "../fonts"; +import { syncInvalidIndices } from "../fractionalIndex"; +import { getSizeFromPoints } from "../points"; +import { randomId } from "../random"; +import { + arrayToMap, + assertNever, + cloneJSON, + getFontString, + isDevEnv, + toBrandedType, +} from "../utils"; + +import type { ElementConstructorOpts } from "../element/newElement"; import type { ElementsMap, ExcalidrawArrowElement, @@ -40,21 +57,6 @@ import type { VerticalAlign, } from "../element/types"; import type { MarkOptional } from "../utility-types"; -import { - arrayToMap, - assertNever, - cloneJSON, - getFontString, - isDevEnv, - toBrandedType, -} from "../utils"; -import { getSizeFromPoints } from "../points"; -import { randomId } from "../random"; -import { syncInvalidIndices } from "../fractionalIndex"; -import { getLineHeight } from "../fonts"; -import { isArrowElement } from "../element/typeChecks"; -import { pointFrom, type LocalPoint } from "@excalidraw/math"; -import { measureText, normalizeText } from "../element/textMeasurements"; export type ValidLinearElement = { type: "arrow" | "line"; diff --git a/packages/excalidraw/data/types.ts b/packages/excalidraw/data/types.ts index f5f5535a5..de3436137 100644 --- a/packages/excalidraw/data/types.ts +++ b/packages/excalidraw/data/types.ts @@ -1,3 +1,5 @@ +import type { cleanAppStateForExport } from "../appState"; +import type { VERSIONS } from "../constants"; import type { ExcalidrawElement } from "../element/types"; import type { AppState, @@ -5,8 +7,6 @@ import type { LibraryItems, LibraryItems_anyVersion, } from "../types"; -import type { cleanAppStateForExport } from "../appState"; -import type { VERSIONS } from "../constants"; export interface ExportedDataState { type: string; diff --git a/packages/excalidraw/data/url.ts b/packages/excalidraw/data/url.ts index 2ab553bb8..a758d233e 100644 --- a/packages/excalidraw/data/url.ts +++ b/packages/excalidraw/data/url.ts @@ -1,4 +1,5 @@ import { sanitizeUrl } from "@braintree/sanitize-url"; + import { escapeDoubleQuotes } from "../utils"; export const normalizeLink = (link: string) => { diff --git a/packages/excalidraw/distribute.ts b/packages/excalidraw/distribute.ts index 368b2f24d..767d3802c 100644 --- a/packages/excalidraw/distribute.ts +++ b/packages/excalidraw/distribute.ts @@ -1,6 +1,7 @@ +import { getCommonBoundingBox } from "./element/bounds"; import { newElementWith } from "./element/mutateElement"; import { getMaximumGroups } from "./groups"; -import { getCommonBoundingBox } from "./element/bounds"; + import type { ElementsMap, ExcalidrawElement } from "./element/types"; export interface Distribution { diff --git a/packages/excalidraw/element/ElementCanvasButtons.tsx b/packages/excalidraw/element/ElementCanvasButtons.tsx index 1bcad97b4..e67e8d5bc 100644 --- a/packages/excalidraw/element/ElementCanvasButtons.tsx +++ b/packages/excalidraw/element/ElementCanvasButtons.tsx @@ -1,11 +1,14 @@ -import type { AppState } from "../types"; -import { sceneCoordsToViewportCoords } from "../utils"; -import type { ElementsMap, NonDeletedExcalidrawElement } from "./types"; -import { getElementAbsoluteCoords } from "."; import { useExcalidrawAppState } from "../components/App"; +import { sceneCoordsToViewportCoords } from "../utils"; import "./ElementCanvasButtons.scss"; +import { getElementAbsoluteCoords } from "."; + +import type { AppState } from "../types"; + +import type { ElementsMap, NonDeletedExcalidrawElement } from "./types"; + const CONTAINER_PADDING = 5; const getContainerCoords = ( diff --git a/packages/excalidraw/element/binding.ts b/packages/excalidraw/element/binding.ts index e716b860f..fa472d211 100644 --- a/packages/excalidraw/element/binding.ts +++ b/packages/excalidraw/element/binding.ts @@ -1,63 +1,3 @@ -import type { - ExcalidrawBindableElement, - ExcalidrawElement, - NonDeleted, - ExcalidrawLinearElement, - PointBinding, - NonDeletedExcalidrawElement, - ElementsMap, - NonDeletedSceneElementsMap, - ExcalidrawTextElement, - ExcalidrawArrowElement, - OrderedExcalidrawElement, - ExcalidrawElbowArrowElement, - FixedPoint, - SceneElementsMap, - FixedPointBinding, -} from "./types"; - -import type { Bounds } from "./bounds"; -import { - getCenterForBounds, - getElementBounds, - doBoundsIntersect, -} from "./bounds"; -import type { AppState } from "../types"; -import { isPointOnShape } from "@excalidraw/utils/collision"; -import { - isArrowElement, - isBindableElement, - isBindingElement, - isBoundToContainer, - isElbowArrow, - isFixedPointBinding, - isFrameLikeElement, - isLinearElement, - isRectanguloidElement, - isTextElement, -} from "./typeChecks"; -import type { ElementUpdate } from "./mutateElement"; -import { mutateElement } from "./mutateElement"; -import type Scene from "../scene/Scene"; -import { LinearElementEditor } from "./linearElementEditor"; -import { - arrayToMap, - isBindingFallthroughEnabled, - tupleToCoors, -} from "../utils"; -import { KEYS } from "../keys"; -import { getBoundTextElement, handleBindTextResize } from "./textElement"; -import { aabbForElement, getElementShape, pointInsideBounds } from "../shapes"; -import { - compareHeading, - HEADING_DOWN, - HEADING_RIGHT, - HEADING_UP, - headingForPointFromElement, - vectorToHeading, - type Heading, -} from "./heading"; -import type { LocalPoint, Radians } from "@excalidraw/math"; import { lineSegment, pointFrom, @@ -76,8 +16,71 @@ import { round, PRECISION, } from "@excalidraw/math"; +import { isPointOnShape } from "@excalidraw/utils/collision"; + +import type { LocalPoint, Radians } from "@excalidraw/math"; + +import { KEYS } from "../keys"; +import { aabbForElement, getElementShape, pointInsideBounds } from "../shapes"; +import { + arrayToMap, + isBindingFallthroughEnabled, + tupleToCoors, +} from "../utils"; + +import { + getCenterForBounds, + getElementBounds, + doBoundsIntersect, +} from "./bounds"; import { intersectElementWithLineSegment } from "./collision"; import { distanceToBindableElement } from "./distance"; +import { + compareHeading, + HEADING_DOWN, + HEADING_RIGHT, + HEADING_UP, + headingForPointFromElement, + vectorToHeading, + type Heading, +} from "./heading"; +import { LinearElementEditor } from "./linearElementEditor"; +import { mutateElement } from "./mutateElement"; +import { getBoundTextElement, handleBindTextResize } from "./textElement"; +import { + isArrowElement, + isBindableElement, + isBindingElement, + isBoundToContainer, + isElbowArrow, + isFixedPointBinding, + isFrameLikeElement, + isLinearElement, + isRectanguloidElement, + isTextElement, +} from "./typeChecks"; + +import type { Bounds } from "./bounds"; +import type { ElementUpdate } from "./mutateElement"; +import type { + ExcalidrawBindableElement, + ExcalidrawElement, + NonDeleted, + ExcalidrawLinearElement, + PointBinding, + NonDeletedExcalidrawElement, + ElementsMap, + NonDeletedSceneElementsMap, + ExcalidrawTextElement, + ExcalidrawArrowElement, + OrderedExcalidrawElement, + ExcalidrawElbowArrowElement, + FixedPoint, + SceneElementsMap, + FixedPointBinding, +} from "./types"; +import type Scene from "../scene/Scene"; +import type { AppState } from "../types"; export type SuggestedBinding = | NonDeleted diff --git a/packages/excalidraw/element/bounds.test.ts b/packages/excalidraw/element/bounds.test.ts index 9d91d0925..936ebf797 100644 --- a/packages/excalidraw/element/bounds.test.ts +++ b/packages/excalidraw/element/bounds.test.ts @@ -1,8 +1,12 @@ -import type { LocalPoint } from "@excalidraw/math"; import { pointFrom } from "@excalidraw/math"; + +import type { LocalPoint } from "@excalidraw/math"; + import { ROUNDNESS } from "../constants"; import { arrayToMap } from "../utils"; + import { getElementAbsoluteCoords, getElementBounds } from "./bounds"; + import type { ExcalidrawElement, ExcalidrawLinearElement } from "./types"; const _ce = ({ diff --git a/packages/excalidraw/element/bounds.ts b/packages/excalidraw/element/bounds.ts index 06f977023..9d8fd22d3 100644 --- a/packages/excalidraw/element/bounds.ts +++ b/packages/excalidraw/element/bounds.ts @@ -1,3 +1,39 @@ +import { + degreesToRadians, + lineSegment, + pointFrom, + pointDistance, + pointFromArray, + pointRotateRads, +} from "@excalidraw/math"; +import { getCurvePathOps } from "@excalidraw/utils/geometry/shape"; +import rough from "roughjs/bin/rough"; + +import type { + Degrees, + GlobalPoint, + LineSegment, + LocalPoint, + Radians, +} from "@excalidraw/math"; + +import { rescalePoints } from "../points"; +import { generateRoughOptions } from "../scene/Shape"; +import { ShapeCache } from "../scene/ShapeCache"; +import { arrayToMap, invariant } from "../utils"; + +import { LinearElementEditor } from "./linearElementEditor"; +import { getBoundTextElement, getContainerElement } from "./textElement"; +import { + isArrowElement, + isBoundToContainer, + isFreeDrawElement, + isLinearElement, + isTextElement, +} from "./typeChecks"; + +import type { AppState } from "../types"; +import type { Mutable } from "../utility-types"; import type { ExcalidrawElement, ExcalidrawLinearElement, @@ -7,40 +43,8 @@ import type { ExcalidrawTextElementWithContainer, ElementsMap, } from "./types"; -import rough from "roughjs/bin/rough"; -import type { Point as RoughPoint } from "roughjs/bin/geometry"; import type { Drawable, Op } from "roughjs/bin/core"; -import type { AppState } from "../types"; -import { generateRoughOptions } from "../scene/Shape"; -import { - isArrowElement, - isBoundToContainer, - isFreeDrawElement, - isLinearElement, - isTextElement, -} from "./typeChecks"; -import { rescalePoints } from "../points"; -import { getBoundTextElement, getContainerElement } from "./textElement"; -import { LinearElementEditor } from "./linearElementEditor"; -import { ShapeCache } from "../scene/ShapeCache"; -import { arrayToMap, invariant } from "../utils"; -import type { - Degrees, - GlobalPoint, - LineSegment, - LocalPoint, - Radians, -} from "@excalidraw/math"; -import { - degreesToRadians, - lineSegment, - pointFrom, - pointDistance, - pointFromArray, - pointRotateRads, -} from "@excalidraw/math"; -import type { Mutable } from "../utility-types"; -import { getCurvePathOps } from "@excalidraw/utils/geometry/shape"; +import type { Point as RoughPoint } from "roughjs/bin/geometry"; export type RectangleBox = { x: number; diff --git a/packages/excalidraw/element/collision.ts b/packages/excalidraw/element/collision.ts index b0a2ce9a5..a86cbd62f 100644 --- a/packages/excalidraw/element/collision.ts +++ b/packages/excalidraw/element/collision.ts @@ -1,31 +1,3 @@ -import type { - ElementsMap, - ExcalidrawDiamondElement, - ExcalidrawElement, - ExcalidrawEllipseElement, - ExcalidrawRectangleElement, - ExcalidrawRectanguloidElement, -} from "./types"; -import { getElementBounds } from "./bounds"; -import type { FrameNameBounds } from "../types"; -import type { GeometricShape } from "@excalidraw/utils/geometry/shape"; -import { getPolygonShape } from "@excalidraw/utils/geometry/shape"; -import { isPointInShape, isPointOnShape } from "@excalidraw/utils/collision"; -import { isTransparent } from "../utils"; -import { - hasBoundTextElement, - isIframeLikeElement, - isImageElement, - isTextElement, -} from "./typeChecks"; -import { getBoundTextShape, isPathALoop } from "../shapes"; -import type { - GlobalPoint, - LineSegment, - LocalPoint, - Polygon, - Radians, -} from "@excalidraw/math"; import { curveIntersectLineSegment, isPointWithinBounds, @@ -40,11 +12,43 @@ import { ellipse, ellipseLineIntersectionPoints, } from "@excalidraw/math/ellipse"; +import { isPointInShape, isPointOnShape } from "@excalidraw/utils/collision"; +import { getPolygonShape } from "@excalidraw/utils/geometry/shape"; + +import type { + GlobalPoint, + LineSegment, + LocalPoint, + Polygon, + Radians, +} from "@excalidraw/math"; +import type { GeometricShape } from "@excalidraw/utils/geometry/shape"; + +import { getBoundTextShape, isPathALoop } from "../shapes"; +import { isTransparent } from "../utils"; + +import { getElementBounds } from "./bounds"; +import { + hasBoundTextElement, + isIframeLikeElement, + isImageElement, + isTextElement, +} from "./typeChecks"; import { deconstructDiamondElement, deconstructRectanguloidElement, } from "./utils"; +import type { + ElementsMap, + ExcalidrawDiamondElement, + ExcalidrawElement, + ExcalidrawEllipseElement, + ExcalidrawRectangleElement, + ExcalidrawRectanguloidElement, +} from "./types"; +import type { FrameNameBounds } from "../types"; + export const shouldTestInside = (element: ExcalidrawElement) => { if (element.type === "arrow") { return false; diff --git a/packages/excalidraw/element/cropElement.ts b/packages/excalidraw/element/cropElement.ts index b10980209..dd75f9360 100644 --- a/packages/excalidraw/element/cropElement.ts +++ b/packages/excalidraw/element/cropElement.ts @@ -1,4 +1,3 @@ -import { type Point } from "points-on-curve"; import { type Radians, pointFrom, @@ -13,6 +12,13 @@ import { clamp, isCloseTo, } from "@excalidraw/math"; +import { type Point } from "points-on-curve"; + +import { + getElementAbsoluteCoords, + getResizedElementAbsoluteCoords, +} from "./bounds"; + import type { TransformHandleType } from "./transformHandles"; import type { ElementsMap, @@ -21,10 +27,6 @@ import type { ImageCrop, NonDeleted, } from "./types"; -import { - getElementAbsoluteCoords, - getResizedElementAbsoluteCoords, -} from "./bounds"; export const MINIMAL_CROP_SIZE = 10; diff --git a/packages/excalidraw/element/distance.ts b/packages/excalidraw/element/distance.ts index 0010ab953..038233555 100644 --- a/packages/excalidraw/element/distance.ts +++ b/packages/excalidraw/element/distance.ts @@ -1,4 +1,3 @@ -import type { GlobalPoint, Radians } from "@excalidraw/math"; import { curvePointDistance, distanceToLineSegment, @@ -6,16 +5,20 @@ import { pointRotateRads, } from "@excalidraw/math"; import { ellipse, ellipseDistanceFromPoint } from "@excalidraw/math/ellipse"; + +import type { GlobalPoint, Radians } from "@excalidraw/math"; + +import { + deconstructDiamondElement, + deconstructRectanguloidElement, +} from "./utils"; + import type { ExcalidrawBindableElement, ExcalidrawDiamondElement, ExcalidrawEllipseElement, ExcalidrawRectanguloidElement, } from "./types"; -import { - deconstructDiamondElement, - deconstructRectanguloidElement, -} from "./utils"; export const distanceToBindableElement = ( element: ExcalidrawBindableElement, diff --git a/packages/excalidraw/element/dragElements.ts b/packages/excalidraw/element/dragElements.ts index bb8fd237e..4cc408b21 100644 --- a/packages/excalidraw/element/dragElements.ts +++ b/packages/excalidraw/element/dragElements.ts @@ -1,17 +1,13 @@ +import { TEXT_AUTOWRAP_THRESHOLD } from "../constants"; +import { getGridPoint } from "../snapping"; +import { getFontString } from "../utils"; + import { updateBoundElements } from "./binding"; -import type { Bounds } from "./bounds"; import { getCommonBounds } from "./bounds"; import { mutateElement } from "./mutateElement"; import { getPerfectElementSize } from "./sizeHelpers"; -import type { NonDeletedExcalidrawElement } from "./types"; -import type { - AppState, - NormalizedZoomValue, - NullableGridSize, - PointerDownState, -} from "../types"; import { getBoundTextElement } from "./textElement"; -import type Scene from "../scene/Scene"; +import { getMinTextElementWidth } from "./textMeasurements"; import { isArrowElement, isElbowArrow, @@ -19,10 +15,16 @@ import { isImageElement, isTextElement, } from "./typeChecks"; -import { getFontString } from "../utils"; -import { TEXT_AUTOWRAP_THRESHOLD } from "../constants"; -import { getGridPoint } from "../snapping"; -import { getMinTextElementWidth } from "./textMeasurements"; + +import type { Bounds } from "./bounds"; +import type { NonDeletedExcalidrawElement } from "./types"; +import type Scene from "../scene/Scene"; +import type { + AppState, + NormalizedZoomValue, + NullableGridSize, + PointerDownState, +} from "../types"; export const dragSelectedElements = ( pointerDownState: PointerDownState, diff --git a/packages/excalidraw/element/elbowArrow.test.tsx b/packages/excalidraw/element/elbowArrow.test.tsx index c00eae989..91e280d30 100644 --- a/packages/excalidraw/element/elbowArrow.test.tsx +++ b/packages/excalidraw/element/elbowArrow.test.tsx @@ -1,4 +1,13 @@ +import { pointFrom } from "@excalidraw/math"; import React from "react"; + +import type { LocalPoint } from "@excalidraw/math"; + +import "../../utils/test-utils"; +import { actionSelectAll } from "../actions"; +import { actionDuplicateSelection } from "../actions/actionDuplicateSelection"; +import { ARROW_TYPE } from "../constants"; +import { Excalidraw, mutateElement } from "../index"; import Scene from "../scene/Scene"; import { API } from "../tests/helpers/api"; import { Pointer, UI } from "../tests/helpers/ui"; @@ -9,19 +18,14 @@ import { queryByTestId, render, } from "../tests/test-utils"; + import { bindLinearElement } from "./binding"; -import { Excalidraw, mutateElement } from "../index"; + import type { ExcalidrawArrowElement, ExcalidrawBindableElement, ExcalidrawElbowArrowElement, } from "./types"; -import { ARROW_TYPE } from "../constants"; -import "../../utils/test-utils"; -import type { LocalPoint } from "@excalidraw/math"; -import { pointFrom } from "@excalidraw/math"; -import { actionDuplicateSelection } from "../actions/actionDuplicateSelection"; -import { actionSelectAll } from "../actions"; const { h } = window; diff --git a/packages/excalidraw/element/elbowArrow.ts b/packages/excalidraw/element/elbowArrow.ts index ce8c3d5a6..5b8dc3813 100644 --- a/packages/excalidraw/element/elbowArrow.ts +++ b/packages/excalidraw/element/elbowArrow.ts @@ -12,11 +12,12 @@ import { type GlobalPoint, type LocalPoint, } from "@excalidraw/math"; + import BinaryHeap from "../binaryheap"; import { getSizeFromPoints } from "../points"; import { aabbForElement, pointInsideBounds } from "../shapes"; import { invariant, isAnyTrue, tupleToCoors } from "../utils"; -import type { AppState } from "../types"; + import { bindPointToSnapToElementOutline, FIXED_BINDING_DISTANCE, @@ -25,8 +26,7 @@ import { snapToMid, getHoveredElementForBinding, } from "./binding"; -import type { Bounds } from "./bounds"; -import type { Heading } from "./heading"; +import { distanceToBindableElement } from "./distance"; import { compareHeading, flipHeading, @@ -46,6 +46,9 @@ import { type NonDeletedSceneElementsMap, type SceneElementsMap, } from "./types"; + +import type { Bounds } from "./bounds"; +import type { Heading } from "./heading"; import type { Arrowhead, ElementsMap, @@ -54,7 +57,7 @@ import type { FixedSegment, NonDeletedExcalidrawElement, } from "./types"; -import { distanceToBindableElement } from "./distance"; +import type { AppState } from "../types"; type GridAddress = [number, number] & { _brand: "gridaddress" }; diff --git a/packages/excalidraw/element/elementLink.ts b/packages/excalidraw/element/elementLink.ts index 991f9caec..79b689c93 100644 --- a/packages/excalidraw/element/elementLink.ts +++ b/packages/excalidraw/element/elementLink.ts @@ -5,6 +5,7 @@ import { ELEMENT_LINK_KEY } from "../constants"; import { normalizeLink } from "../data/url"; import { elementsAreInSameGroup } from "../groups"; + import type { AppProps, AppState } from "../types"; import type { ExcalidrawElement } from "./types"; diff --git a/packages/excalidraw/element/embeddable.ts b/packages/excalidraw/element/embeddable.ts index 8265a0b13..1dc1968a0 100644 --- a/packages/excalidraw/element/embeddable.ts +++ b/packages/excalidraw/element/embeddable.ts @@ -1,18 +1,20 @@ import { register } from "../actions/register"; import { FONT_FAMILY, VERTICAL_ALIGN } from "../constants"; -import type { ExcalidrawProps } from "../types"; -import { escapeDoubleQuotes, getFontString, updateActiveTool } from "../utils"; import { setCursorForShape } from "../cursor"; +import { CaptureUpdateAction } from "../store"; +import { escapeDoubleQuotes, getFontString, updateActiveTool } from "../utils"; + import { newTextElement } from "./newElement"; import { wrapText } from "./textWrapping"; import { isIframeElement } from "./typeChecks"; + +import type { ExcalidrawProps } from "../types"; +import type { MarkRequired } from "../utility-types"; import type { ExcalidrawElement, ExcalidrawIframeLikeElement, IframeData, } from "./types"; -import type { MarkRequired } from "../utility-types"; -import { CaptureUpdateAction } from "../store"; type IframeDataWithSandbox = MarkRequired; diff --git a/packages/excalidraw/element/flowchart.test.tsx b/packages/excalidraw/element/flowchart.test.tsx index d47c850b7..bc026e7d7 100644 --- a/packages/excalidraw/element/flowchart.test.tsx +++ b/packages/excalidraw/element/flowchart.test.tsx @@ -1,9 +1,9 @@ -import { render, unmountComponent } from "../tests/test-utils"; -import { reseed } from "../random"; -import { UI, Keyboard, Pointer } from "../tests/helpers/ui"; import { Excalidraw } from "../index"; -import { API } from "../tests/helpers/api"; import { KEYS } from "../keys"; +import { reseed } from "../random"; +import { API } from "../tests/helpers/api"; +import { UI, Keyboard, Pointer } from "../tests/helpers/ui"; +import { render, unmountComponent } from "../tests/test-utils"; unmountComponent(); diff --git a/packages/excalidraw/element/flowchart.ts b/packages/excalidraw/element/flowchart.ts index 09f006dd0..9880f27af 100644 --- a/packages/excalidraw/element/flowchart.ts +++ b/packages/excalidraw/element/flowchart.ts @@ -1,3 +1,12 @@ +import { pointFrom, type LocalPoint } from "@excalidraw/math"; + +import { elementOverlapsWithFrame, elementsAreInFrameBounds } from "../frame"; +import { KEYS } from "../keys"; +import { aabbForElement } from "../shapes"; +import { invariant, toBrandedType } from "../utils"; + +import { bindLinearElement } from "./binding"; +import { updateElbowArrowPoints } from "./elbowArrow"; import { HEADING_DOWN, HEADING_LEFT, @@ -7,9 +16,15 @@ import { headingForPointFromElement, type Heading, } from "./heading"; -import { bindLinearElement } from "./binding"; import { LinearElementEditor } from "./linearElementEditor"; +import { mutateElement } from "./mutateElement"; import { newArrowElement, newElement } from "./newElement"; +import { + isBindableElement, + isElbowArrow, + isFrameElement, + isFlowchartNodeElement, +} from "./typeChecks"; import { type ElementsMap, type ExcalidrawBindableElement, @@ -19,20 +34,8 @@ import { type Ordered, type OrderedExcalidrawElement, } from "./types"; -import { KEYS } from "../keys"; + import type { AppState, PendingExcalidrawElements } from "../types"; -import { mutateElement } from "./mutateElement"; -import { elementOverlapsWithFrame, elementsAreInFrameBounds } from "../frame"; -import { - isBindableElement, - isElbowArrow, - isFrameElement, - isFlowchartNodeElement, -} from "./typeChecks"; -import { invariant, toBrandedType } from "../utils"; -import { pointFrom, type LocalPoint } from "@excalidraw/math"; -import { aabbForElement } from "../shapes"; -import { updateElbowArrowPoints } from "./elbowArrow"; type LinkDirection = "up" | "right" | "down" | "left"; diff --git a/packages/excalidraw/element/heading.ts b/packages/excalidraw/element/heading.ts index 94e86339d..ddebeca53 100644 --- a/packages/excalidraw/element/heading.ts +++ b/packages/excalidraw/element/heading.ts @@ -1,10 +1,3 @@ -import type { - LocalPoint, - GlobalPoint, - Triangle, - Vector, - Radians, -} from "@excalidraw/math"; import { pointFrom, pointRotateRads, @@ -13,7 +6,17 @@ import { triangleIncludesPoint, vectorFromPoint, } from "@excalidraw/math"; + +import type { + LocalPoint, + GlobalPoint, + Triangle, + Vector, + Radians, +} from "@excalidraw/math"; + import { getCenterForBounds, type Bounds } from "./bounds"; + import type { ExcalidrawBindableElement } from "./types"; export const HEADING_RIGHT = [1, 0] as Heading; diff --git a/packages/excalidraw/element/image.ts b/packages/excalidraw/element/image.ts index 32644b64d..0d5f9fb5a 100644 --- a/packages/excalidraw/element/image.ts +++ b/packages/excalidraw/element/image.ts @@ -3,8 +3,10 @@ // ----------------------------------------------------------------------------- import { MIME_TYPES, SVG_NS } from "../constants"; -import type { AppClassProperties, DataURL, BinaryFiles } from "../types"; + import { isInitializedImageElement } from "./typeChecks"; + +import type { AppClassProperties, DataURL, BinaryFiles } from "../types"; import type { ExcalidrawElement, FileId, diff --git a/packages/excalidraw/element/index.ts b/packages/excalidraw/element/index.ts index a9b747681..abe84e031 100644 --- a/packages/excalidraw/element/index.ts +++ b/packages/excalidraw/element/index.ts @@ -1,10 +1,11 @@ +import { isInvisiblySmallElement } from "./sizeHelpers"; +import { isLinearElementType } from "./typeChecks"; + import type { ExcalidrawElement, NonDeletedExcalidrawElement, NonDeleted, } from "./types"; -import { isInvisiblySmallElement } from "./sizeHelpers"; -import { isLinearElementType } from "./typeChecks"; export { newElement, diff --git a/packages/excalidraw/element/linearElementEditor.ts b/packages/excalidraw/element/linearElementEditor.ts index b616268a6..f9b23f048 100644 --- a/packages/excalidraw/element/linearElementEditor.ts +++ b/packages/excalidraw/element/linearElementEditor.ts @@ -1,3 +1,48 @@ +import { + pointCenter, + pointFrom, + pointRotateRads, + pointsEqual, + type GlobalPoint, + type LocalPoint, + pointDistance, + vectorFromPoint, +} from "@excalidraw/math"; +import { getCurvePathOps } from "@excalidraw/utils/geometry/shape"; + +import type { Radians } from "@excalidraw/math"; + +import { DRAGGING_THRESHOLD } from "../constants"; +import { KEYS, shouldRotateWithDiscreteAngle } from "../keys"; +import { ShapeCache } from "../scene/ShapeCache"; +import { + getBezierCurveLength, + getBezierXY, + getControlPointsForBezierCurve, + isPathALoop, + mapIntervalToBezierT, +} from "../shapes"; +import { getGridPoint } from "../snapping"; +import { invariant, tupleToCoors } from "../utils"; + +import { + bindOrUnbindLinearElement, + getHoveredElementForBinding, + isBindingEnabled, +} from "./binding"; +import { getElementPointsCoords, getMinMaxXYFromCurvePathOps } from "./bounds"; +import { headingIsHorizontal, vectorToHeading } from "./heading"; +import { mutateElement } from "./mutateElement"; +import { getBoundTextElement, handleBindTextResize } from "./textElement"; +import { + isBindingElement, + isElbowArrow, + isFixedPointBinding, +} from "./typeChecks"; + +import { getElementAbsoluteCoords, getLockedLinearCursorAlignSize } from "."; + +import type { Bounds } from "./bounds"; import type { NonDeleted, ExcalidrawLinearElement, @@ -12,9 +57,8 @@ import type { FixedSegment, ExcalidrawElbowArrowElement, } from "./types"; -import { getElementAbsoluteCoords, getLockedLinearCursorAlignSize } from "."; -import type { Bounds } from "./bounds"; -import { getElementPointsCoords, getMinMaxXYFromCurvePathOps } from "./bounds"; +import type Scene from "../scene/Scene"; +import type { Store } from "../store"; import type { AppState, PointerCoords, @@ -23,47 +67,7 @@ import type { NullableGridSize, Zoom, } from "../types"; -import { mutateElement } from "./mutateElement"; - -import { - bindOrUnbindLinearElement, - getHoveredElementForBinding, - isBindingEnabled, -} from "./binding"; -import { invariant, tupleToCoors } from "../utils"; -import { - isBindingElement, - isElbowArrow, - isFixedPointBinding, -} from "./typeChecks"; -import { KEYS, shouldRotateWithDiscreteAngle } from "../keys"; -import { getBoundTextElement, handleBindTextResize } from "./textElement"; -import { DRAGGING_THRESHOLD } from "../constants"; import type { Mutable } from "../utility-types"; -import { ShapeCache } from "../scene/ShapeCache"; -import type { Store } from "../store"; -import type Scene from "../scene/Scene"; -import type { Radians } from "@excalidraw/math"; -import { - pointCenter, - pointFrom, - pointRotateRads, - pointsEqual, - type GlobalPoint, - type LocalPoint, - pointDistance, - vectorFromPoint, -} from "@excalidraw/math"; -import { - getBezierCurveLength, - getBezierXY, - getControlPointsForBezierCurve, - isPathALoop, - mapIntervalToBezierT, -} from "../shapes"; -import { getGridPoint } from "../snapping"; -import { headingIsHorizontal, vectorToHeading } from "./heading"; -import { getCurvePathOps } from "@excalidraw/utils/geometry/shape"; const editorMidPointsCache: { version: number | null; diff --git a/packages/excalidraw/element/mutateElement.ts b/packages/excalidraw/element/mutateElement.ts index cfff5c826..fc96ec312 100644 --- a/packages/excalidraw/element/mutateElement.ts +++ b/packages/excalidraw/element/mutateElement.ts @@ -1,13 +1,16 @@ -import type { ExcalidrawElement, NonDeletedSceneElementsMap } from "./types"; -import Scene from "../scene/Scene"; +import type { Radians } from "@excalidraw/math"; + import { getSizeFromPoints } from "../points"; import { randomInteger } from "../random"; -import { getUpdatedTimestamp, toBrandedType } from "../utils"; -import type { Mutable } from "../utility-types"; +import Scene from "../scene/Scene"; import { ShapeCache } from "../scene/ShapeCache"; -import { isElbowArrow } from "./typeChecks"; +import { getUpdatedTimestamp, toBrandedType } from "../utils"; + import { updateElbowArrowPoints } from "./elbowArrow"; -import type { Radians } from "@excalidraw/math"; +import { isElbowArrow } from "./typeChecks"; + +import type { ExcalidrawElement, NonDeletedSceneElementsMap } from "./types"; +import type { Mutable } from "../utility-types"; export type ElementUpdate = Omit< Partial, diff --git a/packages/excalidraw/element/newElement.test.ts b/packages/excalidraw/element/newElement.test.ts index 9c9006b6c..418ede1be 100644 --- a/packages/excalidraw/element/newElement.test.ts +++ b/packages/excalidraw/element/newElement.test.ts @@ -1,12 +1,16 @@ -import { duplicateElement, duplicateElements } from "./newElement"; -import { mutateElement } from "./mutateElement"; -import { API } from "../tests/helpers/api"; -import { FONT_FAMILY, ROUNDNESS } from "../constants"; -import { isPrimitive } from "../utils"; -import type { ExcalidrawLinearElement } from "./types"; -import type { LocalPoint } from "@excalidraw/math"; import { pointFrom } from "@excalidraw/math"; +import type { LocalPoint } from "@excalidraw/math"; + +import { FONT_FAMILY, ROUNDNESS } from "../constants"; +import { API } from "../tests/helpers/api"; +import { isPrimitive } from "../utils"; + +import { mutateElement } from "./mutateElement"; +import { duplicateElement, duplicateElements } from "./newElement"; + +import type { ExcalidrawLinearElement } from "./types"; + const assertCloneObjects = (source: any, clone: any) => { for (const key in clone) { if (clone.hasOwnProperty(key) && !isPrimitive(clone[key])) { diff --git a/packages/excalidraw/element/newElement.ts b/packages/excalidraw/element/newElement.ts index 7d1f14910..d11c4c20f 100644 --- a/packages/excalidraw/element/newElement.ts +++ b/packages/excalidraw/element/newElement.ts @@ -1,3 +1,32 @@ +import type { Radians } from "@excalidraw/math"; + +import { + DEFAULT_ELEMENT_PROPS, + DEFAULT_FONT_FAMILY, + DEFAULT_FONT_SIZE, + DEFAULT_TEXT_ALIGN, + DEFAULT_VERTICAL_ALIGN, + ORIG_ID, + VERTICAL_ALIGN, +} from "../constants"; +import { getLineHeight } from "../fonts"; +import { getNewGroupIdsForDuplication } from "../groups"; +import { randomInteger, randomId } from "../random"; +import { + arrayToMap, + getFontString, + getUpdatedTimestamp, + isTestEnv, +} from "../utils"; + +import { getResizedElementAbsoluteCoords } from "./bounds"; +import { bumpVersion, newElementWith } from "./mutateElement"; +import { getBoundTextMaxWidth } from "./textElement"; +import { normalizeText, measureText } from "./textMeasurements"; +import { wrapText } from "./textWrapping"; + +import { getElementAbsoluteCoords } from "."; + import type { ExcalidrawElement, ExcalidrawImageElement, @@ -21,33 +50,8 @@ import type { FixedSegment, ExcalidrawElbowArrowElement, } from "./types"; -import { - arrayToMap, - getFontString, - getUpdatedTimestamp, - isTestEnv, -} from "../utils"; -import { randomInteger, randomId } from "../random"; -import { bumpVersion, newElementWith } from "./mutateElement"; -import { getNewGroupIdsForDuplication } from "../groups"; import type { AppState } from "../types"; -import { getElementAbsoluteCoords } from "."; -import { getResizedElementAbsoluteCoords } from "./bounds"; -import { getBoundTextMaxWidth } from "./textElement"; -import { wrapText } from "./textWrapping"; -import { - DEFAULT_ELEMENT_PROPS, - DEFAULT_FONT_FAMILY, - DEFAULT_FONT_SIZE, - DEFAULT_TEXT_ALIGN, - DEFAULT_VERTICAL_ALIGN, - ORIG_ID, - VERTICAL_ALIGN, -} from "../constants"; import type { MarkOptional, Merge, Mutable } from "../utility-types"; -import { getLineHeight } from "../fonts"; -import type { Radians } from "@excalidraw/math"; -import { normalizeText, measureText } from "./textMeasurements"; export type ElementConstructorOpts = MarkOptional< Omit, diff --git a/packages/excalidraw/element/resizeElements.ts b/packages/excalidraw/element/resizeElements.ts index b86ba0279..cd51b8bd5 100644 --- a/packages/excalidraw/element/resizeElements.ts +++ b/packages/excalidraw/element/resizeElements.ts @@ -1,5 +1,60 @@ +import { + pointCenter, + normalizeRadians, + pointFrom, + pointFromPair, + pointRotateRads, + type Radians, + type LocalPoint, +} from "@excalidraw/math"; + +import type { GlobalPoint } from "@excalidraw/math"; + import { MIN_FONT_SIZE, SHIFT_LOCKING_ANGLE } from "../constants"; +import { isInGroup } from "../groups"; import { rescalePoints } from "../points"; +import { getFontString } from "../utils"; + +import { getArrowLocalFixedPoints, updateBoundElements } from "./binding"; +import { + getElementAbsoluteCoords, + getCommonBounds, + getResizedElementAbsoluteCoords, + getCommonBoundingBox, + getElementBounds, +} from "./bounds"; +import { LinearElementEditor } from "./linearElementEditor"; +import { mutateElement } from "./mutateElement"; +import { + getBoundTextElement, + getBoundTextElementId, + getContainerElement, + handleBindTextResize, + getBoundTextMaxWidth, +} from "./textElement"; +import { + getMinTextElementWidth, + measureText, + getApproxMinLineWidth, + getApproxMinLineHeight, +} from "./textMeasurements"; +import { wrapText } from "./textWrapping"; +import { + isArrowElement, + isBoundToContainer, + isElbowArrow, + isFrameLikeElement, + isFreeDrawElement, + isImageElement, + isLinearElement, + isTextElement, +} from "./typeChecks"; + +import type { BoundingBox } from "./bounds"; +import type { + MaybeTransformHandleType, + TransformHandleDirection, +} from "./transformHandles"; import type { ExcalidrawLinearElement, ExcalidrawTextElement, @@ -12,60 +67,9 @@ import type { SceneElementsMap, ExcalidrawElbowArrowElement, } from "./types"; -import type { Mutable } from "../utility-types"; -import { - getElementAbsoluteCoords, - getCommonBounds, - getResizedElementAbsoluteCoords, - getCommonBoundingBox, - getElementBounds, -} from "./bounds"; -import type { BoundingBox } from "./bounds"; -import { - isArrowElement, - isBoundToContainer, - isElbowArrow, - isFrameLikeElement, - isFreeDrawElement, - isImageElement, - isLinearElement, - isTextElement, -} from "./typeChecks"; -import { mutateElement } from "./mutateElement"; -import { getFontString } from "../utils"; -import { getArrowLocalFixedPoints, updateBoundElements } from "./binding"; -import type { - MaybeTransformHandleType, - TransformHandleDirection, -} from "./transformHandles"; -import type { PointerDownState } from "../types"; import type Scene from "../scene/Scene"; -import { - getBoundTextElement, - getBoundTextElementId, - getContainerElement, - handleBindTextResize, - getBoundTextMaxWidth, -} from "./textElement"; -import { wrapText } from "./textWrapping"; -import { LinearElementEditor } from "./linearElementEditor"; -import { isInGroup } from "../groups"; -import type { GlobalPoint } from "@excalidraw/math"; -import { - pointCenter, - normalizeRadians, - pointFrom, - pointFromPair, - pointRotateRads, - type Radians, - type LocalPoint, -} from "@excalidraw/math"; -import { - getMinTextElementWidth, - measureText, - getApproxMinLineWidth, - getApproxMinLineHeight, -} from "./textMeasurements"; +import type { PointerDownState } from "../types"; +import type { Mutable } from "../utility-types"; // Returns true when transform (resizing/rotation) happened export const transformElements = ( diff --git a/packages/excalidraw/element/resizeTest.ts b/packages/excalidraw/element/resizeTest.ts index 375ff980b..1eb36d0b2 100644 --- a/packages/excalidraw/element/resizeTest.ts +++ b/packages/excalidraw/element/resizeTest.ts @@ -1,27 +1,3 @@ -import type { - ExcalidrawElement, - PointerType, - NonDeletedExcalidrawElement, - ElementsMap, -} from "./types"; - -import type { - TransformHandleType, - TransformHandle, - MaybeTransformHandleType, -} from "./transformHandles"; -import { - getTransformHandlesFromCoords, - getTransformHandles, - getOmitSidesForDevice, - canResizeFromSides, -} from "./transformHandles"; -import type { AppState, Device, Zoom } from "../types"; -import type { Bounds } from "./bounds"; -import { getElementAbsoluteCoords } from "./bounds"; -import { SIDE_RESIZING_THRESHOLD } from "../constants"; -import { isImageElement, isLinearElement } from "./typeChecks"; -import type { GlobalPoint, LineSegment, LocalPoint } from "@excalidraw/math"; import { pointFrom, pointOnLineSegment, @@ -29,6 +5,33 @@ import { type Radians, } from "@excalidraw/math"; +import type { GlobalPoint, LineSegment, LocalPoint } from "@excalidraw/math"; + +import { SIDE_RESIZING_THRESHOLD } from "../constants"; + +import { getElementAbsoluteCoords } from "./bounds"; +import { + getTransformHandlesFromCoords, + getTransformHandles, + getOmitSidesForDevice, + canResizeFromSides, +} from "./transformHandles"; +import { isImageElement, isLinearElement } from "./typeChecks"; + +import type { AppState, Device, Zoom } from "../types"; +import type { Bounds } from "./bounds"; +import type { + TransformHandleType, + TransformHandle, + MaybeTransformHandleType, +} from "./transformHandles"; +import type { + ExcalidrawElement, + PointerType, + NonDeletedExcalidrawElement, + ElementsMap, +} from "./types"; + const isInsideTransformHandle = ( transformHandle: TransformHandle, x: number, diff --git a/packages/excalidraw/element/showSelectedShapeActions.ts b/packages/excalidraw/element/showSelectedShapeActions.ts index bbf313d01..44c2e75c3 100644 --- a/packages/excalidraw/element/showSelectedShapeActions.ts +++ b/packages/excalidraw/element/showSelectedShapeActions.ts @@ -1,6 +1,7 @@ -import type { NonDeletedExcalidrawElement } from "./types"; import { getSelectedElements } from "../scene"; + import type { UIAppState } from "../types"; +import type { NonDeletedExcalidrawElement } from "./types"; export const showSelectedShapeActions = ( appState: UIAppState, diff --git a/packages/excalidraw/element/sizeHelpers.test.ts b/packages/excalidraw/element/sizeHelpers.test.ts index 8e63dd9da..c882e1f3c 100644 --- a/packages/excalidraw/element/sizeHelpers.test.ts +++ b/packages/excalidraw/element/sizeHelpers.test.ts @@ -1,7 +1,9 @@ import { vi } from "vitest"; -import { getPerfectElementSize } from "./sizeHelpers"; + import * as constants from "../constants"; +import { getPerfectElementSize } from "./sizeHelpers"; + const EPSILON_DIGITS = 3; // Needed so that we can mock the value of constants which is done in // below tests. In Jest this wasn't needed as global override was possible diff --git a/packages/excalidraw/element/sizeHelpers.ts b/packages/excalidraw/element/sizeHelpers.ts index f633789a9..33b13e188 100644 --- a/packages/excalidraw/element/sizeHelpers.ts +++ b/packages/excalidraw/element/sizeHelpers.ts @@ -1,10 +1,12 @@ -import type { ElementsMap, ExcalidrawElement } from "./types"; +import { SHIFT_LOCKING_ANGLE } from "../constants"; +import { viewportCoordsToSceneCoords } from "../utils"; + +import { getCommonBounds, getElementBounds } from "./bounds"; import { mutateElement } from "./mutateElement"; import { isFreeDrawElement, isLinearElement } from "./typeChecks"; -import { SHIFT_LOCKING_ANGLE } from "../constants"; + +import type { ElementsMap, ExcalidrawElement } from "./types"; import type { AppState, Offsets, Zoom } from "../types"; -import { getCommonBounds, getElementBounds } from "./bounds"; -import { viewportCoordsToSceneCoords } from "../utils"; // TODO: remove invisible elements consistently actions, so that invisible elements are not recorded by the store, exported, broadcasted or persisted // - perhaps could be as part of a standalone 'cleanup' action, in addition to 'finalize' diff --git a/packages/excalidraw/element/sortElements.test.ts b/packages/excalidraw/element/sortElements.test.ts index a7b78e8ea..5f7c4b2e6 100644 --- a/packages/excalidraw/element/sortElements.test.ts +++ b/packages/excalidraw/element/sortElements.test.ts @@ -1,6 +1,8 @@ import { API } from "../tests/helpers/api"; + import { mutateElement } from "./mutateElement"; import { normalizeElementOrder } from "./sortElements"; + import type { ExcalidrawElement } from "./types"; const assertOrder = ( diff --git a/packages/excalidraw/element/sortElements.ts b/packages/excalidraw/element/sortElements.ts index 3078a6827..d395adf2f 100644 --- a/packages/excalidraw/element/sortElements.ts +++ b/packages/excalidraw/element/sortElements.ts @@ -1,4 +1,5 @@ import { arrayToMapWithIndex } from "../utils"; + import type { ExcalidrawElement } from "./types"; const normalizeGroupElementOrder = (elements: readonly ExcalidrawElement[]) => { diff --git a/packages/excalidraw/element/textElement.test.ts b/packages/excalidraw/element/textElement.test.ts index 2c23c2b06..41531a738 100644 --- a/packages/excalidraw/element/textElement.test.ts +++ b/packages/excalidraw/element/textElement.test.ts @@ -1,6 +1,7 @@ import { FONT_FAMILY } from "../constants"; import { getLineHeight } from "../fonts"; import { API } from "../tests/helpers/api"; + import { computeContainerDimensionForBoundText, getContainerCoords, @@ -8,6 +9,7 @@ import { getBoundTextMaxHeight, } from "./textElement"; import { detectLineHeight, getLineHeightInPx } from "./textMeasurements"; + import type { ExcalidrawTextElementWithContainer } from "./types"; describe("Test measureText", () => { diff --git a/packages/excalidraw/element/textElement.ts b/packages/excalidraw/element/textElement.ts index de948d9ce..9893ba5d6 100644 --- a/packages/excalidraw/element/textElement.ts +++ b/packages/excalidraw/element/textElement.ts @@ -1,4 +1,26 @@ +import { + ARROW_LABEL_FONT_SIZE_TO_MIN_WIDTH_RATIO, + ARROW_LABEL_WIDTH_FRACTION, + BOUND_TEXT_PADDING, + DEFAULT_FONT_SIZE, + TEXT_ALIGN, + VERTICAL_ALIGN, +} from "../constants"; import { getFontString, arrayToMap } from "../utils"; + +import { + resetOriginalContainerCache, + updateOriginalContainerCache, +} from "./containerCache"; +import { LinearElementEditor } from "./linearElementEditor"; +import { mutateElement } from "./mutateElement"; +import { measureText } from "./textMeasurements"; +import { wrapText } from "./textWrapping"; +import { isBoundToContainer, isArrowElement } from "./typeChecks"; + +import { isTextElement } from "."; + +import type { MaybeTransformHandleType } from "./transformHandles"; import type { ElementsMap, ExcalidrawElement, @@ -8,27 +30,8 @@ import type { ExcalidrawTextElementWithContainer, NonDeletedExcalidrawElement, } from "./types"; -import { mutateElement } from "./mutateElement"; -import { - ARROW_LABEL_FONT_SIZE_TO_MIN_WIDTH_RATIO, - ARROW_LABEL_WIDTH_FRACTION, - BOUND_TEXT_PADDING, - DEFAULT_FONT_SIZE, - TEXT_ALIGN, - VERTICAL_ALIGN, -} from "../constants"; -import type { MaybeTransformHandleType } from "./transformHandles"; -import { isTextElement } from "."; -import { wrapText } from "./textWrapping"; -import { isBoundToContainer, isArrowElement } from "./typeChecks"; -import { LinearElementEditor } from "./linearElementEditor"; import type { AppState } from "../types"; -import { - resetOriginalContainerCache, - updateOriginalContainerCache, -} from "./containerCache"; import type { ExtractSetType } from "../utility-types"; -import { measureText } from "./textMeasurements"; export const redrawTextBoundingBox = ( textElement: ExcalidrawTextElement, diff --git a/packages/excalidraw/element/textMeasurements.ts b/packages/excalidraw/element/textMeasurements.ts index f2a132a3a..840896cfc 100644 --- a/packages/excalidraw/element/textMeasurements.ts +++ b/packages/excalidraw/element/textMeasurements.ts @@ -4,6 +4,7 @@ import { DEFAULT_FONT_FAMILY, } from "../constants"; import { getFontString, isTestEnv, normalizeEOL } from "../utils"; + import type { FontString, ExcalidrawTextElement } from "./types"; export const measureText = ( diff --git a/packages/excalidraw/element/textWrapping.test.ts b/packages/excalidraw/element/textWrapping.test.ts index 6c7bcb819..357736a2e 100644 --- a/packages/excalidraw/element/textWrapping.test.ts +++ b/packages/excalidraw/element/textWrapping.test.ts @@ -1,4 +1,5 @@ import { wrapText, parseTokens } from "./textWrapping"; + import type { FontString } from "./types"; describe("Test wrapText", () => { diff --git a/packages/excalidraw/element/textWrapping.ts b/packages/excalidraw/element/textWrapping.ts index 1913f6ed3..5df7051c0 100644 --- a/packages/excalidraw/element/textWrapping.ts +++ b/packages/excalidraw/element/textWrapping.ts @@ -1,5 +1,7 @@ import { ENV } from "../constants"; + import { charWidth, getLineWidth } from "./textMeasurements"; + import type { FontString } from "./types"; let cachedCjkRegex: RegExp | undefined; diff --git a/packages/excalidraw/element/textWysiwyg.test.tsx b/packages/excalidraw/element/textWysiwyg.test.tsx index 0842e30fd..11c700e33 100644 --- a/packages/excalidraw/element/textWysiwyg.test.tsx +++ b/packages/excalidraw/element/textWysiwyg.test.tsx @@ -1,29 +1,31 @@ +import { pointFrom } from "@excalidraw/math"; +import { queryByText } from "@testing-library/react"; import React from "react"; + +import { FONT_FAMILY, TEXT_ALIGN, VERTICAL_ALIGN } from "../constants"; import { Excalidraw } from "../index"; +import { CODES, KEYS } from "../keys"; +import { API } from "../tests/helpers/api"; +import { Keyboard, Pointer, UI } from "../tests/helpers/ui"; +import { getTextEditor, updateTextEditor } from "../tests/queries/dom"; import { GlobalTestState, render, screen, unmountComponent, } from "../tests/test-utils"; -import { Keyboard, Pointer, UI } from "../tests/helpers/ui"; -import { CODES, KEYS } from "../keys"; import { fireEvent, mockBoundingClientRect, restoreOriginalGetBoundingClientRect, } from "../tests/test-utils"; -import { queryByText } from "@testing-library/react"; -import { FONT_FAMILY, TEXT_ALIGN, VERTICAL_ALIGN } from "../constants"; +import { getOriginalContainerHeightFromCache } from "./containerCache"; + import type { ExcalidrawTextElement, ExcalidrawTextElementWithContainer, } from "./types"; -import { API } from "../tests/helpers/api"; -import { getOriginalContainerHeightFromCache } from "./containerCache"; -import { getTextEditor, updateTextEditor } from "../tests/queries/dom"; -import { pointFrom } from "@excalidraw/math"; unmountComponent(); diff --git a/packages/excalidraw/element/textWysiwyg.tsx b/packages/excalidraw/element/textWysiwyg.tsx index a7570862d..1d0c19de3 100644 --- a/packages/excalidraw/element/textWysiwyg.tsx +++ b/packages/excalidraw/element/textWysiwyg.tsx @@ -1,24 +1,28 @@ +import { + actionResetZoom, + actionZoomIn, + actionZoomOut, +} from "../actions/actionCanvas"; +import { + actionDecreaseFontSize, + actionIncreaseFontSize, +} from "../actions/actionProperties"; +import { parseClipboard } from "../clipboard"; +import { CLASSES, POINTER_BUTTON } from "../constants"; import { CODES, KEYS } from "../keys"; +import Scene from "../scene/Scene"; import { isWritableElement, getFontString, getFontFamilyString, isTestEnv, } from "../utils"; -import Scene from "../scene/Scene"; + import { - isArrowElement, - isBoundToContainer, - isTextElement, -} from "./typeChecks"; -import { CLASSES, POINTER_BUTTON } from "../constants"; -import type { - ExcalidrawElement, - ExcalidrawLinearElement, - ExcalidrawTextElementWithContainer, - ExcalidrawTextElement, -} from "./types"; -import type { AppState } from "../types"; + originalContainerCache, + updateOriginalContainerCache, +} from "./containerCache"; +import { LinearElementEditor } from "./linearElementEditor"; import { bumpVersion, mutateElement } from "./mutateElement"; import { getBoundTextElementId, @@ -31,25 +35,23 @@ import { computeBoundTextPosition, getBoundTextElement, } from "./textElement"; -import { wrapText } from "./textWrapping"; -import { - actionDecreaseFontSize, - actionIncreaseFontSize, -} from "../actions/actionProperties"; -import { - actionResetZoom, - actionZoomIn, - actionZoomOut, -} from "../actions/actionCanvas"; -import type App from "../components/App"; -import { LinearElementEditor } from "./linearElementEditor"; -import { parseClipboard } from "../clipboard"; -import { - originalContainerCache, - updateOriginalContainerCache, -} from "./containerCache"; import { getTextWidth } from "./textMeasurements"; import { normalizeText } from "./textMeasurements"; +import { wrapText } from "./textWrapping"; +import { + isArrowElement, + isBoundToContainer, + isTextElement, +} from "./typeChecks"; + +import type { + ExcalidrawElement, + ExcalidrawLinearElement, + ExcalidrawTextElementWithContainer, + ExcalidrawTextElement, +} from "./types"; +import type App from "../components/App"; +import type { AppState } from "../types"; const getTransform = ( width: number, diff --git a/packages/excalidraw/element/transformHandles.ts b/packages/excalidraw/element/transformHandles.ts index d34eb3279..ab5691df8 100644 --- a/packages/excalidraw/element/transformHandles.ts +++ b/packages/excalidraw/element/transformHandles.ts @@ -1,26 +1,29 @@ -import type { - ElementsMap, - ExcalidrawElement, - NonDeletedExcalidrawElement, - PointerType, -} from "./types"; +import { pointFrom, pointRotateRads } from "@excalidraw/math"; + +import type { Radians } from "@excalidraw/math"; + +import { + DEFAULT_TRANSFORM_HANDLE_SPACING, + isAndroid, + isIOS, +} from "../constants"; -import type { Bounds } from "./bounds"; import { getElementAbsoluteCoords } from "./bounds"; -import type { Device, InteractiveCanvasAppState, Zoom } from "../types"; import { isElbowArrow, isFrameLikeElement, isImageElement, isLinearElement, } from "./typeChecks"; -import { - DEFAULT_TRANSFORM_HANDLE_SPACING, - isAndroid, - isIOS, -} from "../constants"; -import type { Radians } from "@excalidraw/math"; -import { pointFrom, pointRotateRads } from "@excalidraw/math"; + +import type { Device, InteractiveCanvasAppState, Zoom } from "../types"; +import type { Bounds } from "./bounds"; +import type { + ElementsMap, + ExcalidrawElement, + NonDeletedExcalidrawElement, + PointerType, +} from "./types"; export type TransformHandleDirection = | "n" diff --git a/packages/excalidraw/element/typeChecks.test.ts b/packages/excalidraw/element/typeChecks.test.ts index 60eb9e273..44e4dd755 100644 --- a/packages/excalidraw/element/typeChecks.test.ts +++ b/packages/excalidraw/element/typeChecks.test.ts @@ -1,4 +1,5 @@ import { API } from "../tests/helpers/api"; + import { hasBoundTextElement } from "./typeChecks"; describe("Test TypeChecks", () => { diff --git a/packages/excalidraw/element/typeChecks.ts b/packages/excalidraw/element/typeChecks.ts index 6bb4269f8..77ac38f9d 100644 --- a/packages/excalidraw/element/typeChecks.ts +++ b/packages/excalidraw/element/typeChecks.ts @@ -1,7 +1,8 @@ import { ROUNDNESS } from "../constants"; +import { assertNever } from "../utils"; + import type { ElementOrToolType } from "../types"; import type { MarkNonNullable } from "../utility-types"; -import { assertNever } from "../utils"; import type { Bounds } from "./bounds"; import type { ExcalidrawElement, diff --git a/packages/excalidraw/element/types.ts b/packages/excalidraw/element/types.ts index 596586743..49ad800af 100644 --- a/packages/excalidraw/element/types.ts +++ b/packages/excalidraw/element/types.ts @@ -1,4 +1,5 @@ import type { LocalPoint, Radians } from "@excalidraw/math"; + import type { FONT_FAMILY, ROUNDNESS, diff --git a/packages/excalidraw/element/utils.ts b/packages/excalidraw/element/utils.ts index d85cd78c6..8992850dc 100644 --- a/packages/excalidraw/element/utils.ts +++ b/packages/excalidraw/element/utils.ts @@ -1,5 +1,3 @@ -import { getDiamondPoints } from "."; -import type { Curve, LineSegment } from "@excalidraw/math"; import { curve, lineSegment, @@ -11,7 +9,13 @@ import { vectorScale, type GlobalPoint, } from "@excalidraw/math"; + +import type { Curve, LineSegment } from "@excalidraw/math"; + import { getCornerRadius } from "../shapes"; + +import { getDiamondPoints } from "."; + import type { ExcalidrawDiamondElement, ExcalidrawRectanguloidElement, diff --git a/packages/excalidraw/fonts/Cascadia/index.ts b/packages/excalidraw/fonts/Cascadia/index.ts index ecd111644..aad91ff0c 100644 --- a/packages/excalidraw/fonts/Cascadia/index.ts +++ b/packages/excalidraw/fonts/Cascadia/index.ts @@ -1,7 +1,7 @@ -import CascadiaCodeRegular from "./CascadiaCode-Regular.woff2"; - import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; +import CascadiaCodeRegular from "./CascadiaCode-Regular.woff2"; + export const CascadiaFontFaces: ExcalidrawFontFaceDescriptor[] = [ { uri: CascadiaCodeRegular, diff --git a/packages/excalidraw/fonts/ComicShanns/index.ts b/packages/excalidraw/fonts/ComicShanns/index.ts index bf78650b4..20d23c28e 100644 --- a/packages/excalidraw/fonts/ComicShanns/index.ts +++ b/packages/excalidraw/fonts/ComicShanns/index.ts @@ -1,13 +1,13 @@ // The following file content was generated with https://chinese-font.netlify.app/online-split, // but has been manully rewritten from `@font-face` rules into TS while leveraging FontFace API. -import _0 from "./ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2"; -import _1 from "./ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2"; -import _2 from "./ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2"; -import _3 from "./ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2"; - import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; +import _0 from "./ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2"; +import _3 from "./ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2"; +import _2 from "./ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2"; +import _1 from "./ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2"; + /* Generated By cn-font-split@5.2.2 https://www.npmjs.com/package/cn-font-split CreateTime: Thu, 17 Oct 2024 09:57:51 GMT; Origin File Name Table: diff --git a/packages/excalidraw/fonts/Emoji/index.ts b/packages/excalidraw/fonts/Emoji/index.ts index 491c7b8c9..323d075cb 100644 --- a/packages/excalidraw/fonts/Emoji/index.ts +++ b/packages/excalidraw/fonts/Emoji/index.ts @@ -1,5 +1,4 @@ import { LOCAL_FONT_PROTOCOL } from "../FontMetadata"; - import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; export const EmojiFontFaces: ExcalidrawFontFaceDescriptor[] = [ diff --git a/packages/excalidraw/fonts/ExcalidrawFontFace.ts b/packages/excalidraw/fonts/ExcalidrawFontFace.ts index 9d8b9f78a..615fef20f 100644 --- a/packages/excalidraw/fonts/ExcalidrawFontFace.ts +++ b/packages/excalidraw/fonts/ExcalidrawFontFace.ts @@ -1,6 +1,7 @@ -import { promiseTry } from "../utils"; -import { LOCAL_FONT_PROTOCOL } from "./FontMetadata"; import { subsetWoff2GlyphsByCodepoints } from "../subset/subset-main"; +import { promiseTry } from "../utils"; + +import { LOCAL_FONT_PROTOCOL } from "./FontMetadata"; type DataURL = string; diff --git a/packages/excalidraw/fonts/Excalifont/index.ts b/packages/excalidraw/fonts/Excalifont/index.ts index 7e11af6c6..bcd384be5 100644 --- a/packages/excalidraw/fonts/Excalifont/index.ts +++ b/packages/excalidraw/fonts/Excalifont/index.ts @@ -1,13 +1,13 @@ -import _0 from "./Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2"; -import _1 from "./Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2"; -import _2 from "./Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2"; -import _3 from "./Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2"; -import _4 from "./Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2"; -import _5 from "./Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2"; -import _6 from "./Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2"; - import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; +import _5 from "./Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2"; +import _4 from "./Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2"; +import _3 from "./Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2"; +import _6 from "./Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2"; +import _0 from "./Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2"; +import _2 from "./Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2"; +import _1 from "./Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2"; + /* Generated By cn-font-split@5.2.2 https://www.npmjs.com/package/cn-font-split CreateTime: Mon, 14 Oct 2024 18:59:19 GMT; Origin File Name Table: diff --git a/packages/excalidraw/fonts/FontMetadata.ts b/packages/excalidraw/fonts/FontMetadata.ts index e93380e81..e8b46205b 100644 --- a/packages/excalidraw/fonts/FontMetadata.ts +++ b/packages/excalidraw/fonts/FontMetadata.ts @@ -1,4 +1,3 @@ -import type { JSX } from "react"; import { FreedrawIcon, FontFamilyNormalIcon, @@ -7,6 +6,8 @@ import { } from "../components/icons"; import { FONT_FAMILY, FONT_FAMILY_FALLBACKS } from "../constants"; +import type { JSX } from "react"; + /** * Encapsulates font metrics with additional font metadata. * */ diff --git a/packages/excalidraw/fonts/Fonts.ts b/packages/excalidraw/fonts/Fonts.ts index 4b8ba7828..c1b94bdef 100644 --- a/packages/excalidraw/fonts/Fonts.ts +++ b/packages/excalidraw/fonts/Fonts.ts @@ -7,15 +7,17 @@ import { } from "../constants"; import { isTextElement } from "../element"; import { getContainerElement } from "../element/textElement"; +import { charWidth } from "../element/textMeasurements"; import { containsCJK } from "../element/textWrapping"; import { ShapeCache } from "../scene/ShapeCache"; import { getFontString, PromisePool, promiseTry } from "../utils"; -import { ExcalidrawFontFace } from "./ExcalidrawFontFace"; import { CascadiaFontFaces } from "./Cascadia"; import { ComicShannsFontFaces } from "./ComicShanns"; import { EmojiFontFaces } from "./Emoji"; +import { ExcalidrawFontFace } from "./ExcalidrawFontFace"; import { ExcalifontFontFaces } from "./Excalifont"; +import { FONT_METADATA, type FontMetadata } from "./FontMetadata"; import { HelveticaFontFaces } from "./Helvetica"; import { LiberationFontFaces } from "./Liberation"; import { LilitaFontFaces } from "./Lilita"; @@ -23,7 +25,6 @@ import { NunitoFontFaces } from "./Nunito"; import { VirgilFontFaces } from "./Virgil"; import { XiaolaiFontFaces } from "./Xiaolai"; -import { FONT_METADATA, type FontMetadata } from "./FontMetadata"; import type { ExcalidrawElement, ExcalidrawTextElement, @@ -31,7 +32,6 @@ import type { } from "../element/types"; import type Scene from "../scene/Scene"; import type { ValueOf } from "../utility-types"; -import { charWidth } from "../element/textMeasurements"; export class Fonts { // it's ok to track fonts across multiple instances only once, so let's use diff --git a/packages/excalidraw/fonts/Helvetica/index.ts b/packages/excalidraw/fonts/Helvetica/index.ts index a0e7ecb61..f13d15118 100644 --- a/packages/excalidraw/fonts/Helvetica/index.ts +++ b/packages/excalidraw/fonts/Helvetica/index.ts @@ -1,5 +1,4 @@ import { LOCAL_FONT_PROTOCOL } from "../FontMetadata"; - import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; export const HelveticaFontFaces: ExcalidrawFontFaceDescriptor[] = [ diff --git a/packages/excalidraw/fonts/Liberation/index.ts b/packages/excalidraw/fonts/Liberation/index.ts index 5bc964815..86b3e72ec 100644 --- a/packages/excalidraw/fonts/Liberation/index.ts +++ b/packages/excalidraw/fonts/Liberation/index.ts @@ -1,7 +1,7 @@ -import LiberationSansRegular from "./LiberationSans-Regular.woff2"; - import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; +import LiberationSansRegular from "./LiberationSans-Regular.woff2"; + export const LiberationFontFaces: ExcalidrawFontFaceDescriptor[] = [ { uri: LiberationSansRegular, diff --git a/packages/excalidraw/fonts/Lilita/index.ts b/packages/excalidraw/fonts/Lilita/index.ts index d5ea82e58..37a5d6a5e 100644 --- a/packages/excalidraw/fonts/Lilita/index.ts +++ b/packages/excalidraw/fonts/Lilita/index.ts @@ -1,10 +1,9 @@ -import LilitaLatin from "./Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2"; -import LilitaLatinExt from "./Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2"; - import { GOOGLE_FONTS_RANGES } from "../FontMetadata"; - import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; +import LilitaLatinExt from "./Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2"; +import LilitaLatin from "./Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2"; + export const LilitaFontFaces: ExcalidrawFontFaceDescriptor[] = [ { uri: LilitaLatinExt, diff --git a/packages/excalidraw/fonts/Nunito/index.ts b/packages/excalidraw/fonts/Nunito/index.ts index 4db432d07..3b092b0d6 100644 --- a/packages/excalidraw/fonts/Nunito/index.ts +++ b/packages/excalidraw/fonts/Nunito/index.ts @@ -1,13 +1,12 @@ -import Latin from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2"; -import LatinExt from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2"; -import Cyrilic from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2"; -import CyrilicExt from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2"; -import Vietnamese from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2"; - import { GOOGLE_FONTS_RANGES } from "../FontMetadata"; - import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; +import Cyrilic from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2"; +import Latin from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2"; +import CyrilicExt from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2"; +import LatinExt from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2"; +import Vietnamese from "./Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2"; + export const NunitoFontFaces: ExcalidrawFontFaceDescriptor[] = [ { uri: CyrilicExt, diff --git a/packages/excalidraw/fonts/Virgil/index.ts b/packages/excalidraw/fonts/Virgil/index.ts index fdd5ffcfc..b9ce7ee78 100644 --- a/packages/excalidraw/fonts/Virgil/index.ts +++ b/packages/excalidraw/fonts/Virgil/index.ts @@ -1,7 +1,7 @@ -import Virgil from "./Virgil-Regular.woff2"; - import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; +import Virgil from "./Virgil-Regular.woff2"; + export const VirgilFontFaces: ExcalidrawFontFaceDescriptor[] = [ { uri: Virgil, diff --git a/packages/excalidraw/fonts/Xiaolai/index.ts b/packages/excalidraw/fonts/Xiaolai/index.ts index 17fd71831..67476b912 100644 --- a/packages/excalidraw/fonts/Xiaolai/index.ts +++ b/packages/excalidraw/fonts/Xiaolai/index.ts @@ -1,23 +1,31 @@ // The following file content was generated with https://chinese-font.netlify.app/online-split, // but has been manully rewritten from `@font-face` rules into TS while leveraging FontFace API. +import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; + +import _80 from "./Xiaolai-Regular-019d66dcad46dc156b162d267f981c20.woff2"; +import _16 from "./Xiaolai-Regular-04b718e5623574919c8b0dea5f301444.woff2"; +import _13 from "./Xiaolai-Regular-069e77aac84590e2e991d0a0176d34f2.woff2"; +import _166 from "./Xiaolai-Regular-06c77b8c66e51ed6c63ccb502dd8b8af.woff2"; +import _71 from "./Xiaolai-Regular-08e0dc436ad0ad61ba5558db0674d762.woff2"; +import _161 from "./Xiaolai-Regular-093b9ef39a46ceae95a1df18a0a3a326.woff2"; +import _183 from "./Xiaolai-Regular-095c169f3314805276f603a362766abd.woff2"; import _0 from "./Xiaolai-Regular-09850c4077f3fffe707905872e0e2460.woff2"; -import _1 from "./Xiaolai-Regular-7eb9fffd1aa890d07d0f88cc82e6cfe4.woff2"; +import _14 from "./Xiaolai-Regular-41521fade99856108931b4768b1b2648.woff2"; +import _10 from "./Xiaolai-Regular-544fc28abe2c5c30e62383fd4dac255f.woff2"; import _2 from "./Xiaolai-Regular-60a3089806700d379f11827ee9843b6b.woff2"; +import _1 from "./Xiaolai-Regular-7eb9fffd1aa890d07d0f88cc82e6cfe4.woff2"; import _3 from "./Xiaolai-Regular-6fe5c5973cc06f74b2387a631ea36b88.woff2"; +import _6 from "./Xiaolai-Regular-a4c34be6d42152e64b0df90bc4607f64.woff2"; import _4 from "./Xiaolai-Regular-b96d9226ce77ec94ceca043d712182e6.woff2"; import _5 from "./Xiaolai-Regular-6ae5b42180ad70b971c91e7eefb8eba2.woff2"; -import _6 from "./Xiaolai-Regular-a4c34be6d42152e64b0df90bc4607f64.woff2"; import _7 from "./Xiaolai-Regular-c69f61a4ab18d0488c8d1fc12e7028e8.woff2"; +import _30 from "./Xiaolai-Regular-cb17fc3db95f6d139afc9d31a8e93293.woff2"; import _8 from "./Xiaolai-Regular-e3fcf5180fd466c8915c4e8069491054.woff2"; import _9 from "./Xiaolai-Regular-c1f94158256bb1f3bf665b053d895af9.woff2"; -import _10 from "./Xiaolai-Regular-544fc28abe2c5c30e62383fd4dac255f.woff2"; import _11 from "./Xiaolai-Regular-7197d6fda6cba7c3874c53d6381ca239.woff2"; import _12 from "./Xiaolai-Regular-70c2eb8d64e71a42a834eb857ea9df51.woff2"; -import _13 from "./Xiaolai-Regular-069e77aac84590e2e991d0a0176d34f2.woff2"; -import _14 from "./Xiaolai-Regular-41521fade99856108931b4768b1b2648.woff2"; import _15 from "./Xiaolai-Regular-a004ddfcb26e67bd6e678c8ed19e25ce.woff2"; -import _16 from "./Xiaolai-Regular-04b718e5623574919c8b0dea5f301444.woff2"; import _17 from "./Xiaolai-Regular-7e4bde7e9c7f84cd34d8a845e384c746.woff2"; import _18 from "./Xiaolai-Regular-23686f7f29da6e8008c36dd3a80c83d6.woff2"; import _19 from "./Xiaolai-Regular-69c09cc5fa3e55c74fc4821f76909cc3.woff2"; @@ -31,7 +39,6 @@ import _26 from "./Xiaolai-Regular-7e929f262f30c8ee78bf398150b1a7cd.woff2"; import _27 from "./Xiaolai-Regular-73e309718fd16cea44b4d54a33581811.woff2"; import _28 from "./Xiaolai-Regular-9eb5a99df4e76ac3363453ac9ca288b1.woff2"; import _29 from "./Xiaolai-Regular-3e63ed8162808a9e425ed80a8bc79114.woff2"; -import _30 from "./Xiaolai-Regular-cb17fc3db95f6d139afc9d31a8e93293.woff2"; import _31 from "./Xiaolai-Regular-c8b71798409ccc126ee264a00aadcf21.woff2"; import _32 from "./Xiaolai-Regular-11c345711937f0ba4b8f7b6b919c8440.woff2"; import _33 from "./Xiaolai-Regular-e480d9c614742d05f0e78f274f1e69e6.woff2"; @@ -40,15 +47,17 @@ import _35 from "./Xiaolai-Regular-2cf96d082d35ea3d8106851223ad0d16.woff2"; import _36 from "./Xiaolai-Regular-2d43040e86ff03ba677f6f9c04cd0805.woff2"; import _37 from "./Xiaolai-Regular-2a26d20a23b00898ce82f09d2ee47c3f.woff2"; import _38 from "./Xiaolai-Regular-a365e82ed54697a52f27adcea1315fe8.woff2"; +import _41 from "./Xiaolai-Regular-e4bca6cfa53e499cae0a6be4894a90e9.woff2"; +import _105 from "./Xiaolai-Regular-e51ef413167c6e14e0c0fdcc585f2fc9.woff2"; +import _82 from "./Xiaolai-Regular-e5f453bb04da18eed01675eeebd88bf8.woff2"; +import _45 from "./Xiaolai-Regular-e656f091b9dc4709722c9f4b84d3c797.woff2"; +import _47 from "./Xiaolai-Regular-f0f13b5c60e0af5553bd359f5513be1b.woff2"; import _39 from "./Xiaolai-Regular-f5d079153c99a25b9be5b8583c4cc8a7.woff2"; import _40 from "./Xiaolai-Regular-10a7ae9a371830a80c3d844acf1c02d7.woff2"; -import _41 from "./Xiaolai-Regular-e4bca6cfa53e499cae0a6be4894a90e9.woff2"; import _42 from "./Xiaolai-Regular-60a41c7e1c68f22424e6d22df544bc82.woff2"; import _43 from "./Xiaolai-Regular-7ab2bed91166a9dca83a5ebfbe2a7f38.woff2"; import _44 from "./Xiaolai-Regular-670ba603758d94268e8606f240a42e12.woff2"; -import _45 from "./Xiaolai-Regular-e656f091b9dc4709722c9f4b84d3c797.woff2"; import _46 from "./Xiaolai-Regular-15dc6d811c9cd078f9086a740d5a1038.woff2"; -import _47 from "./Xiaolai-Regular-f0f13b5c60e0af5553bd359f5513be1b.woff2"; import _48 from "./Xiaolai-Regular-8c2f33cee3993174f7e87c28e4bf42ee.woff2"; import _49 from "./Xiaolai-Regular-761d05e3cd968cf574166867998ef06a.woff2"; import _50 from "./Xiaolai-Regular-642b26e2e5f5fb780b51b593dbc8c851.woff2"; @@ -72,7 +81,6 @@ import _67 from "./Xiaolai-Regular-0b5d723fdc4e249c140f0909e87d03b4.woff2"; import _68 from "./Xiaolai-Regular-cdbce89e82cc1ab53a2decbf5819278f.woff2"; import _69 from "./Xiaolai-Regular-739bc1a567439c7cffcd1614644593d2.woff2"; import _70 from "./Xiaolai-Regular-72252d73220fa3cd856677888cee1635.woff2"; -import _71 from "./Xiaolai-Regular-08e0dc436ad0ad61ba5558db0674d762.woff2"; import _72 from "./Xiaolai-Regular-cf6ff4e0f491ca0cf3038187a997b9b4.woff2"; import _73 from "./Xiaolai-Regular-9cfb2a77a4e45025105ad29a1748b90d.woff2"; import _74 from "./Xiaolai-Regular-450da755d5bcb70906e1295e559b9602.woff2"; @@ -81,9 +89,7 @@ import _76 from "./Xiaolai-Regular-1ee544f0f1dac422545c505baa788992.woff2"; import _77 from "./Xiaolai-Regular-4806e761d750087c2d734fc64596eaff.woff2"; import _78 from "./Xiaolai-Regular-33432927cd87d40cfe393c7482bf221f.woff2"; import _79 from "./Xiaolai-Regular-be549ab72f0719d606a5c01e2c0219b6.woff2"; -import _80 from "./Xiaolai-Regular-019d66dcad46dc156b162d267f981c20.woff2"; import _81 from "./Xiaolai-Regular-b5c1596551c256e0e9cf02028595b092.woff2"; -import _82 from "./Xiaolai-Regular-e5f453bb04da18eed01675eeebd88bf8.woff2"; import _83 from "./Xiaolai-Regular-cf2cc71752631e579e35b0e423bf2638.woff2"; import _84 from "./Xiaolai-Regular-6f3256af8454371776bc46670d33cc65.woff2"; import _85 from "./Xiaolai-Regular-23f228f3999c01983860012330e4be08.woff2"; @@ -106,7 +112,6 @@ import _101 from "./Xiaolai-Regular-4a0fdb40036e87b40aa08dd30584cb85.woff2"; import _102 from "./Xiaolai-Regular-0f626226ba1272e832aea87bafd9720e.woff2"; import _103 from "./Xiaolai-Regular-938d90c10ff8c20386af7f242c05d6b0.woff2"; import _104 from "./Xiaolai-Regular-b6d128682ee29e471486354d486a1b90.woff2"; -import _105 from "./Xiaolai-Regular-e51ef413167c6e14e0c0fdcc585f2fc9.woff2"; import _106 from "./Xiaolai-Regular-9d81066dd2b337c938df6e90380a00dc.woff2"; import _107 from "./Xiaolai-Regular-20e7bf72fa05de9adf7dbcc7bf51dde6.woff2"; import _108 from "./Xiaolai-Regular-4095eb84ef3874e2600247bee0b04026.woff2"; @@ -162,15 +167,14 @@ import _157 from "./Xiaolai-Regular-774d4f764a1299da5d28ec2f2ffe0d69.woff2"; import _158 from "./Xiaolai-Regular-7718fe60986d8b42b1be9c5ace5ccf25.woff2"; import _159 from "./Xiaolai-Regular-aa5c9ca6cf4fba00433b7aa3fa10671a.woff2"; import _160 from "./Xiaolai-Regular-4f50e5136e136527280bc902c5817561.woff2"; -import _161 from "./Xiaolai-Regular-093b9ef39a46ceae95a1df18a0a3a326.woff2"; import _162 from "./Xiaolai-Regular-a0ca5df4258213d7fc9fce80f65ce760.woff2"; import _163 from "./Xiaolai-Regular-d2666cbed13462c5dc36fa2f15c202ca.woff2"; import _164 from "./Xiaolai-Regular-1e6fd68f1f3902ce48ce8c69df385622.woff2"; import _165 from "./Xiaolai-Regular-87599f94b6cc129d505b375798d0d751.woff2"; -import _166 from "./Xiaolai-Regular-06c77b8c66e51ed6c63ccb502dd8b8af.woff2"; import _167 from "./Xiaolai-Regular-13ae07ed2e272d26d59bc0691cd7117a.woff2"; import _168 from "./Xiaolai-Regular-353f33792a8f60dc69323ddf635a269e.woff2"; import _169 from "./Xiaolai-Regular-0facdf1ea213ba40261022f5d5ed4493.woff2"; +import _202 from "./Xiaolai-Regular-f6032fc06eb20480f096199713f70885.woff2"; import _170 from "./Xiaolai-Regular-f8ee5d36068a42b51d0e4a1116cfcec1.woff2"; import _171 from "./Xiaolai-Regular-79d494361ae093b69e74ee9dbe65bfd4.woff2"; import _172 from "./Xiaolai-Regular-74e2263a91439c25b91d5132ce9f4d62.woff2"; @@ -184,7 +188,6 @@ import _179 from "./Xiaolai-Regular-8e9f97f01034820170065b2921b4fb5e.woff2"; import _180 from "./Xiaolai-Regular-13d2887ec8ee73c43acdabc52a05af7b.woff2"; import _181 from "./Xiaolai-Regular-72536a3d71b694a0d53dd90ddceae41e.woff2"; import _182 from "./Xiaolai-Regular-603aefd23e350ba7eb124273e3c9bcf1.woff2"; -import _183 from "./Xiaolai-Regular-095c169f3314805276f603a362766abd.woff2"; import _184 from "./Xiaolai-Regular-9544732d2e62d1a429674f8ee41b5d3a.woff2"; import _185 from "./Xiaolai-Regular-d3716376641d615e2995605b29bca7b6.woff2"; import _186 from "./Xiaolai-Regular-5a1ce3117cfe90c48e8fb4a9a00f694d.woff2"; @@ -203,7 +206,6 @@ import _198 from "./Xiaolai-Regular-24476a126f129212beb33f66853ea151.woff2"; import _199 from "./Xiaolai-Regular-1b611157cd46bb184d4fa4dae2d6a2b8.woff2"; import _200 from "./Xiaolai-Regular-56a32a7689abd0326e57c10c6c069bb4.woff2"; import _201 from "./Xiaolai-Regular-3cc70dbb64df5b21f1326cc24dee2195.woff2"; -import _202 from "./Xiaolai-Regular-f6032fc06eb20480f096199713f70885.woff2"; import _203 from "./Xiaolai-Regular-e2ead7ea7da0437f085f42ffc05f8d13.woff2"; import _204 from "./Xiaolai-Regular-97f7f48ce90c9429bf32ae51469db74d.woff2"; import _205 from "./Xiaolai-Regular-24a21c1e4449222e8d1898d69ff3a404.woff2"; @@ -211,8 +213,6 @@ import _206 from "./Xiaolai-Regular-726303e0774b4e678bff8c2deb6ca603.woff2"; import _207 from "./Xiaolai-Regular-5a7fac4b8b23a6e4e5ba0c9bf1756c91.woff2"; import _208 from "./Xiaolai-Regular-2b7441d46298788ac94e610ffcc709b6.woff2"; -import { type ExcalidrawFontFaceDescriptor } from "../Fonts"; - /* Generated By cn-font-split@5.2.2 https://www.npmjs.com/package/cn-font-split CreateTime: Tue, 08 Oct 2024 18:34:44 GMT; Origin File Name Table: diff --git a/packages/excalidraw/fractionalIndex.ts b/packages/excalidraw/fractionalIndex.ts index dfb8a4672..8a1459ddd 100644 --- a/packages/excalidraw/fractionalIndex.ts +++ b/packages/excalidraw/fractionalIndex.ts @@ -1,14 +1,16 @@ import { generateNKeysBetween } from "fractional-indexing"; + import { mutateElement } from "./element/mutateElement"; +import { getBoundTextElement } from "./element/textElement"; +import { hasBoundTextElement } from "./element/typeChecks"; +import { InvalidFractionalIndexError } from "./errors"; +import { arrayToMap } from "./utils"; + import type { ExcalidrawElement, FractionalIndex, OrderedExcalidrawElement, } from "./element/types"; -import { InvalidFractionalIndexError } from "./errors"; -import { hasBoundTextElement } from "./element/typeChecks"; -import { getBoundTextElement } from "./element/textElement"; -import { arrayToMap } from "./utils"; /** * Envisioned relation between array order and fractional indices: diff --git a/packages/excalidraw/frame.test.tsx b/packages/excalidraw/frame.test.tsx index 6a5045b2c..fce420c02 100644 --- a/packages/excalidraw/frame.test.tsx +++ b/packages/excalidraw/frame.test.tsx @@ -1,9 +1,11 @@ -import type { ExcalidrawElement } from "./element/types"; -import { convertToExcalidrawElements, Excalidraw } from "./index"; import { API } from "./tests/helpers/api"; import { Keyboard, Pointer } from "./tests/helpers/ui"; import { getCloneByOrigId, render } from "./tests/test-utils"; +import { convertToExcalidrawElements, Excalidraw } from "./index"; + +import type { ExcalidrawElement } from "./element/types"; + const { h } = window; const mouse = new Pointer("mouse"); diff --git a/packages/excalidraw/frame.ts b/packages/excalidraw/frame.ts index 4659a34a0..758bc273e 100644 --- a/packages/excalidraw/frame.ts +++ b/packages/excalidraw/frame.ts @@ -1,8 +1,25 @@ +import { isPointWithinBounds, pointFrom } from "@excalidraw/math"; +import { + doLineSegmentsIntersect, + elementsOverlappingBBox, +} from "@excalidraw/utils"; + import { getCommonBounds, getElementAbsoluteCoords, isTextElement, } from "./element"; +import { getElementLineSegments } from "./element/bounds"; +import { mutateElement } from "./element/mutateElement"; +import { + getBoundTextElement, + getContainerElement, +} from "./element/textElement"; +import { isFrameElement, isFrameLikeElement } from "./element/typeChecks"; +import { getElementsInGroup, selectGroupsFromGivenElements } from "./groups"; +import { getElementsWithinSelection, getSelectedElements } from "./scene"; +import { arrayToMap } from "./utils"; + import type { ElementsMap, ElementsMapOrArray, @@ -11,28 +28,13 @@ import type { NonDeleted, NonDeletedExcalidrawElement, } from "./element/types"; -import { - getBoundTextElement, - getContainerElement, -} from "./element/textElement"; -import { arrayToMap } from "./utils"; -import { mutateElement } from "./element/mutateElement"; +import type { ExcalidrawElementsIncludingDeleted } from "./scene/Scene"; import type { AppClassProperties, AppState, StaticCanvasAppState, } from "./types"; -import { getElementsWithinSelection, getSelectedElements } from "./scene"; -import { getElementsInGroup, selectGroupsFromGivenElements } from "./groups"; -import type { ExcalidrawElementsIncludingDeleted } from "./scene/Scene"; -import { getElementLineSegments } from "./element/bounds"; -import { - doLineSegmentsIntersect, - elementsOverlappingBBox, -} from "@excalidraw/utils"; -import { isFrameElement, isFrameLikeElement } from "./element/typeChecks"; import type { ReadonlySetLike } from "./utility-types"; -import { isPointWithinBounds, pointFrom } from "@excalidraw/math"; // --------------------------- Frame State ------------------------------------ export const bindElementsToFramesAfterDuplication = ( diff --git a/packages/excalidraw/groups.ts b/packages/excalidraw/groups.ts index 5a4e606f7..cedc4af0f 100644 --- a/packages/excalidraw/groups.ts +++ b/packages/excalidraw/groups.ts @@ -1,3 +1,7 @@ +import { getBoundTextElement } from "./element/textElement"; +import { getSelectedElements } from "./scene"; +import { makeNextSelectedElementIds } from "./scene/selection"; + import type { GroupId, ExcalidrawElement, @@ -11,9 +15,6 @@ import type { AppState, InteractiveCanvasAppState, } from "./types"; -import { getSelectedElements } from "./scene"; -import { getBoundTextElement } from "./element/textElement"; -import { makeNextSelectedElementIds } from "./scene/selection"; import type { Mutable } from "./utility-types"; export const selectGroup = ( diff --git a/packages/excalidraw/history.ts b/packages/excalidraw/history.ts index daed2a394..48ea012bd 100644 --- a/packages/excalidraw/history.ts +++ b/packages/excalidraw/history.ts @@ -1,6 +1,7 @@ +import { Emitter } from "./emitter"; + import type { AppStateChange, ElementsChange } from "./change"; import type { SceneElementsMap } from "./element/types"; -import { Emitter } from "./emitter"; import type { Snapshot } from "./store"; import type { AppState } from "./types"; diff --git a/packages/excalidraw/hooks/useCreatePortalContainer.ts b/packages/excalidraw/hooks/useCreatePortalContainer.ts index e8f5e3db6..b557d7e2f 100644 --- a/packages/excalidraw/hooks/useCreatePortalContainer.ts +++ b/packages/excalidraw/hooks/useCreatePortalContainer.ts @@ -1,4 +1,5 @@ import { useState, useLayoutEffect } from "react"; + import { useDevice, useExcalidrawContainer } from "../components/App"; import { THEME } from "../constants"; import { useUIAppState } from "../context/ui-appState"; diff --git a/packages/excalidraw/hooks/useEmitter.ts b/packages/excalidraw/hooks/useEmitter.ts index 27b94bc91..eebbaaf30 100644 --- a/packages/excalidraw/hooks/useEmitter.ts +++ b/packages/excalidraw/hooks/useEmitter.ts @@ -1,4 +1,5 @@ import { useEffect, useState } from "react"; + import type { Emitter } from "../emitter"; export const useEmitter = ( diff --git a/packages/excalidraw/hooks/useLibraryItemSvg.ts b/packages/excalidraw/hooks/useLibraryItemSvg.ts index 72b648d11..a79aab5c5 100644 --- a/packages/excalidraw/hooks/useLibraryItemSvg.ts +++ b/packages/excalidraw/hooks/useLibraryItemSvg.ts @@ -1,7 +1,9 @@ +import { exportToSvg } from "@excalidraw/utils/export"; import { useEffect, useState } from "react"; + import { COLOR_PALETTE } from "../colors"; import { atom, useAtom } from "../editor-jotai"; -import { exportToSvg } from "@excalidraw/utils/export"; + import type { LibraryItem } from "../types"; export type SvgCache = Map; diff --git a/packages/excalidraw/hooks/useOutsideClick.ts b/packages/excalidraw/hooks/useOutsideClick.ts index da9a54d08..7ec2113c2 100644 --- a/packages/excalidraw/hooks/useOutsideClick.ts +++ b/packages/excalidraw/hooks/useOutsideClick.ts @@ -1,4 +1,5 @@ import { useEffect } from "react"; + import { EVENT } from "../constants"; export function useOutsideClick( diff --git a/packages/excalidraw/hooks/useScrollPosition.ts b/packages/excalidraw/hooks/useScrollPosition.ts index 0be2eab95..1fb1408b5 100644 --- a/packages/excalidraw/hooks/useScrollPosition.ts +++ b/packages/excalidraw/hooks/useScrollPosition.ts @@ -1,6 +1,7 @@ -import { useEffect } from "react"; -import { atom, useAtom } from "../editor-jotai"; import throttle from "lodash.throttle"; +import { useEffect } from "react"; + +import { atom, useAtom } from "../editor-jotai"; const scrollPositionAtom = atom(0); diff --git a/packages/excalidraw/i18n.ts b/packages/excalidraw/i18n.ts index e1da5fc44..47bae5979 100644 --- a/packages/excalidraw/i18n.ts +++ b/packages/excalidraw/i18n.ts @@ -1,6 +1,7 @@ +import { useAtomValue, editorJotaiStore, atom } from "./editor-jotai"; import fallbackLangData from "./locales/en.json"; import percentages from "./locales/percentages.json"; -import { useAtomValue, editorJotaiStore, atom } from "./editor-jotai"; + import type { NestedKeyOf } from "./utility-types"; const COMPLETION_THRESHOLD = 85; diff --git a/packages/excalidraw/index-node.ts b/packages/excalidraw/index-node.ts index e966b1d52..3b9499149 100644 --- a/packages/excalidraw/index-node.ts +++ b/packages/excalidraw/index-node.ts @@ -1,5 +1,7 @@ -import { exportToCanvas } from "./scene/export"; import { getDefaultAppState } from "./appState"; +import { exportToCanvas } from "./scene/export"; + +const fs = require("fs"); const { registerFont, createCanvas } = require("canvas"); @@ -74,7 +76,6 @@ const canvas = exportToCanvas( createCanvas, ); -const fs = require("fs"); const out = fs.createWriteStream("test.png"); const stream = (canvas as any).createPNGStream(); stream.pipe(out); diff --git a/packages/excalidraw/index.tsx b/packages/excalidraw/index.tsx index b4b8574bd..f9f25b530 100644 --- a/packages/excalidraw/index.tsx +++ b/packages/excalidraw/index.tsx @@ -1,21 +1,22 @@ import React, { useEffect } from "react"; -import { InitializeApp } from "./components/InitializeApp"; + import App from "./components/App"; -import { isShallowEqual } from "./utils"; +import { InitializeApp } from "./components/InitializeApp"; +import Footer from "./components/footer/FooterCenter"; +import LiveCollaborationTrigger from "./components/live-collaboration/LiveCollaborationTrigger"; +import MainMenu from "./components/main-menu/MainMenu"; +import WelcomeScreen from "./components/welcome-screen/WelcomeScreen"; +import { DEFAULT_UI_OPTIONS } from "./constants"; +import { defaultLang } from "./i18n"; +import { EditorJotaiProvider, editorJotaiStore } from "./editor-jotai"; import polyfill from "./polyfill"; +import { isShallowEqual } from "./utils"; import "./css/app.scss"; import "./css/styles.scss"; import "./fonts/fonts.css"; import type { AppProps, ExcalidrawProps } from "./types"; -import { defaultLang } from "./i18n"; -import { DEFAULT_UI_OPTIONS } from "./constants"; -import { EditorJotaiProvider, editorJotaiStore } from "./editor-jotai"; -import Footer from "./components/footer/FooterCenter"; -import MainMenu from "./components/main-menu/MainMenu"; -import WelcomeScreen from "./components/welcome-screen/WelcomeScreen"; -import LiveCollaborationTrigger from "./components/live-collaboration/LiveCollaborationTrigger"; polyfill(); diff --git a/packages/excalidraw/keys.ts b/packages/excalidraw/keys.ts index 2088f89d6..948e7f568 100644 --- a/packages/excalidraw/keys.ts +++ b/packages/excalidraw/keys.ts @@ -1,4 +1,5 @@ import { isDarwin } from "./constants"; + import type { ValueOf } from "./utility-types"; export const CODES = { diff --git a/packages/excalidraw/laser-trails.ts b/packages/excalidraw/laser-trails.ts index b7733baed..06e6b573a 100644 --- a/packages/excalidraw/laser-trails.ts +++ b/packages/excalidraw/laser-trails.ts @@ -1,12 +1,14 @@ import type { LaserPointerOptions } from "@excalidraw/laser-pointer"; -import type { Trail } from "./animated-trail"; + import { AnimatedTrail } from "./animated-trail"; +import { getClientColor } from "./clients"; +import { DEFAULT_LASER_COLOR } from "./constants"; +import { easeOut } from "./utils"; + +import type { Trail } from "./animated-trail"; import type { AnimationFrameHandler } from "./animation-frame-handler"; import type App from "./components/App"; import type { SocketId } from "./types"; -import { easeOut } from "./utils"; -import { getClientColor } from "./clients"; -import { DEFAULT_LASER_COLOR } from "./constants"; export class LaserTrails implements Trail { public localTrail: AnimatedTrail; diff --git a/packages/excalidraw/queue.ts b/packages/excalidraw/queue.ts index 9cac2c5e9..b7369e5e0 100644 --- a/packages/excalidraw/queue.ts +++ b/packages/excalidraw/queue.ts @@ -1,6 +1,7 @@ +import { promiseTry, resolvablePromise } from "./utils"; + import type { MaybePromise } from "./utility-types"; import type { ResolvablePromise } from "./utils"; -import { promiseTry, resolvablePromise } from "./utils"; type Job = (...args: TArgs) => MaybePromise; diff --git a/packages/excalidraw/random.ts b/packages/excalidraw/random.ts index 851bc28ec..4a4424a76 100644 --- a/packages/excalidraw/random.ts +++ b/packages/excalidraw/random.ts @@ -1,5 +1,6 @@ -import { Random } from "roughjs/bin/math"; import { nanoid } from "nanoid"; +import { Random } from "roughjs/bin/math"; + import { isTestEnv } from "./utils"; let random = new Random(Date.now()); diff --git a/packages/excalidraw/reactUtils.ts b/packages/excalidraw/reactUtils.ts index 535302d42..5bc466395 100644 --- a/packages/excalidraw/reactUtils.ts +++ b/packages/excalidraw/reactUtils.ts @@ -2,8 +2,9 @@ * @param func handler taking at most single parameter (event). */ -import { unstable_batchedUpdates } from "react-dom"; import { version as ReactVersion } from "react"; +import { unstable_batchedUpdates } from "react-dom"; + import { throttleRAF } from "./utils"; export const withBatchedUpdates = < diff --git a/packages/excalidraw/renderer/helpers.ts b/packages/excalidraw/renderer/helpers.ts index 90f40099f..05097f95b 100644 --- a/packages/excalidraw/renderer/helpers.ts +++ b/packages/excalidraw/renderer/helpers.ts @@ -1,8 +1,7 @@ -import type { StaticCanvasAppState, AppState } from "../types"; +import { THEME, THEME_FILTER } from "../constants"; import type { StaticCanvasRenderConfig } from "../scene/types"; - -import { THEME, THEME_FILTER } from "../constants"; +import type { StaticCanvasAppState, AppState } from "../types"; export const fillCircle = ( context: CanvasRenderingContext2D, diff --git a/packages/excalidraw/renderer/interactiveScene.ts b/packages/excalidraw/renderer/interactiveScene.ts index 9aa832770..257decd62 100644 --- a/packages/excalidraw/renderer/interactiveScene.ts +++ b/packages/excalidraw/renderer/interactiveScene.ts @@ -1,48 +1,23 @@ +import { + pointFrom, + type GlobalPoint, + type LocalPoint, + type Radians, +} from "@excalidraw/math"; +import oc from "open-color"; + +import { getClientColor, renderRemoteCursors } from "../clients"; +import { + DEFAULT_TRANSFORM_HANDLE_SPACING, + FRAME_STYLE, + THEME, +} from "../constants"; import { getElementAbsoluteCoords, getTransformHandlesFromCoords, getTransformHandles, getCommonBounds, } from "../element"; - -import { roundRect } from "../renderer/roundRect"; - -import { - getScrollBars, - SCROLLBAR_COLOR, - SCROLLBAR_WIDTH, -} from "../scene/scrollbars"; - -import { renderSelectionElement } from "../renderer/renderElement"; -import { getClientColor, renderRemoteCursors } from "../clients"; -import { - isSelectedViaGroup, - getSelectedGroupIds, - getElementsInGroup, - selectGroupsFromGivenElements, -} from "../groups"; -import type { - TransformHandles, - TransformHandleType, -} from "../element/transformHandles"; -import { - getOmitSidesForDevice, - shouldShowBoundingBox, -} from "../element/transformHandles"; -import { arrayToMap, invariant, throttleRAF } from "../utils"; -import { - DEFAULT_TRANSFORM_HANDLE_SPACING, - FRAME_STYLE, - THEME, -} from "../constants"; -import { type InteractiveCanvasAppState } from "../types"; - -import { renderSnaps } from "../renderer/renderSnaps"; - -import type { - SuggestedBinding, - SuggestedPointBinding, -} from "../element/binding"; import { BINDING_HIGHLIGHT_OFFSET, BINDING_HIGHLIGHT_THICKNESS, @@ -50,11 +25,9 @@ import { } from "../element/binding"; import { LinearElementEditor } from "../element/linearElementEditor"; import { - bootstrapCanvas, - fillCircle, - getNormalizedCanvasDimensions, -} from "./helpers"; -import oc from "open-color"; + getOmitSidesForDevice, + shouldShowBoundingBox, +} from "../element/transformHandles"; import { isElbowArrow, isFrameLikeElement, @@ -62,6 +35,38 @@ import { isLinearElement, isTextElement, } from "../element/typeChecks"; +import { + isSelectedViaGroup, + getSelectedGroupIds, + getElementsInGroup, + selectGroupsFromGivenElements, +} from "../groups"; +import { renderSelectionElement } from "../renderer/renderElement"; +import { renderSnaps } from "../renderer/renderSnaps"; +import { roundRect } from "../renderer/roundRect"; +import { + getScrollBars, + SCROLLBAR_COLOR, + SCROLLBAR_WIDTH, +} from "../scene/scrollbars"; +import { getCornerRadius } from "../shapes"; +import { type InteractiveCanvasAppState } from "../types"; +import { arrayToMap, invariant, throttleRAF } from "../utils"; + +import { + bootstrapCanvas, + fillCircle, + getNormalizedCanvasDimensions, +} from "./helpers"; + +import type { + SuggestedBinding, + SuggestedPointBinding, +} from "../element/binding"; +import type { + TransformHandles, + TransformHandleType, +} from "../element/transformHandles"; import type { ElementsMap, ExcalidrawBindableElement, @@ -78,13 +83,6 @@ import type { InteractiveSceneRenderConfig, RenderableElementsMap, } from "../scene/types"; -import { - pointFrom, - type GlobalPoint, - type LocalPoint, - type Radians, -} from "@excalidraw/math"; -import { getCornerRadius } from "../shapes"; const renderElbowArrowMidPointHighlight = ( context: CanvasRenderingContext2D, diff --git a/packages/excalidraw/renderer/renderElement.ts b/packages/excalidraw/renderer/renderElement.ts index d93469c2d..9209c46d6 100644 --- a/packages/excalidraw/renderer/renderElement.ts +++ b/packages/excalidraw/renderer/renderElement.ts @@ -1,3 +1,43 @@ +import { isRightAngleRads } from "@excalidraw/math"; +import { getStroke } from "perfect-freehand"; +import rough from "roughjs/bin/rough"; + +import { getDefaultAppState } from "../appState"; +import { + BOUND_TEXT_PADDING, + DEFAULT_REDUCED_GLOBAL_ALPHA, + ELEMENT_READY_TO_ERASE_OPACITY, + FRAME_STYLE, + MIME_TYPES, + THEME, +} from "../constants"; +import { getElementAbsoluteCoords } from "../element/bounds"; +import { getUncroppedImageElement } from "../element/cropElement"; +import { LinearElementEditor } from "../element/linearElementEditor"; +import { + getBoundTextElement, + getContainerCoords, + getContainerElement, + getBoundTextMaxHeight, + getBoundTextMaxWidth, +} from "../element/textElement"; +import { getLineHeightInPx } from "../element/textMeasurements"; +import { + isTextElement, + isLinearElement, + isFreeDrawElement, + isInitializedImageElement, + isArrowElement, + hasBoundTextElement, + isMagicFrameElement, + isImageElement, +} from "../element/typeChecks"; +import { getVerticalOffset } from "../fonts"; +import { getContainingFrame } from "../frame"; +import { ShapeCache } from "../scene/ShapeCache"; +import { getCornerRadius } from "../shapes"; +import { distance, getFontString, isRTL } from "../utils"; + import type { ExcalidrawElement, ExcalidrawTextElement, @@ -9,26 +49,11 @@ import type { NonDeletedSceneElementsMap, ElementsMap, } from "../element/types"; -import { - isTextElement, - isLinearElement, - isFreeDrawElement, - isInitializedImageElement, - isArrowElement, - hasBoundTextElement, - isMagicFrameElement, - isImageElement, -} from "../element/typeChecks"; -import { getElementAbsoluteCoords } from "../element/bounds"; -import type { RoughCanvas } from "roughjs/bin/canvas"; - import type { StaticCanvasRenderConfig, RenderableElementsMap, InteractiveCanvasRenderConfig, } from "../scene/types"; -import { distance, getFontString, isRTL } from "../utils"; -import rough from "roughjs/bin/rough"; import type { AppState, StaticCanvasAppState, @@ -37,33 +62,8 @@ import type { ElementsPendingErasure, PendingExcalidrawElements, } from "../types"; -import { getDefaultAppState } from "../appState"; -import { - BOUND_TEXT_PADDING, - DEFAULT_REDUCED_GLOBAL_ALPHA, - ELEMENT_READY_TO_ERASE_OPACITY, - FRAME_STYLE, - MIME_TYPES, - THEME, -} from "../constants"; import type { StrokeOptions } from "perfect-freehand"; -import { getStroke } from "perfect-freehand"; -import { - getBoundTextElement, - getContainerCoords, - getContainerElement, - getBoundTextMaxHeight, - getBoundTextMaxWidth, -} from "../element/textElement"; -import { LinearElementEditor } from "../element/linearElementEditor"; - -import { getContainingFrame } from "../frame"; -import { ShapeCache } from "../scene/ShapeCache"; -import { getVerticalOffset } from "../fonts"; -import { isRightAngleRads } from "@excalidraw/math"; -import { getCornerRadius } from "../shapes"; -import { getUncroppedImageElement } from "../element/cropElement"; -import { getLineHeightInPx } from "../element/textMeasurements"; +import type { RoughCanvas } from "roughjs/bin/canvas"; // using a stronger invert (100% vs our regular 93%) and saturate // as a temp hack to make images in dark theme look closer to original diff --git a/packages/excalidraw/renderer/renderNewElementScene.ts b/packages/excalidraw/renderer/renderNewElementScene.ts index caa7f581c..f80408366 100644 --- a/packages/excalidraw/renderer/renderNewElementScene.ts +++ b/packages/excalidraw/renderer/renderNewElementScene.ts @@ -1,8 +1,10 @@ -import type { NewElementSceneRenderConfig } from "../scene/types"; import { throttleRAF } from "../utils"; + import { bootstrapCanvas, getNormalizedCanvasDimensions } from "./helpers"; import { renderElement } from "./renderElement"; +import type { NewElementSceneRenderConfig } from "../scene/types"; + const _renderNewElementScene = ({ canvas, rc, diff --git a/packages/excalidraw/renderer/renderSnaps.ts b/packages/excalidraw/renderer/renderSnaps.ts index 878e8e523..8c26675ac 100644 --- a/packages/excalidraw/renderer/renderSnaps.ts +++ b/packages/excalidraw/renderer/renderSnaps.ts @@ -1,5 +1,7 @@ import { pointFrom, type GlobalPoint, type LocalPoint } from "@excalidraw/math"; + import { THEME } from "../constants"; + import type { PointSnapLine, PointerSnapLine } from "../snapping"; import type { InteractiveCanvasAppState } from "../types"; diff --git a/packages/excalidraw/renderer/staticScene.ts b/packages/excalidraw/renderer/staticScene.ts index 90ed8af43..62aeed5b9 100644 --- a/packages/excalidraw/renderer/staticScene.ts +++ b/packages/excalidraw/renderer/staticScene.ts @@ -1,19 +1,28 @@ +import { + EXTERNAL_LINK_IMG, + ELEMENT_LINK_IMG, + getLinkHandleFromCoords, +} from "../components/hyperlink/helpers"; import { FRAME_STYLE } from "../constants"; import { getElementAbsoluteCoords } from "../element"; - -import { - elementOverlapsWithFrame, - getTargetFrame, - shouldApplyFrameClip, -} from "../frame"; +import { isElementLink } from "../element/elementLink"; +import { createPlaceholderEmbeddableLabel } from "../element/embeddable"; +import { getBoundTextElement } from "../element/textElement"; import { isEmbeddableElement, isIframeLikeElement, isTextElement, } from "../element/typeChecks"; +import { + elementOverlapsWithFrame, + getTargetFrame, + shouldApplyFrameClip, +} from "../frame"; import { renderElement } from "../renderer/renderElement"; -import { createPlaceholderEmbeddableLabel } from "../element/embeddable"; -import type { StaticCanvasAppState, Zoom } from "../types"; +import { throttleRAF } from "../utils"; + +import { bootstrapCanvas, getNormalizedCanvasDimensions } from "./helpers"; + import type { ElementsMap, ExcalidrawFrameLikeElement, @@ -23,15 +32,7 @@ import type { StaticCanvasRenderConfig, StaticSceneRenderConfig, } from "../scene/types"; -import { - EXTERNAL_LINK_IMG, - ELEMENT_LINK_IMG, - getLinkHandleFromCoords, -} from "../components/hyperlink/helpers"; -import { bootstrapCanvas, getNormalizedCanvasDimensions } from "./helpers"; -import { throttleRAF } from "../utils"; -import { getBoundTextElement } from "../element/textElement"; -import { isElementLink } from "../element/elementLink"; +import type { StaticCanvasAppState, Zoom } from "../types"; const GridLineColor = { Bold: "#dddddd", diff --git a/packages/excalidraw/renderer/staticSvgScene.ts b/packages/excalidraw/renderer/staticSvgScene.ts index b14faf7f4..23a6890df 100644 --- a/packages/excalidraw/renderer/staticSvgScene.ts +++ b/packages/excalidraw/renderer/staticSvgScene.ts @@ -1,5 +1,3 @@ -import type { Drawable } from "roughjs/bin/core"; -import type { RoughSVG } from "roughjs/bin/svg"; import { FRAME_STYLE, MAX_DECIMALS_FOR_SVG_EXPORT, @@ -8,6 +6,7 @@ import { } from "../constants"; import { normalizeLink, toValidURL } from "../data/url"; import { getElementAbsoluteCoords, hashString } from "../element"; +import { getUncroppedWidthAndHeight } from "../element/cropElement"; import { createPlaceholderEmbeddableLabel, getEmbedLink, @@ -17,27 +16,30 @@ import { getBoundTextElement, getContainerElement, } from "../element/textElement"; +import { getLineHeightInPx } from "../element/textMeasurements"; import { isArrowElement, isIframeLikeElement, isInitializedImageElement, isTextElement, } from "../element/typeChecks"; +import { getVerticalOffset } from "../fonts"; +import { getContainingFrame } from "../frame"; +import { ShapeCache } from "../scene/ShapeCache"; +import { getCornerRadius, isPathALoop } from "../shapes"; +import { getFontFamilyString, isRTL, isTestEnv } from "../utils"; + +import { getFreeDrawSvgPath, IMAGE_INVERT_FILTER } from "./renderElement"; + import type { ExcalidrawElement, ExcalidrawTextElementWithContainer, NonDeletedExcalidrawElement, } from "../element/types"; -import { getContainingFrame } from "../frame"; -import { ShapeCache } from "../scene/ShapeCache"; import type { RenderableElementsMap, SVGRenderConfig } from "../scene/types"; import type { AppState, BinaryFiles } from "../types"; -import { getFontFamilyString, isRTL, isTestEnv } from "../utils"; -import { getFreeDrawSvgPath, IMAGE_INVERT_FILTER } from "./renderElement"; -import { getVerticalOffset } from "../fonts"; -import { getCornerRadius, isPathALoop } from "../shapes"; -import { getUncroppedWidthAndHeight } from "../element/cropElement"; -import { getLineHeightInPx } from "../element/textMeasurements"; +import type { Drawable } from "roughjs/bin/core"; +import type { RoughSVG } from "roughjs/bin/svg"; const roughSVGDrawWithPrecision = ( rsvg: RoughSVG, diff --git a/packages/excalidraw/scene/Renderer.ts b/packages/excalidraw/scene/Renderer.ts index 8048d20c1..19a6c0b10 100644 --- a/packages/excalidraw/scene/Renderer.ts +++ b/packages/excalidraw/scene/Renderer.ts @@ -1,17 +1,17 @@ import { isElementInViewport } from "../element/sizeHelpers"; import { isImageElement } from "../element/typeChecks"; +import { renderInteractiveSceneThrottled } from "../renderer/interactiveScene"; +import { renderStaticSceneThrottled } from "../renderer/staticScene"; +import { memoize, toBrandedType } from "../utils"; + +import type Scene from "./Scene"; +import type { RenderableElementsMap } from "./types"; import type { ExcalidrawElement, NonDeletedElementsMap, NonDeletedExcalidrawElement, } from "../element/types"; -import { renderInteractiveSceneThrottled } from "../renderer/interactiveScene"; -import { renderStaticSceneThrottled } from "../renderer/staticScene"; - import type { AppState } from "../types"; -import { memoize, toBrandedType } from "../utils"; -import type Scene from "./Scene"; -import type { RenderableElementsMap } from "./types"; export class Renderer { private scene: Scene; diff --git a/packages/excalidraw/scene/Scene.ts b/packages/excalidraw/scene/Scene.ts index 99bb9e1e4..0eab8b80e 100644 --- a/packages/excalidraw/scene/Scene.ts +++ b/packages/excalidraw/scene/Scene.ts @@ -1,4 +1,21 @@ import throttle from "lodash.throttle"; + +import { ENV } from "../constants"; +import { isNonDeletedElement } from "../element"; +import { isFrameLikeElement } from "../element/typeChecks"; +import { + syncInvalidIndices, + syncMovedIndices, + validateFractionalIndices, +} from "../fractionalIndex"; +import { getElementsInGroup } from "../groups"; +import { randomInteger } from "../random"; +import { arrayToMap } from "../utils"; +import { toBrandedType } from "../utils"; + +import { getSelectedElements } from "./selection"; + +import type { LinearElementEditor } from "../element/linearElementEditor"; import type { ExcalidrawElement, NonDeletedExcalidrawElement, @@ -10,22 +27,8 @@ import type { OrderedExcalidrawElement, Ordered, } from "../element/types"; -import { isNonDeletedElement } from "../element"; -import type { LinearElementEditor } from "../element/linearElementEditor"; -import { isFrameLikeElement } from "../element/typeChecks"; -import { getSelectedElements } from "./selection"; import type { AppState } from "../types"; import type { Assert, SameType } from "../utility-types"; -import { randomInteger } from "../random"; -import { - syncInvalidIndices, - syncMovedIndices, - validateFractionalIndices, -} from "../fractionalIndex"; -import { arrayToMap } from "../utils"; -import { toBrandedType } from "../utils"; -import { ENV } from "../constants"; -import { getElementsInGroup } from "../groups"; type ElementIdKey = InstanceType["elementId"]; type ElementKey = ExcalidrawElement | ElementIdKey; diff --git a/packages/excalidraw/scene/Shape.ts b/packages/excalidraw/scene/Shape.ts index 9c49db0c2..64d90b789 100644 --- a/packages/excalidraw/scene/Shape.ts +++ b/packages/excalidraw/scene/Shape.ts @@ -1,19 +1,9 @@ -import type { Point as RoughPoint } from "roughjs/bin/geometry"; -import type { Drawable, Options } from "roughjs/bin/core"; -import type { RoughGenerator } from "roughjs/bin/generator"; -import { getDiamondPoints, getArrowheadPoints } from "../element"; -import type { ElementShapes } from "./types"; -import type { - ExcalidrawElement, - NonDeletedExcalidrawElement, - ExcalidrawSelectionElement, - ExcalidrawLinearElement, - Arrowhead, -} from "../element/types"; -import { generateFreeDrawShape } from "../renderer/renderElement"; -import { isTransparent, assertNever } from "../utils"; +import { pointFrom, pointDistance, type LocalPoint } from "@excalidraw/math"; import { simplify } from "points-on-curve"; + import { ROUGHNESS } from "../constants"; +import { getDiamondPoints, getArrowheadPoints } from "../element"; +import { headingForPointIsHorizontal } from "../element/heading"; import { isElbowArrow, isEmbeddableElement, @@ -21,11 +11,24 @@ import { isIframeLikeElement, isLinearElement, } from "../element/typeChecks"; -import { canChangeRoundness } from "./comparisons"; -import type { EmbedsValidationStatus } from "../types"; -import { pointFrom, pointDistance, type LocalPoint } from "@excalidraw/math"; +import { generateFreeDrawShape } from "../renderer/renderElement"; import { getCornerRadius, isPathALoop } from "../shapes"; -import { headingForPointIsHorizontal } from "../element/heading"; +import { isTransparent, assertNever } from "../utils"; + +import { canChangeRoundness } from "./comparisons"; + +import type { + ExcalidrawElement, + NonDeletedExcalidrawElement, + ExcalidrawSelectionElement, + ExcalidrawLinearElement, + Arrowhead, +} from "../element/types"; +import type { EmbedsValidationStatus } from "../types"; +import type { ElementShapes } from "./types"; +import type { Drawable, Options } from "roughjs/bin/core"; +import type { RoughGenerator } from "roughjs/bin/generator"; +import type { Point as RoughPoint } from "roughjs/bin/geometry"; const getDashArrayDashed = (strokeWidth: number) => [8, 8 + strokeWidth]; diff --git a/packages/excalidraw/scene/ShapeCache.ts b/packages/excalidraw/scene/ShapeCache.ts index 39d388a7b..c170b0a80 100644 --- a/packages/excalidraw/scene/ShapeCache.ts +++ b/packages/excalidraw/scene/ShapeCache.ts @@ -1,14 +1,17 @@ -import type { Drawable } from "roughjs/bin/core"; import { RoughGenerator } from "roughjs/bin/generator"; + +import { COLOR_PALETTE } from "../colors"; +import { elementWithCanvasCache } from "../renderer/renderElement"; + +import { _generateElementShape } from "./Shape"; + import type { ExcalidrawElement, ExcalidrawSelectionElement, } from "../element/types"; -import { elementWithCanvasCache } from "../renderer/renderElement"; -import { _generateElementShape } from "./Shape"; -import type { ElementShape, ElementShapes } from "./types"; -import { COLOR_PALETTE } from "../colors"; import type { AppState, EmbedsValidationStatus } from "../types"; +import type { ElementShape, ElementShapes } from "./types"; +import type { Drawable } from "roughjs/bin/core"; export class ShapeCache { private static rg = new RoughGenerator(); diff --git a/packages/excalidraw/scene/export.ts b/packages/excalidraw/scene/export.ts index 7b3325690..8ca35de2c 100644 --- a/packages/excalidraw/scene/export.ts +++ b/packages/excalidraw/scene/export.ts @@ -1,16 +1,6 @@ import rough from "roughjs/bin/rough"; -import type { - ExcalidrawElement, - ExcalidrawFrameLikeElement, - ExcalidrawTextElement, - NonDeletedExcalidrawElement, - NonDeletedSceneElementsMap, -} from "../element/types"; -import type { Bounds } from "../element/bounds"; -import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds"; -import { renderSceneToSvg } from "../renderer/staticSvgScene"; -import { arrayToMap, distance, getFontString, toBrandedType } from "../utils"; -import type { AppState, BinaryFiles } from "../types"; + +import { getDefaultAppState } from "../appState"; import { DEFAULT_EXPORT_PADDING, FRAME_STYLE, @@ -21,27 +11,39 @@ import { MIME_TYPES, EXPORT_DATA_TYPES, } from "../constants"; -import { getDefaultAppState } from "../appState"; +import { base64ToString, decode, encode, stringToBase64 } from "../data/encode"; import { serializeAsJSON } from "../data/json"; +import { newTextElement } from "../element"; +import { getCommonBounds, getElementAbsoluteCoords } from "../element/bounds"; import { getInitializedImageElements, updateImageCache, } from "../element/image"; +import { newElementWith } from "../element/mutateElement"; +import { isFrameLikeElement } from "../element/typeChecks"; +import { Fonts } from "../fonts"; +import { syncInvalidIndices } from "../fractionalIndex"; import { getElementsOverlappingFrame, getFrameLikeElements, getFrameLikeTitle, getRootElements, } from "../frame"; -import { newTextElement } from "../element"; -import { type Mutable } from "../utility-types"; -import { newElementWith } from "../element/mutateElement"; -import { isFrameLikeElement } from "../element/typeChecks"; -import type { RenderableElementsMap } from "./types"; -import { syncInvalidIndices } from "../fractionalIndex"; import { renderStaticScene } from "../renderer/staticScene"; -import { Fonts } from "../fonts"; -import { base64ToString, decode, encode, stringToBase64 } from "../data/encode"; +import { renderSceneToSvg } from "../renderer/staticSvgScene"; +import { type Mutable } from "../utility-types"; +import { arrayToMap, distance, getFontString, toBrandedType } from "../utils"; + +import type { RenderableElementsMap } from "./types"; +import type { Bounds } from "../element/bounds"; +import type { + ExcalidrawElement, + ExcalidrawFrameLikeElement, + ExcalidrawTextElement, + NonDeletedExcalidrawElement, + NonDeletedSceneElementsMap, +} from "../element/types"; +import type { AppState, BinaryFiles } from "../types"; const truncateText = (element: ExcalidrawTextElement, maxWidth: number) => { if (element.width <= maxWidth) { diff --git a/packages/excalidraw/scene/normalize.ts b/packages/excalidraw/scene/normalize.ts index 2a025fc27..0c73c5a8a 100644 --- a/packages/excalidraw/scene/normalize.ts +++ b/packages/excalidraw/scene/normalize.ts @@ -1,5 +1,7 @@ import { clamp, round } from "@excalidraw/math"; + import { MAX_ZOOM, MIN_ZOOM } from "../constants"; + import type { NormalizedZoomValue } from "../types"; export const getNormalizedZoom = (zoom: number): NormalizedZoomValue => { diff --git a/packages/excalidraw/scene/scroll.ts b/packages/excalidraw/scene/scroll.ts index 5d059e5b4..989564eb0 100644 --- a/packages/excalidraw/scene/scroll.ts +++ b/packages/excalidraw/scene/scroll.ts @@ -1,16 +1,16 @@ -import type { AppState, Offsets, PointerCoords, Zoom } from "../types"; -import type { ExcalidrawElement } from "../element/types"; import { getCommonBounds, getClosestElementBounds, getVisibleElements, } from "../element"; - import { sceneCoordsToViewportCoords, viewportCoordsToSceneCoords, } from "../utils"; +import type { ExcalidrawElement } from "../element/types"; +import type { AppState, Offsets, PointerCoords, Zoom } from "../types"; + const isOutsideViewPort = (appState: AppState, cords: Array) => { const [x1, y1, x2, y2] = cords; const { x: viewportX1, y: viewportY1 } = sceneCoordsToViewportCoords( diff --git a/packages/excalidraw/scene/scrollbars.ts b/packages/excalidraw/scene/scrollbars.ts index 70f7033fb..b44d79f2b 100644 --- a/packages/excalidraw/scene/scrollbars.ts +++ b/packages/excalidraw/scene/scrollbars.ts @@ -1,9 +1,10 @@ import { getCommonBounds } from "../element"; +import { getLanguage } from "../i18n"; +import { getGlobalCSSVariable } from "../utils"; + +import type { ExcalidrawElement } from "../element/types"; import type { InteractiveCanvasAppState } from "../types"; import type { ScrollBars } from "./types"; -import { getGlobalCSSVariable } from "../utils"; -import { getLanguage } from "../i18n"; -import type { ExcalidrawElement } from "../element/types"; export const SCROLLBAR_MARGIN = 4; export const SCROLLBAR_WIDTH = 6; diff --git a/packages/excalidraw/scene/selection.ts b/packages/excalidraw/scene/selection.ts index 3ca91cd50..02f8f05e7 100644 --- a/packages/excalidraw/scene/selection.ts +++ b/packages/excalidraw/scene/selection.ts @@ -1,11 +1,5 @@ -import type { - ElementsMap, - ElementsMapOrArray, - ExcalidrawElement, - NonDeletedExcalidrawElement, -} from "../element/types"; import { getElementAbsoluteCoords, getElementBounds } from "../element"; -import type { AppState, InteractiveCanvasAppState } from "../types"; +import { isElementInViewport } from "../element/sizeHelpers"; import { isBoundToContainer, isFrameLikeElement } from "../element/typeChecks"; import { elementOverlapsWithFrame, @@ -13,7 +7,14 @@ import { getFrameChildren, } from "../frame"; import { isShallowEqual } from "../utils"; -import { isElementInViewport } from "../element/sizeHelpers"; + +import type { + ElementsMap, + ElementsMapOrArray, + ExcalidrawElement, + NonDeletedExcalidrawElement, +} from "../element/types"; +import type { AppState, InteractiveCanvasAppState } from "../types"; /** * Frames and their containing elements are not to be selected at the same time. diff --git a/packages/excalidraw/scene/types.ts b/packages/excalidraw/scene/types.ts index c0bfd1bba..3c198d153 100644 --- a/packages/excalidraw/scene/types.ts +++ b/packages/excalidraw/scene/types.ts @@ -1,5 +1,4 @@ -import type { RoughCanvas } from "roughjs/bin/canvas"; -import type { Drawable } from "roughjs/bin/core"; +import type { UserIdleState } from "../constants"; import type { ExcalidrawElement, NonDeletedElementsMap, @@ -18,7 +17,8 @@ import type { PendingExcalidrawElements, } from "../types"; import type { MakeBrand } from "../utility-types"; -import type { UserIdleState } from "../constants"; +import type { RoughCanvas } from "roughjs/bin/canvas"; +import type { Drawable } from "roughjs/bin/core"; export type RenderableElementsMap = NonDeletedElementsMap & MakeBrand<"RenderableElementsMap">; diff --git a/packages/excalidraw/shapes.tsx b/packages/excalidraw/shapes.tsx index cfd639f2d..b2c391a42 100644 --- a/packages/excalidraw/shapes.tsx +++ b/packages/excalidraw/shapes.tsx @@ -17,6 +17,7 @@ import { getPolygonShape, type GeometricShape, } from "@excalidraw/utils/geometry/shape"; + import { ArrowIcon, DiamondIcon, @@ -36,20 +37,21 @@ import { ROUNDNESS, } from "./constants"; import { getElementAbsoluteCoords } from "./element"; -import type { Bounds } from "./element/bounds"; import { shouldTestInside } from "./element/collision"; import { LinearElementEditor } from "./element/linearElementEditor"; import { getBoundTextElement } from "./element/textElement"; +import { KEYS } from "./keys"; +import { ShapeCache } from "./scene/ShapeCache"; +import { invariant } from "./utils"; + +import type { Bounds } from "./element/bounds"; import type { ElementsMap, ExcalidrawElement, ExcalidrawLinearElement, NonDeleted, } from "./element/types"; -import { KEYS } from "./keys"; -import { ShapeCache } from "./scene/ShapeCache"; import type { NormalizedZoomValue, Zoom } from "./types"; -import { invariant } from "./utils"; export const SHAPES = [ { diff --git a/packages/excalidraw/snapping.ts b/packages/excalidraw/snapping.ts index 1b661516e..7c85f4112 100644 --- a/packages/excalidraw/snapping.ts +++ b/packages/excalidraw/snapping.ts @@ -1,4 +1,3 @@ -import type { InclusiveRange } from "@excalidraw/math"; import { pointFrom, pointRotateRads, @@ -7,26 +6,30 @@ import { rangesOverlap, type GlobalPoint, } from "@excalidraw/math"; + +import type { InclusiveRange } from "@excalidraw/math"; + import { TOOL_TYPE } from "./constants"; -import type { Bounds } from "./element/bounds"; import { getCommonBounds, getDraggedElementsBounds, getElementAbsoluteCoords, } from "./element/bounds"; -import type { MaybeTransformHandleType } from "./element/transformHandles"; import { isBoundToContainer, isFrameLikeElement } from "./element/typeChecks"; -import type { - ElementsMap, - ExcalidrawElement, - NonDeletedExcalidrawElement, -} from "./element/types"; import { getMaximumGroups } from "./groups"; import { KEYS } from "./keys"; import { getSelectedElements, getVisibleAndNonSelectedElements, } from "./scene/selection"; + +import type { Bounds } from "./element/bounds"; +import type { MaybeTransformHandleType } from "./element/transformHandles"; +import type { + ElementsMap, + ExcalidrawElement, + NonDeletedExcalidrawElement, +} from "./element/types"; import type { AppClassProperties, AppState, diff --git a/packages/excalidraw/store.ts b/packages/excalidraw/store.ts index 1343347c4..8b0065884 100644 --- a/packages/excalidraw/store.ts +++ b/packages/excalidraw/store.ts @@ -3,11 +3,12 @@ import { AppStateChange, ElementsChange } from "./change"; import { ENV } from "./constants"; import { newElementWith } from "./element/mutateElement"; import { deepCopyElement } from "./element/newElement"; -import type { OrderedExcalidrawElement } from "./element/types"; import { Emitter } from "./emitter"; +import { isShallowEqual } from "./utils"; + +import type { OrderedExcalidrawElement } from "./element/types"; import type { AppState, ObservedAppState } from "./types"; import type { ValueOf } from "./utility-types"; -import { isShallowEqual } from "./utils"; // hidden non-enumerable property for runtime checks const hiddenObservedAppStateProp = "__observedAppState"; diff --git a/packages/excalidraw/subset/harfbuzz/harfbuzz-loader.ts b/packages/excalidraw/subset/harfbuzz/harfbuzz-loader.ts index 9a48d19c4..120dedb7f 100644 --- a/packages/excalidraw/subset/harfbuzz/harfbuzz-loader.ts +++ b/packages/excalidraw/subset/harfbuzz/harfbuzz-loader.ts @@ -7,8 +7,8 @@ * In the future consider separating common utils into a separate shared chunk. */ -import binary from "./harfbuzz-wasm"; import bindings from "./harfbuzz-bindings"; +import binary from "./harfbuzz-wasm"; /** * Lazy loads wasm and respective bindings for font subsetting based on the harfbuzzjs. diff --git a/packages/excalidraw/subset/subset-main.ts b/packages/excalidraw/subset/subset-main.ts index afccf0d20..5f39af99c 100644 --- a/packages/excalidraw/subset/subset-main.ts +++ b/packages/excalidraw/subset/subset-main.ts @@ -1,6 +1,6 @@ -import { WorkerPool } from "../workers"; -import { isServerEnv, promiseTry } from "../utils"; import { WorkerInTheMainChunkError, WorkerUrlNotDefinedError } from "../errors"; +import { isServerEnv, promiseTry } from "../utils"; +import { WorkerPool } from "../workers"; import type { Commands } from "./subset-shared.chunk"; diff --git a/packages/excalidraw/subset/subset-shared.chunk.ts b/packages/excalidraw/subset/subset-shared.chunk.ts index b64a3825e..4e4b88424 100644 --- a/packages/excalidraw/subset/subset-shared.chunk.ts +++ b/packages/excalidraw/subset/subset-shared.chunk.ts @@ -7,8 +7,8 @@ * In the future consider separating common utils into a separate shared chunk. */ -import loadWoff2 from "./woff2/woff2-loader"; import loadHbSubset from "./harfbuzz/harfbuzz-loader"; +import loadWoff2 from "./woff2/woff2-loader"; /** * Shared commands between the main thread and worker threads. diff --git a/packages/excalidraw/subset/woff2/woff2-loader.ts b/packages/excalidraw/subset/woff2/woff2-loader.ts index 446dc9f8b..7d5de3095 100644 --- a/packages/excalidraw/subset/woff2/woff2-loader.ts +++ b/packages/excalidraw/subset/woff2/woff2-loader.ts @@ -7,8 +7,8 @@ * In the future consider separating common utils into a separate shared chunk. */ -import binary from "./woff2-wasm"; import bindings from "./woff2-bindings"; +import binary from "./woff2-wasm"; /** * Lazy loads wasm and respective bindings for woff2 compression and decompression. diff --git a/packages/excalidraw/tests/App.test.tsx b/packages/excalidraw/tests/App.test.tsx index ad62706a5..8b86477f9 100644 --- a/packages/excalidraw/tests/App.test.tsx +++ b/packages/excalidraw/tests/App.test.tsx @@ -1,10 +1,10 @@ import React from "react"; -import * as StaticScene from "../renderer/staticScene"; -import { reseed } from "../random"; -import { render, queryByTestId, unmountComponent } from "../tests/test-utils"; +import { vi } from "vitest"; import { Excalidraw } from "../index"; -import { vi } from "vitest"; +import { reseed } from "../random"; +import * as StaticScene from "../renderer/staticScene"; +import { render, queryByTestId, unmountComponent } from "../tests/test-utils"; const renderStaticScene = vi.spyOn(StaticScene, "renderStaticScene"); diff --git a/packages/excalidraw/tests/MermaidToExcalidraw.test.tsx b/packages/excalidraw/tests/MermaidToExcalidraw.test.tsx index 05727a0f4..cedb70487 100644 --- a/packages/excalidraw/tests/MermaidToExcalidraw.test.tsx +++ b/packages/excalidraw/tests/MermaidToExcalidraw.test.tsx @@ -1,9 +1,11 @@ import React from "react"; -import { render, waitFor } from "./test-utils"; -import { Excalidraw } from "../index"; import { expect } from "vitest"; -import { getTextEditor, updateTextEditor } from "./queries/dom"; + +import { Excalidraw } from "../index"; + import { mockMermaidToExcalidraw } from "./helpers/mocks"; +import { getTextEditor, updateTextEditor } from "./queries/dom"; +import { render, waitFor } from "./test-utils"; mockMermaidToExcalidraw({ mockRef: true, diff --git a/packages/excalidraw/tests/actionStyles.test.tsx b/packages/excalidraw/tests/actionStyles.test.tsx index abe4a7c00..2e60000ad 100644 --- a/packages/excalidraw/tests/actionStyles.test.tsx +++ b/packages/excalidraw/tests/actionStyles.test.tsx @@ -1,4 +1,6 @@ import React from "react"; + +import { copiedStyles } from "../actions/actionStyles"; import { Excalidraw } from "../index"; import { CODES } from "../keys"; import { API } from "../tests/helpers/api"; @@ -10,7 +12,6 @@ import { screen, togglePopover, } from "../tests/test-utils"; -import { copiedStyles } from "../actions/actionStyles"; const { h } = window; diff --git a/packages/excalidraw/tests/align.test.tsx b/packages/excalidraw/tests/align.test.tsx index 47624e8f4..d29e497ec 100644 --- a/packages/excalidraw/tests/align.test.tsx +++ b/packages/excalidraw/tests/align.test.tsx @@ -1,10 +1,5 @@ import React from "react"; -import { act, unmountComponent, render } from "./test-utils"; -import { Excalidraw } from "../index"; -import { defaultLang, setLanguage } from "../i18n"; -import { UI, Pointer, Keyboard } from "./helpers/ui"; -import { API } from "./helpers/api"; -import { KEYS } from "../keys"; + import { actionAlignVerticallyCentered, actionAlignHorizontallyCentered, @@ -14,6 +9,13 @@ import { actionAlignLeft, actionAlignRight, } from "../actions"; +import { defaultLang, setLanguage } from "../i18n"; +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; + +import { API } from "./helpers/api"; +import { UI, Pointer, Keyboard } from "./helpers/ui"; +import { act, unmountComponent, render } from "./test-utils"; const mouse = new Pointer("mouse"); diff --git a/packages/excalidraw/tests/appState.test.tsx b/packages/excalidraw/tests/appState.test.tsx index 4a937cd56..e97c5f3be 100644 --- a/packages/excalidraw/tests/appState.test.tsx +++ b/packages/excalidraw/tests/appState.test.tsx @@ -1,10 +1,13 @@ import React from "react"; -import { fireEvent, queryByTestId, render, waitFor } from "./test-utils"; -import { Excalidraw } from "../index"; -import { API } from "./helpers/api"; + import { getDefaultAppState } from "../appState"; import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants"; +import { Excalidraw } from "../index"; + +import { API } from "./helpers/api"; import { Pointer, UI } from "./helpers/ui"; +import { fireEvent, queryByTestId, render, waitFor } from "./test-utils"; + import type { ExcalidrawTextElement } from "../element/types"; const { h } = window; diff --git a/packages/excalidraw/tests/binding.test.tsx b/packages/excalidraw/tests/binding.test.tsx index 905a44e70..1c50062c1 100644 --- a/packages/excalidraw/tests/binding.test.tsx +++ b/packages/excalidraw/tests/binding.test.tsx @@ -1,13 +1,15 @@ -import React from "react"; -import { fireEvent, render } from "./test-utils"; -import { Excalidraw, isLinearElement } from "../index"; -import { UI, Pointer, Keyboard } from "./helpers/ui"; -import { getTransformHandles } from "../element/transformHandles"; -import { API } from "./helpers/api"; -import { KEYS } from "../keys"; -import { actionWrapTextInContainer } from "../actions/actionBoundText"; -import { arrayToMap } from "../utils"; import { pointFrom } from "@excalidraw/math"; +import React from "react"; + +import { actionWrapTextInContainer } from "../actions/actionBoundText"; +import { getTransformHandles } from "../element/transformHandles"; +import { Excalidraw, isLinearElement } from "../index"; +import { KEYS } from "../keys"; +import { arrayToMap } from "../utils"; + +import { API } from "./helpers/api"; +import { UI, Pointer, Keyboard } from "./helpers/ui"; +import { fireEvent, render } from "./test-utils"; const { h } = window; diff --git a/packages/excalidraw/tests/clipboard.test.tsx b/packages/excalidraw/tests/clipboard.test.tsx index c2c0a5e2d..c7e17f49d 100644 --- a/packages/excalidraw/tests/clipboard.test.tsx +++ b/packages/excalidraw/tests/clipboard.test.tsx @@ -1,22 +1,25 @@ import React from "react"; import { vi } from "vitest"; + +import { createPasteEvent, serializeAsClipboardJSON } from "../clipboard"; +import { getElementBounds } from "../element"; +import { getLineHeightInPx } from "../element/textMeasurements"; +import { getLineHeight } from "../fonts"; +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; +import { arrayToMap } from "../utils"; + +import { API } from "./helpers/api"; +import { mockMermaidToExcalidraw } from "./helpers/mocks"; +import { Pointer, Keyboard } from "./helpers/ui"; import { render, waitFor, GlobalTestState, unmountComponent, } from "./test-utils"; -import { Pointer, Keyboard } from "./helpers/ui"; -import { Excalidraw } from "../index"; -import { KEYS } from "../keys"; -import { getLineHeightInPx } from "../element/textMeasurements"; -import { getElementBounds } from "../element"; + import type { NormalizedZoomValue } from "../types"; -import { API } from "./helpers/api"; -import { createPasteEvent, serializeAsClipboardJSON } from "../clipboard"; -import { arrayToMap } from "../utils"; -import { mockMermaidToExcalidraw } from "./helpers/mocks"; -import { getLineHeight } from "../fonts"; const { h } = window; diff --git a/packages/excalidraw/tests/contextmenu.test.tsx b/packages/excalidraw/tests/contextmenu.test.tsx index bb3f415f1..7de349516 100644 --- a/packages/excalidraw/tests/contextmenu.test.tsx +++ b/packages/excalidraw/tests/contextmenu.test.tsx @@ -1,4 +1,15 @@ import React from "react"; +import { vi } from "vitest"; + +import { copiedStyles } from "../actions/actionStyles"; +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; +import { reseed } from "../random"; +import * as StaticScene from "../renderer/staticScene"; +import { setDateTimeForTests } from "../utils"; + +import { API } from "./helpers/api"; +import { UI, Pointer, Keyboard } from "./helpers/ui"; import { render, fireEvent, @@ -12,16 +23,8 @@ import { togglePopover, unmountComponent, } from "./test-utils"; -import { Excalidraw } from "../index"; -import * as StaticScene from "../renderer/staticScene"; -import { reseed } from "../random"; -import { UI, Pointer, Keyboard } from "./helpers/ui"; -import { KEYS } from "../keys"; + import type { ShortcutName } from "../actions/shortcuts"; -import { copiedStyles } from "../actions/actionStyles"; -import { API } from "./helpers/api"; -import { setDateTimeForTests } from "../utils"; -import { vi } from "vitest"; import type { ActionName } from "../actions/types"; const checkpoint = (name: string) => { diff --git a/packages/excalidraw/tests/cropElement.test.tsx b/packages/excalidraw/tests/cropElement.test.tsx index a508f5d64..ddc93e3fe 100644 --- a/packages/excalidraw/tests/cropElement.test.tsx +++ b/packages/excalidraw/tests/cropElement.test.tsx @@ -1,15 +1,18 @@ import React from "react"; import { vi } from "vitest"; -import { Keyboard, Pointer, UI } from "./helpers/ui"; -import type { ExcalidrawImageElement, ImageCrop } from "../element/types"; -import { act, GlobalTestState, render, unmountComponent } from "./test-utils"; + import { Excalidraw, exportToCanvas, exportToSvg } from ".."; -import { API } from "./helpers/api"; -import type { NormalizedZoomValue } from "../types"; -import { KEYS } from "../keys"; -import { duplicateElement } from "../element"; -import { cloneJSON } from "../utils"; import { actionFlipHorizontal, actionFlipVertical } from "../actions"; +import { duplicateElement } from "../element"; +import { KEYS } from "../keys"; +import { cloneJSON } from "../utils"; + +import { API } from "./helpers/api"; +import { Keyboard, Pointer, UI } from "./helpers/ui"; +import { act, GlobalTestState, render, unmountComponent } from "./test-utils"; + +import type { ExcalidrawImageElement, ImageCrop } from "../element/types"; +import type { NormalizedZoomValue } from "../types"; const { h } = window; const mouse = new Pointer("mouse"); diff --git a/packages/excalidraw/tests/data/reconcile.test.ts b/packages/excalidraw/tests/data/reconcile.test.ts index f0e8105a1..35a33956a 100644 --- a/packages/excalidraw/tests/data/reconcile.test.ts +++ b/packages/excalidraw/tests/data/reconcile.test.ts @@ -1,13 +1,14 @@ -import type { RemoteExcalidrawElement } from "../../data/reconcile"; import { reconcileElements } from "../../data/reconcile"; +import { syncInvalidIndices } from "../../fractionalIndex"; +import { randomInteger } from "../../random"; +import { cloneJSON } from "../../utils"; + +import type { RemoteExcalidrawElement } from "../../data/reconcile"; import type { ExcalidrawElement, OrderedExcalidrawElement, } from "../../element/types"; -import { syncInvalidIndices } from "../../fractionalIndex"; -import { randomInteger } from "../../random"; import type { AppState } from "../../types"; -import { cloneJSON } from "../../utils"; type Id = string; type ElementLike = { diff --git a/packages/excalidraw/tests/data/restore.test.ts b/packages/excalidraw/tests/data/restore.test.ts index 37a27ac6a..529284358 100644 --- a/packages/excalidraw/tests/data/restore.test.ts +++ b/packages/excalidraw/tests/data/restore.test.ts @@ -1,19 +1,21 @@ +import { pointFrom } from "@excalidraw/math"; +import { vi } from "vitest"; + +import { getDefaultAppState } from "../../appState"; +import { DEFAULT_SIDEBAR, FONT_FAMILY, ROUNDNESS } from "../../constants"; import * as restore from "../../data/restore"; +import { newElementWith } from "../../element/mutateElement"; +import * as sizeHelpers from "../../element/sizeHelpers"; +import { API } from "../helpers/api"; + +import type { ImportedDataState } from "../../data/types"; import type { ExcalidrawElement, ExcalidrawFreeDrawElement, ExcalidrawLinearElement, ExcalidrawTextElement, } from "../../element/types"; -import * as sizeHelpers from "../../element/sizeHelpers"; -import { API } from "../helpers/api"; -import { getDefaultAppState } from "../../appState"; -import type { ImportedDataState } from "../../data/types"; import type { NormalizedZoomValue } from "../../types"; -import { DEFAULT_SIDEBAR, FONT_FAMILY, ROUNDNESS } from "../../constants"; -import { newElementWith } from "../../element/mutateElement"; -import { vi } from "vitest"; -import { pointFrom } from "@excalidraw/math"; describe("restoreElements", () => { const mockSizeHelper = vi.spyOn(sizeHelpers, "isInvisiblySmallElement"); diff --git a/packages/excalidraw/tests/dragCreate.test.tsx b/packages/excalidraw/tests/dragCreate.test.tsx index 743ba7947..ecdfbcb64 100644 --- a/packages/excalidraw/tests/dragCreate.test.tsx +++ b/packages/excalidraw/tests/dragCreate.test.tsx @@ -1,8 +1,12 @@ import React from "react"; +import { vi } from "vitest"; + import { Excalidraw } from "../index"; -import * as StaticScene from "../renderer/staticScene"; -import * as InteractiveScene from "../renderer/interactiveScene"; import { KEYS } from "../keys"; +import { reseed } from "../random"; +import * as InteractiveScene from "../renderer/interactiveScene"; +import * as StaticScene from "../renderer/staticScene"; + import { render, fireEvent, @@ -10,9 +14,8 @@ import { restoreOriginalGetBoundingClientRect, unmountComponent, } from "./test-utils"; + import type { ExcalidrawLinearElement } from "../element/types"; -import { reseed } from "../random"; -import { vi } from "vitest"; unmountComponent(); diff --git a/packages/excalidraw/tests/elementLocking.test.tsx b/packages/excalidraw/tests/elementLocking.test.tsx index 281c26892..a1ca14683 100644 --- a/packages/excalidraw/tests/elementLocking.test.tsx +++ b/packages/excalidraw/tests/elementLocking.test.tsx @@ -1,12 +1,13 @@ import React from "react"; + +import { actionSelectAll } from "../actions"; +import { mutateElement } from "../element/mutateElement"; +import { t } from "../i18n"; import { Excalidraw } from "../index"; -import { render, unmountComponent } from "../tests/test-utils"; -import { Keyboard, Pointer, UI } from "../tests/helpers/ui"; import { KEYS } from "../keys"; import { API } from "../tests/helpers/api"; -import { actionSelectAll } from "../actions"; -import { t } from "../i18n"; -import { mutateElement } from "../element/mutateElement"; +import { Keyboard, Pointer, UI } from "../tests/helpers/ui"; +import { render, unmountComponent } from "../tests/test-utils"; unmountComponent(); diff --git a/packages/excalidraw/tests/excalidraw.test.tsx b/packages/excalidraw/tests/excalidraw.test.tsx index 6fbcf2adc..0e33f8167 100644 --- a/packages/excalidraw/tests/excalidraw.test.tsx +++ b/packages/excalidraw/tests/excalidraw.test.tsx @@ -1,10 +1,12 @@ -import React from "react"; -import { fireEvent, GlobalTestState, toggleMenu, render } from "./test-utils"; -import { Excalidraw, Footer, MainMenu } from "../index"; import { queryByText, queryByTestId } from "@testing-library/react"; +import React from "react"; +import { useMemo } from "react"; + import { THEME } from "../constants"; import { t } from "../i18n"; -import { useMemo } from "react"; +import { Excalidraw, Footer, MainMenu } from "../index"; + +import { fireEvent, GlobalTestState, toggleMenu, render } from "./test-utils"; const { h } = window; diff --git a/packages/excalidraw/tests/export.test.tsx b/packages/excalidraw/tests/export.test.tsx index 3547b2978..c10336271 100644 --- a/packages/excalidraw/tests/export.test.tsx +++ b/packages/excalidraw/tests/export.test.tsx @@ -1,18 +1,21 @@ import React from "react"; -import { render, waitFor } from "./test-utils"; -import { Excalidraw } from "../index"; -import { API } from "./helpers/api"; + +import { getDefaultAppState } from "../appState"; +import { SVG_NS } from "../constants"; +import { getDataURL } from "../data/blob"; import { encodePngMetadata } from "../data/image"; import { serializeAsJSON } from "../data/json"; +import { Excalidraw } from "../index"; import { decodeSvgBase64Payload, encodeSvgBase64Payload, exportToSvg, } from "../scene/export"; + +import { API } from "./helpers/api"; +import { render, waitFor } from "./test-utils"; + import type { FileId } from "../element/types"; -import { getDataURL } from "../data/blob"; -import { getDefaultAppState } from "../appState"; -import { SVG_NS } from "../constants"; const { h } = window; diff --git a/packages/excalidraw/tests/fitToContent.test.tsx b/packages/excalidraw/tests/fitToContent.test.tsx index a4f03910b..bfd16c4e1 100644 --- a/packages/excalidraw/tests/fitToContent.test.tsx +++ b/packages/excalidraw/tests/fitToContent.test.tsx @@ -1,9 +1,10 @@ import React from "react"; -import { act, render } from "./test-utils"; -import { API } from "./helpers/api"; +import { vi } from "vitest"; import { Excalidraw } from "../index"; -import { vi } from "vitest"; + +import { API } from "./helpers/api"; +import { act, render } from "./test-utils"; const { h } = window; diff --git a/packages/excalidraw/tests/fixtures/diagramFixture.ts b/packages/excalidraw/tests/fixtures/diagramFixture.ts index 72b909af8..a4fdc1560 100644 --- a/packages/excalidraw/tests/fixtures/diagramFixture.ts +++ b/packages/excalidraw/tests/fixtures/diagramFixture.ts @@ -1,4 +1,5 @@ import { VERSIONS } from "../../constants"; + import { diamondFixture, ellipseFixture, diff --git a/packages/excalidraw/tests/fixtures/elementFixture.ts b/packages/excalidraw/tests/fixtures/elementFixture.ts index f0470ec86..a7d8c5080 100644 --- a/packages/excalidraw/tests/fixtures/elementFixture.ts +++ b/packages/excalidraw/tests/fixtures/elementFixture.ts @@ -1,5 +1,7 @@ import type { Radians } from "@excalidraw/math"; + import { DEFAULT_FONT_FAMILY } from "../../constants"; + import type { ExcalidrawElement } from "../../element/types"; const elementBase: Omit = { diff --git a/packages/excalidraw/tests/flip.test.tsx b/packages/excalidraw/tests/flip.test.tsx index e3f44b333..9d9f68611 100644 --- a/packages/excalidraw/tests/flip.test.tsx +++ b/packages/excalidraw/tests/flip.test.tsx @@ -1,4 +1,21 @@ +import { pointFrom, type Radians } from "@excalidraw/math"; import React from "react"; +import { vi } from "vitest"; + +import type { LocalPoint } from "@excalidraw/math"; + +import { actionFlipHorizontal, actionFlipVertical } from "../actions"; +import { createPasteEvent } from "../clipboard"; +import { ROUNDNESS } from "../constants"; +import { getElementAbsoluteCoords } from "../element"; +import { newLinearElement } from "../element"; +import { getBoundTextElementPosition } from "../element/textElement"; +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; +import { arrayToMap, cloneJSON } from "../utils"; + +import { API } from "./helpers/api"; +import { UI, Pointer, Keyboard } from "./helpers/ui"; import { fireEvent, GlobalTestState, @@ -7,10 +24,7 @@ import { unmountComponent, waitFor, } from "./test-utils"; -import { UI, Pointer, Keyboard } from "./helpers/ui"; -import { API } from "./helpers/api"; -import { actionFlipHorizontal, actionFlipVertical } from "../actions"; -import { getElementAbsoluteCoords } from "../element"; + import type { ExcalidrawElement, ExcalidrawImageElement, @@ -18,17 +32,7 @@ import type { ExcalidrawTextElementWithContainer, FileId, } from "../element/types"; -import { newLinearElement } from "../element"; -import { Excalidraw } from "../index"; import type { NormalizedZoomValue } from "../types"; -import { ROUNDNESS } from "../constants"; -import { vi } from "vitest"; -import { KEYS } from "../keys"; -import { getBoundTextElementPosition } from "../element/textElement"; -import { createPasteEvent } from "../clipboard"; -import { arrayToMap, cloneJSON } from "../utils"; -import type { LocalPoint } from "@excalidraw/math"; -import { pointFrom, type Radians } from "@excalidraw/math"; const { h } = window; const mouse = new Pointer("mouse"); diff --git a/packages/excalidraw/tests/fractionalIndex.test.ts b/packages/excalidraw/tests/fractionalIndex.test.ts index b57af016b..dbd55bd92 100644 --- a/packages/excalidraw/tests/fractionalIndex.test.ts +++ b/packages/excalidraw/tests/fractionalIndex.test.ts @@ -1,15 +1,18 @@ /* eslint-disable no-lone-blocks */ +import { generateKeyBetween } from "fractional-indexing"; + +import { deepCopyElement } from "../element/newElement"; +import { InvalidFractionalIndexError } from "../errors"; import { syncInvalidIndices, syncMovedIndices, validateFractionalIndices, } from "../fractionalIndex"; -import { API } from "./helpers/api"; import { arrayToMap } from "../utils"; -import { InvalidFractionalIndexError } from "../errors"; + +import { API } from "./helpers/api"; + import type { ExcalidrawElement, FractionalIndex } from "../element/types"; -import { deepCopyElement } from "../element/newElement"; -import { generateKeyBetween } from "fractional-indexing"; describe("sync invalid indices with array order", () => { describe("should NOT sync empty array", () => { diff --git a/packages/excalidraw/tests/helpers/api.ts b/packages/excalidraw/tests/helpers/api.ts index 218f37116..2aa9ee999 100644 --- a/packages/excalidraw/tests/helpers/api.ts +++ b/packages/excalidraw/tests/helpers/api.ts @@ -1,3 +1,32 @@ +import fs from "fs"; +import path from "path"; +import util from "util"; + +import { pointFrom, type LocalPoint, type Radians } from "@excalidraw/math"; + +import { getDefaultAppState } from "../../appState"; +import { createTestHook } from "../../components/App"; +import { DEFAULT_VERTICAL_ALIGN, ROUNDNESS } from "../../constants"; +import { getMimeType } from "../../data/blob"; +import { newElement, newTextElement, newLinearElement } from "../../element"; +import { mutateElement } from "../../element/mutateElement"; +import { + newArrowElement, + newEmbeddableElement, + newFrameElement, + newFreeDrawElement, + newIframeElement, + newImageElement, + newMagicFrameElement, +} from "../../element/newElement"; +import { isLinearElementType } from "../../element/typeChecks"; +import { selectGroupsForSelectedElements } from "../../groups"; +import { getSelectedElements } from "../../scene/selection"; +import { assertNever } from "../../utils"; +import { GlobalTestState, createEvent, fireEvent, act } from "../test-utils"; + +import type { Action } from "../../actions/types"; +import type App from "../../components/App"; import type { ExcalidrawElement, ExcalidrawGenericElement, @@ -13,34 +42,8 @@ import type { ExcalidrawArrowElement, FixedSegment, } from "../../element/types"; -import { newElement, newTextElement, newLinearElement } from "../../element"; -import { DEFAULT_VERTICAL_ALIGN, ROUNDNESS } from "../../constants"; -import { getDefaultAppState } from "../../appState"; -import { GlobalTestState, createEvent, fireEvent, act } from "../test-utils"; -import fs from "fs"; -import util from "util"; -import path from "path"; -import { getMimeType } from "../../data/blob"; -import { - newArrowElement, - newEmbeddableElement, - newFrameElement, - newFreeDrawElement, - newIframeElement, - newImageElement, - newMagicFrameElement, -} from "../../element/newElement"; import type { AppState } from "../../types"; -import { getSelectedElements } from "../../scene/selection"; -import { isLinearElementType } from "../../element/typeChecks"; import type { Mutable } from "../../utility-types"; -import { assertNever } from "../../utils"; -import type App from "../../components/App"; -import { createTestHook } from "../../components/App"; -import type { Action } from "../../actions/types"; -import { mutateElement } from "../../element/mutateElement"; -import { pointFrom, type LocalPoint, type Radians } from "@excalidraw/math"; -import { selectGroupsForSelectedElements } from "../../groups"; const readFile = util.promisify(fs.readFile); // so that window.h is available when App.tsx is not imported as well. diff --git a/packages/excalidraw/tests/helpers/mocks.ts b/packages/excalidraw/tests/helpers/mocks.ts index a87523ec1..10e1dee2b 100644 --- a/packages/excalidraw/tests/helpers/mocks.ts +++ b/packages/excalidraw/tests/helpers/mocks.ts @@ -1,7 +1,8 @@ -import { vi } from "vitest"; import * as MermaidToExcalidraw from "@excalidraw/mermaid-to-excalidraw"; -import type { parseMermaidToExcalidraw } from "@excalidraw/mermaid-to-excalidraw"; import React from "react"; +import { vi } from "vitest"; + +import type { parseMermaidToExcalidraw } from "@excalidraw/mermaid-to-excalidraw"; export const mockMermaidToExcalidraw = (opts: { parseMermaidToExcalidraw: typeof parseMermaidToExcalidraw; diff --git a/packages/excalidraw/tests/helpers/ui.ts b/packages/excalidraw/tests/helpers/ui.ts index b9b7023c1..a72e3fa74 100644 --- a/packages/excalidraw/tests/helpers/ui.ts +++ b/packages/excalidraw/tests/helpers/ui.ts @@ -1,3 +1,33 @@ +import { pointFrom, pointRotateRads } from "@excalidraw/math"; + +import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math"; + +import { createTestHook } from "../../components/App"; +import { getCommonBounds, getElementPointsCoords } from "../../element/bounds"; +import { cropElement } from "../../element/cropElement"; +import { mutateElement } from "../../element/mutateElement"; +import { + getTransformHandles, + getTransformHandlesFromCoords, + OMIT_SIDES_FOR_FRAME, + OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, + type TransformHandle, + type TransformHandleDirection, +} from "../../element/transformHandles"; +import { + isLinearElement, + isFreeDrawElement, + isTextElement, + isFrameLikeElement, +} from "../../element/typeChecks"; +import { KEYS } from "../../keys"; +import { arrayToMap } from "../../utils"; +import { getTextEditor } from "../queries/dom"; +import { act, fireEvent, GlobalTestState, screen } from "../test-utils"; + +import { API } from "./api"; + +import type { TransformHandleType } from "../../element/transformHandles"; import type { ExcalidrawElement, ExcalidrawLinearElement, @@ -10,32 +40,6 @@ import type { ExcalidrawTextElementWithContainer, ExcalidrawImageElement, } from "../../element/types"; -import type { TransformHandleType } from "../../element/transformHandles"; -import { - getTransformHandles, - getTransformHandlesFromCoords, - OMIT_SIDES_FOR_FRAME, - OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, - type TransformHandle, - type TransformHandleDirection, -} from "../../element/transformHandles"; -import { KEYS } from "../../keys"; -import { act, fireEvent, GlobalTestState, screen } from "../test-utils"; -import { mutateElement } from "../../element/mutateElement"; -import { API } from "./api"; -import { - isLinearElement, - isFreeDrawElement, - isTextElement, - isFrameLikeElement, -} from "../../element/typeChecks"; -import { getCommonBounds, getElementPointsCoords } from "../../element/bounds"; -import { getTextEditor } from "../queries/dom"; -import { arrayToMap } from "../../utils"; -import { createTestHook } from "../../components/App"; -import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math"; -import { pointFrom, pointRotateRads } from "@excalidraw/math"; -import { cropElement } from "../../element/cropElement"; import type { ToolType } from "../../types"; // so that window.h is available when App.tsx is not imported as well. diff --git a/packages/excalidraw/tests/history.test.tsx b/packages/excalidraw/tests/history.test.tsx index ffddec49f..6e7066b90 100644 --- a/packages/excalidraw/tests/history.test.tsx +++ b/packages/excalidraw/tests/history.test.tsx @@ -1,6 +1,43 @@ import React from "react"; +import { + queryByText, + fireEvent, + queryByTestId, + waitFor, +} from "@testing-library/react"; +import { vi } from "vitest"; +import { pointFrom } from "@excalidraw/math"; + +import type { LocalPoint, Radians } from "@excalidraw/math"; + import "../global.d.ts"; +import "../../utils/test-utils"; + +import { + actionSendBackward, + actionBringForward, + actionSendToBack, +} from "../actions"; +import { createUndoAction, createRedoAction } from "../actions/actionHistory"; +import { actionToggleViewMode } from "../actions/actionToggleViewMode"; +import { getDefaultAppState } from "../appState"; +import { HistoryEntry } from "../history"; +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; import * as StaticScene from "../renderer/staticScene"; +import { EXPORT_DATA_TYPES, MIME_TYPES, ORIG_ID } from "../constants"; +import { Snapshot, CaptureUpdateAction } from "../store"; +import { arrayToMap } from "../utils"; +import { + COLOR_PALETTE, + DEFAULT_ELEMENT_BACKGROUND_COLOR_INDEX, + DEFAULT_ELEMENT_STROKE_COLOR_INDEX, +} from "../colors"; +import { newElementWith } from "../element/mutateElement"; +import { AppStateChange, ElementsChange } from "../change"; + +import { API } from "./helpers/api"; +import { Keyboard, Pointer, UI } from "./helpers/ui"; import { GlobalTestState, act, @@ -9,24 +46,7 @@ import { togglePopover, getCloneByOrigId, } from "./test-utils"; -import "../../utils/test-utils"; -import { Excalidraw } from "../index"; -import { Keyboard, Pointer, UI } from "./helpers/ui"; -import { API } from "./helpers/api"; -import { getDefaultAppState } from "../appState"; -import { fireEvent, queryByTestId, waitFor } from "@testing-library/react"; -import { createUndoAction, createRedoAction } from "../actions/actionHistory"; -import { actionToggleViewMode } from "../actions/actionToggleViewMode"; -import { EXPORT_DATA_TYPES, MIME_TYPES, ORIG_ID } from "../constants"; -import type { AppState } from "../types"; -import { arrayToMap } from "../utils"; -import { - COLOR_PALETTE, - DEFAULT_ELEMENT_BACKGROUND_COLOR_INDEX, - DEFAULT_ELEMENT_STROKE_COLOR_INDEX, -} from "../colors"; -import { KEYS } from "../keys"; -import { newElementWith } from "../element/mutateElement"; + import type { ExcalidrawElbowArrowElement, ExcalidrawFrameElement, @@ -37,18 +57,7 @@ import type { FractionalIndex, SceneElementsMap, } from "../element/types"; -import { - actionSendBackward, - actionBringForward, - actionSendToBack, -} from "../actions"; -import { vi } from "vitest"; -import { queryByText } from "@testing-library/react"; -import { HistoryEntry } from "../history"; -import { AppStateChange, ElementsChange } from "../change"; -import { Snapshot, CaptureUpdateAction } from "../store"; -import type { LocalPoint, Radians } from "@excalidraw/math"; -import { pointFrom } from "@excalidraw/math"; +import type { AppState } from "../types"; const { h } = window; diff --git a/packages/excalidraw/tests/library.test.tsx b/packages/excalidraw/tests/library.test.tsx index 7b48407b7..b084d835b 100644 --- a/packages/excalidraw/tests/library.test.tsx +++ b/packages/excalidraw/tests/library.test.tsx @@ -1,18 +1,20 @@ +import { act, queryByTestId } from "@testing-library/react"; import React from "react"; import { vi } from "vitest"; -import { fireEvent, getCloneByOrigId, render, waitFor } from "./test-utils"; -import { act, queryByTestId } from "@testing-library/react"; -import { Excalidraw } from "../index"; -import { API } from "./helpers/api"; import { MIME_TYPES, ORIG_ID } from "../constants"; -import type { LibraryItem, LibraryItems } from "../types"; -import { UI } from "./helpers/ui"; +import { parseLibraryJSON } from "../data/blob"; import { serializeLibraryAsJSON } from "../data/json"; import { distributeLibraryItemsOnSquareGrid } from "../data/library"; -import type { ExcalidrawGenericElement } from "../element/types"; import { getCommonBoundingBox } from "../element/bounds"; -import { parseLibraryJSON } from "../data/blob"; +import { Excalidraw } from "../index"; + +import { API } from "./helpers/api"; +import { UI } from "./helpers/ui"; +import { fireEvent, getCloneByOrigId, render, waitFor } from "./test-utils"; + +import type { ExcalidrawGenericElement } from "../element/types"; +import type { LibraryItem, LibraryItems } from "../types"; const { h } = window; diff --git a/packages/excalidraw/tests/linearElementEditor.test.tsx b/packages/excalidraw/tests/linearElementEditor.test.tsx index 98d6058c2..162dbb3f0 100644 --- a/packages/excalidraw/tests/linearElementEditor.test.tsx +++ b/packages/excalidraw/tests/linearElementEditor.test.tsx @@ -1,14 +1,25 @@ +import { pointCenter, pointFrom } from "@excalidraw/math"; +import { act, queryByTestId, queryByText } from "@testing-library/react"; import React from "react"; -import type { - ExcalidrawElement, - ExcalidrawLinearElement, - ExcalidrawTextElementWithContainer, - FontString, -} from "../element/types"; +import { vi } from "vitest"; + +import type { GlobalPoint } from "@excalidraw/math"; + +import { ROUNDNESS, VERTICAL_ALIGN } from "../constants"; +import { LinearElementEditor } from "../element/linearElementEditor"; +import { + getBoundTextElementPosition, + getBoundTextMaxWidth, +} from "../element/textElement"; +import * as textElementUtils from "../element/textElement"; +import { wrapText } from "../element/textWrapping"; import { Excalidraw, mutateElement } from "../index"; +import { KEYS } from "../keys"; import { reseed } from "../random"; -import * as StaticScene from "../renderer/staticScene"; import * as InteractiveCanvas from "../renderer/interactiveScene"; +import * as StaticScene from "../renderer/staticScene"; +import { API } from "../tests/helpers/api"; +import { arrayToMap } from "../utils"; import { Keyboard, Pointer, UI } from "./helpers/ui"; import { @@ -18,21 +29,13 @@ import { GlobalTestState, unmountComponent, } from "./test-utils"; -import { API } from "../tests/helpers/api"; -import { KEYS } from "../keys"; -import { LinearElementEditor } from "../element/linearElementEditor"; -import { act, queryByTestId, queryByText } from "@testing-library/react"; -import { - getBoundTextElementPosition, - getBoundTextMaxWidth, -} from "../element/textElement"; -import * as textElementUtils from "../element/textElement"; -import { ROUNDNESS, VERTICAL_ALIGN } from "../constants"; -import { vi } from "vitest"; -import { arrayToMap } from "../utils"; -import type { GlobalPoint } from "@excalidraw/math"; -import { pointCenter, pointFrom } from "@excalidraw/math"; -import { wrapText } from "../element/textWrapping"; + +import type { + ExcalidrawElement, + ExcalidrawLinearElement, + ExcalidrawTextElementWithContainer, + FontString, +} from "../element/types"; const renderInteractiveScene = vi.spyOn( InteractiveCanvas, diff --git a/packages/excalidraw/tests/move.test.tsx b/packages/excalidraw/tests/move.test.tsx index 528f9554d..855496f44 100644 --- a/packages/excalidraw/tests/move.test.tsx +++ b/packages/excalidraw/tests/move.test.tsx @@ -1,19 +1,22 @@ import React from "react"; +import { vi } from "vitest"; + import "../../utils/test-utils"; -import { render, fireEvent, act, unmountComponent } from "./test-utils"; -import { Excalidraw } from "../index"; -import * as StaticScene from "../renderer/staticScene"; -import * as InteractiveCanvas from "../renderer/interactiveScene"; -import { reseed } from "../random"; import { bindOrUnbindLinearElement } from "../element/binding"; +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; +import { reseed } from "../random"; +import * as InteractiveCanvas from "../renderer/interactiveScene"; +import * as StaticScene from "../renderer/staticScene"; + +import { UI, Pointer, Keyboard } from "./helpers/ui"; +import { render, fireEvent, act, unmountComponent } from "./test-utils"; + import type { ExcalidrawLinearElement, NonDeleted, ExcalidrawRectangleElement, } from "../element/types"; -import { UI, Pointer, Keyboard } from "./helpers/ui"; -import { KEYS } from "../keys"; -import { vi } from "vitest"; import type Scene from "../scene/Scene"; unmountComponent(); diff --git a/packages/excalidraw/tests/multiPointCreate.test.tsx b/packages/excalidraw/tests/multiPointCreate.test.tsx index df94eb9da..5cbce4966 100644 --- a/packages/excalidraw/tests/multiPointCreate.test.tsx +++ b/packages/excalidraw/tests/multiPointCreate.test.tsx @@ -1,4 +1,12 @@ import React from "react"; +import { vi } from "vitest"; + +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; +import { reseed } from "../random"; +import * as InteractiveCanvas from "../renderer/interactiveScene"; +import * as StaticScene from "../renderer/staticScene"; + import { render, fireEvent, @@ -6,13 +14,8 @@ import { restoreOriginalGetBoundingClientRect, unmountComponent, } from "./test-utils"; -import { Excalidraw } from "../index"; -import * as StaticScene from "../renderer/staticScene"; -import * as InteractiveCanvas from "../renderer/interactiveScene"; -import { KEYS } from "../keys"; + import type { ExcalidrawLinearElement } from "../element/types"; -import { reseed } from "../random"; -import { vi } from "vitest"; unmountComponent(); diff --git a/packages/excalidraw/tests/packages/events.test.tsx b/packages/excalidraw/tests/packages/events.test.tsx index ad8b3c6d9..3e79239c8 100644 --- a/packages/excalidraw/tests/packages/events.test.tsx +++ b/packages/excalidraw/tests/packages/events.test.tsx @@ -1,11 +1,13 @@ import React from "react"; import { vi } from "vitest"; + import { Excalidraw, CaptureUpdateAction } from "../../index"; -import type { ExcalidrawImperativeAPI } from "../../types"; import { resolvablePromise } from "../../utils"; -import { render } from "../test-utils"; -import { Pointer } from "../helpers/ui"; import { API } from "../helpers/api"; +import { Pointer } from "../helpers/ui"; +import { render } from "../test-utils"; + +import type { ExcalidrawImperativeAPI } from "../../types"; describe("event callbacks", () => { const h = window.h; diff --git a/packages/excalidraw/tests/queries/toolQueries.ts b/packages/excalidraw/tests/queries/toolQueries.ts index df0afa117..ed168735d 100644 --- a/packages/excalidraw/tests/queries/toolQueries.ts +++ b/packages/excalidraw/tests/queries/toolQueries.ts @@ -1,7 +1,9 @@ import { queries, buildQueries } from "@testing-library/react"; -import type { ToolType } from "../../types"; + import { TOOL_TYPE } from "../../constants"; +import type { ToolType } from "../../types"; + const _getAllByToolName = (container: HTMLElement, tool: ToolType | "lock") => { const toolTitle = tool === "lock" ? "lock" : TOOL_TYPE[tool]; return queries.getAllByTestId(container, `toolbar-${toolTitle}`); diff --git a/packages/excalidraw/tests/regressionTests.test.tsx b/packages/excalidraw/tests/regressionTests.test.tsx index a3e45bca6..8407f0766 100644 --- a/packages/excalidraw/tests/regressionTests.test.tsx +++ b/packages/excalidraw/tests/regressionTests.test.tsx @@ -1,10 +1,13 @@ import React from "react"; -import type { ExcalidrawElement } from "../element/types"; -import { CODES, KEYS } from "../keys"; +import { vi } from "vitest"; + +import { FONT_FAMILY } from "../constants"; import { Excalidraw } from "../index"; +import { CODES, KEYS } from "../keys"; import { reseed } from "../random"; import * as StaticScene from "../renderer/staticScene"; import { setDateTimeForTests } from "../utils"; + import { API } from "./helpers/api"; import { Keyboard, Pointer, UI } from "./helpers/ui"; import { @@ -15,8 +18,8 @@ import { togglePopover, unmountComponent, } from "./test-utils"; -import { FONT_FAMILY } from "../constants"; -import { vi } from "vitest"; + +import type { ExcalidrawElement } from "../element/types"; const { h } = window; diff --git a/packages/excalidraw/tests/resize.test.tsx b/packages/excalidraw/tests/resize.test.tsx index 3ebd8cf81..055b097b3 100644 --- a/packages/excalidraw/tests/resize.test.tsx +++ b/packages/excalidraw/tests/resize.test.tsx @@ -1,24 +1,28 @@ +import { pointFrom } from "@excalidraw/math"; import React from "react"; -import { render, unmountComponent } from "./test-utils"; + +import type { LocalPoint } from "@excalidraw/math"; + +import { getElementPointsCoords } from "../element/bounds"; +import { LinearElementEditor } from "../element/linearElementEditor"; +import { resizeSingleElement } from "../element/resizeElements"; +import { isLinearElement } from "../element/typeChecks"; +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; +import { getSizeFromPoints } from "../points"; import { reseed } from "../random"; +import { arrayToMap } from "../utils"; + +import { API } from "./helpers/api"; import { UI, Keyboard, Pointer } from "./helpers/ui"; +import { render, unmountComponent } from "./test-utils"; + +import type { Bounds } from "../element/bounds"; import type { ExcalidrawElbowArrowElement, ExcalidrawFreeDrawElement, ExcalidrawLinearElement, } from "../element/types"; -import type { Bounds } from "../element/bounds"; -import { getElementPointsCoords } from "../element/bounds"; -import { Excalidraw } from "../index"; -import { API } from "./helpers/api"; -import { KEYS } from "../keys"; -import { isLinearElement } from "../element/typeChecks"; -import { LinearElementEditor } from "../element/linearElementEditor"; -import { arrayToMap } from "../utils"; -import type { LocalPoint } from "@excalidraw/math"; -import { pointFrom } from "@excalidraw/math"; -import { resizeSingleElement } from "../element/resizeElements"; -import { getSizeFromPoints } from "../points"; unmountComponent(); diff --git a/packages/excalidraw/tests/rotate.test.tsx b/packages/excalidraw/tests/rotate.test.tsx index 367313ea9..2c678f45b 100644 --- a/packages/excalidraw/tests/rotate.test.tsx +++ b/packages/excalidraw/tests/rotate.test.tsx @@ -1,10 +1,12 @@ import React from "react"; -import { render, unmountComponent } from "./test-utils"; -import { reseed } from "../random"; -import { UI } from "./helpers/ui"; -import { Excalidraw } from "../index"; import { expect } from "vitest"; +import { Excalidraw } from "../index"; +import { reseed } from "../random"; + +import { UI } from "./helpers/ui"; +import { render, unmountComponent } from "./test-utils"; + unmountComponent(); beforeEach(() => { diff --git a/packages/excalidraw/tests/scene/export.test.ts b/packages/excalidraw/tests/scene/export.test.ts index 6fd8a8b85..43c37eb28 100644 --- a/packages/excalidraw/tests/scene/export.test.ts +++ b/packages/excalidraw/tests/scene/export.test.ts @@ -1,8 +1,7 @@ -import type { - ExcalidrawTextElement, - FractionalIndex, - NonDeletedExcalidrawElement, -} from "../../element/types"; +import { exportToCanvas, exportToSvg } from "@excalidraw/utils"; + +import { FONT_FAMILY, FRAME_STYLE } from "../../constants"; +import { prepareElementsForExport } from "../../data"; import * as exportUtils from "../../scene/export"; import { diamondFixture, @@ -11,9 +10,12 @@ import { textFixture, } from "../fixtures/elementFixture"; import { API } from "../helpers/api"; -import { exportToCanvas, exportToSvg } from "@excalidraw/utils"; -import { FONT_FAMILY, FRAME_STYLE } from "../../constants"; -import { prepareElementsForExport } from "../../data"; + +import type { + ExcalidrawTextElement, + FractionalIndex, + NonDeletedExcalidrawElement, +} from "../../element/types"; describe("exportToSvg", () => { const ELEMENT_HEIGHT = 100; diff --git a/packages/excalidraw/tests/scroll.test.tsx b/packages/excalidraw/tests/scroll.test.tsx index 1a3f80c14..2ccc4546f 100644 --- a/packages/excalidraw/tests/scroll.test.tsx +++ b/packages/excalidraw/tests/scroll.test.tsx @@ -1,14 +1,16 @@ import React from "react"; + +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; + +import { API } from "./helpers/api"; +import { Keyboard } from "./helpers/ui"; import { mockBoundingClientRect, render, restoreOriginalGetBoundingClientRect, waitFor, } from "./test-utils"; -import { Excalidraw } from "../index"; -import { API } from "./helpers/api"; -import { Keyboard } from "./helpers/ui"; -import { KEYS } from "../keys"; const { h } = window; diff --git a/packages/excalidraw/tests/search.test.tsx b/packages/excalidraw/tests/search.test.tsx index 68ad65826..d3622d4b1 100644 --- a/packages/excalidraw/tests/search.test.tsx +++ b/packages/excalidraw/tests/search.test.tsx @@ -1,11 +1,14 @@ import React from "react"; -import { act, render, waitFor } from "./test-utils"; -import { Excalidraw } from "../index"; + import { CANVAS_SEARCH_TAB, CLASSES, DEFAULT_SIDEBAR } from "../constants"; -import { Keyboard } from "./helpers/ui"; +import { Excalidraw } from "../index"; import { KEYS } from "../keys"; -import { updateTextEditor } from "./queries/dom"; + import { API } from "./helpers/api"; +import { Keyboard } from "./helpers/ui"; +import { updateTextEditor } from "./queries/dom"; +import { act, render, waitFor } from "./test-utils"; + import type { ExcalidrawTextElement } from "../element/types"; const { h } = window; diff --git a/packages/excalidraw/tests/selection.test.tsx b/packages/excalidraw/tests/selection.test.tsx index 0b1f6e50d..d93d3eff5 100644 --- a/packages/excalidraw/tests/selection.test.tsx +++ b/packages/excalidraw/tests/selection.test.tsx @@ -1,4 +1,15 @@ import React from "react"; +import { vi } from "vitest"; + +import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; +import { reseed } from "../random"; +import * as InteractiveCanvas from "../renderer/interactiveScene"; +import * as StaticScene from "../renderer/staticScene"; +import { SHAPES } from "../shapes"; + +import { API } from "./helpers/api"; +import { Keyboard, Pointer, UI } from "./helpers/ui"; import { render, fireEvent, @@ -7,15 +18,6 @@ import { assertSelectedElements, unmountComponent, } from "./test-utils"; -import { Excalidraw } from "../index"; -import * as StaticScene from "../renderer/staticScene"; -import * as InteractiveCanvas from "../renderer/interactiveScene"; -import { KEYS } from "../keys"; -import { reseed } from "../random"; -import { API } from "./helpers/api"; -import { Keyboard, Pointer, UI } from "./helpers/ui"; -import { SHAPES } from "../shapes"; -import { vi } from "vitest"; unmountComponent(); diff --git a/packages/excalidraw/tests/shortcuts.test.tsx b/packages/excalidraw/tests/shortcuts.test.tsx index e02ed0193..ee2234ecc 100644 --- a/packages/excalidraw/tests/shortcuts.test.tsx +++ b/packages/excalidraw/tests/shortcuts.test.tsx @@ -1,6 +1,8 @@ import React from "react"; -import { KEYS } from "../keys"; + import { Excalidraw } from "../index"; +import { KEYS } from "../keys"; + import { API } from "./helpers/api"; import { Keyboard } from "./helpers/ui"; import { fireEvent, render, waitFor } from "./test-utils"; diff --git a/packages/excalidraw/tests/test-utils.ts b/packages/excalidraw/tests/test-utils.ts index 84936f520..73b340982 100644 --- a/packages/excalidraw/tests/test-utils.ts +++ b/packages/excalidraw/tests/test-utils.ts @@ -1,6 +1,4 @@ import "pepjs"; - -import type { RenderResult, RenderOptions } from "@testing-library/react"; import { act } from "@testing-library/react"; import { render, @@ -9,16 +7,20 @@ import { fireEvent, cleanup, } from "@testing-library/react"; - -import * as toolQueries from "./queries/toolQueries"; -import type { ImportedDataState } from "../data/types"; -import { STORAGE_KEYS } from "../../../excalidraw-app/app_constants"; -import { getSelectedElements } from "../scene/selection"; -import type { ExcalidrawElement } from "../element/types"; -import { UI } from "./helpers/ui"; import ansi from "ansicolor"; + +import { STORAGE_KEYS } from "../../../excalidraw-app/app_constants"; import { ORIG_ID } from "../constants"; +import { getSelectedElements } from "../scene/selection"; import { arrayToMap } from "../utils"; + +import { UI } from "./helpers/ui"; +import * as toolQueries from "./queries/toolQueries"; + +import type { RenderResult, RenderOptions } from "@testing-library/react"; + +import type { ImportedDataState } from "../data/types"; +import type { ExcalidrawElement } from "../element/types"; import type { AllPossibleKeys } from "../utility-types"; export { cleanup as unmountComponent }; diff --git a/packages/excalidraw/tests/tool.test.tsx b/packages/excalidraw/tests/tool.test.tsx index 662c61baa..f70d1ccdd 100644 --- a/packages/excalidraw/tests/tool.test.tsx +++ b/packages/excalidraw/tests/tool.test.tsx @@ -1,9 +1,12 @@ import React from "react"; + import { Excalidraw } from "../index"; -import type { ExcalidrawImperativeAPI } from "../types"; import { resolvablePromise } from "../utils"; -import { act, render } from "./test-utils"; + import { Pointer } from "./helpers/ui"; +import { act, render } from "./test-utils"; + +import type { ExcalidrawImperativeAPI } from "../types"; describe("setActiveTool()", () => { const h = window.h; diff --git a/packages/excalidraw/tests/viewMode.test.tsx b/packages/excalidraw/tests/viewMode.test.tsx index 66eca8c7f..c190ee0e3 100644 --- a/packages/excalidraw/tests/viewMode.test.tsx +++ b/packages/excalidraw/tests/viewMode.test.tsx @@ -1,10 +1,12 @@ import React from "react"; -import { render, GlobalTestState } from "./test-utils"; + +import { CURSOR_TYPE } from "../constants"; import { Excalidraw } from "../index"; import { KEYS } from "../keys"; -import { Keyboard, Pointer, UI } from "./helpers/ui"; -import { CURSOR_TYPE } from "../constants"; + import { API } from "./helpers/api"; +import { Keyboard, Pointer, UI } from "./helpers/ui"; +import { render, GlobalTestState } from "./test-utils"; const mouse = new Pointer("mouse"); const touch = new Pointer("touch"); diff --git a/packages/excalidraw/tests/zindex.test.tsx b/packages/excalidraw/tests/zindex.test.tsx index f15d55931..c8e166aff 100644 --- a/packages/excalidraw/tests/zindex.test.tsx +++ b/packages/excalidraw/tests/zindex.test.tsx @@ -1,7 +1,5 @@ import React from "react"; -import { act, getCloneByOrigId, render, unmountComponent } from "./test-utils"; -import { Excalidraw } from "../index"; -import { reseed } from "../random"; + import { actionSendBackward, actionBringForward, @@ -9,14 +7,19 @@ import { actionSendToBack, actionDuplicateSelection, } from "../actions"; -import type { AppState } from "../types"; -import { API } from "./helpers/api"; import { selectGroupsForSelectedElements } from "../groups"; +import { Excalidraw } from "../index"; +import { reseed } from "../random"; + +import { API } from "./helpers/api"; +import { act, getCloneByOrigId, render, unmountComponent } from "./test-utils"; + import type { ExcalidrawElement, ExcalidrawFrameElement, ExcalidrawSelectionElement, } from "../element/types"; +import type { AppState } from "../types"; unmountComponent(); diff --git a/packages/excalidraw/types.ts b/packages/excalidraw/types.ts index 0562736cd..64c0ac298 100644 --- a/packages/excalidraw/types.ts +++ b/packages/excalidraw/types.ts @@ -1,5 +1,19 @@ -import type { JSX } from "react"; -import type React from "react"; +import type { Action } from "./actions/types"; +import type { Spreadsheet } from "./charts"; +import type { ClipboardData } from "./clipboard"; +import type App from "./components/App"; +import type Library from "./data/library"; +import type { FileSystemHandle } from "./data/filesystem"; +import type { IMAGE_MIME_TYPES, MIME_TYPES } from "./constants"; +import type { ContextMenuItems } from "./components/ContextMenu"; +import type { SnapLine } from "./snapping"; +import type { Merge, MaybePromise, ValueOf, MakeBrand } from "./utility-types"; +import type { CaptureUpdateActionType } from "./store"; +import type { UserIdleState } from "./constants"; +import type { ImportedDataState } from "./data/types"; +import type { SuggestedBinding } from "./element/binding"; +import type { LinearElementEditor } from "./element/linearElementEditor"; +import type { MaybeTransformHandleType } from "./element/transformHandles"; import type { PointerType, ExcalidrawLinearElement, @@ -24,25 +38,11 @@ import type { OrderedExcalidrawElement, ExcalidrawNonSelectionElement, } from "./element/types"; -import type { Action } from "./actions/types"; -import type { LinearElementEditor } from "./element/linearElementEditor"; -import type { SuggestedBinding } from "./element/binding"; -import type { ImportedDataState } from "./data/types"; -import type App from "./components/App"; -import type { throttleRAF } from "./utils"; -import type { Spreadsheet } from "./charts"; import type { Language } from "./i18n"; -import type { ClipboardData } from "./clipboard"; import type { isOverScrollBars } from "./scene/scrollbars"; -import type { MaybeTransformHandleType } from "./element/transformHandles"; -import type Library from "./data/library"; -import type { FileSystemHandle } from "./data/filesystem"; -import type { IMAGE_MIME_TYPES, MIME_TYPES } from "./constants"; -import type { ContextMenuItems } from "./components/ContextMenu"; -import type { SnapLine } from "./snapping"; -import type { Merge, MaybePromise, ValueOf, MakeBrand } from "./utility-types"; -import type { CaptureUpdateActionType } from "./store"; -import type { UserIdleState } from "./constants"; +import type { throttleRAF } from "./utils"; +import type React from "react"; +import type { JSX } from "react"; export type SocketId = string & { _brand: "SocketId" }; diff --git a/packages/excalidraw/utils.ts b/packages/excalidraw/utils.ts index e3e18eb93..84f6637f1 100644 --- a/packages/excalidraw/utils.ts +++ b/packages/excalidraw/utils.ts @@ -1,7 +1,7 @@ -import Pool from "es6-promise-pool"; import { average } from "@excalidraw/math"; +import Pool from "es6-promise-pool"; + import { COLOR_PALETTE } from "./colors"; -import type { EVENT } from "./constants"; import { DEFAULT_VERSION, FONT_FAMILY, @@ -9,6 +9,8 @@ import { isDarwin, WINDOWS_EMOJI_FALLBACK_FONT, } from "./constants"; + +import type { EVENT } from "./constants"; import type { ExcalidrawBindableElement, FontFamilyValues, diff --git a/packages/excalidraw/visualdebug.ts b/packages/excalidraw/visualdebug.ts index 862f2cf20..6b7014864 100644 --- a/packages/excalidraw/visualdebug.ts +++ b/packages/excalidraw/visualdebug.ts @@ -1,4 +1,3 @@ -import type { Curve } from "@excalidraw/math"; import { isLineSegment, lineSegment, @@ -6,10 +5,14 @@ import { type GlobalPoint, type LocalPoint, } from "@excalidraw/math"; + +import type { Curve } from "@excalidraw/math"; import type { LineSegment } from "@excalidraw/utils"; -import type { Bounds } from "./element/bounds"; + import { isBounds } from "./element/typeChecks"; +import type { Bounds } from "./element/bounds"; + // The global data holder to collect the debug operations declare global { interface Window { diff --git a/packages/excalidraw/zindex.ts b/packages/excalidraw/zindex.ts index 1118ed533..8ffcec5d1 100644 --- a/packages/excalidraw/zindex.ts +++ b/packages/excalidraw/zindex.ts @@ -1,15 +1,16 @@ import { isFrameLikeElement } from "./element/typeChecks"; -import type { - ExcalidrawElement, - ExcalidrawFrameLikeElement, -} from "./element/types"; import { syncMovedIndices } from "./fractionalIndex"; import { getElementsInGroup } from "./groups"; import { getSelectedElements } from "./scene"; import Scene from "./scene/Scene"; -import type { AppState } from "./types"; import { arrayToMap, findIndex, findLastIndex } from "./utils"; +import type { + ExcalidrawElement, + ExcalidrawFrameLikeElement, +} from "./element/types"; +import type { AppState } from "./types"; + const isOfTargetFrame = (element: ExcalidrawElement, frameId: string) => { return element.frameId === frameId || element.id === frameId; }; diff --git a/packages/math/angle.ts b/packages/math/angle.ts index 8d473cf55..353dc5dad 100644 --- a/packages/math/angle.ts +++ b/packages/math/angle.ts @@ -1,3 +1,5 @@ +import { PRECISION } from "./utils"; + import type { Degrees, GlobalPoint, @@ -5,7 +7,6 @@ import type { PolarCoords, Radians, } from "./types"; -import { PRECISION } from "./utils"; // TODO: Simplify with modulo and fix for angles beyond 4*Math.PI and - 4*Math.PI export const normalizeRadians = (angle: Radians): Radians => { diff --git a/packages/math/curve.test.ts b/packages/math/curve.test.ts index 94670d7ab..8d60a7346 100644 --- a/packages/math/curve.test.ts +++ b/packages/math/curve.test.ts @@ -1,4 +1,5 @@ import "../utils/test-utils"; + import { curve, curveClosestPoint, diff --git a/packages/math/curve.ts b/packages/math/curve.ts index 9b275ceec..cd466bbc7 100644 --- a/packages/math/curve.ts +++ b/packages/math/curve.ts @@ -1,7 +1,8 @@ -import type { Bounds } from "../excalidraw/element/bounds"; import { isPoint, pointDistance, pointFrom } from "./point"; import { rectangle, rectangleIntersectLineSegment } from "./rectangle"; + import type { Curve, GlobalPoint, LineSegment, LocalPoint } from "./types"; +import type { Bounds } from "../excalidraw/element/bounds"; /** * diff --git a/packages/math/ellipse.test.ts b/packages/math/ellipse.test.ts index 507cc5a1a..bcaab2a5d 100644 --- a/packages/math/ellipse.test.ts +++ b/packages/math/ellipse.test.ts @@ -8,6 +8,7 @@ import { import { line } from "./line"; import { pointFrom } from "./point"; import { lineSegment } from "./segment"; + import type { Ellipse, GlobalPoint } from "./types"; describe("point and ellipse", () => { diff --git a/packages/math/ellipse.ts b/packages/math/ellipse.ts index c32def0eb..741a77df3 100644 --- a/packages/math/ellipse.ts +++ b/packages/math/ellipse.ts @@ -4,13 +4,6 @@ import { pointFromVector, pointsEqual, } from "./point"; -import type { - Ellipse, - GlobalPoint, - Line, - LineSegment, - LocalPoint, -} from "./types"; import { PRECISION } from "./utils"; import { vector, @@ -20,6 +13,14 @@ import { vectorScale, } from "./vector"; +import type { + Ellipse, + GlobalPoint, + Line, + LineSegment, + LocalPoint, +} from "./types"; + /** * Construct an Ellipse object from the parameters * diff --git a/packages/math/line.ts b/packages/math/line.ts index bcb4f6d90..889fa08ce 100644 --- a/packages/math/line.ts +++ b/packages/math/line.ts @@ -1,4 +1,5 @@ import { pointFrom } from "./point"; + import type { GlobalPoint, Line, LocalPoint } from "./types"; /** diff --git a/packages/math/point.test.ts b/packages/math/point.test.ts index 89cc4f8f3..0ed59ee9a 100644 --- a/packages/math/point.test.ts +++ b/packages/math/point.test.ts @@ -1,4 +1,5 @@ import { pointFrom, pointRotateRads } from "./point"; + import type { Radians } from "./types"; describe("rotate", () => { diff --git a/packages/math/point.ts b/packages/math/point.ts index 92df18773..f6234c8ea 100644 --- a/packages/math/point.ts +++ b/packages/math/point.ts @@ -1,4 +1,7 @@ import { degreesToRadians } from "./angle"; +import { PRECISION } from "./utils"; +import { vectorFromPoint, vectorScale } from "./vector"; + import type { LocalPoint, GlobalPoint, @@ -6,8 +9,6 @@ import type { Degrees, Vector, } from "./types"; -import { PRECISION } from "./utils"; -import { vectorFromPoint, vectorScale } from "./vector"; /** * Create a properly typed Point instance from the X and Y coordinates. diff --git a/packages/math/polygon.ts b/packages/math/polygon.ts index 783bc4cf3..762c82dbf 100644 --- a/packages/math/polygon.ts +++ b/packages/math/polygon.ts @@ -1,8 +1,9 @@ import { pointsEqual } from "./point"; import { lineSegment, pointOnLineSegment } from "./segment"; -import type { GlobalPoint, LocalPoint, Polygon } from "./types"; import { PRECISION } from "./utils"; +import type { GlobalPoint, LocalPoint, Polygon } from "./types"; + export function polygon( ...points: Point[] ) { diff --git a/packages/math/range.ts b/packages/math/range.ts index d90530cba..dee3d7edf 100644 --- a/packages/math/range.ts +++ b/packages/math/range.ts @@ -1,4 +1,5 @@ import { toBrandedType } from "@excalidraw/excalidraw/utils"; + import type { InclusiveRange } from "./types"; /** diff --git a/packages/math/rectangle.ts b/packages/math/rectangle.ts index 7dde15ded..394b5c2f8 100644 --- a/packages/math/rectangle.ts +++ b/packages/math/rectangle.ts @@ -1,5 +1,6 @@ import { pointFrom } from "./point"; import { lineSegment, lineSegmentIntersectionPoints } from "./segment"; + import type { GlobalPoint, LineSegment, LocalPoint, Rectangle } from "./types"; export function rectangle

( diff --git a/packages/math/segment.ts b/packages/math/segment.ts index 60943aacb..e38978b7e 100644 --- a/packages/math/segment.ts +++ b/packages/math/segment.ts @@ -5,7 +5,6 @@ import { pointFromVector, pointRotateRads, } from "./point"; -import type { GlobalPoint, LineSegment, LocalPoint, Radians } from "./types"; import { PRECISION } from "./utils"; import { vectorAdd, @@ -15,6 +14,8 @@ import { vectorSubtract, } from "./vector"; +import type { GlobalPoint, LineSegment, LocalPoint, Radians } from "./types"; + /** * Create a line segment from two points. * diff --git a/packages/utils/bbox.ts b/packages/utils/bbox.ts index 19a1a5430..61c75a668 100644 --- a/packages/utils/bbox.ts +++ b/packages/utils/bbox.ts @@ -4,6 +4,7 @@ import { type GlobalPoint, type LocalPoint, } from "@excalidraw/math"; + import type { Bounds } from "@excalidraw/excalidraw/element/bounds"; export type LineSegment

= [P, P]; diff --git a/packages/utils/collision.test.ts b/packages/utils/collision.test.ts index 744bea367..24f96e985 100644 --- a/packages/utils/collision.test.ts +++ b/packages/utils/collision.test.ts @@ -1,4 +1,3 @@ -import type { Curve, Degrees, GlobalPoint } from "@excalidraw/math"; import { curve, degreesToRadians, @@ -7,7 +6,11 @@ import { pointFrom, pointRotateDegs, } from "@excalidraw/math"; + +import type { Curve, Degrees, GlobalPoint } from "@excalidraw/math"; + import { pointOnCurve, pointOnPolyline } from "./collision"; + import type { Polyline } from "./geometry/shape"; describe("point and curve", () => { diff --git a/packages/utils/collision.ts b/packages/utils/collision.ts index fb48f4e45..f90019418 100644 --- a/packages/utils/collision.ts +++ b/packages/utils/collision.ts @@ -1,10 +1,3 @@ -import type { Polycurve, Polyline } from "./geometry/shape"; -import { - pointInEllipse, - pointOnEllipse, - type GeometricShape, -} from "./geometry/shape"; -import type { Curve } from "@excalidraw/math"; import { lineSegment, pointFrom, @@ -17,6 +10,16 @@ import { type Polygon, } from "@excalidraw/math"; +import type { Curve } from "@excalidraw/math"; + +import { + pointInEllipse, + pointOnEllipse, + type GeometricShape, +} from "./geometry/shape"; + +import type { Polycurve, Polyline } from "./geometry/shape"; + // check if the given point is considered on the given shape's border export const isPointOnShape = ( point: Point, diff --git a/packages/utils/export.test.ts b/packages/utils/export.test.ts index e2af763b5..86bbe80b7 100644 --- a/packages/utils/export.test.ts +++ b/packages/utils/export.test.ts @@ -1,9 +1,9 @@ -import * as utils from "."; +import { MIME_TYPES } from "@excalidraw/excalidraw/constants"; +import * as mockedSceneExportUtils from "@excalidraw/excalidraw/scene/export"; import { diagramFactory } from "@excalidraw/excalidraw/tests/fixtures/diagramFixture"; import { vi } from "vitest"; -import * as mockedSceneExportUtils from "@excalidraw/excalidraw/scene/export"; -import { MIME_TYPES } from "@excalidraw/excalidraw/constants"; +import * as utils from "."; const exportToSvgSpy = vi.spyOn(mockedSceneExportUtils, "exportToSvg"); diff --git a/packages/utils/export.ts b/packages/utils/export.ts index 22287ce7e..6de25c62b 100644 --- a/packages/utils/export.ts +++ b/packages/utils/export.ts @@ -1,23 +1,24 @@ -import { - exportToCanvas as _exportToCanvas, - exportToSvg as _exportToSvg, -} from "@excalidraw/excalidraw/scene/export"; import { getDefaultAppState } from "@excalidraw/excalidraw/appState"; -import type { AppState, BinaryFiles } from "@excalidraw/excalidraw/types"; -import type { - ExcalidrawElement, - ExcalidrawFrameLikeElement, - NonDeleted, -} from "@excalidraw/excalidraw/element/types"; -import { restore } from "@excalidraw/excalidraw/data/restore"; -import { MIME_TYPES } from "@excalidraw/excalidraw/constants"; -import { encodePngMetadata } from "@excalidraw/excalidraw/data/image"; -import { serializeAsJSON } from "@excalidraw/excalidraw/data/json"; import { copyBlobToClipboardAsPng, copyTextToSystemClipboard, copyToClipboard, } from "@excalidraw/excalidraw/clipboard"; +import { MIME_TYPES } from "@excalidraw/excalidraw/constants"; +import { encodePngMetadata } from "@excalidraw/excalidraw/data/image"; +import { serializeAsJSON } from "@excalidraw/excalidraw/data/json"; +import { restore } from "@excalidraw/excalidraw/data/restore"; +import { + exportToCanvas as _exportToCanvas, + exportToSvg as _exportToSvg, +} from "@excalidraw/excalidraw/scene/export"; + +import type { + ExcalidrawElement, + ExcalidrawFrameLikeElement, + NonDeleted, +} from "@excalidraw/excalidraw/element/types"; +import type { AppState, BinaryFiles } from "@excalidraw/excalidraw/types"; export { MIME_TYPES }; diff --git a/packages/utils/geometry/geometry.test.ts b/packages/utils/geometry/geometry.test.ts index e9ad067fe..6ddab71b0 100644 --- a/packages/utils/geometry/geometry.test.ts +++ b/packages/utils/geometry/geometry.test.ts @@ -1,9 +1,3 @@ -import type { - GlobalPoint, - LineSegment, - Polygon, - Radians, -} from "@excalidraw/math"; import { pointFrom, lineSegment, @@ -13,6 +7,14 @@ import { polygonIncludesPoint, segmentsIntersectAt, } from "@excalidraw/math"; + +import type { + GlobalPoint, + LineSegment, + Polygon, + Radians, +} from "@excalidraw/math"; + import { pointInEllipse, pointOnEllipse, type Ellipse } from "./shape"; describe("point and line", () => { diff --git a/packages/utils/geometry/shape.ts b/packages/utils/geometry/shape.ts index 10fc06e31..ea3cde3f6 100644 --- a/packages/utils/geometry/shape.ts +++ b/packages/utils/geometry/shape.ts @@ -12,7 +12,8 @@ * to pure shapes */ -import type { Curve, LineSegment, Polygon, Radians } from "@excalidraw/math"; +import { getElementAbsoluteCoords } from "@excalidraw/excalidraw/element"; +import { invariant } from "@excalidraw/excalidraw/utils"; import { curve, lineSegment, @@ -32,7 +33,8 @@ import { type GlobalPoint, type LocalPoint, } from "@excalidraw/math"; -import { getElementAbsoluteCoords } from "@excalidraw/excalidraw/element"; +import { pointsOnBezierCurves } from "points-on-curve"; + import type { ElementsMap, ExcalidrawBindableElement, @@ -49,9 +51,9 @@ import type { ExcalidrawSelectionElement, ExcalidrawTextElement, } from "@excalidraw/excalidraw/element/types"; -import { pointsOnBezierCurves } from "points-on-curve"; +import type { Curve, LineSegment, Polygon, Radians } from "@excalidraw/math"; + import type { Drawable, Op } from "roughjs/bin/core"; -import { invariant } from "@excalidraw/excalidraw/utils"; // a polyline (made up term here) is a line consisting of other line segments // this corresponds to a straight line element in the editor but it could also diff --git a/packages/utils/utils.unmocked.test.ts b/packages/utils/utils.unmocked.test.ts index 341adef95..417eabda1 100644 --- a/packages/utils/utils.unmocked.test.ts +++ b/packages/utils/utils.unmocked.test.ts @@ -1,8 +1,10 @@ -import type { ImportedDataState } from "@excalidraw/excalidraw/data/types"; -import * as utils from "./index"; -import { API } from "@excalidraw/excalidraw/tests/helpers/api"; -import { decodeSvgBase64Payload } from "@excalidraw/excalidraw/scene/export"; import { decodePngMetadata } from "@excalidraw/excalidraw/data/image"; +import { decodeSvgBase64Payload } from "@excalidraw/excalidraw/scene/export"; +import { API } from "@excalidraw/excalidraw/tests/helpers/api"; + +import type { ImportedDataState } from "@excalidraw/excalidraw/data/types"; + +import * as utils from "./index"; // NOTE this test file is using the actual API, unmocked. Hence splitting it // from the other test file, because I couldn't figure out how to test diff --git a/packages/utils/withinBounds.test.ts b/packages/utils/withinBounds.test.ts index 85354d742..b07d0bc33 100644 --- a/packages/utils/withinBounds.test.ts +++ b/packages/utils/withinBounds.test.ts @@ -1,5 +1,7 @@ -import type { Bounds } from "@excalidraw/excalidraw/element/bounds"; import { API } from "@excalidraw/excalidraw/tests/helpers/api"; + +import type { Bounds } from "@excalidraw/excalidraw/element/bounds"; + import { elementPartiallyOverlapsWithOrContainsBBox, elementsOverlappingBBox, diff --git a/packages/utils/withinBounds.ts b/packages/utils/withinBounds.ts index 8d52eb3ad..71bc78969 100644 --- a/packages/utils/withinBounds.ts +++ b/packages/utils/withinBounds.ts @@ -1,9 +1,4 @@ -import type { - ExcalidrawElement, - ExcalidrawFreeDrawElement, - ExcalidrawLinearElement, - NonDeletedExcalidrawElement, -} from "@excalidraw/excalidraw/element/types"; +import { getElementBounds } from "@excalidraw/excalidraw/element/bounds"; import { isArrowElement, isExcalidrawElement, @@ -11,10 +6,7 @@ import { isLinearElement, isTextElement, } from "@excalidraw/excalidraw/element/typeChecks"; -import type { Bounds } from "@excalidraw/excalidraw/element/bounds"; -import { getElementBounds } from "@excalidraw/excalidraw/element/bounds"; import { arrayToMap } from "@excalidraw/excalidraw/utils"; -import type { LocalPoint } from "@excalidraw/math"; import { rangeIncludesValue, pointFrom, @@ -22,6 +14,15 @@ import { rangeInclusive, } from "@excalidraw/math"; +import type { Bounds } from "@excalidraw/excalidraw/element/bounds"; +import type { + ExcalidrawElement, + ExcalidrawFreeDrawElement, + ExcalidrawLinearElement, + NonDeletedExcalidrawElement, +} from "@excalidraw/excalidraw/element/types"; +import type { LocalPoint } from "@excalidraw/math"; + type Element = NonDeletedExcalidrawElement; type Elements = readonly NonDeletedExcalidrawElement[]; diff --git a/scripts/autorelease.js b/scripts/autorelease.js index f506cf13c..6ca5af213 100644 --- a/scripts/autorelease.js +++ b/scripts/autorelease.js @@ -1,5 +1,6 @@ -const fs = require("fs"); const { exec, execSync } = require("child_process"); +const fs = require("fs"); + const core = require("@actions/core"); const excalidrawDir = `${__dirname}/../packages/excalidraw`; diff --git a/scripts/buildMath.js b/scripts/buildMath.js index fd2f820ea..ba421b48c 100644 --- a/scripts/buildMath.js +++ b/scripts/buildMath.js @@ -1,4 +1,5 @@ const path = require("path"); + const { build } = require("esbuild"); const { sassPlugin } = require("esbuild-sass-plugin"); diff --git a/scripts/buildPackage.js b/scripts/buildPackage.js index 653af50f2..3dd15eeca 100644 --- a/scripts/buildPackage.js +++ b/scripts/buildPackage.js @@ -1,6 +1,8 @@ const path = require("path"); + const { build } = require("esbuild"); const { sassPlugin } = require("esbuild-sass-plugin"); + const { parseEnvVariables } = require("../packages/excalidraw/env.cjs"); const ENV_VARS = { diff --git a/scripts/buildUtils.js b/scripts/buildUtils.js index 65b947385..b5718f57e 100644 --- a/scripts/buildUtils.js +++ b/scripts/buildUtils.js @@ -1,6 +1,8 @@ const path = require("path"); + const { build } = require("esbuild"); const { sassPlugin } = require("esbuild-sass-plugin"); + const { woff2ServerPlugin } = require("./woff2/woff2-esbuild-plugins"); // contains all dependencies bundled inside diff --git a/scripts/prerelease.js b/scripts/prerelease.js index 3b8080d33..005a6cf5f 100644 --- a/scripts/prerelease.js +++ b/scripts/prerelease.js @@ -1,5 +1,6 @@ const fs = require("fs"); const util = require("util"); + const exec = util.promisify(require("child_process").exec); const updateChangelog = require("./updateChangelog"); diff --git a/scripts/woff2/woff2-esbuild-plugins.js b/scripts/woff2/woff2-esbuild-plugins.js index 19ebafc93..2116dd059 100644 --- a/scripts/woff2/woff2-esbuild-plugins.js +++ b/scripts/woff2/woff2-esbuild-plugins.js @@ -1,9 +1,10 @@ +const { execSync } = require("child_process"); const fs = require("fs"); const path = require("path"); -const { execSync } = require("child_process"); -const which = require("which"); -const wawoff = require("wawoff2"); + const { Font } = require("fonteditor-core"); +const wawoff = require("wawoff2"); +const which = require("which"); /** * Custom esbuild plugin to: diff --git a/setupTests.ts b/setupTests.ts index 20d2cc517..2aec616b4 100644 --- a/setupTests.ts +++ b/setupTests.ts @@ -1,11 +1,13 @@ +import fs from "fs"; + // vitest.setup.ts import "vitest-canvas-mock"; import "@testing-library/jest-dom"; -import fs from "fs"; import { vi } from "vitest"; + import polyfill from "./packages/excalidraw/polyfill"; -import { testPolyfills } from "./packages/excalidraw/tests/helpers/polyfills"; import { yellow } from "./packages/excalidraw/tests/helpers/colorize"; +import { testPolyfills } from "./packages/excalidraw/tests/helpers/polyfills"; // mock for pep.js not working with setPointerCapture() HTMLElement.prototype.setPointerCapture = vi.fn(); diff --git a/yarn.lock b/yarn.lock index 23e087849..ccd0827fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5354,7 +5354,7 @@ eslint-plugin-flowtype@^8.0.3: lodash "^4.17.21" string-natural-compare "^3.0.1" -eslint-plugin-import@^2.25.3: +eslint-plugin-import@2.31.0, eslint-plugin-import@^2.25.3: version "2.31.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== From 30983d801a12b658d4f6b3ee4bd7471186291c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Tolm=C3=A1cs?= Date: Sat, 15 Mar 2025 12:31:25 +0100 Subject: [PATCH 03/12] fix: Arrow conversion regression (#9241) Signed-off-by: Mark Tolmacs --- .../excalidraw/actions/actionProperties.tsx | 91 +++++++++----- packages/excalidraw/element/binding.ts | 116 ++++++++---------- packages/excalidraw/element/elbowArrow.ts | 24 ++-- .../tests/__snapshots__/history.test.tsx.snap | 8 +- 4 files changed, 129 insertions(+), 110 deletions(-) diff --git a/packages/excalidraw/actions/actionProperties.tsx b/packages/excalidraw/actions/actionProperties.tsx index 1e24d9bed..5a7590667 100644 --- a/packages/excalidraw/actions/actionProperties.tsx +++ b/packages/excalidraw/actions/actionProperties.tsx @@ -111,6 +111,8 @@ import { tupleToCoors, } from "../utils"; +import { updateElbowArrowPoints } from "../element/elbowArrow"; + import { register } from "./register"; import type { @@ -1572,7 +1574,7 @@ export const actionChangeArrowType = register({ if (!isArrowElement(el)) { return el; } - const newElement = newElementWith(el, { + let newElement = newElementWith(el, { roundness: value === ARROW_TYPE.round ? { @@ -1587,6 +1589,8 @@ export const actionChangeArrowType = register({ }); if (isElbowArrow(newElement)) { + newElement.fixedSegments = null; + const elementsMap = app.scene.getNonDeletedElementsMap(); app.dismissLinearEditor(); @@ -1661,46 +1665,71 @@ export const actionChangeArrowType = register({ endHoveredElement && bindLinearElement(newElement, endHoveredElement, "end", elementsMap); - mutateElement(newElement, { - points: [finalStartPoint, finalEndPoint].map( - (p): LocalPoint => - pointFrom(p[0] - newElement.x, p[1] - newElement.y), - ), - ...(startElement && newElement.startBinding + const startBinding = + startElement && newElement.startBinding ? { - startBinding: { - // @ts-ignore TS cannot discern check above - ...newElement.startBinding!, - ...calculateFixedPointForElbowArrowBinding( - newElement, - startElement, - "start", - elementsMap, - ), - }, + // @ts-ignore TS cannot discern check above + ...newElement.startBinding!, + ...calculateFixedPointForElbowArrowBinding( + newElement, + startElement, + "start", + elementsMap, + ), } - : {}), - ...(endElement && newElement.endBinding + : null; + const endBinding = + endElement && newElement.endBinding ? { - endBinding: { - // @ts-ignore TS cannot discern check above - ...newElement.endBinding, - ...calculateFixedPointForElbowArrowBinding( - newElement, - endElement, - "end", - elementsMap, - ), - }, + // @ts-ignore TS cannot discern check above + ...newElement.endBinding, + ...calculateFixedPointForElbowArrowBinding( + newElement, + endElement, + "end", + elementsMap, + ), } - : {}), - }); + : null; + + newElement = { + ...newElement, + startBinding, + endBinding, + ...updateElbowArrowPoints(newElement, elementsMap, { + points: [finalStartPoint, finalEndPoint].map( + (p): LocalPoint => + pointFrom(p[0] - newElement.x, p[1] - newElement.y), + ), + startBinding, + endBinding, + fixedSegments: null, + }), + }; LinearElementEditor.updateEditorMidPointsCache( newElement, elementsMap, app.state, ); + } else { + const elementsMap = app.scene.getNonDeletedElementsMap(); + if (newElement.startBinding) { + const startElement = elementsMap.get( + newElement.startBinding.elementId, + ) as ExcalidrawBindableElement; + if (startElement) { + bindLinearElement(newElement, startElement, "start", elementsMap); + } + } + if (newElement.endBinding) { + const endElement = elementsMap.get( + newElement.endBinding.elementId, + ) as ExcalidrawBindableElement; + if (endElement) { + bindLinearElement(newElement, endElement, "end", elementsMap); + } + } } return newElement; diff --git a/packages/excalidraw/element/binding.ts b/packages/excalidraw/element/binding.ts index fa472d211..93c5292f1 100644 --- a/packages/excalidraw/element/binding.ts +++ b/packages/excalidraw/element/binding.ts @@ -487,32 +487,31 @@ export const bindLinearElement = ( return; } - const binding: PointBinding | FixedPointBinding = { + let binding: PointBinding | FixedPointBinding = { elementId: hoveredElement.id, - ...(isElbowArrow(linearElement) - ? { - ...calculateFixedPointForElbowArrowBinding( - linearElement, - hoveredElement, - startOrEnd, - elementsMap, - ), - focus: 0, - gap: 0, - } - : { - ...normalizePointBinding( - calculateFocusAndGap( - linearElement, - hoveredElement, - startOrEnd, - elementsMap, - ), - hoveredElement, - ), - }), + ...normalizePointBinding( + calculateFocusAndGap( + linearElement, + hoveredElement, + startOrEnd, + elementsMap, + ), + hoveredElement, + ), }; + if (isElbowArrow(linearElement)) { + binding = { + ...binding, + ...calculateFixedPointForElbowArrowBinding( + linearElement, + hoveredElement, + startOrEnd, + elementsMap, + ), + }; + } + mutateElement(linearElement, { [startOrEnd === "start" ? "startBinding" : "endBinding"]: binding, }); @@ -1272,39 +1271,35 @@ const updateBoundPoint = ( pointDistance(adjacentPoint, edgePointAbsolute) + pointDistance(adjacentPoint, center) + Math.max(bindableElement.width, bindableElement.height) * 2; - const intersections = intersectElementWithLineSegment( - bindableElement, - lineSegment( - adjacentPoint, - pointFromVector( - vectorScale( - vectorNormalize(vectorFromPoint(focusPointAbsolute, adjacentPoint)), - interceptorLength, - ), + const intersections = [ + ...intersectElementWithLineSegment( + bindableElement, + lineSegment( adjacentPoint, + pointFromVector( + vectorScale( + vectorNormalize( + vectorFromPoint(focusPointAbsolute, adjacentPoint), + ), + interceptorLength, + ), + adjacentPoint, + ), ), + binding.gap, + ).sort( + (g, h) => + pointDistanceSq(g, adjacentPoint) - pointDistanceSq(h, adjacentPoint), ), - binding.gap, - ).sort( - (g, h) => - pointDistanceSq(g, adjacentPoint) - pointDistanceSq(h, adjacentPoint), - ); - - // debugClear(); - // debugDrawPoint(intersections[0], { color: "red", permanent: true }); - // debugDrawLine( - // lineSegment( - // adjacentPoint, - // pointFromVector( - // vectorScale( - // vectorNormalize(vectorFromPoint(focusPointAbsolute, adjacentPoint)), - // interceptorLength, - // ), - // adjacentPoint, - // ), - // ), - // { permanent: true, color: "green" }, - // ); + // Fallback when arrow doesn't point to the shape + pointFromVector( + vectorScale( + vectorNormalize(vectorFromPoint(focusPointAbsolute, adjacentPoint)), + pointDistance(adjacentPoint, edgePointAbsolute), + ), + adjacentPoint, + ), + ]; if (intersections.length > 1) { // The adjacent point is outside the shape (+ gap) @@ -1727,21 +1722,6 @@ const determineFocusDistance = ( ) .sort((g, h) => Math.abs(g) - Math.abs(h)); - // debugClear(); - // [ - // lineSegmentIntersectionPoints(rotatedInterceptor, interceptees[0]), - // lineSegmentIntersectionPoints(rotatedInterceptor, interceptees[1]), - // ] - // .filter((p): p is GlobalPoint => p !== null) - // .forEach((p) => debugDrawPoint(p, { color: "black", permanent: true })); - // debugDrawPoint(determineFocusPoint(element, ordered[0] ?? 0, rotatedA), { - // color: "red", - // permanent: true, - // }); - // debugDrawLine(rotatedInterceptor, { color: "green", permanent: true }); - // debugDrawLine(interceptees[0], { color: "red", permanent: true }); - // debugDrawLine(interceptees[1], { color: "red", permanent: true }); - const signedDistanceRatio = ordered[0] ?? 0; return signedDistanceRatio; diff --git a/packages/excalidraw/element/elbowArrow.ts b/packages/excalidraw/element/elbowArrow.ts index 5b8dc3813..5e44b6ea6 100644 --- a/packages/excalidraw/element/elbowArrow.ts +++ b/packages/excalidraw/element/elbowArrow.ts @@ -998,23 +998,32 @@ export const updateElbowArrowPoints = ( // 0. During all element replacement in the scene, we just need to renormalize // the arrow // TODO (dwelle,mtolmacs): Remove this once Scene.getScene() is removed + const { + startBinding: updatedStartBinding, + endBinding: updatedEndBinding, + ...restOfTheUpdates + } = updates; const startBinding = - typeof updates.startBinding !== "undefined" - ? updates.startBinding + typeof updatedStartBinding !== "undefined" + ? updatedStartBinding : arrow.startBinding; const endBinding = - typeof updates.endBinding !== "undefined" - ? updates.endBinding + typeof updatedEndBinding !== "undefined" + ? updatedEndBinding : arrow.endBinding; const startElement = startBinding && getBindableElementForId(startBinding.elementId, elementsMap); const endElement = endBinding && getBindableElementForId(endBinding.elementId, elementsMap); + if ( + (startBinding && !startElement) || + (endBinding && !endElement) || (elementsMap.size === 0 && validateElbowPoints(updatedPoints)) || - startElement?.id !== startBinding?.elementId || - endElement?.id !== endBinding?.elementId + (Object.keys(restOfTheUpdates).length === 0 && + (startElement?.id !== startBinding?.elementId || + endElement?.id !== endBinding?.elementId)) ) { return normalizeArrowElementUpdate( updatedPoints.map((p) => @@ -1074,7 +1083,8 @@ export const updateElbowArrowPoints = ( p, arrow.points[i] ?? pointFrom(Infinity, Infinity), ), - ) + ) && + validateElbowPoints(updatedPoints) ) { return {}; } diff --git a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap index 165c135fe..d740e975c 100644 --- a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap @@ -818,8 +818,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", "updated": 1, "version": 30, - "width": 50, - "x": 200, + "width": 0, + "x": "149.29289", "y": 0, } `; @@ -852,7 +852,7 @@ History { 0, ], [ - 50, + 0, 0, ], ], @@ -937,7 +937,7 @@ History { 0, ], [ - 50, + 0, 0, ], ], From 99d8bff1751b0bcfaacd1b0f32689df63ab42f86 Mon Sep 17 00:00:00 2001 From: David Luzar <5153846+dwelle@users.noreply.github.com> Date: Sat, 15 Mar 2025 20:05:42 +0100 Subject: [PATCH 04/12] fix: elements offset incorrectly when action-duplicated during drag (#9275) * fix: elements offset incorrectly when action-duplicated during drag * prevent duplicate action during drag altogether --- .../actions/actionDuplicateSelection.tsx | 4 ++++ packages/excalidraw/element/dragElements.ts | 21 ++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/excalidraw/actions/actionDuplicateSelection.tsx b/packages/excalidraw/actions/actionDuplicateSelection.tsx index ce225a1d6..0125f0288 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.tsx @@ -52,6 +52,10 @@ export const actionDuplicateSelection = register({ 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.editingLinearElement) { // TODO: Invariants should be checked here instead of duplicateSelectedPoints() diff --git a/packages/excalidraw/element/dragElements.ts b/packages/excalidraw/element/dragElements.ts index 4cc408b21..e3a6a48b3 100644 --- a/packages/excalidraw/element/dragElements.ts +++ b/packages/excalidraw/element/dragElements.ts @@ -17,7 +17,7 @@ import { } from "./typeChecks"; import type { Bounds } from "./bounds"; -import type { NonDeletedExcalidrawElement } from "./types"; +import type { ExcalidrawElement, NonDeletedExcalidrawElement } from "./types"; import type Scene from "../scene/Scene"; import type { AppState, @@ -78,13 +78,20 @@ export const dragSelectedElements = ( } } - const commonBounds = getCommonBounds( - Array.from(elementsToUpdate).map( - (el) => pointerDownState.originalElements.get(el.id) ?? el, - ), - ); + const origElements: ExcalidrawElement[] = []; + + for (const element of elementsToUpdate) { + const origElement = pointerDownState.originalElements.get(element.id); + // if original element is not set (e.g. when you duplicate during a drag + // operation), exit to avoid undefined behavior + if (!origElement) { + return; + } + origElements.push(origElement); + } + const adjustedOffset = calculateOffset( - commonBounds, + getCommonBounds(origElements), offset, snapOffset, gridSize, From 58990b41ae3f2339e5bb2616f870d79e3f8d6463 Mon Sep 17 00:00:00 2001 From: WalterMitty Date: Sat, 22 Mar 2025 17:06:23 +0800 Subject: [PATCH 05/12] fix: 'Rotate' spell error (#9288) --- packages/math/point.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/math/point.ts b/packages/math/point.ts index f6234c8ea..b6054a10a 100644 --- a/packages/math/point.ts +++ b/packages/math/point.ts @@ -97,7 +97,7 @@ export function pointsEqual( } /** - * Roate a point by [angle] radians. + * Rotate a point by [angle] radians. * * @param point The point to rotate * @param center The point to rotate around, the center point @@ -116,7 +116,7 @@ export function pointRotateRads( } /** - * Roate a point by [angle] degree. + * Rotate a point by [angle] degree. * * @param point The point to rotate * @param center The point to rotate around, the center point From 77aca48c8437b5bcc356b144c9677616ca80c7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Tolm=C3=A1cs?= Date: Sun, 23 Mar 2025 18:39:33 +0100 Subject: [PATCH 06/12] fix: Refactor and merge duplication and binding (#9246) --- .../excalidraw/actions/actionAddToLibrary.ts | 2 +- .../actions/actionDeleteSelected.tsx | 1 + .../actions/actionDuplicateSelection.test.tsx | 2 +- .../actions/actionDuplicateSelection.tsx | 349 +++---------- packages/excalidraw/actions/actionFlip.ts | 3 +- packages/excalidraw/clipboard.ts | 3 +- packages/excalidraw/components/App.tsx | 238 +++------ .../components/LibraryMenuItems.tsx | 10 +- .../excalidraw/components/Stats/DragInput.tsx | 3 +- packages/excalidraw/element/binding.ts | 336 ++++++------ ...{newElement.test.ts => duplicate.test.tsx} | 345 ++++++++++++- packages/excalidraw/element/duplicate.ts | 484 ++++++++++++++++++ .../excalidraw/element/elbowArrow.test.tsx | 6 +- packages/excalidraw/element/elbowArrow.ts | 81 +-- packages/excalidraw/element/flowchart.ts | 4 +- packages/excalidraw/element/heading.ts | 8 +- packages/excalidraw/element/index.ts | 2 +- .../excalidraw/element/linearElementEditor.ts | 93 +++- packages/excalidraw/element/newElement.ts | 272 +--------- packages/excalidraw/element/textElement.ts | 44 +- packages/excalidraw/groups.ts | 5 +- .../excalidraw/renderer/interactiveScene.ts | 37 +- packages/excalidraw/store.ts | 3 +- .../__snapshots__/contextmenu.test.tsx.snap | 4 +- .../tests/__snapshots__/history.test.tsx.snap | 10 +- .../tests/__snapshots__/move.test.tsx.snap | 10 +- .../regressionTests.test.tsx.snap | 16 +- .../excalidraw/tests/fractionalIndex.test.ts | 3 +- .../excalidraw/tests/regressionTests.test.tsx | 4 + 29 files changed, 1293 insertions(+), 1085 deletions(-) rename packages/excalidraw/element/{newElement.test.ts => duplicate.test.tsx} (52%) create mode 100644 packages/excalidraw/element/duplicate.ts diff --git a/packages/excalidraw/actions/actionAddToLibrary.ts b/packages/excalidraw/actions/actionAddToLibrary.ts index b246918a0..b79353308 100644 --- a/packages/excalidraw/actions/actionAddToLibrary.ts +++ b/packages/excalidraw/actions/actionAddToLibrary.ts @@ -1,5 +1,5 @@ import { LIBRARY_DISABLED_TYPES } from "../constants"; -import { deepCopyElement } from "../element/newElement"; +import { deepCopyElement } from "../element/duplicate"; import { t } from "../i18n"; import { randomId } from "../random"; import { CaptureUpdateAction } from "../store"; diff --git a/packages/excalidraw/actions/actionDeleteSelected.tsx b/packages/excalidraw/actions/actionDeleteSelected.tsx index fe3f8c5d7..7e5ed919c 100644 --- a/packages/excalidraw/actions/actionDeleteSelected.tsx +++ b/packages/excalidraw/actions/actionDeleteSelected.tsx @@ -288,6 +288,7 @@ export const actionDeleteSelected = register({ activeTool: updateActiveTool(appState, { type: "selection" }), multiElement: null, activeEmbeddable: null, + selectedLinearElement: null, }, captureUpdate: isSomeElementSelected( getNonDeletedElements(elements), diff --git a/packages/excalidraw/actions/actionDuplicateSelection.test.tsx b/packages/excalidraw/actions/actionDuplicateSelection.test.tsx index 2c1d44e92..bbaae2ed6 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.test.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.test.tsx @@ -258,7 +258,7 @@ describe("actionDuplicateSelection", () => { assertElements(h.elements, [ { id: frame.id }, { id: text.id, frameId: frame.id }, - { [ORIG_ID]: text.id, frameId: frame.id }, + { [ORIG_ID]: text.id, frameId: frame.id, selected: true }, ]); }); diff --git a/packages/excalidraw/actions/actionDuplicateSelection.tsx b/packages/excalidraw/actions/actionDuplicateSelection.tsx index 0125f0288..a6dd9d2e1 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.tsx @@ -1,29 +1,10 @@ import { ToolButton } from "../components/ToolButton"; import { DuplicateIcon } from "../components/icons"; import { DEFAULT_GRID_SIZE } from "../constants"; -import { duplicateElement, getNonDeletedElements } from "../element"; -import { fixBindingsAfterDuplication } from "../element/binding"; -import { - bindTextToShapeAfterDuplication, - getBoundTextElement, - getContainerElement, -} from "../element/textElement"; -import { - hasBoundTextElement, - isBoundToContainer, - isFrameLikeElement, -} from "../element/typeChecks"; -import { normalizeElementOrder } from "../element/sortElements"; +import { getNonDeletedElements } from "../element"; +import { isBoundToContainer, isLinearElement } from "../element/typeChecks"; import { LinearElementEditor } from "../element/linearElementEditor"; -import { - bindElementsToFramesAfterDuplication, - getFrameChildren, -} from "../frame"; -import { - selectGroupsForSelectedElements, - getSelectedGroupForElement, - getElementsInGroup, -} from "../groups"; +import { selectGroupsForSelectedElements } from "../groups"; import { t } from "../i18n"; import { KEYS } from "../keys"; import { isSomeElementSelected } from "../scene"; @@ -32,19 +13,15 @@ import { getSelectedElements, } from "../scene/selection"; import { CaptureUpdateAction } from "../store"; -import { - arrayToMap, - castArray, - findLastIndex, - getShortcutKey, - invariant, -} from "../utils"; +import { arrayToMap, getShortcutKey } from "../utils"; + +import { syncMovedIndices } from "../fractionalIndex"; + +import { duplicateElements } from "../element/duplicate"; import { register } from "./register"; -import type { ActionResult } from "./types"; import type { ExcalidrawElement } from "../element/types"; -import type { AppState } from "../types"; export const actionDuplicateSelection = register({ name: "duplicateSelection", @@ -75,20 +52,54 @@ export const actionDuplicateSelection = register({ } } - const nextState = duplicateElements(elements, appState); - - if (app.props.onDuplicate && nextState.elements) { - const mappedElements = app.props.onDuplicate( - nextState.elements, + let { newElements: duplicatedElements, elementsWithClones: nextElements } = + duplicateElements({ + type: "in-place", elements, - ); + idsOfElementsToDuplicate: arrayToMap( + getSelectedElements(elements, appState, { + includeBoundTextElement: true, + includeElementsInFrames: true, + }), + ), + appState, + randomizeSeed: true, + overrides: (element) => ({ + x: element.x + DEFAULT_GRID_SIZE / 2, + y: element.y + DEFAULT_GRID_SIZE / 2, + }), + reverseOrder: false, + }); + + if (app.props.onDuplicate && nextElements) { + const mappedElements = app.props.onDuplicate(nextElements, elements); if (mappedElements) { - nextState.elements = mappedElements; + nextElements = mappedElements; } } return { - ...nextState, + elements: syncMovedIndices(nextElements, arrayToMap(duplicatedElements)), + appState: { + ...appState, + ...updateLinearElementEditors(duplicatedElements), + ...selectGroupsForSelectedElements( + { + editingGroupId: appState.editingGroupId, + selectedElementIds: excludeElementsInFramesFromSelection( + duplicatedElements, + ).reduce((acc: Record, element) => { + if (!isBoundToContainer(element)) { + acc[element.id] = true; + } + return acc; + }, {}), + }, + getNonDeletedElements(nextElements), + appState, + null, + ), + }, captureUpdate: CaptureUpdateAction.IMMEDIATELY, }; }, @@ -107,259 +118,23 @@ export const actionDuplicateSelection = register({ ), }); -const duplicateElements = ( - elements: readonly ExcalidrawElement[], - appState: AppState, -): Partial> => { - // --------------------------------------------------------------------------- - - const groupIdMap = new Map(); - const newElements: ExcalidrawElement[] = []; - const oldElements: ExcalidrawElement[] = []; - const oldIdToDuplicatedId = new Map(); - const duplicatedElementsMap = new Map(); - - const elementsMap = arrayToMap(elements); - - const duplicateAndOffsetElement = < - T extends ExcalidrawElement | ExcalidrawElement[], - >( - element: T, - ): T extends ExcalidrawElement[] - ? ExcalidrawElement[] - : ExcalidrawElement | null => { - const elements = castArray(element); - - const _newElements = elements.reduce( - (acc: ExcalidrawElement[], element) => { - if (processedIds.has(element.id)) { - return acc; - } - - processedIds.set(element.id, true); - - const newElement = duplicateElement( - appState.editingGroupId, - groupIdMap, - element, - { - x: element.x + DEFAULT_GRID_SIZE / 2, - y: element.y + DEFAULT_GRID_SIZE / 2, - }, - ); - - processedIds.set(newElement.id, true); - - duplicatedElementsMap.set(newElement.id, newElement); - oldIdToDuplicatedId.set(element.id, newElement.id); - - oldElements.push(element); - newElements.push(newElement); - - acc.push(newElement); - return acc; - }, - [], +const updateLinearElementEditors = (clonedElements: ExcalidrawElement[]) => { + const linears = clonedElements.filter(isLinearElement); + if (linears.length === 1) { + const linear = linears[0]; + const boundElements = linear.boundElements?.map((def) => def.id) ?? []; + const onlySingleLinearSelected = clonedElements.every( + (el) => el.id === linear.id || boundElements.includes(el.id), ); - return ( - Array.isArray(element) ? _newElements : _newElements[0] || null - ) as T extends ExcalidrawElement[] - ? ExcalidrawElement[] - : ExcalidrawElement | null; - }; - - elements = normalizeElementOrder(elements); - - const idsOfElementsToDuplicate = arrayToMap( - getSelectedElements(elements, appState, { - includeBoundTextElement: true, - includeElementsInFrames: true, - }), - ); - - // Ids of elements that have already been processed so we don't push them - // into the array twice if we end up backtracking when retrieving - // discontiguous group of elements (can happen due to a bug, or in edge - // cases such as a group containing deleted elements which were not selected). - // - // This is not enough to prevent duplicates, so we do a second loop afterwards - // to remove them. - // - // For convenience we mark even the newly created ones even though we don't - // loop over them. - const processedIds = new Map(); - - const elementsWithClones: ExcalidrawElement[] = elements.slice(); - - const insertAfterIndex = ( - index: number, - elements: ExcalidrawElement | null | ExcalidrawElement[], - ) => { - invariant(index !== -1, "targetIndex === -1 "); - - if (!Array.isArray(elements) && !elements) { - return; + if (onlySingleLinearSelected) { + return { + selectedLinearElement: new LinearElementEditor(linear), + }; } - - elementsWithClones.splice(index + 1, 0, ...castArray(elements)); - }; - - const frameIdsToDuplicate = new Set( - elements - .filter( - (el) => idsOfElementsToDuplicate.has(el.id) && isFrameLikeElement(el), - ) - .map((el) => el.id), - ); - - for (const element of elements) { - if (processedIds.has(element.id)) { - continue; - } - - if (!idsOfElementsToDuplicate.has(element.id)) { - continue; - } - - // groups - // ------------------------------------------------------------------------- - - const groupId = getSelectedGroupForElement(appState, element); - if (groupId) { - const groupElements = getElementsInGroup(elements, groupId).flatMap( - (element) => - isFrameLikeElement(element) - ? [...getFrameChildren(elements, element.id), element] - : [element], - ); - - const targetIndex = findLastIndex(elementsWithClones, (el) => { - return el.groupIds?.includes(groupId); - }); - - insertAfterIndex(targetIndex, duplicateAndOffsetElement(groupElements)); - continue; - } - - // frame duplication - // ------------------------------------------------------------------------- - - if (element.frameId && frameIdsToDuplicate.has(element.frameId)) { - continue; - } - - if (isFrameLikeElement(element)) { - const frameId = element.id; - - const frameChildren = getFrameChildren(elements, frameId); - - const targetIndex = findLastIndex(elementsWithClones, (el) => { - return el.frameId === frameId || el.id === frameId; - }); - - insertAfterIndex( - targetIndex, - duplicateAndOffsetElement([...frameChildren, element]), - ); - continue; - } - - // text container - // ------------------------------------------------------------------------- - - if (hasBoundTextElement(element)) { - const boundTextElement = getBoundTextElement(element, elementsMap); - - const targetIndex = findLastIndex(elementsWithClones, (el) => { - return ( - el.id === element.id || - ("containerId" in el && el.containerId === element.id) - ); - }); - - if (boundTextElement) { - insertAfterIndex( - targetIndex, - duplicateAndOffsetElement([element, boundTextElement]), - ); - } else { - insertAfterIndex(targetIndex, duplicateAndOffsetElement(element)); - } - - continue; - } - - if (isBoundToContainer(element)) { - const container = getContainerElement(element, elementsMap); - - const targetIndex = findLastIndex(elementsWithClones, (el) => { - return el.id === element.id || el.id === container?.id; - }); - - if (container) { - insertAfterIndex( - targetIndex, - duplicateAndOffsetElement([container, element]), - ); - } else { - insertAfterIndex(targetIndex, duplicateAndOffsetElement(element)); - } - - continue; - } - - // default duplication (regular elements) - // ------------------------------------------------------------------------- - - insertAfterIndex( - findLastIndex(elementsWithClones, (el) => el.id === element.id), - duplicateAndOffsetElement(element), - ); } - // --------------------------------------------------------------------------- - - bindTextToShapeAfterDuplication( - elementsWithClones, - oldElements, - oldIdToDuplicatedId, - ); - fixBindingsAfterDuplication( - elementsWithClones, - oldElements, - oldIdToDuplicatedId, - ); - bindElementsToFramesAfterDuplication( - elementsWithClones, - oldElements, - oldIdToDuplicatedId, - ); - - const nextElementsToSelect = - excludeElementsInFramesFromSelection(newElements); - return { - elements: elementsWithClones, - appState: { - ...appState, - ...selectGroupsForSelectedElements( - { - editingGroupId: appState.editingGroupId, - selectedElementIds: nextElementsToSelect.reduce( - (acc: Record, element) => { - if (!isBoundToContainer(element)) { - acc[element.id] = true; - } - return acc; - }, - {}, - ), - }, - getNonDeletedElements(elementsWithClones), - appState, - null, - ), - }, + selectedLinearElement: null, }; }; diff --git a/packages/excalidraw/actions/actionFlip.ts b/packages/excalidraw/actions/actionFlip.ts index bfc7fb721..6c91445a3 100644 --- a/packages/excalidraw/actions/actionFlip.ts +++ b/packages/excalidraw/actions/actionFlip.ts @@ -6,7 +6,6 @@ import { } from "../element/binding"; import { getCommonBoundingBox } from "../element/bounds"; import { mutateElement, newElementWith } from "../element/mutateElement"; -import { deepCopyElement } from "../element/newElement"; import { resizeMultipleElements } from "../element/resizeElements"; import { isArrowElement, @@ -19,6 +18,8 @@ import { getSelectedElements } from "../scene"; import { CaptureUpdateAction } from "../store"; import { arrayToMap } from "../utils"; +import { deepCopyElement } from "../element/duplicate"; + import { register } from "./register"; import type { diff --git a/packages/excalidraw/clipboard.ts b/packages/excalidraw/clipboard.ts index 1b0e9942a..397c350ff 100644 --- a/packages/excalidraw/clipboard.ts +++ b/packages/excalidraw/clipboard.ts @@ -6,7 +6,6 @@ import { } from "./constants"; import { createFile, isSupportedImageFileType } from "./data/blob"; import { mutateElement } from "./element/mutateElement"; -import { deepCopyElement } from "./element/newElement"; import { isFrameLikeElement, isInitializedImageElement, @@ -15,6 +14,8 @@ import { ExcalidrawError } from "./errors"; import { getContainingFrame } from "./frame"; import { arrayToMap, isMemberOf, isPromiseLike } from "./utils"; +import { deepCopyElement } from "./element/duplicate"; + import type { Spreadsheet } from "./charts"; import type { ExcalidrawElement, diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 6017b73b7..8239493ba 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -126,7 +126,6 @@ import { restore, restoreElements } from "../data/restore"; import { dragNewElement, dragSelectedElements, - duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, @@ -152,7 +151,6 @@ import { bindOrUnbindLinearElement, bindOrUnbindLinearElements, fixBindingsAfterDeletion, - fixBindingsAfterDuplication, getHoveredElementForBinding, isBindingEnabled, isLinearElementSimpleAndAlreadyBound, @@ -163,9 +161,8 @@ import { } from "../element/binding"; import { LinearElementEditor } from "../element/linearElementEditor"; import { mutateElement, newElementWith } from "../element/mutateElement"; +import { deepCopyElement, duplicateElements } from "../element/duplicate"; import { - deepCopyElement, - duplicateElements, newFrameElement, newFreeDrawElement, newEmbeddableElement, @@ -292,7 +289,6 @@ import { } from "../element/image"; import { fileOpen } from "../data/filesystem"; import { - bindTextToShapeAfterDuplication, getBoundTextElement, getContainerCenter, getContainerElement, @@ -309,7 +305,6 @@ import { Fonts, getLineHeight } from "../fonts"; import { getFrameChildren, isCursorInFrame, - bindElementsToFramesAfterDuplication, addElementsToFrame, replaceAllElementsInFrame, removeElementsFromFrame, @@ -3224,17 +3219,16 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint(dx, dy, this.getEffectiveGridSize()); - const newElements = duplicateElements( - elements.map((element) => { + const { newElements } = duplicateElements({ + type: "everything", + elements: elements.map((element) => { return newElementWith(element, { x: element.x + gridX - minX, y: element.y + gridY - minY, }); }), - { - randomizeSeed: !opts.retainSeed, - }, - ); + randomizeSeed: !opts.retainSeed, + }); const prevElements = this.scene.getElementsIncludingDeleted(); let nextElements = [...prevElements, ...newElements]; @@ -6095,7 +6089,12 @@ class App extends React.Component { this.setState({ activeEmbeddable: { element: hitElement, state: "hover" }, }); - } else if (!hitElement || !isElbowArrow(hitElement)) { + } else if ( + !hitElement || + // Ebow arrows can only be moved when unconnected + !isElbowArrow(hitElement) || + !(hitElement.startBinding || hitElement.endBinding) + ) { setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE); if (this.state.activeEmbeddable?.state === "hover") { this.setState({ activeEmbeddable: null }); @@ -6288,7 +6287,13 @@ class App extends React.Component { if (isHoveringAPointHandle || segmentMidPointHoveredCoords) { setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER); } else if (this.hitElement(scenePointerX, scenePointerY, element)) { - setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE); + if ( + // Ebow arrows can only be moved when unconnected + !isElbowArrow(element) || + !(element.startBinding || element.endBinding) + ) { + setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE); + } } } else if (this.hitElement(scenePointerX, scenePointerY, element)) { if ( @@ -8138,6 +8143,7 @@ class App extends React.Component { ...this.state.selectedLinearElement, pointerDownState: ret.pointerDownState, selectedPointsIndices: ret.selectedPointsIndices, + segmentMidPointHoveredCoords: null, }, }); } @@ -8147,6 +8153,7 @@ class App extends React.Component { ...this.state.editingLinearElement, pointerDownState: ret.pointerDownState, selectedPointsIndices: ret.selectedPointsIndices, + segmentMidPointHoveredCoords: null, }, }); } @@ -8160,7 +8167,7 @@ class App extends React.Component { return; } - const didDrag = LinearElementEditor.handlePointDragging( + const newLinearElementEditor = LinearElementEditor.handlePointDragging( event, this, pointerCoords.x, @@ -8174,29 +8181,18 @@ class App extends React.Component { linearElementEditor, this.scene, ); - if (didDrag) { + if (newLinearElementEditor) { pointerDownState.lastCoords.x = pointerCoords.x; pointerDownState.lastCoords.y = pointerCoords.y; pointerDownState.drag.hasOccurred = true; - if ( - this.state.editingLinearElement && - !this.state.editingLinearElement.isDragging - ) { - this.setState({ - editingLinearElement: { - ...this.state.editingLinearElement, - isDragging: true, - }, - }); - } - if (!this.state.selectedLinearElement.isDragging) { - this.setState({ - selectedLinearElement: { - ...this.state.selectedLinearElement, - isDragging: true, - }, - }); - } + + this.setState({ + editingLinearElement: this.state.editingLinearElement + ? newLinearElementEditor + : null, + selectedLinearElement: newLinearElementEditor, + }); + return; } } @@ -8422,145 +8418,55 @@ class App extends React.Component { pointerDownState.hit.hasBeenDuplicated = true; - const nextElements = []; - const elementsToAppend = []; - const groupIdMap = new Map(); - const oldIdToDuplicatedId = new Map(); - const hitElement = pointerDownState.hit.element; - const selectedElementIds = new Set( - this.scene - .getSelectedElements({ - selectedElementIds: this.state.selectedElementIds, - includeBoundTextElement: true, - includeElementsInFrames: true, - }) - .map((element) => element.id), - ); - const elements = this.scene.getElementsIncludingDeleted(); - - for (const element of elements) { - const isInSelection = - selectedElementIds.has(element.id) || - // case: the state.selectedElementIds might not have been - // updated yet by the time this mousemove event is fired - (element.id === hitElement?.id && - pointerDownState.hit.wasAddedToSelection); - // NOTE (mtolmacs): This is a temporary fix for very large scenes - if ( - Math.abs(element.x) > 1e7 || - Math.abs(element.x) > 1e7 || - Math.abs(element.width) > 1e7 || - Math.abs(element.height) > 1e7 - ) { - console.error( - `Alt+dragging element in scene with invalid dimensions`, - element.x, - element.y, - element.width, - element.height, - isInSelection, - ); - - return; - } - - if (isInSelection) { - const duplicatedElement = duplicateElement( - this.state.editingGroupId, - groupIdMap, - element, - ); - - // NOTE (mtolmacs): This is a temporary fix for very large scenes - if ( - Math.abs(duplicatedElement.x) > 1e7 || - Math.abs(duplicatedElement.x) > 1e7 || - Math.abs(duplicatedElement.width) > 1e7 || - Math.abs(duplicatedElement.height) > 1e7 - ) { - console.error( - `Alt+dragging duplicated element with invalid dimensions`, - duplicatedElement.x, - duplicatedElement.y, - duplicatedElement.width, - duplicatedElement.height, - ); - - return; - } - - const origElement = pointerDownState.originalElements.get( - element.id, - )!; - - // NOTE (mtolmacs): This is a temporary fix for very large scenes - if ( - Math.abs(origElement.x) > 1e7 || - Math.abs(origElement.x) > 1e7 || - Math.abs(origElement.width) > 1e7 || - Math.abs(origElement.height) > 1e7 - ) { - console.error( - `Alt+dragging duplicated element with invalid dimensions`, - origElement.x, - origElement.y, - origElement.width, - origElement.height, - ); - - return; - } - - mutateElement(duplicatedElement, { - x: origElement.x, - y: origElement.y, - }); - - // put duplicated element to pointerDownState.originalElements - // so that we can snap to the duplicated element without releasing - pointerDownState.originalElements.set( - duplicatedElement.id, - duplicatedElement, - ); - - nextElements.push(duplicatedElement); - elementsToAppend.push(element); - oldIdToDuplicatedId.set(element.id, duplicatedElement.id); - } else { - nextElements.push(element); - } + const hitElement = pointerDownState.hit.element; + const selectedElements = this.scene.getSelectedElements({ + selectedElementIds: this.state.selectedElementIds, + includeBoundTextElement: true, + includeElementsInFrames: true, + }); + if ( + hitElement && + !selectedElements.find((el) => el.id === hitElement.id) + ) { + selectedElements.push(hitElement); } - let nextSceneElements: ExcalidrawElement[] = [ - ...nextElements, - ...elementsToAppend, - ]; + const { newElements: clonedElements, elementsWithClones } = + duplicateElements({ + type: "in-place", + elements, + appState: this.state, + randomizeSeed: true, + idsOfElementsToDuplicate: new Map( + selectedElements.map((el) => [el.id, el]), + ), + overrides: (el) => { + const origEl = pointerDownState.originalElements.get(el.id); + + if (origEl) { + return { + x: origEl.x, + y: origEl.y, + }; + } + + return {}; + }, + reverseOrder: true, + }); + clonedElements.forEach((element) => { + pointerDownState.originalElements.set(element.id, element); + }); const mappedNewSceneElements = this.props.onDuplicate?.( - nextSceneElements, + elementsWithClones, elements, ); - nextSceneElements = mappedNewSceneElements || nextSceneElements; - - syncMovedIndices(nextSceneElements, arrayToMap(elementsToAppend)); - - bindTextToShapeAfterDuplication( - nextElements, - elementsToAppend, - oldIdToDuplicatedId, - ); - fixBindingsAfterDuplication( - nextSceneElements, - elementsToAppend, - oldIdToDuplicatedId, - "duplicatesServeAsOld", - ); - bindElementsToFramesAfterDuplication( - nextSceneElements, - elementsToAppend, - oldIdToDuplicatedId, + const nextSceneElements = syncMovedIndices( + mappedNewSceneElements || elementsWithClones, + arrayToMap(clonedElements), ); this.scene.replaceAllElements(nextSceneElements); diff --git a/packages/excalidraw/components/LibraryMenuItems.tsx b/packages/excalidraw/components/LibraryMenuItems.tsx index af5b9d3e6..3e32021e0 100644 --- a/packages/excalidraw/components/LibraryMenuItems.tsx +++ b/packages/excalidraw/components/LibraryMenuItems.tsx @@ -8,18 +8,20 @@ import React, { import { MIME_TYPES } from "../constants"; import { serializeLibraryAsJSON } from "../data/json"; -import { duplicateElements } from "../element/newElement"; import { useLibraryCache } from "../hooks/useLibraryItemSvg"; import { useScrollPosition } from "../hooks/useScrollPosition"; import { t } from "../i18n"; import { arrayToMap } from "../utils"; +import { duplicateElements } from "../element/duplicate"; + import { LibraryMenuControlButtons } from "./LibraryMenuControlButtons"; import { LibraryDropdownMenu } from "./LibraryMenuHeaderContent"; import { LibraryMenuSection, LibraryMenuSectionGrid, } from "./LibraryMenuSection"; + import Spinner from "./Spinner"; import Stack from "./Stack"; @@ -160,7 +162,11 @@ export default function LibraryMenuItems({ ...item, // duplicate each library item before inserting on canvas to confine // ids and bindings to each library item. See #6465 - elements: duplicateElements(item.elements, { randomizeSeed: true }), + elements: duplicateElements({ + type: "everything", + elements: item.elements, + randomizeSeed: true, + }).newElements, }; }); }, diff --git a/packages/excalidraw/components/Stats/DragInput.tsx b/packages/excalidraw/components/Stats/DragInput.tsx index fbea33e55..4ee8c7580 100644 --- a/packages/excalidraw/components/Stats/DragInput.tsx +++ b/packages/excalidraw/components/Stats/DragInput.tsx @@ -1,8 +1,9 @@ import clsx from "clsx"; import { useEffect, useRef, useState } from "react"; +import { deepCopyElement } from "@excalidraw/excalidraw/element/duplicate"; + import { EVENT } from "../../constants"; -import { deepCopyElement } from "../../element/newElement"; import { KEYS } from "../../keys"; import { CaptureUpdateAction } from "../../store"; import { cloneJSON } from "../../utils"; diff --git a/packages/excalidraw/element/binding.ts b/packages/excalidraw/element/binding.ts index 93c5292f1..6799d1a6b 100644 --- a/packages/excalidraw/element/binding.ts +++ b/packages/excalidraw/element/binding.ts @@ -13,7 +13,6 @@ import { vectorCross, pointsEqual, lineSegmentIntersectionPoints, - round, PRECISION, } from "@excalidraw/math"; import { isPointOnShape } from "@excalidraw/utils/collision"; @@ -24,7 +23,10 @@ import { KEYS } from "../keys"; import { aabbForElement, getElementShape, pointInsideBounds } from "../shapes"; import { arrayToMap, + invariant, isBindingFallthroughEnabled, + isDevEnv, + isTestEnv, tupleToCoors, } from "../utils"; @@ -36,11 +38,8 @@ import { import { intersectElementWithLineSegment } from "./collision"; import { distanceToBindableElement } from "./distance"; import { - compareHeading, - HEADING_DOWN, - HEADING_RIGHT, - HEADING_UP, headingForPointFromElement, + headingIsHorizontal, vectorToHeading, type Heading, } from "./heading"; @@ -50,7 +49,6 @@ import { getBoundTextElement, handleBindTextResize } from "./textElement"; import { isArrowElement, isBindableElement, - isBindingElement, isBoundToContainer, isElbowArrow, isFixedPointBinding, @@ -60,6 +58,10 @@ import { isTextElement, } from "./typeChecks"; +import { updateElbowArrowPoints } from "./elbowArrow"; + +import type { Mutable } from "../utility-types"; + import type { Bounds } from "./bounds"; import type { ElementUpdate } from "./mutateElement"; import type { @@ -837,14 +839,19 @@ export const updateBoundElements = ( }> => update !== null, ); - LinearElementEditor.movePoints(element, updates, { - ...(changedElement.id === element.startBinding?.elementId - ? { startBinding: bindings.startBinding } - : {}), - ...(changedElement.id === element.endBinding?.elementId - ? { endBinding: bindings.endBinding } - : {}), - }); + LinearElementEditor.movePoints( + element, + updates, + { + ...(changedElement.id === element.startBinding?.elementId + ? { startBinding: bindings.startBinding } + : {}), + ...(changedElement.id === element.endBinding?.elementId + ? { endBinding: bindings.endBinding } + : {}), + }, + elementsMap as NonDeletedSceneElementsMap, + ); const boundText = getBoundTextElement(element, elementsMap); if (boundText && !boundText.isDeleted) { @@ -925,103 +932,104 @@ const getDistanceForBinding = ( export const bindPointToSnapToElementOutline = ( arrow: ExcalidrawElbowArrowElement, - bindableElement: ExcalidrawBindableElement | undefined, + bindableElement: ExcalidrawBindableElement, startOrEnd: "start" | "end", ): GlobalPoint => { - const aabb = bindableElement && aabbForElement(bindableElement); + if (isDevEnv() || isTestEnv()) { + invariant(arrow.points.length > 1, "Arrow should have at least 2 points"); + } + + const aabb = aabbForElement(bindableElement); const localP = arrow.points[startOrEnd === "start" ? 0 : arrow.points.length - 1]; const globalP = pointFrom( arrow.x + localP[0], arrow.y + localP[1], ); - const p = isRectanguloidElement(bindableElement) + const edgePoint = isRectanguloidElement(bindableElement) ? avoidRectangularCorner(bindableElement, globalP) : globalP; + const elbowed = isElbowArrow(arrow); + const center = getCenterForBounds(aabb); + const adjacentPointIdx = startOrEnd === "start" ? 1 : arrow.points.length - 2; + const adjacentPoint = pointRotateRads( + pointFrom( + arrow.x + arrow.points[adjacentPointIdx][0], + arrow.y + arrow.points[adjacentPointIdx][1], + ), + center, + arrow.angle ?? 0, + ); - if (bindableElement && aabb) { - const center = getCenterForBounds(aabb); - - const intersection = intersectElementWithLineSegment( + let intersection: GlobalPoint | null = null; + if (elbowed) { + const isHorizontal = headingIsHorizontal( + headingForPointFromElement(bindableElement, aabb, globalP), + ); + const otherPoint = pointFrom( + isHorizontal ? center[0] : edgePoint[0], + !isHorizontal ? center[1] : edgePoint[1], + ); + intersection = intersectElementWithLineSegment( bindableElement, lineSegment( - center, + otherPoint, pointFromVector( vectorScale( - vectorNormalize(vectorFromPoint(p, center)), - Math.max(bindableElement.width, bindableElement.height), + vectorNormalize(vectorFromPoint(edgePoint, otherPoint)), + Math.max(bindableElement.width, bindableElement.height) * 2, ), - center, + otherPoint, ), ), )[0]; - const currentDistance = pointDistance(p, center); - const fullDistance = Math.max( - pointDistance(intersection ?? p, center), - PRECISION, - ); - const ratio = round(currentDistance / fullDistance, 6); - - switch (true) { - case ratio > 0.9: - if ( - currentDistance - fullDistance > FIXED_BINDING_DISTANCE || - // Too close to determine vector from intersection to p - pointDistanceSq(p, intersection) < PRECISION - ) { - return p; - } - - return pointFromVector( + } else { + intersection = intersectElementWithLineSegment( + bindableElement, + lineSegment( + adjacentPoint, + pointFromVector( vectorScale( - vectorNormalize(vectorFromPoint(p, intersection ?? center)), - ratio > 1 ? FIXED_BINDING_DISTANCE : -FIXED_BINDING_DISTANCE, + vectorNormalize(vectorFromPoint(edgePoint, adjacentPoint)), + pointDistance(edgePoint, adjacentPoint) + + Math.max(bindableElement.width, bindableElement.height) * 2, ), - intersection ?? center, - ); - - default: - return headingToMidBindPoint(p, bindableElement, aabb); - } + adjacentPoint, + ), + ), + FIXED_BINDING_DISTANCE, + ).sort( + (g, h) => + pointDistanceSq(g, adjacentPoint) - pointDistanceSq(h, adjacentPoint), + )[0]; } - return p; -}; - -const headingToMidBindPoint = ( - p: GlobalPoint, - bindableElement: ExcalidrawBindableElement, - aabb: Bounds, -): GlobalPoint => { - const center = getCenterForBounds(aabb); - const heading = vectorToHeading(vectorFromPoint(p, center)); - - switch (true) { - case compareHeading(heading, HEADING_UP): - return pointRotateRads( - pointFrom((aabb[0] + aabb[2]) / 2 + 0.1, aabb[1]), - center, - bindableElement.angle, - ); - case compareHeading(heading, HEADING_RIGHT): - return pointRotateRads( - pointFrom(aabb[2], (aabb[1] + aabb[3]) / 2 + 0.1), - center, - bindableElement.angle, - ); - case compareHeading(heading, HEADING_DOWN): - return pointRotateRads( - pointFrom((aabb[0] + aabb[2]) / 2 - 0.1, aabb[3]), - center, - bindableElement.angle, - ); - default: - return pointRotateRads( - pointFrom(aabb[0], (aabb[1] + aabb[3]) / 2 - 0.1), - center, - bindableElement.angle, - ); + if ( + !intersection || + // Too close to determine vector from intersection to edgePoint + pointDistanceSq(edgePoint, intersection) < PRECISION + ) { + return edgePoint; } + + if (elbowed) { + const scalar = + pointDistanceSq(edgePoint, center) - + pointDistanceSq(intersection, center) > + 0 + ? FIXED_BINDING_DISTANCE + : -FIXED_BINDING_DISTANCE; + + return pointFromVector( + vectorScale( + vectorNormalize(vectorFromPoint(edgePoint, intersection)), + scalar, + ), + intersection, + ); + } + + return edgePoint; }; export const avoidRectangularCorner = ( @@ -1411,107 +1419,75 @@ const getLinearElementEdgeCoors = ( ); }; -// We need to: -// 1: Update elements not selected to point to duplicated elements -// 2: Update duplicated elements to point to other duplicated elements export const fixBindingsAfterDuplication = ( - sceneElements: readonly ExcalidrawElement[], - oldElements: readonly ExcalidrawElement[], + newElements: ExcalidrawElement[], oldIdToDuplicatedId: Map, - // There are three copying mechanisms: Copy-paste, duplication and alt-drag. - // Only when alt-dragging the new "duplicates" act as the "old", while - // the "old" elements act as the "new copy" - essentially working reverse - // to the other two. - duplicatesServeAsOld?: "duplicatesServeAsOld" | undefined, -): void => { - // First collect all the binding/bindable elements, so we only update - // each once, regardless of whether they were duplicated or not. - const allBoundElementIds: Set = new Set(); - const allBindableElementIds: Set = new Set(); - const shouldReverseRoles = duplicatesServeAsOld === "duplicatesServeAsOld"; - const duplicateIdToOldId = new Map( - [...oldIdToDuplicatedId].map(([key, value]) => [value, key]), - ); - oldElements.forEach((oldElement) => { - const { boundElements } = oldElement; - if (boundElements != null && boundElements.length > 0) { - boundElements.forEach((boundElement) => { - if (shouldReverseRoles && !oldIdToDuplicatedId.has(boundElement.id)) { - allBoundElementIds.add(boundElement.id); - } + duplicatedElementsMap: NonDeletedSceneElementsMap, +) => { + for (const element of newElements) { + if ("boundElements" in element && element.boundElements) { + Object.assign(element, { + boundElements: element.boundElements.reduce( + ( + acc: Mutable>, + binding, + ) => { + const newBindingId = oldIdToDuplicatedId.get(binding.id); + if (newBindingId) { + acc.push({ ...binding, id: newBindingId }); + } + return acc; + }, + [], + ), }); - allBindableElementIds.add(oldIdToDuplicatedId.get(oldElement.id)!); } - if (isBindingElement(oldElement)) { - if (oldElement.startBinding != null) { - const { elementId } = oldElement.startBinding; - if (shouldReverseRoles && !oldIdToDuplicatedId.has(elementId)) { - allBindableElementIds.add(elementId); - } - } - if (oldElement.endBinding != null) { - const { elementId } = oldElement.endBinding; - if (shouldReverseRoles && !oldIdToDuplicatedId.has(elementId)) { - allBindableElementIds.add(elementId); - } - } - if (oldElement.startBinding != null || oldElement.endBinding != null) { - allBoundElementIds.add(oldIdToDuplicatedId.get(oldElement.id)!); - } + + if ("containerId" in element && element.containerId) { + Object.assign(element, { + containerId: oldIdToDuplicatedId.get(element.containerId) ?? null, + }); } - }); - // Update the linear elements - ( - sceneElements.filter(({ id }) => - allBoundElementIds.has(id), - ) as ExcalidrawLinearElement[] - ).forEach((element) => { - const { startBinding, endBinding } = element; - mutateElement(element, { - startBinding: newBindingAfterDuplication( - startBinding, - oldIdToDuplicatedId, - ), - endBinding: newBindingAfterDuplication(endBinding, oldIdToDuplicatedId), - }); - }); + if ("endBinding" in element && element.endBinding) { + const newEndBindingId = oldIdToDuplicatedId.get( + element.endBinding.elementId, + ); + Object.assign(element, { + endBinding: newEndBindingId + ? { + ...element.endBinding, + elementId: newEndBindingId, + } + : null, + }); + } + if ("startBinding" in element && element.startBinding) { + const newEndBindingId = oldIdToDuplicatedId.get( + element.startBinding.elementId, + ); + Object.assign(element, { + startBinding: newEndBindingId + ? { + ...element.startBinding, + elementId: newEndBindingId, + } + : null, + }); + } - // Update the bindable shapes - sceneElements - .filter(({ id }) => allBindableElementIds.has(id)) - .forEach((bindableElement) => { - const oldElementId = duplicateIdToOldId.get(bindableElement.id); - const boundElements = sceneElements.find( - ({ id }) => id === oldElementId, - )?.boundElements; - - if (boundElements && boundElements.length > 0) { - mutateElement(bindableElement, { - boundElements: boundElements.map((boundElement) => - oldIdToDuplicatedId.has(boundElement.id) - ? { - id: oldIdToDuplicatedId.get(boundElement.id)!, - type: boundElement.type, - } - : boundElement, - ), - }); - } - }); -}; - -const newBindingAfterDuplication = ( - binding: PointBinding | null, - oldIdToDuplicatedId: Map, -): PointBinding | null => { - if (binding == null) { - return null; + if (isElbowArrow(element)) { + Object.assign( + element, + updateElbowArrowPoints(element, duplicatedElementsMap, { + points: [ + element.points[0], + element.points[element.points.length - 1], + ], + }), + ); + } } - return { - ...binding, - elementId: oldIdToDuplicatedId.get(binding.elementId) ?? binding.elementId, - }; }; export const fixBindingsAfterDeletion = ( diff --git a/packages/excalidraw/element/newElement.test.ts b/packages/excalidraw/element/duplicate.test.tsx similarity index 52% rename from packages/excalidraw/element/newElement.test.ts rename to packages/excalidraw/element/duplicate.test.tsx index 418ede1be..8e070ff98 100644 --- a/packages/excalidraw/element/newElement.test.ts +++ b/packages/excalidraw/element/duplicate.test.tsx @@ -1,16 +1,32 @@ +import React from "react"; import { pointFrom } from "@excalidraw/math"; import type { LocalPoint } from "@excalidraw/math"; -import { FONT_FAMILY, ROUNDNESS } from "../constants"; +import { FONT_FAMILY, ORIG_ID, ROUNDNESS } from "../constants"; import { API } from "../tests/helpers/api"; import { isPrimitive } from "../utils"; +import { + act, + assertElements, + getCloneByOrigId, + render, +} from "../tests/test-utils"; +import { Excalidraw } from ".."; +import { actionDuplicateSelection } from "../actions"; + +import { Keyboard, Pointer } from "../tests/helpers/ui"; + import { mutateElement } from "./mutateElement"; -import { duplicateElement, duplicateElements } from "./newElement"; + +import { duplicateElement, duplicateElements } from "./duplicate"; import type { ExcalidrawLinearElement } from "./types"; +const { h } = window; +const mouse = new Pointer("mouse"); + const assertCloneObjects = (source: any, clone: any) => { for (const key in clone) { if (clone.hasOwnProperty(key) && !isPrimitive(clone[key])) { @@ -45,7 +61,7 @@ describe("duplicating single elements", () => { points: [pointFrom(1, 2), pointFrom(3, 4)], }); - const copy = duplicateElement(null, new Map(), element); + const copy = duplicateElement(null, new Map(), element, undefined, true); assertCloneObjects(element, copy); @@ -64,6 +80,8 @@ describe("duplicating single elements", () => { ...element, id: copy.id, seed: copy.seed, + version: copy.version, + versionNonce: copy.versionNonce, }); }); @@ -149,7 +167,10 @@ describe("duplicating multiple elements", () => { // ------------------------------------------------------------------------- const origElements = [rectangle1, text1, arrow1, arrow2, text2] as const; - const clonedElements = duplicateElements(origElements); + const { newElements: clonedElements } = duplicateElements({ + type: "everything", + elements: origElements, + }); // generic id in-equality checks // -------------------------------------------------------------------------- @@ -206,6 +227,7 @@ describe("duplicating multiple elements", () => { type: clonedText1.type, }), ); + expect(clonedRectangle.type).toBe("rectangle"); clonedArrows.forEach((arrow) => { expect( @@ -281,7 +303,7 @@ describe("duplicating multiple elements", () => { const arrow3 = API.createElement({ type: "arrow", - id: "arrow2", + id: "arrow3", startBinding: { elementId: "rectangle-not-exists", focus: 0.2, @@ -299,9 +321,11 @@ describe("duplicating multiple elements", () => { // ------------------------------------------------------------------------- const origElements = [rectangle1, text1, arrow1, arrow2, arrow3] as const; - const clonedElements = duplicateElements( - origElements, - ) as any as typeof origElements; + const { newElements: clonedElements } = duplicateElements({ + type: "everything", + elements: origElements, + }) as any as { newElements: typeof origElements }; + const [ clonedRectangle, clonedText1, @@ -321,7 +345,6 @@ describe("duplicating multiple elements", () => { elementId: clonedRectangle.id, }); expect(clonedArrow2.endBinding).toBe(null); - expect(clonedArrow3.startBinding).toBe(null); expect(clonedArrow3.endBinding).toEqual({ ...arrow3.endBinding, @@ -345,9 +368,10 @@ describe("duplicating multiple elements", () => { }); const origElements = [rectangle1, rectangle2, rectangle3] as const; - const clonedElements = duplicateElements( - origElements, - ) as any as typeof origElements; + const { newElements: clonedElements } = duplicateElements({ + type: "everything", + elements: origElements, + }) as any as { newElements: typeof origElements }; const [clonedRectangle1, clonedRectangle2, clonedRectangle3] = clonedElements; @@ -368,10 +392,305 @@ describe("duplicating multiple elements", () => { groupIds: ["g1"], }); - const [clonedRectangle1] = duplicateElements([rectangle1]); + const { + newElements: [clonedRectangle1], + } = duplicateElements({ type: "everything", elements: [rectangle1] }); expect(typeof clonedRectangle1.groupIds[0]).toBe("string"); expect(rectangle1.groupIds[0]).not.toBe(clonedRectangle1.groupIds[0]); }); }); }); + +describe("duplication z-order", () => { + beforeEach(async () => { + await render(); + }); + + it("duplication z order with Cmd+D for the lowest z-ordered element should be +1 for the clone", () => { + const rectangle1 = API.createElement({ + type: "rectangle", + x: 0, + y: 0, + }); + const rectangle2 = API.createElement({ + type: "rectangle", + x: 10, + y: 10, + }); + const rectangle3 = API.createElement({ + type: "rectangle", + x: 20, + y: 20, + }); + + API.setElements([rectangle1, rectangle2, rectangle3]); + API.setSelectedElements([rectangle1]); + + act(() => { + h.app.actionManager.executeAction(actionDuplicateSelection); + }); + + assertElements(h.elements, [ + { id: rectangle1.id }, + { [ORIG_ID]: rectangle1.id, selected: true }, + { id: rectangle2.id }, + { id: rectangle3.id }, + ]); + }); + + it("duplication z order with Cmd+D for the highest z-ordered element should be +1 for the clone", () => { + const rectangle1 = API.createElement({ + type: "rectangle", + x: 0, + y: 0, + }); + const rectangle2 = API.createElement({ + type: "rectangle", + x: 10, + y: 10, + }); + const rectangle3 = API.createElement({ + type: "rectangle", + x: 20, + y: 20, + }); + + API.setElements([rectangle1, rectangle2, rectangle3]); + API.setSelectedElements([rectangle3]); + + act(() => { + h.app.actionManager.executeAction(actionDuplicateSelection); + }); + + assertElements(h.elements, [ + { id: rectangle1.id }, + { id: rectangle2.id }, + { id: rectangle3.id }, + { [ORIG_ID]: rectangle3.id, selected: true }, + ]); + }); + + it("duplication z order with alt+drag for the lowest z-ordered element should be +1 for the clone", () => { + const rectangle1 = API.createElement({ + type: "rectangle", + x: 0, + y: 0, + }); + const rectangle2 = API.createElement({ + type: "rectangle", + x: 10, + y: 10, + }); + const rectangle3 = API.createElement({ + type: "rectangle", + x: 20, + y: 20, + }); + + API.setElements([rectangle1, rectangle2, rectangle3]); + + mouse.select(rectangle1); + Keyboard.withModifierKeys({ alt: true }, () => { + mouse.down(rectangle1.x + 5, rectangle1.y + 5); + mouse.up(rectangle1.x + 5, rectangle1.y + 5); + }); + + assertElements(h.elements, [ + { [ORIG_ID]: rectangle1.id }, + { id: rectangle1.id, selected: true }, + { id: rectangle2.id }, + { id: rectangle3.id }, + ]); + }); + + it("duplication z order with alt+drag for the highest z-ordered element should be +1 for the clone", () => { + const rectangle1 = API.createElement({ + type: "rectangle", + x: 0, + y: 0, + }); + const rectangle2 = API.createElement({ + type: "rectangle", + x: 10, + y: 10, + }); + const rectangle3 = API.createElement({ + type: "rectangle", + x: 20, + y: 20, + }); + + API.setElements([rectangle1, rectangle2, rectangle3]); + + mouse.select(rectangle3); + Keyboard.withModifierKeys({ alt: true }, () => { + mouse.down(rectangle3.x + 5, rectangle3.y + 5); + mouse.up(rectangle3.x + 5, rectangle3.y + 5); + }); + + assertElements(h.elements, [ + { id: rectangle1.id }, + { id: rectangle2.id }, + { [ORIG_ID]: rectangle3.id }, + { id: rectangle3.id, selected: true }, + ]); + }); + + it("duplication z order with alt+drag for the lowest z-ordered element should be +1 for the clone", () => { + const rectangle1 = API.createElement({ + type: "rectangle", + x: 0, + y: 0, + }); + const rectangle2 = API.createElement({ + type: "rectangle", + x: 10, + y: 10, + }); + const rectangle3 = API.createElement({ + type: "rectangle", + x: 20, + y: 20, + }); + + API.setElements([rectangle1, rectangle2, rectangle3]); + + mouse.select(rectangle1); + Keyboard.withModifierKeys({ alt: true }, () => { + mouse.down(rectangle1.x + 5, rectangle1.y + 5); + mouse.up(rectangle1.x + 5, rectangle1.y + 5); + }); + + assertElements(h.elements, [ + { [ORIG_ID]: rectangle1.id }, + { id: rectangle1.id, selected: true }, + { id: rectangle2.id }, + { id: rectangle3.id }, + ]); + }); + + it("duplication z order with alt+drag with grouped elements should consider the group together when determining z-index", () => { + const rectangle1 = API.createElement({ + type: "rectangle", + x: 0, + y: 0, + groupIds: ["group1"], + }); + const rectangle2 = API.createElement({ + type: "rectangle", + x: 10, + y: 10, + groupIds: ["group1"], + }); + const rectangle3 = API.createElement({ + type: "rectangle", + x: 20, + y: 20, + groupIds: ["group1"], + }); + + API.setElements([rectangle1, rectangle2, rectangle3]); + + mouse.select(rectangle1); + Keyboard.withModifierKeys({ alt: true }, () => { + mouse.down(rectangle1.x + 5, rectangle1.y + 5); + mouse.up(rectangle1.x + 15, rectangle1.y + 15); + }); + + assertElements(h.elements, [ + { [ORIG_ID]: rectangle1.id }, + { [ORIG_ID]: rectangle2.id }, + { [ORIG_ID]: rectangle3.id }, + { id: rectangle1.id, selected: true }, + { id: rectangle2.id, selected: true }, + { id: rectangle3.id, selected: true }, + ]); + }); + + it("reverse-duplicating text container (in-order)", async () => { + const [rectangle, text] = API.createTextContainer(); + API.setElements([rectangle, text]); + API.setSelectedElements([rectangle, text]); + + Keyboard.withModifierKeys({ alt: true }, () => { + mouse.down(rectangle.x + 5, rectangle.y + 5); + mouse.up(rectangle.x + 15, rectangle.y + 15); + }); + + assertElements(h.elements, [ + { [ORIG_ID]: rectangle.id }, + { + [ORIG_ID]: text.id, + containerId: getCloneByOrigId(rectangle.id)?.id, + }, + { id: rectangle.id, selected: true }, + { id: text.id, containerId: rectangle.id, selected: true }, + ]); + }); + + it("reverse-duplicating text container (out-of-order)", async () => { + const [rectangle, text] = API.createTextContainer(); + API.setElements([text, rectangle]); + API.setSelectedElements([rectangle, text]); + + Keyboard.withModifierKeys({ alt: true }, () => { + mouse.down(rectangle.x + 5, rectangle.y + 5); + mouse.up(rectangle.x + 15, rectangle.y + 15); + }); + + assertElements(h.elements, [ + { [ORIG_ID]: rectangle.id }, + { + [ORIG_ID]: text.id, + containerId: getCloneByOrigId(rectangle.id)?.id, + }, + { id: rectangle.id, selected: true }, + { id: text.id, containerId: rectangle.id, selected: true }, + ]); + }); + + it("reverse-duplicating labeled arrows (in-order)", async () => { + const [arrow, text] = API.createLabeledArrow(); + + API.setElements([arrow, text]); + API.setSelectedElements([arrow, text]); + + Keyboard.withModifierKeys({ alt: true }, () => { + mouse.down(arrow.x + 5, arrow.y + 5); + mouse.up(arrow.x + 15, arrow.y + 15); + }); + + assertElements(h.elements, [ + { [ORIG_ID]: arrow.id }, + { + [ORIG_ID]: text.id, + containerId: getCloneByOrigId(arrow.id)?.id, + }, + { id: arrow.id, selected: true }, + { id: text.id, containerId: arrow.id, selected: true }, + ]); + }); + + it("reverse-duplicating labeled arrows (out-of-order)", async () => { + const [arrow, text] = API.createLabeledArrow(); + + API.setElements([text, arrow]); + API.setSelectedElements([arrow, text]); + + Keyboard.withModifierKeys({ alt: true }, () => { + mouse.down(arrow.x + 5, arrow.y + 5); + mouse.up(arrow.x + 15, arrow.y + 15); + }); + + assertElements(h.elements, [ + { [ORIG_ID]: arrow.id }, + { + [ORIG_ID]: text.id, + containerId: getCloneByOrigId(arrow.id)?.id, + }, + { id: arrow.id, selected: true }, + { id: text.id, containerId: arrow.id, selected: true }, + ]); + }); +}); diff --git a/packages/excalidraw/element/duplicate.ts b/packages/excalidraw/element/duplicate.ts new file mode 100644 index 000000000..c126c57c9 --- /dev/null +++ b/packages/excalidraw/element/duplicate.ts @@ -0,0 +1,484 @@ +import { ORIG_ID } from "../constants"; +import { + getElementsInGroup, + getNewGroupIdsForDuplication, + getSelectedGroupForElement, +} from "../groups"; + +import { randomId, randomInteger } from "../random"; + +import { + arrayToMap, + castArray, + findLastIndex, + getUpdatedTimestamp, + isTestEnv, +} from "../utils"; + +import { + bindElementsToFramesAfterDuplication, + getFrameChildren, +} from "../frame"; + +import { normalizeElementOrder } from "./sortElements"; + +import { bumpVersion } from "./mutateElement"; + +import { + hasBoundTextElement, + isBoundToContainer, + isFrameLikeElement, +} from "./typeChecks"; + +import { getBoundTextElement, getContainerElement } from "./textElement"; + +import { fixBindingsAfterDuplication } from "./binding"; + +import type { AppState } from "../types"; +import type { Mutable } from "../utility-types"; + +import type { + ElementsMap, + ExcalidrawElement, + GroupId, + NonDeletedSceneElementsMap, +} from "./types"; + +/** + * Duplicate an element, often used in the alt-drag operation. + * Note that this method has gotten a bit complicated since the + * introduction of gruoping/ungrouping elements. + * @param editingGroupId The current group being edited. The new + * element will inherit this group and its + * parents. + * @param groupIdMapForOperation A Map that maps old group IDs to + * duplicated ones. If you are duplicating + * multiple elements at once, share this map + * amongst all of them + * @param element Element to duplicate + * @param overrides Any element properties to override + */ +export const duplicateElement = ( + editingGroupId: AppState["editingGroupId"], + groupIdMapForOperation: Map, + element: TElement, + overrides?: Partial, + randomizeSeed?: boolean, +): Readonly => { + let copy = deepCopyElement(element); + + if (isTestEnv()) { + __test__defineOrigId(copy, element.id); + } + + copy.id = randomId(); + copy.updated = getUpdatedTimestamp(); + if (randomizeSeed) { + copy.seed = randomInteger(); + bumpVersion(copy); + } + + copy.groupIds = getNewGroupIdsForDuplication( + copy.groupIds, + editingGroupId, + (groupId) => { + if (!groupIdMapForOperation.has(groupId)) { + groupIdMapForOperation.set(groupId, randomId()); + } + return groupIdMapForOperation.get(groupId)!; + }, + ); + if (overrides) { + copy = Object.assign(copy, overrides); + } + return copy; +}; + +export const duplicateElements = ( + opts: { + elements: readonly ExcalidrawElement[]; + randomizeSeed?: boolean; + overrides?: ( + originalElement: ExcalidrawElement, + ) => Partial; + } & ( + | { + /** + * Duplicates all elements in array. + * + * Use this when programmaticaly duplicating elements, without direct + * user interaction. + */ + type: "everything"; + } + | { + /** + * Duplicates specified elements and inserts them back into the array + * in specified order. + * + * Use this when duplicating Scene elements, during user interaction + * such as alt-drag or on duplicate action. + */ + type: "in-place"; + idsOfElementsToDuplicate: Map< + ExcalidrawElement["id"], + ExcalidrawElement + >; + appState: { + editingGroupId: AppState["editingGroupId"]; + selectedGroupIds: AppState["selectedGroupIds"]; + }; + /** + * If true, duplicated elements are inserted _before_ specified + * elements. Case: alt-dragging elements to duplicate them. + * + * TODO: remove this once (if) we stop replacing the original element + * with the duplicated one in the scene array. + */ + reverseOrder: boolean; + } + ), +) => { + let { elements } = opts; + + const appState = + "appState" in opts + ? opts.appState + : ({ + editingGroupId: null, + selectedGroupIds: {}, + } as const); + + const reverseOrder = opts.type === "in-place" ? opts.reverseOrder : false; + + // Ids of elements that have already been processed so we don't push them + // into the array twice if we end up backtracking when retrieving + // discontiguous group of elements (can happen due to a bug, or in edge + // cases such as a group containing deleted elements which were not selected). + // + // This is not enough to prevent duplicates, so we do a second loop afterwards + // to remove them. + // + // For convenience we mark even the newly created ones even though we don't + // loop over them. + const processedIds = new Map(); + const groupIdMap = new Map(); + const newElements: ExcalidrawElement[] = []; + const oldElements: ExcalidrawElement[] = []; + const oldIdToDuplicatedId = new Map(); + const duplicatedElementsMap = new Map(); + const elementsMap = arrayToMap(elements) as ElementsMap; + const _idsOfElementsToDuplicate = + opts.type === "in-place" + ? opts.idsOfElementsToDuplicate + : new Map(elements.map((el) => [el.id, el])); + + // For sanity + if (opts.type === "in-place") { + for (const groupId of Object.keys(opts.appState.selectedGroupIds)) { + elements + .filter((el) => el.groupIds?.includes(groupId)) + .forEach((el) => _idsOfElementsToDuplicate.set(el.id, el)); + } + } + + elements = normalizeElementOrder(elements); + + const elementsWithClones: ExcalidrawElement[] = elements.slice(); + + // helper functions + // ------------------------------------------------------------------------- + + // Used for the heavy lifing of copying a single element, a group of elements + // an element with bound text etc. + const copyElements = ( + element: T, + ): T extends ExcalidrawElement[] + ? ExcalidrawElement[] + : ExcalidrawElement | null => { + const elements = castArray(element); + + const _newElements = elements.reduce( + (acc: ExcalidrawElement[], element) => { + if (processedIds.has(element.id)) { + return acc; + } + + processedIds.set(element.id, true); + + const newElement = duplicateElement( + appState.editingGroupId, + groupIdMap, + element, + opts.overrides?.(element), + opts.randomizeSeed, + ); + + processedIds.set(newElement.id, true); + + duplicatedElementsMap.set(newElement.id, newElement); + oldIdToDuplicatedId.set(element.id, newElement.id); + + oldElements.push(element); + newElements.push(newElement); + + acc.push(newElement); + return acc; + }, + [], + ); + + return ( + Array.isArray(element) ? _newElements : _newElements[0] || null + ) as T extends ExcalidrawElement[] + ? ExcalidrawElement[] + : ExcalidrawElement | null; + }; + + // Helper to position cloned elements in the Z-order the product needs it + const insertBeforeOrAfterIndex = ( + index: number, + elements: ExcalidrawElement | null | ExcalidrawElement[], + ) => { + if (!elements) { + return; + } + + if (reverseOrder && index < 1) { + elementsWithClones.unshift(...castArray(elements)); + return; + } + + if (!reverseOrder && index > elementsWithClones.length - 1) { + elementsWithClones.push(...castArray(elements)); + return; + } + + elementsWithClones.splice( + index + (reverseOrder ? 0 : 1), + 0, + ...castArray(elements), + ); + }; + + const frameIdsToDuplicate = new Set( + elements + .filter( + (el) => _idsOfElementsToDuplicate.has(el.id) && isFrameLikeElement(el), + ) + .map((el) => el.id), + ); + + for (const element of elements) { + if (processedIds.has(element.id)) { + continue; + } + + if (!_idsOfElementsToDuplicate.has(element.id)) { + continue; + } + + // groups + // ------------------------------------------------------------------------- + + const groupId = getSelectedGroupForElement(appState, element); + if (groupId) { + const groupElements = getElementsInGroup(elements, groupId).flatMap( + (element) => + isFrameLikeElement(element) + ? [...getFrameChildren(elements, element.id), element] + : [element], + ); + + const targetIndex = reverseOrder + ? elementsWithClones.findIndex((el) => { + return el.groupIds?.includes(groupId); + }) + : findLastIndex(elementsWithClones, (el) => { + return el.groupIds?.includes(groupId); + }); + + insertBeforeOrAfterIndex(targetIndex, copyElements(groupElements)); + continue; + } + + // frame duplication + // ------------------------------------------------------------------------- + + if (element.frameId && frameIdsToDuplicate.has(element.frameId)) { + continue; + } + + if (isFrameLikeElement(element)) { + const frameId = element.id; + + const frameChildren = getFrameChildren(elements, frameId); + + const targetIndex = findLastIndex(elementsWithClones, (el) => { + return el.frameId === frameId || el.id === frameId; + }); + + insertBeforeOrAfterIndex( + targetIndex, + copyElements([...frameChildren, element]), + ); + continue; + } + + // text container + // ------------------------------------------------------------------------- + + if (hasBoundTextElement(element)) { + const boundTextElement = getBoundTextElement(element, elementsMap); + + const targetIndex = findLastIndex(elementsWithClones, (el) => { + return ( + el.id === element.id || + ("containerId" in el && el.containerId === element.id) + ); + }); + + if (boundTextElement) { + insertBeforeOrAfterIndex( + targetIndex + (reverseOrder ? -1 : 0), + copyElements([element, boundTextElement]), + ); + } else { + insertBeforeOrAfterIndex(targetIndex, copyElements(element)); + } + + continue; + } + + if (isBoundToContainer(element)) { + const container = getContainerElement(element, elementsMap); + + const targetIndex = findLastIndex(elementsWithClones, (el) => { + return el.id === element.id || el.id === container?.id; + }); + + if (container) { + insertBeforeOrAfterIndex( + targetIndex, + copyElements([container, element]), + ); + } else { + insertBeforeOrAfterIndex(targetIndex, copyElements(element)); + } + + continue; + } + + // default duplication (regular elements) + // ------------------------------------------------------------------------- + + insertBeforeOrAfterIndex( + findLastIndex(elementsWithClones, (el) => el.id === element.id), + copyElements(element), + ); + } + + // --------------------------------------------------------------------------- + + fixBindingsAfterDuplication( + newElements, + oldIdToDuplicatedId, + duplicatedElementsMap as NonDeletedSceneElementsMap, + ); + + bindElementsToFramesAfterDuplication( + elementsWithClones, + oldElements, + oldIdToDuplicatedId, + ); + + return { + newElements, + elementsWithClones, + }; +}; + +// Simplified deep clone for the purpose of cloning ExcalidrawElement. +// +// Only clones plain objects and arrays. Doesn't clone Date, RegExp, Map, Set, +// Typed arrays and other non-null objects. +// +// Adapted from https://github.com/lukeed/klona +// +// The reason for `deepCopyElement()` wrapper is type safety (only allow +// passing ExcalidrawElement as the top-level argument). +const _deepCopyElement = (val: any, depth: number = 0) => { + // only clone non-primitives + if (val == null || typeof val !== "object") { + return val; + } + + const objectType = Object.prototype.toString.call(val); + + if (objectType === "[object Object]") { + const tmp = + typeof val.constructor === "function" + ? Object.create(Object.getPrototypeOf(val)) + : {}; + for (const key in val) { + if (val.hasOwnProperty(key)) { + // don't copy non-serializable objects like these caches. They'll be + // populated when the element is rendered. + if (depth === 0 && (key === "shape" || key === "canvas")) { + continue; + } + tmp[key] = _deepCopyElement(val[key], depth + 1); + } + } + return tmp; + } + + if (Array.isArray(val)) { + let k = val.length; + const arr = new Array(k); + while (k--) { + arr[k] = _deepCopyElement(val[k], depth + 1); + } + return arr; + } + + // we're not cloning non-array & non-plain-object objects because we + // don't support them on excalidraw elements yet. If we do, we need to make + // sure we start cloning them, so let's warn about it. + if (import.meta.env.DEV) { + if ( + objectType !== "[object Object]" && + objectType !== "[object Array]" && + objectType.startsWith("[object ") + ) { + console.warn( + `_deepCloneElement: unexpected object type ${objectType}. This value will not be cloned!`, + ); + } + } + + return val; +}; + +/** + * Clones ExcalidrawElement data structure. Does not regenerate id, nonce, or + * any value. The purpose is to to break object references for immutability + * reasons, whenever we want to keep the original element, but ensure it's not + * mutated. + * + * Only clones plain objects and arrays. Doesn't clone Date, RegExp, Map, Set, + * Typed arrays and other non-null objects. + */ +export const deepCopyElement = ( + val: T, +): Mutable => { + return _deepCopyElement(val); +}; + +const __test__defineOrigId = (clonedObj: object, origId: string) => { + Object.defineProperty(clonedObj, ORIG_ID, { + value: origId, + writable: false, + enumerable: false, + }); +}; diff --git a/packages/excalidraw/element/elbowArrow.test.tsx b/packages/excalidraw/element/elbowArrow.test.tsx index 91e280d30..621d2640f 100644 --- a/packages/excalidraw/element/elbowArrow.test.tsx +++ b/packages/excalidraw/element/elbowArrow.test.tsx @@ -358,7 +358,7 @@ describe("elbow arrow ui", () => { expect(arrow.endBinding).not.toBe(null); }); - it("keeps arrow shape when only the bound arrow is duplicated", async () => { + it("changes arrow shape to unbind variant if only the connected elbow arrow is duplicated", async () => { UI.createElement("rectangle", { x: -150, y: -150, @@ -404,8 +404,8 @@ describe("elbow arrow ui", () => { expect(duplicatedArrow.elbowed).toBe(true); expect(duplicatedArrow.points).toEqual([ [0, 0], - [45, 0], - [45, 200], + [0, 100], + [90, 100], [90, 200], ]); }); diff --git a/packages/excalidraw/element/elbowArrow.ts b/packages/excalidraw/element/elbowArrow.ts index 5e44b6ea6..a8c22abcc 100644 --- a/packages/excalidraw/element/elbowArrow.ts +++ b/packages/excalidraw/element/elbowArrow.ts @@ -238,16 +238,6 @@ const handleSegmentRenormalization = ( nextPoints.map((p) => pointFrom(p[0] - arrow.x, p[1] - arrow.y), ), - arrow.startBinding && - getBindableElementForId( - arrow.startBinding.elementId, - elementsMap, - ), - arrow.endBinding && - getBindableElementForId( - arrow.endBinding.elementId, - elementsMap, - ), ), ) ?? [], ), @@ -341,9 +331,6 @@ const handleSegmentRelease = ( y, ), ], - startBinding && - getBindableElementForId(startBinding.elementId, elementsMap), - endBinding && getBindableElementForId(endBinding.elementId, elementsMap), { isDragging: false }, ); @@ -983,6 +970,8 @@ export const updateElbowArrowPoints = ( ); } + const fixedSegments = updates.fixedSegments ?? arrow.fixedSegments ?? []; + const updatedPoints: readonly LocalPoint[] = updates.points ? updates.points && updates.points.length === 2 ? arrow.points.map((p, idx) => @@ -995,7 +984,7 @@ export const updateElbowArrowPoints = ( : updates.points.slice() : arrow.points.slice(); - // 0. During all element replacement in the scene, we just need to renormalize + // During all element replacement in the scene, we just need to renormalize // the arrow // TODO (dwelle,mtolmacs): Remove this once Scene.getScene() is removed const { @@ -1016,11 +1005,12 @@ export const updateElbowArrowPoints = ( getBindableElementForId(startBinding.elementId, elementsMap); const endElement = endBinding && getBindableElementForId(endBinding.elementId, elementsMap); + const areUpdatedPointsValid = validateElbowPoints(updatedPoints); if ( - (startBinding && !startElement) || - (endBinding && !endElement) || - (elementsMap.size === 0 && validateElbowPoints(updatedPoints)) || + (startBinding && !startElement && areUpdatedPointsValid) || + (endBinding && !endElement && areUpdatedPointsValid) || + (elementsMap.size === 0 && areUpdatedPointsValid) || (Object.keys(restOfTheUpdates).length === 0 && (startElement?.id !== startBinding?.elementId || endElement?.id !== endBinding?.elementId)) @@ -1055,12 +1045,22 @@ export const updateElbowArrowPoints = ( }, elementsMap, updatedPoints, - startElement, - endElement, options, ); - const fixedSegments = updates.fixedSegments ?? arrow.fixedSegments ?? []; + // 0. During all element replacement in the scene, we just need to renormalize + // the arrow + // TODO (dwelle,mtolmacs): Remove this once Scene.getScene() is removed + if (elementsMap.size === 0 && areUpdatedPointsValid) { + return normalizeArrowElementUpdate( + updatedPoints.map((p) => + pointFrom(arrow.x + p[0], arrow.y + p[1]), + ), + arrow.fixedSegments, + arrow.startIsSpecial, + arrow.endIsSpecial, + ); + } //// // 1. Renormalize the arrow @@ -1084,7 +1084,7 @@ export const updateElbowArrowPoints = ( arrow.points[i] ?? pointFrom(Infinity, Infinity), ), ) && - validateElbowPoints(updatedPoints) + areUpdatedPointsValid ) { return {}; } @@ -1195,8 +1195,6 @@ const getElbowArrowData = ( }, elementsMap: NonDeletedSceneElementsMap, nextPoints: readonly LocalPoint[], - startElement: ExcalidrawBindableElement | null, - endElement: ExcalidrawBindableElement | null, options?: { isDragging?: boolean; zoom?: AppState["zoom"]; @@ -1211,8 +1209,8 @@ const getElbowArrowData = ( GlobalPoint >(nextPoints[nextPoints.length - 1], vector(arrow.x, arrow.y)); - let hoveredStartElement = startElement; - let hoveredEndElement = endElement; + let hoveredStartElement = null; + let hoveredEndElement = null; if (options?.isDragging) { const elements = Array.from(elementsMap.values()); hoveredStartElement = @@ -1221,39 +1219,47 @@ const getElbowArrowData = ( elementsMap, elements, options?.zoom, - ) || startElement; + ) || null; hoveredEndElement = getHoveredElement( origEndGlobalPoint, elementsMap, elements, options?.zoom, - ) || endElement; + ) || null; + } else { + hoveredStartElement = arrow.startBinding + ? getBindableElementForId(arrow.startBinding.elementId, elementsMap) || + null + : null; + hoveredEndElement = arrow.endBinding + ? getBindableElementForId(arrow.endBinding.elementId, elementsMap) || null + : null; } const startGlobalPoint = getGlobalPoint( { ...arrow, + type: "arrow", elbowed: true, points: nextPoints, } as ExcalidrawElbowArrowElement, "start", arrow.startBinding?.fixedPoint, origStartGlobalPoint, - startElement, hoveredStartElement, options?.isDragging, ); const endGlobalPoint = getGlobalPoint( { ...arrow, + type: "arrow", elbowed: true, points: nextPoints, } as ExcalidrawElbowArrowElement, "end", arrow.endBinding?.fixedPoint, origEndGlobalPoint, - endElement, hoveredEndElement, options?.isDragging, ); @@ -2199,36 +2205,35 @@ const getGlobalPoint = ( startOrEnd: "start" | "end", fixedPointRatio: [number, number] | undefined | null, initialPoint: GlobalPoint, - boundElement?: ExcalidrawBindableElement | null, - hoveredElement?: ExcalidrawBindableElement | null, + element?: ExcalidrawBindableElement | null, isDragging?: boolean, ): GlobalPoint => { if (isDragging) { - if (hoveredElement) { + if (element) { const snapPoint = bindPointToSnapToElementOutline( arrow, - hoveredElement, + element, startOrEnd, ); - return snapToMid(hoveredElement, snapPoint); + return snapToMid(element, snapPoint); } return initialPoint; } - if (boundElement) { + if (element) { const fixedGlobalPoint = getGlobalFixedPointForBindableElement( fixedPointRatio || [0, 0], - boundElement, + element, ); // NOTE: Resize scales the binding position point too, so we need to update it return Math.abs( - distanceToBindableElement(boundElement, fixedGlobalPoint) - + distanceToBindableElement(element, fixedGlobalPoint) - FIXED_BINDING_DISTANCE, ) > 0.01 - ? bindPointToSnapToElementOutline(arrow, boundElement, startOrEnd) + ? bindPointToSnapToElementOutline(arrow, element, startOrEnd) : fixedGlobalPoint; } diff --git a/packages/excalidraw/element/flowchart.ts b/packages/excalidraw/element/flowchart.ts index 9880f27af..1790ef3f0 100644 --- a/packages/excalidraw/element/flowchart.ts +++ b/packages/excalidraw/element/flowchart.ts @@ -1,4 +1,4 @@ -import { pointFrom, type LocalPoint } from "@excalidraw/math"; +import { type GlobalPoint, pointFrom, type LocalPoint } from "@excalidraw/math"; import { elementOverlapsWithFrame, elementsAreInFrameBounds } from "../frame"; import { KEYS } from "../keys"; @@ -94,7 +94,7 @@ const getNodeRelatives = ( const heading = headingForPointFromElement(node, aabbForElement(node), [ edgePoint[0] + el.x, edgePoint[1] + el.y, - ] as Readonly); + ] as Readonly); acc.push({ relative, diff --git a/packages/excalidraw/element/heading.ts b/packages/excalidraw/element/heading.ts index ddebeca53..474923515 100644 --- a/packages/excalidraw/element/heading.ts +++ b/packages/excalidraw/element/heading.ts @@ -1,4 +1,5 @@ import { + normalizeRadians, pointFrom, pointRotateRads, pointScaleFromOrigin, @@ -30,8 +31,9 @@ export const headingForDiamond = ( b: Point, ) => { const angle = radiansToDegrees( - Math.atan2(b[1] - a[1], b[0] - a[0]) as Radians, + normalizeRadians(Math.atan2(b[1] - a[1], b[0] - a[0]) as Radians), ); + if (angle >= 315 || angle < 45) { return HEADING_UP; } else if (angle >= 45 && angle < 135) { @@ -77,9 +79,7 @@ export const headingIsVertical = (a: Heading) => !headingIsHorizontal(a); // Gets the heading for the point by creating a bounding box around the rotated // close fitting bounding box, then creating 4 search cones around the center of // the external bbox. -export const headingForPointFromElement = < - Point extends GlobalPoint | LocalPoint, ->( +export const headingForPointFromElement = ( element: Readonly, aabb: Readonly, p: Readonly, diff --git a/packages/excalidraw/element/index.ts b/packages/excalidraw/element/index.ts index abe84e031..6244e2740 100644 --- a/packages/excalidraw/element/index.ts +++ b/packages/excalidraw/element/index.ts @@ -14,8 +14,8 @@ export { newLinearElement, newArrowElement, newImageElement, - duplicateElement, } from "./newElement"; +export { duplicateElement } from "./duplicate"; export { getElementAbsoluteCoords, getElementBounds, diff --git a/packages/excalidraw/element/linearElementEditor.ts b/packages/excalidraw/element/linearElementEditor.ts index f9b23f048..adc436236 100644 --- a/packages/excalidraw/element/linearElementEditor.ts +++ b/packages/excalidraw/element/linearElementEditor.ts @@ -25,14 +25,19 @@ import { import { getGridPoint } from "../snapping"; import { invariant, tupleToCoors } from "../utils"; +import Scene from "../scene/Scene"; + import { bindOrUnbindLinearElement, getHoveredElementForBinding, isBindingEnabled, } from "./binding"; + +import { updateElbowArrowPoints } from "./elbowArrow"; + import { getElementPointsCoords, getMinMaxXYFromCurvePathOps } from "./bounds"; import { headingIsHorizontal, vectorToHeading } from "./heading"; -import { mutateElement } from "./mutateElement"; +import { bumpVersion, mutateElement } from "./mutateElement"; import { getBoundTextElement, handleBindTextResize } from "./textElement"; import { isBindingElement, @@ -57,7 +62,6 @@ import type { FixedSegment, ExcalidrawElbowArrowElement, } from "./types"; -import type Scene from "../scene/Scene"; import type { Store } from "../store"; import type { AppState, @@ -67,6 +71,7 @@ import type { NullableGridSize, Zoom, } from "../types"; + import type { Mutable } from "../utility-types"; const editorMidPointsCache: { @@ -232,15 +237,15 @@ export class LinearElementEditor { ) => void, linearElementEditor: LinearElementEditor, scene: Scene, - ): boolean { + ): LinearElementEditor | null { if (!linearElementEditor) { - return false; + return null; } const { elementId } = linearElementEditor; const elementsMap = scene.getNonDeletedElementsMap(); const element = LinearElementEditor.getElement(elementId, elementsMap); if (!element) { - return false; + return null; } if ( @@ -248,24 +253,18 @@ export class LinearElementEditor { !linearElementEditor.pointerDownState.lastClickedIsEndPoint && linearElementEditor.pointerDownState.lastClickedPoint !== 0 ) { - return false; + return null; } const selectedPointsIndices = isElbowArrow(element) - ? linearElementEditor.selectedPointsIndices - ?.reduce( - (startEnd, index) => - (index === 0 - ? [0, startEnd[1]] - : [startEnd[0], element.points.length - 1]) as [ - boolean | number, - boolean | number, - ], - [false, false] as [number | boolean, number | boolean], - ) - .filter( - (idx: number | boolean): idx is number => typeof idx === "number", - ) + ? [ + !!linearElementEditor.selectedPointsIndices?.includes(0) + ? 0 + : undefined, + !!linearElementEditor.selectedPointsIndices?.find((idx) => idx > 0) + ? element.points.length - 1 + : undefined, + ].filter((idx): idx is number => idx !== undefined) : linearElementEditor.selectedPointsIndices; const lastClickedPoint = isElbowArrow(element) ? linearElementEditor.pointerDownState.lastClickedPoint > 0 @@ -274,9 +273,7 @@ export class LinearElementEditor { : linearElementEditor.pointerDownState.lastClickedPoint; // point that's being dragged (out of all selected points) - const draggingPoint = element.points[lastClickedPoint] as - | [number, number] - | undefined; + const draggingPoint = element.points[lastClickedPoint]; if (selectedPointsIndices && draggingPoint) { if ( @@ -384,10 +381,28 @@ export class LinearElementEditor { } } - return true; + return { + ...linearElementEditor, + selectedPointsIndices, + segmentMidPointHoveredCoords: + lastClickedPoint !== 0 && + lastClickedPoint !== element.points.length - 1 + ? this.getPointGlobalCoordinates( + element, + draggingPoint, + elementsMap, + ) + : null, + hoverPointIndex: + lastClickedPoint === 0 || + lastClickedPoint === element.points.length - 1 + ? lastClickedPoint + : -1, + isDragging: true, + }; } - return false; + return null; } static handlePointerUp( @@ -1264,6 +1279,7 @@ export class LinearElementEditor { startBinding?: PointBinding | null; endBinding?: PointBinding | null; }, + sceneElementsMap?: NonDeletedSceneElementsMap, ) { const { points } = element; @@ -1307,6 +1323,7 @@ export class LinearElementEditor { dragging || targetPoint.isDragging === true, false, ), + sceneElementsMap, }, ); } @@ -1420,6 +1437,7 @@ export class LinearElementEditor { options?: { isDragging?: boolean; zoom?: AppState["zoom"]; + sceneElementsMap?: NonDeletedSceneElementsMap; }, ) { if (isElbowArrow(element)) { @@ -1445,9 +1463,28 @@ export class LinearElementEditor { updates.points = Array.from(nextPoints); - mutateElement(element, updates, true, { - isDragging: options?.isDragging, - }); + if (!options?.sceneElementsMap || Scene.getScene(element)) { + mutateElement(element, updates, true, { + isDragging: options?.isDragging, + }); + } else { + // The element is not in the scene, so we need to use the provided + // scene map. + Object.assign(element, { + ...updates, + angle: 0 as Radians, + + ...updateElbowArrowPoints( + element, + options.sceneElementsMap, + updates, + { + isDragging: options?.isDragging, + }, + ), + }); + } + bumpVersion(element); } else { const nextCoords = getElementPointsCoords(element, nextPoints); const prevCoords = getElementPointsCoords(element, element.points); diff --git a/packages/excalidraw/element/newElement.ts b/packages/excalidraw/element/newElement.ts index d11c4c20f..8d0a9bbd8 100644 --- a/packages/excalidraw/element/newElement.ts +++ b/packages/excalidraw/element/newElement.ts @@ -6,21 +6,14 @@ import { DEFAULT_FONT_SIZE, DEFAULT_TEXT_ALIGN, DEFAULT_VERTICAL_ALIGN, - ORIG_ID, VERTICAL_ALIGN, } from "../constants"; import { getLineHeight } from "../fonts"; -import { getNewGroupIdsForDuplication } from "../groups"; import { randomInteger, randomId } from "../random"; -import { - arrayToMap, - getFontString, - getUpdatedTimestamp, - isTestEnv, -} from "../utils"; +import { getFontString, getUpdatedTimestamp } from "../utils"; import { getResizedElementAbsoluteCoords } from "./bounds"; -import { bumpVersion, newElementWith } from "./mutateElement"; +import { newElementWith } from "./mutateElement"; import { getBoundTextMaxWidth } from "./textElement"; import { normalizeText, measureText } from "./textMeasurements"; import { wrapText } from "./textWrapping"; @@ -35,7 +28,6 @@ import type { ExcalidrawGenericElement, NonDeleted, TextAlign, - GroupId, VerticalAlign, Arrowhead, ExcalidrawFreeDrawElement, @@ -50,8 +42,7 @@ import type { FixedSegment, ExcalidrawElbowArrowElement, } from "./types"; -import type { AppState } from "../types"; -import type { MarkOptional, Merge, Mutable } from "../utility-types"; +import type { MarkOptional, Merge } from "../utility-types"; export type ElementConstructorOpts = MarkOptional< Omit, @@ -538,260 +529,3 @@ export const newImageElement = ( crop: opts.crop ?? null, }; }; - -// Simplified deep clone for the purpose of cloning ExcalidrawElement. -// -// Only clones plain objects and arrays. Doesn't clone Date, RegExp, Map, Set, -// Typed arrays and other non-null objects. -// -// Adapted from https://github.com/lukeed/klona -// -// The reason for `deepCopyElement()` wrapper is type safety (only allow -// passing ExcalidrawElement as the top-level argument). -const _deepCopyElement = (val: any, depth: number = 0) => { - // only clone non-primitives - if (val == null || typeof val !== "object") { - return val; - } - - const objectType = Object.prototype.toString.call(val); - - if (objectType === "[object Object]") { - const tmp = - typeof val.constructor === "function" - ? Object.create(Object.getPrototypeOf(val)) - : {}; - for (const key in val) { - if (val.hasOwnProperty(key)) { - // don't copy non-serializable objects like these caches. They'll be - // populated when the element is rendered. - if (depth === 0 && (key === "shape" || key === "canvas")) { - continue; - } - tmp[key] = _deepCopyElement(val[key], depth + 1); - } - } - return tmp; - } - - if (Array.isArray(val)) { - let k = val.length; - const arr = new Array(k); - while (k--) { - arr[k] = _deepCopyElement(val[k], depth + 1); - } - return arr; - } - - // we're not cloning non-array & non-plain-object objects because we - // don't support them on excalidraw elements yet. If we do, we need to make - // sure we start cloning them, so let's warn about it. - if (import.meta.env.DEV) { - if ( - objectType !== "[object Object]" && - objectType !== "[object Array]" && - objectType.startsWith("[object ") - ) { - console.warn( - `_deepCloneElement: unexpected object type ${objectType}. This value will not be cloned!`, - ); - } - } - - return val; -}; - -/** - * Clones ExcalidrawElement data structure. Does not regenerate id, nonce, or - * any value. The purpose is to to break object references for immutability - * reasons, whenever we want to keep the original element, but ensure it's not - * mutated. - * - * Only clones plain objects and arrays. Doesn't clone Date, RegExp, Map, Set, - * Typed arrays and other non-null objects. - */ -export const deepCopyElement = ( - val: T, -): Mutable => { - return _deepCopyElement(val); -}; - -const __test__defineOrigId = (clonedObj: object, origId: string) => { - Object.defineProperty(clonedObj, ORIG_ID, { - value: origId, - writable: false, - enumerable: false, - }); -}; - -/** - * utility wrapper to generate new id. - */ -const regenerateId = () => { - return randomId(); -}; - -/** - * Duplicate an element, often used in the alt-drag operation. - * Note that this method has gotten a bit complicated since the - * introduction of gruoping/ungrouping elements. - * @param editingGroupId The current group being edited. The new - * element will inherit this group and its - * parents. - * @param groupIdMapForOperation A Map that maps old group IDs to - * duplicated ones. If you are duplicating - * multiple elements at once, share this map - * amongst all of them - * @param element Element to duplicate - * @param overrides Any element properties to override - */ -export const duplicateElement = ( - editingGroupId: AppState["editingGroupId"], - groupIdMapForOperation: Map, - element: TElement, - overrides?: Partial, -): Readonly => { - let copy = deepCopyElement(element); - - if (isTestEnv()) { - __test__defineOrigId(copy, element.id); - } - - copy.id = regenerateId(); - copy.boundElements = null; - copy.updated = getUpdatedTimestamp(); - copy.seed = randomInteger(); - copy.groupIds = getNewGroupIdsForDuplication( - copy.groupIds, - editingGroupId, - (groupId) => { - if (!groupIdMapForOperation.has(groupId)) { - groupIdMapForOperation.set(groupId, regenerateId()); - } - return groupIdMapForOperation.get(groupId)!; - }, - ); - if (overrides) { - copy = Object.assign(copy, overrides); - } - return copy; -}; - -/** - * Clones elements, regenerating their ids (including bindings) and group ids. - * - * If bindings don't exist in the elements array, they are removed. Therefore, - * it's advised to supply the whole elements array, or sets of elements that - * are encapsulated (such as library items), if the purpose is to retain - * bindings to the cloned elements intact. - * - * NOTE by default does not randomize or regenerate anything except the id. - */ -export const duplicateElements = ( - elements: readonly ExcalidrawElement[], - opts?: { - /** NOTE also updates version flags and `updated` */ - randomizeSeed: boolean; - }, -) => { - const clonedElements: ExcalidrawElement[] = []; - - const origElementsMap = arrayToMap(elements); - - // used for for migrating old ids to new ids - const elementNewIdsMap = new Map< - /* orig */ ExcalidrawElement["id"], - /* new */ ExcalidrawElement["id"] - >(); - - const maybeGetNewId = (id: ExcalidrawElement["id"]) => { - // if we've already migrated the element id, return the new one directly - if (elementNewIdsMap.has(id)) { - return elementNewIdsMap.get(id)!; - } - // if we haven't migrated the element id, but an old element with the same - // id exists, generate a new id for it and return it - if (origElementsMap.has(id)) { - const newId = regenerateId(); - elementNewIdsMap.set(id, newId); - return newId; - } - // if old element doesn't exist, return null to mark it for removal - return null; - }; - - const groupNewIdsMap = new Map(); - - for (const element of elements) { - const clonedElement: Mutable = _deepCopyElement(element); - - clonedElement.id = maybeGetNewId(element.id)!; - if (isTestEnv()) { - __test__defineOrigId(clonedElement, element.id); - } - - if (opts?.randomizeSeed) { - clonedElement.seed = randomInteger(); - bumpVersion(clonedElement); - } - - if (clonedElement.groupIds) { - clonedElement.groupIds = clonedElement.groupIds.map((groupId) => { - if (!groupNewIdsMap.has(groupId)) { - groupNewIdsMap.set(groupId, regenerateId()); - } - return groupNewIdsMap.get(groupId)!; - }); - } - - if ("containerId" in clonedElement && clonedElement.containerId) { - const newContainerId = maybeGetNewId(clonedElement.containerId); - clonedElement.containerId = newContainerId; - } - - if ("boundElements" in clonedElement && clonedElement.boundElements) { - clonedElement.boundElements = clonedElement.boundElements.reduce( - ( - acc: Mutable>, - binding, - ) => { - const newBindingId = maybeGetNewId(binding.id); - if (newBindingId) { - acc.push({ ...binding, id: newBindingId }); - } - return acc; - }, - [], - ); - } - - if ("endBinding" in clonedElement && clonedElement.endBinding) { - const newEndBindingId = maybeGetNewId(clonedElement.endBinding.elementId); - clonedElement.endBinding = newEndBindingId - ? { - ...clonedElement.endBinding, - elementId: newEndBindingId, - } - : null; - } - if ("startBinding" in clonedElement && clonedElement.startBinding) { - const newEndBindingId = maybeGetNewId( - clonedElement.startBinding.elementId, - ); - clonedElement.startBinding = newEndBindingId - ? { - ...clonedElement.startBinding, - elementId: newEndBindingId, - } - : null; - } - - if (clonedElement.frameId) { - clonedElement.frameId = maybeGetNewId(clonedElement.frameId); - } - - clonedElements.push(clonedElement); - } - - return clonedElements; -}; diff --git a/packages/excalidraw/element/textElement.ts b/packages/excalidraw/element/textElement.ts index 9893ba5d6..b8399f038 100644 --- a/packages/excalidraw/element/textElement.ts +++ b/packages/excalidraw/element/textElement.ts @@ -6,7 +6,7 @@ import { TEXT_ALIGN, VERTICAL_ALIGN, } from "../constants"; -import { getFontString, arrayToMap } from "../utils"; +import { getFontString } from "../utils"; import { resetOriginalContainerCache, @@ -112,48 +112,6 @@ export const redrawTextBoundingBox = ( mutateElement(textElement, boundTextUpdates, informMutation); }; -export const bindTextToShapeAfterDuplication = ( - newElements: ExcalidrawElement[], - oldElements: ExcalidrawElement[], - oldIdToDuplicatedId: Map, -): void => { - const newElementsMap = arrayToMap(newElements) as Map< - ExcalidrawElement["id"], - ExcalidrawElement - >; - oldElements.forEach((element) => { - const newElementId = oldIdToDuplicatedId.get(element.id) as string; - const boundTextElementId = getBoundTextElementId(element); - - if (boundTextElementId) { - const newTextElementId = oldIdToDuplicatedId.get(boundTextElementId); - if (newTextElementId) { - const newContainer = newElementsMap.get(newElementId); - if (newContainer) { - mutateElement(newContainer, { - boundElements: (element.boundElements || []) - .filter( - (boundElement) => - boundElement.id !== newTextElementId && - boundElement.id !== boundTextElementId, - ) - .concat({ - type: "text", - id: newTextElementId, - }), - }); - } - const newTextElement = newElementsMap.get(newTextElementId); - if (newTextElement && isTextElement(newTextElement)) { - mutateElement(newTextElement, { - containerId: newContainer ? newElementId : null, - }); - } - } - } - }); -}; - export const handleBindTextResize = ( container: NonDeletedExcalidrawElement, elementsMap: ElementsMap, diff --git a/packages/excalidraw/groups.ts b/packages/excalidraw/groups.ts index cedc4af0f..61b493fd4 100644 --- a/packages/excalidraw/groups.ts +++ b/packages/excalidraw/groups.ts @@ -214,7 +214,10 @@ export const isSelectedViaGroup = ( ) => getSelectedGroupForElement(appState, element) != null; export const getSelectedGroupForElement = ( - appState: InteractiveCanvasAppState, + appState: Pick< + InteractiveCanvasAppState, + "editingGroupId" | "selectedGroupIds" + >, element: ExcalidrawElement, ) => element.groupIds diff --git a/packages/excalidraw/renderer/interactiveScene.ts b/packages/excalidraw/renderer/interactiveScene.ts index 257decd62..b0971f9f2 100644 --- a/packages/excalidraw/renderer/interactiveScene.ts +++ b/packages/excalidraw/renderer/interactiveScene.ts @@ -886,23 +886,24 @@ const _renderInteractiveScene = ({ ); } - if ( - isElbowArrow(selectedElements[0]) && - appState.selectedLinearElement && - appState.selectedLinearElement.segmentMidPointHoveredCoords - ) { - renderElbowArrowMidPointHighlight(context, appState); - } else if ( - appState.selectedLinearElement && - appState.selectedLinearElement.hoverPointIndex >= 0 && - !( - isElbowArrow(selectedElements[0]) && - appState.selectedLinearElement.hoverPointIndex > 0 && - appState.selectedLinearElement.hoverPointIndex < - selectedElements[0].points.length - 1 - ) - ) { - renderLinearElementPointHighlight(context, appState, elementsMap); + // Arrows have a different highlight behavior when + // they are the only selected element + if (appState.selectedLinearElement) { + const editor = appState.selectedLinearElement; + const firstSelectedLinear = selectedElements.find( + (el) => el.id === editor.elementId, // Don't forget bound text elements! + ); + + if (editor.segmentMidPointHoveredCoords) { + renderElbowArrowMidPointHighlight(context, appState); + } else if ( + isElbowArrow(firstSelectedLinear) + ? editor.hoverPointIndex === 0 || + editor.hoverPointIndex === firstSelectedLinear.points.length - 1 + : editor.hoverPointIndex >= 0 + ) { + renderLinearElementPointHighlight(context, appState, elementsMap); + } } // Paint selected elements @@ -1073,7 +1074,7 @@ const _renderInteractiveScene = ({ const dashedLinePadding = (DEFAULT_TRANSFORM_HANDLE_SPACING * 2) / appState.zoom.value; context.fillStyle = oc.white; - const [x1, y1, x2, y2] = getCommonBounds(selectedElements); + const [x1, y1, x2, y2] = getCommonBounds(selectedElements, elementsMap); const initialLineDash = context.getLineDash(); context.setLineDash([2 / appState.zoom.value]); const lineWidth = context.lineWidth; diff --git a/packages/excalidraw/store.ts b/packages/excalidraw/store.ts index 8b0065884..1723d0aa1 100644 --- a/packages/excalidraw/store.ts +++ b/packages/excalidraw/store.ts @@ -2,10 +2,11 @@ import { getDefaultAppState } from "./appState"; import { AppStateChange, ElementsChange } from "./change"; import { ENV } from "./constants"; import { newElementWith } from "./element/mutateElement"; -import { deepCopyElement } from "./element/newElement"; import { Emitter } from "./emitter"; import { isShallowEqual } from "./utils"; +import { deepCopyElement } from "./element/duplicate"; + import type { OrderedExcalidrawElement } from "./element/types"; import type { AppState, ObservedAppState } from "./types"; import type { ValueOf } from "./utility-types"; diff --git a/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap index f77eb8ddb..89629b93e 100644 --- a/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/contextmenu.test.tsx.snap @@ -2606,8 +2606,8 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 4, - "versionNonce": 238820263, + "version": 5, + "versionNonce": 400692809, "width": 20, "x": 0, "y": 10, diff --git a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap index d740e975c..3f523d005 100644 --- a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap @@ -15172,9 +15172,11 @@ History { "selectedElementIds": { "id61": true, }, + "selectedLinearElementId": "id61", }, "inserted": { "selectedElementIds": {}, + "selectedLinearElementId": null, }, }, }, @@ -18946,7 +18948,7 @@ exports[`history > singleplayer undo/redo > should support duplication of groups "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 3, + "version": 4, "width": 100, "x": 10, "y": 10, @@ -18980,7 +18982,7 @@ exports[`history > singleplayer undo/redo > should support duplication of groups "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 3, + "version": 4, "width": 100, "x": 110, "y": 110, @@ -19014,7 +19016,7 @@ exports[`history > singleplayer undo/redo > should support duplication of groups "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 6, + "version": 7, "width": 100, "x": 10, "y": 10, @@ -19048,7 +19050,7 @@ exports[`history > singleplayer undo/redo > should support duplication of groups "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 6, + "version": 7, "width": 100, "x": 110, "y": 110, diff --git a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap index 90236a4dd..4001c3b17 100644 --- a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap @@ -11,7 +11,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 5`] = ` "groupIds": [], "height": 50, "id": "id2", - "index": "a0", + "index": "Zz", "isDeleted": false, "link": null, "locked": false, @@ -26,8 +26,8 @@ exports[`duplicate element on move when ALT is clicked > rectangle 5`] = ` "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 5, - "versionNonce": 400692809, + "version": 6, + "versionNonce": 1604849351, "width": 30, "x": 30, "y": 20, @@ -45,7 +45,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 6`] = ` "groupIds": [], "height": 50, "id": "id0", - "index": "a1", + "index": "a0", "isDeleted": false, "link": null, "locked": false, @@ -60,7 +60,7 @@ exports[`duplicate element on move when ALT is clicked > rectangle 6`] = ` "strokeWidth": 2, "type": "rectangle", "updated": 1, - "version": 6, + "version": 5, "versionNonce": 23633383, "width": 30, "x": -10, diff --git a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap index 5d48ead6c..4e9c659d0 100644 --- a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap @@ -2139,7 +2139,7 @@ History { "frameId": null, "groupIds": [], "height": 10, - "index": "a0", + "index": "Zz", "isDeleted": false, "link": null, "locked": false, @@ -2164,12 +2164,10 @@ History { "updated": Map { "id0" => Delta { "deleted": { - "index": "a1", "x": 20, "y": 20, }, "inserted": { - "index": "a0", "x": 10, "y": 10, }, @@ -10631,7 +10629,7 @@ History { "id7", ], "height": 10, - "index": "a0", + "index": "Zx", "isDeleted": false, "link": null, "locked": false, @@ -10664,7 +10662,7 @@ History { "id7", ], "height": 10, - "index": "a1", + "index": "Zy", "isDeleted": false, "link": null, "locked": false, @@ -10697,7 +10695,7 @@ History { "id7", ], "height": 10, - "index": "a2", + "index": "Zz", "isDeleted": false, "link": null, "locked": false, @@ -10722,36 +10720,30 @@ History { "updated": Map { "id0" => Delta { "deleted": { - "index": "a3", "x": 20, "y": 20, }, "inserted": { - "index": "a0", "x": 10, "y": 10, }, }, "id1" => Delta { "deleted": { - "index": "a4", "x": 40, "y": 20, }, "inserted": { - "index": "a1", "x": 30, "y": 10, }, }, "id2" => Delta { "deleted": { - "index": "a5", "x": 60, "y": 20, }, "inserted": { - "index": "a2", "x": 50, "y": 10, }, diff --git a/packages/excalidraw/tests/fractionalIndex.test.ts b/packages/excalidraw/tests/fractionalIndex.test.ts index dbd55bd92..e9eb576e7 100644 --- a/packages/excalidraw/tests/fractionalIndex.test.ts +++ b/packages/excalidraw/tests/fractionalIndex.test.ts @@ -1,7 +1,6 @@ /* eslint-disable no-lone-blocks */ import { generateKeyBetween } from "fractional-indexing"; -import { deepCopyElement } from "../element/newElement"; import { InvalidFractionalIndexError } from "../errors"; import { syncInvalidIndices, @@ -10,6 +9,8 @@ import { } from "../fractionalIndex"; import { arrayToMap } from "../utils"; +import { deepCopyElement } from "../element/duplicate"; + import { API } from "./helpers/api"; import type { ExcalidrawElement, FractionalIndex } from "../element/types"; diff --git a/packages/excalidraw/tests/regressionTests.test.tsx b/packages/excalidraw/tests/regressionTests.test.tsx index 8407f0766..42d726f1d 100644 --- a/packages/excalidraw/tests/regressionTests.test.tsx +++ b/packages/excalidraw/tests/regressionTests.test.tsx @@ -1184,3 +1184,7 @@ it( expect(API.getSelectedElements().length).toBe(1); }, ); + +// +// DEPRECATED: DO NOT ADD TESTS HERE +// From e1bb59fb8f115cd8e75fcaaeefa03a81b0fdc697 Mon Sep 17 00:00:00 2001 From: Mursaleen Nisar Date: Tue, 25 Mar 2025 00:14:00 +0530 Subject: [PATCH 07/12] chore: Use isDevEnv() and isTestEnv() (#9264) Signed-off-by: Mark Tolmacs Co-authored-by: Mark Tolmacs --- excalidraw-app/App.tsx | 3 ++- excalidraw-app/collab/Collab.tsx | 8 +++++--- excalidraw-app/components/AppMainMenu.tsx | 4 +++- packages/excalidraw/analytics.ts | 5 ++++- packages/excalidraw/change.ts | 15 ++++++++------- packages/excalidraw/charts.ts | 6 ++++-- packages/excalidraw/components/App.tsx | 6 +++--- .../excalidraw/components/Sidebar/Sidebar.tsx | 4 ++-- packages/excalidraw/data/reconcile.ts | 11 +++-------- packages/excalidraw/element/elbowArrow.ts | 4 ++-- packages/excalidraw/element/newElement.ts | 1 + packages/excalidraw/element/textWrapping.ts | 4 ++-- packages/excalidraw/i18n.ts | 4 +++- packages/excalidraw/scene/Scene.ts | 11 +++-------- packages/excalidraw/store.ts | 5 ++--- packages/excalidraw/utils.ts | 2 +- 16 files changed, 48 insertions(+), 45 deletions(-) diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx index dc399a7d0..7091d4bc5 100644 --- a/excalidraw-app/App.tsx +++ b/excalidraw-app/App.tsx @@ -36,6 +36,7 @@ import { preventUnload, resolvablePromise, isRunningInIframe, + isDevEnv, } from "@excalidraw/excalidraw/utils"; import { GithubIcon, @@ -383,7 +384,7 @@ const ExcalidrawWrapper = () => { const [, forceRefresh] = useState(false); useEffect(() => { - if (import.meta.env.DEV) { + if (isDevEnv()) { const debugState = loadSavedDebugState(); if (debugState.enabled && !window.visualDebug) { diff --git a/excalidraw-app/collab/Collab.tsx b/excalidraw-app/collab/Collab.tsx index 0964a7365..be889e2c7 100644 --- a/excalidraw-app/collab/Collab.tsx +++ b/excalidraw-app/collab/Collab.tsx @@ -6,7 +6,7 @@ import { reconcileElements, } from "@excalidraw/excalidraw"; import { ErrorDialog } from "@excalidraw/excalidraw/components/ErrorDialog"; -import { APP_NAME, ENV, EVENT } from "@excalidraw/excalidraw/constants"; +import { APP_NAME, EVENT } from "@excalidraw/excalidraw/constants"; import { IDLE_THRESHOLD, ACTIVE_THRESHOLD, @@ -24,6 +24,8 @@ import { t } from "@excalidraw/excalidraw/i18n"; import { withBatchedUpdates } from "@excalidraw/excalidraw/reactUtils"; import { assertNever, + isDevEnv, + isTestEnv, preventUnload, resolvablePromise, throttleRAF, @@ -240,7 +242,7 @@ class Collab extends PureComponent { appJotaiStore.set(collabAPIAtom, collabAPI); - if (import.meta.env.MODE === ENV.TEST || import.meta.env.DEV) { + if (isTestEnv() || isDevEnv()) { window.collab = window.collab || ({} as Window["collab"]); Object.defineProperties(window, { collab: { @@ -1013,7 +1015,7 @@ declare global { } } -if (import.meta.env.MODE === ENV.TEST || import.meta.env.DEV) { +if (isTestEnv() || isDevEnv()) { window.collab = window.collab || ({} as Window["collab"]); } diff --git a/excalidraw-app/components/AppMainMenu.tsx b/excalidraw-app/components/AppMainMenu.tsx index 996d6912d..4bc6bb471 100644 --- a/excalidraw-app/components/AppMainMenu.tsx +++ b/excalidraw-app/components/AppMainMenu.tsx @@ -6,6 +6,8 @@ import { import { MainMenu } from "@excalidraw/excalidraw/index"; import React from "react"; +import { isDevEnv } from "@excalidraw/excalidraw/utils"; + import type { Theme } from "@excalidraw/excalidraw/element/types"; import { LanguageList } from "../app-language/LanguageList"; @@ -57,7 +59,7 @@ export const AppMainMenu: React.FC<{ > {isExcalidrawPlusSignedUser ? "Sign in" : "Sign up"} - {import.meta.env.DEV && ( + {isDevEnv() && ( { diff --git a/packages/excalidraw/analytics.ts b/packages/excalidraw/analytics.ts index c8ab15b62..edcd07b58 100644 --- a/packages/excalidraw/analytics.ts +++ b/packages/excalidraw/analytics.ts @@ -1,4 +1,7 @@ // place here categories that you want to track. We want to track just a + +import { isDevEnv } from "./utils"; + // small subset of categories at a given time. const ALLOWED_CATEGORIES_TO_TRACK = new Set(["command_palette", "export"]); @@ -21,7 +24,7 @@ export const trackEvent = ( return; } - if (import.meta.env.DEV) { + if (isDevEnv()) { // comment out to debug in dev return; } diff --git a/packages/excalidraw/change.ts b/packages/excalidraw/change.ts index 206f3dcc7..492b7f79e 100644 --- a/packages/excalidraw/change.ts +++ b/packages/excalidraw/change.ts @@ -1,4 +1,3 @@ -import { ENV } from "./constants"; import { BoundElement, BindableElement, @@ -25,7 +24,9 @@ import { arrayToMap, arrayToObject, assertNever, + isDevEnv, isShallowEqual, + isTestEnv, toBrandedType, } from "./utils"; @@ -514,7 +515,7 @@ export class AppStateChange implements Change { // shouldn't really happen, but just in case console.error(`Couldn't apply appstate change`, e); - if (import.meta.env.DEV || import.meta.env.MODE === ENV.TEST) { + if (isTestEnv() || isDevEnv()) { throw e; } @@ -552,7 +553,7 @@ export class AppStateChange implements Change { // if postprocessing fails it does not make sense to bubble up, but let's make sure we know about it console.error(`Couldn't postprocess appstate change deltas.`); - if (import.meta.env.DEV || import.meta.env.MODE === ENV.TEST) { + if (isTestEnv() || isDevEnv()) { throw e; } } finally { @@ -842,7 +843,7 @@ export class ElementsChange implements Change { change = new ElementsChange(added, removed, updated); } - if (import.meta.env.DEV || import.meta.env.MODE === ENV.TEST) { + if (isTestEnv() || isDevEnv()) { ElementsChange.validate(change, "added", this.satisfiesAddition); ElementsChange.validate(change, "removed", this.satisfiesRemoval); ElementsChange.validate(change, "updated", this.satisfiesUpdate); @@ -1106,7 +1107,7 @@ export class ElementsChange implements Change { } catch (e) { console.error(`Couldn't apply elements change`, e); - if (import.meta.env.DEV || import.meta.env.MODE === ENV.TEST) { + if (isTestEnv() || isDevEnv()) { throw e; } @@ -1137,7 +1138,7 @@ export class ElementsChange implements Change { e, ); - if (import.meta.env.DEV || import.meta.env.MODE === ENV.TEST) { + if (isTestEnv() || isDevEnv()) { throw e; } } finally { @@ -1551,7 +1552,7 @@ export class ElementsChange implements Change { // if postprocessing fails, it does not make sense to bubble up, but let's make sure we know about it console.error(`Couldn't postprocess elements change deltas.`); - if (import.meta.env.DEV || import.meta.env.MODE === ENV.TEST) { + if (isTestEnv() || isDevEnv()) { throw e; } } finally { diff --git a/packages/excalidraw/charts.ts b/packages/excalidraw/charts.ts index e54c55f28..20fa8e87a 100644 --- a/packages/excalidraw/charts.ts +++ b/packages/excalidraw/charts.ts @@ -15,6 +15,8 @@ import { import { newElement, newLinearElement, newTextElement } from "./element"; import { randomId } from "./random"; +import { isDevEnv } from "./utils"; + import type { NonDeletedExcalidrawElement } from "./element/types"; export type ChartElements = readonly NonDeletedExcalidrawElement[]; @@ -373,7 +375,7 @@ const chartTypeBar = ( y, groupId, backgroundColor, - import.meta.env.DEV, + isDevEnv(), ), ]; }; @@ -455,7 +457,7 @@ const chartTypeLine = ( y, groupId, backgroundColor, - import.meta.env.DEV, + isDevEnv(), ), line, ...lines, diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 8239493ba..af8c13d7b 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -85,7 +85,6 @@ import { DRAGGING_THRESHOLD, ELEMENT_SHIFT_TRANSLATE_AMOUNT, ELEMENT_TRANSLATE_AMOUNT, - ENV, EVENT, FRAME_STYLE, IMAGE_MIME_TYPES, @@ -258,6 +257,7 @@ import { getDateTime, isShallowEqual, arrayToMap, + isDevEnv, } from "../utils"; import { createSrcDoc, @@ -2434,7 +2434,7 @@ class App extends React.Component { this.excalidrawContainerValue.container = this.excalidrawContainerRef.current; - if (import.meta.env.MODE === ENV.TEST || import.meta.env.DEV) { + if (isTestEnv() || isDevEnv()) { const setState = this.setState.bind(this); Object.defineProperties(window.h, { state: { @@ -11060,7 +11060,7 @@ declare global { } export const createTestHook = () => { - if (import.meta.env.MODE === ENV.TEST || import.meta.env.DEV) { + if (isTestEnv() || isDevEnv()) { window.h = window.h || ({} as Window["h"]); Object.defineProperties(window.h, { diff --git a/packages/excalidraw/components/Sidebar/Sidebar.tsx b/packages/excalidraw/components/Sidebar/Sidebar.tsx index 8a008fd9f..a702e9330 100644 --- a/packages/excalidraw/components/Sidebar/Sidebar.tsx +++ b/packages/excalidraw/components/Sidebar/Sidebar.tsx @@ -14,7 +14,7 @@ import { useUIAppState } from "../../context/ui-appState"; import { atom, useSetAtom } from "../../editor-jotai"; import { useOutsideClick } from "../../hooks/useOutsideClick"; import { KEYS } from "../../keys"; -import { updateObject } from "../../utils"; +import { isDevEnv, updateObject } from "../../utils"; import { useDevice, useExcalidrawSetAppState } from "../App"; import { Island } from "../Island"; @@ -52,7 +52,7 @@ export const SidebarInner = forwardRef( }: SidebarProps & Omit, "onSelect">, ref: React.ForwardedRef, ) => { - if (import.meta.env.DEV && onDock && docked == null) { + if (isDevEnv() && onDock && docked == null) { console.warn( "Sidebar: `docked` must be set when `onDock` is supplied for the sidebar to be user-dockable. To hide this message, either pass `docked` or remove `onDock`", ); diff --git a/packages/excalidraw/data/reconcile.ts b/packages/excalidraw/data/reconcile.ts index ef644c3aa..06e4a560b 100644 --- a/packages/excalidraw/data/reconcile.ts +++ b/packages/excalidraw/data/reconcile.ts @@ -1,12 +1,11 @@ import throttle from "lodash.throttle"; -import { ENV } from "../constants"; import { orderByFractionalIndex, syncInvalidIndices, validateFractionalIndices, } from "../fractionalIndex"; -import { arrayToMap } from "../utils"; +import { arrayToMap, isDevEnv, isTestEnv } from "../utils"; import type { OrderedExcalidrawElement } from "../element/types"; import type { AppState } from "../types"; @@ -47,11 +46,7 @@ const validateIndicesThrottled = throttle( localElements: readonly OrderedExcalidrawElement[], remoteElements: readonly RemoteExcalidrawElement[], ) => { - if ( - import.meta.env.DEV || - import.meta.env.MODE === ENV.TEST || - window?.DEBUG_FRACTIONAL_INDICES - ) { + if (isDevEnv() || isTestEnv() || window?.DEBUG_FRACTIONAL_INDICES) { // create new instances due to the mutation const elements = syncInvalidIndices( orderedElements.map((x) => ({ ...x })), @@ -59,7 +54,7 @@ const validateIndicesThrottled = throttle( validateFractionalIndices(elements, { // throw in dev & test only, to remain functional on `DEBUG_FRACTIONAL_INDICES` - shouldThrow: import.meta.env.DEV || import.meta.env.MODE === ENV.TEST, + shouldThrow: isTestEnv() || isDevEnv(), includeBoundTextValidation: true, reconciliationContext: { localElements, diff --git a/packages/excalidraw/element/elbowArrow.ts b/packages/excalidraw/element/elbowArrow.ts index a8c22abcc..c72357eb6 100644 --- a/packages/excalidraw/element/elbowArrow.ts +++ b/packages/excalidraw/element/elbowArrow.ts @@ -16,7 +16,7 @@ import { import BinaryHeap from "../binaryheap"; import { getSizeFromPoints } from "../points"; import { aabbForElement, pointInsideBounds } from "../shapes"; -import { invariant, isAnyTrue, tupleToCoors } from "../utils"; +import { invariant, isAnyTrue, isDevEnv, tupleToCoors } from "../utils"; import { bindPointToSnapToElementOutline, @@ -248,7 +248,7 @@ const handleSegmentRenormalization = ( ); } - import.meta.env.DEV && + isDevEnv() && invariant( validateElbowPoints(nextPoints), "Invalid elbow points with fixed segments", diff --git a/packages/excalidraw/element/newElement.ts b/packages/excalidraw/element/newElement.ts index 8d0a9bbd8..67dbed021 100644 --- a/packages/excalidraw/element/newElement.ts +++ b/packages/excalidraw/element/newElement.ts @@ -10,6 +10,7 @@ import { } from "../constants"; import { getLineHeight } from "../fonts"; import { randomInteger, randomId } from "../random"; + import { getFontString, getUpdatedTimestamp } from "../utils"; import { getResizedElementAbsoluteCoords } from "./bounds"; diff --git a/packages/excalidraw/element/textWrapping.ts b/packages/excalidraw/element/textWrapping.ts index 5df7051c0..4585b52c3 100644 --- a/packages/excalidraw/element/textWrapping.ts +++ b/packages/excalidraw/element/textWrapping.ts @@ -1,4 +1,4 @@ -import { ENV } from "../constants"; +import { isDevEnv, isTestEnv } from "../utils"; import { charWidth, getLineWidth } from "./textMeasurements"; @@ -562,7 +562,7 @@ const isSingleCharacter = (maybeSingleCharacter: string) => { * Invariant for the word wrapping algorithm. */ const satisfiesWordInvariant = (word: string) => { - if (import.meta.env.MODE === ENV.TEST || import.meta.env.DEV) { + if (isTestEnv() || isDevEnv()) { if (/\s/.test(word)) { throw new Error("Word should not contain any whitespaces!"); } diff --git a/packages/excalidraw/i18n.ts b/packages/excalidraw/i18n.ts index 47bae5979..095b418dd 100644 --- a/packages/excalidraw/i18n.ts +++ b/packages/excalidraw/i18n.ts @@ -2,6 +2,8 @@ import { useAtomValue, editorJotaiStore, atom } from "./editor-jotai"; import fallbackLangData from "./locales/en.json"; import percentages from "./locales/percentages.json"; +import { isDevEnv } from "./utils"; + import type { NestedKeyOf } from "./utility-types"; const COMPLETION_THRESHOLD = 85; @@ -73,7 +75,7 @@ export const languages: Language[] = [ ]; const TEST_LANG_CODE = "__test__"; -if (import.meta.env.DEV) { +if (isDevEnv()) { languages.unshift( { code: TEST_LANG_CODE, label: "test language" }, { diff --git a/packages/excalidraw/scene/Scene.ts b/packages/excalidraw/scene/Scene.ts index 0eab8b80e..f990e45af 100644 --- a/packages/excalidraw/scene/Scene.ts +++ b/packages/excalidraw/scene/Scene.ts @@ -1,6 +1,5 @@ import throttle from "lodash.throttle"; -import { ENV } from "../constants"; import { isNonDeletedElement } from "../element"; import { isFrameLikeElement } from "../element/typeChecks"; import { @@ -10,7 +9,7 @@ import { } from "../fractionalIndex"; import { getElementsInGroup } from "../groups"; import { randomInteger } from "../random"; -import { arrayToMap } from "../utils"; +import { arrayToMap, isDevEnv, isTestEnv } from "../utils"; import { toBrandedType } from "../utils"; import { getSelectedElements } from "./selection"; @@ -57,14 +56,10 @@ const getNonDeletedElements = ( const validateIndicesThrottled = throttle( (elements: readonly ExcalidrawElement[]) => { - if ( - import.meta.env.DEV || - import.meta.env.MODE === ENV.TEST || - window?.DEBUG_FRACTIONAL_INDICES - ) { + if (isDevEnv() || isTestEnv() || window?.DEBUG_FRACTIONAL_INDICES) { validateFractionalIndices(elements, { // throw only in dev & test, to remain functional on `DEBUG_FRACTIONAL_INDICES` - shouldThrow: import.meta.env.DEV || import.meta.env.MODE === ENV.TEST, + shouldThrow: isDevEnv() || isTestEnv(), includeBoundTextValidation: true, }); } diff --git a/packages/excalidraw/store.ts b/packages/excalidraw/store.ts index 1723d0aa1..83d670d43 100644 --- a/packages/excalidraw/store.ts +++ b/packages/excalidraw/store.ts @@ -1,9 +1,8 @@ import { getDefaultAppState } from "./appState"; import { AppStateChange, ElementsChange } from "./change"; -import { ENV } from "./constants"; import { newElementWith } from "./element/mutateElement"; import { Emitter } from "./emitter"; -import { isShallowEqual } from "./utils"; +import { isDevEnv, isShallowEqual, isTestEnv } from "./utils"; import { deepCopyElement } from "./element/duplicate"; @@ -257,7 +256,7 @@ export class Store implements IStore { const message = `There can be at most three store actions scheduled at the same time, but there are "${this.scheduledActions.size}".`; console.error(message, this.scheduledActions.values()); - if (import.meta.env.DEV || import.meta.env.MODE === ENV.TEST) { + if (isTestEnv() || isDevEnv()) { throw new Error(message); } } diff --git a/packages/excalidraw/utils.ts b/packages/excalidraw/utils.ts index 84f6637f1..18b6f2a93 100644 --- a/packages/excalidraw/utils.ts +++ b/packages/excalidraw/utils.ts @@ -169,7 +169,7 @@ export const throttleRAF = ( }; const ret = (...args: T) => { - if (import.meta.env.MODE === "test") { + if (isTestEnv()) { fn(...args); return; } From 6c3a434f2a8dd92d6ccaefb1b393bc3f164164d5 Mon Sep 17 00:00:00 2001 From: Mubaraq Wahab <48601635+mubaraqwahab@users.noreply.github.com> Date: Tue, 25 Mar 2025 14:32:15 +0100 Subject: [PATCH 08/12] docs: Fix table rendering and broken links in Props page (#9293) * Fix table rendering and broken links --------- Co-authored-by: Marcel Mraz --- dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx index e25aedcf4..5c2a5501b 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx @@ -3,7 +3,7 @@ All `props` are _optional_. | Name | Type | Default | Description | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| --- | --- | --- | --- | | [`initialData`](/docs/@excalidraw/excalidraw/api/props/initialdata) | `object` | `null` | Promise | `null` | The initial data with which app loads. | | [`excalidrawAPI`](/docs/@excalidraw/excalidraw/api/props/excalidraw-api) | `function` | \_ | Callback triggered with the excalidraw api once rendered | | [`isCollaborating`](#iscollaborating) | `boolean` | \_ | This indicates if the app is in `collaboration` mode | @@ -13,7 +13,7 @@ All `props` are _optional_. | [`onScrollChange`](#onscrollchange) | `function` | \_ | This prop if passed gets triggered when scrolling the canvas. | | [`onPaste`](#onpaste) | `function` | \_ | Callback to be triggered if passed when something is pasted into the scene | | [`onLibraryChange`](#onlibrarychange) | `function` | \_ | The callback if supplied is triggered when the library is updated and receives the library items. | -| [`generateLinkForSelection`](#generateLinkForSelection) | `function` | \_ | Allows you to override `url` generation when linking to Excalidraw elements. | +| [`generateLinkForSelection`](#generatelinkforselection) | `function` | \_ | Allows you to override `url` generation when linking to Excalidraw elements. | | [`onLinkOpen`](#onlinkopen) | `function` | \_ | The callback if supplied is triggered when any link is opened. | | [`langCode`](#langcode) | `string` | `en` | Language code string to be used in Excalidraw | | [`renderTopRightUI`](/docs/@excalidraw/excalidraw/api/props/render-props#rendertoprightui) | `function` | \_ | Render function that renders custom UI in top right corner | @@ -29,7 +29,7 @@ All `props` are _optional_. | [`handleKeyboardGlobally`](#handlekeyboardglobally) | `boolean` | `false` | Indicates whether to bind the keyboard events to document. | | [`autoFocus`](#autofocus) | `boolean` | `false` | Indicates whether to focus the Excalidraw component on page load | | [`generateIdForFile`](#generateidforfile) | `function` | \_ | Allows you to override `id` generation for files added on canvas | -| [`validateEmbeddable`](#validateEmbeddable) | string[] | `boolean | RegExp | RegExp[] | ((link: string) => boolean | undefined)` | \_ | use for custom src url validation | +| [`validateEmbeddable`](#validateembeddable) | `string[]` \| `boolean` \| `RegExp` \| `RegExp[]` \| ((link: string) => boolean | undefined) | \_ | use for custom src url validation | | [`renderEmbeddable`](/docs/@excalidraw/excalidraw/api/props/render-props#renderEmbeddable) | `function` | \_ | Render function that can override the built-in `