fix(NodeSwitcher): use custom dropdown instead of native select for consistent styling
Build and Deploy / build-and-push (push) Failing after 1m1s

This commit is contained in:
2026-08-06 14:37:43 +08:00
parent 060a8f132d
commit 2cc584713f
+49 -22
View File
@@ -14,6 +14,7 @@ export function NodeSwitcher() {
const [nodes, setNodes] = useState<InterconnectNode[]>([]) const [nodes, setNodes] = useState<InterconnectNode[]>([])
const [selected, setSelected] = useState(activeInterconnectNodeId || 'local') const [selected, setSelected] = useState(activeInterconnectNodeId || 'local')
const [isMaster, setIsMaster] = useState(false) const [isMaster, setIsMaster] = useState(false)
const [open, setOpen] = useState(false)
const fetchNodes = useCallback(async () => { const fetchNodes = useCallback(async () => {
try { try {
@@ -73,22 +74,9 @@ export function NodeSwitcher() {
if (!isMaster) return null if (!isMaster) return null
return ( const handleSelect = (val: string) => {
<div
className={cn(
'inline-flex h-8 max-w-[150px] shrink-0 items-center gap-1 rounded-md border px-2 text-[11px] leading-none',
selected !== 'local'
? 'border-amber-500/30 bg-amber-500/10 text-amber-600 dark:text-amber-400'
: 'border-[var(--border)] bg-[var(--bg-primary)] text-[var(--text-secondary)]',
)}
>
<MonitorDot size={12} className="shrink-0" />
<select
className="max-w-[120px] truncate border-0 bg-transparent text-[11px] outline-none"
value={selected}
onChange={(e) => {
const val = e.target.value
setSelected(val) setSelected(val)
setOpen(false)
if (val === 'local') { if (val === 'local') {
setActiveInterconnectNodeId('') setActiveInterconnectNodeId('')
} else { } else {
@@ -96,15 +84,54 @@ export function NodeSwitcher() {
setActiveInterconnectNodeId(val, name) setActiveInterconnectNodeId(val, name)
} }
window.location.reload() window.location.reload()
}} }
const allOptions = [
{ value: 'local', label: '本机节点' },
...nodes.map((n) => ({ value: n.id, label: n.name })),
]
const currentLabel = allOptions.find((o) => o.value === selected)?.label || '本机节点'
return (
<div className="relative">
<button
type="button"
onClick={() => setOpen(!open)}
className={cn(
'inline-flex h-8 max-w-[150px] shrink-0 items-center gap-1.5 rounded-md border px-2 text-[11px] leading-none transition-colors',
selected !== 'local'
? 'border-amber-500/30 bg-amber-500/10 text-amber-600 hover:bg-amber-500/15 dark:text-amber-400'
: 'border-[var(--border)] bg-[var(--bg-primary)] text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]',
)}
> >
<option value="local"></option> <MonitorDot size={12} className="shrink-0" />
{nodes.map((n) => ( <span className="max-w-[100px] truncate">{currentLabel}</span>
<option key={n.id} value={n.id}> </button>
{n.name}
</option> {open && (
<div className="absolute left-0 top-full z-[80] mt-1 min-w-[140px] rounded-md border border-[var(--border)] bg-[var(--bg-secondary)] py-1 shadow-lg">
{allOptions.map((opt) => (
<button
key={opt.value}
type="button"
onClick={() => handleSelect(opt.value)}
className={cn(
'w-full px-3 py-1.5 text-left text-xs transition-colors',
opt.value === selected
? 'bg-[var(--bg-selected)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)]',
)}
>
{opt.label}
</button>
))} ))}
</select> </div>
)}
{open && (
<div className="fixed inset-0 z-[70]" onClick={() => setOpen(false)} />
)}
</div> </div>
) )
} }