@tldraw/editor 3.16.0-canary.e4220f725a90 → 3.16.0-canary.e455ab838b8f
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 +27 -28
- package/dist-cjs/index.js +2 -4
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +0 -2
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -4
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +29 -0
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +4 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +17 -17
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +4 -4
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -1
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useHandleEvents.js +6 -6
- package/dist-cjs/lib/hooks/useHandleEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useSelectionEvents.js +8 -8
- package/dist-cjs/lib/hooks/useSelectionEvents.js.map +2 -2
- package/dist-cjs/lib/license/Watermark.js +97 -90
- package/dist-cjs/lib/license/Watermark.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js +1 -12
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/lib/utils/getPointerInfo.js +2 -3
- package/dist-cjs/lib/utils/getPointerInfo.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 +27 -28
- package/dist-esm/index.mjs +3 -7
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +0 -2
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +5 -5
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +29 -0
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +4 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +18 -24
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +5 -10
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +2 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useHandleEvents.mjs +7 -12
- package/dist-esm/lib/hooks/useHandleEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useSelectionEvents.mjs +9 -15
- package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +2 -2
- package/dist-esm/lib/license/Watermark.mjs +98 -91
- package/dist-esm/lib/license/Watermark.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs +1 -12
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/lib/utils/getPointerInfo.mjs +2 -3
- package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +0 -2
- package/src/lib/TldrawEditor.tsx +0 -2
- package/src/lib/components/default-components/DefaultCanvas.tsx +5 -5
- package/src/lib/editor/Editor.ts +33 -0
- package/src/lib/editor/managers/FocusManager/FocusManager.ts +6 -2
- package/src/lib/hooks/useCanvasEvents.ts +18 -24
- package/src/lib/hooks/useDocumentEvents.ts +5 -10
- package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +2 -2
- package/src/lib/hooks/useHandleEvents.ts +7 -12
- package/src/lib/hooks/useSelectionEvents.ts +9 -15
- package/src/lib/license/Watermark.tsx +100 -92
- package/src/lib/utils/dom.test.ts +33 -24
- package/src/lib/utils/dom.ts +1 -31
- package/src/lib/utils/getPointerInfo.ts +3 -3
- package/src/version.ts +3 -3
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { useValue } from '@tldraw/state-react'
|
|
2
2
|
import React, { useEffect, useMemo } from 'react'
|
|
3
3
|
import { RIGHT_MOUSE_BUTTON } from '../constants'
|
|
4
|
-
import {
|
|
5
|
-
markEventAsHandled,
|
|
6
|
-
preventDefault,
|
|
7
|
-
releasePointerCapture,
|
|
8
|
-
setPointerCapture,
|
|
9
|
-
wasEventAlreadyHandled,
|
|
10
|
-
} from '../utils/dom'
|
|
4
|
+
import { preventDefault, releasePointerCapture, setPointerCapture } from '../utils/dom'
|
|
11
5
|
import { getPointerInfo } from '../utils/getPointerInfo'
|
|
12
6
|
import { useEditor } from './useEditor'
|
|
13
7
|
|
|
@@ -18,14 +12,14 @@ export function useCanvasEvents() {
|
|
|
18
12
|
const events = useMemo(
|
|
19
13
|
function canvasEvents() {
|
|
20
14
|
function onPointerDown(e: React.PointerEvent) {
|
|
21
|
-
if (wasEventAlreadyHandled(e)) return
|
|
15
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
22
16
|
|
|
23
17
|
if (e.button === RIGHT_MOUSE_BUTTON) {
|
|
24
18
|
editor.dispatch({
|
|
25
19
|
type: 'pointer',
|
|
26
20
|
target: 'canvas',
|
|
27
21
|
name: 'right_click',
|
|
28
|
-
...getPointerInfo(e),
|
|
22
|
+
...getPointerInfo(editor, e),
|
|
29
23
|
})
|
|
30
24
|
return
|
|
31
25
|
}
|
|
@@ -38,12 +32,12 @@ export function useCanvasEvents() {
|
|
|
38
32
|
type: 'pointer',
|
|
39
33
|
target: 'canvas',
|
|
40
34
|
name: 'pointer_down',
|
|
41
|
-
...getPointerInfo(e),
|
|
35
|
+
...getPointerInfo(editor, e),
|
|
42
36
|
})
|
|
43
37
|
}
|
|
44
38
|
|
|
45
39
|
function onPointerUp(e: React.PointerEvent) {
|
|
46
|
-
if (wasEventAlreadyHandled(e)) return
|
|
40
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
47
41
|
if (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return
|
|
48
42
|
|
|
49
43
|
releasePointerCapture(e.currentTarget, e)
|
|
@@ -52,33 +46,33 @@ export function useCanvasEvents() {
|
|
|
52
46
|
type: 'pointer',
|
|
53
47
|
target: 'canvas',
|
|
54
48
|
name: 'pointer_up',
|
|
55
|
-
...getPointerInfo(e),
|
|
49
|
+
...getPointerInfo(editor, e),
|
|
56
50
|
})
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
function onPointerEnter(e: React.PointerEvent) {
|
|
60
|
-
if (wasEventAlreadyHandled(e)) return
|
|
54
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
61
55
|
if (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return
|
|
62
56
|
const canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'
|
|
63
57
|
editor.updateInstanceState({ isHoveringCanvas: canHover ? true : null })
|
|
64
58
|
}
|
|
65
59
|
|
|
66
60
|
function onPointerLeave(e: React.PointerEvent) {
|
|
67
|
-
if (wasEventAlreadyHandled(e)) return
|
|
61
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
68
62
|
if (editor.getInstanceState().isPenMode && e.pointerType !== 'pen') return
|
|
69
63
|
const canHover = e.pointerType === 'mouse' || e.pointerType === 'pen'
|
|
70
64
|
editor.updateInstanceState({ isHoveringCanvas: canHover ? false : null })
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
function onTouchStart(e: React.TouchEvent) {
|
|
74
|
-
if (wasEventAlreadyHandled(e)) return
|
|
75
|
-
markEventAsHandled(e)
|
|
68
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
69
|
+
editor.markEventAsHandled(e)
|
|
76
70
|
preventDefault(e)
|
|
77
71
|
}
|
|
78
72
|
|
|
79
73
|
function onTouchEnd(e: React.TouchEvent) {
|
|
80
|
-
if (wasEventAlreadyHandled(e)) return
|
|
81
|
-
markEventAsHandled(e)
|
|
74
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
75
|
+
editor.markEventAsHandled(e)
|
|
82
76
|
// check that e.target is an HTMLElement
|
|
83
77
|
if (!(e.target instanceof HTMLElement)) return
|
|
84
78
|
|
|
@@ -97,12 +91,12 @@ export function useCanvasEvents() {
|
|
|
97
91
|
}
|
|
98
92
|
|
|
99
93
|
function onDragOver(e: React.DragEvent<Element>) {
|
|
100
|
-
if (wasEventAlreadyHandled(e)) return
|
|
94
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
101
95
|
preventDefault(e)
|
|
102
96
|
}
|
|
103
97
|
|
|
104
98
|
async function onDrop(e: React.DragEvent<Element>) {
|
|
105
|
-
if (wasEventAlreadyHandled(e)) return
|
|
99
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
106
100
|
preventDefault(e)
|
|
107
101
|
e.stopPropagation()
|
|
108
102
|
|
|
@@ -129,7 +123,7 @@ export function useCanvasEvents() {
|
|
|
129
123
|
}
|
|
130
124
|
|
|
131
125
|
function onClick(e: React.MouseEvent) {
|
|
132
|
-
if (wasEventAlreadyHandled(e)) return
|
|
126
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
133
127
|
e.stopPropagation()
|
|
134
128
|
}
|
|
135
129
|
|
|
@@ -157,8 +151,8 @@ export function useCanvasEvents() {
|
|
|
157
151
|
let lastX: number, lastY: number
|
|
158
152
|
|
|
159
153
|
function onPointerMove(e: PointerEvent) {
|
|
160
|
-
if (wasEventAlreadyHandled(e)) return
|
|
161
|
-
markEventAsHandled(e)
|
|
154
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
155
|
+
editor.markEventAsHandled(e)
|
|
162
156
|
|
|
163
157
|
if (e.clientX === lastX && e.clientY === lastY) return
|
|
164
158
|
lastX = e.clientX
|
|
@@ -174,7 +168,7 @@ export function useCanvasEvents() {
|
|
|
174
168
|
type: 'pointer',
|
|
175
169
|
target: 'canvas',
|
|
176
170
|
name: 'pointer_move',
|
|
177
|
-
...getPointerInfo(singleEvent),
|
|
171
|
+
...getPointerInfo(editor, singleEvent),
|
|
178
172
|
})
|
|
179
173
|
}
|
|
180
174
|
}
|
|
@@ -2,12 +2,7 @@ import { useValue } from '@tldraw/state-react'
|
|
|
2
2
|
import { useEffect } from 'react'
|
|
3
3
|
import { Editor } from '../editor/Editor'
|
|
4
4
|
import { TLKeyboardEventInfo } from '../editor/types/event-types'
|
|
5
|
-
import {
|
|
6
|
-
activeElementShouldCaptureKeys,
|
|
7
|
-
markEventAsHandled,
|
|
8
|
-
preventDefault,
|
|
9
|
-
wasEventAlreadyHandled,
|
|
10
|
-
} from '../utils/dom'
|
|
5
|
+
import { activeElementShouldCaptureKeys, preventDefault } from '../utils/dom'
|
|
11
6
|
import { isAccelKey } from '../utils/keyboard'
|
|
12
7
|
import { useContainer } from './useContainer'
|
|
13
8
|
import { useEditor } from './useEditor'
|
|
@@ -108,8 +103,8 @@ export function useDocumentEvents() {
|
|
|
108
103
|
preventDefault(e)
|
|
109
104
|
}
|
|
110
105
|
|
|
111
|
-
if (wasEventAlreadyHandled(e)) return
|
|
112
|
-
markEventAsHandled(e)
|
|
106
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
107
|
+
editor.markEventAsHandled(e)
|
|
113
108
|
const hasSelectedShapes = !!editor.getSelectedShapeIds().length
|
|
114
109
|
|
|
115
110
|
switch (e.key) {
|
|
@@ -216,8 +211,8 @@ export function useDocumentEvents() {
|
|
|
216
211
|
}
|
|
217
212
|
|
|
218
213
|
const handleKeyUp = (e: KeyboardEvent) => {
|
|
219
|
-
if (wasEventAlreadyHandled(e)) return
|
|
220
|
-
markEventAsHandled(e)
|
|
214
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
215
|
+
editor.markEventAsHandled(e)
|
|
221
216
|
|
|
222
217
|
if (areShortcutsDisabled(editor)) {
|
|
223
218
|
return
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { preventDefault } from '../utils/dom'
|
|
3
3
|
import { useEditor } from './useEditor'
|
|
4
4
|
|
|
5
5
|
const IGNORED_TAGS = ['textarea', 'input']
|
|
@@ -19,7 +19,7 @@ export function useFixSafariDoubleTapZoomPencilEvents(ref: React.RefObject<HTMLE
|
|
|
19
19
|
|
|
20
20
|
const handleEvent = (e: PointerEvent | TouchEvent) => {
|
|
21
21
|
if (e instanceof PointerEvent && e.pointerType === 'pen') {
|
|
22
|
-
markEventAsHandled(e)
|
|
22
|
+
editor.markEventAsHandled(e)
|
|
23
23
|
const { target } = e
|
|
24
24
|
|
|
25
25
|
// Allow events to propagate if the app is editing a shape, or if the event is occurring in a text area or input
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { TLArrowShape, TLLineShape, TLShapeId } from '@tldraw/tlschema'
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import { Editor } from '../editor/Editor'
|
|
4
|
-
import {
|
|
5
|
-
loopToHtmlElement,
|
|
6
|
-
releasePointerCapture,
|
|
7
|
-
setPointerCapture,
|
|
8
|
-
wasEventAlreadyHandled,
|
|
9
|
-
} from '../utils/dom'
|
|
4
|
+
import { loopToHtmlElement, releasePointerCapture, setPointerCapture } from '../utils/dom'
|
|
10
5
|
import { getPointerInfo } from '../utils/getPointerInfo'
|
|
11
6
|
import { useEditor } from './useEditor'
|
|
12
7
|
|
|
@@ -21,7 +16,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
21
16
|
|
|
22
17
|
return React.useMemo(() => {
|
|
23
18
|
const onPointerDown = (e: React.PointerEvent) => {
|
|
24
|
-
if (wasEventAlreadyHandled(e)) return
|
|
19
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
25
20
|
|
|
26
21
|
// Must set pointer capture on an HTML element!
|
|
27
22
|
const target = loopToHtmlElement(e.currentTarget)
|
|
@@ -37,7 +32,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
37
32
|
handle,
|
|
38
33
|
shape,
|
|
39
34
|
name: 'pointer_down',
|
|
40
|
-
...getPointerInfo(e),
|
|
35
|
+
...getPointerInfo(editor, e),
|
|
41
36
|
})
|
|
42
37
|
}
|
|
43
38
|
|
|
@@ -45,7 +40,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
45
40
|
let lastX: number, lastY: number
|
|
46
41
|
|
|
47
42
|
const onPointerMove = (e: React.PointerEvent) => {
|
|
48
|
-
if (wasEventAlreadyHandled(e)) return
|
|
43
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
49
44
|
if (e.clientX === lastX && e.clientY === lastY) return
|
|
50
45
|
lastX = e.clientX
|
|
51
46
|
lastY = e.clientY
|
|
@@ -60,12 +55,12 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
60
55
|
handle,
|
|
61
56
|
shape,
|
|
62
57
|
name: 'pointer_move',
|
|
63
|
-
...getPointerInfo(e),
|
|
58
|
+
...getPointerInfo(editor, e),
|
|
64
59
|
})
|
|
65
60
|
}
|
|
66
61
|
|
|
67
62
|
const onPointerUp = (e: React.PointerEvent) => {
|
|
68
|
-
if (wasEventAlreadyHandled(e)) return
|
|
63
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
69
64
|
|
|
70
65
|
const target = loopToHtmlElement(e.currentTarget)
|
|
71
66
|
releasePointerCapture(target, e)
|
|
@@ -80,7 +75,7 @@ export function useHandleEvents(id: TLShapeId, handleId: string) {
|
|
|
80
75
|
handle,
|
|
81
76
|
shape,
|
|
82
77
|
name: 'pointer_up',
|
|
83
|
-
...getPointerInfo(e),
|
|
78
|
+
...getPointerInfo(editor, e),
|
|
84
79
|
})
|
|
85
80
|
}
|
|
86
81
|
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react'
|
|
2
2
|
import { RIGHT_MOUSE_BUTTON } from '../constants'
|
|
3
3
|
import { TLSelectionHandle } from '../editor/types/selection-types'
|
|
4
|
-
import {
|
|
5
|
-
loopToHtmlElement,
|
|
6
|
-
markEventAsHandled,
|
|
7
|
-
releasePointerCapture,
|
|
8
|
-
setPointerCapture,
|
|
9
|
-
wasEventAlreadyHandled,
|
|
10
|
-
} from '../utils/dom'
|
|
4
|
+
import { loopToHtmlElement, releasePointerCapture, setPointerCapture } from '../utils/dom'
|
|
11
5
|
import { getPointerInfo } from '../utils/getPointerInfo'
|
|
12
6
|
import { useEditor } from './useEditor'
|
|
13
7
|
|
|
@@ -18,7 +12,7 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
18
12
|
const events = useMemo(
|
|
19
13
|
function selectionEvents() {
|
|
20
14
|
const onPointerDown: React.PointerEventHandler = (e) => {
|
|
21
|
-
if (wasEventAlreadyHandled(e)) return
|
|
15
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
22
16
|
|
|
23
17
|
if (e.button === RIGHT_MOUSE_BUTTON) {
|
|
24
18
|
editor.dispatch({
|
|
@@ -26,7 +20,7 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
26
20
|
target: 'selection',
|
|
27
21
|
handle,
|
|
28
22
|
name: 'right_click',
|
|
29
|
-
...getPointerInfo(e),
|
|
23
|
+
...getPointerInfo(editor, e),
|
|
30
24
|
})
|
|
31
25
|
return
|
|
32
26
|
}
|
|
@@ -53,16 +47,16 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
53
47
|
type: 'pointer',
|
|
54
48
|
target: 'selection',
|
|
55
49
|
handle,
|
|
56
|
-
...getPointerInfo(e),
|
|
50
|
+
...getPointerInfo(editor, e),
|
|
57
51
|
})
|
|
58
|
-
markEventAsHandled(e)
|
|
52
|
+
editor.markEventAsHandled(e)
|
|
59
53
|
}
|
|
60
54
|
|
|
61
55
|
// Track the last screen point
|
|
62
56
|
let lastX: number, lastY: number
|
|
63
57
|
|
|
64
58
|
function onPointerMove(e: React.PointerEvent) {
|
|
65
|
-
if (wasEventAlreadyHandled(e)) return
|
|
59
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
66
60
|
if (e.button !== 0) return
|
|
67
61
|
if (e.clientX === lastX && e.clientY === lastY) return
|
|
68
62
|
lastX = e.clientX
|
|
@@ -73,12 +67,12 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
73
67
|
type: 'pointer',
|
|
74
68
|
target: 'selection',
|
|
75
69
|
handle,
|
|
76
|
-
...getPointerInfo(e),
|
|
70
|
+
...getPointerInfo(editor, e),
|
|
77
71
|
})
|
|
78
72
|
}
|
|
79
73
|
|
|
80
74
|
const onPointerUp: React.PointerEventHandler = (e) => {
|
|
81
|
-
if (wasEventAlreadyHandled(e)) return
|
|
75
|
+
if (editor.wasEventAlreadyHandled(e)) return
|
|
82
76
|
if (e.button !== 0) return
|
|
83
77
|
|
|
84
78
|
editor.dispatch({
|
|
@@ -86,7 +80,7 @@ export function useSelectionEvents(handle: TLSelectionHandle) {
|
|
|
86
80
|
type: 'pointer',
|
|
87
81
|
target: 'selection',
|
|
88
82
|
handle,
|
|
89
|
-
...getPointerInfo(e),
|
|
83
|
+
...getPointerInfo(editor, e),
|
|
90
84
|
})
|
|
91
85
|
}
|
|
92
86
|
|
|
@@ -3,7 +3,7 @@ import { memo, useRef } from 'react'
|
|
|
3
3
|
import { useCanvasEvents } from '../hooks/useCanvasEvents'
|
|
4
4
|
import { useEditor } from '../hooks/useEditor'
|
|
5
5
|
import { usePassThroughWheelEvents } from '../hooks/usePassThroughWheelEvents'
|
|
6
|
-
import {
|
|
6
|
+
import { preventDefault } from '../utils/dom'
|
|
7
7
|
import { runtime } from '../utils/runtime'
|
|
8
8
|
import { watermarkDesktopSvg, watermarkMobileSvg } from '../watermarks'
|
|
9
9
|
import { LicenseManager } from './LicenseManager'
|
|
@@ -43,11 +43,13 @@ const UnlicensedWatermark = memo(function UnlicensedWatermark({
|
|
|
43
43
|
isDebugMode: boolean
|
|
44
44
|
isMobile: boolean
|
|
45
45
|
}) {
|
|
46
|
+
const editor = useEditor()
|
|
46
47
|
const events = useCanvasEvents()
|
|
47
48
|
const ref = useRef<HTMLDivElement>(null)
|
|
48
49
|
usePassThroughWheelEvents(ref)
|
|
49
50
|
|
|
50
|
-
const url =
|
|
51
|
+
const url =
|
|
52
|
+
'https://tldraw.dev/pricing?utm_source=dotcom&utm_medium=organic&utm_campaign=watermark'
|
|
51
53
|
|
|
52
54
|
return (
|
|
53
55
|
<div
|
|
@@ -64,26 +66,13 @@ const UnlicensedWatermark = memo(function UnlicensedWatermark({
|
|
|
64
66
|
draggable={false}
|
|
65
67
|
role="button"
|
|
66
68
|
onPointerDown={(e) => {
|
|
67
|
-
markEventAsHandled(e)
|
|
69
|
+
editor.markEventAsHandled(e)
|
|
68
70
|
preventDefault(e)
|
|
69
71
|
}}
|
|
70
|
-
title="
|
|
72
|
+
title="The tldraw SDK requires a license key to work in production. You can get a free 100-day trial license at tldraw.dev/pricing."
|
|
71
73
|
onClick={() => runtime.openWindow(url, '_blank')}
|
|
72
|
-
style={{
|
|
73
|
-
position: 'absolute',
|
|
74
|
-
pointerEvents: 'all',
|
|
75
|
-
cursor: 'pointer',
|
|
76
|
-
color: 'var(--tl-color-text)',
|
|
77
|
-
opacity: 0.8,
|
|
78
|
-
border: 0,
|
|
79
|
-
padding: 0,
|
|
80
|
-
backgroundColor: 'transparent',
|
|
81
|
-
fontSize: '11px',
|
|
82
|
-
fontWeight: '600',
|
|
83
|
-
textAlign: 'center',
|
|
84
|
-
}}
|
|
85
74
|
>
|
|
86
|
-
|
|
75
|
+
Get a license for production
|
|
87
76
|
</button>
|
|
88
77
|
</div>
|
|
89
78
|
)
|
|
@@ -127,10 +116,10 @@ const WatermarkInner = memo(function WatermarkInner({
|
|
|
127
116
|
draggable={false}
|
|
128
117
|
role="button"
|
|
129
118
|
onPointerDown={(e) => {
|
|
130
|
-
markEventAsHandled(e)
|
|
119
|
+
editor.markEventAsHandled(e)
|
|
131
120
|
preventDefault(e)
|
|
132
121
|
}}
|
|
133
|
-
title="
|
|
122
|
+
title="Build infinite canvas applications with the tldraw SDK. Learn more at https://tldraw.dev."
|
|
134
123
|
onClick={() => runtime.openWindow(url, '_blank')}
|
|
135
124
|
style={{ mask: maskCss, WebkitMask: maskCss }}
|
|
136
125
|
/>
|
|
@@ -142,7 +131,8 @@ const LicenseStyles = memo(function LicenseStyles() {
|
|
|
142
131
|
const editor = useEditor()
|
|
143
132
|
const className = LicenseManager.className
|
|
144
133
|
|
|
145
|
-
const CSS =
|
|
134
|
+
const CSS = `
|
|
135
|
+
/* ------------------- SEE LICENSE -------------------
|
|
146
136
|
The tldraw watermark is part of tldraw's license. It is shown for unlicensed
|
|
147
137
|
or "licensed-with-watermark" users. By using this library, you agree to
|
|
148
138
|
preserve the watermark's behavior, keeping it visible, unobscured, and
|
|
@@ -151,87 +141,105 @@ available to user-interaction.
|
|
|
151
141
|
To remove the watermark, please purchase a license at tldraw.dev.
|
|
152
142
|
*/
|
|
153
143
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
144
|
+
.${className} {
|
|
145
|
+
position: absolute;
|
|
146
|
+
bottom: max(var(--tl-space-2), env(safe-area-inset-bottom));
|
|
147
|
+
right: max(var(--tl-space-2), env(safe-area-inset-right));
|
|
148
|
+
width: 96px;
|
|
149
|
+
height: 32px;
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
justify-content: center;
|
|
153
|
+
z-index: var(--tl-layer-watermark) !important;
|
|
154
|
+
background-color: color-mix(in srgb, var(--tl-color-background) 62%, transparent);
|
|
155
|
+
opacity: 1;
|
|
156
|
+
border-radius: 5px;
|
|
157
|
+
pointer-events: all;
|
|
158
|
+
padding: 2px;
|
|
159
|
+
box-sizing: content-box;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.${className} > button {
|
|
163
|
+
position: absolute;
|
|
164
|
+
width: 96px;
|
|
165
|
+
height: 32px;
|
|
166
|
+
pointer-events: all;
|
|
167
|
+
cursor: inherit;
|
|
168
|
+
color: var(--tl-color-text);
|
|
169
|
+
opacity: .38;
|
|
170
|
+
border: 0;
|
|
171
|
+
padding: 0;
|
|
172
|
+
background-color: currentColor;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.${className}[data-debug='true'] {
|
|
176
|
+
bottom: max(46px, env(safe-area-inset-bottom));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.${className}[data-mobile='true'] {
|
|
180
|
+
border-radius: 4px 0px 0px 4px;
|
|
181
|
+
right: max(-2px, calc(env(safe-area-inset-right) - 2px));
|
|
182
|
+
width: 8px;
|
|
183
|
+
height: 48px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.${className}[data-mobile='true'] > button {
|
|
187
|
+
width: 8px;
|
|
188
|
+
height: 32px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.${className}[data-unlicensed='true'] > button {
|
|
192
|
+
font-size: 100px;
|
|
193
|
+
position: absolute;
|
|
194
|
+
pointer-events: all;
|
|
195
|
+
cursor: pointer;
|
|
196
|
+
color: var(--tl-color-text);
|
|
197
|
+
opacity: 0.8;
|
|
198
|
+
border: 0;
|
|
199
|
+
padding: 0;
|
|
200
|
+
background-color: transparent;
|
|
201
|
+
font-size: 11px;
|
|
202
|
+
font-weight: 600;
|
|
203
|
+
text-align: center;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.${className}[data-mobile='true'][data-unlicensed='true'] > button {
|
|
207
|
+
display: none;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@media (hover: hover) {
|
|
211
|
+
.${className}[data-licensed='false'] > button {
|
|
212
|
+
pointer-events: none;
|
|
170
213
|
}
|
|
171
214
|
|
|
172
|
-
.${className}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
pointer-events: all;
|
|
177
|
-
cursor: inherit;
|
|
178
|
-
color: var(--tl-color-text);
|
|
179
|
-
opacity: .38;
|
|
180
|
-
border: 0;
|
|
181
|
-
padding: 0;
|
|
182
|
-
background-color: currentColor;
|
|
215
|
+
.${className}[data-licensed='false']:hover {
|
|
216
|
+
background-color: var(--tl-color-background);
|
|
217
|
+
transition: background-color 0.2s ease-in-out;
|
|
218
|
+
transition-delay: 0.32s;
|
|
183
219
|
}
|
|
184
220
|
|
|
185
|
-
.${className}[data-
|
|
186
|
-
|
|
221
|
+
.${className}[data-licensed='false']:hover > button {
|
|
222
|
+
animation: ${className}_delayed_link 0.2s forwards ease-in-out;
|
|
223
|
+
animation-delay: 0.32s;
|
|
187
224
|
}
|
|
188
225
|
|
|
189
|
-
.${className}[data-
|
|
190
|
-
|
|
191
|
-
right: max(-2px, calc(env(safe-area-inset-right) - 2px));
|
|
192
|
-
width: 8px;
|
|
193
|
-
height: 48px;
|
|
226
|
+
.${className}[data-licensed='false'] > button:focus-visible {
|
|
227
|
+
opacity: 1;
|
|
194
228
|
}
|
|
229
|
+
}
|
|
195
230
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
231
|
+
@keyframes ${className}_delayed_link {
|
|
232
|
+
0% {
|
|
233
|
+
cursor: inherit;
|
|
234
|
+
opacity: .38;
|
|
235
|
+
pointer-events: none;
|
|
199
236
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
.${className}:hover {
|
|
207
|
-
background-color: var(--tl-color-background);
|
|
208
|
-
transition: background-color 0.2s ease-in-out;
|
|
209
|
-
transition-delay: 0.32s;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.${className}:hover > button {
|
|
213
|
-
animation: ${className}_delayed_link 0.2s forwards ease-in-out;
|
|
214
|
-
animation-delay: 0.32s;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.${className} > button:focus-visible {
|
|
218
|
-
opacity: 1;
|
|
219
|
-
}
|
|
237
|
+
100% {
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
opacity: 1;
|
|
240
|
+
pointer-events: all;
|
|
220
241
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
@keyframes ${className}_delayed_link {
|
|
224
|
-
0% {
|
|
225
|
-
cursor: inherit;
|
|
226
|
-
opacity: .38;
|
|
227
|
-
pointer-events: none;
|
|
228
|
-
}
|
|
229
|
-
100% {
|
|
230
|
-
cursor: pointer;
|
|
231
|
-
opacity: 1;
|
|
232
|
-
pointer-events: all;
|
|
233
|
-
}
|
|
234
|
-
}`
|
|
242
|
+
}`
|
|
235
243
|
|
|
236
244
|
return <style nonce={editor.options.nonce}>{CSS}</style>
|
|
237
245
|
})
|
|
@@ -1,18 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TestEditor } from '../test/TestEditor'
|
|
2
2
|
|
|
3
3
|
describe('Event handling utilities', () => {
|
|
4
|
+
let editor: TestEditor
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
editor = new TestEditor()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
editor.dispose()
|
|
12
|
+
})
|
|
4
13
|
describe('markEventAsHandled and wasEventAlreadyHandled', () => {
|
|
5
14
|
it('should track events as handled', () => {
|
|
6
15
|
const mockEvent = new PointerEvent('pointerdown', { pointerId: 1 })
|
|
7
16
|
|
|
8
17
|
// Initially, event should not be marked as handled
|
|
9
|
-
expect(wasEventAlreadyHandled(mockEvent)).toBe(false)
|
|
18
|
+
expect(editor.wasEventAlreadyHandled(mockEvent)).toBe(false)
|
|
10
19
|
|
|
11
20
|
// Mark the event as handled
|
|
12
|
-
markEventAsHandled(mockEvent)
|
|
21
|
+
editor.markEventAsHandled(mockEvent)
|
|
13
22
|
|
|
14
23
|
// Now it should be marked as handled
|
|
15
|
-
expect(wasEventAlreadyHandled(mockEvent)).toBe(true)
|
|
24
|
+
expect(editor.wasEventAlreadyHandled(mockEvent)).toBe(true)
|
|
16
25
|
})
|
|
17
26
|
|
|
18
27
|
it('should work with React synthetic events', () => {
|
|
@@ -20,15 +29,15 @@ describe('Event handling utilities', () => {
|
|
|
20
29
|
const syntheticEvent = { nativeEvent }
|
|
21
30
|
|
|
22
31
|
// Initially not handled
|
|
23
|
-
expect(wasEventAlreadyHandled(syntheticEvent)).toBe(false)
|
|
24
|
-
expect(wasEventAlreadyHandled(nativeEvent)).toBe(false)
|
|
32
|
+
expect(editor.wasEventAlreadyHandled(syntheticEvent)).toBe(false)
|
|
33
|
+
expect(editor.wasEventAlreadyHandled(nativeEvent)).toBe(false)
|
|
25
34
|
|
|
26
35
|
// Mark synthetic event as handled
|
|
27
|
-
markEventAsHandled(syntheticEvent)
|
|
36
|
+
editor.markEventAsHandled(syntheticEvent)
|
|
28
37
|
|
|
29
38
|
// Both synthetic and native should be marked as handled
|
|
30
|
-
expect(wasEventAlreadyHandled(syntheticEvent)).toBe(true)
|
|
31
|
-
expect(wasEventAlreadyHandled(nativeEvent)).toBe(true)
|
|
39
|
+
expect(editor.wasEventAlreadyHandled(syntheticEvent)).toBe(true)
|
|
40
|
+
expect(editor.wasEventAlreadyHandled(nativeEvent)).toBe(true)
|
|
32
41
|
})
|
|
33
42
|
|
|
34
43
|
it('should handle multiple different events independently', () => {
|
|
@@ -37,18 +46,18 @@ describe('Event handling utilities', () => {
|
|
|
37
46
|
const event3 = new MouseEvent('click')
|
|
38
47
|
|
|
39
48
|
// Mark only event1 as handled
|
|
40
|
-
markEventAsHandled(event1)
|
|
49
|
+
editor.markEventAsHandled(event1)
|
|
41
50
|
|
|
42
|
-
expect(wasEventAlreadyHandled(event1)).toBe(true)
|
|
43
|
-
expect(wasEventAlreadyHandled(event2)).toBe(false)
|
|
44
|
-
expect(wasEventAlreadyHandled(event3)).toBe(false)
|
|
51
|
+
expect(editor.wasEventAlreadyHandled(event1)).toBe(true)
|
|
52
|
+
expect(editor.wasEventAlreadyHandled(event2)).toBe(false)
|
|
53
|
+
expect(editor.wasEventAlreadyHandled(event3)).toBe(false)
|
|
45
54
|
|
|
46
55
|
// Mark event2 as handled
|
|
47
|
-
markEventAsHandled(event2)
|
|
56
|
+
editor.markEventAsHandled(event2)
|
|
48
57
|
|
|
49
|
-
expect(wasEventAlreadyHandled(event1)).toBe(true)
|
|
50
|
-
expect(wasEventAlreadyHandled(event2)).toBe(true)
|
|
51
|
-
expect(wasEventAlreadyHandled(event3)).toBe(false)
|
|
58
|
+
expect(editor.wasEventAlreadyHandled(event1)).toBe(true)
|
|
59
|
+
expect(editor.wasEventAlreadyHandled(event2)).toBe(true)
|
|
60
|
+
expect(editor.wasEventAlreadyHandled(event3)).toBe(false)
|
|
52
61
|
})
|
|
53
62
|
|
|
54
63
|
it('should not interfere with event properties', () => {
|
|
@@ -59,7 +68,7 @@ describe('Event handling utilities', () => {
|
|
|
59
68
|
})
|
|
60
69
|
|
|
61
70
|
// Mark as handled
|
|
62
|
-
markEventAsHandled(event)
|
|
71
|
+
editor.markEventAsHandled(event)
|
|
63
72
|
|
|
64
73
|
// Event properties should remain unchanged
|
|
65
74
|
expect(event.pointerId).toBe(1)
|
|
@@ -78,17 +87,17 @@ describe('Event handling utilities', () => {
|
|
|
78
87
|
],
|
|
79
88
|
})
|
|
80
89
|
|
|
81
|
-
expect(wasEventAlreadyHandled(touchEvent)).toBe(false)
|
|
82
|
-
markEventAsHandled(touchEvent)
|
|
83
|
-
expect(wasEventAlreadyHandled(touchEvent)).toBe(true)
|
|
90
|
+
expect(editor.wasEventAlreadyHandled(touchEvent)).toBe(false)
|
|
91
|
+
editor.markEventAsHandled(touchEvent)
|
|
92
|
+
expect(editor.wasEventAlreadyHandled(touchEvent)).toBe(true)
|
|
84
93
|
})
|
|
85
94
|
|
|
86
95
|
it('should work with keyboard events', () => {
|
|
87
96
|
const keyEvent = new KeyboardEvent('keydown', { key: 'Enter' })
|
|
88
97
|
|
|
89
|
-
expect(wasEventAlreadyHandled(keyEvent)).toBe(false)
|
|
90
|
-
markEventAsHandled(keyEvent)
|
|
91
|
-
expect(wasEventAlreadyHandled(keyEvent)).toBe(true)
|
|
98
|
+
expect(editor.wasEventAlreadyHandled(keyEvent)).toBe(false)
|
|
99
|
+
editor.markEventAsHandled(keyEvent)
|
|
100
|
+
expect(editor.wasEventAlreadyHandled(keyEvent)).toBe(true)
|
|
92
101
|
})
|
|
93
102
|
})
|
|
94
103
|
})
|