import { ReactNode } from 'react'; import { ContentItem, PopupModal, SubmitError } from '../schema/index.ts'; import { SubmitOutcome } from './submit'; import { DropEdge } from '../dragSort'; import { CardPreview } from '../cardResize'; export interface PopupContentProps { popup: PopupModal; /** Called when the popup requests to close (X, overlay, esc, auto-close). */ onClose?: () => void; /** Injectable fetch — the builder passes a mock so preview submits never hit a real API. */ fetchImpl?: typeof fetch; /** Preview mode: renders the form frame but blocks real navigation on redirect. */ preview?: boolean; /** * Preview-only: fired with a content item's id when its rendered element is * clicked, so the builder can reveal that item's editor — and with `null` * when the click landed on empty card space, which clears the selection. * Ignored in production. */ onItemActivate?: (id: string | null) => void; /** * Builder-only: the item the editor currently has selected. It gets the * selection outline in the canvas. */ selectedItemId?: string | null; /** * Builder-only: commit a text edit made straight in the rendered form. * Passing it is what turns the canvas into an editing surface: labels and * copy become editable in place, and the form's own controls go inert so a * click selects the field instead of filling it in. */ onItemEdit?: (id: string, patch: Partial) => void; /** * Builder-only: a canvas drag dropped `draggedId` on `edge` of `targetId` — * a side edge meaning "beside it, sharing its row", above or below meaning a * line of its own. Widths follow from that, so there is nothing else to pass. */ onItemReorder?: (draggedId: string, targetId: string, edge: DropEdge) => void; /** * Builder-only: hands the form body out as it mounts, so the builder can aim * a drag that started outside the canvas at the items rendered inside it — * an element dragged in from the sidebar picker. The renderer keeps no * opinion about that gesture; it only lends the surface and, through * `dropHint` below, draws where it would land. */ canvasRef?: (el: HTMLElement | null) => void; /** * Builder-only: where an element the *builder* is dragging would land. Set * only while such a drag is over the canvas; `overId` null then means the * pointer is on the form but not on any item. Drawn with the same line a * reorder draws, because to the author it is the same drop. */ dropHint?: { overId: string | null; edge: DropEdge | null; } | null; /** * Builder-only: a canvas resize settled the submit button on `width` percent * of the form body. Passing it is what puts the grip on the button. Fields * take their widths from what shares their row, so only the button has one. */ onItemResize?: (id: string, width: number) => void; /** * Builder-only: a canvas drag settled a spacer on `height` px. Passing it is * what puts the grip on a spacer's bottom edge. Its own kind of resize: a * spacer's height is the item's, not its style group's, so two spacers on one * form are two separate gaps. */ onSpacerResize?: (id: string, height: number) => void; /** * Builder-only: a canvas drag settled the card's own measurements — how wide * it is, how tall it is at minimum, how far its content sits from the edge, * how round its corners are. Passing it is what puts the handles on the card. */ onCardResize?: (patch: CardPreview) => void; /** * Builder-only: throw the item away, asked for from its own toolbar in the * canvas. Passing it is what puts the delete tool in that toolbar; without * it the bar is just the grip. */ onItemRemove?: (id: string) => void; /** * Builder-only: the items the host pinned to every form. They keep the grip * and lose the delete tool — the renderer has no rules of its own, so which * ones those are comes from the builder, which does. */ pinnedItemIds?: readonly string[]; /** * Builder-only: what an item's own tools call themselves, translated. Same * arrangement as `cardHandleLabels`: unset falls back to English. */ itemToolLabels?: Partial>; /** * Builder-only: what to call each card handle, translated. The renderer ships * without a dictionary, so the names the handles show on hover come from the * builder, which has one. Unset falls back to English. */ cardHandleLabels?: Partial>; /** Builder-only: the hint an empty inline-editable text shows, translated. */ inlineHint?: string; /** * Builder-only: what a step with nothing on it says, translated. Only ever * reached in the canvas — a live popup skips empty steps rather than showing * the visitor a screen with nothing to do. */ emptyStepHint?: string; /** * Builder-only: the in-place editor for a heading's or paragraph's copy. The * builder passes its rich-text editor through here so the renderer bundle * never imports one; without it, copy edits as plain single-line text. */ renderCopyEditor?: (props: { value: string; hint?: string; onCommit: (next: string) => void; }) => ReactNode; /** * Preview-only: reports the current phase whenever it changes, so the builder * can show its Replay control only once the popup leaves the initial form. */ onPhaseChange?: (kind: Phase['kind']) => void; /** * Builder-only: which step of a step form the canvas is showing. The builder * owns the step while editing — its rail is what moves between them, and the * canvas has to follow a click on an item that lives on another screen. Unset * (a live popup, or the canvas in preview mode) leaves the popup to walk its * own steps as the visitor presses Next. Out-of-range values are clamped, so * deleting the last step while it's open lands on the new last one. */ activeStep?: number; /** * Builder-only: commit a step button's wording, edited in place on the * canvas. Passing it is what makes the Next and Back labels editable there; * without it they render as plain text. The patch carries only the label that * changed, so the host merges it into whatever `steps` already holds. */ onStepLabelEdit?: (patch: { nextLabel?: string; backLabel?: string; }) => void; } type Phase = { kind: 'form'; } | { kind: 'error'; error: SubmitError; } | { kind: 'success'; outcome: SubmitOutcome; }; export declare function PopupContent({ popup, onClose, fetchImpl, preview, onItemActivate, selectedItemId, onItemEdit, onItemReorder, canvasRef, dropHint, onItemResize, onSpacerResize, onCardResize, onItemRemove, pinnedItemIds, itemToolLabels, cardHandleLabels, inlineHint, emptyStepHint, renderCopyEditor, onPhaseChange, activeStep, onStepLabelEdit, }: PopupContentProps): import("react").JSX.Element; export {};