Files
Psychotoxical-psysonic/src/components/internetRadio/RadioToolbar.tsx
T
Frank Stellmacher c8e130ecea refactor(internet-radio): G.86 — extract Toolbar + AlphabetFilterBar + RadioCard + RadioEditModal + RadioDirectoryModal (#653)
* refactor(internet-radio): G.86.1 — extract RadioToolbar + AlphabetFilterBar

First cut on InternetRadio.tsx: pulled the two header bars into
their own files under src/components/internetRadio/. RadioToolbar
exports the RadioSortBy type alias so the page state and the
toolbar share the same union. AlphabetFilterBar owns the
A-Z + # key list internally.

Pure code move.

* refactor(internet-radio): G.86.2 — extract RadioCard

Pulled the single radio-station card component (cover, live overlay,
play/delete buttons, name + edit/favourite/homepage chip row) into
its own file. It owns its drag source + the psy-drop listener that
fires onDropOnto with the cursor-side (before/after).

Pure code move.

* refactor(internet-radio): G.86.3 — extract RadioEditModal

Pulled the create/edit-station modal (cover preview + change/remove,
name + stream URL + homepage URL fields, save spinner) into its own
file. station=null means "create new". Pure code move.

* refactor(internet-radio): G.86.4 — extract RadioDirectoryModal

Pulled the radio-browser directory modal (top-stations preload,
debounced search, IntersectionObserver-driven pagination, favicon
+ add-station flow with cover upload from favicon URL) into its
own file. Pure code move.

InternetRadio.tsx is now 299 LOC — every subcomponent lives in
src/components/internetRadio/.
2026-05-13 17:47:00 +02:00

44 lines
1.4 KiB
TypeScript

import React from 'react';
import { useTranslation } from 'react-i18next';
import CustomSelect from '../CustomSelect';
export type RadioSortBy = 'manual' | 'az' | 'za' | 'newest';
interface RadioToolbarProps {
sortBy: RadioSortBy;
activeFilter: string;
onSortChange: (s: RadioSortBy) => void;
onFilterChange: (f: string) => void;
}
export default function RadioToolbar({ sortBy, activeFilter, onSortChange, onFilterChange }: RadioToolbarProps) {
const { t } = useTranslation();
const sortOptions = [
{ value: 'manual', label: t('radio.sortManual') },
{ value: 'az', label: t('radio.sortAZ') },
{ value: 'za', label: t('radio.sortZA') },
{ value: 'newest', label: t('radio.sortNewest') },
];
return (
<div className="radio-toolbar">
<div className="radio-toolbar-chips">
{(['all', 'favorites'] as const).map(f => (
<button
key={f}
className={`radio-filter-chip${activeFilter === f ? ' active' : ''}`}
onClick={() => onFilterChange(f)}
>
{f === 'all' ? t('radio.filterAll') : t('radio.filterFavorites')}
</button>
))}
</div>
<CustomSelect
value={sortBy}
options={sortOptions}
onChange={v => onSortChange(v as RadioSortBy)}
style={{ width: 'max-content', minWidth: 130, maxWidth: 220, flexShrink: 0 }}
/>
</div>
);
}