added react-virtuloso and tenstack-table
This commit is contained in:
parent
3a9d45c543
commit
8e01d523d0
8 changed files with 161 additions and 227 deletions
|
|
@ -20,6 +20,7 @@
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^7.6.2",
|
"react-router-dom": "^7.6.2",
|
||||||
"react-virtualized-auto-sizer": "^1.0.26",
|
"react-virtualized-auto-sizer": "^1.0.26",
|
||||||
|
"react-virtuoso": "^4.12.8",
|
||||||
"react-window": "^1.8.11",
|
"react-window": "^1.8.11",
|
||||||
"react-window-infinite-loader": "^1.0.10",
|
"react-window-infinite-loader": "^1.0.10",
|
||||||
"tailwind-merge": "^3.3.1"
|
"tailwind-merge": "^3.3.1"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useState, ReactNode } from 'react';
|
import { ReactNode, useState } from 'react';
|
||||||
import { Sidebar } from './Sidebar';
|
|
||||||
import { Header } from './Header';
|
import { Header } from './Header';
|
||||||
|
import { Sidebar } from './Sidebar';
|
||||||
|
|
||||||
interface LayoutProps {
|
interface LayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
|
@ -15,8 +15,8 @@ export function Layout({ children, title }: LayoutProps) {
|
||||||
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
|
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
|
||||||
<Header setSidebarOpen={setSidebarOpen} title={title} />
|
<Header setSidebarOpen={setSidebarOpen} title={title} />
|
||||||
|
|
||||||
<main className="py-4 lg:pl-60 w-full">
|
<main className="py-4 lg:pl-60 w-full h-full overflow-y-auto scrollbar-sleek">
|
||||||
<div className="px-4">{children}</div>
|
<div className="px-4 flex-col h-full">{children}</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,7 @@ export function Sidebar({ sidebarOpen, setSidebarOpen }: SidebarProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function SidebarContent() {
|
function SidebarContent() {
|
||||||
return (
|
return ( <div className="flex grow flex-col gap-y-3 overflow-y-auto border-r border-border bg-background px-3">
|
||||||
<div className="flex grow flex-col gap-y-3 overflow-y-auto border-r border-border bg-background px-3 scrollbar-thin">
|
|
||||||
<div className="flex h-12 shrink-0 items-center border-b border-border">
|
<div className="flex h-12 shrink-0 items-center border-b border-border">
|
||||||
<h1 className="text-lg font-bold text-text-primary">Stock Bot</h1>
|
<h1 className="text-lg font-bold text-text-primary">Stock Bot</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,13 @@ import {
|
||||||
SortingState,
|
SortingState,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from '@tanstack/react-table';
|
} from '@tanstack/react-table';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import { TableVirtuoso } from 'react-virtuoso';
|
||||||
import { VariableSizeGrid as Grid } from 'react-window';
|
|
||||||
|
|
||||||
interface DataTableProps<T> {
|
interface DataTableProps<T> {
|
||||||
data: T[];
|
data: T[];
|
||||||
columns: ColumnDef<T>[];
|
columns: ColumnDef<T>[];
|
||||||
rowHeight?: number;
|
height?: number;
|
||||||
headerHeight?: number;
|
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
onRowClick?: (row: T) => void;
|
onRowClick?: (row: T) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
@ -26,8 +24,7 @@ interface DataTableProps<T> {
|
||||||
export function DataTable<T>({
|
export function DataTable<T>({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
rowHeight = 35,
|
height = 500,
|
||||||
headerHeight = 40,
|
|
||||||
loading = false,
|
loading = false,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
className = '',
|
className = '',
|
||||||
|
|
@ -52,91 +49,6 @@ export function DataTable<T>({
|
||||||
});
|
});
|
||||||
|
|
||||||
const { rows } = table.getRowModel();
|
const { rows } = table.getRowModel();
|
||||||
const visibleColumns = table.getVisibleFlatColumns();
|
|
||||||
|
|
||||||
// Calculate column widths as numbers for react-window
|
|
||||||
const columnWidths = useMemo(() => {
|
|
||||||
return visibleColumns.map(column => column.getSize());
|
|
||||||
}, [visibleColumns]);
|
|
||||||
|
|
||||||
// Unified cell renderer that handles both header and data rows
|
|
||||||
const Cell = useCallback(
|
|
||||||
({
|
|
||||||
columnIndex,
|
|
||||||
rowIndex,
|
|
||||||
style,
|
|
||||||
}: {
|
|
||||||
columnIndex: number;
|
|
||||||
rowIndex: number;
|
|
||||||
style: React.CSSProperties;
|
|
||||||
}) => {
|
|
||||||
const column = visibleColumns[columnIndex];
|
|
||||||
|
|
||||||
// Header row (rowIndex 0) - make it sticky
|
|
||||||
if (rowIndex === 0) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
...style,
|
|
||||||
width: columnWidths[columnIndex],
|
|
||||||
position: 'sticky',
|
|
||||||
top: 0,
|
|
||||||
zIndex: 10,
|
|
||||||
}}
|
|
||||||
className={cn(
|
|
||||||
'flex items-center justify-between px-3 py-2 text-xs font-medium text-text-secondary uppercase tracking-wider',
|
|
||||||
'border-r border-b border-border bg-surface hover:bg-surface-secondary',
|
|
||||||
column.getCanSort() && 'cursor-pointer select-none'
|
|
||||||
)}
|
|
||||||
onClick={column.getToggleSortingHandler()}
|
|
||||||
>
|
|
||||||
<span className="truncate">
|
|
||||||
{typeof column.columnDef.header === 'string' ? column.columnDef.header : column.id}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{column.getCanSort() && (
|
|
||||||
<div className="ml-2 flex-shrink-0">
|
|
||||||
{column.getIsSorted() === 'asc' ? (
|
|
||||||
<ChevronUpIcon className="h-4 w-4 text-primary-400" />
|
|
||||||
) : column.getIsSorted() === 'desc' ? (
|
|
||||||
<ChevronDownIcon className="h-4 w-4 text-primary-400" />
|
|
||||||
) : (
|
|
||||||
<div className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data rows (rowIndex > 0)
|
|
||||||
const row = rows[rowIndex - 1]; // Subtract 1 because row 0 is header
|
|
||||||
const cell = row?.getVisibleCells()[columnIndex];
|
|
||||||
|
|
||||||
if (!cell || !column) {
|
|
||||||
return <div style={style} className="border-r border-border bg-background" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
...style,
|
|
||||||
width: columnWidths[columnIndex],
|
|
||||||
}}
|
|
||||||
className={cn(
|
|
||||||
'flex items-center px-3 py-2 text-sm text-text-primary border-r border-border bg-background',
|
|
||||||
'hover:bg-surface cursor-pointer'
|
|
||||||
)}
|
|
||||||
onClick={() => onRowClick?.(row.original)}
|
|
||||||
>
|
|
||||||
<div className="truncate w-full">
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[rows, visibleColumns, onRowClick, columnWidths]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -147,40 +59,103 @@ export function DataTable<T>({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('bg-background text-text-primary w-full h-full flex flex-col', className)}>
|
<div className={cn('bg-background text-text-primary', className)}>
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div className="p-4 border-b border-border flex-shrink-0">
|
<div className="p-4 border-b border-border">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={globalFilter}
|
value={globalFilter}
|
||||||
onChange={e => setGlobalFilter(e.target.value)}
|
onChange={(e) => setGlobalFilter(e.target.value)}
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
className="w-full max-w-md bg-surface border border-border rounded px-3 py-2 text-sm text-text-primary placeholder-text-muted focus:ring-1 focus:ring-primary-500 focus:border-primary-500"
|
className="w-full max-w-md bg-surface border border-border rounded px-3 py-2 text-sm text-text-primary placeholder-text-muted focus:ring-1 focus:ring-primary-500 focus:border-primary-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Virtual Grid with AutoSizer - includes sticky header */}
|
{/* Virtualized Table */}
|
||||||
<div className="flex-1 border border-border rounded-lg overflow-hidden">
|
<TableVirtuoso
|
||||||
<AutoSizer>
|
style={{ height: `${height}px` }}
|
||||||
{({ height: autoHeight, width: autoWidth }) => (
|
className="border border-border rounded-lg"
|
||||||
<Grid
|
totalCount={rows.length}
|
||||||
height={autoHeight}
|
components={{
|
||||||
width={autoWidth}
|
Table: ({ style, ...props }) => (
|
||||||
rowCount={rows.length + 1} // +1 for header row
|
<table
|
||||||
columnCount={visibleColumns.length}
|
{...props}
|
||||||
rowHeight={index => (index === 0 ? headerHeight : rowHeight)} // Header height for row 0
|
style={{
|
||||||
columnWidth={index => columnWidths[index] || 150}
|
...style,
|
||||||
overscanRowCount={5}
|
minWidth: '100%',
|
||||||
overscanColumnCount={2}
|
tableLayout: 'auto',
|
||||||
|
borderCollapse: 'collapse',
|
||||||
|
borderSpacing: 0,
|
||||||
|
}}
|
||||||
|
className="bg-background"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
TableRow: (props) => {
|
||||||
|
const index = props['data-index'] as number;
|
||||||
|
const row = rows[index];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr
|
||||||
|
{...props}
|
||||||
|
className="hover:bg-surface cursor-pointer border-b border-border"
|
||||||
|
onClick={() => onRowClick?.(row.original)}
|
||||||
>
|
>
|
||||||
{Cell}
|
{row.getVisibleCells().map((cell) => (
|
||||||
</Grid>
|
<td
|
||||||
)}
|
key={cell.id}
|
||||||
</AutoSizer>
|
className="px-3 py-2 text-sm text-text-primary border-r border-border"
|
||||||
|
style={{ width: cell.column.getSize() }}
|
||||||
|
>
|
||||||
|
<div className="truncate">
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</div>
|
</div>
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
fixedHeaderContent={() =>
|
||||||
|
table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<tr key={headerGroup.id} className="bg-surface border-b border-border">
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<th
|
||||||
|
key={header.id}
|
||||||
|
colSpan={header.colSpan}
|
||||||
|
className={cn(
|
||||||
|
'px-3 py-2 text-xs font-medium text-text-secondary uppercase tracking-wider text-left border-r border-border',
|
||||||
|
header.column.getCanSort() && 'cursor-pointer select-none hover:bg-surface-secondary'
|
||||||
|
)}
|
||||||
|
style={{ width: header.getSize() }}
|
||||||
|
onClick={header.column.getToggleSortingHandler()}
|
||||||
|
>
|
||||||
|
{header.isPlaceholder ? null : (
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="truncate">
|
||||||
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
|
</span>
|
||||||
|
{header.column.getCanSort() && (
|
||||||
|
<div className="ml-2 flex-shrink-0">
|
||||||
|
{header.column.getIsSorted() === 'asc' ? (
|
||||||
|
<ChevronUpIcon className="h-4 w-4 text-primary-400" />
|
||||||
|
) : header.column.getIsSorted() === 'desc' ? (
|
||||||
|
<ChevronDownIcon className="h-4 w-4 text-primary-400" />
|
||||||
|
) : (
|
||||||
|
<div className="h-4 w-4" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex items-center justify-between mt-2 px-2 text-sm text-text-secondary flex-shrink-0">
|
<div className="flex items-center justify-between mt-2 px-2 text-sm text-text-secondary">
|
||||||
<span>
|
<span>
|
||||||
Showing {rows.length} of {data.length} rows
|
Showing {rows.length} of {data.length} rows
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { ColumnDef } from '@tanstack/react-table';
|
|
||||||
import { DataTable } from '@/components/ui';
|
|
||||||
|
|
||||||
interface SampleData {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
status: 'active' | 'inactive';
|
|
||||||
value: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ExampleTable() {
|
|
||||||
// Sample data
|
|
||||||
const data: SampleData[] = [
|
|
||||||
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'active', value: 100 },
|
|
||||||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive', value: 250 },
|
|
||||||
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'active', value: 150 },
|
|
||||||
// Add more sample data...
|
|
||||||
...Array.from({ length: 100 }, (_, i) => ({
|
|
||||||
id: i + 4,
|
|
||||||
name: `User ${i + 4}`,
|
|
||||||
email: `user${i + 4}@example.com`,
|
|
||||||
status: Math.random() > 0.5 ? 'active' : ('inactive' as const),
|
|
||||||
value: Math.floor(Math.random() * 1000),
|
|
||||||
})),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Define columns
|
|
||||||
const columns: ColumnDef<SampleData>[] = [
|
|
||||||
{
|
|
||||||
id: 'id',
|
|
||||||
header: 'ID',
|
|
||||||
accessorKey: 'id',
|
|
||||||
size: 80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'name',
|
|
||||||
header: 'Name',
|
|
||||||
accessorKey: 'name',
|
|
||||||
size: 200,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'email',
|
|
||||||
header: 'Email',
|
|
||||||
accessorKey: 'email',
|
|
||||||
size: 250,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'status',
|
|
||||||
header: 'Status',
|
|
||||||
accessorKey: 'status',
|
|
||||||
size: 120,
|
|
||||||
cell: ({ getValue }) => {
|
|
||||||
const status = getValue() as string;
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className={`px-2 py-1 rounded text-xs font-medium ${
|
|
||||||
status === 'active' ? 'bg-success/20 text-success' : 'bg-danger/20 text-danger'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{status}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'value',
|
|
||||||
header: 'Value',
|
|
||||||
accessorKey: 'value',
|
|
||||||
size: 120,
|
|
||||||
cell: ({ getValue }) => (
|
|
||||||
<span className="font-mono">${(getValue() as number).toFixed(2)}</span>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="p-4">
|
|
||||||
<h2 className="text-xl font-bold text-text-primary mb-4">Example Data Table</h2>
|
|
||||||
<DataTable
|
|
||||||
data={data}
|
|
||||||
columns={columns}
|
|
||||||
height={500}
|
|
||||||
onRowClick={row => console.log('Clicked row:', row)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -638,11 +638,10 @@ export function PortfolioTable() {
|
||||||
Performance test: 100,000 rows × {columns.length} columns ={' '}
|
Performance test: 100,000 rows × {columns.length} columns ={' '}
|
||||||
{(100000 * columns.length).toLocaleString()} cells
|
{(100000 * columns.length).toLocaleString()} cells
|
||||||
</p>
|
</p>
|
||||||
<div className="h-96 w-full border border-border rounded-lg overflow-hidden">
|
<div className="flex-grow w-full border border-border rounded-lg overflow-hidden">
|
||||||
<DataTable
|
<DataTable
|
||||||
data={data}
|
data={data}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
height="100%"
|
|
||||||
onRowClick={row => console.log('Clicked:', row.symbol)}
|
onRowClick={row => console.log('Clicked:', row.symbol)}
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -50,27 +50,73 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
/* Custom scrollbar for dark theme */
|
/* Global sleek dark-themed scrollbars for all elements */
|
||||||
.scrollbar-thin {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: rgb(42 42 45) transparent;
|
scrollbar-color: rgb(74 74 74) rgb(26 26 26);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar-thin::-webkit-scrollbar {
|
*::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar-thin::-webkit-scrollbar-track {
|
*::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: rgb(26 26 26);
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
*::-webkit-scrollbar-thumb {
|
||||||
background-color: rgb(42 42 45);
|
background-color: rgb(74 74 74);
|
||||||
border-radius: 3px;
|
border-radius: 4px;
|
||||||
|
border: 1px solid rgb(42 42 42);
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
*::-webkit-scrollbar-thumb:hover {
|
||||||
background-color: rgb(51 51 54);
|
background-color: rgb(90 90 90);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb:active {
|
||||||
|
background-color: rgb(106 106 106);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-corner {
|
||||||
|
background: rgb(26 26 26);
|
||||||
|
}
|
||||||
|
/* Global scrollbar for the entire app */
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgb(26 26 26) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-track {
|
||||||
|
background: rgb(0 0 0);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
background-color: rgb(26 26 26);
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid rgb(0 0 0);
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: rgb(42 42 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb:active {
|
||||||
|
background-color: rgb(60 60 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-corner {
|
||||||
|
background: rgb(0 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Trading specific styles */
|
/* Trading specific styles */
|
||||||
|
|
|
||||||
3
bun.lock
3
bun.lock
|
|
@ -150,6 +150,7 @@
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^7.6.2",
|
"react-router-dom": "^7.6.2",
|
||||||
"react-virtualized-auto-sizer": "^1.0.26",
|
"react-virtualized-auto-sizer": "^1.0.26",
|
||||||
|
"react-virtuoso": "^4.12.8",
|
||||||
"react-window": "^1.8.11",
|
"react-window": "^1.8.11",
|
||||||
"react-window-infinite-loader": "^1.0.10",
|
"react-window-infinite-loader": "^1.0.10",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
|
|
@ -1635,6 +1636,8 @@
|
||||||
|
|
||||||
"react-virtualized-auto-sizer": ["react-virtualized-auto-sizer@1.0.26", "", { "peerDependencies": { "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A=="],
|
"react-virtualized-auto-sizer": ["react-virtualized-auto-sizer@1.0.26", "", { "peerDependencies": { "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A=="],
|
||||||
|
|
||||||
|
"react-virtuoso": ["react-virtuoso@4.12.8", "", { "peerDependencies": { "react": ">=16 || >=17 || >= 18 || >= 19", "react-dom": ">=16 || >=17 || >= 18 || >=19" } }, "sha512-NMMKfDBr/+xZZqCQF3tN1SZsh6FwOJkYgThlfnsPLkaEhdyQo0EuWUzu3ix6qjnI7rYwJhMwRGoJBi+aiDfGsA=="],
|
||||||
|
|
||||||
"react-window": ["react-window@1.8.11", "", { "dependencies": { "@babel/runtime": "^7.0.0", "memoize-one": ">=3.1.1 <6" }, "peerDependencies": { "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ=="],
|
"react-window": ["react-window@1.8.11", "", { "dependencies": { "@babel/runtime": "^7.0.0", "memoize-one": ">=3.1.1 <6" }, "peerDependencies": { "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ=="],
|
||||||
|
|
||||||
"react-window-infinite-loader": ["react-window-infinite-loader@1.0.10", "", { "peerDependencies": { "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-NO/csdHlxjWqA2RJZfzQgagAjGHspbO2ik9GtWZb0BY1Nnapq0auG8ErI+OhGCzpjYJsCYerqUlK6hkq9dfAAA=="],
|
"react-window-infinite-loader": ["react-window-infinite-loader@1.0.10", "", { "peerDependencies": { "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-NO/csdHlxjWqA2RJZfzQgagAjGHspbO2ik9GtWZb0BY1Nnapq0auG8ErI+OhGCzpjYJsCYerqUlK6hkq9dfAAA=="],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue