UNPKG

@atlaskit/editor-plugin-breakout

Version:

Breakout plugin for @atlaskit/editor-core

269 lines (268 loc) 12.6 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _createClass from "@babel/runtime/helpers/createClass"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; 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) { _defineProperty(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; } import React, { useState } from 'react'; import { breakout } from '@atlaskit/adf-schema'; import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import { SafePlugin } from '@atlaskit/editor-common/safe-plugin'; import { BreakoutCssClassName } from '@atlaskit/editor-common/styles'; import { usePluginStateEffect } from '@atlaskit/editor-common/use-plugin-state-effect'; import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector'; import { akEditorSwoopCubicBezier } from '@atlaskit/editor-shared-styles'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { pluginKey } from './pm-plugins/plugin-key'; import { createResizingPlugin, resizingPluginKey } from './pm-plugins/resizing-plugin'; import { findSupportedNodeForBreakout } from './pm-plugins/utils/find-breakout-node'; import { getBreakoutMode } from './pm-plugins/utils/get-breakout-mode'; import { GuidelineLabel } from './ui/GuidelineLabel'; import LayoutButton from './ui/LayoutButton'; var BreakoutView = /*#__PURE__*/_createClass(function BreakoutView( /** * Note: this is actually a PMMark -- however our version * of the prosemirror and prosemirror types mean using PMNode * is not problematic. */ mark, view, appearance) { _classCallCheck(this, BreakoutView); var dom = document.createElement('div'); var contentDOM = document.createElement('div'); contentDOM.className = BreakoutCssClassName.BREAKOUT_MARK_DOM; contentDOM.setAttribute('data-testid', 'ak-editor-breakout-mark-dom'); dom.className = BreakoutCssClassName.BREAKOUT_MARK; dom.setAttribute('data-layout', mark.attrs.mode); dom.setAttribute('data-testid', 'ak-editor-breakout-mark'); dom.appendChild(contentDOM); dom.style.transform = 'none'; dom.style.display = 'flex'; dom.style.justifyContent = 'center'; contentDOM.style.transition = "min-width 0.5s ".concat(akEditorSwoopCubicBezier); if (editorExperiment('advanced_layouts', true)) { if (mark.attrs.width) { contentDOM.style.minWidth = "min(".concat(mark.attrs.width, "px, calc(100cqw - var(--ak-editor--breakout-full-page-guttering-padding)))"); } else { // original breakout algorithm is in calcBreakoutWidth from platform/packages/editor/editor-common/src/utils/breakout.ts if (mark.attrs.mode === 'full-width') { contentDOM.style.minWidth = "max(var(--ak-editor--line-length), min(var(--ak-editor--full-width-layout-width), calc(100cqw - var(--ak-editor--breakout-full-page-guttering-padding))))"; } if (mark.attrs.mode === 'wide') { if (appearance && appearance === 'full-width' && editorExperiment('single_column_layouts', true) && true) { contentDOM.style.minWidth = "min(var(--ak-editor--breakout-wide-layout-width), calc(100cqw - var(--ak-editor--breakout-full-page-guttering-padding)))"; } else { contentDOM.style.minWidth = "max(var(--ak-editor--line-length), min(var(--ak-editor--breakout-wide-layout-width), calc(100cqw - var(--ak-editor--breakout-full-page-guttering-padding))))"; } } } } else { // original breakout algorithm is in calcBreakoutWidth from platform/packages/editor/editor-common/src/utils/breakout.ts if (mark.attrs.mode === 'full-width') { contentDOM.style.minWidth = "max(var(--ak-editor--line-length), min(var(--ak-editor--full-width-layout-width), calc(100cqw - var(--ak-editor--breakout-full-page-guttering-padding))))"; } if (mark.attrs.mode === 'wide') { contentDOM.style.minWidth = "max(var(--ak-editor--line-length), min(var(--ak-editor--breakout-wide-layout-width), calc(100cqw - var(--ak-editor--breakout-full-page-guttering-padding))))"; } } this.dom = dom; this.mark = mark; this.view = view; this.contentDOM = contentDOM; }); function shouldPluginStateUpdate(newBreakoutNode, currentBreakoutNode) { if (newBreakoutNode && currentBreakoutNode) { return newBreakoutNode !== currentBreakoutNode; } return newBreakoutNode || currentBreakoutNode ? true : false; } function createPlugin(api, _ref, appearance) { var dispatch = _ref.dispatch; return new SafePlugin({ state: { init: function init() { return { breakoutNode: undefined, activeGuidelineKey: undefined }; }, apply: function apply(tr, pluginState) { var breakoutNode = findSupportedNodeForBreakout(tr.selection); if (shouldPluginStateUpdate(breakoutNode, pluginState.breakoutNode)) { var nextPluginState = _objectSpread(_objectSpread({}, pluginState), {}, { breakoutNode: breakoutNode }); dispatch(pluginKey, nextPluginState); return nextPluginState; } return pluginState; } }, key: pluginKey, props: { nodeViews: { // Note: When we upgrade to prosemirror 1.27.2 -- we should // move this to markViews. // See the following link for more details: // https://prosemirror.net/docs/ref/#view.EditorProps.nodeViews. breakout: function breakout(mark, view) { return new BreakoutView(mark, view, appearance); } } } }); } var LayoutButtonWrapper = function LayoutButtonWrapper(_ref2) { var api = _ref2.api, editorView = _ref2.editorView, boundariesElement = _ref2.boundariesElement, scrollableElement = _ref2.scrollableElement, mountPoint = _ref2.mountPoint; var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['editorViewMode', 'editorDisabled', 'blockControls'], function (states) { var _states$blockControls, _states$blockControls2, _states$editorViewMod, _states$editorDisable; return { isDragging: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isDragging, isPMDragging: (_states$blockControls2 = states.blockControlsState) === null || _states$blockControls2 === void 0 ? void 0 : _states$blockControls2.isPMDragging, mode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode, editorDisabled: (_states$editorDisable = states.editorDisabledState) === null || _states$editorDisable === void 0 ? void 0 : _states$editorDisable.editorDisabled }; }), editorDisabled = _useSharedPluginState.editorDisabled, isDragging = _useSharedPluginState.isDragging, isPMDragging = _useSharedPluginState.isPMDragging, mode = _useSharedPluginState.mode; var _useState = useState(false), _useState2 = _slicedToArray(_useState, 2), breakoutNodePresent = _useState2[0], setBreakoutNodePresent = _useState2[1]; var _useState3 = useState(expValEquals('platform_editor_hydratable_ui', 'isEnabled', true) && !editorView ? undefined : // Remove ! during platform_editor_hydratable_ui cleanup // eslint-disable-next-line @typescript-eslint/no-non-null-assertion getBreakoutMode(editorView.state)), _useState4 = _slicedToArray(_useState3, 2), breakoutMode = _useState4[0], setBreakoutMode = _useState4[1]; usePluginStateEffect(api, ['breakout'], function (_ref3) { var breakoutState = _ref3.breakoutState; if (expValEquals('platform_editor_hydratable_ui', 'isEnabled', true) && !editorView) { return; } if (breakoutState !== null && breakoutState !== void 0 && breakoutState.breakoutNode && !breakoutNodePresent) { setBreakoutNodePresent(true); } if (!(breakoutState !== null && breakoutState !== void 0 && breakoutState.breakoutNode) && breakoutNodePresent) { setBreakoutNodePresent(false); } // Remove ! during platform_editor_hydratable_ui cleanup // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var nextBreakoutMode = getBreakoutMode(editorView.state); if (nextBreakoutMode !== breakoutMode) { setBreakoutMode(nextBreakoutMode); } }); var interactionState = useSharedPluginStateSelector(api, 'interaction.interactionState'); if (interactionState === 'hasNotHadInteraction') { return null; } if (isDragging || isPMDragging) { if (editorExperiment('advanced_layouts', true)) { return null; } } var isViewMode = mode === 'view'; var isEditMode = mode === 'edit'; return !isViewMode && editorDisabled === false ? /*#__PURE__*/React.createElement(LayoutButton, { editorView: editorView, mountPoint: mountPoint, boundariesElement: boundariesElement, scrollableElement: scrollableElement, isLivePage: isEditMode, isBreakoutNodePresent: breakoutNodePresent, breakoutMode: breakoutMode, api: api }) : null; }; export var breakoutPlugin = function breakoutPlugin(_ref4) { var options = _ref4.config, api = _ref4.api; return { name: 'breakout', pmPlugins: function pmPlugins() { if (expValEquals('platform_editor_breakout_resizing', 'isEnabled', true)) { return [{ name: 'breakout-resizing', plugin: function plugin(_ref5) { var getIntl = _ref5.getIntl, nodeViewPortalProviderAPI = _ref5.nodeViewPortalProviderAPI; return createResizingPlugin(api, getIntl, nodeViewPortalProviderAPI, options); } }]; } return [{ name: 'breakout', plugin: function plugin(props) { return createPlugin(api, props, options === null || options === void 0 ? void 0 : options.appearance); } }]; }, marks: function marks() { return [{ name: 'breakout', mark: breakout }]; }, getSharedState: function getSharedState(editorState) { if (!editorState) { return { breakoutNode: undefined }; } if (expValEquals('platform_editor_breakout_resizing', 'isEnabled', true)) { var resizingPluginState = resizingPluginKey.getState(editorState); if (!resizingPluginState) { return { breakoutNode: undefined, activeGuidelineKey: undefined }; } return resizingPluginState; } var pluginState = pluginKey.getState(editorState); if (!pluginState) { return { breakoutNode: undefined }; } return pluginState; }, contentComponent: function contentComponent(_ref6) { var editorView = _ref6.editorView, popupsMountPoint = _ref6.popupsMountPoint, popupsBoundariesElement = _ref6.popupsBoundariesElement, popupsScrollableElement = _ref6.popupsScrollableElement; if (!editorView) { return null; } if (expValEquals('platform_editor_breakout_resizing', 'isEnabled', true)) { return /*#__PURE__*/React.createElement(GuidelineLabel, { api: api, editorView: editorView, mountPoint: popupsMountPoint, boundariesElement: popupsBoundariesElement, scrollableElement: popupsScrollableElement }); } // This is a bit crappy, but should be resolved once we move to a static schema. if (options && !options.allowBreakoutButton) { return null; } return /*#__PURE__*/React.createElement(LayoutButtonWrapper, { api: api, mountPoint: popupsMountPoint, editorView: editorView, boundariesElement: popupsBoundariesElement, scrollableElement: popupsScrollableElement }); } }; };