Download the PHP package zvizvi/villament without Composer
On this page you can find all versions of the php package zvizvi/villament. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download zvizvi/villament
More information about zvizvi/villament
Files in zvizvi/villament
Package villament
Short Description Replace the Livewire-rendered table on a Filament v5 Resource List page with a Vue 3 island, fed by the existing table() schema.
License MIT
Homepage https://github.com/zvizvi/villament
Informations about the package villament
Villament
Replace the built-in Livewire-rendered table on a Filament v5 Resource List page with a Vue 3 island, while keeping 100% of the data pipeline in Filament's PHP layer.
After the initial page load, all table interactions — sorting, pagination, global & per-column search, filtering — are performed via lightweight AJAX (JSON) requests instead of Livewire roundtrips. The Vue table is fed from the Resource's existing table() schema: no redefining columns, filters, scoping, or authorization.
Status: Phase 0 (skeleton & fixtures). Nothing usable ships yet — see the roadmap below.
Requirements
| Dependency | Constraint |
|---|---|
| PHP | ^8.3 |
| Laravel | ^12.0 |
| Filament | ~5.6.0 (pinned per tested minor; widens as the CI matrix grows) |
| Vue | ^3.5 (bundled, no host-app requirement) |
Roadmap
- [x] Phase 0 — Skeleton & fixtures: composer/vite scaffolding, service provider, empty Filament plugin, Testbench test app with a fixture panel +
UserResourcecovering every column type (formatted/linked text, badge with closure color, icon, image, view column), a global query scope, three filter types, row & bulk actions, 500 seeded users. The stock Filament table renders and is exercised by tests (render, global-scope security, search, sort, filter). - [x] Phase 1 — MVP data path:
HeadlessTableHarnessdrives the resource's real List page component off-screen;SchemaSerializer/RecordSerializerturn thetable()definition and each record into pre-formatted JSON payloads (all closures run server-side);RequestStateValidatorwhitelists every sort/search/filter/pagination parameter (422 otherwise); the data endpoint is registered inside each panel's authenticated middleware stack and enforcescanViewAny()+ the resource's global scope;VillamentListRecordsswaps only the embedded table region for a Vue island with inlined schema + first-page data (zero extra requests on first paint); the island does search (global + per-column, debounced + aborted), sorting, offset pagination and URL sync. Parity tests assert the endpoint returns byte-identical record keys/order to the stock Livewire table under identical state, andInternalApiContractTestpins every internal Filament API touched. - [x] Phase 2 — Filters: serializer chain (
selectincl. multiple + enum options,ternary, anything else →livewire-fallback), Vue filter dropdown, server-computed indicator chips (indicateUsingclosures included) with per-chip remove + remove-all, localized UI strings via Filament's own translations (the island follows the panel locale, RTL included), and the fallback mechanism: the page's original Livewire filter form stays mounted (hidden) and a JS bridge pushes its state changes into the Vue store, so one exotic filter never blocks adoption. Per-filter parity tests against the stock table. - [x] Phase 3 — Actions bridge + column manager: row actions (buttons, links, ActionGroup dropdowns) evaluated per record server-side (visibility = Filament authorization; hidden actions never reach the client) and mounted through the page component's unified
mountAction()— Filament's modals, confirmation and forms work unchanged; record selection with select-page and select-all-matching (Filament's inversion semantics) feeding bulk actions with exactly the selected keys; automatic island refresh after any action completes; the column manager applies throughapplyTableColumnManager()so visibility persists in the session and the data endpoint follows automatically; clickable rows (recordUrl/recordAction) and striped tables. - [ ] Phase 4 — Polish (cursor pagination, column toggling, summaries, export, config). In progress: cursor pagination, column toggling, extension API, config, server-computed summaries (page + all) and grouping all shipped — grouping covers Vue-native group/direction settings, server-evaluated group-header rows (with per-group select-all, collapsible groups, per-group summary subheadings, and
groupsOnly()summary rows), URL sync, and is parity-tested against the stock table. Summaries cover the page/all/header rows with theirsummaries()toggles; column headers carry Filament's full modifier set (wrapHeader(),grow(),width(), responsive visibility) andColumnGroupheader rows. The column manager is Filament-native (teleported like the filters), socolumnManagerLayout(), reset position, grid columns, a customized trigger and drag reordering all work as they do in stock. Per-column options from the Columns overview are carried through:$rowLoopinjection, cell click actions (action(),disabledClick(),url()on every column type),tooltip()/emptyTooltip()/headerTooltip(),placeholder(),description(position:), theextra*Attributes()bags, and the table's ownsearchDebounce()/searchOnBlur()/searchPlaceholder()plus session persistence of sort and searches. From the Actions docs: every trigger variant (button(),iconButton(),badge(),size(),tooltip(),hiddenLabel(), counter badges,extraAttributes(), andmodifyUngroupedRecordActionsUsing()) with its class list computed by Filament itself;headerActions()alongside the table'sheading()/description()(andheader(), the custom-view escape hatch, teleported in natively so its own Livewire/Alpine bindings stay live);recordActions(position:)withrecordActionsAlignment()/recordActionsColumnLabel(); and the complete selection API —selectable(),checkIfRecordIsSelectableUsing(),maxSelectableRecords(),selectCurrentPageOnly(),selectGroupsOnly(),disabledSelection(), shift-click ranges and indeterminate checkboxes. The empty state is Filament-native too (icon, heading, description andemptyStateActions()with their full action surface, or a customemptyState()view), andrecordClasses()reaches the row.
Usage
-
Register the plugin on your panel:
- Swap the parent class of your resource's List page (one line — columns, filters, scoping and authorization all come from your existing
table()andgetEloquentQuery()):
Or apply the trait to a customized List page: use Zvizvi\Villament\Concerns\HasVillamentTable;.
After the initial paint (schema + first page inlined, no extra requests), sorting, searching and pagination hit a JSON endpoint that runs behind your panel's full middleware stack, checks canViewAny(), and whitelists every request parameter against the table schema.
Development
Design decisions and every internal Filament API the package touches are logged in AGENTS.md.
Test fixtures
tests/Fixtures/ boots a real Filament panel (admin) with a UserResource designed to exercise every serialization path the package will need. The parity rule governs all future phases: any behavior where the Vue path and the stock Filament path disagree on which records, in which order is a bug.
License
MIT
All versions of villament with dependencies
filament/filament Version ~5.7.0
spatie/laravel-package-tools Version ^1.16