UNPKG

@atlaskit/editor-plugin-block-controls

Version:

Block controls plugin for @atlaskit/editor-core

291 lines (286 loc) 15.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.InlineDropTarget = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _react = require("react"); var _react2 = require("@emotion/react"); var _editorSharedStyles = require("@atlaskit/editor-shared-styles"); var _box = require("@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box"); var _adapter = require("@atlaskit/pragmatic-drag-and-drop/element/adapter"); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _decorationsCommon = require("../pm-plugins/decorations-common"); var _activeAnchorTracker = require("../pm-plugins/utils/active-anchor-tracker"); var _anchorUtils = require("../pm-plugins/utils/anchor-utils"); var _updateSelection = require("../pm-plugins/utils/update-selection"); /* eslint-disable @atlaskit/design-system/consistent-css-prop-usage */ /* eslint-disable @atlaskit/ui-styling-standard/no-unsafe-values */ /** * @jsxRuntime classic * @jsx jsx */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports var HOVER_ZONE_WIDTH = '--editor-blocks-inline-hover-zone-width'; var HOVER_ZONE_HEIGHT = '--editor-blocks-inline-hover-zone-height'; var HOVER_ZONE_TOP = '--editor-blocks-inline-hover-zone-top'; var HOVER_ZONE_BOTTOM = '--editor-blocks-inline-hover-zone-bottom'; var HOVER_ZONE_ANCHOR_NAME = '--editor-blocks-inline-hover-zone-anchor-name'; var hoverZoneCommonStyle = (0, _react2.css)({ position: 'absolute', // above the top and bottom drop zone as block hover zone zIndex: 120, positionAnchor: "var(".concat(HOVER_ZONE_ANCHOR_NAME, ")"), minWidth: "var(--ds-space-100, 8px)", left: 0, right: 0, width: "var(".concat(HOVER_ZONE_WIDTH, ")"), height: "var(".concat(HOVER_ZONE_HEIGHT, ")") }); var leftHoverZoneStyle = (0, _react2.css)({ right: "unset", top: "var(".concat(HOVER_ZONE_TOP, ")"), bottom: 'unset' }); var rightHoverZoneStyle = (0, _react2.css)({ left: "unset", top: 'unset', bottom: "var(".concat(HOVER_ZONE_BOTTOM, ")") }); // gap between node boundary and drop indicator/drop zone var GAP = 4; var dropTargetLayoutHintStyle = (0, _react2.css)({ height: '100%', position: 'absolute', borderRight: "var(--ds-border-width, 1px)".concat(" dashed ", "var(--ds-border-focused, #4688EC)"), width: 0, left: 0 }); var dropTargetLayoutHintLeftStyle = (0, _react2.css)({ left: 'unset', right: 0 }); var defaultNodeDimension = { width: '0', height: '0', top: 'unset', bottom: 'unset' }; var getWidthOffset = function getWidthOffset(node, width, position) { if (['mediaSingle', 'table', 'embedCard'].includes(node.type.name) || // block card (without datasource) is positioned left-aligned, hence share the same logic as align-start node.type.name === 'blockCard' && !node.attrs.datasource) { var isLeftPosition = position === 'left'; if (node.attrs.layout === 'align-start' || node.type.name === 'blockCard') { return isLeftPosition ? "-0.5*(var(--ak-editor--line-length) - ".concat(width, ")") : "0.5*(var(--ak-editor--line-length) - ".concat(width, ")"); } else if ((node === null || node === void 0 ? void 0 : node.attrs.layout) === 'align-end') { return isLeftPosition ? "0.5*(var(--ak-editor--line-length) - ".concat(width, ")") : "-0.5*(var(--ak-editor--line-length) - ".concat(width, ")"); } } if (node.type.name === 'bodiedExtension' || node.type.name === 'extension') { return '-12px'; } }; var TABLE_NUMBERED_COLUMN_WIDTH = 42; var InlineDropTarget = exports.InlineDropTarget = function InlineDropTarget(_ref) { var api = _ref.api, nextNode = _ref.nextNode, position = _ref.position, anchorRectCache = _ref.anchorRectCache, getPos = _ref.getPos; var ref = (0, _react.useRef)(null); var _useState = (0, _react.useState)(false), _useState2 = (0, _slicedToArray2.default)(_useState, 2), isDraggedOver = _useState2[0], setIsDraggedOver = _useState2[1]; var anchorName = (0, _react.useMemo)(function () { if ((0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true)) { var _getPos; return nextNode ? (api === null || api === void 0 ? void 0 : api.core.actions.getAnchorIdForNode(nextNode, (_getPos = getPos()) !== null && _getPos !== void 0 ? _getPos : -1)) || '' : ''; } return nextNode ? (0, _decorationsCommon.getNodeAnchor)(nextNode) : ''; }, [api, getPos, nextNode]); var _useActiveAnchorTrack = (0, _activeAnchorTracker.useActiveAnchorTracker)(anchorName), _useActiveAnchorTrack2 = (0, _slicedToArray2.default)(_useActiveAnchorTrack, 1), isActiveAnchor = _useActiveAnchorTrack2[0]; var isLeftPosition = position === 'left'; var nodeDimension = (0, _react.useMemo)(function () { if (!nextNode) { return defaultNodeDimension; } var nextNodePos = getPos(); var innerContainerWidth = null; var targetAnchorName = anchorName; if (['blockCard', 'embedCard', 'extension'].includes(nextNode.type.name)) { if (nextNode.attrs.layout === 'wide') { innerContainerWidth = "max(var(--ak-editor--legacy-breakout-wide-layout-width), var(--ak-editor--line-length))"; } else if (nextNode.attrs.layout === 'full-width') { innerContainerWidth = "min(calc(100cqw - ".concat(_editorSharedStyles.akEditorBreakoutPadding, "px), 1800px)"); } if (nextNode.type.name === 'blockCard' && !nextNode.attrs.layout && nextNode.attrs.datasource) { // block card with sourceNode and without layout has different width in full-width vs fixed-width editor // Hence we need to set it based on editor mode innerContainerWidth = 'var(--ak-editor-block-card-width)'; } if (nextNode.type.name === 'embedCard' && ['center', 'align-start', 'align-end'].includes(nextNode.attrs.layout)) { var percentageWidth = ((parseFloat(nextNode.attrs.width) || 100) / 100).toFixed(2); innerContainerWidth = "calc(var(--ak-editor--line-length) * ".concat(percentageWidth, ")"); } } else if (nextNode.type.name === 'table' && nextNode.firstChild) { var tableWidthAnchor = (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? typeof nextNodePos === 'number' ? (api === null || api === void 0 ? void 0 : api.core.actions.getAnchorIdForNode(nextNode.firstChild, nextNodePos + 1)) || '' : '' : (0, _decorationsCommon.getNodeAnchor)(nextNode.firstChild); var isNumberColumnEnabled = Boolean(nextNode.attrs.isNumberColumnEnabled); if ((0, _anchorUtils.isAnchorSupported)()) { innerContainerWidth = isNumberColumnEnabled ? "calc(anchor-size(".concat(tableWidthAnchor, " width) + ").concat(TABLE_NUMBERED_COLUMN_WIDTH, "px)") : "anchor-size(".concat(tableWidthAnchor, " width)"); } else { var _anchorRectCache$getR; innerContainerWidth = "".concat(((anchorRectCache === null || anchorRectCache === void 0 || (_anchorRectCache$getR = anchorRectCache.getRect(tableWidthAnchor)) === null || _anchorRectCache$getR === void 0 ? void 0 : _anchorRectCache$getR.width) || 0) + TABLE_NUMBERED_COLUMN_WIDTH, "px"); } if (nextNode.attrs.width) { // when the table has horizontal scroll innerContainerWidth = "min(".concat(nextNode.attrs.width, "px, ").concat(innerContainerWidth, ")"); } } else if (nextNode.type.name === 'mediaSingle' && nextNode.firstChild) { if ((0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true)) { var _nextNode$firstChild; // check pos is a number if (typeof nextNodePos === 'number' && ((_nextNode$firstChild = nextNode.firstChild) === null || _nextNode$firstChild === void 0 ? void 0 : _nextNode$firstChild.type.name) === 'media') { targetAnchorName = (api === null || api === void 0 ? void 0 : api.core.actions.getAnchorIdForNode(nextNode.firstChild, nextNodePos + 1)) || ''; } } else { targetAnchorName = (0, _decorationsCommon.getNodeAnchor)(nextNode.firstChild); } } // Set the height target anchor name to the first or last column of the layout section so that it also works for stacked layout var heightTargetAnchorName = targetAnchorName; if (nextNode.type.name === 'layoutSection' && nextNode.firstChild && nextNode.lastChild) { if (isLeftPosition) { if ((0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true)) { if (typeof nextNodePos === 'number') { heightTargetAnchorName = (api === null || api === void 0 ? void 0 : api.core.actions.getAnchorIdForNode(nextNode.firstChild, nextNodePos + 1)) || ''; } else { heightTargetAnchorName = ''; } } else { heightTargetAnchorName = (0, _decorationsCommon.getNodeAnchor)(nextNode.firstChild); } } else { if ((0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true)) { if (typeof nextNodePos === 'number') { var lastNodeStartPos = nextNode.content.size - nextNode.lastChild.nodeSize; heightTargetAnchorName = (api === null || api === void 0 ? void 0 : api.core.actions.getAnchorIdForNode(nextNode.lastChild, lastNodeStartPos + 1)) || ''; } else { heightTargetAnchorName = ''; } } else { heightTargetAnchorName = (0, _decorationsCommon.getNodeAnchor)(nextNode.lastChild); } } } if ((0, _anchorUtils.isAnchorSupported)()) { var width = innerContainerWidth || "anchor-size(".concat(targetAnchorName, " width)"); var height = "anchor-size(".concat(heightTargetAnchorName, " height)"); return { width: width, height: height, top: 'anchor(top)', bottom: 'anchor(bottom)', widthOffset: getWidthOffset(nextNode, width, position) }; } if (anchorRectCache) { var nodeRect = anchorRectCache.getRect(targetAnchorName); var _width = innerContainerWidth || "".concat((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.width) || 0, "px"); var top = nodeRect !== null && nodeRect !== void 0 && nodeRect.top ? "".concat(nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.top, "px") : 'unset'; var bottom = "100% - ".concat((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.bottom) || 0, "px + ").concat(GAP, "px"); var _height = "".concat((nodeRect === null || nodeRect === void 0 ? void 0 : nodeRect.height) || 0, "px"); if (heightTargetAnchorName !== targetAnchorName) { var nodeHeightRect = anchorRectCache.getRect(heightTargetAnchorName); _height = "".concat((nodeHeightRect === null || nodeHeightRect === void 0 ? void 0 : nodeHeightRect.height) || 0, "px + ").concat(GAP, "px"); } return { width: _width, height: _height, top: top, bottom: bottom, widthOffset: getWidthOffset(nextNode, _width, position) }; } return defaultNodeDimension; }, [nextNode, anchorName, anchorRectCache, getPos, api, isLeftPosition, position]); var onDrop = (0, _react.useCallback)(function () { var _api$blockControls; var _ref2 = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {}, activeNode = _ref2.activeNode; if (!activeNode) { return; } var toPos = getPos(); var mappedTo; if (activeNode && toPos !== undefined) { var _api$core, _api$core2; var start = activeNode.pos; var moveToEnd = position === 'right'; api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref3) { var _api$blockControls2; var tr = _ref3.tr; api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || (_api$blockControls2 = _api$blockControls2.commands) === null || _api$blockControls2 === void 0 || _api$blockControls2.moveToLayout(start, toPos, { moveToEnd: moveToEnd })({ tr: tr }); var insertLayoutStep = (0, _updateSelection.getInsertLayoutStep)(tr); mappedTo = insertLayoutStep === null || insertLayoutStep === void 0 ? void 0 : insertLayoutStep.from; return tr; }); api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (_ref4) { var tr = _ref4.tr; if (mappedTo !== undefined) { (0, _updateSelection.updateSelection)(tr, mappedTo, moveToEnd); } return tr; }); } }, [api, getPos, position]); var hoverZoneRectStyle = (0, _react.useMemo)(function () { var isLayoutNode = (nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name) === 'layoutSection'; var layoutAdjustment = isLayoutNode ? { width: 11, height: 4, top: 6, bottom: 2 } : undefined; return (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, HOVER_ZONE_WIDTH, nodeDimension.widthOffset ? "calc((100% - ".concat(nodeDimension.width, ")/2 - ").concat(GAP, "px + ").concat(nodeDimension.widthOffset, " - ").concat((layoutAdjustment === null || layoutAdjustment === void 0 ? void 0 : layoutAdjustment.width) || 0, "px)") : "calc((100% - ".concat(nodeDimension.width, ")/2 - ").concat(GAP, "px - ").concat((layoutAdjustment === null || layoutAdjustment === void 0 ? void 0 : layoutAdjustment.width) || 0, "px)")), HOVER_ZONE_HEIGHT, "calc(".concat(nodeDimension.height, " + ").concat((layoutAdjustment === null || layoutAdjustment === void 0 ? void 0 : layoutAdjustment.height) || 0, "px)")), HOVER_ZONE_TOP, "calc(".concat(nodeDimension.top, " + ").concat((layoutAdjustment === null || layoutAdjustment === void 0 ? void 0 : layoutAdjustment.top) || 0, "px)")), HOVER_ZONE_BOTTOM, "calc(".concat(nodeDimension.bottom, " - ").concat((layoutAdjustment === null || layoutAdjustment === void 0 ? void 0 : layoutAdjustment.bottom) || 0, "px)")), HOVER_ZONE_ANCHOR_NAME, anchorName); }, [nextNode === null || nextNode === void 0 ? void 0 : nextNode.type.name, nodeDimension, anchorName]); var dropIndicatorPos = (0, _react.useMemo)(function () { return isLeftPosition ? 'right' : 'left'; }, [isLeftPosition]); (0, _react.useEffect)(function () { if (ref.current) { return (0, _adapter.dropTargetForElements)({ element: ref.current, onDragEnter: function onDragEnter() { setIsDraggedOver(true); }, onDragLeave: function onDragLeave() { setIsDraggedOver(false); }, onDrop: onDrop }); } }, [onDrop, setIsDraggedOver]); return (0, _react2.jsx)("div", { ref: ref, "data-testid": "drop-target-hover-zone-".concat(position), css: [hoverZoneCommonStyle, isLeftPosition ? leftHoverZoneStyle : rightHoverZoneStyle] // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop , style: hoverZoneRectStyle }, isDraggedOver ? (0, _react2.jsx)(_box.DropIndicator, { edge: dropIndicatorPos }) : isActiveAnchor && (0, _react2.jsx)("div", { "data-testid": "block-ctrl-drop-hint", css: [dropTargetLayoutHintStyle, isLeftPosition && dropTargetLayoutHintLeftStyle] })); };