azure-devops-ui
Version:
React components for building web UI in Azure DevOps
116 lines (115 loc) • 4.96 kB
JavaScript
import { cellFromEvent } from '../../List';
import { KeyCode } from '../../Util';
import { SortOrder } from "./Table.Props";
/**
* The ColumnSorting class is a behavior that can be used with the Table
* component to provide column sorting. To use the ColumnSorting, create an
* instance passing the sorting delegate to the constructor. Then supply
* the created behavior to the table.
*/
export class ColumnSorting {
constructor(onSort) {
this.initialize = (props, table, eventDispatch) => {
this.props = props;
this.eventDispatch = eventDispatch;
eventDispatch.addEventListener("click", this.onClick);
eventDispatch.addEventListener("keydown", this.onKeyDown);
};
this.componentDidMount = (props) => {
this.props = props;
};
this.componentDidUpdate = (props) => {
this.props = props;
};
this.componentWillUnmount = () => {
if (this.eventDispatch) {
this.eventDispatch.removeEventListener("click", this.onClick);
this.eventDispatch.removeEventListener("keydown", this.onKeyDown);
}
};
this.onClick = (event) => {
if (!event.defaultPrevented) {
this.processSortEvent(event, true);
}
};
this.onKeyDown = (event) => {
if (!event.defaultPrevented) {
if (event.which === KeyCode.enter || event.which === KeyCode.space) {
this.processSortEvent(event);
}
}
};
this.onSort = onSort;
}
processSortEvent(event, click) {
if (document.body.getAttribute('data-resize-active') === 'true' ||
document.body.getAttribute('data-resize-active') === 'false') {
return;
}
const clickedCell = cellFromEvent(event);
// Check if we clicked on an actionable area; walk up to see if we clicked within one
let testHtmlElement = event.target;
let actionableClick = click ? false : testHtmlElement.classList.contains("bolt-table-header-cell-actionable");
if (testHtmlElement.classList.contains("bolt-table-header-sizer")) {
return;
}
while (!actionableClick && testHtmlElement !== clickedCell.cellElement) {
if (testHtmlElement.parentElement) {
testHtmlElement = testHtmlElement.parentElement;
}
else {
break;
}
actionableClick = testHtmlElement.classList.contains("bolt-table-header-cell-actionable");
}
if (clickedCell.rowIndex === -1 && actionableClick) {
const column = this.props.columns[clickedCell.cellIndex];
// If the column is currently sorted ascending then we need to invert the sort.
if (column && column.sortProps) {
const newSortOrder = column.sortProps.sortOrder === SortOrder.ascending ? SortOrder.descending : SortOrder.ascending;
this.onSort(clickedCell.cellIndex, newSortOrder, event);
event.preventDefault();
}
}
}
}
/**
* sortItems is a helper method that works with the ColumnSorting and a Table
* component to make it eaiser to maintain the props of the table. This function
* will update the column definitions and return the sorted data. The caller
* needs to update the props to the table appropriately after calling this
* method.
*
* @param columnIndex The column that should be sorted.
* @param sortOrder The order the data should be sorted.
* @param sortFunctions An array of sort functions. Each sortable column should
* have a function supplied. If there are non-sortable columns, null should be
* supplied for their index.
* @param columns The column definitions for the table.
* @param items The array of items that should be sorted. Note: This is done in
* place so the input array will be updated.
*
* @return The resulting sorted array of items.
*/
export function sortItems(columnIndex, sortOrder, sortFunctions, columns, items) {
let sortFunction = sortFunctions[columnIndex];
// If we are sorting descending, invert the sorting function.
if (sortFunction && sortOrder === SortOrder.descending) {
sortFunction = (item1, item2) => {
return -sortFunctions[columnIndex](item1, item2);
};
}
// Update the sortOrder for each of the columns.
for (let index = 0; index < columns.length; index++) {
const column = columns[index];
if (column.sortProps) {
column.sortProps.sortOrder = index === columnIndex ? sortOrder : undefined;
}
}
if (sortFunction) {
return items.sort(sortFunction);
}
else {
return items;
}
}