refactor: update navbar release channel control (#295)

This commit is contained in:
shenlan 2025-09-22 16:49:44 +08:00 committed by GitHub
parent 86f8a15c71
commit 3bfbc876e2
2 changed files with 55 additions and 27 deletions

View File

@ -261,12 +261,12 @@ export default function Navbar() {
</div>
)
})}
<div className="flex flex-col items-end gap-2">
<div className="flex items-center gap-3">
<LanguageToggle />
<ReleaseChannelSelector
selected={selectedChannels}
onToggle={toggleChannel}
variant="compact"
variant="icon"
/>
</div>
</div>

View File

@ -9,7 +9,7 @@ export type ReleaseChannel = 'stable' | 'beta' | 'develop'
type ReleaseChannelSelectorProps = {
selected: ReleaseChannel[]
onToggle: (channel: ReleaseChannel) => void
variant?: 'default' | 'compact'
variant?: 'default' | 'compact' | 'icon'
}
const CHANNEL_ORDER: ReleaseChannel[] = ['stable', 'beta', 'develop']
@ -24,6 +24,8 @@ export default function ReleaseChannelSelector({
const [open, setOpen] = useState(false)
const containerRef = useRef<HTMLDivElement>(null)
const isCompact = variant === 'compact'
const isIcon = variant === 'icon'
const hasPreviewSelection = selected.some((channel) => channel !== 'stable')
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
@ -47,40 +49,66 @@ export default function ReleaseChannelSelector({
const summary = selectedNames.length > 0 ? selectedNames.join(' + ') : labels.stable.name
return (
<div
className={`relative ${isCompact ? 'group' : ''}`}
ref={containerRef}
>
<div className={`relative ${isCompact || isIcon ? 'group' : ''}`} ref={containerRef}>
<button
type="button"
onClick={() => setOpen((prev) => !prev)}
className={`flex items-center gap-2 rounded-md border border-gray-200 bg-white/80 text-sm text-gray-700 shadow-sm transition hover:border-purple-300 hover:text-purple-600 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 ${
isCompact
? 'px-3 py-1 text-xs font-medium'
: 'w-full justify-between px-3 py-1.5 md:w-auto md:justify-start'
className={`relative flex items-center gap-2 rounded-md border border-gray-200 bg-white/80 text-sm text-gray-700 shadow-sm transition hover:border-purple-300 hover:text-purple-600 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 ${
isIcon
? 'h-9 w-9 justify-center p-0'
: isCompact
? 'px-3 py-1 text-xs font-medium'
: 'w-full justify-between px-3 py-1.5 md:w-auto md:justify-start'
}`}
aria-haspopup="listbox"
aria-expanded={open}
aria-label={labels.label}
>
<span className={`font-medium ${isCompact ? '' : 'text-gray-700'}`}>{labels.label}</span>
{!isCompact && (
<span className="text-xs text-gray-500">
{labels.summaryPrefix}: {summary}
</span>
{isIcon ? (
<svg
className="h-5 w-5 text-purple-600"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={1.5}
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9 3v6.879L4.553 17.71A1.2 1.2 0 0 0 5.6 19.5h12.8a1.2 1.2 0 0 0 1.047-1.79L15 9.879V3"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9 13h6m-6 3h6"
/>
</svg>
) : (
<>
<span className={`font-medium ${isCompact ? '' : 'text-gray-700'}`}>{labels.label}</span>
{!isCompact && (
<span className="text-xs text-gray-500">
{labels.summaryPrefix}: {summary}
</span>
)}
<svg
className={`h-4 w-4 text-gray-400 transition-transform ${open ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</>
)}
{isIcon && hasPreviewSelection && (
<span className="absolute -top-1 -right-1 h-2 w-2 rounded-full bg-purple-500" />
)}
<svg
className={`h-4 w-4 text-gray-400 transition-transform ${open ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{isCompact && (
{(isCompact || isIcon) && (
<div className="pointer-events-none absolute bottom-full left-1/2 z-40 mb-2 hidden w-56 -translate-x-1/2 rounded-md bg-gray-900 px-3 py-2 text-xs text-white opacity-0 transition group-hover:block group-hover:opacity-100 group-focus-within:block group-focus-within:opacity-100">
<div className="font-semibold">{labels.label}</div>
<div className="mt-1 text-gray-200">