@@ -7,7 +7,7 @@ import { PseudoItem } from '../../documents/items/pseudo-item.mjs';
77 * @template {Object} D the document of the sheet being rendered
88 * @template {Object} T the type of the items in the table
99 * @property {string, (() => string) } cssClass
10- * @property {"item", "effect" } [tablePreset="item"]
10+ * @property {"item", "effect", "custom" } [tablePreset="item"]
1111 * @property {(document: D, options: FUTableRendererRenderOptions) => T[] } getItems
1212 * @property {boolean, ((a: D, b: D) => number) } [sort=true] sorting function to determine the order of entries, true means sort using foundry sort order, false means don't sort
1313 * @property {(element: HTMLElement) => void } activateListeners
@@ -17,6 +17,7 @@ import { PseudoItem } from '../../documents/items/pseudo-item.mjs';
1717 * @property {Record<string, ColumnConfig<T>> } columns
1818 * @property {Record<string, ((event: PointerEvent, target: HTMLElement) => void)> } actions
1919 * @property {DragDropConfiguration[] } [dragDrop]
20+ * @property {AdvancedTableConfig<T> } [advancedConfig]
2021 */
2122
2223/**
@@ -29,6 +30,24 @@ import { PseudoItem } from '../../documents/items/pseudo-item.mjs';
2930 * @property {string, ((T) => string | Promise<string>) } renderCell
3031 */
3132
33+ /**
34+ * @typedef AdvancedTableConfig
35+ * @template T
36+ * @property {(T) => string | number } getKey
37+ * @property {string } [keyDataAttribute] the data attribute representing each rows key, defaults to 'data-key'
38+ * @property {AdditionalRowAttribute<T>[] } additionalRowAttributes
39+ * @property {string } tableClass
40+ * @property {string } rowClass
41+ * @property {boolean } draggable defaults to false
42+ */
43+
44+ /**
45+ * @template T
46+ * @typedef AdditionalRowAttribute
47+ * @property {string } attributeName
48+ * @property {(T) => string } getAttributeValue
49+ */
50+
3251export class FUTableRenderer {
3352 /**
3453 * @type TableConfig
@@ -92,6 +111,38 @@ export class FUTableRenderer {
92111
93112 return new foundry . applications . ux . DragDrop . implementation ( dragDropConfig ) ;
94113 } ) ;
114+ config . tablePreset ??= 'item' ;
115+ if ( config . tablePreset === 'item' ) {
116+ config . advancedConfig = {
117+ getKey : ( item ) => item . uuid ,
118+ keyDataAttribute : 'data-uuid' ,
119+ additionalRowAttributes : [ { attributeName : 'data-item-id' , getAttributeValue : ( item ) => item . id } ] ,
120+ tableClass : 'item-list' ,
121+ rowClass : 'item' ,
122+ draggable : true ,
123+ } ;
124+ } else if ( config . tablePreset === 'effect' ) {
125+ config . advancedConfig = {
126+ getKey : ( effect ) => effect . uuid ,
127+ keyDataAttribute : 'data-uuid' ,
128+ additionalRowAttributes : [ { attributeName : 'data-effect-id' , getAttributeValue : ( item ) => item . id } ] ,
129+ tableClass : '' ,
130+ rowClass : '' ,
131+ draggable : false ,
132+ } ;
133+ } else {
134+ const advancedConfig = config . advancedConfig ;
135+ advancedConfig . getKey = advancedConfig . getKey . bind ( this ) ;
136+ advancedConfig . keyDataAttribute ??= 'data-key' ;
137+ if ( ! advancedConfig . keyDataAttribute . startsWith ( 'data-' ) ) {
138+ advancedConfig . keyDataAttribute = `data-${ advancedConfig . keyDataAttribute } ` ;
139+ }
140+ advancedConfig . additionalRowAttributes ??= [ ] ;
141+ advancedConfig . additionalRowAttributes . forEach ( ( value ) => ( value . getAttributeValue = value . getAttributeValue . bind ( this ) ) ) ;
142+ advancedConfig . tableClass ??= '' ;
143+ advancedConfig . rowClass ??= '' ;
144+ advancedConfig . draggable ??= false ;
145+ }
95146
96147 this . initializeOptions ( config ) ;
97148
@@ -133,7 +184,7 @@ export class FUTableRenderer {
133184 const descriptions = { } ;
134185 const rowCssClasses = { } ;
135186 const rowTooltips = { } ;
136- const { getItems, tablePreset, sort, columns : columnConfigs = { } , cssClass, renderDescription, renderRowCaption, hideIfEmpty : configHideIfEmpty } = this . tableConfig ;
187+ const { getItems, tablePreset, sort, columns : columnConfigs = { } , cssClass, renderDescription, renderRowCaption, hideIfEmpty : configHideIfEmpty , advancedConfig } = this . tableConfig ;
137188
138189 const items = getItems ( document , options ) ;
139190
@@ -163,32 +214,42 @@ export class FUTableRenderer {
163214 } ;
164215 }
165216
217+ const rows = [ ] ;
166218 for ( let item of items ) {
167- const uuid = item . uuid ;
219+ const rowKey = advancedConfig . getKey ( item ) ;
168220
169- if ( document !== item . parent && document !== item . parentDocument ) {
170- let directParentItem = item . parent ;
171- while ( ! ( directParentItem instanceof FUItem || directParentItem instanceof PseudoItem ) ) {
172- directParentItem = directParentItem . parent ;
173- }
174- let parentItem = directParentItem ;
175- let parentage = [ ] ;
176- while ( ! ( parentItem instanceof Actor || parentItem == null ) ) {
177- if ( parentItem instanceof FUItem || parentItem instanceof PseudoItem ) {
178- parentage . unshift ( parentItem ) ;
221+ if ( tablePreset !== 'custom' ) {
222+ if ( document !== item . parent && document !== item . parentDocument ) {
223+ let directParentItem = item . parent ;
224+ while ( ! ( directParentItem instanceof FUItem || directParentItem instanceof PseudoItem ) ) {
225+ directParentItem = directParentItem . parent ;
226+ }
227+ let parentItem = directParentItem ;
228+ let parentage = [ ] ;
229+ while ( ! ( parentItem instanceof Actor || parentItem == null ) ) {
230+ if ( parentItem instanceof FUItem || parentItem instanceof PseudoItem ) {
231+ parentage . unshift ( parentItem ) ;
232+ }
233+ parentItem = parentItem . parent ;
179234 }
180- parentItem = parentItem . parent ;
235+ parentage = parentage . map ( ( item ) => item . name ) . join ( ' → ' ) ;
236+ rowCssClasses [ rowKey ] = 'fu-table__row--deeply-nested' ;
237+ rowTooltips [ rowKey ] = game . i18n . format ( 'FU.ItemDeeplyNested' , { parent : parentage } ) ;
181238 }
182- parentage = parentage . map ( ( item ) => item . name ) . join ( ' → ' ) ;
183- rowCssClasses [ uuid ] = 'fu-table__row--deeply-nested' ;
184- rowTooltips [ uuid ] = game . i18n . format ( 'FU.ItemDeeplyNested' , { parent : parentage } ) ;
185239 }
186240
187241 for ( let [ columnKey , columnConfig ] of Object . entries ( columnConfigs ) ) {
188- columns [ columnKey ] . cells [ uuid ] = columnConfig . renderCell instanceof Function ? columnConfig . renderCell ( item ) : columnConfig . renderCell ;
242+ columns [ columnKey ] . cells [ rowKey ] = columnConfig . renderCell instanceof Function ? columnConfig . renderCell ( item ) : columnConfig . renderCell ;
189243 }
190- rowCaptions [ uuid ] = rowCaptionRenderer ( item ) ;
191- descriptions [ uuid ] = descriptionRenderer ( item ) ;
244+ rowCaptions [ rowKey ] = rowCaptionRenderer ( item ) ;
245+ descriptions [ rowKey ] = descriptionRenderer ( item ) ;
246+
247+ const additionalAttributes = { } ;
248+ for ( const { attributeName, getAttributeValue } of advancedConfig . additionalRowAttributes ) {
249+ additionalAttributes [ attributeName ] = getAttributeValue ( item ) ;
250+ }
251+
252+ rows . push ( { key : rowKey , item, additionalAttributes } ) ;
192253 }
193254
194255 for ( const column of Object . values ( columns ) ) {
@@ -206,27 +267,10 @@ export class FUTableRenderer {
206267 descriptions [ key ] = await value ;
207268 }
208269
209- let presets ;
210- if ( tablePreset === 'effect' ) {
211- presets = {
212- dataTypeId : 'data-effect-id' ,
213- tableClass : '' ,
214- rowClass : '' ,
215- draggable : false ,
216- } ;
217- } else {
218- presets = {
219- dataTypeId : 'data-item-id' ,
220- tableClass : 'item-list' ,
221- rowClass : 'item' ,
222- draggable : true ,
223- } ;
224- }
225-
226270 return foundry . applications . handlebars . renderTemplate ( 'systems/projectfu/templates/table/fu-table.hbs' , {
227271 tableId : this . #tableId,
228- presets ,
229- items,
272+ config : advancedConfig ,
273+ items : rows ,
230274 cssClass : cssClass instanceof Function ? cssClass ( ) : cssClass ,
231275 columns,
232276 rowCssClasses,
@@ -265,14 +309,15 @@ export class FUTableRenderer {
265309 #onClick( event ) {
266310 const table = event . target . closest ( `[data-table-id="${ this . #tableId} "]` ) ;
267311 if ( table ) {
268- const row = event . target . closest ( `.fu-table__row-container[data-uuid]` ) ;
312+ const keyDataAttribute = this . tableConfig . advancedConfig . keyDataAttribute ;
313+ const row = event . target . closest ( `.fu-table__row-container[${ keyDataAttribute } ]` ) ;
269314 const actionElement = event . target . closest ( '[data-action]' ) ;
270315 const contextMenuTrigger = event . target . closest ( `[data-context-menu]` ) ;
271316 if ( event . button === 0 && row && ! actionElement && ! contextMenuTrigger ) {
272- const uuid = row . dataset . uuid ;
317+ const rowKey = row . dataset [ this . #convertToDatasetKey ( keyDataAttribute ) ] ;
273318 const expand = row . querySelector ( '.fu-table__row-expand' ) ;
274319 if ( expand ) {
275- this . #expandedItems[ uuid ] = expand . classList . toggle ( 'fu-table__row-expand--visible' ) ;
320+ this . #expandedItems[ rowKey ] = expand . classList . toggle ( 'fu-table__row-expand--visible' ) ;
276321 }
277322 return ;
278323 }
@@ -285,4 +330,12 @@ export class FUTableRenderer {
285330 }
286331 }
287332 }
333+
334+ #convertToDatasetKey( keyDataAttribute ) {
335+ return keyDataAttribute
336+ . substring ( 5 ) //strip 'data-' prefix
337+ . split ( '-' ) // split at dashes
338+ . map ( ( value , index ) => ( index > 0 ? value . capitalize ( ) : value ) ) // capitalize parts beyond first
339+ . join ( '' ) ; // join parts
340+ }
288341}
0 commit comments