@weasel-js/core 0.5.0
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/CHANGELOG.md +381 -0
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/dist/DrawCommand-Dl0bXNfS.d.ts +500 -0
- package/dist/chunk-775XXAHR.js +216 -0
- package/dist/chunk-775XXAHR.js.map +1 -0
- package/dist/chunk-7V6JEOXE.js +27754 -0
- package/dist/chunk-7V6JEOXE.js.map +1 -0
- package/dist/chunk-AM6ARSPN.js +517 -0
- package/dist/chunk-AM6ARSPN.js.map +1 -0
- package/dist/chunk-BGGZ4CVF.js +248 -0
- package/dist/chunk-BGGZ4CVF.js.map +1 -0
- package/dist/chunk-BHVYVFGV.js +29 -0
- package/dist/chunk-BHVYVFGV.js.map +1 -0
- package/dist/chunk-CQNKCG34.js +831 -0
- package/dist/chunk-CQNKCG34.js.map +1 -0
- package/dist/chunk-GVCNT7UH.js +47 -0
- package/dist/chunk-GVCNT7UH.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +9 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-UGFFCMQP.js +28 -0
- package/dist/chunk-UGFFCMQP.js.map +1 -0
- package/dist/chunk-VOVKONXA.js +103 -0
- package/dist/chunk-VOVKONXA.js.map +1 -0
- package/dist/chunk-Y52N27PF.js +39 -0
- package/dist/chunk-Y52N27PF.js.map +1 -0
- package/dist/clipboard.d.ts +91 -0
- package/dist/clipboard.js +6 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/clone.d.ts +8 -0
- package/dist/clone.js +6 -0
- package/dist/clone.js.map +1 -0
- package/dist/fitViewToBounds-evGsnR8Q.d.ts +62 -0
- package/dist/grid-Cf87knjU.d.ts +153 -0
- package/dist/index-DZBYMsHI.d.ts +1555 -0
- package/dist/index.css +121 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +10200 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/insert.d.ts +33 -0
- package/dist/insert.js +81 -0
- package/dist/insert.js.map +1 -0
- package/dist/move.d.ts +69 -0
- package/dist/move.js +145 -0
- package/dist/move.js.map +1 -0
- package/dist/options-BPPBWMa7.d.ts +64 -0
- package/dist/patterns-builtin.d.ts +55 -0
- package/dist/patterns-builtin.js +100 -0
- package/dist/patterns-builtin.js.map +1 -0
- package/dist/pointSnapToGrid-D7s7QmOF.d.ts +185 -0
- package/dist/registerFont-CP-wCsrz.d.ts +109 -0
- package/dist/registerTexture-BzHTLhD9.d.ts +25 -0
- package/dist/renderer.css +121 -0
- package/dist/renderer.css.map +1 -0
- package/dist/renderer.d.ts +376 -0
- package/dist/renderer.js +13 -0
- package/dist/renderer.js.map +1 -0
- package/dist/resize.d.ts +78 -0
- package/dist/resize.js +5 -0
- package/dist/resize.js.map +1 -0
- package/dist/routing.css +35 -0
- package/dist/routing.css.map +1 -0
- package/dist/routing.d.ts +14 -0
- package/dist/routing.js +4 -0
- package/dist/routing.js.map +1 -0
- package/dist/types-B6MMiodD.d.ts +59 -0
- package/dist/types-BJ8_cyT7.d.ts +130 -0
- package/dist/types-B_-khFM0.d.ts +331 -0
- package/dist/types-BjUi2vA-.d.ts +355 -0
- package/dist/types-Cpb4hii1.d.ts +445 -0
- package/dist/types-D2tTKEU0.d.ts +18 -0
- package/dist/view-DSQgxBJB.d.ts +63 -0
- package/package.json +96 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/** One bit per debug feature; absent keys are off. */
|
|
2
|
+
interface DebugConfig {
|
|
3
|
+
hitboxes?: boolean;
|
|
4
|
+
handles?: boolean;
|
|
5
|
+
bounds?: boolean;
|
|
6
|
+
origins?: boolean;
|
|
7
|
+
snap?: boolean;
|
|
8
|
+
layers?: boolean;
|
|
9
|
+
/** Render each tracked node's id as a label at the top-left of its
|
|
10
|
+
* bounds. Pulls from the same `recordBounds` stream `bounds` uses, so
|
|
11
|
+
* no extra sink calls are required to enable. */
|
|
12
|
+
ids?: boolean;
|
|
13
|
+
/** Rolling FPS counter rendered in the top-left corner of the canvas.
|
|
14
|
+
* Tracks the rate at which the debug overlay's draw callback runs;
|
|
15
|
+
* this matches the canvas's effective repaint rate. */
|
|
16
|
+
fps?: boolean;
|
|
17
|
+
/** Optional per-feature color overrides; falls back to the default theme. */
|
|
18
|
+
theme?: Partial<DebugTheme>;
|
|
19
|
+
}
|
|
20
|
+
type DebugFeature = 'hitboxes' | 'handles' | 'bounds' | 'origins' | 'snap' | 'layers' | 'ids' | 'fps';
|
|
21
|
+
interface DebugTheme {
|
|
22
|
+
hitboxFill: string;
|
|
23
|
+
hitboxStroke: string;
|
|
24
|
+
handle: string;
|
|
25
|
+
bounds: string;
|
|
26
|
+
origin: string;
|
|
27
|
+
snap: string;
|
|
28
|
+
layerText: string;
|
|
29
|
+
layerTextBg: string;
|
|
30
|
+
/** Color for the per-node id label rendered when `config.ids` is on. */
|
|
31
|
+
idText: string;
|
|
32
|
+
/** Foreground / background for the FPS panel. */
|
|
33
|
+
fpsText: string;
|
|
34
|
+
fpsTextBg: string;
|
|
35
|
+
}
|
|
36
|
+
type HandleKind = 'corner' | 'rotation' | 'anchor';
|
|
37
|
+
type HitShape = {
|
|
38
|
+
kind: 'rect';
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
rotation?: number;
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'circle';
|
|
46
|
+
cx: number;
|
|
47
|
+
cy: number;
|
|
48
|
+
r: number;
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'path';
|
|
51
|
+
d: Path2D;
|
|
52
|
+
};
|
|
53
|
+
interface RecordedHitbox {
|
|
54
|
+
id: string;
|
|
55
|
+
kind: 'body' | 'handle' | 'rotation' | 'anchor';
|
|
56
|
+
shape: HitShape;
|
|
57
|
+
}
|
|
58
|
+
interface RecordedHandle {
|
|
59
|
+
id: string;
|
|
60
|
+
position: {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
};
|
|
64
|
+
kind: HandleKind;
|
|
65
|
+
}
|
|
66
|
+
interface RecordedBounds {
|
|
67
|
+
id: string;
|
|
68
|
+
bounds: {
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
width: number;
|
|
72
|
+
height: number;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
interface RecordedOrigin {
|
|
76
|
+
id: string;
|
|
77
|
+
point: {
|
|
78
|
+
x: number;
|
|
79
|
+
y: number;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
interface RecordedSnap {
|
|
83
|
+
point: {
|
|
84
|
+
x: number;
|
|
85
|
+
y: number;
|
|
86
|
+
};
|
|
87
|
+
accepted: boolean;
|
|
88
|
+
}
|
|
89
|
+
interface RecordedLayer {
|
|
90
|
+
id: string;
|
|
91
|
+
label: string;
|
|
92
|
+
space: 'world' | 'screen';
|
|
93
|
+
index: number;
|
|
94
|
+
}
|
|
95
|
+
interface DebugSnapshot {
|
|
96
|
+
hitboxes: RecordedHitbox[];
|
|
97
|
+
handles: RecordedHandle[];
|
|
98
|
+
bounds: RecordedBounds[];
|
|
99
|
+
origins: RecordedOrigin[];
|
|
100
|
+
snap: RecordedSnap[];
|
|
101
|
+
layers: RecordedLayer[];
|
|
102
|
+
}
|
|
103
|
+
interface DebugSink {
|
|
104
|
+
recordHitbox(id: string, kind: 'body' | 'handle' | 'rotation' | 'anchor', shape: HitShape): void;
|
|
105
|
+
recordHandle(id: string, position: {
|
|
106
|
+
x: number;
|
|
107
|
+
y: number;
|
|
108
|
+
}, kind: HandleKind): void;
|
|
109
|
+
recordBounds(id: string, bounds: {
|
|
110
|
+
x: number;
|
|
111
|
+
y: number;
|
|
112
|
+
width: number;
|
|
113
|
+
height: number;
|
|
114
|
+
}): void;
|
|
115
|
+
recordOrigin(id: string, point: {
|
|
116
|
+
x: number;
|
|
117
|
+
y: number;
|
|
118
|
+
}): void;
|
|
119
|
+
recordSnapCandidate(point: {
|
|
120
|
+
x: number;
|
|
121
|
+
y: number;
|
|
122
|
+
}, accepted: boolean): void;
|
|
123
|
+
recordLayer(id: string, label: string, space: 'world' | 'screen', index: number): void;
|
|
124
|
+
/** Clears every non-snap array. Called at the start of each Canvas render. */
|
|
125
|
+
beginFrame(): void;
|
|
126
|
+
/** Clears the snap array. Called at gesture end. */
|
|
127
|
+
clearSnap(): void;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export type { DebugSink as D, HandleKind as H, RecordedBounds as R, DebugConfig as a, DebugSnapshot as b, DebugTheme as c, DebugFeature as d, HitShape as e, RecordedHandle as f, RecordedHitbox as g, RecordedLayer as h, RecordedOrigin as i, RecordedSnap as j };
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { Op } from '@weasel-js/history';
|
|
2
|
+
|
|
3
|
+
type ContainerBounds = {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
};
|
|
9
|
+
interface LayoutChild<TPose> {
|
|
10
|
+
id: string;
|
|
11
|
+
pose: TPose;
|
|
12
|
+
}
|
|
13
|
+
interface DropTarget<TPose> {
|
|
14
|
+
/** Where the dragged child lands if this target is picked. */
|
|
15
|
+
pose: TPose;
|
|
16
|
+
/** Reference point for distance metrics (snap algorithms). */
|
|
17
|
+
origin: {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
};
|
|
21
|
+
/** Optional axis-aligned region (world units) used by region-aware snaps
|
|
22
|
+
* (e.g. `containedThenNearest`). When present, a pointer inside this rect
|
|
23
|
+
* is treated as a containment hit on this target. Strategies that emit
|
|
24
|
+
* region-shaped targets (gutters, drop-zones) should populate this.
|
|
25
|
+
* Strategies whose targets are point-like (free-form, snap-point) can omit
|
|
26
|
+
* it and rely on `origin`-distance snaps. */
|
|
27
|
+
hitBounds?: {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
};
|
|
33
|
+
/** Strategy-private metadata (e.g. cell coords for tile-grid). */
|
|
34
|
+
meta?: unknown;
|
|
35
|
+
}
|
|
36
|
+
interface LayoutSnap<TPose> {
|
|
37
|
+
pickTarget(targets: DropTarget<TPose>[], pointer: {
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
}): DropTarget<TPose> | null;
|
|
41
|
+
}
|
|
42
|
+
interface LayoutContainer {
|
|
43
|
+
id: string;
|
|
44
|
+
bounds: ContainerBounds;
|
|
45
|
+
}
|
|
46
|
+
interface LayoutDragged<TPose> {
|
|
47
|
+
id: string;
|
|
48
|
+
/** The pose the dragged child currently has (pre-drop). */
|
|
49
|
+
originPose: TPose;
|
|
50
|
+
/** The pose the gesture proposes (pointer-driven, pre-snap). */
|
|
51
|
+
pose: TPose;
|
|
52
|
+
sourceContainerId: string | null;
|
|
53
|
+
}
|
|
54
|
+
interface LayoutStrategy<TPose> {
|
|
55
|
+
childPoses(container: LayoutContainer, children: ReadonlyArray<LayoutChild<TPose>>): Map<string, TPose>;
|
|
56
|
+
getDropTargets(container: LayoutContainer, children: ReadonlyArray<LayoutChild<TPose>>, dragged: LayoutDragged<TPose>): DropTarget<TPose>[];
|
|
57
|
+
reflowPoses(container: LayoutContainer, children: ReadonlyArray<LayoutChild<TPose>>, dragged: LayoutDragged<TPose>, target: DropTarget<TPose> | null): Map<string, TPose>;
|
|
58
|
+
commitDrop(container: LayoutContainer, children: ReadonlyArray<LayoutChild<TPose>>, dragged: LayoutDragged<TPose>, target: DropTarget<TPose> | null): Op[];
|
|
59
|
+
snap: LayoutSnap<TPose>;
|
|
60
|
+
/** Optional: predicate for whether a world-space point is inside this
|
|
61
|
+
* container. When absent, callers fall back to an axis-aligned bounding-box
|
|
62
|
+
* test on the container's pose. Strategies whose containers aren't
|
|
63
|
+
* rectangular (circles, irregular zones) implement this to override the
|
|
64
|
+
* AABB default. */
|
|
65
|
+
contains?(containerPose: TPose, point: {
|
|
66
|
+
x: number;
|
|
67
|
+
y: number;
|
|
68
|
+
}): boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Opaque clipboard payload. `items` is `unknown[]` so each app's clipboard
|
|
73
|
+
* adapter stores whatever shape it wants; the kit never inspects entries.
|
|
74
|
+
*
|
|
75
|
+
* The adapter is responsible for both producing snapshots
|
|
76
|
+
* (`snapshotSelection`) and consuming them (`commitPaste`). Type safety lives
|
|
77
|
+
* at that boundary, not in the kit.
|
|
78
|
+
*/
|
|
79
|
+
interface ClipboardSnapshot {
|
|
80
|
+
items: unknown[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* SnapTarget — where a dragged node would re-parent to if released.
|
|
84
|
+
*
|
|
85
|
+
* `slotPose` is the pose (in world coordinates) the node should snap to
|
|
86
|
+
* within the target. `metadata` is an opaque pass-through for app-specific
|
|
87
|
+
* snap details (slot index, visual hint, etc.).
|
|
88
|
+
*/
|
|
89
|
+
interface SnapTarget<TPose = unknown> {
|
|
90
|
+
parentId: string;
|
|
91
|
+
slotPose: TPose;
|
|
92
|
+
metadata?: unknown;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Full scene adapter. Most consumers implement narrow per-hook subsets
|
|
96
|
+
* (MoveAdapter, ResizeAdapter, ClipboardAdapter, ...) — TypeScript's
|
|
97
|
+
* structural typing means a wider adapter satisfies any narrower interface.
|
|
98
|
+
*
|
|
99
|
+
* **Pose semantics:** `getPose` / `setPose` work in **local** coordinates —
|
|
100
|
+
* relative to the node's direct parent (or world, for root-parented nodes).
|
|
101
|
+
* The kit composes world poses via `composeWorldPose` when it needs to
|
|
102
|
+
* render, hit-test, or snap. For the common axis-aligned rect pose,
|
|
103
|
+
* `composeRectPose` / `decomposeRectPose` ship as the canonical compose pair.
|
|
104
|
+
*/
|
|
105
|
+
interface SceneAdapter<TNode extends {
|
|
106
|
+
id: string;
|
|
107
|
+
}, TPose> {
|
|
108
|
+
getNodes(): TNode[];
|
|
109
|
+
getNode(id: string): TNode | undefined;
|
|
110
|
+
getSelection(): string[];
|
|
111
|
+
hitTest(worldX: number, worldY: number): string | null;
|
|
112
|
+
getPose(id: string): TPose;
|
|
113
|
+
getParent(id: string): string | null;
|
|
114
|
+
setPose(id: string, pose: TPose): void;
|
|
115
|
+
setParent(id: string, parentId: string | null): void;
|
|
116
|
+
insertNode(node: TNode): void;
|
|
117
|
+
removeNode(id: string): void;
|
|
118
|
+
setSelection(ids: string[]): void;
|
|
119
|
+
applyOps?(ops: Op[], label?: string): void;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Narrow adapter for `useMove`. Includes optional snap-target
|
|
123
|
+
* lookup; apps without container-snapping leave it out.
|
|
124
|
+
*/
|
|
125
|
+
interface MoveAdapter<TNode extends {
|
|
126
|
+
id: string;
|
|
127
|
+
}, TPose> {
|
|
128
|
+
getNode(id: string): TNode | undefined;
|
|
129
|
+
/** Enumerate all nodes. `<Canvas>` derives a default rect-pose `pickEvery`
|
|
130
|
+
* and the scene-iteration loop from this. */
|
|
131
|
+
getNodes(): TNode[];
|
|
132
|
+
getPose(id: string): TPose;
|
|
133
|
+
/** Optional. Required only by hierarchy-aware paths: layout-pass drop
|
|
134
|
+
* targeting (`getLayout` present), nested-hit collapse
|
|
135
|
+
* (`pickTopMostHit`), and group-pose composition. Flat scenes may omit. */
|
|
136
|
+
getParent?(id: string): string | null;
|
|
137
|
+
setPose(id: string, pose: TPose): void;
|
|
138
|
+
/** Optional. Used only by reparent ops (e.g. drag-into-container drops via
|
|
139
|
+
* layout strategies). Flat scenes that never reparent may omit. */
|
|
140
|
+
setParent?(id: string, parentId: string | null): void;
|
|
141
|
+
/** Optional: see SceneAdapter.applyOps. */
|
|
142
|
+
applyOps?(ops: Op[], label: string): void;
|
|
143
|
+
findSnapTarget?(draggedId: string, worldX: number, worldY: number): SnapTarget<TPose> | null;
|
|
144
|
+
/** Optional: direct children of `id`. When present (alongside the
|
|
145
|
+
* `cascadeWorldPose` option on `useMove`), dragging a node
|
|
146
|
+
* auto-cascades its descendants in the live overlay so structurally-
|
|
147
|
+
* grouped children visually follow the parent during the drag. No
|
|
148
|
+
* additional ops are generated — children's local poses don't change
|
|
149
|
+
* when the parent's local pose moves. */
|
|
150
|
+
getChildren?(id: string): string[];
|
|
151
|
+
/** Optional: layout strategy attached to a container, or null if the
|
|
152
|
+
* container uses absolute positioning (default behavior). When present,
|
|
153
|
+
* `useMove` uses the strategy to compute drop targets, sibling reflow,
|
|
154
|
+
* and the commit op batch when a drag ends over the container. */
|
|
155
|
+
getLayout?(containerId: string): LayoutStrategy<TPose> | null;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Narrow adapter for `useResize`. Mirrors `MoveAdapter`'s shape
|
|
159
|
+
* minus reparenting and snap-target lookup.
|
|
160
|
+
* TPose is constrained to { x, y, width, height } inline to avoid a circular
|
|
161
|
+
* import with interactions/types.ts.
|
|
162
|
+
*/
|
|
163
|
+
interface ResizeAdapter<TNode extends {
|
|
164
|
+
id: string;
|
|
165
|
+
}, TPose> {
|
|
166
|
+
getNode(id: string): TNode | undefined;
|
|
167
|
+
getPose(id: string): TPose;
|
|
168
|
+
setPose(id: string, pose: TPose): void;
|
|
169
|
+
/** Optional: see SceneAdapter.applyOps. */
|
|
170
|
+
applyOps?(ops: Op[], label: string): void;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Narrow adapter for `useRotate`. Mirrors `ResizeAdapter`'s shape — rotate
|
|
174
|
+
* mutates pose only and never reparents. TPose is left fully generic so
|
|
175
|
+
* non-rect rotated poses (e.g. a `RotatedPath`) compose without changes.
|
|
176
|
+
*/
|
|
177
|
+
interface RotateAdapter<TNode extends {
|
|
178
|
+
id: string;
|
|
179
|
+
}, TPose> {
|
|
180
|
+
getNode(id: string): TNode | undefined;
|
|
181
|
+
getPose(id: string): TPose;
|
|
182
|
+
setPose(id: string, pose: TPose): void;
|
|
183
|
+
/** Optional: see SceneAdapter.applyOps. */
|
|
184
|
+
applyOps?(ops: Op[], label: string): void;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Narrow adapter for `useAreaSelect`. Transient: no checkpoint, no
|
|
188
|
+
* history. The hook calls `applyOps(ops)` instead of `applyOps(ops, label)`.
|
|
189
|
+
*/
|
|
190
|
+
/**
|
|
191
|
+
* Narrow adapter for `useAreaSelect`. Transient: no checkpoint, no
|
|
192
|
+
* history. The hook calls `applyOps(ops)` instead of `applyOps(ops, label)`.
|
|
193
|
+
*
|
|
194
|
+
* All fields are **optional** — area-select is opt-in. `useSelectTool` only
|
|
195
|
+
* wires the default marquee behavior when `hitTestArea`, `getSelection`,
|
|
196
|
+
* `setSelection`, and `applyOps` are all present on the adapter; otherwise
|
|
197
|
+
* the gesture short-circuits (empty-space drags do nothing, sub-threshold
|
|
198
|
+
* empty clicks still clear selection via the tool's `onClick` path).
|
|
199
|
+
* Demos that don't need marquee selection can omit these methods entirely.
|
|
200
|
+
*/
|
|
201
|
+
interface AreaSelectAdapter {
|
|
202
|
+
/** Returns ids of nodes intersecting the world-space rect. */
|
|
203
|
+
hitTestArea?(rect: {
|
|
204
|
+
x: number;
|
|
205
|
+
y: number;
|
|
206
|
+
width: number;
|
|
207
|
+
height: number;
|
|
208
|
+
}): string[];
|
|
209
|
+
/** Current selection — read by behaviors to compute additive merges. */
|
|
210
|
+
getSelection?(): string[];
|
|
211
|
+
/** Mutator wired by `setSelection` op. */
|
|
212
|
+
setSelection?(ids: string[]): void;
|
|
213
|
+
/** Apply ops without checkpointing or pushing a history entry. The optional
|
|
214
|
+
* `label` parameter is accepted (and ignored by transient implementations)
|
|
215
|
+
* so that this signature is compatible with `MoveAdapter.applyOps`. */
|
|
216
|
+
applyOps?(ops: Op[], label?: string): void;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Hit mode for `LassoSelectAdapter.hitTestLasso`:
|
|
220
|
+
* - 'centers' — bounds center inside polygon.
|
|
221
|
+
* - 'intersect' — bounds intersect polygon (any overlap).
|
|
222
|
+
* - 'enclosed' — bounds fully inside polygon.
|
|
223
|
+
*/
|
|
224
|
+
type LassoHitMode = 'centers' | 'intersect' | 'enclosed';
|
|
225
|
+
/**
|
|
226
|
+
* Narrow adapter for `useLassoSelect`. Extends `AreaSelectAdapter` with a
|
|
227
|
+
* polygon hit-test method. Transient — uses `applyOps` like its rectangular
|
|
228
|
+
* sibling. `hitTestLasso` is optional; when omitted, `useLassoTool` skips
|
|
229
|
+
* wiring the default behavior (same opt-in pattern as `hitTestArea`).
|
|
230
|
+
*/
|
|
231
|
+
interface LassoSelectAdapter extends AreaSelectAdapter {
|
|
232
|
+
/** Hit-test against a closed polygon. Vertex order may be CW or CCW; the
|
|
233
|
+
* closing edge from last → first is implicit. */
|
|
234
|
+
hitTestLasso?(polygon: ReadonlyArray<{
|
|
235
|
+
x: number;
|
|
236
|
+
y: number;
|
|
237
|
+
}>, mode: LassoHitMode): string[];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Narrow adapter for `useInsert` and `useClipboardOps`. The kit knows
|
|
241
|
+
* nothing about what tool is active or what shape to construct; it asks the
|
|
242
|
+
* adapter to produce node(s) given gesture or paste inputs.
|
|
243
|
+
*
|
|
244
|
+
* Drag-rectangle path: `commitInsert(bounds)` returns one new node or null.
|
|
245
|
+
* Clipboard paste path: `commitPaste(clipboard, offset)` returns the array of
|
|
246
|
+
* newly-materialized nodes (in order). Both empty array and array of
|
|
247
|
+
* length N are valid; the kit wraps each entry in an `InsertOp`.
|
|
248
|
+
*
|
|
249
|
+
* `snapshotSelection(ids)` builds the payload that paste later consumes.
|
|
250
|
+
* `getPasteOffset` is optional; the kit defaults to a fixed grid-cell offset
|
|
251
|
+
* supplied by the consumer (passed to `useClipboardOps` options if needed; see
|
|
252
|
+
* the hook for resolution order).
|
|
253
|
+
*/
|
|
254
|
+
interface InsertAdapter<TNode extends {
|
|
255
|
+
id: string;
|
|
256
|
+
}> {
|
|
257
|
+
/** Materialize a new node from drag-rect bounds (drag-to-insert tools).
|
|
258
|
+
* Optional — hooks that don't drive insertion (e.g. `useClone`,
|
|
259
|
+
* read-only clipboard) won't call it, and `sceneToAdapter` only fills
|
|
260
|
+
* it in when `options.commitInsert` is supplied. Hooks that *do* call
|
|
261
|
+
* it (insert tools) document the requirement at their own surface. */
|
|
262
|
+
commitInsert?(bounds: {
|
|
263
|
+
x: number;
|
|
264
|
+
y: number;
|
|
265
|
+
width: number;
|
|
266
|
+
height: number;
|
|
267
|
+
}): TNode | null;
|
|
268
|
+
/** Materialize one or more new nodes from a clipboard snapshot. Optional
|
|
269
|
+
* — required by `useClipboard.paste`, ignored by other consumers. */
|
|
270
|
+
commitPaste?(clipboard: ClipboardSnapshot, offset: {
|
|
271
|
+
dx: number;
|
|
272
|
+
dy: number;
|
|
273
|
+
}, ctx?: {
|
|
274
|
+
dropPoint?: {
|
|
275
|
+
worldX: number;
|
|
276
|
+
worldY: number;
|
|
277
|
+
};
|
|
278
|
+
}): TNode[];
|
|
279
|
+
/** Snapshot the current selection into the clipboard payload shape.
|
|
280
|
+
* Optional — required by `useClipboard.copy` / `cut` and `useClone`'s
|
|
281
|
+
* ghost capture, ignored by other consumers. */
|
|
282
|
+
snapshotSelection?(ids: string[]): ClipboardSnapshot;
|
|
283
|
+
getPasteOffset?(clipboard: ClipboardSnapshot): {
|
|
284
|
+
dx: number;
|
|
285
|
+
dy: number;
|
|
286
|
+
};
|
|
287
|
+
/** Mutator wired by `insertNode`-using ops (kit-side InsertOp). */
|
|
288
|
+
insertNode(node: TNode): void;
|
|
289
|
+
/** Mutator wired by `setSelection` ops batched alongside paste. */
|
|
290
|
+
setSelection(ids: string[]): void;
|
|
291
|
+
/** Optional: see SceneAdapter.applyOps. */
|
|
292
|
+
applyOps?(ops: Op[], label: string): void;
|
|
293
|
+
/** Returns the current selection. Used by clone behaviors. */
|
|
294
|
+
getSelection(): string[];
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Optional surface that lets `buildSceneTree` walk the adapter by layer.
|
|
298
|
+
* Adapters that don't implement this fall back to flat rendering in Canvas's
|
|
299
|
+
* scene layer.
|
|
300
|
+
*/
|
|
301
|
+
interface LayerEnumerableAdapter<TLayer extends string = string> {
|
|
302
|
+
getLayers?(): readonly {
|
|
303
|
+
id: TLayer;
|
|
304
|
+
visible: boolean;
|
|
305
|
+
}[];
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Optional adapter mixin for sibling z-order.
|
|
309
|
+
*
|
|
310
|
+
* Both methods are optional. Reorder ops and `useReorder` no-op when
|
|
311
|
+
* either is absent — adopt z-order incrementally without breaking existing
|
|
312
|
+
* adapters.
|
|
313
|
+
*
|
|
314
|
+
* **Convention:** array order IS z-order. Index 0 is the bottom of the
|
|
315
|
+
* stack, the last index is the top. Hit-testing should iterate the returned
|
|
316
|
+
* list in REVERSE (top to bottom). Render layers iterate FORWARD (bottom to
|
|
317
|
+
* top).
|
|
318
|
+
*
|
|
319
|
+
* For groups, `parentId` may be a group id; the group adapter routes
|
|
320
|
+
* `getChildren`/`setChildOrder` to the group's `members[]` array.
|
|
321
|
+
*/
|
|
322
|
+
interface OrderedAdapter {
|
|
323
|
+
/** Ordered children of `parentId` (or root siblings if null), in z-order:
|
|
324
|
+
* index 0 is bottom, last index is top. */
|
|
325
|
+
getChildren?(parentId: string | null): string[];
|
|
326
|
+
/** Rewrite the order of `parentId`'s children. Length and contents must
|
|
327
|
+
* match the existing children — reorder only, no add/remove. */
|
|
328
|
+
setChildOrder?(parentId: string | null, ids: string[]): void;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export type { AreaSelectAdapter as A, ClipboardSnapshot as C, DropTarget as D, InsertAdapter as I, LayoutStrategy as L, MoveAdapter as M, OrderedAdapter as O, RotateAdapter as R, SnapTarget as S, LassoHitMode as a, LassoSelectAdapter as b, ResizeAdapter as c, SceneAdapter as d, LayerEnumerableAdapter as e, LayoutSnap as f, ContainerBounds as g, LayoutChild as h, LayoutContainer as i, LayoutDragged as j };
|