UNPKG

@atlaskit/editor-plugin-custom-autoformat

Version:

Custom autoformat plugin for @atlaskit/editor-core

155 lines (153 loc) 5.17 kB
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin'; import { keydownHandler } from '@atlaskit/editor-prosemirror/keymap'; import { buildHandler, completeReplacements } from './pm-plugins/doc'; import { triggerInputRule } from './pm-plugins/input-rules'; import reducers from './pm-plugins/reducers'; import { autoformatAction, getPluginState, pluginKey } from './pm-plugins/utils'; export const createPMPlugin = ({ providerFactory }, options, api) => { const rules = []; return new SafePlugin({ state: { init() { return { resolving: [], matches: [], autoformattingProvider: undefined }; }, apply(tr, prevPluginState) { if (!prevPluginState) { return prevPluginState; } // remap positions const remappedPluginState = { ...prevPluginState, resolving: prevPluginState.resolving.map(candidate => ({ ...candidate, start: tr.mapping.map(candidate.start), end: tr.mapping.map(candidate.end, -1) })) }; const meta = tr.getMeta(pluginKey); if (!meta) { return remappedPluginState; } return reducers(remappedPluginState, meta); } }, props: { handleTextInput(view, from, to, text) { triggerInputRule(view, rules, from, to, text); return false; }, handleKeyDown: keydownHandler({ Enter: (_state, _dispatch, view) => { if (view) { triggerInputRule(view, rules, view.state.selection.from, view.state.selection.to, ''); } return false; } }) }, view() { const handleProvider = (name, provider) => { if (name !== 'autoformattingProvider' || !provider) { return; } provider.then(async autoformattingProvider => { const ruleset = await autoformattingProvider.getRules(); Object.keys(ruleset).forEach(rule => { const inputRule = { /** * On Shift + Enter, after the first word an Object Replacement Character (/ufffc) is added. * We still want to match strings that start with an Object Replacement Character. */ // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp matchTyping: new RegExp('(\\s+|^|\\ufffc)' + rule + '(\\s|,|\\.)$'), // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp matchEnter: new RegExp('(\\s+|^)' + rule + '()$'), handler: buildHandler(rule, ruleset[rule]) }; rules.push(inputRule); }); }); }; if (options !== null && options !== void 0 && options.autoformattingProvider) { handleProvider('autoformattingProvider', options.autoformattingProvider); } return { update(view) { const currentState = getPluginState(view.state); if (!currentState) { return; } // make replacements in document for finished autoformats if (currentState.matches) { completeReplacements(view, currentState); } } }; }, key: pluginKey }); }; export const setProvider = provider => tr => { return autoformatAction(tr, { action: 'setProvider', provider }); }; export const customAutoformatPlugin = ({ api, config: options }) => { let previousProvider; if (options !== null && options !== void 0 && options.autoformattingProvider) { options.autoformattingProvider.then(provider => { api === null || api === void 0 ? void 0 : api.core.actions.execute(({ tr }) => setProvider(provider)(tr)); }); } return { name: 'customAutoformat', pmPlugins() { return [{ name: 'customAutoformat', plugin: pmPluginFactoryParams => { return createPMPlugin(pmPluginFactoryParams, options, api); } }]; }, getSharedState(editorState) { var _getPluginState; if (!editorState) { return; } const { autoformattingProvider } = (_getPluginState = getPluginState(editorState)) !== null && _getPluginState !== void 0 ? _getPluginState : {}; return { autoformattingProvider }; }, actions: { setProvider: async providerPromise => { var _api$core$actions$exe; const provider = await providerPromise; // Prevent someone trying to set the exact same provider twice for performance reasons+ if (previousProvider === provider || (options === null || options === void 0 ? void 0 : options.autoformattingProvider) === providerPromise) { return false; } previousProvider = provider; return (_api$core$actions$exe = api === null || api === void 0 ? void 0 : api.core.actions.execute(({ tr }) => setProvider(provider)(tr))) !== null && _api$core$actions$exe !== void 0 ? _api$core$actions$exe : false; } } }; };