All 44 component demos

Layout & Display, Inputs Basic, Inputs Selection, Inputs Advanced,
Navigation, Overlays, Feedback & Status, and Data sections with
live interactive demos for every PettyUI component.
This commit is contained in:
Mats Bosson 2026-03-30 03:36:22 +07:00
parent 3d2257ae3c
commit 8761d317ae
9 changed files with 1079 additions and 8 deletions

View File

@ -1,4 +1,12 @@
import type { JSX } from "solid-js"; import type { JSX } from "solid-js";
import { LayoutDisplaySection } from "./sections/layout-display";
import { InputsBasicSection } from "./sections/inputs-basic";
import { InputsSelectionSection } from "./sections/inputs-selection";
import { InputsAdvancedSection } from "./sections/inputs-advanced";
import { NavigationSection } from "./sections/navigation";
import { OverlaysSection } from "./sections/overlays";
import { FeedbackStatusSection } from "./sections/feedback-status";
import { DataSection } from "./sections/data";
const categories = [ const categories = [
{ id: "layout-display", label: "Layout & Display" }, { id: "layout-display", label: "Layout & Display" },
@ -55,28 +63,28 @@ export function App() {
<main class="max-w-5xl mx-auto px-6 py-12"> <main class="max-w-5xl mx-auto px-6 py-12">
<Section id="layout-display" title="Layout & Display"> <Section id="layout-display" title="Layout & Display">
<p class="text-gray-400 italic">Coming soon...</p> <LayoutDisplaySection />
</Section> </Section>
<Section id="inputs-basic" title="Inputs: Basic"> <Section id="inputs-basic" title="Inputs: Basic">
<p class="text-gray-400 italic">Coming soon...</p> <InputsBasicSection />
</Section> </Section>
<Section id="inputs-selection" title="Inputs: Selection"> <Section id="inputs-selection" title="Inputs: Selection">
<p class="text-gray-400 italic">Coming soon...</p> <InputsSelectionSection />
</Section> </Section>
<Section id="inputs-advanced" title="Inputs: Advanced"> <Section id="inputs-advanced" title="Inputs: Advanced">
<p class="text-gray-400 italic">Coming soon...</p> <InputsAdvancedSection />
</Section> </Section>
<Section id="navigation" title="Navigation"> <Section id="navigation" title="Navigation">
<p class="text-gray-400 italic">Coming soon...</p> <NavigationSection />
</Section> </Section>
<Section id="overlays" title="Overlays"> <Section id="overlays" title="Overlays">
<p class="text-gray-400 italic">Coming soon...</p> <OverlaysSection />
</Section> </Section>
<Section id="feedback-status" title="Feedback & Status"> <Section id="feedback-status" title="Feedback & Status">
<p class="text-gray-400 italic">Coming soon...</p> <FeedbackStatusSection />
</Section> </Section>
<Section id="data" title="Data"> <Section id="data" title="Data">
<p class="text-gray-400 italic">Coming soon...</p> <DataSection />
</Section> </Section>
</main> </main>
</div> </div>

View File

@ -0,0 +1,112 @@
import { For } from "solid-js";
import { Calendar } from "pettyui/calendar";
import { DataTable } from "pettyui/data-table";
import { VirtualList } from "pettyui/virtual-list";
import { ComponentDemo } from "../component-demo";
const cellCls = "px-3 py-2 text-sm text-gray-700 border-b border-gray-100";
const headCls = "px-3 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wide border-b border-gray-200 bg-gray-50 text-left";
type Person = { id: number; name: string; role: string; status: string };
const people: Person[] = [
{ id: 1, name: "Alice Chen", role: "Engineer", status: "Active" },
{ id: 2, name: "Bob Kim", role: "Designer", status: "Active" },
{ id: 3, name: "Carol Diaz", role: "Manager", status: "Away" },
{ id: 4, name: "Dan Park", role: "Engineer", status: "Active" },
{ id: 5, name: "Eva Müller", role: "Product", status: "Inactive" },
];
const tableColumns = [
{ id: "name", header: "Name", sortable: true, accessor: (r: Person) => r.name },
{ id: "role", header: "Role", sortable: true, accessor: (r: Person) => r.role },
{ id: "status", header: "Status", sortable: false, accessor: (r: Person) => r.status },
];
/** Calendar demo showing a navigable month grid. */
function CalendarDemo() {
const navBtn = "w-7 h-7 flex items-center justify-center rounded hover:bg-gray-100 text-gray-600 text-sm";
const content = (
<Calendar class="w-72 border border-gray-200 rounded-lg p-3 bg-white shadow-sm">
<Calendar.Header class="flex items-center justify-between mb-2">
<Calendar.Nav class="flex items-center gap-1">
<Calendar.PrevButton class={navBtn}></Calendar.PrevButton>
<Calendar.NextButton class={navBtn}></Calendar.NextButton>
</Calendar.Nav>
<Calendar.Heading class="text-sm font-semibold text-gray-800" />
</Calendar.Header>
<Calendar.Grid class="w-full border-collapse">
<Calendar.GridHead class="[&_th]:text-xs [&_th]:text-gray-400 [&_th]:font-medium [&_th]:pb-1 [&_th]:text-center" />
<Calendar.GridBody class="[&_td]:text-center [&_td]:p-0.5 [&_td]:text-sm [&_td]:rounded [&_td]:cursor-pointer [&_td:hover]:bg-indigo-50 [&_td[data-selected]]:bg-indigo-600 [&_td[data-selected]]:text-white [&_td[data-today]]:font-bold [&_td[data-outside-month]]:text-gray-300" />
</Calendar.Grid>
</Calendar>
);
return content;
}
/** DataTable demo with sortable columns and 5 sample rows. */
function DataTableDemo() {
const content = (
<div class="w-full overflow-x-auto rounded-lg border border-gray-200">
<DataTable data={people} columns={tableColumns} pageSize={5}>
<table class="w-full">
<thead>
<tr>
<For each={tableColumns}>
{(col) => <th class={headCls}>{col.header}</th>}
</For>
</tr>
</thead>
<DataTable.Body />
</table>
</DataTable>
</div>
);
return content;
}
const ITEM_COUNT = 10000;
const ITEM_HEIGHT = 36;
/** VirtualList demo rendering 10 000 items with windowed scroll. */
function VirtualListDemo() {
const content = (
<div class="w-full max-w-sm border border-gray-200 rounded-lg overflow-hidden">
<div class="px-3 py-2 bg-gray-50 border-b border-gray-200 text-xs text-gray-500">
{ITEM_COUNT.toLocaleString()} items only visible rows are rendered
</div>
<VirtualList
count={ITEM_COUNT}
estimateSize={ITEM_HEIGHT}
style={{ height: "240px" }}
class="w-full"
>
{(item) => (
<div class="flex items-center px-3 h-9 text-sm border-b border-gray-100 last:border-0">
<span class="text-gray-400 w-16 text-xs">#{item.index + 1}</span>
<span class="text-gray-700">Item {item.index + 1}</span>
</div>
)}
</VirtualList>
</div>
);
return content;
}
/** Data section with calendar, table, and virtual list components. */
export function DataSection() {
const content = (
<>
<ComponentDemo name="Calendar" description="Navigable month grid for selecting dates with keyboard and locale support">
<CalendarDemo />
</ComponentDemo>
<ComponentDemo name="DataTable" description="Sortable, filterable table with pagination and row selection">
<DataTableDemo />
</ComponentDemo>
<ComponentDemo name="VirtualList" description="Windowed scroll container that renders only visible items for large datasets">
<VirtualListDemo />
</ComponentDemo>
</>
);
return content;
}

View File

@ -0,0 +1,116 @@
import { Alert } from "pettyui/alert";
import { Toast, toast } from "pettyui/toast";
import { Progress } from "pettyui/progress";
import { Meter } from "pettyui/meter";
import { ComponentDemo } from "../component-demo";
const btnBase = "px-3 py-1.5 text-sm font-medium rounded border";
/** Alert demo showing info, success, and error variants. */
function AlertDemo() {
const content = (
<div class="flex flex-col gap-2 w-full max-w-md">
<Alert class="flex items-start gap-2 p-3 rounded border border-blue-200 bg-blue-50 text-blue-800 text-sm">
<span class="font-semibold">Info:</span> Your session will expire in 5 minutes.
</Alert>
<Alert class="flex items-start gap-2 p-3 rounded border border-green-200 bg-green-50 text-green-800 text-sm">
<span class="font-semibold">Success:</span> Your changes have been saved.
</Alert>
<Alert class="flex items-start gap-2 p-3 rounded border border-red-200 bg-red-50 text-red-800 text-sm">
<span class="font-semibold">Error:</span> Failed to connect to the server.
</Alert>
</div>
);
return content;
}
/** Toast demo with imperative API triggering different toast types. */
function ToastDemo() {
const content = (
<div class="flex flex-col gap-3">
<div class="flex gap-2 flex-wrap">
<button type="button" class={`${btnBase} border-gray-300 hover:bg-gray-50`} onClick={() => toast("Action completed")}>Default</button>
<button type="button" class={`${btnBase} border-green-300 text-green-700 hover:bg-green-50`} onClick={() => toast.success("Saved successfully!")}>Success</button>
<button type="button" class={`${btnBase} border-red-300 text-red-700 hover:bg-red-50`} onClick={() => toast.error("Something went wrong")}>Error</button>
<button type="button" class={`${btnBase} border-blue-300 text-blue-700 hover:bg-blue-50`} onClick={() => toast.loading("Loading...")}>Loading</button>
</div>
<Toast.Region class="fixed bottom-4 right-4 flex flex-col gap-2 z-50 w-72" />
</div>
);
return content;
}
/** Progress demo showing a determinate bar at 60%. */
function ProgressDemo() {
const content = (
<div class="w-full max-w-md space-y-3">
<div class="space-y-1">
<div class="flex justify-between text-xs text-gray-500 mb-1">
<span>Uploading file...</span>
<span>60%</span>
</div>
<div class="h-2 bg-gray-200 rounded-full overflow-hidden">
<Progress value={60} class="h-full bg-indigo-600 rounded-full" style={{ width: "60%" }} />
</div>
</div>
<div class="space-y-1">
<div class="flex justify-between text-xs text-gray-500 mb-1">
<span>Processing...</span>
<span>indeterminate</span>
</div>
<div class="h-2 bg-gray-200 rounded-full overflow-hidden">
<Progress class="h-full bg-indigo-400 rounded-full w-1/3 animate-pulse" />
</div>
</div>
</div>
);
return content;
}
/** Meter demo showing a scalar measurement at 75%. */
function MeterDemo() {
const content = (
<div class="w-full max-w-md space-y-3">
<div class="space-y-1">
<div class="flex justify-between text-xs text-gray-500 mb-1">
<span>Disk usage</span>
<span>75 / 100 GB</span>
</div>
<div class="h-3 bg-gray-200 rounded-full overflow-hidden">
<Meter value={75} class="h-full bg-amber-500 rounded-full" style={{ width: "75%" }} />
</div>
</div>
<div class="space-y-1">
<div class="flex justify-between text-xs text-gray-500 mb-1">
<span>Password strength</span>
<span>Strong</span>
</div>
<div class="h-3 bg-gray-200 rounded-full overflow-hidden">
<Meter value={90} class="h-full bg-green-500 rounded-full" style={{ width: "90%" }} />
</div>
</div>
</div>
);
return content;
}
/** Feedback and Status section with notification and indicator components. */
export function FeedbackStatusSection() {
const content = (
<>
<ComponentDemo name="Alert" description="Announces important messages to assistive technology via role=alert">
<AlertDemo />
</ComponentDemo>
<ComponentDemo name="Toast" description="Ephemeral notifications triggered imperatively with toast() API">
<ToastDemo />
</ComponentDemo>
<ComponentDemo name="Progress" description="Tracks completion of a task with determinate or indeterminate states">
<ProgressDemo />
</ComponentDemo>
<ComponentDemo name="Meter" description="Displays a scalar measurement within a known range such as disk usage">
<MeterDemo />
</ComponentDemo>
</>
);
return content;
}

View File

@ -0,0 +1,56 @@
import { Form } from "pettyui/form";
import { DatePicker } from "pettyui/date-picker";
import { ComponentDemo } from "../component-demo";
/** Form demo with validation and error display. */
function FormDemo() {
const content = (
<Form
onSubmit={(data) => { alert(`Submitted: ${JSON.stringify(data)}`); }}
class="flex flex-col gap-4 max-w-sm"
>
<Form.Field name="name" class="flex flex-col gap-1">
<Form.Label class="text-sm font-medium text-gray-700">Name</Form.Label>
<Form.Control type="text" placeholder="Your name" class="border border-gray-300 rounded px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" />
<Form.Description class="text-xs text-gray-400">Required field</Form.Description>
<Form.ErrorMessage class="text-xs text-red-500" />
</Form.Field>
<Form.Field name="email" class="flex flex-col gap-1">
<Form.Label class="text-sm font-medium text-gray-700">Email</Form.Label>
<Form.Control type="email" placeholder="you@example.com" class="border border-gray-300 rounded px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" />
<Form.ErrorMessage class="text-xs text-red-500" />
</Form.Field>
<Form.Submit class="px-4 py-2 text-sm font-medium rounded bg-indigo-600 text-white hover:bg-indigo-700 self-start">Submit</Form.Submit>
</Form>
);
return content;
}
/** DatePicker demo with calendar dropdown. */
function DatePickerDemo() {
const content = (
<DatePicker class="relative w-48">
<div class="flex items-center border border-gray-300 rounded">
<DatePicker.Input placeholder="Pick a date" class="w-full px-3 py-1.5 text-sm rounded focus:outline-none" />
<DatePicker.Trigger class="px-2 text-gray-400 hover:text-gray-600">📅</DatePicker.Trigger>
</div>
<DatePicker.Content class="absolute top-full mt-1 border border-gray-200 rounded bg-white shadow-lg z-10 p-2" />
</DatePicker>
);
return content;
}
/** Inputs Advanced section with complex input components. */
export function InputsAdvancedSection() {
const content = (
<>
<ComponentDemo name="Form" description="Form with Zod v4 schema validation, field-level error display, and accessible aria linking">
<FormDemo />
</ComponentDemo>
<ComponentDemo name="DatePicker" description="Date input with dropdown calendar for selecting dates">
<DatePickerDemo />
</ComponentDemo>
</>
);
return content;
}

View File

@ -0,0 +1,116 @@
import { Button } from "pettyui/button";
import { TextField } from "pettyui/text-field";
import { NumberField } from "pettyui/number-field";
import { Checkbox } from "pettyui/checkbox";
import { Switch } from "pettyui/switch";
import { Toggle } from "pettyui/toggle";
import { ComponentDemo } from "../component-demo";
/** Button demo with primary, secondary, ghost, disabled variants. */
const ButtonDemo = () => (
<div class="flex items-center gap-3">
<Button class="px-4 py-2 text-sm font-medium rounded bg-indigo-600 text-white hover:bg-indigo-700">Primary</Button>
<Button class="px-4 py-2 text-sm font-medium rounded border border-gray-300 hover:bg-gray-50">Secondary</Button>
<Button class="px-4 py-2 text-sm font-medium rounded text-indigo-600 hover:bg-indigo-50">Ghost</Button>
<Button disabled class="px-4 py-2 text-sm font-medium rounded bg-gray-100 text-gray-400 cursor-not-allowed">Disabled</Button>
</div>
);
/** TextField demo with label, description, and error state. */
const TextFieldDemo = () => (
<div class="flex gap-6">
<TextField class="flex flex-col gap-1">
<TextField.Label class="text-sm font-medium text-gray-700">Name</TextField.Label>
<TextField.Input placeholder="Enter your name" class="border border-gray-300 rounded px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" />
<TextField.Description class="text-xs text-gray-400">Your full name</TextField.Description>
</TextField>
<TextField validationState="invalid" class="flex flex-col gap-1">
<TextField.Label class="text-sm font-medium text-red-600">Email</TextField.Label>
<TextField.Input value="not-an-email" class="border border-red-300 rounded px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-red-500" />
<TextField.ErrorMessage class="text-xs text-red-500">Invalid email address</TextField.ErrorMessage>
</TextField>
</div>
);
/** NumberField demo with increment and decrement buttons. */
const NumberFieldDemo = () => (
<NumberField defaultValue={5} min={0} max={100} class="flex flex-col gap-1">
<NumberField.Label class="text-sm font-medium text-gray-700">Quantity</NumberField.Label>
<div class="flex items-center gap-1">
<NumberField.DecrementTrigger class="w-8 h-8 flex items-center justify-center border border-gray-300 rounded hover:bg-gray-50 text-sm">-</NumberField.DecrementTrigger>
<NumberField.Input class="w-16 text-center border border-gray-300 rounded px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" />
<NumberField.IncrementTrigger class="w-8 h-8 flex items-center justify-center border border-gray-300 rounded hover:bg-gray-50 text-sm">+</NumberField.IncrementTrigger>
</div>
</NumberField>
);
/** Checkbox demo with checked and unchecked states. */
const CheckboxDemo = () => (
<div class="flex flex-col gap-3">
<Checkbox class="flex items-center gap-2 cursor-pointer">
{(state) => (
<>
<div class={`w-4 h-4 border rounded flex items-center justify-center text-xs ${state.checked() ? "bg-indigo-600 border-indigo-600 text-white" : "border-gray-300"}`}>
{state.checked() ? "✓" : ""}
</div>
<span class="text-sm text-gray-700">Accept terms and conditions</span>
</>
)}
</Checkbox>
<Checkbox defaultChecked class="flex items-center gap-2 cursor-pointer">
{(state) => (
<>
<div class={`w-4 h-4 border rounded flex items-center justify-center text-xs ${state.checked() ? "bg-indigo-600 border-indigo-600 text-white" : "border-gray-300"}`}>
{state.checked() ? "✓" : ""}
</div>
<span class="text-sm text-gray-700">Subscribe to newsletter</span>
</>
)}
</Checkbox>
</div>
);
/** Switch demo with on/off toggle. */
const SwitchDemo = () => (
<Switch class="flex items-center gap-3 cursor-pointer">
{(state) => (
<>
<div class={`w-10 h-6 rounded-full relative transition-colors ${state.checked() ? "bg-indigo-600" : "bg-gray-300"}`}>
<div class={`absolute top-0.5 w-5 h-5 rounded-full bg-white shadow transition-transform ${state.checked() ? "translate-x-4" : "translate-x-0.5"}`} />
</div>
<span class="text-sm text-gray-700">{state.checked() ? "On" : "Off"}</span>
</>
)}
</Switch>
);
/** Toggle demo with pressed/unpressed states. */
const ToggleDemo = () => (
<Toggle class="px-3 py-1.5 text-sm border rounded transition-colors data-[pressed]:bg-indigo-100 data-[pressed]:text-indigo-700 data-[pressed]:border-indigo-300 border-gray-300 hover:bg-gray-50">
Bold
</Toggle>
);
/** Inputs Basic section with fundamental input components. */
export const InputsBasicSection = () => (
<>
<ComponentDemo name="Button" description="Clickable element that triggers an action">
<ButtonDemo />
</ComponentDemo>
<ComponentDemo name="TextField" description="Text input field with label, description, and error message support">
<TextFieldDemo />
</ComponentDemo>
<ComponentDemo name="NumberField" description="Numeric input with increment/decrement buttons and keyboard support">
<NumberFieldDemo />
</ComponentDemo>
<ComponentDemo name="Checkbox" description="Toggle control for boolean input, supports indeterminate state">
<CheckboxDemo />
</ComponentDemo>
<ComponentDemo name="Switch" description="Toggle control for on/off states, visually distinct from checkbox">
<SwitchDemo />
</ComponentDemo>
<ComponentDemo name="Toggle" description="Two-state button that can be toggled on or off">
<ToggleDemo />
</ComponentDemo>
</>
);

View File

@ -0,0 +1,146 @@
import { createSignal, For } from "solid-js";
import { RadioGroup } from "pettyui/radio-group";
import { ToggleGroup } from "pettyui/toggle-group";
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "pettyui/select";
import { Combobox, ComboboxInput, ComboboxTrigger, ComboboxContent, ComboboxItem } from "pettyui/combobox";
import { Listbox } from "pettyui/listbox";
import { Slider } from "pettyui/slider";
import { ComponentDemo } from "../component-demo";
const fruits = ["Apple", "Banana", "Cherry", "Date", "Elderberry"];
/** RadioGroup demo with 3 mutually exclusive options. */
function RadioGroupDemo() {
const content = (
<RadioGroup defaultValue="option-b" class="flex flex-col gap-2">
<For each={["option-a", "option-b", "option-c"]}>
{(value) => (
<RadioGroup.Item value={value} class="flex items-center gap-2 cursor-pointer">
{(itemState) => (
<>
<div class={`w-4 h-4 rounded-full border-2 flex items-center justify-center ${itemState.checked() ? "border-indigo-600" : "border-gray-300"}`}>
{itemState.checked() && <div class="w-2 h-2 rounded-full bg-indigo-600" />}
</div>
<span class="text-sm text-gray-700">Option {value.split("-")[1]?.toUpperCase()}</span>
</>
)}
</RadioGroup.Item>
)}
</For>
</RadioGroup>
);
return content;
}
/** ToggleGroup demo with single selection alignment. */
function ToggleGroupDemo() {
const content = (
<ToggleGroup defaultValue="center" class="flex gap-1">
<ToggleGroup.Item value="left" class="px-3 py-1.5 text-sm border rounded-l transition-colors data-[pressed]:bg-indigo-100 data-[pressed]:text-indigo-700 data-[pressed]:border-indigo-300 border-gray-300">Left</ToggleGroup.Item>
<ToggleGroup.Item value="center" class="px-3 py-1.5 text-sm border-y transition-colors data-[pressed]:bg-indigo-100 data-[pressed]:text-indigo-700 data-[pressed]:border-indigo-300 border-gray-300">Center</ToggleGroup.Item>
<ToggleGroup.Item value="right" class="px-3 py-1.5 text-sm border rounded-r transition-colors data-[pressed]:bg-indigo-100 data-[pressed]:text-indigo-700 data-[pressed]:border-indigo-300 border-gray-300">Right</ToggleGroup.Item>
</ToggleGroup>
);
return content;
}
/** Select demo with fruit dropdown. */
function SelectDemo() {
const content = (
<Select options={fruits} placeholder="Pick a fruit" class="relative w-48">
<SelectTrigger class="w-full flex items-center justify-between border border-gray-300 rounded px-3 py-1.5 text-sm hover:bg-gray-50">
<SelectValue class="text-gray-700" placeholder="Pick a fruit" />
<span class="text-gray-400"></span>
</SelectTrigger>
<SelectContent class="absolute top-full mt-1 w-full border border-gray-200 rounded bg-white shadow-lg z-10">
<For each={fruits}>
{(fruit) => (
<SelectItem value={fruit} class="px-3 py-1.5 text-sm hover:bg-indigo-50 cursor-pointer data-[highlighted]:bg-indigo-50">{fruit}</SelectItem>
)}
</For>
</SelectContent>
</Select>
);
return content;
}
/** Combobox demo with searchable fruit list. */
function ComboboxDemo() {
const content = (
<Combobox options={fruits} placeholder="Search fruits..." class="relative w-48">
<div class="flex items-center border border-gray-300 rounded">
<ComboboxInput class="w-full px-3 py-1.5 text-sm rounded focus:outline-none" placeholder="Search fruits..." />
<ComboboxTrigger class="px-2 text-gray-400"></ComboboxTrigger>
</div>
<ComboboxContent class="absolute top-full mt-1 w-full border border-gray-200 rounded bg-white shadow-lg z-10">
<For each={fruits}>
{(fruit) => (
<ComboboxItem value={fruit} class="px-3 py-1.5 text-sm hover:bg-indigo-50 cursor-pointer data-[highlighted]:bg-indigo-50">{fruit}</ComboboxItem>
)}
</For>
</ComboboxContent>
</Combobox>
);
return content;
}
/** Listbox demo with inline selectable items. */
function ListboxDemo() {
const content = (
<Listbox class="w-48 border border-gray-200 rounded overflow-hidden">
<For each={fruits}>
{(fruit) => (
<Listbox.Item value={fruit} class="px-3 py-1.5 text-sm hover:bg-indigo-50 cursor-pointer data-[highlighted]:bg-indigo-50 data-[selected]:bg-indigo-100 data-[selected]:text-indigo-700">{fruit}</Listbox.Item>
)}
</For>
</Listbox>
);
return content;
}
/** Slider demo with value display. */
function SliderDemo() {
const [value, setValue] = createSignal(40);
const content = (
<div class="w-64">
<div class="flex justify-between text-sm text-gray-500 mb-2">
<span>Volume</span>
<span>{value()}%</span>
</div>
<Slider value={value()} onChange={setValue} min={0} max={100} class="relative h-5 flex items-center">
<Slider.Track class="h-1.5 w-full bg-gray-200 rounded-full relative">
<Slider.Range class="absolute h-full bg-indigo-600 rounded-full" />
</Slider.Track>
<Slider.Thumb class="absolute w-4 h-4 bg-white border-2 border-indigo-600 rounded-full shadow -translate-x-1/2 cursor-pointer" />
</Slider>
</div>
);
return content;
}
/** Inputs Selection section with choice and range components. */
export function InputsSelectionSection() {
const content = (
<>
<ComponentDemo name="RadioGroup" description="Group of mutually exclusive options where only one can be selected">
<RadioGroupDemo />
</ComponentDemo>
<ComponentDemo name="ToggleGroup" description="Group of toggle buttons where one or multiple can be selected">
<ToggleGroupDemo />
</ComponentDemo>
<ComponentDemo name="Select" description="Dropdown for selecting a single option from a list">
<SelectDemo />
</ComponentDemo>
<ComponentDemo name="Combobox" description="Searchable select that filters options as the user types">
<ComboboxDemo />
</ComponentDemo>
<ComponentDemo name="Listbox" description="Inline list of selectable options with keyboard navigation">
<ListboxDemo />
</ComponentDemo>
<ComponentDemo name="Slider" description="Range input for selecting numeric values by dragging a thumb along a track">
<SliderDemo />
</ComponentDemo>
</>
);
return content;
}

View File

@ -0,0 +1,107 @@
import { Avatar } from "pettyui/avatar";
import { Badge } from "pettyui/badge";
import { Card } from "pettyui/card";
import { Image } from "pettyui/image";
import { Separator } from "pettyui/separator";
import { Skeleton } from "pettyui/skeleton";
import { ComponentDemo } from "../component-demo";
/** Avatar demo with image and fallback variants. */
const AvatarDemo = () => (
<div class="flex items-center gap-4">
<Avatar class="w-10 h-10 rounded-full overflow-hidden">
<Avatar.Image src="https://i.pravatar.cc/80?img=3" alt="User avatar" class="w-full h-full object-cover" />
<Avatar.Fallback class="w-full h-full bg-indigo-100 text-indigo-700 flex items-center justify-center font-medium">MB</Avatar.Fallback>
</Avatar>
<Avatar class="w-10 h-10 rounded-full overflow-hidden">
<Avatar.Image src="/broken-url.jpg" alt="Broken" class="w-full h-full object-cover" />
<Avatar.Fallback class="w-full h-full bg-emerald-100 text-emerald-700 flex items-center justify-center font-medium">JD</Avatar.Fallback>
</Avatar>
</div>
);
/** Badge demo with multiple variants. */
const BadgeDemo = () => (
<div class="flex items-center gap-2">
<Badge class="px-2 py-0.5 text-xs font-medium rounded-full bg-gray-100 text-gray-700">Default</Badge>
<Badge class="px-2 py-0.5 text-xs font-medium rounded-full bg-blue-100 text-blue-700">Info</Badge>
<Badge class="px-2 py-0.5 text-xs font-medium rounded-full bg-green-100 text-green-700">Success</Badge>
<Badge class="px-2 py-0.5 text-xs font-medium rounded-full bg-yellow-100 text-yellow-700">Warning</Badge>
<Badge class="px-2 py-0.5 text-xs font-medium rounded-full bg-red-100 text-red-700">Error</Badge>
</div>
);
/** Card demo with header, content, and footer. */
const CardDemo = () => (
<Card class="border border-gray-200 rounded-lg shadow-sm max-w-sm">
<Card.Header class="px-4 pt-4">
<Card.Title class="text-lg font-semibold">Card Title</Card.Title>
<Card.Description class="text-sm text-gray-500">A short description of this card.</Card.Description>
</Card.Header>
<Card.Content class="px-4 py-3">
<p class="text-sm text-gray-700">Card body content goes here.</p>
</Card.Content>
<Card.Footer class="px-4 pb-4 flex justify-end gap-2">
<button type="button" class="px-3 py-1.5 text-sm rounded border border-gray-300 hover:bg-gray-50">Cancel</button>
<button type="button" class="px-3 py-1.5 text-sm rounded bg-indigo-600 text-white hover:bg-indigo-700">Save</button>
</Card.Footer>
</Card>
);
/** Image demo with working and broken sources. */
const ImageDemo = () => (
<div class="flex items-center gap-4">
<Image class="w-24 h-24 rounded-lg overflow-hidden">
<Image.Img src="https://picsum.photos/seed/pettyui/200" alt="Sample" class="w-full h-full object-cover" />
<Image.Fallback class="w-full h-full bg-gray-200 flex items-center justify-center text-gray-400 text-xs">Loading...</Image.Fallback>
</Image>
<Image class="w-24 h-24 rounded-lg overflow-hidden">
<Image.Img src="/broken.jpg" alt="Broken" class="w-full h-full object-cover" />
<Image.Fallback class="w-full h-full bg-gray-200 flex items-center justify-center text-gray-400 text-xs">No image</Image.Fallback>
</Image>
</div>
);
/** Separator demo with horizontal divider. */
const SeparatorDemo = () => (
<div>
<p class="text-sm text-gray-700 mb-2">Above the separator</p>
<Separator class="h-px bg-gray-200 my-3" />
<p class="text-sm text-gray-700">Below the separator</p>
</div>
);
/** Skeleton demo with different shapes. */
const SkeletonDemo = () => (
<div class="flex items-center gap-4">
<Skeleton class="w-10 h-10 rounded-full bg-gray-200 animate-pulse" />
<div class="space-y-2">
<Skeleton class="h-4 w-32 rounded bg-gray-200 animate-pulse" />
<Skeleton class="h-3 w-48 rounded bg-gray-200 animate-pulse" />
</div>
</div>
);
/** Layout and Display section with all display components. */
export const LayoutDisplaySection = () => (
<>
<ComponentDemo name="Avatar" description="User profile image with fallback to initials or icon when image fails to load">
<AvatarDemo />
</ComponentDemo>
<ComponentDemo name="Badge" description="Small status indicator label, typically used for counts, tags, or status">
<BadgeDemo />
</ComponentDemo>
<ComponentDemo name="Card" description="Grouped content container with header, body, and footer sections">
<CardDemo />
</ComponentDemo>
<ComponentDemo name="Image" description="Image element with fallback placeholder when the source fails to load">
<ImageDemo />
</ComponentDemo>
<ComponentDemo name="Separator" description="Visual divider between content sections or menu items">
<SeparatorDemo />
</ComponentDemo>
<ComponentDemo name="Skeleton" description="Placeholder loading indicator that mimics the shape of content being loaded">
<SkeletonDemo />
</ComponentDemo>
</>
);

View File

@ -0,0 +1,201 @@
import { For } from "solid-js";
import { Link } from "pettyui/link";
import { Breadcrumbs } from "pettyui/breadcrumbs";
import { Tabs } from "pettyui/tabs";
import { Accordion } from "pettyui/accordion";
import { Collapsible } from "pettyui/collapsible";
import { Pagination } from "pettyui/pagination";
import { NavigationMenu } from "pettyui/navigation-menu";
import { Wizard } from "pettyui/wizard";
import { ComponentDemo } from "../component-demo";
/** Link demo with internal, external, and disabled variants. */
function LinkDemo() {
const content = (
<div class="flex items-center gap-4">
<Link href="#" class="text-sm text-indigo-600 hover:underline">Internal link</Link>
<Link href="https://example.com" external class="text-sm text-indigo-600 hover:underline">External link </Link>
<Link href="#" disabled class="text-sm text-gray-400 cursor-not-allowed">Disabled link</Link>
</div>
);
return content;
}
/** Breadcrumbs demo with 3-level trail. */
function BreadcrumbsDemo() {
const content = (
<Breadcrumbs class="flex items-center gap-1 text-sm">
<Breadcrumbs.Item>
<Breadcrumbs.Link href="#" class="text-gray-500 hover:text-gray-700">Home</Breadcrumbs.Link>
<Breadcrumbs.Separator class="text-gray-300 mx-1">/</Breadcrumbs.Separator>
</Breadcrumbs.Item>
<Breadcrumbs.Item>
<Breadcrumbs.Link href="#" class="text-gray-500 hover:text-gray-700">Components</Breadcrumbs.Link>
<Breadcrumbs.Separator class="text-gray-300 mx-1">/</Breadcrumbs.Separator>
</Breadcrumbs.Item>
<Breadcrumbs.Item>
<span class="text-gray-900 font-medium">Breadcrumbs</span>
</Breadcrumbs.Item>
</Breadcrumbs>
);
return content;
}
/** Tabs demo with 3 content panels. */
function TabsDemo() {
const content = (
<Tabs defaultValue="tab-1" class="w-full">
<Tabs.List class="flex border-b border-gray-200">
<Tabs.Tab value="tab-1" class="px-4 py-2 text-sm border-b-2 -mb-px transition-colors data-[selected]:border-indigo-600 data-[selected]:text-indigo-600 border-transparent text-gray-500 hover:text-gray-700">Account</Tabs.Tab>
<Tabs.Tab value="tab-2" class="px-4 py-2 text-sm border-b-2 -mb-px transition-colors data-[selected]:border-indigo-600 data-[selected]:text-indigo-600 border-transparent text-gray-500 hover:text-gray-700">Password</Tabs.Tab>
<Tabs.Tab value="tab-3" class="px-4 py-2 text-sm border-b-2 -mb-px transition-colors data-[selected]:border-indigo-600 data-[selected]:text-indigo-600 border-transparent text-gray-500 hover:text-gray-700">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="tab-1" class="p-4 text-sm text-gray-700">Account settings content</Tabs.Panel>
<Tabs.Panel value="tab-2" class="p-4 text-sm text-gray-700">Password settings content</Tabs.Panel>
<Tabs.Panel value="tab-3" class="p-4 text-sm text-gray-700">General settings content</Tabs.Panel>
</Tabs>
);
return content;
}
const accordionItems = [
{ value: "item-1", title: "What is PettyUI?", content: "A headless SolidJS component library." },
{ value: "item-2", title: "Is it accessible?", content: "Yes, all components follow WAI-ARIA patterns." },
{ value: "item-3", title: "Can I style it?", content: "Absolutely. It's headless — bring your own styles." },
];
/** Accordion demo with 3 collapsible sections. */
function AccordionDemo() {
const content = (
<Accordion class="w-full max-w-md border border-gray-200 rounded divide-y divide-gray-200">
<For each={accordionItems}>
{(item) => (
<Accordion.Item value={item.value}>
<Accordion.Header>
<Accordion.Trigger class="w-full flex items-center justify-between px-4 py-3 text-sm font-medium text-gray-700 hover:bg-gray-50">
{item.title}
<span class="text-gray-400"></span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="px-4 pb-3 text-sm text-gray-500">{item.content}</Accordion.Content>
</Accordion.Item>
)}
</For>
</Accordion>
);
return content;
}
/** Collapsible demo with expand/collapse toggle. */
function CollapsibleDemo() {
const content = (
<Collapsible class="w-full max-w-md">
<Collapsible.Trigger class="flex items-center gap-2 text-sm font-medium text-gray-700 hover:text-gray-900">
<span></span> Show more details
</Collapsible.Trigger>
<Collapsible.Content class="mt-2 p-3 bg-gray-50 rounded text-sm text-gray-600">
Here are the additional details that were hidden. Click again to collapse.
</Collapsible.Content>
</Collapsible>
);
return content;
}
/** Pagination demo with page navigation. */
function PaginationDemo() {
const content = <Pagination count={50} itemsPerPage={10} class="flex items-center gap-1" />;
return content;
}
/** NavigationMenu demo with dropdown submenu. */
function NavigationMenuDemo() {
const content = (
<NavigationMenu class="relative">
<NavigationMenu.List class="flex items-center gap-1">
<NavigationMenu.Item>
<NavigationMenu.Link href="#" class="px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded">Home</NavigationMenu.Link>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Trigger class="px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded">Products </NavigationMenu.Trigger>
<NavigationMenu.Content class="absolute top-full left-0 mt-1 p-2 border border-gray-200 rounded bg-white shadow-lg z-10 min-w-48">
<NavigationMenu.Link href="#" class="block px-3 py-1.5 text-sm text-gray-700 hover:bg-indigo-50 rounded">Widget A</NavigationMenu.Link>
<NavigationMenu.Link href="#" class="block px-3 py-1.5 text-sm text-gray-700 hover:bg-indigo-50 rounded">Widget B</NavigationMenu.Link>
</NavigationMenu.Content>
</NavigationMenu.Item>
<NavigationMenu.Item>
<NavigationMenu.Link href="#" class="px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded">About</NavigationMenu.Link>
</NavigationMenu.Item>
</NavigationMenu.List>
</NavigationMenu>
);
return content;
}
/** Wizard step indicators helper for the wizard demo. */
function WizardSteps() {
const content = (
<Wizard.StepList class="flex items-center gap-2 mb-4">
<For each={["Details", "Review", "Confirm"]}>
{(label, i) => (
<Wizard.Step index={i()} class="flex items-center gap-2">
<Wizard.StepTrigger class="w-7 h-7 rounded-full border-2 flex items-center justify-center text-xs font-medium data-[active]:border-indigo-600 data-[active]:text-indigo-600 data-[completed]:bg-indigo-600 data-[completed]:text-white data-[completed]:border-indigo-600 border-gray-300 text-gray-400">
{i() + 1}
</Wizard.StepTrigger>
<span class="text-sm text-gray-600">{label}</span>
</Wizard.Step>
)}
</For>
</Wizard.StepList>
);
return content;
}
/** Wizard demo with 3-step flow. */
function WizardDemo() {
const content = (
<Wizard class="w-full max-w-md">
<WizardSteps />
<Wizard.StepContent index={0} class="p-4 border border-gray-200 rounded mb-4 text-sm text-gray-700">Step 1: Enter your details here.</Wizard.StepContent>
<Wizard.StepContent index={1} class="p-4 border border-gray-200 rounded mb-4 text-sm text-gray-700">Step 2: Review your information.</Wizard.StepContent>
<Wizard.StepContent index={2} class="p-4 border border-gray-200 rounded mb-4 text-sm text-gray-700">Step 3: Confirm and submit.</Wizard.StepContent>
<div class="flex gap-2">
<Wizard.Prev class="px-3 py-1.5 text-sm border border-gray-300 rounded hover:bg-gray-50 disabled:opacity-40">Previous</Wizard.Prev>
<Wizard.Next class="px-3 py-1.5 text-sm bg-indigo-600 text-white rounded hover:bg-indigo-700 disabled:opacity-40">Next</Wizard.Next>
</div>
</Wizard>
);
return content;
}
/** Navigation section with all navigation components. */
export function NavigationSection() {
const content = (
<>
<ComponentDemo name="Link" description="Navigation anchor element with external link and disabled support">
<LinkDemo />
</ComponentDemo>
<ComponentDemo name="Breadcrumbs" description="Navigation trail showing the current page location within a hierarchy">
<BreadcrumbsDemo />
</ComponentDemo>
<ComponentDemo name="Tabs" description="Tabbed interface for switching between different views or sections of content">
<TabsDemo />
</ComponentDemo>
<ComponentDemo name="Accordion" description="Vertically stacked sections that expand/collapse to show content">
<AccordionDemo />
</ComponentDemo>
<ComponentDemo name="Collapsible" description="Content section that can be expanded or collapsed with a trigger">
<CollapsibleDemo />
</ComponentDemo>
<ComponentDemo name="Pagination" description="Navigation for paginated content with page numbers and controls">
<PaginationDemo />
</ComponentDemo>
<ComponentDemo name="NavigationMenu" description="Horizontal navigation bar with dropdown submenus">
<NavigationMenuDemo />
</ComponentDemo>
<ComponentDemo name="Wizard" description="Multi-step flow with step indicators and navigation">
<WizardDemo />
</ComponentDemo>
</>
);
return content;
}

View File

@ -0,0 +1,209 @@
import { createSignal } from "solid-js";
import { Dialog } from "pettyui/dialog";
import { AlertDialog } from "pettyui/alert-dialog";
import { Drawer } from "pettyui/drawer";
import { Popover } from "pettyui/popover";
import { TooltipRoot, TooltipTrigger, TooltipContent } from "pettyui/tooltip";
import { HoverCardRoot, HoverCardTrigger, HoverCardContent } from "pettyui/hover-card";
import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from "pettyui/dropdown-menu";
import { ContextMenuRoot, ContextMenuTrigger, ContextMenuContent, ContextMenuItem } from "pettyui/context-menu";
import { CommandPalette } from "pettyui/command-palette";
import { ComponentDemo } from "../component-demo";
const triggerBtn = "px-3 py-1.5 text-sm font-medium rounded border border-gray-300 hover:bg-gray-50";
const overlayBg = "fixed inset-0 bg-black/40 z-40";
const panelBase = "bg-white rounded-lg shadow-xl p-6 z-50";
const modalPos = "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96";
/** Dialog demo with modal overlay. */
function DialogDemo() {
const content = (
<Dialog>
<Dialog.Trigger class={triggerBtn}>Open Dialog</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay class={overlayBg} />
<Dialog.Content class={`${modalPos} ${panelBase}`}>
<Dialog.Title class="text-lg font-semibold mb-1">Dialog Title</Dialog.Title>
<Dialog.Description class="text-sm text-gray-500 mb-4">
This is a modal dialog. Press Escape or click Close to dismiss.
</Dialog.Description>
<Dialog.Close class="px-3 py-1.5 text-sm bg-indigo-600 text-white rounded hover:bg-indigo-700">Close</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog>
);
return content;
}
/** AlertDialog demo with confirm/cancel actions. */
function AlertDialogDemo() {
const content = (
<AlertDialog>
<AlertDialog.Trigger class={triggerBtn}>Delete Item</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Overlay class={overlayBg} />
<AlertDialog.Content class={`${modalPos} ${panelBase}`}>
<AlertDialog.Title class="text-lg font-semibold mb-1">Are you sure?</AlertDialog.Title>
<AlertDialog.Description class="text-sm text-gray-500 mb-4">This action cannot be undone.</AlertDialog.Description>
<div class="flex justify-end gap-2">
<AlertDialog.Cancel class="px-3 py-1.5 text-sm border border-gray-300 rounded hover:bg-gray-50">Cancel</AlertDialog.Cancel>
<AlertDialog.Action class="px-3 py-1.5 text-sm bg-red-600 text-white rounded hover:bg-red-700">Delete</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog>
);
return content;
}
/** Drawer demo sliding in from the right edge. */
function DrawerDemo() {
const content = (
<Drawer>
<Drawer.Trigger class={triggerBtn}>Open Drawer</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay class={overlayBg} />
<Drawer.Content class="fixed top-0 right-0 h-full w-80 bg-white shadow-xl z-50 p-6">
<Drawer.Title class="text-lg font-semibold mb-1">Drawer Panel</Drawer.Title>
<Drawer.Description class="text-sm text-gray-500 mb-4">This drawer slides in from the right.</Drawer.Description>
<Drawer.Close class="px-3 py-1.5 text-sm border border-gray-300 rounded hover:bg-gray-50">Close</Drawer.Close>
</Drawer.Content>
</Drawer.Portal>
</Drawer>
);
return content;
}
/** Popover demo with floating content panel. */
function PopoverDemo() {
const content = (
<Popover>
<Popover.Trigger class={triggerBtn}>Toggle Popover</Popover.Trigger>
<Popover.Portal>
<Popover.Content class="bg-white border border-gray-200 rounded-lg shadow-lg p-4 z-50 w-64">
<p class="text-sm text-gray-700 mb-2">This is a popover with interactive content.</p>
<Popover.Close class="text-xs text-indigo-600 hover:underline">Dismiss</Popover.Close>
</Popover.Content>
</Popover.Portal>
</Popover>
);
return content;
}
/** Tooltip demo that appears on hover. */
function TooltipDemo() {
const content = (
<TooltipRoot>
<TooltipTrigger class={triggerBtn}>Hover me</TooltipTrigger>
<TooltipContent class="bg-gray-900 text-white text-xs rounded px-2 py-1 z-50 shadow">
This is a tooltip
</TooltipContent>
</TooltipRoot>
);
return content;
}
/** HoverCard demo with rich preview on hover. */
function HoverCardDemo() {
const content = (
<HoverCardRoot>
<HoverCardTrigger class="text-sm text-indigo-600 underline cursor-pointer">@pettyui</HoverCardTrigger>
<HoverCardContent class="bg-white border border-gray-200 rounded-lg shadow-lg p-4 z-50 w-64">
<p class="font-medium text-sm">PettyUI</p>
<p class="text-xs text-gray-500 mt-1">AI-native headless UI for SolidJS. 44 components.</p>
</HoverCardContent>
</HoverCardRoot>
);
return content;
}
/** DropdownMenu demo with action items. */
function DropdownMenuDemo() {
const content = (
<DropdownMenuRoot>
<DropdownMenuTrigger class={triggerBtn}>Actions </DropdownMenuTrigger>
<DropdownMenuContent class="border border-gray-200 rounded bg-white shadow-lg z-50 min-w-40 py-1">
<DropdownMenuItem class="px-3 py-1.5 text-sm hover:bg-indigo-50 cursor-pointer">Edit</DropdownMenuItem>
<DropdownMenuItem class="px-3 py-1.5 text-sm hover:bg-indigo-50 cursor-pointer">Duplicate</DropdownMenuItem>
<DropdownMenuSeparator class="h-px bg-gray-200 my-1" />
<DropdownMenuItem class="px-3 py-1.5 text-sm text-red-600 hover:bg-red-50 cursor-pointer">Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenuRoot>
);
return content;
}
/** ContextMenu demo with right-click trigger area. */
function ContextMenuDemo() {
const content = (
<ContextMenuRoot>
<ContextMenuTrigger class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center text-sm text-gray-500">
Right-click here
</ContextMenuTrigger>
<ContextMenuContent class="border border-gray-200 rounded bg-white shadow-lg z-50 min-w-40 py-1">
<ContextMenuItem class="px-3 py-1.5 text-sm hover:bg-indigo-50 cursor-pointer">Copy</ContextMenuItem>
<ContextMenuItem class="px-3 py-1.5 text-sm hover:bg-indigo-50 cursor-pointer">Paste</ContextMenuItem>
<ContextMenuItem class="px-3 py-1.5 text-sm hover:bg-indigo-50 cursor-pointer">Inspect</ContextMenuItem>
</ContextMenuContent>
</ContextMenuRoot>
);
return content;
}
/** CommandPalette demo with searchable commands. */
function CommandPaletteDemo() {
const [open, setOpen] = createSignal(false);
const content = (
<div>
<button type="button" class={triggerBtn} onClick={() => setOpen(true)}>Open Command Palette</button>
<CommandPalette open={open()} onOpenChange={setOpen} class={`${modalPos} bg-white border border-gray-200 rounded-lg shadow-xl z-50`}>
<CommandPalette.Input placeholder="Type a command..." class="w-full px-4 py-3 text-sm border-b border-gray-200 focus:outline-none" />
<CommandPalette.List class="max-h-60 overflow-y-auto p-2">
<CommandPalette.Group>
<CommandPalette.Item value="new-file" class="px-3 py-1.5 text-sm rounded hover:bg-indigo-50 cursor-pointer">New File</CommandPalette.Item>
<CommandPalette.Item value="open-file" class="px-3 py-1.5 text-sm rounded hover:bg-indigo-50 cursor-pointer">Open File</CommandPalette.Item>
<CommandPalette.Item value="save" class="px-3 py-1.5 text-sm rounded hover:bg-indigo-50 cursor-pointer">Save</CommandPalette.Item>
</CommandPalette.Group>
<CommandPalette.Empty class="px-3 py-4 text-sm text-gray-400 text-center">No results found</CommandPalette.Empty>
</CommandPalette.List>
</CommandPalette>
</div>
);
return content;
}
/** Overlays section showcasing all overlay and modal components. */
export function OverlaysSection() {
const content = (
<>
<ComponentDemo name="Dialog" description="Modal overlay that interrupts the user with important content requiring acknowledgment">
<DialogDemo />
</ComponentDemo>
<ComponentDemo name="AlertDialog" description="Modal dialog for critical confirmations that requires explicit user action to dismiss">
<AlertDialogDemo />
</ComponentDemo>
<ComponentDemo name="Drawer" description="Panel that slides in from the edge of the screen">
<DrawerDemo />
</ComponentDemo>
<ComponentDemo name="Popover" description="Floating content panel anchored to a trigger element">
<PopoverDemo />
</ComponentDemo>
<ComponentDemo name="Tooltip" description="Floating label that appears on hover/focus to describe an element">
<TooltipDemo />
</ComponentDemo>
<ComponentDemo name="HoverCard" description="Card that appears on hover to preview linked content">
<HoverCardDemo />
</ComponentDemo>
<ComponentDemo name="DropdownMenu" description="Menu of actions triggered by a button">
<DropdownMenuDemo />
</ComponentDemo>
<ComponentDemo name="ContextMenu" description="Right-click context menu with keyboard navigation">
<ContextMenuDemo />
</ComponentDemo>
<ComponentDemo name="CommandPalette" description="Search-driven command menu for finding and executing actions">
<CommandPaletteDemo />
</ComponentDemo>
</>
);
return content;
}