@tldraw/editor 3.12.0-canary.3acee343372d → 3.12.0-canary.3e2ed74b5e86

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 (104) hide show
  1. package/dist-cjs/index.d.ts +123 -17
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +4 -0
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/GeometryDebuggingView.js +2 -2
  7. package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
  8. package/dist-cjs/lib/components/Shape.js +10 -14
  9. package/dist-cjs/lib/components/Shape.js.map +2 -2
  10. package/dist-cjs/lib/editor/Editor.js +79 -30
  11. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  12. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +1 -1
  13. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
  14. package/dist-cjs/lib/editor/managers/FontManager.js +1 -1
  15. package/dist-cjs/lib/editor/managers/FontManager.js.map +2 -2
  16. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +1 -13
  17. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  18. package/dist-cjs/lib/editor/tools/StateNode.js +4 -1
  19. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  20. package/dist-cjs/lib/exports/StyleEmbedder.js +19 -5
  21. package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
  22. package/dist-cjs/lib/exports/cssRules.js +127 -0
  23. package/dist-cjs/lib/exports/cssRules.js.map +7 -0
  24. package/dist-cjs/lib/exports/parseCss.js +0 -69
  25. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  26. package/dist-cjs/lib/hooks/useCanvasEvents.js +12 -7
  27. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +3 -3
  28. package/dist-cjs/lib/hooks/useGestureEvents.js +12 -6
  29. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  30. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +133 -16
  31. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +3 -3
  32. package/dist-cjs/lib/primitives/geometry/Group2d.js +54 -11
  33. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  34. package/dist-cjs/lib/primitives/intersect.js +20 -0
  35. package/dist-cjs/lib/primitives/intersect.js.map +2 -2
  36. package/dist-cjs/lib/utils/debug-flags.js +2 -1
  37. package/dist-cjs/lib/utils/debug-flags.js.map +2 -2
  38. package/dist-cjs/lib/utils/reorderShapes.js +2 -8
  39. package/dist-cjs/lib/utils/reorderShapes.js.map +2 -2
  40. package/dist-cjs/version.js +3 -3
  41. package/dist-cjs/version.js.map +1 -1
  42. package/dist-esm/index.d.mts +123 -17
  43. package/dist-esm/index.mjs +8 -2
  44. package/dist-esm/index.mjs.map +2 -2
  45. package/dist-esm/lib/TldrawEditor.mjs +4 -0
  46. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  47. package/dist-esm/lib/components/GeometryDebuggingView.mjs +3 -3
  48. package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
  49. package/dist-esm/lib/components/Shape.mjs +11 -15
  50. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  51. package/dist-esm/lib/editor/Editor.mjs +79 -30
  52. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  53. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +1 -1
  54. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  55. package/dist-esm/lib/editor/managers/FontManager.mjs +1 -1
  56. package/dist-esm/lib/editor/managers/FontManager.mjs.map +2 -2
  57. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +1 -13
  58. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  59. package/dist-esm/lib/editor/tools/StateNode.mjs +4 -1
  60. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  61. package/dist-esm/lib/exports/StyleEmbedder.mjs +21 -12
  62. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
  63. package/dist-esm/lib/exports/cssRules.mjs +107 -0
  64. package/dist-esm/lib/exports/cssRules.mjs.map +7 -0
  65. package/dist-esm/lib/exports/parseCss.mjs +0 -69
  66. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  67. package/dist-esm/lib/hooks/useCanvasEvents.mjs +12 -7
  68. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +3 -3
  69. package/dist-esm/lib/hooks/useGestureEvents.mjs +12 -6
  70. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  71. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +137 -14
  72. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  73. package/dist-esm/lib/primitives/geometry/Group2d.mjs +55 -12
  74. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  75. package/dist-esm/lib/primitives/intersect.mjs +20 -0
  76. package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
  77. package/dist-esm/lib/utils/debug-flags.mjs +2 -1
  78. package/dist-esm/lib/utils/debug-flags.mjs.map +2 -2
  79. package/dist-esm/lib/utils/reorderShapes.mjs +2 -8
  80. package/dist-esm/lib/utils/reorderShapes.mjs.map +2 -2
  81. package/dist-esm/version.mjs +3 -3
  82. package/dist-esm/version.mjs.map +1 -1
  83. package/editor.css +17 -11
  84. package/package.json +7 -7
  85. package/src/index.ts +6 -1
  86. package/src/lib/TldrawEditor.tsx +29 -3
  87. package/src/lib/components/GeometryDebuggingView.tsx +3 -3
  88. package/src/lib/components/Shape.tsx +15 -19
  89. package/src/lib/editor/Editor.ts +115 -38
  90. package/src/lib/editor/derivations/notVisibleShapes.ts +1 -1
  91. package/src/lib/editor/managers/FontManager.ts +1 -1
  92. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +3 -15
  93. package/src/lib/editor/tools/StateNode.ts +6 -1
  94. package/src/lib/exports/StyleEmbedder.ts +25 -15
  95. package/src/lib/exports/cssRules.ts +126 -0
  96. package/src/lib/exports/parseCss.ts +0 -79
  97. package/src/lib/hooks/useCanvasEvents.ts +14 -7
  98. package/src/lib/hooks/useGestureEvents.ts +12 -6
  99. package/src/lib/primitives/geometry/Geometry2d.ts +196 -16
  100. package/src/lib/primitives/geometry/Group2d.ts +76 -13
  101. package/src/lib/primitives/intersect.ts +41 -0
  102. package/src/lib/utils/debug-flags.ts +1 -0
  103. package/src/lib/utils/reorderShapes.ts +2 -9
  104. package/src/version.ts +3 -3
