marmar-context / web / context / src / clinician / patient / SourceDrawer.tsx
SourceDrawer.tsx
Raw
import { useEffect, useRef } from "react";
import { clinicianApi } from "../../api/client";
import { cn, Drawer, StatusPill } from "../kit";
import { ErrorNote, Spinner, useLoad } from "../ui";

export type SourceRef = { recordId: string; title: string; lines: number[]; note?: string };

/** REV-1: a fact's source, opened with its line highlighted. */
export function SourceDrawer({ source, onClose }: { source: SourceRef | null; onClose: () => void }) {
  return (
    <Drawer open={!!source} title={source?.title ?? "Source"} onClose={onClose}>
      {source && <Lines source={source} />}
    </Drawer>
  );
}

function Lines({ source }: { source: SourceRef }) {
  const lines = useLoad(() => clinicianApi.recordLines(source.recordId), [source.recordId]);
  const first = useRef<HTMLLIElement>(null);
  useEffect(() => {
    first.current?.scrollIntoView({ block: "center" });
  }, [lines.data]);
  return (
    <>
      {source.note && <p className="m-0 text-[13px] leading-[19px] text-muted">{source.note}</p>}
      {lines.loading && <Spinner />}
      <ErrorNote error={lines.error} onRetry={() => lines.reload()} />
      {lines.data && (
        <ol className="m-0 flex list-none flex-col p-0 text-[13.5px] leading-5 text-body">
          {lines.data.lines.map((l) => {
            const lit = source.lines.includes(l.number);
            return (
              <li
                key={l.number}
                ref={lit && l.number === source.lines[0] ? first : undefined}
                aria-current={lit ? "true" : undefined}
                className={cn(
                  "grid grid-cols-[52px_minmax(0,1fr)] items-start gap-x-3 gap-y-1 rounded-lg px-2 py-1.5",
                  lit ? "bg-amber-fill text-ink shadow-[inset_2px_0_0_var(--color-amber-icon)]" : l.hardToRead && "bg-tile",
                )}
              >
                <span className="pt-px text-right font-mono text-[11.5px] text-subtle tabular-nums">
                  {l.page ? `p${l.page}·` : ""}
                  {l.number}
                </span>
                <span className="min-w-0 break-words whitespace-pre-wrap">{l.text || " "}</span>
                {l.hardToRead && (
                  <span className="col-start-2">
                    <StatusPill tone="amber" icon="error-warning-fill" size="sm">
                      Hard to read{l.handwritten ? " · handwritten" : ""}
                    </StatusPill>
                  </span>
                )}
              </li>
            );
          })}
        </ol>
      )}
    </>
  );
}