@tldraw/editor 3.8.0-internal.af5331ba6061 → 3.8.0

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/dist-cjs/index.d.ts +169 -52
  3. package/dist-cjs/index.js +4 -1
  4. package/dist-cjs/index.js.map +2 -2
  5. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -1
  6. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  7. package/dist-cjs/lib/editor/Editor.js +30 -17
  8. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  9. package/dist-cjs/lib/editor/managers/TextManager.js +1 -0
  10. package/dist-cjs/lib/editor/managers/TextManager.js.map +2 -2
  11. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +13 -0
  12. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  13. package/dist-cjs/lib/editor/shapes/shared/resizeScaled.js +66 -0
  14. package/dist-cjs/lib/editor/shapes/shared/resizeScaled.js.map +7 -0
  15. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  16. package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
  17. package/dist-cjs/lib/hooks/useCanvasEvents.js +19 -8
  18. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  19. package/dist-cjs/lib/hooks/useDocumentEvents.js +1 -3
  20. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  21. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +4 -0
  22. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +3 -3
  23. package/dist-cjs/lib/options.js +2 -2
  24. package/dist-cjs/lib/options.js.map +2 -2
  25. package/dist-cjs/lib/utils/dom.js +6 -0
  26. package/dist-cjs/lib/utils/dom.js.map +2 -2
  27. package/dist-cjs/version.js +3 -3
  28. package/dist-cjs/version.js.map +1 -1
  29. package/dist-esm/index.d.mts +169 -52
  30. package/dist-esm/index.mjs +5 -1
  31. package/dist-esm/index.mjs.map +2 -2
  32. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -1
  33. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  34. package/dist-esm/lib/editor/Editor.mjs +30 -17
  35. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  36. package/dist-esm/lib/editor/managers/TextManager.mjs +1 -0
  37. package/dist-esm/lib/editor/managers/TextManager.mjs.map +2 -2
  38. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +13 -0
  39. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  40. package/dist-esm/lib/editor/shapes/shared/resizeScaled.mjs +46 -0
  41. package/dist-esm/lib/editor/shapes/shared/resizeScaled.mjs.map +7 -0
  42. package/dist-esm/lib/hooks/useCanvasEvents.mjs +19 -8
  43. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  44. package/dist-esm/lib/hooks/useDocumentEvents.mjs +2 -4
  45. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  46. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -0
  47. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +3 -3
  48. package/dist-esm/lib/options.mjs +2 -2
  49. package/dist-esm/lib/options.mjs.map +2 -2
  50. package/dist-esm/lib/utils/dom.mjs +6 -0
  51. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  52. package/dist-esm/version.mjs +3 -3
  53. package/dist-esm/version.mjs.map +1 -1
  54. package/editor.css +2 -1
  55. package/package.json +7 -7
  56. package/src/index.ts +18 -1
  57. package/src/lib/components/default-components/DefaultCanvas.tsx +1 -1
  58. package/src/lib/editor/Editor.ts +56 -27
  59. package/src/lib/editor/managers/TextManager.ts +1 -0
  60. package/src/lib/editor/shapes/ShapeUtil.ts +49 -1
  61. package/src/lib/editor/shapes/shared/resizeScaled.ts +61 -0
  62. package/src/lib/editor/types/emit-types.ts +1 -0
  63. package/src/lib/editor/types/external-content.ts +104 -50
  64. package/src/lib/hooks/useCanvasEvents.ts +20 -8
  65. package/src/lib/hooks/useDocumentEvents.ts +2 -11
  66. package/src/lib/hooks/usePassThroughWheelEvents.ts +7 -0
  67. package/src/lib/options.ts +5 -2
  68. package/src/lib/utils/dom.ts +12 -0
  69. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.8.0-internal.af5331ba6061'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-01-27T17:13:11.548Z',\n\tpatch: '2025-01-27T17:13:11.548Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.8.0'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-02-12T15:48:24.555Z',\n\tpatch: '2025-02-12T15:48:24.555Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
