Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/soft-lions-hover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@cloudflare/kumo": minor
---

Add opt-in hover flyouts for nested sidebar collapsible content via the new
`flyout` prop on `Sidebar.Provider`. When the rail is collapsed,
flyouts appear regardless of the section's open state.

`Sidebar.MenuButton` now falls back to its own text for the collapsed-rail
tooltip when no explicit `tooltip` is set, so every icon-only item is labelled.
142 changes: 142 additions & 0 deletions packages/kumo-docs-astro/src/components/demos/SidebarDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,148 @@ export function SidebarToggleDemo() {
);
}

// ---------------------------------------------------------------------------
// Flyout — collapsed collapsibles reveal nested content in a popup
// ---------------------------------------------------------------------------

/** Turns peeking on (collapsing the sidebar to try it) or off (expanding back). */
function PeekToggle({
peekable,
onPeekableChange,
}: {
peekable: boolean;
onPeekableChange: (peekable: boolean) => void;
}) {
const { setOpen } = useSidebar();
return (
<button
type="button"
onClick={() => {
onPeekableChange(!peekable);
setOpen(peekable);
}}
className="cursor-pointer rounded-lg border border-kumo-line bg-kumo-base px-3 py-1.5 text-base text-kumo-default transition-colors hover:bg-kumo-tint"
>
{peekable ? "Disable peeking" : "Enable peeking"}
</button>
);
}

/**
* Opt-in hover flyouts via `flyout`: hovering a closed section shows
* its nested content in a popup instead of expanding inline. Collapse the rail
* to see closed and open sections alike in a flyout. Childless items (Analytics)
* have nothing to show, so they show a plain tooltip. Enable peeking to see
* flyouts wait for the peek to expand.
*/
export function SidebarFlyoutDemo() {
const [peekable, setPeekable] = useState(false);
return (
<DemoContainer>
<Sidebar.Provider
contained
defaultOpen
peekable={peekable}
flyout
className="h-full min-h-0!"
>
<Sidebar>
<Sidebar.Header>
<BrandLogo />
</Sidebar.Header>
<Sidebar.Content>
<Sidebar.Group>
<Sidebar.GroupLabel>Build</Sidebar.GroupLabel>
<Sidebar.Menu>
<Sidebar.MenuItem>
{/* Childless item: no popup, so its tooltip survives. With no
`tooltip` prop the label falls back to the button text. */}
<Sidebar.MenuButton icon={ChartBarIcon}>
Analytics
</Sidebar.MenuButton>
</Sidebar.MenuItem>
<Sidebar.MenuItem>
{/* Closed section: hovering shows its children in a flyout.
`tooltip` names the parent (the popup lists only its
children). */}
<Sidebar.Collapsible>
<Sidebar.CollapsibleTrigger
render={
<Sidebar.MenuButton icon={CodeIcon} tooltip="Compute">
Compute
<Sidebar.MenuChevron />
</Sidebar.MenuButton>
}
/>
<Sidebar.CollapsibleContent>
<Sidebar.MenuSub>
<Sidebar.MenuSubItem>
<Sidebar.Collapsible>
<Sidebar.CollapsibleTrigger
render={
<Sidebar.MenuSubButton>
Workers & Pages
<Sidebar.MenuChevron />
</Sidebar.MenuSubButton>
}
/>
<Sidebar.CollapsibleContent>
<Sidebar.MenuSub>
<Sidebar.MenuSubButton>
Overview
</Sidebar.MenuSubButton>
<Sidebar.MenuSubButton>
Workers
</Sidebar.MenuSubButton>
<Sidebar.MenuSubButton>
Pages
</Sidebar.MenuSubButton>
</Sidebar.MenuSub>
</Sidebar.CollapsibleContent>
</Sidebar.Collapsible>
</Sidebar.MenuSubItem>
<Sidebar.MenuSubButton>
Durable Objects
</Sidebar.MenuSubButton>
</Sidebar.MenuSub>
</Sidebar.CollapsibleContent>
</Sidebar.Collapsible>
</Sidebar.MenuItem>
<Sidebar.MenuItem>
<Sidebar.Collapsible>
<Sidebar.CollapsibleTrigger
render={
<Sidebar.MenuButton icon={DatabaseIcon}>
Storage
<Sidebar.MenuChevron />
</Sidebar.MenuButton>
}
/>
<Sidebar.CollapsibleContent>
<Sidebar.MenuSub>
<Sidebar.MenuSubButton>R2</Sidebar.MenuSubButton>
<Sidebar.MenuSubButton>KV</Sidebar.MenuSubButton>
<Sidebar.MenuSubButton>D1</Sidebar.MenuSubButton>
</Sidebar.MenuSub>
</Sidebar.CollapsibleContent>
</Sidebar.Collapsible>
</Sidebar.MenuItem>
</Sidebar.Menu>
</Sidebar.Group>
</Sidebar.Content>
<Sidebar.Footer>
<Sidebar.Trigger />
</Sidebar.Footer>
</Sidebar>
<DemoMain>
<PeekToggle peekable={peekable} onPeekableChange={setPeekable} />
<p>Hover a collapsed icon to see its nested items in a flyout</p>
</DemoMain>
</Sidebar.Provider>
</DemoContainer>
);
}

