UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

98 lines (97 loc) 2.98 kB
/** * Creates a ProseMirror plugin's state and handles UI updates. * * Here's a few things to keep in mind: * - plugin's state is stored as a single object * - `Reducer` specifies how plugin's state changes in response to commands * - `Command` describes only what happen, but not how state changes * - `mapping` could be used to map ProseMirror positions stored in plugin's state * * Example: * const { createPluginState, createCommand, getPluginState } = pluginFactory( * reducer, * pluginKey * ); * * export const createPlugin = (dispatch: Dispatch, initialState) => * new SafePlugin({ * state: createPluginState(dispatch, initialState), * key: pluginKey * }); * * Example of a reducer: * * export const reducer = ( * state: TablePluginState, * action: TableAction, * ): TablePluginState => { * switch (action.type) { * case 'TOGGLE_CONTEXTUAL_MENU': * return { * ...state, * isContextualMenuOpen: !state.isContextualMenuOpen, * }; * default: * return state; * } * }; * * * Example of a command: * * export const toggleContextualMenu = createCommand({ * type: 'TOGGLE_CONTEXTUAL_MENU', * }, tr => tr.setMeta('addToHistory', false)); * */ function isFunction(x) { return typeof x === 'function'; } export function pluginFactory(pluginKey, reducer, options = {}) { const { mapping, onDocChanged, onSelectionChanged } = options; return { createPluginState(dispatch, initialState) { return { init: (_, state) => isFunction(initialState) ? initialState(state) : initialState, apply: (tr, _pluginState, _oldEditorState, newEditorState) => { const oldPluginState = mapping ? mapping(tr, _pluginState, newEditorState) : _pluginState; let newPluginState = oldPluginState; const meta = tr.getMeta(pluginKey); if (meta) { newPluginState = reducer(oldPluginState, meta); } if (onDocChanged && tr.docChanged) { newPluginState = onDocChanged(tr, newPluginState, newEditorState); } else if (onSelectionChanged && tr.selectionSet) { newPluginState = onSelectionChanged(tr, newPluginState, newEditorState); } if (newPluginState !== oldPluginState) { dispatch(pluginKey, newPluginState); } return newPluginState; } }; }, createCommand(action, transform) { return (state, dispatch) => { if (dispatch) { const tr = transform ? transform(state.tr, state) : state.tr; const resolvedAction = isFunction(action) ? action(state) : action; if (tr && resolvedAction) { dispatch(tr.setMeta(pluginKey, resolvedAction)); } else { return false; } } return true; }; }, getPluginState(state) { return pluginKey.getState(state); } }; }