azure-devops-ui
Version:
React components for building web UI in Azure DevOps
192 lines (191 loc) • 10.1 kB
JavaScript
import "../../CommonImports";
import "../../Core/core.css";
import "./Table.css";
import * as React from "react";
import { ObservableValue } from '../../Core/Observable';
import { announce } from '../../Core/Util/Accessibility';
import { Checkbox, TriStateCheckbox } from '../../Checkbox';
import { Observer } from '../../Observer';
import * as Resources from '../../Resources.Widgets';
import { css, KeyCode, preventDefault } from '../../Util';
import { IMeasurementStyle, TableColumnLayout } from "./Table.Props";
/**
* ColumnSelect is used to render additional selection ui for a given row.
*
* This renders a fixed width column that contains a checkbox in the header
* as well as each row that represents the selection state of the row. It
* also allows the user to change the selection state. The header checkbox
* is used to set the state of all rows in the table to the same state.
*
*/
export class ColumnSelect {
constructor(props) {
this.isListbox = false;
this.columnSelectBehavior = {
initialize: (props) => {
this.ariaHidden = props.tableProps.role === "menu";
this.itemProvider = props.tableProps.itemProvider;
this.selection = props.tableProps.selection;
this.onSelect = props.tableProps.onSelect;
this.isListbox = props.tableProps.role === "listbox";
},
componentDidMount: (props) => {
this.componentDidMount(props);
},
componentDidUpdate: (props) => {
this.componentDidUpdate(props);
},
componentWillUnmount: () => {
this.componentWillUnmount();
}
};
this.allSelected = new ObservableValue(false);
this.columnLayout = TableColumnLayout.none;
this.id = "_select";
this.width = 2.5;
this.widthStyle = IMeasurementStyle.REM;
this.behaviors = [this.columnSelectBehavior];
this.renderCell = (rowIndex, columnIndex, listColumn) => {
var _a, _b, _c, _d;
const { selection } = this;
const selected = selection && selection.selected(rowIndex);
const selectable = selection && selection.selectable(rowIndex);
return (this.isListbox ? (React.createElement("div", { "aria-hidden": this.ariaHidden, className: css("bolt-table-cell-select bolt-table-cell bolt-list-cell flex-noshrink", "col-" + columnIndex), "data-column-index": columnIndex, key: "col-select", onClick: preventDefault, onDoubleClick: preventDefault, onMouseDown: event => {
this.updateSelection(event, rowIndex);
event.preventDefault();
}, onKeyDown: event => {
if (event.which === KeyCode.space) {
this.updateSelection(event, rowIndex);
event.preventDefault();
}
} },
React.createElement("div", { className: "flex-row justify-center" },
React.createElement("span", { className: "flex-row-inline" },
React.createElement(Checkbox, { ariaLabel: ((_a = this.props) === null || _a === void 0 ? void 0 : _a.role) === "presentation" ? undefined : Resources.SelectRowLabel, checked: !!selected, excludeFocusZone: (_b = this.props) === null || _b === void 0 ? void 0 : _b.excludeFocusZone, excludeTabStop: true, disabled: !selectable }))))) : (React.createElement("td", { "aria-colindex": columnIndex + 1, "aria-hidden": this.ariaHidden, className: css("bolt-table-cell-select bolt-table-cell bolt-list-cell", "col-" + columnIndex), "data-column-index": columnIndex, key: "col-select", onClick: preventDefault, onDoubleClick: preventDefault, onMouseDown: event => {
this.updateSelection(event, rowIndex);
event.preventDefault();
}, onKeyDown: event => {
if (event.which === KeyCode.space) {
this.updateSelection(event, rowIndex);
event.preventDefault();
}
} },
React.createElement("div", { className: "flex-row justify-center" },
React.createElement("span", { className: "flex-row-inline" },
React.createElement(Checkbox, { ariaLabel: ((_c = this.props) === null || _c === void 0 ? void 0 : _c.role) === "presentation" ? undefined : Resources.SelectRowLabel, checked: !!selected, excludeFocusZone: (_d = this.props) === null || _d === void 0 ? void 0 : _d.excludeFocusZone, excludeTabStop: true, disabled: !selectable }))))));
};
this.renderHeaderCell = (columnIndex, listColumn, focuszoneId) => {
return (React.createElement("th", { "aria-colindex": columnIndex + 1, className: css("bolt-table-cell-select bolt-table-header-cell", "col-header-" + columnIndex), "data-column-index": columnIndex, key: "col-select" },
React.createElement("div", { className: "flex-row" },
React.createElement(Observer, { allSelected: this.allSelected }, (props) => {
var _a, _b;
const { itemProvider, selection } = this;
// Get the total number of items within the list.
const itemCount = itemProvider && itemProvider.length;
return selection && selection.multiSelect && itemCount !== -1 ? (React.createElement("div", { className: "flex-row flex-grow justify-center" },
React.createElement(TriStateCheckbox, { ariaLabel: (_a = listColumn.ariaLabel) !== null && _a !== void 0 ? _a : Resources.SelectAllRowsLabel, checked: props.allSelected, focuszoneId: focuszoneId, onChange: this.onChangeHeader }))) : (React.createElement("div", { className: "visually-hidden" }, (_b = listColumn.ariaLabel) !== null && _b !== void 0 ? _b : Resources.SelectionColumnLabel));
}))));
};
this.onChangeHeader = (event) => {
const { itemProvider, onSelect, selection } = this;
// toggle select all
if (selection) {
if (this.allSelected.value !== false) {
selection.clear();
announce(Resources.AllRowsUnselectedMessage, true);
}
else {
selection.select(0, itemProvider && itemProvider.length);
announce(Resources.AllRowsSelectedMessage, true);
}
}
if (onSelect && itemProvider) {
for (let i = 0; i < itemProvider.length; i++) {
onSelect(event, this.getListRow(i));
}
}
};
this.onSelectionChange = () => {
const { itemProvider, selection } = this;
if (selection) {
const selectedCount = selection.selectedCount;
const itemCount = itemProvider && itemProvider.length - selection.unselectableCount;
if (selectedCount > 0) {
if (selectedCount === itemCount) {
this.allSelected.value = true;
}
else {
this.allSelected.value = undefined;
}
}
else {
this.allSelected.value = false;
}
}
};
this.updateSelection = (event, rowIndex) => {
const { onSelect, selection } = this;
if (selection) {
if (selection.selected(rowIndex)) {
selection.unselect(rowIndex);
}
else {
selection.select(rowIndex, 1, true);
}
}
if (onSelect) {
const listRow = this.getListRow(rowIndex);
onSelect(event, listRow);
}
};
this.getListRow = (rowIndex) => {
return { data: this.itemProvider ? this.itemProvider.value[rowIndex] : {}, index: rowIndex };
};
this.props = props;
}
componentDidMount(props) {
const { itemProvider, selection } = this;
// We need to know about changes to the selection to manage the selectAll state.
if (selection) {
selection.subscribe(this.onSelectionChange);
this.onSelectionChange();
}
if (itemProvider && itemProvider.subscribe) {
itemProvider.subscribe(this.onSelectionChange);
}
}
componentDidUpdate(props) {
let { selection } = this;
if (selection !== props.tableProps.selection) {
if (selection) {
selection.unsubscribe(this.onSelectionChange);
}
selection = props.tableProps.selection;
this.selection = selection;
if (selection) {
selection.subscribe(this.onSelectionChange);
}
}
if (selection) {
this.onSelectionChange();
}
if (this.itemProvider !== props.tableProps.itemProvider) {
if (this.itemProvider && this.itemProvider.unsubscribe) {
this.itemProvider.unsubscribe(this.onSelectionChange);
}
this.itemProvider = props.tableProps.itemProvider;
if (this.itemProvider && this.itemProvider.subscribe) {
this.itemProvider.subscribe(this.onSelectionChange);
}
}
}
componentWillUnmount() {
const { selection } = this;
if (selection) {
selection.unsubscribe(this.onSelectionChange);
}
if (this.itemProvider && this.itemProvider.unsubscribe) {
this.itemProvider.unsubscribe(this.onSelectionChange);
}
}
}