@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.
Files changed (71) hide show
  1. package/dist-cjs/index.d.ts +4 -0
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +1 -1
  4. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +1 -1
  5. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  6. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
  7. package/dist-cjs/lib/components/default-components/DefaultScribble.js +1 -1
  8. package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
  9. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +9 -1
  10. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  11. package/dist-cjs/lib/config/TLUserPreferences.js +1 -1
  12. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  13. package/dist-cjs/lib/editor/Editor.js +10 -1
  14. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  15. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +1 -1
  16. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  17. package/dist-cjs/lib/license/Watermark.js +6 -6
  18. package/dist-cjs/lib/license/Watermark.js.map +1 -1
  19. package/dist-cjs/lib/options.js +6 -0
  20. package/dist-cjs/lib/options.js.map +2 -2
  21. package/dist-cjs/version.js +3 -3
  22. package/dist-cjs/version.js.map +1 -1
  23. package/dist-esm/index.d.mts +4 -0
  24. package/dist-esm/index.mjs +1 -1
  25. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +1 -1
  26. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +1 -1
  27. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  28. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  29. package/dist-esm/lib/components/default-components/DefaultScribble.mjs +1 -1
  30. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  31. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +9 -1
  32. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  33. package/dist-esm/lib/config/TLUserPreferences.mjs +1 -1
  34. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  35. package/dist-esm/lib/editor/Editor.mjs +10 -1
  36. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  37. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +1 -1
  38. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  39. package/dist-esm/lib/license/Watermark.mjs +6 -6
  40. package/dist-esm/lib/license/Watermark.mjs.map +1 -1
  41. package/dist-esm/lib/options.mjs +6 -0
  42. package/dist-esm/lib/options.mjs.map +2 -2
  43. package/dist-esm/version.mjs +3 -3
  44. package/dist-esm/version.mjs.map +1 -1
  45. package/editor.css +293 -290
  46. package/package.json +14 -37
  47. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -1
  48. package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
  49. package/src/lib/components/default-components/DefaultScribble.tsx +1 -1
  50. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +5 -1
  51. package/src/lib/config/TLUserPreferences.ts +1 -1
  52. package/src/lib/editor/Editor.test.ts +12 -11
  53. package/src/lib/editor/Editor.ts +11 -1
  54. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +15 -14
  55. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +16 -15
  56. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +49 -48
  57. package/src/lib/editor/managers/FontManager/FontManager.test.ts +24 -23
  58. package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +7 -6
  59. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +12 -11
  60. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +57 -50
  61. package/src/lib/editor/managers/TextManager/TextManager.test.ts +51 -26
  62. package/src/lib/editor/managers/TickManager/TickManager.test.ts +14 -13
  63. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +21 -26
  64. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +1 -1
  65. package/src/lib/license/LicenseManager.test.ts +3 -1
  66. package/src/lib/license/Watermark.test.tsx +2 -1
  67. package/src/lib/license/Watermark.tsx +6 -6
  68. package/src/lib/options.ts +6 -0
  69. package/src/lib/utils/sync/LocalIndexedDb.test.ts +2 -1
  70. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +15 -15
  71. 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.cf24aedcd577",
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": "lazy inherit",
38
- "test": "yarn run -T jest",
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": "lazy inherit",
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.cf24aedcd577",
53
- "@tldraw/state-react": "3.16.0-canary.cf24aedcd577",
54
- "@tldraw/store": "3.16.0-canary.cf24aedcd577",
55
- "@tldraw/tlschema": "3.16.0-canary.cf24aedcd577",
56
- "@tldraw/utils": "3.16.0-canary.cf24aedcd577",
57
- "@tldraw/validate": "3.16.0-canary.cf24aedcd577",
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' && 'matchMedia' in window) {
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 className="tl-shape-indicator" stroke={color ?? 'var(--color-selected)'} opacity={opacity}>
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' && 'matchMedia' in window) {
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 = jest.fn()
63
- editor.user.getAnimationSpeed = jest.fn()
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 = jest.fn(() => new Set([createShapeId('box1')]))
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 = jest.fn(() => new Set([createShapeId('box1')]))
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 = jest.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
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 = jest.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
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 = jest.spyOn(editor, 'isShapeHidden')
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 = jest.spyOn(editor, 'isShapeHidden')
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 = jest.spyOn(editor, 'setSelectedShapes')
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 = jest.spyOn(editor, 'setSelectedShapes')
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 = jest.spyOn(editor, 'setSelectedShapes')
822
+ const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
822
823
 
823
824
  // Call selectAll
824
825
  editor.selectAll()
