UNPKG

@blocknote/react

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

422 lines (421 loc) 13.7 kB
import { ReactElement, ChangeEvent, ComponentType, CSSProperties, ForwardedRef, HTMLInputAutoCompleteAttribute, KeyboardEvent, MouseEvent, ReactNode } from "react"; import { BlockNoteEditor, User } from "@blocknote/core"; import { DefaultReactGridSuggestionItem } from "../components/SuggestionMenu/GridSuggestionMenu/types.js"; import { DefaultReactSuggestionItem } from "../components/SuggestionMenu/types.js"; type ToolbarRootType = { className?: string; children?: ReactNode; onMouseEnter?: () => void; onMouseLeave?: () => void; variant?: "default" | "action-toolbar"; }; type ToolbarButtonType = { className?: string; mainTooltip?: string; secondaryTooltip?: string; icon?: ReactNode; onClick?: (e: MouseEvent) => void; isSelected?: boolean; isDisabled?: boolean; variant?: "default" | "compact"; } & ({ children: ReactNode; label?: string; } | { children?: undefined; label: string; }); type ToolbarSelectType = { className?: string; items: { text: string; icon: ReactNode; onClick: () => void; isSelected: boolean; isDisabled?: boolean; }[]; isDisabled?: boolean; portalElement: HTMLElement | null; /** * When true, the UI library must not move focus into the surface when it * opens (the mobile toolbar: a focus move blurs the editor and closes the * on-screen keyboard). An input inside that asks for focus itself still * gets it. */ preventFocusOnOpen?: boolean; }; type MenuButtonType = { className?: string; onClick?: (e: MouseEvent) => void; icon?: ReactNode; onDragStart?: (e: React.DragEvent) => void; onDragEnd?: (e: React.DragEvent) => void; draggable?: boolean; } & ({ children: ReactNode; label?: string; } | { children?: undefined; label: string; }); export type ComponentProps = { FormattingToolbar: { Root: ToolbarRootType; Button: ToolbarButtonType; Select: ToolbarSelectType; }; FilePanel: { Root: { className?: string; tabs: { name: string; tabPanel: ReactNode; }[]; openTab: string; setOpenTab: (name: string) => void; defaultOpenTab: string; loading: boolean; }; Button: { className?: string; /** * Explicit, because the skins' underlying buttons disagree on the * default (Mantine's is `type="button"`, shadcn's was `"submit"`) and * a submit button inside a `Form.Root` must reliably submit on every * skin. `"submit"` buttons need no `onClick` - the form's `onSubmit` * is the single commit path, so clicking cannot fire twice. */ type: "button" | "submit"; onClick?: () => void; } & ({ children: ReactNode; label?: string; } | { children?: undefined; label: string; }); FileInput: { className?: string; accept: string; value: File | null; placeholder: string; onChange: (payload: File | null) => void; }; TabPanel: { className?: string; children?: ReactNode; }; TextInput: { className?: string; value: string; placeholder: string; onChange: (event: ChangeEvent<HTMLInputElement>) => void; onKeyDown?: (event: KeyboardEvent) => void; }; }; LinkToolbar: { Root: ToolbarRootType; Button: ToolbarButtonType; Select: ToolbarSelectType; }; SideMenu: { Root: { className?: string; children?: ReactNode; }; Button: { className?: string; onClick?: (e: MouseEvent) => void; icon?: ReactNode; onDragStart?: (e: React.DragEvent) => void; onDragEnd?: (e: React.DragEvent) => void; draggable?: boolean; } & ({ children: ReactNode; label?: string; } | { children?: undefined; label: string; }); }; SuggestionMenu: { Root: { id: string; className?: string; children?: ReactNode; }; EmptyItem: { className?: string; children?: ReactNode; }; Item: { className?: string; id: string; isSelected: boolean; onClick: () => void; item: Omit<DefaultReactSuggestionItem, "onItemClick">; }; Label: { className?: string; children?: ReactNode; }; Loader: { className?: string; }; }; GridSuggestionMenu: { Root: { id: string; columns: number; className?: string; children?: ReactNode; }; EmptyItem: { columns: number; className?: string; children?: ReactNode; }; Item: { className?: string; id: string; isSelected: boolean; onClick: () => void; item: DefaultReactGridSuggestionItem; }; Loader: { columns: number; className?: string; children?: ReactNode; }; }; TableHandle: { Root: { className?: string; draggable: boolean; onDragStart: (e: React.DragEvent) => void; onDragEnd: () => void; style?: CSSProperties; } & ({ children: ReactNode; label?: string; } | { children?: undefined; label: string; }); ExtendButton: { className?: string; onClick: (e: React.MouseEvent) => void; onMouseDown: (e: React.MouseEvent) => void; children: ReactNode; }; }; Comments: { Card: { className?: string; headerText?: string; selected?: boolean; onFocus?: (event: React.FocusEvent) => void; onBlur?: (event: React.FocusEvent) => void; tabIndex?: number; children?: ReactNode; }; CardSection: { className?: string; children?: ReactNode; }; ExpandSectionsPrompt: { className?: string; children?: ReactNode; }; Editor: { className?: string; autoFocus?: boolean; editable: boolean; editor: BlockNoteEditor<any, any, any>; onFocus?: () => void; onBlur?: () => void; }; Comment: { className?: string; children?: ReactNode; authorInfo: "loading" | User; timeString: string; edited: boolean; actions?: ReactNode; showActions?: boolean | "hover"; emojiPickerOpen?: boolean; }; }; Versioning: { /** * The scrollable container for the version-history sidebar (header + * snapshot rows). */ Sidebar: { className?: string; children?: ReactNode; }; /** * A single row in the version-history sidebar — the live "current version" * entry or a stored snapshot. */ Snapshot: { className?: string; /** Whether this row is the version currently shown in the editor. */ selected?: boolean; /** Whether this row is the baseline the current diff is compared against. */ comparing?: boolean; onClick?: () => void; /** Row actions (e.g. the "..." menu), revealed on hover. */ actions?: ReactNode; children?: ReactNode; }; }; AttributionTooltip: { /** * The attribution tooltip shown when hovering a suggestion mark. Positioned * by floating-ui and portaled by the controller — this only styles the box. */ Root: { className?: string; /** * App-supplied class from `getAttributionMarkClassName` (override path). * When set, `Root` applies it and ignores `backgroundColor`. */ markClassName?: string; /** * Per-user author color (default path). Applied inline when there's no * `markClassName`, since the tooltip is portaled away from the mark and * can't inherit the mark's color. */ backgroundColor?: string; children?: ReactNode; }; }; Generic: { Badge: { Root: { className?: string; text: string; icon?: ReactNode; isSelected?: boolean; mainTooltip?: string; secondaryTooltip?: string; onClick?: (event: React.MouseEvent) => void; onMouseEnter?: () => void; }; Group: { className?: string; children: ReactNode; }; }; Form: { Root: { children?: ReactNode; /** * Called on the form's `submit` event. Implementations must render a * real `<form>` and `preventDefault`: native submission is the only * path that works for every input source — mobile IMEs commit * through it without dispatching any key event. */ onSubmit?: () => void; /** * The form's submit control, rendered inside the `<form>`. Usually * `ScreenReaderOnlySubmit`; a visible `type="submit"` button to make * it the form's one affordance (the embed tab); or `"none"` — then * the form must have exactly one field, or Enter submits nothing. */ submitButton: ReactElement | "none"; }; TextInput: { className?: string; name: string; label?: string; variant?: "default" | "large"; icon: ReactNode; rightSection?: ReactNode; autoFocus?: boolean; placeholder?: string; disabled?: boolean; value: string; onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void; onChange: (event: ChangeEvent<HTMLInputElement>) => void; autoComplete?: HTMLInputAutoCompleteAttribute; "aria-activedescendant"?: string; ref?: ForwardedRef<HTMLInputElement>; }; }; Menu: { Root: { sub?: boolean; onOpenChange?: (open: boolean) => void; position?: "top" | "right" | "bottom" | "left" | `${"top" | "right" | "bottom" | "left"}-${"start" | "end"}`; portalElement: HTMLElement | null; /** See `ToolbarSelect.preventFocusOnOpen`. */ preventFocusOnOpen?: boolean; children?: ReactNode; }; Divider: { className?: string; }; Dropdown: { className?: string; children?: ReactNode; sub?: boolean; }; Item: { className?: string; children?: ReactNode; subTrigger?: boolean; icon?: ReactNode; checked?: boolean; onClick?: () => void; }; Label: { className?: string; children?: ReactNode; }; Trigger: { children?: ReactNode; sub?: boolean; }; Button: MenuButtonType; }; Popover: { Root: { open?: boolean; onOpenChange?: (open: boolean) => void; position?: "top" | "right" | "bottom" | "left" | `${"top" | "right" | "bottom" | "left"}-${"start" | "end"}`; portalElement: HTMLElement | null; children?: ReactNode; }; Content: { className?: string; variant: "form-popover" | "panel-popover"; children?: ReactNode; }; Trigger: { children?: ReactNode; }; }; Toolbar: { Root: ToolbarRootType; Button: ToolbarButtonType; Select: ToolbarSelectType; }; }; }; export type Components = { [Components in keyof Omit<ComponentProps, "Generic">]: { [Component in keyof ComponentProps[Components]]: ComponentType<ComponentProps[Components][Component]>; }; } & { Generic: { [GenericComponents in keyof ComponentProps["Generic"]]: { [Component in keyof ComponentProps["Generic"][GenericComponents]]: ComponentType<ComponentProps["Generic"][GenericComponents][Component]>; }; }; }; export declare const ComponentsContext: import("react").Context<Components | undefined>; export declare function useComponentsContext(): Components | undefined; export {};