Files
technitium-console/src/ui/PageTabs.tsx
T
DenozordecandClaude Sonnet 5.5 87281a87d3
CI / check (push) Failing after 37s
CI / image (push) Skipped
Match the Technitium canvas: palette, shell, KPI strips, framed tables
The live UI did not follow the canvas because its base layer differed: dark
palette in hex (now the canvas oklch values), 13 px body text, flat Frame panels.

- Tokens: canvas palette, 14 px body, 1.4 line height; Frame without `dense`
  gets the 3 px ring; Panel takes a description line.
- Shell: 56 px brand button with version, 11 px group headings, user card at the
  foot, versions moved into the header status popover, Home / group / section
  crumbs, amber "Attention" status when blocking is off or paused.
- New KpiStrip and PageTabs; Dashboard KPIs in three frames and a Refresh button.
- Zones: KPI strip, tab with count, views, framed table with the canvas columns.
- Apps: Installed / App Store tabs and a grid instead of cards (store inline).
- Blocking: state banner and tab counts; one "Blocking" title.
- Settings: actions in the header, label-and-help rows with the control on the right.
- DHCP, Administration, DNS Client, Logs, Cache, About: subtitles, framed tables,
  KPI strips where the data exists; Login cover with the three-point list.
- docs/compose.shnt.example.yaml: production compose with the console image.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-27 12:47:27 +07:00

37 lines
872 B
TypeScript

import styles from './PageTabs.module.css'
/*
The canvas's page tabs: an underline under the active one and a small count after
its name. They switch a panel on the same screen (an ARIA tablist), unlike
`SubTabs`, whose entries are routes.
*/
export function PageTabs<T extends string>({
label,
tabs,
active,
onChoose,
}: {
label: string
tabs: { id: T; label: string; count?: number }[]
active: T
onChoose: (id: T) => void
}) {
return (
<div className={styles.tabs} role="tablist" aria-label={label}>
{tabs.map((t) => (
<button
key={t.id}
type="button"
role="tab"
aria-selected={t.id === active}
className={styles.tab}
onClick={() => onChoose(t.id)}
>
{t.label}
{t.count != null && <em>{t.count}</em>}
</button>
))}
</div>
)
}