@atlaskit/editor-plugin-block-controls
Version:
Block controls plugin for @atlaskit/editor-core
310 lines (309 loc) • 18.2 kB
JavaScript
/* block-controls-surface-drag-handle.tsx generated by @compiled/babel-plugin v3.0.2 */
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.BlockControlsSurfaceDragHandle = void 0;
require("./block-controls-surface-drag-handle.compiled.css");
var _react = _interopRequireWildcard(require("react"));
var React = _react;
var _runtime = require("@compiled/react/runtime");
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _reactIntl = require("react-intl");
var _analytics = require("@atlaskit/editor-common/analytics");
var _blockControlUiContext = require("@atlaskit/editor-common/block-controls/block-control-ui-context");
var _surfaceDragHandleElement = require("@atlaskit/editor-common/block-controls/surface-drag-handle-element");
var _browser = require("@atlaskit/editor-common/browser");
var _hooks = require("@atlaskit/editor-common/hooks");
var _keymaps = require("@atlaskit/editor-common/keymaps");
var _messages = require("@atlaskit/editor-common/messages");
var _dragHandleVertical = _interopRequireDefault(require("@atlaskit/icon/core/drag-handle-vertical"));
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
var _fg = require("@atlaskit/platform-feature-flags/fg");
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
var _Tooltip = _interopRequireDefault(require("@atlaskit/tooltip/Tooltip"));
var _pluginKey = require("../pm-plugins/selection-preservation/plugin-key");
var _collapsedHeading = require("../pm-plugins/utils/collapsed-heading");
var _expandAndUpdateSelection = require("../pm-plugins/utils/expand-and-update-selection");
var _getSelection = require("../pm-plugins/utils/getSelection");
var _blockControlsSurfaceDragHandleUtils = require("./block-controls-surface-drag-handle-utils");
var _consts = require("./consts");
var _dragHandleNestedIcon = require("./drag-handle-nested-icon");
var _shouldUseNestedDragHandleIcon = require("./should-use-nested-drag-handle-icon");
var _surfaceEditorViewContext = require("./surface-editor-view-context");
var _useBlockControlsSurfaceDragSource = require("./use-block-controls-surface-drag-source");
var _domAttrName = require("./utils/dom-attr-name");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
// oxlint-disable-next-line typescript/consistent-type-imports -- jsx is the runtime factory for the classic JSX pragma above.
// Keep style values co-located for Compiled while matching the legacy drag handle dimensions.
var SURFACE_DRAG_HANDLE_HEIGHT = 24;
var SURFACE_DRAG_HANDLE_WIDTH = 12;
var SURFACE_DRAG_HANDLE_BORDER_RADIUS = 4;
var SURFACE_DRAG_HANDLE_Z_INDEX = 100;
var dragHandleStyles = {
root: "_19itglyw _2rko1y44 _12ji1r31 _1qu2glyw _12y31o36 _v564k6bl _1e0c1txw _vchhusvi _2lx21bp4 _4cvr1h6o _1bah1h6o _4t3i1tcg _1bsb1crf _bfhksm61 _syazdgkc _80om1qgj _1pbyb4wl _ca0qze3t _u5f3ze3t _n3tdze3t _19bvze3t _cun91j28 _vbqb2sac _1ygbdfik _irr31dpa _6g80187o _1ggl5xtf _1di6fcek",
scaled: "_4t3i1myc _1bsbx5ub",
selected: "_bfhk15s3 _syazxl6c",
keyboardFocused: "_1a37dfik",
firefoxSelection: "_1p1y1j28",
layoutColumn: "_t9ec14w8",
iconWrapper: "_1e0c1txw _4cvr1h6o _1bah1h6o"
};
var handleIconDragStart = function handleIconDragStart(event) {
var _event$currentTarget$;
if (!(0, _browser.getBrowserInfo)().chrome) {
return;
}
event.stopPropagation();
(_event$currentTarget$ = event.currentTarget.parentElement) === null || _event$currentTarget$ === void 0 || _event$currentTarget$.dispatchEvent(new DragEvent('dragstart', {
bubbles: true,
cancelable: true,
dataTransfer: event.dataTransfer
}));
};
var isSelected = function isSelected(selection, pos, view) {
if (typeof pos !== 'number' || !selection || !view) {
return false;
}
return (0, _getSelection.isHandleCorrelatedToSelection)(view.state, selection, pos);
};
var BlockControlsSurfaceDragHandle = exports.BlockControlsSurfaceDragHandle = function BlockControlsSurfaceDragHandle(_ref) {
var _activeNode$type$name;
var api = _ref.api,
surfaceContext = _ref.surfaceContext;
var _useIntl = (0, _reactIntl.useIntl)(),
formatMessage = _useIntl.formatMessage;
var view = (0, _surfaceEditorViewContext.useSurfaceEditorView)();
var context = surfaceContext === null || surfaceContext === void 0 ? void 0 : surfaceContext.get(_blockControlUiContext.BLOCK_CONTROL_UI_CONTEXT);
var activeNode = context === null || context === void 0 ? void 0 : context.activeNode;
var pos = activeNode === null || activeNode === void 0 ? void 0 : activeNode.pos;
var nodeType = (_activeNode$type$name = activeNode === null || activeNode === void 0 ? void 0 : activeNode.type.name) !== null && _activeNode$type$name !== void 0 ? _activeNode$type$name : '';
var buttonRef = (0, _react.useRef)(null);
var posRef = (0, _react.useRef)(pos);
posRef.current = pos;
var getPos = (0, _react.useCallback)(function () {
return posRef.current;
}, []);
var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['blockControls', 'interaction', 'selection'], function (states) {
var _states$blockControls, _states$blockControls2, _states$interactionSt, _states$blockControls3, _states$selectionStat;
return {
activeNodeAnchorName: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 || (_states$blockControls = _states$blockControls.activeNode) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.anchorName,
handleOptions: (_states$blockControls2 = states.blockControlsState) === null || _states$blockControls2 === void 0 || (_states$blockControls2 = _states$blockControls2.activeNode) === null || _states$blockControls2 === void 0 ? void 0 : _states$blockControls2.handleOptions,
interactionState: (_states$interactionSt = states.interactionState) === null || _states$interactionSt === void 0 ? void 0 : _states$interactionSt.interactionState,
isShiftDown: (_states$blockControls3 = states.blockControlsState) === null || _states$blockControls3 === void 0 ? void 0 : _states$blockControls3.isShiftDown,
selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection
};
}),
activeNodeAnchorName = _useSharedPluginState.activeNodeAnchorName,
handleOptions = _useSharedPluginState.handleOptions,
interactionState = _useSharedPluginState.interactionState,
isShiftDown = _useSharedPluginState.isShiftDown,
selection = _useSharedPluginState.selection;
var _useState = (0, _react.useState)(false),
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
dragHandleSelected = _useState2[0],
setDragHandleSelected = _useState2[1];
(0, _react.useEffect)(function () {
setDragHandleSelected(isSelected(selection, pos, view));
}, [pos, selection, view]);
var isLayoutColumn = nodeType === 'layoutColumn';
var isTopLevelNode = (activeNode === null || activeNode === void 0 ? void 0 : activeNode.parentType) === 'doc';
var isDisabled = (0, _react.useMemo)(function () {
var _api$blockControls$sh;
if (!isShiftDown || !view || view.state.selection.empty || !(0, _fg.fg)('platform_editor_elements_dnd_shift_click_select')) {
return false;
}
if (isLayoutColumn && (0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true)) {
return false;
}
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;
var $anchor = (multiSelect === null || multiSelect === void 0 ? void 0 : multiSelect.anchor) !== undefined ? view.state.doc.resolve(multiSelect.anchor) : view.state.selection.$anchor;
return !isTopLevelNode || $anchor.depth > _consts.DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH;
}, [api, isLayoutColumn, isShiftDown, isTopLevelNode, view]);
var getAnchorName = (0, _react.useCallback)(function () {
var _api$blockControls$sh2, _ref2, _ref3, _dom$getAttribute, _dom$closest;
var surfaceAnchor = posRef.current === undefined ? undefined : api === null || api === void 0 || (_api$blockControls$sh2 = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh2 === void 0 || (_api$blockControls$sh2 = _api$blockControls$sh2.surfaceAnchors) === null || _api$blockControls$sh2 === void 0 ? void 0 : _api$blockControls$sh2.get(posRef.current);
if (surfaceAnchor) {
return surfaceAnchor;
}
if (activeNodeAnchorName) {
return activeNodeAnchorName;
}
var currentPos = posRef.current;
if (!view || currentPos === undefined) {
return '';
}
var dom = view.nodeDOM(currentPos);
if (!(dom instanceof HTMLElement)) {
return '';
}
var anchorAttrName = (0, _domAttrName.getAnchorAttrName)();
return (_ref2 = (_ref3 = (_dom$getAttribute = dom.getAttribute(anchorAttrName)) !== null && _dom$getAttribute !== void 0 ? _dom$getAttribute : dom.getAttribute(_domAttrName.NODE_ANCHOR_ATTR_NAME)) !== null && _ref3 !== void 0 ? _ref3 : (_dom$closest = dom.closest("[".concat(anchorAttrName, "]"))) === null || _dom$closest === void 0 ? void 0 : _dom$closest.getAttribute(anchorAttrName)) !== null && _ref2 !== void 0 ? _ref2 : '';
}, [activeNodeAnchorName, api, view]);
(0, _react.useLayoutEffect)(function () {
var element = buttonRef.current;
if (!view || !element) {
return;
}
_surfaceDragHandleElement.surfaceDragHandleElementStore.set(view, element);
return function () {
return _surfaceDragHandleElement.surfaceDragHandleElementStore.release(view, element);
};
}, [view]);
(0, _react.useEffect)(function () {
if (!(handleOptions !== null && handleOptions !== void 0 && handleOptions.isFocused) || !buttonRef.current || !view) {
return;
}
var animationFrameId;
var attempts = 0;
var _focusHandle = function focusHandle() {
var button = buttonRef.current;
if (!button) {
return;
}
button.focus();
if (button.ownerDocument.activeElement !== button && attempts < 5) {
attempts++;
animationFrameId = requestAnimationFrame(_focusHandle);
}
};
_focusHandle();
return function () {
if (animationFrameId !== undefined) {
cancelAnimationFrame(animationFrameId);
}
view.focus();
};
}, [handleOptions === null || handleOptions === void 0 ? void 0 : handleOptions.isFocused, view]);
(0, _useBlockControlsSurfaceDragSource.useBlockControlsSurfaceDragSource)({
api: api,
elementRef: buttonRef,
getAnchorName: getAnchorName,
getPos: getPos,
nodeType: nodeType,
start: pos,
view: view
});
var handleMouseDown = (0, _react.useCallback)(function () {
var _api$core, _api$blockControls;
api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.commands.setSelectedViaDragHandle(true));
}, [api]);
var handleClick = (0, _react.useCallback)(function (event) {
var _api$core2;
if (!view || isDisabled) {
return;
}
var openedViaKeyboard = event.detail === 0;
api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (_ref4) {
var _selectionPreservatio, _selectionPreservatio2, _api$analytics, _resolvedStartPos$nod, _api$blockControls2, _api$blockControls3, _activeNode$rootPos;
var tr = _ref4.tr;
var startPos = getPos();
if (startPos === undefined) {
return tr;
}
if (nodeType === 'layoutColumn' && (0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true)) {
tr.setMeta('toggleLayoutColumnMenu', (0, _blockControlsSurfaceDragHandleUtils.buildLayoutColumnMenuMeta)(startPos, openedViaKeyboard));
}
var selection = (_selectionPreservatio = (_selectionPreservatio2 = _pluginKey.selectionPreservationPluginKey.getState(view.state)) === null || _selectionPreservatio2 === void 0 ? void 0 : _selectionPreservatio2.preservedSelection) !== null && _selectionPreservatio !== void 0 ? _selectionPreservatio : tr.selection;
var resolvedStartPos = tr.doc.resolve(startPos);
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.attachAnalyticsEvent({
action: _analytics.ACTION.CLICKED,
actionSubject: _analytics.ACTION_SUBJECT.BUTTON,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.ELEMENT_DRAG_HANDLE,
attributes: {
nodeDepth: resolvedStartPos.depth,
nodeTypes: ((_resolvedStartPos$nod = resolvedStartPos.nodeAfter) === null || _resolvedStartPos$nod === void 0 ? void 0 : _resolvedStartPos$nod.type.name) || ''
},
eventType: _analytics.EVENT_TYPE.UI
})(tr);
(0, _expandAndUpdateSelection.expandAndUpdateSelection)({
api: api,
isShiftPressed: event.shiftKey,
nodeType: nodeType,
selection: selection,
startPos: startPos,
tr: tr
});
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_collapsible_headings')) {
(0, _collapsedHeading.prepareCollapsedHeadingSelection)({
api: api,
expand: true,
headingPos: startPos,
tr: tr
});
}
api === null || api === void 0 || (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 || _api$blockControls2.commands.startPreservingSelection()({
tr: tr
});
api === null || api === void 0 || (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 || _api$blockControls3.commands.toggleBlockMenu({
anchorName: getAnchorName(),
openedViaKeyboard: openedViaKeyboard,
triggerByNode: {
nodeType: nodeType,
pos: startPos,
rootPos: (_activeNode$rootPos = activeNode === null || activeNode === void 0 ? void 0 : activeNode.rootPos) !== null && _activeNode$rootPos !== void 0 ? _activeNode$rootPos : resolvedStartPos.before(1)
}
})({
tr: tr
});
tr.setMeta('scrollIntoView', false);
return tr;
});
view.focus();
}, [activeNode === null || activeNode === void 0 ? void 0 : activeNode.rootPos, api, getAnchorName, getPos, isDisabled, nodeType, view]);
var label = formatMessage(_messages.blockControlsMessages.dragToMoveClickToOpen, {
br: ' '
});
var helpDescriptors = (0, _react.useMemo)(function () {
return [{
description: formatMessage(_messages.blockControlsMessages.dragToMoveClickToOpen, {
br: /*#__PURE__*/React.createElement("br", null)
})
}];
}, [formatMessage]);
var handleTooltipShow = (0, _react.useCallback)(function () {
var _api$accessibilityUti;
api === null || api === void 0 || (_api$accessibilityUti = api.accessibilityUtils) === null || _api$accessibilityUti === void 0 || _api$accessibilityUti.actions.ariaNotify(label, {
priority: 'important'
});
}, [api, label]);
if (!view || !activeNode || pos === undefined) {
return null;
}
var tooltipContent = /*#__PURE__*/React.createElement(_keymaps.TooltipContentWithMultipleShortcuts, {
helpDescriptors: helpDescriptors
});
var browser = (0, _browser.getBrowserInfo)();
var button = /*#__PURE__*/React.createElement("button", {
ref: buttonRef,
type: "button",
"aria-label": label,
disabled: isDisabled,
onClick: handleClick,
onMouseDown: handleMouseDown
// eslint-disable-next-line @atlaskit/design-system/no-direct-use-of-web-platform-drag-and-drop -- Pragmatic DnD owns this native drag source.
,
onDrop: function onDrop(event) {
return event.stopPropagation();
},
"data-editor-block-ctrl-drag-handle": true,
"data-blocks-drag-handle": (0, _fg.fg)('confluence_remix_button_right_side_block_fg') || undefined,
"data-testid": "block-controls-surface-drag-handle",
className: (0, _runtime.ax)([dragHandleStyles.root, browser.gecko && dragHandleStyles.firefoxSelection, isLayoutColumn && dragHandleStyles.layoutColumn, dragHandleSelected && interactionState !== 'hasNotHadInteraction' && dragHandleStyles.selected, (handleOptions === null || handleOptions === void 0 ? void 0 : handleOptions.isFocused) && dragHandleStyles.keyboardFocused, dragHandleStyles.scaled])
}, /*#__PURE__*/React.createElement("span", {
onDragStart: handleIconDragStart,
className: (0, _runtime.ax)([dragHandleStyles.iconWrapper])
}, (0, _shouldUseNestedDragHandleIcon.shouldUseNestedDragHandleIcon)(Boolean(isTopLevelNode), isLayoutColumn) ? /*#__PURE__*/React.createElement(_dragHandleNestedIcon.DragHandleNestedIcon, null) : /*#__PURE__*/React.createElement(_dragHandleVertical.default, {
label: "",
size: "small",
spacing: "spacious"
})));
return /*#__PURE__*/React.createElement(_Tooltip.default, {
position: "top",
content: tooltipContent,
ignoreTooltipPointerEvents: true,
onShow: handleTooltipShow
}, button);
};