@weasel-js/guides 1.7.0 → 1.7.2

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/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { RefObject } from 'react';
2
2
  import { ViewTransform, UnitValue, UnitSystem, Stroke, RenderLayer, FillStyle, SnapStrategy, DebugSink, OriginProjection, ModifierState, Bounds, PoseDescriptor, InsertBehavior, MoveBehavior, BoundsConstraint, LayoutSnap, LayoutStrategy, Vec2 } from '@weasel-js/core';
3
3
  export { p as pointSnapToGrid } from './pointSnapToGrid-BaVEjV23.js';
4
- import { G as Guide } from './types-D2tTKEU0.js';
4
+ import { G as Guide, S as SpacingGap } from './types-DRSPiXfk.js';
5
5
 
6
6
  /** Round `value` to the nearest multiple of `cellSize`. Returns 0 when the result would be -0. */
7
7
  declare function roundToCell(value: number, cellSize: number): number;
@@ -204,8 +204,10 @@ declare function useGuides(initial?: Guide[]): UseGuidesReturn;
204
204
 
205
205
  /**
206
206
  * Guides overlay — a screen-space render layer that draws each guide as a
207
- * thin line spanning the full canvas, projecting the guide's world offset
208
- * through the active view.
207
+ * thin line, projecting the guide's world offset through the active view.
208
+ * A guide with a `span` draws as a segment over that span with a tick at each
209
+ * end; one without spans the full canvas. It also draws equal-spacing gaps
210
+ * (`SpacingGap`) as ticked segments labeled with their size.
209
211
  *
210
212
  * Guides are not scene objects; they live outside the scene and are managed
211
213
  * via `useGuides`. Pair with `guideSnapStrategy` (snap behavior) to keep
@@ -216,10 +218,24 @@ declare function useGuides(initial?: Guide[]): UseGuidesReturn;
216
218
  interface GuidesLayerOpts {
217
219
  /** Stable getter for the live guide list — typically `useGuides().getGuides`. */
218
220
  getGuides: () => readonly Guide[];
221
+ /** Stable getter for the equal-spacing gaps to draw — typically what an
222
+ * alignment behavior's `setActiveGaps` last published. */
223
+ getGaps?: () => readonly SpacingGap[];
224
+ /** `'span'` (default) draws a guide that carries a `span` as a segment over
225
+ * it; `'full'` draws every guide across the whole canvas. */
226
+ extent?: 'span' | 'full';
227
+ /** Length in CSS px of the tick across each end of a segment. 0 for none.
228
+ * Default 6. */
229
+ ticks?: number;
230
+ /** Label each gap with its size: `true` (default) prints it rounded to a
231
+ * whole world unit, a function formats it, `false` omits the label. */
232
+ gapLabels?: boolean | ((size: number) => string);
219
233
  /** Stroke width in CSS pixels. Default 1. */
220
234
  lineWidth?: number;
221
- /** Stroke color. Default a subtle blue. */
235
+ /** Stroke color, and the gap labels' fill. Default a subtle blue. */
222
236
  color?: string;
237
+ /** Gap marker color. Default `color`. */
238
+ gapColor?: string;
223
239
  /** Optional dash pattern (CSS px). Currently unused — reserved for future
224
240
  * parity with chrome that supports dashes. */
225
241
  dash?: number[];
@@ -229,8 +245,8 @@ interface GuidesLayerOpts {
229
245
  /** Override the layer label. */
230
246
  label?: string;
231
247
  }
232
- /** Build a `RenderLayer` that draws guide lines across the full canvas in
233
- * screen space, projected from world offsets via the active view. */
248
+ /** Build a `RenderLayer` that draws guide lines and spacing gaps in screen
249
+ * space, projected from world coordinates via the active view. */
234
250
  declare function createGuidesLayer(opts: GuidesLayerOpts): RenderLayer<unknown>;
235
251
 
236
252
  /** Which feature of a box to test against candidates, per axis.
@@ -245,6 +261,17 @@ interface AlignMatchResult {
245
261
  activeX: Guide | null;
246
262
  activeY: Guide | null;
247
263
  }
264
+ /** Which edges of a box move on one axis during a spacing match: `'both'`
265
+ * for a translate, `'min'`/`'max'` for a resize dragging that edge. */
266
+ type SpacingEdge = 'both' | 'min' | 'max';
267
+ /** The correction an equal-spacing match asks for, and the gaps to draw. */
268
+ interface SpacingMatchResult {
269
+ dx: number;
270
+ dy: number;
271
+ /** Every x gap equal to the one snapped to, the box's own included. */
272
+ gapsX: SpacingGap[];
273
+ gapsY: SpacingGap[];
274
+ }
248
275
  /** Which candidate lines to derive from a set of poses — edges, centers, or
249
276
  * both, and whether the page box contributes its own. */
