@tldraw/editor 3.15.0-canary.14c6b9d1aa1e → 3.15.0-canary.175d131e5195
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 +64 -3
- package/dist-cjs/index.js +3 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/SVGContainer.js +1 -1
- package/dist-cjs/lib/components/SVGContainer.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultBrush.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCanvas.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 +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCursor.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultGrid.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultGrid.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultHandles.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultHandles.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultSpinner.js +27 -15
- package/dist-cjs/lib/components/default-components/DefaultSpinner.js.map +3 -3
- package/dist-cjs/lib/editor/Editor.js +22 -19
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js +20 -1
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditorComponents.js.map +1 -1
- package/dist-cjs/lib/license/Watermark.js +2 -2
- package/dist-cjs/lib/license/Watermark.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +3 -1
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/geometry-constants.js +2 -2
- package/dist-cjs/lib/primitives/geometry/geometry-constants.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +64 -3
- package/dist-esm/index.mjs +3 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/SVGContainer.mjs +1 -1
- package/dist-esm/lib/components/SVGContainer.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCanvas.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 +2 -2
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultGrid.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultGrid.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultSpinner.mjs +17 -15
- package/dist-esm/lib/components/default-components/DefaultSpinner.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +22 -19
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs +20 -1
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditorComponents.mjs.map +1 -1
- package/dist-esm/lib/license/Watermark.mjs +2 -2
- package/dist-esm/lib/license/Watermark.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +3 -1
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/geometry-constants.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/geometry-constants.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +17 -4
- package/package.json +9 -8
- package/src/index.ts +1 -0
- package/src/lib/components/SVGContainer.tsx +1 -1
- package/src/lib/components/default-components/DefaultBrush.tsx +1 -1
- package/src/lib/components/default-components/DefaultCanvas.tsx +1 -1
- package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -1
- package/src/lib/components/default-components/DefaultCursor.tsx +1 -1
- package/src/lib/components/default-components/DefaultGrid.tsx +1 -1
- package/src/lib/components/default-components/DefaultHandles.tsx +5 -1
- package/src/lib/components/default-components/DefaultShapeIndicator.tsx +1 -1
- package/src/lib/components/default-components/DefaultSnapIndictor.tsx +1 -1
- package/src/lib/components/default-components/DefaultSpinner.tsx +12 -12
- package/src/lib/editor/Editor.ts +26 -18
- package/src/lib/editor/shapes/ShapeUtil.ts +57 -0
- package/src/lib/editor/tools/StateNode.test.ts +285 -0
- package/src/lib/editor/tools/StateNode.ts +27 -1
- package/src/lib/hooks/useEditorComponents.tsx +1 -1
- package/src/lib/license/Watermark.tsx +2 -2
- package/src/lib/primitives/geometry/Arc2d.ts +2 -2
- package/src/lib/primitives/geometry/Circle2d.ts +2 -2
- package/src/lib/primitives/geometry/CubicBezier2d.ts +4 -1
- package/src/lib/primitives/geometry/Ellipse2d.ts +2 -2
- package/src/lib/primitives/geometry/geometry-constants.ts +2 -1
- package/src/version.ts +3 -3
- package/src/lib/test/currentToolIdMask.test.ts +0 -49
package/editor.css
CHANGED
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
--color-text: hsl(0, 0%, 0%);
|
|
168
168
|
--color-text-0: hsl(0, 0%, 11%);
|
|
169
169
|
--color-text-1: hsl(0, 0%, 18%);
|
|
170
|
-
--color-text-3: hsl(
|
|
170
|
+
--color-text-3: hsl(204, 4%, 45%);
|
|
171
171
|
--color-text-shadow: hsl(0, 0%, 100%);
|
|
172
172
|
--color-text-highlight: hsl(52, 100%, 50%);
|
|
173
173
|
--color-text-highlight-p3: color(display-p3 0.972 0.8205 0.05);
|
|
@@ -222,7 +222,7 @@
|
|
|
222
222
|
--color-text: hsl(210, 17%, 98%);
|
|
223
223
|
--color-text-0: hsl(0, 9%, 94%);
|
|
224
224
|
--color-text-1: hsl(0, 0%, 85%);
|
|
225
|
-
--color-text-3: hsl(
|
|
225
|
+
--color-text-3: hsl(204, 4%, 75%);
|
|
226
226
|
--color-text-shadow: hsl(210, 13%, 18%);
|
|
227
227
|
--color-text-highlight: hsl(52, 100%, 41%);
|
|
228
228
|
--color-text-highlight-p3: color(display-p3 0.8078 0.6225 0.0312);
|
|
@@ -968,14 +968,14 @@ input,
|
|
|
968
968
|
font-size: 14px;
|
|
969
969
|
font-weight: 500;
|
|
970
970
|
opacity: 0;
|
|
971
|
-
animation: fade-in 0.2s ease-in-out forwards;
|
|
971
|
+
animation: tl-fade-in 0.2s ease-in-out forwards;
|
|
972
972
|
animation-delay: 0.2s;
|
|
973
973
|
position: absolute;
|
|
974
974
|
inset: 0px;
|
|
975
975
|
z-index: var(--layer-canvas-blocker);
|
|
976
976
|
}
|
|
977
977
|
|
|
978
|
-
@keyframes fade-in {
|
|
978
|
+
@keyframes tl-fade-in {
|
|
979
979
|
0% {
|
|
980
980
|
opacity: 0;
|
|
981
981
|
}
|
|
@@ -984,6 +984,19 @@ input,
|
|
|
984
984
|
}
|
|
985
985
|
}
|
|
986
986
|
|
|
987
|
+
.tl-spinner {
|
|
988
|
+
animation: tl-spin 1s linear infinite;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
@keyframes tl-spin {
|
|
992
|
+
0% {
|
|
993
|
+
transform: rotate(0deg);
|
|
994
|
+
}
|
|
995
|
+
100% {
|
|
996
|
+
transform: rotate(360deg);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
987
1000
|
/* ---------------------- Brush --------------------- */
|
|
988
1001
|
|
|
989
1002
|
.tl-brush {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/editor",
|
|
3
|
-
"description": "
|
|
4
|
-
"version": "3.15.0-canary.
|
|
3
|
+
"description": "tldraw infinite canvas SDK (editor).",
|
|
4
|
+
"version": "3.15.0-canary.175d131e5195",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"tldraw",
|
|
20
|
+
"sdk",
|
|
20
21
|
"drawing",
|
|
21
22
|
"app",
|
|
22
23
|
"development",
|
|
@@ -48,12 +49,12 @@
|
|
|
48
49
|
"@tiptap/core": "^2.9.1",
|
|
49
50
|
"@tiptap/pm": "^2.9.1",
|
|
50
51
|
"@tiptap/react": "^2.9.1",
|
|
51
|
-
"@tldraw/state": "3.15.0-canary.
|
|
52
|
-
"@tldraw/state-react": "3.15.0-canary.
|
|
53
|
-
"@tldraw/store": "3.15.0-canary.
|
|
54
|
-
"@tldraw/tlschema": "3.15.0-canary.
|
|
55
|
-
"@tldraw/utils": "3.15.0-canary.
|
|
56
|
-
"@tldraw/validate": "3.15.0-canary.
|
|
52
|
+
"@tldraw/state": "3.15.0-canary.175d131e5195",
|
|
53
|
+
"@tldraw/state-react": "3.15.0-canary.175d131e5195",
|
|
54
|
+
"@tldraw/store": "3.15.0-canary.175d131e5195",
|
|
55
|
+
"@tldraw/tlschema": "3.15.0-canary.175d131e5195",
|
|
56
|
+
"@tldraw/utils": "3.15.0-canary.175d131e5195",
|
|
57
|
+
"@tldraw/validate": "3.15.0-canary.175d131e5195",
|
|
57
58
|
"@types/core-js": "^2.5.8",
|
|
58
59
|
"@use-gesture/react": "^10.3.1",
|
|
59
60
|
"classnames": "^2.5.1",
|
package/src/index.ts
CHANGED
|
@@ -346,6 +346,7 @@ export { CubicBezier2d } from './lib/primitives/geometry/CubicBezier2d'
|
|
|
346
346
|
export { CubicSpline2d } from './lib/primitives/geometry/CubicSpline2d'
|
|
347
347
|
export { Edge2d } from './lib/primitives/geometry/Edge2d'
|
|
348
348
|
export { Ellipse2d } from './lib/primitives/geometry/Ellipse2d'
|
|
349
|
+
export { getVerticesCountForArcLength } from './lib/primitives/geometry/geometry-constants'
|
|
349
350
|
export {
|
|
350
351
|
Geometry2d,
|
|
351
352
|
Geometry2dFilters,
|
|
@@ -7,7 +7,7 @@ export type SVGContainerProps = React.ComponentProps<'svg'>
|
|
|
7
7
|
/** @public @react */
|
|
8
8
|
export function SVGContainer({ children, className = '', ...rest }: SVGContainerProps) {
|
|
9
9
|
return (
|
|
10
|
-
<svg {...rest} className={classNames('tl-svg-container', className)}>
|
|
10
|
+
<svg {...rest} className={classNames('tl-svg-container', className)} aria-hidden="true">
|
|
11
11
|
{children}
|
|
12
12
|
</svg>
|
|
13
13
|
)
|
|
@@ -21,7 +21,7 @@ export const DefaultBrush = ({ brush, color, opacity, className }: TLBrushProps)
|
|
|
21
21
|
const h = toDomPrecision(Math.max(1, brush.h))
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
|
-
<svg className="tl-overlays__item" ref={rSvg}>
|
|
24
|
+
<svg className="tl-overlays__item" ref={rSvg} aria-hidden="true">
|
|
25
25
|
{color ? (
|
|
26
26
|
<g className="tl-brush" opacity={opacity}>
|
|
27
27
|
<rect width={w} height={h} fill={color} opacity={0.75} />
|
|
@@ -139,7 +139,7 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
|
|
139
139
|
data-testid="canvas"
|
|
140
140
|
{...events}
|
|
141
141
|
>
|
|
142
|
-
<svg className="tl-svg-context">
|
|
142
|
+
<svg className="tl-svg-context" aria-hidden="true">
|
|
143
143
|
<defs>
|
|
144
144
|
{shapeSvgDefs}
|
|
145
145
|
<CursorDef />
|
|
@@ -39,7 +39,7 @@ export function DefaultCollaboratorHint({
|
|
|
39
39
|
const cursorHintId = useSharedSafeId('cursor_hint')
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
|
-
<svg ref={rSvg} className={classNames('tl-overlays__item', className)}>
|
|
42
|
+
<svg ref={rSvg} className={classNames('tl-overlays__item', className)} aria-hidden="true">
|
|
43
43
|
<use
|
|
44
44
|
href={`#${cursorHintId}`}
|
|
45
45
|
color={color}
|
|
@@ -33,7 +33,7 @@ export const DefaultCursor = memo(function DefaultCursor({
|
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
35
|
<div ref={rCursor} className={classNames('tl-overlays__item', className)}>
|
|
36
|
-
<svg className="tl-cursor">
|
|
36
|
+
<svg className="tl-cursor" aria-hidden="true">
|
|
37
37
|
<use href={`#${cursorId}`} color={color} />
|
|
38
38
|
</svg>
|
|
39
39
|
{chatMessage ? (
|
|
@@ -16,7 +16,7 @@ export function DefaultGrid({ x, y, z, size }: TLGridProps) {
|
|
|
16
16
|
const editor = useEditor()
|
|
17
17
|
const { gridSteps } = editor.options
|
|
18
18
|
return (
|
|
19
|
-
<svg className="tl-grid" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
|
19
|
+
<svg className="tl-grid" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
20
20
|
<defs>
|
|
21
21
|
{gridSteps.map(({ min, mid, step }, i) => {
|
|
22
22
|
const s = step * size * z
|
|
@@ -7,5 +7,9 @@ export interface TLHandlesProps {
|
|
|
7
7
|
|
|
8
8
|
/** @public @react */
|
|
9
9
|
export const DefaultHandles = ({ children }: TLHandlesProps) => {
|
|
10
|
-
return
|
|
10
|
+
return (
|
|
11
|
+
<svg className="tl-user-handles tl-overlays__item" aria-hidden="true">
|
|
12
|
+
{children}
|
|
13
|
+
</svg>
|
|
14
|
+
)
|
|
11
15
|
}
|
|
@@ -86,7 +86,7 @@ export const DefaultShapeIndicator = memo(function DefaultShapeIndicator({
|
|
|
86
86
|
}, [hidden])
|
|
87
87
|
|
|
88
88
|
return (
|
|
89
|
-
<svg ref={rIndicator} className={classNames('tl-overlays__item', className)}>
|
|
89
|
+
<svg ref={rIndicator} className={classNames('tl-overlays__item', className)} aria-hidden="true">
|
|
90
90
|
<g className="tl-shape-indicator" stroke={color ?? 'var(--color-selected)'} opacity={opacity}>
|
|
91
91
|
<InnerIndicator editor={editor} id={shapeId} />
|
|
92
92
|
</g>
|
|
@@ -163,7 +163,7 @@ export interface TLSnapIndicatorProps {
|
|
|
163
163
|
/** @public @react */
|
|
164
164
|
export function DefaultSnapIndicator({ className, line, zoom }: TLSnapIndicatorProps) {
|
|
165
165
|
return (
|
|
166
|
-
<svg className={classNames('tl-overlays__item', className)}>
|
|
166
|
+
<svg className={classNames('tl-overlays__item', className)} aria-hidden="true">
|
|
167
167
|
{line.type === 'points' ? (
|
|
168
168
|
<PointsSnapIndicator {...line} zoom={zoom} />
|
|
169
169
|
) : line.type === 'gaps' ? (
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
2
|
+
|
|
1
3
|
/** @public @react */
|
|
2
|
-
export function DefaultSpinner() {
|
|
4
|
+
export function DefaultSpinner(props: React.SVGProps<SVGSVGElement>) {
|
|
3
5
|
return (
|
|
4
|
-
<svg
|
|
6
|
+
<svg
|
|
7
|
+
width={16}
|
|
8
|
+
height={16}
|
|
9
|
+
viewBox="0 0 16 16"
|
|
10
|
+
aria-hidden="false"
|
|
11
|
+
{...props}
|
|
12
|
+
className={classNames('tl-spinner', props.className)}
|
|
13
|
+
>
|
|
5
14
|
<g strokeWidth={2} fill="none" fillRule="evenodd">
|
|
6
15
|
<circle strokeOpacity={0.25} cx={8} cy={8} r={7} stroke="currentColor" />
|
|
7
|
-
<path strokeLinecap="round" d="M15 8c0-4.5-4.5-7-7-7" stroke="currentColor"
|
|
8
|
-
<animateTransform
|
|
9
|
-
attributeName="transform"
|
|
10
|
-
type="rotate"
|
|
11
|
-
from="0 8 8"
|
|
12
|
-
to="360 8 8"
|
|
13
|
-
dur="1s"
|
|
14
|
-
repeatCount="indefinite"
|
|
15
|
-
/>
|
|
16
|
-
</path>
|
|
16
|
+
<path strokeLinecap="round" d="M15 8c0-4.5-4.5-7-7-7" stroke="currentColor" />
|
|
17
17
|
</g>
|
|
18
18
|
</svg>
|
|
19
19
|
)
|
package/src/lib/editor/Editor.ts
CHANGED
|
@@ -1863,10 +1863,11 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1863
1863
|
firstParentId &&
|
|
1864
1864
|
selectedShapeIds.every((shapeId) => this.getShape(shapeId)?.parentId === firstParentId) &&
|
|
1865
1865
|
!isPageId(firstParentId)
|
|
1866
|
+
const filteredShapes = isSelectedWithinContainer
|
|
1867
|
+
? this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
|
|
1868
|
+
: this.getCurrentPageShapes().filter((shape) => isPageId(shape.parentId))
|
|
1866
1869
|
const readingOrderShapes = isSelectedWithinContainer
|
|
1867
|
-
? this._getShapesInReadingOrder(
|
|
1868
|
-
this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
|
|
1869
|
-
)
|
|
1870
|
+
? this._getShapesInReadingOrder(filteredShapes)
|
|
1870
1871
|
: this.getCurrentPageShapesInReadingOrder()
|
|
1871
1872
|
const currentShapeId: TLShapeId | undefined =
|
|
1872
1873
|
selectedShapeIds.length === 1
|
|
@@ -1883,7 +1884,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1883
1884
|
adjacentShapeId = shapeIds[adjacentIndex]
|
|
1884
1885
|
} else {
|
|
1885
1886
|
if (!currentShapeId) return
|
|
1886
|
-
adjacentShapeId = this.getNearestAdjacentShape(currentShapeId, direction)
|
|
1887
|
+
adjacentShapeId = this.getNearestAdjacentShape(filteredShapes, currentShapeId, direction)
|
|
1887
1888
|
}
|
|
1888
1889
|
|
|
1889
1890
|
const shape = this.getShape(adjacentShapeId)
|
|
@@ -1982,6 +1983,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1982
1983
|
* @public
|
|
1983
1984
|
*/
|
|
1984
1985
|
getNearestAdjacentShape(
|
|
1986
|
+
shapes: TLShape[],
|
|
1985
1987
|
currentShapeId: TLShapeId,
|
|
1986
1988
|
direction: 'left' | 'right' | 'up' | 'down'
|
|
1987
1989
|
): TLShapeId {
|
|
@@ -1989,7 +1991,6 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
1989
1991
|
const currentShape = this.getShape(currentShapeId)
|
|
1990
1992
|
if (!currentShape) return currentShapeId
|
|
1991
1993
|
|
|
1992
|
-
const shapes = this.getCurrentPageShapes()
|
|
1993
1994
|
const tabbableShapes = shapes.filter(
|
|
1994
1995
|
(shape) => this.getShapeUtil(shape).canTabTo(shape) && shape.id !== currentShapeId
|
|
1995
1996
|
)
|
|
@@ -4420,21 +4421,28 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|
|
4420
4421
|
*/
|
|
4421
4422
|
deletePage(page: TLPageId | TLPage): this {
|
|
4422
4423
|
const id = typeof page === 'string' ? page : page.id
|
|
4423
|
-
this.run(
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4424
|
+
this.run(
|
|
4425
|
+
() => {
|
|
4426
|
+
if (this.getIsReadonly()) return
|
|
4427
|
+
const pages = this.getPages()
|
|
4428
|
+
if (pages.length === 1) return
|
|
4427
4429
|
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
+
const deletedPage = this.getPage(id)
|
|
4431
|
+
if (!deletedPage) return
|
|
4430
4432
|
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4433
|
+
if (id === this.getCurrentPageId()) {
|
|
4434
|
+
const index = pages.findIndex((page) => page.id === id)
|
|
4435
|
+
const next = pages[index - 1] ?? pages[index + 1]
|
|
4436
|
+
this.setCurrentPage(next.id)
|
|
4437
|
+
}
|
|
4438
|
+
|
|
4439
|
+
const shapes = this.getSortedChildIdsForParent(deletedPage.id)
|
|
4440
|
+
this.deleteShapes(shapes)
|
|
4441
|
+
|
|
4442
|
+
this.store.remove([deletedPage.id])
|
|
4443
|
+
},
|
|
4444
|
+
{ ignoreShapeLock: true }
|
|
4445
|
+
)
|
|
4438
4446
|
return this
|
|
4439
4447
|
}
|
|
4440
4448
|
|
|
@@ -584,6 +584,15 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
584
584
|
*/
|
|
585
585
|
onResizeEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void
|
|
586
586
|
|
|
587
|
+
/**
|
|
588
|
+
* A callback called when a shape resize is cancelled.
|
|
589
|
+
*
|
|
590
|
+
* @param initial - The shape at the start of the resize.
|
|
591
|
+
* @param current - The current shape.
|
|
592
|
+
* @public
|
|
593
|
+
*/
|
|
594
|
+
onResizeCancel?(initial: Shape, current: Shape): void
|
|
595
|
+
|
|
587
596
|
/**
|
|
588
597
|
* A callback called when a shape starts being translated.
|
|
589
598
|
*
|
|
@@ -613,6 +622,25 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
613
622
|
*/
|
|
614
623
|
onTranslateEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void
|
|
615
624
|
|
|
625
|
+
/**
|
|
626
|
+
* A callback called when a shape translation is cancelled.
|
|
627
|
+
*
|
|
628
|
+
* @param initial - The shape at the start of the translation.
|
|
629
|
+
* @param current - The current shape.
|
|
630
|
+
* @public
|
|
631
|
+
*/
|
|
632
|
+
onTranslateCancel?(initial: Shape, current: Shape): void
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* A callback called when a shape's handle starts being dragged.
|
|
636
|
+
*
|
|
637
|
+
* @param shape - The shape.
|
|
638
|
+
* @param info - An object containing the handle and whether the handle is 'precise' or not.
|
|
639
|
+
* @returns A change to apply to the shape, or void.
|
|
640
|
+
* @public
|
|
641
|
+
*/
|
|
642
|
+
onHandleDragStart?(shape: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void
|
|
643
|
+
|
|
616
644
|
/**
|
|
617
645
|
* A callback called when a shape's handle changes.
|
|
618
646
|
*
|
|
@@ -623,6 +651,25 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
623
651
|
*/
|
|
624
652
|
onHandleDrag?(shape: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void
|
|
625
653
|
|
|
654
|
+
/**
|
|
655
|
+
* A callback called when a shape's handle finishes being dragged.
|
|
656
|
+
*
|
|
657
|
+
* @param current - The current shape.
|
|
658
|
+
* @param info - An object containing the handle and whether the handle is 'precise' or not.
|
|
659
|
+
* @returns A change to apply to the shape, or void.
|
|
660
|
+
* @public
|
|
661
|
+
*/
|
|
662
|
+
onHandleDragEnd?(current: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* A callback called when a shape's handle drag is cancelled.
|
|
666
|
+
*
|
|
667
|
+
* @param current - The current shape.
|
|
668
|
+
* @param info - An object containing the handle and whether the handle is 'precise' or not.
|
|
669
|
+
* @public
|
|
670
|
+
*/
|
|
671
|
+
onHandleDragCancel?(current: Shape, info: TLHandleDragInfo<Shape>): void
|
|
672
|
+
|
|
626
673
|
/**
|
|
627
674
|
* A callback called when a shape starts being rotated.
|
|
628
675
|
*
|
|
@@ -652,6 +699,15 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
|
|
652
699
|
*/
|
|
653
700
|
onRotateEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void
|
|
654
701
|
|
|
702
|
+
/**
|
|
703
|
+
* A callback called when a shape rotation is cancelled.
|
|
704
|
+
*
|
|
705
|
+
* @param initial - The shape at the start of the rotation.
|
|
706
|
+
* @param current - The current shape.
|
|
707
|
+
* @public
|
|
708
|
+
*/
|
|
709
|
+
onRotateCancel?(initial: Shape, current: Shape): void
|
|
710
|
+
|
|
655
711
|
/**
|
|
656
712
|
* Not currently used.
|
|
657
713
|
*
|
|
@@ -819,5 +875,6 @@ export interface TLResizeInfo<T extends TLShape> {
|
|
|
819
875
|
export interface TLHandleDragInfo<T extends TLShape> {
|
|
820
876
|
handle: TLHandle
|
|
821
877
|
isPrecise: boolean
|
|
878
|
+
isCreatingShape: boolean
|
|
822
879
|
initial?: T | undefined
|
|
823
880
|
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { createTLStore } from '../../config/createTLStore'
|
|
2
|
+
import { Editor } from '../Editor'
|
|
3
|
+
import { StateNode } from './StateNode'
|
|
4
|
+
|
|
5
|
+
describe('StateNode.addChild', () => {
|
|
6
|
+
// Test state node classes for addChild tests
|
|
7
|
+
class ParentState extends StateNode {
|
|
8
|
+
static override id = 'parent'
|
|
9
|
+
static override initial = 'child1'
|
|
10
|
+
static override children() {
|
|
11
|
+
return [ChildState1]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class ChildState1 extends StateNode {
|
|
16
|
+
static override id = 'child1'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class ChildState2 extends StateNode {
|
|
20
|
+
static override id = 'child2'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class ChildState3 extends StateNode {
|
|
24
|
+
static override id = 'child3'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class LeafState extends StateNode {
|
|
28
|
+
static override id = 'leaf'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class RootState extends StateNode {
|
|
32
|
+
static override id = 'root'
|
|
33
|
+
static override initial = 'child1'
|
|
34
|
+
static override children() {
|
|
35
|
+
return [ChildState1]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class RootStateWithoutChildren extends StateNode {
|
|
40
|
+
static override id = 'rootWithoutChildren'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let editor: Editor
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
editor = new Editor({
|
|
47
|
+
initialState: 'parent',
|
|
48
|
+
shapeUtils: [],
|
|
49
|
+
bindingUtils: [],
|
|
50
|
+
tools: [
|
|
51
|
+
ParentState,
|
|
52
|
+
ChildState1,
|
|
53
|
+
ChildState2,
|
|
54
|
+
ChildState3,
|
|
55
|
+
LeafState,
|
|
56
|
+
RootState,
|
|
57
|
+
RootStateWithoutChildren,
|
|
58
|
+
],
|
|
59
|
+
store: createTLStore({ shapeUtils: [], bindingUtils: [] }),
|
|
60
|
+
getContainer: () => document.body,
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should add a child to a branch state node', () => {
|
|
65
|
+
const parentState = editor.root.children!['parent'] as ParentState
|
|
66
|
+
|
|
67
|
+
// Initially should have one child
|
|
68
|
+
expect(Object.keys(parentState.children!)).toHaveLength(1)
|
|
69
|
+
expect(parentState.children!['child1']).toBeDefined()
|
|
70
|
+
|
|
71
|
+
// Add a new child
|
|
72
|
+
parentState.addChild(ChildState2)
|
|
73
|
+
|
|
74
|
+
// Should now have two children
|
|
75
|
+
expect(Object.keys(parentState.children!)).toHaveLength(2)
|
|
76
|
+
expect(parentState.children!['child1']).toBeDefined()
|
|
77
|
+
expect(parentState.children!['child2']).toBeDefined()
|
|
78
|
+
expect(parentState.children!['child2']).toBeInstanceOf(ChildState2)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('should add a child to a root state node', () => {
|
|
82
|
+
const rootState = editor.root.children!['root'] as RootState
|
|
83
|
+
|
|
84
|
+
// Initially should have one child
|
|
85
|
+
expect(Object.keys(rootState.children!)).toHaveLength(1)
|
|
86
|
+
expect(rootState.children!['child1']).toBeDefined()
|
|
87
|
+
|
|
88
|
+
// Add a new child
|
|
89
|
+
rootState.addChild(ChildState2)
|
|
90
|
+
|
|
91
|
+
// Should now have two children
|
|
92
|
+
expect(Object.keys(rootState.children!)).toHaveLength(2)
|
|
93
|
+
expect(rootState.children!['child1']).toBeDefined()
|
|
94
|
+
expect(rootState.children!['child2']).toBeDefined()
|
|
95
|
+
expect(rootState.children!['child2']).toBeInstanceOf(ChildState2)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should throw an error when trying to add a child to a leaf state node', () => {
|
|
99
|
+
const leafState = editor.root.children!['leaf'] as LeafState
|
|
100
|
+
|
|
101
|
+
// Leaf state should not have children
|
|
102
|
+
expect(leafState.children).toBeUndefined()
|
|
103
|
+
|
|
104
|
+
// Should throw an error when trying to add a child
|
|
105
|
+
expect(() => {
|
|
106
|
+
leafState.addChild(ChildState2)
|
|
107
|
+
}).toThrow('StateNode.addChild: cannot add child to a leaf node')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should return the parent state node for chaining', () => {
|
|
111
|
+
const parentState = editor.root.children!['parent'] as ParentState
|
|
112
|
+
|
|
113
|
+
const result = parentState.addChild(ChildState2)
|
|
114
|
+
|
|
115
|
+
expect(result).toBe(parentState)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('should create the child with the correct editor and parent', () => {
|
|
119
|
+
const parentState = editor.root.children!['parent'] as ParentState
|
|
120
|
+
|
|
121
|
+
parentState.addChild(ChildState2)
|
|
122
|
+
const childState = parentState.children!['child2'] as ChildState2
|
|
123
|
+
|
|
124
|
+
expect(childState.editor).toBe(editor)
|
|
125
|
+
expect(childState.parent).toBe(parentState)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('should allow adding multiple children', () => {
|
|
129
|
+
const parentState = editor.root.children!['parent'] as ParentState
|
|
130
|
+
|
|
131
|
+
// Add multiple children
|
|
132
|
+
parentState.addChild(ChildState2).addChild(ChildState3)
|
|
133
|
+
|
|
134
|
+
// Should have three children
|
|
135
|
+
expect(Object.keys(parentState.children!)).toHaveLength(3)
|
|
136
|
+
expect(parentState.children!['child1']).toBeDefined()
|
|
137
|
+
expect(parentState.children!['child2']).toBeDefined()
|
|
138
|
+
expect(parentState.children!['child3']).toBeDefined()
|
|
139
|
+
expect(parentState.children!['child2']).toBeInstanceOf(ChildState2)
|
|
140
|
+
expect(parentState.children!['child3']).toBeInstanceOf(ChildState3)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('should allow transitioning to added children', () => {
|
|
144
|
+
const parentState = editor.root.children!['parent'] as ParentState
|
|
145
|
+
|
|
146
|
+
// Add a new child
|
|
147
|
+
parentState.addChild(ChildState2)
|
|
148
|
+
|
|
149
|
+
// Should be able to transition to the new child
|
|
150
|
+
expect(() => {
|
|
151
|
+
parentState.transition('child2')
|
|
152
|
+
}).not.toThrow()
|
|
153
|
+
|
|
154
|
+
// The current state should be the new child
|
|
155
|
+
expect(parentState.getCurrent()?.id).toBe('child2')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('should maintain existing children when adding new ones', () => {
|
|
159
|
+
const parentState = editor.root.children!['parent'] as ParentState
|
|
160
|
+
const originalChild = parentState.children!['child1']
|
|
161
|
+
|
|
162
|
+
// Add a new child
|
|
163
|
+
parentState.addChild(ChildState2)
|
|
164
|
+
|
|
165
|
+
// Original child should still exist and be the same instance
|
|
166
|
+
expect(parentState.children!['child1']).toBe(originalChild)
|
|
167
|
+
expect(parentState.children!['child1']).toBeInstanceOf(ChildState1)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('should initialize children object for root nodes without static children', () => {
|
|
171
|
+
// Create a StateNode directly as a root node (no parent)
|
|
172
|
+
const mockEditor = {} as Editor
|
|
173
|
+
const rootStateWithoutChildren = new RootStateWithoutChildren(mockEditor, undefined)
|
|
174
|
+
|
|
175
|
+
// Root state without static children should not have children initially
|
|
176
|
+
expect(rootStateWithoutChildren.children).toBeUndefined()
|
|
177
|
+
|
|
178
|
+
// Adding a child should initialize the children object
|
|
179
|
+
rootStateWithoutChildren.addChild(ChildState2)
|
|
180
|
+
|
|
181
|
+
// Should now have children object with the added child
|
|
182
|
+
expect(rootStateWithoutChildren.children).toBeDefined()
|
|
183
|
+
expect(Object.keys(rootStateWithoutChildren.children!)).toHaveLength(1)
|
|
184
|
+
expect(rootStateWithoutChildren.children!['child2']).toBeDefined()
|
|
185
|
+
expect(rootStateWithoutChildren.children!['child2']).toBeInstanceOf(ChildState2)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('should throw an error when trying to add a child with a duplicate ID', () => {
|
|
189
|
+
const parentState = editor.root.children!['parent'] as ParentState
|
|
190
|
+
|
|
191
|
+
// Initially should have one child
|
|
192
|
+
expect(Object.keys(parentState.children!)).toHaveLength(1)
|
|
193
|
+
expect(parentState.children!['child1']).toBeDefined()
|
|
194
|
+
|
|
195
|
+
// Should throw an error when trying to add a child with the same ID
|
|
196
|
+
expect(() => {
|
|
197
|
+
parentState.addChild(ChildState1)
|
|
198
|
+
}).toThrow("StateNode.addChild: a child with id 'child1' already exists")
|
|
199
|
+
|
|
200
|
+
// Should still have only one child
|
|
201
|
+
expect(Object.keys(parentState.children!)).toHaveLength(1)
|
|
202
|
+
expect(parentState.children!['child1']).toBeDefined()
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('should throw an error when trying to add a child with a duplicate ID to a root state', () => {
|
|
206
|
+
const rootState = editor.root.children!['root'] as RootState
|
|
207
|
+
|
|
208
|
+
// Initially should have one child
|
|
209
|
+
expect(Object.keys(rootState.children!)).toHaveLength(1)
|
|
210
|
+
expect(rootState.children!['child1']).toBeDefined()
|
|
211
|
+
|
|
212
|
+
// Should throw an error when trying to add a child with the same ID
|
|
213
|
+
expect(() => {
|
|
214
|
+
rootState.addChild(ChildState1)
|
|
215
|
+
}).toThrow("StateNode.addChild: a child with id 'child1' already exists")
|
|
216
|
+
|
|
217
|
+
// Should still have only one child
|
|
218
|
+
expect(Object.keys(rootState.children!)).toHaveLength(1)
|
|
219
|
+
expect(rootState.children!['child1']).toBeDefined()
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('should throw an error when trying to add a child with a duplicate ID to a root state without static children', () => {
|
|
223
|
+
// Create a StateNode directly as a root node (no parent)
|
|
224
|
+
const mockEditor = {} as Editor
|
|
225
|
+
const rootStateWithoutChildren = new RootStateWithoutChildren(mockEditor, undefined)
|
|
226
|
+
|
|
227
|
+
// Add a child first
|
|
228
|
+
rootStateWithoutChildren.addChild(ChildState1)
|
|
229
|
+
|
|
230
|
+
// Should throw an error when trying to add a child with the same ID
|
|
231
|
+
expect(() => {
|
|
232
|
+
rootStateWithoutChildren.addChild(ChildState1)
|
|
233
|
+
}).toThrow("StateNode.addChild: a child with id 'child1' already exists")
|
|
234
|
+
|
|
235
|
+
// Should still have only one child
|
|
236
|
+
expect(Object.keys(rootStateWithoutChildren.children!)).toHaveLength(1)
|
|
237
|
+
expect(rootStateWithoutChildren.children!['child1']).toBeDefined()
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
describe('current tool id mask', () => {
|
|
242
|
+
// Tool mask test classes
|
|
243
|
+
class ToolA extends StateNode {
|
|
244
|
+
static override id = 'A'
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
class ToolB extends StateNode {
|
|
248
|
+
static override id = 'B'
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
class ToolC extends StateNode {
|
|
252
|
+
static override id = 'C'
|
|
253
|
+
|
|
254
|
+
override onEnter() {
|
|
255
|
+
this.setCurrentToolIdMask('A')
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let toolMaskEditor: Editor
|
|
260
|
+
|
|
261
|
+
beforeEach(() => {
|
|
262
|
+
toolMaskEditor = new Editor({
|
|
263
|
+
initialState: 'A',
|
|
264
|
+
shapeUtils: [],
|
|
265
|
+
bindingUtils: [],
|
|
266
|
+
tools: [ToolA, ToolB, ToolC],
|
|
267
|
+
store: createTLStore({ shapeUtils: [], bindingUtils: [] }),
|
|
268
|
+
getContainer: () => document.body,
|
|
269
|
+
})
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
it('starts with the correct tool id', () => {
|
|
273
|
+
expect(toolMaskEditor.getCurrentToolId()).toBe('A')
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
it('updates the current tool id', () => {
|
|
277
|
+
toolMaskEditor.setCurrentTool('B')
|
|
278
|
+
expect(toolMaskEditor.getCurrentToolId()).toBe('B')
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('masks the current tool id', () => {
|
|
282
|
+
toolMaskEditor.setCurrentTool('C')
|
|
283
|
+
expect(toolMaskEditor.getCurrentToolId()).toBe('A')
|
|
284
|
+
})
|
|
285
|
+
})
|