@tldraw/editor 3.11.0-canary.ec688dc309ec → 3.11.0-canary.f0072703bfd4

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 (62) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/dist-cjs/index.d.ts +16 -8
  3. package/dist-cjs/index.js +1 -1
  4. package/dist-cjs/index.js.map +2 -2
  5. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +4 -4
  6. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  7. package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js +34 -26
  8. package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js.map +2 -2
  9. package/dist-cjs/lib/config/createTLStore.js +2 -1
  10. package/dist-cjs/lib/config/createTLStore.js.map +2 -2
  11. package/dist-cjs/lib/constants.js +1 -1
  12. package/dist-cjs/lib/constants.js.map +2 -2
  13. package/dist-cjs/lib/editor/Editor.js +18 -10
  14. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  15. package/dist-cjs/lib/exports/StyleEmbedder.js +16 -1
  16. package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
  17. package/dist-cjs/lib/exports/exportToSvg.js.map +1 -1
  18. package/dist-cjs/lib/exports/parseCss.js +27 -2
  19. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  20. package/dist-cjs/lib/hooks/useLocalStore.js +3 -0
  21. package/dist-cjs/lib/hooks/useLocalStore.js.map +2 -2
  22. package/dist-cjs/lib/utils/sync/LocalIndexedDb.js +8 -0
  23. package/dist-cjs/lib/utils/sync/LocalIndexedDb.js.map +2 -2
  24. package/dist-cjs/version.js +3 -3
  25. package/dist-cjs/version.js.map +1 -1
  26. package/dist-esm/index.d.mts +16 -8
  27. package/dist-esm/index.mjs +4 -2
  28. package/dist-esm/index.mjs.map +2 -2
  29. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +4 -4
  30. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  31. package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs +34 -26
  32. package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs.map +2 -2
  33. package/dist-esm/lib/config/createTLStore.mjs +2 -1
  34. package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
  35. package/dist-esm/lib/constants.mjs +1 -1
  36. package/dist-esm/lib/constants.mjs.map +2 -2
  37. package/dist-esm/lib/editor/Editor.mjs +18 -10
  38. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  39. package/dist-esm/lib/exports/StyleEmbedder.mjs +22 -2
  40. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
  41. package/dist-esm/lib/exports/exportToSvg.mjs.map +1 -1
  42. package/dist-esm/lib/exports/parseCss.mjs +27 -2
  43. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  44. package/dist-esm/lib/hooks/useLocalStore.mjs +3 -0
  45. package/dist-esm/lib/hooks/useLocalStore.mjs.map +2 -2
  46. package/dist-esm/lib/utils/sync/LocalIndexedDb.mjs +8 -0
  47. package/dist-esm/lib/utils/sync/LocalIndexedDb.mjs.map +2 -2
  48. package/dist-esm/version.mjs +3 -3
  49. package/dist-esm/version.mjs.map +1 -1
  50. package/package.json +10 -7
  51. package/src/index.ts +4 -1
  52. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +4 -4
  53. package/src/lib/components/default-components/DefaultShapeIndicators.tsx +52 -31
  54. package/src/lib/config/createTLStore.ts +1 -0
  55. package/src/lib/constants.ts +1 -1
  56. package/src/lib/editor/Editor.ts +20 -11
  57. package/src/lib/exports/StyleEmbedder.ts +23 -4
  58. package/src/lib/exports/exportToSvg.tsx +1 -1
  59. package/src/lib/exports/parseCss.ts +36 -2
  60. package/src/lib/hooks/useLocalStore.ts +3 -0
  61. package/src/lib/utils/sync/LocalIndexedDb.ts +9 -0
  62. package/src/version.ts +3 -3
@@ -4,10 +4,24 @@ import { memo, useRef } from 'react'
4
4
  import { useEditor } from '../../hooks/useEditor'
5
5
  import { useEditorComponents } from '../../hooks/useEditorComponents'
6
6
 
7
+ /** @public */
8
+ export interface TLShapeIndicatorsProps {
9
+ /** Whether to hide all of the indicators */
10
+ hideAll?: boolean
11
+ /** Whether to show all of the indicators */
12
+ showAll?: boolean
13
+ }
14
+
7
15
  /** @public @react */
