chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
import { Popover } from '@base-ui/react/popover';
import { CheckIcon, PaletteIcon } from 'lucide-react';
import { useSyncExternalStore } from 'react';
import { applyTheme, onTheme, themeId, themes } from '#/lib/theme.js';
import { cn } from '#/lib/utils.js';
/**
* Which palette the page paints in. The account may publish its own beside
* the ones the page ships, and a reader's pick is theirs: it is remembered
* in this browser and never written back.
*/
export function ThemeMenu() {
const chosen = useSyncExternalStore(onTheme, themeId, () => '');
const all = themes();
return (
<Popover.Root>
<Popover.Trigger
aria-label="Theme"
title="Theme"
className="flex size-7 cursor-pointer items-center justify-center rounded-sm text-faint outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50"
>
<PaletteIcon className="size-4" />
</Popover.Trigger>
<Popover.Portal>
<Popover.Positioner className="z-50" sideOffset={6} align="end">
<Popover.Popup className="w-56 rounded-md border border-border bg-card p-1 shadow-lg outline-none">
{all.map((one) => (
<button
key={one.id || 'system'}
type="button"
onClick={() => applyTheme(one.id)}
className={cn(
'flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-[13px]',
one.id === chosen
? 'bg-accent text-foreground'
: 'text-muted-foreground hover:bg-accent/60 hover:text-foreground',
)}
>
<CheckIcon
className={cn(
'size-3.5 shrink-0',
one.id === chosen ? 'opacity-100' : 'opacity-0',
)}
/>
<span className="min-w-0 flex-1 truncate">{one.name}</span>
{one.published && (
<span className="shrink-0 text-[10px] text-faint uppercase tracking-[0.12em]">
yours
</span>
)}
</button>
))}
</Popover.Popup>
</Popover.Positioner>
</Popover.Portal>
</Popover.Root>
);
}