@tldraw/editor 3.15.0 → 3.16.0-canary.0e0fb8bde89d
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 +101 -0
- package/dist-cjs/index.js +3 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +3 -1
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/MenuClickCapture.js +0 -5
- package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +4 -26
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js +53 -0
- package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js.map +7 -0
- package/dist-cjs/lib/editor/Editor.js +64 -35
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/hooks/useCanvasEvents.js +24 -20
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditorComponents.js +2 -0
- package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
- package/dist-cjs/lib/hooks/useStateAttribute.js +35 -0
- package/dist-cjs/lib/hooks/useStateAttribute.js.map +7 -0
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +101 -0
- package/dist-esm/index.mjs +5 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +3 -1
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/MenuClickCapture.mjs +0 -5
- package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +4 -26
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs +23 -0
- package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs.map +7 -0
- package/dist-esm/lib/editor/Editor.mjs +64 -35
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +25 -21
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditorComponents.mjs +4 -0
- package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useStateAttribute.mjs +15 -0
- package/dist-esm/lib/hooks/useStateAttribute.mjs.map +7 -0
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +4 -23
- package/package.json +7 -7
- package/src/index.ts +5 -0
- package/src/lib/TldrawEditor.tsx +7 -5
- package/src/lib/components/MenuClickCapture.tsx +0 -8
- package/src/lib/components/Shape.tsx +6 -21
- package/src/lib/components/default-components/DefaultShapeWrapper.tsx +35 -0
- package/src/lib/editor/Editor.ts +71 -35
- package/src/lib/editor/shapes/ShapeUtil.ts +57 -0
- package/src/lib/editor/types/misc-types.ts +19 -0
- package/src/lib/hooks/useCanvasEvents.ts +39 -32
- package/src/lib/hooks/useEditorComponents.tsx +7 -1
- package/src/lib/hooks/useStateAttribute.ts +15 -0
- package/src/version.ts +3 -3
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { TLShape } from '@tldraw/tlschema'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
import { forwardRef, ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
/** @public */
|
|
6
|
+
export interface TLShapeWrapperProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
/** The shape being rendered. */
|
|
8
|
+
shape: TLShape
|
|
9
|
+
/** Whether this is the shapes regular, or background component. */
|
|
10
|
+
isBackground: boolean
|
|
11
|
+
/** The shape's rendered component. */
|
|
12
|
+
children: ReactNode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** @public @react */
|
|
16
|
+
export const DefaultShapeWrapper = forwardRef(function DefaultShapeWrapper(
|
|
17
|
+
{ children, shape, isBackground, ...props }: TLShapeWrapperProps,
|
|
18
|
+
ref: React.Ref<HTMLDivElement>
|
|
19
|
+
) {
|
|
20
|
+
const isFilledShape = 'fill' in shape.props && shape.props.fill !== 'none'
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
ref={ref}
|
|
25
|
+
data-shape-type={shape.type}
|
|
26
|
+
data-shape-is-filled={isBackground ? undefined : isFilledShape}
|
|
27
|
+
data-shape-id={shape.id}
|
|
28
|
+
draggable={false}
|
|
29
|
+
{...props}
|
|
30
|
+
className={classNames('tl-shape', isBackground && 'tl-shape-background', props.className)}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
})
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -178,6 +178,7 @@ import {
|
|
|
178
178
|
TLCameraOptions,
|
|
179
179
|
TLImageExportOptions,
|
|
180
180
|
TLSvgExportOptions,
|
|
181
|
+
TLUpdatePointerOptions,
|
|
181
182
|
} from './types/misc-types'
|
|
182
183
|
import { TLAdjacentDirection, TLResizeHandle } from './types/selection-types'
|
|
183
184
|
|
|
@@ -3072,7 +3073,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3072
3073
|
// Dispatch a new pointer move because the pointer's page will have changed
|
|
3073
3074
|
// (its screen position will compute to a new page position given the new camera position)
|
|
3074
3075
|
const { currentScreenPoint, currentPagePoint } = this.inputs
|
|
3075
|
-
const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
3076
3076
|
|
|
3077
3077
|
// compare the next page point (derived from the current camera) to the current page point
|
|
3078
3078
|
if (
|
|
@@ -3080,27 +3080,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3080
3080
|
currentScreenPoint.y / z - y !== currentPagePoint.y
|
|
3081
3081
|
) {
|
|
3082
3082
|
// If it's changed, dispatch a pointer event
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
target: 'canvas',
|
|
3086
|
-
name: 'pointer_move',
|
|
3087
|
-
// weird but true: we need to put the screen point back into client space
|
|
3088
|
-
point: Vec.AddXY(currentScreenPoint, screenBounds.x, screenBounds.y),
|
|
3083
|
+
this.updatePointer({
|
|
3084
|
+
immediate: opts?.immediate,
|
|
3089
3085
|
pointerId: INTERNAL_POINTER_IDS.CAMERA_MOVE,
|
|
3090
|
-
|
|
3091
|
-
altKey: this.inputs.altKey,
|
|
3092
|
-
shiftKey: this.inputs.shiftKey,
|
|
3093
|
-
metaKey: this.inputs.metaKey,
|
|
3094
|
-
accelKey: isAccelKey(this.inputs),
|
|
3095
|
-
button: 0,
|
|
3096
|
-
isPen: this.getInstanceState().isPenMode ?? false,
|
|
3097
|
-
}
|
|
3098
|
-
|
|
3099
|
-
if (opts?.immediate) {
|
|
3100
|
-
this._flushEventForTick(event)
|
|
3101
|
-
} else {
|
|
3102
|
-
this.dispatch(event)
|
|
3103
|
-
}
|
|
3086
|
+
})
|
|
3104
3087
|
}
|
|
3105
3088
|
|
|
3106
3089
|
this._tickCameraState()
|
|
@@ -4421,21 +4404,28 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4421
4404
|
*/
|
|
4422
4405
|
deletePage(page: TLPageId | TLPage): this {
|
|
4423
4406
|
const id = typeof page === 'string' ? page : page.id
|
|
4424
|
-
this.run(
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4407
|
+
this.run(
|
|
4408
|
+
() => {
|
|
4409
|
+
if (this.getIsReadonly()) return
|
|
4410
|
+
const pages = this.getPages()
|
|
4411
|
+
if (pages.length === 1) return
|
|
4428
4412
|
|
|
4429
|
-
|
|
4430
|
-
|
|
4413
|
+
const deletedPage = this.getPage(id)
|
|
4414
|
+
if (!deletedPage) return
|
|
4431
4415
|
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4416
|
+
if (id === this.getCurrentPageId()) {
|
|
4417
|
+
const index = pages.findIndex((page) => page.id === id)
|
|
4418
|
+
const next = pages[index - 1] ?? pages[index + 1]
|
|
4419
|
+
this.setCurrentPage(next.id)
|
|
4420
|
+
}
|
|
4421
|
+
|
|
4422
|
+
const shapes = this.getSortedChildIdsForParent(deletedPage.id)
|
|
4423
|
+
this.deleteShapes(shapes)
|
|
4424
|
+
|
|
4425
|
+
this.store.remove([deletedPage.id])
|
|
4426
|
+
},
|
|
4427
|
+
{ ignoreShapeLock: true }
|
|
4428
|
+
)
|
|
4439
4429
|
return this
|
|
4440
4430
|
}
|
|
4441
4431
|
|
|
@@ -5222,8 +5212,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5222
5212
|
// Check labels first
|
|
5223
5213
|
if (
|
|
5224
5214
|
this.isShapeOfType<TLFrameShape>(shape, 'frame') ||
|
|
5225
|
-
(this.isShapeOfType<TLArrowShape>(shape, 'arrow') && shape.props.text.trim()) ||
|
|
5226
5215
|
((this.isShapeOfType<TLNoteShape>(shape, 'note') ||
|
|
5216
|
+
this.isShapeOfType<TLArrowShape>(shape, 'arrow') ||
|
|
5227
5217
|
(this.isShapeOfType<TLGeoShape>(shape, 'geo') && shape.props.fill === 'none')) &&
|
|
5228
5218
|
this.getShapeUtil(shape).getText(shape)?.trim())
|
|
5229
5219
|
) {
|
|
@@ -9673,6 +9663,52 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9673
9663
|
return this
|
|
9674
9664
|
}
|
|
9675
9665
|
|
|
9666
|
+
/**
|
|
9667
|
+
* Dispatch a pointer move event in the current position of the pointer. This is useful when
|
|
9668
|
+
* external circumstances have changed (e.g. the camera moved or a shape was moved) and you want
|
|
9669
|
+
* the current interaction to respond to that change.
|
|
9670
|
+
*
|
|
9671
|
+
* @example
|
|
9672
|
+
* ```ts
|
|
9673
|
+
* editor.updatePointer()
|
|
9674
|
+
* ```
|
|
9675
|
+
*
|
|
9676
|
+
* @param options - The options for updating the pointer.
|
|
9677
|
+
* @returns The editor instance.
|
|
9678
|
+
* @public
|
|
9679
|
+
*/
|
|
9680
|
+
updatePointer(options?: TLUpdatePointerOptions): this {
|
|
9681
|
+
const event: TLPointerEventInfo = {
|
|
9682
|
+
type: 'pointer',
|
|
9683
|
+
target: 'canvas',
|
|
9684
|
+
name: 'pointer_move',
|
|
9685
|
+
point:
|
|
9686
|
+
options?.point ??
|
|
9687
|
+
// weird but true: what `inputs` calls screen-space is actually viewport space. so
|
|
9688
|
+
// we need to convert back into true screen space first. we should fix this...
|
|
9689
|
+
Vec.Add(
|
|
9690
|
+
this.inputs.currentScreenPoint,
|
|
9691
|
+
this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
|
|
9692
|
+
),
|
|
9693
|
+
pointerId: options?.pointerId ?? 0,
|
|
9694
|
+
button: options?.button ?? 0,
|
|
9695
|
+
isPen: options?.isPen ?? this.inputs.isPen,
|
|
9696
|
+
shiftKey: options?.shiftKey ?? this.inputs.shiftKey,
|
|
9697
|
+
altKey: options?.altKey ?? this.inputs.altKey,
|
|
9698
|
+
ctrlKey: options?.ctrlKey ?? this.inputs.ctrlKey,
|
|
9699
|
+
metaKey: options?.metaKey ?? this.inputs.metaKey,
|
|
9700
|
+
accelKey: options?.accelKey ?? isAccelKey(this.inputs),
|
|
9701
|
+
}
|
|
9702
|
+
|
|
9703
|
+
if (options?.immediate) {
|
|
9704
|
+
this._flushEventForTick(event)
|
|
9705
|
+
} else {
|
|
9706
|
+
this.dispatch(event)
|
|
9707
|
+
}
|
|
9708
|
+
|
|
9709
|
+
return this
|
|
9710
|
+
}
|
|
9711
|
+
|
|
9676
9712
|
/**
|
|
9677
9713
|
* Puts the editor into focused mode.
|
|
9678
9714
|
*
|
|
@@ -584,6 +584,15 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
584
584
|
*/
|
|
585
585
|
onResizeEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void
|
|
586
586
|
|
|
587
|
+
/**
|
|
588
|
+
* A callback called when a shape resize is cancelled.
|
|
589
|
+
*
|
|
590
|
+
* @param initial - The shape at the start of the resize.
|
|
591
|
+
* @param current - The current shape.
|
|
592
|
+
* @public
|
|
593
|
+
*/
|
|
594
|
+
onResizeCancel?(initial: Shape, current: Shape): void
|
|
595
|
+
|
|
587
596
|
/**
|
|
588
597
|
* A callback called when a shape starts being translated.
|
|
589
598
|
*
|
|
@@ -613,6 +622,25 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
613
622
|
*/
|
|
614
623
|
onTranslateEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void
|
|
615
624
|
|
|
625
|
+
/**
|
|
626
|
+
* A callback called when a shape translation is cancelled.
|
|
627
|
+
*
|
|
628
|
+
* @param initial - The shape at the start of the translation.
|
|
629
|
+
* @param current - The current shape.
|
|
630
|
+
* @public
|
|
631
|
+
*/
|
|
632
|
+
onTranslateCancel?(initial: Shape, current: Shape): void
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* A callback called when a shape's handle starts being dragged.
|
|
636
|
+
*
|
|
637
|
+
* @param shape - The shape.
|
|
638
|
+
* @param info - An object containing the handle and whether the handle is 'precise' or not.
|
|
639
|
+
* @returns A change to apply to the shape, or void.
|
|
640
|
+
* @public
|
|
641
|
+
*/
|
|
642
|
+
onHandleDragStart?(shape: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void
|
|
643
|
+
|
|
616
644
|
/**
|
|
617
645
|
* A callback called when a shape's handle changes.
|
|
618
646
|
*
|
|
@@ -623,6 +651,25 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
623
651
|
*/
|
|
624
652
|
onHandleDrag?(shape: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void
|
|
625
653
|
|
|
654
|
+
/**
|
|
655
|
+
* A callback called when a shape's handle finishes being dragged.
|
|
656
|
+
*
|
|
657
|
+
* @param current - The current shape.
|
|
658
|
+
* @param info - An object containing the handle and whether the handle is 'precise' or not.
|
|
659
|
+
* @returns A change to apply to the shape, or void.
|
|
660
|
+
* @public
|
|
661
|
+
*/
|
|
662
|
+
onHandleDragEnd?(current: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* A callback called when a shape's handle drag is cancelled.
|
|
666
|
+
*
|
|
667
|
+
* @param current - The current shape.
|
|
668
|
+
* @param info - An object containing the handle and whether the handle is 'precise' or not.
|
|
669
|
+
* @public
|
|
670
|
+
*/
|
|
671
|
+
onHandleDragCancel?(current: Shape, info: TLHandleDragInfo<Shape>): void
|
|
672
|
+
|
|
626
673
|
/**
|
|
627
674
|
* A callback called when a shape starts being rotated.
|
|
628
675
|
*
|
|
@@ -652,6 +699,15 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
652
699
|
*/
|
|
653
700
|
onRotateEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void
|
|
654
701
|
|
|
702
|
+
/**
|
|
703
|
+
* A callback called when a shape rotation is cancelled.
|
|
704
|
+
*
|
|
705
|
+
* @param initial - The shape at the start of the rotation.
|
|
706
|
+
* @param current - The current shape.
|
|
707
|
+
* @public
|
|
708
|
+
*/
|
|
709
|
+
onRotateCancel?(initial: Shape, current: Shape): void
|
|
710
|
+
|
|
655
711
|
/**
|
|
656
712
|
* Not currently used.
|
|
657
713
|
*
|
|
@@ -819,5 +875,6 @@ export interface TLResizeInfo<T extends TLShape> {
|
|
|
819
875
|
export interface TLHandleDragInfo<T extends TLShape> {
|
|
820
876
|
handle: TLHandle
|
|
821
877
|
isPrecise: boolean
|
|
878
|
+
isCreatingShape: boolean
|
|
822
879
|
initial?: T | undefined
|
|
823
880
|
}
|
|
@@ -187,3 +187,22 @@ export interface TLCameraConstraints {
|
|
|
187
187
|
y: 'free' | 'fixed' | 'inside' | 'outside' | 'contain'
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
+
|
|
191
|
+
/** @public */
|
|
192
|
+
export interface TLUpdatePointerOptions {
|
|
193
|
+
/** Whether to update the pointer immediately, rather than on the next tick. */
|
|
194
|
+
immediate?: boolean
|
|
195
|
+
/**
|
|
196
|
+
* The point, in screen-space, to update the pointer to. Defaults to the position of the last
|
|
197
|
+
* pointer event.
|
|
198
|
+
*/
|
|
199
|
+
point?: VecLike
|
|
200
|
+
pointerId?: number
|
|
201
|
+
ctrlKey?: boolean
|
|
202
|
+
altKey?: boolean
|
|
203
|
+
shiftKey?: boolean
|
|
204
|
+
metaKey?: boolean
|
|
205
|
+
accelKey?: boolean
|
|
206
|
+
isPen?: boolean
|
|
207
|
+
button?: number
|
|
208
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useValue } from '@tldraw/state-react'
|
|
2
|
-
import React, { useMemo } from 'react'
|
|
2
|
+
import React, { useEffect, useMemo } from 'react'
|
|
3
3
|
import { RIGHT_MOUSE_BUTTON } from '../constants'
|
|
4
4
|
import {
|
|
5
5
|
preventDefault,
|
|
@@ -16,9 +16,6 @@ export function useCanvasEvents() {
|
|
|
16
16
|
|
|
17
17
|
const events = useMemo(
|
|
18
18
|
function canvasEvents() {
|
|
19
|
-
// Track the last screen point
|
|
20
|
-
let lastX: number, lastY: number
|
|
21
|
-
|
|
22
19
|
function onPointerDown(e: React.PointerEvent) {
|
|
23
20
|
if ((e as any).isKilled) return
|
|
24
21
|
|
|
@@ -44,35 +41,9 @@ export function useCanvasEvents() {
|
|
|
44
41
|
})
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
function onPointerMove(e: React.PointerEvent) {
|
|
48
|
-
if ((e as any).isKilled) return
|
|
49
|
-
|
|
50
|
-
if (e.clientX === lastX && e.clientY === lastY) return
|
|
51
|
-
lastX = e.clientX
|
|
52
|
-
lastY = e.clientY
|
|
53
|
-
|
|
54
|
-
// For tools that benefit from a higher fidelity of events,
|
|
55
|
-
// we dispatch the coalesced events.
|
|
56
|
-
// N.B. Sometimes getCoalescedEvents isn't present on iOS, ugh.
|
|
57
|
-
const events =
|
|
58
|
-
currentTool.useCoalescedEvents && e.nativeEvent.getCoalescedEvents
|
|
59
|
-
? e.nativeEvent.getCoalescedEvents()
|
|
60
|
-
: [e]
|
|
61
|
-
for (const singleEvent of events) {
|
|
62
|
-
editor.dispatch({
|
|
63
|
-
type: 'pointer',
|
|
64
|
-
target: 'canvas',
|
|
65
|
-
name: 'pointer_move',
|
|
66
|
-
...getPointerInfo(singleEvent),
|
|
67
|
-
})
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
44
|
function onPointerUp(e: React.PointerEvent) {
|
|
72
45
|
if ((e as any).isKilled) return
|
|
73
46
|
if (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return
|
|
74
|
-
lastX = e.clientX
|
|
75
|
-
lastY = e.clientY
|
|
76
47
|
|
|
77
48
|
releasePointerCapture(e.currentTarget, e)
|
|
78
49
|
|
|
@@ -158,7 +129,6 @@ export function useCanvasEvents() {
|
|
|
158
129
|
|
|
159
130
|
return {
|
|
160
131
|
onPointerDown,
|
|
161
|
-
onPointerMove,
|
|
162
132
|
onPointerUp,
|
|
163
133
|
onPointerEnter,
|
|
164
134
|
onPointerLeave,
|
|
@@ -169,8 +139,45 @@ export function useCanvasEvents() {
|
|
|
169
139
|
onClick,
|
|
170
140
|
}
|
|
171
141
|
},
|
|
172
|
-
[editor
|
|
142
|
+
[editor]
|
|
173
143
|
)
|
|
174
144
|
|
|
145
|
+
// onPointerMove is special: where we're only interested in the other events when they're
|
|
146
|
+
// happening _on_ the canvas (as opposed to outside of it, or on UI floating over it), we want
|
|
147
|
+
// the pointer position to be up to date regardless of whether it's over the tldraw canvas or
|
|
148
|
+
// not. So instead of returning a listener to be attached to the canvas, we directly attach a
|
|
149
|
+
// listener to the whole document instead.
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
let lastX: number, lastY: number
|
|
152
|
+
|
|
153
|
+
function onPointerMove(e: PointerEvent) {
|
|
154
|
+
if ((e as any).isKilled) return
|
|
155
|
+
;(e as any).isKilled = true
|
|
156
|
+
|
|
157
|
+
if (e.clientX === lastX && e.clientY === lastY) return
|
|
158
|
+
lastX = e.clientX
|
|
159
|
+
lastY = e.clientY
|
|
160
|
+
|
|
161
|
+
// For tools that benefit from a higher fidelity of events,
|
|
162
|
+
// we dispatch the coalesced events.
|
|
163
|
+
// N.B. Sometimes getCoalescedEvents isn't present on iOS, ugh.
|
|
164
|
+
const events =
|
|
165
|
+
currentTool.useCoalescedEvents && e.getCoalescedEvents ? e.getCoalescedEvents() : [e]
|
|
166
|
+
for (const singleEvent of events) {
|
|
167
|
+
editor.dispatch({
|
|
168
|
+
type: 'pointer',
|
|
169
|
+
target: 'canvas',
|
|
170
|
+
name: 'pointer_move',
|
|
171
|
+
...getPointerInfo(singleEvent),
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
document.body.addEventListener('pointermove', onPointerMove)
|
|
177
|
+
return () => {
|
|
178
|
+
document.body.removeEventListener('pointermove', onPointerMove)
|
|
179
|
+
}
|
|
180
|
+
}, [editor, currentTool])
|
|
181
|
+
|
|
175
182
|
return events
|
|
176
183
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ComponentType, ReactNode, createContext, useContext, useMemo } from 'react'
|
|
1
|
+
import { ComponentType, ReactNode, RefAttributes, createContext, useContext, useMemo } from 'react'
|
|
2
2
|
import { DefaultBackground } from '../components/default-components/DefaultBackground'
|
|
3
3
|
import { DefaultBrush, TLBrushProps } from '../components/default-components/DefaultBrush'
|
|
4
4
|
import {
|
|
@@ -37,6 +37,10 @@ import {
|
|
|
37
37
|
TLShapeIndicatorErrorFallbackComponent,
|
|
38
38
|
} from '../components/default-components/DefaultShapeIndicatorErrorFallback'
|
|
39
39
|
import { DefaultShapeIndicators } from '../components/default-components/DefaultShapeIndicators'
|
|
40
|
+
import {
|
|
41
|
+
DefaultShapeWrapper,
|
|
42
|
+
TLShapeWrapperProps,
|
|
43
|
+
} from '../components/default-components/DefaultShapeWrapper'
|
|
40
44
|
import {
|
|
41
45
|
DefaultSnapIndicator,
|
|
42
46
|
TLSnapIndicatorProps,
|
|
@@ -68,6 +72,7 @@ export interface TLEditorComponents {
|
|
|
68
72
|
SelectionForeground?: ComponentType<TLSelectionForegroundProps> | null
|
|
69
73
|
ShapeIndicator?: ComponentType<TLShapeIndicatorProps> | null
|
|
70
74
|
ShapeIndicators?: ComponentType | null
|
|
75
|
+
ShapeWrapper?: ComponentType<TLShapeWrapperProps & RefAttributes<HTMLDivElement>> | null
|
|
71
76
|
SnapIndicator?: ComponentType<TLSnapIndicatorProps> | null
|
|
72
77
|
Spinner?: ComponentType<React.SVGProps<SVGSVGElement>> | null
|
|
73
78
|
SvgDefs?: ComponentType | null
|
|
@@ -114,6 +119,7 @@ export function EditorComponentsProvider({
|
|
|
114
119
|
SelectionForeground: DefaultSelectionForeground,
|
|
115
120
|
ShapeIndicator: DefaultShapeIndicator,
|
|
116
121
|
ShapeIndicators: DefaultShapeIndicators,
|
|
122
|
+
ShapeWrapper: DefaultShapeWrapper,
|
|
117
123
|
SnapIndicator: DefaultSnapIndicator,
|
|
118
124
|
Spinner: DefaultSpinner,
|
|
119
125
|
SvgDefs: DefaultSvgDefs,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { react } from '@tldraw/state'
|
|
2
|
+
import { useLayoutEffect } from 'react'
|
|
3
|
+
import { useEditor } from './useEditor'
|
|
4
|
+
|
|
5
|
+
export function useStateAttribute() {
|
|
6
|
+
const editor = useEditor()
|
|
7
|
+
|
|
8
|
+
// we use a layout effect because we don't want there to be any perceptible delay between the
|
|
9
|
+
// editor mounting and this attribute being applied, because styles may depend on it:
|
|
10
|
+
useLayoutEffect(() => {
|
|
11
|
+
return react('stateAttribute', () => {
|
|
12
|
+
editor.getContainer().setAttribute('data-state', editor.getPath())
|
|
13
|
+
})
|
|
14
|
+
}, [editor])
|
|
15
|
+
}
|
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.
|
|
4
|
+
export const version = '3.16.0-canary.0e0fb8bde89d'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '2025-07-
|
|
8
|
-
patch: '2025-07-
|
|
7
|
+
minor: '2025-07-31T14:45:19.563Z',
|
|
8
|
+
patch: '2025-07-31T14:45:19.563Z',
|
|
9
9
|
}
|