Blocknote Headless
This module provides a Vue component that renders a BlockNote editor with Cristal-specific features, such as the handling of internal links, attachments, macros, etc.
User
See Blocknote.
Admin
See Blocknote.
Developer
Concept and limitations
The rendered portion of BlockNote is in React, and a wrapper is designed around it to make its integration seamless in Vue. In other words, no React integration is required on the developer's part ; simply using the component 'as-is' will work. No configuration is required in the bundler (Vite, Rollup, ...) and React does not need to be imported.
Some limitations of that approach are:
- A React app is created for each instance of the BlockNote editor
- Communication with the editor is handled using events, as React and Vue events aren't compatible
- React versions need to be aligned ; if an application is using a given version and the BlockNote Headless module is using another, some problems may arise
Explanations
CBlockNoteView
type Props = {
editorProps: Omit<
BlockNoteViewWrapperProps,
"content" | "linkEditionCtx"
>;
editorContent: UniAst | Error;
collaborationProvider?: () => CollaborationInitializer;
container: Container;
}
type BlockNoteViewWrapperProps = {
/**
* Options to forward to the BlockNote editor
*/
blockNoteOptions?: Partial<
Omit<DefaultEditorOptionsType, "schema" | "collaboration">
>;
/**
* The display theme to use
*/
theme?: "light" | "dark";
/**
* The editor's language
*/
lang: EditorLanguage;
/**
* The editor's initial content
* If realtime is enabled, this content may be replaced by the other users' own editor content
*/
content: BlockType[];
/**
* Realtime options
*/
realtime?: {
hocusPocus: HocuspocusProviderConfiguration;
user: { name: string; color: string };
};
/**
* Run a function when the document's content change
* WARN: this function may be fired at a rapid rate if the user types rapidly. Debouncing may be required on your end.
*/
onChange?: (editor: EditorType) => void;
/**
* Link edition utilities
*/
linkEditionCtx: LinkEditionContext;
/**
* Make the wrapper forward some data through references
*/
refs?: {
setEditor?: (editor: EditorType) => void;
setProvider?: (provider: HocuspocusProvider) => void;
};
};
Vue Events
{
// Emitted as soon as a user-triggered change happens into the editor
// The event won't be triggered when the editor is filled with its initial content,
// or when the editor's content changes due to modifications made by other players in the realtime session
"instant-change": [];
// Emitted in the same context as "instant-change", but debounced
"debounced-change": [content: UniAst];
// Emitted when the realtime provider is set up in the editor
"setup-provider": [provider: HocuspocusProvider];
}
Vue Expose
{
// Get the editor's content
getContent(): UniAst | Error
}
Example
Here we are importing the CBlockNoteView
component from blocknote-headless
and using it inside our own.
This will render a complete BlockNote editor, along with the customizations that are proper to Cristal (handling of internal links, custom blocks, etc).
Many properties are available to customize the editor further, adding realtime capabilities or being notified when the editor's content changes.
<script setup lang="ts">
import { collaborationManagerProviderName } from "@xwiki/cristal-collaboration-api";
import type {
CollaborationInitializer,
CollaborationManagerProvider,
User,
} from "@xwiki/cristal-collaboration-api";
const collaborationManager = container
.get<CollaborationManagerProvider>(collaborationManagerProviderName)
.get();
const collaborationProvider = await collaborationManager.get();
</script>
<template>
<CBlockNoteView
:editor-props="{
lang: 'en',
// Optional
blockNoteOptions: {
// ...
},
// Optional
onChange(editor) {
// Get the entire document from the editor
console.log(editor.document)
},
// Optional
theme: 'dark'
}"
// Optional
:collaboration-provider="collaborationProvider"
@setup-provider="
(provider) =>
console.log('The realtime provider was set up', provider)
"
:editor-content="{
blocks: [
{
type: 'paragraph',
content: [
{
type: 'text',
content: 'Hello world!',
styles: { bold: true },
},
],
styles: {
textColor: '#123456',
},
},
],
}"
@instant-change="
console.log('Something changed inside the editor!')
"
@debounced-change="
console.log('Something changed inside the editor (only triggered once a few delay passed after the user stopped typing)')
"
/>
</template>

This project is being financed by the French State as part of the France 2030 program
Ce projet est financé par l’État Français dans le cadre de France 2030