@atlaskit/editor-plugin-block-controls
Version:
Block controls plugin for @atlaskit/editor-core
116 lines (115 loc) • 7.55 kB
JavaScript
import { fg } from '@atlaskit/platform-feature-flags/fg';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
import { getNodeTypeWithLevel } from './decorations-common';
const OPAQUE_BLOCKS = new Set(['table', 'bulletList', 'orderedList', 'taskList', 'decisionList', 'mediaSingle']);
const EXCLUDED_BLOCKS = new Set(['listItem', 'tableRow', 'tableCell', 'tableHeader', 'caption']);
const isPanelNodeTypeName = nodeTypeName => nodeTypeName === 'panel' || nodeTypeName === 'panel_c1' && expValEqualsNoExposure('platform_editor_controls_reliable_anchor', 'isEnabled', true) && fg('platform_editor_controls_reliable_anchor_patch_1');
const redirectParagraphToWrappedMedia = (view, $target, targetPosition) => {
const node = view.state.doc.nodeAt(targetPosition);
if (!node || node.type.name !== 'paragraph') {
return {
position: targetPosition,
node
};
}
const index = $target.index();
for (const siblingIndex of [index - 1, index + 1]) {
if (siblingIndex < 0 || siblingIndex >= $target.parent.childCount) {
continue;
}
const sibling = $target.parent.child(siblingIndex);
if ((sibling.type.name === 'mediaSingle' || sibling.type.name === 'embedCard') && (sibling.attrs.layout === 'wrap-left' || sibling.attrs.layout === 'wrap-right')) {
return {
position: siblingIndex < index ? targetPosition - sibling.nodeSize : targetPosition + node.nodeSize,
node: sibling
};
}
}
return {
position: targetPosition,
node
};
};
/** Resolves hover from PM structure so the first hover can create its sparse candidate. */
export const handleSparseMouseOver = (view, event, api) => {
var _api$editorDisabled$s, _api$editorDisabled, _api$editorDisabled$s2, _api$editorViewMode, _api$editorViewMode$s, _api$showDiff$sharedS, _api$showDiff, _api$showDiff$sharedS2, _api$userIntent, _api$userIntent$share, _$pos$nodeAfter, _controls$activeNode, _anchors$get, _anchors$get2;
const controls = api === null || api === void 0 ? void 0 : api.blockControls.sharedState.currentState();
if (!api || controls !== null && controls !== void 0 && controls.isDragging || !(event.target instanceof Element) || event.target === view.dom) {
return false;
}
const editorDisabled = (_api$editorDisabled$s = (_api$editorDisabled = api.editorDisabled) === null || _api$editorDisabled === void 0 ? void 0 : (_api$editorDisabled$s2 = _api$editorDisabled.sharedState.currentState()) === null || _api$editorDisabled$s2 === void 0 ? void 0 : _api$editorDisabled$s2.editorDisabled) !== null && _api$editorDisabled$s !== void 0 ? _api$editorDisabled$s : false;
const editorViewMode = (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : (_api$editorViewMode$s = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode$s === void 0 ? void 0 : _api$editorViewMode$s.mode;
if (editorDisabled && (editorViewMode !== 'view' || !(controls !== null && controls !== void 0 && controls.rightSideControlsEnabled))) {
return false;
}
const isDisplayingDiff = (_api$showDiff$sharedS = (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 ? void 0 : (_api$showDiff$sharedS2 = _api$showDiff.sharedState.currentState()) === null || _api$showDiff$sharedS2 === void 0 ? void 0 : _api$showDiff$sharedS2.isDisplayingChanges) !== null && _api$showDiff$sharedS !== void 0 ? _api$showDiff$sharedS : false;
const currentUserIntent = (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : (_api$userIntent$share = _api$userIntent.sharedState.currentState()) === null || _api$userIntent$share === void 0 ? void 0 : _api$userIntent$share.currentUserIntent;
if ((isDisplayingDiff || currentUserIntent === 'reviewing') && expValEqualsNoExposure('platform_editor_diff_plugin_extended', 'isEnabled', true)) {
return false;
}
let position;
try {
position = view.posAtDOM(event.target, 0);
} catch {
return false;
}
const $pos = view.state.doc.resolve(position);
let targetPosition;
let opaque = false;
for (let depth = 1; depth <= $pos.depth; depth++) {
const node = $pos.node(depth);
if (!node.isBlock || EXCLUDED_BLOCKS.has(node.type.name)) {
continue;
}
targetPosition = $pos.before(depth);
if (OPAQUE_BLOCKS.has(node.type.name)) {
opaque = true;
break;
}
}
if (!opaque && (_$pos$nodeAfter = $pos.nodeAfter) !== null && _$pos$nodeAfter !== void 0 && _$pos$nodeAfter.isBlock && !EXCLUDED_BLOCKS.has($pos.nodeAfter.type.name)) {
// Browser positions over non-editable node chrome can resolve to the first editable child.
// Keep the ancestor unless that child actually owns the hovered DOM target.
const nodeAfterDOM = view.nodeDOM(position);
if (nodeAfterDOM instanceof Element && nodeAfterDOM.contains(event.target)) {
targetPosition = position;
}
}
if (targetPosition === undefined) {
return false;
}
const initialNode = view.state.doc.nodeAt(targetPosition);
if (!initialNode) {
return false;
}
const target = redirectParagraphToWrappedMedia(view, view.state.doc.resolve(targetPosition), targetPosition);
targetPosition = target.position;
const node = target.node;
if (!node || (controls === null || controls === void 0 ? void 0 : (_controls$activeNode = controls.activeNode) === null || _controls$activeNode === void 0 ? void 0 : _controls$activeNode.pos) === targetPosition) {
return false;
}
const $target = view.state.doc.resolve(targetPosition);
if ($target.depth > 0 && (node.type.name === 'paragraph' || node.type.name === 'heading') && node.content.size === 0) {
return false;
}
const parentNode = $target.parent;
const parentPosition = $target.depth > 0 ? $target.before($target.depth) : undefined;
const parentDOM = parentPosition === undefined ? undefined : view.nodeDOM(parentPosition);
if (isPanelNodeTypeName(parentNode.type.name) && !(parentDOM instanceof HTMLElement && parentDOM.classList.contains('ak-editor-panel__no-icon')) && $target.index() === 0) {
return false;
}
if (node.type.name === 'layoutColumn' && parentNode.type.name === 'layoutSection' && parentNode.childCount === 1 && expValEqualsNoExposure('advanced_layouts', 'isEnabled', true) && !expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
return false;
}
const anchors = controls === null || controls === void 0 ? void 0 : controls.surfaceAnchors;
const anchorName = (_anchors$get = anchors === null || anchors === void 0 ? void 0 : anchors.get(targetPosition)) !== null && _anchors$get !== void 0 ? _anchors$get : api.core.actions.getAnchorIdForNode(node, targetPosition);
const rootPos = $target.depth > 0 ? $target.before(1) : targetPosition;
const rootNode = view.state.doc.nodeAt(rootPos);
const rootAnchorName = (_anchors$get2 = anchors === null || anchors === void 0 ? void 0 : anchors.get(rootPos)) !== null && _anchors$get2 !== void 0 ? _anchors$get2 : rootNode ? api.core.actions.getAnchorIdForNode(rootNode, rootPos) : undefined;
if (!anchorName || !rootNode) {
return false;
}
api.core.actions.execute(api.blockControls.commands.showDragHandleAt(targetPosition, anchorName, getNodeTypeWithLevel(node), undefined, rootPos, rootAnchorName !== null && rootAnchorName !== void 0 ? rootAnchorName : anchorName, getNodeTypeWithLevel(rootNode)));
return false;
};