UNPKG

@atlaskit/editor-plugin-block-controls

Version:

Block controls plugin for @atlaskit/editor-core

310 lines (309 loc) 18.2 kB
/* 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); };