UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

226 lines • 11.5 kB
import * as tslib_1 from "tslib"; import * as React from 'react'; import { BaseComponent, classNamesFunction, divProperties, getNativeProps } from '../../Utilities'; import { FocusZone, FocusZoneDirection } from '../../FocusZone'; import { ActionButton } from '../../Button'; import { Icon } from '../../Icon'; import { buttonStyles } from './Nav.styles'; // The number pixels per indentation level for Nav links. var _indentationSize = 14; // The number of pixels of left margin var _baseIndent = 3; // global var used in _isLinkSelectedKey var _urlResolver; export function isRelativeUrl(url) { // A URL is relative if it has no protocol. return !!url && !/^[a-z0-9+-.]:\/\//i.test(url); } var getClassNames = classNamesFunction(); var NavBase = /** @class */ (function (_super) { tslib_1.__extends(NavBase, _super); function NavBase(props) { var _this = _super.call(this, props) || this; _this._onRenderLink = function (link) { var _a = _this.props, styles = _a.styles, groups = _a.groups, theme = _a.theme; var classNames = getClassNames(styles, { theme: theme, groups: groups }); return React.createElement("div", { className: classNames.linkText }, link.name); }; _this._renderGroup = function (group, groupIndex) { var _a = _this.props, styles = _a.styles, groups = _a.groups, theme = _a.theme; var classNames = getClassNames(styles, { theme: theme, isGroup: true, isExpanded: !_this.state.isGroupCollapsed[group.name], groups: groups }); return (React.createElement("div", { key: groupIndex, className: classNames.group }, group.name ? (React.createElement("button", { className: classNames.chevronButton, onClick: _this._onGroupHeaderClicked.bind(_this, group) }, React.createElement(Icon, { className: classNames.chevronIcon, iconName: "ChevronDown" }), group.name)) : null, React.createElement("div", { className: classNames.groupContent }, _this._renderLinks(group.links, 0 /* nestingLevel */)))); }; _this.state = { isGroupCollapsed: {}, isLinkExpandStateChanged: false, selectedKey: props.initialSelectedKey || props.selectedKey }; if (props.groups) { for (var _i = 0, _a = props.groups; _i < _a.length; _i++) { var group = _a[_i]; if (group.collapseByDefault && group.name) { _this.state.isGroupCollapsed[group.name] = true; } } } return _this; } NavBase.prototype.componentWillReceiveProps = function (newProps) { var newGroups = newProps.groups || []; var isGroupCollapsed = this.state.isGroupCollapsed; // If the component's props were updated, new groups may have been added, which may have // collapseByDefault set. Ensure that setting is respected for any new groups. // (If isGroupCollapsed is already set for a group, don't overwrite that.) var hasUpdated = false; for (var _i = 0, newGroups_1 = newGroups; _i < newGroups_1.length; _i++) { var newGroup = newGroups_1[_i]; if (newGroup.name && newGroup.collapseByDefault && !isGroupCollapsed.hasOwnProperty(newGroup.name)) { isGroupCollapsed[newGroup.name] = true; hasUpdated = true; } } if (hasUpdated) { this.setState({ isGroupCollapsed: isGroupCollapsed }); } }; NavBase.prototype.render = function () { var _a = this.props, styles = _a.styles, groups = _a.groups, className = _a.className, isOnTop = _a.isOnTop, theme = _a.theme; if (!groups) { return null; } var groupElements = groups.map(this._renderGroup); var classNames = getClassNames(styles, { theme: theme, className: className, isOnTop: isOnTop, groups: groups }); return (React.createElement(FocusZone, { direction: FocusZoneDirection.vertical }, React.createElement("nav", { role: "navigation", className: classNames.root, "aria-label": this.props.ariaLabel }, groupElements))); }; Object.defineProperty(NavBase.prototype, "selectedKey", { get: function () { return this.state.selectedKey; }, enumerable: true, configurable: true }); NavBase.prototype._renderNavLink = function (link, linkIndex, nestingLevel) { var _a = this.props, styles = _a.styles, groups = _a.groups, theme = _a.theme, _b = _a.onRenderLink, onRenderLink = _b === void 0 ? this._onRenderLink : _b; var classNames = getClassNames(styles, { theme: theme, isSelected: this._isLinkSelected(link), isButtonEntry: link.onClick && !link.forceAnchor, leftPadding: _indentationSize * nestingLevel + _baseIndent, groups: groups }); // Prevent hijacking of the parent window if link.target is defined var rel = link.url && link.target && !isRelativeUrl(link.url) ? 'noopener noreferrer' : undefined; return (React.createElement(ActionButton, { className: classNames.link, styles: buttonStyles, href: link.url || (link.forceAnchor ? 'javascript:' : undefined), iconProps: link.iconProps || { iconName: link.icon || '' }, ariaDescription: link.title || link.name, onClick: link.onClick ? this._onNavButtonLinkClicked.bind(this, link) : this._onNavAnchorLinkClicked.bind(this, link), title: link.title || link.name, target: link.target, rel: rel, "aria-label": link.ariaLabel }, onRenderLink(link, this._onRenderLink))); }; NavBase.prototype._renderCompositeLink = function (link, linkIndex, nestingLevel) { var divProps = tslib_1.__assign({}, getNativeProps(link, divProperties, ['onClick'])); var _a = this.props, styles = _a.styles, groups = _a.groups, theme = _a.theme; var classNames = getClassNames(styles, { theme: theme, isExpanded: !!link.isExpanded, isSelected: this._isLinkSelected(link), isLink: true, position: _indentationSize * nestingLevel + 1, groups: groups }); return (React.createElement("div", tslib_1.__assign({}, divProps, { key: link.key || linkIndex, className: classNames.compositeLink }), link.links && link.links.length > 0 ? (React.createElement("button", { className: classNames.chevronButton, onClick: this._onLinkExpandClicked.bind(this, link), "aria-label": this.props.expandButtonAriaLabel, "aria-expanded": link.isExpanded ? 'true' : 'false' }, React.createElement(Icon, { className: classNames.chevronIcon, iconName: "ChevronDown" }))) : null, this._renderNavLink(link, linkIndex, nestingLevel))); }; NavBase.prototype._renderLink = function (link, linkIndex, nestingLevel) { var _a = this.props, styles = _a.styles, groups = _a.groups, theme = _a.theme; var classNames = getClassNames(styles, { theme: theme, groups: groups }); return (React.createElement("li", { key: link.key || linkIndex, role: "listitem", className: classNames.navItem }, this._renderCompositeLink(link, linkIndex, nestingLevel), link.isExpanded ? this._renderLinks(link.links, ++nestingLevel) : null)); }; NavBase.prototype._renderLinks = function (links, nestingLevel) { var _this = this; if (!links || !links.length) { return null; } var linkElements = links.map(function (link, linkIndex) { return _this._renderLink(link, linkIndex, nestingLevel); }); var _a = this.props, styles = _a.styles, groups = _a.groups, theme = _a.theme; var classNames = getClassNames(styles, { theme: theme, groups: groups }); return (React.createElement("ul", { role: "list", className: classNames.navItems }, linkElements)); }; NavBase.prototype._onGroupHeaderClicked = function (group, ev) { var isGroupCollapsed = this.state.isGroupCollapsed; var groupKey = group.name; var isCollapsed = !isGroupCollapsed[groupKey]; if (group.onHeaderClick) { group.onHeaderClick(ev, isCollapsed); } isGroupCollapsed[groupKey] = isCollapsed; this.setState({ isGroupCollapsed: isGroupCollapsed }); ev.preventDefault(); ev.stopPropagation(); }; NavBase.prototype._onLinkExpandClicked = function (link, ev) { var onLinkExpandClick = this.props.onLinkExpandClick; if (onLinkExpandClick) { onLinkExpandClick(ev, link); } if (!ev.defaultPrevented) { link.isExpanded = !link.isExpanded; this.setState({ isLinkExpandStateChanged: true }); } ev.preventDefault(); ev.stopPropagation(); }; NavBase.prototype._onNavAnchorLinkClicked = function (link, ev) { if (this.props.onLinkClick) { this.props.onLinkClick(ev, link); } if (!link.url && link.links && link.links.length > 0) { this._onLinkExpandClicked(link, ev); } this.setState({ selectedKey: link.key }); }; NavBase.prototype._onNavButtonLinkClicked = function (link, ev) { if (link.onClick) { link.onClick(ev, link); } if (!link.url && link.links && link.links.length > 0) { this._onLinkExpandClicked(link, ev); } this.setState({ selectedKey: link.key }); }; NavBase.prototype._isLinkSelected = function (link) { // if caller passes in selectedKey, use it as first choice or // if current state.selectedKey (from addressbar) is match to the link if (this.props.selectedKey !== undefined) { return link.key === this.props.selectedKey; } else if (this.state.selectedKey !== undefined && link.key === this.state.selectedKey) { return true; } // resolve is not supported for ssr if (typeof window === 'undefined') { return false; } if (!link.url) { return false; } _urlResolver = _urlResolver || document.createElement('a'); _urlResolver.href = link.url || ''; var target = _urlResolver.href; if (location.href === target) { return true; } if (location.protocol + '//' + location.host + location.pathname === target) { return true; } if (location.hash) { // Match the hash to the url. if (location.hash === link.url) { return true; } // Match a rebased url. (e.g. #foo becomes http://hostname/foo) _urlResolver.href = location.hash.substring(1); return _urlResolver.href === target; } return false; }; NavBase.defaultProps = { groups: null }; return NavBase; }(BaseComponent)); export { NavBase }; //# sourceMappingURL=Nav.base.js.map