250
277
  interface DeriveAlignmentGuidesOptions<TPose = Bounds> {
@@ -269,11 +296,20 @@ interface AlignmentBehaviorBase {
269
296
  tolerance?: number;
270
297
  /** Modifier key that bypasses snapping while held. */
271
298
  bypassKey?: keyof ModifierState;
299
+ /** Boxes to measure gaps between for equal-spacing snaps (`matchSpacing`):
300
+ * the siblings' visual bounds, without the dragged ones. Omit to snap to
301
+ * alignment lines only. On each axis the nearer of the two snaps wins.
302
+ * Move and unrotated resize use it; insert does not. */
303
+ getSpacingTargets?: () => readonly Bounds[];
304
+ /** Publish the equal-gap markers. Called with the guides; cleared (`[]`) on
305
+ * a miss and on onEnd. */
306
+ setActiveGaps?: (gaps: readonly SpacingGap[]) => void;
272
307
  }
273
308
 
274
309
  /** Derive candidate alignment lines from a set of sibling poses plus an
275
310
  * optional page box. Each box contributes up to 3 guides per axis: the two
276
- * edges and the center. Overlapping offsets collapse to one candidate.
311
+ * edges and the center. Overlapping offsets collapse to one candidate, whose
312
+ * `span` covers every box that produced it.
277
313
  * Poses go through the same descriptor `alignMoveBehavior` matches with, so
278
314
  * a rotated sibling advertises its ink edges rather than its stored box. */
279
315
  declare function deriveAlignmentGuides<TPose = Bounds>(targets: readonly TPose[], opts?: DeriveAlignmentGuidesOptions<TPose>): Guide[];
@@ -287,7 +323,9 @@ declare const MOVE_ANCHORS: {
287
323
  * Match a moving box's selected edge/center features against candidate guide
288
324
  * lines. Returns the per-axis snap delta and the matched candidate line(s).
289
325
  * The two axes resolve independently; on each axis the closest in-tolerance
290
- * (feature, candidate) pair wins.
326
+ * (feature, candidate) pair wins. A matched guide that carries a `span` comes
327
+ * back with it stretched to cover the snapped box, so the drawn segment runs
328
+ * from the aligned siblings to the box being dragged.
291
329
  *
292
330
  * `worldTolerance` is per axis because a screen-pixel tolerance is not one
293
331
  * world distance under non-uniform zoom — and each axis here is matched by a
@@ -302,6 +340,35 @@ declare function matchAlignment(bounds: Bounds, candidates: readonly Guide[], wo
302
340
  y: readonly AlignAnchor[];
303
341
  }): AlignMatchResult;
304
342
 
343
+ type Axis = 'x' | 'y';
344
+ /**
345
+ * The visible gaps between neighboring boxes along `axis`: for `'x'`, each box
346
+ * and the nearest box to its right that shares some of its vertical range, as
347
+ * long as no third box stands in the strip between them. These are the gaps an
348
+ * equal-spacing snap can reproduce.
349
+ */
350
+ declare function measureGaps(targets: readonly Bounds[], axis: Axis): SpacingGap[];
351
+ /**
352
+ * Match a box against the gaps between `targets`, Figma's "equal spacing":
353
+ * on each axis, find where the box's gap to a neighbor equals a gap that
354
+ * already exists between two other boxes, or where it sits centered between
355
+ * its two neighbors. `moving` says which edges move on each axis — `'both'`
356
+ * for a translate, `'min'`/`'max'` for a resize dragging that edge, `null` to
357
+ * leave the axis alone. Default `'both'` on each axis.
358
+ *
359
+ * Targets should exclude the box itself. A reference gap the box now stands
360
+ * inside no longer counts. The returned gaps are every gap equal to the one
361
+ * snapped to, with the box at its snapped position — ready to draw with
362
+ * `createGuidesLayer`'s `getGaps`.
363
+ */
364
+ declare function matchSpacing(bounds: Bounds, targets: readonly Bounds[], worldTolerance: {
365
+ x: number;
366
+ y: number;
367
+ }, moving?: {
368
+ x: SpacingEdge | null;
369
+ y: SpacingEdge | null;
370
+ }): SpacingMatchResult;
371
+
305
372
  /** Options for move and resize — adds the pose descriptor for non-rect poses. */
306
373
  interface AlignMoveArgs<TPose> extends AlignmentBehaviorBase {
307
374
  poseDescriptor?: PoseDescriptor<TPose>;
@@ -405,12 +472,39 @@ interface FreeformOptions<TPose> {
405
472
  */
406
473
  declare function freeform<TPose>(opts?: FreeformOptions<TPose>): LayoutStrategy<TPose>;
407
474
 
475
+ type Rect = {
476
+ x: number;
477
+ y: number;
478
+ width: number;
479
+ height: number;
480
+ };
481
+ /**
482
+ * What a `tileGrid` does with a child past its `cols × rows` cells.
483
+ *
484
+ * - `'reject'` (default) — the grid holds `cols × rows` and refuses more, by
485
+ * any route: a drop finds no free cell, and an insert or reparent that would
486
+ * overfill it is reverted whole.
487
+ * - `'grow'` — the grid adds a line of cells whenever the last one fills
488
+ * (a row, or a column for `flow: 'column'`) and the container grows by its
489
+ * pitch to hold it, and gives the line back when children leave and it
490
+ * empties. `rows` (or `cols`) is the minimum.
491
+ * - `'scroll'` — the container keeps its size, and the extra children take
492
+ * cells past its last visible line, at the same pitch. `contentExtent`
493
+ * reports the region they cover, for a host to scroll over.
494
+ */
495
+ type TileGridOverflow = 'reject' | 'grow' | 'scroll';
408
496
  /** Options for `tileGrid`. */
409
497
  interface TileGridOptions<TPose> {
410
498
  cols: number;
411
499
  rows: number;
412
500
  /** Gap between cells, in world units. Default 0. */
413
501
  gap?: number;
502
+ /** The order cells fill in: along each row (`'row'`, the default) or down
503
+ * each column (`'column'`). It is also the direction an overflowing grid
504
+ * runs on in. */
505
+ flow?: 'row' | 'column';
506
+ /** What happens past `cols × rows` children. Default `'reject'`. */
507
+ overflow?: TileGridOverflow;
414
508
  snap?: LayoutSnap<TPose>;
415
509
  /** Optional: map a cell rect + the dragged pose to the new pose. Default
416
510
  * writes `{ x, y, width, height }` into the dragged pose (assumes
@@ -418,12 +512,7 @@ interface TileGridOptions<TPose> {
418
512
  * `dragged` argument is the pre-drop pose of the child being placed —
419
513
  * use it to preserve fields the cell rect doesn't carry (e.g. rotation,
420
514
  * domain metadata). */
421
- cellToPose?(cellRect: {
422
- x: number;
423
- y: number;
424
- width: number;
425
- height: number;
426
- }, dragged: TPose): TPose;
515
+ cellToPose?(cellRect: Rect, dragged: TPose): TPose;
427
516
  /** Optional: the point that decides which cell a child occupies — a child
428
517
  * occupies the cell containing it. Default reads `x`/`y` plus half of
429
518
  * `width`/`height` when the pose has them, which is the center of a
@@ -434,13 +523,14 @@ interface TileGridOptions<TPose> {
434
523
  };
435
524
  }
436
525
  /**
437
- * Layout strategy that arranges children into a fixed grid of cells, one
438
- * child per cell, and the container holds at most `cols × rows` children.
526
+ * Layout strategy that arranges children into a grid of cells, one child per
527
+ * cell. What happens past `cols × rows` children is the `overflow` policy.
439
528
  *
440
529
  * A child occupies the cell its pose sits in (see `centerOf`). A drag from
441
530
  * inside the container swaps with the child in the cell it lands on; a drop
442
531
  * from outside is offered only the free cells, so it lands in the nearest
443
- * free one and a full grid rejects it.
532
+ * free one. A child that joins by an insert or a reparent is placed the same
533
+ * way, in the free cell nearest to where it was put.
444
534
  */
445
535
  declare function tileGrid<TPose>(opts: TileGridOptions<TPose>): LayoutStrategy<TPose>;
446
536
 
@@ -460,4 +550,4 @@ interface SnapPointOptions<TPose> {
460
550
  * child to land on. */
461
551
  declare function snapPoint<TPose extends Vec2>(opts: SnapPointOptions<TPose>): LayoutStrategy<TPose>;
462
552
 
463
- export { type AlignAnchor, type AlignMatchResult, type AlignMoveArgs, type AlignmentBehaviorBase, type CellHighlightLayerOpts, DEFAULT_GUIDE_TOLERANCE_PX, type DeriveAlignmentGuidesOptions, type FreeformOptions, type GridLayerOpts, Guide, type GuideSnapOptions, type GuidesLayerOpts, MOVE_ANCHORS, type SnapPattern, type SnapPointOptions, type TileGridOptions, type UseGridCellHoverOptions, type UseGridCellHoverReturn, type UseGuidesReturn, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, cellAt, containedThenNearest, createCellHighlightLayer, createGridLayer, createGuidesLayer, deriveAlignmentGuides, freeform, gridSnapStrategy, guideSnapStrategy, matchAlignment, nearest, nearestWithin, none, pointToGridCell, roundToCell, snapPoint, tileGrid, useGridCellHover, useGuides };
553
+ export { type AlignAnchor, type AlignMatchResult, type AlignMoveArgs, type AlignmentBehaviorBase, type CellHighlightLayerOpts, DEFAULT_GUIDE_TOLERANCE_PX, type DeriveAlignmentGuidesOptions, type FreeformOptions, type GridLayerOpts, Guide, type GuideSnapOptions, type GuidesLayerOpts, MOVE_ANCHORS, type SnapPattern, type SnapPointOptions, type SpacingEdge, SpacingGap, type SpacingMatchResult, type TileGridOptions, type TileGridOverflow, type UseGridCellHoverOptions, type UseGridCellHoverReturn, type UseGuidesReturn, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, cellAt, containedThenNearest, createCellHighlightLayer, createGridLayer, createGuidesLayer, deriveAlignmentGuides, freeform, gridSnapStrategy, guideSnapStrategy, matchAlignment, matchSpacing, measureGaps, nearest, nearestWithin, none, pointToGridCell, roundToCell, snapPoint, tileGrid, useGridCellHover, useGuides };