UNPKG

@hashicorp/design-system-components

Version:
356 lines (338 loc) 10.3 kB
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