@witchcraft/editor
    Preparing search index...

    Configures the document api which tells the editor how to load/unload/save/cache documents, including embedded ones.

    The cache implementation is left up to the user, hence why it's defined as a get/set interface. A load function must be provided for requesting uncached documents.

    save is optional, but you probably want to save. The function is automatically debounced the configured amount.

    **Any function that needs to return a document should return it unwrapped with toRaw if it was a ref or in a ref or reactive. **

    A refCounter object can be provided with the respective load/unload functions to be notified when embedded views load/unload documents to reference count them and unload them once no editors have them in use.

    It is safe to call load multiple times for the same document, the actual load function will only be called once per document, but the refCounter will be called multiple times.

    The getTitle function can be provided to customize the title of the embedded document. It's passed the full embed id and returns it as docId#blockId by default.

    If there are extensions that use onCreate to set state or have plugins that need to change the state on init with appendTransaction, they will not work since there is no view to initialize the plugins. To get around this, plugins can specify a stateInit function that will be called with a transaction from the initial loaded state which it can then modify while having access to this and the extension options.

    It can be a bit confusing how this works. It's quite different from how tiptap works.

    First, the api creates a single instance of the editor with the editor options initially provided (it can be replaced by passing your own editor instance).

    On load per document, the state of the doc is initialized. The plugins are copied from the single editor instance so that we can properly initialize the state. The plugin's stateInit is called, see above.

    When an editor component is mounted, useEditorContent will call the document api's preEditorInit to get a configuration for that component. This configuration can be edited in preEditorInit and is per editor component. It is disconnected from the per document configuration.

    There is no per document editor instance. This makes it tricky to work with extensions like Collaboration that expect this setup. They require some weird workarounds unfortunately.

    See useTestDocumentApi for an example of how to set things up.

    Type Parameters

    • T extends Record<string, any> = Record<string, any>

    Implements

    Index

    Constructors

    • Type Parameters

      • T extends Record<string, any> = Record<string, any>

      Parameters

      • __namedParameters: {
            cache: {
                get: (docId: string) => EditorState | undefined;
                set: (docId: string, state: EditorState) => void;
            };
            editorOptions: Partial<EditorOptions>;
            getSuggestions: (
                searchString: string,
            ) => Promise<{ docId: string; title: string }[]>;
            getTitle?: (docId: string, blockId?: string) => string;
            load: (
                docId: string,
                schema: Schema,
                plugins: Plugin<any>[],
                getConnectedEditors: () => Editor[],
            ) => Promise<{ data?: T; state: EditorState }>;
            postEditorInit?: (docId: string, editor: Editor) => void;
            preEditorInit?: (
                docId: string,
                options: Partial<EditorOptions>,
                state: EditorState,
            ) => Partial<EditorOptions>;
            refCounter: {
                load: (docId: string, loaded: { data?: T; state: EditorState }) => void;
                unload: (docId: string) => void;
            };
            save?: (docId: string) => Promise<void>;
            saveDebounce?: number;
            updateFilter?: (tr: Transaction) => boolean | undefined;
        }
        • cache: {
              get: (docId: string) => EditorState | undefined;
              set: (docId: string, state: EditorState) => void;
          }
        • editorOptions: Partial<EditorOptions>

          The editor options for the internal instance to use to keep track of the document state. It should include the same extensions as the root editor.

        • getSuggestions: (searchString: string) => Promise<{ docId: string; title: string }[]>
        • OptionalgetTitle?: (docId: string, blockId?: string) => string
        • load: (
              docId: string,
              schema: Schema,
              plugins: Plugin<any>[],
              getConnectedEditors: () => Editor[],
          ) => Promise<{ data?: T; state: EditorState }>

          Load should create the editor state and return it. It can also optionally return extra data which will be passed to the refCounter's load function.

        • OptionalpostEditorInit?: (docId: string, editor: Editor) => void
        • OptionalpreEditorInit?: (
              docId: string,
              options: Partial<EditorOptions>,
              state: EditorState,
          ) => Partial<EditorOptions>
        • refCounter: {
              load: (docId: string, loaded: { data?: T; state: EditorState }) => void;
              unload: (docId: string) => void;
          }
        • Optionalsave?: (docId: string) => Promise<void>
        • OptionalsaveDebounce?: number
        • OptionalupdateFilter?: (tr: Transaction) => boolean | undefined

      Returns DocumentApi<T>

    Properties

    connectedEditors: Record<string, Editor[]> = {}
    editor: Editor
    getEmbedTitle: (embedId: EmbedId) => string

    How to format the title of the embedded document. Defaults to docId#blockId

    getSuggestions: (
        searchString: string,
    ) => Promise<{ docId: string; title: string }[]>

    For the embedded document picker, should return suggestions for the search string.

    postEditorInit: (docId: string, editor: Editor) => void

    For replacing DocumentApi.preEditorInit which runs after initializing and loading the document but before the transaction listeners are added.

    Can be used to add the Collaboration extension for example (see useTestDocumentApi for an example).

    The default implementation just sets the content:

    preEditorInit: (_docId, options, state) => {
    options.content = state.doc.toJSON()
    return options
    }
    preEditorInit: (
        docId: string,
        options: Partial<EditorOptions>,
        state: EditorState,
    ) => Partial<EditorOptions> = ...

    Sets options before initializing the editor. By default just does options.content = state.doc.toJSON(), but can be useful for using per editor component plugins.

    This is normally a bit tricky to do since the editor component initializes the editor before the document is loaded and is re-used (the wrapper Editor component, not the editor) when the document changes.

    So this hook can be used to add these additional per-editor instances of extensions. Be sure to clone the properties you are modifying. They are only shallow cloned before being passed to the function.

    If you need per doc plugins use load instead. See useTestDocumentApi for an example.

    preEditorInit(docId, options: Partial<EditorOptions>, state: EditorState) {
    // we do not need to set options.content when using collab
    // so no options.content = state.doc.toJSON()
    const ydoc = cache.value[docId].ydoc
    // it's suggested you add the collab extension only here
    // otherwise you would have to initially configure it with a dummy document
    options.extensions = [
    ...(options.extensions ?? []),
    // per editor extensions
    ]
    return options
    },
    save: (docId: string) => void

    Debounced save (to storage) function. Use the event listeners to get notified when saving finishes.

    updateFilter?: (tr: Transaction) => boolean | undefined = ...

    Return false to prevent applying the transaction to the state in the cache.

    This used to be needed to ignore yjs transactions, but that's no longer the case. Even with multiple editors loaded to use the same ydoc, everything should work. Leaving the option in case it's needed for some other rare use case.

    Methods

    • Tells the document api how to load an unloaded document and any additional data. Whatever this function returns will be passed to the refCounter.load option in the default DocumentApi implementation.

      	 load: async ( docId: string, schema: Schema, plugins: Plugin[], getConnectedEditors: () => Editor[]) => {
      const dbDoc = getFromYourDb(docId)

      const state = EditorState.create({
      doc: yjs.doc,
      schema,
      plugins
      })
      // return the state and any additional data we want to cache
      return { state, data: { dbDoc } }
      },

      See DocumentApi.preEditorInit for how to set this up with sync (e.g. yjs).

      Parameters

      Returns Promise<{ data?: T; state: EditorState }>