/** * CreatePageModal * * The "Create a new page" modal, opened from the PageNavigator's "+ New page". * Single-screen design: pick a starting point, name the page (which derives the * URL slug), optionally open an Advanced panel, then "Create page". * * NOTE: This uses a lightweight custom modal shell (backdrop + panel) rather * than the PDS `Modal`. The PDS v2 `Modal` (through alpha.16) calls * `useOverlayContext()` on every render, which throws unless an * `OverlayContextProvider` is an ancestor — but that provider is not exported * from the package, making `Modal` unusable here. Interactive fields use native * inputs (the PDS TextInput/Textarea are not usable in this context either) and * are styled with PDS design tokens. * * Phase 1 scope: * - Starting points: "Blank page" is active; "Content type template" and * "Generate with AI" are disabled; "New page template" is admin-only and * disabled. Later phases enable the rest. * - Page title auto-derives the slug until the slug is edited manually. * - Once the slug is edited manually, it is typed free-text (no live * stripping — mangling characters as the user types made it impossible to * land the cursor mid-string and insert a `/`); the current value is * validated instead, with an inline error and a disabled "Create" button * while it's invalid (lowercase letters, digits, `-`, `/` and `:` only, * `:` kept so a dynamic route segment like `:category` can be typed). * - The Advanced panel is rendered as designed but fully disabled. * - "Create page" creates the page via onCreateDocument(slug, title); the * title is persisted into the new page's root.props.title (see useDocuments). */ import React from 'react'; import type { LocaleFieldOption } from './LocaleField.js'; /** * A content-type template as the modal consumes it. Intentionally a minimal, * local shape (not the feature's full `Template` type) to keep this PDS * component decoupled — the editor maps real templates into this on the way in. */ export interface CreatePageModalTemplate { id: string; /** Kebab identifier; shown when the template has no label. */ name: string; label: string; description?: string; /** Route pattern whose `:params` become user-filled route inputs. */ defaultUrlPattern?: string; version: number; } export interface CreatePageModalProps { /** Whether the modal is open. */ open: boolean; /** Called when the modal should close (close button, Cancel, backdrop, Escape, or success). */ onClose: () => void; /** * Called with the new page path and the entered page title when the user * creates a page. When created from a content-type template, the template id * is passed so the page is scaffolded from it and bound to it. A locale is * passed only when one was chosen, which creates the page in that market * alone. Should resolve on success and reject with an Error whose message is * shown on failure. */ onCreateDocument: (path: string, title: string, templateId?: string, locale?: string) => Promise; /** * The site's configured market locales. With none, no locale field is offered * and no page can be translated. */ locales?: LocaleFieldOption[]; /** * The site's locales could not be read, so an empty `locales` means unknown * rather than none. */ localesFailed?: boolean; /** Ask for the site's locales again. */ onRetryLocales?: () => void; /** * Pages a translation can start from — canonicals, since a translation hangs * off one. Titles are shown where a page has one, paths otherwise. */ translatablePages?: { id: string; path: string; title?: string; }[]; /** * Create a locale version of an existing page. Omit to leave the translate * tile disabled. */ onCreateTranslation?: (params: { sourceDocumentId: string; locale: string; mode: 'copy'; }) => Promise; /** * Whether the current user is an administrator. Reserved for gating admin-only * options (e.g. "New page template") once permission checks are added — not * enforced yet; admin-only tiles are currently shown to everyone. */ isAdmin?: boolean; /** * Host shown as the URL-slug prefix and in the full-path preview (e.g. "example.com"). * TODO: source this from the site's domain once the css-client `Site` type / API * exposes it (it currently does not). For now it defaults to the current window host. */ siteHost?: string; /** * Available data sources for the (exploratory) Dynamic route / collection builder * dropdown — both user-defined and built-in. `inputs` are the param names the * source expects (mocked for now); one input auto-generates the route param. */ datasources?: { id: string; label: string; inputs?: string[]; }[]; /** * Real content-type templates from the backend. The modal has NO built-in/fake * list: when there are none (undefined or empty), the "Page type template" * section shows an empty message — a customer running P1 with zero templates * is a normal state. The "New template" action is always available. */ templates?: CreatePageModalTemplate[]; /** * Create a new template from the "New template" screen. Receives the kebab * `name`, `label`, and optional `description` / `defaultUrlPattern`. Should * resolve with the created template (its `name` is used to open its editor). */ onCreateTemplate?: (params: { name: string; label: string; description?: string; defaultUrlPattern?: string; }) => Promise<{ name: string; } | undefined>; /** * Navigate the editor to an existing page path (no creation). Used by the * collection recap's "Edit index page" / "Edit dynamic page" buttons. */ onNavigate?: (path: string) => void; /** * Initial screen when the modal opens. `'new-template'` lands directly on the * New-template definition form (equivalent to choosing "From page template" → * "+ New template"). `'translate'` lands on the translate starting point, * where `initialLocale` and `initialSourceDocumentId` preselect what is being * translated. `'page'` (default) shows the starting-point grid. */ initialMode?: 'page' | 'new-template' | 'translate'; /** Market preselected on whichever starting point the modal opens on. */ initialLocale?: string; /** Page preselected as what the translation starts from. */ initialSourceDocumentId?: string; /** * Hand a "Generate with AI" brief to the chatbot, along with the page the user wants. The * page is not created here: the chat settles which template it starts from first, and a * document's template can only be set as it is created. Omit to leave the tile a placeholder. */ onGenerateWithAI?: (brief: string, page: { path: string; title: string; }) => void; /** Checked by Generate with AI before handing off, since it creates nothing here to be refused. */ isPathTaken?: (path: string) => Promise; } export declare function CreatePageModal({ open, ...props }: CreatePageModalProps): React.JSX.Element | null;