import { ReactElement, CSSProperties, ReactNode, ElementType, Ref, JSX } from 'react'; import { EditorStateSnapshot, Editor, Extensions } from '@tiptap/react'; import { BlockquoteOptions } from '@tiptap/extension-blockquote'; import { BoldOptions } from '@tiptap/extension-bold'; import { CodeOptions } from '@tiptap/extension-code'; import { CodeBlockOptions } from '@tiptap/extension-code-block'; import { HardBreakOptions } from '@tiptap/extension-hard-break'; import { HeadingOptions } from '@tiptap/extension-heading'; import { HorizontalRuleOptions } from '@tiptap/extension-horizontal-rule'; import { ItalicOptions } from '@tiptap/extension-italic'; import { LinkOptions } from '@tiptap/extension-link'; import { BulletListOptions, ListItemOptions, ListKeymapOptions, OrderedListOptions } from '@tiptap/extension-list'; import { ParagraphOptions } from '@tiptap/extension-paragraph'; import { StrikeOptions } from '@tiptap/extension-strike'; import { TextAlignOptions } from '@tiptap/extension-text-align'; import { UnderlineOptions } from '@tiptap/extension-underline'; type ItemSelector = { index: number; zone?: string; }; declare const defaultEditorState: (ctx: EditorStateSnapshot, readOnly: boolean) => { isAlignLeft?: undefined; canAlignLeft?: undefined; isAlignCenter?: undefined; canAlignCenter?: undefined; isAlignRight?: undefined; canAlignRight?: undefined; isAlignJustify?: undefined; canAlignJustify?: undefined; isBold?: undefined; canBold?: undefined; isItalic?: undefined; canItalic?: undefined; isUnderline?: undefined; canUnderline?: undefined; isStrike?: undefined; canStrike?: undefined; isInlineCode?: undefined; canInlineCode?: undefined; isBulletList?: undefined; canBulletList?: undefined; isOrderedList?: undefined; canOrderedList?: undefined; isCodeBlock?: undefined; canCodeBlock?: undefined; isBlockquote?: undefined; canBlockquote?: undefined; canHorizontalRule?: undefined; } | { isAlignLeft: boolean; canAlignLeft: boolean; isAlignCenter: boolean; canAlignCenter: boolean; isAlignRight: boolean; canAlignRight: boolean; isAlignJustify: boolean; canAlignJustify: boolean; isBold: boolean; canBold: boolean; isItalic: boolean; canItalic: boolean; isUnderline: boolean; canUnderline: boolean; isStrike: boolean; canStrike: boolean; isInlineCode: boolean; canInlineCode: boolean; isBulletList: boolean; canBulletList: boolean; isOrderedList: boolean; canOrderedList: boolean; isCodeBlock: boolean; canCodeBlock: boolean; isBlockquote: boolean; canBlockquote: boolean; canHorizontalRule: boolean; }; type RichTextSelector = (ctx: EditorStateSnapshot, readOnly: boolean) => Partial>; type DefaultEditorState = ReturnType; type EditorState = DefaultEditorState & ReturnType & Record; interface PuckRichTextOptions { /** * If set to false, the blockquote extension will not be registered * @example blockquote: false */ blockquote: Partial | false; /** * If set to false, the bold extension will not be registered * @example bold: false */ bold: Partial | false; /** * If set to false, the bulletList extension will not be registered * @example bulletList: false */ bulletList: Partial | false; /** * If set to false, the code extension will not be registered * @example code: false */ code: Partial | false; /** * If set to false, the codeBlock extension will not be registered * @example codeBlock: false */ codeBlock: Partial | false; /** * If set to false, the document extension will not be registered * @example document: false */ document: false; /** * If set to false, the hardBreak extension will not be registered * @example hardBreak: false */ hardBreak: Partial | false; /** * If set to false, the heading extension will not be registered * @example heading: false */ heading: Partial | false; /** * If set to false, the horizontalRule extension will not be registered * @example horizontalRule: false */ horizontalRule: Partial | false; /** * If set to false, the italic extension will not be registered * @example italic: false */ italic: Partial | false; /** * If set to false, the listItem extension will not be registered * @example listItem: false */ listItem: Partial | false; /** * If set to false, the listItemKeymap extension will not be registered * @example listKeymap: false */ listKeymap: Partial | false; /** * If set to false, the link extension will not be registered * @example link: false */ link: Partial | false; /** * If set to false, the orderedList extension will not be registered * @example orderedList: false */ orderedList: Partial | false; /** * If set to false, the paragraph extension will not be registered * @example paragraph: false */ paragraph: Partial | false; /** * If set to false, the strike extension will not be registered * @example strike: false */ strike: Partial | false; /** * If set to false, the text extension will not be registered * @example text: false */ text: false; /** * If set to false, the textAlign extension will not be registered * @example text: false */ textAlign: Partial | false; /** * If set to false, the underline extension will not be registered * @example underline: false */ underline: Partial | false; } type FieldOption = { label: string; value: string | number | boolean | undefined | null | object; }; type FieldOptions = Array | ReadonlyArray; interface BaseField { label?: string; labelIcon?: ReactElement; metadata?: FieldMetadata; visible?: boolean; } interface TextField extends BaseField { type: "text"; placeholder?: string; contentEditable?: boolean; } interface NumberField extends BaseField { type: "number"; placeholder?: string; min?: number; max?: number; step?: number; } interface TextareaField extends BaseField { type: "textarea"; placeholder?: string; contentEditable?: boolean; } interface SelectField extends BaseField { type: "select"; options: FieldOptions; } interface RadioField extends BaseField { type: "radio"; options: FieldOptions; } interface RichtextField extends BaseField { type: "richtext"; contentEditable?: boolean; initialHeight?: CSSProperties["height"]; options?: Partial; renderMenu?: (props: { children: ReactNode; editor: Editor | null; editorState: EditorState | null; readOnly: boolean; }) => ReactNode; renderInlineMenu?: (props: { children: ReactNode; editor: Editor | null; editorState: EditorState | null; readOnly: boolean; }) => ReactNode; tiptap?: { selector?: UserSelector; extensions?: Extensions; }; } interface ArrayField extends BaseField { type: "array"; arrayFields: { [SubPropName in keyof Props[0]]: UserField extends { type: PropertyKey; } ? Field | UserField : Field; }; defaultItemProps?: Props[0] | ((index: number) => Props[0]); getItemSummary?: (item: Props[0], index?: number) => ReactNode; max?: number; min?: number; } interface ObjectField extends BaseField { type: "object"; objectFields: { [SubPropName in keyof Props]: UserField extends { type: PropertyKey; } ? Field | UserField : Field; }; } type Adaptor = {}, PropShape = TableShape> = { name: string; fetchList: (adaptorParams?: AdaptorParams) => Promise; mapProp?: (value: TableShape) => PropShape; }; type NotUndefined = T extends undefined ? never : T; type ExternalFieldWithAdaptor = BaseField & { type: "external"; placeholder?: string; adaptor: Adaptor; adaptorParams?: object; getItemSummary: (item: NotUndefined, index?: number) => ReactNode; }; type CacheOpts = { enabled?: boolean; }; interface ExternalField extends BaseField { type: "external"; cache?: CacheOpts; placeholder?: string; fetchList: (params: { query: string; filters: Record; }) => Promise; mapProp?: (value: any) => Props; mapRow?: (value: any) => Record; getItemSummary?: (item: NotUndefined, index?: number) => ReactNode; showSearch?: boolean; renderFooter?: (props: { items: any[]; }) => ReactElement; initialQuery?: string; filterFields?: Record; initialFilters?: Record; } type CustomFieldRender = (props: { field: CustomField; name: string; id: string; value: Value; onChange: (value: Value, uiState?: Partial) => void; readOnly?: boolean; }) => ReactElement; interface CustomField extends BaseField { type: "custom"; render: CustomFieldRender; contentEditable?: boolean; key?: string; } interface SlotField extends BaseField { type: "slot"; allow?: string[]; disallow?: string[]; } type Field = TextField | RichtextField | NumberField | TextareaField | SelectField | RadioField | ArrayField | ObjectField | ExternalField | ExternalFieldWithAdaptor | CustomField | SlotField; type Fields = { [PropName in keyof Omit]: UserField extends { type: PropertyKey; } ? Field | UserField : Field; }; type FieldProps, ValueType = any> = { field: F; value: ValueType; id?: string; onChange: (value: ValueType, uiState?: Partial) => void; readOnly?: boolean; }; type DropZoneProps = { zone: string; allow?: string[]; disallow?: string[]; style?: CSSProperties; minEmptyHeight?: CSSProperties["minHeight"] | number; className?: string; collisionAxis?: DragAxis; as?: ElementType; ref?: Ref; }; type PuckContext = { renderDropZone: (props: DropZoneProps) => React.ReactNode; metadata: Metadata; isEditing: boolean; dragRef: ((element: Element | null) => void) | null; }; type DefaultRootFieldProps = { title?: string; }; /** * Merge the default root field props into user-provided root props, letting * the user's props take precedence. Using a plain intersection would turn an * overridden `title` (e.g. `number`) into `never` (`number & string`). */ type WithDefaultRootFieldProps = RootProps & Omit; type DefaultComponentProps = { [key: string]: any; }; type WithId = Props & { id: string; }; type WithPuckProps = Props & { puck: PuckContext; editMode?: boolean; }; type AsFieldProps = Omit; type WithChildren = Props & { children: ReactNode; }; type UserGenerics = ExtractConfigParams, UserData extends Data | Data = Data, UserAppState extends PrivateAppState = PrivateAppState, UserPublicAppState extends AppState = AppState, UserComponentData extends ComponentData = UserData["content"][0]> = { UserConfig: UserConfig; UserParams: UserParams; UserProps: UserParams["props"]; UserRootProps: WithDefaultRootFieldProps; UserData: UserData; UserAppState: UserAppState; UserPublicAppState: UserPublicAppState; UserComponentData: UserComponentData; UserField: UserParams["field"]; }; type ExtractField = Extract; type SlotComponent = (props?: Omit) => ReactNode; type PuckComponent = (props: WithId; }>>) => JSX.Element; type ResolveDataTrigger = "insert" | "replace" | "load" | "force" | "move"; type WithPartialProps = Omit & { props?: Partial; }; interface ComponentConfigExtensions { } type ComponentConfigInternal, "type">, // NB this doesn't include AllProps, so types will not contain deep slot types. To fix, we require a breaking change. UserField extends BaseField = {}> = { render: PuckComponent; label?: string; defaultProps?: FieldProps; fields?: Fields; permissions?: Partial; inline?: boolean; resolveFields?: (data: DataShape, params: { changed: Partial & { id: string; }>; fields: Fields; lastFields: Fields; lastData: DataShape | null; metadata: ComponentMetadata; appState: AppState; parent: ComponentData | null; }) => Promise> | Fields; resolveData?: (data: DataShape, params: { changed: Partial & { id: string; }>; lastData: DataShape | null; metadata: ComponentMetadata; trigger: ResolveDataTrigger; parent: ComponentData | null; root: RootData; }) => Promise> | WithPartialProps; resolvePermissions?: (data: DataShape, params: { changed: Partial & { id: string; }>; lastPermissions: Partial; permissions: Partial; appState: AppState; lastData: DataShape | null; parent: ComponentData | null; }) => Promise> | Partial; metadata?: ComponentMetadata; } & ComponentConfigExtensions; type RootConfigInternal = Partial, AsFieldProps, RootData>, UserField>>; type Category = { components?: ComponentName[]; title?: string; visible?: boolean; defaultExpanded?: boolean; }; type ConfigInternal = { categories?: Record> & { other?: Category; }; components: { [ComponentName in keyof Props]: Omit, "type">, UserField>, "type">; }; root?: RootConfigInternal; }; type DefaultComponents = Record; type Config = DefaultComponents | ConfigParams, RootProps extends DefaultComponentProps = any, CategoryName extends string = string> = PropsOrParams extends ConfigParams ? ConfigInternal : PropsOrParams extends ConfigParams ? ConfigInternal : PropsOrParams extends ConfigParams ? ConfigInternal : ConfigInternal; type ExtractConfigParams = UserConfig extends ConfigInternal ? { props: PropsOrParams; rootProps: WithDefaultRootFieldProps; categoryNames: CategoryName; field: UserField extends { type: string; } ? UserField : Field; } : never; type ConfigParams = { components?: Components; root?: RootProps; categories?: CategoryNames; fields?: AssertHasValue; }; type BaseData = { readOnly?: Partial>; }; type RootDataWithProps = BaseData & { props: Props; }; type RootDataWithoutProps = Props; type RootData = Partial>> & Partial>; type ComponentData = Record> = { type: Name; props: WithDeepSlots, Content>; } & BaseData; type ComponentDataOptionalId = { type: Name; props: Props & { id?: string; }; } & BaseData; type ComponentDataMap = { [K in keyof Components]: ComponentData; }[keyof Components]; type Content = ComponentDataMap[]; type Data = { root: WithDeepSlots, Content>; content: Content; zones?: Record>; }; type Metadata = { [key: string]: any; }; interface PuckMetadata extends Metadata { } interface ComponentMetadata extends PuckMetadata { } interface FieldMetadata extends Metadata { } type ItemWithId = { _arrayId: string; _originalIndex: number; _currentIndex: number; }; type ArrayState = { items: ItemWithId[]; openId: string; }; type UiState = { leftSideBarVisible: boolean; rightSideBarVisible: boolean; leftSideBarWidth?: number | null; rightSideBarWidth?: number | null; mobilePanelExpanded?: boolean; itemSelector: ItemSelector | null; arrayState: Record; previewMode: "interactive" | "edit"; componentList: Record; isDragging: boolean; /** Outline items whose children are shown, keyed by item id */ itemExpanded?: Record; viewports: { current: { width: number | "100%"; height: number | "auto"; }; controlsVisible: boolean; options: Viewport[]; }; field: { focus?: string | null; metadata?: Record; }; plugin: { current: string | null; }; }; type AppState = { data: UserData; ui: UiState; }; type ZoneType = "root" | "dropzone" | "slot"; type PuckNodeData = { data: ComponentData; flatData: ComponentData; parentId: string | null; zone: string; path: string[]; }; type PuckZoneData = { contentIds: string[]; type: ZoneType; }; type NodeIndex = Record; type ZoneIndex = Record; type PrivateAppState = AppState & { indexes: { nodes: NodeIndex; zones: ZoneIndex; }; }; type BuiltinTypes = Date | RegExp | Error | Function | symbol | null | undefined; /** * Recursively walk T and replace Slots with SlotComponents */ type WithDeepSlots = T extends Slot ? SlotType : T extends (infer U)[] ? Array> : T extends (infer U)[] ? WithDeepSlots[] : T extends BuiltinTypes ? T : T extends object ? { [K in keyof T]: WithDeepSlots; } : T; type FieldsExtension = { [Type in string]: { type: Type; }; }; type Exact = Record, never>; type LeftOrExactRight = (Left & Union extends Right ? Exact : Left) | (Right & Exact); type AssertHasValue = [keyof T] extends [ never ] ? False : True; type RenderFunc = (props: Props) => ReactElement; type MapFnParams = { value: any; parentId: string; propName: string; field: ThisField; propPath: string; }; type FieldTransformFnParams = Omit, "parentId"> & { isReadOnly: boolean; componentId: string; }; type FieldTransformFn = (params: FieldTransformFnParams) => any; type FieldTransforms, // Setting fields: {} helps TS choose default field types G extends UserGenerics = UserGenerics, UserField extends { type: string; } = Field | G["UserField"]> = Partial<{ [Type in UserField["type"]]: FieldTransformFn>; }>; declare const overrideKeys: readonly ["header", "headerActions", "fields", "fieldLabel", "drawer", "drawerItem", "componentOverlay", "outline", "puck", "preview"]; type OverrideKey = (typeof overrideKeys)[number]; type OverridesGeneric = Shape; type Overrides = OverridesGeneric<{ fieldTypes: Partial>; header: RenderFunc<{ actions: ReactNode; children: ReactNode; }>; actionBar: RenderFunc<{ label?: string; children: ReactNode; parentAction: ReactNode; }>; headerActions: RenderFunc<{ children: ReactNode; }>; preview: RenderFunc; fields: RenderFunc<{ children: ReactNode; isLoading: boolean; itemSelector?: ItemSelector | null; }>; fieldLabel: RenderFunc<{ children?: ReactNode; icon?: ReactNode; label: string; el?: "label" | "div"; readOnly?: boolean; className?: string; }>; components: RenderFunc; componentItem: RenderFunc<{ children: ReactNode; name: string; }>; drawer: RenderFunc; drawerItem: RenderFunc<{ children: ReactNode; name: string; }>; iframe: RenderFunc<{ children: ReactNode; document?: Document; }>; outline: RenderFunc; componentOverlay: RenderFunc<{ children: ReactNode; hover: boolean; isSelected: boolean; componentId: string; componentType: string; }>; puck: RenderFunc; }>; type FieldRenderFunctions = UserGenerics, UserField extends { type: string; } = Field | G["UserField"]> = Omit<{ [Type in UserField["type"]]: React.FunctionComponent, any> & { children: ReactNode; name: string; }>; }, "custom">; type DragAxis = "dynamic" | "y" | "x"; type iconTypes = "Smartphone" | "Monitor" | "Tablet"; type Viewport = { width: number | "100%"; height?: number | "auto"; label?: string; icon?: iconTypes | ReactNode; }; type Permissions = { drag: boolean; duplicate: boolean; delete: boolean; edit: boolean; insert: boolean; } & Record; type Plugin = { name?: string; label?: string; icon?: ReactNode; render?: () => ReactElement; overrides?: Partial>; fieldTransforms?: FieldTransforms; mobilePanelHeight?: "toggle" | "min-content"; }; type Slot = { [K in keyof Props]: ComponentDataOptionalId; }[keyof Props][]; declare const headingAnalyzer: Plugin; export { headingAnalyzer as default };