Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 1x 6x 6x 6x 1x 1x 1x 6x 5x 6x | "use client";
/**
* The full value of a truncated row (SPEC.md decision #4).
*
* An **overlay**, not a taller row. Every scroll offset in `useRowWindow` is
* `index × rowHeight`; letting a row grow would invalidate all of them and put
* this package back in measured-virtualization territory — the "different,
* much larger problem" `@usefy/diff-viewer` documented and declined. Wrapping
* the value across several fixed-height rows was the other candidate and is
* worse still: the row count would then depend on the container's width, so a
* resize would silently change `rowCount()`.
*/
import { useEffect, useRef } from "react";
import { useEventListener } from "@usefy/use-event-listener";
import { useOnClickOutside } from "@usefy/use-on-click-outside";
import styles from "./styles/JsonViewer.module.scss";
export interface ValueOverlayProps {
text: string;
/** Position within the viewer root, in pixels. */
top: number;
left: number;
/** Row label, for the accessible name. */
label: string;
onCopy: () => void;
onClose: () => void;
}
export function ValueOverlay({
text,
top,
left,
label,
onCopy,
onClose,
}: ValueOverlayProps) {
const ref = useRef<HTMLDivElement>(null);
useOnClickOutside(ref, onClose);
useEventListener("keydown", (event: KeyboardEvent) => {
Eif (event.key === "Escape") {
event.stopPropagation();
onClose();
}
});
// Focus moves in so the value is reachable by keyboard and by a screen
// reader; `JsonViewer` returns focus to the row that opened it.
useEffect(() => {
ref.current?.focus();
}, []);
return (
<div
ref={ref}
role="dialog"
aria-label={`Full value of ${label || "this node"}`}
tabIndex={-1}
className={styles.overlay}
style={{ top, left }}
data-testid="json-value-overlay"
>
<div className={styles.overlayBar}>
<span>{`${text.length.toLocaleString()} characters`}</span>
<button type="button" className={styles.button} onClick={onCopy}>
Copy
</button>
<button type="button" className={styles.button} onClick={onClose}>
Close
</button>
</div>
<p className={styles.overlayText}>{text}</p>
</div>
);
}
|