Files
mcma-webui/src/components/common/Icon.tsx
T
Senko-san aed0572071 Scaffold global navigation aligned to routes plan
Build out the full web route map from music-selfhost-routes.md as
scaffolding (no functionality on new screens):

- Full route tree: /login, /albums/:id, /artists/:id, /playlists(+detail),
  /discover, /upload, metadata editor (single + batch), /storage/maintenance,
  /queue, nested /settings and /admin, and a 404.
- Sidebar rebuilt to the A1 spec with permission-gated Discover/Upload.
- ProtectedRoute gains requirePermission; Permission exported.
- AppShell wraps Outlet in a Suspense boundary for lazy routes.
- Reusable Placeholder + SubNav; Settings/Admin become nested layouts.
- Settings/Profile: wired language + theme selectors.
- Remove orphaned Home feature (web has no Home; / -> /library) and the
  now-unused house icon + nav.home keys.
- i18n keys (en + ru) and CSS for page-title/sub-nav.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 17:05:21 +03:00

101 lines
2.3 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,
DotsSixVertical,
GearSix,
HardDrives,
Heart,
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,
'thumbs-down': ThumbsDown,
'speaker-high': SpeakerHigh,
'speaker-x': SpeakerSimpleX,
cloud: Cloud,
'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 }}
/>
);
}