fix(ui): release the scroll lock on closed drawers, add stepper wizard controls, slide tabs
Quality / quality (ubuntu-latest) (push) Failing after 12m29s
Quality / quality (windows-latest) (push) Canceled after 0s

The scroll lock was mine, and it broke every page carrying a Drawer or Modal.
Making dialog visibility testable, I replaced a size check with a data-show
check -- but a Drawer animates open, so its panel cannot be hidden with
data-show at all: display:none is not transitionable. Every closed Drawer
therefore looked open, took the body scroll lock and never released it, and
the page could not be scrolled. Both components publish data-open, which is
the signal that actually means open, and that is what is read now.

Stepper gains the wizard surface: showPanel renders each step body and shows
only the active one, the same contract Tabs uses, and controls adds Back,
Skip and Next, which becomes Finish on the last step. nextDisabled lets a form
hold the step; the component never validates anything itself, since the page
owns the form.

Stepper also gets a single root. The panels and controls were siblings of the
list, so the component had several roots and anything scoped to
data-ui-component missed most of it.

Tabs panels now slide in the direction of travel rather than fading upward.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 23:41:18 +05:30
co-authored by Claude Opus 5
parent 68c7b96a9f
commit 5ce2771718
13 changed files with 643 additions and 39 deletions
@@ -558,6 +558,15 @@ export const componentProfiles = {
{ label: "Confirm", description: "Review and submit" },
],
active: 1,
clickable: false,
showPanel: false,
controls: false,
allowSkip: false,
nextDisabled: false,
backLabel: "Back",
nextLabel: "Next",
skipLabel: "Skip",
finishLabel: "Finish",
},
),
standard(
@@ -571,6 +580,56 @@ export const componentProfiles = {
],
active: 2,
orientation: "vertical",
clickable: false,
showPanel: false,
controls: false,
allowSkip: false,
nextDisabled: false,
backLabel: "Back",
nextLabel: "Next",
skipLabel: "Skip",
finishLabel: "Finish",
},
),
advanced(
"Wizard with content and controls",
"showPanel renders each step body and shows only the active one, the same contract Tabs uses. controls adds Back, Skip and Next, which becomes Finish on the last step.",
{
steps: [
{ label: "Account", title: "Your details", content: "Name, email and a password." },
{ label: "Billing", title: "How you pay", content: "Card or invoice." },
{ label: "Confirm", title: "Review", content: "Check everything before submitting." },
],
active: 1,
clickable: false,
showPanel: true,
controls: true,
allowSkip: true,
nextDisabled: false,
backLabel: "Back",
nextLabel: "Next",
skipLabel: "Skip",
finishLabel: "Finish",
},
),
advanced(
"Held until the step is valid",
"The same wizard with nextDisabled set. The component never validates anything itself: the page owns the form, sets this while the step is incomplete, and clears it once the step passes.",
{
steps: [
{ label: "Account", title: "Your details", content: "This step is not complete yet." },
{ label: "Billing", title: "How you pay", content: "Card or invoice." },
],
active: 0,
clickable: false,
showPanel: true,
controls: true,
allowSkip: false,
nextDisabled: true,
backLabel: "Back",
nextLabel: "Next",
skipLabel: "Skip",
finishLabel: "Finish",
},
),
advanced(
@@ -580,6 +639,14 @@ export const componentProfiles = {
steps: [{ label: "One" }, { label: "Two" }, { label: "Three" }],
active: 0,
clickable: true,
showPanel: false,
controls: false,
allowSkip: false,
nextDisabled: false,
backLabel: "Back",
nextLabel: "Next",
skipLabel: "Skip",
finishLabel: "Finish",
},
),
],