@atlaskit/editor-plugin-block-controls
Version:
Block controls plugin for @atlaskit/editor-core
243 lines (241 loc) • 11.1 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
/**
* @jsxRuntime classic
* @jsx jsx
*/
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports
import { css, jsx } from '@emotion/react';
import { layoutBreakpointWidth } from '@atlaskit/editor-shared-styles';
import { DropIndicator } from '@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box';
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { getNodeAnchor } from '../pm-plugins/decorations-common';
import { useActiveAnchorTracker } from '../pm-plugins/utils/active-anchor-tracker';
import { isAnchorSupported } from '../pm-plugins/utils/anchor-utils';
import { getInsertLayoutStep, updateSelection } from '../pm-plugins/utils/update-selection';
import { getAnchorAttrName } from './utils/dom-attr-name';
// 8px gap + 16px on left and right
var DROP_TARGET_LAYOUT_DROP_ZONE_WIDTH = 40;
var dropTargetLayoutStyle = css({
height: '100%',
width: "".concat(DROP_TARGET_LAYOUT_DROP_ZONE_WIDTH, "px"),
transform: 'translateX(-50%)',
zIndex: 120,
position: 'relative',
display: 'flex',
justifyContent: 'center'
});
var dropTargetLayoutHintStyle = css({
height: '100%',
position: 'relative',
borderRight: "var(--ds-border-width, 1px)".concat(" dashed ", "var(--ds-border-focused, #4688EC)"),
width: 0
});
export var DropTargetLayout = function DropTargetLayout(props) {
var _ref$current, _api$blockControls;
var api = props.api,
getPos = props.getPos,
parent = props.parent,
anchorRectCache = props.anchorRectCache;
var ref = useRef(null);
var _useState = useState(false),
_useState2 = _slicedToArray(_useState, 2),
isDraggedOver = _useState2[0],
setIsDraggedOver = _useState2[1];
var anchorName = getNodeAnchor(parent);
var nextNodeAnchorName = (_ref$current = ref.current) === null || _ref$current === void 0 || (_ref$current = _ref$current.parentElement) === null || _ref$current === void 0 || (_ref$current = _ref$current.nextElementSibling) === null || _ref$current === void 0 ? void 0 : _ref$current.getAttribute(getAnchorAttrName());
var height = '100%';
if (nextNodeAnchorName) {
if (isAnchorSupported()) {
height = "anchor-size(".concat(nextNodeAnchorName, " height)");
} else if (anchorRectCache) {
var layoutColumnRect = anchorRectCache.getRect(nextNodeAnchorName);
height = "".concat((layoutColumnRect === null || layoutColumnRect === void 0 ? void 0 : layoutColumnRect.height) || 0, "px");
}
}
var dropTargetStackLayoutHintStyle = css(_defineProperty({}, "@container layout-area (max-width:".concat(layoutBreakpointWidth.MEDIUM - 1, "px)"), {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
height: height,
marginTop: "var(--ds-space-050, 4px)"
}));
var _useActiveAnchorTrack = useActiveAnchorTracker(anchorName),
_useActiveAnchorTrack2 = _slicedToArray(_useActiveAnchorTrack, 1),
isActiveAnchor = _useActiveAnchorTrack2[0];
var _ref = (api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.sharedState.currentState()) || {},
activeNode = _ref.activeNode;
var onDrop = useCallback(function () {
if (!activeNode) {
return;
}
var to = getPos();
var mappedTo;
if (to !== undefined) {
var _api$core, _api$core2;
var from = activeNode.pos;
api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref2) {
var _api$blockControls2;
var tr = _ref2.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(from, to)({
tr: tr
});
var insertColumnStep = getInsertLayoutStep(tr);
mappedTo = insertColumnStep === null || insertColumnStep === void 0 ? void 0 : insertColumnStep.from;
return tr;
});
api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (_ref3) {
var tr = _ref3.tr;
if (mappedTo !== undefined) {
updateSelection(tr, mappedTo);
}
return tr;
});
}
}, [api, getPos, activeNode]);
useEffect(function () {
if (ref.current) {
return dropTargetForElements({
element: ref.current,
onDragEnter: function onDragEnter() {
setIsDraggedOver(true);
},
onDragLeave: function onDragLeave() {
setIsDraggedOver(false);
},
onDrop: onDrop
});
}
}, [onDrop]);
if ((activeNode === null || activeNode === void 0 ? void 0 : activeNode.nodeType) === 'layoutSection') {
return null;
}
return jsx("div", {
ref: ref
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
,
css: [dropTargetLayoutStyle, dropTargetStackLayoutHintStyle],
"data-testid": "block-ctrl-drop-indicator"
}, isDraggedOver ? jsx(DropIndicator, {
edge: "right",
gap: "-".concat(DROP_TARGET_LAYOUT_DROP_ZONE_WIDTH, "px")
}) : (isActiveAnchor || expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true)) && jsx("div", {
"data-testid": "block-ctrl-drop-hint",
css: dropTargetLayoutHintStyle
}));
};
export var DropTargetLayoutNativeAnchorSupport = function DropTargetLayoutNativeAnchorSupport(props) {
var _api$blockControls3;
var api = props.api,
getPos = props.getPos,
parent = props.parent,
anchorRectCache = props.anchorRectCache;
var ref = useRef(null);
var _useState3 = useState(false),
_useState4 = _slicedToArray(_useState3, 2),
isDraggedOver = _useState4[0],
setIsDraggedOver = _useState4[1];
var anchorName = getNodeAnchor(parent);
var _useState5 = useState(null),
_useState6 = _slicedToArray(_useState5, 2),
nextNodeAnchorName = _useState6[0],
setNextNodeAnchorName = _useState6[1];
var readNextNodeAnchor = useCallback(function () {
var _ref$current2, _nextElementSibling$g;
var nextElementSibling = (_ref$current2 = ref.current) === null || _ref$current2 === void 0 || (_ref$current2 = _ref$current2.parentElement) === null || _ref$current2 === void 0 ? void 0 : _ref$current2.nextElementSibling;
var attrName = getAnchorAttrName();
var nextAnchorName = (_nextElementSibling$g = nextElementSibling === null || nextElementSibling === void 0 ? void 0 : nextElementSibling.getAttribute(attrName)) !== null && _nextElementSibling$g !== void 0 ? _nextElementSibling$g : null;
setNextNodeAnchorName(function (prev) {
return prev === nextAnchorName ? prev : nextAnchorName;
});
}, []);
var height = useMemo(function () {
if (nextNodeAnchorName) {
if (isAnchorSupported()) {
return "anchor-size(".concat(nextNodeAnchorName, " height)");
} else if (anchorRectCache) {
var layoutColumnRect = anchorRectCache.getRect(nextNodeAnchorName);
return "".concat((layoutColumnRect === null || layoutColumnRect === void 0 ? void 0 : layoutColumnRect.height) || 0, "px");
}
}
// Stacked mode fallback: minimal height to avoid oversized hint on first render
return '0px';
}, [nextNodeAnchorName, anchorRectCache]);
useLayoutEffect(function () {
var raf = requestAnimationFrame(function () {
readNextNodeAnchor();
});
return function () {
return cancelAnimationFrame(raf);
};
}, [readNextNodeAnchor]);
var dropTargetStackLayoutHintStyle = css(_defineProperty({}, "@container layout-area (max-width:".concat(layoutBreakpointWidth.MEDIUM - 1, "px)"), {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values
height: height,
marginTop: "var(--ds-space-050, 4px)"
}));
var _useActiveAnchorTrack3 = useActiveAnchorTracker(anchorName),
_useActiveAnchorTrack4 = _slicedToArray(_useActiveAnchorTrack3, 1),
isActiveAnchor = _useActiveAnchorTrack4[0];
var _ref4 = (api === null || api === void 0 || (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 ? void 0 : _api$blockControls3.sharedState.currentState()) || {},
activeNode = _ref4.activeNode;
var onDrop = useCallback(function () {
if (!activeNode) {
return;
}
var to = getPos();
var mappedTo;
if (to !== undefined) {
var _api$core3, _api$core4;
var from = activeNode.pos;
api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref5) {
var _api$blockControls4;
var tr = _ref5.tr;
api === null || api === void 0 || (_api$blockControls4 = api.blockControls) === null || _api$blockControls4 === void 0 || (_api$blockControls4 = _api$blockControls4.commands) === null || _api$blockControls4 === void 0 || _api$blockControls4.moveToLayout(from, to)({
tr: tr
});
var insertColumnStep = getInsertLayoutStep(tr);
mappedTo = insertColumnStep === null || insertColumnStep === void 0 ? void 0 : insertColumnStep.from;
return tr;
});
api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(function (_ref6) {
var tr = _ref6.tr;
if (mappedTo !== undefined) {
updateSelection(tr, mappedTo);
}
return tr;
});
}
}, [api, getPos, activeNode]);
useEffect(function () {
if (ref.current) {
return dropTargetForElements({
element: ref.current,
onDragEnter: function onDragEnter() {
setIsDraggedOver(true);
readNextNodeAnchor();
},
onDragLeave: function onDragLeave() {
setIsDraggedOver(false);
},
onDrop: onDrop
});
}
}, [onDrop, readNextNodeAnchor]);
if ((activeNode === null || activeNode === void 0 ? void 0 : activeNode.nodeType) === 'layoutSection') {
return null;
}
return jsx("div", {
ref: ref
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
,
css: [dropTargetLayoutStyle, dropTargetStackLayoutHintStyle],
"data-testid": "block-ctrl-drop-indicator"
}, isDraggedOver ? jsx(DropIndicator, {
edge: "right",
gap: "-".concat(DROP_TARGET_LAYOUT_DROP_ZONE_WIDTH, "px")
}) : (isActiveAnchor || expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true)) && jsx("div", {
"data-testid": "block-ctrl-drop-hint",
css: dropTargetLayoutHintStyle
}));
};