@@ -50,7 +50,7 @@ import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
50
50
 
51
51
  /**
52
52
  * Props for the {@link tldraw#Tldraw} and {@link TldrawEditor} components, when passing in a
53
- * {@link store#TLStore} directly. If you would like tldraw to create a store for you, use
53
+ * `TLStore` directly. If you would like tldraw to create a store for you, use
54
54
  * {@link TldrawEditorWithoutStoreProps}.
55
55
  *
56
56
  * @public
@@ -64,7 +64,7 @@ export interface TldrawEditorWithStoreProps {
64
64
 
65
65
  /**
66
66
  * Props for the {@link tldraw#Tldraw} and {@link TldrawEditor} components, when not passing in a
67
- * {@link store#TLStore} directly. If you would like to pass in a store directly, use
67
+ * `TLStore` directly. If you would like to pass in a store directly, use
68
68
  * {@link TldrawEditorWithStoreProps}.
69
69
  *
70
70
  * @public
@@ -191,11 +191,33 @@ export interface TldrawEditorBaseProps {
191
191
  /**
192
192
  * Predicate for whether or not a shape should be hidden.
193
193
  *
194
+ * @deprecated Use {@link TldrawEditorBaseProps#getShapeVisibility} instead.
195
+ */
196
+ isShapeHidden?(shape: TLShape, editor: Editor): boolean
197
+
198
+ /**
199
+ * Provides a way to hide shapes.
200
+ *
194
201
  * Hidden shapes will not render in the editor, and they will not be eligible for hit test via
195
202
  * {@link Editor#getShapeAtPoint} and {@link Editor#getShapesAtPoint}. But otherwise they will
196
203
  * remain in the store and participate in all other operations.
204
+ *
205
+ * @example
206
+ * ```ts
207
+ * getShapeVisibility={(shape, editor) => shape.meta.hidden ? 'hidden' : 'inherit'}
208
+ * ```
209
+ *
210
+ * - `'inherit' | undefined` - (default) The shape will be visible unless its parent is hidden.
211
+ * - `'hidden'` - The shape will be hidden.
212
+ * - `'visible'` - The shape will be visible.
213
+ *
214
+ * @param shape - The shape to check.
215
+ * @param editor - The editor instance.
197
216
  */
198
- isShapeHidden?(shape: TLShape, editor: Editor): boolean
217
+ getShapeVisibility?(
218
+ shape: TLShape,
219
+ editor: Editor
220
+ ): 'visible' | 'hidden' | 'inherit' | null | undefined
199
221
 
