@excalidraw/excalidraw 0.17.1-1ed53b1 → 0.17.1-22b3927
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-JKPJV7MZ.js → chunk-Q6A4M3MN.js} +4 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-Q6A4M3MN.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-OKAZAA6U.js → chunk-VC7RRIDZ.js} +230 -93
- package/dist/browser/dev/excalidraw-assets-dev/chunk-VC7RRIDZ.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{dist-ITJNUBZF.js → dist-6QVAH5JA.js} +36 -14
- package/dist/browser/dev/excalidraw-assets-dev/dist-6QVAH5JA.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-BF4XUPIZ.js → en-Y27YPU72.js} +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-LVS32KQQ.js → image-J7S3ALXP.js} +2 -2
- package/dist/browser/dev/index.js +335 -116
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/chunk-CWO763YJ.js +55 -0
- package/dist/browser/prod/excalidraw-assets/{chunk-O4AI3NNG.js → chunk-IZMZ6RPD.js} +1 -1
- package/dist/browser/prod/excalidraw-assets/dist-567JAXHK.js +7 -0
- package/dist/browser/prod/excalidraw-assets/{en-N7CLNF6C.js → en-GSUSWMSH.js} +1 -1
- package/dist/browser/prod/excalidraw-assets/image-SZBFRCU2.js +1 -0
- package/dist/browser/prod/index.js +24 -24
- package/dist/dev/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
- package/dist/dev/index.js +576 -207
- package/dist/dev/index.js.map +4 -4
- package/dist/excalidraw/actions/actionAddToLibrary.d.ts +3 -3
- package/dist/excalidraw/actions/actionAlign.d.ts +6 -6
- package/dist/excalidraw/actions/actionBoundText.d.ts +3 -3
- package/dist/excalidraw/actions/actionBoundText.js +3 -1
- package/dist/excalidraw/actions/actionCanvas.d.ts +13 -13
- package/dist/excalidraw/actions/actionClipboard.d.ts +12 -12
- package/dist/excalidraw/actions/actionDeleteSelected.d.ts +3 -3
- package/dist/excalidraw/actions/actionDistribute.d.ts +2 -2
- package/dist/excalidraw/actions/actionDuplicateSelection.d.ts +1 -1
- package/dist/excalidraw/actions/actionElementLock.d.ts +2 -2
- package/dist/excalidraw/actions/actionExport.d.ts +11 -11
- package/dist/excalidraw/actions/actionFinalize.d.ts +2 -2
- package/dist/excalidraw/actions/actionFlip.d.ts +2 -2
- package/dist/excalidraw/actions/actionFrame.d.ts +312 -4
- package/dist/excalidraw/actions/actionGroup.d.ts +312 -2
- package/dist/excalidraw/actions/actionHistory.js +4 -4
- package/dist/excalidraw/actions/actionLinearEditor.d.ts +1 -1
- package/dist/excalidraw/actions/actionLink.d.ts +1 -1
- package/dist/excalidraw/actions/actionMenu.d.ts +3 -3
- package/dist/excalidraw/actions/actionNavigate.d.ts +2 -2
- package/dist/excalidraw/actions/actionProperties.d.ts +13 -13
- package/dist/excalidraw/actions/actionProperties.js +1 -1
- package/dist/excalidraw/actions/actionSelectAll.d.ts +1 -1
- package/dist/excalidraw/actions/actionStyles.d.ts +5 -2
- package/dist/excalidraw/actions/actionTextAutoResize.d.ts +17 -0
- package/dist/excalidraw/actions/actionTextAutoResize.js +38 -0
- package/dist/excalidraw/actions/actionToggleGridMode.d.ts +1 -1
- package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +1 -1
- package/dist/excalidraw/actions/actionToggleStats.d.ts +1 -1
- package/dist/excalidraw/actions/actionToggleViewMode.d.ts +1 -1
- package/dist/excalidraw/actions/actionToggleZenMode.d.ts +1 -1
- package/dist/excalidraw/actions/actionZindex.d.ts +4 -4
- package/dist/excalidraw/actions/types.d.ts +1 -1
- package/dist/excalidraw/change.js +13 -6
- package/dist/excalidraw/components/Actions.js +1 -1
- package/dist/excalidraw/components/App.d.ts +2 -2
- package/dist/excalidraw/components/App.js +133 -51
- package/dist/excalidraw/components/ButtonIconSelect.js +1 -1
- package/dist/excalidraw/components/CheckboxItem.js +1 -1
- package/dist/excalidraw/components/CommandPalette/CommandPalette.js +2 -2
- package/dist/excalidraw/components/ContextMenu.js +1 -1
- package/dist/excalidraw/components/Dialog.js +1 -1
- package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
- package/dist/excalidraw/components/IconPicker.js +2 -2
- package/dist/excalidraw/components/LayerUI.js +2 -2
- package/dist/excalidraw/components/MobileMenu.js +1 -1
- package/dist/excalidraw/components/PasteChartDialog.js +1 -1
- package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +3 -2
- package/dist/excalidraw/components/canvases/InteractiveCanvas.js +4 -2
- package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +1 -1
- package/dist/excalidraw/components/canvases/StaticCanvas.js +2 -2
- package/dist/excalidraw/components/icons.js +6 -2
- package/dist/excalidraw/constants.d.ts +1 -0
- package/dist/excalidraw/constants.js +5 -0
- package/dist/excalidraw/data/restore.js +3 -0
- package/dist/excalidraw/element/dragElements.d.ts +2 -2
- package/dist/excalidraw/element/dragElements.js +27 -3
- package/dist/excalidraw/element/embeddable.d.ts +1 -1
- package/dist/excalidraw/element/index.d.ts +1 -1
- package/dist/excalidraw/element/index.js +1 -1
- package/dist/excalidraw/element/mutateElement.d.ts +1 -1
- package/dist/excalidraw/element/mutateElement.js +5 -3
- package/dist/excalidraw/element/newElement.d.ts +2 -5
- package/dist/excalidraw/element/newElement.js +16 -14
- package/dist/excalidraw/element/resizeElements.js +73 -21
- package/dist/excalidraw/element/resizeTest.js +2 -4
- package/dist/excalidraw/element/textElement.d.ts +1 -0
- package/dist/excalidraw/element/textElement.js +11 -3
- package/dist/excalidraw/element/textWysiwyg.d.ts +10 -4
- package/dist/excalidraw/element/textWysiwyg.js +38 -17
- package/dist/excalidraw/element/transformHandles.js +0 -10
- package/dist/excalidraw/element/types.d.ts +7 -0
- package/dist/excalidraw/fractionalIndex.js +2 -4
- package/dist/excalidraw/locales/en.json +3 -1
- package/dist/excalidraw/mermaid.d.ts +2 -0
- package/dist/excalidraw/mermaid.js +28 -0
- package/dist/excalidraw/renderer/interactiveScene.d.ts +1 -1
- package/dist/excalidraw/renderer/interactiveScene.js +31 -5
- package/dist/excalidraw/renderer/renderElement.d.ts +2 -2
- package/dist/excalidraw/renderer/renderElement.js +2 -2
- package/dist/excalidraw/scene/Fonts.d.ts +1 -3
- package/dist/excalidraw/scene/Fonts.js +6 -12
- package/dist/excalidraw/scene/Renderer.d.ts +1 -1
- package/dist/excalidraw/scene/Renderer.js +2 -3
- package/dist/excalidraw/scene/Scene.d.ts +10 -4
- package/dist/excalidraw/scene/Scene.js +14 -8
- package/dist/excalidraw/scene/export.js +1 -1
- package/dist/excalidraw/scene/types.d.ts +2 -1
- package/dist/excalidraw/snapping.js +2 -1
- package/dist/excalidraw/store.d.ts +32 -2
- package/dist/excalidraw/store.js +27 -0
- package/dist/excalidraw/types.d.ts +1 -0
- package/dist/prod/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
- package/dist/prod/index.js +42 -42
- package/package.json +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-JKPJV7MZ.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/chunk-OKAZAA6U.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/dist-ITJNUBZF.js.map +0 -7
- package/dist/browser/prod/excalidraw-assets/chunk-SXBDZOS3.js +0 -55
- package/dist/browser/prod/excalidraw-assets/dist-54276HPL.js +0 -6
- package/dist/browser/prod/excalidraw-assets/image-VAGBVQ3G.js +0 -1
- /package/dist/browser/dev/excalidraw-assets-dev/{en-BF4XUPIZ.js.map → en-Y27YPU72.js.map} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{image-LVS32KQQ.js.map → image-J7S3ALXP.js.map} +0 -0
|
@@ -11,11 +11,12 @@ import { actions } from "../actions/register";
|
|
|
11
11
|
import { trackEvent } from "../analytics";
|
|
12
12
|
import { getDefaultAppState, isEraserActive, isHandToolActive, } from "../appState";
|
|
13
13
|
import { copyTextToSystemClipboard, parseClipboard } from "../clipboard";
|
|
14
|
-
import {
|
|
14
|
+
import { DEFAULT_FONT_SIZE } from "../constants";
|
|
15
|
+
import { APP_NAME, CURSOR_TYPE, DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT, DEFAULT_VERTICAL_ALIGN, DRAGGING_THRESHOLD, ELEMENT_SHIFT_TRANSLATE_AMOUNT, ELEMENT_TRANSLATE_AMOUNT, ENV, EVENT, FRAME_STYLE, GRID_SIZE, IMAGE_MIME_TYPES, IMAGE_RENDER_TIMEOUT, isBrave, LINE_CONFIRM_THRESHOLD, MAX_ALLOWED_FILE_BYTES, MIME_TYPES, MQ_MAX_HEIGHT_LANDSCAPE, MQ_MAX_WIDTH_LANDSCAPE, MQ_MAX_WIDTH_PORTRAIT, MQ_RIGHT_SIDEBAR_MIN_WIDTH, POINTER_BUTTON, ROUNDNESS, SCROLL_TIMEOUT, TAP_TWICE_TIMEOUT, TEXT_TO_CENTER_SNAP_THRESHOLD, THEME, THEME_FILTER, TOUCH_CTX_MENU_TIMEOUT, VERTICAL_ALIGN, YOUTUBE_STATES, ZOOM_STEP, POINTER_EVENTS, TOOL_TYPE, EDITOR_LS_KEYS, isIOS, supportsResizeObserver, DEFAULT_COLLISION_THRESHOLD, DEFAULT_TEXT_ALIGN, } from "../constants";
|
|
15
16
|
import { exportCanvas, loadFromBlob } from "../data";
|
|
16
17
|
import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library";
|
|
17
18
|
import { restore, restoreElements } from "../data/restore";
|
|
18
|
-
import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, getResizeArrowDirection, getResizeOffsetXY, getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords, isInvisiblySmallElement, isNonDeletedElement, isTextElement, newElement, newLinearElement, newTextElement, newImageElement, transformElements,
|
|
19
|
+
import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, getResizeArrowDirection, getResizeOffsetXY, getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords, isInvisiblySmallElement, isNonDeletedElement, isTextElement, newElement, newLinearElement, newTextElement, newImageElement, transformElements, refreshTextDimensions, redrawTextBoundingBox, getElementAbsoluteCoords, } from "../element";
|
|
19
20
|
import { bindOrUnbindLinearElement, bindOrUnbindLinearElements, fixBindingsAfterDeletion, fixBindingsAfterDuplication, getHoveredElementForBinding, isBindingEnabled, isLinearElementSimpleAndAlreadyBound, maybeBindLinearElement, shouldEnableBindingForPointerEvent, updateBoundElements, getSuggestedBindingsForArrows, } from "../element/binding";
|
|
20
21
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
|
21
22
|
import { mutateElement, newElementWith } from "../element/mutateElement";
|
|
@@ -44,7 +45,7 @@ import { dataURLToFile, generateIdFromFile, getDataURL, getFileFromEvent, ImageU
|
|
|
44
45
|
import { getInitializedImageElements, loadHTMLImageElement, normalizeSVG, updateImageCache as _updateImageCache, } from "../element/image";
|
|
45
46
|
import throttle from "lodash.throttle";
|
|
46
47
|
import { fileOpen } from "../data/filesystem";
|
|
47
|
-
import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getDefaultLineHeight, getLineHeightInPx, isMeasureTextSupported, isValidTextContainer, } from "../element/textElement";
|
|
48
|
+
import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getDefaultLineHeight, getLineHeightInPx, getMinTextElementWidth, isMeasureTextSupported, isValidTextContainer, measureText, wrapText, } from "../element/textElement";
|
|
48
49
|
import { showHyperlinkTooltip, hideHyperlinkToolip, Hyperlink, } from "../components/hyperlink/Hyperlink";
|
|
49
50
|
import { isLocalLink, normalizeLink, toValidURL } from "../data/url";
|
|
50
51
|
import { shouldShowBoundingBox } from "../element/transformHandles";
|
|
@@ -90,6 +91,9 @@ import { isOverScrollBars } from "../scene/scrollbars";
|
|
|
90
91
|
import { syncInvalidIndices, syncMovedIndices } from "../fractionalIndex";
|
|
91
92
|
import { isPointHittingLink, isPointHittingLinkIcon, } from "./hyperlink/helpers";
|
|
92
93
|
import { getShortcutFromShortcutName } from "../actions/shortcuts";
|
|
94
|
+
import { actionTextAutoResize } from "../actions/actionTextAutoResize";
|
|
95
|
+
import { getVisibleSceneBounds } from "../element/bounds";
|
|
96
|
+
import { isMaybeMermaidDefinition } from "../mermaid";
|
|
93
97
|
const AppContext = React.createContext(null);
|
|
94
98
|
const AppPropsContext = React.createContext(null);
|
|
95
99
|
const deviceContextInitialValue = {
|
|
@@ -286,10 +290,7 @@ class App extends React.Component {
|
|
|
286
290
|
container: this.excalidrawContainerRef.current,
|
|
287
291
|
id: this.id,
|
|
288
292
|
};
|
|
289
|
-
this.fonts = new Fonts({
|
|
290
|
-
scene: this.scene,
|
|
291
|
-
onSceneUpdated: this.onSceneUpdated,
|
|
292
|
-
});
|
|
293
|
+
this.fonts = new Fonts({ scene: this.scene });
|
|
293
294
|
this.history = new History();
|
|
294
295
|
this.actionManager.registerAll(actions);
|
|
295
296
|
this.actionManager.registerAction(createUndoAction(this.history, this.store));
|
|
@@ -444,7 +445,7 @@ class App extends React.Component {
|
|
|
444
445
|
return false;
|
|
445
446
|
});
|
|
446
447
|
if (updated) {
|
|
447
|
-
this.scene.
|
|
448
|
+
this.scene.triggerUpdate();
|
|
448
449
|
}
|
|
449
450
|
// GC
|
|
450
451
|
this.iFrameRefs.forEach((ref, id) => {
|
|
@@ -803,9 +804,9 @@ class App extends React.Component {
|
|
|
803
804
|
render() {
|
|
804
805
|
const selectedElements = this.scene.getSelectedElements(this.state);
|
|
805
806
|
const { renderTopRightUI, renderCustomStats } = this.props;
|
|
806
|
-
const
|
|
807
|
+
const sceneNonce = this.scene.getSceneNonce();
|
|
807
808
|
const { elementsMap, visibleElements } = this.renderer.getRenderableElements({
|
|
808
|
-
|
|
809
|
+
sceneNonce,
|
|
809
810
|
zoom: this.state.zoom,
|
|
810
811
|
offsetLeft: this.state.offsetLeft,
|
|
811
812
|
offsetTop: this.state.offsetTop,
|
|
@@ -874,14 +875,14 @@ class App extends React.Component {
|
|
|
874
875
|
this.focusContainer();
|
|
875
876
|
callback?.();
|
|
876
877
|
});
|
|
877
|
-
} })), _jsx(StaticCanvas, { canvas: this.canvas, rc: this.rc, elementsMap: elementsMap, allElementsMap: allElementsMap, visibleElements: visibleElements,
|
|
878
|
+
} })), _jsx(StaticCanvas, { canvas: this.canvas, rc: this.rc, elementsMap: elementsMap, allElementsMap: allElementsMap, visibleElements: visibleElements, sceneNonce: sceneNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, renderConfig: {
|
|
878
879
|
imageCache: this.imageCache,
|
|
879
880
|
isExporting: false,
|
|
880
881
|
renderGrid: true,
|
|
881
882
|
canvasBackgroundColor: this.state.viewBackgroundColor,
|
|
882
883
|
embedsValidationStatus: this.embedsValidationStatus,
|
|
883
884
|
elementsPendingErasure: this.elementsPendingErasure,
|
|
884
|
-
} }), _jsx(InteractiveCanvas, { containerRef: this.excalidrawContainerRef, canvas: this.interactiveCanvas, elementsMap: elementsMap, visibleElements: visibleElements, selectedElements: selectedElements,
|
|
885
|
+
} }), _jsx(InteractiveCanvas, { containerRef: this.excalidrawContainerRef, canvas: this.interactiveCanvas, elementsMap: elementsMap, visibleElements: visibleElements, allElementsMap: allElementsMap, selectedElements: selectedElements, sceneNonce: sceneNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, device: this.device, renderInteractiveSceneCallback: this.renderInteractiveSceneCallback, handleCanvasRef: this.handleInteractiveCanvasRef, onContextMenu: this.handleCanvasContextMenu, onPointerMove: this.handleCanvasPointerMove, onPointerUp: this.handleCanvasPointerUp, onPointerCancel: this.removePointer, onTouchMove: this.handleTouchMove, onPointerDown: this.handleCanvasPointerDown, onDoubleClick: this.handleCanvasDoubleClick }), this.state.userToFollow && (_jsx(FollowMode, { width: this.state.width, height: this.state.height, userToFollow: this.state.userToFollow, onDisconnect: this.maybeUnfollowRemoteUser })), this.renderFrameNames()] }), this.renderEmbeddables()] }) }) }) }) }) }) }) }));
|
|
885
886
|
}
|
|
886
887
|
focusContainer = () => {
|
|
887
888
|
this.excalidrawContainerRef.current?.focus();
|
|
@@ -931,7 +932,7 @@ class App extends React.Component {
|
|
|
931
932
|
mutateElement(frameElement, { customData: { generationData: data } }, false);
|
|
932
933
|
}
|
|
933
934
|
this.magicGenerations.set(frameElement.id, data);
|
|
934
|
-
this.
|
|
935
|
+
this.triggerRender();
|
|
935
936
|
};
|
|
936
937
|
getTextFromElements(elements) {
|
|
937
938
|
const text = elements
|
|
@@ -1441,7 +1442,7 @@ class App extends React.Component {
|
|
|
1441
1442
|
this.store.onStoreIncrementEmitter.on((increment) => {
|
|
1442
1443
|
this.history.record(increment.elementsChange, increment.appStateChange);
|
|
1443
1444
|
});
|
|
1444
|
-
this.scene.
|
|
1445
|
+
this.scene.onUpdate(this.triggerRender);
|
|
1445
1446
|
this.addEventListeners();
|
|
1446
1447
|
if (this.props.autoFocus && this.excalidrawContainerRef.current) {
|
|
1447
1448
|
this.focusContainer();
|
|
@@ -1479,6 +1480,7 @@ class App extends React.Component {
|
|
|
1479
1480
|
componentWillUnmount() {
|
|
1480
1481
|
this.renderer.destroy();
|
|
1481
1482
|
this.scene = new Scene();
|
|
1483
|
+
this.fonts = new Fonts({ scene: this.scene });
|
|
1482
1484
|
this.renderer = new Renderer(this.scene);
|
|
1483
1485
|
this.files = {};
|
|
1484
1486
|
this.imageCache.clear();
|
|
@@ -1534,7 +1536,7 @@ class App extends React.Component {
|
|
|
1534
1536
|
this.onRemoveEventListenersEmitter.once(addEventListener(document, EVENT.KEYDOWN, this.onKeyDown, false));
|
|
1535
1537
|
}
|
|
1536
1538
|
this.onRemoveEventListenersEmitter.once(addEventListener(this.excalidrawContainerRef.current, EVENT.WHEEL, this.onWheel, { passive: false }), addEventListener(window, EVENT.MESSAGE, this.onWindowMessage, false), addEventListener(document, EVENT.POINTER_UP, this.removePointer), // #3553
|
|
1537
|
-
addEventListener(document, EVENT.COPY, this.onCopy), addEventListener(document, EVENT.KEYUP, this.onKeyUp, { passive: true }), addEventListener(document, EVENT.
|
|
1539
|
+
addEventListener(document, EVENT.COPY, this.onCopy), addEventListener(document, EVENT.KEYUP, this.onKeyUp, { passive: true }), addEventListener(document, EVENT.POINTER_MOVE, this.updateCurrentCursorPosition),
|
|
1538
1540
|
// rerender text elements on font load to fix #637 && #1553
|
|
1539
1541
|
addEventListener(document.fonts, "loadingdone", (event) => {
|
|
1540
1542
|
const loadedFontFaces = event.fontfaces;
|
|
@@ -1543,6 +1545,9 @@ class App extends React.Component {
|
|
|
1543
1545
|
// Safari-only desktop pinch zoom
|
|
1544
1546
|
addEventListener(document, EVENT.GESTURE_START, this.onGestureStart, false), addEventListener(document, EVENT.GESTURE_CHANGE, this.onGestureChange, false), addEventListener(document, EVENT.GESTURE_END, this.onGestureEnd, false), addEventListener(window, EVENT.FOCUS, () => {
|
|
1545
1547
|
this.maybeCleanupAfterMissingPointerUp(null);
|
|
1548
|
+
// browsers (chrome?) tend to free up memory a lot, which results
|
|
1549
|
+
// in canvas context being cleared. Thus re-render on focus.
|
|
1550
|
+
this.triggerRender(true);
|
|
1546
1551
|
}));
|
|
1547
1552
|
if (this.state.viewModeEnabled) {
|
|
1548
1553
|
return;
|
|
@@ -1849,6 +1854,26 @@ class App extends React.Component {
|
|
|
1849
1854
|
});
|
|
1850
1855
|
}
|
|
1851
1856
|
else if (data.text) {
|
|
1857
|
+
if (data.text && isMaybeMermaidDefinition(data.text)) {
|
|
1858
|
+
const api = await import("@excalidraw/mermaid-to-excalidraw");
|
|
1859
|
+
try {
|
|
1860
|
+
const { elements: skeletonElements, files } = await api.parseMermaidToExcalidraw(data.text, {
|
|
1861
|
+
fontSize: DEFAULT_FONT_SIZE,
|
|
1862
|
+
});
|
|
1863
|
+
const elements = convertToExcalidrawElements(skeletonElements, {
|
|
1864
|
+
regenerateIds: true,
|
|
1865
|
+
});
|
|
1866
|
+
this.addElementsFromPasteOrLibrary({
|
|
1867
|
+
elements,
|
|
1868
|
+
files,
|
|
1869
|
+
position: "cursor",
|
|
1870
|
+
});
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
catch (err) {
|
|
1874
|
+
console.warn(`parsing pasted text as mermaid definition failed: ${err.message}`);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1852
1877
|
const nonEmptyLines = normalizeEOL(data.text)
|
|
1853
1878
|
.split(/\n+/)
|
|
1854
1879
|
.map((s) => s.trim())
|
|
@@ -2053,27 +2078,46 @@ class App extends React.Component {
|
|
|
2053
2078
|
text,
|
|
2054
2079
|
fontSize: this.state.currentItemFontSize,
|
|
2055
2080
|
fontFamily: this.state.currentItemFontFamily,
|
|
2056
|
-
textAlign:
|
|
2081
|
+
textAlign: DEFAULT_TEXT_ALIGN,
|
|
2057
2082
|
verticalAlign: DEFAULT_VERTICAL_ALIGN,
|
|
2058
2083
|
locked: false,
|
|
2059
2084
|
};
|
|
2085
|
+
const fontString = getFontString({
|
|
2086
|
+
fontSize: textElementProps.fontSize,
|
|
2087
|
+
fontFamily: textElementProps.fontFamily,
|
|
2088
|
+
});
|
|
2089
|
+
const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
|
|
2090
|
+
const [x1, , x2] = getVisibleSceneBounds(this.state);
|
|
2091
|
+
// long texts should not go beyond 800 pixels in width nor should it go below 200 px
|
|
2092
|
+
const maxTextWidth = Math.max(Math.min((x2 - x1) * 0.5, 800), 200);
|
|
2060
2093
|
const LINE_GAP = 10;
|
|
2061
2094
|
let currentY = y;
|
|
2062
2095
|
const lines = isPlainPaste ? [text] : text.split("\n");
|
|
2063
2096
|
const textElements = lines.reduce((acc, line, idx) => {
|
|
2064
|
-
const
|
|
2065
|
-
|
|
2066
|
-
if (text.length) {
|
|
2097
|
+
const originalText = line.trim();
|
|
2098
|
+
if (originalText.length) {
|
|
2067
2099
|
const topLayerFrame = this.getTopLayerFrameAtSceneCoords({
|
|
2068
2100
|
x,
|
|
2069
2101
|
y: currentY,
|
|
2070
2102
|
});
|
|
2103
|
+
let metrics = measureText(originalText, fontString, lineHeight);
|
|
2104
|
+
const isTextWrapped = metrics.width > maxTextWidth;
|
|
2105
|
+
const text = isTextWrapped
|
|
2106
|
+
? wrapText(originalText, fontString, maxTextWidth)
|
|
2107
|
+
: originalText;
|
|
2108
|
+
metrics = isTextWrapped
|
|
2109
|
+
? measureText(text, fontString, lineHeight)
|
|
2110
|
+
: metrics;
|
|
2111
|
+
const startX = x - metrics.width / 2;
|
|
2112
|
+
const startY = currentY - metrics.height / 2;
|
|
2071
2113
|
const element = newTextElement({
|
|
2072
2114
|
...textElementProps,
|
|
2073
|
-
x,
|
|
2074
|
-
y:
|
|
2115
|
+
x: startX,
|
|
2116
|
+
y: startY,
|
|
2075
2117
|
text,
|
|
2118
|
+
originalText,
|
|
2076
2119
|
lineHeight,
|
|
2120
|
+
autoResize: !isTextWrapped,
|
|
2077
2121
|
frameId: topLayerFrame ? topLayerFrame.id : null,
|
|
2078
2122
|
});
|
|
2079
2123
|
acc.push(element);
|
|
@@ -2292,7 +2336,7 @@ class App extends React.Component {
|
|
|
2292
2336
|
ShapeCache.delete(element);
|
|
2293
2337
|
}
|
|
2294
2338
|
});
|
|
2295
|
-
this.scene.
|
|
2339
|
+
this.scene.triggerUpdate();
|
|
2296
2340
|
this.addNewImagesToImageCache();
|
|
2297
2341
|
});
|
|
2298
2342
|
updateScene = withBatchedUpdates((sceneData) => {
|
|
@@ -2326,8 +2370,15 @@ class App extends React.Component {
|
|
|
2326
2370
|
this.setState({ collaborators: sceneData.collaborators });
|
|
2327
2371
|
}
|
|
2328
2372
|
});
|
|
2329
|
-
|
|
2330
|
-
|
|
2373
|
+
triggerRender = (
|
|
2374
|
+
/** force always re-renders canvas even if no change */
|
|
2375
|
+
force) => {
|
|
2376
|
+
if (force === true) {
|
|
2377
|
+
this.scene.triggerUpdate();
|
|
2378
|
+
}
|
|
2379
|
+
else {
|
|
2380
|
+
this.setState({});
|
|
2381
|
+
}
|
|
2331
2382
|
};
|
|
2332
2383
|
/**
|
|
2333
2384
|
* @returns whether the menu was toggled on or off
|
|
@@ -2772,15 +2823,16 @@ class App extends React.Component {
|
|
|
2772
2823
|
});
|
|
2773
2824
|
handleTextWysiwyg(element, { isExistingElement = false, }) {
|
|
2774
2825
|
const elementsMap = this.scene.getElementsMapIncludingDeleted();
|
|
2775
|
-
const updateElement = (
|
|
2826
|
+
const updateElement = (nextOriginalText, isDeleted) => {
|
|
2776
2827
|
this.scene.replaceAllElements([
|
|
2777
2828
|
// Not sure why we include deleted elements as well hence using deleted elements map
|
|
2778
2829
|
...this.scene.getElementsIncludingDeleted().map((_element) => {
|
|
2779
2830
|
if (_element.id === element.id && isTextElement(_element)) {
|
|
2780
|
-
return
|
|
2781
|
-
|
|
2782
|
-
isDeleted,
|
|
2783
|
-
|
|
2831
|
+
return newElementWith(_element, {
|
|
2832
|
+
originalText: nextOriginalText,
|
|
2833
|
+
isDeleted: isDeleted ?? _element.isDeleted,
|
|
2834
|
+
// returns (wrapped) text and new dimensions
|
|
2835
|
+
...refreshTextDimensions(_element, getContainerElement(_element, elementsMap), elementsMap, nextOriginalText),
|
|
2784
2836
|
});
|
|
2785
2837
|
}
|
|
2786
2838
|
return _element;
|
|
@@ -2800,15 +2852,15 @@ class App extends React.Component {
|
|
|
2800
2852
|
viewportY - this.state.offsetTop,
|
|
2801
2853
|
];
|
|
2802
2854
|
},
|
|
2803
|
-
onChange: withBatchedUpdates((
|
|
2804
|
-
updateElement(
|
|
2855
|
+
onChange: withBatchedUpdates((nextOriginalText) => {
|
|
2856
|
+
updateElement(nextOriginalText, false);
|
|
2805
2857
|
if (isNonDeletedElement(element)) {
|
|
2806
2858
|
updateBoundElements(element, elementsMap);
|
|
2807
2859
|
}
|
|
2808
2860
|
}),
|
|
2809
|
-
onSubmit: withBatchedUpdates(({
|
|
2810
|
-
const isDeleted = !
|
|
2811
|
-
updateElement(
|
|
2861
|
+
onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
|
|
2862
|
+
const isDeleted = !nextOriginalText.trim();
|
|
2863
|
+
updateElement(nextOriginalText, isDeleted);
|
|
2812
2864
|
// select the created text element only if submitting via keyboard
|
|
2813
2865
|
// (when submitting via click it should act as signal to deselect)
|
|
2814
2866
|
if (!isDeleted && viaKeyboard) {
|
|
@@ -2842,12 +2894,17 @@ class App extends React.Component {
|
|
|
2842
2894
|
element,
|
|
2843
2895
|
excalidrawContainer: this.excalidrawContainerRef.current,
|
|
2844
2896
|
app: this,
|
|
2897
|
+
// when text is selected, it's hard (at least on iOS) to re-position the
|
|
2898
|
+
// caret (i.e. deselect). There's not much use for always selecting
|
|
2899
|
+
// the text on edit anyway (and users can select-all from contextmenu
|
|
2900
|
+
// if needed)
|
|
2901
|
+
autoSelect: !this.device.isTouchScreen,
|
|
2845
2902
|
});
|
|
2846
2903
|
// deselect all other elements when inserting text
|
|
2847
2904
|
this.deselectElements();
|
|
2848
2905
|
// do an initial update to re-initialize element position since we were
|
|
2849
2906
|
// modifying element's x/y for sake of editor (case: syncing to remote)
|
|
2850
|
-
updateElement(element.
|
|
2907
|
+
updateElement(element.originalText, false);
|
|
2851
2908
|
}
|
|
2852
2909
|
deselectElements() {
|
|
2853
2910
|
this.setState({
|
|
@@ -3044,7 +3101,7 @@ class App extends React.Component {
|
|
|
3044
3101
|
}
|
|
3045
3102
|
return isTextBindableContainer(hitElement, false) ? hitElement : null;
|
|
3046
3103
|
}
|
|
3047
|
-
startTextEditing = ({ sceneX, sceneY, insertAtParentCenter = true, container, }) => {
|
|
3104
|
+
startTextEditing = ({ sceneX, sceneY, insertAtParentCenter = true, container, autoEdit = true, }) => {
|
|
3048
3105
|
let shouldBindToContainer = false;
|
|
3049
3106
|
let parentCenterPosition = insertAtParentCenter &&
|
|
3050
3107
|
this.getTextWysiwygSnappedToCenterPosition(sceneX, sceneY, this.state, container);
|
|
@@ -3145,12 +3202,17 @@ class App extends React.Component {
|
|
|
3145
3202
|
this.scene.insertElement(element);
|
|
3146
3203
|
}
|
|
3147
3204
|
}
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3205
|
+
if (autoEdit || existingTextElement || container) {
|
|
3206
|
+
this.handleTextWysiwyg(element, {
|
|
3207
|
+
isExistingElement: !!existingTextElement,
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
3210
|
+
else {
|
|
3211
|
+
this.setState({
|
|
3212
|
+
draggingElement: element,
|
|
3213
|
+
multiElement: null,
|
|
3214
|
+
});
|
|
3215
|
+
}
|
|
3154
3216
|
};
|
|
3155
3217
|
handleCanvasDoubleClick = (event) => {
|
|
3156
3218
|
// case: double-clicking with arrow/line tool selected would both create
|
|
@@ -3317,8 +3379,11 @@ class App extends React.Component {
|
|
|
3317
3379
|
}, state);
|
|
3318
3380
|
this.translateCanvas({
|
|
3319
3381
|
zoom: zoomState.zoom,
|
|
3320
|
-
|
|
3321
|
-
|
|
3382
|
+
// 2x multiplier is just a magic number that makes this work correctly
|
|
3383
|
+
// on touchscreen devices (note: if we get report that panning is slower/faster
|
|
3384
|
+
// than actual movement, consider swapping with devicePixelRatio)
|
|
3385
|
+
scrollX: zoomState.scrollX + 2 * (deltaX / nextZoom),
|
|
3386
|
+
scrollY: zoomState.scrollY + 2 * (deltaY / nextZoom),
|
|
3322
3387
|
shouldCacheIgnoreZoom: true,
|
|
3323
3388
|
});
|
|
3324
3389
|
});
|
|
@@ -3639,7 +3704,7 @@ class App extends React.Component {
|
|
|
3639
3704
|
}
|
|
3640
3705
|
}
|
|
3641
3706
|
this.elementsPendingErasure = new Set(this.elementsPendingErasure);
|
|
3642
|
-
this.
|
|
3707
|
+
this.triggerRender();
|
|
3643
3708
|
}
|
|
3644
3709
|
};
|
|
3645
3710
|
// set touch moving for mobile context menu
|
|
@@ -3850,7 +3915,6 @@ class App extends React.Component {
|
|
|
3850
3915
|
}
|
|
3851
3916
|
if (this.state.activeTool.type === "text") {
|
|
3852
3917
|
this.handleTextOnPointerDown(event, pointerDownState);
|
|
3853
|
-
return;
|
|
3854
3918
|
}
|
|
3855
3919
|
else if (this.state.activeTool.type === "arrow" ||
|
|
3856
3920
|
this.state.activeTool.type === "line") {
|
|
@@ -4398,6 +4462,7 @@ class App extends React.Component {
|
|
|
4398
4462
|
sceneY,
|
|
4399
4463
|
insertAtParentCenter: !event.altKey,
|
|
4400
4464
|
container,
|
|
4465
|
+
autoEdit: false,
|
|
4401
4466
|
});
|
|
4402
4467
|
resetCursor(this.interactiveCanvas);
|
|
4403
4468
|
if (!this.state.activeTool.locked) {
|
|
@@ -5288,6 +5353,21 @@ class App extends React.Component {
|
|
|
5288
5353
|
}
|
|
5289
5354
|
return;
|
|
5290
5355
|
}
|
|
5356
|
+
if (isTextElement(draggingElement)) {
|
|
5357
|
+
const minWidth = getMinTextElementWidth(getFontString({
|
|
5358
|
+
fontSize: draggingElement.fontSize,
|
|
5359
|
+
fontFamily: draggingElement.fontFamily,
|
|
5360
|
+
}), draggingElement.lineHeight);
|
|
5361
|
+
if (draggingElement.width < minWidth) {
|
|
5362
|
+
mutateElement(draggingElement, {
|
|
5363
|
+
autoResize: true,
|
|
5364
|
+
});
|
|
5365
|
+
}
|
|
5366
|
+
this.resetCursor();
|
|
5367
|
+
this.handleTextWysiwyg(draggingElement, {
|
|
5368
|
+
isExistingElement: true,
|
|
5369
|
+
});
|
|
5370
|
+
}
|
|
5291
5371
|
if (activeTool.type !== "selection" &&
|
|
5292
5372
|
draggingElement &&
|
|
5293
5373
|
isInvisiblySmallElement(draggingElement)) {
|
|
@@ -5323,7 +5403,7 @@ class App extends React.Component {
|
|
|
5323
5403
|
groupIds: [],
|
|
5324
5404
|
});
|
|
5325
5405
|
removeElementsFromFrame([linearElement], this.scene.getNonDeletedElementsMap());
|
|
5326
|
-
this.scene.
|
|
5406
|
+
this.scene.triggerUpdate();
|
|
5327
5407
|
}
|
|
5328
5408
|
}
|
|
5329
5409
|
}
|
|
@@ -5640,7 +5720,7 @@ class App extends React.Component {
|
|
|
5640
5720
|
}
|
|
5641
5721
|
restoreReadyToEraseElements = () => {
|
|
5642
5722
|
this.elementsPendingErasure = new Set();
|
|
5643
|
-
this.
|
|
5723
|
+
this.triggerRender();
|
|
5644
5724
|
};
|
|
5645
5725
|
eraseElements = () => {
|
|
5646
5726
|
let didChange = false;
|
|
@@ -5932,7 +6012,7 @@ class App extends React.Component {
|
|
|
5932
6012
|
if (uncachedImageElements.length) {
|
|
5933
6013
|
const { updatedFiles } = await this.updateImageCache(uncachedImageElements, files);
|
|
5934
6014
|
if (updatedFiles.size) {
|
|
5935
|
-
this.scene.
|
|
6015
|
+
this.scene.triggerUpdate();
|
|
5936
6016
|
}
|
|
5937
6017
|
}
|
|
5938
6018
|
};
|
|
@@ -6211,7 +6291,7 @@ class App extends React.Component {
|
|
|
6211
6291
|
}
|
|
6212
6292
|
if (draggingElement.type === "selection" &&
|
|
6213
6293
|
this.state.activeTool.type !== "eraser") {
|
|
6214
|
-
dragNewElement(draggingElement, this.state.activeTool.type, pointerDownState.origin.x, pointerDownState.origin.y, pointerCoords.x, pointerCoords.y, distance(pointerDownState.origin.x, pointerCoords.x), distance(pointerDownState.origin.y, pointerCoords.y), shouldMaintainAspectRatio(event), shouldResizeFromCenter(event));
|
|
6294
|
+
dragNewElement(draggingElement, this.state.activeTool.type, pointerDownState.origin.x, pointerDownState.origin.y, pointerCoords.x, pointerCoords.y, distance(pointerDownState.origin.x, pointerCoords.x), distance(pointerDownState.origin.y, pointerCoords.y), shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), this.state.zoom.value);
|
|
6215
6295
|
}
|
|
6216
6296
|
else {
|
|
6217
6297
|
let [gridX, gridY] = getGridPoint(pointerCoords.x, pointerCoords.y, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize);
|
|
@@ -6237,7 +6317,7 @@ class App extends React.Component {
|
|
|
6237
6317
|
});
|
|
6238
6318
|
dragNewElement(draggingElement, this.state.activeTool.type, pointerDownState.originInGrid.x, pointerDownState.originInGrid.y, gridX, gridY, distance(pointerDownState.originInGrid.x, gridX), distance(pointerDownState.originInGrid.y, gridY), isImageElement(draggingElement)
|
|
6239
6319
|
? !shouldMaintainAspectRatio(event)
|
|
6240
|
-
: shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), aspectRatio, this.state.originSnapOffset);
|
|
6320
|
+
: shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), this.state.zoom.value, aspectRatio, this.state.originSnapOffset);
|
|
6241
6321
|
// highlight elements that are to be added to frames on frames creation
|
|
6242
6322
|
if (this.state.activeTool.type === TOOL_TYPE.frame ||
|
|
6243
6323
|
this.state.activeTool.type === TOOL_TYPE.magicframe) {
|
|
@@ -6346,6 +6426,7 @@ class App extends React.Component {
|
|
|
6346
6426
|
return [actionCopy, ...options];
|
|
6347
6427
|
}
|
|
6348
6428
|
return [
|
|
6429
|
+
CONTEXT_MENU_SEPARATOR,
|
|
6349
6430
|
actionCut,
|
|
6350
6431
|
actionCopy,
|
|
6351
6432
|
actionPaste,
|
|
@@ -6358,6 +6439,7 @@ class App extends React.Component {
|
|
|
6358
6439
|
actionPasteStyles,
|
|
6359
6440
|
CONTEXT_MENU_SEPARATOR,
|
|
6360
6441
|
actionGroup,
|
|
6442
|
+
actionTextAutoResize,
|
|
6361
6443
|
actionUnbindText,
|
|
6362
6444
|
actionBindText,
|
|
6363
6445
|
actionWrapTextInContainer,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import clsx from "clsx";
|
|
3
3
|
// TODO: It might be "clever" to add option.icon to the existing component <ButtonSelect />
|
|
4
|
-
export const ButtonIconSelect = (props) => (_jsx("div", { className: "buttonList buttonListIcon", children: props.options.map((option) => props.type === "button" ? (_jsx("button", { onClick: (event) => props.onClick(option.value, event), className: clsx({
|
|
4
|
+
export const ButtonIconSelect = (props) => (_jsx("div", { className: "buttonList buttonListIcon", children: props.options.map((option) => props.type === "button" ? (_jsx("button", { type: "button", onClick: (event) => props.onClick(option.value, event), className: clsx({
|
|
5
5
|
active: option.active ?? props.value === option.value,
|
|
6
6
|
}), "data-testid": option.testId, title: option.text, children: option.icon }, option.text)) : (_jsxs("label", { className: clsx({ active: props.value === option.value }), title: option.text, children: [_jsx("input", { type: "radio", name: props.group, onChange: () => props.onChange(option.value), checked: props.value === option.value, "data-testid": option.testId }), option.icon] }, option.text))) }));
|
|
@@ -6,5 +6,5 @@ export const CheckboxItem = ({ children, checked, onChange, className }) => {
|
|
|
6
6
|
return (_jsxs("div", { className: clsx("Checkbox", className, { "is-checked": checked }), onClick: (event) => {
|
|
7
7
|
onChange(!checked, event);
|
|
8
8
|
event.currentTarget.querySelector(".Checkbox-box").focus();
|
|
9
|
-
}, children: [_jsx("button", { className: "Checkbox-box", role: "checkbox", "aria-checked": checked, children: checkIcon }), _jsx("div", { className: "Checkbox-label", children: children })] }));
|
|
9
|
+
}, children: [_jsx("button", { type: "button", className: "Checkbox-box", role: "checkbox", "aria-checked": checked, children: checkIcon }), _jsx("div", { className: "Checkbox-label", children: children })] }));
|
|
10
10
|
};
|
|
@@ -416,7 +416,7 @@ function CommandPaletteInner({ customCommandPaletteItems, }) {
|
|
|
416
416
|
...command,
|
|
417
417
|
icon: command.icon || boltIcon,
|
|
418
418
|
order: command.order ?? getCategoryOrder(command.category),
|
|
419
|
-
haystack: `${deburr(command.label)} ${command.keywords?.join(" ") || ""}`,
|
|
419
|
+
haystack: `${deburr(command.label.toLocaleLowerCase())} ${command.keywords?.join(" ") || ""}`,
|
|
420
420
|
};
|
|
421
421
|
});
|
|
422
422
|
setAllCommands(allCommands);
|
|
@@ -584,7 +584,7 @@ function CommandPaletteInner({ customCommandPaletteItems, }) {
|
|
|
584
584
|
setCurrentCommand(showLastUsed ? lastUsed : matchingCommands[0] || null);
|
|
585
585
|
return;
|
|
586
586
|
}
|
|
587
|
-
const _query = deburr(commandSearch.replace(/[
|
|
587
|
+
const _query = deburr(commandSearch.toLocaleLowerCase().replace(/[<>_| -]/g, ""));
|
|
588
588
|
matchingCommands = fuzzy
|
|
589
589
|
.filter(_query, matchingCommands, {
|
|
590
590
|
extract: (command) => command.haystack,
|
|
@@ -46,7 +46,7 @@ export const ContextMenu = React.memo(({ actionManager, items, top, left, onClos
|
|
|
46
46
|
onClose(() => {
|
|
47
47
|
actionManager.executeAction(item, "contextMenu");
|
|
48
48
|
});
|
|
49
|
-
}, children: _jsxs("button", { className: clsx("context-menu-item", {
|
|
49
|
+
}, children: _jsxs("button", { type: "button", className: clsx("context-menu-item", {
|
|
50
50
|
dangerous: actionName === "deleteSelectedElements",
|
|
51
51
|
checkmark: item.checked?.(appState),
|
|
52
52
|
}), children: [_jsx("div", { className: "context-menu-item__label", children: label }), _jsx("kbd", { className: "context-menu-item__shortcut", children: actionName
|
|
@@ -72,5 +72,5 @@ export const Dialog = (props) => {
|
|
|
72
72
|
};
|
|
73
73
|
return (_jsx(Modal, { className: clsx("Dialog", props.className, {
|
|
74
74
|
"Dialog--fullscreen": isFullscreen,
|
|
75
|
-
}), labelledBy: "dialog-title", maxWidth: getDialogSize(props.size), onCloseRequest: onClose, closeOnClickOutside: props.closeOnClickOutside, children: _jsxs(Island, { ref: setIslandNode, children: [props.title && (_jsx("h2", { id: `${id}-dialog-title`, className: "Dialog__title", children: _jsx("span", { className: "Dialog__titleContent", children: props.title }) })), isFullscreen && (_jsx("button", { className: "Dialog__close", onClick: onClose, title: t("buttons.close"), "aria-label": t("buttons.close"), children: CloseIcon })), _jsx("div", { className: "Dialog__content", children: props.children })] }) }));
|
|
75
|
+
}), labelledBy: "dialog-title", maxWidth: getDialogSize(props.size), onCloseRequest: onClose, closeOnClickOutside: props.closeOnClickOutside, children: _jsxs(Island, { ref: setIslandNode, children: [props.title && (_jsx("h2", { id: `${id}-dialog-title`, className: "Dialog__title", children: _jsx("span", { className: "Dialog__titleContent", children: props.title }) })), isFullscreen && (_jsx("button", { className: "Dialog__close", onClick: onClose, title: t("buttons.close"), "aria-label": t("buttons.close"), type: "button", children: CloseIcon })), _jsx("div", { className: "Dialog__content", children: props.children })] }) }));
|
|
76
76
|
};
|
|
@@ -2,6 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { CloseIcon } from "../icons";
|
|
3
3
|
import "./FollowMode.scss";
|
|
4
4
|
const FollowMode = ({ height, width, userToFollow, onDisconnect, }) => {
|
|
5
|
-
return (_jsx("div", { className: "follow-mode", style: { width, height }, children: _jsxs("div", { className: "follow-mode__badge", children: [_jsxs("div", { className: "follow-mode__badge__label", children: ["Following", " ", _jsx("span", { className: "follow-mode__badge__username", title: userToFollow.username, children: userToFollow.username })] }), _jsx("button", { onClick: onDisconnect, className: "follow-mode__disconnect-btn", children: CloseIcon })] }) }));
|
|
5
|
+
return (_jsx("div", { className: "follow-mode", style: { width, height }, children: _jsxs("div", { className: "follow-mode__badge", children: [_jsxs("div", { className: "follow-mode__badge__label", children: ["Following", " ", _jsx("span", { className: "follow-mode__badge__username", title: userToFollow.username, children: userToFollow.username })] }), _jsx("button", { type: "button", onClick: onDisconnect, className: "follow-mode__disconnect-btn", children: CloseIcon })] }) }));
|
|
6
6
|
};
|
|
7
7
|
export default FollowMode;
|
|
@@ -72,7 +72,7 @@ function Picker({ options, value, label, onChange, onClose, }) {
|
|
|
72
72
|
event.nativeEvent.stopImmediatePropagation();
|
|
73
73
|
event.stopPropagation();
|
|
74
74
|
};
|
|
75
|
-
return (_jsx("div", { className: `picker`, role: "dialog", "aria-modal": "true", "aria-label": label, onKeyDown: handleKeyDown, children: _jsx("div", { className: "picker-content", ref: rGallery, children: options.map((option, i) => (_jsxs("button", { className: clsx("picker-option", {
|
|
75
|
+
return (_jsx("div", { className: `picker`, role: "dialog", "aria-modal": "true", "aria-label": label, onKeyDown: handleKeyDown, children: _jsx("div", { className: "picker-content", ref: rGallery, children: options.map((option, i) => (_jsxs("button", { type: "button", className: clsx("picker-option", {
|
|
76
76
|
active: value === option.value,
|
|
77
77
|
}), onClick: (event) => {
|
|
78
78
|
event.currentTarget.focus();
|
|
@@ -92,7 +92,7 @@ export function IconPicker({ value, label, options, onChange, group = "", }) {
|
|
|
92
92
|
const [isActive, setActive] = React.useState(false);
|
|
93
93
|
const rPickerButton = React.useRef(null);
|
|
94
94
|
const isRTL = getLanguage().rtl;
|
|
95
|
-
return (_jsxs("div", { children: [_jsx("button", { name: group, className: isActive ? "active" : "", "aria-label": label, onClick: () => setActive(!isActive), ref: rPickerButton, children: options.find((option) => option.value === value)?.icon }), _jsx(React.Suspense, { fallback: "", children: isActive ? (_jsxs(_Fragment, { children: [_jsx(Popover, { onCloseRequest: (event) => event.target !== rPickerButton.current && setActive(false), ...(isRTL ? { right: 5.5 } : { left: -5.5 }), children: _jsx(Picker, { options: options.filter((opt) => opt.showInPicker !== false), value: value, label: label, onChange: onChange, onClose: () => {
|
|
95
|
+
return (_jsxs("div", { children: [_jsx("button", { name: group, type: "button", className: isActive ? "active" : "", "aria-label": label, onClick: () => setActive(!isActive), ref: rPickerButton, children: options.find((option) => option.value === value)?.icon }), _jsx(React.Suspense, { fallback: "", children: isActive ? (_jsxs(_Fragment, { children: [_jsx(Popover, { onCloseRequest: (event) => event.target !== rPickerButton.current && setActive(false), ...(isRTL ? { right: 5.5 } : { left: -5.5 }), children: _jsx(Picker, { options: options.filter((opt) => opt.showInPicker !== false), value: value, label: label, onChange: onChange, onClose: () => {
|
|
96
96
|
setActive(false);
|
|
97
97
|
rPickerButton.current?.focus();
|
|
98
98
|
} }) }), _jsx("div", { className: "picker-triangle" })] })) : null })] }));
|
|
@@ -126,7 +126,7 @@ const LayerUI = ({ actionManager, appState, files, setAppState, elements, canvas
|
|
|
126
126
|
}, false);
|
|
127
127
|
ShapeCache.delete(element);
|
|
128
128
|
}
|
|
129
|
-
Scene.getScene(selectedElements[0])?.
|
|
129
|
+
Scene.getScene(selectedElements[0])?.triggerUpdate();
|
|
130
130
|
}
|
|
131
131
|
else if (colorPickerType === "elementBackground") {
|
|
132
132
|
setAppState({
|
|
@@ -160,7 +160,7 @@ const LayerUI = ({ actionManager, appState, files, setAppState, elements, canvas
|
|
|
160
160
|
? { width: `calc(100% - ${LIBRARY_SIDEBAR_WIDTH}px)` }
|
|
161
161
|
: {}, children: [renderWelcomeScreen && _jsx(tunnels.WelcomeScreenCenterTunnel.Out, {}), renderFixedSideContainer(), _jsx(Footer, { appState: appState, actionManager: actionManager, showExitZenModeBtn: showExitZenModeBtn, renderWelcomeScreen: renderWelcomeScreen }), appState.showStats && (_jsx(Stats, { appState: appState, setAppState: setAppState, elements: elements, onClose: () => {
|
|
162
162
|
actionManager.executeAction(actionToggleStats);
|
|
163
|
-
}, renderCustomStats: renderCustomStats })), appState.scrolledOutside && (_jsx("button", { className: "scroll-back-to-content", onClick: () => {
|
|
163
|
+
}, renderCustomStats: renderCustomStats })), appState.scrolledOutside && (_jsx("button", { type: "button", className: "scroll-back-to-content", onClick: () => {
|
|
164
164
|
setAppState((appState) => ({
|
|
165
165
|
...calculateScrollCenter(elements, appState),
|
|
166
166
|
}));
|
|
@@ -37,7 +37,7 @@ export const MobileMenu = ({ appState, elements, actionManager, setAppState, onL
|
|
|
37
37
|
!appState.viewModeEnabled &&
|
|
38
38
|
showSelectedShapeActions(appState, elements) ? (_jsx(Section, { className: "App-mobile-menu", heading: "selectedShapeActions", children: _jsx(SelectedShapeActions, { appState: appState, elementsMap: app.scene.getNonDeletedElementsMap(), renderAction: actionManager.renderAction }) })) : null, _jsxs("footer", { className: "App-toolbar", children: [renderAppToolbar(), appState.scrolledOutside &&
|
|
39
39
|
!appState.openMenu &&
|
|
40
|
-
!appState.openSidebar && (_jsx("button", { className: "scroll-back-to-content", onClick: () => {
|
|
40
|
+
!appState.openSidebar && (_jsx("button", { type: "button", className: "scroll-back-to-content", onClick: () => {
|
|
41
41
|
setAppState((appState) => ({
|
|
42
42
|
...calculateScrollCenter(elements, appState),
|
|
43
43
|
}));
|
|
@@ -35,7 +35,7 @@ const ChartPreviewBtn = (props) => {
|
|
|
35
35
|
previewNode.replaceChildren();
|
|
36
36
|
};
|
|
37
37
|
}, [props.spreadsheet, props.chartType, props.selected]);
|
|
38
|
-
return (_jsx("button", { className: "ChartPreview", onClick: () => {
|
|
38
|
+
return (_jsx("button", { type: "button", className: "ChartPreview", onClick: () => {
|
|
39
39
|
if (chartElements) {
|
|
40
40
|
props.onClick(props.chartType, chartElements);
|
|
41
41
|
}
|
|
@@ -2,14 +2,15 @@ import React from "react";
|
|
|
2
2
|
import type { DOMAttributes } from "react";
|
|
3
3
|
import type { Device, InteractiveCanvasAppState } from "../../types";
|
|
4
4
|
import type { RenderableElementsMap, RenderInteractiveSceneCallback } from "../../scene/types";
|
|
5
|
-
import type { NonDeletedExcalidrawElement } from "../../element/types";
|
|
5
|
+
import type { NonDeletedExcalidrawElement, NonDeletedSceneElementsMap } from "../../element/types";
|
|
6
6
|
type InteractiveCanvasProps = {
|
|
7
7
|
containerRef: React.RefObject<HTMLDivElement>;
|
|
8
8
|
canvas: HTMLCanvasElement | null;
|
|
9
9
|
elementsMap: RenderableElementsMap;
|
|
10
10
|
visibleElements: readonly NonDeletedExcalidrawElement[];
|
|
11
11
|
selectedElements: readonly NonDeletedExcalidrawElement[];
|
|
12
|
-
|
|
12
|
+
allElementsMap: NonDeletedSceneElementsMap;
|
|
13
|
+
sceneNonce: number | undefined;
|
|
13
14
|
selectionNonce: number | undefined;
|
|
14
15
|
scale: number;
|
|
15
16
|
appState: InteractiveCanvasAppState;
|
|
@@ -49,6 +49,7 @@ const InteractiveCanvas = (props) => {
|
|
|
49
49
|
elementsMap: props.elementsMap,
|
|
50
50
|
visibleElements: props.visibleElements,
|
|
51
51
|
selectedElements: props.selectedElements,
|
|
52
|
+
allElementsMap: props.allElementsMap,
|
|
52
53
|
scale: window.devicePixelRatio,
|
|
53
54
|
appState: props.appState,
|
|
54
55
|
renderConfig: {
|
|
@@ -99,14 +100,15 @@ const getRelevantAppStateProps = (appState) => ({
|
|
|
99
100
|
activeEmbeddable: appState.activeEmbeddable,
|
|
100
101
|
snapLines: appState.snapLines,
|
|
101
102
|
zenModeEnabled: appState.zenModeEnabled,
|
|
103
|
+
editingElement: appState.editingElement,
|
|
102
104
|
});
|
|
103
105
|
const areEqual = (prevProps, nextProps) => {
|
|
104
106
|
// This could be further optimised if needed, as we don't have to render interactive canvas on each scene mutation
|
|
105
107
|
if (prevProps.selectionNonce !== nextProps.selectionNonce ||
|
|
106
|
-
prevProps.
|
|
108
|
+
prevProps.sceneNonce !== nextProps.sceneNonce ||
|
|
107
109
|
prevProps.scale !== nextProps.scale ||
|
|
108
110
|
// we need to memoize on elementsMap because they may have renewed
|
|
109
|
-
// even if
|
|
111
|
+
// even if sceneNonce didn't change (e.g. we filter elements out based
|
|
110
112
|
// on appState)
|
|
111
113
|
prevProps.elementsMap !== nextProps.elementsMap ||
|
|
112
114
|
prevProps.visibleElements !== nextProps.visibleElements ||
|