UNPKG

@atlaskit/editor-plugin-base

Version:

Base plugin for @atlaskit/editor-core

170 lines (168 loc) 5.93 kB
import { doc, paragraph, text } from '@atlaskit/adf-schema'; import { keymap } from '@atlaskit/editor-common/keymaps'; import { baseKeymap } from '@atlaskit/editor-prosemirror/commands'; import { history } from '@atlaskit/prosemirror-history'; import { setKeyboardHeight } from './editor-commands/set-keyboard-height'; import disableSpellcheckingPlugin from './pm-plugins/disable-spell-checking'; import filterStepsPlugin from './pm-plugins/filter-steps'; import frozenEditor from './pm-plugins/frozen-editor'; import inlineCursorTargetPlugin from './pm-plugins/inline-cursor-target'; import { createLazyNodeViewDecorationPlugin } from './pm-plugins/lazy-node-view-decoration'; import newlinePreserveMarksPlugin from './pm-plugins/newline-preserve-marks'; import scrollGutter from './pm-plugins/scroll-gutter/plugin'; import { getKeyboardHeight } from './pm-plugins/scroll-gutter/util/get-keyboard-height'; import { inputTracking } from './pm-plugins/utils/inputTrackingConfig'; export function resolveCallbacks(from, to, tr, callbacks) { const { doc } = tr; doc.nodesBetween(from, to, (node, pos) => { callbacks.forEach(cb => cb({ tr, node, pos, from, to })); }); } const SMART_TO_ASCII = { '…': '...', '→': '->', '←': '<-', '–': '--', '“': '"', '”': '"', '‘': "'", '’': "'" }; // eslint-disable-next-line require-unicode-regexp const FIND_SMART_CHAR = new RegExp(`[${Object.keys(SMART_TO_ASCII).join('')}]`, 'g'); const basePlugin = ({ config: options, api }) => { var _api$featureFlags, _api$base; const featureFlags = (api === null || api === void 0 ? void 0 : (_api$featureFlags = api.featureFlags) === null || _api$featureFlags === void 0 ? void 0 : _api$featureFlags.sharedState.currentState()) || {}; const callbacks = []; api === null || api === void 0 ? void 0 : (_api$base = api.base) === null || _api$base === void 0 ? void 0 : _api$base.actions.registerMarks(({ tr, node, pos, from, to }) => { const { doc } = tr; const { schema } = doc.type; const { text: textNodeType } = schema.nodes; if (node.type === textNodeType && node.text) { // Find a valid start and end position because the text may be partially selected. const startPositionInSelection = Math.max(pos, from); const endPositionInSelection = Math.min(pos + node.nodeSize, to); const textForReplacing = doc.textBetween(startPositionInSelection, endPositionInSelection); const newText = textForReplacing.replace(FIND_SMART_CHAR, match => { var _SMART_TO_ASCII$match; return (_SMART_TO_ASCII$match = SMART_TO_ASCII[match]) !== null && _SMART_TO_ASCII$match !== void 0 ? _SMART_TO_ASCII$match : match; }); const currentStartPos = tr.mapping.map(startPositionInSelection); const currentEndPos = tr.mapping.map(endPositionInSelection); tr.replaceWith(currentStartPos, currentEndPos, schema.text(newText, node.marks)); } }); return { name: 'base', getSharedState(editorState) { return { allowScrollGutter: options === null || options === void 0 ? void 0 : options.allowScrollGutter, keyboardHeight: getKeyboardHeight(editorState) }; }, actions: { setKeyboardHeight, resolveMarks: (from, to, tr) => resolveCallbacks(from, to, tr, callbacks), registerMarks: callback => { callbacks.push(callback); } }, pmPlugins() { const plugins = [{ name: 'filterStepsPlugin', plugin: ({ dispatchAnalyticsEvent }) => filterStepsPlugin(dispatchAnalyticsEvent) }]; plugins.push({ name: 'lazyNodeViewDecorationsPlugin', plugin: () => createLazyNodeViewDecorationPlugin() }); // In Chrome, when the selection is placed between adjacent nodes which are not contenteditatble // the cursor appears at the right most point of the parent container. // // In Firefox, when the selection is placed between adjacent nodes which are not contenteditatble // no cursor is presented to users. // // In Safari, when the selection is placed between adjacent nodes which are not contenteditatble // it is not possible to navigate with arrow keys. // // This plugin works around the issues by inserting decorations between // inline nodes which are set as contenteditable, and have a zero width space. plugins.push({ name: 'inlineCursorTargetPlugin', plugin: () => options && options.allowInlineCursorTarget ? inlineCursorTargetPlugin() : undefined }); plugins.push({ name: 'newlinePreserveMarksPlugin', plugin: newlinePreserveMarksPlugin }, { name: 'frozenEditor', plugin: ({ dispatchAnalyticsEvent }) => { return frozenEditor(api === null || api === void 0 ? void 0 : api.contextIdentifier)(dispatchAnalyticsEvent, inputTracking, undefined); } }, { name: 'history', plugin: () => history() }, // should be last :( { name: 'codeBlockIndent', plugin: () => keymap({ ...baseKeymap, 'Mod-[': () => true, 'Mod-]': () => true }) }); if (options && options.allowScrollGutter) { plugins.push({ name: 'scrollGutterPlugin', plugin: () => scrollGutter(options.allowScrollGutter) }); } plugins.push({ name: 'disableSpellcheckingPlugin', plugin: () => disableSpellcheckingPlugin(featureFlags) }); return plugins; }, nodes() { return [{ name: 'doc', node: doc }, { name: 'paragraph', node: paragraph }, { name: 'text', node: text }]; } }; }; export default basePlugin;