UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

209 lines • 12.8 kB
define(["require", "exports", "tslib", "react", "../../Utilities", "../../FocusZone", "../../Button", "../../Icon", "./Nav.scss", "../../Styling"], function (require, exports, tslib_1, React, Utilities_1, FocusZone_1, Button_1, Icon_1, stylesImport, Styling_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var styles = stylesImport; // The number pixels per indentation level for Nav links. var _indentationSize = 14; // Tne number of pixels of left margin when there is expand/collaps button var _indentWithExpandButton = 28; // Tne number of pixels of left margin when there is expand/collaps button var _indentNoExpandButton = 20; // global var used in _isLinkSelectedKey var _urlResolver; function isRelativeUrl(url) { // A URL is relative if it has no protocol. return !!url && !/^[a-z0-9+-.]:\/\//i.test(url); } exports.isRelativeUrl = isRelativeUrl; var Nav = (function (_super) { tslib_1.__extends(Nav, _super); function Nav(props) { var _this = _super.call(this, props) || this; _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; } } } _this._hasExpandButton = false; return _this; } Nav.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 }); } }; Nav.prototype.render = function () { var _a = this.props, groups = _a.groups, className = _a.className, isOnTop = _a.isOnTop; if (!groups) { return null; } // When groups[x].name is specified or any of the links have children, the expand/collapse // chevron button is shown and different padding is needed. _hasExpandButton marks this condition. this._hasExpandButton = groups.some(function (group) { return group ? !!group.name || (group.links && group.links.some(function (link) { return !!(link && link.links && link.links.length); })) : false; }); var groupElements = groups.map(this._renderGroup); return (React.createElement(FocusZone_1.FocusZone, { direction: FocusZone_1.FocusZoneDirection.vertical }, React.createElement("nav", { role: 'navigation', className: Utilities_1.css('ms-Nav', styles.root, className, isOnTop && Utilities_1.css('is-onTop', styles.rootIsOnTop, Styling_1.AnimationClassNames.slideRightIn40)) }, groupElements))); }; Object.defineProperty(Nav.prototype, "selectedKey", { get: function () { return this.state.selectedKey; }, enumerable: true, configurable: true }); Nav.prototype._renderAnchorLink = function (link, linkIndex, nestingLevel) { // Determine the appropriate padding to add before this link. // In RTL, the "before" padding will go on the right instead of the left. var isRtl = Utilities_1.getRTL(); var paddingBefore = _indentationSize * nestingLevel + (this._hasExpandButton ? _indentWithExpandButton : _indentNoExpandButton); // 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("a", { className: Utilities_1.css('ms-Nav-link', styles.link), style: (_a = {}, _a[isRtl ? 'paddingRight' : 'paddingLeft'] = paddingBefore + 'px', _a), href: link.url || 'javascript:', onClick: this._onNavAnchorLinkClicked.bind(this, link), "aria-label": link.ariaLabel, title: link.title || link.name, target: link.target, rel: rel }, link.iconClassName && (React.createElement(Icon_1.Icon, { className: Utilities_1.css(link.iconClassName, styles.iconLink), iconName: 'IconLink' })), this.props.onRenderLink(link))); var _a; }; Nav.prototype._renderButtonLink = function (link, linkIndex) { return (React.createElement(Button_1.CommandButton, { className: Utilities_1.css('ms-Nav-link ms-Nav-linkButton', styles.link, this._hasExpandButton && 'isOnExpanded ' + styles.linkIsOnExpanded), href: link.url, iconProps: { iconName: link.icon }, description: link.title || link.name, onClick: this._onNavButtonLinkClicked.bind(this, link) }, link.name)); }; Nav.prototype._renderCompositeLink = function (link, linkIndex, nestingLevel) { var isLinkSelected = this._isLinkSelected(link); var isRtl = Utilities_1.getRTL(); var paddingBefore = _indentationSize * nestingLevel + "px"; return (React.createElement("div", { key: link.key || linkIndex, className: Utilities_1.css('ms-Nav-compositeLink', styles.compositeLink, !!link.isExpanded && 'is-expanded', isLinkSelected && 'is-selected', !!link.isExpanded && styles.compositeLinkIsExpanded, isLinkSelected && styles.compositeLinkIsSelected) }, (link.links && link.links.length > 0 ? React.createElement("button", { style: (_a = {}, _a[isRtl ? 'marginRight' : 'marginLeft'] = paddingBefore, _a), className: Utilities_1.css('ms-Nav-chevronButton ms-Nav-chevronButton--link', styles.chevronButton, styles.chevronButtonLink), onClick: this._onLinkExpandClicked.bind(this, link), "aria-label": this.props.expandButtonAriaLabel, "aria-expanded": link.isExpanded ? 'true' : 'false' }, React.createElement(Icon_1.Icon, { className: Utilities_1.css('ms-Nav-chevron', styles.chevronIcon, link.isExpanded), iconName: 'ChevronDown' })) : null), link.onClick && !link.forceAnchor ? this._renderButtonLink(link, linkIndex) : this._renderAnchorLink(link, linkIndex, nestingLevel))); var _a; }; Nav.prototype._renderLink = function (link, linkIndex, nestingLevel) { return (React.createElement("li", { key: link.key || linkIndex, role: 'listitem', className: Utilities_1.css(styles.navItem) }, this._renderCompositeLink(link, linkIndex, nestingLevel), (link.isExpanded ? this._renderLinks(link.links, ++nestingLevel) : null))); }; Nav.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); }); return (React.createElement("ul", { role: 'list', "aria-label": this.props.ariaLabel, className: Utilities_1.css(styles.navItems) }, linkElements)); }; Nav.prototype._renderGroup = function (group, groupIndex) { var isGroupExpanded = !this.state.isGroupCollapsed[group.name]; return (React.createElement("div", { key: groupIndex, className: Utilities_1.css('ms-Nav-group', styles.group, isGroupExpanded && ('is-expanded ' + styles.groupIsExpanded)) }, (group.name ? React.createElement("button", { className: Utilities_1.css('ms-Nav-chevronButton ms-Nav-chevronButton--group ms-Nav-groupHeaderFontSize', styles.chevronButton, styles.chevronButtonIsGroup, styles.groupHeaderFontSize), onClick: this._onGroupHeaderClicked.bind(this, group) }, React.createElement(Icon_1.Icon, { className: Utilities_1.css('ms-Nav-chevron', styles.chevronIcon, isGroupExpanded && styles.chevronIsExpanded), iconName: 'ChevronDown' }), group.name) : null), React.createElement("div", { className: Utilities_1.css('ms-Nav-groupContent', Styling_1.AnimationClassNames.slideDownIn20, styles.groupContent) }, this._renderLinks(group.links, 0 /* nestingLevel */)))); }; Nav.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(); }; Nav.prototype._onLinkExpandClicked = function (link, ev) { link.isExpanded = !link.isExpanded; this.setState({ isLinkExpandStateChanged: true }); ev.preventDefault(); ev.stopPropagation(); }; Nav.prototype._onNavAnchorLinkClicked = function (link, ev) { if (this.props.onLinkClick) { this.props.onLinkClick(ev, link); } this.setState({ selectedKey: link.key }); }; Nav.prototype._onNavButtonLinkClicked = function (link, ev) { if (link.onClick) { link.onClick(ev, link); } this.setState({ selectedKey: link.key }); }; Nav.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; }; Nav.defaultProps = { groups: null, onRenderLink: function (link) { return (React.createElement("span", { className: Utilities_1.css('ms-Nav-linkText', styles.linkText) }, link.name)); } }; tslib_1.__decorate([ Utilities_1.autobind ], Nav.prototype, "_renderGroup", null); return Nav; }(Utilities_1.BaseComponent)); exports.Nav = Nav; }); //# sourceMappingURL=Nav.js.map