@weasel-js/guides 1.13.0-rc.0 → 1.14.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/README.md CHANGED
@@ -27,24 +27,24 @@ A grid you can see and snap to:
27
27
 
28
28
  ```tsx
29
29
  import { SceneCanvas } from '@weasel-js/core';
30
- import { createGridLayer, gridSnapSource } from '@weasel-js/guides';
30
+ import { createGridPreset } from '@weasel-js/guides';
31
31
 
32
- const grid = createGridLayer({ spacing: 20, bounds: () => ({ x: 0, y: 0, width: 800, height: 600 }) });
33
- const snap = { sources: [gridSnapSource({ spacing: 20 })] };
32
+ const grid = createGridPreset({ spacing: 20 });
34
33
 
35
- <SceneCanvas
36
- /* … */
37
- snap={snap}
38
- layers={{ grid: { layer: grid } }}
39
- />;
34
+ <SceneCanvas /* … */ features={['draw', grid]} />;
40
35
  ```
41
36
 
42
- The grid source snaps every snapping gesture — move, resize, insert, the pen —
43
- and, being a fallback, gives way on any axis a real target in reach claims.
37
+ The preset draws the grid in the `grid` slot and snaps every snapping gesture
38
+ to it — move, resize, insert, the pen — and, being a fallback, its snap gives
39
+ way on any axis a real target in reach claims. It takes every `createGridLayer`
40
+ option, and `snap: false` to draw without snapping. The layer and the snap
41
+ source it is built from, `createGridLayer` and `gridSnapSource`, go to
42
+ `<SceneCanvas layers>` and `snap.sources` on their own where a canvas wants
43
+ only one of them.
44
44
 
45
45
  | Area | Exports |
46
46
  |---|---|
47
- | Grid | `createGridLayer`, `createCellHighlightLayer`, `useGridCellHover`, `pointToGridCell`, `roundToCell` |
47
+ | Grid | `createGridPreset`, `createGridLayer`, `createCellHighlightLayer`, `useGridCellHover`, `pointToGridCell`, `roundToCell` |
48
48
  | Guides | `useGuides`, `createGuidesLayer`, `deriveAlignmentGuides`, `matchSpacing`, `measureGaps`, `siblingBounds`, `worldBoundsOf`, `worldUnionOf` |
49
49
  | Snap sources | `gridSnapSource`, `guideSnapSource`, `alignmentSnapSource`, `spacingSnapSource`, for `<SceneCanvas snap={{ sources }}>` or a preset's `snapSources` |
50
50
  | Spacing hints | `createSpacingHintsPreset` (below), `distanceReadout`, `measureDistances` |
package/dist/index.d.ts CHANGED
@@ -1,10 +1,94 @@
1
1
  import { RefObject } from 'react';
2
- import { ViewTransform, PlaneValue, PlaneUnits, View, Dims, CommandStroke, RenderLayer, CommandFill, UnitScope, PerAxis, Bounds, PlaneMap, PoseDescriptor, Scene, NodeId, SnapSource, SceneNode, SurfaceContribution, FeaturePreset, LayoutSnap, LayoutStrategy, LayoutContainer, Vec2, ContainerBounds, LayoutChild } from '@weasel-js/core';
3
- import { Rect } from '@weasel-js/geom';
2
+ import { UnitScope, Scene, NodeId, PlaneValue, PlaneUnits, PerAxis, ViewTransform, View, Dims, CommandStroke, RenderLayer, CommandFill, SnapSource, FeaturePreset, Bounds, PlaneMap, PoseDescriptor, SceneNode, SurfaceContribution, LayoutSnap, LayoutStrategy, LayoutContainer, Vec2, ContainerBounds, LayoutChild } from '@weasel-js/core';
3
+ import { Affine, Rect as Rect$1 } from '@weasel-js/geom';
4
4
 
5
5
  /** Round `value` to the nearest multiple of `cellSize`. Returns 0 when the result would be -0. */
6
6
  declare function roundToCell(value: number, cellSize: number): number;
7
7
 
