@hashicorp/design-system-components
Version:
Helios Design System Components
356 lines (338 loc) • 10.3 kB
JavaScript
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { guidFor } from '@ember/object/internals';
import { g, i, n } from 'decorator-transforms/runtime';
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
const DEFAULT_WIDTH = '1fr'; // default to '1fr' to allow flexible width
const DEFAULT_MIN_WIDTH = '150px';
const DEFAULT_MAX_WIDTH = '800px';
function isPxSize(value) {
if (value === undefined) {
return false;
}
return /^-?\d+(\.\d+)?px$/.test(value);
}
function pxToNumber(pxString) {
return parseFloat(pxString.slice(0, -2));
}
class HdsAdvancedTableColumn {
static {
g(this.prototype, "label", [tracked], function () {
return '';
});
}
#label = (i(this, "label"), void 0);
static {
g(this.prototype, "align", [tracked], function () {
return 'left';
});
}
#align = (i(this, "align"), void 0);
static {
g(this.prototype, "isBeingDragged", [tracked], function () {
return false;
});
}
#isBeingDragged = (i(this, "isBeingDragged"), void 0);
static {
g(this.prototype, "isExpandable", [tracked], function () {
return false;
});
}
#isExpandable = (i(this, "isExpandable"), void 0);
static {
g(this.prototype, "isSortable", [tracked], function () {
return false;
});
}
#isSortable = (i(this, "isSortable"), void 0);
static {
g(this.prototype, "isVisuallyHidden", [tracked], function () {
return false;
});
}
#isVisuallyHidden = (i(this, "isVisuallyHidden"), void 0);
static {
g(this.prototype, "key", [tracked]);
}
#key = (i(this, "key"), void 0);
static {
g(this.prototype, "tooltip", [tracked], function () {
return undefined;
});
}
#tooltip = (i(this, "tooltip"), void 0);
static {
g(this.prototype, "sortingFunction", [tracked], function () {
return undefined;
});
}
#sortingFunction = (i(this, "sortingFunction"), void 0);
static {
g(this.prototype, "thElement", [tracked], function () {
return undefined;
});
}
#thElement = (i(this, "thElement"), void 0); // elements
static {
g(this.prototype, "reorderHandleElement", [tracked], function () {
return undefined;
});
}
#reorderHandleElement = (i(this, "reorderHandleElement"), void 0);
static {
g(this.prototype, "thContextMenuToggleElement", [tracked], function () {
return undefined;
});
}
#thContextMenuToggleElement = (i(this, "thContextMenuToggleElement"), void 0);
static {
g(this.prototype, "transientWidth", [tracked], function () {
return undefined;
});
}
#transientWidth = (i(this, "transientWidth"), void 0); // width properties
static {
g(this.prototype, "width", [tracked], function () {
return DEFAULT_WIDTH;
});
}
#width = (i(this, "width"), void 0); // used for transient width changes
static {
g(this.prototype, "minWidth", [tracked], function () {
return DEFAULT_MIN_WIDTH;
});
}
#minWidth = (i(this, "minWidth"), void 0);
static {
g(this.prototype, "maxWidth", [tracked], function () {
return DEFAULT_MAX_WIDTH;
});
}
#maxWidth = (i(this, "maxWidth"), void 0);
static {
g(this.prototype, "originalWidth", [tracked], function () {
return this.width;
});
}
#originalWidth = (i(this, "originalWidth"), void 0);
static {
g(this.prototype, "widthDebts", [tracked], function () {
return {};
});
}
#widthDebts = (i(this, "widthDebts"), void 0); // used to restore the width when resetting
// used to track width changes imposed by other columns
table;
get cells() {
return this.table.flattenedVisibleRows.map(row => {
const cell = row.cells.find(cell => cell.columnKey === this.key);
return cell;
});
}
get appliedWidth() {
return this.transientWidth ?? this.width;
}
get pxAppliedWidth() {
if (isPxSize(this.appliedWidth)) {
return pxToNumber(this.appliedWidth);
}
}
get pxTransientWidth() {
if (this.transientWidth !== undefined) {
return pxToNumber(this.transientWidth);
}
}
set pxTransientWidth(value) {
if (value !== undefined && value >= 0) {
this.transientWidth = `${value}px`;
} else {
this.transientWidth = undefined;
}
}
get pxWidth() {
if (isPxSize(this.width)) {
return pxToNumber(this.width);
} else {
return this.thElement?.offsetWidth ?? 0;
}
}
set pxWidth(value) {
this.width = `${value}px`;
}
get pxMinWidth() {
return isPxSize(this.minWidth) ? pxToNumber(this.minWidth) : 0;
}
get pxMaxWidth() {
return isPxSize(this.maxWidth) ? pxToNumber(this.maxWidth) : Infinity;
}
get index() {
const {
orderedColumns
} = this.table;
if (orderedColumns.length === 0) {
return -1;
}
return orderedColumns.findIndex(column => column.key === this.key);
}
get isFirst() {
return this.index === 0;
}
get isLast() {
return this.index !== -1 && this.index === this.table.columns.length - 1;
}
get siblings() {
const {
index,
table
} = this;
const {
orderedColumns
} = table;
if (index === -1) {
return {};
}
return {
previous: this.isFirst ? undefined : orderedColumns[index - 1],
next: this.isLast ? undefined : orderedColumns[index + 1]
};
}
constructor(args) {
const {
column,
table
} = args;
// set reference to table model
this.table = table;
// set column properties
this.label = column.label;
this.align = column.align ?? 'left';
this.isExpandable = 'isExpandable' in column ? column.isExpandable : false;
this.isSortable = column.isSortable ?? false;
this.isVisuallyHidden = column.isVisuallyHidden ?? false;
this.key = column.key ?? guidFor(this);
this.tooltip = column.tooltip;
this._setWidthValues(column);
this.sortingFunction = column.sortingFunction;
}
// main collection function
collectWidthDebts() {
this.table.columns.forEach(debtor => {
const debtToCollect = debtor.widthDebts[this.key] ?? 0;
if (debtToCollect <= 0) {
return;
}
const amountPaid = debtor._sourceFundsForPayment(debtToCollect);
if (amountPaid > 0) {
this.pxWidth = (this.pxWidth ?? 0) + amountPaid;
const remainingDebt = debtToCollect - amountPaid;
if (remainingDebt > 0) {
debtor.widthDebts[this.key] = remainingDebt;
} else {
delete debtor.widthDebts[this.key];
}
}
});
}
// function for recursively recovering width debts without ending up in a deficit
_sourceFundsForPayment(amountNeeded) {
let fundsSourced = 0;
// preferentially source width from our own surplus first
const surplus = Math.max(0, (this.pxWidth ?? 0) - this.pxMinWidth);
const paymentFromSurplus = Math.min(amountNeeded, surplus);
if (paymentFromSurplus > 0) {
this.pxWidth = (this.pxWidth ?? 0) - paymentFromSurplus;
fundsSourced = fundsSourced + paymentFromSurplus;
}
// if we dont have enough to cover, source from debtors recursively
const shortfall = amountNeeded - fundsSourced;
if (shortfall > 0) {
const ourDebtors = this.table.columns.filter(column => column.widthDebts[this.key]);
for (const subDebtor of ourDebtors) {
const amountStillNeeded = amountNeeded - fundsSourced;
if (amountStillNeeded <= 0) {
break;
}
const subDebtOwed = subDebtor.widthDebts[this.key] ?? 0;
const amountToRequest = Math.min(amountStillNeeded, subDebtOwed);
const collectedFromSubDebtor = subDebtor._sourceFundsForPayment(amountToRequest);
if (collectedFromSubDebtor > 0) {
fundsSourced = fundsSourced + collectedFromSubDebtor;
// Update the sub-debtor's ledger.
const remainingSubDebt = subDebtOwed - collectedFromSubDebtor;
if (remainingSubDebt > 0) {
subDebtor.widthDebts[this.key] = remainingSubDebt;
} else {
delete subDebtor.widthDebts[this.key];
}
}
}
}
return fundsSourced;
}
payWidthDebts() {
Object.entries(this.widthDebts).forEach(([lenderKey, amount]) => {
const lender = this.table.getColumnByKey(lenderKey);
if (lender !== undefined) {
// Give the width back to the column that lent it to us
lender.pxWidth = (lender.pxWidth ?? 0) + amount;
}
});
// Clear our own debt ledger, as we've paid everyone back
this.widthDebts = {};
}
settleWidthDebts() {
this.collectWidthDebts();
this.payWidthDebts();
}
// set initial width values
_setWidthValues({
width,
minWidth,
maxWidth
}) {
this.width = width ?? DEFAULT_WIDTH;
// capture the width at the time of instantiation so it can be restored
this.originalWidth = this.width;
this.minWidth = minWidth ?? DEFAULT_MIN_WIDTH;
this.maxWidth = maxWidth ?? DEFAULT_MAX_WIDTH;
}
focusReorderHandle() {
if (this.thElement === undefined) {
return;
}
// focus the th element first (parent) to ensure the handle is visible
this.thElement.focus({
preventScroll: true
});
if (this.reorderHandleElement === undefined) {
return;
}
// then focus the reorder handle element
this.reorderHandleElement.focus();
}
// Sets the column width in pixels, ensuring it respects the min and max width constraints.
static {
n(this.prototype, "focusReorderHandle", [action]);
}
setPxTransientWidth(newPxWidth) {
const pxMinWidth = this.pxMinWidth ?? 1;
const minLimitedPxWidth = Math.max(newPxWidth, pxMinWidth);
this.pxTransientWidth = this.pxMaxWidth !== undefined ? Math.min(minLimitedPxWidth, this.pxMaxWidth) : minLimitedPxWidth;
if (this.key === undefined) {
return;
}
}
restoreWidth() {
this.settleWidthDebts();
this.width = this.originalWidth;
}
static {
n(this.prototype, "restoreWidth", [action]);
}
}
export { DEFAULT_MAX_WIDTH, DEFAULT_MIN_WIDTH, DEFAULT_WIDTH, HdsAdvancedTableColumn as default };
//# sourceMappingURL=column.js.map