@weasel-js/guides 1.9.3 → 1.10.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 +1 -1
- package/dist/chunk-LOFNN7EQ.js +45 -0
- package/dist/chunk-LOFNN7EQ.js.map +1 -0
- package/dist/{chunk-5BCCIJY3.js → chunk-TLFMAZ6M.js} +11 -13
- package/dist/chunk-TLFMAZ6M.js.map +1 -0
- package/dist/index.d.ts +276 -70
- package/dist/index.js +500 -53
- package/dist/index.js.map +1 -1
- package/dist/insert.d.ts +1 -1
- package/dist/insert.js +12 -31
- package/dist/insert.js.map +1 -1
- package/dist/move.d.ts +1 -1
- package/dist/move.js +2 -2
- package/dist/resize.d.ts +1 -1
- package/dist/resize.js +10 -32
- package/dist/resize.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-5BCCIJY3.js.map +0 -1
- package/dist/chunk-GEDAPTOM.js +0 -48
- package/dist/chunk-GEDAPTOM.js.map +0 -1
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ import { snapToGrid } from '@weasel-js/guides/insert';
|
|
|
52
52
|
|---|---|
|
|
53
53
|
| Grid | `createGridLayer`, `createCellHighlightLayer`, `useGridCellHover`, `gridSnapStrategy`, `pointToGridCell`, `pointSnapToGrid`, `roundToCell` |
|
|
54
54
|
| Guides | `useGuides`, `createGuidesLayer`, `guideSnapStrategy`, `deriveAlignmentGuides`, `matchAlignment`, `alignMoveBehavior`, `alignResizeBehavior`, `alignInsertBehavior` |
|
|
55
|
-
| Layout | `freeform`, `tileGrid`, `snapPoint`, and the drop-target pickers `none`, `nearest`, `nearestWithin`, `containedThenNearest`, `cellAt` |
|
|
55
|
+
| Layout | `freeform`, `tileGrid`, `snapPoint`, `slots` (with the `rowSlots`, `gridSlots`, and `ringSlots` arrangements), `pack`, `quadtree`, `perContainer` for a strategy that keeps state, and the drop-target pickers `none`, `nearest`, `nearestWithin`, `containedThenNearest`, `cellAt` |
|
|
56
56
|
|
|
57
57
|
## License
|
|
58
58
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AUTO_ORIGIN_PROJECTION, screenTolerance } from '@weasel-js/core';
|
|
2
|
+
|
|
3
|
+
// src/strategies/guides.ts
|
|
4
|
+
|
|
5
|
+
// src/strategies/onPlane.ts
|
|
6
|
+
function guideOffsetOn(g, plane) {
|
|
7
|
+
return plane ? g.offset * plane.scale[g.axis] + plane.offset[g.axis] : g.offset;
|
|
8
|
+
}
|
|
9
|
+
function nearestGuideDelta(guides, axis, value, tol, plane) {
|
|
10
|
+
let best = null;
|
|
11
|
+
for (const g of guides) {
|
|
12
|
+
if (g.axis !== axis) continue;
|
|
13
|
+
const d = guideOffsetOn(g, plane) - value;
|
|
14
|
+
if (Math.abs(d) <= tol && (best === null || Math.abs(d) < Math.abs(best))) best = d;
|
|
15
|
+
}
|
|
16
|
+
return best;
|
|
17
|
+
}
|
|
18
|
+
function roundToGridOn(value, axis, spacing, plane) {
|
|
19
|
+
if (!plane) return Math.round(value / spacing) * spacing;
|
|
20
|
+
const camera = (value - plane.offset[axis]) / plane.scale[axis];
|
|
21
|
+
return Math.round(camera / spacing) * spacing * plane.scale[axis] + plane.offset[axis];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/strategies/guides.ts
|
|
25
|
+
var DEFAULT_GUIDE_TOLERANCE_PX = 6;
|
|
26
|
+
function guideSnapStrategy(getGuides, options = {}) {
|
|
27
|
+
const tolerance = options.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;
|
|
28
|
+
const proj = options.origin ?? AUTO_ORIGIN_PROJECTION;
|
|
29
|
+
return {
|
|
30
|
+
snap(pose, ctx) {
|
|
31
|
+
const guides = getGuides();
|
|
32
|
+
if (guides.length === 0) return null;
|
|
33
|
+
const tol = screenTolerance(tolerance, ctx);
|
|
34
|
+
const o = proj.getOrigin(pose);
|
|
35
|
+
const dx = nearestGuideDelta(guides, "x", o.x, tol.x, ctx.plane) ?? 0;
|
|
36
|
+
const dy = nearestGuideDelta(guides, "y", o.y, tol.y, ctx.plane) ?? 0;
|
|
37
|
+
if (dx === 0 && dy === 0) return null;
|
|
38
|
+
return proj.translate(pose, dx, dy);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { DEFAULT_GUIDE_TOLERANCE_PX, guideOffsetOn, guideSnapStrategy, nearestGuideDelta, roundToGridOn };
|
|
44
|
+
//# sourceMappingURL=chunk-LOFNN7EQ.js.map
|
|
45
|
+
//# sourceMappingURL=chunk-LOFNN7EQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/strategies/onPlane.ts","../src/strategies/guides.ts"],"names":[],"mappings":";;;;;AAQO,SAAS,aAAA,CAAc,GAAU,KAAA,EAAsB;AAC5D,EAAA,OAAO,KAAA,GAAQ,CAAA,CAAE,MAAA,GAAS,KAAA,CAAM,KAAA,CAAM,CAAA,CAAE,IAAI,CAAA,GAAI,KAAA,CAAM,MAAA,CAAO,CAAA,CAAE,IAAI,IAAI,CAAA,CAAE,MAAA;AAC3E;AAGO,SAAS,iBAAA,CACd,MAAA,EACA,IAAA,EACA,KAAA,EACA,KACA,KAAA,EACe;AACf,EAAA,IAAI,IAAA,GAAsB,IAAA;AAC1B,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,IAAI,CAAA,CAAE,SAAS,IAAA,EAAM;AACrB,IAAA,MAAM,CAAA,GAAI,aAAA,CAAc,CAAA,EAAG,KAAK,CAAA,GAAI,KAAA;AACpC,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,IAAK,QAAQ,IAAA,KAAS,IAAA,IAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,IAAI,IAAI,IAAA,GAAO,CAAA;AAAA,EACpF;AACA,EAAA,OAAO,IAAA;AACT;AAGO,SAAS,aAAA,CAAc,KAAA,EAAe,IAAA,EAAY,OAAA,EAAiB,KAAA,EAAsB;AAC9F,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,KAAK,KAAA,CAAM,KAAA,GAAQ,OAAO,CAAA,GAAI,OAAA;AACjD,EAAA,MAAM,MAAA,GAAA,CAAU,QAAQ,KAAA,CAAM,MAAA,CAAO,IAAI,CAAA,IAAK,KAAA,CAAM,MAAM,IAAI,CAAA;AAC9D,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,GAAS,OAAO,CAAA,GAAI,OAAA,GAAU,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA,GAAI,KAAA,CAAM,MAAA,CAAO,IAAI,CAAA;AACvF;;;ACxBO,IAAM,0BAAA,GAA6B;AAuCnC,SAAS,iBAAA,CACd,SAAA,EACA,OAAA,GAAmC,EAAC,EACf;AACrB,EAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,0BAAA;AACvC,EAAA,MAAM,IAAA,GACJ,QAAQ,MAAA,IAAW,sBAAA;AAErB,EAAA,OAAO;AAAA,IACL,IAAA,CAAK,MAAM,GAAA,EAAK;AACd,MAAA,MAAM,SAAS,SAAA,EAAU;AACzB,MAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAIhC,MAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,SAAA,EAAW,GAAG,CAAA;AAE1C,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAC7B,MAAA,MAAM,EAAA,GAAK,iBAAA,CAAkB,MAAA,EAAQ,GAAA,EAAK,CAAA,CAAE,GAAG,GAAA,CAAI,CAAA,EAAG,GAAA,CAAI,KAAK,CAAA,IAAK,CAAA;AACpE,MAAA,MAAM,EAAA,GAAK,iBAAA,CAAkB,MAAA,EAAQ,GAAA,EAAK,CAAA,CAAE,GAAG,GAAA,CAAI,CAAA,EAAG,GAAA,CAAI,KAAK,CAAA,IAAK,CAAA;AACpE,MAAA,IAAI,EAAA,KAAO,CAAA,IAAK,EAAA,KAAO,CAAA,EAAG,OAAO,IAAA;AACjC,MAAA,OAAO,IAAA,CAAK,SAAA,CAAU,IAAA,EAAM,EAAA,EAAI,EAAE,CAAA;AAAA,IACpC;AAAA,GACF;AACF","file":"chunk-LOFNN7EQ.js","sourcesContent":["import type { GestureContext } from '@weasel-js/core';\nimport type { Guide } from '../guides/types';\n\n/** A gesture's `plane`: how the camera's world maps into the world its poses are in. */\ntype Plane = GestureContext<unknown>['plane'];\ntype Axis = 'x' | 'y';\n\n/** Where a guide the camera draws lies along its own axis in the world a gesture's poses are in. */\nexport function guideOffsetOn(g: Guide, plane: Plane): number {\n return plane ? g.offset * plane.scale[g.axis] + plane.offset[g.axis] : g.offset;\n}\n\n/** The signed distance from `value` to the nearest guide on `axis` within `tol`, or null when none is. */\nexport function nearestGuideDelta(\n guides: readonly Guide[],\n axis: Axis,\n value: number,\n tol: number,\n plane: Plane,\n): number | null {\n let best: number | null = null;\n for (const g of guides) {\n if (g.axis !== axis) continue;\n const d = guideOffsetOn(g, plane) - value;\n if (Math.abs(d) <= tol && (best === null || Math.abs(d) < Math.abs(best))) best = d;\n }\n return best;\n}\n\n/** `value` on `axis` moved to the nearest line of the camera's grid, in the world a gesture's poses are in. */\nexport function roundToGridOn(value: number, axis: Axis, spacing: number, plane: Plane): number {\n if (!plane) return Math.round(value / spacing) * spacing;\n const camera = (value - plane.offset[axis]) / plane.scale[axis];\n return Math.round(camera / spacing) * spacing * plane.scale[axis] + plane.offset[axis];\n}\n","import {\n type SnapStrategy,\n screenTolerance,\n type OriginProjection,\n AUTO_ORIGIN_PROJECTION,\n} from '@weasel-js/core';\nimport type { Guide } from '../guides/types';\nimport { nearestGuideDelta } from './onPlane';\n\n/** Default snap tolerance (screen pixels). */\nexport const DEFAULT_GUIDE_TOLERANCE_PX = 6;\n\n/** Options for `guideSnapStrategy`. */\nexport interface GuideSnapOptions<TPose> {\n /**\n * Tolerance in screen pixels, divided by the gesture view's scale so the\n * trigger zone stays the same on screen at every zoom. Defaults to\n * `DEFAULT_GUIDE_TOLERANCE_PX`.\n */\n tolerance?: number;\n /**\n * Projection used when `TPose` doesn't expose `{x, y}` directly (Path,\n * polygon, etc.) — same contract as `gridSnapStrategy`. Defaults to the\n * rect-pose identity projection.\n */\n origin?: OriginProjection<TPose>;\n}\n\n/**\n * Snap-strategy that snaps the pose's origin to the nearest guide line on\n * each axis when within tolerance. Each axis is considered independently:\n * a vertical guide (`axis: 'x'`) constrains X, a horizontal guide\n * (`axis: 'y'`) constrains Y. When multiple guides are within range on the\n * same axis, the closest wins.\n *\n * Pair with `useGuides` for storage and `createGuidesLayer` for rendering.\n * Combining with `gridSnapStrategy` is left to the consumer.\n */\nexport function guideSnapStrategy<TPose extends { x: number; y: number }>(\n getGuides: () => readonly Guide[],\n options?: Omit<GuideSnapOptions<TPose>, 'origin'>,\n): SnapStrategy<TPose>;\n/** As above, for a `TPose` that is not a rect: `origin` tells the strategy how\n * to read and write the pose's origin. */\nexport function guideSnapStrategy<TPose>(\n getGuides: () => readonly Guide[],\n options: GuideSnapOptions<TPose> & { origin: OriginProjection<TPose> },\n): SnapStrategy<TPose>;\n/** Snap a pose's origin to the nearest guide line within tolerance, per axis. */\nexport function guideSnapStrategy<TPose>(\n getGuides: () => readonly Guide[],\n options: GuideSnapOptions<TPose> = {},\n): SnapStrategy<TPose> {\n const tolerance = options.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;\n const proj: OriginProjection<TPose> =\n options.origin ?? (AUTO_ORIGIN_PROJECTION as unknown as OriginProjection<TPose>);\n\n return {\n snap(pose, ctx) {\n const guides = getGuides();\n if (guides.length === 0) return null;\n\n // Per axis: a guide on the x axis is matched by a horizontal distance,\n // so it answers to `scale.x` alone.\n const tol = screenTolerance(tolerance, ctx);\n\n const o = proj.getOrigin(pose);\n const dx = nearestGuideDelta(guides, 'x', o.x, tol.x, ctx.plane) ?? 0;\n const dy = nearestGuideDelta(guides, 'y', o.y, tol.y, ctx.plane) ?? 0;\n if (dx === 0 && dy === 0) return null;\n return proj.translate(pose, dx, dy);\n },\n };\n}\n"]}
|
|
@@ -1,33 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { roundToGridOn } from './chunk-LOFNN7EQ.js';
|
|
2
|
+
import { AUTO_ORIGIN_PROJECTION, resolveAxes } from '@weasel-js/core';
|
|
2
3
|
|
|
3
|
-
// src/strategies/grid.ts
|
|
4
4
|
function gridSnapStrategy(spacing, arg) {
|
|
5
|
-
const isOpts = typeof arg === "object" && arg !== null && ("origin" in arg || "debug" in arg || "unitSystem" in arg) && !("base" in arg);
|
|
5
|
+
const isOpts = typeof arg === "object" && arg !== null && ("origin" in arg || "debug" in arg || "unitSystem" in arg) && !("base" in arg) && !("x" in arg);
|
|
6
6
|
const optsArg = isOpts ? arg : null;
|
|
7
7
|
const unitSystem = optsArg ? optsArg.unitSystem : arg;
|
|
8
8
|
const proj = optsArg && optsArg.origin ? optsArg.origin : AUTO_ORIGIN_PROJECTION;
|
|
9
9
|
const debug = optsArg ? optsArg.debug : void 0;
|
|
10
|
-
const c =
|
|
10
|
+
const c = resolveAxes(spacing, unitSystem);
|
|
11
11
|
return {
|
|
12
12
|
snap(pose, ctx) {
|
|
13
13
|
const o = proj.getOrigin(pose);
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const cell = { x: Math.round(w.x / c) * c, y: Math.round(w.y / c) * c };
|
|
17
|
-
const { x: sx, y: sy } = m ? toPlane(m, cell) : cell;
|
|
14
|
+
const sx = roundToGridOn(o.x, "x", c.x, ctx?.plane);
|
|
15
|
+
const sy = roundToGridOn(o.y, "y", c.y, ctx?.plane);
|
|
18
16
|
debug?.recordSnapCandidate({ x: sx, y: sy }, true);
|
|
19
17
|
return proj.translate(pose, sx - o.x, sy - o.y);
|
|
20
18
|
}
|
|
21
19
|
};
|
|
22
20
|
}
|
|
23
21
|
function pointToGridCell(point, spacing, unitSystem, origin = { x: 0, y: 0 }) {
|
|
24
|
-
const s =
|
|
22
|
+
const s = resolveAxes(spacing, unitSystem);
|
|
25
23
|
return {
|
|
26
|
-
col: Math.floor((point.x - origin.x) / s),
|
|
27
|
-
row: Math.floor((point.y - origin.y) / s)
|
|
24
|
+
col: Math.floor((point.x - origin.x) / s.x),
|
|
25
|
+
row: Math.floor((point.y - origin.y) / s.y)
|
|
28
26
|
};
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
export { gridSnapStrategy, pointToGridCell };
|
|
32
|
-
//# sourceMappingURL=chunk-
|
|
33
|
-
//# sourceMappingURL=chunk-
|
|
30
|
+
//# sourceMappingURL=chunk-TLFMAZ6M.js.map
|
|
31
|
+
//# sourceMappingURL=chunk-TLFMAZ6M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/strategies/grid.ts"],"names":[],"mappings":";;;AAkCO,SAAS,gBAAA,CACd,SACA,GAAA,EACqB;AACrB,EAAA,MAAM,SACJ,OAAO,GAAA,KAAQ,QAAA,IACf,GAAA,KAAQ,SACP,QAAA,IAAY,GAAA,IAAO,OAAA,IAAW,GAAA,IAAO,gBAAgB,GAAA,CAAA,IACtD,EAAE,MAAA,IAAU,GAAA,CAAA,IACZ,EAAE,GAAA,IAAO,GAAA,CAAA;AACX,EAAA,MAAM,OAAA,GAAU,SACX,GAAA,GACD,IAAA;AACJ,EAAA,MAAM,UAAA,GAAa,OAAA,GAAU,OAAA,CAAQ,UAAA,GAAc,GAAA;AACnD,EAAA,MAAM,IAAA,GAAgC,OAAA,IAAW,OAAA,CAAQ,MAAA,GACrD,QAAQ,MAAA,GACP,sBAAA;AACL,EAAA,MAAM,KAAA,GAA+B,OAAA,GAAU,OAAA,CAAQ,KAAA,GAAQ,MAAA;AAC/D,EAAA,MAAM,CAAA,GAAI,WAAA,CAAY,OAAA,EAAS,UAAU,CAAA;AACzC,EAAA,OAAO;AAAA,IACL,IAAA,CAAK,MAAM,GAAA,EAAK;AACd,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAC7B,MAAA,MAAM,EAAA,GAAK,cAAc,CAAA,CAAE,CAAA,EAAG,KAAK,CAAA,CAAE,CAAA,EAAG,KAAK,KAAK,CAAA;AAClD,MAAA,MAAM,EAAA,GAAK,cAAc,CAAA,CAAE,CAAA,EAAG,KAAK,CAAA,CAAE,CAAA,EAAG,KAAK,KAAK,CAAA;AAClD,MAAA,KAAA,EAAO,oBAAoB,EAAE,CAAA,EAAG,IAAI,CAAA,EAAG,EAAA,IAAM,IAAI,CAAA;AACjD,MAAA,OAAO,IAAA,CAAK,UAAU,IAAA,EAAM,EAAA,GAAK,EAAE,CAAA,EAAG,EAAA,GAAK,EAAE,CAAC,CAAA;AAAA,IAChD;AAAA,GACF;AACF;AAOO,SAAS,eAAA,CACd,KAAA,EACA,OAAA,EACA,UAAA,EACA,MAAA,GAAmC,EAAE,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAE,EAClB;AAC9B,EAAA,MAAM,CAAA,GAAI,WAAA,CAAY,OAAA,EAAS,UAAU,CAAA;AACzC,EAAA,OAAO;AAAA,IACL,GAAA,EAAK,KAAK,KAAA,CAAA,CAAO,KAAA,CAAM,IAAI,MAAA,CAAO,CAAA,IAAK,EAAE,CAAC,CAAA;AAAA,IAC1C,GAAA,EAAK,KAAK,KAAA,CAAA,CAAO,KAAA,CAAM,IAAI,MAAA,CAAO,CAAA,IAAK,EAAE,CAAC;AAAA,GAC5C;AACF","file":"chunk-TLFMAZ6M.js","sourcesContent":["import {\n AUTO_ORIGIN_PROJECTION,\n resolveAxes,\n type DebugSink,\n type OriginProjection,\n type SnapStrategy,\n type PlaneUnits,\n type PlaneValue,\n} from '@weasel-js/core';\nimport { roundToGridOn } from './onPlane';\n\n/** Snap-strategy that rounds the pose's origin to the nearest multiple of\n * `spacing` (resolved through `unitSystem`). Either may be per axis —\n * `{ x, y }` — for a grid whose axes have different pitches or units. For\n * non-rect TPose pass an `OriginProjection` so the strategy knows how to\n * read/write the origin. */\nexport function gridSnapStrategy<TPose>(\n spacing: PlaneValue,\n unitSystem?: PlaneUnits,\n): SnapStrategy<TPose>;\n/** As above, additionally reporting each candidate to a debug sink. */\nexport function gridSnapStrategy<TPose>(\n spacing: PlaneValue,\n opts: { unitSystem?: PlaneUnits; debug?: DebugSink },\n): SnapStrategy<TPose>;\n/** As above, for a `TPose` that is not a rect: `origin` tells the strategy how\n * to read and write the pose's origin. */\nexport function gridSnapStrategy<TPose>(\n spacing: PlaneValue,\n opts: { unitSystem?: PlaneUnits; origin: OriginProjection<TPose>; debug?: DebugSink },\n): SnapStrategy<TPose>;\n/** Snap-strategy that rounds the pose's origin to the nearest multiple of\n * `spacing` (resolved through `unitSystem`). For non-rect TPose pass an\n * `OriginProjection` so the strategy knows how to read/write the origin. */\nexport function gridSnapStrategy<TPose>(\n spacing: PlaneValue,\n arg?: PlaneUnits | { unitSystem?: PlaneUnits; origin?: OriginProjection<TPose>; debug?: DebugSink },\n): SnapStrategy<TPose> {\n const isOpts =\n typeof arg === 'object' &&\n arg !== null &&\n ('origin' in arg || 'debug' in arg || 'unitSystem' in arg) &&\n !('base' in arg) &&\n !('x' in arg);\n const optsArg = isOpts\n ? (arg as { unitSystem?: PlaneUnits; origin?: OriginProjection<TPose>; debug?: DebugSink })\n : null;\n const unitSystem = optsArg ? optsArg.unitSystem : (arg as PlaneUnits | undefined);\n const proj: OriginProjection<TPose> = optsArg && optsArg.origin\n ? optsArg.origin\n : (AUTO_ORIGIN_PROJECTION as unknown as OriginProjection<TPose>);\n const debug: DebugSink | undefined = optsArg ? optsArg.debug : undefined;\n const c = resolveAxes(spacing, unitSystem);\n return {\n snap(pose, ctx) {\n const o = proj.getOrigin(pose);\n const sx = roundToGridOn(o.x, 'x', c.x, ctx?.plane);\n const sy = roundToGridOn(o.y, 'y', c.y, ctx?.plane);\n debug?.recordSnapCandidate({ x: sx, y: sy }, true);\n return proj.translate(pose, sx - o.x, sy - o.y);\n },\n };\n}\n\n/**\n * The integer cell `{col, row}` that contains `point`. Pair with\n * `createCellHighlightLayer` (its `getCell` callback) to draw a snap-target\n * preview that uses the same spacing as `gridSnapStrategy`.\n */\nexport function pointToGridCell(\n point: { x: number; y: number },\n spacing: PlaneValue,\n unitSystem?: PlaneUnits,\n origin: { x: number; y: number } = { x: 0, y: 0 },\n): { col: number; row: number } {\n const s = resolveAxes(spacing, unitSystem);\n return {\n col: Math.floor((point.x - origin.x) / s.x),\n row: Math.floor((point.y - origin.y) / s.y),\n };\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
|
-
import { ViewTransform,
|
|
2
|
+
import { ViewTransform, PlaneValue, PlaneUnits, CommandStroke, RenderLayer, CommandFill, SnapStrategy, DebugSink, OriginProjection, ModifierState, Bounds, PlaneMap, PoseDescriptor, InsertBehavior, MoveBehavior, BoundsConstraint, LayoutSnap, LayoutStrategy, LayoutContainer, Vec2, ContainerBounds, LayoutChild } from '@weasel-js/core';
|
|
3
3
|
export { p as pointSnapToGrid } from './pointSnapToGrid-BaVEjV23.js';
|
|
4
4
|
import { G as Guide, S as SpacingGap } from './types-DRSPiXfk.js';
|
|
5
|
+
import { Rect } from '@weasel-js/geom';
|
|
5
6
|
|
|
6
7
|
/** Round `value` to the nearest multiple of `cellSize`. Returns 0 when the result would be -0. */
|
|
7
8
|
declare function roundToCell(value: number, cellSize: number): number;
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Pointer-tracking glue that turns hover position into a grid cell, suitable
|
|
11
|
-
* for dropping straight into `createCellHighlightLayer({ getCell })`.
|
|
12
|
-
* with `gridSnapStrategy` / `createGridLayer` so the visual hover preview
|
|
13
|
-
* uses the exact same spacing as the snap behavior.
|
|
14
|
-
*
|
|
15
|
-
* Wires three things you'd otherwise glue by hand:
|
|
16
|
-
* 1. pointermove on a target element → client coords
|
|
17
|
-
* 2. client coords → world coords via your `ViewTransform`
|
|
18
|
-
* 3. world coords → integer `{col, row}` via `pointToGridCell`
|
|
12
|
+
* for dropping straight into `createCellHighlightLayer({ getCell })`.
|
|
19
13
|
*
|
|
20
14
|
* State updates only fire when the cell actually changes (not on every
|
|
21
15
|
* pointer tick). Pass `onChange` to trigger a canvas redraw on each transition.
|
|
@@ -30,9 +24,9 @@ interface UseGridCellHoverOptions {
|
|
|
30
24
|
view: () => ViewTransform;
|
|
31
25
|
/** Cell spacing in world units. Same value you pass to `createGridLayer`
|
|
32
26
|
* and `createCellHighlightLayer`. */
|
|
33
|
-
spacing:
|
|
27
|
+
spacing: PlaneValue;
|
|
34
28
|
/** Required if `spacing` is tagged (e.g. `{ value: 1, unit: 'ft' }`). */
|
|
35
|
-
unitSystem?:
|
|
29
|
+
unitSystem?: PlaneUnits;
|
|
36
30
|
/** Origin the cell grid is anchored to, in world units. Default `(0, 0)`. */
|
|
37
31
|
origin?: {
|
|
38
32
|
x: number;
|
|
@@ -80,10 +74,11 @@ interface GridLayerOpts {
|
|
|
80
74
|
* Distance between adjacent grid lines (also known as grid pitch),
|
|
81
75
|
* in world (base) units. Accepts a bare number or a tagged
|
|
82
76
|
* `{ value, unit }`. Tagged values require `unitSystem` to be supplied.
|
|
77
|
+
* Either may be per axis — `{ x, y }`.
|
|
83
78
|
*/
|
|
84
|
-
spacing:
|
|
79
|
+
spacing: PlaneValue;
|
|
85
80
|
/** Optional unit system for resolving tagged `spacing` values. */
|
|
86
|
-
unitSystem?:
|
|
81
|
+
unitSystem?: PlaneUnits;
|
|
87
82
|
/** Bounds of the area to cover, in world units. */
|
|
88
83
|
bounds: () => {
|
|
89
84
|
x: number;
|
|
@@ -95,11 +90,11 @@ interface GridLayerOpts {
|
|
|
95
90
|
accentEvery?: number;
|
|
96
91
|
/** Optional finer subdivisions per cell. e.g. 4 -> 4 sub-lines per cell. */
|
|
97
92
|
subdivisions?: number;
|
|
98
|
-
/** Per-band stroke styles. Each is a
|
|
93
|
+
/** Per-band stroke styles. Each is a Paint+width (etc.) — see `Stroke`. */
|
|
99
94
|
style?: {
|
|
100
|
-
line?:
|
|
101
|
-
accent?:
|
|
102
|
-
sub?:
|
|
95
|
+
line?: CommandStroke;
|
|
96
|
+
accent?: CommandStroke;
|
|
97
|
+
sub?: CommandStroke;
|
|
103
98
|
};
|
|
104
99
|
}
|
|
105
100
|
/** Build a `RenderLayer` that draws a world-space grid with optional accent lines and subdivisions. */
|
|
@@ -114,9 +109,9 @@ declare function createGridLayer(opts: GridLayerOpts): RenderLayer<unknown>;
|
|
|
114
109
|
/** Options for `createCellHighlightLayer`. */
|
|
115
110
|
interface CellHighlightLayerOpts {
|
|
116
111
|
/** Cell size in world units (matches the grid's `spacing`). */
|
|
117
|
-
spacing:
|
|
112
|
+
spacing: PlaneValue;
|
|
118
113
|
/** Optional unit system for resolving tagged `spacing` values. */
|
|
119
|
-
unitSystem?:
|
|
114
|
+
unitSystem?: PlaneUnits;
|
|
120
115
|
/** Origin the cell grid is anchored to, in world units. Defaults to (0,0). */
|
|
121
116
|
origin?: () => {
|
|
122
117
|
x: number;
|
|
@@ -127,49 +122,39 @@ interface CellHighlightLayerOpts {
|
|
|
127
122
|
col: number;
|
|
128
123
|
row: number;
|
|
129
124
|
} | null;
|
|
130
|
-
/**
|
|
131
|
-
fill?:
|
|
125
|
+
/** Fill for the highlighted cell. Defaults to a soft green. */
|
|
126
|
+
fill?: CommandFill;
|
|
132
127
|
}
|
|
133
128
|
/** Build a `RenderLayer` that fills a single grid cell — typically a snap-target preview. */
|
|
134
129
|
declare function createCellHighlightLayer(opts: CellHighlightLayerOpts): RenderLayer<unknown>;
|
|
135
130
|
|
|
136
131
|
/** Snap-strategy that rounds the pose's origin to the nearest multiple of
|
|
137
|
-
* `spacing` (resolved through `unitSystem`).
|
|
138
|
-
* `
|
|
139
|
-
|
|
132
|
+
* `spacing` (resolved through `unitSystem`). Either may be per axis —
|
|
133
|
+
* `{ x, y }` — for a grid whose axes have different pitches or units. For
|
|
134
|
+
* non-rect TPose pass an `OriginProjection` so the strategy knows how to
|
|
135
|
+
* read/write the origin. */
|
|
136
|
+
declare function gridSnapStrategy<TPose>(spacing: PlaneValue, unitSystem?: PlaneUnits): SnapStrategy<TPose>;
|
|
140
137
|
/** As above, additionally reporting each candidate to a debug sink. */
|
|
141
|
-
declare function gridSnapStrategy<TPose>(spacing:
|
|
142
|
-
unitSystem?:
|
|
138
|
+
declare function gridSnapStrategy<TPose>(spacing: PlaneValue, opts: {
|
|
139
|
+
unitSystem?: PlaneUnits;
|
|
143
140
|
debug?: DebugSink;
|
|
144
141
|
}): SnapStrategy<TPose>;
|
|
145
142
|
/** As above, for a `TPose` that is not a rect: `origin` tells the strategy how
|
|
146
143
|
* to read and write the pose's origin. */
|
|
147
|
-
declare function gridSnapStrategy<TPose>(spacing:
|
|
148
|
-
unitSystem?:
|
|
144
|
+
declare function gridSnapStrategy<TPose>(spacing: PlaneValue, opts: {
|
|
145
|
+
unitSystem?: PlaneUnits;
|
|
149
146
|
origin: OriginProjection<TPose>;
|
|
150
147
|
debug?: DebugSink;
|
|
151
148
|
}): SnapStrategy<TPose>;
|
|
152
149
|
/**
|
|
153
|
-
*
|
|
154
|
-
* `spacing` and optional `origin` and `unitSystem`. Pair with
|
|
150
|
+
* The integer cell `{col, row}` that contains `point`. Pair with
|
|
155
151
|
* `createCellHighlightLayer` (its `getCell` callback) to draw a snap-target
|
|
156
|
-
* preview that uses the same spacing as `gridSnapStrategy
|
|
157
|
-
* and behavioral grids stay in lockstep:
|
|
158
|
-
*
|
|
159
|
-
* const SPACING = 20;
|
|
160
|
-
* const snap = gridSnapStrategy(SPACING);
|
|
161
|
-
* // visual grid:
|
|
162
|
-
* createGridLayer({ spacing: SPACING, bounds: () => ... });
|
|
163
|
-
* // hover-preview overlay:
|
|
164
|
-
* createCellHighlightLayer({
|
|
165
|
-
* spacing: SPACING,
|
|
166
|
-
* getCell: () => hoverPoint && pointToGridCell(hoverPoint, SPACING),
|
|
167
|
-
* });
|
|
152
|
+
* preview that uses the same spacing as `gridSnapStrategy`.
|
|
168
153
|
*/
|
|
169
154
|
declare function pointToGridCell(point: {
|
|
170
155
|
x: number;
|
|
171
156
|
y: number;
|
|
172
|
-
}, spacing:
|
|
157
|
+
}, spacing: PlaneValue, unitSystem?: PlaneUnits, origin?: {
|
|
173
158
|
x: number;
|
|
174
159
|
y: number;
|
|
175
160
|
}): {
|
|
@@ -275,8 +260,14 @@ interface SpacingMatchResult {
|
|
|
275
260
|
/** Which candidate lines to derive from a set of poses — edges, centers, or
|
|
276
261
|
* both, and whether the page box contributes its own. */
|
|
277
262
|
interface DeriveAlignmentGuidesOptions<TPose = Bounds> {
|
|
278
|
-
/** Include the document/page box's edges + center as candidates.
|
|
263
|
+
/** Include the document/page box's edges + center as candidates. It is in
|
|
264
|
+
* the camera's world, as every line derived is. */
|
|
279
265
|
page?: Bounds;
|
|
266
|
+
/** The parallax plane a target's pose is stored in, for a sibling on a
|
|
267
|
+
* `parallax` layer (`view.planeOf(layer)`). Its box is carried out to the
|
|
268
|
+
* camera's world, which is the world alignment candidates are in. Omit it,
|
|
269
|
+
* or answer null, for a target already there. */
|
|
270
|
+
planeOf?: (target: TPose, index: number) => PlaneMap | null | undefined;
|
|
280
271
|
/** Emit left/right (x) and top/bottom (y) edge guides. Default true. */
|
|
281
272
|
edges?: boolean;
|
|
282
273
|
/** Emit centerX (x) and centerY (y) guides. Default true. */
|
|
@@ -285,9 +276,17 @@ interface DeriveAlignmentGuidesOptions<TPose = Bounds> {
|
|
|
285
276
|
* gets, or the two sides disagree. Default `AUTO_POSE_DESCRIPTOR`. */
|
|
286
277
|
poseDescriptor?: PoseDescriptor<TPose>;
|
|
287
278
|
}
|
|
288
|
-
/** Common options shared by the three alignment behavior factories.
|
|
279
|
+
/** Common options shared by the three alignment behavior factories.
|
|
280
|
+
*
|
|
281
|
+
* Everything crossing this interface is in the camera's world, whatever
|
|
282
|
+
* layer the gesture edits: the candidate lines and spacing boxes handed in,
|
|
283
|
+
* and the lines and gaps published, which a layer the camera draws shows.
|
|
284
|
+
* A gesture on a `parallax` layer carries them into its plane to match. */
|
|
289
285
|
interface AlignmentBehaviorBase {
|
|
290
|
-
/** Live candidate lines — consumer derives from current siblings + page.
|
|
286
|
+
/** Live candidate lines — consumer derives from current siblings + page.
|
|
287
|
+
* A sibling on a parallax layer goes through `deriveAlignmentGuides`'
|
|
288
|
+
* `planeOf`, or its lines land where its plane's numbers say, not where
|
|
289
|
+
* it is drawn. */
|
|
291
290
|
getCandidates: () => readonly Guide[];
|
|
292
291
|
/** Publish the currently-matched line(s). Called every onMove; cleared
|
|
293
292
|
* (`[]`) on a miss and on onEnd. */
|
|
@@ -311,7 +310,9 @@ interface AlignmentBehaviorBase {
|
|
|
311
310
|
* edges and the center. Overlapping offsets collapse to one candidate, whose
|
|
312
311
|
* `span` covers every box that produced it.
|
|
313
312
|
* Poses go through the same descriptor `alignMoveBehavior` matches with, so
|
|
314
|
-
* a rotated sibling advertises its ink edges rather than its stored box.
|
|
313
|
+
* a rotated sibling advertises its ink edges rather than its stored box.
|
|
314
|
+
* The lines are in the camera's world: a sibling on a parallax layer needs
|
|
315
|
+
* `planeOf` to say which plane its pose is in. */
|
|
315
316
|
declare function deriveAlignmentGuides<TPose = Bounds>(targets: readonly TPose[], opts?: DeriveAlignmentGuidesOptions<TPose>): Guide[];
|
|
316
317
|
|
|
317
318
|
/** Move/insert test all three features per axis. */
|
|
@@ -328,9 +329,8 @@ declare const MOVE_ANCHORS: {
|
|
|
328
329
|
* from the aligned siblings to the box being dragged.
|
|
329
330
|
*
|
|
330
331
|
* `worldTolerance` is per axis because a screen-pixel tolerance is not one
|
|
331
|
-
* world distance under non-uniform zoom
|
|
332
|
-
*
|
|
333
|
-
* approximation. Pass the same number twice for a world-space tolerance.
|
|
332
|
+
* world distance under non-uniform zoom. Pass the same number twice for a
|
|
333
|
+
* world-space tolerance.
|
|
334
334
|
*/
|
|
335
335
|
declare function matchAlignment(bounds: Bounds, candidates: readonly Guide[], worldTolerance: {
|
|
336
336
|
x: number;
|
|
@@ -340,14 +340,13 @@ declare function matchAlignment(bounds: Bounds, candidates: readonly Guide[], wo
|
|
|
340
340
|
y: readonly AlignAnchor[];
|
|
341
341
|
}): AlignMatchResult;
|
|
342
342
|
|
|
343
|
-
type Axis = 'x' | 'y';
|
|
344
343
|
/**
|
|
345
344
|
* The visible gaps between neighboring boxes along `axis`: for `'x'`, each box
|
|
346
345
|
* and the nearest box to its right that shares some of its vertical range, as
|
|
347
346
|
* long as no third box stands in the strip between them. These are the gaps an
|
|
348
347
|
* equal-spacing snap can reproduce.
|
|
349
348
|
*/
|
|
350
|
-
declare function measureGaps(targets: readonly Bounds[], axis:
|
|
349
|
+
declare function measureGaps(targets: readonly Bounds[], axis: 'x' | 'y'): SpacingGap[];
|
|
351
350
|
/**
|
|
352
351
|
* Match a box against the gaps between `targets`, Figma's "equal spacing":
|
|
353
352
|
* on each axis, find where the box's gap to a neighbor equals a gap that
|
|
@@ -378,10 +377,10 @@ interface AlignMoveArgs<TPose> extends AlignmentBehaviorBase {
|
|
|
378
377
|
* transform uniformly to every dragged id, so the selection shifts together
|
|
379
378
|
* and stays rigid. Single-select is the degenerate one-box union. Publishes
|
|
380
379
|
* the matched line(s); clears on miss, end and cancel. */
|
|
381
|
-
declare function alignMoveBehavior<TPose>(
|
|
380
|
+
declare function alignMoveBehavior<TPose>(given: AlignMoveArgs<TPose>): MoveBehavior<TPose>;
|
|
382
381
|
/** Insert behavior: snap the live `current` point to candidates (treating it
|
|
383
382
|
* as a zero-size box). Publishes the matched line(s). */
|
|
384
|
-
declare function alignInsertBehavior<TPose>(
|
|
383
|
+
declare function alignInsertBehavior<TPose>(given: AlignmentBehaviorBase): InsertBehavior<TPose>;
|
|
385
384
|
/** Resize constraint: snap the corner(s) the drag moves, as drawn, to
|
|
386
385
|
* candidates; the pinned corner stays fixed. On an unrotated pose that is
|
|
387
386
|
* the moving edge(s). On a rotated one the node's own axes set how the
|
|
@@ -389,7 +388,7 @@ declare function alignInsertBehavior<TPose>(args: AlignmentBehaviorBase): Insert
|
|
|
389
388
|
* slides its edge until the nearer of its two corners meets a line.
|
|
390
389
|
* `poseDescriptor` reads the origin pose's box and rotation, and must match
|
|
391
390
|
* the resize action's. Publishes the matched line(s). */
|
|
392
|
-
declare function alignResizeBehavior<TPose extends Bounds>(
|
|
391
|
+
declare function alignResizeBehavior<TPose extends Bounds>(given: AlignMoveArgs<TPose>): BoundsConstraint<TPose>;
|
|
393
392
|
|
|
394
393
|
/** Default snap tolerance (screen pixels). */
|
|
395
394
|
declare const DEFAULT_GUIDE_TOLERANCE_PX = 6;
|
|
@@ -416,10 +415,7 @@ interface GuideSnapOptions<TPose> {
|
|
|
416
415
|
* same axis, the closest wins.
|
|
417
416
|
*
|
|
418
417
|
* Pair with `useGuides` for storage and `createGuidesLayer` for rendering.
|
|
419
|
-
*
|
|
420
|
-
* **Composition:** combining with `gridSnapStrategy` is left to the
|
|
421
|
-
* consumer in v1 — typically by picking one based on a modifier key, or
|
|
422
|
-
* by stacking two move/resize/insert behaviors in priority order.
|
|
418
|
+
* Combining with `gridSnapStrategy` is left to the consumer.
|
|
423
419
|
*/
|
|
424
420
|
declare function guideSnapStrategy<TPose extends {
|
|
425
421
|
x: number;
|
|
@@ -460,6 +456,34 @@ declare function containedThenNearest<TPose>(): LayoutSnap<TPose>;
|
|
|
460
456
|
*/
|
|
461
457
|
declare function cellAt<TPose>(): LayoutSnap<TPose>;
|
|
462
458
|
|
|
459
|
+
/** Options for `perContainer`. */
|
|
460
|
+
interface PerContainerOptions<TPose> {
|
|
461
|
+
/** The snap every container shares: a strategy's `snap` is read with no
|
|
462
|
+
* container in hand, so it cannot come from the one the factory builds. */
|
|
463
|
+
snap: LayoutSnap<TPose>;
|
|
464
|
+
/** Shared for the same reason. Absent, a container's body is its bounds. */
|
|
465
|
+
contains?: LayoutStrategy<TPose>['contains'];
|
|
466
|
+
/** How many containers' strategies to keep. Past it, the one used longest
|
|
467
|
+
* ago is dropped and built again when next asked for. Default 256. */
|
|
468
|
+
max?: number;
|
|
469
|
+
}
|
|
470
|
+
/** A strategy `perContainer` built, and the way to drop one container's state. */
|
|
471
|
+
type PerContainerStrategy<TPose> = LayoutStrategy<TPose> & {
|
|
472
|
+
/** Drop what was built for `containerId`: for a container that is gone, or
|
|
473
|
+
* whose cached state no longer describes it. */
|
|
474
|
+
forget(containerId: string): void;
|
|
475
|
+
};
|
|
476
|
+
/**
|
|
477
|
+
* A layout strategy that keeps state, as one strategy value: `factory` runs
|
|
478
|
+
* once for each container the layout is asked about, and what it returns
|
|
479
|
+
* serves that container from then on, closing over whatever it caches — a
|
|
480
|
+
* spatial index, the last arrangement, a solver's warm start.
|
|
481
|
+
*
|
|
482
|
+
* Declare the result on as many containers as you like. A method the built
|
|
483
|
+
* strategy leaves out behaves as its absence would.
|
|
484
|
+
*/
|
|
485
|
+
declare function perContainer<TPose>(factory: (container: LayoutContainer) => LayoutStrategy<TPose>, opts: PerContainerOptions<TPose>): PerContainerStrategy<TPose>;
|
|
486
|
+
|
|
463
487
|
/** Options for `freeform`. */
|
|
464
488
|
interface FreeformOptions<TPose> {
|
|
465
489
|
snap?: LayoutSnap<TPose>;
|
|
@@ -472,12 +496,6 @@ interface FreeformOptions<TPose> {
|
|
|
472
496
|
*/
|
|
473
497
|
declare function freeform<TPose>(opts?: FreeformOptions<TPose>): LayoutStrategy<TPose>;
|
|
474
498
|
|
|
475
|
-
type Rect = {
|
|
476
|
-
x: number;
|
|
477
|
-
y: number;
|
|
478
|
-
width: number;
|
|
479
|
-
height: number;
|
|
480
|
-
};
|
|
481
499
|
/**
|
|
482
500
|
* What a `tileGrid` does with a child past its `cols × rows` cells.
|
|
483
501
|
*
|
|
@@ -508,10 +526,8 @@ interface TileGridOptions<TPose> {
|
|
|
508
526
|
snap?: LayoutSnap<TPose>;
|
|
509
527
|
/** Optional: map a cell rect + the dragged pose to the new pose. Default
|
|
510
528
|
* writes `{ x, y, width, height }` into the dragged pose (assumes
|
|
511
|
-
* `RectPose`).
|
|
512
|
-
*
|
|
513
|
-
* use it to preserve fields the cell rect doesn't carry (e.g. rotation,
|
|
514
|
-
* domain metadata). */
|
|
529
|
+
* `RectPose`). `dragged` is the pre-drop pose of the child being placed —
|
|
530
|
+
* use it to preserve fields the cell rect doesn't carry (e.g. rotation). */
|
|
515
531
|
cellToPose?(cellRect: Rect, dragged: TPose): TPose;
|
|
516
532
|
/** Optional: the point that decides which cell a child occupies — a child
|
|
517
533
|
* occupies the cell containing it. Default reads `x`/`y` plus half of
|
|
@@ -550,4 +566,194 @@ interface SnapPointOptions<TPose> {
|
|
|
550
566
|
* child to land on. */
|
|
551
567
|
declare function snapPoint<TPose extends Vec2>(opts: SnapPointOptions<TPose>): LayoutStrategy<TPose>;
|
|
552
568
|
|
|
553
|
-
|
|
569
|
+
/** One place a child's center goes. */
|
|
570
|
+
interface Slot {
|
|
571
|
+
x: number;
|
|
572
|
+
y: number;
|
|
573
|
+
/** The direction the slot faces, in radians, where the arrangement has one
|
|
574
|
+
* — outward from the center on a ring. `slotToPose` decides what to do
|
|
575
|
+
* with it; the default ignores it. */
|
|
576
|
+
angle?: number;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Where a container's slots are when it holds `count` children, in fill
|
|
580
|
+
* order. Returning fewer than `count` caps the container: `slots` refuses
|
|
581
|
+
* whatever would not fit.
|
|
582
|
+
*/
|
|
583
|
+
type Arrangement = (bounds: ContainerBounds, count: number) => Slot[];
|
|
584
|
+
/** Options for `rowSlots`. */
|
|
585
|
+
interface RowSlotsOptions {
|
|
586
|
+
/** The axis the row runs along. Default `'x'`. */
|
|
587
|
+
axis?: 'x' | 'y';
|
|
588
|
+
/** Distance between neighboring slots, in world units, the row centered in
|
|
589
|
+
* the container. Absent, the slots divide the container's length evenly. */
|
|
590
|
+
pitch?: number;
|
|
591
|
+
}
|
|
592
|
+
/** Slots in a line, centered across the container. */
|
|
593
|
+
declare function rowSlots(opts?: RowSlotsOptions): Arrangement;
|
|
594
|
+
/** Options for `gridSlots`. */
|
|
595
|
+
interface GridSlotsOptions {
|
|
596
|
+
cols: number;
|
|
597
|
+
/** Rows the grid has before any child needs one. Default 1. */
|
|
598
|
+
rows?: number;
|
|
599
|
+
}
|
|
600
|
+
/** Slots at the centers of a grid that fills row by row, adding a row when the
|
|
601
|
+
* last fills; the rows always divide the container's height. */
|
|
602
|
+
declare function gridSlots(opts: GridSlotsOptions): Arrangement;
|
|
603
|
+
/** Options for `ringSlots`. */
|
|
604
|
+
interface RingSlotsOptions {
|
|
605
|
+
/** Angle of the first slot, in radians, 0 pointing along +x and positive
|
|
606
|
+
* turning toward +y. Default −π/2: the top, on a y-down canvas. */
|
|
607
|
+
start?: number;
|
|
608
|
+
/** How far round the slots run. Default a full turn, which spaces them
|
|
609
|
+
* evenly; anything less puts a slot on each end of the arc. */
|
|
610
|
+
sweep?: number;
|
|
611
|
+
/** Distance to pull the ring in from the container's edges. Default 0. */
|
|
612
|
+
inset?: number;
|
|
613
|
+
}
|
|
614
|
+
/** Slots around the ellipse inscribed in the container, each facing outward. */
|
|
615
|
+
declare function ringSlots(opts?: RingSlotsOptions): Arrangement;
|
|
616
|
+
|
|
617
|
+
/** Options for `slots`. */
|
|
618
|
+
interface SlotsOptions<TPose> {
|
|
619
|
+
/** Where the slots are for a given child count: `rowSlots`, `gridSlots`,
|
|
620
|
+
* `ringSlots`, or a function of your own. */
|
|
621
|
+
arrangement: Arrangement;
|
|
622
|
+
/** Which slot a drag means. Default `nearest()`. */
|
|
623
|
+
snap?: LayoutSnap<TPose>;
|
|
624
|
+
/** The pose a child takes in a slot. Default centers the child's own
|
|
625
|
+
* `width`/`height` on the slot and keeps every other field. */
|
|
626
|
+
slotToPose?(slot: Slot, pose: TPose): TPose;
|
|
627
|
+
/** The point that decides which slot a child sits in. Default is the
|
|
628
|
+
* center of a `RectPose`, and the point itself for a point pose. */
|
|
629
|
+
centerOf?(pose: TPose): {
|
|
630
|
+
x: number;
|
|
631
|
+
y: number;
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Layout strategy that keeps its children in a sequence of slots — a row, a
|
|
636
|
+
* grid, a ring — whose positions depend on how many children there are, so
|
|
637
|
+
* the arrangement re-spaces itself as children come and go.
|
|
638
|
+
*
|
|
639
|
+
* A child's place in the sequence is the slot it sits nearest. Dropping a
|
|
640
|
+
* drag on a slot inserts it there and shifts the children between; a drag
|
|
641
|
+
* from outside is offered one slot more than the container holds. An insert
|
|
642
|
+
* or reparent lands at the slot nearest where the child was put.
|
|
643
|
+
*/
|
|
644
|
+
declare function slots<TPose>(opts: SlotsOptions<TPose>): LayoutStrategy<TPose>;
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* What `pack` does when its shelves run past the container's height.
|
|
648
|
+
*
|
|
649
|
+
* - `'reject'` (default) — refuse the child that would not fit, by any route.
|
|
650
|
+
* - `'grow'` — the container's height follows its content, never below
|
|
651
|
+
* `minHeight`.
|
|
652
|
+
* - `'scroll'` — the container keeps its size and the shelves run on below
|
|
653
|
+
* it; `contentExtent` reports the region they cover.
|
|
654
|
+
*/
|
|
655
|
+
type PackOverflow = 'reject' | 'grow' | 'scroll';
|
|
656
|
+
/** Options for `pack`. */
|
|
657
|
+
interface PackOptions<TPose> {
|
|
658
|
+
/** Space between neighbors on a shelf and between shelves, in world units. Default 0. */
|
|
659
|
+
gap?: number;
|
|
660
|
+
/** What happens past the container's height. Default `'reject'`. */
|
|
661
|
+
overflow?: PackOverflow;
|
|
662
|
+
/** The least height a `'grow'` container shrinks to. Default 0. */
|
|
663
|
+
minHeight?: number;
|
|
664
|
+
/** Which index a drag means. Default `nearest()`. */
|
|
665
|
+
snap?: LayoutSnap<TPose>;
|
|
666
|
+
/** A child's box. Default reads `x`, `y`, `width`, and `height` off the pose. */
|
|
667
|
+
rectOf?(pose: TPose): Rect;
|
|
668
|
+
/** `pose` with its box's top-left at `at`. Default writes `x` and `y`. */
|
|
669
|
+
moveTo?(pose: TPose, at: {
|
|
670
|
+
x: number;
|
|
671
|
+
y: number;
|
|
672
|
+
}): TPose;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Layout strategy that packs children of any size onto shelves: left to
|
|
676
|
+
* right at their own sizes, wrapping at the container's width, each shelf as
|
|
677
|
+
* tall as its tallest child. A child wider than the container is refused.
|
|
678
|
+
*
|
|
679
|
+
* The children's order is the order they read in — shelf by shelf, left to
|
|
680
|
+
* right. A drop inserts at the index nearest the pointer and everything after
|
|
681
|
+
* it reflows.
|
|
682
|
+
*/
|
|
683
|
+
declare function pack<TPose>(opts?: PackOptions<TPose>): LayoutStrategy<TPose>;
|
|
684
|
+
|
|
685
|
+
/** Options for `quadtree`. */
|
|
686
|
+
interface QuadtreeOptions<TPose> {
|
|
687
|
+
/** How many times the container may be quartered. Default 4, a 16 × 16 grid at its finest. */
|
|
688
|
+
maxDepth?: number;
|
|
689
|
+
/** Which free cell a drag means. Default `containedThenNearest()`. */
|
|
690
|
+
snap?: LayoutSnap<TPose>;
|
|
691
|
+
/** The length a cell's shorter side must reach to hold the child. Default
|
|
692
|
+
* is the larger of the pose's `width` and `height`. */
|
|
693
|
+
sizeOf?(pose: TPose): number;
|
|
694
|
+
/** The pose a child takes in a cell. Default centers the child's own size
|
|
695
|
+
* in the cell and keeps every other field. */
|
|
696
|
+
cellToPose?(cell: Rect, pose: TPose): TPose;
|
|
697
|
+
/** The point that decides which cell a child sits in. Default is the
|
|
698
|
+
* center of a `RectPose`, and the point itself for a point pose. */
|
|
699
|
+
centerOf?(pose: TPose): {
|
|
700
|
+
x: number;
|
|
701
|
+
y: number;
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
/** One cell a child holds: {@link QuadtreeStrategy.cells}. */
|
|
705
|
+
interface QuadtreeCell {
|
|
706
|
+
id: string;
|
|
707
|
+
/** How many times the container was quartered to reach the cell. */
|
|
708
|
+
depth: number;
|
|
709
|
+
rect: Rect;
|
|
710
|
+
}
|
|
711
|
+
/** A `quadtree` layout, which also reports the cells it has given out. */
|
|
712
|
+
interface QuadtreeStrategy<TPose> extends LayoutStrategy<TPose> {
|
|
713
|
+
/** The cell each child holds, largest first: what a debug overlay draws. */
|
|
714
|
+
cells(container: LayoutContainer, children: ReadonlyArray<LayoutChild<TPose>>): QuadtreeCell[];
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Layout strategy that quarters its container as far as each child needs: a
|
|
718
|
+
* child holds the smallest cell that fits it, so large and small children
|
|
719
|
+
* share one container without a fixed grid. A cell is free while it, the
|
|
720
|
+
* cells it sits inside, and the cells inside it hold nobody.
|
|
721
|
+
*
|
|
722
|
+
* A drag is offered the free cells of its own size. Children never move to
|
|
723
|
+
* make room: a child that finds its cell covered goes to the nearest free
|
|
724
|
+
* one, and an arrival with no free cell of its size is refused.
|
|
725
|
+
*/
|
|
726
|
+
declare function quadtree<TPose>(opts?: QuadtreeOptions<TPose>): QuadtreeStrategy<TPose>;
|
|
727
|
+
|
|
728
|
+
/** What makes a sequence layout a row of slots or a shelf-packed flow. */
|
|
729
|
+
interface Sequencer<TPose> {
|
|
730
|
+
/** `children` in sequence, read off the poses they were given when the
|
|
731
|
+
* container was last arranged for `count` children. */
|
|
732
|
+
order(container: LayoutContainer, children: ReadonlyArray<LayoutChild<TPose>>, count: number): LayoutChild<TPose>[];
|
|
733
|
+
/** A pose for every child of `sequence`, or `null` when the container
|
|
734
|
+
* cannot hold them all. */
|
|
735
|
+
place(container: LayoutContainer, sequence: ReadonlyArray<LayoutChild<TPose>>): Map<string, TPose> | null;
|
|
736
|
+
/** The bounds the container takes to hold `sequence`, for one that sizes
|
|
737
|
+
* itself to its children. */
|
|
738
|
+
resize?(container: LayoutContainer, sequence: ReadonlyArray<LayoutChild<TPose>>): ContainerBounds | undefined;
|
|
739
|
+
centerOf(pose: TPose): {
|
|
740
|
+
x: number;
|
|
741
|
+
y: number;
|
|
742
|
+
};
|
|
743
|
+
snap: LayoutSnap<TPose>;
|
|
744
|
+
/** Labels for the two kinds of op a drop writes. */
|
|
745
|
+
labels: {
|
|
746
|
+
drop: string;
|
|
747
|
+
shift: string;
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* The behavior every layout that keeps its children in one ordered sequence
|
|
752
|
+
* shares: a drop inserts at an index and shifts the children after it, an
|
|
753
|
+
* arrival goes in at the index that lands it nearest where it was put, and a
|
|
754
|
+
* departure closes the gap. The `Sequencer` says what the order is and where
|
|
755
|
+
* a sequence sits.
|
|
756
|
+
*/
|
|
757
|
+
declare function sequenceLayout<TPose>(seq: Sequencer<TPose>): LayoutStrategy<TPose>;
|
|
758
|
+
|
|
759
|
+
export { type AlignAnchor, type AlignMatchResult, type AlignMoveArgs, type AlignmentBehaviorBase, type Arrangement, type CellHighlightLayerOpts, DEFAULT_GUIDE_TOLERANCE_PX, type DeriveAlignmentGuidesOptions, type FreeformOptions, type GridLayerOpts, type GridSlotsOptions, Guide, type GuideSnapOptions, type GuidesLayerOpts, MOVE_ANCHORS, type PackOptions, type PackOverflow, type PerContainerOptions, type PerContainerStrategy, type QuadtreeCell, type QuadtreeOptions, type QuadtreeStrategy, type RingSlotsOptions, type RowSlotsOptions, type Sequencer, type Slot, type SlotsOptions, 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, gridSlots, gridSnapStrategy, guideSnapStrategy, matchAlignment, matchSpacing, measureGaps, nearest, nearestWithin, none, pack, perContainer, pointToGridCell, quadtree, ringSlots, roundToCell, rowSlots, sequenceLayout, slots, snapPoint, tileGrid, useGridCellHover, useGuides };
|