Files
docmost/apps/client/src/ee/base/components/row-detail-modal/row-detail-title.tsx
T
2026-09-01 19:16:46 +01:00

83 lines
2.3 KiB
TypeScript

import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { IBaseProperty, IBaseRow } from "@/ee/base/types/base.types";
import { timeAgo } from "@/lib/time.ts";
import classes from "@/ee/base/styles/row-detail-modal.module.css";
type RowDetailTitleProps = {
row: IBaseRow;
primaryProperty: IBaseProperty | undefined;
canEdit: boolean;
onCommit: (value: string) => void;
onEditingChange?: (editing: boolean) => void;
};
export function RowDetailTitle({
row,
primaryProperty,
canEdit,
onCommit,
onEditingChange,
}: RowDetailTitleProps) {
const { t } = useTranslation();
const initial = primaryProperty
? (((row.cells ?? {})[primaryProperty.id] as string) ?? "")
: "";
const [value, setValue] = useState(initial);
const cancelRef = useRef(false);
const commit = () => {
onEditingChange?.(false);
if (cancelRef.current) {
cancelRef.current = false;
setValue(initial);
return;
}
if (value !== initial) onCommit(value);
};
// Re-sync when the row changes underneath us (navigation or remote edit).
useEffect(() => {
setValue(initial);
}, [initial]);
const updatedAgo = row.updatedAt ? timeAgo(new Date(row.updatedAt)) : "";
return (
<header className={classes.header}>
{canEdit ? (
<input
type="text"
className={classes.titleInput}
{...(!initial ? { "data-autofocus": true } : {})}
placeholder={t("Untitled")}
aria-label={primaryProperty?.name ?? t("Untitled")}
value={value}
maxLength={1000}
onFocus={() => {
onEditingChange?.(true);
}}
onChange={(e) => setValue(e.currentTarget.value)}
onBlur={commit}
onKeyDown={(e) => {
if (e.key === "Escape") {
cancelRef.current = true;
e.currentTarget.blur();
} else if (e.key === "Enter") {
e.preventDefault();
e.currentTarget.blur();
}
}}
/>
) : (
<h1 className={classes.titleStatic}>{value || t("Untitled")}</h1>
)}
{updatedAgo && (
<div className={classes.metaRow}>
<span>{t("Updated {{when}}", { when: updatedAgo })}</span>
</div>
)}
</header>
);
}