Polish pass: removed lastTouched dead code, selective useWatch perf, token sweep, required-field messages, scroll-to-first-error, file prefill display, matrix sticky shadow
This commit is contained in:
@@ -14,10 +14,11 @@ interface MatrixGroupProps {
|
||||
* - Row labels live in the leftmost column (sticky on horizontal scroll).
|
||||
* - Column headers carry the period labels (M1..M12, Q1..Q4, …).
|
||||
* - Each row's `type` controls the cell input rendering: currency cells get
|
||||
* a $ prefix on the row label rather than per-cell to save space; percent
|
||||
* a $ suffix on the row label rather than per-cell to save space; percent
|
||||
* cells append %.
|
||||
* - On narrow viewports the table scrolls horizontally; the metric column
|
||||
* stays pinned via position:sticky.
|
||||
* stays pinned via position:sticky with a paper background and a hairline
|
||||
* ink rule on its right edge so the boundary reads cleanly during scroll.
|
||||
*
|
||||
* Cells are still individual react-hook-form fields, registered by
|
||||
* `field.fields[colIndex]`. The matrix only changes layout — submission and
|
||||
@@ -27,14 +28,17 @@ export function MatrixGroup({ group, register }: MatrixGroupProps) {
|
||||
return (
|
||||
<section
|
||||
aria-labelledby={`${group.id}-heading`}
|
||||
className="rounded-xl border border-stone-200 bg-white p-4 shadow-sm sm:p-5"
|
||||
className="rounded-lg border border-rule bg-paper p-4 shadow-sm sm:p-5"
|
||||
>
|
||||
<header className="mb-3">
|
||||
<h3 id={`${group.id}-heading`} className="text-base font-semibold text-stone-900">
|
||||
<h3
|
||||
id={`${group.id}-heading`}
|
||||
className="font-display text-lg font-medium leading-tight tracking-tight text-ink"
|
||||
>
|
||||
{group.label}
|
||||
</h3>
|
||||
{group.intro && (
|
||||
<p className="mt-1 text-sm text-stone-600 leading-relaxed">{group.intro}</p>
|
||||
<p className="mt-1 text-sm text-ink-soft leading-relaxed">{group.intro}</p>
|
||||
)}
|
||||
</header>
|
||||
|
||||
@@ -44,7 +48,7 @@ export function MatrixGroup({ group, register }: MatrixGroupProps) {
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
className="sticky left-0 z-10 bg-stone-50 border-b border-stone-200 px-3 py-2 text-left font-medium text-stone-700"
|
||||
className="sticky left-0 z-10 bg-paper-2 border-b border-rule px-3 py-2 text-left text-xs font-medium uppercase tracking-wide text-ink-mute shadow-[1px_0_0_0_var(--color-rule)]"
|
||||
>
|
||||
Metric
|
||||
</th>
|
||||
@@ -52,7 +56,7 @@ export function MatrixGroup({ group, register }: MatrixGroupProps) {
|
||||
<th
|
||||
key={c.key}
|
||||
scope="col"
|
||||
className="border-b border-stone-200 bg-stone-50 px-2 py-2 text-center font-medium text-stone-700 min-w-[5.5rem]"
|
||||
className="border-b border-rule bg-paper-2 px-2 py-2 text-center text-xs font-medium uppercase tracking-wide text-ink-mute min-w-[5.5rem] tabular-nums"
|
||||
>
|
||||
{c.label}
|
||||
</th>
|
||||
@@ -60,29 +64,38 @@ export function MatrixGroup({ group, register }: MatrixGroupProps) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{group.rows.map((row, rowIdx) => (
|
||||
<tr key={rowIdx} className={rowIdx % 2 === 0 ? "bg-white" : "bg-stone-50/40"}>
|
||||
<th
|
||||
scope="row"
|
||||
className="sticky left-0 z-10 border-b border-stone-100 bg-inherit px-3 py-1.5 text-left font-medium text-stone-800 whitespace-nowrap"
|
||||
>
|
||||
{decoratedRowLabel(row.label, row.type)}
|
||||
</th>
|
||||
{row.fields.map((fieldName, colIdx) => (
|
||||
<td
|
||||
key={`${rowIdx}-${colIdx}`}
|
||||
className="border-b border-stone-100 px-1 py-1"
|
||||
{group.rows.map((row, rowIdx) => {
|
||||
const zebra = rowIdx % 2 === 1;
|
||||
return (
|
||||
<tr key={rowIdx}>
|
||||
<th
|
||||
scope="row"
|
||||
className={
|
||||
"sticky left-0 z-10 border-b border-rule-soft px-3 py-1.5 text-left text-sm font-medium text-ink whitespace-nowrap shadow-[1px_0_0_0_var(--color-rule)] " +
|
||||
(zebra ? "bg-paper-2/50" : "bg-paper")
|
||||
}
|
||||
>
|
||||
<CellInput
|
||||
fieldName={fieldName}
|
||||
type={row.type}
|
||||
label={`${row.label} ${group.cols[colIdx]?.label ?? ""}`.trim()}
|
||||
register={register}
|
||||
/>
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
{decoratedRowLabel(row.label, row.type)}
|
||||
</th>
|
||||
{row.fields.map((fieldName, colIdx) => (
|
||||
<td
|
||||
key={`${rowIdx}-${colIdx}`}
|
||||
className={
|
||||
"border-b border-rule-soft px-1 py-1 " +
|
||||
(zebra ? "bg-paper-2/50" : "bg-paper")
|
||||
}
|
||||
>
|
||||
<CellInput
|
||||
fieldName={fieldName}
|
||||
type={row.type}
|
||||
label={`${row.label} ${group.cols[colIdx]?.label ?? ""}`.trim()}
|
||||
register={register}
|
||||
/>
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -111,7 +124,7 @@ interface CellInputProps {
|
||||
function CellInput({ fieldName, type, label, register }: CellInputProps) {
|
||||
const isNumeric = type === "currency" || type === "percent" || type === "number";
|
||||
const baseClass =
|
||||
"w-full rounded border border-stone-200 bg-white px-2 py-1 text-right text-stone-900 " +
|
||||
"w-full rounded border border-rule-soft bg-paper px-2 py-1 text-right text-ink " +
|
||||
"tabular-nums shadow-sm transition " +
|
||||
"focus:border-leaf-500 focus:outline-none focus:ring-2 focus:ring-leaf-500/30";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user