@wonderlandlabs-pixi-ux/box
Repository: https://github.com/wonderlandlabs-pixi-ux/wonderlandlabs-pixi-ux/tree/main/packages/box
box is a render-agnostic layout tree. It gives you deterministic 2D layout (area, alignment, constraints, ordering)
without tying your state to Pixi objects.
Installation
yarn add @wonderlandlabs-pixi-ux/box
Basic Usage
import {
ALIGN,
BoxTree,
MEASUREMENT_MODE,
} from '@wonderlandlabs-pixi-ux/box';
const layout = new BoxTree({
id: 'root',
styleName: 'button',
globalVerb: [],
area: {
x: 60,
y: 50,
width: { mode: MEASUREMENT_MODE.PX, value: 720 },
height: { mode: MEASUREMENT_MODE.PX, value: 340 },
px: 's',
py: 's',
},
align: {
x: ALIGN.START,
y: ALIGN.START,
direction: 'column',
},
children: {
header: {
styleName: 'header',
area: {
x: 0,
y: 0,
width: { mode: MEASUREMENT_MODE.PERCENT, value: 1 },
height: { mode: MEASUREMENT_MODE.PX, value: 60 },
px: 's',
py: 's',
},
align: { x: 's', y: 's', direction: 'row' },
},
icon: {
styleName: 'icon',
modeVerb: ['hover'],
area: {
x: 0,
y: 0,
width: { mode: MEASUREMENT_MODE.PX, value: 24 },
height: { mode: MEASUREMENT_MODE.PX, value: 24 },
px: 's',
py: 's',
},
align: { x: 's', y: 's', direction: 'column' },
},
},
});
layout.toggleGlobalVerb('disabled');
layout.getChild('icon')?.toggleModeVerb('selected');
Ux Assignment
BoxTree defaults to the built-in Pixi UX map. Override with assignUx(ux, applyToChildren?):
import { BoxUxPixi } from '@wonderlandlabs-pixi-ux/box';
layout.styles = styles;
layout.assignUx((box) => new BoxUxPixi(box));
layout.render();
addChild() inherits the UX map function from its parent.
Constructor shortcut:
const layout = new BoxTree({
id: 'root',
area: { x: 0, y: 0, width: 200, height: 100 },
ux: (box) => new BoxUxPixi(box),
});
layout.styles = styles;
Style Resolution
Each node has a styleName and optional state verbs:
styleName: style noun for the nodemodeVerb: node-local states (hover, selected, active, ...)globalVerb: root-wide states shared by descendants (disabled, readonly, ...)
When you call resolveStyle(styleManager, extraStates?), BoxTree queries:
- Hierarchical path first (
button.icon,toolbar.button.label, ...) - Atomic fallback (
icon,label, ...)
With combined states:
globalVerb + modeVerb + extraStates
Default Ux
Use BoxUxPixi for default Pixi rendering.
It:
- Creates a container when none is provided
- Uses public
content: MapEnhanced(Map<string, unknown>) content.$boxpoints to the owning box- Exposes
ux.getContainer(key): unknownfor child UX handoff:ROOT_CONTAINER,BACKGROUND_CONTAINER,CHILD_CONTAINER,CONTENT_CONTAINER,OVERLAY_CONTAINER,STROKE_CONTAINER
- Exposes
ux.attach(targetContainer?):- attaches root container to
targetContainer - if omitted, uses
ux.app?.stage
- attaches root container to
- Creates default layers by key when absent:
BOX_RENDER_CONTENT_ORDER.BACKGROUND = 0BOX_RENDER_CONTENT_ORDER.CHILDREN = 50BOX_RENDER_CONTENT_ORDER.CONTENT = 75BOX_RENDER_CONTENT_ORDER.OVERLAY = 100
- Layer order can be looked up/extended by name:
BOX_UX_ORDERmapsetUxOrder(name, zIndex)(throws on duplicate z-index)getUxOrder(name)
- Clears/rebuilds the children layer each render cycle
- Draws background from style props:
[stylePath].bgColor[stylePath].bgAlpha[stylePath].bgStrokeColor[stylePath].bgStrokeAlpha[stylePath].bgStrokeSize
- Exposes
ux.generateStyleMap(box):fill: { color, alpha }stroke: { color, alpha, width }
- Honors
box.isVisible:falsedetaches (hides) container and keeps render contenttruereuses existing layers on next render
- Iterates content map and injects non-empty items sorted by
zIndex
import { Graphics } from 'pixi.js';
import {
BOX_RENDER_CONTENT_ORDER,
BoxTree,
BoxUxPixi,
} from '@wonderlandlabs-pixi-ux/box';
import { fromJSON } from '@wonderlandlabs-pixi-ux/style-tree';
const styles = fromJSON({
panel: {
bgColor: { $*: 0x2d3a45 },
bgStrokeColor: { $*: 0x8fd3ff },
bgStrokeSize: { $*: 2 },
},
});
const root = new BoxTree({
id: 'root',
styleName: 'panel',
area: { x: 20, y: 20, width: 220, height: 120 },
});
root.styles = styles;
const ux = new BoxUxPixi(root);
root.render();
ux.attach(app.stage);
const custom = new Graphics();
custom.zIndex = 76; // 75 is reserved for CONTENT
custom.visible = true;
ux.content.set('CUSTOM', custom);
ux.content.get('OVERLAY')?.visible = true;
Override Points
BoxUxBase is the renderer-agnostic lifecycle base.
BoxUxPixi extends it with Pixi-specific containers/graphics/content behavior.
Build custom rendering by returning your own UX object from assignUx((box) => ...):
- extend
BoxUxBasefor a non-Pixi renderer, or - extend
BoxUxPixifor Pixi customization.
See dedicated page:
Optional Pixi Geometry Preview
import { boxTreeToPixi } from '@wonderlandlabs-pixi-ux/box';
const graphics = await boxTreeToPixi(layout, {
includeRoot: true,
fill: 0x2d3a45,
fillAlpha: 0.35,
stroke: 0x8fd3ff,
strokeAlpha: 0.9,
strokeWidth: 2,
});
Public API Snapshot
BoxTreeBoxUx(UX object type)BoxUxMapFnBoxRenderer(legacy alias ofBoxUx)BoxRenderMapFn(legacy alias ofBoxUxMapFn)MapEnhancedBoxUxBaseBoxUxPixiBoxTreeRenderer(legacy alias ofBoxUxPixi)BOX_UX_ORDER,getUxOrder,setUxOrderBOX_RENDER_CONTENT_ORDERcreateBoxTreeStateresolveTreeMeasurementresolveMeasurementPxresolveConstraintValuePxapplyAxisConstraintsboxTreeToPixiboxTreeToSvgpathToString,pathString,combinePathsALIGN,AXIS,MEASUREMENT_MODE,SIZE_MODE,SIZE_MODE_INPUT