import type { AnyFunction, CommandFunction, CommandFunctionProps, EmptyShape, Fragment, FromToProps, LiteralUnion, MarkType, NodeType, PrimitiveSelection, ProsemirrorAttributes, ProsemirrorNode, RemirrorContentType, Static, Transaction } from '@remirror/core-types'; import { RemoveMarkProps, ReplaceTextProps, ToggleBlockItemProps } from '@remirror/core-utils'; import { Mark } from '@remirror/pm/model'; import type { EditorView } from '@remirror/pm/view'; import { InsertTextOptions, ToggleMarkProps } from '../commands'; import { AnyExtension, ChainedFromExtensions, CommandNames, CommandsFromExtensions, Helper, PlainExtension, UiCommandNames } from '../extension'; import type { CreateExtensionPlugin, ExtensionCommandReturn, FocusType, StateUpdateLifecycleProps } from '../types'; import { CommandDecoratorOptions } from './builtin-decorators'; export interface CommandOptions { /** * The className that is added to all tracker positions * * '@defaultValue 'remirror-tracker-position' */ trackerClassName?: Static; /** * The default element that is used for all trackers. * * @defaultValue 'span' */ trackerNodeName?: Static; } /** * Generate chained and unchained commands for making changes to the editor. * * @remarks * * Typically actions are used to create interactive menus. For example a menu * can use a command to toggle bold formatting or to undo the last action. * * @category Builtin Extension */ export declare class CommandsExtension extends PlainExtension { get name(): "commands"; /** * The current transaction which allows for making commands chainable. * * It is shared by all the commands helpers and can even be used in the * [[`KeymapExtension`]]. * * @internal */ get transaction(): Transaction; /** * This is the holder for the shared transaction which is shared by commands * in order to support chaining. * * @internal */ private _transaction?; /** * Track the decorated command data. */ private readonly decorated; onCreate(): void; /** * Attach commands once the view is attached. */ onView(view: EditorView): void; /** * Update the cached transaction whenever the state is updated. */ onStateUpdate({ state }: StateUpdateLifecycleProps): void; /** * Create a plugin that solely exists to track forced updates via the * generated plugin key. */ createPlugin(): CreateExtensionPlugin; /** * Enable custom commands to be used within the editor by users. * * This is preferred to the initial idea of setting commands on the * manager or even as a prop. The problem is that there's no typechecking * and it should be just fine to add your custom commands here to see the * dispatched immediately. * * To use it, firstly define the command. * * ```ts * import { CommandFunction } from 'remirror'; * * const myCustomCommand: CommandFunction = ({ tr, dispatch }) => { * dispatch?.(tr.insertText('My Custom Command')); * * return true; * } * ``` * * And then use it within the component. * * ```ts * import React, { useCallback } from 'react'; * import { useRemirror } from '@remirror/react'; * * const MyEditorButton = () => { * const { commands } = useRemirror(); * const onClick = useCallback(() => { * commands.customDispatch(myCustomCommand); * }, [commands]) * * return * } * ``` * * An alternative is to use a custom command directly from a * `prosemirror-*` library. This can be accomplished in the following way. * * * ```ts * import { joinDown } from 'prosemirror-commands'; * import { convertCommand } from 'remirror'; * * const MyEditorButton = () => { * const { commands } = useRemirror(); * const onClick = useCallback(() => { * commands.customDispatch(convertCommand(joinDown)); * }, [commands]); * * return ; * }; * ``` */ customDispatch(command: CommandFunction): CommandFunction; /** * Insert text into the dom at the current location by default. If a * promise is provided instead of text the resolved value will be inserted * at the tracked position. */ insertText(text: string | (() => Promise), options?: InsertTextOptions): CommandFunction; /** * Select the text within the provided range. * * Here are some ways it can be used. * * ```ts * // Set to the end of the document. * commands.selectText('end'); * * // Set the selection to the start of the document. * commands.selectText('start'); * * // Select all the text in the document. * commands.selectText('all') * * // Select a range of text. It's up to you to make sure the selected * // range is valid. * commands.selectText({ from: 10, to: 15 }); * * // Specify the anchor and range in the selection. * commands.selectText({ anchor: 10, head: 15 }); * * // Set to a specific position. * commands.selectText(10); * * // Use a ProseMirror selection * commands.selectText(TextSelection.near(state.doc.resolve(10))) * ``` * * Although this is called `selectText` you can provide your own selection * option which can be any type of selection. */ selectText(selection: PrimitiveSelection, options?: { forceUpdate?: boolean; }): CommandFunction; /** * Select the link at the current location. */ selectMark(type: string | MarkType): CommandFunction; /** * Delete the provided range or current selection. */ delete(range?: FromToProps): CommandFunction; /** * Fire an empty update to trigger an update to all decorations, and state * that may not yet have run. * * This can be used in extensions to trigger updates when certain options that * affect the editor state have changed. * * @param action - provide an action which is called just before the empty * update is dispatched (only when dispatch is available). This can be used in * chainable editor scenarios when you want to lazily invoke an action at the * point the update is about to be applied. */ emptyUpdate(action?: () => void): CommandFunction; /** * Force an update of the specific updatable ProseMirror props. * * This command is always available as a builtin command. * * @category Builtin Command */ forceUpdate(...keys: UpdatableViewProps[]): CommandFunction; /** * Update the attributes for the node at the specified `pos` in the * editor. * * @category Builtin Command */ updateNodeAttributes(pos: number, attrs: ProsemirrorAttributes): CommandFunction; /** * Set the content of the editor while preserving history. * * Under the hood this is replacing the content in the document with the new * state.doc of the provided content. * * If the content is a string you will need to ensure you have the proper * string handler set up in the editor. */ setContent(content: RemirrorContentType, selection?: PrimitiveSelection): CommandFunction; /** * Reset the content of the editor while preserving the history. * * This means that undo and redo will still be active since the doc is replaced with a new doc. */ resetContent(): CommandFunction; /** * Fire an update to remove the current range selection. The cursor will * be placed at the anchor of the current range selection. * * A range selection is a non-empty text selection. * * @category Builtin Command */ emptySelection(): CommandFunction; /** * Insert a new line into the editor. * * Depending on editor setup and where the cursor is placed this may have * differing impacts. * * @category Builtin Command */ insertNewLine(): CommandFunction; /** * Insert a node into the editor with the provided content. * * @category Builtin Command */ insertNode(node: string | NodeType | ProsemirrorNode | Fragment, options?: InsertNodeOptions): CommandFunction; /** * Set the focus for the editor. * * If using this with chaining this should only be placed at the end of * the chain. It can cause hard to debug issues when used in the middle of * a chain. * * ```tsx * import { useCallback } from 'react'; * import { useRemirrorContext } from '@remirror/react'; * * const MenuButton = () => { * const { chain } = useRemirrorContext(); * const onClick = useCallback(() => { * chain * .toggleBold() * .focus('end') * .run(); * }, [chain]) * * return * } * ``` */ focus(position?: FocusType): CommandFunction; /** * Blur focus from the editor and also update the selection at the same * time. */ blur(position?: PrimitiveSelection): CommandFunction; /** * Set the block type of the current selection or the provided range. * * @param nodeType - the node type to create * @param attrs - the attributes to add to the node type * @param selection - the position in the document to set the block node * @param preserveAttrs - when true preserve the attributes at the provided selection */ setBlockNodeType(nodeType: string | NodeType, attrs?: ProsemirrorAttributes, selection?: PrimitiveSelection, preserveAttrs?: boolean): CommandFunction; /** * Toggle between wrapping an inactive node with the provided node type, and * lifting it up into it's parent. * * @param nodeType - the node type to toggle * @param attrs - the attrs to use for the node * @param selection - the selection point in the editor to perform the action */ toggleWrappingNode(nodeType: string | NodeType, attrs?: ProsemirrorAttributes, selection?: PrimitiveSelection): CommandFunction; /** * Toggle a block between the provided type and toggleType. */ toggleBlockNodeItem(toggleProps: ToggleBlockItemProps): CommandFunction; /** * Wrap the selection or the provided text in a node of the given type with the * given attributes. */ wrapInNode(nodeType: string | NodeType, attrs?: ProsemirrorAttributes, range?: FromToProps | undefined): CommandFunction; /** * Removes a mark from the current selection or provided range. */ applyMark(markType: string | MarkType, attrs?: ProsemirrorAttributes, selection?: PrimitiveSelection): CommandFunction; /** * Removes a mark from the current selection or provided range. */ toggleMark(props: ToggleMarkProps): CommandFunction; /** * Removes a mark from the current selection or provided range. */ removeMark(props: RemoveMarkProps): CommandFunction; /** * Set the meta data to attach to the editor on the next update. */ setMeta(name: string, value: unknown): CommandFunction; /** * Select all text in the editor. */ selectAll(): CommandFunction; /** * Copy the selected content for non empty selections. */ copy(): CommandFunction; /** * Select all text in the editor. */ paste(): CommandFunction; /** * Cut the selected content. */ cut(): CommandFunction; /** * Replaces text with an optional appended string at the end. The replacement * can be text, or a custom node. * * @param props - see [[`ReplaceTextProps`]] */ replaceText(props: ReplaceTextProps): CommandFunction; /** * Get the all the decorated commands available on the editor instance. */ getAllCommandOptions(): Helper>>; /** * Get the options that were passed into the provided command. */ getCommandOptions(name: string): Helper | undefined>; /** * A short hand way of getting the `view`, `state`, `tr` and `dispatch` * methods. */ getCommandProp(): Helper>; /** * Update the command options via a shallow merge of the provided options. If * no options are provided the entry is deleted. * * @internal */ updateDecorated(name: string, options?: Partial>): void; /** * Needed on iOS since `requestAnimationFrame` doesn't breaks the focus * implementation. */ private handleIosFocus; /** * Focus the editor after a slight delay. */ private delayedFocus; /** * A helper for forcing through updates in the view layer. The view layer can * check for the meta data of the transaction with * `manager.store.getForcedUpdate(tr)`. If that has a value then it should use * the unique symbol to update the key. */ private readonly forceUpdateTransaction; /** * Check for a forced update in the transaction. This pulls the meta data * from the transaction and if it is true then it was a forced update. * * ```ts * import { CommandsExtension } from 'remirror/extensions'; * * const commandsExtension = manager.getExtension(CommandsExtension); * log(commandsExtension.getForcedUpdates(tr)) * ``` * * This can be used for updating: * * - `nodeViews` * - `editable` status of the editor * - `attributes` - for the top level node * * @internal */ private getForcedUpdates; /** * Get the command metadata. */ private getCommandMeta; private setCommandMeta; /** * Add the commands from the provided `commands` property to the `chained`, * `original` and `unchained` objects. */ private addCommands; /** * Create an unchained command method. */ private unchainedFactory; /** * Create the unchained command. */ private createUnchainedCommand; /** * Create a chained command method. */ private chainedFactory; } export interface InsertNodeOptions { attrs?: ProsemirrorAttributes; marks?: Array; /** * The content to insert. */ content?: Fragment | ProsemirrorNode | ProsemirrorNode[] | string; /** * @deprecated use selection property instead. */ range?: FromToProps; /** * Set the selection where the command should occur. */ selection?: PrimitiveSelection; /** * Set this to true to replace an empty parent block with this content (if the * content is a block node). */ replaceEmptyParentBlock?: boolean; } /** * Provides the list of Prosemirror EditorView props that should be updated/ */ export declare type ForcedUpdateMeta = UpdatableViewProps[]; export declare type UpdatableViewProps = 'attributes' | 'editable'; export interface CommandExtensionMeta { forcedUpdates?: UpdatableViewProps[]; } /** * A type with a name property. */ declare type WithName = Type & { name: string; }; declare global { namespace Remirror { interface ManagerStore { /** * Get the forced updates from the provided transaction. */ getForcedUpdates: (tr: Transaction) => ForcedUpdateMeta; /** * Enables the use of custom commands created by extensions which extend * the functionality of your editor in an expressive way. * * @remarks * * Commands are synchronous and immediately dispatched. This means that * they can be used to create menu items when the functionality you need * is already available by the commands. * * ```ts * if (commands.toggleBold.isEnabled()) { * commands.toggleBold(); * } * ``` */ commands: CommandsFromExtensions; /** * Chainable commands for composing functionality together in quaint and * beautiful ways * * @remarks * * You can use this property to create expressive and complex commands * that build up the transaction until it can be run. * * The way chainable commands work is by adding multiple steps to a shared * transaction which is then dispatched when the `run` command is called. * This requires making sure that commands within your code use the `tr` * that is provided rather than the `state.tr` property. `state.tr` * creates a new transaction which is not shared by the other steps in a * chainable command. * * The aim is to make as many commands as possible chainable as explained * [here](https://github.com/remirror/remirror/issues/418#issuecomment-666922209). * * There are certain commands that can't be made chainable. * * - undo * - redo * * ```ts * chain * .toggleBold() * .insertText('Hi') * .setSelection('all') * .run(); * ``` * * The `run()` method ends the chain and dispatches the command. */ chain: ChainedFromExtensions; } interface BaseExtension { /** * `ExtensionCommands` * * This pseudo property makes it easier to infer Generic types of this * class. * * @internal */ ['~C']: this['createCommands'] extends AnyFunction ? ReturnType : EmptyShape; /** * @experimental * * Stores all the command names for this decoration that have been added * as decorators to the extension instance. This is used by the * `CommandsExtension` to pick the commands and store meta data attached * to each command. * * @internal */ decoratedCommands?: Record; /** * Create and register commands for that can be called within the editor. * * These are typically used to create menu's actions and as a direct * response to user actions. * * @remarks * * The `createCommands` method should return an object with each key being * unique within the editor. To ensure that this is the case it is * recommended that the keys of the command are namespaced with the name * of the extension. * * ```ts * import { ExtensionFactory } from '@remirror/core'; * * const MyExtension = ExtensionFactory.plain({ * name: 'myExtension', * version: '1.0.0', * createCommands() { * return { * haveFun() { * return ({ state, dispatch }) => { * if (dispatch) { * dispatch(tr.insertText('Have fun!')); * } * * return true; // True return signifies that this command is enabled. * } * }, * } * } * }) * ``` * * The actions available in this case would be `undoHistory` and * `redoHistory`. It is unlikely that any other extension would override * these commands. * * Another benefit of commands is that they are picked up by typescript * and can provide code completion for consumers of the extension. */ createCommands?(): ExtensionCommandReturn; } interface ExtensionStore { /** * A property containing all the available commands in the editor. * * This should only be accessed after the `onView` lifecycle method * otherwise it will throw an error. If you want to use it in the * `createCommands` function then make sure it is used within the returned * function scope and not in the outer scope. */ commands: CommandsFromExtensions; /** * A method that returns an object with all the chainable commands * available to be run. * * @remarks * * Each chainable command mutates the states transaction so after running * all your commands. you should dispatch the desired transaction. * * This should only be called when the view has been initialized (i.e.) * within the `createCommands` method calls. * * ```ts * import { ExtensionFactory } from '@remirror/core'; * * const MyExtension = ExtensionFactory.plain({ * name: 'myExtension', * version: '1.0.0', * createCommands: () => { * // This will throw since it can only be called within the returned * methods. * const chain = this.store.chain; // ❌ * * return { * // This is good 😋 * haveFun() { * return ({ state, dispatch }) => * this.store.chain.insertText('fun!').run(); ✅ * }, * } * } * }) * ``` * * This should only be accessed after the `EditorView` has been fully * attached to the `RemirrorManager`. * * The chain can also be called as a function with a custom `tr` * parameter. This allows you to provide a custom transaction to use * within the chainable commands. * * Use the command at the beginning of the command chain to override the * shared transaction. * * There are times when you want to be sure of the transaction which is * being updated. * * To restore the previous transaction call the `restore` chained method. * * @param tr - the transaction to set */ chain: ChainedFromExtensions; } interface AllExtensions { commands: CommandsExtension; } /** * The command names for all core extensions. */ type AllCommandNames = LiteralUnion, string>; /** * The command names for all core extensions. */ type AllUiCommandNames = LiteralUnion, string>; } } export {};