@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.
Files changed (74) hide show
  1. package/dist-cjs/index.d.ts +27 -28
  2. package/dist-cjs/index.js +2 -4
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +0 -2
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -4
  7. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  8. package/dist-cjs/lib/editor/Editor.js +29 -0
  9. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  10. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +4 -2
  11. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
  12. package/dist-cjs/lib/hooks/useCanvasEvents.js +17 -17
  13. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  14. package/dist-cjs/lib/hooks/useDocumentEvents.js +4 -4
  15. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  16. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -1
  17. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  18. package/dist-cjs/lib/hooks/useHandleEvents.js +6 -6
  19. package/dist-cjs/lib/hooks/useHandleEvents.js.map +2 -2
  20. package/dist-cjs/lib/hooks/useSelectionEvents.js +8 -8
  21. package/dist-cjs/lib/hooks/useSelectionEvents.js.map +2 -2
  22. package/dist-cjs/lib/license/Watermark.js +97 -90
  23. package/dist-cjs/lib/license/Watermark.js.map +2 -2
  24. package/dist-cjs/lib/utils/dom.js +1 -12
  25. package/dist-cjs/lib/utils/dom.js.map +2 -2
  26. package/dist-cjs/lib/utils/getPointerInfo.js +2 -3
  27. package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
  28. package/dist-cjs/version.js +3 -3
  29. package/dist-cjs/version.js.map +1 -1
  30. package/dist-esm/index.d.mts +27 -28
  31. package/dist-esm/index.mjs +3 -7
  32. package/dist-esm/index.mjs.map +2 -2
  33. package/dist-esm/lib/TldrawEditor.mjs +0 -2
  34. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  35. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +5 -5
  36. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  37. package/dist-esm/lib/editor/Editor.mjs +29 -0
  38. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  39. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +4 -2
  40. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
  41. package/dist-esm/lib/hooks/useCanvasEvents.mjs +18 -24
  42. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  43. package/dist-esm/lib/hooks/useDocumentEvents.mjs +5 -10
  44. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  45. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +2 -2
  46. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  47. package/dist-esm/lib/hooks/useHandleEvents.mjs +7 -12
  48. package/dist-esm/lib/hooks/useHandleEvents.mjs.map +2 -2
  49. package/dist-esm/lib/hooks/useSelectionEvents.mjs +9 -15
  50. package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +2 -2
  51. package/dist-esm/lib/license/Watermark.mjs +98 -91
  52. package/dist-esm/lib/license/Watermark.mjs.map +2 -2
  53. package/dist-esm/lib/utils/dom.mjs +1 -12
  54. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  55. package/dist-esm/lib/utils/getPointerInfo.mjs +2 -3
  56. package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
  57. package/dist-esm/version.mjs +3 -3
  58. package/dist-esm/version.mjs.map +1 -1
  59. package/package.json +7 -7
  60. package/src/index.ts +0 -2
  61. package/src/lib/TldrawEditor.tsx +0 -2
  62. package/src/lib/components/default-components/DefaultCanvas.tsx +5 -5
  63. package/src/lib/editor/Editor.ts +33 -0
  64. package/src/lib/editor/managers/FocusManager/FocusManager.ts +6 -2
  65. package/src/lib/hooks/useCanvasEvents.ts +18 -24
  66. package/src/lib/hooks/useDocumentEvents.ts +5 -10
  67. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +2 -2
  68. package/src/lib/hooks/useHandleEvents.ts +7 -12
  69. package/src/lib/hooks/useSelectionEvents.ts +9 -15
  70. package/src/lib/license/Watermark.tsx +100 -92
  71. package/src/lib/utils/dom.test.ts +33 -24
  72. package/src/lib/utils/dom.ts +1 -31
  73. package/src/lib/utils/getPointerInfo.ts +3 -3
  74. 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 { markEventAsHandled, preventDefault } from '../utils/dom'
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 { markEventAsHandled, preventDefault } from '../utils/dom'
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 = 'https://tldraw.dev/?utm_source=dotcom&utm_medium=organic&utm_campaign=watermark'
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="Unlicensed - click to get a license"
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
- Unlicensed
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="made with tldraw"
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 = `/* ------------------- SEE LICENSE -------------------
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
- .${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;
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} > 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;
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-debug='true'] {
186
- bottom: max(46px, env(safe-area-inset-bottom));
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-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;
226
+ .${className}[data-licensed='false'] > button:focus-visible {
227
+ opacity: 1;
194
228
  }
229
+ }
195
230
 
196
- .${className}[data-mobile='true'] > button {
197
- width: 8px;
198
- height: 32px;
231
+ @keyframes ${className}_delayed_link {
232
+ 0% {
233
+ cursor: inherit;
234
+ opacity: .38;
235
+ pointer-events: none;
199
236
  }
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
- }
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 { markEventAsHandled, wasEventAlreadyHandled } from './dom'
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
  })