citation-work/src/work/EvidenceFormBody.tsx
tegwick 0cd2ca5d04
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 3s
feat: extract review-workspace slice into @citation-evidence/work (CWORK-WP-0001)
Establishes citation-work as the standalone home of the review workspace,
migrated out of the citation-evidence umbrella app.

- Package/tooling scaffold: package.json, tsconfig, vite/vitest/eslint configs
  with eslint-plugin-boundaries enforcing engine/anchor/source-only imports
- Providers/hooks: EngineProvider, SessionProvider + use* hooks (T02)
- Panes: CollectionList, ViewerShell, EvidenceSidebar (T03/T04/T05)
- Capture/edit: InlineCaptureForm, EvidenceFormBody; UploadDropzone owned here
- ReviewShell: repo-owned three-pane layout with an upload slot seam
- Tests: CollectionList, InlineCaptureForm capture flow, EvidenceSidebar
  export/edit/activation (11 tests, all green)
- Anchor consumed as @citation-evidence/evidence-anchor package; @source kept
  on the umbrella facade for its local viewer-url policy
- Docs (README/SCOPE) refreshed; deferred gaps recorded

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 01:49:28 +02:00

104 lines
3.1 KiB
TypeScript

/**
* EvidenceFormBody — the shared "citation + commentary" editor.
*
* One form drives both:
* - the *capture* flow (creating evidence from a fresh selection
* inside `InlineCaptureForm`), and
* - the *edit* flow (modifying an existing evidence card inside
* `EvidenceSidebar`).
*
* Keeping a single component means changes to the field layout, hint
* placement, or save-button copy land in one file and apply to both
* surfaces. Callers control the labels and any badge/helper text.
*/
import type { CSSProperties, ReactNode } from "react";
export interface EvidenceFormBodyProps {
readonly quote: string;
readonly commentary: string;
onChangeQuote(next: string): void;
onChangeCommentary(next: string): void;
onSave(): void;
onCancel(): void;
/** Save-button label, defaults to "Save". */
readonly saveLabel?: string;
/** Cancel-button label, defaults to "Cancel". */
readonly cancelLabel?: string;
/** Short caption rendered at the top of the form, e.g. a selector
* count badge or "Editing". */
readonly badge?: ReactNode;
/** Inline note shown under the buttons (e.g. "won't move the
* marked passage" when editing an existing item). */
readonly helper?: ReactNode;
/** data-testid prefix for the input + button hooks. */
readonly testidPrefix: string;
}
export function EvidenceFormBody(p: EvidenceFormBodyProps) {
const saveLabel = p.saveLabel ?? "Save";
const cancelLabel = p.cancelLabel ?? "Cancel";
return (
<div style={{ padding: 8, fontSize: 12 }}>
{p.badge && (
<div style={{ marginBottom: 6, fontWeight: 600 }}>{p.badge}</div>
)}
<label style={labelStyle}>Citation text</label>
<textarea
value={p.quote}
onChange={(e) => p.onChangeQuote(e.target.value)}
data-testid={`${p.testidPrefix}-quote`}
rows={3}
style={{
...textareaStyle,
fontStyle: "italic",
}}
/>
<label style={labelStyle}>Commentary</label>
<textarea
value={p.commentary}
onChange={(e) => p.onChangeCommentary(e.target.value)}
data-testid={`${p.testidPrefix}-commentary`}
rows={2}
placeholder="(optional)"
style={textareaStyle}
/>
<div style={{ display: "flex", gap: 6, alignItems: "center" }}>
<button
type="button"
onClick={p.onSave}
data-testid={`${p.testidPrefix}-save`}
style={{ fontSize: 12, padding: "4px 10px" }}
>
{saveLabel}
</button>
<button
type="button"
onClick={p.onCancel}
data-testid={`${p.testidPrefix}-cancel`}
style={{ fontSize: 12, padding: "4px 10px" }}
>
{cancelLabel}
</button>
{p.helper && (
<span style={{ fontSize: 11, color: "#888" }}>{p.helper}</span>
)}
</div>
</div>
);
}
const labelStyle: CSSProperties = {
display: "block",
color: "#666",
fontSize: 11,
marginBottom: 2,
};
const textareaStyle: CSSProperties = {
width: "100%",
boxSizing: "border-box",
fontSize: 12,
padding: 4,
marginBottom: 6,
};