@@ -6333,7 +6333,17 @@ export class Editor extends EventEmitter<TLEventMap> {
6333
6333
 
6334
6334
  this.createShapes(shapesToCreate)
6335
6335
  this.createBindings(bindingsToCreate)
6336
- this.setSelectedShapes(compact(ids.map((id) => shapeIds.get(id))))
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
- jest.mock('../../Editor')
7
+ vi.mock('../../Editor')
7
8
 
8
9
  describe('ClickManager', () => {
9
- let editor: jest.Mocked<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
- jest.useFakeTimers()
33
+ vi.useFakeTimers()
33
34
  mockTimers = {
34
- setTimeout: jest.fn((fn, delay) => setTimeout(fn, delay)),
35
+ setTimeout: vi.fn((fn, delay) => setTimeout(fn, delay)),
35
36
  }
36
37
 
37
38
  editor = {
38
39
  timers: mockTimers,
39
- dispatch: jest.fn(),
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: jest.fn(() => ({
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
- jest.useRealTimers()
59
- jest.clearAllMocks()
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
- jest.advanceTimersByTime(350)
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
- jest.advanceTimersByTime(350)
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
- jest.advanceTimersByTime(350)
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
- jest.advanceTimersByTime(350)
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
- jest.clearAllMocks()
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
- jest.advanceTimersByTime(350)
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
- jest.mock('../../Editor')
8
+ vi.mock('../../Editor')
8
9
 
9
10
  describe('EdgeScrollManager', () => {
10
- let editor: jest.Mocked<
11
+ let editor: Mocked<
11
12
  Editor & {
12
- user: { getEdgeScrollSpeed: jest.Mock }
13
- getCamera: jest.Mock
14
- getCameraOptions: jest.Mock
15
- getZoomLevel: jest.Mock
16
- getViewportScreenBounds: jest.Mock
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: jest.fn(() => 1),
37
+ getEdgeScrollSpeed: vi.fn(() => 1),
37
38
  },
38
- getViewportScreenBounds: jest.fn(() => new Box(0, 0, 1000, 600)),
39
- getInstanceState: jest.fn(
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: jest.fn(() => ({
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: jest.fn(() => 1),
54
- getCamera: jest.fn(() => new Vec(0, 0, 1)),
55
- setCamera: jest.fn(),
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
- jest.clearAllMocks()
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
- jest.mock('../../Editor')
6
+ vi.mock('../../Editor')
6
7
 
7
8
  describe('FocusManager', () => {
8
- let editor: jest.Mocked<
9
+ let editor: Mocked<
9
10
  Editor & {
10
11
  sideEffects: {
11
- registerAfterChangeHandler: jest.Mock
12
+ registerAfterChangeHandler: Mock
12
13
  }
13
- getInstanceState: jest.Mock
14
- updateInstanceState: jest.Mock
15
- getContainer: jest.Mock
16
- isIn: jest.Mock
17
- getSelectedShapeIds: jest.Mock
18
- complete: jest.Mock
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: jest.Mock
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 = jest.fn()
31
- mockContainer.blur = jest.fn()
32
- jest.spyOn(mockContainer.classList, 'add')
33
- jest.spyOn(mockContainer.classList, 'remove')
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 = jest.fn()
37
+ mockDispose = vi.fn()
37
38
 
38
39
  // Mock editor
39
40
  editor = {
40
41
  sideEffects: {
41
- registerAfterChangeHandler: jest.fn(() => mockDispose),
42
+ registerAfterChangeHandler: vi.fn(() => mockDispose),
42
43
  },
43
- getInstanceState: jest.fn(() => ({ isFocused: false })),
44
- updateInstanceState: jest.fn(),
45
- getContainer: jest.fn(() => mockContainer),
46
- isIn: jest.fn(() => false),
47
- getSelectedShapeIds: jest.fn(() => []),
48
- complete: jest.fn(),
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 = jest.fn()
55
- document.body.removeEventListener = jest.fn()
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
- jest.clearAllMocks()
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
- jest.clearAllMocks()
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
- jest.clearAllMocks()
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
- jest.clearAllMocks()
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 jest.Mock).mock.calls
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
- jest.clearAllMocks()
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 jest.Mock).mock.calls
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
- jest.clearAllMocks()
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 = jest.fn(() => callOrder.push('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
- jest.clearAllMocks()
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
- jest.clearAllMocks()
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 jest.Mock).mock.calls
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 jest.Mock).mock.calls
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
- jest.clearAllMocks()
421
+ vi.clearAllMocks()
421
422
 
422
423
  // Mouse down adds no-focus-ring
423
424
  mousedownHandler()