From e3f4f567b8d68b71a4837dd9f40dd58a7c33a701 Mon Sep 17 00:00:00 2001 From: zsviczian Date: Mon, 18 Sep 2023 17:10:28 +0200 Subject: [PATCH 1/7] feat: holding down CMD/CTRL will disable snap to grid when grid is active (#6983) Co-authored-by: dwelle --- src/components/App.tsx | 37 ++++++++++++++++++++---------- src/components/HintViewer.tsx | 31 ++++++++++++++++--------- src/element/linearElementEditor.ts | 17 +++++++------- src/locales/en.json | 3 ++- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 48eda7677..373ff7b66 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -3944,7 +3944,7 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( scenePointerX, scenePointerY, - this.state.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); const [lastCommittedX, lastCommittedY] = @@ -4761,7 +4761,11 @@ class App extends React.Component { origin, withCmdOrCtrl: event[KEYS.CTRL_OR_CMD], originInGrid: tupleToCoors( - getGridPoint(origin.x, origin.y, this.state.gridSize), + getGridPoint( + origin.x, + origin.y, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, + ), ), scrollbars: isOverScrollBars( currentScrollBars, @@ -5285,7 +5289,11 @@ class App extends React.Component { sceneY: number; link: string; }) => { - const [gridX, gridY] = getGridPoint(sceneX, sceneY, this.state.gridSize); + const [gridX, gridY] = getGridPoint( + sceneX, + sceneY, + this.lastPointerDown?.[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, + ); const embedLink = getEmbedLink(link); @@ -5331,7 +5339,11 @@ class App extends React.Component { sceneX: number; sceneY: number; }) => { - const [gridX, gridY] = getGridPoint(sceneX, sceneY, this.state.gridSize); + const [gridX, gridY] = getGridPoint( + sceneX, + sceneY, + this.lastPointerDown?.[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, + ); const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ x: gridX, @@ -5414,7 +5426,7 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( pointerDownState.origin.x, pointerDownState.origin.y, - this.state.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ @@ -5507,7 +5519,7 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( pointerDownState.origin.x, pointerDownState.origin.y, - this.state.gridSize, + this.lastPointerDown?.[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ @@ -5565,7 +5577,7 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( pointerDownState.origin.x, pointerDownState.origin.y, - this.state.gridSize, + this.lastPointerDown?.[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); const frame = newFrameElement({ @@ -5648,7 +5660,7 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( pointerCoords.x, pointerCoords.y, - this.state.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); // for arrows/lines, don't start dragging until a given threshold @@ -5694,6 +5706,7 @@ class App extends React.Component { this.state.selectedLinearElement, pointerCoords, this.state, + !event[KEYS.CTRL_OR_CMD], ); if (!ret) { return; @@ -5819,7 +5832,7 @@ class App extends React.Component { const [dragX, dragY] = getGridPoint( pointerCoords.x - pointerDownState.drag.offset.x, pointerCoords.y - pointerDownState.drag.offset.y, - this.state.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); const [dragDistanceX, dragDistanceY] = [ @@ -5886,7 +5899,7 @@ class App extends React.Component { const [originDragX, originDragY] = getGridPoint( pointerDownState.origin.x - pointerDownState.drag.offset.x, pointerDownState.origin.y - pointerDownState.drag.offset.y, - this.state.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); mutateElement(duplicatedElement, { x: duplicatedElement.x + (originDragX - dragX), @@ -7679,7 +7692,7 @@ class App extends React.Component { const [gridX, gridY] = getGridPoint( pointerCoords.x, pointerCoords.y, - this.state.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); const image = @@ -7748,7 +7761,7 @@ class App extends React.Component { const [resizeX, resizeY] = getGridPoint( pointerCoords.x - pointerDownState.resize.offset.x, pointerCoords.y - pointerDownState.resize.offset.y, - this.state.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, ); const frameElementsOffsetsMap = new Map< diff --git a/src/components/HintViewer.tsx b/src/components/HintViewer.tsx index 611f24925..18dba0e11 100644 --- a/src/components/HintViewer.tsx +++ b/src/components/HintViewer.tsx @@ -83,27 +83,36 @@ const getHints = ({ appState, isMobile, device, app }: HintViewerProps) => { if (activeTool.type === "selection") { if ( appState.draggingElement?.type === "selection" && + !selectedElements.length && !appState.editingElement && !appState.editingLinearElement ) { return t("hints.deepBoxSelect"); } + + if (appState.gridSize && appState.draggingElement) { + return t("hints.disableSnapping"); + } + if (!selectedElements.length && !isMobile) { return t("hints.canvasPanning"); } - } - if (selectedElements.length === 1) { - if (isLinearElement(selectedElements[0])) { - if (appState.editingLinearElement) { - return appState.editingLinearElement.selectedPointsIndices - ? t("hints.lineEditor_pointSelected") - : t("hints.lineEditor_nothingSelected"); + if (selectedElements.length === 1) { + if (isLinearElement(selectedElements[0])) { + if (appState.editingLinearElement) { + return appState.editingLinearElement.selectedPointsIndices + ? t("hints.lineEditor_pointSelected") + : t("hints.lineEditor_nothingSelected"); + } + return t("hints.lineEditor_info"); + } + if ( + !appState.draggingElement && + isTextBindableContainer(selectedElements[0]) + ) { + return t("hints.bindTextToElement"); } - return t("hints.lineEditor_info"); - } - if (isTextBindableContainer(selectedElements[0])) { - return t("hints.bindTextToElement"); } } diff --git a/src/element/linearElementEditor.ts b/src/element/linearElementEditor.ts index f0dee4faa..adc5aafc4 100644 --- a/src/element/linearElementEditor.ts +++ b/src/element/linearElementEditor.ts @@ -42,7 +42,7 @@ import { } from "./binding"; import { tupleToCoors } from "../utils"; import { isBindingElement } from "./typeChecks"; -import { shouldRotateWithDiscreteAngle } from "../keys"; +import { KEYS, shouldRotateWithDiscreteAngle } from "../keys"; import { getBoundTextElement, handleBindTextResize } from "./textElement"; import { DRAGGING_THRESHOLD } from "../constants"; import { Mutable } from "../utility-types"; @@ -221,7 +221,7 @@ export class LinearElementEditor { element, referencePoint, [scenePointerX, scenePointerY], - appState.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize, ); LinearElementEditor.movePoints(element, [ @@ -238,7 +238,7 @@ export class LinearElementEditor { element, scenePointerX - linearElementEditor.pointerOffset.x, scenePointerY - linearElementEditor.pointerOffset.y, - appState.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize, ); const deltaX = newDraggingPointPosition[0] - draggingPoint[0]; @@ -254,7 +254,7 @@ export class LinearElementEditor { element, scenePointerX - linearElementEditor.pointerOffset.x, scenePointerY - linearElementEditor.pointerOffset.y, - appState.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize, ) : ([ element.points[pointIndex][0] + deltaX, @@ -647,7 +647,7 @@ export class LinearElementEditor { element, scenePointer.x, scenePointer.y, - appState.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize, ), ], }); @@ -798,7 +798,7 @@ export class LinearElementEditor { element, lastCommittedPoint, [scenePointerX, scenePointerY], - appState.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize, ); newPoint = [ @@ -810,7 +810,7 @@ export class LinearElementEditor { element, scenePointerX - appState.editingLinearElement.pointerOffset.x, scenePointerY - appState.editingLinearElement.pointerOffset.y, - appState.gridSize, + event[KEYS.CTRL_OR_CMD] ? null : appState.gridSize, ); } @@ -1176,6 +1176,7 @@ export class LinearElementEditor { linearElementEditor: LinearElementEditor, pointerCoords: PointerCoords, appState: AppState, + snapToGrid: boolean, ) { const element = LinearElementEditor.getElement( linearElementEditor.elementId, @@ -1196,7 +1197,7 @@ export class LinearElementEditor { element, pointerCoords.x, pointerCoords.y, - appState.gridSize, + snapToGrid ? appState.gridSize : null, ); const points = [ ...element.points.slice(0, segmentMidpoint.index!), diff --git a/src/locales/en.json b/src/locales/en.json index 938535c5e..6799d0649 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -264,7 +264,8 @@ "bindTextToElement": "Press enter to add text", "deepBoxSelect": "Hold CtrlOrCmd to deep select, and to prevent dragging", "eraserRevert": "Hold Alt to revert the elements marked for deletion", - "firefox_clipboard_write": "This feature can likely be enabled by setting the \"dom.events.asyncClipboard.clipboardItem\" flag to \"true\". To change the browser flags in Firefox, visit the \"about:config\" page." + "firefox_clipboard_write": "This feature can likely be enabled by setting the \"dom.events.asyncClipboard.clipboardItem\" flag to \"true\". To change the browser flags in Firefox, visit the \"about:config\" page.", + "disableSnapping": "Hold CtrlOrCmd to disable snapping" }, "canvasError": { "cannotShowPreview": "Cannot show preview", From 4b11f43117519c37f34497e6c1b268f8e4c06326 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Tue, 19 Sep 2023 14:20:59 +0200 Subject: [PATCH 2/7] feat: zen-mode/go-to-plus button style tweaks (#7006) --- src/components/LayerUI.scss | 28 +++++++++++++++------ src/css/styles.scss | 11 -------- src/excalidraw-app/components/AppFooter.tsx | 8 ++++-- src/excalidraw-app/index.scss | 5 ++-- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/components/LayerUI.scss b/src/components/LayerUI.scss index b5c88cdff..73e392294 100644 --- a/src/components/LayerUI.scss +++ b/src/components/LayerUI.scss @@ -56,22 +56,36 @@ } .disable-zen-mode { - height: 30px; + padding: 10px; position: absolute; - bottom: 10px; + bottom: 0; [dir="ltr"] & { - right: 15px; + right: 1rem; } [dir="rtl"] & { - left: 15px; + left: 1rem; } - font-size: 10px; - padding: 10px; - font-weight: 500; opacity: 0; visibility: hidden; transition: visibility 0s linear 0s, opacity 0.5s; + font-family: var(--ui-font); + font-size: 0.75rem; + font-weight: 500; + line-height: 1; + + border-radius: var(--border-radius-lg); + border: 1px solid var(--default-border-color); + background-color: var(--island-bg-color); + color: var(--text-primary-color); + + &:hover { + background-color: var(--button-hover-bg); + } + &:active { + border-color: var(--color-primary); + } + &--visible { opacity: 1; visibility: visible; diff --git a/src/css/styles.scss b/src/css/styles.scss index d46ab1bc2..bb9063320 100644 --- a/src/css/styles.scss +++ b/src/css/styles.scss @@ -425,17 +425,6 @@ } } - .disable-zen-mode { - border-radius: var(--border-radius-lg); - background-color: var(--color-gray-20); - border: 1px solid var(--color-gray-30); - padding: 10px 20px; - - &:hover { - background-color: var(--color-gray-30); - } - } - .scroll-back-to-content { border-radius: var(--border-radius-lg); background-color: var(--island-bg-color); diff --git a/src/excalidraw-app/components/AppFooter.tsx b/src/excalidraw-app/components/AppFooter.tsx index 7011a1c4d..624873218 100644 --- a/src/excalidraw-app/components/AppFooter.tsx +++ b/src/excalidraw-app/components/AppFooter.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Footer } from "../../packages/excalidraw/index"; import { EncryptedIcon } from "./EncryptedIcon"; import { ExcalidrawPlusAppLink } from "./ExcalidrawPlusAppLink"; +import { isExcalidrawPlusSignedUser } from "../app_constants"; export const AppFooter = React.memo(() => { return ( @@ -13,8 +14,11 @@ export const AppFooter = React.memo(() => { alignItems: "center", }} > - - + {isExcalidrawPlusSignedUser ? ( + + ) : ( + + )} ); diff --git a/src/excalidraw-app/index.scss b/src/excalidraw-app/index.scss index 38c4c1a38..d7ab79836 100644 --- a/src/excalidraw-app/index.scss +++ b/src/excalidraw-app/index.scss @@ -77,13 +77,14 @@ align-items: center; border: 1px solid var(--color-primary); padding: 0.5rem 0.75rem; - border-radius: var(--space-factor); + border-radius: var(--border-radius-lg); + background-color: var(--island-bg-color); color: var(--color-primary) !important; text-decoration: none !important; font-size: 0.75rem; box-sizing: border-box; - height: var(--default-button-size); + height: var(--lg-button-size); &:hover { background-color: var(--color-primary); From 99dbc0acb9c73249b1e3ceb22e525afa2923ae2d Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Tue, 19 Sep 2023 18:28:53 +0530 Subject: [PATCH 3/7] docs: Add docs for Excalidraw Element Skeleton (#6879) * docs: Add docs for Excalidraw Element Skeleton * fix * upgrade package * tweaks * fix * tweak * Update dev-docs/docs/@excalidraw/excalidraw/api/excalidraw-element-skeleton.mdx Co-authored-by: David Luzar * fix --------- Co-authored-by: David Luzar --- .../api/excalidraw-element-skeleton.mdx | 429 ++++++++++++++++++ dev-docs/package.json | 2 +- dev-docs/sidebars.js | 8 +- dev-docs/yarn.lock | 8 +- 4 files changed, 436 insertions(+), 11 deletions(-) create mode 100644 dev-docs/docs/@excalidraw/excalidraw/api/excalidraw-element-skeleton.mdx diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/excalidraw-element-skeleton.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/excalidraw-element-skeleton.mdx new file mode 100644 index 000000000..3c1baeeaf --- /dev/null +++ b/dev-docs/docs/@excalidraw/excalidraw/api/excalidraw-element-skeleton.mdx @@ -0,0 +1,429 @@ +# Creating Elements programmatically + +We support a simplified API to make it easier to generate Excalidraw elements programmatically. This API is in beta and subject to change before stable. You can check the [PR](https://github.com/excalidraw/excalidraw/pull/6546) for more details. + +For this purpose we introduced a new type [`ExcalidrawElementSkeleton`](https://github.com/excalidraw/excalidraw/blob/master/src/data/transform.ts#L133). This is the simplified version of [`ExcalidrawElement`](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L134) type with the minimum possible attributes so that creating elements programmatically is much easier (especially for cases like binding arrows or creating text containers). + +The [`ExcalidrawElementSkeleton`](https://github.com/excalidraw/excalidraw/blob/master/src/data/transform.ts#L133) can be converted to fully qualified Excalidraw elements by using [`convertToExcalidrawElements`](/docs/@excalidraw/excalidraw/api/excalidraw-element-skeleton#converttoexcalidrawelements). + +## convertToExcalidrawElements + +**_Signature_** + +
+  convertToExcalidrawElements(elements:{" "}
+  
+    ExcalidrawElementSkeleton
+  
+  )
+
+ +**_How to use_** + +```js +import { convertToExcalidrawElements } from "@excalidraw/excalidraw"; +``` + +This function converts the Excalidraw Element Skeleton to excalidraw elements which could be then rendered on the canvas. Hence calling this function is necessary before passing it to APIs like [`initialData`](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/props/initialdata), [`updateScene`](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/props/ref#updatescene) if you are using the Skeleton API + +## Supported Features + +### Rectangle, Ellipse, and Diamond + +To create these shapes you need to pass its `type` and `x` and `y` coordinates for position. The rest of the attributes are optional_. + +For the Skeleton API to work, `convertToExcalidrawElements` needs to be called before passing it to Excalidraw Component via initialData, updateScene or any such API. + +```jsx live +function App() { + const elements = convertToExcalidrawElements([ + { + type: "rectangle", + x: 100, + y: 250, + }, + { + type: "ellipse", + x: 250, + y: 250, + }, + { + type: "diamond", + x: 380, + y: 250, + }, + ]); + return ( +
+ +
+ ); +} +``` + +You can pass additional [`properties`](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L27) as well to decorate the shapes. + +:::info + +You can copy the below test examples and replace the elements in the live editor above to test it out. + +::: + +```js +convertToExcalidrawElements([ + { + type: "rectangle", + x: 50, + y: 250, + width: 200, + height: 100, + backgroundColor: "#c0eb75", + strokeWidth: 2, + }, + { + type: "ellipse", + x: 300, + y: 250, + width: 200, + height: 100, + backgroundColor: "#ffc9c9", + strokeStyle: "dotted", + fillStyle: "solid", + strokeWidth: 2, + }, + { + type: "diamond", + x: 550, + y: 250, + width: 200, + height: 100, + backgroundColor: "#a5d8ff", + strokeColor: "#1971c2", + strokeStyle: "dashed", + fillStyle: "cross-hatch", + strokeWidth: 2, + }, +]); +``` + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/70ca7063-88fb-434c-838a-cd466e1bc3c2) + +### Text Element + +The `type`, `x`, `y` and `text` properties are required to create a text element, rest of the attributes are optional + +```js +convertToExcalidrawElements([ + { + type: "text", + x: 100, + y: 100, + text: "HELLO WORLD!", + }, + { + type: "text", + x: 100, + y: 150, + text: "STYLED HELLO WORLD!", + fontSize: 20, + strokeColor: "#5f3dc4", + }, +]); +``` + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/085c7ac3-7952-4f22-b9c3-6beb51438526) + +### Lines and Arrows + +The `type`, `x`, and `y` properties are required, rest of the attributes are optional + +```js +convertToExcalidrawElements([ + { + type: "arrow", + x: 100, + y: 20, + }, + { + type: "line", + x: 100, + y: 60, + }, +]); +``` + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/0c22a06b-b568-4ab5-9848-a5f0160f66a6) + +#### With Addtional properties + +```js +convertToExcalidrawElements([ + { + type: "arrow", + x: 450, + y: 20, + startArrowhead: "dot", + endArrowhead: "triangle", + strokeColor: "#1971c2", + strokeWidth: 2, + }, + { + type: "line", + x: 450, + y: 60, + strokeColor: "#2f9e44", + strokeWidth: 2, + strokeStyle: "dotted", + }, +]); +``` + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/14f1bf3f-ad81-4096-8c1c-f35235084ec5) + +### Text Containers + +In addition to `type`, `x` and `y` properties, [`label`](https://github.com/excalidraw/excalidraw/blob/master/src/data/transform.ts#L124C7-L130C59) property is required for text containers. The `text` property in `label` is required, rest of the attributes are optional. + +If you don't provide the dimensions of container, we calculate it based of the label dimensions. + +```js +convertToExcalidrawElements([ + { + type: "rectangle", + x: 300, + y: 290, + label: { + text: "RECTANGLE TEXT CONTAINER", + }, + }, + { + type: "ellipse", + x: 500, + y: 100, + label: { + text: "ELLIPSE\n TEXT CONTAINER", + }, + }, + { + type: "diamond", + x: 100, + y: 100, + label: { + text: "DIAMOND\nTEXT CONTAINER", + }, + }, +]); +``` + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/1e2c7b5d-fcb4-4f86-946d-0bfb0e97d532) + +#### With Additional properties + +```js +convertToExcalidrawElements([ + { + type: "diamond", + x: -120, + y: 100, + width: 270, + backgroundColor: "#fff3bf", + strokeWidth: 2, + label: { + text: "STYLED DIAMOND TEXT CONTAINER", + strokeColor: "#099268", + fontSize: 20, + }, + }, + { + type: "rectangle", + x: 180, + y: 150, + width: 200, + strokeColor: "#c2255c", + label: { + text: "TOP LEFT ALIGNED RECTANGLE TEXT CONTAINER", + textAlign: "left", + verticalAlign: "top", + fontSize: 20, + }, + }, + { + type: "ellipse", + x: 400, + y: 130, + strokeColor: "#f08c00", + backgroundColor: "#ffec99", + width: 200, + label: { + text: "STYLED ELLIPSE TEXT CONTAINER", + strokeColor: "#c2255c", + }, + }, +]); +``` + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/f8123cd1-c9aa-452d-b96b-05c846c5030d) + +### Labelled Arrows + +Similar to Text Containers, you can create labelled arrows as well. + +```js +convertToExcalidrawElements([ + { + type: "arrow", + x: 100, + y: 100, + label: { + text: "LABELED ARROW", + }, + }, + { + type: "arrow", + x: 100, + y: 200, + label: { + text: "STYLED LABELED ARROW", + strokeColor: "#099268", + fontSize: 20, + }, + }, + { + type: "arrow", + x: 100, + y: 300, + strokeColor: "#1098ad", + strokeWidth: 2, + label: { + text: "ANOTHER STYLED LABELLED ARROW", + }, + }, + { + type: "arrow", + x: 100, + y: 400, + strokeColor: "#1098ad", + strokeWidth: 2, + label: { + text: "ANOTHER STYLED LABELLED ARROW", + strokeColor: "#099268", + }, + }, +]); +``` + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/70635e9b-f1c8-4839-89e1-73b813abeb93) + +### Arrow bindings + +To bind arrow to a shape you need to specify its [`start`](https://github.com/excalidraw/excalidraw/blob/master/src/data/transform.ts#L86) and [`end`](https://github.com/excalidraw/excalidraw/blob/master/src/data/transform.ts#L54) properties. You need to pass either `type` or `id` property in `start` and `end` properties, rest of the attributes are optional + +```js +convertToExcalidrawElements([ + { + type: "arrow", + x: 255, + y: 239, + label: { + text: "HELLO WORLD!!", + }, + start: { + type: "rectangle", + }, + end: { + type: "ellipse", + }, + }, +]); +``` + +When position for `start` and `end ` properties are not specified, we compute it according to arrow position. + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/5aff09fd-b7e8-4c63-98be-da40b0698704) + +```js +convertToExcalidrawElements([ + { + type: "arrow", + x: 255, + y: 239, + label: { + text: "HELLO WORLD!!", + }, + start: { + type: "text", + text: "HEYYYYY", + }, + end: { + type: "text", + text: "WHATS UP ?", + }, + }, +]); +``` + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/2a9f03ac-e45c-4fbd-9be0-5d9f8c8e0343) + +#### When passing `id` + +Useful when you want to bind multiple arrows to one diagram / use some existing diagram + +```js +convertToExcalidrawElements([ + { + type: "ellipse", + id: "ellipse-1", + strokeColor: "#66a80f", + x: 390, + y: 356, + width: 150, + height: 150, + backgroundColor: "#d8f5a2", + }, + { + type: "diamond", + id: "diamond-1", + strokeColor: "#9c36b5", + width: 100, + x: -30, + y: 380, + }, + { + type: "arrow", + x: 100, + y: 440, + width: 295, + height: 35, + strokeColor: "#1864ab", + start: { + type: "rectangle", + width: 150, + height: 150, + }, + end: { + id: "ellipse-1", + }, + }, + { + type: "arrow", + x: 60, + y: 420, + width: 330, + strokeColor: "#e67700", + start: { + id: "diamond-1", + }, + end: { + id: "ellipse-1", + }, + }, +]); +``` + +![image](https://github.com/excalidraw/excalidraw/assets/11256141/a8b047c8-2eed-4aea-82a2-e1e6bbddb8d4) diff --git a/dev-docs/package.json b/dev-docs/package.json index bef57d76e..4601891c9 100644 --- a/dev-docs/package.json +++ b/dev-docs/package.json @@ -18,7 +18,7 @@ "@docusaurus/core": "2.2.0", "@docusaurus/preset-classic": "2.2.0", "@docusaurus/theme-live-codeblock": "2.2.0", - "@excalidraw/excalidraw": "0.15.2-6546-3398d86", + "@excalidraw/excalidraw": "0.15.2-eb020d0", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "docusaurus-plugin-sass": "0.2.3", diff --git a/dev-docs/sidebars.js b/dev-docs/sidebars.js index 86356aeff..257d16b54 100644 --- a/dev-docs/sidebars.js +++ b/dev-docs/sidebars.js @@ -81,12 +81,8 @@ const sidebars = { "@excalidraw/excalidraw/api/utils/restore", ], }, - { - type: "category", - label: "Constants", - link: { type: "doc", id: "@excalidraw/excalidraw/api/constants" }, - items: [], - }, + "@excalidraw/excalidraw/api/constants", + "@excalidraw/excalidraw/api/excalidraw-element-skeleton", ], }, "@excalidraw/excalidraw/faq", diff --git a/dev-docs/yarn.lock b/dev-docs/yarn.lock index 3ef8a9190..f06b9fb24 100644 --- a/dev-docs/yarn.lock +++ b/dev-docs/yarn.lock @@ -1631,10 +1631,10 @@ url-loader "^4.1.1" webpack "^5.73.0" -"@excalidraw/excalidraw@0.15.2-6546-3398d86": - version "0.15.2-6546-3398d86" - resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.15.2-6546-3398d86.tgz#e74d5ad944b8b414924d27ee91469a32b4f08dbf" - integrity sha512-Tzq6qighJUytXRA8iMzQ8onoGclo9CuvPSw7DMvPxME8nxAxn5CeK/gsxIs3zwooj9CC6XF42BSrx0+n+fPxaQ== +"@excalidraw/excalidraw@0.15.2-eb020d0": + version "0.15.2-eb020d0" + resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.15.2-eb020d0.tgz#25bd61e6f23da7c084fb16a3e0fe0dd9ad8e6533" + integrity sha512-TKGLzpOVqFQcwK1GFKTDXgg1s2U6tc5KE3qXuv87osbzOtftQn3x4+VH61vwdj11l00nEN80SMdXUC43T9uJqQ== "@hapi/hoek@^9.0.0": version "9.3.0" From 3d617958cc194b0e994f65c04bd81427fabc7345 Mon Sep 17 00:00:00 2001 From: David Luzar Date: Tue, 19 Sep 2023 16:01:40 +0200 Subject: [PATCH 4/7] fix: improperly disabling UI pointer-events on canvas interaction (#7005) Co-authored-by: Aakansha Doshi --- src/components/App.tsx | 21 +++++++++++++++---- src/components/FixedSideContainer.scss | 2 +- src/components/LayerUI.scss | 10 ++++++--- src/components/LayerUI.tsx | 10 ++------- src/components/Sidebar/Sidebar.scss | 2 ++ src/components/Stats.scss | 2 +- src/components/UserList.scss | 2 +- src/components/footer/Footer.tsx | 2 +- src/components/footer/FooterCenter.scss | 3 ++- .../welcome-screen/WelcomeScreen.scss | 6 +++--- src/constants.ts | 8 +++++++ src/css/styles.scss | 14 ++++++------- .../components/AppWelcomeScreen.tsx | 3 ++- 13 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 373ff7b66..367c8f7f6 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -85,6 +85,7 @@ import { VERTICAL_ALIGN, YOUTUBE_STATES, ZOOM_STEP, + POINTER_EVENTS, } from "../constants"; import { exportCanvas, loadFromBlob } from "../data"; import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library"; @@ -857,7 +858,9 @@ class App extends React.Component { width: isVisible ? `${el.width}px` : 0, height: isVisible ? `${el.height}px` : 0, transform: isVisible ? `rotate(${el.angle}rad)` : "none", - pointerEvents: isActive ? "auto" : "none", + pointerEvents: isActive + ? POINTER_EVENTS.enabled + : POINTER_EVENTS.disabled, }} > {isHovered && ( @@ -1081,9 +1084,9 @@ class App extends React.Component { whiteSpace: "nowrap", textOverflow: "ellipsis", cursor: CURSOR_TYPE.MOVE, - // disable all interaction (e.g. cursor change) when in view - // mode - pointerEvents: this.state.viewModeEnabled ? "none" : "all", + pointerEvents: this.state.viewModeEnabled + ? POINTER_EVENTS.disabled + : POINTER_EVENTS.inheritFromUI, }} onPointerDown={(event) => this.handleCanvasPointerDown(event)} onWheel={(event) => this.handleWheel(event)} @@ -1125,6 +1128,16 @@ class App extends React.Component { "excalidraw--view-mode": this.state.viewModeEnabled, "excalidraw--mobile": this.device.isMobile, })} + style={{ + ["--ui-pointerEvents" as any]: + this.state.selectionElement || + this.state.draggingElement || + this.state.resizingElement || + (this.state.editingElement && + !isTextElement(this.state.editingElement)) + ? POINTER_EVENTS.disabled + : POINTER_EVENTS.enabled, + }} ref={this.excalidrawContainerRef} onDrop={this.handleAppOnDrop} tabIndex={0} diff --git a/src/components/FixedSideContainer.scss b/src/components/FixedSideContainer.scss index a485d6d25..62d77d505 100644 --- a/src/components/FixedSideContainer.scss +++ b/src/components/FixedSideContainer.scss @@ -7,7 +7,7 @@ } .FixedSideContainer > * { - pointer-events: all; + pointer-events: var(--ui-pointerEvents); } .FixedSideContainer_side_top { diff --git a/src/components/LayerUI.scss b/src/components/LayerUI.scss index 73e392294..8898b0f83 100644 --- a/src/components/LayerUI.scss +++ b/src/components/LayerUI.scss @@ -91,13 +91,17 @@ visibility: visible; transition: visibility 0s linear 300ms, opacity 0.5s; transition-delay: 0.8s; + + pointer-events: var(--ui-pointerEvents); } } .layer-ui__wrapper__footer-left, - .layer-ui__wrapper__footer-right, - .disable-zen-mode--visible { - pointer-events: all; + .footer-center, + .layer-ui__wrapper__footer-right { + & > * { + pointer-events: var(--ui-pointerEvents); + } } .layer-ui__wrapper__footer-right { diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index 26be77aef..02f360e25 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -2,7 +2,7 @@ import clsx from "clsx"; import React from "react"; import { ActionManager } from "../actions/manager"; import { CLASSES, DEFAULT_SIDEBAR, LIBRARY_SIDEBAR_WIDTH } from "../constants"; -import { isTextElement, showSelectedShapeActions } from "../element"; +import { showSelectedShapeActions } from "../element"; import { NonDeletedExcalidrawElement } from "../element/types"; import { Language, t } from "../i18n"; import { calculateScrollCenter } from "../scene"; @@ -427,13 +427,7 @@ const LayerUI = ({ {!device.isMobile && ( <>
* { - pointer-events: all; + pointer-events: var(--ui-pointerEvents); } .UserList_mobile { diff --git a/src/components/footer/Footer.tsx b/src/components/footer/Footer.tsx index 173ea451a..85844aad6 100644 --- a/src/components/footer/Footer.tsx +++ b/src/components/footer/Footer.tsx @@ -73,7 +73,7 @@ const Footer = ({
diff --git a/src/components/footer/FooterCenter.scss b/src/components/footer/FooterCenter.scss index 1e17db6c5..ce6565922 100644 --- a/src/components/footer/FooterCenter.scss +++ b/src/components/footer/FooterCenter.scss @@ -1,10 +1,11 @@ .footer-center { pointer-events: none; & > * { - pointer-events: all; + pointer-events: var(--ui-pointerEvents); } display: flex; width: 100%; justify-content: flex-start; + margin-inline-end: 0.6rem; } diff --git a/src/components/welcome-screen/WelcomeScreen.scss b/src/components/welcome-screen/WelcomeScreen.scss index a1b88d8bd..8f1a09bf3 100644 --- a/src/components/welcome-screen/WelcomeScreen.scss +++ b/src/components/welcome-screen/WelcomeScreen.scss @@ -161,7 +161,7 @@ .welcome-screen-menu-item { box-sizing: border-box; - pointer-events: all; + pointer-events: var(--ui-pointerEvents); color: var(--color-gray-50); font-size: 0.875rem; @@ -202,7 +202,7 @@ } } - &:not(:active) .welcome-screen-menu-item:hover { + .welcome-screen-menu-item:hover { text-decoration: none; background: var(--color-gray-10); @@ -246,7 +246,7 @@ } } - &:not(:active) .welcome-screen-menu-item:hover { + .welcome-screen-menu-item:hover { background: var(--color-gray-85); .welcome-screen-menu-item__shortcut { diff --git a/src/constants.ts b/src/constants.ts index 46ae25b40..90fc66758 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -41,6 +41,14 @@ export const POINTER_BUTTON = { TOUCH: -1, } as const; +export const POINTER_EVENTS = { + enabled: "all", + disabled: "none", + // asserted as any so it can be freely assigned to React Element + // "pointerEnvets" CSS prop + inheritFromUI: "var(--ui-pointerEvents)" as any, +} as const; + export enum EVENT { COPY = "copy", PASTE = "paste", diff --git a/src/css/styles.scss b/src/css/styles.scss index bb9063320..4ca58af4f 100644 --- a/src/css/styles.scss +++ b/src/css/styles.scss @@ -253,7 +253,7 @@ max-height: 100%; display: flex; flex-direction: column; - pointer-events: initial; + pointer-events: var(--ui-pointerEvents); .panelColumn { padding: 8px 8px 0 8px; @@ -301,7 +301,7 @@ pointer-events: none !important; & > * { - pointer-events: all; + pointer-events: var(--ui-pointerEvents); } } @@ -312,16 +312,16 @@ cursor: default; pointer-events: none !important; + & > * { + pointer-events: var(--ui-pointerEvents); + } + @media (min-width: 1536px) { grid-template-columns: 1fr 1fr 1fr; grid-gap: 3rem; } } - .layer-ui__wrapper:not(.disable-pointerEvents) .App-menu_top > * { - pointer-events: all; - } - .App-menu_top > *:first-child { justify-self: flex-start; } @@ -436,7 +436,7 @@ left: 50%; bottom: 30px; transform: translateX(-50%); - pointer-events: all; + pointer-events: var(--ui-pointerEvents); font-family: inherit; &:hover { diff --git a/src/excalidraw-app/components/AppWelcomeScreen.tsx b/src/excalidraw-app/components/AppWelcomeScreen.tsx index 699c3ba88..72b688d9b 100644 --- a/src/excalidraw-app/components/AppWelcomeScreen.tsx +++ b/src/excalidraw-app/components/AppWelcomeScreen.tsx @@ -3,6 +3,7 @@ import { PlusPromoIcon } from "../../components/icons"; import { useI18n } from "../../i18n"; import { WelcomeScreen } from "../../packages/excalidraw/index"; import { isExcalidrawPlusSignedUser } from "../app_constants"; +import { POINTER_EVENTS } from "../../constants"; export const AppWelcomeScreen: React.FC<{ setCollabDialogShown: (toggle: boolean) => any; @@ -18,7 +19,7 @@ export const AppWelcomeScreen: React.FC<{ if (bit === "Excalidraw+") { return ( Date: Tue, 19 Sep 2023 19:59:53 +0530 Subject: [PATCH 5/7] =?UTF-8?q?docs:=20release=20@excalidraw/excalidraw@0.?= =?UTF-8?q?16.0=20=20=F0=9F=8E=89=20(#6993)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/excalidraw/CHANGELOG.md | 253 ++++++++++++++++++++++++--- src/packages/excalidraw/package.json | 2 +- 2 files changed, 233 insertions(+), 22 deletions(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index 4d06ecc9b..6b87f8335 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -11,28 +11,10 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> -## Unreleased - -### renderEmbeddable - -```tsx -(element: NonDeletedExcalidrawElement, radius: number, appState: UIAppState) => JSX.Element | null;` -``` - -The renderEmbeddable function allows you to customize the rendering of a JSX component instead of using the default `