@atlaskit/editor-plugin-text-formatting
Version:
Text-formatting plugin for @atlaskit/editor-core
168 lines (167 loc) • 8.86 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
// TODO: ED-26962 - Ideally this should use the custom toggleMark function from @atlaskit/editor-common so we also disable the options when selecting inline nodes but it disables the marks when the selection is empty at this point in time which is undesirable
// import { toggleMark } from '@atlaskit/editor-common/mark';
import { moveLeft as keymapMoveLeft, moveRight as keymapMoveRight } from '@atlaskit/editor-common/keymaps';
import { anyMarkActive, wholeSelectionHasMarks } from '@atlaskit/editor-common/mark';
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
import { shallowEqual } from '@atlaskit/editor-common/utils';
import { toggleMark } from '@atlaskit/editor-prosemirror/commands';
import { MarkType } from '@atlaskit/editor-prosemirror/model';
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
import { createInlineCodeFromTextInputWithAnalytics } from '../editor-commands/text-formatting';
// Ignored via go/ees005
// eslint-disable-next-line import/no-namespace
import * as commands from '../editor-commands/text-formatting';
import { pluginKey } from './plugin-key';
var isSelectionInlineCursor = function isSelectionInlineCursor(selection) {
if (selection instanceof NodeSelection) {
return true;
}
return false;
};
var checkNodeSelection = function checkNodeSelection(mark, editorState, type) {
var selection = editorState.selection;
if (isSelectionInlineCursor(selection)) {
return false;
}
return toggleMark(mark, {
type: type
})(editorState);
};
var getTextFormattingState = function getTextFormattingState(editorState, _editorAnalyticsAPI) {
var _editorState$schema$m = editorState.schema.marks,
em = _editorState$schema$m.em,
code = _editorState$schema$m.code,
strike = _editorState$schema$m.strike,
strong = _editorState$schema$m.strong,
subsup = _editorState$schema$m.subsup,
underline = _editorState$schema$m.underline;
var state = {
isInitialised: true
};
var showOnlyCommonMarks = expValEquals('platform_editor_controls', 'cohort', 'variant1');
if (showOnlyCommonMarks) {
// Code marks will disable all other formatting options when they are included in a
// selection but (for now) we do not want to make it behave differently in regards to which
// toolbar items are highlighted on selection. We need to track code in selection seperately
// to ensure all other formatting options are disabled appropriately.
if (code) {
state.codeInSelection = anyMarkActive(editorState, code.create());
}
var marks = [[code, 'code'], [em, 'em'], [strike, 'strike'], [strong, 'strong'], [underline, 'underline'], [subsup === null || subsup === void 0 ? void 0 : subsup.create({
type: 'sub'
}), 'subscript'], [subsup === null || subsup === void 0 ? void 0 : subsup.create({
type: 'sup'
}), 'superscript']].filter(function (_ref) {
var _ref2 = _slicedToArray(_ref, 1),
mark = _ref2[0];
return mark;
});
var marksToName = new Map(marks);
var activeMarks = wholeSelectionHasMarks(editorState, Array.from(marksToName.keys()));
var _iterator = _createForOfIteratorHelper(marks),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _step$value = _slicedToArray(_step.value, 2),
mark = _step$value[0],
markName = _step$value[1];
var active = activeMarks.get(mark);
if (active !== undefined) {
state["".concat(markName, "Active")] = active;
}
state["".concat(markName, "Disabled")] =
// Disable when code is active, except for code itself which should not be disabled
// when code is in selection 😅
state.codeInSelection && markName !== 'code' ? true : !checkNodeSelection(mark instanceof MarkType ? mark : mark.type, editorState);
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return state;
}
if (code) {
state.codeActive = anyMarkActive(editorState, code.create());
state.codeDisabled = !checkNodeSelection(code, editorState);
}
if (em) {
state.emActive = anyMarkActive(editorState, em);
state.emDisabled = state.codeActive ? true : !checkNodeSelection(em, editorState);
}
if (strike) {
state.strikeActive = anyMarkActive(editorState, strike);
state.strikeDisabled = state.codeActive ? true : !checkNodeSelection(strike, editorState);
}
if (strong) {
state.strongActive = anyMarkActive(editorState, strong);
state.strongDisabled = state.codeActive ? true : !checkNodeSelection(strong, editorState);
}
if (subsup) {
var subMark = subsup.create({
type: 'sub'
});
var supMark = subsup.create({
type: 'sup'
});
state.subscriptActive = anyMarkActive(editorState, subMark);
state.superscriptActive = anyMarkActive(editorState, supMark);
state.subscriptDisabled = state.codeActive ? true : !checkNodeSelection(subsup, editorState, 'sub');
state.superscriptDisabled = state.codeActive ? true : !checkNodeSelection(subsup, editorState, 'sup');
}
if (underline) {
state.underlineActive = anyMarkActive(editorState, underline);
state.underlineDisabled = state.codeActive ? true : !checkNodeSelection(underline, editorState);
}
return state;
};
export var plugin = function plugin(dispatch, editorAnalyticsAPI) {
return new SafePlugin({
state: {
init: function init(_config, state) {
return getTextFormattingState(state, editorAnalyticsAPI);
},
apply: function apply(_tr, pluginState, _oldState, newState) {
var state = getTextFormattingState(newState, editorAnalyticsAPI);
if (!shallowEqual(pluginState, state)) {
dispatch(pluginKey, state);
return state;
}
return pluginState;
}
},
key: pluginKey,
props: {
handleKeyDown: function handleKeyDown(view, event) {
var _pluginKey$getState;
var state = view.state,
dispatch = view.dispatch;
if (event.key === keymapMoveRight.common && !event.metaKey) {
return commands.moveRight()(state, dispatch);
} else if (event.key === keymapMoveLeft.common && !event.metaKey) {
return commands.moveLeft()(state, dispatch);
} else if (event.key === 'u' && event.metaKey && (_pluginKey$getState = pluginKey.getState(state)) !== null && _pluginKey$getState !== void 0 && _pluginKey$getState.underlineDisabled && editorExperiment('platform_editor_controls', 'variant1')) {
// This is a workaround for browser behaviour with cmd+u (in Chrome only) where the underline mark being applied around the selection
event.preventDefault();
}
return false;
},
handleTextInput: function handleTextInput(view, from, to, text) {
var state = view.state,
dispatch = view.dispatch;
var schema = state.schema,
parentNodeType = state.selection.$from.parent.type;
if (parentNodeType.allowsMarkType(schema.marks.code)) {
return createInlineCodeFromTextInputWithAnalytics(editorAnalyticsAPI)(from, to, text)(state, dispatch);
}
return false;
}
}
});
};