mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-28 13:08:57 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 39150d5a8f |
@@ -36,6 +36,11 @@ export interface DocumentContext {
|
||||
* @default false
|
||||
*/
|
||||
withLinkPreviews?: boolean;
|
||||
|
||||
/**
|
||||
* Optional table row search query.
|
||||
*/
|
||||
tableSearchQuery?: string;
|
||||
}
|
||||
|
||||
export interface DocumentContextProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DocumentBlockTable, DocumentTableRecord } from '@gitbook/api';
|
||||
import type { DocumentBlockTable } from '@gitbook/api';
|
||||
import assertNever from 'assert-never';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
@@ -9,8 +9,9 @@ import { StickyViewGrid } from './StickyViewGrid';
|
||||
import { ViewCards } from './ViewCards';
|
||||
import { ViewGrid, ViewGridHeader } from './ViewGrid';
|
||||
import { getViewGridLayout, hasVisibleHeader } from './layout';
|
||||
import { type TableRecordKV, filterTableRecordsBySearchTerm } from './search';
|
||||
|
||||
export type TableRecordKV = [string, DocumentTableRecord];
|
||||
export type { TableRecordKV };
|
||||
|
||||
export interface TableViewProps<View> extends BlockProps<DocumentBlockTable> {
|
||||
view: View;
|
||||
@@ -22,9 +23,15 @@ export function Table(props: BlockProps<DocumentBlockTable>) {
|
||||
const { block, ancestorBlocks, document, context, style } = props;
|
||||
const isOffscreen = isBlockOffscreen({ block, ancestorBlocks, document });
|
||||
|
||||
const records: TableRecordKV[] = Object.entries(block.data.records).sort((a, b) => {
|
||||
return a[1].orderIndex.localeCompare(b[1].orderIndex);
|
||||
});
|
||||
const records: TableRecordKV[] = Object.entries(block.data.records).sort((a, b) =>
|
||||
a[1].orderIndex.localeCompare(b[1].orderIndex)
|
||||
);
|
||||
|
||||
const filteredRecords = filterTableRecordsBySearchTerm(
|
||||
block,
|
||||
records,
|
||||
context.tableSearchQuery
|
||||
);
|
||||
|
||||
switch (block.data.view.type) {
|
||||
case 'cards':
|
||||
@@ -32,7 +39,7 @@ export function Table(props: BlockProps<DocumentBlockTable>) {
|
||||
<ViewCards
|
||||
view={block.data.view}
|
||||
isOffscreen={isOffscreen}
|
||||
records={records}
|
||||
records={filteredRecords}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -41,7 +48,7 @@ export function Table(props: BlockProps<DocumentBlockTable>) {
|
||||
...props,
|
||||
view: block.data.view,
|
||||
isOffscreen,
|
||||
records,
|
||||
records: filteredRecords,
|
||||
};
|
||||
const { tableWidth } = getViewGridLayout({
|
||||
block,
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { DocumentBlockTable, DocumentTableRecord } from '@gitbook/api';
|
||||
|
||||
import { getNodeFragmentByName, getNodeText } from '@/lib/document';
|
||||
|
||||
export type TableRecordKV = [string, DocumentTableRecord];
|
||||
|
||||
export function filterTableRecordsBySearchTerm(
|
||||
block: DocumentBlockTable,
|
||||
records: TableRecordKV[],
|
||||
searchTerm: string | undefined
|
||||
): TableRecordKV[] {
|
||||
const trimmedSearchTerm = searchTerm?.trim();
|
||||
if (!trimmedSearchTerm) {
|
||||
return records;
|
||||
}
|
||||
|
||||
return records.filter(([, record]) =>
|
||||
block.data.view.columns.some((column) => {
|
||||
const text = getTableCellSearchText(block, record, column);
|
||||
return new RegExp(trimmedSearchTerm, 'iu').test(text);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function getTableCellSearchText(
|
||||
block: DocumentBlockTable,
|
||||
record: DocumentTableRecord,
|
||||
column: string
|
||||
): string {
|
||||
const definition = block.data.definition[column];
|
||||
const value = record.values[column];
|
||||
|
||||
if (!definition || value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch (definition.type) {
|
||||
case 'text': {
|
||||
if (typeof value !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const fragment = getNodeFragmentByName(block, value);
|
||||
return normalizeSearchText(fragment ? getNodeText(fragment) : '');
|
||||
}
|
||||
case 'select': {
|
||||
if (!Array.isArray(value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return normalizeSearchText(
|
||||
value
|
||||
.map((selectId) => {
|
||||
return (
|
||||
definition.options.find((option) => option.value === selectId)?.label ??
|
||||
selectId
|
||||
);
|
||||
})
|
||||
.join(' ')
|
||||
);
|
||||
}
|
||||
case 'number':
|
||||
case 'rating': {
|
||||
return typeof value === 'number' ? `${value}` : '';
|
||||
}
|
||||
case 'checkbox': {
|
||||
return typeof value === 'boolean' ? `${value}` : '';
|
||||
}
|
||||
case 'files':
|
||||
case 'users': {
|
||||
return Array.isArray(value) ? normalizeSearchText(value.join(' ')) : '';
|
||||
}
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeSearchText(text: string): string {
|
||||
return text.replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
Reference in New Issue
Block a user