@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
307 lines (303 loc) • 12.4 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ignoreResizerMutations = exports.BreakoutResizer = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = _interopRequireWildcard(require("react"));
var _bindEventListener = require("bind-event-listener");
var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
var _analytics = require("../analytics");
var _styles = require("../styles");
var _browser = require("../utils/browser");
var _Resizer = _interopRequireDefault(require("./Resizer"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var breakoutSupportedNodes = ['layoutSection', 'expand', 'codeBlock'];
var getHandleStyle = function getHandleStyle(node) {
var layoutMarginOffset = (0, _platformFeatureFlags.fg)('platform_editor_advanced_layouts_post_fix_patch_2') ? 12 : 8;
switch (node) {
case 'codeBlock':
return {
left: {
left: '-12px'
},
right: {
right: '-12px'
}
};
// expand and layout section elements have a negative margin applied
default:
var handleOffset = (0, _experiments.editorExperiment)('nested-dnd', true) ? _styles.LAYOUT_SECTION_MARGIN * 2 + layoutMarginOffset : _styles.LAYOUT_COLUMN_PADDING * 2;
return {
left: {
left: "-".concat(handleOffset, "px"),
height: 'calc(100% - 8px)',
bottom: '0px',
top: 'unset'
},
right: {
right: "-".concat(handleOffset, "px"),
height: 'calc(100% - 8px)',
bottom: '0px',
top: 'unset'
}
};
}
};
var ignoreResizerMutations = exports.ignoreResizerMutations = function ignoreResizerMutations(mutation) {
if ((0, _platformFeatureFlags.fg)('platform_editor_breakoutresizer_remove_assertion')) {
if (mutation.target instanceof Element) {
return mutation.target.classList.contains('resizer-item') || mutation.type === 'attributes' && mutation.attributeName === 'style';
}
return mutation.type === 'attributes' && mutation.attributeName === 'style';
} else {
return (
// eslint-disable-next-line @atlaskit/editor/no-as-casting
mutation.target.classList.contains('resizer-item') || mutation.type === 'attributes' && mutation.attributeName === 'style'
);
}
};
var resizingStyles = {
left: '50%',
transform: 'translateX(-50%)',
display: 'grid'
};
// Apply grid to stop drag handles rendering inside .resizer-item affecting its height
var defaultStyles = {
display: 'grid'
};
var RESIZE_STEP_VALUE = 10;
/**
* BreakoutResizer is a common component used to resize nodes that support the 'Breakout' mark, so it requires
* correct ADF support.
*
* use experiment platform_editor_advanced_layouts
*/
var BreakoutResizer = exports.BreakoutResizer = function BreakoutResizer(_ref) {
var editorView = _ref.editorView,
nodeType = _ref.nodeType,
getPos = _ref.getPos,
getRef = _ref.getRef,
disabled = _ref.disabled,
getEditorWidth = _ref.getEditorWidth,
parentRef = _ref.parentRef,
editorAnalyticsApi = _ref.editorAnalyticsApi,
displayGapCursor = _ref.displayGapCursor;
var _useState = (0, _react.useState)({
minWidth: undefined,
maxWidth: undefined,
isResizing: false
}),
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
_useState2$ = _useState2[0],
minWidth = _useState2$.minWidth,
maxWidth = _useState2$.maxWidth,
isResizing = _useState2$.isResizing,
setResizingState = _useState2[1];
var areResizeMetaKeysPressed = (0, _react.useRef)(false);
var resizerRef = (0, _react.useRef)(null);
// Relying on re-renders caused by selection changes inside/around node
var isSelectionInNode = (0, _react.useMemo)(function () {
var pos = getPos();
if (pos === undefined) {
return false;
}
var node = editorView.state.doc.nodeAt(pos);
if (node === null) {
return false;
}
var endPos = pos + node.nodeSize;
var startPos = pos;
var _editorView$state$sel = editorView.state.selection,
$from = _editorView$state$sel.$from,
$to = _editorView$state$sel.$to;
return $from.pos >= startPos && endPos >= $to.pos;
}, [editorView.state.doc, editorView.state.selection, getPos]);
var handleResizeStart = (0, _react.useCallback)(function () {
var newMinWidth;
var newMaxWidth;
var widthState = getEditorWidth();
var dispatch = editorView.dispatch,
state = editorView.state;
displayGapCursor(false);
if (widthState !== undefined && widthState.lineLength !== undefined && widthState.width !== undefined) {
newMaxWidth = Math.min(widthState.width - (0, _editorSharedStyles.akEditorGutterPaddingDynamic)() * 2 - _editorSharedStyles.akEditorGutterPadding, _editorSharedStyles.akEditorFullWidthLayoutWidth);
if ((0, _platformFeatureFlags.fg)('platform_editor_advanced_layouts_post_fix_patch_2')) {
newMinWidth = Math.min(widthState.lineLength, _editorSharedStyles.akEditorDefaultLayoutWidth, newMaxWidth);
} else {
newMinWidth = Math.min(widthState.lineLength, _editorSharedStyles.akEditorDefaultLayoutWidth);
}
}
setResizingState({
isResizing: true,
minWidth: newMinWidth,
maxWidth: newMaxWidth
});
dispatch(state.tr.setMeta('is-resizer-resizing', true));
}, [getEditorWidth, editorView, displayGapCursor]);
var handleResizeStop = (0, _react.useCallback)(function (originalState, delta) {
var newWidth = originalState.width + delta.width;
var pos = getPos();
if (pos === undefined) {
return;
}
var state = editorView.state,
dispatch = editorView.dispatch;
var breakout = state.schema.marks.breakout;
var node = state.doc.nodeAt(pos);
var newTr = state.tr;
if (node && breakoutSupportedNodes.includes(node.type.name)) {
var newBreakoutWidth = Math.max(newWidth, _editorSharedStyles.akEditorDefaultLayoutWidth);
newTr.setNodeMarkup(pos, node.type, node.attrs, [breakout.create({
width: newBreakoutWidth
})]);
var breakoutResizePayload = {
action: _analytics.ACTION.RESIZED,
actionSubject: _analytics.ACTION_SUBJECT.ELEMENT,
eventType: _analytics.EVENT_TYPE.TRACK,
attributes: {
nodeType: node.type.name,
prevWidth: originalState.width,
newWidth: newBreakoutWidth
}
};
editorAnalyticsApi === null || editorAnalyticsApi === void 0 || editorAnalyticsApi.attachAnalyticsEvent(breakoutResizePayload)(newTr);
}
newTr.setMeta('is-resizer-resizing', false).setMeta('scrollIntoView', false);
displayGapCursor(true);
dispatch(newTr);
setResizingState({
isResizing: false,
minWidth: undefined,
maxWidth: undefined
});
}, [getPos, editorView, displayGapCursor, editorAnalyticsApi]);
var handleEscape = (0, _react.useCallback)(function () {
editorView === null || editorView === void 0 || editorView.focus();
}, [editorView]);
var handleLayoutSizeChangeOnKeypress = (0, _react.useCallback)(function (step) {
if (!parentRef) {
return;
}
var resizerItem = parentRef.closest('.resizer-item');
if (!(resizerItem instanceof HTMLElement)) {
return;
}
var newWidth = resizerItem.offsetWidth + step;
if (maxWidth && newWidth > maxWidth || minWidth && newWidth < minWidth) {
return;
}
handleResizeStop({
width: resizerItem.offsetWidth,
x: 0,
y: 0,
height: 0
}, {
width: step,
height: 0
});
}, [handleResizeStop, maxWidth, minWidth, parentRef]);
var resizeHandleKeyDownHandler = (0, _react.useCallback)(function (event) {
var isBracketKey = event.code === 'BracketRight' || event.code === 'BracketLeft';
var metaKey = _browser.browser.mac ? event.metaKey : event.ctrlKey;
if (event.altKey || metaKey || event.shiftKey) {
areResizeMetaKeysPressed.current = true;
}
if (event.altKey && metaKey) {
if (isBracketKey) {
event.preventDefault();
handleLayoutSizeChangeOnKeypress(event.code === 'BracketRight' ? RESIZE_STEP_VALUE : -RESIZE_STEP_VALUE);
}
} else if (!areResizeMetaKeysPressed.current) {
handleEscape();
}
}, [handleEscape, handleLayoutSizeChangeOnKeypress]);
var resizeHandleKeyUpHandler = (0, _react.useCallback)(function (event) {
if (event.altKey || event.metaKey) {
areResizeMetaKeysPressed.current = false;
}
return;
}, [areResizeMetaKeysPressed]);
var resizerGlobalKeyDownHandler = (0, _react.useCallback)(function (event) {
if (!resizerRef.current) {
return;
}
var resizeHandleThumbEl = resizerRef.current.getResizerThumbEl();
var metaKey = _browser.browser.mac ? event.metaKey : event.ctrlKey;
var isTargetResizeHandle = event.target instanceof HTMLElement && event.target.classList.contains('resizer-handle-thumb');
if (event.altKey && event.shiftKey && metaKey && event.code === 'KeyR' || isTargetResizeHandle && (event.altKey || metaKey || event.shiftKey)) {
event.preventDefault();
if (!resizeHandleThumbEl) {
return;
}
resizeHandleThumbEl.focus();
resizeHandleThumbEl.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest'
});
}
}, [resizerRef]);
(0, _react.useLayoutEffect)(function () {
if (!resizerRef.current || !editorView) {
return;
}
var resizeHandleThumbEl = resizerRef.current.getResizerThumbEl();
if (!resizeHandleThumbEl) {
return;
}
if ((0, _platformFeatureFlags.fg)('platform_editor_advanced_layouts_a11y')) {
var editorViewDom = editorView.dom;
var unbindEditorViewDom = (0, _bindEventListener.bind)(editorViewDom, {
type: 'keydown',
listener: resizerGlobalKeyDownHandler
});
var unbindResizeHandle = (0, _bindEventListener.bindAll)(resizeHandleThumbEl, [{
type: 'keydown',
listener: resizeHandleKeyDownHandler
}, {
type: 'keyup',
listener: resizeHandleKeyUpHandler
}]);
return function () {
unbindEditorViewDom();
unbindResizeHandle();
};
}
}, [editorView, resizerGlobalKeyDownHandler, resizeHandleKeyDownHandler, resizeHandleKeyUpHandler]);
if (disabled) {
return /*#__PURE__*/_react.default.createElement("div", {
"data-testid": "breakout-resizer-editor-view-wrapper",
ref: function ref(_ref2) {
return getRef && getRef(_ref2);
}
});
}
return /*#__PURE__*/_react.default.createElement(_Resizer.default, {
ref: resizerRef,
enable: {
left: true,
right: true
},
handleStyles: getHandleStyle(nodeType),
minWidth: minWidth,
maxWidth: maxWidth
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
,
style: isResizing ? resizingStyles : defaultStyles,
handleResizeStart: handleResizeStart,
handleResizeStop: handleResizeStop,
childrenDOMRef: getRef,
resizeRatio: 2,
isHandleVisible: isSelectionInNode,
handleSize: "clamped",
handleHighlight: "full-height",
handlePositioning: "adjacent",
handleAlignmentMethod: "sticky"
});
};