PHPackages                             quellabs/canvas-loom - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Admin Panels](/categories/admin)
4. /
5. quellabs/canvas-loom

ActiveLibrary[Admin Panels](/categories/admin)

quellabs/canvas-loom
====================

Definition-driven page builder for Canvas that turns PHP builder definitions into data-bound admin interfaces.

1.0.3(1mo ago)00MITPHPPHP &gt;=8.3

Since Apr 14Pushed 1mo agoCompare

[ Source](https://github.com/quellabs/canvas-loom)[ Packagist](https://packagist.org/packages/quellabs/canvas-loom)[ Docs](https://canvasphp.com)[ RSS](/packages/quellabs-canvas-loom/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (3)Versions (5)Used By (0)

Loom
====

[](#loom)

A definition-driven page builder for Canvas that turns structured PHP definitions into interactive admin pages — forms, settings panels, editors — with automatic data binding and WakaPAC initialisation.

Installation
------------

[](#installation)

```
composer require quellabs/canvas-loom
```

Quick Start
-----------

[](#quick-start)

```
use Quellabs\Canvas\Loom\Loom;
use Quellabs\Canvas\Loom\Builder\Resource;
use Quellabs\Canvas\Loom\Builder\Section;
use Quellabs\Canvas\Loom\Builder\Field;

$definition = Resource::make('post-form', '/admin/posts/save')
    ->title('Edit Post')
    ->add(Section::make()
        ->add(Field::text('title', 'Title')->required())
        ->add(Field::textarea('body', 'Content')->rows(10))
    )
    ->build();

$loom = new Loom();
echo $loom->render($definition, [
    'title' => 'My First Post',
    'body'  => 'Hello world.',
]);
```

How It Works
------------

[](#how-it-works)

You build a node tree using fluent PHP builders. `Loom::render()` walks the tree, delegates each node to a renderer, and emits HTML with the appropriate WakaPAC initialisation scripts. Entity data passed to `render()` is automatically distributed to field values via DOM hydration.

Node Types
----------

[](#node-types)

NodePurpose`Resource`Page root — renders a `` with header, title, save/cancel`Section`Visual grouping without WakaPAC init`Tabs` / `Tab`Tabbed interface managed by WakaPAC`Panel`Reactive WakaPAC container without tabs`Columns` / `Column`Flex layout with percentage widths`Field`Input with label, validation, and data binding`Text`Read-only label/value pair, supports interpolation`Button`Action trigger bound to WakaPAC expressionsFields
------

[](#fields)

```
Field::text('title', 'Title')->required()->maxlength(200)
Field::textarea('body', 'Content')->rows(10)
Field::select('status', 'Status')->options(['draft' => 'Draft', 'published' => 'Published'])
Field::checkbox('featured', 'Featured post')
Field::number('priority', 'Priority')->min(1)->max(10)
```

Dependent dropdowns are supported via `->dependsOn('parent_field')`. Dependent fields must be direct siblings within the same `Column` or `Section`.

Buttons
-------

[](#buttons)

Buttons execute WakaPAC expressions via `->action()`. Three variants: primary (default), `->secondary()`, `->danger()`.

```
Button::make('Save')->action('submit()')
Button::make('Publish')->action("post('/admin/posts/publish')")
Button::make('Delete')->danger()->action("Stdlib.sendMessage('post-form', MSG_DELETE, 0, 0)")
```

Every container exposes `submit()` and `post(url)` on its WakaPAC abstraction automatically.

Abstraction
-----------

[](#abstraction)

Named message constants and other non-reactive properties can be added to a container's WakaPAC abstraction. Underscore-prefixed keys are non-reactive.

```
Resource::make('post-form', '/admin/posts/save')
    ->abstraction([
        '_MSG_SHOW_DELETE' => 1001,
        '_MSG_HIDE_DELETE' => 1002,
    ]);
```

Notifications
-------------

[](#notifications)

```
$loom = new Loom();
$loom->notification('success', 'Post saved.');
$loom->notification('error', 'Title is required.');
echo $loom->render($definition, $data);
```

Types: `success`, `error`, `warning`, `info`.

Custom Renderers
----------------

[](#custom-renderers)

Extend `AbstractRenderer`, implement `RendererInterface`, and register on the `Loom` instance:

```
$loom = new Loom();
$loom->register('field', MapFieldRenderer::class);
echo $loom->render($definition, $data);
```

To override only specific nodes within a type, check the node properties and delegate to the default renderer for everything else.

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance90

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

4

Last Release

45d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/57e4ab872b3e37536367f2d26b192df3d3bb6a6a1cebec9a104d14a6d2ffe157?d=identicon)[noescom](/maintainers/noescom)

---

Tags

phpadmincanvasadmin-panelform-builderloomwakapac

### Embed Badge

![Health badge](/badges/quellabs-canvas-loom/health.svg)

```
[![Health](https://phpackages.com/badges/quellabs-canvas-loom/health.svg)](https://phpackages.com/packages/quellabs-canvas-loom)
```

###  Alternatives

[superv/platform

SuperV Platform Package

2412.1k5](/packages/superv-platform)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
