remirror/remirror

View on GitHub
packages/remirror__core/src/framework/base-framework.ts

Summary

Maintainability
A
0 mins
Test Coverage
F
0%
import type { Unsubscribe } from 'nanoevents';
import type {
  EditorState,
  EditorStateProps,
  EditorViewProps,
  PrimitiveSelection,
  RemirrorContentType,
  TextProps,
  Transaction,
  TransactionProps,
  TransactionTransformer,
} from '@remirror/core-types';
import type { DirectEditorProps } from '@remirror/pm/view';

import type { UpdatableViewProps } from '../builtins';
import type { AnyExtension, AnyExtensionConstructor } from '../extension';
import { ClassName } from '../helpers';
import type { ManagerEvents, RemirrorManager } from '../manager';
import type { FocusType } from '../types';

export interface BaseFramework<Extension extends AnyExtension> {
  /**
   * The name of the framework being used.
   */
  readonly name: string;

  /**
   * The state that is initially passed into the editor.
   */
  initialEditorState: EditorState;

  /**
   * The minimum required output from the framework.
   */
  readonly frameworkOutput: FrameworkOutput<Extension>;

  /**
   * Destroy the framework and cleanup all created listeners.
   */
  destroy(): void;
}

export interface FrameworkOptions<
  Extension extends AnyExtension,
  Props extends FrameworkProps<Extension>,
> {
  /**
   * The initial editor state
   */
  initialEditorState: EditorState;

  /**
   * A method for getting the passed in props.
   */
  getProps: () => Props;

  /**
   * When provided the view will immediately be inserted into the dom within
   * this element.
   */
  element?: Element;
}

/**
 * The base options for an editor wrapper. This is used within the react and dom
 * implementations.
 */
export interface FrameworkProps<Extension extends AnyExtension> {
  /**
   * Pass in the extension manager.
   *
   * The manager is responsible for handling all Prosemirror related
   * functionality.
   */
  manager: RemirrorManager<Extension>;

  /**
   * Set the starting value for the editor.
   *
   * Without setting the value prop `onChange` remirror renders as an uncontrolled
   * component. Value changes are passed back out of the editor and there is now
   * way to set the value via props. As a result this is the only opportunity to
   * directly control the rendered text.
   *
   * @defaultValue `{ type: 'doc', content: [{ type: 'paragraph' }] }`
   */
  initialContent?: RemirrorContentType | [RemirrorContentType, PrimitiveSelection];

  /**
   * Adds attributes directly to the prosemirror element.
   *
   * @defaultValue {}
   */
  attributes?: Record<string, string> | AttributePropFunction<Extension>;

  /**
   * Additional classes which can be passed into the the editor wrapper. These
   * are placed on root `Prosemirror` element and can be used to effect styling
   * within the editor.
   */
  classNames?: ClassName[];

  /**
   * Determines whether this editor is editable or not.
   *
   * @defaultValue true
   */
  editable?: boolean;

  /**
   * When set to true focus will be place on the editor as soon as it first
   * loads.
   *
   * @defaultValue false
   */
  autoFocus?: FocusType;

  /**
   * An event listener which is called whenever the editor gains focus.
   */
  onFocus?: (params: RemirrorEventListenerProps<Extension>, event: Event) => void;

  /**
   * An event listener which is called whenever the editor is blurred.
   */
  onBlur?: (params: RemirrorEventListenerProps<Extension>, event: Event) => void;

  /**
   * Called on every change to the Prosemirror state.
   */
  onChange?: RemirrorEventListener<Extension>;

  /**
   * A method called when the editor is dispatching the transaction.
   *
   * @remarks
   * Use this to update the transaction which will be used to update the editor
   * state.
   */
  onDispatchTransaction?: TransactionTransformer;

  /**
   * Sets the accessibility label for the editor instance.
   *
   * @defaultValue ''
   */
  label?: string;
}

export type AddFrameworkHandler<Extension extends AnyExtension> = <
  Key extends keyof FrameworkEvents<Extension>,
>(
  event: Key,
  cb: FrameworkEvents<Extension>[Key],
) => Unsubscribe;

/**
 * This is the base output that is created by a framework.
 */
