Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/gitbook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@sindresorhus/fnv1a": "^3.1.0",
"@tailwindcss/container-queries": "^0.1.1",
"@tusbar/cache-control": "^1.0.2",
"@tanstack/react-virtual": "^3.13.12",
"ai": "^4.2.2",
"assert-never": "catalog:",
"bidc": "catalog:",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { type ClassValue, tcls } from '@/lib/tailwind';
import type { DocumentTableViewGrid } from '@gitbook/api';

import { tcls } from '@/lib/tailwind';

import { RecordColumnValue } from './RecordColumnValue';
import type { TableRecordKV, TableViewProps } from './Table';
import { getColumnWidth } from './ViewGrid';
Expand All @@ -13,12 +11,13 @@ export function RecordRow(
record: TableRecordKV;
autoSizedColumns: string[];
fixedColumns: string[];
className?: ClassValue;
}
) {
const { view, autoSizedColumns, fixedColumns, block, context } = props;
const { view, autoSizedColumns, fixedColumns, block, context, className } = props;

return (
<div className={styles.row} role="row">
<div className={tcls(styles.row, className)} role="row">
{view.columns.map((column) => {
const columnWidth = getColumnWidth({
column,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { tcls } from '@/lib/tailwind';
import type { DocumentTableViewGrid } from '@gitbook/api';
import { useWindowVirtualizer } from '@tanstack/react-virtual';
import React from 'react';
import { RecordRow } from './RecordRow';
import type { TableViewProps } from './Table';

/**
* Virtualized group of rows for tables with many records.
*/
export const VIRTUALIZATION_THRESHOLD = 200;
const ROW_ESTIMATE_PX = 40;

export function RowGroupVirtualized(
props: TableViewProps<DocumentTableViewGrid> & {
autoSizedColumns: string[];
fixedColumns: string[];
tableWidth: string;
}
) {
const { records, tableWidth, ...rest } = props;
const [isHydrated, setIsHydrated] = React.useState(false);
const [scrollMargin, setScrollMargin] = React.useState(0);
const parentRef = React.useRef<HTMLDivElement>(null);

React.useEffect(() => {
setIsHydrated(true);
}, []);

React.useEffect(() => {
if (!isHydrated) {
return;
}

const updateScrollMargin = () => {
if (!parentRef.current) {
return;
}

const rect = parentRef.current.getBoundingClientRect();
setScrollMargin(rect.top + window.scrollY);
};

updateScrollMargin();
window.addEventListener('resize', updateScrollMargin);

return () => {
window.removeEventListener('resize', updateScrollMargin);
};
}, [isHydrated]);

const rowVirtualizer = useWindowVirtualizer({
count: records.length,
estimateSize: () => ROW_ESTIMATE_PX,
overscan: 8,
scrollMargin,
});

if (!isHydrated) {
return (
<div role="rowgroup" className={tcls('flex', 'flex-col', tableWidth)}>
{records.map((record) => (
<RecordRow key={record[0]} record={record} records={records} {...rest} />
))}
</div>
);
}

return (
<div
ref={parentRef}
role="rowgroup"
className={tcls('relative', 'flex', 'flex-col', tableWidth)}
style={{ height: rowVirtualizer.getTotalSize() }}
>
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
const record = records[virtualRow.index];

if (!record) {
return null;
}

return (
<div
key={record[0]}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${virtualRow.start - scrollMargin}px)`,
}}
>
<RecordRow
records={records}
record={record}
className={virtualRow.index === 0 ? undefined : 'border-t'}
{...rest}
/>
</div>
);
})}
</div>
);
}
43 changes: 28 additions & 15 deletions packages/gitbook/src/components/DocumentView/Table/ViewGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { DocumentTableViewGrid } from '@gitbook/api';
import { tcls } from '@/lib/tailwind';

import { RecordRow } from './RecordRow';
import { RowGroupVirtualized, VIRTUALIZATION_THRESHOLD } from './RowGroupVirtualized';
import type { TableViewProps } from './Table';
import styles from './table.module.css';
import { getColumnAlignment } from './utils';
Expand All @@ -13,7 +14,7 @@ import { getColumnAlignment } from './utils';
3. Auto-size is turned off without setting a width, we then default to a fixed width of 100px
*/
export function ViewGrid(props: TableViewProps<DocumentTableViewGrid>) {
const { block, view, records, style, context } = props;
const { block, view, records, style, context, isOffscreen } = props;

/* Calculate how many columns are auto-sized vs fixed width */
const columnWidths = context.mode === 'print' ? undefined : view.columnWidths;
Expand All @@ -29,6 +30,9 @@ export function ViewGrid(props: TableViewProps<DocumentTableViewGrid>) {
(columnId) => (block.data.definition[columnId]?.title.trim().length ?? 0) > 0
);

const shouldVirtualize =
context.mode !== 'print' && !isOffscreen && records.length >= VIRTUALIZATION_THRESHOLD;

return (
<div className={tcls(style, styles.tableWrapper)}>
{/* Table */}
Expand Down Expand Up @@ -73,20 +77,29 @@ export function ViewGrid(props: TableViewProps<DocumentTableViewGrid>) {
</div>
</div>
)}
<div
role="rowgroup"
className={tcls('flex', 'flex-col', tableWidth, '[&>*+*]:border-t')}
>
{records.map((record) => (
<RecordRow
key={record[0]}
record={record}
autoSizedColumns={autoSizedColumns}
fixedColumns={fixedColumns}
{...props}
/>
))}
</div>
{shouldVirtualize ? (
<RowGroupVirtualized
autoSizedColumns={autoSizedColumns}
fixedColumns={fixedColumns}
tableWidth={tableWidth}
{...props}
/>
) : (
<div
role="rowgroup"
className={tcls('flex', 'flex-col', tableWidth, '[&>*+*]:border-t')}
>
{records.map((record) => (
<RecordRow
key={record[0]}
record={record}
autoSizedColumns={autoSizedColumns}
fixedColumns={fixedColumns}
{...props}
/>
))}
</div>
)}
</div>
</div>
);
Expand Down
Loading