@@ -22,28 +22,81 @@ const columnsWithSubRows = [
2222 "httpRealm" ,
2323] ;
2424
25+ const TableContainer = styled . div `
26+ overflow-x: "auto";
27+ ` ;
2528// styled-components allow us to create smaller customizable components
2629const StyledTable = styled . table `
30+
31+ border: 1px solid #8080804f;
32+ border-radius: 4px;
33+ margin-bottom: 4px;
34+
35+ tr {
36+ width: fit-content;
37+ height: 30px;
38+ }
39+
40+ th {
41+ position: relative;
42+ }
43+
44+ td {
45+ padding: 4px;
46+ height: 30px;
47+ position: relative;
48+ }
49+
2750 tbody tr {
2851 :nth-of-type(odd) {
2952 background-color: #f0f0f0;
3053 }
3154 :hover {
3255 background-color: lightgrey;
3356 }
57+
58+ th {
59+ padding: 2px 4px;
60+ position: relative;
61+ font-weight: bold;
62+ text-align: center;
63+ height: 30px;
64+ }
65+ } // end of tbody
66+
67+ .resizer {
68+ position: absolute;
69+ right: 0;
70+ top: 0;
71+ height: 100%;
72+ width: 5px;
73+ background: rgba(0, 0, 0, 0.5);
74+ cursor: col-resize;
75+ user-select: none;
76+ touch-action: none;
3477 }
3578
36- td {
37- padding: 4px;
79+ .resizer.isResizing {
80+ background: blue;
81+ opacity: 1;
3882 }
83+
84+ @media (hover: hover) {
85+ .resizer {
86+ opacity: 0;
87+ }
88+
89+ *:hover > .resizer {
90+ opacity: 1;
91+ }
3992` ;
4093
4194export default function DynamicTableView ( props ) {
4295 const [ columnFilters , setColumnFilters ] = React . useState ( [ ] ) ;
4396 const [ expanded , setExpanded ] = React . useState ( { } ) ;
4497 const [ globalFilter , setGlobalFilter ] = React . useState ( "" ) ;
4598
46- let records = JSON . parse ( stringify ( props . data ) ) ;
99+ let records = props . data ;
47100
48101 let tableKeys = new Set ( ) ;
49102 // Since each table comes with it's own schema, we just iterate over
@@ -104,6 +157,7 @@ export default function DynamicTableView(props) {
104157 const table = useReactTable ( {
105158 data,
106159 columns,
160+ columnResizeMode : "onChange" ,
107161 state : {
108162 columnFilters,
109163 globalFilter,
@@ -115,8 +169,8 @@ export default function DynamicTableView(props) {
115169 return row . tabs ;
116170 }
117171
118- // If the row in question has an object we return it
119- // to be rendered by the subcomponent
172+ // If the row has a nested object we return it
173+ // so renderSubComponent can render it within the cell
120174 for ( const key of columnsWithSubRows ) {
121175 if ( row [ key ] ) {
122176 return [ row [ key ] ] ;
@@ -137,144 +191,172 @@ export default function DynamicTableView(props) {
137191
138192 return (
139193 < div className = "p-2" >
140- < div className = "h-2" />
141- < StyledTable >
142- < thead >
143- { table . getHeaderGroups ( ) . map ( ( headerGroup ) => (
144- < tr key = { headerGroup . id } >
145- { headerGroup . headers . map ( ( header ) => {
146- return (
147- < th key = { header . id } colSpan = { header . colSpan } >
148- { header . isPlaceholder ? null : (
149- < >
150- < div >
151- { flexRender (
194+ < TableContainer >
195+ < StyledTable
196+ { ...{
197+ style : {
198+ width : table . getCenterTotalSize ( ) ,
199+ } ,
200+ } }
201+ >
202+ < thead >
203+ { table . getHeaderGroups ( ) . map ( ( headerGroup ) => (
204+ < tr key = { headerGroup . id } >
205+ { headerGroup . headers . map ( ( header ) => {
206+ return (
207+ < th
208+ { ...{
209+ key : header . id ,
210+ colSpan : header . colSpan ,
211+ style : {
212+ width : header . getSize ( ) ,
213+ } ,
214+ } }
215+ >
216+ { header . isPlaceholder
217+ ? null
218+ : flexRender (
152219 header . column . columnDef . header ,
153220 header . getContext ( )
154221 ) }
222+ < div
223+ { ...{
224+ onMouseDown : header . getResizeHandler ( ) ,
225+ onTouchStart : header . getResizeHandler ( ) ,
226+ className : `resizer ${
227+ header . column . getIsResizing ( ) ? "isResizing" : ""
228+ } `,
229+ } }
230+ />
231+ { header . column . getCanFilter ( ) ? (
232+ < div >
233+ < Filter header = { header } table = { table } />
155234 </ div >
156- { header . column . getCanFilter ( ) ? (
157- < div >
158- < Filter column = { header . column } table = { table } />
159- </ div >
160- ) : null }
161- </ >
162- ) }
163- </ th >
164- ) ;
165- } ) }
166- </ tr >
167- ) ) }
168- </ thead >
169- < tbody >
170- { table . getRowModel ( ) . rows . map ( ( row ) => {
171- return (
172- < Fragment key = { row . id } >
173- < tr >
174- { row . getVisibleCells ( ) . map ( ( cell ) => {
175- return (
176- < td key = { cell . id } >
177- { flexRender (
178- cell . column . columnDef . cell ,
179- cell . getContext ( )
180- ) }
181- </ td >
182- ) ;
183- } ) }
184- </ tr >
185- { row . getIsExpanded ( ) && (
235+ ) : null }
236+ </ th >
237+ ) ;
238+ } ) }
239+ </ tr >
240+ ) ) }
241+ </ thead >
242+ < tbody >
243+ { table . getRowModel ( ) . rows . map ( ( row ) => {
244+ return (
245+ < Fragment key = { row . id } >
186246 < tr >
187- { /* 2nd row is a custom 1 cell row */ }
188- < td colSpan = { row . getVisibleCells ( ) . length } >
189- { renderSubComponent ( { row } ) }
190- </ td >
247+ { row . getVisibleCells ( ) . map ( ( cell ) => {
248+ return (
249+ < td
250+ { ...{
251+ key : cell . id ,
252+ style : {
253+ width : cell . column . getSize ( ) ,
254+ } ,
255+ } }
256+ >
257+ { flexRender (
258+ cell . column . columnDef . cell ,
259+ cell . getContext ( )
260+ ) }
261+ </ td >
262+ ) ;
263+ } ) }
191264 </ tr >
192- ) }
193- </ Fragment >
194- ) ;
195- } ) }
196- </ tbody >
197- </ StyledTable >
198- < div className = "h-2" />
199- < div className = "flex items-center gap-2" >
200- < button
201- className = "border rounded p-1"
202- onClick = { ( ) => table . setPageIndex ( 0 ) }
203- disabled = { ! table . getCanPreviousPage ( ) }
204- >
205- { "<<" }
206- </ button >
207- < button
208- className = "border rounded p-1"
209- onClick = { ( ) => table . previousPage ( ) }
210- disabled = { ! table . getCanPreviousPage ( ) }
211- >
212- { "<" }
213- </ button >
214- < button
215- className = "border rounded p-1"
216- onClick = { ( ) => table . nextPage ( ) }
217- disabled = { ! table . getCanNextPage ( ) }
218- >
219- { ">" }
220- </ button >
221- < button
222- className = "border rounded p-1"
223- onClick = { ( ) => table . setPageIndex ( table . getPageCount ( ) - 1 ) }
224- disabled = { ! table . getCanNextPage ( ) }
225- >
226- { ">>" }
227- </ button >
228- < span className = "flex items-center gap-1" >
229- < div > Page</ div >
230- < strong >
231- { table . getState ( ) . pagination . pageIndex + 1 } of{ " " }
232- { table . getPageCount ( ) }
233- </ strong >
234- </ span >
235- < span className = "flex items-center gap-1" >
236- | Go to page:
237- < input
238- type = "number"
239- defaultValue = { table . getState ( ) . pagination . pageIndex + 1 }
265+ { row . getIsExpanded ( ) && (
266+ < tr >
267+ { /* 2nd row is a custom 1 cell row */ }
268+ < td colSpan = { row . getVisibleCells ( ) . length } >
269+ { renderSubComponent ( { row } ) }
270+ </ td >
271+ </ tr >
272+ ) }
273+ </ Fragment >
274+ ) ;
275+ } ) }
276+ </ tbody >
277+ </ StyledTable >
278+ < div className = "h-2" />
279+ < div className = "flex items-center gap-2" >
280+ < button
281+ className = "border rounded p-1"
282+ onClick = { ( ) => table . setPageIndex ( 0 ) }
283+ disabled = { ! table . getCanPreviousPage ( ) }
284+ >
285+ { "<<" }
286+ </ button >
287+ < button
288+ className = "border rounded p-1"
289+ onClick = { ( ) => table . previousPage ( ) }
290+ disabled = { ! table . getCanPreviousPage ( ) }
291+ >
292+ { "<" }
293+ </ button >
294+ < button
295+ className = "border rounded p-1"
296+ onClick = { ( ) => table . nextPage ( ) }
297+ disabled = { ! table . getCanNextPage ( ) }
298+ >
299+ { ">" }
300+ </ button >
301+ < button
302+ className = "border rounded p-1"
303+ onClick = { ( ) => table . setPageIndex ( table . getPageCount ( ) - 1 ) }
304+ disabled = { ! table . getCanNextPage ( ) }
305+ >
306+ { ">>" }
307+ </ button >
308+ < span className = "flex items-center gap-1" >
309+ < div > Page</ div >
310+ < strong >
311+ { table . getState ( ) . pagination . pageIndex + 1 } of{ " " }
312+ { table . getPageCount ( ) }
313+ </ strong >
314+ </ span >
315+ < span className = "flex items-center gap-1" >
316+ | Go to page:
317+ < input
318+ type = "number"
319+ defaultValue = { table . getState ( ) . pagination . pageIndex + 1 }
320+ onChange = { ( e ) => {
321+ const page = e . target . value ? Number ( e . target . value ) - 1 : 0 ;
322+ table . setPageIndex ( page ) ;
323+ } }
324+ className = "border p-1 rounded w-16"
325+ />
326+ </ span >
327+ < select
328+ value = { table . getState ( ) . pagination . pageSize }
240329 onChange = { ( e ) => {
241- const page = e . target . value ? Number ( e . target . value ) - 1 : 0 ;
242- table . setPageIndex ( page ) ;
330+ table . setPageSize ( Number ( e . target . value ) ) ;
243331 } }
244- className = "border p-1 rounded w-16"
245- />
246- </ span >
247- < select
248- value = { table . getState ( ) . pagination . pageSize }
249- onChange = { ( e ) => {
250- table . setPageSize ( Number ( e . target . value ) ) ;
251- } }
252- >
253- { [ 10 , 25 , 50 ] . map ( ( pageSize ) => (
254- < option key = { pageSize } value = { pageSize } >
255- Show { pageSize }
256- </ option >
257- ) ) }
258- </ select >
259- </ div >
260- < div > { table . getPrePaginationRowModel ( ) . rows . length } Rows</ div >
261- { /* Debug object, uncomment this to see all the values of the table in realtime */ }
262- { /* <pre>{JSON.stringify(table.getState(), null, 2)}</pre> */ }
332+ >
333+ { [ 10 , 25 , 50 ] . map ( ( pageSize ) => (
334+ < option key = { pageSize } value = { pageSize } >
335+ Show { pageSize }
336+ </ option >
337+ ) ) }
338+ </ select >
339+ </ div >
340+ < div > { table . getPrePaginationRowModel ( ) . rows . length } Rows</ div >
341+ { /* Debug object, uncomment this to see all the values of the table in realtime */ }
342+ { /* <pre>{JSON.stringify(table.getState(), null, 2)}</pre> */ }
343+ </ TableContainer >
263344 </ div >
264345 ) ;
265346}
266347
267- function Filter ( { column, table } ) {
348+ function Filter ( { header, table } ) {
349+ const { column, getSize } = header ;
268350 const columnFilterValue = column . getFilterValue ( ) ;
269351 return (
270352 < >
271353 < DebouncedInput
354+ style = { { width : getSize ( ) } }
272355 type = "text"
273356 autoComplete = "off"
274357 value = { columnFilterValue ?? "" }
275358 onChange = { ( value ) => column . setFilterValue ( value ) }
276359 placeholder = { `Search... (${ column . getFacetedUniqueValues ( ) . size } )` }
277- className = "w-36 border shadow rounded"
278360 list = { column . id + "list" }
279361 />
280362 < div className = "h-1" />
0 commit comments