Adapters
The core of Gridla is a set of pure functions over plain objects. An adapter binds that core to a rendering model: it measures the canvas, positions items, turns pointer and keyboard input into solver calls, and hands the committed layout back to your state. Every adapter ships from the same gridla package as its own subpath export, with the framework as an optional peer dependency and nothing else.
Appearance is always yours: the styling guide lists every data-gridla-* attribute and shows how to style items, handles, and the preview outline once for all of them.
Overview
Preact reuses the React adapter through preact/compat; the Preact page documents the alias setup and the verification that runs against it in the package contract suite.
Which one?
Pick the entry for the framework that renders your page; the layout behaves the same in every one of them, and the core stays available for solving outside a component tree. Without a framework, use gridla/dom when you mount from code (one function, one element) and gridla/elements when you declare the canvas in HTML or from a template engine. Mixing adapters on one page is fine; they share no global state.
Shared contract
Adapters share a contract so that switching frameworks, or mixing them on one page, changes nothing about how a layout behaves. The React adapter defines the reference behavior; every guide above has the same sections (install, minimal example, controlled and uncontrolled, nested layouts and transfers, server rendering, API) so the differences are easy to find.
- Same names.
GridProvider(or the framework's idiom for it:mountGridin the DOM,<gridla-canvas>as a custom element,[gridlaProvider]in Angular),GridCanvas,GridItem,GridPreviewOutline, andGridTransferScope. - Same props. Controlled (
layoutplusonLayoutChange) and uncontrolled (defaultLayout) modes,responsive,configor the individualSolveOptionsfields (gap,snapDistance,snap,onTrace),selectedId,acceptTransfers,onTransferIn,onTransferOut,onCommit, andonItemClick. Change callbacks receive the next layout and aGridChangeDetail(reason,itemId, solverstrategy). Frameworks spell the binding their own way (v-model:layout,bind:layout,[(layout)],onLayoutChange$), but the semantics do not change. - Same data attributes. Every adapter marks the canvas with
data-gridla-canvas, each item withdata-gridla-item="<id>", the active, selected, shifted, and transferring items withdata-gridla-active,data-gridla-selected,data-gridla-shifted, anddata-gridla-transferring, the landing box withdata-gridla-preview, and handles withdata-gridla-drag-handle,data-gridla-resize-handle(the item id), anddata-gridla-edge="<edge>". The root carriesdata-gridla-draggingwhile a gesture runs. Styles, tests, and tooling written against one adapter work against all of them. - Headless. Adapters position items (
transformby default, orleft/topwithpositioning: 'absolute') and nothing more. No stylesheet ships; theming is yours. - Server rendering. No
windowordocumentaccess at import time. The server output is the authored layout; on the client the canvas measures itself before the first paint where the framework allows it, so the projected layout is what the user sees first. - Nested layouts and transfer scope. Nested canvases are nested providers, and items move between canvases through a transfer scope: the pointer decides the target, the target previews the drop, and releasing commits it, exactly as in the transfer recipe.
Write your own adapter
All adapters are thin bindings over gridla/interaction, the framework-neutral layer the React adapter is built on. It is a public entry, so an adapter for a framework not listed here is a small module rather than a reimplementation. The four functions to know:
createGridControllerowns the store, the projection onto the measured size, the gesture API, the controlled and uncontrolled sync, and theonLayoutChangereasons. Your adapter rendersstoresnapshots and forwards prop changes withsetLayout,setSize, andsetConfig.createPointerGestureturns pointer and keyboard input into controller calls. It takes a minimal event shape, so it works with synthetic events too;bindPointer(element)attaches native listeners and returns an unsubscribe.createTransferScoperegisters controllers and hit-tests their canvases during a drag, so several providers (from any adapter) can exchange items.observeSizemeasures an element synchronously and then throughResizeObserver, which is how every adapter feedssetSize.
Emit the data attributes above from GRID_DATA, keep your module free of window and document at import time, and the shared browser contract suite can run against your demo app unchanged.
Verification
Each adapter is exercised three ways: unit tests against the framework's own test utilities, the shared browser contract suite that runs the same scenarios against a small demo app per adapter, and the package contract suite that installs the packed tarball into a consumer project for that framework. The demo apps are published with the documentation site under /adapters/<name>/.