UNPKG

@atlaskit/editor-plugin-text-formatting

Version:

Text-formatting plugin for @atlaskit/editor-core

168 lines (167 loc) 8.86 kB
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; } } }); };