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