feat(ui): add ScopeTrail component for scope-based breadcrumbs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
40
ui/src/components/ScopeTrail.module.css
Normal file
40
ui/src/components/ScopeTrail.module.css
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
.trail {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
min-height: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.segment {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover {
|
||||||
|
color: var(--amber);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
margin: 0 0.375rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
38
ui/src/components/ScopeTrail.tsx
Normal file
38
ui/src/components/ScopeTrail.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import type { Scope } from '../hooks/useScope';
|
||||||
|
import styles from './ScopeTrail.module.css';
|
||||||
|
|
||||||
|
interface ScopeTrailProps {
|
||||||
|
scope: Scope;
|
||||||
|
onNavigate: (path: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ScopeTrail({ scope, onNavigate }: ScopeTrailProps) {
|
||||||
|
const segments: { label: string; path: string }[] = [
|
||||||
|
{ label: 'All Applications', path: `/${scope.tab}` },
|
||||||
|
];
|
||||||
|
|
||||||
|
if (scope.appId) {
|
||||||
|
segments.push({ label: scope.appId, path: `/${scope.tab}/${scope.appId}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope.routeId) {
|
||||||
|
segments.push({ label: scope.routeId, path: `/${scope.tab}/${scope.appId}/${scope.routeId}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className={styles.trail}>
|
||||||
|
{segments.map((seg, i) => (
|
||||||
|
<span key={seg.path} className={styles.segment}>
|
||||||
|
{i > 0 && <span className={styles.separator}>></span>}
|
||||||
|
{i < segments.length - 1 ? (
|
||||||
|
<button className={styles.link} onClick={() => onNavigate(seg.path)}>
|
||||||
|
{seg.label}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<span className={styles.current}>{seg.label}</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user