office-ui-fabric-react
Version:
Reusable React components for building experiences for Office 365.
226 lines • 11.5 kB
JavaScript
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