5180 fichiers 38.5 Mo 0 téléchargements 259 vues mis à jour il y a 5 heures ui
RadioView.tsx 11 Ko Télécharger le fichier
import { Collapsible } from '@base-ui/react/collapsible';
import { Toggle } from '@base-ui/react/toggle';
import { ToggleGroup } from '@base-ui/react/toggle-group';
import { FC, useEffect, useState } from 'react';
import { FaBroadcastTower, FaChevronDown, FaChevronUp, FaPlay, FaStop, FaTimes } from 'react-icons/fa';
import { LocalizeText, localizeWithFallback } from '../../api';
import { DraggableWindow, DraggableWindowPosition, LayoutImage } from '../../common';
import { RadioStation, useRadio } from '../../hooks';
import { Slider } from '../../common/Slider';
import { BaseButton } from '../../common/BaseButton';
import { Hint } from '../../common/Tooltip';
import { NativeTextTree } from '../../common/native-text/NativeTextTree';

export const RadioView: FC<{}> = () => {
    const { stations, currentId, isPlaying, volume, loadError, play, stop, setVolume } = useRadio();
    const [open, setOpen] = useState(false);
    const [collapsed, setCollapsed] = useState(false);
    const [hidden, setHidden] = useState(false);
    const [selectedId, setSelectedId] = useState<string | null>(null);

    useEffect(() => {
        if (!selectedId && stations.length) setSelectedId(stations[0].id);
    }, [stations, selectedId]);

    const selected: RadioStation | null = stations.find((s) => s.id === selectedId) ?? stations[0] ?? null;
    const selectedPlaying = !!selected && currentId === selected.id && isPlaying;

    const onPlayToggle = () => {
        if (!selected) return;
        if (selectedPlaying) stop();
        else play(selected);
    };

    const onPick = (station: RadioStation) => {
        setSelectedId(station.id);
        setOpen(false);
        play(station);
    };

    const onGroupChange = (pressed: string[]) => {
        const nextId = pressed.find((id) => id !== selectedId) ?? selectedId;
        const station = stations.find((s) => s.id === nextId);

        if (station) onPick(station);
    };

    const onCollapsedChange = (expanded: boolean) => {
        setCollapsed(!expanded);
        if (!expanded) setOpen(false);
    };

    const onClose = () => {
        stop();
        setHidden(true);
    };

    if (hidden) return null;

    return (
        <NativeTextTree>
            <DraggableWindow uniqueKey="octane-radio" windowPosition={DraggableWindowPosition.TOP_LEFT}>
                <Collapsible.Root className="radio-widget w-[244px] max-w-[64vw] select-none overflow-hidden rounded-xl border border-white/10 bg-gradient-to-b from-[rgba(22,24,30,0.94)] to-[rgba(10,11,14,0.94)] text-white shadow-[0_8px_24px_rgba(0,0,0,0.4)] backdrop-blur-sm" open={!collapsed} onOpenChange={onCollapsedChange}>
                    <div className="drag-handler flex cursor-move items-center gap-2 border-b border-white/10 px-3 py-1.5">
                        <FaBroadcastTower className={`text-[11px] ${isPlaying ? 'text-sky-400' : 'text-white/45'}`} />
                        <span className="grow text-[10px] font-bold uppercase tracking-[0.14em] text-white/55">{LocalizeText('radio.title')}</span>
                        <div className={`radio-eq ${isPlaying ? 'is-live' : ''}`}>
                            <span />
                            <span />
                            <span />
                            <span />
                        </div>
                        <Collapsible.Trigger
                            title={collapsed ? LocalizeText('radio.show') : LocalizeText('radio.hide')}
                            className="flex h-5 w-5 shrink-0 items-center justify-center rounded-md bg-white/8 text-white/60 transition-colors hover:bg-white/15 hover:text-white"
                        >
                            {collapsed ? <FaChevronDown className="text-[9px]" /> : <FaChevronUp className="text-[9px]" />}
                        </Collapsible.Trigger>
                        <Hint content={localizeWithFallback('radio.close', 'Close')}>
                            <BaseButton aria-label={localizeWithFallback('radio.close', 'Close')}
                                type="button"
                                onClick={onClose}
                                className="flex h-5 w-5 shrink-0 items-center justify-center rounded-md bg-white/8 text-white/60 transition-colors hover:bg-white/15 hover:text-white"
                            >
                                <FaTimes className="text-[9px]" />
                            </BaseButton>
                        </Hint>
                    </div>

                    <Collapsible.Panel>
                        <Collapsible.Root open={open} onOpenChange={setOpen} className="contents">
                            <div className="flex items-center gap-2.5 px-3 py-2.5">
                                <Hint content={selectedPlaying ? LocalizeText('radio.stop') : LocalizeText('radio.title')}><BaseButton
                                    type="button"
                                    onClick={onPlayToggle}
                                    disabled={!selected}
                                    className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-emerald-500 text-xs text-white shadow-inner transition-all hover:bg-emerald-400 disabled:opacity-40"
                                >
                                    {selectedPlaying ? <FaStop /> : <FaPlay className="translate-x-px" />}
                                </BaseButton></Hint>
                                <div className="min-w-0 grow">
                                    <div className="truncate text-sm font-bold leading-tight">{selected ? selected.name : LocalizeText('radio.title')}</div>
                                    <div className="mt-0.5 flex items-center gap-1.5">
                                        {selectedPlaying && (
                                            <span className="flex items-center gap-1 text-[9px] font-bold uppercase tracking-wide text-sky-400">
                                                <span className="h-1.5 w-1.5 rounded-full bg-sky-400" /> {LocalizeText('radio.live')}
                                            </span>
                                        )}
                                        {selected?.genre && <span className="truncate text-[10px] text-white/45">{selected.genre}</span>}
                                    </div>
                                </div>
                                <Collapsible.Trigger
                                    title={LocalizeText('radio.title')}
                                    className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg transition-colors ${open ? 'bg-white/20' : 'bg-white/8 hover:bg-white/15'}`}
                                >
                                    <FaChevronDown className={`text-[10px] transition-transform ${open ? 'rotate-180' : ''}`} />
                                </Collapsible.Trigger>
                            </div>

                            {selectedPlaying && (
                                <div className="flex items-center gap-2 px-3 pb-2.5">
                                    <span className="text-xs text-white/55">🔊</span>
                                    <Slider disabledButton className="octane-slider--compact radio-vol grow" min={0} max={1} step={0.01} value={volume} onChange={(value) => setVolume(value)} />
                                </div>
                            )}

                            <Collapsible.Panel className="border-t border-white/10 bg-black/20 p-1.5">
                                {loadError && <div className="px-2 py-2 text-[11px] text-red-400">{LocalizeText('radio.error')}</div>}
                                {!loadError && !stations.length && <div className="px-2 py-2 text-[11px] text-white/50">{LocalizeText('radio.empty')}</div>}
                                <ToggleGroup
                                    aria-label={LocalizeText('radio.title')}
                                    className="radio-scroll flex max-h-[156px] flex-col gap-1 overflow-y-auto pr-0.5"
                                    orientation="vertical"
                                    value={selectedId ? [selectedId] : []}
                                    onValueChange={onGroupChange}
                                >
                                    {stations.map((station) => {
                                        const isActive = station.id === selectedId;
                                        const playingThis = currentId === station.id && isPlaying;
                                        return (
                                            <Toggle
                                                key={station.id}
                                                nativeButton={false}
                                                render={<div />}
                                                value={station.id}
                                                className={`flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 transition-colors ${isActive ? 'bg-sky-500/15 ring-1 ring-sky-400/40' : 'hover:bg-white/8'}`}
                                            >
                                                {station.logo ? (
                                                    <LayoutImage imageUrl={station.logo} className="h-7 w-7 shrink-0 rounded bg-contain bg-center bg-no-repeat" />
                                                ) : (
                                                    <div className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[11px] ${playingThis ? 'bg-sky-500/80' : 'bg-white/10'}`}>
                                                        {playingThis ? <FaStop /> : <FaPlay className="translate-x-px" />}
                                                    </div>
                                                )}
                                                <div className="min-w-0 grow">
                                                    <div className="truncate text-xs font-bold leading-tight">{station.name}</div>
                                                    {station.genre && <div className="truncate text-[10px] text-white/45">{station.genre}</div>}
                                                </div>
                                                {playingThis && (
                                                    <div className="radio-eq is-live shrink-0">
                                                        <span />
                                                        <span />
                                                        <span />
                                                        <span />
                                                    </div>
                                                )}
                                            </Toggle>
                                        );
                                    })}
                                </ToggleGroup>
                            </Collapsible.Panel>
                        </Collapsible.Root>
                    </Collapsible.Panel>
                </Collapsible.Root>
            </DraggableWindow>
        </NativeTextTree>
    );
};
15Sujets
1Dépôts
20Membres
2En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser