@weasel-js/guides 1.7.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/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/chunk-5BCCIJY3.js +33 -0
- package/dist/chunk-5BCCIJY3.js.map +1 -0
- package/dist/chunk-B25KFI3I.js +18 -0
- package/dist/chunk-B25KFI3I.js.map +1 -0
- package/dist/chunk-GEDAPTOM.js +48 -0
- package/dist/chunk-GEDAPTOM.js.map +1 -0
- package/dist/index.d.ts +463 -0
- package/dist/index.js +756 -0
- package/dist/index.js.map +1 -0
- package/dist/insert.d.ts +30 -0
- package/dist/insert.js +77 -0
- package/dist/insert.js.map +1 -0
- package/dist/move.d.ts +42 -0
- package/dist/move.js +18 -0
- package/dist/move.js.map +1 -0
- package/dist/pointSnapToGrid-BaVEjV23.d.ts +13 -0
- package/dist/resize.d.ts +38 -0
- package/dist/resize.js +111 -0
- package/dist/resize.js.map +1 -0
- package/dist/types-D2tTKEU0.d.ts +18 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/grid/roundToCell.ts","../src/grid/useGridCellHover.ts","../src/grid/layer.ts","../src/grid/cellHighlight.ts","../src/guides/useGuides.ts","../src/guides/layer.ts","../src/guides/alignment/derive.ts","../src/guides/alignment/match.ts","../src/guides/alignment/behaviors.ts","../src/layout/snaps.ts","../src/layout/strategies/freeform.ts","../src/layout/strategies/tileGrid.ts","../src/layout/strategies/snapPoint.ts"],"names":["resolveUnit","useState","useLatest","useCallback","PATH_M","PATH_L","AUTO_POSE_DESCRIPTOR","visualBoundsViaDescriptor","createTransformOp"],"mappings":"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","file":"index.js","sourcesContent":["/** Round `value` to the nearest multiple of `cellSize`. Returns 0 when the result would be -0. */\nexport function roundToCell(value: number, cellSize: number): number {\n return Math.round(value / cellSize) * cellSize || 0;\n}\n","/**\n * Pointer-tracking glue that turns hover position into a grid cell, suitable\n * for dropping straight into `createCellHighlightLayer({ getCell })`. Pairs\n * with `gridSnapStrategy` / `createGridLayer` so the visual hover preview\n * uses the exact same spacing as the snap behavior.\n *\n * Wires three things you'd otherwise glue by hand:\n * 1. pointermove on a target element → client coords\n * 2. client coords → world coords via your `ViewTransform`\n * 3. world coords → integer `{col, row}` via `pointToGridCell`\n *\n * State updates only fire when the cell actually changes (not on every\n * pointer tick). Pass `onChange` to trigger a canvas redraw on each transition.\n */\n\nimport { useCallback, useEffect, useRef, useState, type RefObject } from 'react';\nimport {\n useLatest,\n screenToWorld,\n type ViewTransform,\n type UnitSystem,\n type UnitValue,\n} from '@weasel-js/core';\nimport { pointToGridCell } from '../strategies/grid';\n\n/** Options for `useGridCellHover`. */\nexport interface UseGridCellHoverOptions {\n /** Element to attach pointer listeners to (typically the canvas). */\n ref: RefObject<HTMLElement | null>;\n /** Read the current view transform. Called on every pointermove so it can\n * reflect live pan/zoom without a re-binding step. */\n view: () => ViewTransform;\n /** Cell spacing in world units. Same value you pass to `createGridLayer`\n * and `createCellHighlightLayer`. */\n spacing: UnitValue;\n /** Required if `spacing` is tagged (e.g. `{ value: 1, unit: 'ft' }`). */\n unitSystem?: UnitSystem;\n /** Origin the cell grid is anchored to, in world units. Default `(0, 0)`. */\n origin?: { x: number; y: number };\n /** Fires when the hovered cell changes (including to/from `null`). Use it\n * to invalidate your layer renderer. */\n onChange?: (cell: { col: number; row: number } | null) => void;\n /** Disable the listeners without unmounting. Default `true`. */\n enabled?: boolean;\n}\n\n/** Return shape of `useGridCellHover`. */\nexport interface UseGridCellHoverReturn {\n /** Current hovered cell, or `null` when the pointer is outside the element. */\n cell: { col: number; row: number } | null;\n /** Stable callback that returns the current cell — drop straight into\n * `createCellHighlightLayer({ getCell })`. */\n getCell: () => { col: number; row: number } | null;\n}\n\nfunction cellsEqual(\n a: { col: number; row: number } | null,\n b: { col: number; row: number } | null,\n): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n return a.col === b.col && a.row === b.row;\n}\n\n/** Track which grid cell the pointer is over; pairs with `createCellHighlightLayer`. */\nexport function useGridCellHover(opts: UseGridCellHoverOptions): UseGridCellHoverReturn {\n const [cell, setCell] = useState<{ col: number; row: number } | null>(null);\n const cellRef = useRef<{ col: number; row: number } | null>(null);\n const optsRef = useLatest(opts);\n\n const getCell = useCallback(() => cellRef.current, []);\n\n const enabled = opts.enabled ?? true;\n useEffect(() => {\n if (!enabled) return;\n const el = opts.ref.current;\n if (!el) return;\n\n const update = (next: { col: number; row: number } | null) => {\n if (cellsEqual(cellRef.current, next)) return;\n cellRef.current = next;\n setCell(next);\n optsRef.current.onChange?.(next);\n };\n\n const onMove = (e: PointerEvent) => {\n const o = optsRef.current;\n const rect = el.getBoundingClientRect();\n const sx = e.clientX - rect.left;\n const sy = e.clientY - rect.top;\n const [wx, wy] = screenToWorld(sx, sy, o.view());\n update(pointToGridCell({ x: wx, y: wy }, o.spacing, o.unitSystem, o.origin));\n };\n const onLeave = () => update(null);\n\n el.addEventListener('pointermove', onMove);\n el.addEventListener('pointerleave', onLeave);\n el.addEventListener('pointercancel', onLeave);\n return () => {\n el.removeEventListener('pointermove', onMove);\n el.removeEventListener('pointerleave', onLeave);\n el.removeEventListener('pointercancel', onLeave);\n };\n }, [enabled, opts.ref, optsRef]);\n\n return { cell, getCell };\n}\n","/**\n * Grid overlay — a reusable render layer that draws a world-space grid with\n * optional finer subdivisions and accent lines every N cells.\n *\n * The layer renders in world space — the caller is expected to have applied\n * any view transform to the canvas context already. To draw a highlighted\n * cell (snap-target preview), stack `createCellHighlightLayer` alongside\n * this one.\n */\n\nimport {\n type DrawCommand,\n type RenderLayer,\n type Stroke,\n resolveStrokeWidth,\n resolveUnit,\n type UnitSystem,\n type UnitValue,\n pxExtent,\n} from '@weasel-js/core';\nimport { PATH_L, PATH_M, type PolygonPath } from '@weasel-js/geom';\n\n/** Options for `createGridLayer`. */\nexport interface GridLayerOpts {\n /**\n * Distance between adjacent grid lines (also known as grid pitch),\n * in world (base) units. Accepts a bare number or a tagged\n * `{ value, unit }`. Tagged values require `unitSystem` to be supplied.\n */\n spacing: UnitValue;\n /** Optional unit system for resolving tagged `spacing` values. */\n unitSystem?: UnitSystem;\n /** Bounds of the area to cover, in world units. */\n bounds: () => { x: number; y: number; width: number; height: number };\n /** Lines every N cells get the accent style. 0/undef = no accent. */\n accentEvery?: number;\n /** Optional finer subdivisions per cell. e.g. 4 -> 4 sub-lines per cell. */\n subdivisions?: number;\n /** Per-band stroke styles. Each is a FillStyle+width (etc.) — see `Stroke`. */\n style?: {\n line?: Stroke;\n accent?: Stroke;\n sub?: Stroke;\n };\n}\n\n// Defaults express weight via opacity rather than a fixed dark color so the\n// grid reads correctly against any background fill (kit demos use a neutral\n// gray; consumer apps may use cream / dark / themed surfaces). Override\n// per-canvas via `style.{line,accent,sub}.paint` if a hard color is needed.\nconst DEFAULT_LINE: Stroke = {\n paint: { fill: 'solid', color: 'rgba(255, 255, 255, 0.08)' },\n width: 1,\n};\nconst DEFAULT_ACCENT: Stroke = {\n paint: { fill: 'solid', color: 'rgba(255, 255, 255, 0.16)' },\n width: 1,\n};\nconst DEFAULT_SUB: Stroke = {\n paint: { fill: 'solid', color: 'rgba(255, 255, 255, 0.04)' },\n width: 1,\n};\n\n/** Build a `RenderLayer` that draws a world-space grid with optional accent lines and subdivisions. */\nexport function createGridLayer(opts: GridLayerOpts): RenderLayer<unknown> {\n const line = opts.style?.line ?? DEFAULT_LINE;\n const accent = opts.style?.accent ?? DEFAULT_ACCENT;\n const sub = opts.style?.sub ?? DEFAULT_SUB;\n\n return {\n id: 'grid',\n label: 'Grid',\n draw: (_data, view) => {\n const b = opts.bounds();\n if (b.width <= 0 || b.height <= 0) return [];\n\n const { accentEvery, subdivisions } = opts;\n const spacing = resolveUnit(opts.spacing, opts.unitSystem);\n const x0 = b.x;\n const y0 = b.y;\n const x1 = b.x + b.width;\n const y1 = b.y + b.height;\n // Every line is axis-aligned, so its width runs along one screen axis\n // and one pixel of it is exactly that axis's extent.\n const px = pxExtent(1, view.scale);\n\n const children: DrawCommand[] = [];\n\n const pushSegment = (sx: number, sy: number, ex: number, ey: number, s: Stroke): void => {\n const path: PolygonPath = {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L]),\n coords: new Float32Array([sx, sy, ex, ey]),\n fillRule: 'nonzero',\n };\n const across = sx === ex ? px.x : px.y;\n children.push({\n kind: 'path',\n path,\n stroke: { ...s, width: resolveStrokeWidth(s.width ?? 1, 1) * across },\n });\n };\n\n // Sub-lines (drawn first so cell lines paint on top).\n if (subdivisions && subdivisions > 1) {\n const step = spacing / subdivisions;\n for (let x = x0; x <= x1 + 1e-9; x += step) {\n const k = Math.round((x - x0) / step);\n if (k % subdivisions === 0) continue;\n pushSegment(x, y0, x, y1, sub);\n }\n for (let y = y0; y <= y1 + 1e-9; y += step) {\n const k = Math.round((y - y0) / step);\n if (k % subdivisions === 0) continue;\n pushSegment(x0, y, x1, y, sub);\n }\n }\n\n // Cell lines (skip ones that will become accents).\n let idx = 0;\n for (let x = x0; x <= x1 + 1e-9; x += spacing) {\n const isAccent = !!accentEvery && accentEvery > 0 && idx % accentEvery === 0;\n if (!isAccent) pushSegment(x, y0, x, y1, line);\n idx++;\n }\n idx = 0;\n for (let y = y0; y <= y1 + 1e-9; y += spacing) {\n const isAccent = !!accentEvery && accentEvery > 0 && idx % accentEvery === 0;\n if (!isAccent) pushSegment(x0, y, x1, y, line);\n idx++;\n }\n\n // Accent lines on top.\n if (accentEvery && accentEvery > 0) {\n idx = 0;\n for (let x = x0; x <= x1 + 1e-9; x += spacing) {\n if (idx % accentEvery === 0) pushSegment(x, y0, x, y1, accent);\n idx++;\n }\n idx = 0;\n for (let y = y0; y <= y1 + 1e-9; y += spacing) {\n if (idx % accentEvery === 0) pushSegment(x0, y, x1, y, accent);\n idx++;\n }\n }\n\n // World-space commands; drawLayers wraps in viewToMat3 automatically.\n return children;\n },\n };\n}\n","/**\n * Cell highlight overlay — a thin render layer that fills a single grid cell\n * (e.g. a snap-target preview). Stack it under or over `createGridLayer` in\n * a layer sequence.\n */\n\nimport {\n type DrawCommand,\n type RenderLayer,\n type FillStyle,\n resolveUnit,\n type UnitSystem,\n type UnitValue,\n} from '@weasel-js/core';\n\n/** Options for `createCellHighlightLayer`. */\nexport interface CellHighlightLayerOpts {\n /** Cell size in world units (matches the grid's `spacing`). */\n spacing: UnitValue;\n /** Optional unit system for resolving tagged `spacing` values. */\n unitSystem?: UnitSystem;\n /** Origin the cell grid is anchored to, in world units. Defaults to (0,0). */\n origin?: () => { x: number; y: number };\n /** Cell to highlight, or `null` to skip drawing. */\n getCell: () => { col: number; row: number } | null;\n /** FillStyle strategy for the filled cell. Defaults to a soft green. */\n fill?: FillStyle;\n}\n\nconst DEFAULT_FILL: FillStyle = { fill: 'solid', color: 'rgba(127,176,105,0.15)' };\n\n/** Build a `RenderLayer` that fills a single grid cell — typically a snap-target preview. */\nexport function createCellHighlightLayer(opts: CellHighlightLayerOpts): RenderLayer<unknown> {\n const fill = opts.fill ?? DEFAULT_FILL;\n return {\n id: 'cell-highlight',\n label: 'Cell highlight',\n draw: () => {\n const cell = opts.getCell();\n if (!cell) return [];\n const spacing = resolveUnit(opts.spacing, opts.unitSystem);\n const o = opts.origin ? opts.origin() : { x: 0, y: 0 };\n const x = o.x + cell.col * spacing;\n const y = o.y + cell.row * spacing;\n const children: DrawCommand[] = [\n {\n kind: 'path',\n path: { kind: 'rect', x, y, width: spacing, height: spacing },\n fill,\n },\n ];\n // World-space commands; drawLayers wraps in viewToMat3 automatically.\n return children;\n },\n };\n}\n","import { useCallback, useState } from 'react';\nimport { useLatest } from '@weasel-js/core';\nimport type { Guide } from './types';\n\n/** Return shape for `useGuides`. */\nexport interface UseGuidesReturn {\n /** Live list (state). Re-renders when guides change. */\n guides: Guide[];\n /** Stable getter; reads the latest list without forcing a re-render.\n * Pass to `guideSnapStrategy` and `createGuidesLayer`. */\n getGuides: () => Guide[];\n /** Add a guide. If a guide with the same id already exists it is replaced. */\n addGuide: (guide: Guide) => void;\n /** Remove a guide by id. No-op when missing. */\n removeGuide: (id: string) => void;\n /** Drop all guides. */\n clearGuides: () => void;\n}\n\n/**\n * In-memory guide storage. Holds a list of horizontal/vertical world-space\n * lines that callers can pass to `guideSnapStrategy` (snap behavior) and\n * `createGuidesLayer` (render layer).\n *\n * v1 is intentionally minimal: no undo integration, no persistence, no\n * batched mutation. Wrap calls in your own history-aware action if you\n * want guide CRUD to be undoable.\n */\nexport function useGuides(initial: Guide[] = []): UseGuidesReturn {\n const [guides, setGuides] = useState<Guide[]>(initial);\n const ref = useLatest(guides);\n\n const getGuides = useCallback(() => ref.current, [ref]);\n\n const addGuide = useCallback((guide: Guide) => {\n setGuides((prev) => {\n const idx = prev.findIndex((g) => g.id === guide.id);\n if (idx === -1) return [...prev, guide];\n const next = prev.slice();\n next[idx] = guide;\n return next;\n });\n }, []);\n\n const removeGuide = useCallback((id: string) => {\n setGuides((prev) => {\n const idx = prev.findIndex((g) => g.id === id);\n if (idx === -1) return prev;\n const next = prev.slice();\n next.splice(idx, 1);\n return next;\n });\n }, []);\n\n const clearGuides = useCallback(() => {\n setGuides((prev) => (prev.length === 0 ? prev : []));\n }, []);\n\n return { guides, getGuides, addGuide, removeGuide, clearGuides };\n}\n","/**\n * Guides overlay — a screen-space render layer that draws each guide as a\n * thin line spanning the full canvas, projecting the guide's world offset\n * through the active view.\n *\n * Guides are not scene objects; they live outside the scene and are managed\n * via `useGuides`. Pair with `guideSnapStrategy` (snap behavior) to keep\n * the visual guide and the snap target in lockstep.\n */\n\nimport type { DrawCommand, RenderLayer, Stroke } from '@weasel-js/core';\nimport { PATH_L, PATH_M, type PolygonPath } from '@weasel-js/geom';\nimport type { Guide } from './types';\n\n/** Options for `createGuidesLayer`. */\nexport interface GuidesLayerOpts {\n /** Stable getter for the live guide list — typically `useGuides().getGuides`. */\n getGuides: () => readonly Guide[];\n /** Stroke width in CSS pixels. Default 1. */\n lineWidth?: number;\n /** Stroke color. Default a subtle blue. */\n color?: string;\n /** Optional dash pattern (CSS px). Currently unused — reserved for future\n * parity with chrome that supports dashes. */\n dash?: number[];\n /** Override the layer id. Useful if a consumer needs more than one\n * guides-layer (e.g. user-guides vs. system-guides). */\n id?: string;\n /** Override the layer label. */\n label?: string;\n}\n\nconst DEFAULT_COLOR = '#3aa0ff';\n\n/** Build a `RenderLayer` that draws guide lines across the full canvas in\n * screen space, projected from world offsets via the active view. */\nexport function createGuidesLayer(opts: GuidesLayerOpts): RenderLayer<unknown> {\n const lineWidth = opts.lineWidth ?? 1;\n const color = opts.color ?? DEFAULT_COLOR;\n const stroke: Stroke = {\n paint: { fill: 'solid', color },\n width: lineWidth,\n };\n\n return {\n id: opts.id ?? 'guides',\n label: opts.label ?? 'Guides',\n space: 'screen',\n draw: (_data, view, dims) => {\n const guides = opts.getGuides();\n if (guides.length === 0) return [];\n\n const out: DrawCommand[] = [];\n for (const g of guides) {\n if (g.axis === 'x') {\n // Vertical line at world x = offset → screen x = (offset - view.x) * scale.\n const sx = (g.offset - view.x) * view.scale.x;\n if (sx < -lineWidth || sx > dims.width + lineWidth) continue;\n const path: PolygonPath = {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L]),\n coords: new Float32Array([sx, 0, sx, dims.height]),\n fillRule: 'nonzero',\n };\n out.push({ kind: 'path', path, stroke });\n } else {\n // Horizontal line at world y = offset → screen y = (offset - view.y) * scale.\n const sy = (g.offset - view.y) * view.scale.y;\n if (sy < -lineWidth || sy > dims.height + lineWidth) continue;\n const path: PolygonPath = {\n kind: 'polygon',\n commands: new Uint8Array([PATH_M, PATH_L]),\n coords: new Float32Array([0, sy, dims.width, sy]),\n fillRule: 'nonzero',\n };\n out.push({ kind: 'path', path, stroke });\n }\n }\n return out;\n },\n };\n}\n","import type { Guide } from '../types';\nimport type { DeriveAlignmentGuidesOptions } from './types';\nimport {\n type Bounds,\n type PoseDescriptor,\n visualBoundsViaDescriptor,\n AUTO_POSE_DESCRIPTOR,\n} from '@weasel-js/core';\n\nconst EPS = 1e-3;\n\n/** Derive candidate alignment lines from a set of sibling poses plus an\n * optional page box. Each box contributes up to 3 guides per axis: the two\n * edges and the center. Overlapping offsets collapse to one candidate.\n * Poses go through the same descriptor `alignMoveBehavior` matches with, so\n * a rotated sibling advertises its ink edges rather than its stored box. */\nexport function deriveAlignmentGuides<TPose = Bounds>(\n targets: readonly TPose[],\n opts: DeriveAlignmentGuidesOptions<TPose> = {},\n): Guide[] {\n const d = (opts.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n const edges = opts.edges ?? true;\n const centers = opts.centers ?? true;\n // Dedup per axis: key = rounded offset. First writer wins (stable id).\n const seenX = new Map<number, Guide>();\n const seenY = new Map<number, Guide>();\n\n const add = (axis: 'x' | 'y', offset: number): void => {\n const seen = axis === 'x' ? seenX : seenY;\n const key = Math.round(offset / EPS);\n if (seen.has(key)) return;\n seen.set(key, { id: `align:${axis}:${offset.toFixed(3)}`, axis, offset });\n };\n\n const emit = (b: Bounds): void => {\n if (edges) {\n add('x', b.x);\n add('x', b.x + b.width);\n add('y', b.y);\n add('y', b.y + b.height);\n }\n if (centers) {\n add('x', b.x + b.width / 2);\n add('y', b.y + b.height / 2);\n }\n };\n\n for (const t of targets) emit(visualBoundsViaDescriptor(t, d));\n if (opts.page) emit(opts.page);\n\n return [...seenX.values(), ...seenY.values()];\n}\n","import type { Guide } from '../types';\nimport type {\n AlignAnchor,\n AlignMatchResult,\n} from './types';\nimport type { Bounds } from '@weasel-js/core';\n\n/** Move/insert test all three features per axis. */\nexport const MOVE_ANCHORS: { x: readonly AlignAnchor[]; y: readonly AlignAnchor[] } = {\n x: ['min', 'center', 'max'],\n y: ['min', 'center', 'max'],\n};\n\nfunction featureOffset(b: Bounds, axis: 'x' | 'y', anchor: AlignAnchor): number {\n if (axis === 'x') {\n if (anchor === 'min') return b.x;\n if (anchor === 'center') return b.x + b.width / 2;\n return b.x + b.width;\n }\n if (anchor === 'min') return b.y;\n if (anchor === 'center') return b.y + b.height / 2;\n return b.y + b.height;\n}\n\n/** Best (feature, candidate) match on one axis, within tolerance. */\nfunction bestAxis(\n b: Bounds,\n axis: 'x' | 'y',\n anchors: readonly AlignAnchor[],\n candidates: readonly Guide[],\n worldTolerance: number,\n): { delta: number; guide: Guide | null } {\n let bestAbs = Infinity;\n let bestDelta = 0;\n let bestGuide: Guide | null = null;\n for (const anchor of anchors) {\n const o = featureOffset(b, axis, anchor);\n for (const g of candidates) {\n if (g.axis !== axis) continue;\n const d = g.offset - o;\n const ad = Math.abs(d);\n if (ad <= worldTolerance && ad < bestAbs) {\n bestAbs = ad;\n bestDelta = d;\n bestGuide = g;\n }\n }\n }\n return { delta: bestGuide ? bestDelta : 0, guide: bestGuide };\n}\n\n/**\n * Match a moving box's selected edge/center features against candidate guide\n * lines. Returns the per-axis snap delta and the matched candidate line(s).\n * The two axes resolve independently; on each axis the closest in-tolerance\n * (feature, candidate) pair wins.\n *\n * `worldTolerance` is per axis because a screen-pixel tolerance is not one\n * world distance under non-uniform zoom — and each axis here is matched by a\n * distance along that axis alone, so there is an exact answer rather than an\n * approximation. Pass the same number twice for a world-space tolerance.\n */\nexport function matchAlignment(\n bounds: Bounds,\n candidates: readonly Guide[],\n worldTolerance: { x: number; y: number },\n anchors: { x: readonly AlignAnchor[]; y: readonly AlignAnchor[] },\n): AlignMatchResult {\n const x = bestAxis(bounds, 'x', anchors.x, candidates, worldTolerance.x);\n const y = bestAxis(bounds, 'y', anchors.y, candidates, worldTolerance.y);\n return { dx: x.delta, dy: y.delta, activeX: x.guide, activeY: y.guide };\n}\n","import type { Guide } from '../types';\nimport {\n type BoundsConstraint,\n type InsertBehavior,\n type MoveBehavior,\n type ResizeAnchor,\n type Bounds,\n screenTolerance,\n unionBounds,\n type PoseDescriptor,\n translatePoseViaDescriptor,\n visualBoundsViaDescriptor,\n AUTO_POSE_DESCRIPTOR,\n fixedCornerOf,\n} from '@weasel-js/core';\nimport type {\n AlignAnchor,\n AlignmentBehaviorBase,\n} from './types';\nimport { MOVE_ANCHORS, matchAlignment } from './match';\n\n/** Options for move and resize — adds the pose descriptor for non-rect poses. */\nexport interface AlignMoveArgs<TPose> extends AlignmentBehaviorBase {\n poseDescriptor?: PoseDescriptor<TPose>;\n}\n\nconst activeList = (m: { activeX: Guide | null; activeY: Guide | null }): Guide[] =>\n [m.activeX, m.activeY].filter((g): g is Guide => g !== null);\n\nfunction worldTol(\n base: AlignmentBehaviorBase,\n ctx: Parameters<typeof screenTolerance>[1],\n): { x: number; y: number } {\n return screenTolerance(base.tolerance ?? 6, ctx);\n}\n\n/** Move behavior: snap the dragged selection's union box (edges + center) to\n * candidates, shaping the proposed translate. The gesture applies the\n * transform uniformly to every dragged id, so the selection shifts together\n * and stays rigid. Single-select is the degenerate one-box union. Publishes\n * the matched line(s); clears on miss, end and cancel. */\nexport function alignMoveBehavior<TPose>(args: AlignMoveArgs<TPose>): MoveBehavior<TPose> {\n const d = (args.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n return {\n onMove(ctx, transform) {\n if (args.bypassKey && ctx.modifiers[args.bypassKey]) { args.setActiveGuides([]); return; }\n if (transform.kind !== 'translate') return;\n // Union of every dragged id's visual box at its proposed position.\n const boxes: Bounds[] = [];\n for (const id of ctx.draggedIds) {\n const originPose = ctx.origin.get(id);\n if (originPose === undefined) continue;\n boxes.push(visualBoundsViaDescriptor(\n translatePoseViaDescriptor(originPose, transform.dx, transform.dy, d),\n d,\n ));\n }\n const union = unionBounds(boxes);\n if (union === null) return;\n const m = matchAlignment(union, args.getCandidates(), worldTol(args, ctx), MOVE_ANCHORS);\n if (m.activeX === null && m.activeY === null) { args.setActiveGuides([]); return; }\n args.setActiveGuides(activeList(m));\n return { transform: { kind: 'translate', dx: transform.dx + m.dx, dy: transform.dy + m.dy } };\n },\n onEnd() { args.setActiveGuides([]); },\n onCancel() { args.setActiveGuides([]); },\n };\n}\n\n/** Insert behavior: snap the live `current` point to candidates (treating it\n * as a zero-size box). Publishes the matched line(s). */\nexport function alignInsertBehavior<TPose>(args: AlignmentBehaviorBase): InsertBehavior<TPose> {\n const pointAnchors: { x: readonly AlignAnchor[]; y: readonly AlignAnchor[] } = { x: ['min'], y: ['min'] };\n return {\n onMove(ctx, { current }) {\n if (args.bypassKey && ctx.modifiers[args.bypassKey]) { args.setActiveGuides([]); return; }\n const box = { x: current.x, y: current.y, width: 0, height: 0 };\n const m = matchAlignment(box, args.getCandidates(), worldTol(args, ctx), pointAnchors);\n if (m.activeX === null && m.activeY === null) { args.setActiveGuides([]); return; }\n args.setActiveGuides(activeList(m));\n return { current: { x: current.x + m.dx, y: current.y + m.dy } };\n },\n onEnd() { args.setActiveGuides([]); },\n onCancel() { args.setActiveGuides([]); },\n };\n}\n\n/** Resize constraint: snap the corner(s) the drag moves, as drawn, to\n * candidates; the pinned corner stays fixed. On an unrotated pose that is\n * the moving edge(s). On a rotated one the node's own axes set how the\n * corner can travel: a corner handle snaps each world axis, an edge handle\n * slides its edge until the nearer of its two corners meets a line.\n * `poseDescriptor` reads the origin pose's box and rotation, and must match\n * the resize action's. Publishes the matched line(s). */\nexport function alignResizeBehavior<TPose extends Bounds>(\n args: AlignMoveArgs<TPose>,\n): BoundsConstraint<TPose> {\n const d = (args.poseDescriptor ?? AUTO_POSE_DESCRIPTOR) as PoseDescriptor<TPose>;\n return {\n onMove(ctx, { pose, anchor }) {\n if (args.bypassKey && ctx.modifiers[args.bypassKey]) { args.setActiveGuides([]); return; }\n const origin = ctx.origin.get(ctx.draggedIds[0]);\n const rotation = origin === undefined ? 0 : (d.getRotation?.(origin) ?? 0);\n const m = rotation === 0\n ? matchMovingEdges(pose, anchor, args.getCandidates(), worldTol(args, ctx))\n : matchRotatedCorners(pose, anchor, d.getBounds(origin!), rotation, args.getCandidates(), worldTol(args, ctx));\n if (m === null) { args.setActiveGuides([]); return; }\n\n let { x, y, width, height } = pose;\n if (anchor.x === 'min') width += m.lx;\n else if (anchor.x === 'max') { x += m.lx; width -= m.lx; }\n if (anchor.y === 'min') height += m.ly;\n else if (anchor.y === 'max') { y += m.ly; height -= m.ly; }\n args.setActiveGuides(m.guides);\n return { pose: { ...pose, x, y, width, height } };\n },\n onEnd() { args.setActiveGuides([]); },\n onCancel() { args.setActiveGuides([]); },\n };\n}\n\n/** How far to move the dragged corner in the node's local frame, and the\n * lines that asked for it. */\ninterface CornerShift { lx: number; ly: number; guides: Guide[] }\n\nfunction matchMovingEdges(\n pose: Bounds,\n anchor: ResizeAnchor,\n candidates: readonly Guide[],\n tol: { x: number; y: number },\n): CornerShift | null {\n // A 'min' anchor pins the west/north edge, so the max edge moves.\n const movingX: AlignAnchor[] = anchor.x === 'min' ? ['max'] : anchor.x === 'max' ? ['min'] : [];\n const movingY: AlignAnchor[] = anchor.y === 'min' ? ['max'] : anchor.y === 'max' ? ['min'] : [];\n const m = matchAlignment(pose, candidates, tol, { x: movingX, y: movingY });\n if (m.activeX === null && m.activeY === null) return null;\n return { lx: m.dx, ly: m.dy, guides: activeList(m) };\n}\n\n/** The rotated case. The resize action pins the fixed corner in world after\n * constraints run, so a corner's drawn position is the origin's fixed corner\n * plus the rotated offset to it within `pose`. */\nfunction matchRotatedCorners(\n pose: Bounds,\n anchor: ResizeAnchor,\n originBox: Bounds,\n rotation: number,\n candidates: readonly Guide[],\n tol: { x: number; y: number },\n): CornerShift | null {\n const cos = Math.cos(rotation);\n const sin = Math.sin(rotation);\n const toWorld = (lx: number, ly: number) => ({ x: lx * cos - ly * sin, y: lx * sin + ly * cos });\n const fixedLocal = fixedCornerOf(pose, anchor);\n const f0 = fixedCornerOf(originBox, anchor);\n const pivot = toWorld(f0.x - originBox.x - originBox.width / 2, f0.y - originBox.y - originBox.height / 2);\n const fixedWorld = { x: originBox.x + originBox.width / 2 + pivot.x, y: originBox.y + originBox.height / 2 + pivot.y };\n const drawn = (lx: number, ly: number) => {\n const o = toWorld(lx - fixedLocal.x, ly - fixedLocal.y);\n return { x: fixedWorld.x + o.x, y: fixedWorld.y + o.y };\n };\n const movingEdge = (axis: 'x' | 'y'): number[] => {\n const lo = axis === 'x' ? pose.x : pose.y;\n const hi = lo + (axis === 'x' ? pose.width : pose.height);\n const a = anchor[axis];\n return a === 'min' ? [hi] : a === 'max' ? [lo] : [lo, hi];\n };\n\n if (anchor.x !== 'free' && anchor.y !== 'free') {\n // A corner handle's corner has two degrees of freedom: snap it as a point.\n const c = drawn(movingEdge('x')[0], movingEdge('y')[0]);\n const m = matchAlignment({ x: c.x, y: c.y, width: 0, height: 0 }, candidates, tol, { x: ['min'], y: ['min'] });\n if (m.activeX === null && m.activeY === null) return null;\n const lx = m.dx * cos + m.dy * sin;\n const ly = -m.dx * sin + m.dy * cos;\n return { lx, ly, guides: activeList(m) };\n }\n if (anchor.x === 'free' && anchor.y === 'free') return null;\n\n // An edge handle moves along one local axis, carrying two corners with it.\n const along: 'x' | 'y' = anchor.x === 'free' ? 'y' : 'x';\n const dir = along === 'x' ? toWorld(1, 0) : toWorld(0, 1);\n const edge = movingEdge(along)[0];\n let best: { t: number; guide: Guide } | null = null;\n for (const across of movingEdge(along === 'x' ? 'y' : 'x')) {\n const c = along === 'x' ? drawn(edge, across) : drawn(across, edge);\n for (const g of candidates) {\n const reach = g.axis === 'x' ? dir.x : dir.y;\n if (Math.abs(reach) < 1e-9) continue;\n const t = (g.offset - (g.axis === 'x' ? c.x : c.y)) / reach;\n if (Math.abs(t * dir.x) > tol.x || Math.abs(t * dir.y) > tol.y) continue;\n if (best === null || Math.abs(t) < Math.abs(best.t)) best = { t, guide: g };\n }\n }\n if (best === null) return null;\n return along === 'x'\n ? { lx: best.t, ly: 0, guides: [best.guide] }\n : { lx: 0, ly: best.t, guides: [best.guide] };\n}\n","import type { DropTarget, LayoutSnap } from '@weasel-js/core';\n\nfunction dist2(a: { x: number; y: number }, b: { x: number; y: number }): number {\n const dx = a.x - b.x;\n const dy = a.y - b.y;\n return dx * dx + dy * dy;\n}\n\nfunction nearestOf<TPose>(\n targets: DropTarget<TPose>[],\n pointer: { x: number; y: number },\n): DropTarget<TPose> | null {\n if (targets.length === 0) return null;\n let best = targets[0];\n let bestD = dist2(best.origin, pointer);\n for (let i = 1; i < targets.length; i++) {\n const d = dist2(targets[i].origin, pointer);\n if (d < bestD) {\n bestD = d;\n best = targets[i];\n }\n }\n return best;\n}\n\n/** Snap policy that never picks a target — every drop is rejected, which is\n * what a free-form container wants. */\nexport function none<TPose>(): LayoutSnap<TPose> {\n return { pickTarget: () => null };\n}\n\n/** Snap policy that always picks the target closest to the pointer, however\n * far away it is. */\nexport function nearest<TPose>(): LayoutSnap<TPose> {\n return { pickTarget: (targets, pointer) => nearestOf(targets, pointer) };\n}\n\n/** Snap policy that picks the closest target, but only within `tolerance`\n * world units — beyond that the drop is rejected. */\nexport function nearestWithin<TPose>(opts: { tolerance: number }): LayoutSnap<TPose> {\n const tol2 = opts.tolerance * opts.tolerance;\n return {\n pickTarget(targets, pointer) {\n const got = nearestOf(targets, pointer);\n if (got === null) return null;\n return dist2(got.origin, pointer) <= tol2 ? got : null;\n },\n };\n}\n\nfunction rectContains(\n r: { x: number; y: number; width: number; height: number },\n p: { x: number; y: number },\n): boolean {\n return p.x >= r.x && p.x < r.x + r.width && p.y >= r.y && p.y < r.y + r.height;\n}\n\n/**\n * Region-aware snap. If the pointer falls inside any target's `hitBounds`,\n * that target wins; otherwise falls back to nearest-origin. Useful for\n * strategies whose targets are area-shaped (gutters, drop-zones, gridded\n * cells) rather than point-shaped.\n *\n * Pointer-in-multiple-bounds order: first match in iteration order wins, so\n * strategies should emit narrower/more-specific targets before broader ones\n * (e.g. row-gutter before tray-corner).\n */\nexport function containedThenNearest<TPose>(): LayoutSnap<TPose> {\n return {\n pickTarget(targets, pointer) {\n for (const t of targets) {\n if (t.hitBounds && rectContains(t.hitBounds, pointer)) return t;\n }\n return nearestOf(targets, pointer);\n },\n };\n}\n\ninterface CellMeta {\n cellRect: { x: number; y: number; width: number; height: number };\n}\n\nfunction isCellMeta(m: unknown): m is CellMeta {\n return (\n typeof m === 'object' &&\n m !== null &&\n 'cellRect' in m &&\n typeof (m as { cellRect: unknown }).cellRect === 'object'\n );\n}\n\n/**\n * Backwards-compatible cell snap. Prefer `containedThenNearest` for new\n * strategies; this variant remains for `tileGrid` and any caller that puts\n * its cell rect in `meta.cellRect` rather than `hitBounds`.\n */\nexport function cellAt<TPose>(): LayoutSnap<TPose> {\n return {\n pickTarget(targets, pointer) {\n for (const t of targets) {\n if (t.hitBounds && rectContains(t.hitBounds, pointer)) return t;\n if (isCellMeta(t.meta) && rectContains(t.meta.cellRect, pointer)) return t;\n }\n return nearestOf(targets, pointer);\n },\n };\n}\n","import { createTransformOp, type LayoutSnap, type LayoutStrategy } from '@weasel-js/core';\nimport { none } from '../snaps';\n\n/** Options for `freeform`. */\nexport interface FreeformOptions<TPose> {\n snap?: LayoutSnap<TPose>;\n}\n\n/**\n * Identity layout: each child stays where it's stored, no reflow, no drop\n * targets. `snap` is accepted for API symmetry — overrides take effect only\n * if a caller wires up an external drop-target source, since `getDropTargets`\n * returns empty by default.\n */\nexport function freeform<TPose>(opts: FreeformOptions<TPose> = {}): LayoutStrategy<TPose> {\n const snap = opts.snap ?? none<TPose>();\n\n return {\n snap,\n\n childPoses(_container, children) {\n const out = new Map<string, TPose>();\n for (const c of children) out.set(c.id, c.pose);\n return out;\n },\n\n getDropTargets() {\n return [];\n },\n\n reflowPoses() {\n return new Map();\n },\n\n commitDrop(_container, _children, dragged, target) {\n return [\n createTransformOp<TPose>({\n id: dragged.id,\n from: dragged.originPose,\n to: target?.pose ?? dragged.pose,\n label: 'Drop',\n }),\n ];\n },\n };\n}\n","import {\n createTransformOp,\n type Op,\n type ContainerBounds,\n type DropTarget,\n type LayoutChild,\n type LayoutContainer,\n type LayoutDragged,\n type LayoutSnap,\n type LayoutStrategy,\n} from '@weasel-js/core';\nimport { cellAt } from '../snaps';\n\ninterface TileMeta {\n col: number;\n row: number;\n cellRect: { x: number; y: number; width: number; height: number };\n}\n\n/** Options for `tileGrid`. */\nexport interface TileGridOptions<TPose> {\n cols: number;\n rows: number;\n /** Gap between cells, in world units. Default 0. */\n gap?: number;\n snap?: LayoutSnap<TPose>;\n /** Optional: map a cell rect + the dragged pose to the new pose. Default\n * writes `{ x, y, width, height }` into the dragged pose (assumes\n * `RectPose`). Override for point-only poses or other shapes. The\n * `dragged` argument is the pre-drop pose of the child being placed —\n * use it to preserve fields the cell rect doesn't carry (e.g. rotation,\n * domain metadata). */\n cellToPose?(\n cellRect: { x: number; y: number; width: number; height: number },\n dragged: TPose,\n ): TPose;\n /** Optional: the point that decides which cell a child occupies — a child\n * occupies the cell containing it. Default reads `x`/`y` plus half of\n * `width`/`height` when the pose has them, which is the center of a\n * `RectPose` and the point itself for a point pose. */\n centerOf?(pose: TPose): { x: number; y: number };\n}\n\nfunction defaultCenterOf(pose: unknown): { x: number; y: number } {\n const p = pose as { x?: number; y?: number; width?: number; height?: number };\n return { x: (p.x ?? 0) + (p.width ?? 0) / 2, y: (p.y ?? 0) + (p.height ?? 0) / 2 };\n}\n\nfunction cellRectAt(\n bounds: ContainerBounds,\n cols: number,\n rows: number,\n gap: number,\n col: number,\n row: number,\n): { x: number; y: number; width: number; height: number } {\n const cw = (bounds.width - gap * (cols - 1)) / cols;\n const ch = (bounds.height - gap * (rows - 1)) / rows;\n return {\n x: bounds.x + col * (cw + gap),\n y: bounds.y + row * (ch + gap),\n width: cw,\n height: ch,\n };\n}\n\n\n/**\n * Layout strategy that arranges children into a fixed grid of cells, one\n * child per cell, and the container holds at most `cols × rows` children.\n *\n * A child occupies the cell its pose sits in (see `centerOf`). A drag from\n * inside the container swaps with the child in the cell it lands on; a drop\n * from outside is offered only the free cells, so it lands in the nearest\n * free one and a full grid rejects it.\n */\nexport function tileGrid<TPose>(\n opts: TileGridOptions<TPose>,\n): LayoutStrategy<TPose> {\n const { cols, rows } = opts;\n const gap = opts.gap ?? 0;\n const snap = opts.snap ?? cellAt<TPose>();\n const capacity = cols * rows;\n const cellToPose: (cell: { x: number; y: number; width: number; height: number }, dragged: TPose) => TPose =\n opts.cellToPose ?? ((cell, dragged) => {\n // Default: spread the cell rect over the dragged pose. Preserves any\n // extra fields the dragged pose carries.\n return { ...(dragged as object), ...cell } as TPose;\n });\n\n const centerOf = opts.centerOf ?? defaultCenterOf;\n\n function cellPose(bounds: ContainerBounds, col: number, row: number, basis: TPose): TPose {\n return cellToPose(cellRectAt(bounds, cols, rows, gap, col, row), basis);\n }\n\n /** The cell index (`row * cols + col`) `pose` sits in, or `null` when it\n * sits in a gap or outside the grid. */\n function cellIndexOf(bounds: ContainerBounds, pose: TPose): number | null {\n const p = centerOf(pose);\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n const r = cellRectAt(bounds, cols, rows, gap, col, row);\n if (p.x >= r.x && p.x < r.x + r.width && p.y >= r.y && p.y < r.y + r.height) {\n return row * cols + col;\n }\n }\n }\n return null;\n }\n\n /**\n * Compute the swap induced by dragging `dragged` onto `target`, if any.\n * Returns `null` when there is no swap (cross-container drop, empty cell,\n * or null target). The same logic backs both `reflowPoses` (preview) and\n * `commitDrop` (commit) so they cannot disagree.\n */\n function computeSwap(\n container: LayoutContainer,\n children: ReadonlyArray<LayoutChild<TPose>>,\n dragged: LayoutDragged<TPose>,\n target: DropTarget<TPose> | null,\n ): { occupant: string; from: TPose; newPose: TPose } | null {\n if (target === null) return null;\n if (dragged.sourceContainerId !== container.id) return null;\n const meta = target.meta as TileMeta;\n const idx = meta.row * cols + meta.col;\n const occupant = children.find(\n (c) => c.id !== dragged.id && cellIndexOf(container.bounds, c.pose) === idx,\n );\n if (occupant === undefined) return null;\n // The occupant takes the cell the dragged child is leaving — where the\n // container state says it is now, which an earlier placement of the same\n // drop may have moved it to.\n const self = children.find((c) => c.id === dragged.id);\n const fromIdx = cellIndexOf(container.bounds, self ? self.pose : dragged.originPose);\n if (fromIdx === null || fromIdx === idx) return null;\n const oldCellRect = cellRectAt(\n container.bounds, cols, rows, gap, fromIdx % cols, Math.floor(fromIdx / cols),\n );\n return { occupant: occupant.id, from: occupant.pose, newPose: cellToPose(oldCellRect, occupant.pose) };\n }\n\n return {\n snap,\n\n childPoses(container, children) {\n const out = new Map<string, TPose>();\n // Order by the cell each child already sits in, so a reflow compacts\n // the survivors rather than re-sorting them — an earlier swap among\n // them has to outlive a sibling leaving the grid. Children sitting in\n // no cell (a gap, or outside the container) sort last; ties break by\n // id so an arrangement with no cells yet is still deterministic.\n const ordered = children\n .map((c) => ({ c, cell: cellIndexOf(container.bounds, c.pose) }))\n .sort((a, b) => {\n if (a.cell !== b.cell) {\n if (a.cell === null) return 1;\n if (b.cell === null) return -1;\n return a.cell - b.cell;\n }\n return a.c.id < b.c.id ? -1 : a.c.id > b.c.id ? 1 : 0;\n });\n for (let i = 0; i < ordered.length && i < capacity; i++) {\n const col = i % cols;\n const row = Math.floor(i / cols);\n // Use each child's own pose as the basis so non-rect fields survive\n // the cellToPose mapping.\n out.set(ordered[i].c.id, cellPose(container.bounds, col, row, ordered[i].c.pose));\n }\n return out;\n },\n\n getDropTargets(container, children, dragged) {\n // A drag from inside may land on an occupied cell and swap; one from\n // outside has nobody to swap with, so occupied cells are not offered.\n const occupied = new Set<number>();\n if (dragged.sourceContainerId !== container.id) {\n for (const c of children) {\n if (c.id === dragged.id) continue;\n const idx = cellIndexOf(container.bounds, c.pose);\n if (idx !== null) occupied.add(idx);\n }\n }\n const out: DropTarget<TPose>[] = [];\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n if (occupied.has(row * cols + col)) continue;\n const cellRect = cellRectAt(container.bounds, cols, rows, gap, col, row);\n out.push({\n pose: cellToPose(cellRect, dragged.pose),\n origin: { x: cellRect.x + cellRect.width / 2, y: cellRect.y + cellRect.height / 2 },\n meta: { col, row, cellRect } satisfies TileMeta,\n });\n }\n }\n return out;\n },\n\n reflowPoses(container, children, dragged, target) {\n const out = new Map<string, TPose>();\n const swap = computeSwap(container, children, dragged, target);\n if (swap !== null) {\n out.set(swap.occupant, swap.newPose);\n }\n return out;\n },\n\n commitDrop(container, children, dragged, target) {\n const ops: Op[] = [];\n\n let droppedPose: TPose;\n if (target === null) {\n droppedPose = dragged.pose;\n } else {\n const meta = target.meta as TileMeta;\n droppedPose = cellToPose(meta.cellRect, dragged.pose);\n const swap = computeSwap(container, children, dragged, target);\n if (swap !== null) {\n ops.push(\n createTransformOp<TPose>({\n id: swap.occupant,\n from: swap.from,\n to: swap.newPose,\n label: 'Tile swap',\n }),\n );\n }\n }\n ops.push(\n createTransformOp<TPose>({\n id: dragged.id,\n from: dragged.originPose,\n to: droppedPose,\n label: 'Tile drop',\n }),\n );\n return ops;\n },\n };\n}\n","import {\n type Vec2,\n createTransformOp,\n type LayoutSnap,\n type LayoutStrategy,\n} from '@weasel-js/core';\nimport { nearestWithin } from '../snaps';\n\n/** Which points within a container a child can snap to. */\nexport type SnapPattern = 'corners' | 'edges' | 'center' | 'grid';\n\n/** Options for `snapPoint`. */\nexport interface SnapPointOptions<TPose> {\n pattern: SnapPattern;\n /** Spacing for the 'grid' pattern, in world units. Default 50. */\n gridSpacing?: number;\n /** Tolerance for the default snap policy (nearestWithin). Default Infinity. */\n tolerance?: number;\n snap?: LayoutSnap<TPose>;\n}\n\nfunction buildPoints(\n bounds: { x: number; y: number; width: number; height: number },\n pattern: SnapPattern,\n gridSpacing: number,\n): Vec2[] {\n switch (pattern) {\n case 'corners':\n return [\n { x: bounds.x, y: bounds.y },\n { x: bounds.x + bounds.width, y: bounds.y },\n { x: bounds.x, y: bounds.y + bounds.height },\n { x: bounds.x + bounds.width, y: bounds.y + bounds.height },\n ];\n case 'edges':\n return [\n { x: bounds.x + bounds.width / 2, y: bounds.y },\n { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height },\n { x: bounds.x, y: bounds.y + bounds.height / 2 },\n { x: bounds.x + bounds.width, y: bounds.y + bounds.height / 2 },\n ];\n case 'center':\n return [{ x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height / 2 }];\n case 'grid': {\n const out: Vec2[] = [];\n const cols = Math.floor(bounds.width / gridSpacing);\n const rows = Math.floor(bounds.height / gridSpacing);\n for (let j = 0; j <= rows; j++) {\n for (let i = 0; i <= cols; i++) {\n out.push({ x: bounds.x + i * gridSpacing, y: bounds.y + j * gridSpacing });\n }\n }\n return out;\n }\n }\n}\n\n/** Layout strategy that leaves children where they are but offers a fixed set\n * of points — corners, edge midpoints, center, or a grid — for a dragged\n * child to land on. */\nexport function snapPoint<TPose extends Vec2>(\n opts: SnapPointOptions<TPose>,\n): LayoutStrategy<TPose> {\n const gridSpacing = opts.gridSpacing ?? 50;\n const tolerance = opts.tolerance ?? Infinity;\n const snap = opts.snap ?? nearestWithin<TPose>({ tolerance });\n\n return {\n snap,\n\n childPoses(_container, children) {\n const out = new Map<string, TPose>();\n for (const c of children) out.set(c.id, c.pose);\n return out;\n },\n\n getDropTargets(container, _children, dragged) {\n const points = buildPoints(container.bounds, opts.pattern, gridSpacing);\n return points.map((p) => ({\n // Anchor the dragged child's top-left at the snap point.\n pose: { ...dragged.pose, x: p.x, y: p.y } as TPose,\n origin: p,\n }));\n },\n\n reflowPoses() {\n return new Map();\n },\n\n commitDrop(_container, _children, dragged, target) {\n const to = target === null ? dragged.pose : target.pose;\n return [\n createTransformOp<TPose>({\n id: dragged.id,\n from: dragged.originPose,\n to,\n label: 'Snap drop',\n }),\n ];\n },\n };\n}\n"]}
|
package/dist/insert.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ModifierState, InsertBehavior } from '@weasel-js/core';
|
|
2
|
+
import { G as Guide } from './types-D2tTKEU0.js';
|
|
3
|
+
|
|
4
|
+
type ModKey$1 = keyof ModifierState;
|
|
5
|
+
/** Insert behavior that rounds the insertion point to a grid, so a shape
|
|
6
|
+
* drawn freehand lands on grid intersections. Hold `bypassKey` to place
|
|
7
|
+
* freely. */
|
|
8
|
+
declare function snapToGrid<TPose>(args: {
|
|
9
|
+
spacing: number;
|
|
10
|
+
bypassKey?: ModKey$1;
|
|
11
|
+
}): InsertBehavior<TPose>;
|
|
12
|
+
|
|
13
|
+
type ModKey = keyof ModifierState;
|
|
14
|
+
/** Options for the insert-flavored `snapToGuides`. */
|
|
15
|
+
interface SnapToGuidesInsertArgs {
|
|
16
|
+
/** Stable getter into the live guide list (typically from `useGuides`). */
|
|
17
|
+
getGuides: () => readonly Guide[];
|
|
18
|
+
/** Snap tolerance in screen px, read through the gesture's view. */
|
|
19
|
+
tolerance?: number;
|
|
20
|
+
/** Modifier key that bypasses snapping while held. */
|
|
21
|
+
bypassKey?: ModKey;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Insert behavior that snaps the start anchor (on gesture start) and the
|
|
25
|
+
* live current point (during drag) to the nearest guide on each axis when
|
|
26
|
+
* within `tolerance`. Pair with `useGuides` and `createGuidesLayer`.
|
|
27
|
+
*/
|
|
28
|
+
declare function snapToGuides<TPose>(args: SnapToGuidesInsertArgs): InsertBehavior<TPose>;
|
|
29
|
+
|
|
30
|
+
export { snapToGrid, snapToGuides };
|
package/dist/insert.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { DEFAULT_GUIDE_TOLERANCE_PX } from './chunk-GEDAPTOM.js';
|
|
2
|
+
import { screenTolerance } from '@weasel-js/core';
|
|
3
|
+
|
|
4
|
+
// src/behaviors/insert/snapToGrid.ts
|
|
5
|
+
function snapToGrid(args) {
|
|
6
|
+
const { spacing, bypassKey } = args;
|
|
7
|
+
const round = (v) => Math.round(v / spacing) * spacing;
|
|
8
|
+
return {
|
|
9
|
+
onStart(ctx) {
|
|
10
|
+
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
11
|
+
const id = ctx.draggedIds[0];
|
|
12
|
+
const o = ctx.origin.get(id);
|
|
13
|
+
if (!o) return;
|
|
14
|
+
ctx.origin.set(id, { x: round(o.x), y: round(o.y) });
|
|
15
|
+
},
|
|
16
|
+
onMove(ctx, { current }) {
|
|
17
|
+
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
18
|
+
return { current: { x: round(current.x), y: round(current.y) } };
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function snapPoint(p, guides, tol) {
|
|
23
|
+
let bestDx = 0;
|
|
24
|
+
let bestAbsDx = Infinity;
|
|
25
|
+
let bestDy = 0;
|
|
26
|
+
let bestAbsDy = Infinity;
|
|
27
|
+
for (const g of guides) {
|
|
28
|
+
if (g.axis === "x") {
|
|
29
|
+
const d = g.offset - p.x;
|
|
30
|
+
const ad = Math.abs(d);
|
|
31
|
+
if (ad <= tol.x && ad < bestAbsDx) {
|
|
32
|
+
bestAbsDx = ad;
|
|
33
|
+
bestDx = d;
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
const d = g.offset - p.y;
|
|
37
|
+
const ad = Math.abs(d);
|
|
38
|
+
if (ad <= tol.y && ad < bestAbsDy) {
|
|
39
|
+
bestAbsDy = ad;
|
|
40
|
+
bestDy = d;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (bestAbsDx === Infinity && bestAbsDy === Infinity) return null;
|
|
45
|
+
const dx = bestAbsDx === Infinity ? 0 : bestDx;
|
|
46
|
+
const dy = bestAbsDy === Infinity ? 0 : bestDy;
|
|
47
|
+
if (dx === 0 && dy === 0) return null;
|
|
48
|
+
return { x: p.x + dx, y: p.y + dy };
|
|
49
|
+
}
|
|
50
|
+
function snapToGuides(args) {
|
|
51
|
+
const tolerance = args.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;
|
|
52
|
+
const bypassKey = args.bypassKey;
|
|
53
|
+
return {
|
|
54
|
+
onStart(ctx) {
|
|
55
|
+
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
56
|
+
const guides = args.getGuides();
|
|
57
|
+
if (guides.length === 0) return;
|
|
58
|
+
const id = ctx.draggedIds[0];
|
|
59
|
+
const o = ctx.origin.get(id);
|
|
60
|
+
if (!o) return;
|
|
61
|
+
const snapped = snapPoint(o, guides, screenTolerance(tolerance, ctx));
|
|
62
|
+
if (snapped) ctx.origin.set(id, snapped);
|
|
63
|
+
},
|
|
64
|
+
onMove(ctx, { current }) {
|
|
65
|
+
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
66
|
+
const guides = args.getGuides();
|
|
67
|
+
if (guides.length === 0) return;
|
|
68
|
+
const snapped = snapPoint(current, guides, screenTolerance(tolerance, ctx));
|
|
69
|
+
if (!snapped) return;
|
|
70
|
+
return { current: snapped };
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { snapToGrid, snapToGuides };
|
|
76
|
+
//# sourceMappingURL=insert.js.map
|
|
77
|
+
//# sourceMappingURL=insert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/behaviors/insert/snapToGrid.ts","../src/behaviors/insert/snapToGuides.ts"],"names":[],"mappings":"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","file":"insert.js","sourcesContent":["import type {\n InsertBehavior,\n InsertPoint,\n ModifierState,\n} from '@weasel-js/core';\n\ntype ModKey = keyof ModifierState;\n\n/** Insert behavior that rounds the insertion point to a grid, so a shape\n * drawn freehand lands on grid intersections. Hold `bypassKey` to place\n * freely. */\nexport function snapToGrid<TPose>(args: {\n spacing: number;\n bypassKey?: ModKey;\n}): InsertBehavior<TPose> {\n const { spacing, bypassKey } = args;\n const round = (v: number) => Math.round(v / spacing) * spacing;\n return {\n onStart(ctx) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const id = ctx.draggedIds[0];\n const o = ctx.origin.get(id) as unknown as InsertPoint | undefined;\n if (!o) return;\n ctx.origin.set(id, { x: round(o.x), y: round(o.y) } as unknown as TPose);\n },\n onMove(ctx, { current }) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n return { current: { x: round(current.x), y: round(current.y) } };\n },\n };\n}\n","import {\n type InsertBehavior,\n type InsertPoint,\n type ModifierState,\n screenTolerance,\n} from '@weasel-js/core';\nimport type { Guide } from '../../guides/types';\nimport { DEFAULT_GUIDE_TOLERANCE_PX } from '../../strategies/guides';\n\ntype ModKey = keyof ModifierState;\n\n/** Options for the insert-flavored `snapToGuides`. */\nexport interface SnapToGuidesInsertArgs {\n /** Stable getter into the live guide list (typically from `useGuides`). */\n getGuides: () => readonly Guide[];\n /** Snap tolerance in screen px, read through the gesture's view. */\n tolerance?: number;\n /** Modifier key that bypasses snapping while held. */\n bypassKey?: ModKey;\n}\n\nfunction snapPoint(\n p: InsertPoint,\n guides: readonly Guide[],\n tol: { x: number; y: number },\n): InsertPoint | null {\n let bestDx = 0;\n let bestAbsDx = Infinity;\n let bestDy = 0;\n let bestAbsDy = Infinity;\n for (const g of guides) {\n if (g.axis === 'x') {\n const d = g.offset - p.x;\n const ad = Math.abs(d);\n if (ad <= tol.x && ad < bestAbsDx) {\n bestAbsDx = ad;\n bestDx = d;\n }\n } else {\n const d = g.offset - p.y;\n const ad = Math.abs(d);\n if (ad <= tol.y && ad < bestAbsDy) {\n bestAbsDy = ad;\n bestDy = d;\n }\n }\n }\n if (bestAbsDx === Infinity && bestAbsDy === Infinity) return null;\n const dx = bestAbsDx === Infinity ? 0 : bestDx;\n const dy = bestAbsDy === Infinity ? 0 : bestDy;\n if (dx === 0 && dy === 0) return null;\n return { x: p.x + dx, y: p.y + dy };\n}\n\n/**\n * Insert behavior that snaps the start anchor (on gesture start) and the\n * live current point (during drag) to the nearest guide on each axis when\n * within `tolerance`. Pair with `useGuides` and `createGuidesLayer`.\n */\nexport function snapToGuides<TPose>(\n args: SnapToGuidesInsertArgs,\n): InsertBehavior<TPose> {\n const tolerance = args.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;\n const bypassKey = args.bypassKey;\n\n return {\n onStart(ctx) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const guides = args.getGuides();\n if (guides.length === 0) return;\n const id = ctx.draggedIds[0];\n const o = ctx.origin.get(id) as unknown as InsertPoint | undefined;\n if (!o) return;\n const snapped = snapPoint(o, guides, screenTolerance(tolerance, ctx));\n if (snapped) ctx.origin.set(id, snapped as unknown as TPose);\n },\n onMove(ctx, { current }) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const guides = args.getGuides();\n if (guides.length === 0) return;\n const snapped = snapPoint(current, guides, screenTolerance(tolerance, ctx));\n if (!snapped) return;\n return { current: snapped };\n },\n };\n}\n"]}
|
package/dist/move.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ModifierState, MoveBehavior, OriginProjection } from '@weasel-js/core';
|
|
2
|
+
import { G as Guide } from './types-D2tTKEU0.js';
|
|
3
|
+
|
|
4
|
+
type ModKey$1 = keyof ModifierState;
|
|
5
|
+
/** Move behavior that snaps the dragged pose's origin to a grid. Hold
|
|
6
|
+
* `bypassKey` to drag freely. */
|
|
7
|
+
declare function snapToGrid<TPose extends {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
}>(args: {
|
|
11
|
+
spacing: number;
|
|
12
|
+
bypassKey?: ModKey$1;
|
|
13
|
+
}): MoveBehavior<TPose>;
|
|
14
|
+
|
|
15
|
+
type ModKey = keyof ModifierState;
|
|
16
|
+
/** Options for the move-flavored `snapToGuides`. */
|
|
17
|
+
interface SnapToGuidesMoveArgs<TPose> {
|
|
18
|
+
/** Stable getter into the live guide list (typically from `useGuides`). */
|
|
19
|
+
getGuides: () => readonly Guide[];
|
|
20
|
+
/** Snap tolerance in screen px, read through the gesture's view. */
|
|
21
|
+
tolerance?: number;
|
|
22
|
+
/** Modifier key that bypasses snapping while held. */
|
|
23
|
+
bypassKey?: ModKey;
|
|
24
|
+
/** Origin projection for non-rect TPose (e.g. Path). */
|
|
25
|
+
origin?: OriginProjection<TPose>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Move behavior that snaps the dragged pose's origin to the nearest world-space
|
|
29
|
+
* guide line within `tolerance`. Closest-guide-per-axis wins; the two axes are
|
|
30
|
+
* considered independently. Pair with `useGuides` and `createGuidesLayer`.
|
|
31
|
+
*/
|
|
32
|
+
declare function snapToGuides<TPose extends {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
}>(args: Omit<SnapToGuidesMoveArgs<TPose>, 'origin'>): MoveBehavior<TPose>;
|
|
36
|
+
/** As above, for a `TPose` that is not a rect: `origin` tells the behavior how
|
|
37
|
+
* to read and write the pose's origin. */
|
|
38
|
+
declare function snapToGuides<TPose>(args: SnapToGuidesMoveArgs<TPose> & {
|
|
39
|
+
origin: OriginProjection<TPose>;
|
|
40
|
+
}): MoveBehavior<TPose>;
|
|
41
|
+
|
|
42
|
+
export { snapToGrid, snapToGuides };
|
package/dist/move.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { gridSnapStrategy } from './chunk-5BCCIJY3.js';
|
|
2
|
+
import { guideSnapStrategy } from './chunk-GEDAPTOM.js';
|
|
3
|
+
import { snap } from '@weasel-js/core';
|
|
4
|
+
|
|
5
|
+
function snapToGrid(args) {
|
|
6
|
+
return snap(gridSnapStrategy(args.spacing), { bypassKey: args.bypassKey });
|
|
7
|
+
}
|
|
8
|
+
function snapToGuides(args) {
|
|
9
|
+
const strategy = guideSnapStrategy(args.getGuides, {
|
|
10
|
+
tolerance: args.tolerance,
|
|
11
|
+
origin: args.origin
|
|
12
|
+
});
|
|
13
|
+
return snap(strategy, { bypassKey: args.bypassKey });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { snapToGrid, snapToGuides };
|
|
17
|
+
//# sourceMappingURL=move.js.map
|
|
18
|
+
//# sourceMappingURL=move.js.map
|
package/dist/move.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/behaviors/move/snapToGrid.ts","../src/behaviors/move/snapToGuides.ts"],"names":["snap"],"mappings":";;;;AAOO,SAAS,WAAmD,IAAA,EAG3C;AACtB,EAAA,OAAO,IAAA,CAAK,iBAAwB,IAAA,CAAK,OAAO,GAAG,EAAE,SAAA,EAAW,IAAA,CAAK,SAAA,EAAW,CAAA;AAClF;ACyBO,SAAS,aACd,IAAA,EACqB;AACrB,EAAA,MAAM,QAAA,GAAW,iBAAA,CAAyB,IAAA,CAAK,SAAA,EAAW;AAAA,IACxD,WAAW,IAAA,CAAK,SAAA;AAAA,IAChB,QAAQ,IAAA,CAAK;AAAA,GACd,CAAA;AACD,EAAA,OAAOA,KAAK,QAAA,EAAU,EAAE,SAAA,EAAW,IAAA,CAAK,WAAW,CAAA;AACrD","file":"move.js","sourcesContent":["import { type ModifierState, type MoveBehavior, snap } from '@weasel-js/core';\nimport { gridSnapStrategy } from '../../strategies/grid';\n\ntype ModKey = keyof ModifierState;\n\n/** Move behavior that snaps the dragged pose's origin to a grid. Hold\n * `bypassKey` to drag freely. */\nexport function snapToGrid<TPose extends { x: number; y: number }>(args: {\n spacing: number;\n bypassKey?: ModKey;\n}): MoveBehavior<TPose> {\n return snap(gridSnapStrategy<TPose>(args.spacing), { bypassKey: args.bypassKey });\n}\n","import {\n type ModifierState,\n type MoveBehavior,\n type OriginProjection,\n snap,\n} from '@weasel-js/core';\nimport type { Guide } from '../../guides/types';\nimport { guideSnapStrategy } from '../../strategies/guides';\n\ntype ModKey = keyof ModifierState;\n\n/** Options for the move-flavored `snapToGuides`. */\nexport interface SnapToGuidesMoveArgs<TPose> {\n /** Stable getter into the live guide list (typically from `useGuides`). */\n getGuides: () => readonly Guide[];\n /** Snap tolerance in screen px, read through the gesture's view. */\n tolerance?: number;\n /** Modifier key that bypasses snapping while held. */\n bypassKey?: ModKey;\n /** Origin projection for non-rect TPose (e.g. Path). */\n origin?: OriginProjection<TPose>;\n}\n\n/**\n * Move behavior that snaps the dragged pose's origin to the nearest world-space\n * guide line within `tolerance`. Closest-guide-per-axis wins; the two axes are\n * considered independently. Pair with `useGuides` and `createGuidesLayer`.\n */\nexport function snapToGuides<TPose extends { x: number; y: number }>(\n args: Omit<SnapToGuidesMoveArgs<TPose>, 'origin'>,\n): MoveBehavior<TPose>;\n/** As above, for a `TPose` that is not a rect: `origin` tells the behavior how\n * to read and write the pose's origin. */\nexport function snapToGuides<TPose>(\n args: SnapToGuidesMoveArgs<TPose> & { origin: OriginProjection<TPose> },\n): MoveBehavior<TPose>;\n/** Snap the dragged pose's origin to the nearest guide line, per axis. */\nexport function snapToGuides<TPose>(\n args: SnapToGuidesMoveArgs<TPose>,\n): MoveBehavior<TPose> {\n const strategy = guideSnapStrategy<TPose>(args.getGuides, {\n tolerance: args.tolerance,\n origin: args.origin as OriginProjection<TPose>,\n });\n return snap(strategy, { bypassKey: args.bypassKey });\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Bounds, PointSnapFrame, ModifierState, PointSnapBehavior } from '@weasel-js/core';
|
|
2
|
+
|
|
3
|
+
/** Resize behavior that snaps one reference point of the resize — the dragged
|
|
4
|
+
* corner, the fixed corner, or the center, per `frame` — to a grid, moving
|
|
5
|
+
* the whole pose by the same delta. Unlike the bounds-level `snapToGrid`,
|
|
6
|
+
* this preserves the node's dimensions. */
|
|
7
|
+
declare function pointSnapToGrid<TPose extends Bounds>(args: {
|
|
8
|
+
spacing: number;
|
|
9
|
+
frame?: PointSnapFrame;
|
|
10
|
+
bypassKey?: keyof ModifierState;
|
|
11
|
+
}): PointSnapBehavior<TPose>;
|
|
12
|
+
|
|
13
|
+
export { pointSnapToGrid as p };
|
package/dist/resize.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Bounds, ModifierState, BoundsConstraint } from '@weasel-js/core';
|
|
2
|
+
import { G as Guide } from './types-D2tTKEU0.js';
|
|
3
|
+
export { p as pointSnapToGrid } from './pointSnapToGrid-BaVEjV23.js';
|
|
4
|
+
|
|
5
|
+
type ModKey$1 = keyof ModifierState;
|
|
6
|
+
/** Resize constraint that snaps the moving edges to a grid, leaving the
|
|
7
|
+
* anchored ones where they are. By default a node already smaller than one
|
|
8
|
+
* grid cell on an axis is left unsnapped on that axis, so it stays resizable
|
|
9
|
+
* at sub-grid sizes. */
|
|
10
|
+
declare function snapToGrid<TPose extends Bounds>(args: {
|
|
11
|
+
spacing: number;
|
|
12
|
+
bypassKey?: ModKey$1;
|
|
13
|
+
suspendBelowDim?: boolean;
|
|
14
|
+
}): BoundsConstraint<TPose>;
|
|
15
|
+
|
|
16
|
+
type ModKey = keyof ModifierState;
|
|
17
|
+
/** Options for the resize-flavored `snapToGuides`. */
|
|
18
|
+
interface SnapToGuidesResizeArgs {
|
|
19
|
+
/** Stable getter into the live guide list (typically from `useGuides`). */
|
|
20
|
+
getGuides: () => readonly Guide[];
|
|
21
|
+
/** Snap tolerance in screen px, read through the gesture's view. */
|
|
22
|
+
tolerance?: number;
|
|
23
|
+
/** Modifier key that bypasses snapping while held. */
|
|
24
|
+
bypassKey?: ModKey;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resize behavior that snaps the active edge of the dragged rect to the
|
|
28
|
+
* nearest guide on the corresponding axis when within `tolerance`. The
|
|
29
|
+
* "free" axis is left untouched. The fixed (anchor) edge stays pinned;
|
|
30
|
+
* only the moving edge snaps.
|
|
31
|
+
*
|
|
32
|
+
* On the X axis, vertical guides (`axis: 'x'`) constrain the moving vertical
|
|
33
|
+
* edge — east when `anchor.x === 'min'` (west pinned), west when `'max'`.
|
|
34
|
+
* Likewise on Y for horizontal guides.
|
|
35
|
+
*/
|
|
36
|
+
declare function snapToGuides<TPose extends Bounds>(args: SnapToGuidesResizeArgs): BoundsConstraint<TPose>;
|
|
37
|
+
|
|
38
|
+
export { snapToGrid, snapToGuides };
|
package/dist/resize.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export { pointSnapToGrid } from './chunk-B25KFI3I.js';
|
|
2
|
+
import { DEFAULT_GUIDE_TOLERANCE_PX } from './chunk-GEDAPTOM.js';
|
|
3
|
+
import { screenTolerance } from '@weasel-js/core';
|
|
4
|
+
|
|
5
|
+
// src/behaviors/resize/snapToGrid.ts
|
|
6
|
+
function snapToGrid(args) {
|
|
7
|
+
const { spacing, bypassKey, suspendBelowDim = true } = args;
|
|
8
|
+
const round = (v) => Math.round(v / spacing) * spacing;
|
|
9
|
+
return {
|
|
10
|
+
onMove(ctx, { pose, anchor }) {
|
|
11
|
+
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
12
|
+
const origin = ctx.origin.get(ctx.draggedIds[0]);
|
|
13
|
+
const subX = suspendBelowDim && origin.width < spacing;
|
|
14
|
+
const subY = suspendBelowDim && origin.height < spacing;
|
|
15
|
+
let { x, y, width, height } = pose;
|
|
16
|
+
let changed = false;
|
|
17
|
+
if (anchor.x !== "free" && !subX) {
|
|
18
|
+
if (anchor.x === "min") {
|
|
19
|
+
const east = round(x + width);
|
|
20
|
+
width = east - x;
|
|
21
|
+
} else {
|
|
22
|
+
const right = origin.x + origin.width;
|
|
23
|
+
const newX = round(x);
|
|
24
|
+
width = right - newX;
|
|
25
|
+
x = newX;
|
|
26
|
+
}
|
|
27
|
+
changed = true;
|
|
28
|
+
}
|
|
29
|
+
if (anchor.y !== "free" && !subY) {
|
|
30
|
+
if (anchor.y === "min") {
|
|
31
|
+
const south = round(y + height);
|
|
32
|
+
height = south - y;
|
|
33
|
+
} else {
|
|
34
|
+
const bottom = origin.y + origin.height;
|
|
35
|
+
const newY = round(y);
|
|
36
|
+
height = bottom - newY;
|
|
37
|
+
y = newY;
|
|
38
|
+
}
|
|
39
|
+
changed = true;
|
|
40
|
+
}
|
|
41
|
+
if (!changed) return;
|
|
42
|
+
return { pose: { ...pose, x, y, width, height } };
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function snapToGuides(args) {
|
|
47
|
+
const tolerance = args.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;
|
|
48
|
+
const bypassKey = args.bypassKey;
|
|
49
|
+
return {
|
|
50
|
+
onMove(ctx, { pose, anchor }) {
|
|
51
|
+
if (bypassKey && ctx.modifiers[bypassKey]) return;
|
|
52
|
+
const guides = args.getGuides();
|
|
53
|
+
if (guides.length === 0) return;
|
|
54
|
+
const tol = screenTolerance(tolerance, ctx);
|
|
55
|
+
let { x, y, width, height } = pose;
|
|
56
|
+
let changed = false;
|
|
57
|
+
if (anchor.x !== "free") {
|
|
58
|
+
const movingX = anchor.x === "min" ? x + width : x;
|
|
59
|
+
let bestD = 0;
|
|
60
|
+
let bestAbs = Infinity;
|
|
61
|
+
for (const g of guides) {
|
|
62
|
+
if (g.axis !== "x") continue;
|
|
63
|
+
const d = g.offset - movingX;
|
|
64
|
+
const ad = Math.abs(d);
|
|
65
|
+
if (ad <= tol.x && ad < bestAbs) {
|
|
66
|
+
bestAbs = ad;
|
|
67
|
+
bestD = d;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (bestAbs !== Infinity && bestD !== 0) {
|
|
71
|
+
if (anchor.x === "min") {
|
|
72
|
+
width = width + bestD;
|
|
73
|
+
} else {
|
|
74
|
+
x = x + bestD;
|
|
75
|
+
width = width - bestD;
|
|
76
|
+
}
|
|
77
|
+
changed = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (anchor.y !== "free") {
|
|
81
|
+
const movingY = anchor.y === "min" ? y + height : y;
|
|
82
|
+
let bestD = 0;
|
|
83
|
+
let bestAbs = Infinity;
|
|
84
|
+
for (const g of guides) {
|
|
85
|
+
if (g.axis !== "y") continue;
|
|
86
|
+
const d = g.offset - movingY;
|
|
87
|
+
const ad = Math.abs(d);
|
|
88
|
+
if (ad <= tol.y && ad < bestAbs) {
|
|
89
|
+
bestAbs = ad;
|
|
90
|
+
bestD = d;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (bestAbs !== Infinity && bestD !== 0) {
|
|
94
|
+
if (anchor.y === "min") {
|
|
95
|
+
height = height + bestD;
|
|
96
|
+
} else {
|
|
97
|
+
y = y + bestD;
|
|
98
|
+
height = height - bestD;
|
|
99
|
+
}
|
|
100
|
+
changed = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!changed) return;
|
|
104
|
+
return { pose: { ...pose, x, y, width, height } };
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { snapToGrid, snapToGuides };
|
|
110
|
+
//# sourceMappingURL=resize.js.map
|
|
111
|
+
//# sourceMappingURL=resize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/behaviors/resize/snapToGrid.ts","../src/behaviors/resize/snapToGuides.ts"],"names":[],"mappings":"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","file":"resize.js","sourcesContent":["import type { ModifierState, BoundsConstraint, Bounds } from '@weasel-js/core';\n\ntype ModKey = keyof ModifierState;\n\n/** Resize constraint that snaps the moving edges to a grid, leaving the\n * anchored ones where they are. By default a node already smaller than one\n * grid cell on an axis is left unsnapped on that axis, so it stays resizable\n * at sub-grid sizes. */\nexport function snapToGrid<TPose extends Bounds>(args: {\n spacing: number;\n bypassKey?: ModKey;\n suspendBelowDim?: boolean;\n}): BoundsConstraint<TPose> {\n const { spacing, bypassKey, suspendBelowDim = true } = args;\n const round = (v: number) => Math.round(v / spacing) * spacing;\n\n return {\n onMove(ctx, { pose, anchor }) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const origin = ctx.origin.get(ctx.draggedIds[0])!;\n const subX = suspendBelowDim && origin.width < spacing;\n const subY = suspendBelowDim && origin.height < spacing;\n\n let { x, y, width, height } = pose;\n let changed = false;\n\n if (anchor.x !== 'free' && !subX) {\n if (anchor.x === 'min') {\n // East edge moves; west (x) stays.\n const east = round(x + width);\n width = east - x;\n } else {\n // West edge moves; east (x+width) stays.\n const right = origin.x + origin.width;\n const newX = round(x);\n width = right - newX;\n x = newX;\n }\n changed = true;\n }\n if (anchor.y !== 'free' && !subY) {\n if (anchor.y === 'min') {\n const south = round(y + height);\n height = south - y;\n } else {\n const bottom = origin.y + origin.height;\n const newY = round(y);\n height = bottom - newY;\n y = newY;\n }\n changed = true;\n }\n if (!changed) return;\n return { pose: { ...pose, x, y, width, height } };\n },\n };\n}\n","import {\n type ModifierState,\n type BoundsConstraint,\n screenTolerance,\n type Bounds,\n} from '@weasel-js/core';\nimport type { Guide } from '../../guides/types';\nimport { DEFAULT_GUIDE_TOLERANCE_PX } from '../../strategies/guides';\n\ntype ModKey = keyof ModifierState;\n\n/** Options for the resize-flavored `snapToGuides`. */\nexport interface SnapToGuidesResizeArgs {\n /** Stable getter into the live guide list (typically from `useGuides`). */\n getGuides: () => readonly Guide[];\n /** Snap tolerance in screen px, read through the gesture's view. */\n tolerance?: number;\n /** Modifier key that bypasses snapping while held. */\n bypassKey?: ModKey;\n}\n\n/**\n * Resize behavior that snaps the active edge of the dragged rect to the\n * nearest guide on the corresponding axis when within `tolerance`. The\n * \"free\" axis is left untouched. The fixed (anchor) edge stays pinned;\n * only the moving edge snaps.\n *\n * On the X axis, vertical guides (`axis: 'x'`) constrain the moving vertical\n * edge — east when `anchor.x === 'min'` (west pinned), west when `'max'`.\n * Likewise on Y for horizontal guides.\n */\nexport function snapToGuides<TPose extends Bounds>(\n args: SnapToGuidesResizeArgs,\n): BoundsConstraint<TPose> {\n const tolerance = args.tolerance ?? DEFAULT_GUIDE_TOLERANCE_PX;\n const bypassKey = args.bypassKey;\n\n return {\n onMove(ctx, { pose, anchor }) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n const guides = args.getGuides();\n if (guides.length === 0) return;\n\n // Per axis: a vertical guide is matched by a horizontal distance, so it\n // answers to `scale.x` alone.\n const tol = screenTolerance(tolerance, ctx);\n\n let { x, y, width, height } = pose;\n let changed = false;\n\n // X-axis: snap the moving vertical edge to the nearest 'x' guide.\n if (anchor.x !== 'free') {\n const movingX = anchor.x === 'min' ? x + width : x;\n let bestD = 0;\n let bestAbs = Infinity;\n for (const g of guides) {\n if (g.axis !== 'x') continue;\n const d = g.offset - movingX;\n const ad = Math.abs(d);\n if (ad <= tol.x && ad < bestAbs) {\n bestAbs = ad;\n bestD = d;\n }\n }\n if (bestAbs !== Infinity && bestD !== 0) {\n if (anchor.x === 'min') {\n // East edge moves; west (x) stays.\n width = width + bestD;\n } else {\n // West edge moves; east (x+width) stays.\n x = x + bestD;\n width = width - bestD;\n }\n changed = true;\n }\n }\n\n // Y-axis: snap the moving horizontal edge to the nearest 'y' guide.\n if (anchor.y !== 'free') {\n const movingY = anchor.y === 'min' ? y + height : y;\n let bestD = 0;\n let bestAbs = Infinity;\n for (const g of guides) {\n if (g.axis !== 'y') continue;\n const d = g.offset - movingY;\n const ad = Math.abs(d);\n if (ad <= tol.y && ad < bestAbs) {\n bestAbs = ad;\n bestD = d;\n }\n }\n if (bestAbs !== Infinity && bestD !== 0) {\n if (anchor.y === 'min') {\n height = height + bestD;\n } else {\n y = y + bestD;\n height = height - bestD;\n }\n changed = true;\n }\n }\n\n if (!changed) return;\n return { pose: { ...pose, x, y, width, height } };\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guide — a horizontal or vertical world-space line used as a snap target
|
|
3
|
+
* and an optional rendered overlay.
|
|
4
|
+
*
|
|
5
|
+
* `axis === 'x'` means a vertical line at world `x = offset` (snaps the X
|
|
6
|
+
* coordinate); `axis === 'y'` means a horizontal line at world `y = offset`
|
|
7
|
+
* (snaps the Y coordinate). This matches "the X axis snaps along X."
|
|
8
|
+
*/
|
|
9
|
+
interface Guide {
|
|
10
|
+
/** Stable, caller-supplied id. Used by removeGuide and as a render key. */
|
|
11
|
+
id: string;
|
|
12
|
+
/** Which world axis this guide constrains. */
|
|
13
|
+
axis: 'x' | 'y';
|
|
14
|
+
/** World-space offset along the constrained axis. */
|
|
15
|
+
offset: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type { Guide as G };
|