azure-devops-ui
Version:
React components for building web UI in Azure DevOps
776 lines (775 loc) • 46.7 kB
JavaScript
import { __assign, __extends, __spreadArray } from "tslib";
import "../../CommonImports";
import "../../Core/core.css";
import "./Table.css";
import { ObservableLike } from '../../Core/Observable';
import * as Utils_Accessibility from '../../Core/Util/Accessibility';
import { format } from '../../Core/Util/String';
import { FocusOrMouseWithin } from '../../FocusOrMouseWithin';
import { FocusWithin } from '../../FocusWithin';
import { FocusZone, FocusZoneContext, FocusZoneDirection, FocusZoneKeyStroke } from '../../FocusZone';
import { Icon } from '../../Icon';
import { Intersection } from '../../Intersection';
import { getDefaultLinkProps } from '../../Link';
import { List, renderListCell, DropdownList } from '../../List';
import { Observer } from '../../Observer';
import * as Resources from '../../Resources.Widgets';
import { Orientation, Position, Sizer } from '../../Sizer';
import { Tooltip } from '../../TooltipEx';
import { KeyCode, css, getSafeId, getSafeIdWithSymbolConversion } from '../../Util';
import { EventDispatch } from '../../Utilities/Dispatch';
import { getTabIndex } from '../../Utilities/Focus';
import { ScreenSizeConditional } from '../../Utilities/ScreenSize';
import * as React from "react";
import { ColumnJustification, IMeasurementStyle, SortOrder, TableColumnLayout, TableColumnStyle } from "./Table.Props";
import { TableBreakpoint } from "./TableBreakpoint";
/** Id used for the ColumnFill */
export var ColumnFillId = "_fill";
/**
* ColumnFill is used to fill the remaining space in the parent element with an
* empty column. This column can be used anywhere in the column order. Columns
* that appear after this will be pushed to the right.
*/
export var ColumnFill = {
columnLayout: TableColumnLayout.none,
id: ColumnFillId,
renderCell: function (rowIndex, columnIndex) {
return (React.createElement("td", { key: "col-fill", "aria-colindex": columnIndex + 1, "aria-hidden": true, className: css("bolt-table-cell bolt-list-cell", "col-" + columnIndex), "data-column-index": columnIndex, role: "presentation" }));
},
renderHeaderCell: function (columnIndex, tableColumn) {
return (React.createElement("th", { key: "col-fill", "aria-hidden": true, className: css(tableColumn.headerClassName, "bolt-table-header-cell bolt-table-header-cell-empty", "col-header-" + columnIndex), "data-column-index": columnIndex, role: "presentation" }));
},
width: -100
};
/**
* The Table is a multi-column List component with an optional header.
*/
var Table = /** @class */ (function (_super) {
__extends(Table, _super);
function Table(props) {
var _this = _super.call(this, props) || this;
_this.currentElement = React.createRef();
// Reference to the underlying list interface.
_this.list = React.createRef();
_this.dropdownList = React.createRef();
_this.onBreakpoint = function () {
var visibleColumnsKey = _this.props.tableBreakpoints
? getVisibleColumnsAndIndices(_this.props.columns)
.map(function (_a) {
var originalIndex = _a.originalIndex;
return originalIndex;
})
.join(",")
: "";
// If any column has toggled its visibility, we have to re-render.
if (_this.state.renderInvisible || _this.state.visibleColumnsKey !== visibleColumnsKey) {
_this.setState({ renderInvisible: false, visibleColumnsKey: visibleColumnsKey });
}
};
_this.onColumnsChanged = function () {
_this.forceUpdate();
return false;
};
_this.renderHeader = function () {
var _a = _this.props, columns = _a.columns, showHeader = _a.showHeader, renderHeader = _a.renderHeader, spacerWidth = _a.spacerWidth;
var widths = [];
var proportionalTotal = 0;
// Determine the percentage for proportionally sized columns.
for (var columnIndex = 0; columnIndex < columns.length; columnIndex++) {
var width = ObservableLike.getValue(columns[columnIndex].width);
if (width < 0) {
proportionalTotal += width;
}
}
// Compute the width of all columns based on the fixed/proportional values
for (var columnIndex = 0; columnIndex < columns.length; columnIndex++) {
var width = ObservableLike.getValue(columns[columnIndex].width);
widths[columnIndex] = width >= 0 ? width : -((width / proportionalTotal) * 100);
}
var header = null;
// 0 is a valid ScreenSize, so we have strict inequality checking here
if (showHeader !== false) {
header = renderHeader ? renderHeader(columns) : React.createElement(TableHeader, { tableColumns: columns });
}
if (typeof showHeader === "function") {
header = React.createElement(ScreenSizeConditional, { condition: function (screenSize) { return showHeader(screenSize); } }, header);
}
return (React.createElement(React.Fragment, null,
React.createElement("colgroup", null,
React.createElement("col", { key: "col-group-left-spacer", style: { width: spacerWidth === 0 ? spacerWidth + "%" : spacerWidth + "px" } }),
getVisibleColumnsAndIndices(columns).map(function (_a) {
var column = _a.column, originalIndex = _a.originalIndex;
var _b = column.widthStyle, widthStyle = _b === void 0 ? IMeasurementStyle.Pixel : _b;
return (React.createElement(Observer, { key: "col-group-" + originalIndex, width: {
filter: _this.onColumnsChanged,
observableValue: column.width
} }, function () { return (React.createElement("col", { style: {
width: Math.abs(widths[originalIndex]) +
(widths[originalIndex] < 0 ? "%" : widthStyle === IMeasurementStyle.Pixel ? "px" : "rem")
} })); }));
}),
React.createElement("col", { key: "col-group-right-spacer", style: { width: spacerWidth === 0 ? spacerWidth + "%" : spacerWidth + "px" } })),
header));
};
_this.renderLoadingRow = function (rowIndex, details) {
var _a;
var _b = _this.props, columns = _b.columns, renderLoadingRow = _b.renderLoadingRow;
var rowDetails = {
ariaBusy: true,
ariaRowOffset: details.ariaRowOffset,
data: details.data,
eventDispatch: _this.state.eventDispatch,
excludeFocusZone: true,
excludeTabStop: (_a = _this.props.excludeTabStop) !== null && _a !== void 0 ? _a : details.excludeTabStop,
itemProvider: _this.props.itemProvider,
listProps: details.listProps,
onFocusItem: details.onFocusItem,
renderSpacer: _this.props.renderSpacer,
selection: _this.props.selection,
singleClickActivation: _this.props.onActivate && _this.props.singleClickActivation
};
// If a custom row loading animation is available use it.
if (renderLoadingRow) {
return renderLoadingRow(rowIndex, rowDetails);
}
return React.createElement(TableLoadingRow, { columns: columns, details: rowDetails, rowIndex: rowIndex });
};
_this.renderRow = function (rowIndex, item, details) {
var _a;
var rowDetails = {
selectableText: details.selectableText,
ariaRowOffset: details.ariaRowOffset,
eventDispatch: _this.state.eventDispatch,
data: details.data,
excludeTabStop: (_a = _this.props.excludeTabStop) !== null && _a !== void 0 ? _a : details.excludeTabStop,
itemProvider: _this.props.itemProvider,
listProps: details.listProps,
onFocusItem: details.onFocusItem,
renderSpacer: _this.props.renderSpacer,
selection: _this.props.selection,
singleClickActivation: _this.props.onActivate && _this.props.singleClickActivation,
role: _this.props.role === "tree" ? "treeitem" : undefined
};
// First determine if the item supplied a custom row rendering function, if not
// attempt to use the global row rendering function.
var renderRow = item.renderRow || _this.props.renderRow;
if (renderRow) {
return renderRow(rowIndex, item, rowDetails);
}
// If no custom row renderer is available use the default row renderer.
return (React.createElement(TableRow, { details: rowDetails, index: rowIndex, linkProps: item.linkProps }, renderColumns(rowIndex, _this.props.columns, item, rowDetails)));
};
var newListEnabled;
if (window.isNewListEnabled) {
newListEnabled = window.isNewListEnabled;
}
else {
newListEnabled = document.body.classList.contains('dropdown-list-component-enabled');
window.isNewListEnabled = newListEnabled;
}
_this.state = {
columnBehaviors: [],
eventDispatch: props.eventDispatch || new EventDispatch(),
renderInvisible: !!props.tableBreakpoints,
visibleColumnsKey: "",
tableBehaviors: [],
tableWidth: "",
newListEnabled: newListEnabled
};
// Initialize any column behaviors.
for (var columnIndex = 0; columnIndex < props.columns.length; columnIndex++) {
var tableColumn = props.columns[columnIndex];
if (tableColumn.behaviors) {
for (var _i = 0, _a = tableColumn.behaviors; _i < _a.length; _i++) {
var behavior = _a[_i];
if (behavior && behavior.initialize) {
behavior.initialize({ tableProps: props, columnIndex: columnIndex }, {}, _this.state.eventDispatch);
}
}
}
}
// Initialize the supplied behaviors.
if (props.behaviors) {
for (var _b = 0, _c = props.behaviors; _b < _c.length; _b++) {
var behavior = _c[_b];
if (behavior.initialize) {
behavior.initialize(props, _this, _this.state.eventDispatch);
}
}
}
return _this;
}
Table.getDerivedStateFromProps = function (props, state) {
var tableBehaviors = [];
var columnBehaviors = [];
// Build the set of behaviors columns have attached to them
for (var columnIndex = 0; columnIndex < props.columns.length; columnIndex++) {
var tableColumn = props.columns[columnIndex];
if (tableColumn.behaviors) {
for (var _i = 0, _a = tableColumn.behaviors; _i < _a.length; _i++) {
var behavior = _a[_i];
columnBehaviors.push(behavior);
}
}
}
// Build the set of behaviors the table has attached to it
if (props.behaviors) {
tableBehaviors.splice.apply(tableBehaviors, __spreadArray([tableBehaviors.length, 0], props.behaviors, false));
}
return {
columnBehaviors: columnBehaviors,
tableBehaviors: tableBehaviors
};
};
Table.prototype.render = function () {
var _a;
var _b = this.state, eventDispatch = _b.eventDispatch, renderInvisible = _b.renderInvisible, visibleColumnsKey = _b.visibleColumnsKey, newListEnabled = _b.newListEnabled;
var _c = this.props, selectableText = _c.selectableText, className = _c.className, columns = _c.columns, containerClassName = _c.containerClassName, enforceSingleSelect = _c.enforceSingleSelect, excludeTabStop = _c.excludeTabStop, focuszoneProps = _c.focuszoneProps, showLines = _c.showLines, id = _c.id, itemProvider = _c.itemProvider, maxHeight = _c.maxHeight, onActivate = _c.onActivate, onFocus = _c.onFocus, onSelect = _c.onSelect, pageSize = _c.pageSize, role = _c.role, rowHeight = _c.rowHeight, rowHeights = _c.rowHeights, scrollable = _c.scrollable, selection = _c.selection, singleClickActivation = _c.singleClickActivation, selectRowOnClick = _c.selectRowOnClick, showScroll = _c.showScroll, tableBreakpoints = _c.tableBreakpoints, virtualize = _c.virtualize;
var columnWidths = [];
var spacerWidth = (this.props.spacerWidth || 0) * 2;
var tableMaxWidth = spacerWidth;
var tableMinWidth = spacerWidth;
var tableWidth = spacerWidth;
var hasBoundedColumn = false;
var hasUnboundedColumn = false;
var fill = false;
var columnCount = 0;
var columnFillCount = 0;
// Compute the table size based on the current column definition. Size the sizes
// are observable, we need to recompute each render to ensure we have the
// proper values.
for (var _i = 0, columns_1 = columns; _i < columns_1.length; _i++) {
var column = columns_1[_i];
var maxWidth = column.maxWidth, minWidth = column.minWidth, _d = column.widthStyle, widthStyle = _d === void 0 ? IMeasurementStyle.Pixel : _d;
var width = ObservableLike.getValue(column.width);
// Add the column width the set of available columnWidths.
columnWidths.push(column.width);
if (width < 0) {
// This is a variable width columnn so we will fill the container.
fill = true;
// Update the min/max width of the table based on the supplied value.
// We use a minumum width of 100px if one isnt specified.
tableMinWidth += minWidth ? minWidth : 0;
// If all variable width columns have a maxWidth, let the table fill the available
// space, but set the table's max width to the sum of the column widths/maxWidths.
if (maxWidth) {
hasBoundedColumn = true;
tableMaxWidth += maxWidth;
}
else {
// If there are any variable width columns without maxWidth, let the table
// fill the available space with no maxWidth.
hasUnboundedColumn = true;
}
columnCount++;
}
else if (width > 0) {
if (widthStyle === IMeasurementStyle.Pixel) {
tableWidth += width;
tableMinWidth += width;
tableMaxWidth += width;
}
else {
// @NOTE: For now we are going to estimate a rem = 16px which is the default.
// We could attempt to measure this if an exact measurement is really important.
tableWidth += width * 16;
tableMinWidth += width * 16;
tableMaxWidth += width * 16;
}
columnCount++;
}
if (column.id === "_fill") {
columnFillCount++;
}
}
var listProps = {
selectableText: selectableText,
ariaColumnCount: columnCount - columnFillCount,
ariaLabel: this.props.ariaLabel,
ariaRowOffset: this.props.showHeader ? 1 : 0,
className: css(className, "bolt-table", showLines && "bolt-table-show-lines"),
columnCount: columnCount + 2,
enforceSingleSelect: enforceSingleSelect,
eventDispatch: eventDispatch,
excludeTabStop: excludeTabStop,
focuszoneProps: focuszoneProps,
id: id,
itemProvider: itemProvider,
maxWidth: hasBoundedColumn && !hasUnboundedColumn ? tableMaxWidth + "px" : undefined,
maxHeight: maxHeight,
minWidth: tableMinWidth !== tableWidth ? tableMinWidth + "px" : undefined,
onActivate: onActivate,
onFocus: onFocus,
onSelect: onSelect,
pageSize: pageSize,
renderHeader: this.renderHeader,
renderLoadingRow: this.renderLoadingRow,
renderRow: this.renderRow,
role: role,
rowHeight: rowHeight,
rowHeights: rowHeights,
selection: selection,
selectRowOnClick: selectRowOnClick,
singleClickActivation: singleClickActivation,
showScroll: showScroll,
virtualize: virtualize,
width: fill ? "100%" : tableWidth + "px"
};
var firstActionableHeaderIndex = getActionableIndex(columns);
if (firstActionableHeaderIndex >= 0) {
// If the header is tabbable, the rows do not need to be since they are
// in the focus zone.
listProps.defaultTabbableRow = -1;
}
var table = (React.createElement("div", { className: css(containerClassName, "bolt-table-container flex-grow", renderInvisible && "invisible", scrollable && "v-scroll-auto", tableBreakpoints && "h-scroll-hidden"), ref: this.currentElement },
tableBreakpoints ? (React.createElement(TableBreakpoint, { columnWidths: columnWidths, onBreakpoint: this.onBreakpoint, breakpoints: tableBreakpoints })) : undefined,
newListEnabled && listProps.role === "listbox" && !((_a = listProps.className) === null || _a === void 0 ? void 0 : _a.includes("bolt-list-box-tree"))
? React.createElement(DropdownList, __assign({}, listProps, { key: visibleColumnsKey, ref: this.dropdownList, renderHeader: function () { return React.createElement(React.Fragment, null); } }))
: React.createElement(List, __assign({}, listProps, { key: visibleColumnsKey, ref: this.list }))));
if (scrollable) {
table = React.createElement(Intersection, null, table);
}
return table;
};
Table.prototype.componentDidMount = function () {
// Mount any of the attached tableBehaviors.
for (var _i = 0, _a = this.state.tableBehaviors; _i < _a.length; _i++) {
var behavior = _a[_i];
if (behavior.componentDidMount) {
behavior.componentDidMount(this.props);
}
}
for (var _b = 0, _c = this.state.columnBehaviors; _b < _c.length; _b++) {
var behavior = _c[_b];
if (behavior.componentDidMount) {
behavior.componentDidMount({ tableProps: this.props });
}
}
};
Table.prototype.componentDidUpdate = function () {
// Update any of the attached tableBehaviors.
for (var _i = 0, _a = this.state.tableBehaviors; _i < _a.length; _i++) {
var behavior = _a[_i];
if (behavior.componentDidUpdate) {
behavior.componentDidUpdate(this.props);
}
}
for (var _b = 0, _c = this.state.columnBehaviors; _b < _c.length; _b++) {
var behavior = _c[_b];
if (behavior.componentDidUpdate) {
behavior.componentDidUpdate({ tableProps: this.props });
}
}
};
Table.prototype.componentWillUnmount = function () {
// Unmount any of the attached tableBehaviors.
for (var _i = 0, _a = this.state.tableBehaviors; _i < _a.length; _i++) {
var behavior = _a[_i];
if (behavior.componentWillUnmount) {
behavior.componentWillUnmount();
}
}
for (var _b = 0, _c = this.state.columnBehaviors; _b < _c.length; _b++) {
var behavior = _c[_b];
if (behavior.componentDidUpdate) {
behavior.componentDidUpdate({ tableProps: this.props });
}
}
};
Table.prototype.addOverlay = function (id, rowIndex, render, zIndex, columnIndex) {
if (zIndex === void 0) { zIndex = 0; }
if (this.list.current) {
return this.list.current.addOverlay(id, rowIndex, render, zIndex, columnIndex);
}
};
Table.prototype.removeOverlay = function (id) {
if (this.list.current) {
return this.list.current.removeOverlay(id);
}
};
Table.prototype.focusRow = function (rowIndex, direction) {
if (direction === void 0) { direction = 1; }
if (this.list.current) {
return this.list.current.focusRow(rowIndex, direction);
}
else {
return Promise.resolve();
}
};
Table.prototype.getFocusIndex = function () {
if (this.list.current) {
return this.list.current.getFocusIndex();
}
return -1;
};
Table.prototype.getStats = function () {
if (this.list.current) {
return this.list.current.getStats();
}
return {
firstMaterialized: -1,
firstRendered: -1,
lastMaterialized: -1,
lastRendered: -1
};
};
Table.prototype.scrollIntoView = function (rowIndex, options) {
if (this.list.current) {
return this.list.current.scrollIntoView(rowIndex, options);
}
};
Table.defaultProps = {
role: "grid",
selectRowOnClick: true,
showHeader: true,
showLines: true,
singleClickActivation: true,
spacerWidth: 8
};
return Table;
}(React.Component));
export { Table };
export function renderColumns(rowIndex, columns, item, details) {
return getVisibleColumnsAndIndices(columns).map(function (_a, columnIndex) {
var column = _a.column;
return column.renderCell(rowIndex, columnIndex, column, item, rowIndex + (details.ariaRowOffset ? details.ariaRowOffset : 1), details.role);
});
}
function getVisibleColumnsAndIndices(columns) {
return columns.map(function (column, index) { return ({ column: column, originalIndex: index }); }).filter(function (_a) {
var column = _a.column;
return ObservableLike.getValue(column.width);
});
}
function getActionableIndex(columns) {
return columns.findIndex(function (column) { return ObservableLike.getValue(column.width) !== 0 && ((column.behaviors && column.behaviors.length > 0) || !!column.sortProps); });
}
var TableHeader = /** @class */ (function (_super) {
__extends(TableHeader, _super);
function TableHeader() {
return _super !== null && _super.apply(this, arguments) || this;
}
TableHeader.prototype.render = function () {
var _this = this;
var firstActionableIndex = getActionableIndex(this.props.tableColumns);
return (React.createElement(FocusZoneContext.Consumer, null, function (rowContext) {
return (React.createElement(FocusZone, { direction: FocusZoneDirection.Horizontal },
React.createElement("thead", null,
React.createElement(FocusWithin, null, function (focusStatus) {
return (React.createElement("tr", { "aria-rowindex": 1, className: css("bolt-table-header-row", focusStatus.hasFocus && "focused"), "data-row-index": -1, onBlur: focusStatus.onBlur, onFocus: focusStatus.onFocus, role: "row" },
React.createElement("th", { "aria-hidden": "true", key: "left-spacer", role: "presentation", className: "bolt-table-header-border" }),
getVisibleColumnsAndIndices(_this.props.tableColumns).map(function (_a, columnIndex) {
var column = _a.column, originalIndex = _a.originalIndex;
if (column.renderHeaderCell) {
return column.renderHeaderCell(columnIndex, column, rowContext.focuszoneId, originalIndex === firstActionableIndex);
}
else if (column.iconProps || column.name) {
return (React.createElement(TableHeaderCell, { key: "col-header-" + columnIndex, ariaLabel: column.ariaLabel || column.name, column: column, columnIndex: columnIndex, focuszoneId: rowContext.focuszoneId, isFirstActionableHeader: originalIndex === firstActionableIndex },
React.createElement(Tooltip, { overflowOnly: true, text: column.name },
React.createElement("div", { className: "bolt-table-header-cell-text text-ellipsis body-s" },
column.iconProps && Icon(column.iconProps),
React.createElement("span", { className: css(column.headerTitleClassName, column.required && "bolt-table-header-cell-text--required") }, column.name)))));
}
else {
return (React.createElement("th", { "aria-colindex": columnIndex + 1, "aria-label": column.ariaLabel || Resources.EmptyColumnHeaderLabel, "aria-readonly": column.readonly !== undefined ? column.readonly : "true", className: "bolt-table-header-border", key: "col-header-" + columnIndex }));
}
}),
React.createElement("th", { "aria-hidden": "true", key: "right-spacer", role: "presentation", className: "bolt-table-header-border" })));
}))));
}));
};
return TableHeader;
}(React.Component));
var boltTableHeaderCellCount = 0;
var TableHeaderCell = /** @class */ (function (_super) {
__extends(TableHeaderCell, _super);
function TableHeaderCell(props) {
var _this = _super.call(this, props) || this;
_this.element = React.createRef();
_this.state = { measuredWidth: 0, isFocused: false };
_this.onSize = function (event, updatedSize) {
var column = _this.props.column;
// Ensure we havent had our column definition updated and onSize removed.
if (column.onSize) {
column.onSize(event, _this.props.columnIndex, updatedSize, column);
}
};
_this.headerCellId = boltTableHeaderCellCount++;
return _this;
}
TableHeaderCell.prototype.render = function () {
var _this = this;
var _a = this.props, ariaLabel = _a.ariaLabel, column = _a.column, columnIndex = _a.columnIndex, focuszoneId = _a.focuszoneId, isFirstActionableHeader = _a.isFirstActionableHeader;
var sizer;
if (column.onSize) {
var showDivider = !!column.showSizerDivider;
sizer = (React.createElement(Observer, { width: column.width }, function (props) {
// If we are sizable we will either use the supplied width (desired), or the
// measured width if we are a proportional column.
var width = props.width;
if (width < 0) {
width = _this.state.measuredWidth;
}
return (React.createElement(Sizer, { className: "bolt-table-header-sizer", divider: showDivider, keyboardStepMultiplier: 24, maxSize: column.maxWidth, minSize: column.minWidth, onSize: _this.onSize, onSizeEnd: _this.props.column.onSizeEnd, orientation: Orientation.row, position: Position.near, size: width, tabIndex: _this.state.isFocused ? 0 : -1, getFocusedElement: function (e) { return _this.element; } }));
}));
}
return (React.createElement(FocusZoneContext.Consumer, null, function (cellContext) {
var actionable = (column.behaviors && column.behaviors.length > 0) || !!column.sortProps;
var _a = column.sortProps, sortProps = _a === void 0 ? {} : _a;
var sortIcon = column.sortProps && sortProps.sortOrder !== undefined
? Icon({
className: "bolt-table-header-sort-icon body-s",
iconName: sortProps.sortOrder === SortOrder.ascending ? "SortUp" : "SortDown"
})
: null;
var justificationClassName;
if (column.justification === ColumnJustification.Left) {
justificationClassName = "justify-start";
}
else if (column.justification === ColumnJustification.Right) {
justificationClassName = "justify-end";
}
var colIndex = columnIndex + 1;
var childId = getSafeId("th-col-content-" + _this.headerCellId);
return (React.createElement("th", { role: "columnheader", "aria-colindex": colIndex, "aria-label": ariaLabel, "aria-labelledby": !ariaLabel ? childId : undefined, "aria-readonly": "true", "aria-sort": sortProps.sortOrder !== undefined
? sortProps.sortOrder === SortOrder.ascending
? "ascending"
: "descending"
: undefined, className: css(column.headerClassName, "bolt-table-header-cell", "col-header-" + columnIndex, actionable && "bolt-table-header-cell-actionable"), "data-column-index": columnIndex, "data-focuszone": actionable && css(isFirstActionableHeader && focuszoneId, cellContext.focuszoneId), ref: _this.element, onFocus: function () { return _this.setState({ isFocused: true }); }, onBlur: function () { return _this.setState({ isFocused: false }); }, tabIndex: actionable || sizer ? 0 : -1 },
React.createElement("div", { className: css("bolt-table-header-cell-content flex-row", justificationClassName) },
column.justification === ColumnJustification.Right && sortIcon,
React.createElement("div", { id: childId, className: "scroll-hidden" }, _this.props.children),
column.justification !== ColumnJustification.Right && sortIcon,
React.createElement("div", { "aria-hidden": !_this.state.isFocused }, sizer))));
}));
};
TableHeaderCell.prototype.componentDidMount = function () {
this.updateMeasuredWidth();
};
TableHeaderCell.prototype.componentDidUpdate = function () {
this.updateMeasuredWidth();
};
TableHeaderCell.prototype.updateMeasuredWidth = function () {
var column = this.props.column;
if (column.onSize && this.element.current && ObservableLike.getValue(column.width) < 0) {
var measuredWidth = this.element.current.getBoundingClientRect().width;
if (measuredWidth !== this.state.measuredWidth) {
this.setState({ measuredWidth: measuredWidth });
}
}
};
return TableHeaderCell;
}(React.Component));
export { TableHeaderCell };
export function TableRow(props) {
var onFocus = function (event) {
var _a;
props.details.onFocusItem(props.index, event);
var rowNumber = (_a = props.details.ariaPosInSet) !== null && _a !== void 0 ? _a : props.index + ariaRowOffset;
if (props.details.ariaSetSize) {
if (props.linkProps) {
Utils_Accessibility.announce(format(Resources.ClickableRowAnnouncementWithSize, rowNumber, props.details.ariaSetSize), true);
}
else if (role == "option") {
Utils_Accessibility.announce(Resources.ListItem, true);
}
else {
Utils_Accessibility.announce(format(Resources.RowAnnouncementWithSize, rowNumber, props.details.ariaSetSize), true);
}
}
else {
if (props.linkProps) {
Utils_Accessibility.announce(format(Resources.ClickableRowAnnouncement, rowNumber), true);
}
else {
Utils_Accessibility.announce(format(Resources.RowAnnouncement, rowNumber), true);
}
}
};
var postprocessKeyStroke = function (event) {
var nodeName = event.target.nodeName;
if (!event.defaultPrevented && nodeName !== "INPUT" && nodeName !== "TEXTAREA") {
if (event.which === KeyCode.leftArrow && rowElement.current) {
rowElement.current.focus();
event.preventDefault();
}
}
return FocusZoneKeyStroke.IgnoreNone;
};
var rowElement = React.useState(function () { return React.createRef(); })[0];
var details = props.details, index = props.index, linkProps = props.linkProps;
var selectableText = details.selectableText, ariaLabel = details.ariaLabel, ariaBusy = details.ariaBusy, ariaDescribedBy = details.ariaDescribedBy, ariaPosInSet = details.ariaPosInSet, ariaRowOffset = details.ariaRowOffset, ariaSetSize = details.ariaSetSize, excludeFocusZone = details.excludeFocusZone, id = details.id, renderSpacer = details.renderSpacer, role = details.role, selection = details.selection, singleClickActivation = details.singleClickActivation;
var ariaChecked;
var ariaSelected;
if (role === "menuitemcheckbox") {
ariaChecked = selection && selection.selected(index);
}
else {
ariaSelected = selection && selection.selected(index);
}
var rowElem = (React.createElement(FocusOrMouseWithin, { onFocus: onFocus }, function (focusOrMouseWithinStatus) {
return (React.createElement(FocusZoneContext.Consumer, null, function (rowContext) {
var _a, _b, _c;
var rowProps = {
"aria-busy": ariaBusy,
"aria-checked": ariaChecked,
"aria-describedby": ariaDescribedBy,
"aria-label": ariaLabel,
"aria-rowindex": role === "menuitemcheckbox" || role === "option" || role === "presentation" ? undefined : index + ariaRowOffset,
"aria-posinset": ariaPosInSet === null ? undefined : ariaPosInSet,
"aria-selected": role === "presentation" ? undefined : ariaSelected,
"aria-setsize": ariaSetSize === null ? undefined : ariaSetSize,
className: css(props.className, "bolt-table-row bolt-list-row", index === 0 && "first-row", focusOrMouseWithinStatus.hasFocus && "focused", selection && selection.selected(index) && "selected", singleClickActivation && "single-click-activation", linkProps && "v-align-middle", selectableText && "selectable-text", document.body.classList.contains('dropdown-list-component-enabled') &&
((_a = props === null || props === void 0 ? void 0 : props.className) === null || _a === void 0 ? void 0 : _a.includes("bolt-list-box-row")) && "dropdown-list"),
"data-focuszone": excludeFocusZone || (selection && !selection.selectable(index)) ? undefined : rowContext.focuszoneId,
"data-row-index": props.index,
id: getSafeIdWithSymbolConversion(id),
role: role || "row",
tabIndex: getTabIndex(details),
onBlur: focusOrMouseWithinStatus.onBlur,
onFocus: focusOrMouseWithinStatus.onFocus,
onMouseEnter: focusOrMouseWithinStatus.onMouseEnter,
onMouseLeave: focusOrMouseWithinStatus.onMouseLeave,
ref: rowElement
};
var rowChildren = [];
if ((_b = rowProps === null || rowProps === void 0 ? void 0 : rowProps.className) === null || _b === void 0 ? void 0 : _b.includes("dropdown-list")) {
rowChildren = [props.children];
}
else {
rowChildren = [
React.createElement("td", { "aria-hidden": "true", key: "left-spacer", className: "bolt-table-cell-compact bolt-table-cell bolt-list-cell bolt-table-spacer-cell", role: "presentation" }, renderSpacer && renderSpacer(index, true)),
props.children,
React.createElement("td", { "aria-hidden": "true", key: "right-spacer", className: "bolt-table-cell-compact bolt-table-cell bolt-list-cell bolt-table-spacer-cell", role: "presentation" }, renderSpacer && renderSpacer(index, false))
];
}
return (React.createElement(FocusZone, { direction: FocusZoneDirection.Horizontal, postprocessKeyStroke: postprocessKeyStroke }, linkProps ? (React.createElement("a", __assign({}, getDefaultLinkProps(linkProps), rowProps), rowChildren)) : (((_c = rowProps === null || rowProps === void 0 ? void 0 : rowProps.className) === null || _c === void 0 ? void 0 : _c.includes("dropdown-list")) ? (React.createElement("li", __assign({}, rowProps), rowChildren)) : (React.createElement("tr", __assign({}, rowProps), rowChildren)))));
}));
}));
if (details.tooltipProps) {
return React.createElement(Tooltip, __assign({}, details.tooltipProps), rowElem);
}
return rowElem;
}
export function TableLoadingRow(props) {
return (
// Return the default row loading animation.
React.createElement(TableRow, { className: "bolt-list-row-loading", details: props.details, index: props.rowIndex }, getVisibleColumnsAndIndices(props.columns).map(function (_a, columnIndex) {
var column = _a.column;
return SimpleTableCell({ columnIndex: columnIndex, children: renderLoadingCell(column.columnLayout) });
})));
}
export function TableCell(props) {
var _a, _b, _c;
var ariaLabel = props.ariaLabel, ariaRowIndex = props.ariaRowIndex, className = props.className, colspan = props.colspan, columnIndex = props.columnIndex, role = props.role, tableColumn = props.tableColumn;
var justificationClassName;
if (tableColumn) {
if (tableColumn.justification === ColumnJustification.Left) {
justificationClassName = "justify-cell-start";
}
else if (tableColumn.justification === ColumnJustification.Right) {
justificationClassName = "justify-cell-end";
}
}
return (((_b = (_a = props.tableColumn) === null || _a === void 0 ? void 0 : _a.className) === null || _b === void 0 ? void 0 : _b.includes("dropdown-list")) || ((_c = props.className) === null || _c === void 0 ? void 0 : _c.includes("dropdown-list")) ? (React.createElement("span", { "aria-colindex": role === "presentation" || role === "treeitem" ? undefined : columnIndex + 1, "aria-label": role === "presentation" || role === "treeitem" ? undefined : ariaLabel, "aria-readonly": role === "presentation" || role === "treeitem" ? undefined : tableColumn && tableColumn.readonly, "aria-rowindex": role === "presentation" || role === "treeitem" ? undefined : ariaRowIndex, className: css(className, tableColumn && tableColumn.className, "bolt-table-cell bolt-list-cell", justificationClassName), "data-column-index": columnIndex, key: "col-" + columnIndex, role: role === "treeitem" ? "presentation" : role || "gridcell" }, props.children)) : (React.createElement("td", { "aria-colindex": role === "presentation" || role === "treeitem" ? undefined : columnIndex + 1, "aria-label": role === "presentation" || role === "treeitem" ? undefined : ariaLabel, "aria-readonly": role === "presentation" || role === "treeitem" ? undefined : tableColumn && tableColumn.readonly, "aria-rowindex": role === "presentation" || role === "treeitem" ? undefined : ariaRowIndex, className: css(className, tableColumn && tableColumn.className, "bolt-table-cell bolt-list-cell", justificationClassName), colSpan: colspan, "data-column-index": columnIndex, key: "col-" + columnIndex, role: role === "treeitem" ? "presentation" : role || "gridcell" }, props.children)));
}
export function SimpleTableCell(props) {
var children = React.createElement("div", { className: css(props.contentClassName, "bolt-table-cell-content flex-row flex-center") }, props.children);
return TableCell({
ariaLabel: props.ariaLabel,
ariaRowIndex: props.ariaRowIndex,
children: children,
className: props.className,
colspan: props.colspan,
columnIndex: props.columnIndex,
role: props.role,
tableColumn: props.tableColumn
});
}
export function TwoLineTableCell(props) {
var rowClasses = "bolt-table-two-line-cell-item flex-row scroll-hidden";
var line1 = (React.createElement("div", { className: rowClasses },
props.line1,
props.trailingLine1IconProps &&
Icon(__assign(__assign({}, props.trailingLine1IconProps), { className: css(props.trailingLine1IconProps.className, "flex-noshrink") }))));
var line2 = React.createElement("div", { className: rowClasses }, props.line2);
var lines = (React.createElement(React.Fragment, null,
line1,
line2));
var children;
if (props.iconProps) {
children = (React.createElement("div", { className: css(props.className, "bolt-table-cell-content flex-row flex-center") },
Icon(__assign(__assign({}, props.iconProps), { className: css(props.iconProps.className, "bolt-table-two-line-cell-icon flex-noshrink") })),
React.createElement("div", { className: "flex-column scroll-hidden" }, lines),
props.trailingIconProps &&
Icon(__assign(__assign({}, props.trailingIconProps), { className: css(props.trailingIconProps.className, "bolt-table-two-line-cell-icon flex-noshrink") }))));
}
else {
children = (React.createElement("div", { className: css(props.className, "bolt-table-cell-content flex-column") },
lines,
props.trailingIconProps &&
Icon(__assign(__assign({}, props.trailingIconProps), { className: css(props.trailingIconProps.className, "bolt-table-two-line-cell-icon flex-noshrink") }))));
}
return TableCell({
ariaRowIndex: props.ariaRowIndex,
children: children,
colspan: props.colspan,
columnIndex: props.columnIndex,
className: "bolt-table-two-line-cell",
tableColumn: props.tableColumn
});
}
export function renderEmptyCell(rowIndex, columnIndex) {
return React.createElement(TableCell, { columnIndex: columnIndex, key: columnIndex, role: "presentation" });
}
/**
* A basic cell renderer that works well for most simple columns. Gets the value of the
* the {column.id} property in the given table item and displays it as a string
*
* @param rowIndex Index of the row being rendered
* @param columnIndex Index of the column being rendered
* @param tableColumn Column definition
* @param tableItem The data item being rendered for the current row
*/
export function renderSimpleCell(rowIndex, columnIndex, tableColumn, tableItem, ariaRowIndex) {
return renderSimpleCellValue(columnIndex, tableColumn, tableItem[tableColumn.id], ariaRowIndex);
}
/**
* Renders a simple table cell value
*
* @param columnIndex Index of the column being rendered
* @param tableColumn Column definition
* @param tableCell Simple value to render as text
*/
export function renderSimpleCellValue(columnIndex, tableColumn, tableCell, ariaRowIndex) {
var columnStyle = tableColumn.columnStyle;
// Do not include padding if the table cell has an href
var hasLink = !!(tableCell && typeof tableCell !== "string" && typeof tableCell !== "number" && tableCell.href);
return (React.createElement(SimpleTableCell, { ariaRowIndex: ariaRowIndex, className: css(columnStyle === TableColumnStyle.Primary && "bolt-table-cell-primary", columnStyle === TableColumnStyle.Secondary && "bolt-table-cell-secondary", columnStyle === TableColumnStyle.Tertiary && "bolt-table-cell-tertiary"), columnIndex: columnIndex, contentClassName: hasLink ? "bolt-table-cell-content-with-link" : undefined, key: columnIndex, tableColumn: tableColumn }, tableCell && renderListCell(tableCell)));
}
function getVariableLength() {
return Math.random() * 80 + 20 + "%";
}
export function renderLoadingCell(columnLayout) {
if (columnLayout === TableColumnLayout.singleLine || columnLayout === undefined) {
return (React.createElement("div", { className: "shimmer shimmer-line", style: { width: getVariableLength() } }, "\u00A0"));
}
else if (columnLayout === TableColumnLayout.singleLinePrefix) {
return (React.createElement(React.Fragment, null,
React.createElement("div", { className: "shimmer shimmer-circle-small flex-noshrink" }),
React.createElement("div", { className: "shimmer shimmer-line", style: { width: getVariableLength() } }, "\u00A0")));
}
else if (columnLayout === TableColumnLayout.twoLine) {
return (React.createElement("div", { className: "flex-column flex-grow" },
React.createElement("div", { className: "bolt-table-two-line-cell-item shimmer shimmer-line", style: { width: getVariableLength() } }, "\u00A0"),
React.createElement("div", { className: "bolt-table-two-line-cell-item shimmer shimmer-line", style: { width: getVariableLength() } }, "\u00A0")));
}
else if (columnLayout === TableColumnLayout.twoLinePrefix) {
return (React.createElement(React.Fragment, null,
React.createElement("div", { className: "shimmer shimmer-circle-large flex-noshrink" }),
React.createElement("div", { className: "flex-column flex-grow" },
React.createElement("div", { className: "bolt-table-two-line-cell-item shimmer shimmer-line", style: { width: getVariableLength() } }, "\u00A0"),
React.createElement("div", { className: "bolt-table-two-line-cell-item shimmer shimmer-line", style: { width: getVariableLength() } }, "\u00A0"))));
}
return null;
}