Composing a component
Most behavior a new component needs already exists as a tested controller or helper. Compose them in the component and keep only what is specific to it.
Compose, then extend
- A component composes controllers. Hold each one in a field of the custom element with
createX(this, config). Config is getters (getItems,getTarget) and callbacks (onChange), so the element keeps its own state and the controller never reaches into it. - Extend a controller to change how it decides. A controller documents
protectedhooks at its decision points. Subclass it, override the hook, and register the subclass withbindController. - A component never extends a controller. The component is the host; the controller is a part it holds.
Prefer the lightest tool that works: a config callback first, a documented protected hook by subclassing second, a new controller last. Use a helper, not a controller, for anything that only maps input to output.
Extending through a hook
SortableController.resolveTargetIndex decides the index a lifted item takes from where the pointer is. This subclass snaps the drop position to a grid by changing only that decision:
import { SortableController, bindController, type SortableRect } from '@ecopages/radiant-ui/controllers';
class SnapSortable extends SortableController {
protected override resolveTargetIndex(
rects: readonly SortableRect[],
from: number | null,
point: { x: number; y: number },
) {
return super.resolveTargetIndex(rects, from, { x: Math.round(point.x / 40) * 40, y: point.y });
}
}
// in the element: bindController(this, new SnapSortable({ getContainer, getItems, onChange }));createX always returns the base class, so a subclass is constructed directly. The component holds the instance; it does not become one.
Steps
- Name the behaviors: open and close, keyboard movement, dragging, filtering, date math, positioning.
- Pick one primitive per behavior from Controllers and Helpers. Controllers own the DOM wiring; helpers own the decisions and formatting.
- Keep what is specific to the component (state, markup,
rui-changeevents) in the component. - Look at Recipes for compositions of several primitives.