-
Notifications
You must be signed in to change notification settings - Fork 0
feat(navigation): standardise multi-page mode menus in the header #1645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
901c336
4dddce2
2e91af9
e135e45
d730792
a8b4e10
7fdda1c
50a1bdb
c956fc3
075f0f5
fe56d98
43a8318
525be31
2a71d46
af3a89d
9a91b5c
9958a5b
c8c3c88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| "use client"; | ||
|
|
||
| import { FileText, GitCompareArrows, ListChecks, Network, Search, type LucideIcon } from "lucide-react"; | ||
|
|
||
| import { ModeNav, type ModeNavItem } from "@/components/mode-nav/mode-nav"; | ||
| import { appModeDefinition, type AppModeId } from "@/lib/app-modes"; | ||
| import { modeSecondaryNavigationEntries, modeSecondaryNavigationHref } from "@/lib/mode-secondary-navigation"; | ||
|
|
||
| const iconByItemId: Record<string, LucideIcon> = { | ||
| search: Search, | ||
| diagnoses: FileText, | ||
| compare: GitCompareArrows, | ||
| builder: ListChecks, | ||
| map: Network, | ||
| }; | ||
|
|
||
| /** | ||
| * Adapts the canonical route registry to the universal header-integrated mode | ||
| * navigation. Keeping this mapping in one component prevents the page shell, | ||
| * Specifiers, and Formulation from drifting onto different labels or URLs. | ||
| */ | ||
| export function RegistryModeNav({ | ||
| modeId, | ||
| activeId, | ||
| searchParamString = "", | ||
| }: { | ||
| modeId: AppModeId; | ||
| activeId: string; | ||
| searchParamString?: string; | ||
| }) { | ||
| const currentSearchParams = new URLSearchParams(searchParamString); | ||
| const items = modeSecondaryNavigationEntries(modeId).flatMap<ModeNavItem>((entry) => { | ||
| if (!entry.href) return []; | ||
| return [ | ||
| { | ||
| id: entry.id, | ||
| label: entry.label, | ||
| href: modeSecondaryNavigationHref({ | ||
| modeId, | ||
| itemId: entry.id, | ||
| href: entry.href, | ||
| currentSearchParams, | ||
| }), | ||
| icon: iconByItemId[entry.id] ?? FileText, | ||
| }, | ||
| ]; | ||
| }); | ||
|
|
||
| return <ModeNav items={items} label={`${appModeDefinition(modeId).label} pages`} activeId={activeId} />; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ import type { ComponentType, CSSProperties, ReactNode } from "react"; | |
| import { ArrowRight, CheckCircle2, ChevronsUpDown, Info, Minus, ShieldAlert, Tags } from "lucide-react"; | ||
|
|
||
| import { InformationPageBreadcrumbs, InformationPageShell } from "@/components/information-page-shell"; | ||
| import { RegistryModeNav } from "@/components/mode-nav/registry-mode-nav"; | ||
| import { cn, eyebrowText } from "@/components/ui-primitives"; | ||
| import type { SpecifierFamily, SpecifierRecord } from "@/lib/specifiers"; | ||
| import { specifierFamilies } from "@/lib/specifiers"; | ||
|
|
@@ -20,41 +21,7 @@ export function SpecifierBreadcrumbs({ current }: { current?: string }) { | |
| } | ||
|
|
||
| export function SpecifierSubnav({ active }: { active: "search" | "builder" | "compare" | "map" }) { | ||
| const items = [ | ||
| { id: "search" as const, label: "Find", shortLabel: "Find", href: "/specifiers" }, | ||
| { id: "builder" as const, label: "Build wording", shortLabel: "Build", href: "/specifiers/builder" }, | ||
| { id: "compare" as const, label: "Compare", shortLabel: "Compare", href: "/specifiers/compare" }, | ||
| { id: "map" as const, label: "Map", shortLabel: "Map", href: "/specifiers/map" }, | ||
| ]; | ||
|
|
||
| return ( | ||
| <nav | ||
| aria-label="Specifier tools" | ||
| className="polished-scroll flex max-w-full gap-1 overflow-x-auto rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-raised)] p-1 shadow-[var(--shadow-inset)]" | ||
| > | ||
| {items.map((item) => ( | ||
| <Link | ||
| key={item.id} | ||
| href={item.href} | ||
| aria-label={item.label} | ||
| aria-current={active === item.id ? "page" : undefined} | ||
| className={cn( | ||
| "inline-flex min-h-tap shrink-0 items-center justify-center rounded-md px-3 text-xs font-bold transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] sm:text-sm", | ||
| active === item.id | ||
| ? "bg-[color:var(--clinical-accent)] text-[color:var(--clinical-accent-contrast)] shadow-[var(--shadow-tight)]" | ||
| : "text-[color:var(--text-muted)] hover:bg-[color:var(--surface)] hover:text-[color:var(--text)]", | ||
| )} | ||
| > | ||
| <span className="sm:hidden" aria-hidden> | ||
| {item.shortLabel} | ||
| </span> | ||
| <span className="hidden sm:inline" aria-hidden> | ||
| {item.label} | ||
| </span> | ||
| </Link> | ||
| ))} | ||
| </nav> | ||
| ); | ||
| return <RegistryModeNav modeId="specifiers" activeId={active} />; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Pass the current query string to This call uses the adapter default of Thread the encoded route query string through 🤖 Prompt for AI Agents |
||
| } | ||
|
Comment on lines
23
to
25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Selections are dropped when moving between Specifiers or Formulation workflow pages The Specifiers and Formulation page menus are built without the page's current query string ( Why the state-carrying helper never runs for these two modes
The shared shell path does pass the bridged query string ( Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
| const familyChipBase = | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Pass the current query string to
RegistryModeNav.This call uses the adapter default of
"". A Formulation workflow URL withmechanism,a,b, ortemplateloses that state when a user selects another mode-navigation item.Thread the encoded route query string through
FormulationSubnavand its callers. Add a navigation test that starts with selected mechanisms and a template.🤖 Prompt for AI Agents