import { BoundingBoxConfig, BoundingBoxDeclaration, BoundingBoxOrigin, LayoutConfig } from '../types'; import { MeasuredSize } from './measure'; /** * Pure layout arithmetic — measure results in, absolute centre positions out. * * One pass, one direction: measure → arrange (stack along the main axis with a gap, align on * the cross axis) → anchor (pin the whole block on the canvas). No reflow, no wrapping, no * grow/shrink — children are rigid boxes (text does not wrap yet; the measure contract already * carries availableWidth for the day it does). * * All values are canvas pixels. The renderer (or any host) converts the returned centres into * each child's native position prop. */ export interface LayoutItem { /** Caller's identifier for the child (node id). */ id: string; size: MeasuredSize; } export interface LayoutPlacement { centerXPx: number; centerYPx: number; } export interface ResolvedLayout { mode: 'column' | 'row'; gapPx: number; align: 'start' | 'center' | 'end'; anchor: BoundingBoxOrigin; anchorOffsetX: number; anchorOffsetY: number; } /** Fill defaults; null when the config doesn't activate layout. */ export declare function resolveLayoutConfig(cfg: LayoutConfig | undefined, cw: number, ch: number): ResolvedLayout | null; /** The block's own size: main axis = stacked sizes + gaps, cross axis = widest child. */ export declare function layoutBlockSize(layout: ResolvedLayout, items: LayoutItem[]): MeasuredSize; /** * Arrange + anchor. Items stack in array order (the caller passes render order); the returned * map holds each child's absolute render CENTRE in canvas px. */ export declare function computeLayout(layout: ResolvedLayout, items: LayoutItem[], cw: number, ch: number): Map; /** Minimal node view the layout walk needs. Children in RENDER ORDER. */ export interface LayoutNodeView { id: string; componentName: string; visible: boolean; /** Per-child escape hatch — absolute children are out of flow. */ absolute?: boolean; /** Filters (requiresChild) never take a flow slot — even with an explicit box. */ requiresChild?: boolean; /** Group layout config, when this node is a Group. */ layout?: LayoutConfig; decl: BoundingBoxDeclaration | undefined; /** Raw prop snapshot (pre-transform values, _rawDimensional where present). */ props: Record; boundingBox?: Partial | null; /** True when a mouse/map driver owns the position prop — the slot is kept, the write skipped. */ positionDriven?: boolean; children: LayoutNodeView[]; } /** One write the layout pass wants applied. */ export type LayoutWrite = { /** Write the child's native position prop (absolute canvas-UV render centre). */ kind: 'position'; id: string; prop: string; value: { x: number; y: number; }; } | { /** Position a box-measured child (media/generator): full box, px, top-left origin. */ kind: 'box'; id: string; xPx: number; yPx: number; widthPx: number; heightPx: number; }; /** * A child's size for layout. A nested layout group measures as its own content block * (recursion); anything else answers through measureNode. */ export declare function measureChild(view: LayoutNodeView, cw: number, ch: number): MeasuredSize | null; /** * The offset between a shape's VISUAL box centre and its stored centre prop, when its * computeBounds declares one (Text: the ink box sits off the font-box centre the renderer * uses). Zero for centred shapes (Blob, Ring, shape effects). */ export declare function computeBoundsShift(view: LayoutNodeView, cw: number, ch: number): { x: number; y: number; }; /** Placed content-block rect of a layout group (canvas px, top-left). */ export interface GroupBlockRect { leftPx: number; topPx: number; widthPx: number; heightPx: number; } /** * Inverse of the block anchoring: given a dragged block top-left, the anchorOffset that puts it * there under the group's current anchor (px, resize-stable gap-inset convention). Lets the * editor translate "user dragged the group's block box" into layout config. */ export declare function anchorOffsetForBlockTopLeft(group: LayoutNodeView, leftPx: number, topPx: number, cw: number, ch: number): { x: number; y: number; } | null; /** * Walk the whole tree and compute every layout write. Only TOP-LEVEL layout groups anchor * themselves; nested layout groups are placed by their parent's flow. Non-layout subtrees are * still traversed (a layout group may sit anywhere in the tree). */ export declare function computeTreeLayout(roots: LayoutNodeView[], cw: number, ch: number, rects?: Map): LayoutWrite[]; /** * The PLACED content-block rect of every active layout group in the tree — top-level groups * anchored by their own config, NESTED groups where their parent's flow put them. This is what * the Design Editor draws when a layout group is selected (a nested row's box must sit in its * slot in the parent column, not at its own default anchor). */ export declare function computeGroupBlockRects(roots: LayoutNodeView[], cw: number, ch: number): Map; //# sourceMappingURL=layout.d.ts.map