import { useCallback, useRef, useState } from "react"; import { getDescriptor } from "@/ee/base/property-types/property-type.registry"; import { FieldProps, FieldShell } from "./detail-field"; import classes from "@/ee/base/styles/row-detail-modal.module.css"; /** Person, file and page editors are popover pickers owned by their cell * components; the shell supplies modal styling and click-anywhere * activation while the cell keeps its picker behavior. */ export function FieldCellAdapter({ property, value, rowId, readOnly, onChange, }: FieldProps) { const [editing, setEditing] = useState(false); // Whether the picker was open when the current gesture's mousedown fired. const editingAtMouseDownRef = useRef(false); const CellComponent = getDescriptor(property.type)?.cellComponent; // Files stay openable read-only (download-only popover), matching the grid. const canActivate = !readOnly || property.type === "file"; // Activate on click, not mousedown: opening on mousedown mounts the cell's // picker mid-dispatch, and its document-level outside-mousedown listener // then catches the same still-bubbling event and instantly closes it. By // click time the mousedown has fully finished. The ref keeps toggle-close // working: when the gesture started with the picker open, the picker's own // outside-close already handled it and we must not reopen. const handleMouseDown = useCallback(() => { editingAtMouseDownRef.current = editing; }, [editing]); const handleClick = useCallback(() => { if (!canActivate || editingAtMouseDownRef.current || editing) return; setEditing(true); }, [canActivate, editing]); const handleCommit = useCallback( (next: unknown) => { setEditing(false); onChange(next); }, [onChange], ); const handleCancel = useCallback(() => setEditing(false), []); if (!CellComponent) return ; return ( { if (canActivate && !editing && (e.key === "Enter" || e.key === " ")) { e.preventDefault(); setEditing(true); } }} >
); }