@bratislava/wysimark-editor
Version:
The customized fork of wysimark editor - A modern and clean rich text editor for React that supports 100% of CommonMark and GitHub Flavored Markdown.
1,079 lines (1,024 loc) • 35.6 kB
TypeScript
import * as react_jsx_runtime from 'react/jsx-runtime';
import * as slate from 'slate';
import { Element as Element$2, BaseEditor, NodeEntry, BaseRange, Location, Editor, Descendant, Text as Text$1, Path, BaseText } from 'slate';
import { Client } from '@portive/client';
import { SetReturnType, SetOptional, UnionToIntersection, Simplify, RequireExactlyOne } from 'type-fest';
import * as zustand from 'zustand';
import { HistoryEditor } from 'slate-history';
import { ReactEditor, Editable as Editable$1 } from 'slate-react';
import react from 'react';
import { RenderElementProps, RenderLeafProps, RenderPlaceholderProps, EditableProps as EditableProps$1 } from 'slate-react/dist/components/editable';
/**
* SinkEditor just adds a `sink` object where we drop all of our sink
* related data on.
*/
type SinkEditor = {
/**
* a master Element is one that has one or more elements that are depedant
* on it. For example, a `table` Element. For clarity, a `table-row` Element
* is not considered a master Element. Only the top-most element is.
*
* One use for identify a master is for adding a block quote. We want the
* block quote to always surround the master Element. A block-quote that
* surrounded a table-row, for example, would not make sense.
*/
isMaster: (node: Element$2) => boolean;
/**
* a slave Element is one that is dependant on another Element. For example,
* `table-row`, `table-cell` and `table-cotent` elements are all considered
* slave elements.
*
* At the time of writing, I haven't figured out a use case for a slave
* element actually...
*/
isSlave: (node: Element$2) => boolean;
isStandalone: (node: Element$2) => boolean;
sink: {
plugins: BasePluginPolicy[];
};
};
type FullSinkEditor = SinkEditor & BaseEditor & ReactEditor & HistoryEditor;
/**
* On the editor, there are several methods that return void that are used to
* override default editor behaviors.
*
* These are referred to as Overrideable core actions in the docs.
*
* - deleteBackward
* - deleteForward
* - deleteFragment
* - insertBreak
* - insertFragment
* - insertNode
* - insertText
* - normalizeNode
*
* When there are plugins, Sink tries to find a plugin to handle the event and
* if it cannot, uses the previously defined handler. For example, the user may
* have specified `editor.insertBreak` on the `editor` earlier and that will
* be called after all the plugins have been searched.
*
* We search through the plugins from the first to the last plugin.
*
* In a plugin, we specify the functions like we don on the editor but the
* return value informs us how the plugin should proceed after. The return
* value generally indicates whether the plugin has handled the event with one
* special case:
*
* - `true`: If the return type is `true` then the plugin has indicated it has
* handled the event and no further processing is required. The handlers in
* all remaining plugins are skipped.
*
* - `false`: If the return type is `false` then the plugin has indicated it has
* not handled the event and will continue through the rest of the plugins
* looking for a plugin to handle the event.
*
* - `() => void`: If the return tyep is a function, the plugin has indicated
* it has not handled the event, but that it would like to register another
* function that should execute after the actual event handler has been
* executed. In particular, this is used when in certain situations we may
* want a normalizer to execute after the event handler has triggered. This
* is used in the `normalize-after-delete-plugin` for example.
*
* NOTE:
*
* This seems like an unusual specification at first glance and a purist might
* argue, this could be handled more succinctly with a `next` function passed
* in as the final argument.
*
* Here's why I elected to go this route but it boils down to the fact that
* `next` functions make the function difficult to reason about.
*
* - 99% of the true, we want to indicate whether we handled the function or
* not and for that use case, true/false is simple to understand and natural.
* In the case where we need something to happen after, returning a function
* is unusual, but still easy to reason about. Also, the exclusivity of
* the function return is nice in that it assumes that the event wasn't
* handled, and of course, the function return would only ever be used if the
* function indeed wasn't handled. For if it was handled, there would be no
* need to have the after function because that could just be in the original
* function handler.
*
* - To use this, you have to build nested contexts that are always hard to
* reason about because you are passing a set of contexts from inner child
* to outer parent. This created difficult to comprehend complexity in the
* old Slate plugins architecture and is probably why it was abandoned.
*
* - It's also harder to type properly and to reason about it. The argument
* list changes in length depending on the function; furthermore, in some
* cases it is natural to ignore the arguments but we'd have to accept blank
* arguments that are unused to access the `next` function.
*
* - It's hard to debug. The plugin system as it currently is designed to
* execute linearly, instead of in a nested fashion. This makes it easy to
* add debug code, and know what happens before and after each step.
*/
type VoidActionReturn = boolean | (() => void);
type RenderEditableProps = {
attributes: EditableProps$1;
Editable: typeof Editable$1;
};
type RenderEditable = (props: RenderEditableProps) => react.ReactElement;
/**
* The return type of the BasePluginFn which specifies how the Plugin is
* supposed to behave.
*/
type BasePluginPolicy = {
name: string;
editor?: {
isInline?: (element: Element) => boolean | void;
isVoid?: (element: Element) => boolean | void;
isMaster?: (element: Element) => boolean | void;
isSlave?: (element: Element) => boolean | void;
isStandalone?: (element: Element) => boolean | void;
deleteBackward?: (unit: "character" | "word" | "line" | "block") => VoidActionReturn;
deleteForward?: (unit: "character" | "word" | "line" | "block") => VoidActionReturn;
deleteFragment?: () => VoidActionReturn;
insertBreak?: () => VoidActionReturn;
insertFragment?: (fragment: Node[]) => VoidActionReturn;
insertNode?: (node: Node) => VoidActionReturn;
insertText?: (text: string) => VoidActionReturn;
normalizeNode?: (entry: NodeEntry) => VoidActionReturn;
};
renderEditable?: RenderEditable;
editableProps?: {
decorate?: ((entry: NodeEntry) => BaseRange[]) | undefined;
renderElement?: (props: RenderElementProps) => react.ReactElement | undefined;
renderLeaf?: (props: RenderLeafProps) => react.ReactElement | undefined;
renderPlaceholder?: (props: RenderPlaceholderProps) => JSX.Element;
onKeyDown?: EditableVoidToBooleanHandlerType<"onKeyDown">;
onKeyUp?: EditableVoidToBooleanHandlerType<"onKeyDown">;
onPaste?: EditableVoidToBooleanHandlerType<"onPaste">;
onDrop?: EditableVoidToBooleanHandlerType<"onDrop">;
};
};
type EditableVoidToBooleanHandlerType<K extends keyof EditableProps$1> = SetReturnType<NonNullable<EditableProps$1[K]>, boolean>;
/**
* IMPORTANT!
*
* NEVER!
*
* refer to a type that is defined in the `slate` package. This is because
* any reference to a Slate type will cause a circular reference type error that
* is very hard to track down.
*
* NOTE: This kind of happens in that `Element` will often have a reference to
* `Descendant` but it looks like this is okay; however, let's not tempt fate
* by only using it where the definition is absolutely necessary.
*
* ALWAYS!
*
* Be explicity about return types. If they are inferred through the return
* type, because we need to provide `Editor` as an argument in certain cases,
* we don't want to accidentally have `Editor` be provided as a return type
* or this will create the circular reference.
*/
type BasePluginSchema = {
Name: string;
Options: Record<string, unknown>;
Editor: Record<string, unknown>;
Element: {
type: string;
};
Text: Record<string, unknown>;
};
/**
* These are the PluginTypes that are accepted as inputs into `createPlugin`
* which has the same basic signature as `BasePluginTypes` but some of the
* types are optional.
*
* These `InputPluginTypes` need to have their optional types filled in with
* defaults before they can be used.
*
* See `NormalizeInputPluginTypes`
*/
type InputPluginSchema = SetOptional<BasePluginSchema, "Options" | "Editor" | "Element" | "Text">;
/**
* Takes an `InputPluginSchema` (that has some optional types) and turns them
* into a regular PluginTypes with any missing types filled in with defaults.
*/
type NormalizeInputPluginSchema<T extends InputPluginSchema> = {
Name: T["Name"];
Options: T["Options"] extends object ? T["Options"] : {};
Editor: T["Editor"] extends object ? T["Editor"] : {};
Element: T["Element"] extends object ? T["Element"] : never;
Text: T["Text"] extends object ? T["Text"] : {};
};
/**
* Shape of a PluginFn (Plugin Function).
*/
type BasePluginFn = (editor: FullSinkEditor, options: {}, helpers: {
createPolicy: (value: unknown) => unknown;
}) => BasePluginPolicy;
/**
* IMPORTANT!
*
* NEVER!
*
* refer to a type that is defined in the `slate` package. This is because
* any reference to a Slate type will cause a circular reference type error that
* is very hard to track down.
*
* NOTE: This kind of happens in that `Element` will often have a reference to
* `Descendant` but it looks like this is okay; however, let's not tempt fate
* by only using it where the definition is absolutely necessary.
*
* ALWAYS!
*
* Be explicity about return types. If they are inferred through the return
* type, because we need to provide `Editor` as an argument in certain cases,
* we don't want to accidentally have `Editor` be provided as a return type
* or this will create the circular reference.
*/
/**
* When a Plugin is created using the `createPlugin` method, it returns a
* Plugin.
*/
type BasePlugin = {
fn: BasePluginFn;
__types__: BasePluginSchema;
};
/**
* When a Plugin is created using `createPlugin` we must
*/
type TypedPlugin<T extends InputPluginSchema> = {
fn: BasePluginFn;
__types__: NormalizeInputPluginSchema<T>;
};
type ExtractCustomTypes<TA extends Array<BasePlugin>> =
/**
* This code takes an array of types and merges them together into a union.
*/
TA extends Array<{
__types__: infer U;
}> ? {
Editor: SinkEditor & BaseEditor & ReactEditor & HistoryEditor & UnionToIntersection<U extends {
Editor: infer E;
} ? E : never>;
Element: U extends {
Element: infer E;
} ? E : never;
Text: Simplify<UnionToIntersection<U extends {
Text: infer T;
} ? T : never>>;
Options: Simplify<UnionToIntersection<U extends {
Options: infer T;
} ? T : never>>;
} : never;
/**
* Defines a value you'd find in a function's parameters as a replacement for
* `at`. The benefit of using `BetterAt` is that it allows you to search
* using an `Element`.
*/
type BetterAt = Location | Element$2 | null;
/**
* The TargetElement can be specified either as the actual value or as a
* function that takes a srcElement and returns the targetElement.
*/
type TargetElement<T extends Element$2 = Element$2> = Omit<T, "children"> | ((srcElement: Element$2) => Omit<T, "children">);
type PlaceholderEditor = {
placeholder: {};
};
type PlaceholderPluginCustomTypes = {
Name: "placeholder";
Editor: PlaceholderEditor;
};
declare function createImageMethods(editor: Editor): {
noop: () => void;
};
type ImageSize = {
width: number;
height: number;
};
type ImageMethods = ReturnType<typeof createImageMethods>;
type ImagePluginConfig = {
/**
* When an image is uploaded, the plugin needs to decide whether the image
* should be an inline image (like an icon that displays within a line of
* text) or a block image (like a photo that appears as its own block).
*
* This setting is the maximum size of an image for it to be defaulted to an
* inline image.
*
* NOTE:
*
* The user can convert an image from one image type to the other manually.
*/
maxInitialInlineImageSize: ImageSize;
/**
* When an image is first uploaded, it may come in at a large size but for
* some applications, you don't want the image to overwhelm the page,
* like when the editor is visually a small size.
*
* This specifies the maximum initial size when an image is first uploaded
* to the page. The user can resize to a larger size.
*
* If the value is null, the image will be displayed at full size.
*
* NOTE:
*
* This is the displayed image width. On retina displays, the actualy image
* file delivered to the browser may be a multiple of the provided value.
*/
maxInitialImageSize: ImageSize | null;
/**
* When an image is displayed at full size, you may still want to limit the
* size of the image file.
*
* NOTE:
*
* This is the maximum visual image
*/
maxImageSize: ImageSize;
imageBlockPresets: ImageSizePreset[];
imageInlinePresets: ImageSizePreset[];
};
type ImagePluginOptions = {
image: Partial<ImagePluginConfig>;
};
type ImageEditor = {
image: ImageMethods & ImagePluginConfig;
};
type ImageSharedElement = {
/**
* The `url` represents either
*
* - a `hashUrl` that begins with a `#` during the upload process which
* represents a unique id reference to a Zustand store where the actual
* information about the upload is kept.
* - The actual `url` of the uploaded file. When the file is saved, the
* `hashUrl` will be converted to the actual `url` of the file.
*/
url: string;
title?: string;
alt?: string;
bytes?: number;
/**
* If the `maxWidth` and `maxHeight` are present, it indicates that the image
* is resizable.
*
* If they are not present, it indicates that the `width` and `height` should
* be used, but they cannot be resized.
*
* If the `width` and `height` are also not present, it indicates we are not
* aware of the current size of the image, so just display it.
*/
srcWidth?: number;
srcHeight?: number;
width?: number;
height?: number;
children: Descendant[];
};
/**
* Default for larger images, over 48px
*
* Larger images can be converted to inline images though.
*/
type ImageBlockElement = {
type: "image-block";
} & ImageSharedElement;
/**
* Default for smaller images, 48px and less
*
* Smaller images can be converted to block images though.
*/
type ImageInlineElement = {
type: "image-inline";
} & ImageSharedElement;
type ImagePluginCustomTypes = {
Name: "image";
Editor: ImageEditor;
Element: ImageBlockElement | ImageInlineElement;
Options: ImagePluginOptions;
};
/**
* A preset is defined either as a bound or as a scale:
*
* - bounds: The image will be placed within the bounds.
* - scale: The image will be scaled to the given `scale` value. The max
* value should be `1`.
*/
type ImageSizePreset = {
name: string;
title: string;
type: "bounds";
width: number;
height: number;
} | {
name: string;
title: string;
type: "scale";
scale: number;
};
declare function createAnchorMethods(editor: Editor): {
insertLink: (args_0: string, args_1?: string | undefined, args_2?: {
select?: boolean | undefined;
} | undefined) => void;
removeLink: (args_0: {
at?: BetterAt | undefined;
}) => boolean;
editLink: (args_0: {
href: string;
title?: string | undefined;
}, args_1: {
at?: BetterAt | undefined;
}) => boolean;
};
type AnchorMethods = ReturnType<typeof createAnchorMethods>;
type AnchorEditor = {
anchor: AnchorMethods;
};
type AnchorElement = {
type: "anchor";
href: string;
target?: string;
title?: string;
children: Descendant[];
};
type AnchorPluginCustomTypes = {
Name: "anchor";
Editor: AnchorEditor;
Element: AnchorElement;
};
declare function createHeadingMethods(editor: Editor): {
convertHeading: (level: 1 | 3 | 2 | 4 | 5 | 6, allowToggle: boolean) => void;
};
type HeadingEditor = {
heading: ReturnType<typeof createHeadingMethods>;
};
type HeadingElement = {
type: "heading";
/**
* NOTE:
*
* Don't extract these into a new type. It's easier to just repeat this and
* there's less indirection.
*/
level: 1 | 2 | 3 | 4 | 5 | 6;
children: Descendant[];
};
type HeadingPluginCustomTypes = {
Name: "heading";
Editor: HeadingEditor;
Element: HeadingElement;
};
type BlockQuoteEditor = {
supportsBlockQuote: true;
blockQuotePlugin: {
indent: () => void;
outdent: () => void;
};
};
type BlockQuoteElement = {
type: "block-quote";
children: Descendant[];
};
type BlockQuotePluginCustomTypes = {
Name: "block-quote";
Editor: BlockQuoteEditor;
Element: BlockQuoteElement;
};
declare function createCodeBlockMethods(editor: Editor): {
createCodeBlock: (args_0: {
language: BuiltInLanguage;
}) => void;
setCodeBlockLanguage: (language: BuiltInLanguage, options?: {
at?: BetterAt | undefined;
} | undefined) => boolean;
};
type CodeBlockMethods = ReturnType<typeof createCodeBlockMethods>;
type BuiltInLanguage = "text" | "html" | "svg" | "markup" | "css" | "javascript" | "js" | "java" | "c" | "clike";
type CodeBlockEditor = {
codeBlock: CodeBlockMethods;
};
/**
* The code block element is the root element of a code block.
*/
type CodeBlockElement = {
type: "code-block";
/**
* The language of the code block. Can accept any string because Markdown can
* accept any string; however, the built-in Prism languages are defined in:
* `BuiltInLanguage`
*/
language: string;
children: CodeBlockLineElement[];
};
type CodeBlockLineElement = {
type: "code-block-line";
children: Text$1[];
};
type CodeBlockPluginCustomTypes = {
Name: "code-block";
Editor: CodeBlockEditor;
Element: CodeBlockElement | CodeBlockLineElement;
Text: {
text: string;
prismToken?: string;
};
};
/**
* Alignment of Table Columns
*/
type TableColumnAlign = "left" | "center" | "right";
/**
* Table Column values
*/
type TableColumn = {
align: TableColumnAlign;
};
/**
* Table Element
*/
type TableElement = {
type: "table";
columns: TableColumn[];
children: TableRowElement[];
};
/**
* Table Row Element
*/
type TableRowElement = {
type: "table-row";
children: TableCellElement[];
};
/**
* Table Cell Element
*
* The children of a `TdElement` is exactly one `ParagraphElement`.
*
* This is a good choice for Slate because copying and pasting a range of
* elements will split the lowest child element by default. If the child of
* a `TdElement` is a leaf, then we split the `TdElement` which is never what
* we want.
*
* Instead, by having a lower level element, the `ParagraphElement`, we allow
* that to be split.
*
* But of course, insertion means we have many child elements in the `TdElement`
* but these are easier to fix using normalization. We can keep iterating
* through normalizations until we end up with a single Paragraph.
*/
type TableCellElement = {
type: "table-cell";
x?: number;
y?: number;
children: TableContentElement[];
};
type TableContentElement = {
type: "table-content";
children: Descendant[];
};
/**
* The TableInfo object that includes quick access information starting from a
* cell in a table including information about the row and the table.
*
* NOTE:
*
* This is flat and not nested because it makes destructuring easier, for
* example, in the table methods.
*/
type TableInfo = {
tableElement: TableElement;
tablePath: Path;
tableColumns: TableColumn[];
rowElement: TableRowElement;
rowPath: Path;
rowIndex: number;
rowCount: number;
cellElement: TableCellElement;
cellPath: Path;
cellIndex: number;
cellCount: number;
};
declare function createHorizontalRuleMethods(editor: Editor): {
insertHorizontalRule: () => boolean;
};
type HorizontalRuleMethods = ReturnType<typeof createHorizontalRuleMethods>;
type HorizontalRuleEditor = {
horizontalRule: HorizontalRuleMethods;
};
type HorizontalRuleElement = {
type: "horizontal-rule";
children: [{
text: "";
}];
};
type HorizontalRulePluginCustomTypes = {
Name: "horizontal-rule";
Editor: HorizontalRuleEditor;
Element: HorizontalRuleElement;
};
declare function createListMethods(editor: Editor): {
indent: () => boolean;
outdent: () => boolean;
convertUnorderedList: (allowToggle: boolean) => void;
convertOrderedList: (allowToggle: boolean) => void;
convertTaskList: (allowToggle: boolean) => void;
insertBreak: () => boolean;
toggleTaskListItem: (args_0?: {
at?: BetterAt | undefined;
} | undefined) => false | undefined;
};
/**
* List Editor
*/
type ListEditor = {
list: ReturnType<typeof createListMethods>;
};
/**
* Ordered List Item Element
*/
type OrderedListItemElement = {
type: "ordered-list-item";
depth: number;
__firstAtDepth?: boolean;
children: Descendant[];
};
/**
* Unordered List Item Element
*/
type UnorderedListItemElement = {
type: "unordered-list-item";
depth: number;
__firstAtDepth?: boolean;
children: Descendant[];
};
/**
* Checkable Task List Item Element
*/
type TaskListItemElement = {
type: "task-list-item";
depth: number;
__firstAtDepth?: boolean;
checked: boolean;
children: Descendant[];
};
/**
* List Plugins Custom Types
*/
type ListPluginCustomTypes = {
Name: "list";
Editor: ListEditor;
Element: OrderedListItemElement | UnorderedListItemElement | TaskListItemElement;
};
type CollapsibleParagraphEditor = {
collapsibleParagraph: {
convertParagraph: () => void;
};
};
type ParagraphElement = {
type: "paragraph";
__collapsible?: true;
children: Descendant[];
};
type CollapsibleParagraphPluginCustomTypes = {
Name: "collapsible-paragraph";
Editor: CollapsibleParagraphEditor;
Element: ParagraphElement;
};
declare function createTableMethods(editor: Editor): {
getTableInfo: (args_0?: {
at?: ImageBlockElement | ImageInlineElement | ParagraphElement | OrderedListItemElement | UnorderedListItemElement | TaskListItemElement | HorizontalRuleElement | TableElement | TableRowElement | TableCellElement | TableContentElement | CodeBlockElement | CodeBlockLineElement | BlockQuoteElement | HeadingElement | AnchorElement | slate.Location | null | undefined;
} | undefined) => TableInfo | undefined;
insertTable: (args_0: number, args_1: number, args_2?: {
at?: slate.Location | null | undefined;
} | undefined) => boolean;
insertColumn: (args_0?: {
offset?: 0 | 1 | undefined;
at?: BetterAt | undefined;
} | undefined) => boolean;
insertRow: (args_0?: {
at?: BetterAt | undefined;
offset?: 0 | 1 | undefined;
} | undefined) => boolean;
removeTable: () => boolean;
removeColumn: (args_0?: {
at?: BetterAt | undefined;
} | undefined) => boolean | undefined;
removeRow: (args_0?: {
at?: BetterAt | undefined;
} | undefined) => boolean;
tabForward: () => boolean;
tabBackward: () => boolean | undefined;
selectCell: (args_0?: {
at?: BetterAt | undefined;
} | undefined) => boolean;
down: () => boolean;
up: () => boolean;
setTableColumnAlign: (options: {
align: "center" | "left" | "right";
}) => boolean;
};
type TableEditor = {
supportsTable: true;
tablePlugin: ReturnType<typeof createTableMethods>;
};
type TablePluginCustomTypes = {
Name: "table";
Editor: TableEditor;
Element: TableElement | TableRowElement | TableCellElement | TableContentElement;
};
declare function createUploadMethods(editor: Editor): {
upload: (file: File) => boolean;
setElementTimeTraveling: <T extends ImageBlockElement | ImageInlineElement | ParagraphElement | OrderedListItemElement | UnorderedListItemElement | TaskListItemElement | HorizontalRuleElement | TableElement | TableRowElement | TableCellElement | TableContentElement | CodeBlockElement | CodeBlockLineElement | BlockQuoteElement | HeadingElement | AnchorElement, K extends keyof T = keyof T>(prev: RequireExactlyOne<T, K>, next: RequireExactlyOne<T, K>) => void;
};
/**
* Indicates an `Origin` that is uploading and the state of the Upload
*/
type UploadProgress = {
/**
* This is a URL but not the final upload URL. This is a URL that represents
* a secure value in the form of a string that represents a location on the
* user's computer.
*/
url: string;
status: "progress";
sentBytes: number;
totalBytes: number;
};
/**
* Indicates an `Origin` that has completed uploading
*/
type UploadComplete = {
status: "success";
/**
* This is a URL to the final place of the file
*/
url: string;
};
/**
* Indicates an `Origin` that has an error during uploading and the Error
* message
*/
type UploadError = {
status: "error";
url: string;
message: string;
};
type Upload = UploadProgress | UploadComplete | UploadError;
/**
* Creates an origin store using `zustand`.
*
* The purpose of this is to keep track of uploads and their progress but only
* storing the key to the lookup in the Element itself.
*
* This is necessary so that the Element value stays the same even as the image
* progress is updating during the upload.
*
* We want this because we don't want the progress updates to be part of the
* document's edit history. Consider that a user executes an undo and it undoes
* the progress of the upload.
*
* Te return value of `createUploadStore` is a React hook.
*
* The hook should be referenced as `useUploadStore`
*/
declare const createUploadStore: ({ uploads }?: {
uploads: Record<string, Upload>;
}) => zustand.UseBoundStore<zustand.StoreApi<UploadStore>>;
/**
* Types related to the `zustand` state-management library which we use to
* store the state of uploads.
*/
type GetUpload = (id: string) => Upload;
type SetUpload = (id: string, upload: Upload) => void;
type UploadStore = {
uploads: Record<string, Upload>;
getUpload: GetUpload;
setUpload: SetUpload;
};
type UploadMethods = ReturnType<typeof createUploadMethods>;
type UploadFileEvent = {
hashUrl: string;
file: File;
};
type UploadImageFileEvent = UploadFileEvent & {
width: number;
height: number;
};
type UploadFileSuccessEvent = UploadFileEvent & {
url: string;
};
type UploadImageFileSuccessEvent = UploadImageFileEvent & {
url: string;
};
type UploadEditor = {
upload: UploadMethods & {
client?: Client;
onUploadIconClick: () => void;
onUploadImageFile: (e: UploadImageFileEvent) => boolean;
onUploadFile: (e: UploadFileEvent) => boolean;
onUploadImageFileSuccess: (e: UploadImageFileSuccessEvent) => boolean;
onUploadFileSuccess: (e: UploadFileSuccessEvent) => boolean;
useUploadStore: ReturnType<typeof createUploadStore>;
};
};
type UploadOptions = {
upload?: {
authToken?: string;
onUploadButtonClick?: () => void;
};
};
type UploadPluginCustomTypes = {
Name: "upload";
Editor: UploadEditor;
Options: UploadOptions;
};
type AttachmentEditor = {
attachment: {};
};
type AttachmentPluginCustomTypes = {
Name: "attachment";
Editor: AttachmentEditor & UploadEditor;
};
type ToolbarEditor = {
toolbar: {
height?: number;
minHeight?: number;
maxHeight?: number;
showUploadButtons?: boolean;
showCustomUploadButton?: boolean;
};
};
type ToolbarOptions = {
toolbar: {
height?: number;
minHeight?: number;
maxHeight?: number;
showUploadButtons?: boolean;
showCustomUploadButton?: boolean;
};
};
type ToolbarPluginCustomTypes = {
Name: "toolbar";
Editor: ToolbarEditor;
Options: ToolbarOptions;
};
type ThemeEditor = {
theme: true;
};
type ThemePluginCustomTypes = {
Name: "theme";
Editor: ThemeEditor;
};
type NormalizeAfterDeleteEditor = {
normalizeAfterDelete: true;
};
type NormalizeAfterDeletePluginCustomTypes = {
Name: "normalize-after-delete";
Editor: NormalizeAfterDeleteEditor;
};
type AtomicDeleteEditor = {
atomicDelete: true;
};
type AtomicDeletePluginCustomTypes = {
Name: "atomic-delete";
Editor: AtomicDeleteEditor;
};
type InlineCodeEditor = {
inlineCode: {
toggleInlineCode: () => void;
};
};
type InlineCodeText = {
text: string;
code?: true;
};
type InlineCodePluginCustomTypes = {
Name: "inline-code";
Editor: InlineCodeEditor;
Text: InlineCodeText;
};
declare function createMarksMethods(editor: Editor): {
removeMarks: (args_0?: {
at?: slate.Location | null | undefined;
} | undefined) => void;
toggleMark: (args_0: "text" | "prismToken" | "code" | "bold" | "italic" | "underline" | "sup" | "sub" | "strike", args_1?: "text" | "prismToken" | "code" | "bold" | "italic" | "underline" | "sup" | "sub" | "strike" | undefined, args_2?: {
at?: slate.Location | null | undefined;
} | undefined) => void;
toggleBold: () => void;
toggleItalic: () => void;
toggleUnderline: () => void;
toggleSup: () => void;
toggleSub: () => void;
toggleStrike: () => void;
};
type MarksEditor = {
/**
* IMPORTANT:
*
* This cannot be named `marks` because it conflicts with the `editor.marks`
* built into the BaseEditor.j
*/
marksPlugin: ReturnType<typeof createMarksMethods>;
};
type MarksText = {
text: string;
bold?: true;
italic?: true;
underline?: true;
sup?: true;
sub?: true;
strike?: true;
};
type MarksPluginCustomTypes = {
Name: "marks";
Editor: MarksEditor;
Text: MarksText;
};
/**
* A type with generic for `convertElements` (below) to be used with the curry
* method. TypeScript, unfortunately, cannot automatically curry generics for
* us so we have to do it manually.
*/
type CurriedConvertElements = <T extends Element$2 = Element$2>(matchForToggle: (element: Element$2) => boolean, targetElement: TargetElement<T>, allowToggle: boolean) => void;
declare function createConvertElementMethods(editor: Editor): {
convertElementTypes: string[];
addConvertElementType: (type: "anchor" | "heading" | "block-quote" | "table" | "horizontal-rule" | "code-block" | "paragraph" | "image-block" | "image-inline" | "ordered-list-item" | "unordered-list-item" | "task-list-item" | "table-row" | "table-cell" | "table-content" | "code-block-line" | ("anchor" | "heading" | "block-quote" | "table" | "horizontal-rule" | "code-block" | "paragraph" | "image-block" | "image-inline" | "ordered-list-item" | "unordered-list-item" | "task-list-item" | "table-row" | "table-cell" | "table-content" | "code-block-line")[]) => void;
isConvertibleElement: (element: ImageBlockElement | ImageInlineElement | ParagraphElement | OrderedListItemElement | UnorderedListItemElement | TaskListItemElement | HorizontalRuleElement | TableElement | TableRowElement | TableCellElement | TableContentElement | CodeBlockElement | CodeBlockLineElement | BlockQuoteElement | HeadingElement | AnchorElement) => boolean;
convertElements: CurriedConvertElements;
};
type ConvertElementEditor = {
convertElement: ReturnType<typeof createConvertElementMethods>;
};
type ConvertElementPluginCustomTypes = {
Name: "convert-element";
Editor: ConvertElementEditor;
};
declare function createPasteMarkdownMethods(editor: Editor): {
pasteMarkdown: (markdown: string) => void;
};
type PasteMarkdownMethods = ReturnType<typeof createPasteMarkdownMethods>;
type PasteMarkdownEditor = {
pasteMarkdown: PasteMarkdownMethods;
};
type PasteMarkdownPluginCustomTypes = {
Name: "paste-markdown";
Editor: PasteMarkdownEditor;
};
declare function createFullscreenMethods(): {
toggleFullscreen: () => void;
isFullscreen: boolean;
};
type FullscreenMethods = ReturnType<typeof createFullscreenMethods>;
type FullscreenEditor = {
fullscreen: FullscreenMethods;
};
type FullscreenPluginCustomTypes = {
Name: "fullscreen";
Editor: FullscreenEditor;
};
type WysimarkEditor = {
/**
* Private state for the wysimark editor.
*/
wysimark: {
prevValue?: {
markdown: string;
children: Descendant[];
};
};
/**
* Public methods for the wysimark editor.
*/
getMarkdown: () => string;
setMarkdown: (markdown: string) => void;
};
declare const plugins: (TypedPlugin<FullscreenPluginCustomTypes> | TypedPlugin<PasteMarkdownPluginCustomTypes> | TypedPlugin<ConvertElementPluginCustomTypes> | TypedPlugin<AnchorPluginCustomTypes> | TypedPlugin<HeadingPluginCustomTypes> | TypedPlugin<MarksPluginCustomTypes> | TypedPlugin<InlineCodePluginCustomTypes> | TypedPlugin<BlockQuotePluginCustomTypes> | TypedPlugin<CodeBlockPluginCustomTypes> | TypedPlugin<TablePluginCustomTypes> | TypedPlugin<HorizontalRulePluginCustomTypes> | TypedPlugin<{
Name: "trailing-block";
Editor: {
allowTrailingBlock: true;
};
}> | TypedPlugin<ListPluginCustomTypes> | TypedPlugin<AtomicDeletePluginCustomTypes> | TypedPlugin<NormalizeAfterDeletePluginCustomTypes> | TypedPlugin<CollapsibleParagraphPluginCustomTypes> | TypedPlugin<ThemePluginCustomTypes> | TypedPlugin<ToolbarPluginCustomTypes> | TypedPlugin<UploadPluginCustomTypes> | TypedPlugin<AttachmentPluginCustomTypes> | TypedPlugin<ImagePluginCustomTypes> | TypedPlugin<PlaceholderPluginCustomTypes>)[];
type PluginTypes = ExtractCustomTypes<typeof plugins>;
type CustomEditor = PluginTypes["Editor"];
type CustomElement = PluginTypes["Element"];
type CustomText = PluginTypes["Text"];
type Element$1 = CustomElement;
type Text = CustomText;
declare module "slate" {
interface CustomTypes {
Editor: BaseEditor & ReactEditor & HistoryEditor & CustomEditor & WysimarkEditor;
Element: CustomElement;
Text: BaseText & CustomText;
}
}
declare function useEditor({ authToken, height, minHeight, maxHeight, onUploadButtonClick, }: {
authToken?: string;
height?: number;
minHeight?: number;
maxHeight?: number;
onUploadButtonClick?: () => void;
}): Editor & ReactEditor & WysimarkEditor;
type EditableProps = {
editor: Editor;
value: string;
onChange: (markdown: string) => void;
throttleInMs?: number;
placeholder?: string;
className?: string;
style?: React.CSSProperties;
};
declare function Editable({ editor, value, onChange, throttleInMs, placeholder, className, style, }: EditableProps): react_jsx_runtime.JSX.Element;
export { Editable, EditableProps, Element$1 as Element, Text, useEditor };