@atlaskit/editor-plugin-breakout
Version:
Breakout plugin for @atlaskit/editor-core
269 lines (268 loc) • 12.6 kB
JavaScript
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
});
}
};
};