mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-18 08:33:29 +00:00
fix(stats): correct breadcrumb behavior
This commit is contained in:
parent
49d997aec3
commit
92cc8f553a
4 changed files with 85 additions and 8 deletions
|
|
@ -39,6 +39,7 @@ import {
|
|||
uniqueComparisonPairs,
|
||||
type ComparisonModelRef,
|
||||
} from "../compare-cards"
|
||||
import { createBreadcrumbMenuRoot } from "../breadcrumb-menu"
|
||||
import {
|
||||
applyThemePreference,
|
||||
Footer,
|
||||
|
|
@ -284,9 +285,10 @@ function ModelHero(props: {
|
|||
const labModels = () =>
|
||||
props.catalogData?.labs.find((lab) => lab.id === providerSlug(labId()))?.models ??
|
||||
(props.catalog ? [props.catalog] : [])
|
||||
const menuRoot = createBreadcrumbMenuRoot()
|
||||
return (
|
||||
<section id="overview" data-section="model-hero">
|
||||
<nav data-component="model-hero-breadcrumb" aria-label="Data breadcrumb">
|
||||
<nav ref={menuRoot} data-component="model-hero-breadcrumb" aria-label="Data breadcrumb">
|
||||
<a data-slot="model-hero-crumb" href={language.route(import.meta.env.BASE_URL)}>
|
||||
Data
|
||||
</a>
|
||||
|
|
@ -294,14 +296,14 @@ function ModelHero(props: {
|
|||
<Show
|
||||
when={labs().length > 0}
|
||||
fallback={
|
||||
<span data-slot="model-hero-crumb" data-current="true">
|
||||
<span data-slot="model-hero-crumb" data-menu="true">
|
||||
<span>{props.labName}</span>
|
||||
<ChevronDownIcon />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<details data-component="model-hero-menu">
|
||||
<summary data-slot="model-hero-crumb" data-current="true">
|
||||
<summary data-slot="model-hero-crumb" data-menu="true">
|
||||
<span>{props.labName}</span>
|
||||
<ChevronDownIcon />
|
||||
</summary>
|
||||
|
|
@ -324,14 +326,19 @@ function ModelHero(props: {
|
|||
<Show
|
||||
when={labModels().length > 0}
|
||||
fallback={
|
||||
<span data-slot="model-hero-crumb" data-current="true" aria-current="page">
|
||||
<span data-slot="model-hero-crumb" data-menu="true" data-current="true" aria-current="page">
|
||||
<span>{modelName()}</span>
|
||||
<ChevronDownIcon />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<details data-component="model-hero-menu">
|
||||
<summary data-slot="model-hero-crumb" data-current="true" aria-current="page">
|
||||
<summary
|
||||
data-slot="model-hero-crumb"
|
||||
data-menu="true"
|
||||
data-current="true"
|
||||
aria-current="page"
|
||||
>
|
||||
<span>{modelName()}</span>
|
||||
<ChevronDownIcon />
|
||||
</summary>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import { SectionHeading } from "../section-heading"
|
|||
import { runStatsEffect } from "../../stats-runtime"
|
||||
import { setStatsPageCacheHeaders } from "../stats-cache"
|
||||
import { ComparisonCardsSection, modelRefFromCatalog, uniqueComparisonPairs } from "../compare-cards"
|
||||
import { createBreadcrumbMenuRoot } from "../breadcrumb-menu"
|
||||
import {
|
||||
applyThemePreference,
|
||||
Footer,
|
||||
|
|
@ -205,8 +206,9 @@ function LabHero(props: { lab: ModelCatalogLab; labs: ModelCatalogLab[] }) {
|
|||
function LabHeroBreadcrumb(props: { label: string; labs?: ModelCatalogLab[] }) {
|
||||
const language = useLanguage()
|
||||
const labs = () => props.labs ?? []
|
||||
const menuRoot = createBreadcrumbMenuRoot()
|
||||
return (
|
||||
<nav data-component="lab-hero-breadcrumb" aria-label="Data breadcrumb">
|
||||
<nav ref={menuRoot} data-component="lab-hero-breadcrumb" aria-label="Data breadcrumb">
|
||||
<a data-slot="lab-hero-crumb" href={language.route(import.meta.env.BASE_URL)}>
|
||||
Data
|
||||
</a>
|
||||
|
|
|
|||
52
packages/stats/app/src/routes/breadcrumb-menu.ts
Normal file
52
packages/stats/app/src/routes/breadcrumb-menu.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { onCleanup, onMount } from "solid-js"
|
||||
|
||||
export function createBreadcrumbMenuRoot() {
|
||||
let root: HTMLElement | undefined
|
||||
|
||||
onMount(() => {
|
||||
const closeMenus = (except?: HTMLDetailsElement) => {
|
||||
root?.querySelectorAll<HTMLDetailsElement>("details[open]").forEach((menu) => {
|
||||
if (menu !== except) menu.open = false
|
||||
})
|
||||
}
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
const path = event.composedPath()
|
||||
root?.querySelectorAll<HTMLDetailsElement>("details[open]").forEach((menu) => {
|
||||
if (!path.includes(menu)) menu.open = false
|
||||
})
|
||||
}
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key !== "Escape") return
|
||||
const menu = root?.querySelector<HTMLDetailsElement>("details[open]")
|
||||
if (!menu) return
|
||||
event.preventDefault()
|
||||
menu.open = false
|
||||
menu.querySelector<HTMLElement>("summary")?.focus()
|
||||
}
|
||||
const onToggle = (event: Event) => {
|
||||
if (!(event.target instanceof HTMLDetailsElement) || !event.target.open) return
|
||||
closeMenus(event.target)
|
||||
}
|
||||
const onFocusOut = () => {
|
||||
queueMicrotask(() => {
|
||||
if (root?.contains(document.activeElement)) return
|
||||
closeMenus()
|
||||
})
|
||||
}
|
||||
|
||||
document.addEventListener("pointerdown", onPointerDown)
|
||||
document.addEventListener("keydown", onKeyDown)
|
||||
root?.addEventListener("toggle", onToggle, true)
|
||||
root?.addEventListener("focusout", onFocusOut)
|
||||
onCleanup(() => {
|
||||
document.removeEventListener("pointerdown", onPointerDown)
|
||||
document.removeEventListener("keydown", onKeyDown)
|
||||
root?.removeEventListener("toggle", onToggle, true)
|
||||
root?.removeEventListener("focusout", onFocusOut)
|
||||
})
|
||||
})
|
||||
|
||||
return (element: HTMLElement) => {
|
||||
root = element
|
||||
}
|
||||
}
|
||||
|
|
@ -3113,10 +3113,18 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-page="stats"] [data-slot="model-hero-crumb"][data-current="true"] {
|
||||
[data-page="stats"] a[data-slot="model-hero-crumb"] {
|
||||
color: var(--stats-hero-muted);
|
||||
}
|
||||
|
||||
[data-page="stats"] [data-slot="model-hero-crumb"][data-menu="true"] {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
[data-page="stats"] [data-slot="model-hero-crumb"][data-current="true"] {
|
||||
color: var(--stats-text);
|
||||
}
|
||||
|
||||
[data-page="stats"] [data-component="model-hero-menu"] {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
|
@ -3151,7 +3159,7 @@
|
|||
flex: 0 0 auto;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--stats-faint);
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
[data-page="stats"] [data-component="model-hero-menu"][open] [data-slot="model-hero-crumb"] svg {
|
||||
|
|
@ -5533,6 +5541,14 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-page="stats"] a[data-slot="compare-home-crumb"] {
|
||||
color: var(--stats-hero-muted);
|
||||
}
|
||||
|
||||
[data-page="stats"] [data-slot="compare-home-crumb"][data-current="true"] {
|
||||
color: var(--stats-text);
|
||||
}
|
||||
|
||||
[data-page="stats"] a[data-slot="compare-home-crumb"]:hover,
|
||||
[data-page="stats"] a[data-slot="compare-home-crumb"]:focus-visible {
|
||||
color: var(--stats-text);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue