@tldraw/editor 3.16.0-canary.cf24aedcd577 → 3.16.0-canary.d98fc0b9bd6a
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 +4 -0
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +1 -1
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultScribble.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +9 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
- package/dist-cjs/lib/config/TLUserPreferences.js +1 -1
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +10 -1
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +1 -1
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/license/Watermark.js +6 -6
- package/dist-cjs/lib/license/Watermark.js.map +1 -1
- package/dist-cjs/lib/options.js +6 -0
- package/dist-cjs/lib/options.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 +4 -0
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +1 -1
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +9 -1
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs +1 -1
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +10 -1
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +1 -1
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/license/Watermark.mjs +6 -6
- package/dist-esm/lib/license/Watermark.mjs.map +1 -1
- package/dist-esm/lib/options.mjs +6 -0
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +293 -290
- package/package.json +14 -37
- package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -1
- package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
- package/src/lib/components/default-components/DefaultScribble.tsx +1 -1
- package/src/lib/components/default-components/DefaultShapeIndicator.tsx +5 -1
- package/src/lib/config/TLUserPreferences.ts +1 -1
- package/src/lib/editor/Editor.test.ts +12 -11
- package/src/lib/editor/Editor.ts +11 -1
- package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +15 -14
- package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +16 -15
- package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +49 -48
- package/src/lib/editor/managers/FontManager/FontManager.test.ts +24 -23
- package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +7 -6
- package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +12 -11
- package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +57 -50
- package/src/lib/editor/managers/TextManager/TextManager.test.ts +51 -26
- package/src/lib/editor/managers/TickManager/TickManager.test.ts +14 -13
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +21 -26
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +1 -1
- package/src/lib/license/LicenseManager.test.ts +3 -1
- package/src/lib/license/Watermark.test.tsx +2 -1
- package/src/lib/license/Watermark.tsx +6 -6
- package/src/lib/options.ts +6 -0
- package/src/lib/utils/sync/LocalIndexedDb.test.ts +2 -1
- package/src/lib/utils/sync/TLLocalSyncClient.test.ts +15 -15
- package/src/version.ts +3 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/editor",
|
|
3
3
|
"description": "tldraw infinite canvas SDK (editor).",
|
|
4
|
-
"version": "3.16.0-canary.
|
|
4
|
+
"version": "3.16.0-canary.d98fc0b9bd6a",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -34,27 +34,28 @@
|
|
|
34
34
|
"src"
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
|
-
"test-ci": "
|
|
38
|
-
"test": "yarn run -T
|
|
37
|
+
"test-ci": "yarn run -T vitest run --passWithNoTests",
|
|
38
|
+
"test": "yarn run -T vitest --passWithNoTests",
|
|
39
39
|
"benchmark": "yarn run -T tsx ./internal/scripts/benchmark.ts",
|
|
40
|
-
"test-coverage": "
|
|
40
|
+
"test-coverage": "yarn run -T vitest run --coverage --passWithNoTests",
|
|
41
41
|
"build": "yarn run -T tsx ../../internal/scripts/build-package.ts",
|
|
42
42
|
"build-api": "yarn run -T tsx ../../internal/scripts/build-api.ts",
|
|
43
43
|
"prepack": "yarn run -T tsx ../../internal/scripts/prepack.ts",
|
|
44
44
|
"postpack": "../../internal/scripts/postpack.sh",
|
|
45
45
|
"pack-tarball": "yarn pack",
|
|
46
|
-
"lint": "yarn run -T tsx ../../internal/scripts/lint.ts"
|
|
46
|
+
"lint": "yarn run -T tsx ../../internal/scripts/lint.ts",
|
|
47
|
+
"context": "yarn run -T tsx ../../internal/scripts/context.ts"
|
|
47
48
|
},
|
|
48
49
|
"dependencies": {
|
|
49
50
|
"@tiptap/core": "^2.9.1",
|
|
50
51
|
"@tiptap/pm": "^2.9.1",
|
|
51
52
|
"@tiptap/react": "^2.9.1",
|
|
52
|
-
"@tldraw/state": "3.16.0-canary.
|
|
53
|
-
"@tldraw/state-react": "3.16.0-canary.
|
|
54
|
-
"@tldraw/store": "3.16.0-canary.
|
|
55
|
-
"@tldraw/tlschema": "3.16.0-canary.
|
|
56
|
-
"@tldraw/utils": "3.16.0-canary.
|
|
57
|
-
"@tldraw/validate": "3.16.0-canary.
|
|
53
|
+
"@tldraw/state": "3.16.0-canary.d98fc0b9bd6a",
|
|
54
|
+
"@tldraw/state-react": "3.16.0-canary.d98fc0b9bd6a",
|
|
55
|
+
"@tldraw/store": "3.16.0-canary.d98fc0b9bd6a",
|
|
56
|
+
"@tldraw/tlschema": "3.16.0-canary.d98fc0b9bd6a",
|
|
57
|
+
"@tldraw/utils": "3.16.0-canary.d98fc0b9bd6a",
|
|
58
|
+
"@tldraw/validate": "3.16.0-canary.d98fc0b9bd6a",
|
|
58
59
|
"@types/core-js": "^2.5.8",
|
|
59
60
|
"@use-gesture/react": "^10.3.1",
|
|
60
61
|
"classnames": "^2.5.1",
|
|
@@ -69,41 +70,17 @@
|
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
72
|
"@peculiar/webcrypto": "^1.5.0",
|
|
72
|
-
"@testing-library/jest-dom": "^5.17.0",
|
|
73
73
|
"@testing-library/react": "^15.0.7",
|
|
74
74
|
"@types/benchmark": "^2.1.5",
|
|
75
75
|
"@types/react": "^18.3.18",
|
|
76
76
|
"@types/wicg-file-system-access": "^2020.9.8",
|
|
77
77
|
"benchmark": "^2.1.4",
|
|
78
78
|
"fake-indexeddb": "^4.0.2",
|
|
79
|
-
"jest-canvas-mock": "^2.5.2",
|
|
80
|
-
"jest-environment-jsdom": "^29.7.0",
|
|
81
79
|
"lazyrepo": "0.0.0-alpha.27",
|
|
82
80
|
"react": "^18.3.1",
|
|
83
81
|
"react-dom": "^18.3.1",
|
|
84
|
-
"resize-observer-polyfill": "^1.5.1"
|
|
85
|
-
|
|
86
|
-
"jest": {
|
|
87
|
-
"preset": "../../internal/config/jest/node/jest-preset.js",
|
|
88
|
-
"testEnvironment": "../../../packages/utils/patchedJestJsDom.js",
|
|
89
|
-
"fakeTimers": {
|
|
90
|
-
"enableGlobally": true
|
|
91
|
-
},
|
|
92
|
-
"testPathIgnorePatterns": [
|
|
93
|
-
"^.+\\.*.css$"
|
|
94
|
-
],
|
|
95
|
-
"moduleNameMapper": {
|
|
96
|
-
"^~(.*)": "<rootDir>/src/$1",
|
|
97
|
-
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
|
|
98
|
-
},
|
|
99
|
-
"setupFiles": [
|
|
100
|
-
"raf/polyfill",
|
|
101
|
-
"jest-canvas-mock",
|
|
102
|
-
"<rootDir>/setupTests.js"
|
|
103
|
-
],
|
|
104
|
-
"setupFilesAfterEnv": [
|
|
105
|
-
"../../internal/config/setupJest.ts"
|
|
106
|
-
]
|
|
82
|
+
"resize-observer-polyfill": "^1.5.1",
|
|
83
|
+
"vitest": "^3.2.4"
|
|
107
84
|
},
|
|
108
85
|
"module": "dist-esm/index.mjs",
|
|
109
86
|
"source": "src/index.ts",
|
|
@@ -44,7 +44,7 @@ export function DefaultCollaboratorHint({
|
|
|
44
44
|
href={`#${cursorHintId}`}
|
|
45
45
|
color={color}
|
|
46
46
|
strokeWidth={3}
|
|
47
|
-
stroke="var(--color-background)"
|
|
47
|
+
stroke="var(--tl-color-background)"
|
|
48
48
|
/>
|
|
49
49
|
<use href={`#${cursorHintId}`} color={color} opacity={opacity} />
|
|
50
50
|
</svg>
|
|
@@ -75,7 +75,7 @@ export const DefaultErrorFallback: TLErrorFallbackComponent = ({ error, editor }
|
|
|
75
75
|
|
|
76
76
|
// if we can't find a theme class from the app or from a parent, we have
|
|
77
77
|
// to fall back on using a media query:
|
|
78
|
-
if (typeof window !== 'undefined' &&
|
|
78
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
79
79
|
setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
80
80
|
}
|
|
81
81
|
}, [isDarkModeFromApp])
|
|
@@ -21,7 +21,7 @@ export function DefaultScribble({ scribble, zoom, color, opacity, className }: T
|
|
|
21
21
|
<path
|
|
22
22
|
className="tl-scribble"
|
|
23
23
|
d={getSvgPathFromPoints(scribble.points, false)}
|
|
24
|
-
stroke={color ?? `var(--color-${scribble.color})`}
|
|
24
|
+
stroke={color ?? `var(--tl-color-${scribble.color})`}
|
|
25
25
|
fill="none"
|
|
26
26
|
strokeWidth={8 / zoom}
|
|
27
27
|
opacity={opacity ?? scribble.opacity}
|
|
@@ -87,7 +87,11 @@ export const DefaultShapeIndicator = memo(function DefaultShapeIndicator({
|
|
|
87
87
|
|
|
88
88
|
return (
|
|
89
89
|
<svg ref={rIndicator} className={classNames('tl-overlays__item', className)} aria-hidden="true">
|
|
90
|
-
<g
|
|
90
|
+
<g
|
|
91
|
+
className="tl-shape-indicator"
|
|
92
|
+
stroke={color ?? 'var(--tl-color-selected)'}
|
|
93
|
+
opacity={opacity}
|
|
94
|
+
>
|
|
91
95
|
<InnerIndicator editor={editor} id={shapeId} />
|
|
92
96
|
</g>
|
|
93
97
|
</svg>
|
|
@@ -135,7 +135,7 @@ function getRandomColor() {
|
|
|
135
135
|
|
|
136
136
|
/** @internal */
|
|
137
137
|
export function userPrefersReducedMotion() {
|
|
138
|
-
if (typeof window !== 'undefined' &&
|
|
138
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
139
139
|
return window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ?? false
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { vi } from 'vitest'
|
|
1
2
|
import {
|
|
2
3
|
Box,
|
|
3
4
|
Geometry2d,
|
|
@@ -59,8 +60,8 @@ beforeEach(() => {
|
|
|
59
60
|
getContainer: () => document.body,
|
|
60
61
|
})
|
|
61
62
|
editor.setCameraOptions({ isLocked: true })
|
|
62
|
-
editor.setCamera =
|
|
63
|
-
editor.user.getAnimationSpeed =
|
|
63
|
+
editor.setCamera = vi.fn()
|
|
64
|
+
editor.user.getAnimationSpeed = vi.fn()
|
|
64
65
|
})
|
|
65
66
|
|
|
66
67
|
describe('centerOnPoint', () => {
|
|
@@ -94,13 +95,13 @@ describe('updateShape', () => {
|
|
|
94
95
|
|
|
95
96
|
describe('zoomToFit', () => {
|
|
96
97
|
it('no-op when isLocked is set', () => {
|
|
97
|
-
editor.getCurrentPageShapeIds =
|
|
98
|
+
editor.getCurrentPageShapeIds = vi.fn(() => new Set([createShapeId('box1')]))
|
|
98
99
|
editor.zoomToFit()
|
|
99
100
|
expect(editor.setCamera).not.toHaveBeenCalled()
|
|
100
101
|
})
|
|
101
102
|
|
|
102
103
|
it('sets camera when isLocked is set and force flag is set', () => {
|
|
103
|
-
editor.getCurrentPageShapeIds =
|
|
104
|
+
editor.getCurrentPageShapeIds = vi.fn(() => new Set([createShapeId('box1')]))
|
|
104
105
|
editor.zoomToFit({ force: true })
|
|
105
106
|
expect(editor.setCamera).toHaveBeenCalled()
|
|
106
107
|
})
|
|
@@ -144,13 +145,13 @@ describe('zoomOut', () => {
|
|
|
144
145
|
|
|
145
146
|
describe('zoomToSelection', () => {
|
|
146
147
|
it('no-op when isLocked is set', () => {
|
|
147
|
-
editor.getSelectionPageBounds =
|
|
148
|
+
editor.getSelectionPageBounds = vi.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
|
|
148
149
|
editor.zoomToSelection()
|
|
149
150
|
expect(editor.setCamera).not.toHaveBeenCalled()
|
|
150
151
|
})
|
|
151
152
|
|
|
152
153
|
it('sets camera when isLocked is set and force flag is set', () => {
|
|
153
|
-
editor.getSelectionPageBounds =
|
|
154
|
+
editor.getSelectionPageBounds = vi.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
|
|
154
155
|
editor.zoomToSelection({ force: true })
|
|
155
156
|
expect(editor.setCamera).toHaveBeenCalled()
|
|
156
157
|
})
|
|
@@ -286,7 +287,7 @@ describe('getShapesAtPoint', () => {
|
|
|
286
287
|
|
|
287
288
|
it('filters out hidden shapes', () => {
|
|
288
289
|
// Create a spy to mock isShapeHidden
|
|
289
|
-
const isShapeHiddenSpy =
|
|
290
|
+
const isShapeHiddenSpy = vi.spyOn(editor, 'isShapeHidden')
|
|
290
291
|
isShapeHiddenSpy.mockImplementation((shape) => {
|
|
291
292
|
return typeof shape === 'string' ? shape === ids.shape3 : shape.id === ids.shape3
|
|
292
293
|
})
|
|
@@ -352,7 +353,7 @@ describe('getShapesAtPoint', () => {
|
|
|
352
353
|
|
|
353
354
|
it('returns empty array when all shapes are hidden', () => {
|
|
354
355
|
// Mock all shapes as hidden
|
|
355
|
-
const isShapeHiddenSpy =
|
|
356
|
+
const isShapeHiddenSpy = vi.spyOn(editor, 'isShapeHidden')
|
|
356
357
|
isShapeHiddenSpy.mockReturnValue(true)
|
|
357
358
|
|
|
358
359
|
const shapes = editor.getShapesAtPoint({ x: 50, y: 50 })
|
|
@@ -692,7 +693,7 @@ describe('selectAll', () => {
|
|
|
692
693
|
const initialSelectedIds = editor.getSelectedShapeIds()
|
|
693
694
|
|
|
694
695
|
// Spy on setSelectedShapes to verify it's not called
|
|
695
|
-
const setSelectedShapesSpy =
|
|
696
|
+
const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
|
|
696
697
|
|
|
697
698
|
// Call selectAll
|
|
698
699
|
editor.selectAll()
|
|
@@ -713,7 +714,7 @@ describe('selectAll', () => {
|
|
|
713
714
|
const initialSelectedIds = editor.getSelectedShapeIds()
|
|
714
715
|
|
|
715
716
|
// Spy on setSelectedShapes to verify it's not called
|
|
716
|
-
const setSelectedShapesSpy =
|
|
717
|
+
const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
|
|
717
718
|
|
|
718
719
|
// Call selectAll
|
|
719
720
|
editor.selectAll()
|
|
@@ -818,7 +819,7 @@ describe('selectAll', () => {
|
|
|
818
819
|
const initialSelectedIds = Array.from(editor.getSelectedShapeIds())
|
|
819
820
|
|
|
820
821
|
// Spy on setSelectedShapes to verify it's not called
|
|
821
|
-
const setSelectedShapesSpy =
|
|
822
|
+
const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
|
|
822
823
|
|
|
823
824
|
// Call selectAll
|
|
824
825
|
editor.selectAll()
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -6333,7 +6333,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6333
6333
|
|
|
6334
6334
|
this.createShapes(shapesToCreate)
|
|
6335
6335
|
this.createBindings(bindingsToCreate)
|
|
6336
|
-
|
|
6336
|
+
|
|
6337
|
+
this.setSelectedShapes(
|
|
6338
|
+
compact(
|
|
6339
|
+
ids.map((oldId) => {
|
|
6340
|
+
const newId = shapeIds.get(oldId)
|
|
6341
|
+
if (!newId) return null
|
|
6342
|
+
if (!this.getShape(newId)) return null
|
|
6343
|
+
return newId
|
|
6344
|
+
})
|
|
6345
|
+
)
|
|
6346
|
+
)
|
|
6337
6347
|
|
|
6338
6348
|
if (offset !== undefined) {
|
|
6339
6349
|
// If we've offset the duplicated shapes, check to see whether their new bounds is entirely
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { Mocked, vi } from 'vitest'
|
|
1
2
|
import { Editor } from '../../Editor'
|
|
2
3
|
import { TLClickEventInfo, TLPointerEventInfo } from '../../types/event-types'
|
|
3
4
|
import { ClickManager } from './ClickManager'
|
|
4
5
|
|
|
5
6
|
// Mock the Editor class
|
|
6
|
-
|
|
7
|
+
vi.mock('../../Editor')
|
|
7
8
|
|
|
8
9
|
describe('ClickManager', () => {
|
|
9
|
-
let editor:
|
|
10
|
+
let editor: Mocked<Editor>
|
|
10
11
|
let clickManager: ClickManager
|
|
11
12
|
let mockTimers: any
|
|
12
13
|
|
|
@@ -29,14 +30,14 @@ describe('ClickManager', () => {
|
|
|
29
30
|
})
|
|
30
31
|
|
|
31
32
|
beforeEach(() => {
|
|
32
|
-
|
|
33
|
+
vi.useFakeTimers()
|
|
33
34
|
mockTimers = {
|
|
34
|
-
setTimeout:
|
|
35
|
+
setTimeout: vi.fn((fn, delay) => setTimeout(fn, delay)),
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
editor = {
|
|
38
39
|
timers: mockTimers,
|
|
39
|
-
dispatch:
|
|
40
|
+
dispatch: vi.fn(),
|
|
40
41
|
options: {
|
|
41
42
|
doubleClickDurationMs: 300,
|
|
42
43
|
multiClickDurationMs: 300,
|
|
@@ -46,7 +47,7 @@ describe('ClickManager', () => {
|
|
|
46
47
|
inputs: {
|
|
47
48
|
currentScreenPoint: { x: 0, y: 0 },
|
|
48
49
|
},
|
|
49
|
-
getInstanceState:
|
|
50
|
+
getInstanceState: vi.fn(() => ({
|
|
50
51
|
isCoarsePointer: false,
|
|
51
52
|
})),
|
|
52
53
|
} as any
|
|
@@ -55,8 +56,8 @@ describe('ClickManager', () => {
|
|
|
55
56
|
})
|
|
56
57
|
|
|
57
58
|
afterEach(() => {
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
vi.useRealTimers()
|
|
60
|
+
vi.clearAllMocks()
|
|
60
61
|
})
|
|
61
62
|
|
|
62
63
|
describe('constructor and initial state', () => {
|
|
@@ -100,7 +101,7 @@ describe('ClickManager', () => {
|
|
|
100
101
|
clickManager.handlePointerEvent(pointerEvent)
|
|
101
102
|
expect(clickManager.clickState).toBe('pendingDouble')
|
|
102
103
|
|
|
103
|
-
|
|
104
|
+
vi.advanceTimersByTime(350)
|
|
104
105
|
|
|
105
106
|
expect(clickManager.clickState).toBe('idle')
|
|
106
107
|
})
|
|
@@ -141,7 +142,7 @@ describe('ClickManager', () => {
|
|
|
141
142
|
clickManager.handlePointerEvent(firstDown)
|
|
142
143
|
clickManager.handlePointerEvent(secondDown)
|
|
143
144
|
|
|
144
|
-
|
|
145
|
+
vi.advanceTimersByTime(350)
|
|
145
146
|
|
|
146
147
|
expect(editor.dispatch).toHaveBeenCalledWith(
|
|
147
148
|
expect.objectContaining({
|
|
@@ -235,7 +236,7 @@ describe('ClickManager', () => {
|
|
|
235
236
|
clickManager.handlePointerEvent(pointerDown) // second
|
|
236
237
|
clickManager.handlePointerEvent(pointerDown) // third
|
|
237
238
|
|
|
238
|
-
|
|
239
|
+
vi.advanceTimersByTime(350)
|
|
239
240
|
|
|
240
241
|
expect(editor.dispatch).toHaveBeenCalledWith(
|
|
241
242
|
expect.objectContaining({
|
|
@@ -255,7 +256,7 @@ describe('ClickManager', () => {
|
|
|
255
256
|
clickManager.handlePointerEvent(pointerDown) // third
|
|
256
257
|
clickManager.handlePointerEvent(pointerDown) // fourth
|
|
257
258
|
|
|
258
|
-
|
|
259
|
+
vi.advanceTimersByTime(350)
|
|
259
260
|
|
|
260
261
|
expect(editor.dispatch).toHaveBeenCalledWith(
|
|
261
262
|
expect.objectContaining({
|
|
@@ -277,7 +278,7 @@ describe('ClickManager', () => {
|
|
|
277
278
|
editor.options.doubleClickDurationMs
|
|
278
279
|
)
|
|
279
280
|
|
|
280
|
-
|
|
281
|
+
vi.clearAllMocks()
|
|
281
282
|
|
|
282
283
|
// Second click - should use multiClickDurationMs
|
|
283
284
|
clickManager.handlePointerEvent(pointerDown)
|
|
@@ -392,7 +393,7 @@ describe('ClickManager', () => {
|
|
|
392
393
|
clickManager.cancelDoubleClickTimeout()
|
|
393
394
|
|
|
394
395
|
// Advance time - should not dispatch settle event
|
|
395
|
-
|
|
396
|
+
vi.advanceTimersByTime(350)
|
|
396
397
|
|
|
397
398
|
expect(editor.dispatch).not.toHaveBeenCalled()
|
|
398
399
|
expect(clickManager.clickState).toBe('idle')
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import { Mock, Mocked, vi } from 'vitest'
|
|
1
2
|
import { Box } from '../../../primitives/Box'
|
|
2
3
|
import { Vec } from '../../../primitives/Vec'
|
|
3
4
|
import { Editor } from '../../Editor'
|
|
4
5
|
import { EdgeScrollManager } from './EdgeScrollManager'
|
|
5
6
|
|
|
6
7
|
// Mock the Editor class
|
|
7
|
-
|
|
8
|
+
vi.mock('../../Editor')
|
|
8
9
|
|
|
9
10
|
describe('EdgeScrollManager', () => {
|
|
10
|
-
let editor:
|
|
11
|
+
let editor: Mocked<
|
|
11
12
|
Editor & {
|
|
12
|
-
user: { getEdgeScrollSpeed:
|
|
13
|
-
getCamera:
|
|
14
|
-
getCameraOptions:
|
|
15
|
-
getZoomLevel:
|
|
16
|
-
getViewportScreenBounds:
|
|
13
|
+
user: { getEdgeScrollSpeed: Mock }
|
|
14
|
+
getCamera: Mock
|
|
15
|
+
getCameraOptions: Mock
|
|
16
|
+
getZoomLevel: Mock
|
|
17
|
+
getViewportScreenBounds: Mock
|
|
17
18
|
}
|
|
18
19
|
>
|
|
19
20
|
let edgeScrollManager: EdgeScrollManager
|
|
@@ -33,33 +34,33 @@ describe('EdgeScrollManager', () => {
|
|
|
33
34
|
isPanning: false,
|
|
34
35
|
},
|
|
35
36
|
user: {
|
|
36
|
-
getEdgeScrollSpeed:
|
|
37
|
+
getEdgeScrollSpeed: vi.fn(() => 1),
|
|
37
38
|
},
|
|
38
|
-
getViewportScreenBounds:
|
|
39
|
-
getInstanceState:
|
|
39
|
+
getViewportScreenBounds: vi.fn(() => new Box(0, 0, 1000, 600)),
|
|
40
|
+
getInstanceState: vi.fn(
|
|
40
41
|
() =>
|
|
41
42
|
({
|
|
42
43
|
isCoarsePointer: false,
|
|
43
44
|
insets: [false, false, false, false], // [top, right, bottom, left]
|
|
44
45
|
}) as any
|
|
45
46
|
),
|
|
46
|
-
getCameraOptions:
|
|
47
|
+
getCameraOptions: vi.fn(() => ({
|
|
47
48
|
isLocked: false,
|
|
48
49
|
panSpeed: 1,
|
|
49
50
|
zoomSpeed: 1,
|
|
50
51
|
zoomSteps: [1],
|
|
51
52
|
wheelBehavior: 'pan' as const,
|
|
52
53
|
})),
|
|
53
|
-
getZoomLevel:
|
|
54
|
-
getCamera:
|
|
55
|
-
setCamera:
|
|
54
|
+
getZoomLevel: vi.fn(() => 1),
|
|
55
|
+
getCamera: vi.fn(() => new Vec(0, 0, 1)),
|
|
56
|
+
setCamera: vi.fn(),
|
|
56
57
|
} as any
|
|
57
58
|
|
|
58
59
|
edgeScrollManager = new EdgeScrollManager(editor as any)
|
|
59
60
|
})
|
|
60
61
|
|
|
61
62
|
afterEach(() => {
|
|
62
|
-
|
|
63
|
+
vi.clearAllMocks()
|
|
63
64
|
})
|
|
64
65
|
|
|
65
66
|
describe('constructor and initialization', () => {
|
|
@@ -1,58 +1,59 @@
|
|
|
1
|
+
import { Mock, Mocked, vi } from 'vitest'
|
|
1
2
|
import { Editor } from '../../Editor'
|
|
2
3
|
import { FocusManager } from './FocusManager'
|
|
3
4
|
|
|
4
5
|
// Mock the Editor class
|
|
5
|
-
|
|
6
|
+
vi.mock('../../Editor')
|
|
6
7
|
|
|
7
8
|
describe('FocusManager', () => {
|
|
8
|
-
let editor:
|
|
9
|
+
let editor: Mocked<
|
|
9
10
|
Editor & {
|
|
10
11
|
sideEffects: {
|
|
11
|
-
registerAfterChangeHandler:
|
|
12
|
+
registerAfterChangeHandler: Mock
|
|
12
13
|
}
|
|
13
|
-
getInstanceState:
|
|
14
|
-
updateInstanceState:
|
|
15
|
-
getContainer:
|
|
16
|
-
isIn:
|
|
17
|
-
getSelectedShapeIds:
|
|
18
|
-
complete:
|
|
14
|
+
getInstanceState: Mock
|
|
15
|
+
updateInstanceState: Mock
|
|
16
|
+
getContainer: Mock
|
|
17
|
+
isIn: Mock
|
|
18
|
+
getSelectedShapeIds: Mock
|
|
19
|
+
complete: Mock
|
|
19
20
|
}
|
|
20
21
|
>
|
|
21
22
|
let focusManager: FocusManager
|
|
22
23
|
let mockContainer: HTMLElement
|
|
23
|
-
let mockDispose:
|
|
24
|
+
let mockDispose: Mock
|
|
24
25
|
let originalAddEventListener: typeof document.body.addEventListener
|
|
25
26
|
let originalRemoveEventListener: typeof document.body.removeEventListener
|
|
26
27
|
|
|
27
28
|
beforeEach(() => {
|
|
28
29
|
// Create mock container element
|
|
29
30
|
mockContainer = document.createElement('div')
|
|
30
|
-
mockContainer.focus =
|
|
31
|
-
mockContainer.blur =
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
mockContainer.focus = vi.fn()
|
|
32
|
+
mockContainer.blur = vi.fn()
|
|
33
|
+
vi.spyOn(mockContainer.classList, 'add')
|
|
34
|
+
vi.spyOn(mockContainer.classList, 'remove')
|
|
34
35
|
|
|
35
36
|
// Create mock dispose function
|
|
36
|
-
mockDispose =
|
|
37
|
+
mockDispose = vi.fn()
|
|
37
38
|
|
|
38
39
|
// Mock editor
|
|
39
40
|
editor = {
|
|
40
41
|
sideEffects: {
|
|
41
|
-
registerAfterChangeHandler:
|
|
42
|
+
registerAfterChangeHandler: vi.fn(() => mockDispose),
|
|
42
43
|
},
|
|
43
|
-
getInstanceState:
|
|
44
|
-
updateInstanceState:
|
|
45
|
-
getContainer:
|
|
46
|
-
isIn:
|
|
47
|
-
getSelectedShapeIds:
|
|
48
|
-
complete:
|
|
44
|
+
getInstanceState: vi.fn(() => ({ isFocused: false })),
|
|
45
|
+
updateInstanceState: vi.fn(),
|
|
46
|
+
getContainer: vi.fn(() => mockContainer),
|
|
47
|
+
isIn: vi.fn(() => false),
|
|
48
|
+
getSelectedShapeIds: vi.fn(() => []),
|
|
49
|
+
complete: vi.fn(),
|
|
49
50
|
} as any
|
|
50
51
|
|
|
51
52
|
// Mock document.body event listeners
|
|
52
53
|
originalAddEventListener = document.body.addEventListener
|
|
53
54
|
originalRemoveEventListener = document.body.removeEventListener
|
|
54
|
-
document.body.addEventListener =
|
|
55
|
-
document.body.removeEventListener =
|
|
55
|
+
document.body.addEventListener = vi.fn()
|
|
56
|
+
document.body.removeEventListener = vi.fn()
|
|
56
57
|
})
|
|
57
58
|
|
|
58
59
|
afterEach(() => {
|
|
@@ -65,7 +66,7 @@ describe('FocusManager', () => {
|
|
|
65
66
|
focusManager.dispose()
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
vi.clearAllMocks()
|
|
69
70
|
})
|
|
70
71
|
|
|
71
72
|
describe('constructor', () => {
|
|
@@ -131,7 +132,7 @@ describe('FocusManager', () => {
|
|
|
131
132
|
const handler = handlerCall[1]
|
|
132
133
|
|
|
133
134
|
// Clear previous calls
|
|
134
|
-
|
|
135
|
+
vi.clearAllMocks()
|
|
135
136
|
|
|
136
137
|
// Simulate focus state change
|
|
137
138
|
const prev = { isFocused: false }
|
|
@@ -149,7 +150,7 @@ describe('FocusManager', () => {
|
|
|
149
150
|
const handlerCall = editor.sideEffects.registerAfterChangeHandler.mock.calls[0]
|
|
150
151
|
const handler = handlerCall[1]
|
|
151
152
|
|
|
152
|
-
|
|
153
|
+
vi.clearAllMocks()
|
|
153
154
|
|
|
154
155
|
// Simulate no focus state change
|
|
155
156
|
const prev = { isFocused: true }
|
|
@@ -170,7 +171,7 @@ describe('FocusManager', () => {
|
|
|
170
171
|
// Get the handler before clearing mocks
|
|
171
172
|
const handlerCall = editor.sideEffects.registerAfterChangeHandler.mock.calls[0]
|
|
172
173
|
handler = handlerCall[1]
|
|
173
|
-
|
|
174
|
+
vi.clearAllMocks()
|
|
174
175
|
})
|
|
175
176
|
|
|
176
177
|
it('should add focused class when editor is focused', () => {
|
|
@@ -205,11 +206,11 @@ describe('FocusManager', () => {
|
|
|
205
206
|
focusManager = new FocusManager(editor)
|
|
206
207
|
|
|
207
208
|
// Get the keydown handler that was registered
|
|
208
|
-
const addEventListenerCalls = (document.body.addEventListener as
|
|
209
|
-
const keydownCall = addEventListenerCalls.find((call) => call[0] === 'keydown')
|
|
210
|
-
keydownHandler = keydownCall[1]
|
|
209
|
+
const addEventListenerCalls = (document.body.addEventListener as Mock).mock.calls
|
|
210
|
+
const keydownCall = addEventListenerCalls.find((call: any) => call[0] === 'keydown')
|
|
211
|
+
keydownHandler = keydownCall![1]
|
|
211
212
|
|
|
212
|
-
|
|
213
|
+
vi.clearAllMocks()
|
|
213
214
|
})
|
|
214
215
|
|
|
215
216
|
it('should remove no-focus-ring class on Tab key', () => {
|
|
@@ -283,11 +284,11 @@ describe('FocusManager', () => {
|
|
|
283
284
|
focusManager = new FocusManager(editor)
|
|
284
285
|
|
|
285
286
|
// Get the mousedown handler that was registered
|
|
286
|
-
const addEventListenerCalls = (document.body.addEventListener as
|
|
287
|
-
const mousedownCall = addEventListenerCalls.find((call) => call[0] === 'mousedown')
|
|
288
|
-
mousedownHandler = mousedownCall[1]
|
|
287
|
+
const addEventListenerCalls = (document.body.addEventListener as Mock).mock.calls
|
|
288
|
+
const mousedownCall = addEventListenerCalls.find((call: any) => call[0] === 'mousedown')
|
|
289
|
+
mousedownHandler = mousedownCall![1]
|
|
289
290
|
|
|
290
|
-
|
|
291
|
+
vi.clearAllMocks()
|
|
291
292
|
})
|
|
292
293
|
|
|
293
294
|
it('should add no-focus-ring class on mouse down', () => {
|
|
@@ -326,7 +327,7 @@ describe('FocusManager', () => {
|
|
|
326
327
|
it('should complete before bluring', () => {
|
|
327
328
|
const callOrder: string[] = []
|
|
328
329
|
editor.complete.mockImplementation(() => callOrder.push('complete'))
|
|
329
|
-
mockContainer.blur =
|
|
330
|
+
mockContainer.blur = vi.fn(() => callOrder.push('blur'))
|
|
330
331
|
|
|
331
332
|
focusManager.blur()
|
|
332
333
|
|
|
@@ -337,7 +338,7 @@ describe('FocusManager', () => {
|
|
|
337
338
|
describe('dispose', () => {
|
|
338
339
|
beforeEach(() => {
|
|
339
340
|
focusManager = new FocusManager(editor)
|
|
340
|
-
|
|
341
|
+
vi.clearAllMocks()
|
|
341
342
|
})
|
|
342
343
|
|
|
343
344
|
it('should remove keyboard event listener', () => {
|
|
@@ -376,7 +377,7 @@ describe('FocusManager', () => {
|
|
|
376
377
|
const handlerCall = editor.sideEffects.registerAfterChangeHandler.mock.calls[0]
|
|
377
378
|
const handler = handlerCall[1]
|
|
378
379
|
|
|
379
|
-
|
|
380
|
+
vi.clearAllMocks()
|
|
380
381
|
|
|
381
382
|
// Rapid focus changes
|
|
382
383
|
editor.getInstanceState.mockReturnValue({ isFocused: true })
|
|
@@ -394,9 +395,9 @@ describe('FocusManager', () => {
|
|
|
394
395
|
|
|
395
396
|
it('should handle keyboard navigation while editing', () => {
|
|
396
397
|
focusManager = new FocusManager(editor)
|
|
397
|
-
const addEventListenerCalls = (document.body.addEventListener as
|
|
398
|
-
const keydownCall = addEventListenerCalls.find((call) => call[0] === 'keydown')
|
|
399
|
-
const keydownHandler = keydownCall[1]
|
|
398
|
+
const addEventListenerCalls = (document.body.addEventListener as Mock).mock.calls
|
|
399
|
+
const keydownCall = addEventListenerCalls.find((call: any) => call[0] === 'keydown')
|
|
400
|
+
const keydownHandler = keydownCall![1]
|
|
400
401
|
|
|
401
402
|
editor.isIn.mockReturnValue(true) // Editing mode
|
|
402
403
|
|
|
@@ -409,15 +410,15 @@ describe('FocusManager', () => {
|
|
|
409
410
|
|
|
410
411
|
it('should handle mouse and keyboard interaction sequence', () => {
|
|
411
412
|
focusManager = new FocusManager(editor)
|
|
412
|
-
const addEventListenerCalls = (document.body.addEventListener as
|
|
413
|
+
const addEventListenerCalls = (document.body.addEventListener as Mock).mock.calls
|
|
413
414
|
|
|
414
|
-
const mousedownCall = addEventListenerCalls.find((call) => call[0] === 'mousedown')
|
|
415
|
-
const keydownCall = addEventListenerCalls.find((call) => call[0] === 'keydown')
|
|
415
|
+
const mousedownCall = addEventListenerCalls.find((call: any) => call[0] === 'mousedown')
|
|
416
|
+
const keydownCall = addEventListenerCalls.find((call: any) => call[0] === 'keydown')
|
|
416
417
|
|
|
417
|
-
const mousedownHandler = mousedownCall[1]
|
|
418
|
-
const keydownHandler = keydownCall[1]
|
|
418
|
+
const mousedownHandler = mousedownCall![1]
|
|
419
|
+
const keydownHandler = keydownCall![1]
|
|
419
420
|
|
|
420
|
-
|
|
421
|
+
vi.clearAllMocks()
|
|
421
422
|
|
|
422
423
|
// Mouse down adds no-focus-ring
|
|
423
424
|
mousedownHandler()
|