@
Quality / quality (ubuntu-latest) (push) Failing after 12m30s
Quality / quality (windows-latest) (push) Canceled after 0s

feat(ui): add DataTable and Toaster, drop the legacy Table, fix overlay dialogs

DataTable replaces the 20-line Table scaffold entirely: columns, sorting,
filtering, pagination, selection, bulk actions, comparison layout, sticky
first column, custom HTML cells, and a remote source driven by a `request`
output rather than a function prop (props travel as HTML attributes, so a
function arrives as its own source text).

Toaster replaces the hand-rolled status div: tone icons, actions, hover
pause/resume and a progress bar.

Overlays audit -- Modal and Drawer declared aria-modal="true" but nothing
ever moved focus into the panel, so the @keydown handler on their root
never ran and closeOnEscape did nothing. Focus, focus restore, a Tab trap
and a body scroll lock now live in the reactive runtime, shared by both.

ContextMenu placed pointer menus by subtracting a guessed 340x420 from the
viewport, which pushed every menu that was not that size away from the
pointer; it now positions at the pointer and lets the anchored clamp pull
it back once it can be measured.

The reactive runtime size budget moves 150k -> 175k to cover anchored
overlays, dialog behaviour, the toaster and the DataTable client half.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@
This commit is contained in:
2026-08-07 14:57:17 +05:30
parent 296728d51d
commit cc98bccd6c
151 changed files with 14350 additions and 9189 deletions
@@ -2841,3 +2841,87 @@ body {
text-align: center;
}
}
/*
* Toaster demo controls. The Toaster is a host with no visible surface of its
* own, so its showcase demo is a row of buttons that raise real toasts through
* the runtime `toast()` global.
*/
.showcase-toast-demo {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
}
.showcase-toast-demo button {
appearance: none;
padding: 0.45rem 0.75rem;
color: var(--wire-color-text);
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
border-radius: 0.6rem;
font: inherit;
font-size: 0.8rem;
cursor: pointer;
transition:
border-color 150ms ease,
background 150ms ease;
}
.showcase-toast-demo button:hover {
border-color: color-mix(in srgb, var(--wire-color-primary) 45%, var(--wire-color-border));
background: var(--wire-color-surface-soft);
}
/*
* The contained overlay above resolves `inset: 0` against its nearest
* positioned ancestor. That was the component root itself (position: relative,
* display: inline-flex), so a Drawer/Modal preview collapsed to the size of
* its own trigger button -- about 198x41 -- instead of filling the canvas.
* Making the root static hands the containing block back to the canvas, and
* the canvas gets enough height for the panel to be worth looking at.
*/
:is(.catalog-card-preview, .demo-render, .playground-preview-source)
:is([data-ui-component="Drawer"], [data-ui-component="Modal"]) {
position: static;
}
:is(.demo-render, .playground-preview-source):has(
[data-ui-component="Drawer"],
[data-ui-component="Modal"]
) {
min-height: 22rem;
}
/*
* Anchored overlays must not be clipped by the demo canvas.
*
* .demo-canvas scrolls (overflow: auto) so tall demos stay contained, but that
* also makes it a clipping context -- and a tooltip or popover that opens
* ABOVE its trigger was simply cut off at the canvas edge. Full-screen
* overlays (Modal, Drawer) still get clipped on purpose, by the rule further
* up: they are meant to stay inside their preview instead of covering the
* whole documentation app. Anchored ones are small and belong outside it.
*/
:is(
.demo-case,
.demo-workbench,
.demo-canvas,
.playground-workbench,
.playground-preview,
.playground-preview-source
):has(
[data-ui-component="Tooltip"],
[data-ui-component="Popover"],
[data-ui-component="Dropdown"],
[data-ui-component="ContextMenu"],
[data-ui-component="MegaMenu"]
) {
overflow: visible;
}
/* Room above the trigger so a top-placed overlay has somewhere to go. */
.demo-canvas:has([data-ui-component="Tooltip"], [data-ui-component="Popover"]) .demo-render {
padding-block: 5rem;
}