office-ui-fabric-react
Version: 
Reusable React components for building experiences for Office 365.
240 lines • 12.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var React = require("react");
var Utilities_1 = require("../../Utilities");
var FocusZone_1 = require("../../FocusZone");
var Button_1 = require("../../Button");
var Icon_1 = require("../../Icon");
var Nav_styles_1 = require("./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;
function isRelativeUrl(url) {
    // A URL is relative if it has no protocol.
    return !!url && !/^[a-z0-9+-.]:\/\//i.test(url);
}
exports.isRelativeUrl = isRelativeUrl;
var getClassNames = Utilities_1.classNamesFunction();
var NavBase = /** @class */ (function (_super) {
    tslib_1.__extends(NavBase, _super);
    function NavBase(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;
    }
    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, getStyles = _a.getStyles, groups = _a.groups, className = _a.className, isOnTop = _a.isOnTop, theme = _a.theme;
        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);
        var classNames = getClassNames(getStyles, { theme: theme, className: className, isOnTop: isOnTop, groups: groups });
        return (React.createElement(FocusZone_1.FocusZone, { direction: FocusZone_1.FocusZoneDirection.vertical },
            React.createElement("nav", { role: 'navigation', className: classNames.root }, groupElements)));
    };
    Object.defineProperty(NavBase.prototype, "selectedKey", {
        get: function () {
            return this.state.selectedKey;
        },
        enumerable: true,
        configurable: true
    });
    NavBase.prototype._onRenderLink = function (link) {
        var _a = this.props, getStyles = _a.getStyles, groups = _a.groups, theme = _a.theme;
        var classNames = getClassNames(getStyles, { theme: theme, groups: groups });
        return (React.createElement("div", { className: classNames.linkText }, link.name));
    };
    NavBase.prototype._renderNavLink = function (link, linkIndex, nestingLevel) {
        var _a = this.props, getStyles = _a.getStyles, groups = _a.groups, theme = _a.theme, _b = _a.onRenderLink, onRenderLink = _b === void 0 ? this._onRenderLink : _b;
        var classNames = getClassNames(getStyles, {
            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(Button_1.ActionButton, { className: classNames.link, styles: Nav_styles_1.buttonStyles, href: link.url || (link.forceAnchor ? 'javascript:' : undefined), iconProps: link.iconProps || { iconName: link.icon || '' }, description: 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({}, Utilities_1.getNativeProps(link, Utilities_1.divProperties, ['onClick']));
        var _a = this.props, getStyles = _a.getStyles, groups = _a.groups, theme = _a.theme;
        var classNames = getClassNames(getStyles, {
            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_1.Icon, { className: classNames.chevronIcon, iconName: 'ChevronDown' })) : null),
            this._renderNavLink(link, linkIndex, nestingLevel)));
    };
    NavBase.prototype._renderLink = function (link, linkIndex, nestingLevel) {
        var _a = this.props, getStyles = _a.getStyles, groups = _a.groups, theme = _a.theme;
        var classNames = getClassNames(getStyles, { 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, getStyles = _a.getStyles, groups = _a.groups, theme = _a.theme;
        var classNames = getClassNames(getStyles, { theme: theme, groups: groups });
        return (React.createElement("ul", { role: 'list', "aria-label": this.props.ariaLabel, className: classNames.navItems }, linkElements));
    };
    NavBase.prototype._renderGroup = function (group, groupIndex) {
        var _a = this.props, getStyles = _a.getStyles, groups = _a.groups, theme = _a.theme;
        var classNames = getClassNames(getStyles, {
            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_1.Icon, { className: classNames.chevronIcon, iconName: 'ChevronDown' }),
                    group.name) : null),
            React.createElement("div", { className: classNames.groupContent }, this._renderLinks(group.links, 0 /* nestingLevel */))));
    };
    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);
        }
        this.setState({ selectedKey: link.key });
    };
    NavBase.prototype._onNavButtonLinkClicked = function (link, ev) {
        if (link.onClick) {
            link.onClick(ev, link);
        }
        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
    };
    tslib_1.__decorate([
        Utilities_1.autobind
    ], NavBase.prototype, "_onRenderLink", null);
    tslib_1.__decorate([
        Utilities_1.autobind
    ], NavBase.prototype, "_renderGroup", null);
    NavBase = tslib_1.__decorate([
        Utilities_1.customizable('Nav', ['theme'])
    ], NavBase);
    return NavBase;
}(Utilities_1.BaseComponent));
exports.NavBase = NavBase;
//# sourceMappingURL=Nav.base.js.map