@tldraw/editor 3.16.0-next.fe14f1b4181f → 4.0.1
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 +91 -111
- package/dist-cjs/index.js +3 -5
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +1 -7
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +11 -1
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/config/TLUserPreferences.js +15 -4
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +58 -114
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +4 -0
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +4 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +11 -6
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +10 -0
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/hooks/useCanvasEvents.js +19 -16
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +5 -5
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
- package/dist-cjs/lib/hooks/useGestureEvents.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/usePassThroughMouseOverEvents.js +4 -1
- package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.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/LicenseManager.js +147 -59
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +39 -1
- package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
- package/dist-cjs/lib/license/Watermark.js +144 -75
- package/dist-cjs/lib/license/Watermark.js.map +3 -3
- package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
- package/dist-cjs/lib/primitives/Vec.js +0 -4
- package/dist-cjs/lib/primitives/Vec.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js +50 -20
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +8 -1
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/lib/utils/getPointerInfo.js +2 -3
- package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
- package/dist-cjs/lib/utils/reparenting.js +7 -36
- package/dist-cjs/lib/utils/reparenting.js.map +3 -3
- package/dist-cjs/version.js +4 -4
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +91 -111
- package/dist-esm/index.mjs +3 -5
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +1 -7
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +11 -1
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs +15 -4
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +58 -114
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +4 -0
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +4 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +11 -6
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +10 -0
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +20 -22
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +6 -6
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +1 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useGestureEvents.mjs +2 -2
- package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useHandleEvents.mjs +6 -6
- package/dist-esm/lib/hooks/useHandleEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +4 -1
- package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useSelectionEvents.mjs +9 -14
- package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseManager.mjs +148 -60
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +39 -2
- package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
- package/dist-esm/lib/license/Watermark.mjs +145 -76
- package/dist-esm/lib/license/Watermark.mjs.map +3 -3
- package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
- package/dist-esm/lib/primitives/Vec.mjs +0 -4
- package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +53 -21
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +8 -1
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/lib/utils/getPointerInfo.mjs +2 -3
- package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
- package/dist-esm/lib/utils/reparenting.mjs +8 -41
- package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
- package/dist-esm/version.mjs +4 -4
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +8 -3
- package/package.json +7 -7
- package/src/index.ts +2 -10
- package/src/lib/TldrawEditor.tsx +1 -15
- package/src/lib/components/default-components/DefaultCanvas.tsx +7 -1
- package/src/lib/config/TLUserPreferences.ts +16 -3
- package/src/lib/editor/Editor.test.ts +90 -0
- package/src/lib/editor/Editor.ts +77 -151
- package/src/lib/editor/derivations/notVisibleShapes.ts +6 -0
- package/src/lib/editor/managers/FocusManager/FocusManager.ts +6 -2
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +30 -8
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +10 -3
- package/src/lib/editor/shapes/ShapeUtil.ts +32 -0
- package/src/lib/editor/types/misc-types.ts +0 -6
- package/src/lib/hooks/useCanvasEvents.ts +20 -20
- package/src/lib/hooks/useDocumentEvents.ts +6 -6
- package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -1
- package/src/lib/hooks/useGestureEvents.ts +2 -2
- package/src/lib/hooks/useHandleEvents.ts +6 -6
- package/src/lib/hooks/usePassThroughMouseOverEvents.ts +4 -1
- package/src/lib/hooks/useSelectionEvents.ts +9 -14
- package/src/lib/license/LicenseManager.test.ts +780 -377
- package/src/lib/license/LicenseManager.ts +207 -70
- package/src/lib/license/LicenseProvider.tsx +74 -2
- package/src/lib/license/Watermark.tsx +152 -77
- package/src/lib/license/useLicenseManagerState.ts +2 -2
- package/src/lib/primitives/Vec.ts +0 -5
- package/src/lib/primitives/geometry/Geometry2d.test.ts +420 -0
- package/src/lib/primitives/geometry/Geometry2d.ts +78 -21
- package/src/lib/primitives/geometry/Group2d.ts +10 -1
- package/src/lib/test/InFrontOfTheCanvas.test.tsx +187 -0
- package/src/lib/utils/dom.test.ts +103 -0
- package/src/lib/utils/dom.ts +8 -1
- package/src/lib/utils/getPointerInfo.ts +3 -2
- package/src/lib/utils/reparenting.ts +10 -70
- package/src/version.ts +4 -4
package/src/lib/TldrawEditor.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MigrationSequence, Store } from '@tldraw/store'
|
|
2
2
|
import { TLShape, TLStore, TLStoreSnapshot } from '@tldraw/tlschema'
|
|
3
3
|
import { annotateError, Required } from '@tldraw/utils'
|
|
4
|
+
import classNames from 'classnames'
|
|
4
5
|
import React, {
|
|
5
6
|
memo,
|
|
6
7
|
ReactNode,
|
|
@@ -12,8 +13,6 @@ import React, {
|
|
|
12
13
|
useState,
|
|
13
14
|
useSyncExternalStore,
|
|
14
15
|
} from 'react'
|
|
15
|
-
|
|
16
|
-
import classNames from 'classnames'
|
|
17
16
|
import { version } from '../version'
|
|
18
17
|
import { DefaultErrorFallback } from './components/default-components/DefaultErrorFallback'
|
|
19
18
|
import { OptionalErrorBoundary } from './components/ErrorBoundary'
|
|
@@ -45,7 +44,6 @@ import { LicenseProvider } from './license/LicenseProvider'
|
|
|
45
44
|
import { Watermark } from './license/Watermark'
|
|
46
45
|
import { TldrawOptions } from './options'
|
|
47
46
|
import { TLDeepLinkOptions } from './utils/deepLinks'
|
|
48
|
-
import { stopEventPropagation } from './utils/dom'
|
|
49
47
|
import { TLTextOptions } from './utils/richText'
|
|
50
48
|
import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
|
|
51
49
|
|
|
@@ -189,13 +187,6 @@ export interface TldrawEditorBaseProps {
|
|
|
189
187
|
*/
|
|
190
188
|
deepLinks?: true | TLDeepLinkOptions
|
|
191
189
|
|
|
192
|
-
/**
|
|
193
|
-
* Predicate for whether or not a shape should be hidden.
|
|
194
|
-
*
|
|
195
|
-
* @deprecated Use {@link TldrawEditorBaseProps#getShapeVisibility} instead.
|
|
196
|
-
*/
|
|
197
|
-
isShapeHidden?(shape: TLShape, editor: Editor): boolean
|
|
198
|
-
|
|
199
190
|
/**
|
|
200
191
|
* Provides a way to hide shapes.
|
|
201
192
|
*
|
|
@@ -283,7 +274,6 @@ export const TldrawEditor = memo(function TldrawEditor({
|
|
|
283
274
|
data-tldraw={version}
|
|
284
275
|
draggable={false}
|
|
285
276
|
className={classNames(`${TL_CONTAINER_CLASS} tl-theme__light`, className)}
|
|
286
|
-
onPointerDown={stopEventPropagation}
|
|
287
277
|
tabIndex={-1}
|
|
288
278
|
role="application"
|
|
289
279
|
aria-label={_options?.branding ?? 'tldraw'}
|
|
@@ -412,8 +402,6 @@ function TldrawEditorWithReadyStore({
|
|
|
412
402
|
options,
|
|
413
403
|
licenseKey,
|
|
414
404
|
deepLinks: _deepLinks,
|
|
415
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
416
|
-
isShapeHidden,
|
|
417
405
|
getShapeVisibility,
|
|
418
406
|
assetUrls,
|
|
419
407
|
}: Required<
|
|
@@ -473,7 +461,6 @@ function TldrawEditorWithReadyStore({
|
|
|
473
461
|
textOptions,
|
|
474
462
|
options,
|
|
475
463
|
licenseKey,
|
|
476
|
-
isShapeHidden,
|
|
477
464
|
getShapeVisibility,
|
|
478
465
|
fontAssetUrls: assetUrls?.fonts,
|
|
479
466
|
})
|
|
@@ -509,7 +496,6 @@ function TldrawEditorWithReadyStore({
|
|
|
509
496
|
user,
|
|
510
497
|
setEditor,
|
|
511
498
|
licenseKey,
|
|
512
|
-
isShapeHidden,
|
|
513
499
|
getShapeVisibility,
|
|
514
500
|
textOptions,
|
|
515
501
|
assetUrls,
|
|
@@ -172,7 +172,13 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
|
|
172
172
|
<LiveCollaborators />
|
|
173
173
|
</div>
|
|
174
174
|
</div>
|
|
175
|
-
<div
|
|
175
|
+
<div
|
|
176
|
+
className="tl-canvas__in-front"
|
|
177
|
+
onPointerDown={editor.markEventAsHandled}
|
|
178
|
+
onPointerUp={editor.markEventAsHandled}
|
|
179
|
+
onTouchStart={editor.markEventAsHandled}
|
|
180
|
+
onTouchEnd={editor.markEventAsHandled}
|
|
181
|
+
>
|
|
176
182
|
<InFrontOfTheCanvasWrapper />
|
|
177
183
|
</div>
|
|
178
184
|
<MovingCameraHitTestBlocker />
|
|
@@ -24,7 +24,8 @@ export interface TLUserPreferences {
|
|
|
24
24
|
isWrapMode?: boolean | null
|
|
25
25
|
isDynamicSizeMode?: boolean | null
|
|
26
26
|
isPasteAtCursorMode?: boolean | null
|
|
27
|
-
|
|
27
|
+
enhancedA11yMode?: boolean | null
|
|
28
|
+
inputMode?: 'trackpad' | 'mouse' | null
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
interface UserDataSnapshot {
|
|
@@ -53,7 +54,8 @@ export const userTypeValidator: T.Validator<TLUserPreferences> = T.object<TLUser
|
|
|
53
54
|
isWrapMode: T.boolean.nullable().optional(),
|
|
54
55
|
isDynamicSizeMode: T.boolean.nullable().optional(),
|
|
55
56
|
isPasteAtCursorMode: T.boolean.nullable().optional(),
|
|
56
|
-
|
|
57
|
+
enhancedA11yMode: T.boolean.nullable().optional(),
|
|
58
|
+
inputMode: T.literalEnum('trackpad', 'mouse').nullable().optional(),
|
|
57
59
|
})
|
|
58
60
|
|
|
59
61
|
const Versions = {
|
|
@@ -67,6 +69,8 @@ const Versions = {
|
|
|
67
69
|
AddPasteAtCursor: 8,
|
|
68
70
|
AddKeyboardShortcuts: 9,
|
|
69
71
|
AddShowUiLabels: 10,
|
|
72
|
+
AddPointerPeripheral: 11,
|
|
73
|
+
RenameShowUiLabelsToEnhancedA11yMode: 12,
|
|
70
74
|
} as const
|
|
71
75
|
|
|
72
76
|
const CURRENT_VERSION = Math.max(...Object.values(Versions))
|
|
@@ -108,6 +112,14 @@ function migrateSnapshot(data: { version: number; user: any }) {
|
|
|
108
112
|
if (data.version < Versions.AddShowUiLabels) {
|
|
109
113
|
data.user.showUiLabels = false
|
|
110
114
|
}
|
|
115
|
+
if (data.version < Versions.RenameShowUiLabelsToEnhancedA11yMode) {
|
|
116
|
+
data.user.enhancedA11yMode = data.user.showUiLabels
|
|
117
|
+
delete data.user.showUiLabels
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (data.version < Versions.AddPointerPeripheral) {
|
|
121
|
+
data.user.inputMode = null
|
|
122
|
+
}
|
|
111
123
|
|
|
112
124
|
// finally
|
|
113
125
|
data.version = CURRENT_VERSION
|
|
@@ -156,8 +168,9 @@ export const defaultUserPreferences = Object.freeze({
|
|
|
156
168
|
isWrapMode: false,
|
|
157
169
|
isDynamicSizeMode: false,
|
|
158
170
|
isPasteAtCursorMode: false,
|
|
159
|
-
|
|
171
|
+
enhancedA11yMode: false,
|
|
160
172
|
colorScheme: 'light',
|
|
173
|
+
inputMode: null,
|
|
161
174
|
}) satisfies Readonly<Omit<TLUserPreferences, 'id'>>
|
|
162
175
|
|
|
163
176
|
/** @public */
|
|
@@ -833,3 +833,93 @@ describe('selectAll', () => {
|
|
|
833
833
|
setSelectedShapesSpy.mockRestore()
|
|
834
834
|
})
|
|
835
835
|
})
|
|
836
|
+
|
|
837
|
+
describe('putExternalContent', () => {
|
|
838
|
+
let mockHandler: any
|
|
839
|
+
|
|
840
|
+
beforeEach(() => {
|
|
841
|
+
mockHandler = vi.fn()
|
|
842
|
+
editor.registerExternalContentHandler('text', mockHandler)
|
|
843
|
+
})
|
|
844
|
+
|
|
845
|
+
it('calls external content handler when not readonly', async () => {
|
|
846
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
847
|
+
|
|
848
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
849
|
+
await editor.putExternalContent(info)
|
|
850
|
+
|
|
851
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
it('does not call external content handler when readonly', async () => {
|
|
855
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
856
|
+
|
|
857
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
858
|
+
await editor.putExternalContent(info)
|
|
859
|
+
|
|
860
|
+
expect(mockHandler).not.toHaveBeenCalled()
|
|
861
|
+
})
|
|
862
|
+
|
|
863
|
+
it('calls external content handler when readonly but force is true', async () => {
|
|
864
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
865
|
+
|
|
866
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
867
|
+
await editor.putExternalContent(info, { force: true })
|
|
868
|
+
|
|
869
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
870
|
+
})
|
|
871
|
+
|
|
872
|
+
it('calls external content handler when force is false and not readonly', async () => {
|
|
873
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
874
|
+
|
|
875
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
876
|
+
await editor.putExternalContent(info, { force: false })
|
|
877
|
+
|
|
878
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
879
|
+
})
|
|
880
|
+
})
|
|
881
|
+
|
|
882
|
+
describe('replaceExternalContent', () => {
|
|
883
|
+
let mockHandler: any
|
|
884
|
+
|
|
885
|
+
beforeEach(() => {
|
|
886
|
+
mockHandler = vi.fn()
|
|
887
|
+
editor.registerExternalContentHandler('text', mockHandler)
|
|
888
|
+
})
|
|
889
|
+
|
|
890
|
+
it('calls external content handler when not readonly', async () => {
|
|
891
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
892
|
+
|
|
893
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
894
|
+
await editor.replaceExternalContent(info)
|
|
895
|
+
|
|
896
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
897
|
+
})
|
|
898
|
+
|
|
899
|
+
it('does not call external content handler when readonly', async () => {
|
|
900
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
901
|
+
|
|
902
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
903
|
+
await editor.replaceExternalContent(info)
|
|
904
|
+
|
|
905
|
+
expect(mockHandler).not.toHaveBeenCalled()
|
|
906
|
+
})
|
|
907
|
+
|
|
908
|
+
it('calls external content handler when readonly but force is true', async () => {
|
|
909
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
|
|
910
|
+
|
|
911
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
912
|
+
await editor.replaceExternalContent(info, { force: true })
|
|
913
|
+
|
|
914
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
915
|
+
})
|
|
916
|
+
|
|
917
|
+
it('calls external content handler when force is false and not readonly', async () => {
|
|
918
|
+
vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
|
|
919
|
+
|
|
920
|
+
const info = { type: 'text' as const, text: 'test-data' }
|
|
921
|
+
await editor.replaceExternalContent(info, { force: false })
|
|
922
|
+
|
|
923
|
+
expect(mockHandler).toHaveBeenCalledWith(info)
|
|
924
|
+
})
|
|
925
|
+
})
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -116,7 +116,6 @@ import {
|
|
|
116
116
|
} from '../constants'
|
|
117
117
|
import { exportToSvg } from '../exports/exportToSvg'
|
|
118
118
|
import { getSvgAsImage } from '../exports/getSvgAsImage'
|
|
119
|
-
import { tlenv } from '../globals/environment'
|
|
120
119
|
import { tlmenus } from '../globals/menus'
|
|
121
120
|
import { tltime } from '../globals/time'
|
|
122
121
|
import { TldrawOptions, defaultTldrawOptions } from '../options'
|
|
@@ -244,16 +243,6 @@ export interface TLEditorOptions {
|
|
|
244
243
|
options?: Partial<TldrawOptions>
|
|
245
244
|
licenseKey?: string
|
|
246
245
|
fontAssetUrls?: { [key: string]: string | undefined }
|
|
247
|
-
/**
|
|
248
|
-
* A predicate that should return true if the given shape should be hidden.
|
|
249
|
-
*
|
|
250
|
-
* @deprecated Use {@link Editor#getShapeVisibility} instead.
|
|
251
|
-
*
|
|
252
|
-
* @param shape - The shape to check.
|
|
253
|
-
* @param editor - The editor instance.
|
|
254
|
-
*/
|
|
255
|
-
isShapeHidden?(shape: TLShape, editor: Editor): boolean
|
|
256
|
-
|
|
257
246
|
/**
|
|
258
247
|
* Provides a way to hide shapes.
|
|
259
248
|
*
|
|
@@ -309,21 +298,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
309
298
|
autoFocus,
|
|
310
299
|
inferDarkMode,
|
|
311
300
|
options,
|
|
312
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
313
|
-
isShapeHidden,
|
|
314
301
|
getShapeVisibility,
|
|
315
302
|
fontAssetUrls,
|
|
316
303
|
}: TLEditorOptions) {
|
|
317
304
|
super()
|
|
318
|
-
assert(
|
|
319
|
-
!(isShapeHidden && getShapeVisibility),
|
|
320
|
-
'Cannot use both isShapeHidden and getShapeVisibility'
|
|
321
|
-
)
|
|
322
305
|
|
|
323
|
-
this._getShapeVisibility =
|
|
324
|
-
? // eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
325
|
-
(shape: TLShape, editor: Editor) => (isShapeHidden(shape, editor) ? 'hidden' : 'inherit')
|
|
326
|
-
: getShapeVisibility
|
|
306
|
+
this._getShapeVisibility = getShapeVisibility
|
|
327
307
|
|
|
328
308
|
this.options = { ...defaultTldrawOptions, ...options }
|
|
329
309
|
|
|
@@ -363,6 +343,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
363
343
|
this.root = new NewRoot(this)
|
|
364
344
|
this.root.children = {}
|
|
365
345
|
|
|
346
|
+
this.markEventAsHandled = this.markEventAsHandled.bind(this)
|
|
347
|
+
|
|
366
348
|
const allShapeUtils = checkShapesAndAddCore(shapeUtils)
|
|
367
349
|
|
|
368
350
|
const _shapeUtils = {} as Record<string, ShapeUtil<any>>
|
|
@@ -907,14 +889,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
907
889
|
*/
|
|
908
890
|
readonly fonts: FontManager
|
|
909
891
|
|
|
910
|
-
/**
|
|
911
|
-
* A manager for the editor's environment.
|
|
912
|
-
*
|
|
913
|
-
* @deprecated This is deprecated and will be removed in a future version. Use the `tlenv` global export instead.
|
|
914
|
-
* @public
|
|
915
|
-
*/
|
|
916
|
-
readonly environment = tlenv
|
|
917
|
-
|
|
918
892
|
/**
|
|
919
893
|
* A manager for the editor's scribbles.
|
|
920
894
|
*
|
|
@@ -1119,35 +1093,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1119
1093
|
return this.history.getNumRedos() > 0
|
|
1120
1094
|
}
|
|
1121
1095
|
|
|
1122
|
-
/**
|
|
1123
|
-
* Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
|
|
1124
|
-
* any redos.
|
|
1125
|
-
*
|
|
1126
|
-
* @example
|
|
1127
|
-
* ```ts
|
|
1128
|
-
* editor.mark()
|
|
1129
|
-
* editor.mark('flip shapes')
|
|
1130
|
-
* ```
|
|
1131
|
-
*
|
|
1132
|
-
* @param markId - The mark's id, usually the reason for adding the mark.
|
|
1133
|
-
*
|
|
1134
|
-
* @public
|
|
1135
|
-
* @deprecated use {@link Editor.markHistoryStoppingPoint} instead
|
|
1136
|
-
*/
|
|
1137
|
-
mark(markId?: string): this {
|
|
1138
|
-
if (typeof markId === 'string') {
|
|
1139
|
-
console.warn(
|
|
1140
|
-
`[tldraw] \`editor.history.mark("${markId}")\` is deprecated. Please use \`const myMarkId = editor.markHistoryStoppingPoint()\` instead.`
|
|
1141
|
-
)
|
|
1142
|
-
} else {
|
|
1143
|
-
console.warn(
|
|
1144
|
-
'[tldraw] `editor.mark()` is deprecated. Use `editor.markHistoryStoppingPoint()` instead.'
|
|
1145
|
-
)
|
|
1146
|
-
}
|
|
1147
|
-
this.history._mark(markId ?? uniqueId())
|
|
1148
|
-
return this
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
1096
|
/**
|
|
1152
1097
|
* Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
|
|
1153
1098
|
* any redos. You typically want to do this just before a user interaction begins or is handled.
|
|
@@ -1272,13 +1217,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1272
1217
|
return this
|
|
1273
1218
|
}
|
|
1274
1219
|
|
|
1275
|
-
/**
|
|
1276
|
-
* @deprecated Use `Editor.run` instead.
|
|
1277
|
-
*/
|
|
1278
|
-
batch(fn: () => void, opts?: TLEditorRunOptions): this {
|
|
1279
|
-
return this.run(fn, opts)
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
1220
|
/* --------------------- Errors --------------------- */
|
|
1283
1221
|
|
|
1284
1222
|
/** @internal */
|
|
@@ -1580,54 +1518,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1580
1518
|
|
|
1581
1519
|
menus = tlmenus.forContext(this.contextId)
|
|
1582
1520
|
|
|
1583
|
-
/**
|
|
1584
|
-
* @deprecated Use `editor.menus.getOpenMenus` instead.
|
|
1585
|
-
*
|
|
1586
|
-
* @public
|
|
1587
|
-
*/
|
|
1588
|
-
@computed getOpenMenus(): string[] {
|
|
1589
|
-
return this.menus.getOpenMenus()
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
/**
|
|
1593
|
-
* @deprecated Use `editor.menus.addOpenMenu` instead.
|
|
1594
|
-
*
|
|
1595
|
-
* @public
|
|
1596
|
-
*/
|
|
1597
|
-
addOpenMenu(id: string): this {
|
|
1598
|
-
this.menus.addOpenMenu(id)
|
|
1599
|
-
return this
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
/**
|
|
1603
|
-
* @deprecated Use `editor.menus.deleteOpenMenu` instead.
|
|
1604
|
-
*
|
|
1605
|
-
* @public
|
|
1606
|
-
*/
|
|
1607
|
-
deleteOpenMenu(id: string): this {
|
|
1608
|
-
this.menus.deleteOpenMenu(id)
|
|
1609
|
-
return this
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
/**
|
|
1613
|
-
* @deprecated Use `editor.menus.clearOpenMenus` instead.
|
|
1614
|
-
*
|
|
1615
|
-
* @public
|
|
1616
|
-
*/
|
|
1617
|
-
clearOpenMenus(): this {
|
|
1618
|
-
this.menus.clearOpenMenus()
|
|
1619
|
-
return this
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
/**
|
|
1623
|
-
* @deprecated Use `editor.menus.hasAnyOpenMenus` instead.
|
|
1624
|
-
*
|
|
1625
|
-
* @public
|
|
1626
|
-
*/
|
|
1627
|
-
@computed getIsMenuOpen(): boolean {
|
|
1628
|
-
return this.menus.hasAnyOpenMenus()
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
1521
|
/* --------------------- Cursor --------------------- */
|
|
1632
1522
|
|
|
1633
1523
|
/**
|
|
@@ -4792,8 +4682,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4792
4682
|
return this.store.createComputedCache<Box, TLShape>('pageBoundsCache', (shape) => {
|
|
4793
4683
|
const pageTransform = this.getShapePageTransform(shape)
|
|
4794
4684
|
if (!pageTransform) return undefined
|
|
4795
|
-
|
|
4796
|
-
return Box.FromPoints(
|
|
4685
|
+
|
|
4686
|
+
return Box.FromPoints(
|
|
4687
|
+
pageTransform.applyToPoints(this.getShapeGeometry(shape).boundsVertices)
|
|
4688
|
+
)
|
|
4797
4689
|
})
|
|
4798
4690
|
}
|
|
4799
4691
|
|
|
@@ -4860,27 +4752,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4860
4752
|
return this.store.createComputedCache('pageMaskCache', (shape) => {
|
|
4861
4753
|
if (isPageId(shape.parentId)) return undefined
|
|
4862
4754
|
|
|
4863
|
-
const
|
|
4864
|
-
|
|
4865
|
-
)
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
return []
|
|
4883
|
-
})
|
|
4755
|
+
const clipPaths: Vec[][] = []
|
|
4756
|
+
// Get all ancestors that can potentially clip this shape
|
|
4757
|
+
for (const ancestor of this.getShapeAncestors(shape.id)) {
|
|
4758
|
+
const util = this.getShapeUtil(ancestor)
|
|
4759
|
+
const clipPath = util.getClipPath?.(ancestor)
|
|
4760
|
+
if (!clipPath) continue
|
|
4761
|
+
if (util.shouldClipChild?.(shape) === false) continue
|
|
4762
|
+
const pageTransform = this.getShapePageTransform(ancestor.id)
|
|
4763
|
+
clipPaths.push(pageTransform.applyToPoints(clipPath))
|
|
4764
|
+
}
|
|
4765
|
+
if (clipPaths.length === 0) return undefined
|
|
4766
|
+
|
|
4767
|
+
const pageMask = clipPaths.reduce((acc, b) => {
|
|
4768
|
+
const intersection = intersectPolygonPolygon(acc, b)
|
|
4769
|
+
if (intersection) {
|
|
4770
|
+
return intersection.map(Vec.Cast)
|
|
4771
|
+
}
|
|
4772
|
+
return []
|
|
4773
|
+
})
|
|
4884
4774
|
|
|
4885
4775
|
return pageMask
|
|
4886
4776
|
})
|
|
@@ -5841,11 +5731,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5841
5731
|
return shapeIds
|
|
5842
5732
|
}
|
|
5843
5733
|
|
|
5844
|
-
/** @deprecated Use {@link Editor.getDraggingOverShape} instead */
|
|
5845
|
-
getDroppingOverShape(point: Vec, droppingShapes: TLShape[]): TLShape | undefined {
|
|
5846
|
-
return this.getDraggingOverShape(point, droppingShapes)
|
|
5847
|
-
}
|
|
5848
|
-
|
|
5849
5734
|
/**
|
|
5850
5735
|
* Get the shape that some shapes should be dropped on at a given point.
|
|
5851
5736
|
*
|
|
@@ -8950,8 +8835,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8950
8835
|
* Handle external content, such as files, urls, embeds, or plain text which has been put into the app, for example by pasting external text or dropping external images onto canvas.
|
|
8951
8836
|
*
|
|
8952
8837
|
* @param info - Info about the external content.
|
|
8838
|
+
* @param opts - Options for handling external content, including force flag to bypass readonly checks.
|
|
8953
8839
|
*/
|
|
8954
|
-
async putExternalContent<E>(
|
|
8840
|
+
async putExternalContent<E>(
|
|
8841
|
+
info: TLExternalContent<E>,
|
|
8842
|
+
opts = {} as { force?: boolean }
|
|
8843
|
+
): Promise<void> {
|
|
8844
|
+
if (!opts.force && this.getIsReadonly()) return
|
|
8955
8845
|
return this.externalContentHandlers[info.type]?.(info as any)
|
|
8956
8846
|
}
|
|
8957
8847
|
|
|
@@ -8959,8 +8849,13 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
8959
8849
|
* Handle replacing external content.
|
|
8960
8850
|
*
|
|
8961
8851
|
* @param info - Info about the external content.
|
|
8852
|
+
* @param opts - Options for handling external content, including force flag to bypass readonly checks.
|
|
8962
8853
|
*/
|
|
8963
|
-
async replaceExternalContent<E>(
|
|
8854
|
+
async replaceExternalContent<E>(
|
|
8855
|
+
info: TLExternalContent<E>,
|
|
8856
|
+
opts = {} as { force?: boolean }
|
|
8857
|
+
): Promise<void> {
|
|
8858
|
+
if (!opts.force && this.getIsReadonly()) return
|
|
8964
8859
|
return this.externalContentHandlers[info.type]?.(info as any)
|
|
8965
8860
|
}
|
|
8966
8861
|
|
|
@@ -9461,13 +9356,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9461
9356
|
}
|
|
9462
9357
|
}
|
|
9463
9358
|
|
|
9464
|
-
/** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
|
|
9465
|
-
async getSvg(shapes: TLShapeId[] | TLShape[], opts: TLSvgExportOptions = {}) {
|
|
9466
|
-
const result = await this.getSvgElement(shapes, opts)
|
|
9467
|
-
if (!result) return undefined
|
|
9468
|
-
return result.svg
|
|
9469
|
-
}
|
|
9470
|
-
|
|
9471
9359
|
/**
|
|
9472
9360
|
* Get an exported image of the given shapes.
|
|
9473
9361
|
*
|
|
@@ -10211,6 +10099,37 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10211
10099
|
/** @internal */
|
|
10212
10100
|
private performanceTrackerTimeout = -1 as any
|
|
10213
10101
|
|
|
10102
|
+
/** @internal */
|
|
10103
|
+
private handledEvents = new WeakSet<Event>()
|
|
10104
|
+
|
|
10105
|
+
/**
|
|
10106
|
+
* In tldraw, events are sometimes handled by multiple components. For example, the shapes might
|
|
10107
|
+
* have events, but the canvas handles events too. The way that the canvas handles events can
|
|
10108
|
+
* interfere with the with the shapes event handlers - for example, it calls `.preventDefault()`
|
|
10109
|
+
* on `pointerDown`, which also prevents `click` events from firing on the shapes.
|
|
10110
|
+
*
|
|
10111
|
+
* You can use `.stopPropagation()` to prevent the event from propagating to the rest of the
|
|
10112
|
+
* DOM, but that can impact non-tldraw event handlers set up elsewhere. By using
|
|
10113
|
+
* `markEventAsHandled`, you'll stop other parts of tldraw from handling the event without
|
|
10114
|
+
* impacting other, non-tldraw event handlers. See also {@link Editor.wasEventAlreadyHandled}.
|
|
10115
|
+
*
|
|
10116
|
+
* @public
|
|
10117
|
+
*/
|
|
10118
|
+
markEventAsHandled(e: Event | { nativeEvent: Event }) {
|
|
10119
|
+
const nativeEvent = 'nativeEvent' in e ? e.nativeEvent : e
|
|
10120
|
+
this.handledEvents.add(nativeEvent)
|
|
10121
|
+
}
|
|
10122
|
+
|
|
10123
|
+
/**
|
|
10124
|
+
* Checks if an event has already been handled. See {@link Editor.markEventAsHandled}.
|
|
10125
|
+
*
|
|
10126
|
+
* @public
|
|
10127
|
+
*/
|
|
10128
|
+
wasEventAlreadyHandled(e: Event | { nativeEvent: Event }) {
|
|
10129
|
+
const nativeEvent = 'nativeEvent' in e ? e.nativeEvent : e
|
|
10130
|
+
return this.handledEvents.has(nativeEvent)
|
|
10131
|
+
}
|
|
10132
|
+
|
|
10214
10133
|
/**
|
|
10215
10134
|
* Dispatch an event to the editor.
|
|
10216
10135
|
*
|
|
@@ -10415,7 +10334,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
10415
10334
|
|
|
10416
10335
|
this._updateInputsFromEvent(info)
|
|
10417
10336
|
|
|
10418
|
-
const { panSpeed, zoomSpeed
|
|
10337
|
+
const { panSpeed, zoomSpeed } = cameraOptions
|
|
10338
|
+
let wheelBehavior = cameraOptions.wheelBehavior
|
|
10339
|
+
const inputMode = this.user.getUserPreferences().inputMode
|
|
10340
|
+
|
|
10341
|
+
// If the user has set their input mode preference, then use that to determine the wheel behavior
|
|
10342
|
+
if (inputMode !== null) {
|
|
10343
|
+
wheelBehavior = inputMode === 'trackpad' ? 'pan' : 'zoom'
|
|
10344
|
+
}
|
|
10419
10345
|
|
|
10420
10346
|
if (wheelBehavior !== 'none') {
|
|
10421
10347
|
// Stop any camera animation
|
|
@@ -7,6 +7,12 @@ function fromScratch(editor: Editor): Set<TLShapeId> {
|
|
|
7
7
|
const viewportPageBounds = editor.getViewportPageBounds()
|
|
8
8
|
const notVisibleShapes = new Set<TLShapeId>()
|
|
9
9
|
shapesIds.forEach((id) => {
|
|
10
|
+
const shape = editor.getShape(id)
|
|
11
|
+
if (!shape) return
|
|
12
|
+
|
|
13
|
+
const canCull = editor.getShapeUtil(shape.type).canCull(shape)
|
|
14
|
+
if (!canCull) return
|
|
15
|
+
|
|
10
16
|
// If the shape is fully outside of the viewport page bounds, add it to the set.
|
|
11
17
|
// We'll ignore masks here, since they're more expensive to compute and the overhead is not worth it.
|
|
12
18
|
const pageBounds = editor.getShapePageBounds(id)
|
|
@@ -58,8 +58,12 @@ export class FocusManager {
|
|
|
58
58
|
|
|
59
59
|
private handleKeyDown(keyEvent: KeyboardEvent) {
|
|
60
60
|
const container = this.editor.getContainer()
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
const activeEl = document.activeElement
|
|
62
|
+
// Edit mode should remove the focus ring, however if the active element's
|
|
63
|
+
// parent is the contextual toolbar, then allow it.
|
|
64
|
+
if (this.editor.isIn('select.editing_shape') && !activeEl?.closest('.tlui-contextual-toolbar'))
|
|
65
|
+
return
|
|
66
|
+
if (activeEl === container && this.editor.getSelectedShapeIds().length > 0) return
|
|
63
67
|
if (['Tab', 'ArrowUp', 'ArrowDown'].includes(keyEvent.key)) {
|
|
64
68
|
container.classList.remove('tl-container__no-focus-ring')
|
|
65
69
|
}
|
|
@@ -23,13 +23,14 @@ describe('UserPreferencesManager', () => {
|
|
|
23
23
|
locale: 'en',
|
|
24
24
|
animationSpeed: 1,
|
|
25
25
|
areKeyboardShortcutsEnabled: true,
|
|
26
|
-
|
|
26
|
+
enhancedA11yMode: false,
|
|
27
27
|
edgeScrollSpeed: 1,
|
|
28
28
|
colorScheme: 'light',
|
|
29
29
|
isSnapMode: false,
|
|
30
30
|
isWrapMode: false,
|
|
31
31
|
isDynamicSizeMode: false,
|
|
32
32
|
isPasteAtCursorMode: false,
|
|
33
|
+
inputMode: null,
|
|
33
34
|
...overrides,
|
|
34
35
|
})
|
|
35
36
|
|
|
@@ -227,12 +228,13 @@ describe('UserPreferencesManager', () => {
|
|
|
227
228
|
color: mockUserPreferences.color,
|
|
228
229
|
animationSpeed: mockUserPreferences.animationSpeed,
|
|
229
230
|
areKeyboardShortcutsEnabled: mockUserPreferences.areKeyboardShortcutsEnabled,
|
|
230
|
-
|
|
231
|
+
enhancedA11yMode: mockUserPreferences.enhancedA11yMode,
|
|
231
232
|
isSnapMode: mockUserPreferences.isSnapMode,
|
|
232
233
|
colorScheme: mockUserPreferences.colorScheme,
|
|
233
234
|
isDarkMode: false, // light mode
|
|
234
235
|
isWrapMode: mockUserPreferences.isWrapMode,
|
|
235
236
|
isDynamicResizeMode: mockUserPreferences.isDynamicSizeMode,
|
|
237
|
+
inputMode: mockUserPreferences.inputMode,
|
|
236
238
|
})
|
|
237
239
|
})
|
|
238
240
|
|
|
@@ -376,14 +378,18 @@ describe('UserPreferencesManager', () => {
|
|
|
376
378
|
})
|
|
377
379
|
})
|
|
378
380
|
|
|
379
|
-
describe('
|
|
380
|
-
it('should return user
|
|
381
|
-
expect(userPreferencesManager.
|
|
381
|
+
describe('getEnhancedA11yMode', () => {
|
|
382
|
+
it('should return user enhanced a11y mode setting', () => {
|
|
383
|
+
expect(userPreferencesManager.getEnhancedA11yMode()).toBe(
|
|
384
|
+
mockUserPreferences.enhancedA11yMode
|
|
385
|
+
)
|
|
382
386
|
})
|
|
383
387
|
|
|
384
|
-
it('should return default
|
|
385
|
-
userPreferencesAtom.set({ ...mockUserPreferences,
|
|
386
|
-
expect(userPreferencesManager.
|
|
388
|
+
it('should return default enhanced a11y mode when null', () => {
|
|
389
|
+
userPreferencesAtom.set({ ...mockUserPreferences, enhancedA11yMode: null })
|
|
390
|
+
expect(userPreferencesManager.getEnhancedA11yMode()).toBe(
|
|
391
|
+
defaultUserPreferences.enhancedA11yMode
|
|
392
|
+
)
|
|
387
393
|
})
|
|
388
394
|
})
|
|
389
395
|
|
|
@@ -453,6 +459,22 @@ describe('UserPreferencesManager', () => {
|
|
|
453
459
|
)
|
|
454
460
|
})
|
|
455
461
|
})
|
|
462
|
+
|
|
463
|
+
describe('getInputMode', () => {
|
|
464
|
+
it('should return user input mode setting', () => {
|
|
465
|
+
expect(userPreferencesManager.getInputMode()).toBe(null)
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
it('should return trackpad if input mode is trackpad', () => {
|
|
469
|
+
userPreferencesAtom.set({ ...mockUserPreferences, inputMode: 'trackpad' })
|
|
470
|
+
expect(userPreferencesManager.getInputMode()).toBe('trackpad')
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
it('should return mouse if input mode is mouse', () => {
|
|
474
|
+
userPreferencesAtom.set({ ...mockUserPreferences, inputMode: 'mouse' })
|
|
475
|
+
expect(userPreferencesManager.getInputMode()).toBe('mouse')
|
|
476
|
+
})
|
|
477
|
+
})
|
|
456
478
|
})
|
|
457
479
|
|
|
458
480
|
describe('reactive behavior', () => {
|