style(select): Custom Glass Select mit dunklem Hover (besserer Kontrast)
This commit is contained in:
parent
196f473b01
commit
57a06570ef
2 changed files with 80 additions and 12 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { fetchChannels, fetchSounds, playSound, setVolumeLive } from './api';
|
||||
import type { VoiceChannelInfo, Sound } from './types';
|
||||
import { getCookie, setCookie } from './cookies';
|
||||
|
|
@ -103,15 +103,11 @@ export default function App() {
|
|||
aria-label="Suche"
|
||||
/>
|
||||
</div>
|
||||
<div className="control select">
|
||||
<select value={selected} onChange={(e) => setSelected(e.target.value)} aria-label="Voice-Channel">
|
||||
{channels.map((c) => (
|
||||
<option key={`${c.guildId}:${c.channelId}`} value={`${c.guildId}:${c.channelId}`}>
|
||||
{c.guildName} – {c.channelName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<CustomSelect
|
||||
channels={channels}
|
||||
value={selected}
|
||||
onChange={setSelected}
|
||||
/>
|
||||
<div className="control volume">
|
||||
<label>🔊 {Math.round(volume * 100)}%</label>
|
||||
<input
|
||||
|
|
@ -192,8 +188,49 @@ export default function App() {
|
|||
);
|
||||
}
|
||||
|
||||
function handlePlayWithPathFactory(play: (name: string, rel?: string) => Promise<void>) {
|
||||
return (s: Sound & { relativePath?: string }) => play(s.name, s.relativePath);
|
||||
type SelectProps = {
|
||||
channels: VoiceChannelInfo[];
|
||||
value: string;
|
||||
onChange: (v: string) => void;
|
||||
};
|
||||
|
||||
function CustomSelect({ channels, value, onChange }: SelectProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
useEffect(() => {
|
||||
const close = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); };
|
||||
window.addEventListener('click', close);
|
||||
return () => window.removeEventListener('click', close);
|
||||
}, []);
|
||||
|
||||
const current = channels.find(c => `${c.guildId}:${c.channelId}` === value);
|
||||
|
||||
return (
|
||||
<div className="control select custom-select" ref={ref}>
|
||||
<button type="button" className="select-trigger" onClick={() => setOpen(v => !v)}>
|
||||
{current ? `${current.guildName} – ${current.channelName}` : 'Channel wählen'}
|
||||
<span className="chev">▾</span>
|
||||
</button>
|
||||
{open && (
|
||||
<div className="select-menu">
|
||||
{channels.map((c) => {
|
||||
const v = `${c.guildId}:${c.channelId}`;
|
||||
const active = v === value;
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
key={v}
|
||||
className={`select-item ${active ? 'active' : ''}`}
|
||||
onClick={() => { onChange(v); setOpen(false); }}
|
||||
>
|
||||
{c.guildName} – {c.channelName}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,37 @@ header p { opacity: .8; }
|
|||
}
|
||||
.control select option { background-color: #0f1530; color: #e7e7ee; }
|
||||
.control select optgroup { background-color: #0f1530; color: #c8c8d8; }
|
||||
|
||||
/* Custom Select */
|
||||
.custom-select { position: relative; }
|
||||
.select-trigger {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding: 12px 14px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(255,255,255,.25);
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.14), rgba(255,255,255,.06));
|
||||
color: #e7e7ee;
|
||||
backdrop-filter: blur(18px);
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.2);
|
||||
}
|
||||
.select-trigger .chev { float: right; opacity: .8; }
|
||||
.select-menu {
|
||||
position: absolute; inset: auto 0 auto 0; top: calc(100% + 6px);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255,255,255,.25);
|
||||
background: rgba(15,21,48,.98);
|
||||
box-shadow: 0 24px 48px rgba(0,0,0,.5);
|
||||
max-height: 280px; overflow-y: auto;
|
||||
z-index: 20;
|
||||
}
|
||||
.select-item {
|
||||
width: 100%; text-align: left; padding: 10px 12px; color: #e7e7ee;
|
||||
background: transparent; border: 0;
|
||||
}
|
||||
.select-item:hover { background: rgba(255,255,255,.08); color: #fff; }
|
||||
.select-item.active { background: rgba(255,255,255,.14); color: #fff; }
|
||||
.control input::placeholder { color: #c8c8d8; }
|
||||
|
||||
.control.volume { display: grid; grid-template-columns: auto 1fr; gap: 10px; align-items: center; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue