-
v0.36.0 Stable
released this
2026-09-22 21:27:00 +00:00 | 3 commits to main since this releaseAdded
TreeIndexgains search and filters that dim rather than remove (ADR 0012).
GivenqueryOptions(or astatefromuseIndexQuery, which no longer
needs aload), it owns the same query state asIndexand provides it, so
FilterCheckbox,FilterSelect,FilterMultiSelect,FilterRange, the
search box, thefiltersslot and the active-filter chips work unchanged,
in a page aside included, withsyncQuerykeeping the URL. It never reloads
for a query: it emitsquerywith the committedsearchandfilters
(TreeQuery), the project answers withmatching, a set of row keys, and
every row outside the set is dimmed (data-dimmedon the row), keeps its
move button and stays offered as a destination, so the tree keeps its shape.
The match count is announced through the table's live region and a dimmed
row's header carries a visually hidden "Does not match." cue; both strings
arematchLabels.TreeTabletakes the samematchingandmatchLabels
for controlled data.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.35.0 Stable
released this
2026-09-22 14:34:59 +00:00 | 8 commits to main since this releaseBreaking
- The tree leaves
IndexandDataTablefor a component of its own,
TreeIndexat@[secure]/ui/components/TreeIndex.vue(ADR 0012).Index
losesloadTree,move,parentKey,canMove,canParent,rowLabel
andmoveLabels,useIndexQuerylosesloadTreeandtree, and
DataTablelosesparentKey,canMove,canParent,rowLabel,
moveLabels,@moveandannounce(); both are the paged list and the flat
table again.MoveLabels.childOfis replaced bylevel(n, parent), and
treeItemsgives each item adepthandlanesinstead of alaneand
four booleans. To migrate an index that used them, replace<Index>with
<TreeIndex>: pass the tree loader asload(the flatload,
queryOptions,stateandsyncQuerygo, since a Tree Index has no
query), keepcolumnswith thelevelandmovecolumns,parentKey,
move,canParent,rowLabel,moveLabels, selection and row click, and
dropsortablefrom the columns. Thesummaryslot receivestotalonly.
The move request body is unchanged.
Added
TreeIndexcomposes a newTreeTable
(@[secure]/ui/components/TreeTable.vue) the wayIndexcomposes
DataTable: the data-driven tree overTablefor controlled data, taking
rows, the same columns and predicates,v-model:placingand
v-model:selected, and emittingmovewith the intent; it never reorders
rows.TreeIndexshows the tree to any depth: every record is drawn, a row can
be moved into or out of any parent, and the moving row's whole subtree
travels with it. The optionalmaxDepthremoves the destinations a move
would exceed, so a backend that allows two levels never sees a third;
without it any depth is offered. Each row's header cell carries a visually
hidden "Level n, under ." prefix, so a screen reader hears depth and
direct parent while the table stays a plain<table>. Loading, error and
retry, the placing state, the optimistic move with rollback and
announcement are asIndexhad them. Search and filters are a follow-up.
Fixed
- A table no longer stretches the page to its own height. Its visually hidden
content (the caption, a typed column's header text,TreeTable's live
region in theafterslot) is absolutely positioned and was contained by
the page rather than by the table's scroll port, so a long tree made the
window scroll behind the app layout's own scrolling main and left a gap
under the sticky footer. The wrap now contains it.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- The tree leaves
-
v0.34.0 Stable
released this
2026-09-22 11:13:39 +00:00 | 17 commits to main since this releaseAdded
Sidebargains a#linkslot, forwarded byAppLayout, that renders every
navigable entry — plain links, group rows with anhref, the bottom nav and
the collapsed rail's flyout — through the consumer's own element, so an
application sets its RouterLink once and drops its click interception. It
follows Breadcrumb's#link: the scope carriesitem,linkClassand, in
addition,attrs(the ARIA state, data hooks and listeners the Sidebar's
keyboard, flyout and drawer wiring depend on) andcontent(the icon, label
and badge). Render<RouterLink :to="item.href" :class="linkClass" v-bind="attrs"><component :is="content" /></RouterLink>. Active state and
aria-currentstay the Sidebar's decision. Without the slot the output is
unchanged.SidebarLinkSlotScopeis exported fromSidebar.types.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.33.0 Stable
released this
2026-09-22 11:09:05 +00:00 | 23 commits to main since this releaseBreaking
RecordDetailsis removed, withRecordDetailsFieldand the
@[secure]/ui/components/RecordDetails.vueandRecordDetails.types
exports. A record's view page is its edit form withreadonly, which
renders the same layout, tabs and fields read-only, without an actions row
or asubmit. A related record beside a form is a read-onlyRecordFields
group bound to that record: replace<RecordDetails :record :fields>with
<RecordFields :model-value="record" :fields readonly>, retype the fields
asRecordFormField[], and turn a#field:nameslot that rendered a link
or badge into atype: 'custom'field whose slot renders the value inside
a.pui-fieldwith a.pui-field__label. The description list's—
placeholder, Yes/No booleans and JSON objects have no equivalent: a
boolean is acheckboxorswitchfield and an object is its own fields.
Move an#actionsslot to the page header.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.32.0 Stable
released this
2026-09-22 10:41:49 +00:00 | 30 commits to main since this releaseAdded
RecordFormandRecordFieldsacceptpassword,url,tel,switch
andtagsfield types, rendering TextInput with that input type, Switch
and TagsInput. Each is bound to its path with the control's value type (a
string, a boolean, a string array), takesbusy,disabledand backend
issues like the existing built-ins, and view mode renders it read-only, not
disabled.tagsforwardsplaceholderandmaxTags. A user form no longer
needs a#field:nameslot for its password input. Relation pickers stay
type: 'custom'.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.31.1 Stable
released this
2026-09-22 10:30:41 +00:00 | 35 commits to main since this releaseFixed
useUnsavedChangesasks before moving to another record inside the same
component. It registeredonBeforeRouteLeaveonly, so going from
/blog/1/editto/blog/2/editwith a dirty draft reused the component,
skipped the guard and dropped the edits. It now registers
onBeforeRouteUpdatetoo. A navigation that keeps the route params, such as
a?tab=or hash change, does not ask. Pass
{ sameRecord: (to, from) => boolean }as the second argument when the
record is not identified by the params. Leaving the route asks as before.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.31.0 Stable
released this
2026-09-21 13:53:52 +00:00 | 38 commits to main since this releaseAdded
IndexandDataTableshow records that carry a parent and a manual order
as a two-level tree, and move them by click-to-place (ADR 0011). This is for
an index that doubles as the menu editor, such as Pages.- The project adds two typed columns,
{ key, label, type: 'level' }and
{ key, label, type: 'move' }, and names the parent field with
parentKey. The level column draws the hierarchy, so every other column
stays aligned. Rows must arrive in manual order; nothing orders them. - A row's move button opens a placing state in which every legal
destination is a button that says where it leads ("Top level",
"Inside About"). Hovering or focusing one previews the row in that
place. There is no drag and drop. Escape, Cancel or the move button
again leaves the state. IndextakesloadTree({ signal }), returning{ data }with every
record, and uses it instead ofloadwhile no search, filter or sort is
active; pagination hides in that view. Every other view is the flat
paginated list without the two typed columns.useIndexQuerytakes the
same option and exposestree.Indextakesmove({ id, parentId, index, siblingIds }), an async
function. The move is applied at once, rolled back with the rejection's
message shown and announced if it rejects, and the tree is reloaded if
it resolves.DataTableemits the same intent asmoveand never
reordersrows;applyMovefrom@[secure]/ui/utilsapplies one.canMove(row)andcanParent(row)remove move buttons and "inside"
destinations a backend would refuse. The package itself enforces two
levels.rowLabel(row)names a row andmoveLabelsoverrides any of
the English strings; they are functions so a translation owns its word
order.Tablegains anafterslot, rendered inside the wrap after the
<table>.- The HTTP side is in
docs/cms/backend-contract.mdunder "Manual order
and moves".
- The project adds two typed columns,
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.30.0 Stable
released this
2026-09-19 22:59:21 +00:00 | 47 commits to main since this releaseChanged
BlockEditoredits the open block in aSheetwhen its container is
narrower than 44rem, instead of stacking the detail pane under the list:
label, type and position in the sheet's head, Previous, Next and Close in
its footer, no prompt. Closing the sheet setsopentonulland returns
focus to the row.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.29.1 Stable
released this
2026-09-19 22:55:01 +00:00 | 52 commits to main since this releaseFixed
Indexvalidates its sortable columns againstqueryOptions.sortsbefore
it creates its own query state. The state loads at once, so a column the
vocabulary does not allow fired one request before the configuration error
was thrown.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.29.0 Stable
released this
2026-09-19 22:49:51 +00:00 | 58 commits to main since this releaseBreaking
BlockEditorreplaces the row of per-type add buttons with one Add block
button that opens a type picker: aDropdownMenu, or a searchable
Commandlist whensearchableis set or, left unset, above 8 block
types (searchPlaceholder,searchEmptyText). Each row gains an insert
button above it, shown on row hover or keyboard focus without moving the
rows, that opens the same picker and inserts before that row.addLabel
is now a string (defaultAdd block) instead of(typeLabel) => string:
a page that passed a function passes the single button's label. The add
announcement names the position (Text added at position 2 of 4.).
Added
DropdownMenutakesanchor, the trigger to position against when
severalpopovertargettriggers share one menu; the default stays the
first matching trigger.
Fixed
DropdownMenuno longer stays visible while closed. Itsdisplay: flex
applied in every state and overrode the browser rule that hides a closed
popover; it now applies under:popover-openonly.Sheetnow spans the full edge it is anchored to. The browser's
fit-contentsize andmax-height/max-widthfor<dialog>won over the
top/bottom(orleft/right) pair, so a short sheet stopped under its
footer; each side now resets them on its stretched axis.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads