11import { DocumentTableViewGrid } from '@gitbook/api' ;
2+ import * as React from 'react' ;
23
34import { tcls } from '@/lib/tailwind' ;
45
@@ -7,159 +8,106 @@ import { TableViewProps } from './Table';
78import styles from './table.module.css' ;
89import { getColumnAlignment } from './utils' ;
910
11+ /* Columns are sized in 3 ways:
12+ 1. Set to auto-size by default, these columns share the available width
13+ 2. Explicitly set by the user by dragging column separator (we then turn off auto-size)
14+ 3. Auto-size is turned off without setting a width, we then default to a fixed width of 100px
15+ */
1016export function ViewGrid ( props : TableViewProps < DocumentTableViewGrid > ) {
1117 const { block, view, records, style } = props ;
12- const columnsOverThreshold = view . columns . length >= 7 ;
1318
14- const tableWrapper = columnsOverThreshold
15- ? [
16- // has over X columns
17- 'overflow-x-auto' ,
18- 'overflow-y-hidden' ,
19- 'mx-auto' ,
20- 'rounded-md' ,
21- 'border' ,
22- 'border-dark/3' ,
23- 'dark:border-light/2' ,
24- ]
25- : [ 'overflow-x-auto' , 'overflow-y-hidden' , 'mx-auto' ] ;
19+ /* Calculate how many columns are auto-sized vs fixed width */
20+ const columnWidths = view . columnWidths ;
21+ const autoSizedColumns = view . columns . filter ( ( column ) => ! columnWidths ?. [ column ] ) ;
22+ const fixedColumns = view . columns . filter ( ( column ) => columnWidths ?. [ column ] ) ;
2623
27- const tableTR = columnsOverThreshold
28- ? [ '[&>*+*]:border-l' , '[&>*]:px-4' ]
29- : [ '[&>*+*]:border-l' , '[&>*+*]:px-4' ] ;
24+ const tableWidth = autoSizedColumns . length > 0 ? 'w-full' : 'w-fit' ;
3025
31- const tableTH = columnsOverThreshold ? [ 'py-3' ] : [ 'py-1' , 'pt-0' ] ;
32-
33- // Only show the header when configured and not empty
26+ /* Only show the header when configured and not empty */
3427 const withHeader =
3528 ! view . hideHeader &&
3629 view . columns . some ( ( columnId ) => block . data . definition [ columnId ] . title . trim ( ) . length > 0 ) ;
3730
3831 return (
39- < div
40- className = { `${ tcls ( style , 'relative' , 'grid' , tableWrapper , styles . progressContainer ) } ` }
41- >
42- { /* ProgressScroller: */ }
43- < div
44- className = { `${ styles . progressOpacitySharp } ${ tcls (
45- 'grid' ,
46- 'items-center' ,
47- 'grid-area-1-1' ,
48- 'w-[5rem]' ,
49- 'h-full' ,
50- 'top-0' ,
51- 'z-[1]' ,
52- 'sticky' ,
53- 'left-[calc(100%-5rem)]' ,
54- 'pointer-events-none' ,
55- ) } `}
56- >
57- < svg
58- className = { `${ styles . progressSvg } ${ tcls (
59- 'grid-area-1-1' ,
60- 'relative' ,
61- '[strokeDasharray:_0_100]' ,
62- 'z-[1]' ,
63- 'w-7' ,
64- 'mt-3' ,
65- 'mr-3' ,
66- 'self-start' ,
67- 'justify-self-end' ,
68- 'stroke-primary-600' ,
69- 'shadow-1xs' ,
70- 'bg-light' ,
71- 'ring-1' ,
72- 'ring-inset' ,
73- 'rounded-full' ,
74- 'ring-dark/2' ,
75- 'dark:ring-light/2' ,
76- 'dark:bg-dark' ,
77- 'dark:stroke-primary-400' ,
78- ) } `}
79- preserveAspectRatio = "xMaxYMid meet"
80- width = "100%"
81- viewBox = "0 0 26 26"
82- fill = "none"
83- xmlns = "http://www.w3.org/2000/svg"
84- >
85- < circle
86- cx = "13"
87- className = { `${ styles . strokeOpacityProgressInverted } ` }
88- cy = "13"
89- r = "12.5"
90- fill = "none"
91- stroke = "inherit"
92- strokeWidth = "1.5"
93- pathLength = "100"
94- strokeLinecap = "round"
95- strokeOpacity = { 0 }
96- />
97-
98- < path
99- strokeDasharray = "none"
100- d = "M12 10L15 13L12 16"
101- stroke = "inherit"
102- fill = "none"
103- strokeOpacity = { 0.64 }
104- />
105- </ svg >
106-
107- < div
108- className = { `${ styles . progressOpacity } ${ tcls (
109- 'bg-gradient-to-r' ,
110- 'from-transparent' ,
111- 'to-light' ,
112- 'to-40%' ,
113- 'grid-area-1-1' ,
114- 'w-full' ,
115- 'h-full' ,
116- 'dark:from-transparent' ,
117- 'dark:to-dark/10' ,
118- ) } `}
119- > </ div >
120- </ div >
121-
122- { /* Table: */ }
123- < table className = { tcls ( 'w-full' , 'grid-area-1-1' , 'table-auto' ) } >
124- { withHeader ? (
125- < thead >
126- < tr className = { tcls ( tableTR ) } >
32+ < div className = { tcls ( style , styles . tableWrapper ) } >
33+ { /* Table */ }
34+ < div role = "table" className = { tcls ( 'flex' , 'flex-col' ) } >
35+ { /* Header */ }
36+ { withHeader && (
37+ < div role = "rowgroup" className = { tcls ( 'flex flex-col' , tableWidth ) } >
38+ < div role = "row" className = { tcls ( 'flex' , 'w-full' , '[&>*+*]:border-l' ) } >
12739 { view . columns . map ( ( column ) => {
128- const columnWidth = view . columnWidths ?. [ column ] ;
12940 const alignment = getColumnAlignment ( block . data . definition [ column ] ) ;
130-
13141 return (
132- < th
42+ < div
13343 key = { column }
44+ role = "columnheader"
13445 className = { tcls (
135- tableTH ,
136- 'align-middle' ,
137- 'text-balance' ,
138- 'border-b' ,
139- 'border-b-dark/5' ,
140- 'text-left' ,
141- 'text-xs' ,
142- 'lg:text-base' ,
143- 'dark:border-l-light/2' ,
144- 'dark:border-b-light/4' ,
46+ styles . columnHeader ,
14547 alignment === 'right' ? 'text-right' : null ,
14648 alignment === 'center' ? 'text-center' : null ,
14749 ) }
148- style = { columnWidth ? { width : columnWidth } : undefined }
50+ style = { {
51+ width : getColumnWidth ( {
52+ column,
53+ columnWidths,
54+ autoSizedColumns,
55+ fixedColumns,
56+ } ) ,
57+ minWidth : columnWidths ?. [ column ] || '100px' ,
58+ } }
59+ title = { block . data . definition [ column ] . title }
14960 >
15061 { block . data . definition [ column ] . title }
151- </ th >
62+ </ div >
15263 ) ;
15364 } ) }
154- </ tr >
155- </ thead >
156- ) : null }
157- < tbody className = { tcls ( '[&>*+*]:border-t' ) } >
158- { records . map ( ( record ) => {
159- return < RecordRow key = { record [ 0 ] } { ...props } record = { record } /> ;
160- } ) }
161- </ tbody >
162- </ table >
65+ </ div >
66+ </ div >
67+ ) }
68+ < div
69+ role = "rowgroup"
70+ className = { tcls ( 'flex' , 'flex-col' , tableWidth , '[&>*+*]:border-t' ) }
71+ >
72+ { records . map ( ( record ) => (
73+ < RecordRow
74+ key = { record [ 0 ] }
75+ record = { record }
76+ autoSizedColumns = { autoSizedColumns }
77+ fixedColumns = { fixedColumns }
78+ { ...props }
79+ />
80+ ) ) }
81+ </ div >
82+ </ div >
16383 </ div >
16484 ) ;
16585}
86+
87+ export const getColumnWidth = ( {
88+ column,
89+ columnWidths,
90+ autoSizedColumns,
91+ fixedColumns,
92+ } : {
93+ column : string ;
94+ columnWidths : Record < string , number > | undefined ;
95+ autoSizedColumns : string [ ] ;
96+ fixedColumns : string [ ] ;
97+ } ) => {
98+ const columnWidth = columnWidths ?. [ column ] ;
99+
100+ /* Column was explicitly set by user or user turned off auto-sizing (in that case, columnWidth should've also been set to 100px) */
101+ if ( columnWidth ) return `${ columnWidth } px` ;
102+
103+ /* Fallback minimum width for columns, so the columns don't become unreadable from being too narrow and instead table will become scrollable. */
104+ const minAutoColumnWidth = '100px' ;
105+
106+ const totalFixedWidth = fixedColumns . reduce ( ( sum , col ) => {
107+ return sum + ( columnWidths ?. [ col ] || 0 ) ;
108+ } , 0 ) ;
109+
110+ /* Column should use auto-sizing, which means it grows to fill available space */
111+ const availableWidth = `calc((100% - ${ totalFixedWidth } px) / ${ autoSizedColumns . length } )` ;
112+ return `clamp(${ minAutoColumnWidth } , ${ availableWidth } , 100%)` ;
113+ } ;
0 commit comments