export interface FrameworkOutput<Extension extends AnyExtension>
  extends Remirror.ManagerStore<Extension> {
  /**
   * The manager which was used to create this editor.
   */
  manager: RemirrorManager<Extension>;

  /**
   * Add event handlers to the remirror editor at runtime.
   */
  addHandler: AddFrameworkHandler<Extension>;

  /**
   * The unique id for the editor instance.
   */
  uid: string;

  /**
   * Clears all editor content.
   *
   * @param options - includes a `triggerChange` handler which should be
   * triggered by the update.
   *
   * To use this in a controlled editor, you must set `triggerChange` to `true`.
   */
  clearContent: (options?: TriggerChangeProps) => void;

  /**
   * Replace all editor content with the new content.
   *
   * @remarks
   *
   * Allows for the editor content to be overridden by force.
   *
   * @param triggerOnChange - whether the `onChange` handler should be triggered
   * by the update. Defaults to `false`.
   *
   * To use this in a controlled editor, you must set `triggerChange` to `true`.
   */
  setContent: (content: RemirrorContentType, options?: TriggerChangeProps) => void;

  /**
   * A getter function for the current editor state. It's a wrapper around
   * `view.state`.
   */
  getState: () => EditorState;

  /**
   * A getter function for the previous prosemirror editor state. It can be used
   * to check what's changed between states.
   */
  getPreviousState: () => EditorState;

  /**
   * Get an extension by it's constructor.
   */
  getExtension: <ExtensionConstructor extends AnyExtensionConstructor>(
    Constructor: ExtensionConstructor,
  ) => InstanceType<ExtensionConstructor>;

  /**
   * Assert if an extension is present by it's constructor.
   */
  hasExtension: <ExtensionConstructor extends AnyExtensionConstructor>(
    Constructor: ExtensionConstructor,
  ) => boolean;

  /**
   * Focus the editor at the `start` | `end` a specific position or at a valid
   * range between `{ from, to }`.
   *
   * @deprecated This method may be removed in the future and it is advisable to
   * use `commands.focus()`.
   */
  focus: (position?: FocusType) => void;

  /**
   * Blur the editor.
   *
   * @deprecated This method may be removed in the future and it is advisable to
   * use `commands.blur()`.
   */
  blur: (position?: PrimitiveSelection) => void;
}

export type CreateStateFromContent = (
  content: RemirrorContentType,
  selection?: PrimitiveSelection,
) => EditorState;

export interface RemirrorEventListenerProps<Extension extends AnyExtension>
  extends EditorStateProps,
    Remirror.ListenerProperties<Extension>,
    EditorViewProps {
  /**
   * The original transaction which caused this state update.
   *
   * This allows for inspecting the reason behind the state change. When
   * undefined this means that the state was updated externally.
   *
   * If available:
   * - Metadata on the transaction can be inspected. `tr.getMeta`
   * - Was the change caused by added / removed content? `tr.docChanged`
   * - Was ths change caused by an updated selection? `tr.selectionSet`
   * - `tr.steps` can be inspected for further granularity.
   */
  tr?: Transaction;

  /**
   * When the state updates are not controlled and it was a transaction that
   * caused the state to be updated this value captures all the transaction
   * updates caused by prosemirror plugins hook state methods like
   * `filterTransactions` and `appendTransactions`.
   *
   * This is for advanced users only.
   */
  transactions?: readonly Transaction[];

  /**
   * A shorthand way of checking whether the update was triggered by editor
   * usage (internal) or overwriting the state.
   *
   * - `true` The update was triggered by a change in the prosemirror doc or an
   *   update to the selection. In these cases `tr` will have a value.
   * - `false` The update was caused by a call to `setContent` or `resetContent`
   */
  internalUpdate: boolean;

  /**
   * True when this is the first render of the editor. This applies when the
   * editor is first attached to the DOM.
   */
  firstRender: boolean;

  /**
   * The previous state.
   */
  previousState: EditorState;

  /**
   * Manually create a new state object with the desired content.
   */
  createStateFromContent: CreateStateFromContent;
}

export type RemirrorEventListener<Extension extends AnyExtension> = (
  params: RemirrorEventListenerProps<Extension>,
) => void;

export type AttributePropFunction<Extension extends AnyExtension> = (
  params: RemirrorEventListenerProps<Extension>,
) => Record<string, string>;

export interface PlaceholderConfig extends TextProps {
  className: string;
}

export interface UpdateStateProps
  extends Partial<TransactionProps>,
    EditorStateProps,
    TriggerChangeProps {
  /**
   * When the state updates are not controlled and it was a transaction that
   * caused the state to be updated this value captures all the transaction
   * updates caused by prosemirror plugins hook state methods like
   * `filterTransactions` and `appendTransactions`.
   *
   * This is for advanced users only.
   */
  transactions?: readonly Transaction[];
}

export interface TriggerChangeProps {
  /**
   * Whether or not to trigger this as a change and call any handlers.
   *
   * @defaultValue true
   */
  triggerChange?: boolean;
}

export interface ListenerProps extends Partial<EditorStateProps>, Partial<TransactionProps> {
  /**
   * When the state updates are not controlled and it was a transaction that
   * caused the state to be updated this value captures all the transaction
   * updates caused by prosemirror plugins hook state methods like
   * `filterTransactions` and `appendTransactions`.
   *
   * This is for advanced users only.
   */
  transactions?: readonly Transaction[];
}

export interface FrameworkEvents<Extension extends AnyExtension>
  extends Pick<ManagerEvents, 'destroy'> {
  /**
   * An event listener which is called whenever the editor gains focus.
   */
  focus: (params: RemirrorEventListenerProps<Extension>, event: Event) => void;

  /**
   * An event listener which is called whenever the editor is blurred.
   */
  blur: (params: RemirrorEventListenerProps<Extension>, event: Event) => void;

  /**
   * Called on every state update after the state has been applied to the editor.
   *
   * This should be used to track the current editor state and check if commands
   * are enabled.
   */
  updated: RemirrorEventListener<Extension>;
}

export type UpdatableViewPropsObject = { [Key in UpdatableViewProps]: DirectEditorProps[Key] };

declare global {
  namespace Remirror {
    interface ListenerProperties<Extension extends AnyExtension> {}
  }
}