@tldraw/editor 3.12.0-canary.3acee343372d → 3.12.0-canary.423f9b4f2a86
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-cjs/index.d.ts +45 -5
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/TldrawEditor.js +4 -0
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +10 -14
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +40 -22
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +1 -1
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FontManager.js +1 -1
- package/dist-cjs/lib/editor/managers/FontManager.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js +4 -1
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/exports/StyleEmbedder.js +19 -5
- package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
- package/dist-cjs/lib/exports/cssRules.js +127 -0
- package/dist-cjs/lib/exports/cssRules.js.map +7 -0
- package/dist-cjs/lib/exports/parseCss.js +0 -69
- package/dist-cjs/lib/exports/parseCss.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +12 -7
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +3 -3
- package/dist-cjs/lib/hooks/useGestureEvents.js +12 -6
- package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
- package/dist-cjs/lib/utils/debug-flags.js +2 -1
- package/dist-cjs/lib/utils/debug-flags.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +45 -5
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/TldrawEditor.mjs +4 -0
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +11 -15
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +40 -22
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +1 -1
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FontManager.mjs +1 -1
- package/dist-esm/lib/editor/managers/FontManager.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs +4 -1
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/exports/StyleEmbedder.mjs +21 -12
- package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
- package/dist-esm/lib/exports/cssRules.mjs +107 -0
- package/dist-esm/lib/exports/cssRules.mjs.map +7 -0
- package/dist-esm/lib/exports/parseCss.mjs +0 -69
- package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +12 -7
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +3 -3
- package/dist-esm/lib/hooks/useGestureEvents.mjs +12 -6
- package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
- package/dist-esm/lib/utils/debug-flags.mjs +2 -1
- package/dist-esm/lib/utils/debug-flags.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +17 -11
- package/package.json +7 -7
- package/src/lib/TldrawEditor.tsx +29 -3
- package/src/lib/components/Shape.tsx +15 -19
- package/src/lib/editor/Editor.ts +71 -23
- package/src/lib/editor/derivations/notVisibleShapes.ts +1 -1
- package/src/lib/editor/managers/FontManager.ts +1 -1
- package/src/lib/editor/tools/StateNode.ts +6 -1
- package/src/lib/exports/StyleEmbedder.ts +25 -15
- package/src/lib/exports/cssRules.ts +126 -0
- package/src/lib/exports/parseCss.ts +0 -79
- package/src/lib/hooks/useCanvasEvents.ts +14 -7
- package/src/lib/hooks/useGestureEvents.ts +12 -6
- package/src/lib/utils/debug-flags.ts +1 -0
- package/src/version.ts +3 -3
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -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.
|
|
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
|
|
808
|
+
private readonly _getShapeVisibility?: TLEditorOptions['getShapeVisibility']
|
|
777
809
|
@computed
|
|
778
810
|
private getIsShapeHiddenCache() {
|
|
779
|
-
if (!this.
|
|
811
|
+
if (!this._getShapeVisibility) return null
|
|
780
812
|
return this.store.createComputedCache<boolean, TLShape>('isShapeHidden', (shape: TLShape) => {
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
@@ -8139,8 +8182,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8139
8182
|
if (!currentTool) return styles
|
|
8140
8183
|
|
|
8141
8184
|
if (currentTool.shapeType) {
|
|
8142
|
-
|
|
8143
|
-
|
|
8185
|
+
if (
|
|
8186
|
+
currentTool.shapeType === 'frame' &&
|
|
8187
|
+
!(this.getShapeUtil('frame')!.options as any).showColors
|
|
8188
|
+
) {
|
|
8189
|
+
for (const style of this.styleProps[currentTool.shapeType].keys()) {
|
|
8190
|
+
if (style.id === 'tldraw:color') continue
|
|
8191
|
+
styles.applyValue(style, this.getStyleForNextShape(style))
|
|
8192
|
+
}
|
|
8193
|
+
} else {
|
|
8194
|
+
for (const style of this.styleProps[currentTool.shapeType].keys()) {
|
|
8195
|
+
styles.applyValue(style, this.getStyleForNextShape(style))
|
|
8196
|
+
}
|
|
8144
8197
|
}
|
|
8145
8198
|
}
|
|
8146
8199
|
|
|
@@ -9849,12 +9902,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9849
9902
|
|
|
9850
9903
|
const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
|
|
9851
9904
|
|
|
9852
|
-
const { panSpeed
|
|
9905
|
+
const { panSpeed } = cameraOptions
|
|
9853
9906
|
this._setCamera(
|
|
9854
9907
|
new Vec(
|
|
9855
|
-
cx + (dx * panSpeed) / cz - x / cz + x /
|
|
9856
|
-
cy + (dy * panSpeed) / cz - y / cz + y /
|
|
9857
|
-
z
|
|
9908
|
+
cx + (dx * panSpeed) / cz - x / cz + x / z,
|
|
9909
|
+
cy + (dy * panSpeed) / cz - y / cz + y / z,
|
|
9910
|
+
z
|
|
9858
9911
|
),
|
|
9859
9912
|
{ immediate: true }
|
|
9860
9913
|
)
|
|
@@ -9929,14 +9982,9 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9929
9982
|
}
|
|
9930
9983
|
|
|
9931
9984
|
const zoom = cz + (delta ?? 0) * zoomSpeed * cz
|
|
9932
|
-
this._setCamera(
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
cy + (y / zoom - y) - (y / cz - y),
|
|
9936
|
-
zoom
|
|
9937
|
-
),
|
|
9938
|
-
{ immediate: true }
|
|
9939
|
-
)
|
|
9985
|
+
this._setCamera(new Vec(cx + x / zoom - x / cz, cy + y / zoom - y / cz, zoom), {
|
|
9986
|
+
immediate: true,
|
|
9987
|
+
})
|
|
9940
9988
|
this.maybeTrackPerformance('Zooming')
|
|
9941
9989
|
return
|
|
9942
9990
|
}
|
|
@@ -31,7 +31,7 @@ export const notVisibleShapes = (editor: Editor) => {
|
|
|
31
31
|
})
|
|
32
32
|
return notVisibleShapes
|
|
33
33
|
}
|
|
34
|
-
return computed<Set<TLShapeId>>('
|
|
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>(
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
270
|
-
|
|
278
|
+
|
|
279
|
+
const rule = getOwnProperty(cssRules, property)
|
|
280
|
+
if (rule && rule(value, property, ruleOptions)) continue
|
|
271
281
|
|
|
272
282
|
styles[property] = value
|
|
273
283
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export type Styles = { [K in string]?: string }
|
|
2
|
+
export type ReadonlyStyles = { readonly [K in string]?: string }
|
|
3
|
+
|
|
4
|
+
type CanSkipRule = (
|
|
5
|
+
value: string,
|
|
6
|
+
property: string,
|
|
7
|
+
options: {
|
|
8
|
+
getStyle(property: string): string
|
|
9
|
+
parentStyles: ReadonlyStyles
|
|
10
|
+
defaultStyles: ReadonlyStyles
|
|
11
|
+
currentColor: string
|
|
12
|
+
}
|
|
13
|
+
) => boolean
|
|
14
|
+
|
|
15
|
+
const isCoveredByCurrentColor: CanSkipRule = (value, property, { currentColor }) => {
|
|
16
|
+
return value === 'currentColor' || value === currentColor
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const isInherited: CanSkipRule = (value, property, { parentStyles }) => {
|
|
20
|
+
return parentStyles[property] === value
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// see comment below about why we exclude border styles
|
|
24
|
+
const isExcludedBorder =
|
|
25
|
+
(borderDirection: string): CanSkipRule =>
|
|
26
|
+
(value, property, { getStyle }) => {
|
|
27
|
+
const borderWidth = getStyle(`border-${borderDirection}-width`)
|
|
28
|
+
const borderStyle = getStyle(`border-${borderDirection}-style`)
|
|
29
|
+
|
|
30
|
+
if (borderWidth === '0px') return true
|
|
31
|
+
if (borderStyle === 'none') return true
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const cssRules = {
|
|
36
|
+
// currentColor properties:
|
|
37
|
+
'border-block-end-color': isCoveredByCurrentColor,
|
|
38
|
+
'border-block-start-color': isCoveredByCurrentColor,
|
|
39
|
+
'border-bottom-color': isCoveredByCurrentColor,
|
|
40
|
+
'border-inline-end-color': isCoveredByCurrentColor,
|
|
41
|
+
'border-inline-start-color': isCoveredByCurrentColor,
|
|
42
|
+
'border-left-color': isCoveredByCurrentColor,
|
|
43
|
+
'border-right-color': isCoveredByCurrentColor,
|
|
44
|
+
'border-top-color': isCoveredByCurrentColor,
|
|
45
|
+
'caret-color': isCoveredByCurrentColor,
|
|
46
|
+
'column-rule-color': isCoveredByCurrentColor,
|
|
47
|
+
'outline-color': isCoveredByCurrentColor,
|
|
48
|
+
'text-decoration': (value, property, { currentColor }) => {
|
|
49
|
+
return value === 'none solid currentColor' || value === 'none solid ' + currentColor
|
|
50
|
+
},
|
|
51
|
+
'text-decoration-color': isCoveredByCurrentColor,
|
|
52
|
+
'text-emphasis-color': isCoveredByCurrentColor,
|
|
53
|
+
|
|
54
|
+
// inherited properties:
|
|
55
|
+
'border-collapse': isInherited,
|
|
56
|
+
'border-spacing': isInherited,
|
|
57
|
+
'caption-side': isInherited,
|
|
58
|
+
// N.B. We shouldn't inherit 'color' because there's some UA styling, e.g. `mark` elements
|
|
59
|
+
// 'color': isInherited,
|
|
60
|
+
cursor: isInherited,
|
|
61
|
+
direction: isInherited,
|
|
62
|
+
'empty-cells': isInherited,
|
|
63
|
+
'font-family': isInherited,
|
|
64
|
+
'font-size': isInherited,
|
|
65
|
+
'font-style': isInherited,
|
|
66
|
+
'font-variant': isInherited,
|
|
67
|
+
'font-weight': isInherited,
|
|
68
|
+
'font-size-adjust': isInherited,
|
|
69
|
+
'font-stretch': isInherited,
|
|
70
|
+
font: isInherited,
|
|
71
|
+
'letter-spacing': isInherited,
|
|
72
|
+
'line-height': isInherited,
|
|
73
|
+
'list-style-image': isInherited,
|
|
74
|
+
'list-style-position': isInherited,
|
|
75
|
+
'list-style-type': isInherited,
|
|
76
|
+
'list-style': isInherited,
|
|
77
|
+
orphans: isInherited,
|
|
78
|
+
'overflow-wrap': isInherited,
|
|
79
|
+
quotes: isInherited,
|
|
80
|
+
'stroke-linecap': isInherited,
|
|
81
|
+
'stroke-linejoin': isInherited,
|
|
82
|
+
'tab-size': isInherited,
|
|
83
|
+
'text-align': isInherited,
|
|
84
|
+
'text-align-last': isInherited,
|
|
85
|
+
'text-indent': isInherited,
|
|
86
|
+
'text-justify': isInherited,
|
|
87
|
+
'text-shadow': isInherited,
|
|
88
|
+
'text-transform': isInherited,
|
|
89
|
+
visibility: isInherited,
|
|
90
|
+
'white-space': isInherited,
|
|
91
|
+
'white-space-collapse': isInherited,
|
|
92
|
+
widows: isInherited,
|
|
93
|
+
'word-break': isInherited,
|
|
94
|
+
'word-spacing': isInherited,
|
|
95
|
+
'word-wrap': isInherited,
|
|
96
|
+
|
|
97
|
+
// special border cases - we have a weird case (tailwind seems to trigger this) where all
|
|
98
|
+
// border-styles sometimes get set to 'solid', but the border-width is 0 so they don't render.
|
|
99
|
+
// but in SVGs, **sometimes**, the border-width defaults (i think from a UA style-sheet? but
|
|
100
|
+
// honestly can't tell) to 1.5px so the border displays. we work around this by only including
|
|
101
|
+
// border styles at all if both the border-width and border-style are set to something that
|
|
102
|
+
// would show a border.
|
|
103
|
+
'border-top': isExcludedBorder('top'),
|
|
104
|
+
'border-right': isExcludedBorder('right'),
|
|
105
|
+
'border-bottom': isExcludedBorder('bottom'),
|
|
106
|
+
'border-left': isExcludedBorder('left'),
|
|
107
|
+
'border-block-end': isExcludedBorder('block-end'),
|
|
108
|
+
'border-block-start': isExcludedBorder('block-start'),
|
|
109
|
+
'border-inline-end': isExcludedBorder('inline-end'),
|
|
110
|
+
'border-inline-start': isExcludedBorder('inline-start'),
|
|
111
|
+
'border-top-style': isExcludedBorder('top'),
|
|
112
|
+
'border-right-style': isExcludedBorder('right'),
|
|
113
|
+
'border-bottom-style': isExcludedBorder('bottom'),
|
|
114
|
+
'border-left-style': isExcludedBorder('left'),
|
|
115
|
+
'border-block-end-style': isExcludedBorder('block-end'),
|
|
116
|
+
'border-block-start-style': isExcludedBorder('block-start'),
|
|
117
|
+
'border-inline-end-style': isExcludedBorder('inline-end'),
|
|
118
|
+
'border-inline-start-style': isExcludedBorder('inline-start'),
|
|
119
|
+
'border-top-width': isExcludedBorder('top'),
|
|
120
|
+
'border-right-width': isExcludedBorder('right'),
|
|
121
|
+
'border-bottom-width': isExcludedBorder('bottom'),
|
|
122
|
+
'border-left-width': isExcludedBorder('left'),
|
|
123
|
+
'border-block-end-width': isExcludedBorder('block-end'),
|
|
124
|
+
'border-block-start-width': isExcludedBorder('block-start'),
|
|
125
|
+
'border-inline-end-width': isExcludedBorder('inline-end'),
|
|
126
|
+
} satisfies Record<string, CanSkipRule>
|
|
@@ -110,82 +110,3 @@ export function parseCssValueUrls(value: string) {
|
|
|
110
110
|
url: m[1] || m[2] || m[3],
|
|
111
111
|
}))
|
|
112
112
|
}
|
|
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
|
-
|
|
145
|
-
const inheritedProperties = new Set([
|
|
146
|
-
'border-collapse',
|
|
147
|
-
'border-spacing',
|
|
148
|
-
'caption-side',
|
|
149
|
-
// N.B. We shouldn't inherit 'color' because there's some UA styling, e.g. `mark` elements
|
|
150
|
-
// 'color',
|
|
151
|
-
'cursor',
|
|
152
|
-
'direction',
|
|
153
|
-
'empty-cells',
|
|
154
|
-
'font-family',
|
|
155
|
-
'font-size',
|
|
156
|
-
'font-style',
|
|
157
|
-
'font-variant',
|
|
158
|
-
'font-weight',
|
|
159
|
-
'font-size-adjust',
|
|
160
|
-
'font-stretch',
|
|
161
|
-
'font',
|
|
162
|
-
'letter-spacing',
|
|
163
|
-
'line-height',
|
|
164
|
-
'list-style-image',
|
|
165
|
-
'list-style-position',
|
|
166
|
-
'list-style-type',
|
|
167
|
-
'list-style',
|
|
168
|
-
'orphans',
|
|
169
|
-
'overflow-wrap',
|
|
170
|
-
'quotes',
|
|
171
|
-
'stroke-linecap',
|
|
172
|
-
'stroke-linejoin',
|
|
173
|
-
'tab-size',
|
|
174
|
-
'text-align',
|
|
175
|
-
'text-align-last',
|
|
176
|
-
'text-indent',
|
|
177
|
-
'text-justify',
|
|
178
|
-
'text-shadow',
|
|
179
|
-
'text-transform',
|
|
180
|
-
'visibility',
|
|
181
|
-
'white-space',
|
|
182
|
-
'white-space-collapse',
|
|
183
|
-
'widows',
|
|
184
|
-
'word-break',
|
|
185
|
-
'word-spacing',
|
|
186
|
-
'word-wrap',
|
|
187
|
-
])
|
|
188
|
-
|
|
189
|
-
export function isPropertyInherited(property: string) {
|
|
190
|
-
return inheritedProperties.has(property)
|
|
191
|
-
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useValue } from '@tldraw/state-react'
|
|
1
2
|
import React, { useMemo } from 'react'
|
|
2
3
|
import { RIGHT_MOUSE_BUTTON } from '../constants'
|
|
3
4
|
import {
|
|
@@ -11,6 +12,7 @@ import { useEditor } from './useEditor'
|
|
|
11
12
|
|
|
12
13
|
export function useCanvasEvents() {
|
|
13
14
|
const editor = useEditor()
|
|
15
|
+
const currentTool = useValue('current tool', () => editor.getCurrentTool(), [editor])
|
|
14
16
|
|
|
15
17
|
const events = useMemo(
|
|
16
18
|
function canvasEvents() {
|
|
@@ -49,12 +51,17 @@ export function useCanvasEvents() {
|
|
|
49
51
|
lastX = e.clientX
|
|
50
52
|
lastY = e.clientY
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
// For tools that benefit from a higher fidelity of events,
|
|
55
|
+
// we dispatch the coalesced events.
|
|
56
|
+
const events = currentTool.useCoalescedEvents ? e.nativeEvent.getCoalescedEvents() : [e]
|
|
57
|
+
for (const singleEvent of events) {
|
|
58
|
+
editor.dispatch({
|
|
59
|
+
type: 'pointer',
|
|
60
|
+
target: 'canvas',
|
|
61
|
+
name: 'pointer_move',
|
|
62
|
+
...getPointerInfo(singleEvent),
|
|
63
|
+
})
|
|
64
|
+
}
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
function onPointerUp(e: React.PointerEvent) {
|
|
@@ -159,7 +166,7 @@ export function useCanvasEvents() {
|
|
|
159
166
|
onClick,
|
|
160
167
|
}
|
|
161
168
|
},
|
|
162
|
-
[editor]
|
|
169
|
+
[editor, currentTool]
|
|
163
170
|
)
|
|
164
171
|
|
|
165
172
|
return events
|
|
@@ -135,7 +135,6 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
|
|
|
135
135
|
|
|
136
136
|
let initDistanceBetweenFingers = 1 // the distance between the two fingers when the pinch starts
|
|
137
137
|
let initZoom = 1 // the browser's zoom level when the pinch starts
|
|
138
|
-
let currZoom = 1 // the current zoom level according to the pinch gesture recognizer
|
|
139
138
|
let currDistanceBetweenFingers = 0
|
|
140
139
|
const initPointBetweenFingers = new Vec()
|
|
141
140
|
const prevPointBetweenFingers = new Vec()
|
|
@@ -239,7 +238,7 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
|
|
|
239
238
|
|
|
240
239
|
switch (pinchState) {
|
|
241
240
|
case 'zooming': {
|
|
242
|
-
currZoom = offset[0]
|
|
241
|
+
const currZoom = offset[0] ** editor.getCameraOptions().zoomSpeed
|
|
243
242
|
|
|
244
243
|
editor.dispatch({
|
|
245
244
|
type: 'pinch',
|
|
@@ -278,7 +277,7 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
|
|
|
278
277
|
if (event instanceof WheelEvent) return
|
|
279
278
|
if (!(event.target === elm || elm?.contains(event.target as Node))) return
|
|
280
279
|
|
|
281
|
-
const scale = offset[0]
|
|
280
|
+
const scale = offset[0] ** editor.getCameraOptions().zoomSpeed
|
|
282
281
|
|
|
283
282
|
pinchState = 'not sure'
|
|
284
283
|
|
|
@@ -309,14 +308,21 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
|
|
|
309
308
|
target: ref,
|
|
310
309
|
eventOptions: { passive: false },
|
|
311
310
|
pinch: {
|
|
312
|
-
from: () =>
|
|
311
|
+
from: () => {
|
|
312
|
+
const { zoomSpeed } = editor.getCameraOptions()
|
|
313
|
+
const level = editor.getZoomLevel() ** (1 / zoomSpeed)
|
|
314
|
+
return [level, 0]
|
|
315
|
+
}, // Return the camera z to use when pinch starts
|
|
313
316
|
scaleBounds: () => {
|
|
314
317
|
const baseZoom = editor.getBaseZoom()
|
|
315
|
-
const zoomSteps = editor.getCameraOptions()
|
|
318
|
+
const { zoomSteps, zoomSpeed } = editor.getCameraOptions()
|
|
316
319
|
const zoomMin = zoomSteps[0] * baseZoom
|
|
317
320
|
const zoomMax = zoomSteps[zoomSteps.length - 1] * baseZoom
|
|
318
321
|
|
|
319
|
-
return {
|
|
322
|
+
return {
|
|
323
|
+
max: zoomMax ** (1 / zoomSpeed),
|
|
324
|
+
min: zoomMin ** (1 / zoomSpeed),
|
|
325
|
+
}
|
|
320
326
|
},
|
|
321
327
|
},
|
|
322
328
|
})
|
|
@@ -53,6 +53,7 @@ export const debugFlags = {
|
|
|
53
53
|
debugGeometry: createDebugValue('debugGeometry', { defaults: { all: false } }),
|
|
54
54
|
hideShapes: createDebugValue('hideShapes', { defaults: { all: false } }),
|
|
55
55
|
editOnType: createDebugValue('editOnType', { defaults: { all: false } }),
|
|
56
|
+
a11y: createDebugValue('a11y', { defaults: { all: false } }),
|
|
56
57
|
} as const
|
|
57
58
|
|
|
58
59
|
declare global {
|
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.12.0-canary.
|
|
4
|
+
export const version = '3.12.0-canary.423f9b4f2a86'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '2025-
|
|
8
|
-
patch: '2025-
|
|
7
|
+
minor: '2025-04-03T13:02:18.145Z',
|
|
8
|
+
patch: '2025-04-03T13:02:18.145Z',
|
|
9
9
|
}
|