200
222
  /**
201
223
  * The URLs for the fonts to use in the editor.
@@ -387,7 +409,9 @@ function TldrawEditorWithReadyStore({
387
409
  options,
388
410
  licenseKey,
389
411
  deepLinks: _deepLinks,
412
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
390
413
  isShapeHidden,
414
+ getShapeVisibility,
391
415
  assetUrls,
392
416
  }: Required<
393
417
  TldrawEditorProps & {
@@ -447,6 +471,7 @@ function TldrawEditorWithReadyStore({
447
471
  options,
448
472
  licenseKey,
449
473
  isShapeHidden,
474
+ getShapeVisibility,
450
475
  fontAssetUrls: assetUrls?.fonts,
451
476
  })
452
477
 
@@ -482,6 +507,7 @@ function TldrawEditorWithReadyStore({
482
507
  setEditor,
483
508
  licenseKey,
484
509
  isShapeHidden,
510
+ getShapeVisibility,
485
511
  textOptions,
486
512
  assetUrls,
487
513
  ]
@@ -114,13 +114,13 @@ export const GeometryDebuggingView = track(function GeometryDebuggingView({
114
114
  function GeometryStroke({ geometry }: { geometry: Geometry2d }) {
115
115
  if (geometry instanceof Group2d) {
116
116
  return (
117
- <>
117
+ <g stroke={geometry.debugColor}>
118
118
  {[...geometry.children, ...geometry.ignoredChildren].map((child, i) => (
119
119
  <GeometryStroke geometry={child} key={i} />
120
120
  ))}
121
- </>
121
+ </g>
122
122
  )
123
123
  }
124
124
 
125
- return <path d={geometry.toSimpleSvgPath()} />
125
+ return <path d={geometry.toSimpleSvgPath()} stroke={geometry.debugColor} />
126
126
  }
@@ -1,7 +1,7 @@
1
1
  import { react } from '@tldraw/state'
2
2
  import { useQuickReactor, useStateTracking } from '@tldraw/state-react'
3
3
  import { TLShape, TLShapeId } from '@tldraw/tlschema'
4
- import { memo, useCallback, useEffect, useRef } from 'react'
4
+ import { memo, useCallback, useEffect, useLayoutEffect, useRef } from 'react'
5
5
  import { ShapeUtil } from '../editor/shapes/ShapeUtil'
6
6
  import { useEditor } from '../hooks/useEditor'
7
7
  import { useEditorComponents } from '../hooks/useEditorComponents'
@@ -44,10 +44,10 @@ export const Shape = memo(function Shape({
44
44
 
45
45
  useEffect(() => {
46
46
  return react('load fonts', () => {
47
- const fonts = editor.fonts.getShapeFontFaces(shape)
47
+ const fonts = editor.fonts.getShapeFontFaces(id)
48
48
  editor.fonts.requestFonts(fonts)
49
49
  })
50
- }, [editor, shape])
50
+ }, [editor, id])
51
51
 
52
52
  const memoizedStuffRef = useRef({
53
53
  transform: '',
@@ -104,22 +104,18 @@ export const Shape = memo(function Shape({
104
104
  )
105
105
 
106
106
  // This stuff changes pretty infrequently, so we can change them together
107
- useQuickReactor(
108
- 'set opacity and z-index',
109
- () => {
110
- const container = containerRef.current
111
- const bgContainer = bgContainerRef.current
112
-
113
- // Opacity
114
- setStyleProperty(container, 'opacity', opacity)
115
- setStyleProperty(bgContainer, 'opacity', opacity)
116
-
117
- // Z-Index
118
- setStyleProperty(container, 'z-index', index)
119
- setStyleProperty(bgContainer, 'z-index', backgroundIndex)
120
- },
121
- [opacity, index, backgroundIndex]
122
- )
107
+ useLayoutEffect(() => {
108
+ const container = containerRef.current
109
+ const bgContainer = bgContainerRef.current
110
+
111
+ // Opacity
112
+ setStyleProperty(container, 'opacity', opacity)
113
+ setStyleProperty(bgContainer, 'opacity', opacity)
114
+
115
+ // Z-Index
116
+ setStyleProperty(container, 'z-index', index)
117
+ setStyleProperty(bgContainer, 'z-index', backgroundIndex)
118
+ }, [opacity, index, backgroundIndex])
123
119
 
124
120
  useQuickReactor(
125
121
  'set display',
@@ -241,10 +241,33 @@ export interface TLEditorOptions {
241
241
  fontAssetUrls?: { [key: string]: string | undefined }
242
242
  /**
243
243
  * A predicate that should return true if the given shape should be hidden.
244
+ *
245
+ * @deprecated Use {@link Editor#getShapeVisibility} instead.
246
+ *
244
247
  * @param shape - The shape to check.
245
248
  * @param editor - The editor instance.
246
249
  */
