@atlaskit/editor-plugin-layout
Version:
Layout plugin for @atlaskit/editor-core
192 lines (189 loc) • 10.6 kB
JavaScript
"use strict";
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 _hooks = require("@atlaskit/editor-common/hooks");
var _styles = require("@atlaskit/editor-common/styles");
var _ui = require("@atlaskit/editor-common/ui");
var _uiMenu = require("@atlaskit/editor-common/ui-menu");
var _uiReact = require("@atlaskit/editor-common/ui-react");
var _userIntent = require("@atlaskit/editor-common/user-intent");
var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
var _editorToolbar = require("@atlaskit/editor-toolbar");
var _editorUiControlModel = require("@atlaskit/editor-ui-control-model");
var _layoutColumnSelection = require("../../pm-plugins/utils/layout-column-selection");
var _components = require("./components");
var _keys = require("./keys");
var _utils = require("./utils");
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, _uiReact.withReactEditorViewOuterListeners)(_ui.Popup);
var TOOLBAR_MENU_SELECTOR = '[data-toolbar-component="menu"]';
var NESTED_DROPDOWN_MENU_SELECTOR = '[data-toolbar-nested-dropdown-menu]';
/**
* 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(_styles.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, _hooks.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(_styles.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 target = (0, _react.useMemo)(function () {
return isLayoutColumnMenuOpen ? getLayoutColumnMenuTarget(editorView, selection, layoutColumnMenuAnchorPos) : null;
}, [editorView, isLayoutColumnMenuOpen, layoutColumnMenuAnchorPos, selection]);
var hasValidTarget = target instanceof HTMLElement;
var positionLayoutColumnMenu = (0, _react.useCallback)(function (position) {
var _menuWrapperRef$curre;
var popup = (_menuWrapperRef$curre = menuWrapperRef.current) === null || _menuWrapperRef$curre === void 0 ? void 0 : _menuWrapperRef$curre.parentElement;
if (!(target instanceof HTMLElement) || !(popup instanceof HTMLElement)) {
return position;
}
if ((0, _utils.shouldOpenLayoutColumnMenuBelow)({
editorView: editorView,
popup: popup,
scrollableElement: scrollableElement,
target: target
})) {
return (0, _utils.calculateFallbackBottomPosition)(position, target, popup);
}
return position;
}, [editorView, scrollableElement, target]);
(0, _react.useEffect)(function () {
if (isLayoutColumnMenuOpen && (!hasValidTarget || components.length === 0)) {
closeLayoutColumnMenu();
}
}, [closeLayoutColumnMenu, components.length, hasValidTarget, isLayoutColumnMenuOpen]);
var _useMemo = (0, _react.useMemo)(function () {
return (0, _utils.getLayoutColumnMenuPositioningProps)();
}, []),
alignX = _useMemo.alignX,
alignY = _useMemo.alignY,
offset = _useMemo.offset,
useManualBelowFlip = _useMemo.useManualBelowFlip;
if (!isLayoutColumnMenuOpen || components.length === 0 || !hasValidTarget) {
return null;
}
return /*#__PURE__*/_react.default.createElement(PopupWithListeners, {
target: target,
mountTo: mountTo,
boundariesElement: boundariesElement,
scrollableElement: scrollableElement,
zIndex: _editorSharedStyles.akEditorFloatingOverlapPanelZIndex,
alignX: alignX,
alignY: alignY,
forcePlacement: true,
preventOverflow: true,
stick: true,
offset: offset,
onPositionCalculated: useManualBelowFlip ? positionLayoutColumnMenu : undefined,
handleClickOutside: handleClickOutside,
handleEscapeKeydown: closeLayoutColumnMenu,
focusTrap: openedViaKeyboard ? focusTrap : undefined
}, /*#__PURE__*/_react.default.createElement("div", {
ref: menuWrapperRef
}, /*#__PURE__*/_react.default.createElement(_userIntent.UserIntentPopupWrapper, {
api: api,
userIntent: "layoutColumnMenuPopupOpen"
}, /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownMenuProvider, {
isOpen: isLayoutColumnMenuOpen,
setIsOpen: handleSetIsOpen
}, /*#__PURE__*/_react.default.createElement(_uiMenu.ArrowKeyNavigationProvider, {
type: _uiMenu.ArrowKeyNavigationType.MENU,
handleClose: handleArrowKeyNavigationClose,
disableArrowKeyNavigation: shouldDisableArrowKeyNavigation
}, /*#__PURE__*/_react.default.createElement(_editorUiControlModel.SurfaceRenderer, {
components: components,
fallbacks: _components.LAYOUT_COLUMN_MENU_FALLBACKS,
surface: _keys.LAYOUT_COLUMN_MENU
}))))));
});