UNPKG

@hashicorp/design-system-components

Version:
270 lines (267 loc) 10.3 kB
import Component from '@glimmer/component'; import { modifier } from 'ember-modifier'; import { TrackedMap } from 'tracked-built-ins'; import { hash } from '@ember/helper'; import { isPixelSize, pixelToNumber } from '../utils.js'; import { precompileTemplate } from '@ember/template-compilation'; import { setComponentTemplate } from '@ember/component'; const DEFAULT_WIDTH = '1fr'; // default to '1fr' to allow flexible width const DEFAULT_MIN_WIDTH = '150px'; const DEFAULT_MAX_WIDTH = '800px'; class HdsAdvancedTableColumnManagerWidth extends Component { _columnWidths = new TrackedMap(); _originalColumnWidths = new TrackedMap(); _transientColumnWidths = new TrackedMap(); _columnDebts = new TrackedMap(); syncWidthValues = modifier(() => { const { columns } = this.args; for (const column of columns) { this._columnWidths.set(column.key, column.width ?? DEFAULT_WIDTH); this._originalColumnWidths.set(column.key, column.width ?? DEFAULT_WIDTH); } }); get gridTemplateColumns() { const { isSelectable, orderedColumns } = this.args; let style = isSelectable ? 'min-content ' : ''; for (const col of orderedColumns) { const appliedWidth = this.getAppliedWidth(col.key); style += ` ${appliedWidth}`; } return style; } getAppliedWidth = columnKey => { const width = this._columnWidths.get(columnKey); const transientWidth = this._transientColumnWidths.get(columnKey); return transientWidth ?? width ?? `${this.args.thElements.get(columnKey)?.offsetWidth ?? 0}px`; }; getSiblingColumnKeys = columnKey => { if (columnKey === null) { return {}; } const columnIndex = this.args.columnOrder.indexOf(columnKey); if (columnIndex === -1) { return {}; } return { previous: columnIndex === 0 ? undefined : this.args.columnOrder[columnIndex - 1], next: columnIndex === this.args.columnOrder.length - 1 ? undefined : this.args.columnOrder[columnIndex + 1] }; }; _getPxWidth(key) { const width = this._columnWidths.get(key); if (width !== undefined && isPixelSize(width)) { return pixelToNumber(width); } else { return this.args.thElements.get(key)?.offsetWidth ?? 0; } } _getPxMinWidth(key) { const column = this.args.getColumnByKey(key); const minWidth = column?.minWidth ?? DEFAULT_MIN_WIDTH; return isPixelSize(minWidth) ? pixelToNumber(minWidth) : 0; } applyTransientWidth = columnKey => { if (columnKey == undefined) { return; } const transientWidth = this._transientColumnWidths.get(columnKey); this._columnWidths.set(columnKey, transientWidth); }; setTransientColumnWidths = (options = {}) => { const roundValues = options.roundValues ?? false; this._columnWidths.forEach((width, key) => { let _width; if (width !== undefined && isPixelSize(width)) { _width = pixelToNumber(width); } else { _width = this.args.thElements.get(key)?.offsetWidth ?? 0; } this._transientColumnWidths.set(key, `${roundValues ? Math.round(_width) : _width}px`); }); }; resetTransientColumnWidths = () => { this._transientColumnWidths.clear(); }; setTransientColumnWidth = (columnKey, width, clamped = true) => { const column = this.args.getColumnByKey(columnKey); if (column === undefined) { return; } if (clamped) { const { minWidth, maxWidth } = column; const minWidthInPixels = minWidth === undefined ? 1 : pixelToNumber(minWidth); const maxWidthInPixels = maxWidth === undefined ? Infinity : pixelToNumber(maxWidth); const transientColumnWidthInPixels = Math.min(Math.max(pixelToNumber(width), minWidthInPixels), maxWidthInPixels); this._transientColumnWidths.set(columnKey, `${transientColumnWidthInPixels}px`); } else { this._transientColumnWidths.set(columnKey, width); } }; updateResizeDebt = (columnKey, delta) => { if (delta === 0) { return; } const { next: nextColumnKey } = this.getSiblingColumnKeys(columnKey); if (nextColumnKey === undefined) { return; } // determine borrower and lender const borrowerKey = delta > 0 ? columnKey : nextColumnKey; const lenderKey = delta > 0 ? nextColumnKey : columnKey; let amount = Math.abs(delta); // check if lender has existing debt to borrower const lenderDebts = this._columnDebts.get(lenderKey) ?? {}; const existingDebt = lenderDebts[borrowerKey] ?? 0; if (existingDebt > 0) { const paymentAmount = Math.min(amount, existingDebt); const newDebt = existingDebt - paymentAmount; const updatedDebts = { ...lenderDebts }; if (newDebt <= 0) { delete updatedDebts[borrowerKey]; } else { updatedDebts[borrowerKey] = newDebt; } this._columnDebts.set(lenderKey, updatedDebts); amount = amount - paymentAmount; } // if amount remains, create new debt if (amount > 0) { const borrowerDebts = this._columnDebts.get(borrowerKey) ?? {}; this._columnDebts.set(borrowerKey, { ...borrowerDebts, [lenderKey]: (borrowerDebts[lenderKey] ?? 0) + amount }); } }; // restores a column to its original width, settling all debts first restoreColumnWidth = columnKey => { // settle debts (collect from debtors, pay lenders) this._settleWidthDebts(columnKey); // restore original width const originalWidth = this._originalColumnWidths.get(columnKey); if (originalWidth) { this._columnWidths.set(columnKey, originalWidth); this._transientColumnWidths.delete(columnKey); } }; _settleWidthDebts(key) { this._collectWidthDebts(key); this._payWidthDebts(key); } _collectWidthDebts(collectorKey) { // iterate over all known columns to see if they owe the collector this.args.columnOrder.forEach(debtorKey => { if (debtorKey === collectorKey) { return; } const debtorDebts = this._columnDebts.get(debtorKey); const debtToCollect = debtorDebts?.[collectorKey] ?? 0; if (debtToCollect <= 0) { return; } // attempt to source funds from the debtor const amountPaid = this._sourceFundsForPayment(debtorKey, debtToCollect); if (amountPaid > 0) { // add funds to collector const currentCollectorWidth = this._getPxWidth(collectorKey); this._columnWidths.set(collectorKey, `${currentCollectorWidth + amountPaid}px`); this._transientColumnWidths.delete(collectorKey); // update debtors ledger const remainingDebt = debtToCollect - amountPaid; const currentDebts = this._columnDebts.get(debtorKey) ?? {}; const updatedDebts = { ...currentDebts }; if (remainingDebt > 0) { updatedDebts[collectorKey] = remainingDebt; } else { delete updatedDebts[collectorKey]; } this._columnDebts.set(debtorKey, updatedDebts); } }); } _payWidthDebts(payerKey) { const debts = this._columnDebts.get(payerKey); if (debts === undefined) { return; } Object.entries(debts).forEach(([lenderKey, amount]) => { // give width back to lender const currentLenderWidth = this._getPxWidth(lenderKey); this._columnWidths.set(lenderKey, `${currentLenderWidth + amount}px`); this._transientColumnWidths.delete(lenderKey); }); // lear payers debts this._columnDebts.delete(payerKey); } _sourceFundsForPayment(key, amountNeeded) { let fundsSourced = 0; // preferentially source width from our own surplus first const currentWidth = this._getPxWidth(key); const minWidth = this._getPxMinWidth(key); const surplus = Math.max(0, currentWidth - minWidth); const paymentFromSurplus = Math.min(amountNeeded, surplus); if (paymentFromSurplus > 0) { this._columnWidths.set(key, `${currentWidth - paymentFromSurplus}px`); this._transientColumnWidths.delete(key); fundsSourced = fundsSourced + paymentFromSurplus; } // if shortfall, source from our debtors (recursive) const shortfall = amountNeeded - fundsSourced; if (shortfall > 0) { // find who owes this column (key) const ourDebtors = this.args.columnOrder.filter(debtorKey => { const debtorDebts = this._columnDebts.get(debtorKey); return debtorDebts && debtorDebts[key] && debtorDebts[key] > 0; }); for (const subDebtorKey of ourDebtors) { const amountStillNeeded = amountNeeded - fundsSourced; if (amountStillNeeded <= 0) { break; } const subDebtorDebts = this._columnDebts.get(subDebtorKey); const subDebtOwed = subDebtorDebts[key] ?? 0; const amountToRequest = Math.min(amountStillNeeded, subDebtOwed); const collectedFromSubDebtor = this._sourceFundsForPayment(subDebtorKey, amountToRequest); if (collectedFromSubDebtor > 0) { fundsSourced = fundsSourced + collectedFromSubDebtor; // update sub-debtor ledger const remaining = subDebtOwed - collectedFromSubDebtor; const updatedSubDebts = { ...subDebtorDebts }; if (remaining > 0) { updatedSubDebts[key] = remaining; } else { delete updatedSubDebts[key]; } this._columnDebts.set(subDebtorKey, updatedSubDebts); } } } return fundsSourced; } static { setComponentTemplate(precompileTemplate("{{yield (hash gridTemplateColumns=this.gridTemplateColumns syncWidthValues=this.syncWidthValues applyTransientWidth=this.applyTransientWidth getAppliedWidth=this.getAppliedWidth getSiblingColumnKeys=this.getSiblingColumnKeys restoreColumnWidth=this.restoreColumnWidth setTransientColumnWidths=this.setTransientColumnWidths setTransientColumnWidth=this.setTransientColumnWidth resetTransientColumnWidths=this.resetTransientColumnWidths updateResizeDebt=this.updateResizeDebt)}}", { strictMode: true, scope: () => ({ hash }) }), this); } } export { DEFAULT_MAX_WIDTH, DEFAULT_MIN_WIDTH, DEFAULT_WIDTH, HdsAdvancedTableColumnManagerWidth as default }; //# sourceMappingURL=width.js.map