@tldraw/editor 3.15.1 → 3.16.0-canary.03ed24d72068
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 +181 -9
- package/dist-cjs/index.js +5 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/TldrawEditor.js +3 -1
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/MenuClickCapture.js +0 -5
- package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +4 -26
- package/dist-cjs/lib/components/Shape.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +1 -1
- package/dist-cjs/lib/components/default-components/DefaultScribble.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +9 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js +53 -0
- package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js.map +7 -0
- package/dist-cjs/lib/config/TLUserPreferences.js +8 -2
- package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +100 -58
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +8 -3
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- 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 +1 -2
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +24 -20
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditorComponents.js +2 -0
- package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
- package/dist-cjs/lib/hooks/useStateAttribute.js +35 -0
- package/dist-cjs/lib/hooks/useStateAttribute.js.map +7 -0
- package/dist-cjs/lib/license/Watermark.js +6 -6
- package/dist-cjs/lib/license/Watermark.js.map +1 -1
- package/dist-cjs/lib/options.js +1 -0
- package/dist-cjs/lib/options.js.map +2 -2
- package/dist-cjs/lib/utils/EditorAtom.js +45 -0
- package/dist-cjs/lib/utils/EditorAtom.js.map +7 -0
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +181 -9
- package/dist-esm/index.mjs +7 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/TldrawEditor.mjs +3 -1
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/MenuClickCapture.mjs +0 -5
- package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +4 -26
- package/dist-esm/lib/components/Shape.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +1 -1
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +9 -1
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs +23 -0
- package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs.map +7 -0
- package/dist-esm/lib/config/TLUserPreferences.mjs +8 -2
- package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +100 -58
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +8 -3
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +25 -21
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditorComponents.mjs +4 -0
- package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useStateAttribute.mjs +15 -0
- package/dist-esm/lib/hooks/useStateAttribute.mjs.map +7 -0
- package/dist-esm/lib/license/Watermark.mjs +6 -6
- package/dist-esm/lib/license/Watermark.mjs.map +1 -1
- package/dist-esm/lib/options.mjs +1 -0
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/utils/EditorAtom.mjs +25 -0
- package/dist-esm/lib/utils/EditorAtom.mjs.map +7 -0
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +297 -311
- package/package.json +7 -7
- package/src/index.ts +7 -0
- package/src/lib/TldrawEditor.tsx +7 -5
- package/src/lib/components/MenuClickCapture.tsx +0 -8
- package/src/lib/components/Shape.tsx +6 -21
- package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -1
- package/src/lib/components/default-components/DefaultScribble.tsx +1 -1
- package/src/lib/components/default-components/DefaultShapeIndicator.tsx +5 -1
- package/src/lib/components/default-components/DefaultShapeWrapper.tsx +35 -0
- package/src/lib/config/TLUserPreferences.ts +7 -0
- package/src/lib/editor/Editor.ts +130 -81
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +13 -0
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +5 -0
- package/src/lib/editor/shapes/ShapeUtil.ts +57 -0
- package/src/lib/editor/types/misc-types.ts +73 -1
- package/src/lib/exports/getSvgJsx.tsx +2 -2
- package/src/lib/hooks/useCanvasEvents.ts +39 -32
- package/src/lib/hooks/useEditorComponents.tsx +7 -1
- package/src/lib/hooks/useStateAttribute.ts +15 -0
- package/src/lib/license/Watermark.tsx +6 -6
- package/src/lib/options.ts +2 -0
- package/src/lib/utils/EditorAtom.ts +37 -0
- 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.
|
|
4
|
+
"version": "3.16.0-canary.03ed24d72068",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"@tiptap/core": "^2.9.1",
|
|
50
50
|
"@tiptap/pm": "^2.9.1",
|
|
51
51
|
"@tiptap/react": "^2.9.1",
|
|
52
|
-
"@tldraw/state": "3.
|
|
53
|
-
"@tldraw/state-react": "3.
|
|
54
|
-
"@tldraw/store": "3.
|
|
55
|
-
"@tldraw/tlschema": "3.
|
|
56
|
-
"@tldraw/utils": "3.
|
|
57
|
-
"@tldraw/validate": "3.
|
|
52
|
+
"@tldraw/state": "3.16.0-canary.03ed24d72068",
|
|
53
|
+
"@tldraw/state-react": "3.16.0-canary.03ed24d72068",
|
|
54
|
+
"@tldraw/store": "3.16.0-canary.03ed24d72068",
|
|
55
|
+
"@tldraw/tlschema": "3.16.0-canary.03ed24d72068",
|
|
56
|
+
"@tldraw/utils": "3.16.0-canary.03ed24d72068",
|
|
57
|
+
"@tldraw/validate": "3.16.0-canary.03ed24d72068",
|
|
58
58
|
"@types/core-js": "^2.5.8",
|
|
59
59
|
"@use-gesture/react": "^10.3.1",
|
|
60
60
|
"classnames": "^2.5.1",
|
package/src/index.ts
CHANGED
|
@@ -67,6 +67,10 @@ export {
|
|
|
67
67
|
DefaultShapeIndicators,
|
|
68
68
|
type TLShapeIndicatorsProps,
|
|
69
69
|
} from './lib/components/default-components/DefaultShapeIndicators'
|
|
70
|
+
export {
|
|
71
|
+
DefaultShapeWrapper,
|
|
72
|
+
type TLShapeWrapperProps,
|
|
73
|
+
} from './lib/components/default-components/DefaultShapeWrapper'
|
|
70
74
|
export {
|
|
71
75
|
DefaultSnapIndicator,
|
|
72
76
|
type TLSnapIndicatorProps,
|
|
@@ -261,9 +265,11 @@ export {
|
|
|
261
265
|
type TLCameraMoveOptions,
|
|
262
266
|
type TLCameraOptions,
|
|
263
267
|
type TLExportType,
|
|
268
|
+
type TLGetShapeAtPointOptions,
|
|
264
269
|
type TLImageExportOptions,
|
|
265
270
|
type TLSvgExportOptions,
|
|
266
271
|
type TLSvgOptions,
|
|
272
|
+
type TLUpdatePointerOptions,
|
|
267
273
|
} from './lib/editor/types/misc-types'
|
|
268
274
|
export {
|
|
269
275
|
type TLAdjacentDirection,
|
|
@@ -445,6 +451,7 @@ export {
|
|
|
445
451
|
setPointerCapture,
|
|
446
452
|
stopEventPropagation,
|
|
447
453
|
} from './lib/utils/dom'
|
|
454
|
+
export { EditorAtom } from './lib/utils/EditorAtom'
|
|
448
455
|
export { getIncrementedName } from './lib/utils/getIncrementedName'
|
|
449
456
|
export { getPointerInfo } from './lib/utils/getPointerInfo'
|
|
450
457
|
export { getSvgPathFromPoints } from './lib/utils/getSvgPathFromPoints'
|
package/src/lib/TldrawEditor.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { MigrationSequence, Store } from '@tldraw/store'
|
|
2
2
|
import { TLShape, TLStore, TLStoreSnapshot } from '@tldraw/tlschema'
|
|
3
|
-
import {
|
|
3
|
+
import { annotateError, Required } from '@tldraw/utils'
|
|
4
4
|
import React, {
|
|
5
|
-
ReactNode,
|
|
6
5
|
memo,
|
|
6
|
+
ReactNode,
|
|
7
7
|
useCallback,
|
|
8
8
|
useEffect,
|
|
9
9
|
useLayoutEffect,
|
|
@@ -15,13 +15,13 @@ import React, {
|
|
|
15
15
|
|
|
16
16
|
import classNames from 'classnames'
|
|
17
17
|
import { version } from '../version'
|
|
18
|
-
import { OptionalErrorBoundary } from './components/ErrorBoundary'
|
|
19
18
|
import { DefaultErrorFallback } from './components/default-components/DefaultErrorFallback'
|
|
20
|
-
import {
|
|
19
|
+
import { OptionalErrorBoundary } from './components/ErrorBoundary'
|
|
21
20
|
import { TLStoreBaseOptions } from './config/createTLStore'
|
|
22
|
-
import {
|
|
21
|
+
import { createTLUser, TLUser } from './config/createTLUser'
|
|
23
22
|
import { TLAnyBindingUtilConstructor } from './config/defaultBindings'
|
|
24
23
|
import { TLAnyShapeUtilConstructor } from './config/defaultShapes'
|
|
24
|
+
import { TLEditorSnapshot } from './config/TLEditorSnapshot'
|
|
25
25
|
import { Editor } from './editor/Editor'
|
|
26
26
|
import { TLStateNodeConstructor } from './editor/tools/StateNode'
|
|
27
27
|
import { TLCameraOptions } from './editor/types/misc-types'
|
|
@@ -39,6 +39,7 @@ import { useForceUpdate } from './hooks/useForceUpdate'
|
|
|
39
39
|
import { useShallowObjectIdentity } from './hooks/useIdentity'
|
|
40
40
|
import { useLocalStore } from './hooks/useLocalStore'
|
|
41
41
|
import { useRefState } from './hooks/useRefState'
|
|
42
|
+
import { useStateAttribute } from './hooks/useStateAttribute'
|
|
42
43
|
import { useZoomCss } from './hooks/useZoomCss'
|
|
43
44
|
import { LicenseProvider } from './license/LicenseProvider'
|
|
44
45
|
import { Watermark } from './license/Watermark'
|
|
@@ -646,6 +647,7 @@ function Layout({ children, onMount }: { children: ReactNode; onMount?: TLOnMoun
|
|
|
646
647
|
useCursor()
|
|
647
648
|
useDarkMode()
|
|
648
649
|
useForceUpdate()
|
|
650
|
+
useStateAttribute()
|
|
649
651
|
useOnMount((editor) => {
|
|
650
652
|
const teardownStore = editor.store.props.onMount(editor)
|
|
651
653
|
const teardownCallback = onMount?.(editor)
|
|
@@ -50,12 +50,6 @@ export function MenuClickCapture() {
|
|
|
50
50
|
// Do nothing unless we're pointing
|
|
51
51
|
if (!rPointerState.current.isDown) return
|
|
52
52
|
|
|
53
|
-
// If we're already dragging, pass on the event as it is
|
|
54
|
-
if (rPointerState.current.isDragging) {
|
|
55
|
-
canvasEvents.onPointerMove?.(e)
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
53
|
if (
|
|
60
54
|
// We're pointing, but are we dragging?
|
|
61
55
|
Vec.Dist2(rPointerState.current.start, new Vec(e.clientX, e.clientY)) >
|
|
@@ -75,8 +69,6 @@ export function MenuClickCapture() {
|
|
|
75
69
|
clientY: y,
|
|
76
70
|
button: 0,
|
|
77
71
|
})
|
|
78
|
-
// call the pointer move with the current pointer position
|
|
79
|
-
canvasEvents.onPointerMove?.(e)
|
|
80
72
|
}
|
|
81
73
|
},
|
|
82
74
|
[canvasEvents, editor]
|
|
@@ -40,7 +40,7 @@ export const Shape = memo(function Shape({
|
|
|
40
40
|
}) {
|
|
41
41
|
const editor = useEditor()
|
|
42
42
|
|
|
43
|
-
const { ShapeErrorFallback } = useEditorComponents()
|
|
43
|
+
const { ShapeErrorFallback, ShapeWrapper } = useEditorComponents()
|
|
44
44
|
|
|
45
45
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
46
46
|
const bgContainerRef = useRef<HTMLDivElement>(null)
|
|
@@ -145,37 +145,22 @@ export const Shape = memo(function Shape({
|
|
|
145
145
|
[editor]
|
|
146
146
|
)
|
|
147
147
|
|
|
148
|
-
if (!shape) return null
|
|
149
|
-
|
|
150
|
-
const isFilledShape = 'fill' in shape.props && shape.props.fill !== 'none'
|
|
148
|
+
if (!shape || !ShapeWrapper) return null
|
|
151
149
|
|
|
152
150
|
return (
|
|
153
151
|
<>
|
|
154
152
|
{util.backgroundComponent && (
|
|
155
|
-
<
|
|
156
|
-
ref={bgContainerRef}
|
|
157
|
-
className="tl-shape tl-shape-background"
|
|
158
|
-
data-shape-type={shape.type}
|
|
159
|
-
data-shape-id={shape.id}
|
|
160
|
-
draggable={false}
|
|
161
|
-
>
|
|
153
|
+
<ShapeWrapper ref={bgContainerRef} shape={shape} isBackground={true}>
|
|
162
154
|
<OptionalErrorBoundary fallback={ShapeErrorFallback} onError={annotateError}>
|
|
163
155
|
<InnerShapeBackground shape={shape} util={util} />
|
|
164
156
|
</OptionalErrorBoundary>
|
|
165
|
-
</
|
|
157
|
+
</ShapeWrapper>
|
|
166
158
|
)}
|
|
167
|
-
<
|
|
168
|
-
ref={containerRef}
|
|
169
|
-
className="tl-shape"
|
|
170
|
-
data-shape-type={shape.type}
|
|
171
|
-
data-shape-is-filled={isFilledShape}
|
|
172
|
-
data-shape-id={shape.id}
|
|
173
|
-
draggable={false}
|
|
174
|
-
>
|
|
159
|
+
<ShapeWrapper ref={containerRef} shape={shape} isBackground={false}>
|
|
175
160
|
<OptionalErrorBoundary fallback={ShapeErrorFallback as any} onError={annotateError}>
|
|
176
161
|
<InnerShape shape={shape} util={util} />
|
|
177
162
|
</OptionalErrorBoundary>
|
|
178
|
-
</
|
|
163
|
+
</ShapeWrapper>
|
|
179
164
|
</>
|
|
180
165
|
)
|
|
181
166
|
})
|
|
@@ -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>
|
|
@@ -21,7 +21,7 @@ export function DefaultScribble({ scribble, zoom, color, opacity, className }: T
|
|
|
21
21
|
<path
|
|
22
22
|
className="tl-scribble"
|
|
23
23
|
d={getSvgPathFromPoints(scribble.points, false)}
|
|
24
|
-
stroke={color ?? `var(--color-${scribble.color})`}
|
|
24
|
+
stroke={color ?? `var(--tl-color-${scribble.color})`}
|
|
25
25
|
fill="none"
|
|
26
26
|
strokeWidth={8 / zoom}
|
|
27
27
|
opacity={opacity ?? scribble.opacity}
|
|
@@ -87,7 +87,11 @@ export const DefaultShapeIndicator = memo(function DefaultShapeIndicator({
|
|
|
87
87
|
|
|
88
88
|
return (
|
|
89
89
|
<svg ref={rIndicator} className={classNames('tl-overlays__item', className)} aria-hidden="true">
|
|
90
|
-
<g
|
|
90
|
+
<g
|
|
91
|
+
className="tl-shape-indicator"
|
|
92
|
+
stroke={color ?? 'var(--tl-color-selected)'}
|
|
93
|
+
opacity={opacity}
|
|
94
|
+
>
|
|
91
95
|
<InnerIndicator editor={editor} id={shapeId} />
|
|
92
96
|
</g>
|
|
93
97
|
</svg>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { TLShape } from '@tldraw/tlschema'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
import { forwardRef, ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
/** @public */
|
|
6
|
+
export interface TLShapeWrapperProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
/** The shape being rendered. */
|
|
8
|
+
shape: TLShape
|
|
9
|
+
/** Whether this is the shapes regular, or background component. */
|
|
10
|
+
isBackground: boolean
|
|
11
|
+
/** The shape's rendered component. */
|
|
12
|
+
children: ReactNode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** @public @react */
|
|
16
|
+
export const DefaultShapeWrapper = forwardRef(function DefaultShapeWrapper(
|
|
17
|
+
{ children, shape, isBackground, ...props }: TLShapeWrapperProps,
|
|
18
|
+
ref: React.Ref<HTMLDivElement>
|
|
19
|
+
) {
|
|
20
|
+
const isFilledShape = 'fill' in shape.props && shape.props.fill !== 'none'
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
ref={ref}
|
|
25
|
+
data-shape-type={shape.type}
|
|
26
|
+
data-shape-is-filled={isBackground ? undefined : isFilledShape}
|
|
27
|
+
data-shape-id={shape.id}
|
|
28
|
+
draggable={false}
|
|
29
|
+
{...props}
|
|
30
|
+
className={classNames('tl-shape', isBackground && 'tl-shape-background', props.className)}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
})
|
|
@@ -24,6 +24,7 @@ export interface TLUserPreferences {
|
|
|
24
24
|
isWrapMode?: boolean | null
|
|
25
25
|
isDynamicSizeMode?: boolean | null
|
|
26
26
|
isPasteAtCursorMode?: boolean | null
|
|
27
|
+
showUiLabels?: boolean | null
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
interface UserDataSnapshot {
|
|
@@ -52,6 +53,7 @@ export const userTypeValidator: T.Validator<TLUserPreferences> = T.object<TLUser
|
|
|
52
53
|
isWrapMode: T.boolean.nullable().optional(),
|
|
53
54
|
isDynamicSizeMode: T.boolean.nullable().optional(),
|
|
54
55
|
isPasteAtCursorMode: T.boolean.nullable().optional(),
|
|
56
|
+
showUiLabels: T.boolean.nullable().optional(),
|
|
55
57
|
})
|
|
56
58
|
|
|
57
59
|
const Versions = {
|
|
@@ -64,6 +66,7 @@ const Versions = {
|
|
|
64
66
|
AllowSystemColorScheme: 7,
|
|
65
67
|
AddPasteAtCursor: 8,
|
|
66
68
|
AddKeyboardShortcuts: 9,
|
|
69
|
+
AddShowUiLabels: 10,
|
|
67
70
|
} as const
|
|
68
71
|
|
|
69
72
|
const CURRENT_VERSION = Math.max(...Object.values(Versions))
|
|
@@ -102,6 +105,9 @@ function migrateSnapshot(data: { version: number; user: any }) {
|
|
|
102
105
|
if (data.version < Versions.AddKeyboardShortcuts) {
|
|
103
106
|
data.user.areKeyboardShortcutsEnabled = true
|
|
104
107
|
}
|
|
108
|
+
if (data.version < Versions.AddShowUiLabels) {
|
|
109
|
+
data.user.showUiLabels = false
|
|
110
|
+
}
|
|
105
111
|
|
|
106
112
|
// finally
|
|
107
113
|
data.version = CURRENT_VERSION
|
|
@@ -150,6 +156,7 @@ export const defaultUserPreferences = Object.freeze({
|
|
|
150
156
|
isWrapMode: false,
|
|
151
157
|
isDynamicSizeMode: false,
|
|
152
158
|
isPasteAtCursorMode: false,
|
|
159
|
+
showUiLabels: false,
|
|
153
160
|
colorScheme: 'light',
|
|
154
161
|
}) satisfies Readonly<Omit<TLUserPreferences, 'id'>>
|
|
155
162
|
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -176,8 +176,10 @@ import {
|
|
|
176
176
|
RequiredKeys,
|
|
177
177
|
TLCameraMoveOptions,
|
|
178
178
|
TLCameraOptions,
|
|
179
|
+
TLGetShapeAtPointOptions,
|
|
179
180
|
TLImageExportOptions,
|
|
180
181
|
TLSvgExportOptions,
|
|
182
|
+
TLUpdatePointerOptions,
|
|
181
183
|
} from './types/misc-types'
|
|
182
184
|
import { TLAdjacentDirection, TLResizeHandle } from './types/selection-types'
|
|
183
185
|
|
|
@@ -3072,7 +3074,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3072
3074
|
// Dispatch a new pointer move because the pointer's page will have changed
|
|
3073
3075
|
// (its screen position will compute to a new page position given the new camera position)
|
|
3074
3076
|
const { currentScreenPoint, currentPagePoint } = this.inputs
|
|
3075
|
-
const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
|
|
3076
3077
|
|
|
3077
3078
|
// compare the next page point (derived from the current camera) to the current page point
|
|
3078
3079
|
if (
|
|
@@ -3080,27 +3081,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
3080
3081
|
currentScreenPoint.y / z - y !== currentPagePoint.y
|
|
3081
3082
|
) {
|
|
3082
3083
|
// If it's changed, dispatch a pointer event
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
target: 'canvas',
|
|
3086
|
-
name: 'pointer_move',
|
|
3087
|
-
// weird but true: we need to put the screen point back into client space
|
|
3088
|
-
point: Vec.AddXY(currentScreenPoint, screenBounds.x, screenBounds.y),
|
|
3084
|
+
this.updatePointer({
|
|
3085
|
+
immediate: opts?.immediate,
|
|
3089
3086
|
pointerId: INTERNAL_POINTER_IDS.CAMERA_MOVE,
|
|
3090
|
-
|
|
3091
|
-
altKey: this.inputs.altKey,
|
|
3092
|
-
shiftKey: this.inputs.shiftKey,
|
|
3093
|
-
metaKey: this.inputs.metaKey,
|
|
3094
|
-
accelKey: isAccelKey(this.inputs),
|
|
3095
|
-
button: 0,
|
|
3096
|
-
isPen: this.getInstanceState().isPenMode ?? false,
|
|
3097
|
-
}
|
|
3098
|
-
|
|
3099
|
-
if (opts?.immediate) {
|
|
3100
|
-
this._flushEventForTick(event)
|
|
3101
|
-
} else {
|
|
3102
|
-
this.dispatch(event)
|
|
3103
|
-
}
|
|
3087
|
+
})
|
|
3104
3088
|
}
|
|
3105
3089
|
|
|
3106
3090
|
this._tickCameraState()
|
|
@@ -4421,21 +4405,28 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4421
4405
|
*/
|
|
4422
4406
|
deletePage(page: TLPageId | TLPage): this {
|
|
4423
4407
|
const id = typeof page === 'string' ? page : page.id
|
|
4424
|
-
this.run(
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4408
|
+
this.run(
|
|
4409
|
+
() => {
|
|
4410
|
+
if (this.getIsReadonly()) return
|
|
4411
|
+
const pages = this.getPages()
|
|
4412
|
+
if (pages.length === 1) return
|
|
4428
4413
|
|
|
4429
|
-
|
|
4430
|
-
|
|
4414
|
+
const deletedPage = this.getPage(id)
|
|
4415
|
+
if (!deletedPage) return
|
|
4431
4416
|
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4417
|
+
if (id === this.getCurrentPageId()) {
|
|
4418
|
+
const index = pages.findIndex((page) => page.id === id)
|
|
4419
|
+
const next = pages[index - 1] ?? pages[index + 1]
|
|
4420
|
+
this.setCurrentPage(next.id)
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4423
|
+
const shapes = this.getSortedChildIdsForParent(deletedPage.id)
|
|
4424
|
+
this.deleteShapes(shapes)
|
|
4425
|
+
|
|
4426
|
+
this.store.remove([deletedPage.id])
|
|
4427
|
+
},
|
|
4428
|
+
{ ignoreShapeLock: true }
|
|
4429
|
+
)
|
|
4439
4430
|
return this
|
|
4440
4431
|
}
|
|
4441
4432
|
|
|
@@ -5164,20 +5155,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5164
5155
|
*
|
|
5165
5156
|
* @returns The shape at the given point, or undefined if there is no shape at the point.
|
|
5166
5157
|
*/
|
|
5167
|
-
getShapeAtPoint(
|
|
5168
|
-
point: VecLike,
|
|
5169
|
-
opts = {} as {
|
|
5170
|
-
renderingOnly?: boolean
|
|
5171
|
-
margin?: number
|
|
5172
|
-
hitInside?: boolean
|
|
5173
|
-
hitLocked?: boolean
|
|
5174
|
-
// TODO: we probably need to rename this, we don't quite _always_
|
|
5175
|
-
// respect this esp. in the part below that does "Check labels first"
|
|
5176
|
-
hitLabels?: boolean
|
|
5177
|
-
hitFrameInside?: boolean
|
|
5178
|
-
filter?(shape: TLShape): boolean
|
|
5179
|
-
}
|
|
5180
|
-
): TLShape | undefined {
|
|
5158
|
+
getShapeAtPoint(point: VecLike, opts: TLGetShapeAtPointOptions = {}): TLShape | undefined {
|
|
5181
5159
|
const zoomLevel = this.getZoomLevel()
|
|
5182
5160
|
const viewportPageBounds = this.getViewportPageBounds()
|
|
5183
5161
|
const {
|
|
@@ -5189,6 +5167,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5189
5167
|
hitFrameInside = false,
|
|
5190
5168
|
} = opts
|
|
5191
5169
|
|
|
5170
|
+
const [innerMargin, outerMargin] = Array.isArray(margin) ? margin : [margin, margin]
|
|
5171
|
+
|
|
5192
5172
|
let inHollowSmallestArea = Infinity
|
|
5193
5173
|
let inHollowSmallestAreaHit: TLShape | null = null
|
|
5194
5174
|
|
|
@@ -5208,7 +5188,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5208
5188
|
return false
|
|
5209
5189
|
const pageMask = this.getShapeMask(shape)
|
|
5210
5190
|
if (pageMask && !pointInPolygon(point, pageMask)) return false
|
|
5211
|
-
if (filter
|
|
5191
|
+
if (filter && !filter(shape)) return false
|
|
5212
5192
|
return true
|
|
5213
5193
|
})
|
|
5214
5194
|
|
|
@@ -5222,8 +5202,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5222
5202
|
// Check labels first
|
|
5223
5203
|
if (
|
|
5224
5204
|
this.isShapeOfType<TLFrameShape>(shape, 'frame') ||
|
|
5225
|
-
(this.isShapeOfType<TLArrowShape>(shape, 'arrow') && shape.props.text.trim()) ||
|
|
5226
5205
|
((this.isShapeOfType<TLNoteShape>(shape, 'note') ||
|
|
5206
|
+
this.isShapeOfType<TLArrowShape>(shape, 'arrow') ||
|
|
5227
5207
|
(this.isShapeOfType<TLGeoShape>(shape, 'geo') && shape.props.fill === 'none')) &&
|
|
5228
5208
|
this.getShapeUtil(shape).getText(shape)?.trim())
|
|
5229
5209
|
) {
|
|
@@ -5234,13 +5214,18 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5234
5214
|
}
|
|
5235
5215
|
}
|
|
5236
5216
|
|
|
5237
|
-
if (this.isShapeOfType(shape, 'frame')) {
|
|
5217
|
+
if (this.isShapeOfType<TLFrameShape>(shape, 'frame')) {
|
|
5238
5218
|
// On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
|
|
5239
5219
|
// this prevents clicks from passing through the body of a frame to shapes behind it.
|
|
5240
5220
|
|
|
5241
5221
|
// If the hit is within the frame's outer margin, then select the frame
|
|
5242
|
-
const distance = geometry.distanceToPoint(pointInShapeSpace,
|
|
5243
|
-
if (
|
|
5222
|
+
const distance = geometry.distanceToPoint(pointInShapeSpace, hitFrameInside)
|
|
5223
|
+
if (
|
|
5224
|
+
hitFrameInside
|
|
5225
|
+
? (distance > 0 && distance <= outerMargin) ||
|
|
5226
|
+
(distance <= 0 && distance > -innerMargin)
|
|
5227
|
+
: distance > 0 && distance <= outerMargin
|
|
5228
|
+
) {
|
|
5244
5229
|
return inMarginClosestToEdgeHit || shape
|
|
5245
5230
|
}
|
|
5246
5231
|
|
|
@@ -5279,11 +5264,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5279
5264
|
// If the margin is zero and the geometry has a very small width or height,
|
|
5280
5265
|
// then check the actual distance. This is to prevent a bug where straight
|
|
5281
5266
|
// lines would never pass the broad phase (point-in-bounds) check.
|
|
5282
|
-
if (
|
|
5267
|
+
if (outerMargin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
|
|
5283
5268
|
distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
|
|
5284
5269
|
} else {
|
|
5285
5270
|
// Broad phase
|
|
5286
|
-
if (geometry.bounds.containsPoint(pointInShapeSpace,
|
|
5271
|
+
if (geometry.bounds.containsPoint(pointInShapeSpace, outerMargin)) {
|
|
5287
5272
|
// Narrow phase (actual distance)
|
|
5288
5273
|
distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
|
|
5289
5274
|
} else {
|
|
@@ -5298,7 +5283,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5298
5283
|
// the shape or negative if inside of the shape. If the distance
|
|
5299
5284
|
// is greater than the margin, then it's a miss. Otherwise...
|
|
5300
5285
|
|
|
5301
|
-
|
|
5286
|
+
// Are we close to the shape's edge?
|
|
5287
|
+
if (distance <= outerMargin || (hitInside && distance <= 0 && distance > -innerMargin)) {
|
|
5302
5288
|
if (geometry.isFilled || (isGroup && geometry.children[0].isFilled)) {
|
|
5303
5289
|
// If the shape is filled, then it's a hit. Remember, we're
|
|
5304
5290
|
// starting from the TOP-MOST shape in z-index order, so any
|
|
@@ -5308,11 +5294,21 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5308
5294
|
// If the shape is bigger than the viewport, then skip it.
|
|
5309
5295
|
if (this.getShapePageBounds(shape)!.contains(viewportPageBounds)) continue
|
|
5310
5296
|
|
|
5311
|
-
//
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5297
|
+
// If we're close to the edge of the shape, and if it's the closest edge among
|
|
5298
|
+
// all the edges that we've gotten close to so far, then we will want to hit the
|
|
5299
|
+
// shape unless we hit something else or closer in later iterations.
|
|
5300
|
+
if (
|
|
5301
|
+
hitInside
|
|
5302
|
+
? // On hitInside, the distance will be negative for hits inside
|
|
5303
|
+
// If the distance is positive, check against the outer margin
|
|
5304
|
+
(distance > 0 && distance <= outerMargin) ||
|
|
5305
|
+
// If the distance is negative, check against the inner margin
|
|
5306
|
+
(distance <= 0 && distance > -innerMargin)
|
|
5307
|
+
: // If hitInside is false, then sadly _we do not know_ whether the
|
|
5308
|
+
// point is inside or outside of the shape, so we check against
|
|
5309
|
+
// the max of the two margins
|
|
5310
|
+
Math.abs(distance) <= Math.max(innerMargin, outerMargin)
|
|
5311
|
+
) {
|
|
5316
5312
|
if (Math.abs(distance) < inMarginClosestToEdgeDistance) {
|
|
5317
5313
|
inMarginClosestToEdgeDistance = Math.abs(distance)
|
|
5318
5314
|
inMarginClosestToEdgeHit = shape
|
|
@@ -5334,6 +5330,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
5334
5330
|
} else {
|
|
5335
5331
|
// For open shapes (e.g. lines or draw shapes) always use the margin.
|
|
5336
5332
|
// If the distance is less than the margin, return the shape as the hit.
|
|
5333
|
+
// Use the editor's configurable hit test margin.
|
|
5337
5334
|
if (distance < this.options.hitTestMargin / zoomLevel) {
|
|
5338
5335
|
return shape
|
|
5339
5336
|
}
|
|
@@ -7390,7 +7387,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7390
7387
|
if (
|
|
7391
7388
|
!this.getShapeUtil(shape).canBeLaidOut?.(shape, {
|
|
7392
7389
|
type: 'stretch',
|
|
7393
|
-
shapes: shapesToStretchFirstPass,
|
|
7394
7390
|
})
|
|
7395
7391
|
) {
|
|
7396
7392
|
continue
|
|
@@ -7861,25 +7857,32 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
7861
7857
|
) {
|
|
7862
7858
|
let parentId: TLParentId = this.getFocusedGroupId()
|
|
7863
7859
|
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7860
|
+
const isPositioned = partial.x !== undefined && partial.y !== undefined
|
|
7861
|
+
|
|
7862
|
+
// If the shape has been explicitly positioned, we'll try to find a parent at
|
|
7863
|
+
// that position. If not, we'll assume the user isn't deliberately placing the
|
|
7864
|
+
// shape and the positioning will be handled later by another system.
|
|
7865
|
+
if (isPositioned) {
|
|
7866
|
+
for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
|
|
7867
|
+
const parent = currentPageShapesSorted[i]
|
|
7868
|
+
const util = this.getShapeUtil(parent)
|
|
7869
|
+
if (
|
|
7870
|
+
util.canReceiveNewChildrenOfType(parent, partial.type) &&
|
|
7871
|
+
!this.isShapeHidden(parent) &&
|
|
7872
|
+
this.isPointInShape(
|
|
7873
|
+
parent,
|
|
7874
|
+
// If no parent is provided, then we can treat the
|
|
7875
|
+
// shape's provided x/y as being in the page's space.
|
|
7876
|
+
{ x: partial.x ?? 0, y: partial.y ?? 0 },
|
|
7877
|
+
{
|
|
7878
|
+
margin: 0,
|
|
7879
|
+
hitInside: true,
|
|
7880
|
+
}
|
|
7881
|
+
)
|
|
7882
|
+
) {
|
|
7883
|
+
parentId = parent.id
|
|
7884
|
+
break
|
|
7885
|
+
}
|
|
7883
7886
|
}
|
|
7884
7887
|
}
|
|
7885
7888
|
|
|
@@ -9673,6 +9676,52 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
9673
9676
|
return this
|
|
9674
9677
|
}
|
|
9675
9678
|
|
|
9679
|
+
/**
|
|
9680
|
+
* Dispatch a pointer move event in the current position of the pointer. This is useful when
|
|
9681
|
+
* external circumstances have changed (e.g. the camera moved or a shape was moved) and you want
|
|
9682
|
+
* the current interaction to respond to that change.
|
|
9683
|
+
*
|
|
9684
|
+
* @example
|
|
9685
|
+
* ```ts
|
|
9686
|
+
* editor.updatePointer()
|
|
9687
|
+
* ```
|
|
9688
|
+
*
|
|
9689
|
+
* @param options - The options for updating the pointer.
|
|
9690
|
+
* @returns The editor instance.
|
|
9691
|
+
* @public
|
|
9692
|
+
*/
|
|
9693
|
+
updatePointer(options?: TLUpdatePointerOptions): this {
|
|
9694
|
+
const event: TLPointerEventInfo = {
|
|
9695
|
+
type: 'pointer',
|
|
9696
|
+
target: 'canvas',
|
|
9697
|
+
name: 'pointer_move',
|
|
9698
|
+
point:
|
|
9699
|
+
options?.point ??
|
|
9700
|
+
// weird but true: what `inputs` calls screen-space is actually viewport space. so
|
|
9701
|
+
// we need to convert back into true screen space first. we should fix this...
|
|
9702
|
+
Vec.Add(
|
|
9703
|
+
this.inputs.currentScreenPoint,
|
|
9704
|
+
this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
|
|
9705
|
+
),
|
|
9706
|
+
pointerId: options?.pointerId ?? 0,
|
|
9707
|
+
button: options?.button ?? 0,
|
|
9708
|
+
isPen: options?.isPen ?? this.inputs.isPen,
|
|
9709
|
+
shiftKey: options?.shiftKey ?? this.inputs.shiftKey,
|
|
9710
|
+
altKey: options?.altKey ?? this.inputs.altKey,
|
|
9711
|
+
ctrlKey: options?.ctrlKey ?? this.inputs.ctrlKey,
|
|
9712
|
+
metaKey: options?.metaKey ?? this.inputs.metaKey,
|
|
9713
|
+
accelKey: options?.accelKey ?? isAccelKey(this.inputs),
|
|
9714
|
+
}
|
|
9715
|
+
|
|
9716
|
+
if (options?.immediate) {
|
|
9717
|
+
this._flushEventForTick(event)
|
|
9718
|
+
} else {
|
|
9719
|
+
this.dispatch(event)
|
|
9720
|
+
}
|
|
9721
|
+
|
|
9722
|
+
return this
|
|
9723
|
+
}
|
|
9724
|
+
|
|
9676
9725
|
/**
|
|
9677
9726
|
* Puts the editor into focused mode.
|
|
9678
9727
|
*
|
|
@@ -25,6 +25,7 @@ describe('UserPreferencesManager', () => {
|
|
|
25
25
|
locale: 'en',
|
|
26
26
|
animationSpeed: 1,
|
|
27
27
|
areKeyboardShortcutsEnabled: true,
|
|
28
|
+
showUiLabels: false,
|
|
28
29
|
edgeScrollSpeed: 1,
|
|
29
30
|
colorScheme: 'light',
|
|
30
31
|
isSnapMode: false,
|
|
@@ -231,6 +232,7 @@ describe('UserPreferencesManager', () => {
|
|
|
231
232
|
color: mockUserPreferences.color,
|
|
232
233
|
animationSpeed: mockUserPreferences.animationSpeed,
|
|
233
234
|
areKeyboardShortcutsEnabled: mockUserPreferences.areKeyboardShortcutsEnabled,
|
|
235
|
+
showUiLabels: mockUserPreferences.showUiLabels,
|
|
234
236
|
isSnapMode: mockUserPreferences.isSnapMode,
|
|
235
237
|
colorScheme: mockUserPreferences.colorScheme,
|
|
236
238
|
isDarkMode: false, // light mode
|
|
@@ -379,6 +381,17 @@ describe('UserPreferencesManager', () => {
|
|
|
379
381
|
})
|
|
380
382
|
})
|
|
381
383
|
|
|
384
|
+
describe('getShowUiLabels', () => {
|
|
385
|
+
it('should return user show ui labels setting', () => {
|
|
386
|
+
expect(userPreferencesManager.getShowUiLabels()).toBe(mockUserPreferences.showUiLabels)
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('should return default show ui labels when null', () => {
|
|
390
|
+
userPreferencesAtom.set({ ...mockUserPreferences, showUiLabels: null })
|
|
391
|
+
expect(userPreferencesManager.getShowUiLabels()).toBe(defaultUserPreferences.showUiLabels)
|
|
392
|
+
})
|
|
393
|
+
})
|
|
394
|
+
|
|
382
395
|
describe('getEdgeScrollSpeed', () => {
|
|
383
396
|
it('should return user edge scroll speed', () => {
|
|
384
397
|
expect(userPreferencesManager.getEdgeScrollSpeed()).toBe(
|
|
@@ -49,6 +49,7 @@ export class UserPreferencesManager {
|
|
|
49
49
|
isDarkMode: this.getIsDarkMode(),
|
|
50
50
|
isWrapMode: this.getIsWrapMode(),
|
|
51
51
|
isDynamicResizeMode: this.getIsDynamicResizeMode(),
|
|
52
|
+
showUiLabels: this.getShowUiLabels(),
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -119,4 +120,8 @@ export class UserPreferencesManager {
|
|
|
119
120
|
defaultUserPreferences.isPasteAtCursorMode
|
|
120
121
|
)
|
|
121
122
|
}
|
|
123
|
+
|
|
124
|
+
@computed getShowUiLabels() {
|
|
125
|
+
return this.user.userPreferences.get().showUiLabels ?? defaultUserPreferences.showUiLabels
|
|
126
|
+
}
|
|
122
127
|
}
|