@hashicorp/design-system-components
Version:
Helios Design System Components
382 lines (370 loc) • 11.2 kB
JavaScript
import HdsAdvancedTableRow from './row.js';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { isEmpty } from '@ember/utils';
import HdsAdvancedTableColumn from './column.js';
import { HdsAdvancedTableThSortOrderValues, HdsAdvancedTableColumnReorderSideValues } from '../types.js';
import { g, i, n } from 'decorator-transforms/runtime';
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
function getVisibleRows(rows) {
return rows.reduce((acc, row) => {
acc.push(row);
if (row.isOpen && row.children) {
acc.push(...getVisibleRows(row.children));
}
return acc;
}, []);
}
function getChildrenCount(rows) {
return rows.reduce((acc, row) => acc + 1 + getChildrenCount(row.children ?? []), 0);
}
class HdsAdvancedTableTableModel {
static {
g(this.prototype, "columns", [tracked], function () {
return [];
});
}
#columns = (i(this, "columns"), void 0);
static {
g(this.prototype, "columnOrder", [tracked], function () {
return [];
});
}
#columnOrder = (i(this, "columnOrder"), void 0);
static {
g(this.prototype, "reorderDraggedColumn", [tracked], function () {
return null;
});
}
#reorderDraggedColumn = (i(this, "reorderDraggedColumn"), void 0);
static {
g(this.prototype, "reorderHoveredColumn", [tracked], function () {
return null;
});
}
#reorderHoveredColumn = (i(this, "reorderHoveredColumn"), void 0);
static {
g(this.prototype, "rows", [tracked], function () {
return [];
});
}
#rows = (i(this, "rows"), void 0);
static {
g(this.prototype, "sortBy", [tracked], function () {
return undefined;
});
}
#sortBy = (i(this, "sortBy"), void 0);
static {
g(this.prototype, "sortOrder", [tracked], function () {
return HdsAdvancedTableThSortOrderValues.Asc;
});
}
#sortOrder = (i(this, "sortOrder"), void 0);
static {
g(this.prototype, "gridElement", [tracked], function () {
return undefined;
});
}
#gridElement = (i(this, "gridElement"), void 0);
childrenKey;
hasReorderableColumns;
hasResizableColumns;
onColumnReorder;
onSort;
constructor(args) {
const {
model,
columns,
columnOrder,
childrenKey,
hasReorderableColumns,
hasResizableColumns,
sortBy,
sortOrder,
onColumnReorder,
onSort
} = args;
this.childrenKey = childrenKey;
this.hasReorderableColumns = hasReorderableColumns;
this.hasResizableColumns = hasResizableColumns;
this.onSort = onSort;
this.setupData({
model,
columns,
sortBy,
sortOrder
});
// set initial column order
if (this.hasReorderableColumns) {
this.columnOrder = isEmpty(columnOrder) ? this.columns.map(column => column.key) : columnOrder; // ensured non-empty
this.onColumnReorder = onColumnReorder;
}
}
get hasColumnBeingDragged() {
return this.reorderDraggedColumn !== null;
}
get reorderDraggedColumnCells() {
if (this.reorderDraggedColumn === null) {
return [];
}
const {
key
} = this.reorderDraggedColumn;
return this.flattenedVisibleRows.map(row => {
const cell = row.cells.find(cell => cell.columnKey === key);
return cell;
});
}
get orderedColumns() {
if (this.hasReorderableColumns) {
return this.columnOrder.reduce((acc, key) => {
const column = this.columns.find(column => column.key === key);
if (column !== undefined) {
acc.push(column);
}
return acc;
}, []);
} else {
return this.columns;
}
}
get sortCriteria() {
// get the current column
const currentColumn = this.columns.find(column => column.key === this.sortBy);
if (
// check if there is a custom sorting function associated with the current `sortBy` column (we assume the column has `isSortable`)
currentColumn?.sortingFunction && typeof currentColumn.sortingFunction === 'function') {
return currentColumn.sortingFunction;
} else {
// otherwise fallback to the default format "sortBy:sortOrder"
return `${this.sortBy}:${this.sortOrder}`;
}
}
get sortedRows() {
const criteria = this.sortCriteria;
const rows = this.rows;
if (rows.length <= 1 || criteria === undefined) {
return rows;
}
if (typeof criteria === 'function') {
// Use custom sort function
return [...rows].sort(criteria);
} else {
// Parse the criteria string format "sortBy:sortOrder"
const [sortBy, sortOrder] = criteria.split(':');
if (!sortBy) {
return rows;
}
return [...rows].sort((a, b) => {
const valueA = a[sortBy];
const valueB = b[sortBy];
if (valueA < valueB) {
return sortOrder === 'asc' ? -1 : 1;
}
if (valueA > valueB) {
return sortOrder === 'asc' ? 1 : -1;
}
return 0;
});
}
}
get totalRowCount() {
return getChildrenCount(this.sortedRows);
}
get flattenedVisibleRows() {
return getVisibleRows(this.sortedRows);
}
get lastVisibleRow() {
return this.flattenedVisibleRows[this.flattenedVisibleRows.length - 1];
}
get hasRowsWithChildren() {
return this.rows.some(row => row.hasChildren);
}
get allRowsAreOpen() {
return this.flattenedVisibleRows.length === this.totalRowCount;
}
get expandState() {
if (this.allRowsAreOpen) {
return true;
} else {
return false;
}
}
setTransientColumnWidths(options = {}) {
const roundValues = options.roundValues ?? false;
this.columns.forEach(column => {
column.pxTransientWidth = roundValues ? Math.round(column.pxWidth) : column.pxWidth;
});
}
resetTransientColumnWidths() {
this.columns.forEach(column => {
column.pxTransientWidth = undefined;
});
}
getColumnByKey(key) {
return this.columns.find(column => column.key === key);
}
setupData(args) {
const {
model,
columns,
sortBy,
sortOrder
} = args;
this.sortBy = sortBy;
this.sortOrder = sortOrder ?? HdsAdvancedTableThSortOrderValues.Asc;
this.columns = columns.map(column => new HdsAdvancedTableColumn({
column,
table: this
}));
this.rows = model.map(row => {
return new HdsAdvancedTableRow({
...row,
childrenKey: this.childrenKey,
columns,
table: this
});
});
}
static {
n(this.prototype, "setupData", [action]);
}
restoreColumnWidths() {
this.columns.forEach(column => {
column.width = column.originalWidth;
});
}
static {
n(this.prototype, "restoreColumnWidths", [action]);
}
setSortBy(column) {
if (this.sortBy === column) {
// check to see if the column is already sorted and invert the sort order if so
this.sortOrder = this.sortOrder === HdsAdvancedTableThSortOrderValues.Asc ? HdsAdvancedTableThSortOrderValues.Desc : HdsAdvancedTableThSortOrderValues.Asc;
} else {
// otherwise, set the sort order to ascending
this.sortBy = column;
this.sortOrder = HdsAdvancedTableThSortOrderValues.Asc;
}
if (typeof this.onSort === 'function') {
this.onSort(this.sortBy, this.sortOrder);
}
}
static {
n(this.prototype, "setSortBy", [action]);
}
openAll() {
this.rows.forEach(row => row.openAll());
}
static {
n(this.prototype, "openAll", [action]);
}
collapseAll() {
this.rows.forEach(row => row.collapseAll());
}
static {
n(this.prototype, "collapseAll", [action]);
}
toggleAll() {
if (this.allRowsAreOpen) {
this.collapseAll();
} else {
this.openAll();
}
}
static {
n(this.prototype, "toggleAll", [action]);
}
stepColumn(column, step) {
const {
table
} = column;
const oldIndex = table.orderedColumns.indexOf(column);
const newIndex = oldIndex + step;
// Check if the new position is within the array bounds.
if (newIndex < 0 || newIndex >= table.orderedColumns.length) {
return;
}
const targetColumn = table.orderedColumns[newIndex];
if (targetColumn === undefined) {
return;
}
// Determine the side based on the step direction.
const side = step > 0 ? HdsAdvancedTableColumnReorderSideValues.Right : HdsAdvancedTableColumnReorderSideValues.Left;
table.moveColumnToTarget(column, targetColumn, side);
}
static {
n(this.prototype, "stepColumn", [action]);
}
moveColumnToTerminalPosition(column, position) {
const firstColumn = this.orderedColumns.find(column => column.isFirst);
const {
targetColumn,
side
} = position === 'start' ? {
targetColumn: firstColumn,
side: HdsAdvancedTableColumnReorderSideValues.Left
} : {
targetColumn: this.orderedColumns[this.orderedColumns.length - 1],
side: HdsAdvancedTableColumnReorderSideValues.Right
};
if (targetColumn === undefined) {
return;
}
// Move the column to the target position
this.moveColumnToTarget(column, targetColumn, side);
}
static {
n(this.prototype, "moveColumnToTerminalPosition", [action]);
}
moveColumnToTarget(sourceColumn, targetColumn, side) {
const sourceKey = sourceColumn.key;
const targetKey = targetColumn.key;
const oldIndex = this.columnOrder.indexOf(sourceKey);
const newIndex = this.columnOrder.indexOf(targetKey);
if (oldIndex !== -1 && newIndex !== -1) {
const updated = [...this.columnOrder];
updated.splice(oldIndex, 1); // Remove from old position
// Calculate the insertion index based on the side
// If dropping to the right of the target, insert after the target
// If dropping to the left of the target, insert before the target
// Adjust for the shift in indices caused by removing the source column
const adjustedIndex = side === HdsAdvancedTableColumnReorderSideValues.Right ? newIndex > oldIndex ? newIndex : newIndex + 1 : newIndex > oldIndex ? newIndex - 1 : newIndex;
updated.splice(adjustedIndex, 0, sourceColumn.key); // Insert at new position
this.columnOrder = updated;
// we need to wait until the reposition has finished
requestAnimationFrame(() => {
sourceColumn.thElement?.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'center'
});
sourceColumn.isBeingDragged = false;
this.onColumnReorder?.({
column: sourceColumn,
newOrder: updated,
insertedAt: updated.indexOf(sourceColumn.key)
});
});
}
}
static {
n(this.prototype, "moveColumnToTarget", [action]);
}
moveColumnToDropTarget(targetColumn, side) {
const sourceColumn = this.reorderDraggedColumn;
if (sourceColumn == null || sourceColumn === targetColumn) {
return;
}
this.moveColumnToTarget(sourceColumn, targetColumn, side);
}
static {
n(this.prototype, "moveColumnToDropTarget", [action]);
}
}
export { HdsAdvancedTableTableModel as default };
//# sourceMappingURL=table.js.map