@@ -17,7 +17,12 @@ Table.defineProperty(Table, "columns", {default = {}, type = "table", canTrigger
1717 if type (col ) == " string" then
1818 t [i ] = {name = col , width = # col + 1 }
1919 elseif type (col ) == " table" then
20- t [i ] = {name = col .name or " " , width = col .width or # col .name + 1 }
20+ t [i ] = {
21+ name = col .name or " " ,
22+ width = col .width , -- Can be number, "auto", or percentage like "30%"
23+ minWidth = col .minWidth or 3 ,
24+ maxWidth = col .maxWidth or nil
25+ }
2126 end
2227 end
2328 return t
@@ -44,6 +49,8 @@ Table.defineProperty(Table, "sortColumn", {default = nil, type = "number", canTr
4449Table .defineProperty (Table , " sortDirection" , {default = " asc" , type = " string" , canTriggerRender = true })
4550--- @property scrollOffset number 0 Current scroll position
4651Table .defineProperty (Table , " scrollOffset" , {default = 0 , type = " number" , canTriggerRender = true })
52+ --- @property customSortFunction table {} Custom sort functions for columns
53+ Table .defineProperty (Table , " customSortFunction" , {default = {}, type = " table" })
4754
4855Table .defineEvent (Table , " mouse_click" )
4956Table .defineEvent (Table , " mouse_scroll" )
@@ -95,6 +102,140 @@ function Table:addData(...)
95102 return self
96103end
97104
105+ --- Sets a custom sort function for a specific column
106+ --- @shortDescription Sets a custom sort function for a column
107+ --- @param columnIndex number The index of the column
108+ --- @param sortFn function Function that takes (rowA , rowB ) and returns comparison result
109+ --- @return Table self The Table instance
110+ function Table :setColumnSortFunction (columnIndex , sortFn )
111+ local customSorts = self .get (" customSortFunction" )
112+ customSorts [columnIndex ] = sortFn
113+ self .set (" customSortFunction" , customSorts )
114+ return self
115+ end
116+
117+ --- Adds data with both display and sort values
118+ --- @shortDescription Adds formatted data with raw sort values
119+ --- @param displayData table The formatted data for display
120+ --- @param sortData table The raw data for sorting (optional )
121+ --- @return Table self The Table instance
122+ function Table :setFormattedData (displayData , sortData )
123+ local enrichedData = {}
124+
125+ for i , row in ipairs (displayData ) do
126+ local enrichedRow = {}
127+ for j , cell in ipairs (row ) do
128+ enrichedRow [j ] = cell
129+ end
130+
131+ if sortData and sortData [i ] then
132+ enrichedRow ._sortValues = sortData [i ]
133+ end
134+
135+ table.insert (enrichedData , enrichedRow )
136+ end
137+
138+ self .set (" data" , enrichedData )
139+ return self
140+ end
141+
142+ --- Set data with automatic formatting
143+ --- @shortDescription Sets table data with optional column formatters
144+ --- @param rawData table The raw data array
145+ --- @param formatters table Optional formatter functions for columns {[2] = function (value ) return value end }
146+ --- @return Table self The Table instance
147+ function Table :setData (rawData , formatters )
148+ if not formatters then
149+ self .set (" data" , rawData )
150+ return self
151+ end
152+
153+ local formattedData = {}
154+ for i , row in ipairs (rawData ) do
155+ local formattedRow = {}
156+ for j , cell in ipairs (row ) do
157+ if formatters [j ] then
158+ formattedRow [j ] = formatters [j ](cell )
159+ else
160+ formattedRow [j ] = cell
161+ end
162+ end
163+ table.insert (formattedData , formattedRow )
164+ end
165+
166+ return self :setFormattedData (formattedData , rawData )
167+ end
168+
169+ --- @shortDescription Calculates column widths for rendering
170+ --- @param columns table The column definitions
171+ --- @param totalWidth number The total available width
172+ --- @return table The columns with calculated visibleWidth
173+ --- @private
174+ function Table :calculateColumnWidths (columns , totalWidth )
175+ local calculatedColumns = {}
176+ local remainingWidth = totalWidth
177+ local autoColumns = {}
178+ local fixedWidth = 0
179+
180+ for i , col in ipairs (columns ) do
181+ calculatedColumns [i ] = {
182+ name = col .name ,
183+ width = col .width ,
184+ minWidth = col .minWidth or 3 ,
185+ maxWidth = col .maxWidth
186+ }
187+ if type (col .width ) == " number" then
188+ calculatedColumns [i ].visibleWidth = math.max (col .width , calculatedColumns [i ].minWidth )
189+ if calculatedColumns [i ].maxWidth then
190+ calculatedColumns [i ].visibleWidth = math.min (calculatedColumns [i ].visibleWidth , calculatedColumns [i ].maxWidth )
191+ end
192+ remainingWidth = remainingWidth - calculatedColumns [i ].visibleWidth
193+ fixedWidth = fixedWidth + calculatedColumns [i ].visibleWidth
194+ elseif type (col .width ) == " string" and col .width :match (" %%$" ) then
195+ local percent = tonumber (col .width :match (" (%d+)%%" ))
196+ if percent then
197+ calculatedColumns [i ].visibleWidth = math.floor (totalWidth * percent / 100 )
198+ calculatedColumns [i ].visibleWidth = math.max (calculatedColumns [i ].visibleWidth , calculatedColumns [i ].minWidth )
199+ if calculatedColumns [i ].maxWidth then
200+ calculatedColumns [i ].visibleWidth = math.min (calculatedColumns [i ].visibleWidth , calculatedColumns [i ].maxWidth )
201+ end
202+ remainingWidth = remainingWidth - calculatedColumns [i ].visibleWidth
203+ fixedWidth = fixedWidth + calculatedColumns [i ].visibleWidth
204+ else
205+ table.insert (autoColumns , i )
206+ end
207+ else
208+ table.insert (autoColumns , i )
209+ end
210+ end
211+
212+ if # autoColumns > 0 and remainingWidth > 0 then
213+ local autoWidth = math.floor (remainingWidth / # autoColumns )
214+ for _ , colIndex in ipairs (autoColumns ) do
215+ calculatedColumns [colIndex ].visibleWidth = math.max (autoWidth , calculatedColumns [colIndex ].minWidth )
216+ if calculatedColumns [colIndex ].maxWidth then
217+ calculatedColumns [colIndex ].visibleWidth = math.min (calculatedColumns [colIndex ].visibleWidth , calculatedColumns [colIndex ].maxWidth )
218+ end
219+ end
220+ end
221+
222+ local totalCalculated = 0
223+ for i , col in ipairs (calculatedColumns ) do
224+ totalCalculated = totalCalculated + (col .visibleWidth or 0 )
225+ end
226+
227+ if totalCalculated > totalWidth then
228+ local scale = totalWidth / totalCalculated
229+ for i , col in ipairs (calculatedColumns ) do
230+ if col .visibleWidth then
231+ col .visibleWidth = math.max (1 , math.floor (col .visibleWidth * scale ))
232+ end
233+ end
234+ end
235+
236+ return calculatedColumns
237+ end
238+
98239--- Sorts the table data by column
99240--- @shortDescription Sorts the table data by the specified column
100241--- @param columnIndex number The index of the column to sort by
@@ -103,17 +244,47 @@ end
103244function Table :sortData (columnIndex , fn )
104245 local data = self .get (" data" )
105246 local direction = self .get (" sortDirection" )
106- if not fn then
247+ local customSorts = self .get (" customSortFunction" )
248+
249+ local sortFn = fn or customSorts [columnIndex ]
250+
251+ if sortFn then
107252 table.sort (data , function (a , b )
108- if direction == " asc" then
109- return a [columnIndex ] < b [columnIndex ]
110- else
111- return a [columnIndex ] > b [columnIndex ]
112- end
253+ return sortFn (a , b , direction )
113254 end )
114255 else
115256 table.sort (data , function (a , b )
116- return fn (a [columnIndex ], b [columnIndex ])
257+ if not a or not b then return false end
258+
259+ local valueA , valueB
260+
261+ if a ._sortValues and a ._sortValues [columnIndex ] then
262+ valueA = a ._sortValues [columnIndex ]
263+ else
264+ valueA = a [columnIndex ]
265+ end
266+
267+ if b ._sortValues and b ._sortValues [columnIndex ] then
268+ valueB = b ._sortValues [columnIndex ]
269+ else
270+ valueB = b [columnIndex ]
271+ end
272+
273+ if type (valueA ) == " number" and type (valueB ) == " number" then
274+ if direction == " asc" then
275+ return valueA < valueB
276+ else
277+ return valueA > valueB
278+ end
279+ else
280+ local strA = tostring (valueA or " " )
281+ local strB = tostring (valueB or " " )
282+ if direction == " asc" then
283+ return strA < strB
284+ else
285+ return strA > strB
286+ end
287+ end
117288 end )
118289 end
119290 return self
@@ -131,9 +302,14 @@ function Table:mouse_click(button, x, y)
131302 local relX , relY = self :getRelativePosition (x , y )
132303
133304 if relY == 1 then
305+ local columns = self .get (" columns" )
306+ local width = self .get (" width" )
307+ local calculatedColumns = self :calculateColumnWidths (columns , width )
308+
134309 local currentX = 1
135- for i , col in ipairs (self .get (" columns" )) do
136- if relX >= currentX and relX < currentX + col .width then
310+ for i , col in ipairs (calculatedColumns ) do
311+ local colWidth = col .visibleWidth or col .width or 10
312+ if relX >= currentX and relX < currentX + colWidth then
137313 if self .get (" sortColumn" ) == i then
138314 self .set (" sortDirection" , self .get (" sortDirection" ) == " asc" and " desc" or " asc" )
139315 else
@@ -143,7 +319,7 @@ function Table:mouse_click(button, x, y)
143319 self :sortData (i )
144320 break
145321 end
146- currentX = currentX + col . width
322+ currentX = currentX + colWidth
147323 end
148324 end
149325
@@ -189,24 +365,20 @@ function Table:render()
189365 local height = self .get (" height" )
190366 local width = self .get (" width" )
191367
368+ local calculatedColumns = self :calculateColumnWidths (columns , width )
369+
192370 local totalWidth = 0
193- local lastVisibleColumn = # columns
194- for i , col in ipairs (columns ) do
195- if totalWidth + col .width > width then
196- if i == 1 then
197- col .visibleWidth = width
198- else
199- col .visibleWidth = width - totalWidth
200- lastVisibleColumn = i
201- end
371+ local lastVisibleColumn = # calculatedColumns
372+ for i , col in ipairs (calculatedColumns ) do
373+ if totalWidth + col .visibleWidth > width then
374+ lastVisibleColumn = i - 1
202375 break
203376 end
204- col .visibleWidth = col .width
205- totalWidth = totalWidth + col .width
377+ totalWidth = totalWidth + col .visibleWidth
206378 end
207379
208380 local currentX = 1
209- for i , col in ipairs (columns ) do
381+ for i , col in ipairs (calculatedColumns ) do
210382 if i > lastVisibleColumn then break end
211383 local text = col .name
212384 if i == sortCol then
@@ -224,7 +396,7 @@ function Table:render()
224396 currentX = 1
225397 local bg = (rowIndex + 1 ) == selected and self .get (" selectedColor" ) or self .get (" background" )
226398
227- for i , col in ipairs (columns ) do
399+ for i , col in ipairs (calculatedColumns ) do
228400 if i > lastVisibleColumn then break end
229401 local cellText = tostring (rowData [i ] or " " )
230402 local paddedText = cellText .. string.rep (" " , col .visibleWidth - # cellText )
0 commit comments