feat(design): glass surfaces by role, the static Badge, LinkButton, user pickers on the Combobox (E28 S3d1)

This commit is contained in:
2026-09-25 00:34:35 +02:00
parent db10bfbbca
commit c47c193ad1
89 changed files with 818 additions and 478 deletions
+13 -11
View File
@@ -5,19 +5,21 @@ import { PopoverItem, PopoverSurface } from "./ui/AnchoredPopover";
import { closeThenAct, useAnchoredPopover } from "./ui/anchoredPopoverHooks";
import { useSavedViews } from "./savedViewsContext";
import { IconButton } from "./ui/IconButton";
import { Pressable } from "./ui/Pressable";
import { SURFACE } from "./ui/surfaces";
import { SelectTrigger } from "./ui/SelectTrigger";
// The saved-views quick-picker. The filter-panel header's icon (and the toolbar's view chip) use it as
// their trigger: click → a popover list of the user's saved views (apply on click, the active one
// checked) + a "Manage views…" entry that opens the Personal Views modal, where the editing lives.
export default function SavedViewsPopover({
trigger,
className,
toolbar = false,
ariaLabel,
placement = "bottom-start",
}: {
trigger: ReactNode;
className?: string;
/** The toolbar's view chip: the A5 dropdown trigger around `trigger`, instead of a glyph. */
toolbar?: boolean;
ariaLabel?: string;
placement?: "bottom-start" | "bottom-end";
}) {
@@ -28,16 +30,16 @@ export default function SavedViewsPopover({
const label = ariaLabel ?? t("views.saved");
return (
<>
{/* A caller-shaped trigger (the toolbar's view chip) is a surface; the default one a glyph. */}
{className ? (
<Pressable
{toolbar ? (
<SelectTrigger
context="toolbar"
open={open}
ref={pop.refs.setReference}
{...pop.getReferenceProps()}
aria-label={label}
className={className}
>
{trigger}
</Pressable>
</SelectTrigger>
) : (
<IconButton
variant="ghost-accent"
@@ -51,7 +53,7 @@ export default function SavedViewsPopover({
<PopoverSurface
popover={pop}
ariaLabel={label}
className="glass-menu min-w-56 rounded-xl p-1.5 shadow-lg animate-[popIn_0.16s_ease]"
className={`${SURFACE.menu} min-w-56 animate-[popIn_0.16s_ease]`}
>
{views.length === 0 ? (
<p className="px-2 py-1.5 text-2xs leading-snug text-muted">{t("views.empty")}</p>
@@ -67,7 +69,7 @@ export default function SavedViewsPopover({
setOpen(false);
}}
label={v.name}
className={`flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition ${
className={`flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-sm transition ${
v.id === activeViewId ? "text-fg" : "text-muted"
}`}
>
@@ -84,7 +86,7 @@ export default function SavedViewsPopover({
// Opens a dialog: close first, or the menu's focus return steals its focus (closeThenAct).
onSelect={() => closeThenAct(() => setOpen(false), openModal)}
label={t("views.manage")}
className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm text-muted transition hover:text-fg"
className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-sm text-muted transition hover:text-fg"
>
<Settings2 className="h-3.5 w-3.5 shrink-0" aria-hidden="true" />
<span className="flex-1">{t("views.manage")}</span>