mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
5516d95b52
### Added - SVG logo with theme-adaptive gradient in sidebar (full wordmark + P-icon for collapsed state) - Player bar: song title/artist marquee scroll on overflow - Player bar: live volume percentage tooltip on slider hover ### Changed - Sidebar collapse button moved to right-edge hover tab - Player bar: fixed 320px track info width, increased waveform margins - Settings: Server tab opens by default - Crossfade: experimental badge removed (stable) - Help page: Lyrics/Keybindings/Font entries added, theme count corrected ### Fixed - Global shortcuts double-fire (Rust-side ShortcutMap idempotency fix) - W98 theme: comprehensive contrast fixes for navy hover backgrounds - Help page: removed orphaned translation key under Playback Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
50 lines
1.4 KiB
TypeScript
50 lines
1.4 KiB
TypeScript
import React, { useRef, useState, useEffect, useCallback } from 'react';
|
|
|
|
interface Props {
|
|
text: string;
|
|
className?: string;
|
|
style?: React.CSSProperties;
|
|
onClick?: () => void;
|
|
}
|
|
|
|
export default function MarqueeText({ text, className, style, onClick }: Props) {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const textRef = useRef<HTMLSpanElement>(null);
|
|
const [scrollAmount, setScrollAmount] = useState(0);
|
|
|
|
const measure = useCallback(() => {
|
|
const container = containerRef.current;
|
|
const text = textRef.current;
|
|
if (!container || !text) return;
|
|
text.style.display = 'inline-block';
|
|
const textWidth = text.getBoundingClientRect().width;
|
|
text.style.display = '';
|
|
const overflow = textWidth - container.clientWidth;
|
|
setScrollAmount(overflow > 4 ? Math.ceil(overflow) : 0);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
measure();
|
|
const ro = new ResizeObserver(measure);
|
|
if (containerRef.current) ro.observe(containerRef.current);
|
|
return () => ro.disconnect();
|
|
}, [text, measure]);
|
|
|
|
return (
|
|
<div
|
|
ref={containerRef}
|
|
className={`marquee-wrap${className ? ` ${className}` : ''}`}
|
|
style={style}
|
|
onClick={onClick}
|
|
>
|
|
<span
|
|
ref={textRef}
|
|
className={scrollAmount > 0 ? 'marquee-scroll' : ''}
|
|
style={scrollAmount > 0 ? { '--marquee-amount': `-${scrollAmount}px` } as React.CSSProperties : {}}
|
|
>
|
|
{text}
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|