@excalidraw/excalidraw 0.18.0-de715913e → 0.18.0-f29e9df

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/dev/index.js CHANGED
@@ -189,6 +189,7 @@ import {
189
189
  randomInteger as randomInteger4,
190
190
  CLASSES as CLASSES7,
191
191
  Emitter as Emitter3,
192
+ isMobile,
192
193
  MINIMUM_ARROW_SIZE
193
194
  } from "@excalidraw/common";
194
195
  import {
@@ -3121,7 +3122,9 @@ var actionDeleteSelected = register({
3121
3122
  elements: nextElements,
3122
3123
  appState: {
3123
3124
  ...nextAppState,
3124
- activeTool: updateActiveTool(appState, { type: "selection" }),
3125
+ activeTool: updateActiveTool(appState, {
3126
+ type: app.defaultSelectionTool
3127
+ }),
3125
3128
  multiElement: null,
3126
3129
  activeEmbeddable: null,
3127
3130
  selectedLinearElement: null
@@ -7377,7 +7380,7 @@ var actionClearCanvas = register({
7377
7380
  gridModeEnabled: appState.gridModeEnabled,
7378
7381
  stats: appState.stats,
7379
7382
  pasteDialog: appState.pasteDialog,
7380
- activeTool: appState.activeTool.type === "image" ? { ...appState.activeTool, type: "selection" } : appState.activeTool
7383
+ activeTool: appState.activeTool.type === "image" ? { ...appState.activeTool, type: app.defaultSelectionTool } : appState.activeTool
7381
7384
  },
7382
7385
  captureUpdate: CaptureUpdateAction6.IMMEDIATELY
7383
7386
  };
@@ -7676,12 +7679,12 @@ var actionToggleEraserTool = register({
7676
7679
  name: "toggleEraserTool",
7677
7680
  label: "toolBar.eraser",
7678
7681
  trackEvent: { category: "toolbar" },
7679
- perform: (elements, appState) => {
7682
+ perform: (elements, appState, _, app) => {
7680
7683
  let activeTool;
7681
7684
  if (isEraserActive(appState)) {
7682
7685
  activeTool = updateActiveTool2(appState, {
7683
7686
  ...appState.activeTool.lastActiveTool || {
7684
- type: "selection"
7687
+ type: app.defaultSelectionTool
7685
7688
  },
7686
7689
  lastActiveToolBeforeEraser: null
7687
7690
  });
@@ -7709,6 +7712,9 @@ var actionToggleLassoTool = register({
7709
7712
  label: "toolBar.lasso",
7710
7713
  icon: LassoIcon,
7711
7714
  trackEvent: { category: "toolbar" },
7715
+ predicate: (elements, appState, props, app) => {
7716
+ return app.defaultSelectionTool !== "lasso";
7717
+ },
7712
7718
  perform: (elements, appState, _, app) => {
7713
7719
  let activeTool;
7714
7720
  if (appState.activeTool.type !== "lasso") {
@@ -7992,13 +7998,13 @@ var actionFinalize = register({
7992
7998
  if (appState.activeTool.type === "eraser") {
7993
7999
  activeTool = updateActiveTool4(appState, {
7994
8000
  ...appState.activeTool.lastActiveTool || {
7995
- type: "selection"
8001
+ type: app.defaultSelectionTool
7996
8002
  },
7997
8003
  lastActiveToolBeforeEraser: null
7998
8004
  });
7999
8005
  } else {
8000
8006
  activeTool = updateActiveTool4(appState, {
8001
- type: "selection"
8007
+ type: app.defaultSelectionTool
8002
8008
  });
8003
8009
  }
8004
8010
  return {
@@ -14918,7 +14924,7 @@ var LibraryUnit = memo(
14918
14924
  };
14919
14925
  }, [svg]);
14920
14926
  const [isHovered, setIsHovered] = useState15(false);
14921
- const isMobile = useDevice().editor.isMobile;
14927
+ const isMobile2 = useDevice().editor.isMobile;
14922
14928
  const adder = isPending && /* @__PURE__ */ jsx63("div", { className: "library-unit__adder", children: PlusIcon });
14923
14929
  return /* @__PURE__ */ jsxs33(
14924
14930
  "div",
@@ -14958,7 +14964,7 @@ var LibraryUnit = memo(
14958
14964
  }
14959
14965
  ),
14960
14966
  adder,
14961
- id && elements && (isHovered || isMobile || selected) && /* @__PURE__ */ jsx63(
14967
+ id && elements && (isHovered || isMobile2 || selected) && /* @__PURE__ */ jsx63(
14962
14968
  CheckboxItem,
14963
14969
  {
14964
14970
  checked: selected,
@@ -15791,8 +15797,20 @@ var SHAPES = [
15791
15797
  fillable: false
15792
15798
  }
15793
15799
  ];
15794
- var findShapeByKey = (key) => {
15795
- const shape = SHAPES.find((shape2, index) => {
15800
+ var getToolbarTools = (app) => {
15801
+ return app.defaultSelectionTool === "lasso" ? [
15802
+ {
15803
+ value: "lasso",
15804
+ icon: SelectionIcon,
15805
+ key: KEYS33.V,
15806
+ numericKey: KEYS33["1"],
15807
+ fillable: true
15808
+ },
15809
+ ...SHAPES.slice(1)
15810
+ ] : SHAPES;
15811
+ };
15812
+ var findShapeByKey = (key, app) => {
15813
+ const shape = getToolbarTools(app).find((shape2, index) => {
15796
15814
  return shape2.numericKey != null && key === shape2.numericKey.toString() || shape2.key && (typeof shape2.key === "string" ? shape2.key === key : shape2.key.includes(key));
15797
15815
  });
15798
15816
  return shape?.value || null;
@@ -15968,58 +15986,60 @@ var ShapesSwitcher = ({
15968
15986
  const [isExtraToolsMenuOpen, setIsExtraToolsMenuOpen] = useState21(false);
15969
15987
  const frameToolSelected = activeTool.type === "frame";
15970
15988
  const laserToolSelected = activeTool.type === "laser";
15971
- const lassoToolSelected = activeTool.type === "lasso";
15989
+ const lassoToolSelected = activeTool.type === "lasso" && app.defaultSelectionTool !== "lasso";
15972
15990
  const embeddableToolSelected = activeTool.type === "embeddable";
15973
15991
  const { TTDDialogTriggerTunnel } = useTunnels();
15974
15992
  return /* @__PURE__ */ jsxs39(Fragment11, { children: [
15975
- SHAPES.map(({ value, icon, key, numericKey, fillable }, index) => {
15976
- if (UIOptions.tools?.[value] === false) {
15977
- return null;
15978
- }
15979
- const label = t(`toolBar.${value}`);
15980
- const letter = key && capitalizeString(typeof key === "string" ? key : key[0]);
15981
- const shortcut = letter ? `${letter} ${t("helpDialog.or")} ${numericKey}` : `${numericKey}`;
15982
- return /* @__PURE__ */ jsx71(
15983
- ToolButton,
15984
- {
15985
- className: clsx31("Shape", { fillable }),
15986
- type: "radio",
15987
- icon,
15988
- checked: activeTool.type === value,
15989
- name: "editor-current-shape",
15990
- title: `${capitalizeString(label)} \u2014 ${shortcut}`,
15991
- keyBindingLabel: numericKey || letter,
15992
- "aria-label": capitalizeString(label),
15993
- "aria-keyshortcuts": shortcut,
15994
- "data-testid": `toolbar-${value}`,
15995
- onPointerDown: ({ pointerType }) => {
15996
- if (!appState.penDetected && pointerType === "pen") {
15997
- app.togglePenMode(true);
15998
- }
15999
- if (value === "selection") {
16000
- if (appState.activeTool.type === "selection") {
16001
- app.setActiveTool({ type: "lasso" });
15993
+ getToolbarTools(app).map(
15994
+ ({ value, icon, key, numericKey, fillable }, index) => {
15995
+ if (UIOptions.tools?.[value] === false) {
15996
+ return null;
15997
+ }
15998
+ const label = t(`toolBar.${value}`);
15999
+ const letter = key && capitalizeString(typeof key === "string" ? key : key[0]);
16000
+ const shortcut = letter ? `${letter} ${t("helpDialog.or")} ${numericKey}` : `${numericKey}`;
16001
+ return /* @__PURE__ */ jsx71(
16002
+ ToolButton,
16003
+ {
16004
+ className: clsx31("Shape", { fillable }),
16005
+ type: "radio",
16006
+ icon,
16007
+ checked: activeTool.type === value,
16008
+ name: "editor-current-shape",
16009
+ title: `${capitalizeString(label)} \u2014 ${shortcut}`,
16010
+ keyBindingLabel: numericKey || letter,
16011
+ "aria-label": capitalizeString(label),
16012
+ "aria-keyshortcuts": shortcut,
16013
+ "data-testid": `toolbar-${value}`,
16014
+ onPointerDown: ({ pointerType }) => {
16015
+ if (!appState.penDetected && pointerType === "pen") {
16016
+ app.togglePenMode(true);
16017
+ }
16018
+ if (value === "selection") {
16019
+ if (appState.activeTool.type === "selection") {
16020
+ app.setActiveTool({ type: "lasso" });
16021
+ } else {
16022
+ app.setActiveTool({ type: "selection" });
16023
+ }
16024
+ }
16025
+ },
16026
+ onChange: ({ pointerType }) => {
16027
+ if (appState.activeTool.type !== value) {
16028
+ trackEvent("toolbar", value, "ui");
16029
+ }
16030
+ if (value === "image") {
16031
+ app.setActiveTool({
16032
+ type: value
16033
+ });
16002
16034
  } else {
16003
- app.setActiveTool({ type: "selection" });
16035
+ app.setActiveTool({ type: value });
16004
16036
  }
16005
16037
  }
16006
16038
  },
16007
- onChange: ({ pointerType }) => {
16008
- if (appState.activeTool.type !== value) {
16009
- trackEvent("toolbar", value, "ui");
16010
- }
16011
- if (value === "image") {
16012
- app.setActiveTool({
16013
- type: value
16014
- });
16015
- } else {
16016
- app.setActiveTool({ type: value });
16017
- }
16018
- }
16019
- },
16020
- value
16021
- );
16022
- }),
16039
+ value
16040
+ );
16041
+ }
16042
+ ),
16023
16043
  /* @__PURE__ */ jsx71("div", { className: "App-toolbar__divider" }),
16024
16044
  /* @__PURE__ */ jsxs39(DropdownMenu_default, { open: isExtraToolsMenuOpen, children: [
16025
16045
  /* @__PURE__ */ jsx71(
@@ -16075,7 +16095,7 @@ var ShapesSwitcher = ({
16075
16095
  children: t("toolBar.laser")
16076
16096
  }
16077
16097
  ),
16078
- /* @__PURE__ */ jsx71(
16098
+ app.defaultSelectionTool !== "lasso" && /* @__PURE__ */ jsx71(
16079
16099
  DropdownMenu_default.Item,
16080
16100
  {
16081
16101
  onSelect: () => app.setActiveTool({ type: "lasso" }),
@@ -19285,7 +19305,8 @@ var isSnappingEnabled = ({
19285
19305
  selectedElements
19286
19306
  }) => {
19287
19307
  if (event) {
19288
- return app.state.activeTool.type !== "lasso" && (app.state.objectsSnapModeEnabled && !event[KEYS42.CTRL_OR_CMD] || !app.state.objectsSnapModeEnabled && event[KEYS42.CTRL_OR_CMD] && !isGridModeEnabled(app));
19308
+ const isLassoDragging = app.state.activeTool.type === "lasso" && app.state.selectedElementsAreBeingDragged;
19309
+ return (app.state.activeTool.type !== "lasso" || isLassoDragging) && (app.state.objectsSnapModeEnabled && !event[KEYS42.CTRL_OR_CMD] || !app.state.objectsSnapModeEnabled && event[KEYS42.CTRL_OR_CMD] && !isGridModeEnabled(app));
19289
19310
  }
19290
19311
  if (selectedElements.length === 1 && selectedElements[0].type === "arrow") {
19291
19312
  return false;
@@ -23568,7 +23589,7 @@ import { isNodeInFlowchart } from "@excalidraw/element";
23568
23589
  import { jsx as jsx87 } from "react/jsx-runtime";
23569
23590
  var getHints = ({
23570
23591
  appState,
23571
- isMobile,
23592
+ isMobile: isMobile2,
23572
23593
  device,
23573
23594
  app
23574
23595
  }) => {
@@ -23631,7 +23652,7 @@ var getHints = ({
23631
23652
  if (isGridModeEnabled(app) && appState.selectedElementsAreBeingDragged) {
23632
23653
  return t("hints.disableSnapping");
23633
23654
  }
23634
- if (!selectedElements.length && !isMobile) {
23655
+ if (!selectedElements.length && !isMobile2) {
23635
23656
  return [t("hints.canvasPanning")];
23636
23657
  }
23637
23658
  if (selectedElements.length === 1) {
@@ -23659,13 +23680,13 @@ var getHints = ({
23659
23680
  };
23660
23681
  var HintViewer = ({
23661
23682
  appState,
23662
- isMobile,
23683
+ isMobile: isMobile2,
23663
23684
  device,
23664
23685
  app
23665
23686
  }) => {
23666
23687
  const hints = getHints({
23667
23688
  appState,
23668
- isMobile,
23689
+ isMobile: isMobile2,
23669
23690
  device,
23670
23691
  app
23671
23692
  });
@@ -31283,6 +31304,7 @@ var App = class _App extends React43.Component {
31283
31304
  __publicField(this, "onScrollChangeEmitter", new Emitter3());
31284
31305
  __publicField(this, "missingPointerEventCleanupEmitter", new Emitter3());
31285
31306
  __publicField(this, "onRemoveEventListenersEmitter", new Emitter3());
31307
+ __publicField(this, "defaultSelectionTool", "selection");
31286
31308
  __publicField(this, "updateEditorAtom", (atom2, ...args) => {
31287
31309
  const result = editorJotaiStore.set(atom2, ...args);
31288
31310
  this.triggerRender();
@@ -31793,6 +31815,7 @@ var App = class _App extends React43.Component {
31793
31815
  repairBindings: true,
31794
31816
  deleteInvisibleElements: true
31795
31817
  });
31818
+ const activeTool = scene.appState.activeTool;
31796
31819
  scene.appState = {
31797
31820
  ...scene.appState,
31798
31821
  theme: this.props.theme || scene.appState.theme,
@@ -31801,7 +31824,10 @@ var App = class _App extends React43.Component {
31801
31824
  // update the state outside of initialData (e.g. when loading the app
31802
31825
  // with a library install link, which should auto-open the library)
31803
31826
  openSidebar: scene.appState?.openSidebar || this.state.openSidebar,
31804
- activeTool: scene.appState.activeTool.type === "image" ? { ...scene.appState.activeTool, type: "selection" } : scene.appState.activeTool,
31827
+ activeTool: activeTool.type === "image" || activeTool.type === "lasso" || activeTool.type === "selection" ? {
31828
+ ...activeTool,
31829
+ type: this.defaultSelectionTool
31830
+ } : scene.appState.activeTool,
31805
31831
  isLoading: false,
31806
31832
  toast: this.state.toast
31807
31833
  };
@@ -31831,6 +31857,12 @@ var App = class _App extends React43.Component {
31831
31857
  this.scrollToContent(window.location.href, { animate: false });
31832
31858
  }
31833
31859
  });
31860
+ __publicField(this, "isMobileOrTablet", () => {
31861
+ const hasTouch = "ontouchstart" in window || navigator.maxTouchPoints > 0;
31862
+ const hasCoarsePointer = "matchMedia" in window && window?.matchMedia("(pointer: coarse)")?.matches;
31863
+ const isTouchMobile = hasTouch && hasCoarsePointer;
31864
+ return isMobile || isTouchMobile;
31865
+ });
31834
31866
  __publicField(this, "isMobileBreakpoint", (width, height) => {
31835
31867
  return width < MQ_MAX_WIDTH_PORTRAIT || height < MQ_MAX_HEIGHT_LANDSCAPE && width < MQ_MAX_WIDTH_LANDSCAPE;
31836
31868
  });
@@ -32053,7 +32085,7 @@ var App = class _App extends React43.Component {
32053
32085
  this.addElementsFromPasteOrLibrary({
32054
32086
  elements,
32055
32087
  files: data.files || null,
32056
- position: "cursor",
32088
+ position: this.isMobileOrTablet() ? "center" : "cursor",
32057
32089
  retainSeed: isPlainPaste
32058
32090
  });
32059
32091
  } else if (data.text) {
@@ -32067,7 +32099,7 @@ var App = class _App extends React43.Component {
32067
32099
  this.addElementsFromPasteOrLibrary({
32068
32100
  elements,
32069
32101
  files,
32070
- position: "cursor"
32102
+ position: this.isMobileOrTablet() ? "center" : "cursor"
32071
32103
  });
32072
32104
  return;
32073
32105
  } catch (err) {
@@ -32107,7 +32139,7 @@ var App = class _App extends React43.Component {
32107
32139
  }
32108
32140
  this.addTextFromPaste(data.text, isPlainPaste);
32109
32141
  }
32110
- this.setActiveTool({ type: "selection" });
32142
+ this.setActiveTool({ type: this.defaultSelectionTool }, true);
32111
32143
  event?.preventDefault();
32112
32144
  }
32113
32145
  ));
@@ -32211,7 +32243,7 @@ var App = class _App extends React43.Component {
32211
32243
  }
32212
32244
  }
32213
32245
  );
32214
- this.setActiveTool({ type: "selection" });
32246
+ this.setActiveTool({ type: this.defaultSelectionTool }, true);
32215
32247
  if (opts.fitToContent) {
32216
32248
  this.scrollToContent(duplicatedElements, {
32217
32249
  fitToContent: true,
@@ -32242,7 +32274,7 @@ var App = class _App extends React43.Component {
32242
32274
  ...prevState.activeTool,
32243
32275
  ...updateActiveTool7(
32244
32276
  this.state,
32245
- prevState.activeTool.locked ? { type: "selection" } : prevState.activeTool
32277
+ prevState.activeTool.locked ? { type: this.defaultSelectionTool } : prevState.activeTool
32246
32278
  ),
32247
32279
  locked: !prevState.activeTool.locked
32248
32280
  }
@@ -32478,19 +32510,17 @@ var App = class _App extends React43.Component {
32478
32510
  }
32479
32511
  ));
32480
32512
  __publicField(this, "applyDeltas", (deltas, options) => {
32481
- if (!deltas.length) {
32482
- throw new Error("No deltas were passed in!");
32483
- }
32513
+ const aggregatedDelta = StoreDelta2.squash(...deltas);
32514
+ const nextAppState = { ...this.state };
32484
32515
  const nextElements = new Map(
32485
32516
  this.scene.getElementsMapIncludingDeleted()
32486
32517
  );
32487
- const nextAppState = { ...this.state };
32488
- const [head, ...tail] = deltas;
32489
- const mainDelta = StoreDelta2.load(head);
32490
- for (const delta of tail) {
32491
- mainDelta.squash(delta);
32492
- }
32493
- return StoreDelta2.applyTo(mainDelta, nextElements, nextAppState, options);
32518
+ return StoreDelta2.applyTo(
32519
+ aggregatedDelta,
32520
+ nextElements,
32521
+ nextAppState,
32522
+ options
32523
+ );
32494
32524
  });
32495
32525
  __publicField(this, "mutateElement", (element, updates, informMutation = true) => {
32496
32526
  return this.scene.mutateElement(element, updates, {
@@ -32859,7 +32889,7 @@ var App = class _App extends React43.Component {
32859
32889
  }
32860
32890
  }
32861
32891
  } else if (!event.ctrlKey && !event.altKey && !event.metaKey && !this.state.newElement && !this.state.selectionElement && !this.state.selectedElementsAreBeingDragged) {
32862
- const shape = findShapeByKey(event.key);
32892
+ const shape = findShapeByKey(event.key, this);
32863
32893
  if (shape) {
32864
32894
  if (this.state.activeTool.type !== shape) {
32865
32895
  trackEvent(
@@ -32916,7 +32946,7 @@ var App = class _App extends React43.Component {
32916
32946
  }
32917
32947
  if (event.key === KEYS52.K && !event.altKey && !event[KEYS52.CTRL_OR_CMD]) {
32918
32948
  if (this.state.activeTool.type === "laser") {
32919
- this.setActiveTool({ type: "selection" });
32949
+ this.setActiveTool({ type: this.defaultSelectionTool });
32920
32950
  } else {
32921
32951
  this.setActiveTool({ type: "laser" });
32922
32952
  }
@@ -33291,7 +33321,7 @@ var App = class _App extends React43.Component {
33291
33321
  if (this.state.multiElement) {
33292
33322
  return;
33293
33323
  }
33294
- if (this.state.activeTool.type !== "selection") {
33324
+ if (this.state.activeTool.type !== this.defaultSelectionTool) {
33295
33325
  return;
33296
33326
  }
33297
33327
  const selectedElements = this.scene.getSelectedElements(this.state);
@@ -33727,7 +33757,7 @@ var App = class _App extends React43.Component {
33727
33757
  return;
33728
33758
  }
33729
33759
  const hasDeselectedButton = Boolean(event.buttons);
33730
- if (hasDeselectedButton || this.state.activeTool.type !== "selection" && this.state.activeTool.type !== "text" && this.state.activeTool.type !== "eraser") {
33760
+ if (hasDeselectedButton || this.state.activeTool.type !== "selection" && this.state.activeTool.type !== "lasso" && this.state.activeTool.type !== "text" && this.state.activeTool.type !== "eraser") {
33731
33761
  return;
33732
33762
  }
33733
33763
  const elements = this.scene.getNonDeletedElements();
@@ -33841,7 +33871,9 @@ var App = class _App extends React43.Component {
33841
33871
  });
33842
33872
  } else if (!hitElement || // Ebow arrows can only be moved when unconnected
33843
33873
  !isElbowArrow11(hitElement) || !(hitElement.startBinding || hitElement.endBinding)) {
33844
- setCursor(this.interactiveCanvas, CURSOR_TYPE4.MOVE);
33874
+ if (this.state.activeTool.type !== "lasso" || selectedElements.length > 0) {
33875
+ setCursor(this.interactiveCanvas, CURSOR_TYPE4.MOVE);
33876
+ }
33845
33877
  if (this.state.activeEmbeddable?.state === "hover") {
33846
33878
  this.setState({ activeEmbeddable: null });
33847
33879
  }
@@ -34039,11 +34071,76 @@ var App = class _App extends React43.Component {
34039
34071
  return;
34040
34072
  }
34041
34073
  if (this.state.activeTool.type === "lasso") {
34042
- this.lassoTrail.startPath(
34043
- pointerDownState.origin.x,
34044
- pointerDownState.origin.y,
34045
- event.shiftKey
34046
- );
34074
+ const hitSelectedElement = pointerDownState.hit.element && this.isASelectedElement(pointerDownState.hit.element);
34075
+ const isMobileOrTablet = this.isMobileOrTablet();
34076
+ if (!pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements && !pointerDownState.resize.handleType && !hitSelectedElement) {
34077
+ this.lassoTrail.startPath(
34078
+ pointerDownState.origin.x,
34079
+ pointerDownState.origin.y,
34080
+ event.shiftKey
34081
+ );
34082
+ pointerDownState.drag.blockDragging = !isMobileOrTablet;
34083
+ }
34084
+ if (isMobileOrTablet && pointerDownState.hit.element && !hitSelectedElement) {
34085
+ this.setState((prevState) => {
34086
+ const nextSelectedElementIds = {
34087
+ ...prevState.selectedElementIds,
34088
+ [pointerDownState.hit.element.id]: true
34089
+ };
34090
+ const previouslySelectedElements = [];
34091
+ Object.keys(prevState.selectedElementIds).forEach((id) => {
34092
+ const element = this.scene.getElement(id);
34093
+ element && previouslySelectedElements.push(element);
34094
+ });
34095
+ const hitElement = pointerDownState.hit.element;
34096
+ if (isFrameLikeElement15(hitElement)) {
34097
+ getFrameChildren6(previouslySelectedElements, hitElement.id).forEach(
34098
+ (element) => {
34099
+ delete nextSelectedElementIds[element.id];
34100
+ }
34101
+ );
34102
+ } else if (hitElement.frameId) {
34103
+ if (nextSelectedElementIds[hitElement.frameId]) {
34104
+ delete nextSelectedElementIds[hitElement.id];
34105
+ }
34106
+ } else {
34107
+ const groupIds = hitElement.groupIds;
34108
+ const framesInGroups = new Set(
34109
+ groupIds.flatMap(
34110
+ (gid) => getElementsInGroup9(this.scene.getNonDeletedElements(), gid)
34111
+ ).filter((element) => isFrameLikeElement15(element)).map((frame) => frame.id)
34112
+ );
34113
+ if (framesInGroups.size > 0) {
34114
+ previouslySelectedElements.forEach((element) => {
34115
+ if (element.frameId && framesInGroups.has(element.frameId)) {
34116
+ delete nextSelectedElementIds[element.id];
34117
+ element.groupIds.flatMap(
34118
+ (gid) => getElementsInGroup9(
34119
+ this.scene.getNonDeletedElements(),
34120
+ gid
34121
+ )
34122
+ ).forEach((element2) => {
34123
+ delete nextSelectedElementIds[element2.id];
34124
+ });
34125
+ }
34126
+ });
34127
+ }
34128
+ }
34129
+ return {
34130
+ ...selectGroupsForSelectedElements6(
34131
+ {
34132
+ editingGroupId: prevState.editingGroupId,
34133
+ selectedElementIds: nextSelectedElementIds
34134
+ },
34135
+ this.scene.getNonDeletedElements(),
34136
+ prevState,
34137
+ this
34138
+ ),
34139
+ showHyperlinkPopup: hitElement.link || isEmbeddableElement4(hitElement) ? "info" : false
34140
+ };
34141
+ });
34142
+ pointerDownState.hit.wasAddedToSelection = true;
34143
+ }
34047
34144
  } else if (this.state.activeTool.type === "text") {
34048
34145
  this.handleTextOnPointerDown(event, pointerDownState);
34049
34146
  } else if (this.state.activeTool.type === "arrow" || this.state.activeTool.type === "line") {
@@ -34270,7 +34367,7 @@ var App = class _App extends React43.Component {
34270
34367
  * @returns whether the pointer event has been completely handled
34271
34368
  */
34272
34369
  __publicField(this, "handleSelectionOnPointerDown", (event, pointerDownState) => {
34273
- if (this.state.activeTool.type === "selection") {
34370
+ if (this.state.activeTool.type === "selection" || this.state.activeTool.type === "lasso") {
34274
34371
  const elements = this.scene.getNonDeletedElements();
34275
34372
  const elementsMap = this.scene.getNonDeletedElementsMap();
34276
34373
  const selectedElements = this.scene.getSelectedElements(this.state);
@@ -34420,6 +34517,15 @@ var App = class _App extends React43.Component {
34420
34517
  } else if (hitElement != null) {
34421
34518
  if (event[KEYS52.CTRL_OR_CMD]) {
34422
34519
  if (event.altKey) {
34520
+ if (this.state.openDialog?.name === "elementLinkSelector") {
34521
+ this.setOpenDialog(null);
34522
+ }
34523
+ this.lassoTrail.startPath(
34524
+ pointerDownState.origin.x,
34525
+ pointerDownState.origin.y,
34526
+ event.shiftKey
34527
+ );
34528
+ this.setActiveTool({ type: "lasso", fromSelection: true });
34423
34529
  return false;
34424
34530
  }
34425
34531
  if (!this.state.selectedElementIds[hitElement.id]) {
@@ -34546,7 +34652,9 @@ var App = class _App extends React43.Component {
34546
34652
  resetCursor(this.interactiveCanvas);
34547
34653
  if (!this.state.activeTool.locked) {
34548
34654
  this.setState({
34549
- activeTool: updateActiveTool7(this.state, { type: "selection" })
34655
+ activeTool: updateActiveTool7(this.state, {
34656
+ type: this.defaultSelectionTool
34657
+ })
34550
34658
  });
34551
34659
  }
34552
34660
  });
@@ -35103,7 +35211,9 @@ var App = class _App extends React43.Component {
35103
35211
  this.setState(
35104
35212
  {
35105
35213
  newElement: null,
35106
- activeTool: updateActiveTool7(this.state, { type: "selection" })
35214
+ activeTool: updateActiveTool7(this.state, {
35215
+ type: this.defaultSelectionTool
35216
+ })
35107
35217
  },
35108
35218
  () => {
35109
35219
  this.actionManager.executeAction(actionFinalize);
@@ -35140,7 +35250,7 @@ var App = class _App extends React43.Component {
35140
35250
  if (erroredFiles.size) {
35141
35251
  this.store.scheduleAction(CaptureUpdateAction37.NEVER);
35142
35252
  this.scene.replaceAllElements(
35143
- elements.map((element) => {
35253
+ this.scene.getElementsIncludingDeleted().map((element) => {
35144
35254
  if (isInitializedImageElement3(element) && erroredFiles.has(element.fileId)) {
35145
35255
  return newElementWith11(element, {
35146
35256
  status: "error"
@@ -35357,7 +35467,7 @@ var App = class _App extends React43.Component {
35357
35467
  __publicField(this, "handleCanvasContextMenu", (event) => {
35358
35468
  event.preventDefault();
35359
35469
  if (("pointerType" in event.nativeEvent && event.nativeEvent.pointerType === "touch" || "pointerType" in event.nativeEvent && event.nativeEvent.pointerType === "pen" && // always allow if user uses a pen secondary button
35360
- event.button !== POINTER_BUTTON2.SECONDARY) && this.state.activeTool.type !== "selection") {
35470
+ event.button !== POINTER_BUTTON2.SECONDARY) && this.state.activeTool.type !== this.defaultSelectionTool) {
35361
35471
  return;
35362
35472
  }
35363
35473
  const { x, y } = viewportCoordsToSceneCoords4(event, this.state);
@@ -35846,6 +35956,7 @@ var App = class _App extends React43.Component {
35846
35956
  this.setState({ ...this.getCanvasOffsets() });
35847
35957
  });
35848
35958
  const defaultAppState = getDefaultAppState();
35959
+ this.defaultSelectionTool = this.isMobileOrTablet() ? "lasso" : "selection";
35849
35960
  const {
35850
35961
  excalidrawAPI,
35851
35962
  viewModeEnabled = false,
@@ -36366,7 +36477,7 @@ var App = class _App extends React43.Component {
36366
36477
  showExitZenModeBtn: typeof this.props?.zenModeEnabled === "undefined" && this.state.zenModeEnabled,
36367
36478
  UIOptions: this.props.UIOptions,
36368
36479
  onExportImage: this.onExportImage,
36369
- renderWelcomeScreen: !this.state.isLoading && this.state.showWelcomeScreen && this.state.activeTool.type === "selection" && !this.state.zenModeEnabled && !this.scene.getElementsIncludingDeleted().length,
36480
+ renderWelcomeScreen: !this.state.isLoading && this.state.showWelcomeScreen && this.state.activeTool.type === this.defaultSelectionTool && !this.state.zenModeEnabled && !this.scene.getElementsIncludingDeleted().length,
36370
36481
  app: this,
36371
36482
  isCollaborating: this.props.isCollaborating,
36372
36483
  generateLinkForSelection: this.props.generateLinkForSelection,
@@ -37441,7 +37552,9 @@ var App = class _App extends React43.Component {
37441
37552
  // Ebow arrows can only be moved when unconnected
37442
37553
  !isElbowArrow11(element) || !(element.startBinding || element.endBinding)
37443
37554
  ) {
37444
- setCursor(this.interactiveCanvas, CURSOR_TYPE4.MOVE);
37555
+ if (this.state.activeTool.type !== "lasso" || Object.keys(this.state.selectedElementIds).length > 0) {
37556
+ setCursor(this.interactiveCanvas, CURSOR_TYPE4.MOVE);
37557
+ }
37445
37558
  }
37446
37559
  }
37447
37560
  } else if (this.hitElement(scenePointerX, scenePointerY, element)) {
@@ -37449,7 +37562,9 @@ var App = class _App extends React43.Component {
37449
37562
  // Ebow arrows can only be moved when unconnected
37450
37563
  !isElbowArrow11(element) || !(element.startBinding || element.endBinding)
37451
37564
  ) {
37452
- setCursor(this.interactiveCanvas, CURSOR_TYPE4.MOVE);
37565
+ if (this.state.activeTool.type !== "lasso" || Object.keys(this.state.selectedElementIds).length > 0) {
37566
+ setCursor(this.interactiveCanvas, CURSOR_TYPE4.MOVE);
37567
+ }
37453
37568
  }
37454
37569
  }
37455
37570
  if (this.state.selectedLinearElement.hoverPointIndex !== hoverPointIndex) {
@@ -37534,7 +37649,8 @@ var App = class _App extends React43.Component {
37534
37649
  drag: {
37535
37650
  hasOccurred: false,
37536
37651
  offset: null,
37537
- origin: { ...origin }
37652
+ origin: { ...origin },
37653
+ blockDragging: false
37538
37654
  },
37539
37655
  eventListeners: {
37540
37656
  onMove: null,
@@ -37800,9 +37916,9 @@ var App = class _App extends React43.Component {
37800
37916
  (element) => this.isASelectedElement(element)
37801
37917
  );
37802
37918
  const isSelectingPointsInLineEditor = this.state.selectedLinearElement?.isEditing && event.shiftKey && this.state.selectedLinearElement.elementId === pointerDownState.hit.element?.id;
37803
- if ((hasHitASelectedElement || pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements) && !isSelectingPointsInLineEditor && this.state.activeTool.type !== "lasso") {
37919
+ if ((hasHitASelectedElement || pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements) && !isSelectingPointsInLineEditor && !pointerDownState.drag.blockDragging) {
37804
37920
  const selectedElements = this.scene.getSelectedElements(this.state);
37805
- if (selectedElements.every((element) => element.locked)) {
37921
+ if (selectedElements.length > 0 && selectedElements.every((element) => element.locked)) {
37806
37922
  return;
37807
37923
  }
37808
37924
  const selectedElementsHasAFrame = selectedElements.find(
@@ -37816,6 +37932,12 @@ var App = class _App extends React43.Component {
37816
37932
  });
37817
37933
  }
37818
37934
  pointerDownState.drag.hasOccurred = true;
37935
+ if (this.state.activeTool.type === "lasso" && this.lassoTrail.hasCurrentTrail && !(this.isMobileOrTablet() && pointerDownState.hit.element) && !this.state.activeTool.fromSelection) {
37936
+ return;
37937
+ }
37938
+ if (this.state.activeTool.type === "lasso" && selectedElements.length > 0 && pointerDownState.drag.hasOccurred && !this.state.activeTool.fromSelection) {
37939
+ this.lassoTrail.endPath();
37940
+ }
37819
37941
  if (selectedElements.length > 0 && !pointerDownState.withCmdOrCtrl && !this.state.editingTextElement && this.state.activeEmbeddable?.state !== "active") {
37820
37942
  const dragOffset = {
37821
37943
  x: pointerCoords.x - pointerDownState.drag.origin.x,
@@ -38261,6 +38383,7 @@ var App = class _App extends React43.Component {
38261
38383
  onPointerUpFromPointerDownHandler(pointerDownState) {
38262
38384
  return withBatchedUpdates((childEvent) => {
38263
38385
  this.removePointer(childEvent);
38386
+ pointerDownState.drag.blockDragging = false;
38264
38387
  if (pointerDownState.eventListeners.onMove) {
38265
38388
  pointerDownState.eventListeners.onMove.flush();
38266
38389
  }
@@ -38475,7 +38598,7 @@ var App = class _App extends React43.Component {
38475
38598
  this.setState((prevState) => ({
38476
38599
  newElement: null,
38477
38600
  activeTool: updateActiveTool7(this.state, {
38478
- type: "selection"
38601
+ type: this.defaultSelectionTool
38479
38602
  }),
38480
38603
  selectedElementIds: makeNextSelectedElementIds2(
38481
38604
  {
@@ -38912,7 +39035,9 @@ var App = class _App extends React43.Component {
38912
39035
  this.setState({
38913
39036
  newElement: null,
38914
39037
  suggestedBindings: [],
38915
- activeTool: updateActiveTool7(this.state, { type: "selection" })
39038
+ activeTool: updateActiveTool7(this.state, {
39039
+ type: this.defaultSelectionTool
39040
+ })
38916
39041
  });
38917
39042
  } else {
38918
39043
  this.setState({