Plugins for rich text editors
DraftJS
Demo
New FormulaNew TableH1H2H3H4H5H6ULOLCode Block
BoldItalicUnderlineMonospace
Installation
npm i @reactivepad/draftjs# oryarn add @reactivepad/draftjs
import React, { Component, Fragment } from "react";import {convertFromRaw,CompositeDecorator,Editor,EditorState,Modifier,AtomicBlockUtils} from "draft-js";import { sample } from "./samples";import {buildPlugin,ENTITY_TYPES,syncStoreWithDoc,formulaDecoratorStrategy} from "@reactivepad/draftjs";class DraftEditor extends Component {constructor(props) {super(props);this.reactivepadPlugin = buildPlugin({toggleReadOnly: this.toggleReadOnly});const {components: { Formula }} = this.reactivepadPlugin;const decorator = new CompositeDecorator([{strategy: formulaDecoratorStrategy,component: props => (<Formula {...props} updateContentState={this.updateContentState} />)}]);const blocks = convertFromRaw(sample1);this.state = {editorState: EditorState.createWithContent(blocks, decorator),readOnly: false};}componentWillUnmount() {clearTimeout(this.formulaTimeoutId);this.reactivepadPlugin.destroy();}toggleReadOnly = (readOnly = !this.state.readOnly) => {this.setState({ readOnly });};onChange = (editorState, skipSyncWithStore) => {const prevContentState = this.state.editorState.getCurrentContent();this.setState({ editorState }, () => {if (!skipSyncWithStore &&prevContentState !== this.state.editorState.getCurrentContent()// don't sync when only selection has changed) {syncStoreWithDoc(this.reactivepadPlugin.store, this.state.editorState);}});};updateContentState = (contentState,skipSyncWithStore, // sometimes sync is unnecessaryeditorState = this.state.editorState) => {const newEditorState = EditorState.push(editorState,contentState,"insert-characters");this.onChange(newEditorState, skipSyncWithStore);};insertFormula = e => {e.preventDefault();const { editorState } = this.state;const formula = this.reactivepadPlugin.store.createAndAddFormula();const whitespace = " ";let contentState = editorState.getCurrentContent();contentState = Modifier.replaceText(contentState,editorState.getSelection(),whitespace);const contentStateWithEntity = contentState.createEntity(ENTITY_TYPES.FORMULA,"IMMUTABLE",formula.serialize());const entityKey = contentStateWithEntity.getLastCreatedEntityKey();const selectionState = editorState.getSelection();const contentStateWithFormula = Modifier.applyEntity(contentStateWithEntity,selectionState.merge({focusOffset: selectionState.getAnchorOffset() + whitespace.length}),entityKey);this.updateContentState(contentStateWithFormula, true);this.formulaTimeoutId = setTimeout(() => {formula.toggleEditing(true);}, 5);};insertTable = e => {e.preventDefault();const { editorState } = this.state;const table = this.reactivepadPlugin.store.createAndAddTable();const whitespace = " ";const contentStateWithEntity = editorState.getCurrentContent().createEntity(ENTITY_TYPES.TABLE, "IMMUTABLE", table.serialize());const entityKey = contentStateWithEntity.getLastCreatedEntityKey();let newEditorState = EditorState.set(editorState, {currentContent: contentStateWithEntity});newEditorState = AtomicBlockUtils.insertAtomicBlock(newEditorState,entityKey,whitespace);this.onChange(newEditorState);};blockRenderer = block => {const entityKey = block.getEntityAt(0);if (!entityKey) {return null;}const entity = this.state.editorState.getCurrentContent().getEntity(entityKey);const type = entity.getType();if (type !== ENTITY_TYPES.TABLE) {return null;}const { Table } = this.reactivepadPlugin.components;return {component: props => (<Table {...props} updateContentState={this.updateContentState} />),editable: false};};render() {const { editorState, readOnly } = this.state;return (<Fragment><button onClick={this.insertFormula}>Formula</button><button onClick={this.insertTable}>Table</button><div style={{ minHeight: "6rem" }}><EditoreditorState={editorState}onChange={this.onChange}blockRendererFn={this.blockRenderer}readOnly={readOnly}/></div></Fragment>);}}
ProseMirror
Demo
loading...
Installation
npm i @reactivepad/prosemirror# oryarn add @reactivepad/prosemirror
Below is slightly a modified version of a basic ProseMirror sample setup from official documentation https://prosemirror.net/examples/basic.
const {buildPlugin,nodes,menuItems,buildMenuDropdown} = "@reactivepad/prosemirror";// create a pluginconst reactivepadPlugin = buildPlugin();// extend default editor schema with nodes from Reactivepadconst editorSchema = new Schema({nodes: addListNodes(schema.spec.nodes, "paragraph block*", "block").append(nodes),marks: schema.spec.marks});// add Reactivepad's dropdown to menubarconst menu = buildMenuItems(editorSchema);menu.fullMenu.unshift([buildMenuDropdown(menuItems)]);// create ProseMirror editor instance with Reactivepad pluginnew EditorView(this.editorRef, {state: EditorState.create({schema: editorSchema,plugins: [...exampleSetup({schema: editorSchema,menuContent: menu.fullMenu}),reactivepadPlugin.plugin]})});}
CKEditor4
https://ckeditor.com/ckeditor-4
Demo
Installation
- Extract the contents of this
.zip
file to<CKEditor folder>/plugins/reactivepad
. Download link: https://github.com/reactivepad/reactivepad-ckeditor4/archive/master.zip - Add
reactivepad
to the list of extra plugins:
CKEDITOR.replace("editorId", {extraPlugins: "reactivepad"});
This plugin requires "widget" and "dialog" plugins, please make sure to have them installed.
For more information on CKEditor plugins installation, please refer to the official manual: https://ckeditor.com/docs/ckeditor4/latest/guide/dev_plugins.html#manual-installation