blob: 3feec6900d1bd224f1e87c625f50a523975905b0 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @unrestricted
* @extends {DataGrid.ViewportDataGrid<!NODE_TYPE>}
* @template NODE_TYPE
*/
export default class SortableDataGrid extends DataGrid.ViewportDataGrid {
/**
* @param {!Array<!DataGrid.DataGrid.ColumnDescriptor>} columnsArray
* @param {function(!NODE_TYPE, string, string, string)=} editCallback
* @param {function(!NODE_TYPE)=} deleteCallback
* @param {function()=} refreshCallback
*/
constructor(columnsArray, editCallback, deleteCallback, refreshCallback) {
super(columnsArray, editCallback, deleteCallback, refreshCallback);
/** @type {function(!NODE_TYPE, !NODE_TYPE):number} */
this._sortingFunction = DataGrid.SortableDataGrid.TrivialComparator;
this.setRootNode(/** @type {!DataGrid.SortableDataGridNode<!NODE_TYPE>} */ (new DataGrid.SortableDataGridNode()));
}
/**
* @param {!DataGrid.SortableDataGridNode} a
* @param {!DataGrid.SortableDataGridNode} b
* @return {number}
*/
static TrivialComparator(a, b) {
return 0;
}
/**
* @param {string} columnId
* @param {!DataGrid.SortableDataGridNode} a
* @param {!DataGrid.SortableDataGridNode} b
* @return {number}
*/
static NumericComparator(columnId, a, b) {
const aValue = a.data[columnId];
const bValue = b.data[columnId];
const aNumber = Number(aValue instanceof Node ? aValue.textContent : aValue);
const bNumber = Number(bValue instanceof Node ? bValue.textContent : bValue);
return aNumber < bNumber ? -1 : (aNumber > bNumber ? 1 : 0);
}
/**
* @param {string} columnId
* @param {!DataGrid.SortableDataGridNode} a
* @param {!DataGrid.SortableDataGridNode} b
* @return {number}
*/
static StringComparator(columnId, a, b) {
const aValue = a.data[columnId];
const bValue = b.data[columnId];
const aString = aValue instanceof Node ? aValue.textContent : String(aValue);
const bString = bValue instanceof Node ? bValue.textContent : String(bValue);
return aString < bString ? -1 : (aString > bString ? 1 : 0);
}
/**
* @param {function(!NODE_TYPE, !NODE_TYPE):number} comparator
* @param {boolean} reverseMode
* @param {!NODE_TYPE} a
* @param {!NODE_TYPE} b
* @return {number}
* @template NODE_TYPE
*/
static Comparator(comparator, reverseMode, a, b) {
return reverseMode ? comparator(b, a) : comparator(a, b);
}
/**
* @param {!Array.<string>} columnNames
* @param {!Array.<string>} values
* @return {?DataGrid.SortableDataGrid<!DataGrid.SortableDataGridNode>}
*/
static create(columnNames, values) {
const numColumns = columnNames.length;
if (!numColumns) {
return null;
}
const columns = /** @type {!Array<!DataGrid.DataGrid.ColumnDescriptor>} */ ([]);
for (let i = 0; i < columnNames.length; ++i) {
columns.push({id: String(i), title: columnNames[i], width: columnNames[i].length, sortable: true});
}
const nodes = [];
for (let i = 0; i < values.length / numColumns; ++i) {
const data = {};
for (let j = 0; j < columnNames.length; ++j) {
data[j] = values[numColumns * i + j];
}
const node = new DataGrid.SortableDataGridNode(data);
node.selectable = false;
nodes.push(node);
}
const dataGrid = new DataGrid.SortableDataGrid(columns);
const length = nodes.length;
const rootNode = dataGrid.rootNode();
for (let i = 0; i < length; ++i) {
rootNode.appendChild(nodes[i]);
}
dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, sortDataGrid);
function sortDataGrid() {
const nodes = dataGrid.rootNode().children;
const sortColumnId = dataGrid.sortColumnId();
if (!sortColumnId) {
return;
}
let columnIsNumeric = true;
for (let i = 0; i < nodes.length; i++) {
const value = nodes[i].data[sortColumnId];
if (isNaN(value instanceof Node ? value.textContent : value)) {
columnIsNumeric = false;
break;
}
}
const comparator =
columnIsNumeric ? DataGrid.SortableDataGrid.NumericComparator : DataGrid.SortableDataGrid.StringComparator;
dataGrid.sortNodes(comparator.bind(null, sortColumnId), !dataGrid.isSortOrderAscending());
}
return dataGrid;
}
/**
* @param {!NODE_TYPE} node
*/
insertChild(node) {
const root = /** @type {!DataGrid.SortableDataGridNode<!NODE_TYPE>} */ (this.rootNode());
root.insertChildOrdered(node);
}
/**
* @param {function(!NODE_TYPE, !NODE_TYPE):number} comparator
* @param {boolean} reverseMode
*/
sortNodes(comparator, reverseMode) {
this._sortingFunction = DataGrid.SortableDataGrid.Comparator.bind(null, comparator, reverseMode);
this.rootNode().recalculateSiblings(0);
this.rootNode()._sortChildren(reverseMode);
this.scheduleUpdateStructure();
}
}
/**
* @unrestricted
* @extends {DataGrid.ViewportDataGridNode<!NODE_TYPE>}
* @template NODE_TYPE
*/
export class SortableDataGridNode extends DataGrid.ViewportDataGridNode {
/**
* @param {?Object.<string, *>=} data
* @param {boolean=} hasChildren
*/
constructor(data, hasChildren) {
super(data, hasChildren);
}
/**
* @param {!NODE_TYPE} node
*/
insertChildOrdered(node) {
this.insertChild(node, this.children.upperBound(node, this.dataGrid._sortingFunction));
}
_sortChildren() {
this.children.sort(this.dataGrid._sortingFunction);
for (let i = 0; i < this.children.length; ++i) {
this.children[i].recalculateSiblings(i);
}
for (const child of this.children) {
child._sortChildren();
}
}
}
/* Legacy exported object */
self.DataGrid = self.DataGrid || {};
/* Legacy exported object */
DataGrid = DataGrid || {};
/**
* @unrestricted
* @constructor
*/
DataGrid.SortableDataGrid = SortableDataGrid;
/**
* @unrestricted
* @constructor
* @extends {DataGrid.ViewportDataGridNode<!NODE_TYPE>}
*/
DataGrid.SortableDataGridNode = SortableDataGridNode;