Files
siftlode/frontend/src/components/LogLine.tsx
T
peter 3e7c79fa69 fix(design): the keyboard ring of a flush row stays visible (E28)
A list item's ring sat 2 px outside it. On a table row it ran under the
sticky head and past the table's sides; on a Plex playlist show header, flush
in its clipping card, it was cut away and the focus was invisible. A table row
(by its tag), a log line and the Plex show header and episode rows now ring
inside themselves, the show header with its card's corners.

The sticky header's fade-below was drawn at rest, over the first row's top
16 px, hiding the ring's top edge: it now fades in with the first 1rem of
scroll (a scroll-driven animation; no fade where unsupported).
2026-09-26 20:01:04 +02:00

65 lines
2.2 KiB
TypeScript

import type { ReactNode } from "react";
import type { Level } from "../lib/log";
import { navItemAttrs } from "../lib/listNav";
import { LEVEL_TAG, logClock } from "./logLineParts";
import { Pressable } from "./ui/Pressable";
// One log line as both log viewers draw it — "Saját napló" (this tab's ring) and the admin
// diagnostics console (the server's stored log): time, level, event code, message, and the line opens
// in place to whatever detail its screen has. One component, so a level reads the same on both.
// Inside a NavGrid the keyboard walks the lines (`j`/`k`) and Enter opens one, as a click does.
export default function LogLine({
ts,
level,
event,
msg,
open,
onToggle,
title,
navIndex,
children,
}: {
ts: string;
level: Level;
event: string;
msg: string;
open: boolean;
onToggle: () => void;
/** The row button's tooltip — the admin console puts the full date there. */
title?: string;
/** The line's place in its NavGrid. */
navIndex: number;
/** The detail shown while the line is open. */
children: ReactNode;
}) {
const tag = LEVEL_TAG[level];
return (
<div
className="border-b border-border/50 last:border-b-0"
{...navItemAttrs(navIndex, `${event} ${msg}`, { inset: true })}
>
<Pressable
selectable
layer={!open}
data-nav-open
aria-expanded={open}
onClick={onToggle}
title={title}
className={`grid w-full grid-cols-[6.5em_3.5em_minmax(0,1fr)] items-baseline sm:grid-cols-[6.5em_3.5em_minmax(0,10em)_minmax(0,1fr)] gap-x-2 px-3 py-1 touch-target pointer-coarse:content-center ${
open ? "bg-accent/10" : ""
}`}
>
<span className="tabular-nums text-muted">{logClock(ts)}</span>
<span className={`font-semibold ${tag.className}`}>{tag.text}</span>
<span className="truncate">{event}</span>
{/* A phone has no room for a fourth column: the message takes a line of its own under the
code, full width — sharing the code's line left it a few characters. */}
<span className="col-span-3 truncate sm:col-span-1">{msg}</span>
</Pressable>
{open && <div className="bg-accent/5 px-3 py-2 text-muted">{children}</div>}
</div>
);
}