package/editor.css CHANGED
@@ -39,9 +39,10 @@
39
39
  --layer-overlays-user-brush: 50;
40
40
  --layer-overlays-user-indicator-selected: 60;
41
41
  --layer-overlays-user-indicator-hovered: 70;
42
- --layer-overlays-user-handles: 80;
43
42
  --layer-overlays-user-snapline: 90;
44
43
  --layer-overlays-selection-fg: 100;
44
+ /* User handles need to be above selection edges / corners, matters for sticky note clone handles */
45
+ --layer-overlays-user-handles: 105;
45
46
  --layer-overlays-user-indicator-hint: 110;
46
47
  --layer-overlays-collaborator-cursor-hint: 120;
47
48
  --layer-overlays-collaborator-cursor: 130;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/editor",
3
3
  "description": "A tiny little drawing app (editor).",
4
- "version": "3.8.0-internal.af5331ba6061",
4
+ "version": "3.8.0",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -45,12 +45,12 @@
45
45
  "lint": "yarn run -T tsx ../../internal/scripts/lint.ts"
46
46
  },
47
47
  "dependencies": {
48
- "@tldraw/state": "3.8.0-internal.af5331ba6061",
49
- "@tldraw/state-react": "3.8.0-internal.af5331ba6061",
50
- "@tldraw/store": "3.8.0-internal.af5331ba6061",
51
- "@tldraw/tlschema": "3.8.0-internal.af5331ba6061",
52
- "@tldraw/utils": "3.8.0-internal.af5331ba6061",
53
- "@tldraw/validate": "3.8.0-internal.af5331ba6061",
48
+ "@tldraw/state": "3.8.0",
49
+ "@tldraw/state-react": "3.8.0",
50
+ "@tldraw/store": "3.8.0",
51
+ "@tldraw/tlschema": "3.8.0",
52
+ "@tldraw/utils": "3.8.0",
53
+ "@tldraw/validate": "3.8.0",
54
54
  "@types/core-js": "^2.5.8",
55
55
  "@use-gesture/react": "^10.3.1",
56
56
  "canvas-size": "~2.0.0",
package/src/index.ts CHANGED
@@ -25,6 +25,7 @@ export {
25
25
  useStateTracking,
26
26
  useValue,
27
27
  } from '@tldraw/state-react'
28
+ export { resizeScaled } from './lib/editor/shapes/shared/resizeScaled'
28
29
  export { LocalIndexedDb, Table, type StoreName } from './lib/utils/sync/LocalIndexedDb'
29
30
  // eslint-disable-next-line local/no-export-star
30
31
  export * from '@tldraw/store'
@@ -182,6 +183,7 @@ export { UserPreferencesManager } from './lib/editor/managers/UserPreferencesMan
182
183
  export { BaseBoxShapeUtil, type TLBaseBoxShape } from './lib/editor/shapes/BaseBoxShapeUtil'
183
184
  export {
184
185
  ShapeUtil,
186
+ type TLCropInfo,
185
187
  type TLHandleDragInfo,
186
188
  type TLResizeInfo,
187
189
  type TLResizeMode,
@@ -238,9 +240,23 @@ export {
238
240
  type UiEventType,
239
241
  } from './lib/editor/types/event-types'
240
242
  export {
241
- type TLExternalAssetContent,
243
+ type TLBaseExternalContent,
244
+ type TLEmbedExternalContent,
245
+ type TLErrorExternalContentSource,
246
+ type TLExcalidrawExternalContent,
247
+ type TLExcalidrawExternalContentSource,
248
+ type TLExternalAsset,
242
249
  type TLExternalContent,
243
250
  type TLExternalContentSource,
251
+ type TLFileExternalAsset,
252
+ type TLFilesExternalContent,
253
+ type TLSvgTextExternalContent,
254
+ type TLTextExternalContent,
255
+ type TLTextExternalContentSource,
256
+ type TLTldrawExternalContent,
257
+ type TLTldrawExternalContentSource,
258
+ type TLUrlExternalAsset,
259
+ type TLUrlExternalContent,
244
260
  } from './lib/editor/types/external-content'
245
261
  export {
246
262
  type TLHistoryBatchOptions,
@@ -399,6 +415,7 @@ export {
399
415
  type TLDeepLinkOptions,
400
416
  } from './lib/utils/deepLinks'
401
417
  export {
418
+ activeElementShouldCaptureKeys,
402
419
  loopToHtmlElement,
403
420
  preventDefault,
404
421
  releasePointerCapture,
@@ -160,7 +160,6 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
160
160
  <div className="tl-overlays">
161
161
  <div ref={rHtmlLayer2} className="tl-html-layer">
162
162
  {debugGeometry ? <GeometryDebuggingView /> : null}
163
- <HandlesWrapper />
164
163
  <BrushWrapper />
165
164
  <ScribbleWrapper />
166
165
  <ZoomBrushWrapper />
@@ -168,6 +167,7 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
168
167
  <HintedShapeIndicator />
169
168
  <SnapIndicatorWrapper />
170
169
  <SelectionForegroundWrapper />
170
+ <HandlesWrapper />
171
171
  <LiveCollaborators />
172
172
  </div>
173
173
  </div>
@@ -155,7 +155,7 @@ import {
155
155
  TLPointerEventInfo,
156
156
  TLWheelEventInfo,
157
157
  } from './types/event-types'
158
- import { TLExternalAssetContent, TLExternalContent } from './types/external-content'
158
+ import { TLExternalAsset, TLExternalContent } from './types/external-content'
159
159
  import { TLHistoryBatchOptions } from './types/history-types'
160
160
  import {
161
161
  OptionalKeys,
@@ -929,6 +929,21 @@ export class Editor extends EventEmitter<TLEventMap> {
929
929
  return shapeUtil
930
930
  }
931
931
 
932
+ /**
933
+ * Returns true if the editor has a shape util for the given shape / shape type.
934
+ *
935
+ * @param shape - A shape, shape partial, or shape type.
936
+ */
937
+ hasShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): boolean
938
+ hasShapeUtil<S extends TLUnknownShape>(type: S['type']): boolean
939
+ hasShapeUtil<T extends ShapeUtil>(
940
+ type: T extends ShapeUtil<infer R> ? R['type'] : string
941
+ ): boolean
942
+ hasShapeUtil(arg: string | { type: string }): boolean {
943
+ const type = typeof arg === 'string' ? arg : arg.type
944
+ return hasOwnProperty(this.shapeUtils, type)
945
+ }
946
+
932
947
  /* ------------------- Binding Utils ------------------ */
933
948
  /**
934
949
  * A map of shape utility classes (TLShapeUtils) by shape type.
@@ -1385,8 +1400,8 @@ export class Editor extends EventEmitter<TLEventMap> {
1385
1400
  *
1386
1401
  * @example
1387
1402
  * ```ts
1388
- * state.getStateDescendant('select')
1389
- * state.getStateDescendant('select.brushing')
1403
+ * editor.getStateDescendant('select')
1404
+ * editor.getStateDescendant('select.brushing')
1390
1405
  * ```
1391
1406
  *
1392
1407
  * @param path - The descendant's path of state ids, separated by periods.
@@ -6760,6 +6775,8 @@ export class Editor extends EventEmitter<TLEventMap> {
6760
6775
  }
6761
6776
  }
6762
6777
 
6778
+ let didResize = false
6779
+
6763
6780
  if (util.onResize && util.canResize(initialShape)) {
6764
6781
  // get the model changes from the shape util
6765
6782
  const newPagePoint = this._scalePagePoint(
@@ -6798,24 +6815,30 @@ export class Editor extends EventEmitter<TLEventMap> {
6798
6815
  )
6799
6816
  }
6800
6817
 
6818
+ const resizedShape = util.onResize(
6819
+ { ...initialShape, x, y },
6820
+ {
6821
+ newPoint: newLocalPoint,
6822
+ handle: opts.dragHandle ?? 'bottom_right',
6823
+ // don't set isSingle to true for children
6824
+ mode: opts.mode ?? 'scale_shape',
6825
+ scaleX: myScale.x,
6826
+ scaleY: myScale.y,
6827
+ initialBounds,
6828
+ initialShape,
6829
+ }
6830
+ )
6831
+
6832
+ if (resizedShape) {
6833
+ didResize = true
6834
+ }
6835
+
6801
6836
  workingShape = applyPartialToRecordWithProps(workingShape, {
6802
6837
  id,
6803
6838
  type: initialShape.type as any,
6804
6839
  x: newLocalPoint.x,
6805
6840
  y: newLocalPoint.y,
6806
- ...util.onResize(
6807
- { ...initialShape, x, y },
6808
- {
6809
- newPoint: newLocalPoint,
6810
- handle: opts.dragHandle ?? 'bottom_right',
6811
- // don't set isSingle to true for children
6812
- mode: opts.mode ?? 'scale_shape',
6813
- scaleX: myScale.x,
6814
- scaleY: myScale.y,
6815
- initialBounds,
6816
- initialShape,
6817
- }
6818
- ),
6841
+ ...resizedShape,
6819
6842
  })
6820
6843
 
6821
6844
  if (!opts.skipStartAndEndCallbacks) {
@@ -6826,7 +6849,11 @@ export class Editor extends EventEmitter<TLEventMap> {
6826
6849
  }
6827
6850
 
6828
6851
  this.updateShapes([workingShape])
6829
- } else {
6852
+ }
6853
+
6854
+ if (!didResize) {
6855
+ // reposition shape (rather than resizing it) based on where its resized center would be
6856
+
6830
6857
  const initialPageCenter = Mat.applyToPoint(pageTransform, initialBounds.center)
6831
6858
  // get the model changes from the shape util
6832
6859
  const newPageCenter = this._scalePagePoint(
@@ -7948,10 +7975,8 @@ export class Editor extends EventEmitter<TLEventMap> {
7948
7975
 
7949
7976
  /** @internal */
7950
7977
  externalAssetContentHandlers: {
7951
- [K in TLExternalAssetContent['type']]: {
7952
- [Key in K]:
7953
- | null
7954
- | ((info: TLExternalAssetContent & { type: Key }) => Promise<TLAsset | undefined>)
7978
+ [K in TLExternalAsset['type']]: {
7979
+ [Key in K]: null | ((info: TLExternalAsset & { type: Key }) => Promise<TLAsset | undefined>)
7955
7980
  }[K]
7956
7981
  } = {
7957
7982
  file: null,
@@ -7980,9 +8005,9 @@ export class Editor extends EventEmitter<TLEventMap> {
7980
8005
  *
7981
8006
  * @public
7982
8007
  */
7983
- registerExternalAssetHandler<T extends TLExternalAssetContent['type']>(
8008
+ registerExternalAssetHandler<T extends TLExternalAsset['type']>(
7984
8009
  type: T,
7985
- handler: null | ((info: TLExternalAssetContent & { type: T }) => Promise<TLAsset>)
8010
+ handler: null | ((info: TLExternalAsset & { type: T }) => Promise<TLAsset>)
7986
8011
  ): this {
7987
8012
  this.externalAssetContentHandlers[type] = handler as any
7988
8013
  return this
@@ -8050,18 +8075,18 @@ export class Editor extends EventEmitter<TLEventMap> {
8050
8075
  * @param info - Info about the external content.
8051
8076
  * @returns The asset.
8052
8077
  */
8053
- async getAssetForExternalContent(info: TLExternalAssetContent): Promise<TLAsset | undefined> {
8078
+ async getAssetForExternalContent(info: TLExternalAsset): Promise<TLAsset | undefined> {
8054
8079
  return await this.externalAssetContentHandlers[info.type]?.(info as any)
8055
8080
  }
8056
8081
 
8057
- hasExternalAssetHandler(type: TLExternalAssetContent['type']): boolean {
8082
+ hasExternalAssetHandler(type: TLExternalAsset['type']): boolean {
8058
8083
  return !!this.externalAssetContentHandlers[type]
8059
8084
  }
8060
8085
 
8061
8086
  /** @internal */
8062
8087
  externalContentHandlers: {
8063
8088
  [K in TLExternalContent<any>['type']]: {
8064
- [Key in K]: null | ((info: TLExternalContent<any> & { type: Key }) => void)
8089
+ [Key in K]: null | ((info: Extract<TLExternalContent<any>, { type: Key }>) => void)
8065
8090
  }[K]
8066
8091
  } = {
8067
8092
  text: null,
@@ -8069,6 +8094,8 @@ export class Editor extends EventEmitter<TLEventMap> {
8069
8094
  embed: null,
8070
8095
  'svg-text': null,
8071
8096
  url: null,
8097
+ tldraw: null,
8098
+ excalidraw: null,
8072
8099
  }
8073
8100
 
8074
8101
  /**
@@ -8096,7 +8123,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8096
8123
  | null
8097
8124
  | ((
8098
8125
  info: T extends TLExternalContent<E>['type']
8099
- ? TLExternalContent<E> & { type: T }
8126
+ ? Extract<TLExternalContent<E>, { type: T }>
8100
8127
  : TLExternalContent<E>
8101
8128
  ) => void)
8102
8129
  ): this {
@@ -9342,6 +9369,8 @@ export class Editor extends EventEmitter<TLEventMap> {
9342
9369
  // todo: replace with new readonly mode?
9343
9370
  if (this.getCrashingError()) return this
9344
9371
 
9372
+ this.emit('before-event', info)
9373
+
9345
9374
  const { inputs } = this
9346
9375
  const { type } = info
9347
9376
 
@@ -230,6 +230,7 @@ export class TextManager {
230
230
  elm.style.setProperty('font-weight', opts.fontWeight)
231
231
  elm.style.setProperty('line-height', `${opts.lineHeight * opts.fontSize}px`)
232
232
  elm.style.setProperty('text-align', textAlignmentsForLtr[opts.textAlign])
233
+ elm.style.setProperty('font-style', opts.fontStyle)
233
234
 
234
235
  const shouldTruncateToFirstLine =
235
236
  opts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'
@@ -5,11 +5,12 @@ import {
5
5
  TLHandle,
6
6
  TLPropsMigrations,
7
7
  TLShape,
8
+ TLShapeCrop,
8
9
  TLShapePartial,
9
10
  TLUnknownShape,
10
11
  } from '@tldraw/tlschema'
11
12
  import { ReactElement } from 'react'
12
- import { Box } from '../../primitives/Box'
13
+ import { Box, SelectionHandle } from '../../primitives/Box'
13
14
  import { Vec } from '../../primitives/Vec'
14
15
  import { Geometry2d } from '../../primitives/geometry/Geometry2d'
15
16
  import type { Editor } from '../Editor'
@@ -52,8 +53,27 @@ export interface TLShapeUtilCanvasSvgDef {
52
53
 
53
54
  /** @public */
54
55
  export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
56
+ /** Configure this shape utils {@link ShapeUtil.options | `options`}. */
57
+ static configure<T extends TLShapeUtilConstructor<any, any>>(
58
+ this: T,
59
+ options: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never
60
+ ): T {
61
+ // @ts-expect-error -- typescript has no idea what's going on here but it's fine
62
+ return class extends this {
63
+ // @ts-expect-error
64
+ options = { ...this.options, ...options }
65
+ }
66
+ }
67
+
55
68
  constructor(public editor: Editor) {}
56
69
 
70
+ /**
71
+ * Options for this shape util. If you're implementing a custom shape util, you can override
72
+ * this to provide customization options for your shape. If using an existing shape util, you
73
+ * can customizing this by calling {@link ShapeUtil.configure}.
74
+ */
75
+ options = {}
76
+
57
77
  /**
58
78
  * Props allow you to define the shape's properties in a way that the editor can understand.
59
79
  * This has two main uses:
@@ -419,6 +439,19 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
419
439
  */
420
440
  onBeforeUpdate?(prev: Shape, next: Shape): Shape | void
421
441
 
442
+ /**
443
+ * A callback called when a shape changes from a crop.
444
+ *
445
+ * @param shape - The shape at the start of the crop.
446
+ * @param info - Info about the crop.
447
+ * @returns A change to apply to the shape, or void.
448
+ * @public
449
+ */
450
+ onCrop?(
451
+ shape: Shape,
452
+ info: TLCropInfo<Shape>
453
+ ): Omit<TLShapePartial<Shape>, 'id' | 'type'> | undefined | void
454
+
422
455
  /**
423
456
  * A callback called when some other shapes are dragged over this one.
424
457
  *
@@ -616,6 +649,21 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
616
649
  onEditEnd?(shape: Shape): void
617
650
  }
618
651
 
652
+ /**
653
+ * Info about a crop.
654
+ * @param handle - The handle being dragged.
655
+ * @param change - The distance the handle is moved.
656
+ * @param initialShape - The shape at the start of the resize.
657
+ * @public
658
+ */
659
+ export interface TLCropInfo<T extends TLShape> {
660
+ handle: SelectionHandle
661
+ change: Vec
662
+ crop: TLShapeCrop
663
+ uncroppedSize: { w: number; h: number }
664
+ initialShape: T
665
+ }
666
+
619
667
  /**
620
668
  * The type of resize.
621
669
  *
@@ -0,0 +1,61 @@
1
+ import { TLBaseShape } from '@tldraw/tlschema'
2
+ import { exhaustiveSwitchError } from '@tldraw/utils'
3
+ import { Vec } from '../../../primitives/Vec'
4
+ import { TLResizeInfo } from '../ShapeUtil'
5
+
6
+ /**
7
+ * Resize a shape that has a scale prop.
8
+ *
9
+ * @param shape - The shape to resize
10
+ * @param info - The resize info
11
+ *
12
+ * @public */
13
+ export function resizeScaled(
14
+ shape: TLBaseShape<any, { scale: number }>,
15
+ { initialBounds, scaleX, scaleY, newPoint, handle }: TLResizeInfo<any>
16
+ ) {
17
+ let scaleDelta: number
18
+ switch (handle) {
19
+ case 'bottom_left':
20
+ case 'bottom_right':
21
+ case 'top_left':
22
+ case 'top_right': {
23
+ scaleDelta = Math.max(0.01, Math.max(Math.abs(scaleX), Math.abs(scaleY)))
24
+ break
25
+ }
26
+ case 'left':
27
+ case 'right': {
28
+ scaleDelta = Math.max(0.01, Math.abs(scaleX))
29
+ break
30
+ }
31
+ case 'bottom':
32
+ case 'top': {
33
+ scaleDelta = Math.max(0.01, Math.abs(scaleY))
34
+ break
35
+ }
36
+ default: {
37
+ throw exhaustiveSwitchError(handle)
38
+ }
39
+ }
40
+
41
+ // Compute the offset (if flipped X or flipped Y)
42
+ const offset = new Vec(0, 0)
43
+
44
+ if (scaleX < 0) {
45
+ offset.x = -(initialBounds.width * scaleDelta)
46
+ }
47
+ if (scaleY < 0) {
48
+ offset.y = -(initialBounds.height * scaleDelta)
49
+ }
50
+
51
+ // Apply the offset to the new point
52
+ const { x, y } = Vec.Add(newPoint, offset.rot(shape.rotation))
53
+
54
+ return {
55
+ x,
56
+ y,
57
+ props: {
58
+ scale: scaleDelta * shape.props.scale,
59
+ },
60
+ }
61
+ }
@@ -12,6 +12,7 @@ export interface TLEventMap {
12
12
  crash: [{ error: unknown }]
13
13
  'stop-camera-animation': []
14
14
  'stop-following': []
15
+ 'before-event': [TLEventInfo]
15
16
  event: [TLEventInfo]
16
17
  tick: [number]
17
18
  frame: [number]
@@ -2,57 +2,111 @@ import { TLAssetId } from '@tldraw/tlschema'
2
2
  import { VecLike } from '../../primitives/Vec'
3
3
  import { TLContent } from './clipboard-types'
4
4
 
5
+ /** @public */
6
+ export interface TLTldrawExternalContentSource {
7
+ type: 'tldraw'
8
+ data: TLContent
9
+ }
10
+
11
+ /** @public */
12
+ export interface TLExcalidrawExternalContentSource {
13
+ type: 'excalidraw'
14
+ data: any
15
+ }
16
+
17
+ /** @public */
18
+ export interface TLTextExternalContentSource {
19
+ type: 'text'
20
+ data: string
21
+ subtype: 'json' | 'html' | 'text' | 'url'
22
+ }
23
+
24
+ /** @public */
25
+ export interface TLErrorExternalContentSource {
26
+ type: 'error'
27
+ data: string | null
28
+ reason: string
29
+ }
30
+
5
31
  /** @public */
6
32
  export type TLExternalContentSource =
7
- | {
8
- type: 'tldraw'
9
- data: TLContent
10
- }
11
- | {
12
- type: 'excalidraw'
13
- data: any
14
- }
15
- | {
16
- type: 'text'
17
- data: string
18
- subtype: 'json' | 'html' | 'text' | 'url'
19
- }
20
- | {
21
- type: 'error'
22
- data: string | null
23
- reason: string
24
- }
25
-
26
- /** @public */
27
- export type TLExternalContent<EmbedDefinition> = {
33
+ | TLTldrawExternalContentSource
34
+ | TLExcalidrawExternalContentSource
35
+ | TLTextExternalContentSource
36
+ | TLErrorExternalContentSource
37
+
38
+ /** @public */
39
+ export interface TLBaseExternalContent {
28
40
  sources?: TLExternalContentSource[]
29
41
  point?: VecLike
30
- } & (
31
- | {
32
- type: 'text'
33
- text: string
34
- }
35
- | {
36
- type: 'files'
37
- files: File[]
38
- ignoreParent: boolean
39
- }
40
- | {
41
- type: 'url'
42
- url: string
43
- }
44
- | {
45
- type: 'svg-text'
46
- text: string
47
- }
48
- | {
49
- type: 'embed'
50
- url: string
51
- embed: EmbedDefinition
52
- }
53
- )
54
-
55
- /** @public */
56
- export type TLExternalAssetContent =
57
- | { type: 'file'; file: File; assetId?: TLAssetId }
58
- | { type: 'url'; url: string }
42
+ }
43
+
44
+ /** @public */
45
+ export interface TLTextExternalContent extends TLBaseExternalContent {
46
+ type: 'text'
47
+ text: string
48
+ }
49
+
50
+ /** @public */
51
+ export interface TLFilesExternalContent extends TLBaseExternalContent {
52
+ type: 'files'
53
+ files: File[]
54
+ ignoreParent: boolean
55
+ }
56
+
57
+ /** @public */
58
+ export interface TLUrlExternalContent extends TLBaseExternalContent {
59
+ type: 'url'
60
+ url: string
61
+ }
62
+
63
+ /** @public */
64
+ export interface TLSvgTextExternalContent extends TLBaseExternalContent {
65
+ type: 'svg-text'
66
+ text: string
67
+ }
68
+
69
+ /** @public */
70
+ export interface TLEmbedExternalContent<EmbedDefinition> extends TLBaseExternalContent {
71
+ type: 'embed'
72
+ url: string
73
+ embed: EmbedDefinition
74
+ }
75
+
76
+ /** @public */
77
+ export interface TLTldrawExternalContent extends TLBaseExternalContent {
78
+ type: 'tldraw'
79
+ content: TLContent
80
+ }
81
+
82
+ /** @public */
83
+ export interface TLExcalidrawExternalContent extends TLBaseExternalContent {
84
+ type: 'excalidraw'
85
+ content: any
86
+ }
87
+
88
+ /** @public */
89
+ export type TLExternalContent<EmbedDefinition> =
90
+ | TLTextExternalContent
91
+ | TLFilesExternalContent
92
+ | TLUrlExternalContent
93
+ | TLSvgTextExternalContent
94
+ | TLEmbedExternalContent<EmbedDefinition>
95
+ | TLTldrawExternalContent
96
+ | TLExcalidrawExternalContent
97
+
98
+ /** @public */
99
+ export interface TLFileExternalAsset {
100
+ type: 'file'
101
+ file: File
102
+ assetId?: TLAssetId
103
+ }
104
+
105
+ /** @public */
106
+ export interface TLUrlExternalAsset {
107
+ type: 'url'
108
+ url: string
109
+ }
110
+
111
+ /** @public */
112
+ export type TLExternalAsset = TLFileExternalAsset | TLUrlExternalAsset
@@ -117,16 +117,28 @@ export function useCanvasEvents() {
117
117
  async function onDrop(e: React.DragEvent<Element>) {
118
118
  preventDefault(e)
119
119
  stopEventPropagation(e)
120
- if (!e.dataTransfer?.files?.length) return
121
120
 
122
- const files = Array.from(e.dataTransfer.files)
121
+ if (e.dataTransfer?.files?.length) {
122
+ const files = Array.from(e.dataTransfer.files)
123
123
 
124
- await editor.putExternalContent({
125
- type: 'files',
126
- files,
127
- point: editor.screenToPage({ x: e.clientX, y: e.clientY }),
128
- ignoreParent: false,
129
- })
124
+ await editor.putExternalContent({
125
+ type: 'files',
126
+ files,
127
+ point: editor.screenToPage({ x: e.clientX, y: e.clientY }),
128
+ ignoreParent: false,
129
+ })
130
+ return
131
+ }
132
+
133
+ const url = e.dataTransfer.getData('url')
134
+ if (url) {
135
+ await editor.putExternalContent({
136
+ type: 'url',
137
+ url,
138
+ point: editor.screenToPage({ x: e.clientX, y: e.clientY }),
139
+ })
140
+ return
141
+ }
130
142
  }
131
143
 
132
144
  function onClick(e: React.MouseEvent) {
@@ -2,7 +2,7 @@ import { useValue } from '@tldraw/state-react'
2
2
  import { useEffect } from 'react'
3
3
  import { Editor } from '../editor/Editor'
4
4
  import { TLKeyboardEventInfo } from '../editor/types/event-types'
5
- import { preventDefault, stopEventPropagation } from '../utils/dom'
5
+ import { activeElementShouldCaptureKeys, preventDefault, stopEventPropagation } from '../utils/dom'
6
6
  import { isAccelKey } from '../utils/keyboard'
7
7
  import { useContainer } from './useContainer'
8
8
  import { useEditor } from './useEditor'
@@ -274,15 +274,6 @@ export function useDocumentEvents() {
274
274
  }, [editor, container, isAppFocused])
275
275
  }
276
276
 
277
- const INPUTS = ['input', 'select', 'button', 'textarea']
278
-
279
277
  function areShortcutsDisabled(editor: Editor) {
280
- const { activeElement } = document
281
-
282
- return (
283
- editor.menus.hasOpenMenus() ||
284
- (activeElement &&
285
- (activeElement.getAttribute('contenteditable') ||
286
- INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1))
287
- )
278
+ return editor.menus.hasOpenMenus() || activeElementShouldCaptureKeys()
288
279
  }