8
+ type Point$1 = {
9
+ x: number;
10
+ y: number;
11
+ };
12
+ /** A container that declares its own unit scope, where a grid draws and
13
+ * hit-tests that scope's lattice instead of its parent's. */
14
+ interface GridRegion {
15
+ /** The container's outline in world units, as a closed polygon. */
16
+ outline: readonly Point$1[];
17
+ /** The scopes its lattice is read in, outermost first, rooted at the
18
+ * grid's `scopes.root`, the container's own scope last. */
19
+ chain: readonly UnitScope[];
20
+ /** Scoped containers inside this one. */
21
+ regions?: readonly GridRegion[];
22
+ /** The container's frame, its own coordinates to world (`sceneBoxFrames`):
23
+ * its lattice runs from the frame's origin along its axes, in its units,
24
+ * as `gridSnapSource` snaps. Absent, the lattice is world-aligned and runs
25
+ * through the grid's anchor. */
26
+ frame?: Affine;
27
+ }
28
+ /**
29
+ * Every container in `scene` declaring a unit scope, as `GridRegion`s nested
30
+ * the way the containers are, each in its box frame. `outlineOf` gives a
31
+ * node's world outline, or `null` to leave it, and every scope inside it,
32
+ * out; by default, a box pose's outline as it is drawn, and none for any
33
+ * other pose.
34
+ */
35
+ declare function sceneGridRegions(scene: Scene<unknown, string, unknown>, root: UnitScope, outlineOf?: (id: NodeId) => readonly Point$1[] | null): GridRegion[];
36
+ /** The innermost region holding `point`, or `null` outside them all. */
37
+ declare function regionAt(regions: readonly GridRegion[] | undefined, point: Point$1): GridRegion | null;
38
+
39
+ /** How a grid reads the unit scopes a scene's containers declare
40
+ * (`GroupNode.units`). */
41
+ interface GridScopes {
42
+ /** The scene's outermost scope, as `sceneUnitScopes` is handed it. The grid's
43
+ * `spacing` is read in its system, in place of `unitSystem`. */
44
+ root: UnitScope;
45
+ /** The pitch inside a container's scope, written in that scope's units.
46
+ * Default: the grid's `spacing`, read there, so a bare 10 is ten of its
47
+ * base units. */
48
+ spacing?: (scope: UnitScope) => PlaneValue;
49
+ }
50
+ /**
51
+ * A grid's pitch on each axis, in world units, where `chain` (outermost
52
+ * first, rooted at `scopes.root`) encloses it: `spacing` in the root, and
53
+ * `scopes.spacing` of the innermost scope inside a container's.
54
+ */
55
+ declare function scopedGridPitch(spacing: PlaneValue, scopes: GridScopes, chain: readonly UnitScope[]): PerAxis<number>;
56
+ /**
57
+ * A grid's pitch in world units: read in `chain` (the root alone by default)
58
+ * when `scopes` is given, else against `unitSystem`. What every grid reader
59
+ * that takes both asks.
60
+ */
61
+ declare function gridPitch(spacing: PlaneValue, unitSystem: PlaneUnits | undefined, scopes: GridScopes | undefined, chain?: readonly UnitScope[]): PerAxis<number>;
62
+
63
+ /** A cell of a grid. `pitch` names the lattice it belongs to, in world units,
64
+ * where a grid reads unit scopes; without it, the cell is the grid's own.
65
+ * `frame` is that lattice's region's frame, the cell counted from its origin. */
66
+ interface GridCell {
67
+ col: number;
68
+ row: number;
69
+ pitch?: PerAxis<number>;
70
+ frame?: Affine;
71
+ }
72
+ /** Where a grid reads unit scopes: `scopes`, and the containers declaring them. */
73
+ interface GridScoping {
74
+ scopes: GridScopes;
75
+ regions?: readonly GridRegion[];
76
+ }
77
+ /**
78
+ * The integer cell `{col, row}` that contains `point`. Pair with
79
+ * `createCellHighlightLayer` (its `getCell` callback) to draw a snap-target
80
+ * preview that uses the same spacing as `gridSnapSource`. Given `scoped`,
81
+ * the cell is on the lattice of the innermost region holding `point`, and
82
+ * carries that lattice's `pitch`, and its `frame` where the region has one.
83
+ */
84
+ declare function pointToGridCell(point: {
85
+ x: number;
86
+ y: number;
87
+ }, spacing: PlaneValue, unitSystem?: PlaneUnits, origin?: {
88
+ x: number;
89
+ y: number;
90
+ }, scoped?: GridScoping): GridCell;
91
+
8
92
  /**
9
93
  * Pointer-tracking glue that turns hover position into a grid cell, suitable
10
94
  * for dropping straight into `createCellHighlightLayer({ getCell })`.
@@ -30,32 +114,36 @@ interface UseGridCellHoverOptions {
30
114
  x: number;
31
115
  y: number;
32
116
  };
117
+ /** Read `spacing` in these scopes, as `createGridLayer` does: a cell inside
118
+ * one of `regions` is on its scope's lattice and carries that `pitch`. */
119
+ scopes?: GridScopes;
120
+ /** The containers declaring scopes, read on every pointermove. */
121
+ regions?: () => readonly GridRegion[];
33
122
  /** Fires when the hovered cell changes (including to/from `null`). Use it
34
123
  * to invalidate your layer renderer. */
