Primitives
Menu — settings page
Descriptions put the icon in a tile; counts, badges and a danger item. Controlled with value / onValueChange.
Settings
security
Password, two-step sign-in and active sessions.
Section content goes here. Try ↑ ↓ Home End, and → ← on “Site”.
menu-settings.tsx
"use client";
import { Avatar, Badge, Menu, MenuGroup, MenuItem, MenuSeparator, MenuSub } from "@waveinno/ui";
import { Bell, Building2, CreditCard, KeyRound, LogOut, Receipt, ShieldCheck, UserRound } from "lucide-react";
import * as React from "react";
const SECTIONS: Record<string, string> = {
profile: "Your name, photo and phone number.",
security: "Password, two-step sign-in and active sessions.",
notifications: "What reaches you by SMS, email and push.",
site: "Name, address and opening hours of Gulshan Parking.",
rates: "What a car or bike pays, by the hour and by the month.",
billing: "Your plan, invoices and the card on file.",
keys: "Keys for the gate controllers and your own systems.",
};
export default function SettingsDemo() {
const [section, setSection] = React.useState("security");
return (
<div className="grid gap-5 rounded-card border border-border bg-canvas p-3 md:grid-cols-[280px_1fr]">
<Menu variant="raised" value={section} onValueChange={setSection} aria-label="Settings">
<div className="flex items-center gap-3 px-2.5 pb-2 pt-1.5">
<Avatar name="Karim Ahmed" />
<div className="min-w-0">
<p className="truncate text-[13px] font-semibold text-text">Karim Ahmed</p>
<p className="truncate text-[11.5px] text-muted">Owner · Gulshan Parking Ltd</p>
</div>
</div>
<MenuSeparator />
<MenuGroup label="You">
<MenuItem value="profile" icon={<UserRound />} description="Name, photo, phone">
Profile
</MenuItem>
<MenuItem value="security" icon={<ShieldCheck />} description="Password, two-step sign-in">
Security
</MenuItem>
<MenuItem value="notifications" icon={<Bell />} description="SMS, email, push" count={3}>
Notifications
</MenuItem>
</MenuGroup>
<MenuGroup label="Business">
<MenuSub label="Site" icon={<Building2 />} description="Gulshan · 2 gates" defaultOpen>
<MenuItem value="site">Details</MenuItem>
<MenuItem value="rates">Rates</MenuItem>
</MenuSub>
<MenuItem
value="billing"
icon={<CreditCard />}
description="Plan, invoices"
badge={
<Badge variant="gradient" dot={false}>
Pro
</Badge>
}
>
Billing
</MenuItem>
<MenuItem value="keys" icon={<KeyRound />} description="Gate controllers, API">
API keys
</MenuItem>
</MenuGroup>
<MenuSeparator />
<MenuItem danger icon={<LogOut />}>
Sign out
</MenuItem>
</Menu>
<div className="rounded-card border border-border bg-raised p-5">
<p className="ov">Settings</p>
<h3 className="disp mt-1 text-[18px] font-semibold capitalize text-text">{section === "keys" ? "API keys" : section}</h3>
<p className="mt-1 text-[13px] text-body">{SECTIONS[section]}</p>
<div className="mt-5 flex items-center gap-2 rounded-ctl border border-dashed border-border2 p-4 text-[12.5px] text-muted">
<Receipt className="size-4" /> Section content goes here. Try ↑ ↓ Home End, and → ← on “Site”.
</div>
</div>
</div>
);
}Import#
Everything comes from the package root. The example above uses:
your-component.tsx
import { Avatar, Badge, Menu, MenuGroup, MenuItem, MenuSeparator, MenuSub } from "@waveinno/ui";Props#
Read from the source of this version, so they match the code exactly.
<Avatar>
src/components/ui/avatar.tsx| Prop | Type | Default | Description |
|---|---|---|---|
namerequired | string | — | |
src | string | null | — | |
size | keyof typeof sizes | "md" | |
status | keyof typeof STATUS | — | |
verified | boolean | — | |
ring | "gradient" | — | |
shape | "circle" | "square" | "circle" | |
className | string | — |
<Badge>
src/components/ui/badge.tsx| Prop | Type | Default | Description |
|---|---|---|---|
dot | boolean | true | |
variant | "soft" | "solid" | "outline" | "dot" | "gradient" | "live" | "soft" |
Also takes HTMLAttributes<HTMLSpanElement> & VariantProps<typeof badgeVariants>.
<Menu>
src/components/ui/menu.tsx| Prop | Type | Default | Description |
|---|---|---|---|
variant | "soft" | "line" | "raised" | "gradient" | "dark" | "panel" | "rail" | "soft" | |
value | string | — | The chosen item's `value` (controlled). |
defaultValue | string | — | |
onValueChange | (value: string) => void | — | |
heading | React.ReactNode | — | On top of the list: a title, or in the rail, the product mark. |
collapsible | boolean | — | A collapse toggle in the heading row. |
collapsed | boolean | — | |
defaultCollapsed | boolean | false | |
onCollapsedChange | (collapsed: boolean) => void | — | |
collapsedMode | "icons" | "hidden" | "icons" | `icons`: narrow to the icons. `hidden`: fold away entirely (toggle it from outside). |
collapseShortcut | boolean | false | ⌘B / Ctrl+B toggles it. Off by default: AppShell already uses ⌘B for its panel. |
Also takes Omit<React.HTMLAttributes<HTMLElement>, "defaultValue" | "onChange">.
<MenuGroup>
src/components/ui/menu.tsx| Prop | Type | Default | Description |
|---|---|---|---|
label | React.ReactNode | — | |
action | React.ReactNode | — | Something small on the label's right: a "+" button, a count. |
className | string | — | |
childrenrequired | React.ReactNode | — |
<MenuItem>
src/components/ui/menu.tsx| Prop | Type | Default | Description |
|---|---|---|---|
icon | React.ReactNode | — | |
description | React.ReactNode | — | Second line, muted: what is behind the item. Puts the icon in a tile. |
badge | React.ReactNode | — | |
count | number | string | — | A number on the right: unread, pending, items inside. |
shortcut | string | — | |
value | string | — | Chosen when it matches Menu's `value`; clicking makes it the value. |
active | boolean | — | Mark it chosen yourself, e.g. from the router's pathname. Overrides `value`. |
danger | boolean | — | |
asChild | boolean | — | Render your own element (a Next.js Link): `<MenuItem asChild><Link href="/x">Label</Link></MenuItem>`. |
Also takes Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "value">.
<MenuSeparator>
src/components/ui/menu.tsx| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — |
<MenuSub>
src/components/ui/menu.tsx| Prop | Type | Default | Description |
|---|---|---|---|
icon | React.ReactNode | — | |
description | React.ReactNode | — | Second line, muted: what is behind the item. Puts the icon in a tile. |
badge | React.ReactNode | — | |
count | number | string | — | A number on the right: unread, pending, items inside. |
shortcut | string | — | |
labelrequired | React.ReactNode | — | |
defaultOpen | boolean | false | |
open | boolean | — | |
onOpenChange | (open: boolean) => void | — | |
disabled | boolean | — | |
className | string | — | |
childrenrequired | React.ReactNode | — |
Install#
If the project does not have the package yet, add it, then follow the setup for Next.js or Vite once.
$ npm install @waveinno/ui