@carbon/ibm-security
Version:
Carbon for Cloud & Cognitive IBM Security UI components
393 lines (390 loc) • 17.3 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _extends from "@babel/runtime/helpers/extends";
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 = ["children", "href", "content", "icon", "id", "title"],
_excluded2 = ["href", "element", "content", "id", "title"];
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
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; })(); }
/**
* @file Toolbar.
* @copyright IBM Security 2019 - 2021
*/
import { ArrowLeft20, Close20, Help20, Menu20, Settings20 } from '@carbon/icons-react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { isClient } from '../../globals/utils/capabilities';
import root from '../../globals/utils/globalRoot';
import toggle from '../Component';
import IconButton from '../IconButton';
import Nav from '../Nav';
import NavList from '../Nav/NavList';
import NavItem from '../Nav/NavItem';
import Transition from '../Transition';
import { getComponentNamespace } from '../../globals/namespace';
export var namespace = getComponentNamespace('toolbar');
/**
* Toolbar component.
* @param {Record<string, any>} htmlContent Toolbar props.
* @returns {Toolbar} Toolbar instance.
*/
var Toolbar = /*#__PURE__*/function (_Component) {
function Toolbar() {
var _this;
_classCallCheck(this, Toolbar);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _callSuper(this, Toolbar, [].concat(args));
_defineProperty(_this, "state", {
isActive: {
menu: false,
settings: false,
support: false
},
content: undefined,
showContent: false
});
_defineProperty(_this, "wrapper", /*#__PURE__*/React.createRef());
/**
* Handle a click outside of the Toolbar wrapper.
* @param {Event} event A click event.
*/
_defineProperty(_this, "handleClickOutside", function (event) {
var activeElement = event.target.getRootNode().activeElement || document.activeElement;
if (event.target !== activeElement && event.target.nodeName !== 'SHELL-COMPONENT' && _this.wrapper && !_this.wrapper.current.contains(event.target)) {
_this.setState({
isActive: {
menu: false,
settings: false,
support: false
}
});
_this.props.onToggle(false);
}
});
_defineProperty(_this, "toggleContent", function () {
var htmlContent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
_this.setState({
content: htmlContent,
showContent: !_this.state.showContent
});
});
return _this;
}
_inherits(Toolbar, _Component);
return _createClass(Toolbar, [{
key: "componentDidMount",
value: function componentDidMount() {
if (isClient() && root.document) {
root.document.addEventListener('click', this.handleClickOutside);
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
if (isClient() && root.document) {
root.document.removeEventListener('click', this.handleClickOutside);
}
}
}, {
key: "toggleIcon",
value:
/**
* Toggles the appropriate icon button based on whether the relevant panel is open.
* @param {string} label The icon label.
* @param {Function} renderIcon The icon to use.
* @param {string} type The panel to check.
* @returns {IconButton} The icon button to return.
*/
function toggleIcon(label, renderIcon, type) {
var _this2 = this;
var isActiveItem = this.state.isActive[type];
var iconButtonClass = "".concat(namespace, "__button");
var iconButtonClasses = classnames(iconButtonClass, _defineProperty({}, "".concat(iconButtonClass, "--active"), isActiveItem));
return /*#__PURE__*/React.createElement(IconButton, _extends({
className: iconButtonClasses,
"aria-expanded": isActiveItem
}, isActiveItem ? _defineProperty({}, "aria-controls", "".concat(namespace, "--toolbar--").concat(type)) : {}, {
"aria-label": label,
iconClassName: "".concat(namespace, "__icon"),
label: label,
onClick: function onClick() {
return _this2.togglePanel(type);
},
renderIcon: isActiveItem ? Close20 : renderIcon,
state: isActiveItem,
tooltip: !isActiveItem,
tooltipDirection: IconButton.TooltipDirection.RIGHT
}));
}
/**
* Toggles the panel and applies the appropriate class to the body.
* @param {string} type The type of panel to toggle.
*/
}, {
key: "togglePanel",
value: function togglePanel(type) {
var _this3 = this;
toggle.call(this, type, function (state) {
var isToggled = state[type];
_this3.props.onToggle(isToggled);
root.dispatchEvent(new CustomEvent("".concat(namespace, ":toggle"), {
detail: {
isToggled: isToggled
}
}));
});
}
}, {
key: "renderContent",
value:
/**
* Renders the panel content.
* @param {string} type The type of panel to render.
* @returns {React.Element} The rendered panel.
*/
function renderContent(type) {
var _this4 = this;
var navigationType = this.props[type];
var isActiveItem = this.state.isActive[type];
return isActiveItem && navigationType.length > 0 && navigationType.map(function (_ref2) {
var id = _ref2.id,
navigation = _ref2.navigation,
title = _ref2.title;
return /*#__PURE__*/React.createElement(Nav, {
key: id,
heading: title,
label: title
}, navigation.filter(function (item) {
return item !== null && item !== undefined;
}).map(function (_ref3) {
var children = _ref3.children,
href = _ref3.href,
content = _ref3.content,
icon = _ref3.icon,
navigationItemId = _ref3.id,
navigationItemTitle = _ref3.title,
props = _objectWithoutProperties(_ref3, _excluded);
var hasIcon = icon !== undefined;
return children ? /*#__PURE__*/React.createElement(NavList, {
className: classnames(_defineProperty({}, "".concat(namespace, "__nav__list__title--icon"), hasIcon)),
renderIcon: hasIcon,
icon: icon,
key: navigationItemId,
navigationItemTitle: navigationItemTitle,
title: navigationItemTitle
}, children.map(function (_ref4) {
var navigationListItemHref = _ref4.href,
navigationListItemElement = _ref4.element,
content = _ref4.content,
navigationListItemId = _ref4.id,
navigationListItemTitle = _ref4.title,
props = _objectWithoutProperties(_ref4, _excluded2);
return /*#__PURE__*/React.createElement(NavItem, _extends({
key: navigationListItemId,
onClick: function onClick() {
return _this4.togglePanel(type);
},
id: navigationListItemId,
href: navigationListItemHref,
element: navigationListItemElement,
link: content === undefined,
handleItemSelect: function handleItemSelect() {
return _this4.toggleContent(content);
}
}, props), navigationListItemTitle);
})) : /*#__PURE__*/React.createElement(NavItem, _extends({
key: navigationItemId,
id: navigationItemId,
onClick: function onClick() {
return _this4.togglePanel(type);
},
href: href,
link: content === undefined,
handleItemSelect: function handleItemSelect() {
return _this4.toggleContent(content);
}
}, props), hasIcon && /*#__PURE__*/React.createElement("img", {
alt: navigationItemTitle,
className: "".concat(namespace, "__nav__item__icon"),
src: icon
}), /*#__PURE__*/React.createElement("div", {
className: classnames("".concat(namespace, "__nav__item__title"), _defineProperty({}, "".concat(namespace, "__nav__item__title--icon"), hasIcon))
}, navigationItemTitle));
}));
});
}
}, {
key: "render",
value: function render() {
var _this5 = this;
var _this$props = this.props,
className = _this$props.className,
_this$props$labels = _this$props.labels,
_this$props$labels$ma = _this$props$labels.mainNavigation,
_this$props$labels$ma2 = _this$props$labels$ma === void 0 ? {} : _this$props$labels$ma,
_this$props$labels$ma3 = _this$props$labels$ma2.ariaLabel,
ariaLabel = _this$props$labels$ma3 === void 0 ? null : _this$props$labels$ma3,
menu = _this$props$labels.menu,
settings = _this$props$labels.settings,
support = _this$props$labels.support,
_this$props$renderAdd = _this$props.renderAddons,
renderAddons = _this$props$renderAdd === void 0 ? [] : _this$props$renderAdd;
var renderSupport = this.props.support.length > 0;
var renderSettings = this.props.settings.length > 0;
var classes = classnames(namespace, className, _defineProperty({}, "".concat(namespace, "__minimized"), !renderSupport && !renderSettings));
var isActive = this.state.isActive;
var activeItems = Object.entries(isActive)
// eslint-disable-next-line no-unused-vars
.filter(function (_ref5) {
var _ref6 = _slicedToArray(_ref5, 2),
type = _ref6[0],
isActiveItem = _ref6[1];
return isActiveItem;
});
var currentType = '';
var isPanelActive = false;
if (activeItems.length > 0) {
var _activeItems = _slicedToArray(activeItems, 1);
var _activeItems$ = _slicedToArray(_activeItems[0], 2);
currentType = _activeItems$[0];
isPanelActive = _activeItems$[1];
}
return /*#__PURE__*/React.createElement("div", {
ref: this.wrapper
}, /*#__PURE__*/React.createElement("nav", {
"aria-label": ariaLabel,
className: classes
}, /*#__PURE__*/React.createElement("ul", {
className: "".concat(namespace, "__group")
}, /*#__PURE__*/React.createElement("li", null, this.toggleIcon(menu.button, Menu20, 'menu')), renderSettings && /*#__PURE__*/React.createElement("li", null, this.toggleIcon(settings.button, Settings20, 'settings')), renderSupport && /*#__PURE__*/React.createElement("li", null, this.toggleIcon(support.button, Help20, 'support')), renderAddons.map(function (_ref7) {
var id = _ref7.id,
render = _ref7.render;
return /*#__PURE__*/React.createElement("li", {
key: id
}, render({
className: "".concat(namespace, "__button"),
iconClassName: "".concat(namespace, "__icon")
}));
}))), /*#__PURE__*/React.createElement(Transition, {
className: namespace,
component: "span"
}, isPanelActive && this.state.showContent ? /*#__PURE__*/React.createElement("div", {
role: "navigation",
"aria-label": currentType,
id: "".concat(namespace, "--toolbar--").concat(currentType),
className: "".concat(namespace, "__panel")
}, /*#__PURE__*/React.createElement(IconButton, {
onClick: this.toggleContent,
renderIcon: ArrowLeft20
}), /*#__PURE__*/React.createElement("div", {
className: "".concat(namespace, "__content"),
dangerouslySetInnerHTML: {
__html: this.state.content
} // eslint-disable-line react/no-danger
})) : isPanelActive && /*#__PURE__*/React.createElement("div", {
role: "navigation",
"aria-label": currentType,
id: "".concat(namespace, "--toolbar--").concat(currentType),
className: "".concat(namespace, "__panel")
}, Object.keys(isActive).map(function (type) {
return /*#__PURE__*/React.createElement(Transition, {
key: type,
className: "".concat(namespace, "__content"),
component: "span"
}, _this5.renderContent(type));
}))));
}
}]);
}(Component);
export { Toolbar as default };
var href = PropTypes.string.isRequired;
var navigation = {
/** @type {string} The ID of the navigation. */
id: PropTypes.string.isRequired,
/** @type {string} The title of the navigation. */
title: PropTypes.node.isRequired,
/** @type {node} Content. */
content: PropTypes.node,
/** @type {string} Icon. */
icon: PropTypes.string
};
var panel = function panel() {
return /** @type {Array<Object.*>} An array list of navigation lists and sub-navigation. */PropTypes.arrayOf(/** @type {Record<object, object>} An object list of navigation. */
PropTypes.shape(_objectSpread(_objectSpread({}, navigation), {}, {
/** @type {Array<Object.*>} An array list of navigation items. */
navigation: PropTypes.arrayOf(/** @type {Record<object, object>} An object list of navigation. */
PropTypes.shape(Object.assign({}, navigation, {
/** @type {Array<Object.*>} An array list of sub-navigation items. */
children: PropTypes.arrayOf(/** @type {Record<object, object>} An object list of sub-navigation. */
PropTypes.shape(Object.assign({}, navigation, href))),
href: href
}))).isRequired
})).isRequired);
};
Toolbar.propTypes = {
/** @type {string} Extra classes to add. */
className: PropTypes.string,
/** @type {Record<object, object>} An object list of labels. */
labels: PropTypes.shape({
/** @type {Record<string, string>} An object list of navigation labels for the top level navigation item. */
mainNavigation: PropTypes.shape({
/** Specify the `aria-label` for the primary navigation */
ariaLabel: PropTypes.string.isRequired
}).isRequired,
/** @type {Record<string, string>} An object list of menu labels. */
menu: PropTypes.shape({
/** @type {string} The button label. */
button: PropTypes.string.isRequired,
/** @type {string} The tooltip label. */
tooltip: PropTypes.string
}).isRequired,
/** @type {Record<string, string>} An object list of settings labels. */
settings: PropTypes.shape({
/** @type {string} The button label. */
button: PropTypes.string.isRequired,
/** @type {string} The tooltip label. */
tooltip: PropTypes.string
}),
/** @type {Record<string, string>} An object list of support labels. */
support: PropTypes.shape({
/** @type {string} The button label. */
button: PropTypes.string.isRequired,
/** @type {string} The tooltip label. */
tooltip: PropTypes.string
})
}).isRequired,
// eslint-disable-next-line react/no-unused-prop-types
menu: panel(),
/** @type {Function} Toggle handler. */
onToggle: PropTypes.func,
/** @type {Array<{id: string, tooltip: string, render: Function: React.Element}>} An object list to render custom addon icons. */
renderAddons: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
render: PropTypes.func.isRequired,
tooltip: PropTypes.string
})),
// eslint-disable-next-line react/no-unused-prop-types
settings: panel(),
// eslint-disable-next-line react/no-unused-prop-types
support: panel()
};
Toolbar.defaultProps = {
className: null,
menu: [],
onToggle: function onToggle(isToggled) {
return isToggled;
},
renderAddons: [],
settings: [],
support: []
};