8
- export const DefaultShapeIndicators = memo(function DefaultShapeIndicators() {
16
+ export const DefaultShapeIndicators = memo(function DefaultShapeIndicators({
17
+ hideAll,
18
+ showAll,
19
+ }: TLShapeIndicatorsProps) {
9
20
  const editor = useEditor()
10
21
 
22
+ if (hideAll && showAll)
23
+ throw Error('You cannot set both hideAll and showAll props to true, cmon now')
24
+
11
25
  const rPreviousSelectedShapeIds = useRef<Set<TLShapeId>>(new Set())
12
26
 
13
27
  const idsToDisplay = useValue(
@@ -16,34 +30,38 @@ export const DefaultShapeIndicators = memo(function DefaultShapeIndicators() {
16
30
  const prev = rPreviousSelectedShapeIds.current
17
31
  const next = new Set<TLShapeId>()
18
32
 
19
- if (
20
- // We only show indicators when in the following states...
21
- editor.isInAny(
22
- 'select.idle',
23
- 'select.brushing',
24
- 'select.scribble_brushing',
25
- 'select.editing_shape',
26
- 'select.pointing_shape',
27
- 'select.pointing_selection',
28
- 'select.pointing_handle'
29
- ) &&
30
- // ...but we hide indicators when we've just changed a style (so that the user can see the change)
31
- !editor.getInstanceState().isChangingStyle
32
- ) {
33
- // We always want to show indicators for the selected shapes, if any
34
- const selected = editor.getSelectedShapeIds()
35
- for (const id of selected) {
36
- next.add(id)
37
- }
33
+ const instanceState = editor.getInstanceState()
38
34
 
39
- // If we're idle or editing a shape, we want to also show an indicator for the hovered shape, if any
40
- if (editor.isInAny('select.idle', 'select.editing_shape')) {
41
- const instanceState = editor.getInstanceState()
42
- if (instanceState.isHoveringCanvas && !instanceState.isCoarsePointer) {
43
- const hovered = editor.getHoveredShapeId()
44
- if (hovered) next.add(hovered)
45
- }
46
- }
35
+ const isChangingStyle = instanceState.isChangingStyle
36
+
37
+ // todo: this is tldraw specific and is duplicated at the tldraw layer. What should we do here instead?
38
+
39
+ const isIdleOrEditing = editor.isInAny('select.idle', 'select.editing_shape')
40
+
41
+ const isInSelectState = editor.isInAny(
42
+ 'select.brushing',
43
+ 'select.scribble_brushing',
44
+ 'select.pointing_shape',
45
+ 'select.pointing_selection',
46
+ 'select.pointing_handle'
47
+ )
48
+
49
+ // We hide all indicators if we're changing style or in certain interactions
50
+ // todo: move this to some kind of Tool.hideIndicators property
51
+ if (isChangingStyle || !(isIdleOrEditing || isInSelectState)) {
52
+ rPreviousSelectedShapeIds.current = next
53
+ return next
54
+ }
55
+
56
+ // We always want to show indicators for the selected shapes, if any
57
+ for (const id of editor.getSelectedShapeIds()) {
58
+ next.add(id)
59
+ }
60
+
61
+ // If we're idle or editing a shape, we want to also show an indicator for the hovered shape, if any
62
+ if (isIdleOrEditing && instanceState.isHoveringCanvas && !instanceState.isCoarsePointer) {
63
+ const hovered = editor.getHoveredShapeId()
64
+ if (hovered) next.add(hovered)
47
65
  }
48
66
 
49
67
  // Ok, has anything changed?
@@ -54,7 +72,7 @@ export const DefaultShapeIndicators = memo(function DefaultShapeIndicators() {
54
72
  return next
55
73
  }
56
74
 
57
- // If any of the new ids are not in the previous set, then the selection has changed
75
+ // Set difference check
58
76
  for (const id of next) {
59
77
  if (!prev.has(id)) {
60
78
  rPreviousSelectedShapeIds.current = next
@@ -62,7 +80,6 @@ export const DefaultShapeIndicators = memo(function DefaultShapeIndicators() {
62
80
  }
63
81
  }
64
82
 
65
- // If nothing has changed, then return the previous value
66
83
  return prev
67
84
  },
68
85
  [editor]
@@ -75,6 +92,10 @@ export const DefaultShapeIndicators = memo(function DefaultShapeIndicators() {
75
92
  if (!ShapeIndicator) return null
76
93
 
77
94
  return renderingShapes.map(({ id }) => (
78
- <ShapeIndicator key={id + '_indicator'} shapeId={id} hidden={!idsToDisplay.has(id)} />
95
+ <ShapeIndicator
96
+ key={id + '_indicator'}
97
+ shapeId={id}
98
+ hidden={!showAll && (hideAll || !idsToDisplay.has(id))}
99
+ />
79
100
  ))
80
101
  })
@@ -119,6 +119,7 @@ export function createTLStore({
119
119
  assets: {
120
120
  upload: assets.upload,
121
121
  resolve: assets.resolve ?? defaultAssetResolve,
122
+ remove: assets.remove ?? (() => Promise.resolve()),
122
123
  },
123
124
  onMount: (editor) => {
124
125
  assert(editor instanceof Editor)
@@ -7,7 +7,7 @@ export const DEFAULT_CAMERA_OPTIONS: TLCameraOptions = {
7
7
  wheelBehavior: 'pan',
8
8
  panSpeed: 1,
9
9
  zoomSpeed: 1,
10
- zoomSteps: [0.1, 0.25, 0.5, 1, 2, 4, 8],
10
+ zoomSteps: [0.05, 0.1, 0.25, 0.5, 1, 2, 4, 8],
11
11
  }
12
12
 
13
13
  /** @internal */
@@ -4218,7 +4218,13 @@ export class Editor extends EventEmitter<TLEventMap> {
4218
4218
  : (assets as TLAsset[]).map((a) => a.id)
4219
4219
  if (ids.length <= 0) return this
4220
4220
 
4221
- this.run(() => this.store.remove(ids), { history: 'ignore' })
4221
+ this.run(
4222
+ () => {
4223
+ this.store.props.assets.remove?.(ids)
4224
+ this.store.remove(ids)
4225
+ },
4226
+ { history: 'ignore' }
4227
+ )
4222
4228
  return this
4223
4229
  }
4224
4230
 
@@ -6328,21 +6334,22 @@ export class Editor extends EventEmitter<TLEventMap> {
6328
6334
  *
6329
6335
  * @example
6330
6336
  * ```ts
6331
- * editor.stackShapes([box1, box2], 'horizontal', 32)
6332
- * editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal', 32)
6337
+ * editor.stackShapes([box1, box2], 'horizontal')
6338
+ * editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal')
6333
6339
  * ```
6334
6340
  *
6335
6341
  * @param shapes - The shapes (or shape ids) to stack.
6336
6342
  * @param operation - Whether to stack horizontally or vertically.
6337
- * @param gap - The gap to leave between shapes.
6343
+ * @param gap - The gap to leave between shapes. By default, uses the editor's `adjacentShapeMargin` option.
6338
6344
  *
6339
6345
  * @public
6340
6346
  */
6341
6347
  stackShapes(
6342
6348
  shapes: TLShapeId[] | TLShape[],
6343
6349
  operation: 'horizontal' | 'vertical',
6344
- gap: number
6350
+ gap?: number
6345
6351
  ): this {
6352
+ const _gap = gap ?? this.options.adjacentShapeMargin
6346
6353
  const ids =
6347
6354
  typeof shapes[0] === 'string'
6348
6355
  ? (shapes as TLShapeId[])
@@ -6400,7 +6407,7 @@ export class Editor extends EventEmitter<TLEventMap> {
6400
6407
  }
6401
6408
 
6402
6409
  const len = shapeClustersToStack.length
6403
- if ((gap === 0 && len < 3) || len < 2) return this
6410
+ if ((_gap === 0 && len < 3) || len < 2) return this
6404
6411
 
6405
6412
  let val: 'x' | 'y'
6406
6413
  let min: 'minX' | 'minY'
@@ -6421,7 +6428,7 @@ export class Editor extends EventEmitter<TLEventMap> {
6421
6428
 
6422
6429
  let shapeGap: number = 0
6423
6430
 
6424
- if (gap === 0) {
6431
+ if (_gap === 0) {
6425
6432
  // note: this is not used in the current tldraw.com; there we use a specified stack
6426
6433
 
6427
6434
  const gaps: Record<number, number> = {}
@@ -6461,7 +6468,7 @@ export class Editor extends EventEmitter<TLEventMap> {
6461
6468
  }
6462
6469
  } else {
6463
6470
  // If a gap was provided, then use that instead.
6464
- shapeGap = gap
6471
+ shapeGap = _gap
6465
6472
  }
6466
6473
 
6467
6474
  const changes: TLShapePartial[] = []
@@ -6500,17 +6507,19 @@ export class Editor extends EventEmitter<TLEventMap> {
6500
6507
  *
6501
6508
  * @example
6502
6509
  * ```ts
6503
- * editor.packShapes([box1, box2], 32)
6510
+ * editor.packShapes([box1, box2])
6504
6511
  * editor.packShapes(editor.getSelectedShapeIds(), 32)
6505
6512
  * ```
6506
6513
  *
6507
6514
  *
6508
6515
  * @param shapes - The shapes (or shape ids) to pack.
6509
- * @param gap - The padding to apply to the packed shapes. Defaults to 16.
6516
+ * @param gap - The padding to apply to the packed shapes. Defaults to the editor's `adjacentShapeMargin` option.
6510
6517
  */
6511
- packShapes(shapes: TLShapeId[] | TLShape[], gap: number): this {
6518
+ packShapes(shapes: TLShapeId[] | TLShape[], _gap?: number): this {
6512
6519
  if (this.getIsReadonly()) return this
6513
6520
 
6521
+ const gap = _gap ?? this.options.adjacentShapeMargin
6522
+
6514
6523
  const ids =
6515
6524
  typeof shapes[0] === 'string'
6516
6525
  ? (shapes as TLShapeId[])
@@ -7,7 +7,12 @@ import {
7
7
  getRenderedChildren,
8
8
  } from './domUtils'
9
9
  import { resourceToDataUrl } from './fetchCache'
10
- import { isPropertyInherited, parseCssValueUrls, shouldIncludeCssProperty } from './parseCss'
10
+ import {
11
+ isPropertyCoveredByCurrentColor,
12
+ isPropertyInherited,
13
+ parseCssValueUrls,
14
+ shouldIncludeCssProperty,
15
+ } from './parseCss'
11
16
 
12
17
  type Styles = { [K in string]?: string }
13
18
  type ReadonlyStyles = { readonly [K in string]?: string }
@@ -53,9 +58,20 @@ export class StyleEmbedder {
53
58
  ? getDefaultStylesForTagName(element.tagName.toLowerCase())
54
59
  : NO_STYLES
55
60
 
56
- const parentStyles = shouldSkipInheritedParentStyles
57
- ? (this.styles.get(element.parentElement as Element)?.self ?? NO_STYLES)
58
- : NO_STYLES
61
+ const parentStyles = Object.assign({}, NO_STYLES) as Styles
62
+ if (shouldSkipInheritedParentStyles) {
63
+ let el = element.parentElement
64
+ // Keep going up the tree to find all the relevant styles
65
+ while (el) {
66
+ const currentStyles = this.styles.get(el)?.self
67
+ for (const style in currentStyles) {
68
+ if (!parentStyles[style]) {
69
+ parentStyles[style] = currentStyles[style]
70
+ }
71
+ }
72
+ el = el.parentElement
73
+ }
74
+ }
59
75
 
60
76
  const info: ElementStyleInfo = {
61
77
  self: styleFromElement(element, { defaultStyles, parentStyles }),
@@ -230,6 +246,8 @@ function styleFromComputedStyleMap(
230
246
 
231
247
  if (defaultStyles[property] === value) continue
232
248
  if (parentStyles[property] === value && isPropertyInherited(property)) continue
249
+ if (isPropertyCoveredByCurrentColor(style.get('color')?.toString() || '', property, value))
250
+ continue
233
251
 
234
252
  styles[property] = value
235
253
  }
@@ -249,6 +267,7 @@ function styleFromComputedStyle(
249
267
 
250
268
  if (defaultStyles[property] === value) continue
251
269
  if (parentStyles[property] === value && isPropertyInherited(property)) continue
270
+ if (isPropertyCoveredByCurrentColor(style.color, property, value)) continue
252
271
 
253
272
  styles[property] = value
254
273
  }
@@ -62,7 +62,7 @@ export async function exportToSvg(
62
62
  const svg = renderTarget.firstElementChild
63
63
  assert(svg instanceof SVGSVGElement, 'Expected an SVG element')
64
64
 
65
- // And apply any changes to <foreignObject> elements that we need to make. Whilst we're in
65
+ // And apply any changes to <foreignObject> elements that we need to make. while we're in
66
66
  // the document, these elements work exactly as we'd expect from other dom elements - they
67
67
  // can load external resources, and any stylesheets in the document apply to them as we
68
68
  // would expect them to. But when we pull the SVG into its own file or draw it to a canvas
@@ -111,11 +111,43 @@ export function parseCssValueUrls(value: string) {
111
111
  }))
112
112
  }
113
113
 
114
+ const currentColorProperties = new Set([
115
+ 'border-block-end-color',
116
+ 'border-block-start-color',
117
+ 'border-bottom-color',
118
+ 'border-inline-end-color',
119
+ 'border-inline-start-color',
120
+ 'border-left-color',
121
+ 'border-right-color',
122
+ 'border-top-color',
123
+ 'caret-color',
124
+ 'column-rule-color',
125
+ 'outline-color',
126
+ 'text-decoration',
127
+ 'text-decoration-color',
128
+ 'text-emphasis-color',
129
+ ])
130
+
131
+ export function isPropertyCoveredByCurrentColor(
132
+ currentColor: string,
133
+ property: string,
134
+ value: string
135
+ ) {
136
+ if (currentColorProperties.has(property)) {
137
+ return (
138
+ value === 'currentColor' ||
139
+ value === currentColor ||
140
+ (property === 'text-decoration' && value === `none solid ${currentColor}`)
141
+ )
142
+ }
143
+ }
144
+
114
145
  const inheritedProperties = new Set([
115
146
  'border-collapse',
116
147
  'border-spacing',
117
148
  'caption-side',
118
- 'color',
149
+ // N.B. We shouldn't inherit 'color' because there's some UA styling, e.g. `mark` elements
150
+ // 'color',
119
151
  'cursor',
120
152
  'direction',
121
153
  'empty-cells',
@@ -136,16 +168,18 @@ const inheritedProperties = new Set([
136
168
  'orphans',
137
169
  'overflow-wrap',
138
170
  'quotes',
171
+ 'stroke-linecap',
172
+ 'stroke-linejoin',
139
173
  'tab-size',
140
174
  'text-align',
141
175
  'text-align-last',
142
- 'text-decoration-color',
143
176
  'text-indent',
144
177
  'text-justify',
145
178
  'text-shadow',
146
179
  'text-transform',
147
180
  'visibility',
148
181
  'white-space',
182
+ 'white-space-collapse',
149
183
  'widows',
150
184
  'word-break',
151
185
  'word-spacing',
@@ -52,6 +52,9 @@ export function useLocalStore(
52
52
 
53
53
  return asset.props.src
54
54
  },
55
+ remove: async (assetIds) => {
56
+ await client.db.removeAssets(assetIds)
57
+ },
55
58
  ...rest.assets,
56
59
  }
57
60
 
@@ -303,6 +303,15 @@ export class LocalIndexedDb {
303
303
  await assetsStore.put(blob, assetId)
304
304
  })
305
305
  }
306
+
307
+ async removeAssets(assetId: string[]) {
308
+ await this.tx('readwrite', [Table.Assets], async (tx) => {
309
+ const assetsStore = tx.objectStore(Table.Assets)
310
+ for (const id of assetId) {
311
+ await assetsStore.delete(id)
312
+ }
313
+ })
314
+ }
306
315
  }
307
316
 
308
317
  /** @internal */
package/src/version.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '3.11.0-canary.ec688dc309ec'
4
+ export const version = '3.11.0-canary.f0072703bfd4'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-03-13T14:36:37.598Z',
8
- patch: '2025-03-13T14:36:37.598Z',
7
+ minor: '2025-03-19T13:05:59.554Z',
8
+ patch: '2025-03-19T13:05:59.554Z',
9
9
  }