@elastic/eui
Version:
Elastic UI Component Library
328 lines (324 loc) • 13 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/inherits";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
var _excluded = ["isSeparator", "key"],
_excluded2 = ["panel", "name", "key", "icon", "onClick"],
_excluded3 = ["stylesMemoizer", "panels", "onPanelChange", "className", "initialPanelId", "size"];
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { Component, Fragment } from 'react';
import classNames from 'classnames';
import { withEuiStylesMemoizer } from '../../services';
import { EuiHorizontalRule } from '../horizontal_rule';
import { EuiContextMenuPanel } from './context_menu_panel';
import { EuiContextMenuItem } from './context_menu_item';
import { euiContextMenuStyles } from './context_menu.styles';
import { jsx as ___EmotionJSX } from "@emotion/react";
export var SIZES = ['s', 'm'];
var isItemSeparator = function isItemSeparator(item) {
return item.isSeparator === true;
};
function mapIdsToPanels(panels) {
var map = {};
panels.forEach(function (panel) {
map[panel.id] = panel;
});
return map;
}
function mapIdsToPreviousPanels(panels) {
var idToPreviousPanelIdMap = {};
panels.forEach(function (panel) {
if (Array.isArray(panel.items)) {
panel.items.forEach(function (item) {
if (isItemSeparator(item)) return;
var isCloseable = item.panel !== undefined;
if (isCloseable) {
idToPreviousPanelIdMap[item.panel] = panel.id;
}
});
}
});
return idToPreviousPanelIdMap;
}
function mapPanelItemsToPanels(panels) {
var idAndItemIndexToPanelIdMap = {};
panels.forEach(function (panel) {
idAndItemIndexToPanelIdMap[panel.id] = {};
if (panel.items) {
panel.items.forEach(function (item, index) {
if (isItemSeparator(item)) return;
if (item.panel) {
idAndItemIndexToPanelIdMap[panel.id][index] = item.panel;
}
});
}
});
return idAndItemIndexToPanelIdMap;
}
export var EuiContextMenuClass = /*#__PURE__*/function (_Component) {
function EuiContextMenuClass(props) {
var _this;
_classCallCheck(this, EuiContextMenuClass);
_this = _callSuper(this, EuiContextMenuClass, [props]);
_defineProperty(_this, "hasPreviousPanel", function (panelId) {
var previousPanelId = _this.state.idToPreviousPanelIdMap[panelId];
return typeof previousPanelId !== 'undefined';
});
_defineProperty(_this, "showNextPanel", function (itemIndex) {
if (itemIndex == null) {
return;
}
var nextPanelId = _this.state.idAndItemIndexToPanelIdMap[_this.state.incomingPanelId][itemIndex];
if (nextPanelId) {
if (_this.state.isUsingKeyboardToNavigate) {
_this.setState(function (_ref) {
var idToPanelMap = _ref.idToPanelMap;
return {
focusedItemIndex: idToPanelMap[nextPanelId].initialFocusedItemIndex
};
});
}
_this.showPanel(nextPanelId, 'next');
}
});
_defineProperty(_this, "showPreviousPanel", function () {
// If there's a previous panel, then we can close the current panel to go back to it.
if (_this.hasPreviousPanel(_this.state.incomingPanelId)) {
var previousPanelId = _this.state.idToPreviousPanelIdMap[_this.state.incomingPanelId];
// Set focus on the item which shows the panel we're leaving.
var previousPanel = _this.state.idToPanelMap[previousPanelId];
var focusedItemIndex = previousPanel.items.filter(function (item) {
return !isItemSeparator(item);
}).findIndex(function (item) {
return item.panel === _this.state.incomingPanelId;
});
if (focusedItemIndex !== -1) {
_this.setState({
focusedItemIndex: focusedItemIndex
});
}
_this.showPanel(previousPanelId, 'previous');
}
});
_defineProperty(_this, "onIncomingPanelHeightChange", function (height) {
_this.setState(function (_ref2) {
var prevHeight = _ref2.height;
if (height === prevHeight) {
return null;
}
return {
height: height
};
});
});
_defineProperty(_this, "onOutGoingPanelTransitionComplete", function () {
_this.setState({
isOutgoingPanelVisible: false
});
});
_defineProperty(_this, "onUseKeyboardToNavigate", function () {
if (!_this.state.isUsingKeyboardToNavigate) {
_this.setState({
isUsingKeyboardToNavigate: true
});
}
});
_defineProperty(_this, "mapIdsToRenderedItems", function () {
var panels = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var idToRenderedItemsMap = {};
// Pre-rendering the items lets us check reference equality inside of EuiContextMenuPanel.
panels.forEach(function (panel) {
idToRenderedItemsMap[panel.id] = _this.renderItems(panel.items);
});
return idToRenderedItemsMap;
});
_this.state = {
prevProps: {},
idToPanelMap: {},
idToPreviousPanelIdMap: {},
idAndItemIndexToPanelIdMap: {},
idToRenderedItemsMap: _this.mapIdsToRenderedItems(_this.props.panels),
height: undefined,
outgoingPanelId: undefined,
incomingPanelId: props.initialPanelId,
transitionDirection: undefined,
isOutgoingPanelVisible: false,
focusedItemIndex: undefined,
isUsingKeyboardToNavigate: false
};
return _this;
}
_inherits(EuiContextMenuClass, _Component);
return _createClass(EuiContextMenuClass, [{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
if (prevProps.panels !== this.props.panels) {
this.setState({
idToRenderedItemsMap: this.mapIdsToRenderedItems(this.props.panels)
});
}
}
}, {
key: "showPanel",
value: function showPanel(panelId, direction) {
var _this$props$onPanelCh, _this$props;
this.setState({
outgoingPanelId: this.state.incomingPanelId,
incomingPanelId: panelId,
transitionDirection: direction,
isOutgoingPanelVisible: true
});
(_this$props$onPanelCh = (_this$props = this.props).onPanelChange) === null || _this$props$onPanelCh === void 0 || _this$props$onPanelCh.call(_this$props, {
panelId: panelId,
direction: direction
});
}
}, {
key: "renderItems",
value: function renderItems() {
var _this2 = this;
var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
return items.map(function (item, index) {
if (item.renderItem) {
var _item$key;
return ___EmotionJSX(Fragment, {
key: (_item$key = item.key) !== null && _item$key !== void 0 ? _item$key : index
}, item.renderItem());
}
if (isItemSeparator(item)) {
var omit = item.isSeparator,
_item$key2 = item.key,
_key = _item$key2 === void 0 ? index : _item$key2,
_rest = _objectWithoutProperties(item, _excluded);
return ___EmotionJSX(EuiHorizontalRule, _extends({
key: _key,
margin: "none"
}, _rest));
}
var panel = item.panel,
name = item.name,
key = item.key,
icon = item.icon,
onClick = item.onClick,
rest = _objectWithoutProperties(item, _excluded2);
var onClickHandler = panel ? function (event) {
if (onClick && event) {
event.persist();
}
// This component is commonly wrapped in a EuiOutsideClickDetector, which means we'll
// need to wait for that logic to complete before re-rendering the DOM via showPanel.
window.requestAnimationFrame(function () {
if (onClick) {
onClick(event);
}
_this2.showNextPanel(index);
});
} : onClick;
return ___EmotionJSX(EuiContextMenuItem, _extends({
key: key || (typeof name === 'string' ? name : undefined) || index,
icon: icon,
onClick: onClickHandler,
hasPanel: Boolean(panel)
}, rest), name);
});
}
}, {
key: "renderPanel",
value: function renderPanel(panelId, transitionType) {
var _this3 = this;
var panel = this.state.idToPanelMap[panelId];
if (!panel) {
return;
}
// As above, we need to wait for EuiOutsideClickDetector to complete its logic before
// re-rendering via showPanel.
var onClose;
if (this.hasPreviousPanel(panelId)) {
onClose = function onClose() {
return window.requestAnimationFrame(_this3.showPreviousPanel);
};
}
var cssStyles = {
position: 'absolute',
label: 'euiContextMenu__panel'
};
return ___EmotionJSX(EuiContextMenuPanel, {
key: panelId,
size: this.props.size,
css: cssStyles,
onHeightChange: transitionType === 'in' ? this.onIncomingPanelHeightChange : undefined,
onTransitionComplete: transitionType === 'out' ? this.onOutGoingPanelTransitionComplete : undefined,
title: panel.title,
onClose: onClose,
transitionType: this.state.isOutgoingPanelVisible ? transitionType : undefined,
transitionDirection: this.state.isOutgoingPanelVisible ? this.state.transitionDirection : undefined,
items: this.state.idToRenderedItemsMap[panelId],
initialFocusedItemIndex: this.state.isUsingKeyboardToNavigate ? this.state.focusedItemIndex : panel.initialFocusedItemIndex,
onUseKeyboardToNavigate: this.onUseKeyboardToNavigate,
showNextPanel: this.showNextPanel,
showPreviousPanel: this.showPreviousPanel
}, panel.content);
}
}, {
key: "render",
value: function render() {
var _this$props2 = this.props,
stylesMemoizer = _this$props2.stylesMemoizer,
panels = _this$props2.panels,
onPanelChange = _this$props2.onPanelChange,
className = _this$props2.className,
initialPanelId = _this$props2.initialPanelId,
size = _this$props2.size,
rest = _objectWithoutProperties(_this$props2, _excluded3);
var incomingPanel = this.renderPanel(this.state.incomingPanelId, 'in');
var outgoingPanel;
if (this.state.isOutgoingPanelVisible) {
outgoingPanel = this.renderPanel(this.state.outgoingPanelId, 'out');
}
var width = this.state.idToPanelMap[this.state.incomingPanelId] && this.state.idToPanelMap[this.state.incomingPanelId].width ? this.state.idToPanelMap[this.state.incomingPanelId].width : undefined;
var classes = classNames('euiContextMenu', className);
var styles = stylesMemoizer(euiContextMenuStyles);
return ___EmotionJSX("div", _extends({
css: styles.euiContextMenu,
className: classes,
style: {
height: this.state.height,
width: width
}
}, rest), outgoingPanel, incomingPanel);
}
}], [{
key: "getDerivedStateFromProps",
value: function getDerivedStateFromProps(nextProps, prevState) {
var panels = nextProps.panels;
if (panels && prevState.prevProps.panels !== panels) {
return {
prevProps: {
panels: panels
},
idToPanelMap: mapIdsToPanels(panels),
idToPreviousPanelIdMap: mapIdsToPreviousPanels(panels),
idAndItemIndexToPanelIdMap: mapPanelItemsToPanels(panels)
};
}
return null;
}
}]);
}(Component);
_defineProperty(EuiContextMenuClass, "defaultProps", {
panels: [],
size: 'm'
});
export var EuiContextMenu = withEuiStylesMemoizer(EuiContextMenuClass);