247
250
  isShapeHidden?(shape: TLShape, editor: Editor): boolean
251
+
252
+ /**
253
+ * Provides a way to hide shapes.
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * getShapeVisibility={(shape, editor) => shape.meta.hidden ? 'hidden' : 'inherit'}
258
+ * ```
259
+ *
260
+ * - `'inherit' | undefined` - (default) The shape will be visible unless its parent is hidden.
261
+ * - `'hidden'` - The shape will be hidden.
262
+ * - `'visible'` - The shape will be visible.
263
+ *
264
+ * @param shape - The shape to check.
265
+ * @param editor - The editor instance.
266
+ */
267
+ getShapeVisibility?(
268
+ shape: TLShape,
269
+ editor: Editor
270
+ ): 'visible' | 'hidden' | 'inherit' | null | undefined
248
271
  }
249
272
 
250
273
  /**
@@ -281,12 +304,21 @@ export class Editor extends EventEmitter<TLEventMap> {
281
304
  autoFocus,
282
305
  inferDarkMode,
283
306
  options,
307
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
284
308
  isShapeHidden,
309
+ getShapeVisibility,
285
310
  fontAssetUrls,
286
311
  }: TLEditorOptions) {
287
312
  super()
313
+ assert(
314
+ !(isShapeHidden && getShapeVisibility),
315
+ 'Cannot use both isShapeHidden and getShapeVisibility'
316
+ )
288
317
 
289
- this._isShapeHiddenPredicate = isShapeHidden
318
+ this._getShapeVisibility = isShapeHidden
319
+ ? // eslint-disable-next-line @typescript-eslint/no-deprecated
320
+ (shape: TLShape, editor: Editor) => (isShapeHidden(shape, editor) ? 'hidden' : 'inherit')
321
+ : getShapeVisibility
290
322
 
291
323
  this.options = { ...defaultTldrawOptions, ...options }
292
324
 
@@ -773,18 +805,22 @@ export class Editor extends EventEmitter<TLEventMap> {
773
805
  }
774
806
  }
775
807
 
776
- private readonly _isShapeHiddenPredicate?: (shape: TLShape, editor: Editor) => boolean
808
+ private readonly _getShapeVisibility?: TLEditorOptions['getShapeVisibility']
777
809
  @computed
778
810
  private getIsShapeHiddenCache() {
779
- if (!this._isShapeHiddenPredicate) return null
811
+ if (!this._getShapeVisibility) return null
780
812
  return this.store.createComputedCache<boolean, TLShape>('isShapeHidden', (shape: TLShape) => {
781
- const hiddenParent = this.findShapeAncestor(shape, (p) => this.isShapeHidden(p))
782
- if (hiddenParent) return true
783
- return this._isShapeHiddenPredicate!(shape, this) ?? false
813
+ const visibility = this._getShapeVisibility!(shape, this)
814
+ const isParentHidden = PageRecordType.isId(shape.parentId)
815
+ ? false
816
+ : this.isShapeHidden(shape.parentId)
817
+
818
+ if (isParentHidden) return visibility !== 'visible'
819
+ return visibility === 'hidden'
784
820
  })
785
821
  }
786
822
  isShapeHidden(shapeOrId: TLShape | TLShapeId): boolean {
787
- if (!this._isShapeHiddenPredicate) return false
823
+ if (!this._getShapeVisibility) return false
788
824
  return !!this.getIsShapeHiddenCache!()!.get(
789
825
  typeof shapeOrId === 'string' ? shapeOrId : shapeOrId.id
790
826
  )
@@ -1216,7 +1252,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1216
1252
  run(fn: () => void, opts?: TLEditorRunOptions): this {
1217
1253
  const previousIgnoreShapeLock = this._shouldIgnoreShapeLock
1218
1254
  this._shouldIgnoreShapeLock = opts?.ignoreShapeLock ?? previousIgnoreShapeLock
1219
-
1220
1255
  try {
1221
1256
  this.history.batch(fn, opts)
1222
1257
  } finally {
@@ -3712,7 +3747,15 @@ export class Editor extends EventEmitter<TLEventMap> {
3712
3747
  const addShapeById = (id: TLShapeId, opacity: number, isAncestorErasing: boolean) => {
3713
3748
  const shape = this.getShape(id)
3714
3749
  if (!shape) return
3715
- if (this.isShapeHidden(shape)) return
3750
+
3751
+ if (this.isShapeHidden(shape)) {
3752
+ // process children just in case they are overriding the hidden state
3753
+ const isErasing = isAncestorErasing || erasingShapeIds.includes(id)
3754
+ for (const childId of this.getSortedChildIdsForParent(id)) {
3755
+ addShapeById(childId, opacity, isErasing)
3756
+ }
3757
+ return
3758
+ }
3716
3759
 
3717
3760
  opacity *= shape.opacity
3718
3761
  let isShapeErasing = false
@@ -4287,7 +4330,7 @@ export class Editor extends EventEmitter<TLEventMap> {
4287
4330
  private _shapeGeometryCaches: Record<string, ComputedCache<Geometry2d, TLShape>> = {}
4288
4331
 
4289
4332
  /**
4290
- * Get the geometry of a shape.
4333
+ * Get the geometry of a shape in shape-space.
4291
4334
  *
4292
4335
  * @example
4293
4336
  * ```ts
@@ -4318,6 +4361,44 @@ export class Editor extends EventEmitter<TLEventMap> {
4318
4361
  )! as T
4319
4362
  }
4320
4363
 
4364
+ private _shapePageGeometryCaches: Record<string, ComputedCache<Geometry2d, TLShape>> = {}
4365
+
4366
+ /**
4367
+ * Get the geometry of a shape in page-space.
4368
+ *
4369
+ * @example
4370
+ * ```ts
4371
+ * editor.getShapePageGeometry(myShape)
4372
+ * editor.getShapePageGeometry(myShapeId)
4373
+ * editor.getShapePageGeometry(myShapeId, { context: "arrow" })
4374
+ * ```
4375
+ *
4376
+ * @param shape - The shape (or shape id) to get the geometry for.
4377
+ * @param opts - Additional options about the request for geometry. Passed to {@link ShapeUtil.getGeometry}.
4378
+ *
4379
+ * @public
4380
+ */
4381
+ getShapePageGeometry<T extends Geometry2d>(shape: TLShape | TLShapeId, opts?: TLGeometryOpts): T {
4382
+ const context = opts?.context ?? 'none'
4383
+ if (!this._shapePageGeometryCaches[context]) {
4384
+ this._shapePageGeometryCaches[context] = this.store.createComputedCache(
4385
+ 'bounds',
4386
+ (shape) => {
4387
+ const geometry = this.getShapeGeometry(shape.id, opts)
4388
+ const pageTransform = this.getShapePageTransform(shape.id)
4389
+ return geometry.transform(pageTransform)
4390
+ },
4391
+ {
4392
+ // we only depend directly on the shape id, and changing geometry/transform will update us anyway
4393
+ areRecordsEqual: () => true,
4394
+ }
4395
+ )
4396
+ }
4397
+ return this._shapePageGeometryCaches[context].get(
4398
+ typeof shape === 'string' ? shape : shape.id
4399
+ )! as T
4400
+ }
4401
+
4321
4402
  /** @internal */
