@blueprintjs/core
Version:
Core styles & components
163 lines (162 loc) • 8.13 kB
JavaScript
/*
* Copyright 2015 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the terms of the LICENSE file distributed with this project.
*/
import * as tslib_1 from "tslib";
import * as classNames from "classnames";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { AbstractComponent } from "../../common/abstractComponent";
import * as Classes from "../../common/classes";
import * as Errors from "../../common/errors";
import { Position } from "../../common/position";
import { Popover, PopoverInteractionKind } from "../popover/popover";
import { Menu } from "./menu";
var REACT_CONTEXT_TYPES = {
alignLeft: function (obj, key) {
if (obj[key] != null && typeof obj[key] !== "boolean") {
return new Error("[Blueprint] MenuItem context alignLeft must be boolean");
}
return undefined;
},
};
var MenuItem = (function (_super) {
tslib_1.__extends(MenuItem, _super);
function MenuItem() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = {
alignLeft: false,
};
_this.liRefHandler = function (r) { return (_this.liElement = r); };
_this.measureSubmenu = function (el) {
if (el != null) {
var submenuRect = ReactDOM.findDOMNode(el).getBoundingClientRect();
var parentWidth = _this.liElement.parentElement.getBoundingClientRect().width;
var adjustmentWidth = submenuRect.width + parentWidth;
// this ensures that the left and right measurements represent a submenu opened to the right
var submenuLeft = submenuRect.left;
var submenuRight = submenuRect.right;
if (_this.state.alignLeft) {
submenuLeft += adjustmentWidth;
submenuRight += adjustmentWidth;
}
var _a = _this.props.submenuViewportMargin.left, left = _a === void 0 ? 0 : _a;
var _b = _this.props.submenuViewportMargin.right, right = _b === void 0 ? 0 : _b;
if (typeof document !== "undefined" &&
typeof document.documentElement !== "undefined" &&
Number(document.documentElement.clientWidth)) {
// we're in a browser context and the clientWidth is available,
// use it to set calculate 'right'
right = document.documentElement.clientWidth - right;
}
// uses context to prioritize the previous positioning
var alignLeft = _this.context.alignLeft || false;
if (alignLeft) {
if (submenuLeft - adjustmentWidth <= left) {
alignLeft = false;
}
}
else {
if (submenuRight >= right) {
alignLeft = true;
}
}
_this.setState({ alignLeft: alignLeft });
}
};
_this.renderChildren = function () {
var _a = _this.props, children = _a.children, submenu = _a.submenu;
if (children != null) {
var childProps_1 = _this.cascadeProps();
if (Object.keys(childProps_1).length === 0) {
return children;
}
else {
return React.Children.map(children, function (child) {
return React.cloneElement(child, childProps_1);
});
}
}
else if (submenu != null) {
return submenu.map(_this.cascadeProps).map(renderMenuItem);
}
else {
return undefined;
}
};
/**
* Evalutes this.props and cascades prop values into new props when:
* - submenuViewportMargin is defined, but is undefined for the supplied input.
* - useSmartPositioning is false, but is undefined for the supplied input.
* @param {IMenuItemProps} newProps If supplied, object will be modified, otherwise, defaults to an empty object.
* @returns An object to be used as child props.
*/
_this.cascadeProps = function (newProps) {
if (newProps === void 0) { newProps = {}; }
var _a = _this.props, submenuViewportMargin = _a.submenuViewportMargin, useSmartPositioning = _a.useSmartPositioning;
if (submenuViewportMargin != null && newProps.submenuViewportMargin == null) {
newProps.submenuViewportMargin = submenuViewportMargin;
}
if (useSmartPositioning === false && newProps.useSmartPositioning == null) {
newProps.useSmartPositioning = useSmartPositioning;
}
return newProps;
};
return _this;
}
MenuItem.prototype.render = function () {
var _a = this.props, children = _a.children, disabled = _a.disabled, label = _a.label, submenu = _a.submenu, popoverProps = _a.popoverProps;
var hasSubmenu = children != null || submenu != null;
var liClasses = classNames((_b = {},
_b[Classes.MENU_SUBMENU] = hasSubmenu,
_b));
var anchorClasses = classNames(Classes.MENU_ITEM, Classes.intentClass(this.props.intent), (_c = {},
_c[Classes.DISABLED] = disabled,
// prevent popover from closing when clicking on submenu trigger or disabled item
_c[Classes.POPOVER_DISMISS] = this.props.shouldDismissPopover && !disabled && !hasSubmenu,
_c), Classes.iconClass(this.props.iconName), this.props.className);
var labelElement;
if (label != null) {
labelElement = React.createElement("span", { className: "pt-menu-item-label" }, label);
}
var content = (React.createElement("a", { className: anchorClasses, href: disabled ? undefined : this.props.href, onClick: disabled ? undefined : this.props.onClick, tabIndex: disabled ? undefined : 0, target: this.props.target },
labelElement,
this.props.text));
if (hasSubmenu) {
var measureSubmenu = this.props.useSmartPositioning ? this.measureSubmenu : null;
var submenuElement = React.createElement(Menu, { ref: measureSubmenu }, this.renderChildren());
var popoverClasses = classNames(Classes.MINIMAL, Classes.MENU_SUBMENU, popoverProps.popoverClassName, (_d = {},
_d[Classes.ALIGN_LEFT] = this.state.alignLeft,
_d));
content = (React.createElement(Popover, tslib_1.__assign({ isDisabled: disabled, enforceFocus: false, hoverCloseDelay: 0, inline: true, interactionKind: PopoverInteractionKind.HOVER, position: this.state.alignLeft ? Position.LEFT_TOP : Position.RIGHT_TOP, useSmartArrowPositioning: false }, popoverProps, { content: submenuElement, popoverClassName: popoverClasses }), content));
}
return (React.createElement("li", { className: liClasses, ref: this.liRefHandler }, content));
var _b, _c, _d;
};
MenuItem.prototype.getChildContext = function () {
return { alignLeft: this.state.alignLeft };
};
MenuItem.prototype.validateProps = function (props) {
if (props.children != null && props.submenu != null) {
console.warn(Errors.MENU_WARN_CHILDREN_SUBMENU_MUTEX);
}
};
MenuItem.defaultProps = {
disabled: false,
popoverProps: {},
shouldDismissPopover: true,
submenuViewportMargin: {},
text: "",
useSmartPositioning: true,
};
MenuItem.displayName = "Blueprint.MenuItem";
MenuItem.contextTypes = REACT_CONTEXT_TYPES;
MenuItem.childContextTypes = REACT_CONTEXT_TYPES;
return MenuItem;
}(AbstractComponent));
export { MenuItem };
export function renderMenuItem(props, key) {
return React.createElement(MenuItem, tslib_1.__assign({ key: key }, props));
}
export var MenuItemFactory = React.createFactory(MenuItem);