UNPKG

@atlaskit/editor-plugin-toolbar

Version:

Toolbar plugin for @atlaskit/editor-core

247 lines (245 loc) 12.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.toolbarPlugin = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _react = _interopRequireDefault(require("react")); var _bindEventListener = require("bind-event-listener"); var _safePlugin = require("@atlaskit/editor-common/safe-plugin"); var _state = require("@atlaskit/editor-prosemirror/state"); var _utils = require("@atlaskit/editor-prosemirror/utils"); var _editorToolbarModel = require("@atlaskit/editor-toolbar-model"); var _platformFeatureFlags = require("@atlaskit/platform-feature-flags"); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _selectionToolbarOpenExperience = require("./pm-plugins/experiences/selection-toolbar-open-experience"); var _pluginKey = require("./pm-plugins/plugin-key"); var _consts = require("./ui/consts"); var _SelectionToolbar = require("./ui/SelectionToolbar"); var _toolbarComponents = require("./ui/toolbar-components"); var _toolbar = require("./ui/utils/toolbar"); function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function getSelectedNode(editorState) { var selection = editorState.selection; if (selection instanceof _state.NodeSelection) { return { node: selection.node, pos: selection.from, nodeType: selection.node.type.name, marks: selection.node.marks.map(function (mark) { return "".concat(mark.type.name, ":").concat(JSON.stringify(mark.attrs)); }) }; } var nodes = editorState.schema.nodes; var selectedNode = (0, _utils.findSelectedNodeOfType)([nodes.paragraph, nodes.heading, nodes.blockquote, nodes.panel, nodes.codeBlock])(selection); if (selectedNode) { return { node: selectedNode.node, pos: selectedNode.pos, nodeType: selectedNode.node.type.name, marks: selectedNode.node.marks.map(function (mark) { return "".concat(mark.type.name, ":").concat(JSON.stringify(mark.attrs)); }) }; } var parentNode = (0, _utils.findParentNodeOfType)([nodes.paragraph, nodes.heading, nodes.blockquote, nodes.panel, nodes.listItem, nodes.taskItem])(selection); if (parentNode) { return { node: parentNode.node, pos: parentNode.pos, nodeType: parentNode.node.type.name, marks: parentNode.node.marks.map(function (mark) { return "".concat(mark.type.name, ":").concat(JSON.stringify(mark.attrs)); }) }; } var $pos = selection.$from; return { node: $pos.parent, pos: $pos.pos, nodeType: $pos.parent.type.name, marks: $pos.marks().map(function (mark) { return "".concat(mark.type.name, ":").concat(JSON.stringify(mark.attrs)); }) }; } var toolbarPlugin = exports.toolbarPlugin = function toolbarPlugin(_ref) { var api = _ref.api, _ref$config = _ref.config, config = _ref$config === void 0 ? { disableSelectionToolbar: false, disableSelectionToolbarWhenPinned: false } : _ref$config; var refs = {}; var disableSelectionToolbar = config.disableSelectionToolbar, disableSelectionToolbarWhenPinned = config.disableSelectionToolbarWhenPinned, _config$contextualFor = config.contextualFormattingEnabled, contextualFormattingEnabled = _config$contextualFor === void 0 ? 'always-pinned' : _config$contextualFor, breakpointPreset = config.breakpointPreset; var registry = (0, _editorToolbarModel.createComponentRegistry)(); registry.register((0, _toolbarComponents.getToolbarComponents)(contextualFormattingEnabled, api, breakpointPreset)); return { name: 'toolbar', actions: { registerComponents: function registerComponents(toolbarComponents) { var replaceItems = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (replaceItems) { registry.safeRegister(toolbarComponents); } else { registry.register(toolbarComponents); } }, // EDITOR-6558: `componentFilter` is evaluated at read time (not at // `registerComponents` time) so it can react to runtime state // (e.g. the current markdown view mode) without forcing every // plugin to re-register its components. Returning a new array on // every call means React-tree consumers re-render whenever the // underlying filter would change. getComponents: function getComponents() { return config !== null && config !== void 0 && config.componentFilter ? registry.components.filter(config.componentFilter) : registry.components; }, contextualFormattingMode: function contextualFormattingMode() { var _config$contextualFor2; // EDITOR-6558: `contextualFormattingModeOverride` lets a // consumer (Markdown Mode in source/preview view) force the // toolbar to a specific docking position regardless of the // user's saved preference. Used to lock the toolbar to // `always-pinned` while the floating toolbar would be useless // (i.e. when there's no ProseMirror selection to anchor to). var override = config === null || config === void 0 || (_config$contextualFor2 = config.contextualFormattingModeOverride) === null || _config$contextualFor2 === void 0 ? void 0 : _config$contextualFor2.call(config); if (override) { return override; } return contextualFormattingEnabled !== null && contextualFormattingEnabled !== void 0 ? contextualFormattingEnabled : 'always-pinned'; }, getBreakpointPreset: function getBreakpointPreset() { return breakpointPreset; } }, getSharedState: function getSharedState(editorState) { if (!editorState) { return undefined; } return _pluginKey.editorToolbarPluginKey.getState(editorState); }, pmPlugins: function pmPlugins() { return [{ name: 'editor-toolbar-selection', plugin: function plugin() { // Tracks mouse-down state to prevent the focus event (first page load) // from prematurely showing the toolbar mid-drag var mouseState = { isMouseDown: false }; return new _safePlugin.SafePlugin({ key: _pluginKey.editorToolbarPluginKey, state: { init: function init(_, editorState) { return { shouldShowToolbar: false, selectedNode: getSelectedNode(editorState) }; }, apply: function apply(tr, pluginState, _, newState) { var meta = tr.getMeta(_pluginKey.editorToolbarPluginKey); var newPluginState = _objectSpread({}, pluginState); var shouldUpdateNode = tr.docChanged || tr.selectionSet; if (shouldUpdateNode) { var newSelectedNode = getSelectedNode(newState); var oldNode = pluginState.selectedNode; var hasNodeChanged = !oldNode || !newSelectedNode || oldNode.nodeType !== newSelectedNode.nodeType || oldNode.pos !== newSelectedNode.pos || JSON.stringify(oldNode.marks) !== JSON.stringify(newSelectedNode.marks); if (hasNodeChanged) { newPluginState.selectedNode = newSelectedNode; } } if (meta) { newPluginState = _objectSpread(_objectSpread({}, newPluginState), meta); } return newPluginState; } }, view: function view(_view) { var unbind = (0, _bindEventListener.bind)(_view.root, { type: 'mouseup', listener: function listener(ev) { var _api$editorViewMode; mouseState.isMouseDown = false; var event = ev; var isInToolbar = (0, _toolbar.isEventInContainer)(event, _consts.DEFAULT_POPUP_SELECTORS.toolbarContainer); var isInPortal = (0, _toolbar.isEventInContainer)(event, _consts.DEFAULT_POPUP_SELECTORS.portal); // We only want to set selectionStable to true if the editor has focus // to prevent the toolbar from showing when the editor is blurred // due to a click outside the editor. var editorViewModePlugin = api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.sharedState.currentState(); var isViewModeEnabled = (editorViewModePlugin === null || editorViewModePlugin === void 0 ? void 0 : editorViewModePlugin.mode) === 'view'; _view.dispatch(_view.state.tr.setMeta(_pluginKey.editorToolbarPluginKey, { shouldShowToolbar: !isViewModeEnabled ? _view.hasFocus() || isInToolbar || isInPortal : true })); } }); var unbindEditorViewFocus = (0, _bindEventListener.bind)(_view.dom, { type: 'focus', listener: function listener() { // On first page load, focus fires after mousedown — skip to // avoid showing the toolbar mid-drag if (mouseState.isMouseDown && (0, _platformFeatureFlags.fg)('platform_editor_fix_toolbar_on_first_highlight')) { return; } _view.dispatch(_view.state.tr.setMeta(_pluginKey.editorToolbarPluginKey, { shouldShowToolbar: true })); } }); return { destroy: function destroy() { unbind(); unbindEditorViewFocus(); } }; }, props: { handleDOMEvents: { mousedown: function mousedown(view) { mouseState.isMouseDown = true; view.dispatch(view.state.tr.setMeta(_pluginKey.editorToolbarPluginKey, { shouldShowToolbar: false })); return false; } } } }); } }].concat((0, _toConsumableArray2.default)(!disableSelectionToolbar && (0, _expValEquals.expValEquals)('platform_editor_experience_tracking', 'isEnabled', true) ? [{ name: 'selectionToolbarOpenExperience', plugin: function plugin() { return (0, _selectionToolbarOpenExperience.getSelectionToolbarOpenExperiencePlugin)({ refs: refs, dispatchAnalyticsEvent: function dispatchAnalyticsEvent(payload) { var _api$analytics; return api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 ? void 0 : _api$analytics.fireAnalyticsEvent(payload); } }); } }] : [])); }, contentComponent: !disableSelectionToolbar ? function (_ref2) { var editorView = _ref2.editorView, popupsMountPoint = _ref2.popupsMountPoint; refs.popupsMountPoint = popupsMountPoint || undefined; if (!editorView) { return null; } return /*#__PURE__*/_react.default.createElement(_SelectionToolbar.SelectionToolbarWithErrorBoundary, { api: api, editorView: editorView, mountPoint: popupsMountPoint, disableSelectionToolbarWhenPinned: disableSelectionToolbarWhenPinned !== null && disableSelectionToolbarWhenPinned !== void 0 ? disableSelectionToolbarWhenPinned : false }); } : undefined }; };