@atlaskit/editor-plugin-base
Version:
Base plugin for @atlaskit/editor-core
170 lines (168 loc) • 5.93 kB
JavaScript
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;