// ---------------------------------------------------------------------------
// Loading — nav-item-shaped skeleton shown while nav resolves
// ---------------------------------------------------------------------------
Expand Down
49 changes: 49 additions & 0 deletions packages/kumo-docs-astro/src/pages/components/sidebar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
SidebarResizableDemo,
SidebarRightDemo,
SidebarPeekingDemo,
SidebarFlyoutDemo,
SidebarAutoScrollDemo,
SidebarTransitionCompleteDemo,
SidebarSlidingViewsDemo,
Expand Down Expand Up @@ -314,6 +315,54 @@ scrollToItem("zero-trust", { align: "center", behavior: "smooth" });`}>
</ComponentExample>
</div>

<div>
<Heading level={3}>Flyout</Heading>
<p>
Set <code class="rounded bg-kumo-control px-1 py-0.5 text-xs">flyout</code> on the Provider (default
<code class="rounded bg-kumo-control px-1 py-0.5 text-xs">false</code>) to reveal a closed collapsible's nested
content in a flyout on hover, instead of expanding it inline. A collapsed rail can't render inline
content, so its sections show a flyout regardless of open state. Flyouts are mouse-only (hidden from the
accessibility tree); keyboard and assistive-tech users get inline expansion.
</p>
<p>
The collapsed top-level popup shows the parent's name as a title (from the trigger's
<code class="rounded bg-kumo-control px-1 py-0.5 text-xs">tooltip</code>, or its text when omitted), replacing
the trigger's own tooltip. Nested popups omit it &mdash; the parent row is already visible beside them. Items
with no nested content keep their tooltip instead. Every popup lines its first item up with the trigger, so
the pointer can move straight across onto it. Combined with
<code class="rounded bg-kumo-control px-1 py-0.5 text-xs">peekable</code>, hovering the collapsed rail peeks
first; flyouts only appear once the sidebar has fully expanded.
</p>
<ComponentExample code={`<Sidebar.Provider flyout>
<Sidebar>
<Sidebar.Content>
<Sidebar.Menu>
<Sidebar.MenuItem>
{/* Childless: no popup, tooltip falls back to the button text */}
<Sidebar.MenuButton icon={ChartBarIcon}>Analytics</Sidebar.MenuButton>
</Sidebar.MenuItem>
<Sidebar.MenuItem>
<Sidebar.Collapsible>
<Sidebar.CollapsibleTrigger
render={
<Sidebar.MenuButton icon={CodeIcon} tooltip="Compute">
Compute <Sidebar.MenuChevron />
</Sidebar.MenuButton>
}
/>
<Sidebar.CollapsibleContent>
<Sidebar.MenuSub>...</Sidebar.MenuSub>
</Sidebar.CollapsibleContent>
</Sidebar.Collapsible>
</Sidebar.MenuItem>
</Sidebar.Menu>
</Sidebar.Content>
</Sidebar>
</Sidebar.Provider>`}>
<SidebarFlyoutDemo client:load />
</ComponentExample>
</div>

<div>
<Heading level={3}>Sliding Views</Heading>
<p>
Expand Down
Loading