/** * What the editor's settings column is showing. * * The column has two jobs that cannot both be visible: the tab the author * picked, and a panel that has taken the column over to edit one thing. Only * their contents compete — the tab rail stays up either way, so moving to * another tab is always one click away. A panel is opened by pointing at * something — a field or the card in the canvas — or by asking to add a field, * and it is closed by pressing Esc, by moving to another tab, or by clicking * the canvas away from the card. * * It lives here, apart from the view, because the view cannot be rendered in * this project's test runner (no DOM), and these rules are worth more tests * than a component of that size would otherwise get. */ export type EditorPanel = /** No takeover: the column is showing the active tab. */ { kind: 'none'; } /** One field's settings and design. */ | { kind: 'field'; id: string; } /** The card's design, and the outline of what the form holds. */ | { kind: 'card'; } /** The field picker, which rows can be dragged out of onto the canvas. */ | { kind: 'picker'; }; export type PanelAction = /** * The canvas reported a click. `id` is the item clicked, or `null` for the * card itself — the renderer already sends `null` for a click on empty card * space (see PopupContent's `onClickCapture`), which is exactly this. */ { type: 'canvasClick'; id: string | null; } | { type: 'openPicker'; } | { type: 'close'; } | { type: 'tabChange'; } /** An item was deleted; if its panel is open it has nothing left to show. */ | { type: 'fieldRemoved'; id: string; }; export declare const INITIAL_PANEL: EditorPanel; export declare function panelReducer(state: EditorPanel, action: PanelAction): EditorPanel; /** * A click on the builder's canvas, described by the gesture that produced it * rather than by the element it happened to land on. * * Both ends of the gesture matter, which is why this is not simply "where did * it hit". A press that starts on the card and travels off it is a resize or a * reorder wandering out, not a click away; a click the canvas never saw the * press for is the tail of a drag out of the sidebar picker, which ends over * the canvas by design. */ export interface CanvasClick { /** * Where the press began: on the card, on the canvas around it, or nowhere * the canvas saw — a drag out of the sidebar picker, or a key pressed on a * focused control, neither of which is a press on the canvas at all. */ press: 'card' | 'away' | 'elsewhere'; /** Where the release landed. */ release: 'card' | 'away'; /** The click is the tail of a drag that has just ended, not a click of its own. */ afterDrag: boolean; } /** * Whether a click on the canvas should close the open panel. * * Pointing at the card or a field is what opens a panel, so pointing at * neither is what closes one: the checkered area around the form is the * nearest thing the canvas has to blank space. * * The picker closes on the same terms as the rest, and that is what keeps it * open through a run of adds. An author adding several elements in a row * clicks the picker's own rows and drags out of them, and neither of those is * a click on the canvas away from the card; so the run survives the general * rule and needs no exception carved for it, which is the reason it doesn't * get one. A dismissal rule with an exception is harder to learn than one * without. * * Only a press *and* a release that both land away from the card count, and * never the tail of a drag. Everything the canvas is dragged on — resizing the * card by its handles, dragging a field to another row, dropping a new element * out of the picker — releases somewhere the author did not press, and none of * it is a dismissal. */ export declare function closesOnCanvasClick(panel: EditorPanel, click: CanvasClick): boolean;