55
66import { tracked } from '@glimmer/tracking' ;
77import { action } from '@ember/object' ;
8+ import { guidFor } from '@ember/object/internals' ;
89
910import type HdsAdvancedTableModel from './table.ts' ;
1011import type {
1112 HdsAdvancedTableHorizontalAlignment ,
1213 HdsAdvancedTableColumn as HdsAdvancedTableColumnType ,
1314} from '../types' ;
1415
16+ export const DEFAULT_WIDTH = '1fr' ; // default to '1fr' to allow flexible width
1517export const DEFAULT_MIN_WIDTH = '150px' ;
1618export const DEFAULT_MAX_WIDTH = '800px' ;
1719
@@ -31,40 +33,63 @@ export default class HdsAdvancedTableColumn {
3133 @tracked label : string = '' ;
3234 @tracked align ?: HdsAdvancedTableHorizontalAlignment = 'left' ;
3335 @tracked isExpandable ?: boolean = false ;
34- @tracked isReorderable ?: boolean = false ;
3536 @tracked isSortable ?: boolean = false ;
3637 @tracked isVisuallyHidden ?: boolean = false ;
37- @tracked key ?: string = undefined ;
38- @tracked minWidth ?: `${number } px` = DEFAULT_MIN_WIDTH ;
39- @tracked maxWidth ?: `${number } px` = DEFAULT_MAX_WIDTH ;
38+ @tracked key : string ;
4039 @tracked tooltip ?: string = undefined ;
41- @tracked width ?: string = undefined ;
42- @tracked originalWidth ?: string = undefined ; // used to restore the width when resetting
43- @tracked imposedWidthDelta : number = 0 ; // used to track the width change imposed by the previous column
40+ @tracked thElement ?: HTMLDivElement = undefined ;
41+
42+ // width properties
43+ @tracked transientWidth ?: `${number } px` = undefined ; // used for transient width changes
44+ @tracked width : string = DEFAULT_WIDTH ;
45+ @tracked minWidth : `${number } px` = DEFAULT_MIN_WIDTH ;
46+ @tracked maxWidth : `${number } px` = DEFAULT_MAX_WIDTH ;
47+ @tracked originalWidth : string = this . width ; // used to restore the width when resetting
48+ @tracked widthDebts : Record < string , number > = { } ; // used to track width changes imposed by other columns
4449
4550 @tracked sortingFunction ?: ( a : unknown , b : unknown ) => number = undefined ;
4651
4752 table : HdsAdvancedTableModel ;
4853
49- get pxWidth ( ) : number | undefined {
54+ get appliedWidth ( ) : string {
55+ return this . transientWidth ?? this . width ;
56+ }
57+ get pxAppliedWidth ( ) : number | undefined {
58+ if ( isPxSize ( this . appliedWidth ) ) {
59+ return pxToNumber ( this . appliedWidth ) ;
60+ }
61+ }
62+
63+ get pxTransientWidth ( ) : number | undefined {
64+ if ( this . transientWidth !== undefined ) {
65+ return pxToNumber ( this . transientWidth ) ;
66+ }
67+ }
68+ set pxTransientWidth ( value : number | undefined ) {
69+ if ( value !== undefined && value >= 0 ) {
70+ this . transientWidth = `${ value } px` ;
71+ } else {
72+ this . transientWidth = undefined ;
73+ }
74+ }
75+
76+ get pxWidth ( ) : number {
5077 if ( isPxSize ( this . width ) ) {
51- return pxToNumber ( this . width ! ) ;
78+ return pxToNumber ( this . width ) ;
79+ } else {
80+ return this . thElement ?. offsetWidth ?? 0 ;
5281 }
5382 }
5483 set pxWidth ( value : number ) {
5584 this . width = `${ value } px` ;
5685 }
5786
58- get pxMinWidth ( ) : number | undefined {
59- if ( isPxSize ( this . minWidth ) ) {
60- return pxToNumber ( this . minWidth ! ) ;
61- }
87+ get pxMinWidth ( ) : number {
88+ return isPxSize ( this . minWidth ) ? pxToNumber ( this . minWidth ) : 0 ;
6289 }
6390
64- get pxMaxWidth ( ) : number | undefined {
65- if ( isPxSize ( this . maxWidth ) ) {
66- return pxToNumber ( this . maxWidth ! ) ;
67- }
91+ get pxMaxWidth ( ) : number {
92+ return isPxSize ( this . maxWidth ) ? pxToNumber ( this . maxWidth ) : Infinity ;
6893 }
6994
7095 get index ( ) : number {
@@ -117,37 +142,130 @@ export default class HdsAdvancedTableColumn {
117142 this . isExpandable = 'isExpandable' in column ? column . isExpandable : false ;
118143 this . isSortable = column . isSortable ?? false ;
119144 this . isVisuallyHidden = column . isVisuallyHidden ?? false ;
120- this . key = column . key ;
145+ this . key = column . key ?? guidFor ( this ) ;
121146 this . tooltip = column . tooltip ;
122147 this . _setWidthValues ( column ) ;
123148 this . sortingFunction = column . sortingFunction ;
124149 }
125150
151+ // main collection function
152+ private collectWidthDebts ( ) : void {
153+ this . table . columns . forEach ( ( debtor ) => {
154+ const debtToCollect = debtor . widthDebts [ this . key ] ?? 0 ;
155+
156+ if ( debtToCollect <= 0 ) {
157+ return ;
158+ }
159+
160+ const amountPaid = debtor . _sourceFundsForPayment ( debtToCollect ) ;
161+
162+ if ( amountPaid > 0 ) {
163+ this . pxWidth = ( this . pxWidth ?? 0 ) + amountPaid ;
164+
165+ const remainingDebt = debtToCollect - amountPaid ;
166+
167+ if ( remainingDebt > 0 ) {
168+ debtor . widthDebts [ this . key ] = remainingDebt ;
169+ } else {
170+ delete debtor . widthDebts [ this . key ] ;
171+ }
172+ }
173+ } ) ;
174+ }
175+
176+ // function for recursively recovering width debts without ending up in a deficit
177+ private _sourceFundsForPayment ( amountNeeded : number ) : number {
178+ let fundsSourced = 0 ;
179+
180+ // preferentially source width from our own surplus first
181+ const surplus = Math . max ( 0 , ( this . pxWidth ?? 0 ) - this . pxMinWidth ) ;
182+ const paymentFromSurplus = Math . min ( amountNeeded , surplus ) ;
183+
184+ if ( paymentFromSurplus > 0 ) {
185+ this . pxWidth = ( this . pxWidth ?? 0 ) - paymentFromSurplus ;
186+
187+ fundsSourced = fundsSourced + paymentFromSurplus ;
188+ }
189+
190+ // if we dont have enough to cover, source from debtors recursively
191+ const shortfall = amountNeeded - fundsSourced ;
192+
193+ if ( shortfall > 0 ) {
194+ const ourDebtors = this . table . columns . filter (
195+ ( column ) => column . widthDebts [ this . key ]
196+ ) ;
197+
198+ for ( const subDebtor of ourDebtors ) {
199+ const amountStillNeeded = amountNeeded - fundsSourced ;
200+
201+ if ( amountStillNeeded <= 0 ) {
202+ break ;
203+ }
204+
205+ const subDebtOwed = subDebtor . widthDebts [ this . key ] ?? 0 ;
206+ const amountToRequest = Math . min ( amountStillNeeded , subDebtOwed ) ;
207+
208+ const collectedFromSubDebtor =
209+ subDebtor . _sourceFundsForPayment ( amountToRequest ) ;
210+
211+ if ( collectedFromSubDebtor > 0 ) {
212+ fundsSourced = fundsSourced + collectedFromSubDebtor ;
213+
214+ // Update the sub-debtor's ledger.
215+ const remainingSubDebt = subDebtOwed - collectedFromSubDebtor ;
216+
217+ if ( remainingSubDebt > 0 ) {
218+ subDebtor . widthDebts [ this . key ] = remainingSubDebt ;
219+ } else {
220+ delete subDebtor . widthDebts [ this . key ] ;
221+ }
222+ }
223+ }
224+ }
225+
226+ return fundsSourced ;
227+ }
228+
229+ private payWidthDebts ( ) : void {
230+ Object . entries ( this . widthDebts ) . forEach ( ( [ lenderKey , amount ] ) => {
231+ const lender = this . table . getColumnByKey ( lenderKey ) ;
232+
233+ if ( lender !== undefined ) {
234+ // Give the width back to the column that lent it to us
235+ lender . pxWidth = ( lender . pxWidth ?? 0 ) + amount ;
236+ }
237+ } ) ;
238+
239+ // Clear our own debt ledger, as we've paid everyone back
240+ this . widthDebts = { } ;
241+ }
242+
243+ private settleWidthDebts ( ) : void {
244+ this . collectWidthDebts ( ) ;
245+ this . payWidthDebts ( ) ;
246+ }
247+
248+ // set initial width values
126249 private _setWidthValues ( {
127250 width,
128251 minWidth,
129252 maxWidth,
130253 } : HdsAdvancedTableColumnType ) : void {
131- if ( width === undefined ) {
132- return ;
133- }
134-
135- this . width = width ;
254+ this . width = width ?? DEFAULT_WIDTH ;
136255
137256 // capture the width at the time of instantiation so it can be restored
138- this . originalWidth = width ;
257+ this . originalWidth = this . width ;
139258
140259 this . minWidth = minWidth ?? DEFAULT_MIN_WIDTH ;
141260 this . maxWidth = maxWidth ?? DEFAULT_MAX_WIDTH ;
142261 }
143262
144263 // Sets the column width in pixels, ensuring it respects the min and max width constraints.
145- @action
146- setPxWidth ( newPxWidth : number ) : void {
264+ setPxTransientWidth ( newPxWidth : number ) : void {
147265 const pxMinWidth = this . pxMinWidth ?? 1 ;
148266 const minLimitedPxWidth = Math . max ( newPxWidth , pxMinWidth ) ;
149267
150- this . pxWidth =
268+ this . pxTransientWidth =
151269 this . pxMaxWidth !== undefined
152270 ? Math . min ( minLimitedPxWidth , this . pxMaxWidth )
153271 : minLimitedPxWidth ;
@@ -157,29 +275,10 @@ export default class HdsAdvancedTableColumn {
157275 }
158276 }
159277
160- // This method is called when the column width is changed by the previous column.
161- @action
162- onPreviousColumnWidthRestored ( ) : void {
163- const restoredWidth = ( this . pxWidth ?? 0 ) + this . imposedWidthDelta ;
164-
165- this . setPxWidth ( restoredWidth ) ;
166-
167- this . imposedWidthDelta = 0 ;
168- }
169-
170- // This method is called when the next column width is restored.
171- @action
172- onNextColumnWidthRestored ( imposedWidthDelta : number ) : void {
173- this . setPxWidth ( ( this . pxWidth ?? 0 ) - imposedWidthDelta ) ;
174- }
175-
176278 @action
177279 restoreWidth ( ) : void {
178- this . width = this . originalWidth ;
179- this . imposedWidthDelta = 0 ;
280+ this . settleWidthDebts ( ) ;
180281
181- if ( this . key === undefined ) {
182- return ;
183- }
282+ this . width = this . originalWidth ?? this . width ;
184283 }
185284}
0 commit comments