import { useEffect, useRef, useState } from "react";
import { IconExternalLink, IconMail } from "@tabler/icons-react";
import { FieldProps, FieldShell } from "./detail-field";
import classes from "@/ee/base/styles/row-detail-modal.module.css";
const toText = (value: unknown) => (typeof value === "string" ? value : "");
export function FieldText({ property, value, readOnly, onChange }: FieldProps) {
const text = toText(value);
const [draft, setDraft] = useState(text);
const [focused, setFocused] = useState(false);
// Esc sets this; blur() then runs commit synchronously with the stale
// draft, so the revert must be decided here, not via setDraft.
const cancelRef = useRef(false);
// Track remote/navigation updates while not typing.
useEffect(() => {
if (!focused) setDraft(text);
}, [text, focused]);
const commit = () => {
setFocused(false);
if (cancelRef.current) {
cancelRef.current = false;
setDraft(text);
return;
}
if (draft !== text) onChange(draft);
};
if (readOnly) {
return (
{text}
);
}
const linkHref =
!focused && text
? property.type === "email"
? text.includes("@")
? `mailto:${text}`
: null
: property.type === "url" && /^https?:\/\//i.test(text)
? text
: null
: null;
return (
setFocused(true)}
onChange={(e) => setDraft(e.currentTarget.value)}
onBlur={commit}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
e.currentTarget.blur();
} else if (e.key === "Escape") {
cancelRef.current = true;
e.currentTarget.blur();
}
}}
aria-label={property.name}
/>
{linkHref && (
e.stopPropagation()}
aria-label={property.type === "email" ? `Email ${text}` : `Open ${text}`}
>
{property.type === "email" ? (
) : (
)}
)}
);
}