Drawers
Right-docked companion panels — the non-modal counterpart to the modal and side tray. Drawers open into a shared dock that squeezes the page rather than covering it: one drawer fills the dock, two split it, and any beyond that collapse to an icon rail. Below lg a drawer becomes a full-screen sheet with a scrim. One controller (koalaDrawerDock) and one shell (<koala-drawer-dock>) power every surface — the Portal comms rail (Joey + softphone) mounts the exact same component.
Canonical
A row opens a drawer by { kind, id }; the dock resolves its icon,
title and content and docks it beside the feed. Open several — two share the width in full, more collapse
to the icon rail (page with the chevrons or jump by icon; hover an icon to preview).
Drag the seam to resize. Content persists when a drawer closes, so a stateful drawer
(the softphone's live call) survives close/reopen.
The feed keeps working while drawers are open beside it — open any row.
Tasks
3 open| Chase Searchflow | High · due tomorrow |
| Order office copies | Medium · due Friday |
| Approve the draft plan | Low · no due date |
Messages
2 unread| Mortgage offer issued | Halifax · 4h ago |
| Quick question on completion | J. Okafor · 1d ago |
| Keys ready for collection | Bramley & Co · 2d ago |
Documents
3 files| Draft contract pack | PDF · 14 pages |
| Local authority search | PDF · 8 pages |
| TA6 property information | PDF · 6 pages |
Chase Searchflow for the outstanding local authority search. Flagged high — completion is booked for the 24th.
Order official copies of the title register and plan from HM Land Registry before drafting the transfer.
Review and approve the draft plan attached to the seller’s pack. No due date yet.
Please find the formal mortgage offer attached. Valid for 6 months from the date of issue.
Hi — are we still on track for completion on the 24th? Happy to send the balance whenever you need it.
Keys will be with us from completion morning. The buyer can collect once we confirm funds have arrived.
The seller’s draft contract pack. Review clauses 5 and 9 before approving and returning.
Local authority search result. No adverse entries, but note the pending planning application at no. 9.
Seller’s completed TA6 property information form. The boundaries section needs a follow-up enquiry.
<div x-data="koalaDrawerDock({ iconRegistryId: 'dock-icons',
iconKinds: ['task','message','document','close','prev','next'],
title: d => catalogue[d.kind+':'+d.id].title,
meta: d => catalogue[d.kind+':'+d.id].meta })"
x-ref="row" class="flex">
<div class="flex-1 min-w-0">
@* the page — any row opens a companion drawer *@
<tr x-on:click="openDrawer('task','searchflow')">…</tr>
</div>
<div x-ref="handle" class="ws-drawer-resize-handle"
x-bind:style="drawers.length ? 'display:flex' : 'display:none'">
<span class="ws-drawer-resize-grip"></span>
</div>
<koala-drawer-dock /> @* THE dock: columns, rail, paging, resize, preview *@
<div x-ref="contentHolder" class="hidden">
<div data-drawer-content="task:searchflow">…</div> @* moved into the column; state persists *@
</div>
</div>
Mounted once and opened from anywhere (like modals / side trays): call
openDrawer(kind, id) from any row. The host supplies the icon
registry, the row (x-ref="row"), the resize handle and the
[data-drawer-content] nodes; the dock owns everything else.
Variants
3 behavioursWidth-sharing
Visible drawers share the dock exactly — one fills it, two split it evenly. Never a cropped column.
Overflow rail
Drawers that don't fit collapse to the bottom icon rail. Page with the chevrons or jump by icon; the track book-scrolls to the target.
Resize
Drag the seam to set the dock width; the page keeps pageMinWidth. Columns re-share as it widens or narrows.
States
3 statesProps
koalaDrawerDock(config)The behaviour is configured once where the controller is declared on the row. Everything is optional except the icon registry.
| Attribute | Values | Notes |
|---|---|---|
| iconRegistryId | string | Id of a hidden element holding one [data-drawer-icon="kind"] span per kind (server-rendered <koala-icon>). The dock captures the SVG at init. |
| iconKinds | string[] | Kinds to capture from the registry (include close, prev, next for the chrome). |
| title | (drawer) => string | Resolves a drawer's header title from its { kind, id }. AJAX-fed in the app. |
| meta | (drawer) => string | Resolves the small header sub-line (the mono reference / timestamp). |
| columnWidth | number = 320 | Minimum full width (px) of one visible column — decides how many fit before overflow. |
| maxAutoColumns | number = 2 | How many columns the dock auto-sizes to before you drag to widen it. |
| pageMinWidth | number = 320 | Width (px) the squeezed page must keep — caps how wide the dock can grow. |
The base <koala-drawer> primitive (used directly for a single
fixed drawer, and under the hood for the mobile sheet) takes:
| Attribute | Values | Notes |
|---|---|---|
| flag | string | Alpine boolean controlling open/closed (e.g. $store.call.open). The parent scope must declare it. |
| width | CSS length | Docked width on desktop. Defaults to 24rem. Mobile is always a full-screen sheet. |
| aria-label | string | Accessible label when no title is set. Falls back to Panel. |
Sub-components
3 helpers| Tag | Provides | Notes |
|---|---|---|
| <koala-drawer-dock> | the whole dock | The shared shell: the column window, icon rail, prev/next paging, resize seam wiring and hover preview. Paired with the koalaDrawerDock() controller. Drop it once inside the x-ref="row"; open drawers with openDrawer(kind, id). |
| <koala-drawer-header> | flag (string) | Title row with a close button, for a single base <koala-drawer> that needs a richer header (icon + title + mono reference). flag mirrors the parent so the close-X clears it. |
| <koala-drawer-content> | class (string?) | Scrollable body (flex-1 overflow-y-auto) for a base drawer. Optional — content that scrolls itself (the softphone panel) can be a direct child instead. |