I am super excited to share with you all something I have been working as a side project for the past 1.5 years.
bangle.dev is a Javascript library that provides high level components built on top of amazing Prosemirror API.
Here are some of the highlights of this library:
React support: While the core is written in vanillaJS, I built a React wrapper to create some pretty UI using NodeViews without much friction.
Doesn’t add more abstractions: Looking at other libraries which try to blanket Prosemirror’s API with their own, I realized that it is not a great approach, as the moment you try to write a moderately complex app, you end up hitting the walls of these libraries. BangleJS focuses on providing code as components with minimal abstraction over Prosemirror.
There are tons of components within @banglejs please check them out here
I will keep this brief and show you some screenshots:
BangleJS is currently in its early stage, so I would love to hear your feedback and thoughts on the library.
Also, would like to thank this awesome community and the maintainer marijnh, I have learnt a lot and I hope this contribution will help some of you out there.
This is very interesting. Do I understand correctly that the core is mostly a set of tools for composing configurations more easily than with raw ProseMirror, or is there more going on? How does a BangleEditorState differ from a raw EditorState?
The goal of BangleJS is to provide pluggable opinionated components to build a reasonably good editor quickly. As soon as you hit the wall with these components, BangleJS doesn’t stand in your way to build complex components that leverages Prosemirror’s API.
Here is a skeleton of the bold component to show how grouping of various related things are done in Bangle component:
export const bold = {
spec(opts) {
type: 'mark',
name: 'bold',
schema: {
// Prosemirror MarkSpec
},
markdown: {
// markdown parsing and serializing things
}
// plans to add more fields in future
},
plugins(opts) {
// can be a deeply nested array of plugins
return [
...
]
},
commands: {
toggleBold: (opts) => (state, dispatch) => { .... },
...
}
}
To answer your question, BangleEditorState is a thin wrapper which does the following:
Accepts:
Nested array of PM Plugins
Array of objects where one of the field is schema this is where you put the Prosemirror Node/Mark spec of the component.
Initial content of the doc
pipes anything else directly to the PM EditorState.create static method
Processes:
Flattens the deeply nested array of plugins.
does same basic sanity checks and adds some reasonable default like doc, paragraph, text, history etc
Outputs:
An instance where the field pmState points to the PM EditorState instance built using the inputs.
I have intentionally kept the EditorState initialization separate from EditorView, just like Prosemirror so that if one fancies a Bangle component to be used in an existing Prosemirror app, they can do:
import {pluginLoader} from '@bangljs/core';
import tooltip from '@banglejs/tooltip';
const bangleRegistry = new SpecRegistry([tooltip.spec()], {defaultSpecs: false});
const banglePlugins = pluginLoader(bangleRegistry, [tooltip.plugins()]);
const newSchema = mergeSchema(myExistingSchema, bangleRegistry.schema)
const plugins = [myExistingPlugins, ...banglePlugins]
// use the above to create a PM EditorView
const view = new EditorView(...)
This has only been possible due to amazing level of modularity in Prosemirror, so hats off to you for that.
Note to myself: Write a guide on interoperability with PM.