UNPKG

@atlaskit/editor-plugin-layout

Version:

Layout plugin for @atlaskit/editor-core

182 lines (179 loc) • 11.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.LayoutColumnMenu = void 0; var _react = _interopRequireWildcard(require("react")); var _bindEventListener = require("bind-event-listener"); var _surfaceDragHandleElement = require("@atlaskit/editor-common/block-controls/surface-drag-handle-element"); var _Popup = _interopRequireDefault(require("@atlaskit/editor-common/Popup")); var _dragHandle = require("@atlaskit/editor-common/styles/drag-handle"); var _ArrowKeyNavigationProvider = require("@atlaskit/editor-common/ui-menu/ArrowKeyNavigationProvider"); var _types = require("@atlaskit/editor-common/ui-menu/ArrowKeyNavigationProvider/types"); var _withReactEditorViewOuterListeners = _interopRequireDefault(require("@atlaskit/editor-common/ui-react/with-react-editor-view-outer-listeners")); var _UserIntentPopupWrapper = require("@atlaskit/editor-common/UserIntentPopupWrapper"); var _useSharedPluginStateWithSelector = require("@atlaskit/editor-common/useSharedPluginStateWithSelector"); var _constants = require("@atlaskit/editor-shared-styles/constants"); var _toolbarDropdownMenuContext = require("@atlaskit/editor-toolbar/toolbar-dropdown-menu-context"); var _surfaceRenderer = require("@atlaskit/editor-ui-control-model/surface-renderer"); var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled"); var _layoutColumnSelection = require("../../pm-plugins/utils/layout-column-selection"); var _components = require("./components"); var _keys = require("./keys"); 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); } var PopupWithListeners = (0, _withReactEditorViewOuterListeners.default)(_Popup.default); var TOOLBAR_MENU_SELECTOR = '[data-toolbar-component="menu"]'; var NESTED_DROPDOWN_MENU_SELECTOR = '[data-toolbar-nested-dropdown-menu]'; var LAYOUT_COLUMN_MENU_POPUP_OFFSET_BELOW = [0, 2]; /** * Returns the drag handle button for the selected layout column. */ var getLayoutColumnMenuTarget = function getLayoutColumnMenuTarget(editorView, selection, anchorPosFromHandle) { var _columnDomRef$parentE; var anchorPos = selection && (0, _layoutColumnSelection.getLayoutColumnMenuAnchorPos)(selection, anchorPosFromHandle); if (anchorPos === undefined) { return null; } var columnDomRef = editorView.nodeDOM(anchorPos); if (!(columnDomRef instanceof HTMLElement)) { return null; } var dragHandleContainer = (_columnDomRef$parentE = columnDomRef.parentElement) === null || _columnDomRef$parentE === void 0 ? void 0 : _columnDomRef$parentE.querySelector(':scope > [data-blocks-drag-handle-container]'); return dragHandleContainer === null || dragHandleContainer === void 0 ? void 0 : dragHandleContainer.querySelector(_dragHandle.DRAG_HANDLE_SELECTOR); }; var focusTrap = { initialFocus: undefined }; var LayoutColumnMenu = exports.LayoutColumnMenu = /*#__PURE__*/_react.default.memo(function LayoutColumnMenu(_ref) { var _api$uiControlRegistr, _api$uiControlRegistr2; var api = _ref.api, editorView = _ref.editorView, mountTo = _ref.mountTo, boundariesElement = _ref.boundariesElement, scrollableElement = _ref.scrollableElement; var _useSharedPluginState = (0, _useSharedPluginStateWithSelector.useSharedPluginStateWithSelector)(api, ['layout', 'selection'], function (states) { var _states$layoutState$i, _states$layoutState, _states$layoutState2, _states$layoutState$l, _states$layoutState3, _states$selectionStat; return { isLayoutColumnMenuOpen: (_states$layoutState$i = (_states$layoutState = states.layoutState) === null || _states$layoutState === void 0 ? void 0 : _states$layoutState.isLayoutColumnMenuOpen) !== null && _states$layoutState$i !== void 0 ? _states$layoutState$i : false, layoutColumnMenuAnchorPos: (_states$layoutState2 = states.layoutState) === null || _states$layoutState2 === void 0 ? void 0 : _states$layoutState2.layoutColumnMenuAnchorPos, openedViaKeyboard: (_states$layoutState$l = (_states$layoutState3 = states.layoutState) === null || _states$layoutState3 === void 0 ? void 0 : _states$layoutState3.layoutColumnMenuOpenedViaKeyboard) !== null && _states$layoutState$l !== void 0 ? _states$layoutState$l : false, selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection }; }), isLayoutColumnMenuOpen = _useSharedPluginState.isLayoutColumnMenuOpen, layoutColumnMenuAnchorPos = _useSharedPluginState.layoutColumnMenuAnchorPos, openedViaKeyboard = _useSharedPluginState.openedViaKeyboard, selection = _useSharedPluginState.selection; var closeLayoutColumnMenu = (0, _react.useCallback)(function () { var _api$core, _api$layout; api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.toggleLayoutColumnMenu({ isOpen: false })); }, [api]); var handleClickOutside = (0, _react.useCallback)(function (event) { if (event.target instanceof Element && (event.target.closest(TOOLBAR_MENU_SELECTOR) || event.target.closest(NESTED_DROPDOWN_MENU_SELECTOR))) { return; } // Clicking a drag handle should let the drag handle's own click handler // update selection/menu state. Treating it as a generic outside click // races that transaction and can immediately close the layout column menu. if (event.target instanceof Element && event.target.closest(_dragHandle.DRAG_HANDLE_SELECTOR)) { return; } closeLayoutColumnMenu(); }, [closeLayoutColumnMenu]); var handleSetIsOpen = (0, _react.useCallback)(function (isOpen) { if (!isOpen) { closeLayoutColumnMenu(); } }, [closeLayoutColumnMenu]); var handleArrowKeyNavigationClose = (0, _react.useCallback)(function (event) { event.preventDefault(); closeLayoutColumnMenu(); }, [closeLayoutColumnMenu]); var shouldDisableArrowKeyNavigation = (0, _react.useCallback)(function (event) { if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') { return false; } var target = event.target; if (!(target instanceof HTMLElement)) { return false; } return target.closest(NESTED_DROPDOWN_MENU_SELECTOR) !== null; }, []); var menuWrapperRef = (0, _react.useRef)(null); var handleMenuKeyDown = (0, _react.useCallback)(function (event) { // Keep menu keyboard events scoped to the menu while preserving Escape and // ArrowUp/ArrowDown handling from Popup and ArrowKeyNavigationProvider. if (event.key !== 'Escape' && event.key !== 'ArrowUp' && event.key !== 'ArrowDown') { event.stopPropagation(); } }, []); (0, _react.useEffect)(function () { var menuWrapper = menuWrapperRef.current; if (!isLayoutColumnMenuOpen || !menuWrapper) { return; } return (0, _bindEventListener.bind)(menuWrapper, { type: 'keydown', listener: handleMenuKeyDown }); }, [handleMenuKeyDown, isLayoutColumnMenuOpen]); var components = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(_keys.LAYOUT_COLUMN_MENU.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : []; var hasValidMenuSelection = !(0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_block_control_migration') || selection !== undefined && (0, _layoutColumnSelection.getLayoutColumnMenuAnchorPos)(selection, layoutColumnMenuAnchorPos) !== undefined; var legacyTarget = (0, _react.useMemo)(function () { return isLayoutColumnMenuOpen && !(0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_block_control_migration') ? getLayoutColumnMenuTarget(editorView, selection, layoutColumnMenuAnchorPos) : null; }, [editorView, isLayoutColumnMenuOpen, layoutColumnMenuAnchorPos, selection]); var surfaceTargetRef = (0, _react.useRef)(null); var surfaceAnchorPosRef = (0, _react.useRef)(undefined); if (!(0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_block_control_migration') || !isLayoutColumnMenuOpen) { surfaceTargetRef.current = null; surfaceAnchorPosRef.current = undefined; } else if (!surfaceTargetRef.current || surfaceAnchorPosRef.current !== layoutColumnMenuAnchorPos) { var _surfaceDragHandleEle; surfaceTargetRef.current = (_surfaceDragHandleEle = _surfaceDragHandleElement.surfaceDragHandleElementStore.get(editorView)) !== null && _surfaceDragHandleEle !== void 0 ? _surfaceDragHandleEle : surfaceTargetRef.current; surfaceAnchorPosRef.current = layoutColumnMenuAnchorPos; } var target = (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_block_control_migration') ? surfaceTargetRef.current : legacyTarget; var hasValidTarget = target instanceof HTMLElement; (0, _react.useEffect)(function () { if (isLayoutColumnMenuOpen && (!hasValidMenuSelection || !hasValidTarget || components.length === 0)) { closeLayoutColumnMenu(); } }, [closeLayoutColumnMenu, components.length, hasValidMenuSelection, hasValidTarget, isLayoutColumnMenuOpen]); if (!isLayoutColumnMenuOpen || !hasValidMenuSelection || components.length === 0 || !hasValidTarget) { return null; } return /*#__PURE__*/_react.default.createElement(PopupWithListeners, { target: target, mountTo: mountTo, boundariesElement: boundariesElement, scrollableElement: scrollableElement, zIndex: _constants.akEditorFloatingOverlapPanelZIndex, forcePlacement: true, preventOverflow: true, stick: true, offset: LAYOUT_COLUMN_MENU_POPUP_OFFSET_BELOW, handleClickOutside: handleClickOutside, handleEscapeKeydown: closeLayoutColumnMenu, focusTrap: openedViaKeyboard ? focusTrap : undefined }, /*#__PURE__*/_react.default.createElement("div", { ref: menuWrapperRef }, /*#__PURE__*/_react.default.createElement(_UserIntentPopupWrapper.UserIntentPopupWrapper, { api: api, userIntent: "layoutColumnMenuPopupOpen" }, /*#__PURE__*/_react.default.createElement(_toolbarDropdownMenuContext.ToolbarDropdownMenuProvider, { isOpen: isLayoutColumnMenuOpen, setIsOpen: handleSetIsOpen }, /*#__PURE__*/_react.default.createElement(_ArrowKeyNavigationProvider.ArrowKeyNavigationProvider, { type: _types.ArrowKeyNavigationType.MENU, handleClose: handleArrowKeyNavigationClose, disableArrowKeyNavigation: shouldDisableArrowKeyNavigation }, /*#__PURE__*/_react.default.createElement(_surfaceRenderer.SurfaceRenderer, { components: components, fallbacks: _components.LAYOUT_COLUMN_MENU_FALLBACKS, surface: _keys.LAYOUT_COLUMN_MENU })))))); });