mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-02 12:55:04 +00:00
- Sessions: All + per-provider logo buttons (detected, cost > 0) that drive the same app-level provider state as the top-bar dropdown, so the two controls can never disagree; aria-pressed group semantics - Spend By project: rows expand inline (single-open, aria-expanded, chevron rotation) to the project's sessions: date, top model, calls, currency-aware cost; date-only strings parsed at local noon so days never roll across time zones 194/194, typecheck + build green.
58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import type { CSSProperties, KeyboardEvent, ReactNode } from 'react'
|
||
|
||
export { seriesColorForModel } from '../lib/modelSeries'
|
||
|
||
/**
|
||
* A `.li` list row: optional rank `.no`, optional model series `.mdot`, a
|
||
* title + sub line `.lx`, an optional right-aligned `.val`. When `onClick` is
|
||
* provided the row becomes a keyboard-operable button and shows the trailing
|
||
* chevron; without it the row is inert and the chevron is omitted (honest).
|
||
* Pass `expanded` on an interactive row to reflect its open/closed state
|
||
* (`aria-expanded`, which also rotates the chevron).
|
||
*/
|
||
export function ListRow({
|
||
no,
|
||
dotColor,
|
||
title,
|
||
sub,
|
||
value,
|
||
valueClass,
|
||
onClick,
|
||
expanded,
|
||
}: {
|
||
no?: ReactNode
|
||
dotColor?: string
|
||
title: ReactNode
|
||
sub?: ReactNode
|
||
value?: ReactNode
|
||
valueClass?: string
|
||
onClick?: () => void
|
||
expanded?: boolean
|
||
}) {
|
||
const dot: CSSProperties | undefined = dotColor ? { background: dotColor } : undefined
|
||
const interactive = onClick !== undefined
|
||
const onKeyDown = interactive
|
||
? (event: KeyboardEvent<HTMLDivElement>) => {
|
||
if (event.key === 'Enter' || event.key === ' ') {
|
||
event.preventDefault()
|
||
onClick()
|
||
}
|
||
}
|
||
: undefined
|
||
return (
|
||
<div
|
||
className={interactive ? 'li li-clickable' : 'li'}
|
||
{...(interactive ? { role: 'button', tabIndex: 0, onClick, onKeyDown } : {})}
|
||
{...(interactive && expanded !== undefined ? { 'aria-expanded': expanded } : {})}
|
||
>
|
||
{no !== undefined && <span className="no">{no}</span>}
|
||
{dotColor !== undefined && <span className="mdot" style={dot} />}
|
||
<div className="lx">
|
||
<b>{title}</b>
|
||
{sub !== undefined && <span>{sub}</span>}
|
||
</div>
|
||
{value !== undefined && <span className={valueClass ? `val ${valueClass}` : 'val'}>{value}</span>}
|
||
{interactive && <span className="chev">›</span>}
|
||
</div>
|
||
)
|
||
}
|