35
- onChange?: (cell: {
36
- col: number;
37
- row: number;
38
- } | null) => void;
124
+ onChange?: (cell: GridCell | null) => void;
39
125
  /** Disable the listeners without unmounting. Default `true`. */
40
126
  enabled?: boolean;
41
127
  }
42
128
  /** Return shape of `useGridCellHover`. */
43
129
  interface UseGridCellHoverReturn {
44
130
  /** Current hovered cell, or `null` when the pointer is outside the element. */
45
- cell: {
46
- col: number;
47
- row: number;
48
- } | null;
131
+ cell: GridCell | null;
49
132
  /** Stable callback that returns the current cell — drop straight into
50
133
  * `createCellHighlightLayer({ getCell })`. */
51
- getCell: () => {
52
- col: number;
53
- row: number;
54
- } | null;
134
+ getCell: () => GridCell | null;
55
135
  }
56
136
  /** Track which grid cell the pointer is over; pairs with `createCellHighlightLayer`. */
57
137
  declare function useGridCellHover(opts: UseGridCellHoverOptions): UseGridCellHoverReturn;
58
138
 
139
+ /** An axis-aligned rectangle, in whatever coordinates a lattice is drawn in. */
140
+ type Rect = {
141
+ x: number;
142
+ y: number;
143
+ width: number;
144
+ height: number;
145
+ };
146
+
59
147
  /**
60
148
  * Grid overlay — a reusable render layer that draws a world-space grid with
61
149
  * optional finer subdivisions and accent lines every N cells.
@@ -77,18 +165,20 @@ interface GridLayerOpts {
77
165
  spacing: PlaneValue;
78
166
  /** Optional unit system for resolving tagged `spacing` values. */
79
167
  unitSystem?: PlaneUnits;
168
+ /** Read `spacing` in these scopes, in place of `unitSystem`: the root's
169
+ * system outside every region, and each region's own scope inside it. */
170
+ scopes?: GridScopes;
171
+ /** The containers drawing their own scope's lattice, read every paint,
172
+ * each cut out of the lattice around it and drawn in its own `frame`.
173
+ * Read only with `scopes`; `sceneGridRegions` builds them from a scene. */
174
+ regions?: () => readonly GridRegion[];
80
175
  /**
81
176
  * The area to cover, in world units, read every paint with that paint's
82
177
  * view and canvas size. Lines start at its own `x`/`y`. Omitted, the grid
83
178
  * covers whatever the canvas shows, with its lines on world multiples of
84
179
  * `spacing` so they stay put as the view pans.
85
180
  */
86
- bounds?: (view: View, dims: Dims) => {
87
- x: number;
88
- y: number;
89
- width: number;
90
- height: number;
91
- };
181
+ bounds?: (view: View, dims: Dims) => Rect;
92
182
  /** Lines every N cells get the accent style. 0/undef = no accent. */
93
183
  accentEvery?: number;
94
184
  /** Optional finer subdivisions per cell. e.g. 4 -> 4 sub-lines per cell. */
@@ -120,50 +210,57 @@ interface CellHighlightLayerOpts {
120
210
  x: number;
121
211
  y: number;
122
212
  };
