@blueprintjs/core
Version:
Core styles & components
195 lines (193 loc) • 9.31 kB
JavaScript
/*
* Copyright 2015 Palantir Technologies, Inc. All rights reserved.
* Licensed under the BSD-3 License as modified (the “License”); you may obtain a copy
* of the license at https://github.com/palantir/blueprint/blob/master/LICENSE
* and https://github.com/palantir/blueprint/blob/master/PATENTS
*/
;
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var classNames = require("classnames");
var PureRender = require("pure-render-decorator");
var React = require("react");
var abstractComponent_1 = require("../../common/abstractComponent");
var Classes = require("../../common/classes");
var Keys = require("../../common/keys");
var utils_1 = require("../../common/utils");
var tab2_1 = require("./tab2");
var tabTitle_1 = require("./tabTitle");
exports.Expander = function () { return React.createElement("div", { className: "pt-flex-expander" }); };
var TAB_SELECTOR = "." + Classes.TAB;
var Tabs2 = (function (_super) {
tslib_1.__extends(Tabs2, _super);
function Tabs2(props) {
var _this = _super.call(this, props) || this;
_this.refHandlers = {
tablist: function (tabElement) { return _this.tablistElement = tabElement; },
};
_this.handleKeyDown = function (e) {
var focusedElement = document.activeElement.closest(TAB_SELECTOR);
// rest of this is potentially expensive and futile, so bail if no tab is focused
if (focusedElement == null) {
return;
}
// must rely on DOM state because we have no way of mapping `focusedElement` to a JSX.Element
var enabledTabElements = _this.getTabElements()
.filter(function (el) { return el.getAttribute("aria-disabled") === "false"; });
var focusedIndex = enabledTabElements.indexOf(focusedElement);
var direction = _this.getKeyCodeDirection(e);
if (focusedIndex >= 0 && direction !== undefined) {
e.preventDefault();
var length_1 = enabledTabElements.length;
// auto-wrapping at 0 and `length`
var nextFocusedIndex = (focusedIndex + direction + length_1) % length_1;
enabledTabElements[nextFocusedIndex].focus();
}
};
_this.handleKeyPress = function (e) {
var targetTabElement = e.target.closest(TAB_SELECTOR);
if (targetTabElement != null && isEventKeyCode(e, Keys.SPACE, Keys.ENTER)) {
e.preventDefault();
targetTabElement.click();
}
};
_this.handleTabClick = function (newTabId) {
var selectedTabId = _this.state.selectedTabId;
if (newTabId !== selectedTabId) {
utils_1.safeInvoke(_this.props.onChange, newTabId, selectedTabId);
if (_this.props.selectedTabId === undefined) {
_this.setState({ selectedTabId: newTabId });
}
}
};
_this.renderTabPanel = function (tab) {
var _a = tab.props, className = _a.className, panel = _a.panel, id = _a.id;
if (panel === undefined) {
return undefined;
}
return (React.createElement("div", { "aria-labelledby": tabTitle_1.generateTabTitleId(_this.props.id, id), "aria-hidden": id !== _this.state.selectedTabId, className: classNames(Classes.TAB_PANEL, className), id: tabTitle_1.generateTabPanelId(_this.props.id, id), key: id, role: "tabpanel" }, panel));
};
_this.renderTabTitle = function (tab) {
var id = tab.props.id;
return (React.createElement(tabTitle_1.TabTitle, tslib_1.__assign({}, tab.props, { parentId: _this.props.id, onClick: _this.handleTabClick, selected: id === _this.state.selectedTabId })));
};
var selectedTabId = _this.getInitialSelectedTabId();
_this.state = { selectedTabId: selectedTabId };
return _this;
}
Tabs2.prototype.render = function () {
var _this = this;
var _a = this.state, indicatorWrapperStyle = _a.indicatorWrapperStyle, selectedTabId = _a.selectedTabId;
var tabTitles = React.Children.map(this.props.children, function (child) { return (isTab(child) ? _this.renderTabTitle(child) : child); });
var tabPanels = this.getTabChildren()
.filter(this.props.renderActiveTabPanelOnly ? function (tab) { return tab.props.id === selectedTabId; } : function () { return true; })
.map(this.renderTabPanel);
var tabIndicator = (React.createElement("div", { className: "pt-tab-indicator-wrapper", style: indicatorWrapperStyle },
React.createElement("div", { className: "pt-tab-indicator" })));
var classes = classNames(Classes.TABS, (_b = {}, _b[Classes.VERTICAL] = this.props.vertical, _b), this.props.className);
return (React.createElement("div", { className: classes },
React.createElement("div", { className: Classes.TAB_LIST, onKeyDown: this.handleKeyDown, onKeyPress: this.handleKeyPress, ref: this.refHandlers.tablist, role: "tablist" },
this.props.animate ? tabIndicator : undefined,
tabTitles),
tabPanels));
var _b;
};
Tabs2.prototype.componentDidMount = function () {
this.moveSelectionIndicator();
};
Tabs2.prototype.componentWillReceiveProps = function (_a) {
var selectedTabId = _a.selectedTabId;
if (selectedTabId !== undefined) {
// keep state in sync with controlled prop, so state is canonical source of truth
this.setState({ selectedTabId: selectedTabId });
}
};
Tabs2.prototype.componentDidUpdate = function (_prevProps, prevState) {
if (this.state.selectedTabId !== prevState.selectedTabId) {
this.moveSelectionIndicator();
}
};
Tabs2.prototype.getInitialSelectedTabId = function () {
// NOTE: providing an unknown ID will hide the selection
var _a = this.props, defaultSelectedTabId = _a.defaultSelectedTabId, selectedTabId = _a.selectedTabId;
if (selectedTabId !== undefined) {
return selectedTabId;
}
else if (defaultSelectedTabId !== undefined) {
return defaultSelectedTabId;
}
else {
// select first tab in absence of user input
var tabs = this.getTabChildren();
return tabs.length === 0 ? undefined : tabs[0].props.id;
}
};
Tabs2.prototype.getKeyCodeDirection = function (e) {
if (isEventKeyCode(e, Keys.ARROW_LEFT, Keys.ARROW_UP)) {
return -1;
}
else if (isEventKeyCode(e, Keys.ARROW_RIGHT, Keys.ARROW_DOWN)) {
return 1;
}
return undefined;
};
/** Filters this.props.children to only `<Tab>`s */
Tabs2.prototype.getTabChildren = function () {
return React.Children.toArray(this.props.children).filter(isTab);
};
/** Queries root HTML element for all `.pt-tab`s with optional filter selector */
Tabs2.prototype.getTabElements = function (subselector) {
if (subselector === void 0) { subselector = ""; }
if (this.tablistElement == null) {
return [];
}
return this.tablistElement.queryAll(TAB_SELECTOR + subselector);
};
/**
* Calculate the new height, width, and position of the tab indicator.
* Store the CSS values so the transition animation can start.
*/
Tabs2.prototype.moveSelectionIndicator = function () {
if (this.tablistElement === undefined) {
return;
}
var tabIdSelector = TAB_SELECTOR + "[data-tab-id=\"" + this.state.selectedTabId + "\"]";
var selectedTabElement = this.tablistElement.query(tabIdSelector);
var indicatorWrapperStyle = { display: "none" };
if (selectedTabElement != null) {
var clientHeight = selectedTabElement.clientHeight, clientWidth = selectedTabElement.clientWidth, offsetLeft = selectedTabElement.offsetLeft, offsetTop = selectedTabElement.offsetTop;
indicatorWrapperStyle = {
height: clientHeight,
transform: "translateX(" + Math.floor(offsetLeft) + "px) translateY(" + Math.floor(offsetTop) + "px)",
width: clientWidth,
};
}
this.setState({ indicatorWrapperStyle: indicatorWrapperStyle });
};
return Tabs2;
}(abstractComponent_1.AbstractComponent));
/** Insert a `Tabs2.Expander` between any two children to right-align all subsequent children. */
Tabs2.Expander = exports.Expander;
Tabs2.Tab = tab2_1.Tab2;
Tabs2.defaultProps = {
animate: true,
renderActiveTabPanelOnly: false,
vertical: false,
};
Tabs2.displayName = "Blueprint.Tabs2";
Tabs2 = tslib_1.__decorate([
PureRender
], Tabs2);
exports.Tabs2 = Tabs2;
exports.Tabs2Factory = React.createFactory(Tabs2);
function isEventKeyCode(e) {
var codes = [];
for (var _i = 1; _i < arguments.length; _i++) {
codes[_i - 1] = arguments[_i];
}
return codes.indexOf(e.which) >= 0;
}
function isTab(child) {
return child != null && child.type === tab2_1.Tab2;
}
//# sourceMappingURL=tabs2.js.map