MasterDetail
A list beside the detail of what is picked in it, for a settings pane of many things of one kind: accounts, devices, shortcut groups. Each side scrolls on its own, and the detail takes the rest of the width.
import { DetailHeader, MasterDetail, MasterItem } from '@basmilius/react-ui/settings';import { useState } from 'react';
import { Laptop, Smartphone, Trash } from 'lucide-react';
import { Button, Icon } from '@basmilius/react-ui';
import { DetailHeader, MasterDetail, MasterItem, SettingsRow, SettingsSection } from '@basmilius/react-ui/settings';
const DEVICES = [
{ id: 'laptop', name: 'Work laptop', kind: 'macOS', icon: Laptop },
{ id: 'phone', name: 'Phone', kind: 'iOS', icon: Smartphone }
];
export default function MasterDetailDemo() {
const [picked, setPicked] = useState('laptop');
const device = DEVICES.find((entry) => entry.id === picked)!;
return (
<div className="flex h-80 w-full flex-col overflow-hidden rounded-lg border border-border bg-surface">
<MasterDetail
listWidth={280}
listLabel="Devices"
list={DEVICES.map((entry) => (
<MasterItem key={entry.id} selected={picked === entry.id} onSelect={() => setPicked(entry.id)}>
<Icon icon={entry.icon} className="text-text-muted" />
{entry.name}
</MasterItem>
))}
detail={
<>
<DetailHeader
mark={
<span className="grid size-10 place-items-center rounded-lg bg-surface-sunken text-text-muted">
<Icon icon={device.icon} size={20} />
</span>
}
title={device.name}
subtitle={<>{device.kind}, signed in</>}
actions={
<Button size="sm" variant="danger-outline">
<Icon icon={Trash} size={14} />
Remove
</Button>
}
/>
<SettingsSection>
<SettingsRow label="Last seen" control={<span className="text-xs text-text-muted">Today</span>} />
</SettingsSection>
</>
}
/>
</div>
);
}Inside a SettingsDialog, put it in a split section, so the dialog hands the pane its whole height instead of a padded scrolling column. Under 640 pixels the list stacks above the detail.
Parts
MasterDetail takes the list, its width (280, 320 or 340 pixels), a listLabel that names the list for a screen reader, and the detail.
MasterItem is one row of the list, a button that lifts a shade when selected and carries aria-current. It takes selected, onSelect and children.
DetailHeader is the head of a detail: a mark (a logo or an icon tile), the title, a subtitle line under it, and the actions of the thing on the right.
| Component | Prop | Type |
|---|---|---|
MasterDetail | list | ReactNode |
listWidth | 280 | 320 | 340 | |
listLabel | string | |
detail | ReactNode | |
MasterItem | selected | boolean |
onSelect | () => void | |
children | ReactNode | |
DetailHeader | mark | ReactNode |
title | string | |
subtitle | ReactNode | |
actions | ReactNode, optional |
Every prop is required unless it says otherwise, and all three take className and ref. MasterDetailProps, MasterItemProps and DetailHeaderProps are exported types.