Files
mcma-webui/src/components/common/Icon.tsx
T
Senko-san 8ae447e08d
Docker Build & Publish / build (push) Has been cancelled
Docker Build & Publish / push (push) Has been cancelled
Docker Build & Publish / Prune old image versions (push) Has been cancelled
feat(track): icon-based status badges, detect locally-cached tracks
Replace the labelled availability/metadata badges in track rows with
small icon+tooltip indicators (cloud/hard-drives/warning/etc, derived
from TrackAvailability and MetadataStatus).

Add a `connection` slice fed by a single status poller (Sidebar) so
other components can cheaply check backend reachability. TrackRow uses
this plus the offline audio cache to show "Local" instead of a stale
"On server" when the backend is down but the track is already cached.
2026-06-13 18:00:48 +03:00

105 lines
2.4 KiB
TypeScript

/*
* Thin wrapper over @phosphor-icons/react so app code can reference icons by
* the kebab names used in the design reference (`<Icon name="vinyl-record" />`)
* instead of importing each component. modern-sk is intentionally icon-agnostic
* (its SearchField/MenuItem/Callout take an `icon` ReactNode), so the icon set
* is the app's concern.
*
* The rendered <svg> carries className "ph" and sizes to 1em, so the shell CSS
* controls size/colour via font-size + currentColor, exactly like the reference.
*/
// TODO: remove this component, use phosphor's icons project-wide
import type { CSSProperties } from 'react';
import {
ArrowCircleDown,
ArrowsClockwise,
CheckCircle,
Cloud,
CloudSlash,
DotsSixVertical,
GearSix,
HardDrives,
Heart,
Info,
MagnifyingGlass,
Pause,
Play,
Playlist,
Plus,
PushPin,
Queue,
Radio,
Repeat,
ShieldCheck,
Shuffle,
SignOut,
SkipBack,
SkipForward,
Sparkle,
SpeakerHigh,
SpeakerSimpleX,
ThumbsDown,
Trash,
UploadSimple,
VinylRecord,
WarningCircle,
X,
type IconProps,
} from '@phosphor-icons/react';
const ICONS = {
'vinyl-record': VinylRecord,
'magnifying-glass': MagnifyingGlass,
'arrow-circle-down': ArrowCircleDown,
'upload-simple': UploadSimple,
'hard-drives': HardDrives,
'push-pin': PushPin,
playlist: Playlist,
plus: Plus,
'shield-check': ShieldCheck,
'gear-six': GearSix,
queue: Queue,
trash: Trash,
x: X,
radio: Radio,
sparkle: Sparkle,
'dots-six-vertical': DotsSixVertical,
shuffle: Shuffle,
'skip-back': SkipBack,
play: Play,
pause: Pause,
'skip-forward': SkipForward,
repeat: Repeat,
heart: Heart,
info: Info,
'thumbs-down': ThumbsDown,
'speaker-high': SpeakerHigh,
'speaker-x': SpeakerSimpleX,
cloud: Cloud,
'cloud-slash': CloudSlash,
'check-circle': CheckCircle,
'warning-circle': WarningCircle,
'sign-out': SignOut,
'arrows-clockwise': ArrowsClockwise,
} satisfies Record<string, React.ComponentType<IconProps>>;
export type IconName = keyof typeof ICONS;
interface Props {
name: IconName;
fill?: boolean;
style?: CSSProperties;
className?: string;
}
export function Icon({ name, fill, style, className }: Props) {
const Cmp = ICONS[name];
return (
<Cmp
weight={fill ? 'fill' : 'regular'}
className={className ? `ph ${className}` : 'ph'}
style={{ flexShrink: 0, ...style }}
/>
);
}