mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-05-22 07:34:47 +02:00
fix(dashboard): info popover toggle and click-outside dismiss
- Replace fixed overlay with useRef-based click-outside detection - Add X close button in top-right corner of popover - Click ? to toggle (open if closed, close if open) - Click anywhere outside popover to dismiss - Remove fixed inset-0 overlay that was blocking page interaction Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
64faf33e3b
commit
23151f63ba
5 changed files with 2485 additions and 2463 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import NodePicker from '@/components/NodePicker'
|
||||
import ChannelPicker from '@/components/ChannelPicker'
|
||||
import {
|
||||
|
|
@ -309,12 +309,28 @@ const US_STATES = [
|
|||
{ value: 'US-WI', label: 'Wisconsin' }, { value: 'US-WY', label: 'Wyoming' },
|
||||
]
|
||||
|
||||
// InfoButton component
|
||||
// InfoButton component with click-outside dismiss and X close button
|
||||
function InfoButton({ info, link, linkText = 'Learn more' }: { info: string; link?: string; linkText?: string }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const popoverRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Close on click outside
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
function handleClickOutside(e: MouseEvent) {
|
||||
if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) {
|
||||
setOpen(false)
|
||||
}
|
||||
}
|
||||
const timer = setTimeout(() => document.addEventListener('mousedown', handleClickOutside), 0)
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
}
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
<div className="relative inline-block">
|
||||
<div className="relative inline-block" ref={popoverRef}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => { e.stopPropagation(); setOpen(!open) }}
|
||||
|
|
@ -324,10 +340,16 @@ function InfoButton({ info, link, linkText = 'Learn more' }: { info: string; lin
|
|||
?
|
||||
</button>
|
||||
{open && (
|
||||
<>
|
||||
<div className="fixed inset-0 z-40" onClick={() => setOpen(false)} />
|
||||
<div className="absolute left-0 top-6 z-50 w-72 p-3 bg-[#1a2332] border border-[#2a3a4a] rounded-lg shadow-xl text-xs text-slate-300 leading-relaxed">
|
||||
{info}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(false)}
|
||||
className="absolute top-1 right-1 w-5 h-5 rounded hover:bg-slate-700 text-slate-500 hover:text-slate-300 inline-flex items-center justify-center transition-colors"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X size={12} />
|
||||
</button>
|
||||
<div className="pr-4">{info}</div>
|
||||
{link && (
|
||||
<a
|
||||
href={link}
|
||||
|
|
@ -340,7 +362,6 @@ function InfoButton({ info, link, linkText = 'Learn more' }: { info: string; lin
|
|||
</a>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
|
@ -2139,6 +2160,7 @@ function EnvironmentalSection({ data, onChange }: { data: EnvironmentalConfig; o
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DashboardSection({ data, onChange }: { data: DashboardConfig; onChange: (d: DashboardConfig) => void }) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
|
|
|
|||
1
meshai/dashboard/static/assets/index-CYHGOAxN.css
Normal file
1
meshai/dashboard/static/assets/index-CYHGOAxN.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -8,8 +8,8 @@
|
|||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script type="module" crossorigin src="/assets/index-DrKrP8CJ.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-E1oMzltW.css">
|
||||
<script type="module" crossorigin src="/assets/index-DARDkZhk.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CYHGOAxN.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue