From 57a06570efab066e09559e24028824df9ae4762b Mon Sep 17 00:00:00 2001 From: vibe-bot Date: Fri, 8 Aug 2025 13:14:27 +0200 Subject: [PATCH] style(select): Custom Glass Select mit dunklem Hover (besserer Kontrast) --- web/src/App.tsx | 61 +++++++++++++++++++++++++++++++++++++--------- web/src/styles.css | 31 +++++++++++++++++++++++ 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/web/src/App.tsx b/web/src/App.tsx index 0fe0c1a..112041f 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -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" /> -
- -
+
Promise) { - 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(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 ( +
+ + {open && ( +
+ {channels.map((c) => { + const v = `${c.guildId}:${c.channelId}`; + const active = v === value; + return ( + + ); + })} +
+ )} +
+ ); } diff --git a/web/src/styles.css b/web/src/styles.css index 390cd11..385634a 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -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; }