);
}
/** Title on the left, `DetailActions` on the right, on one line. */
export function DetailTitleRow({
children,
className,
...props
}: React.ComponentProps<"div">) {
return (
);
}
/** Edit-in-place field for `DetailTitle` / `DetailDescription`; commits on blur. */
export function DetailInput({
value,
onCommit,
multiline = false,
className,
...props
}: Omit<
React.ComponentProps,
"value" | "defaultValue" | "onBlur" | "onKeyDown" | "onInput"
> & {
value: string;
onCommit: (value: string) => void;
multiline?: boolean;
}) {
// Keyed by the server value: a new one resets the draft as it remounts.
const [draft, setDraft] = useState({ base: value, text: value });
const text = draft.base === value ? draft.text : value;
return (
{/* Invisible mirror in the same cell sizes the textarea in every browser. */}
{text}{" "}
);
}
export function DetailMeta({
children,
className,
...props
}: React.ComponentProps<"div">) {
return (
{children}
);
}
export function DetailMetaItem({
children,
className,
...props
}: React.ComponentProps<"span">) {
return (
{children}
);
}
/** Absolute by default; pass children for a relative or custom label. */
export function DetailMetaTime({
date,
children,
className,
...props
}: Omit, "dateTime"> & { date: Date }) {
// local-time text differs between server and browser
const hydrated = useHydrated();
return (
);
}
export function DetailContent({
children,
className,
...props
}: React.ComponentProps<"div">) {
return (