Basic Dropdown
A menu button with the full keyboard model - arrows, Home/End, Escape, focus restore.
6 个框架中级
A workspace switcher with avatars, the active one checked, and an add action.
<!--
Each workspace is role="menuitemradio" because switching PICKS the active one -
exactly one is aria-checked. "Add workspace" below the divider is a plain
menuitem: an action, not one of the choices. Both live on one arrow-key ring,
so it walks straight through the divider. Avatars are gradient initials.
-->
<div class="relative inline-block">
<button type="button" aria-haspopup="menu" aria-expanded="true" aria-controls="as-menu" class="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white py-1.5 pl-1.5 pr-2.5 text-sm font-semibold text-gray-700 hover:bg-gray-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:focus-visible:ring-blue-400 dark:focus-visible:ring-offset-gray-900">
<span aria-hidden="true" class="flex h-6 w-6 flex-none items-center justify-center rounded-md bg-gradient-to-br from-blue-500 to-violet-500 text-[10px] font-bold text-white">NW</span>
<span class="max-w-[8rem] truncate">Northwind</span>
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m6 9 6 6 6-6" /></svg>
</button>
<div id="as-menu" role="menu" aria-label="Switch workspace" class="absolute left-0 top-[calc(100%+0.5rem)] z-20 w-72 max-w-[calc(100vw-2rem)] rounded-xl border border-gray-200 bg-white p-1 shadow-lg dark:border-gray-700 dark:bg-gray-900">
<button type="button" role="menuitemradio" aria-checked="true" class="flex w-full items-center gap-3 rounded-md px-2.5 py-2 text-left hover:bg-gray-100 focus-visible:bg-gray-100 focus-visible:outline-none dark:hover:bg-gray-800 dark:focus-visible:bg-gray-800">
<span aria-hidden="true" class="flex h-8 w-8 flex-none items-center justify-center rounded-md bg-gradient-to-br from-blue-500 to-violet-500 text-xs font-bold text-white">NW</span>
<span class="min-w-0 flex-1">
<span class="block truncate text-sm font-medium text-gray-900 dark:text-gray-100">Northwind</span>
<span class="block truncate text-xs text-gray-500 dark:text-gray-400">team@northwind.io</span>
</span>
<svg class="h-4 w-4 flex-none text-blue-600 dark:text-blue-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5" /></svg>
</button>
<button type="button" role="menuitemradio" aria-checked="false" class="flex w-full items-center gap-3 rounded-md px-2.5 py-2 text-left hover:bg-gray-100 focus-visible:bg-gray-100 focus-visible:outline-none dark:hover:bg-gray-800 dark:focus-visible:bg-gray-800">
<span aria-hidden="true" class="flex h-8 w-8 flex-none items-center justify-center rounded-md bg-gradient-to-br from-blue-500 to-violet-500 text-xs font-bold text-white">AC</span>
<span class="min-w-0 flex-1">
<span class="block truncate text-sm font-medium text-gray-900 dark:text-gray-100">Acme Corp</span>
<span class="block truncate text-xs text-gray-500 dark:text-gray-400">ops@acme.com</span>
</span>
</button>
<hr role="separator" class="my-1 h-px border-0 bg-gray-200 dark:bg-gray-700" />
<button type="button" role="menuitem" class="flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-left text-sm font-medium text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus-visible:bg-gray-100 focus-visible:text-gray-900 focus-visible:outline-none dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-100 dark:focus-visible:bg-gray-800 dark:focus-visible:text-gray-100">
<svg class="h-4 w-4 flex-none" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12h14" /></svg>
Add workspace
</button>
</div>
</div>| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
accounts必填 | Account[] | - | 要渲染的项目数组。 |
activeId必填 | string | - | 指标的当前值,需预先格式化。 |
onSwitch | (id: string) => void | - | On switch |
onAdd | () => void | - | On add |
Each workspace is a `role="menuitemradio"` because switching PICKS the active one - exactly one is `aria-checked`, marked with a trailing tick. "Add workspace" below the divider is a plain `menuitem`: it is an action, not one of the choices, and both sit on one arrow-key ring so it walks straight through the divider. Avatars are gradient initials, never images, so the component ships with no network dependency. Names and emails truncate rather than stretching the 18rem panel to fit the longest address.