4322
4403
  @computed private _getShapeHandlesCache(): ComputedCache<TLHandle[] | undefined, TLShape> {
4323
4404
  return this.store.createComputedCache('handles', (shape) => {
@@ -4424,15 +4505,7 @@ export class Editor extends EventEmitter<TLEventMap> {
4424
4505
  /** @internal */
4425
4506
  @computed private _getShapePageBoundsCache(): ComputedCache<Box, TLShape> {
4426
4507
  return this.store.createComputedCache<Box, TLShape>('pageBoundsCache', (shape) => {
4427
- const pageTransform = this._getShapePageTransformCache().get(shape.id)
4428
-
4429
- if (!pageTransform) return new Box()
4430
-
4431
- const result = Box.FromPoints(
4432
- Mat.applyToPoints(pageTransform, this.getShapeGeometry(shape).vertices)
4433
- )
4434
-
4435
- return result
4508
+ return this.getShapePageGeometry(shape).bounds
4436
4509
  })
4437
4510
  }
4438
4511
 
@@ -4506,11 +4579,10 @@ export class Editor extends EventEmitter<TLEventMap> {
4506
4579
  if (frameAncestors.length === 0) return undefined
4507
4580
 
4508
4581
  const pageMask = frameAncestors
4509
- .map<Vec[] | undefined>((s) =>
4510
- // Apply the frame transform to the frame outline to get the frame outline in the current page space
4511
- this._getShapePageTransformCache()
4512
- .get(s.id)!
4513
- .applyToPoints(this.getShapeGeometry(s).vertices)
4582
+ .map<Vec[] | undefined>(
4583
+ (s) =>
4584
+ // Apply the frame transform to the frame outline to get the frame outline in the current page space
4585
+ this.getShapePageGeometry(s.id).vertices
4514
4586
  )
4515
4587
  .reduce((acc, b) => {
4516
4588
  if (!(b && acc)) return undefined
@@ -8139,8 +8211,18 @@ export class Editor extends EventEmitter<TLEventMap> {
8139
8211
  if (!currentTool) return styles
8140
8212
 
8141
8213
  if (currentTool.shapeType) {
8142
- for (const style of this.styleProps[currentTool.shapeType].keys()) {
8143
- styles.applyValue(style, this.getStyleForNextShape(style))
8214
+ if (
8215
+ currentTool.shapeType === 'frame' &&
8216
+ !(this.getShapeUtil('frame')!.options as any).showColors
8217
+ ) {
8218
+ for (const style of this.styleProps[currentTool.shapeType].keys()) {
8219
+ if (style.id === 'tldraw:color') continue
8220
+ styles.applyValue(style, this.getStyleForNextShape(style))
8221
+ }
8222
+ } else {
8223
+ for (const style of this.styleProps[currentTool.shapeType].keys()) {
8224
+ styles.applyValue(style, this.getStyleForNextShape(style))
8225
+ }
8144
8226
  }
8145
8227
  }
8146
8228
 
@@ -9849,12 +9931,12 @@ export class Editor extends EventEmitter<TLEventMap> {
9849
9931
 
9850
9932
  const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
9851
9933
 
9852
- const { panSpeed, zoomSpeed } = cameraOptions
9934
+ const { panSpeed } = cameraOptions
9853
9935
  this._setCamera(
9854
9936
  new Vec(
9855
- cx + (dx * panSpeed) / cz - x / cz + x / (z * zoomSpeed),
9856
- cy + (dy * panSpeed) / cz - y / cz + y / (z * zoomSpeed),
9857
- z * zoomSpeed
9937
+ cx + (dx * panSpeed) / cz - x / cz + x / z,
9938
+ cy + (dy * panSpeed) / cz - y / cz + y / z,
9939
+ z
9858
9940
  ),
9859
9941
  { immediate: true }
9860
9942
  )
@@ -9929,14 +10011,9 @@ export class Editor extends EventEmitter<TLEventMap> {
9929
10011
  }
9930
10012
 
9931
10013
  const zoom = cz + (delta ?? 0) * zoomSpeed * cz
9932
- this._setCamera(
9933
- new Vec(
9934
- cx + (x / zoom - x) - (x / cz - x),
9935
- cy + (y / zoom - y) - (y / cz - y),
9936
- zoom
9937
- ),
9938
- { immediate: true }
9939
- )
10014
+ this._setCamera(new Vec(cx + x / zoom - x / cz, cy + y / zoom - y / cz, zoom), {
10015
+ immediate: true,
10016
+ })
9940
10017
  this.maybeTrackPerformance('Zooming')
9941
10018
  return
9942
10019
  }
@@ -31,7 +31,7 @@ export const notVisibleShapes = (editor: Editor) => {
31
31
  })
32
32
  return notVisibleShapes
33
33
  }
34
- return computed<Set<TLShapeId>>('getCulledShapes', (prevValue) => {
34
+ return computed<Set<TLShapeId>>('notVisibleShapes', (prevValue) => {
35
35
  if (isUninitialized(prevValue)) {
36
36
  return fromScratch(editor)
37
37
  }
@@ -94,7 +94,7 @@ export class FontManager {
94
94
  const shapeUtil = this.editor.getShapeUtil(shape)
95
95
  return shapeUtil.getFontFaces(shape)
96
96
  },
97
- { areResultsEqual: areArraysShallowEqual }
97
+ { areResultsEqual: areArraysShallowEqual, areRecordsEqual: (a, b) => a.props === b.props }
98
98
  )
99
99
 
100
100
  this.shapeFontLoadStateCache = editor.store.createCache<(FontState | null)[], TLShape>(
@@ -2,8 +2,6 @@ import { TLGroupShape, groupShapeMigrations, groupShapeProps } from '@tldraw/tls
2
2
  import { SVGContainer } from '../../../components/SVGContainer'
3
3
  import { Geometry2d } from '../../../primitives/geometry/Geometry2d'
4
4
  import { Group2d } from '../../../primitives/geometry/Group2d'
5
- import { Polygon2d } from '../../../primitives/geometry/Polygon2d'
6
- import { Polyline2d } from '../../../primitives/geometry/Polyline2d'
7
5
  import { Rectangle2d } from '../../../primitives/geometry/Rectangle2d'
8
6
  import { ShapeUtil } from '../ShapeUtil'
9
7
  import { DashedOutlineBox } from './DashedOutlineBox'
@@ -35,19 +33,9 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
35
33
  return new Group2d({
36
34
  children: children.map((childId) => {
37
35
  const shape = this.editor.getShape(childId)!
38
- const geometry = this.editor.getShapeGeometry(childId)
39
- const points = this.editor.getShapeLocalTransform(shape)!.applyToPoints(geometry.vertices)
40
-
41
- if (geometry.isClosed) {
42
- return new Polygon2d({
43
- points,
44
- isFilled: true,
45
- })
46
- }
47
-
48
- return new Polyline2d({
49
- points,
50
- })
36
+ return this.editor
37
+ .getShapeGeometry(childId)
38
+ .transform(this.editor.getShapeLocalTransform(shape)!)
51
39
  }),
52
40
  })
53
41
  }
@@ -38,6 +38,7 @@ export interface TLStateNodeConstructor {
38
38
  initial?: string
39
39
  children?(): TLStateNodeConstructor[]
40
40
  isLockable: boolean
41
+ useCoalescedEvents: boolean
41
42
  }
42
43
 
43
44
  /** @public */
@@ -47,7 +48,8 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
47
48
  public editor: Editor,
48
49
  parent?: StateNode
49
50
  ) {
50
- const { id, children, initial, isLockable } = this.constructor as TLStateNodeConstructor
51
+ const { id, children, initial, isLockable, useCoalescedEvents } = this
52
+ .constructor as TLStateNodeConstructor
51
53
 
52
54
  this.id = id
53
55
  this._isActive = atom<boolean>('toolIsActive' + this.id, false)
@@ -83,6 +85,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
83
85
  }
84
86
  }
85
87
  this.isLockable = isLockable
88
+ this.useCoalescedEvents = useCoalescedEvents
86
89
  this.performanceTracker = new PerformanceTracker()
87
90
  }
88
91
 
@@ -90,6 +93,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
90
93
  static initial?: string
91
94
  static children?: () => TLStateNodeConstructor[]
92
95
  static isLockable = true
96
+ static useCoalescedEvents = false
93
97
 
94
98
  id: string
95
99
  type: 'branch' | 'leaf' | 'root'
@@ -97,6 +101,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
97
101
  initial?: string
98
102
  children?: Record<string, StateNode>
99
103
  isLockable: boolean
104
+ useCoalescedEvents: boolean
100
105
  parent: StateNode
101
106
 
102
107
  /**
@@ -1,5 +1,6 @@
1
- import { assertExists, objectMapValues, uniqueId } from '@tldraw/utils'
1
+ import { assertExists, getOwnProperty, objectMapValues, uniqueId } from '@tldraw/utils'
2
2
  import { FontEmbedder } from './FontEmbedder'
3
+ import { ReadonlyStyles, Styles, cssRules } from './cssRules'
3
4
  import {
4
5
  elementStyle,
5
6
  getComputedStyle,
@@ -7,15 +8,8 @@ import {
7
8
  getRenderedChildren,
8
9
  } from './domUtils'
9
10
  import { resourceToDataUrl } from './fetchCache'
10
- import {
11
- isPropertyCoveredByCurrentColor,
12
- isPropertyInherited,
13
- parseCssValueUrls,
14
- shouldIncludeCssProperty,
15
- } from './parseCss'
16
-
17
- type Styles = { [K in string]?: string }
18
- type ReadonlyStyles = { readonly [K in string]?: string }
11
+ import { parseCssValueUrls, shouldIncludeCssProperty } from './parseCss'
12
+
19
13
  const NO_STYLES = {} as const
20
14
 
21
15
  interface ElementStyleInfo {
@@ -239,15 +233,22 @@ function styleFromComputedStyleMap(
239
233
  { defaultStyles, parentStyles }: ReadStyleOpts
240
234
  ) {
241
235
  const styles: Record<string, string> = {}
236
+ const currentColor = style.get('color')?.toString() || ''
237
+ const ruleOptions = {
238
+ currentColor,
239
+ parentStyles,
240
+ defaultStyles,
241
+ getStyle: (property: string) => style.get(property)?.toString() ?? '',
242
+ }
242
243
  for (const property of style.keys()) {
243
244
  if (!shouldIncludeCssProperty(property)) continue
244
245
 
245
246
  const value = style.get(property)!.toString()
246
247
 
247
248
  if (defaultStyles[property] === value) continue
248
- if (parentStyles[property] === value && isPropertyInherited(property)) continue
249
- if (isPropertyCoveredByCurrentColor(style.get('color')?.toString() || '', property, value))
250
- continue
249
+
250
+ const rule = getOwnProperty(cssRules, property)
251
+ if (rule && rule(value, property, ruleOptions)) continue
251
252
 
252
253
  styles[property] = value
253
254
  }
@@ -260,14 +261,23 @@ function styleFromComputedStyle(
260
261
  { defaultStyles, parentStyles }: ReadStyleOpts
261
262
  ) {
262
263
  const styles: Record<string, string> = {}
264
+ const currentColor = style.color
265
+ const ruleOptions = {
266
+ currentColor,
267
+ parentStyles,
268
+ defaultStyles,
269
+ getStyle: (property: string) => style.getPropertyValue(property),
270
+ }
271
+
263
272
  for (const property in style) {
264
273
  if (!shouldIncludeCssProperty(property)) continue
265
274
 
266
275
  const value = style.getPropertyValue(property)
267
276
 
268
277
  if (defaultStyles[property] === value) continue
269
- if (parentStyles[property] === value && isPropertyInherited(property)) continue
270
- if (isPropertyCoveredByCurrentColor(style.color, property, value)) continue
278
+
279
+ const rule = getOwnProperty(cssRules, property)
280
+ if (rule && rule(value, property, ruleOptions)) continue
271
281
 
272
282
  styles[property] = value
273
283
  }