feat(frontend): implement SectionAnchorTabs for improved navigation
- Replaced the existing navigation in PromptEditor and PanelAnchorNavigation with the new SectionAnchorTabs component for better code reuse and consistency. - The SectionAnchorTabs component provides a shared interface for section navigation with equal-width columns and an active state indicator.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Shared underline section tabs for prompt editor and workflow side panels.
|
||||
* Equal-width columns; active state is a primary bottom border (not pill Tabs).
|
||||
*/
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export type SectionAnchorTab = {
|
||||
id: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export function SectionAnchorTabs({
|
||||
ariaLabel,
|
||||
sections,
|
||||
activeSectionId,
|
||||
onSelect,
|
||||
className,
|
||||
contentClassName,
|
||||
}: {
|
||||
ariaLabel: string;
|
||||
sections: readonly SectionAnchorTab[];
|
||||
activeSectionId: string;
|
||||
onSelect: (sectionId: string) => void;
|
||||
className?: string;
|
||||
/** Optional width/centering wrapper around the tab row (prompt page uses max-w-7xl). */
|
||||
contentClassName?: string;
|
||||
}) {
|
||||
if (sections.length === 0) return null;
|
||||
|
||||
const list = (
|
||||
<div
|
||||
// w-full matters inside overflow-x-auto parents: without it the grid
|
||||
// shrink-wraps to minWidth and tabs look left-clustered.
|
||||
className="grid w-full border-b border-hairline"
|
||||
style={{
|
||||
gridTemplateColumns: `repeat(${sections.length}, minmax(0, 1fr))`,
|
||||
minWidth: `${Math.max(sections.length * 5.6, 12)}rem`,
|
||||
}}
|
||||
>
|
||||
{sections.map((section) => {
|
||||
const active = activeSectionId === section.id;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={section.id}
|
||||
type="button"
|
||||
aria-current={active ? "location" : undefined}
|
||||
onClick={() => onSelect(section.id)}
|
||||
className={cn(
|
||||
"-mb-px whitespace-nowrap border-b-2 px-2 py-2.5 text-center text-xs transition-colors sm:px-3 sm:text-sm",
|
||||
active
|
||||
? "border-primary text-foreground"
|
||||
: "border-transparent text-muted-foreground hover:bg-surface-strong/50 hover:text-foreground",
|
||||
)}
|
||||
>
|
||||
{section.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<nav
|
||||
aria-label={ariaLabel}
|
||||
className={cn("shrink-0 overflow-x-auto overscroll-none", className)}
|
||||
>
|
||||
{contentClassName ? (
|
||||
<div className={contentClassName}>{list}</div>
|
||||
) : (
|
||||
list
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user