123
- /** Cell to highlight, or `null` to skip drawing. */
124
- getCell: () => {
125
- col: number;
126
- row: number;
127
- } | null;
213
+ /** Cell to highlight, or `null` to skip drawing. A cell carrying its
214
+ * lattice's `pitch` (`useGridCellHover` with `scopes`) is drawn at it,
215
+ * and one carrying a `frame` is drawn in it, from its origin. */
216
+ getCell: () => GridCell | null;
128
217
  /** Fill for the highlighted cell. Defaults to a soft green. */
129
218
  fill?: CommandFill;
130
219
  }
131
220
  /** Build a `RenderLayer` that fills a single grid cell — typically a snap-target preview. */
132
221
  declare function createCellHighlightLayer(opts: CellHighlightLayerOpts): RenderLayer<unknown>;
133
222
 
134
- /**
135
- * The integer cell `{col, row}` that contains `point`. Pair with
136
- * `createCellHighlightLayer` (its `getCell` callback) to draw a snap-target
137
- * preview that uses the same spacing as `gridSnapSource`.
138
- */
139
- declare function pointToGridCell(point: {
140
- x: number;
141
- y: number;
142
- }, spacing: PlaneValue, unitSystem?: PlaneUnits, origin?: {
223
+ type Point = {
143
224
  x: number;
144
225
  y: number;
145
- }): {
146
- col: number;
147
- row: number;
148
226
  };
227
+ /** Options for `gridSnapSource`. */
228
+ interface GridSnapSourceOptions {
229
+ /** The grid's pitch, or one per axis, in `unitSystem`'s units. */
230
+ spacing: PlaneValue;
231
+ /** A point the lattice passes through. Default `(0, 0)`. */
232
+ origin?: Point;
233
+ unitSystem?: PlaneUnits;
234
+ /** Read the unit scope the subject sits in: a node inside a millimeter
235
+ * sub-assembly snaps on that scope's pitch. The subject's scope is the one
236
+ * all its nodes share; for a gesture with none, the scope of the
237
+ * container it draws into (`SnapCollect.parent`), else the root. When that
238
+ * scope is one container's, the lattice runs in its frame
239
+ * (`SnapCollect.frameOf`): from its corner, turned as it is. */
240
+ scopes?: GridScopes;
241
+ }
242
+ /**
243
+ * A grid as snap targets: for each origin or pointer probe, the grid line on
244
+ * either side of it on each axis. Its reach is unlimited and it is a
245
+ * fallback, so it holds every axis no target claims and never displaces one.
246
+ * Its lines are the camera's, carried into the plane a gesture runs in.
247
+ */
248
+ declare function gridSnapSource(opts: GridSnapSourceOptions): SnapSource;
149
249
 
150
- /** How a grid reads the unit scopes a scene's containers declare
151
- * (`GroupNode.units`). */
152
- interface GridScopes {
153
- /** The scene's outermost scope, as `sceneUnitScopes` is handed it. The grid's
154
- * `spacing` is read in its system, in place of `unitSystem`. */
155
- root: UnitScope;
156
- /** The pitch inside a container's scope, written in that scope's units.
157
- * Default: the grid's `spacing`, read there, so a bare 10 is ten of its
158
- * base units. */
159
- spacing?: (scope: UnitScope) => PlaneValue;
250
+ /** Options for `createGridPreset`: the grid's, and how it snaps. */
251
+ interface GridPresetOptions extends GridLayerOpts {
252
+ /** Preset id. Default `'grid'`. */
253
+ id?: string;
254
+ /** Snap gestures to the grid's lines, on its `spacing`, `unitSystem` and
255
+ * `scopes`; `origin` is where the snap lattice starts. `false` draws the
256
+ * grid without snapping to it. Default on, from `(0, 0)`. */
257
+ snap?: false | Pick<GridSnapSourceOptions, 'origin'>;
160
258
  }
161
259
  /**
162
- * A grid's pitch on each axis, in world units, where `chain` (outermost
163
- * first, rooted at `scopes.root`) encloses it: `spacing` in the root, and
164
- * `scopes.spacing` of the innermost scope inside a container's.
260
+ * A grid as one `<SceneCanvas features>` preset: the lattice drawn in the
261
+ * grid slot, and the snap source that pulls gestures onto it.
165
262
  */
166
- declare function scopedGridPitch(spacing: PlaneValue, scopes: GridScopes, chain: readonly UnitScope[]): PerAxis<number>;
263
+ declare function createGridPreset(opts: GridPresetOptions): FeaturePreset;
167
264
 
168
265
  /**
169
266
  * Guide — a horizontal or vertical world-space line used as a snap target
@@ -361,31 +458,6 @@ declare function worldUnionOf(scene: Scene<unknown, string, unknown>, ids: Itera
361
458
  */
362
459
  declare function siblingBounds(scene: Scene<unknown, string, unknown>, ids: readonly NodeId[], opts?: SceneBoundsOptions): Bounds[];
363
460
 
364
- type Point = {
365
- x: number;
366
- y: number;
367
- };
368
- /** Options for `gridSnapSource`. */
369
- interface GridSnapSourceOptions {
370
- /** The grid's pitch, or one per axis, in `unitSystem`'s units. */
371
- spacing: PlaneValue;
372
- /** A point the lattice passes through. Default `(0, 0)`. */
373
- origin?: Point;
374
- unitSystem?: PlaneUnits;
375
- /** Read the unit scope the subject sits in: a node inside a millimeter
376
- * sub-assembly snaps on that scope's pitch. The subject's scope is the one
377
- * all its nodes share; for a gesture with none, the scope of the
378
- * container it draws into (`SnapCollect.parent`), else the root. */
379
- scopes?: GridScopes;
380
- }
381
- /**
382
- * A grid as snap targets: for each origin or pointer probe, the grid line on
383
- * either side of it on each axis. Its reach is unlimited and it is a
384
- * fallback, so it holds every axis no target claims and never displaces one.
385
- * Its lines are the camera's, carried into the plane a gesture runs in.
386
- */
387
- declare function gridSnapSource(opts: GridSnapSourceOptions): SnapSource;
388
-
389
461
  /**
390
462
  * Ruler guides as snap targets: each guide within reach of an origin or
391
463
  * pointer probe is a line it may land on. Reads `getGuides` every frame, so
@@ -613,7 +685,7 @@ interface TileGridOptions<TPose> {
613
685
  * writes `{ x, y, width, height }` into the dragged pose (assumes
614
686
  * `RectPose`). `dragged` is the pre-drop pose of the child being placed —
615
687
  * use it to preserve fields the cell rect doesn't carry (e.g. rotation). */
616
- cellToPose?(cellRect: Rect, dragged: TPose): TPose;
688
+ cellToPose?(cellRect: Rect$1, dragged: TPose): TPose;
617
689
  /** Optional: the point that decides which cell a child occupies — a child
618
690
  * occupies the cell containing it. Default reads `x`/`y` plus half of
619
691
  * `width`/`height` when the pose has them, which is the center of a
@@ -749,7 +821,7 @@ interface PackOptions<TPose> {
749
821
  /** Which index a drag means. Default `nearest()`. */
750
822
  snap?: LayoutSnap<TPose>;
751
823
  /** A child's box. Default reads `x`, `y`, `width`, and `height` off the pose. */
752
- rectOf?(pose: TPose): Rect;
824
+ rectOf?(pose: TPose): Rect$1;
753
825
  /** `pose` with its box's top-left at `at`. Default writes `x` and `y`. */
754
826
  moveTo?(pose: TPose, at: {
755
827
  x: number;
@@ -778,7 +850,7 @@ interface QuadtreeOptions<TPose> {
778
850
  sizeOf?(pose: TPose): number;
779
851
  /** The pose a child takes in a cell. Default centers the child's own size
780
852
  * in the cell and keeps every other field. */
781
- cellToPose?(cell: Rect, pose: TPose): TPose;
853
+ cellToPose?(cell: Rect$1, pose: TPose): TPose;
782
854
  /** The point that decides which cell a child sits in. Default is the
783
855
  * center of a `RectPose`, and the point itself for a point pose. */
784
856
  centerOf?(pose: TPose): {
@@ -791,7 +863,7 @@ interface QuadtreeCell {
791
863
  id: string;
792
864
  /** How many times the container was quartered to reach the cell. */
793
865
  depth: number;
794
- rect: Rect;
866
+ rect: Rect$1;
795
867
  }
796
868
  /** A `quadtree` layout, which also reports the cells it has given out. */
797
869
  interface QuadtreeStrategy<TPose> extends LayoutStrategy<TPose> {
@@ -841,4 +913,4 @@ interface Sequencer<TPose> {
841
913
  */
842
914
  declare function sequenceLayout<TPose>(seq: Sequencer<TPose>): LayoutStrategy<TPose>;
843
915
 
844
- export { type AlignmentSnapSourceOptions, type Arrangement, type CellHighlightLayerOpts, type DeriveAlignmentGuidesOptions, type DistanceReadout, type DistanceReadoutContribution, type DistanceReadoutOptions, type FreeformOptions, type GridLayerOpts, type GridScopes, type GridSlotsOptions, type GridSnapSourceOptions, type Guide, type GuidesLayerOpts, type PackOptions, type PackOverflow, type PerContainerOptions, type PerContainerStrategy, type QuadtreeCell, type QuadtreeOptions, type QuadtreeStrategy, type RingSlotsOptions, type RowSlotsOptions, type SceneBoundsOptions, type Sequencer, type Slot, type SlotsOptions, type SnapPattern, type SnapPointOptions, type SpacingEdge, type SpacingGap, type SpacingHintsOptions, type SpacingMatchResult, type SpacingSnapSourceOptions, type TargetFilter, type TileGridOptions, type TileGridOverflow, type UseGridCellHoverOptions, type UseGridCellHoverReturn, type UseGuidesReturn, alignmentSnapSource, cellAt, containedThenNearest, createCellHighlightLayer, createGridLayer, createGuidesLayer, createSpacingHintsPreset, deriveAlignmentGuides, distanceReadout, freeform, gridSlots, gridSnapSource, guideSnapSource, matchSpacing, measureDistances, measureGaps, nearest, nearestWithin, none, pack, perContainer, pointToGridCell, quadtree, ringSlots, roundToCell, rowSlots, scopedGridPitch, sequenceLayout, siblingBounds, slots, snapPoint, spacingSnapSource, tileGrid, useGridCellHover, useGuides, worldBoundsOf, worldUnionOf };
916
+ export { type AlignmentSnapSourceOptions, type Arrangement, type CellHighlightLayerOpts, type DeriveAlignmentGuidesOptions, type DistanceReadout, type DistanceReadoutContribution, type DistanceReadoutOptions, type FreeformOptions, type GridCell, type GridLayerOpts, type GridPresetOptions, type GridRegion, type GridScopes, type GridScoping, type GridSlotsOptions, type GridSnapSourceOptions, type Guide, type GuidesLayerOpts, type PackOptions, type PackOverflow, type PerContainerOptions, type PerContainerStrategy, type QuadtreeCell, type QuadtreeOptions, type QuadtreeStrategy, type RingSlotsOptions, type RowSlotsOptions, type SceneBoundsOptions, type Sequencer, type Slot, type SlotsOptions, type SnapPattern, type SnapPointOptions, type SpacingEdge, type SpacingGap, type SpacingHintsOptions, type SpacingMatchResult, type SpacingSnapSourceOptions, type TargetFilter, type TileGridOptions, type TileGridOverflow, type UseGridCellHoverOptions, type UseGridCellHoverReturn, type UseGuidesReturn, alignmentSnapSource, cellAt, containedThenNearest, createCellHighlightLayer, createGridLayer, createGridPreset, createGuidesLayer, createSpacingHintsPreset, deriveAlignmentGuides, distanceReadout, freeform, gridPitch, gridSlots, gridSnapSource, guideSnapSource, matchSpacing, measureDistances, measureGaps, nearest, nearestWithin, none, pack, perContainer, pointToGridCell, quadtree, regionAt, ringSlots, roundToCell, rowSlots, sceneGridRegions, scopedGridPitch, sequenceLayout, siblingBounds, slots, snapPoint, spacingSnapSource, tileGrid, useGridCellHover, useGuides, worldBoundsOf, worldUnionOf };