UNPKG

@atlaskit/editor-plugin-block-controls

Version:

Block controls plugin for @atlaskit/editor-core

177 lines (176 loc) 8.88 kB
import { useEffect } from 'react'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import { startMeasure } from '@atlaskit/editor-common/performance-measures'; import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils'; import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled'; import { draggable } from '@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter'; import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/utils/set-custom-native-drag-preview'; import { defaultActiveAnchorTracker } from '../pm-plugins/utils/active-anchor-tracker'; import { getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics'; import { isCollapsedHeading, prepareCollapsedHeadingSelection } from '../pm-plugins/utils/collapsed-heading'; import { isHandleCorrelatedToSelection, selectNode } from '../pm-plugins/utils/getSelection'; import { getNodeMarginsForDragPreview, getNodeSpacingForDragPreview } from './block-controls-surface-drag-handle-utils'; import { dragPreview } from './drag-preview'; var EDITOR_BLOCKS_DRAG_INIT = 'Editor Blocks Drag Initialization Time'; /** * Registry surface drag source. This intentionally duplicates the stable widget handle's drag * contract so the legacy implementation remains byte-for-byte unchanged during migration. */ export var useBlockControlsSurfaceDragSource = function useBlockControlsSurfaceDragSource(_ref) { var api = _ref.api, elementRef = _ref.elementRef, getAnchorName = _ref.getAnchorName, getPos = _ref.getPos, nodeType = _ref.nodeType, start = _ref.start, view = _ref.view; useEffect(function () { var element = elementRef.current; if (!element || !view) { return; } return draggable({ element: element, getInitialData: function getInitialData() { return { type: 'element', start: start }; }, onGenerateDragPreview: function onGenerateDragPreview(_ref2) { var _api$core, _api$blockControls$sh; var nativeSetDragImage = _ref2.nativeSetDragImage; api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref3) { var tr = _ref3.tr; var handlePos = getPos(); if (typeof handlePos !== 'number') { return tr; } if (isExperimentEnabled('platform_editor_collapsible_headings') && isCollapsedHeading(api, handlePos)) { prepareCollapsedHeadingSelection({ api: api, expand: false, headingPos: handlePos, tr: tr }); return tr; } if (!tr.selection.empty && isHandleCorrelatedToSelection(view.state, tr.selection, handlePos)) { var _api$blockControls; api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 || _api$blockControls.commands.setMultiSelectPositions()({ tr: tr }); } else { tr = selectNode(tr, handlePos, nodeType, api); } return tr; }); var startPos = getPos(); var _view$state = view.state, doc = _view$state.doc, selection = _view$state.selection; var sliceFrom = selection.from; var sliceTo = selection.to; var multiSelect = api === null || api === void 0 || (_api$blockControls$sh = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh === void 0 ? void 0 : _api$blockControls$sh.multiSelectDnD; if (multiSelect) { sliceFrom = Math.min(multiSelect.anchor, multiSelect.head); sliceTo = Math.max(multiSelect.anchor, multiSelect.head); } var expandedSlice = doc.slice(sliceFrom, sliceTo); var isDraggingMultipleNodes = startPos !== undefined && startPos >= sliceFrom && startPos < sliceTo && expandedSlice.content.childCount > 1; setCustomNativeDragPreview({ getOffset: function getOffset() { if (!isDraggingMultipleNodes || startPos === undefined) { return { x: 0, y: 0 }; } var domAtPos = view.domAtPos.bind(view); var heightBeforeHandle = 0; var nodeStart = sliceFrom; var activeNodeMarginTop = 0; for (var index = 0; index < expandedSlice.content.childCount; index++) { var _node$nodeSize; var node = expandedSlice.content.maybeChild(index); var nodeEnd = nodeStart + ((_node$nodeSize = node === null || node === void 0 ? void 0 : node.nodeSize) !== null && _node$nodeSize !== void 0 ? _node$nodeSize : 0); if (nodeEnd <= startPos) { var nodeElement = findDomRefAtPos(nodeStart, domAtPos); var margins = getNodeMarginsForDragPreview(node); heightBeforeHandle += (nodeElement instanceof HTMLElement ? nodeElement.offsetHeight : 0) + margins.top + margins.bottom; } else { activeNodeMarginTop = getNodeMarginsForDragPreview(node).top; break; } nodeStart = nodeEnd; } return { x: 0, y: heightBeforeHandle + activeNodeMarginTop }; }, render: function render(_ref4) { var container = _ref4.container; if (!isDraggingMultipleNodes && startPos !== undefined) { var dom = findDomRefAtPos(startPos, view.domAtPos.bind(view)); return dom instanceof HTMLElement ? dragPreview(container, { dom: dom, nodeType: nodeType }) : undefined; } var previewContent = []; expandedSlice.content.descendants(function (node, pos) { var dom = findDomRefAtPos(sliceFrom + pos, view.domAtPos.bind(view)); if (dom instanceof HTMLElement) { previewContent.push({ dom: dom, nodeSpacing: getNodeSpacingForDragPreview(node), nodeType: node.type.name }); } return false; }); return previewContent.length > 0 ? dragPreview(container, previewContent) : undefined; }, nativeSetDragImage: nativeSetDragImage }); }, onDragStart: function onDragStart() { var _api$core2; if (start === undefined) { return; } // This handle is outside ProseMirror, so it owns the side effects normally started by PM. startMeasure(EDITOR_BLOCKS_DRAG_INIT); defaultActiveAnchorTracker.reset(); api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (_ref5) { var _api$blockControls$sh2, _resolvedMovingNode$n, _api$blockControls2, _api$analytics; var tr = _ref5.tr; var resolvedMovingNode = tr.doc.resolve(start); var multiSelect = api === null || api === void 0 || (_api$blockControls$sh2 = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh2 === void 0 ? void 0 : _api$blockControls$sh2.multiSelectDnD; var attributes = multiSelect ? getMultiSelectAnalyticsAttributes(tr, multiSelect.anchor, multiSelect.head) : { hasSelectedMultipleNodes: false, nodeTypes: (_resolvedMovingNode$n = resolvedMovingNode.nodeAfter) === null || _resolvedMovingNode$n === void 0 ? void 0 : _resolvedMovingNode$n.type.name }; api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || _api$blockControls2.commands.setNodeDragged(getPos, getAnchorName(), nodeType)({ tr: tr }); tr.setMeta('scrollIntoView', false); api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.attachAnalyticsEvent({ action: ACTION.DRAGGED, actionSubject: ACTION_SUBJECT.ELEMENT, actionSubjectId: ACTION_SUBJECT_ID.ELEMENT_DRAG_HANDLE, attributes: { hasSelectedMultipleNodes: attributes.hasSelectedMultipleNodes, nodeDepth: resolvedMovingNode.depth, nodeTypes: attributes.nodeTypes || '' }, eventType: EVENT_TYPE.UI })(tr); return tr; }); view.focus(); } }); }, [api, elementRef, getAnchorName, getPos, nodeType, start, view]); };