@tldraw/editor 3.16.0-next.282b7be564ae → 3.16.0-next.4337ae1ab96d
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 +54 -101
- package/dist-cjs/index.js +1 -5
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +5 -5
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +7 -10
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -23
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- 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/config/TLUserPreferences.js +1 -1
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +41 -110
- 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/editor/shapes/ShapeUtil.js +13 -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/exports/getSvgJsx.js +34 -14
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +7 -5
- package/dist-cjs/lib/hooks/useCanvasEvents.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/usePassThroughWheelEvents.js +4 -1
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
- package/dist-cjs/lib/license/LicenseManager.js +17 -22
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +5 -0
- package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
- package/dist-cjs/lib/license/Watermark.js +4 -4
- package/dist-cjs/lib/license/Watermark.js.map +1 -1
- package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
- package/dist-cjs/lib/options.js +6 -0
- package/dist-cjs/lib/options.js.map +2 -2
- package/dist-cjs/lib/primitives/Box.js +3 -0
- package/dist-cjs/lib/primitives/Box.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 +26 -18
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +3 -0
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/utils/reparenting.js +2 -35
- package/dist-cjs/lib/utils/reparenting.js.map +3 -3
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +54 -101
- package/dist-esm/index.mjs +1 -5
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +5 -5
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +7 -10
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +4 -23
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- 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/config/TLUserPreferences.mjs +1 -1
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +41 -110
- 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/editor/shapes/ShapeUtil.mjs +13 -0
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs +34 -14
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +7 -5
- package/dist-esm/lib/hooks/useCanvasEvents.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/usePassThroughWheelEvents.mjs +4 -1
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseManager.mjs +17 -22
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +5 -0
- package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
- package/dist-esm/lib/license/Watermark.mjs +4 -4
- package/dist-esm/lib/license/Watermark.mjs.map +1 -1
- package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
- package/dist-esm/lib/options.mjs +6 -0
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/primitives/Box.mjs +4 -1
- package/dist-esm/lib/primitives/Box.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 +29 -19
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +3 -0
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/utils/reparenting.mjs +3 -40
- package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +8 -0
- package/package.json +14 -37
- package/src/index.ts +1 -9
- package/src/lib/TldrawEditor.tsx +6 -12
- package/src/lib/components/Shape.tsx +6 -12
- package/src/lib/components/default-components/DefaultCanvas.tsx +5 -22
- package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
- package/src/lib/config/TLUserPreferences.ts +1 -1
- package/src/lib/editor/Editor.test.ts +12 -11
- package/src/lib/editor/Editor.ts +49 -147
- 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/editor/shapes/ShapeUtil.ts +35 -0
- package/src/lib/editor/types/misc-types.ts +0 -6
- package/src/lib/exports/getSvgJsx.test.ts +868 -0
- package/src/lib/exports/getSvgJsx.tsx +76 -19
- package/src/lib/hooks/useCanvasEvents.ts +6 -6
- package/src/lib/hooks/usePassThroughMouseOverEvents.ts +4 -1
- package/src/lib/hooks/usePassThroughWheelEvents.ts +6 -1
- package/src/lib/license/LicenseManager.test.ts +61 -52
- package/src/lib/license/LicenseManager.ts +32 -24
- package/src/lib/license/LicenseProvider.tsx +8 -0
- package/src/lib/license/Watermark.test.tsx +2 -1
- package/src/lib/license/Watermark.tsx +4 -4
- package/src/lib/license/useLicenseManagerState.ts +2 -2
- package/src/lib/options.ts +6 -0
- package/src/lib/primitives/Box.test.ts +126 -0
- package/src/lib/primitives/Box.ts +10 -1
- package/src/lib/primitives/Vec.ts +0 -5
- package/src/lib/primitives/geometry/Geometry2d.ts +49 -19
- package/src/lib/primitives/geometry/Group2d.ts +4 -0
- package/src/lib/utils/reparenting.ts +3 -69
- 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/dist-cjs/lib/utils/nearestMultiple.js +0 -34
- package/dist-cjs/lib/utils/nearestMultiple.js.map +0 -7
- package/dist-esm/lib/utils/nearestMultiple.mjs +0 -14
- package/dist-esm/lib/utils/nearestMultiple.mjs.map +0 -7
- package/src/lib/utils/nearestMultiple.ts +0 -13
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
|
|
|
@@ -907,14 +887,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
907
887
|
*/
|
|
908
888
|
readonly fonts: FontManager
|
|
909
889
|
|
|
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
890
|
/**
|
|
919
891
|
* A manager for the editor's scribbles.
|
|
920
892
|
*
|
|
@@ -1119,35 +1091,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1119
1091
|
return this.history.getNumRedos() > 0
|
|
1120
1092
|
}
|
|
1121
1093
|
|
|
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
1094
|
/**
|
|
1152
1095
|
* Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
|
|
1153
1096
|
* any redos. You typically want to do this just before a user interaction begins or is handled.
|
|
@@ -1272,13 +1215,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1272
1215
|
return this
|
|
1273
1216
|
}
|
|
1274
1217
|
|
|
1275
|
-
/**
|
|
1276
|
-
* @deprecated Use `Editor.run` instead.
|
|
1277
|
-
*/
|
|
1278
|
-
batch(fn: () => void, opts?: TLEditorRunOptions): this {
|
|
1279
|
-
return this.run(fn, opts)
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
1218
|
/* --------------------- Errors --------------------- */
|
|
1283
1219
|
|
|
1284
1220
|
/** @internal */
|
|
@@ -1580,54 +1516,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1580
1516
|
|
|
1581
1517
|
menus = tlmenus.forContext(this.contextId)
|
|
1582
1518
|
|
|
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
1519
|
/* --------------------- Cursor --------------------- */
|
|
1632
1520
|
|
|
1633
1521
|
/**
|
|
@@ -4860,27 +4748,25 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4860
4748
|
return this.store.createComputedCache('pageMaskCache', (shape) => {
|
|
4861
4749
|
if (isPageId(shape.parentId)) return undefined
|
|
4862
4750
|
|
|
4863
|
-
const
|
|
4864
|
-
|
|
4865
|
-
)
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
return []
|
|
4883
|
-
})
|
|
4751
|
+
const clipPaths: Vec[][] = []
|
|
4752
|
+
// Get all ancestors that can potentially clip this shape
|
|
4753
|
+
for (const ancestor of this.getShapeAncestors(shape.id)) {
|
|
4754
|
+
const util = this.getShapeUtil(ancestor)
|
|
4755
|
+
const clipPath = util.getClipPath?.(ancestor)
|
|
4756
|
+
if (!clipPath) continue
|
|
4757
|
+
if (util.shouldClipChild?.(shape) === false) continue
|
|
4758
|
+
const pageTransform = this.getShapePageTransform(ancestor.id)
|
|
4759
|
+
clipPaths.push(pageTransform.applyToPoints(clipPath))
|
|
4760
|
+
}
|
|
4761
|
+
if (clipPaths.length === 0) return undefined
|
|
4762
|
+
|
|
4763
|
+
const pageMask = clipPaths.reduce((acc, b) => {
|
|
4764
|
+
const intersection = intersectPolygonPolygon(acc, b)
|
|
4765
|
+
if (intersection) {
|
|
4766
|
+
return intersection.map(Vec.Cast)
|
|
4767
|
+
}
|
|
4768
|
+
return []
|
|
4769
|
+
})
|
|
4884
4770
|
|
|
4885
4771
|
return pageMask
|
|
4886
4772
|
})
|
|
@@ -5841,11 +5727,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5841
5727
|
return shapeIds
|
|
5842
5728
|
}
|
|
5843
5729
|
|
|
5844
|
-
/** @deprecated Use {@link Editor.getDraggingOverShape} instead */
|
|
5845
|
-
getDroppingOverShape(point: Vec, droppingShapes: TLShape[]): TLShape | undefined {
|
|
5846
|
-
return this.getDraggingOverShape(point, droppingShapes)
|
|
5847
|
-
}
|
|
5848
|
-
|
|
5849
5730
|
/**
|
|
5850
5731
|
* Get the shape that some shapes should be dropped on at a given point.
|
|
5851
5732
|
*
|
|
@@ -6333,7 +6214,17 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
6333
6214
|
|
|
6334
6215
|
this.createShapes(shapesToCreate)
|
|
6335
6216
|
this.createBindings(bindingsToCreate)
|
|
6336
|
-
|
|
6217
|
+
|
|
6218
|
+
this.setSelectedShapes(
|
|
6219
|
+
compact(
|
|
6220
|
+
ids.map((oldId) => {
|
|
6221
|
+
const newId = shapeIds.get(oldId)
|
|
6222
|
+
if (!newId) return null
|
|
6223
|
+
if (!this.getShape(newId)) return null
|
|
6224
|
+
return newId
|
|
6225
|
+
})
|
|
6226
|
+
)
|
|
6227
|
+
)
|
|
6337
6228
|
|
|
6338
6229
|
if (offset !== undefined) {
|
|
6339
6230
|
// If we've offset the duplicated shapes, check to see whether their new bounds is entirely
|
|
@@ -9451,13 +9342,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9451
9342
|
}
|
|
9452
9343
|
}
|
|
9453
9344
|
|
|
9454
|
-
/** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
|
|
9455
|
-
async getSvg(shapes: TLShapeId[] | TLShape[], opts: TLSvgExportOptions = {}) {
|
|
9456
|
-
const result = await this.getSvgElement(shapes, opts)
|
|
9457
|
-
if (!result) return undefined
|
|
9458
|
-
return result.svg
|
|
9459
|
-
}
|
|
9460
|
-
|
|
9461
9345
|
/**
|
|
9462
9346
|
* Get an exported image of the given shapes.
|
|
9463
9347
|
*
|
|
@@ -9509,6 +9393,24 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9509
9393
|
}
|
|
9510
9394
|
}
|
|
9511
9395
|
|
|
9396
|
+
/**
|
|
9397
|
+
* Get an exported image of the given shapes as a data URL.
|
|
9398
|
+
*
|
|
9399
|
+
* @param shapes - The shapes (or shape ids) to export.
|
|
9400
|
+
* @param opts - Options for the export.
|
|
9401
|
+
*
|
|
9402
|
+
* @returns A data URL of the image.
|
|
9403
|
+
* @public
|
|
9404
|
+
*/
|
|
9405
|
+
async toImageDataUrl(shapes: TLShapeId[] | TLShape[], opts: TLImageExportOptions = {}) {
|
|
9406
|
+
const { blob, width, height } = await this.toImage(shapes, opts)
|
|
9407
|
+
return {
|
|
9408
|
+
url: await FileHelpers.blobToDataUrl(blob),
|
|
9409
|
+
width,
|
|
9410
|
+
height,
|
|
9411
|
+
}
|
|
9412
|
+
}
|
|
9413
|
+
|
|
9512
9414
|
/* --------------------- Events --------------------- */
|
|
9513
9415
|
|
|
9514
9416
|
/**
|
|
@@ -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()
|