Skip to content

Commit 51b55ef

Browse files
authored
Merge pull request #4 from shakibdshy/dev
Enhance Virtualization with Dynamic Row Heights and Accessibility
2 parents 190bec1 + 645a925 commit 51b55ef

32 files changed

+1572
-257
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/* Custom theme variables */
2+
:root {
3+
--rtg-body-bg: #ffffff;
4+
--rtg-body-text: #374151;
5+
--rtg-body-border: #e5e7eb;
6+
--rtg-body-divide-color: #e5e7eb;
7+
--rtg-body-hover-bg: rgba(243, 244, 246, 0.5);
8+
--rtg-body-even-bg: rgba(249, 250, 251, 0.3);
9+
}
10+
11+
.rtg-body {
12+
--rtg-body-bg: #ffffff;
13+
--rtg-body-text: #374151;
14+
--rtg-body-border: #e5e7eb;
15+
--rtg-body-transition: all 0.2s ease;
16+
--rtg-body-divide-color: #e5e7eb;
17+
--rtg-body-hover-bg: rgba(243, 244, 246, 0.5);
18+
--rtg-body-even-bg: rgba(249, 250, 251, 0.3);
19+
20+
position: relative;
21+
isolation: isolate;
22+
transition: var(--rtg-body-transition);
23+
border-top: 1px solid var(--rtg-body-divide-color);
24+
}
25+
26+
.rtg-body-loading {
27+
--rtg-loading-bg: #ffffff;
28+
--rtg-loading-text: #6b7280;
29+
30+
display: flex;
31+
align-items: center;
32+
justify-content: center;
33+
padding: 2rem;
34+
background-color: var(--rtg-loading-bg);
35+
color: var(--rtg-loading-text);
36+
}
37+
38+
.rtg-body-empty {
39+
--rtg-empty-bg: #ffffff;
40+
--rtg-empty-text: #6b7280;
41+
42+
display: flex;
43+
align-items: center;
44+
justify-content: center;
45+
padding: 2rem;
46+
background-color: var(--rtg-empty-bg);
47+
color: var(--rtg-empty-text);
48+
text-align: center;
49+
}
50+
51+
.rtg-body-row {
52+
display: grid;
53+
align-items: center;
54+
transition: var(--rtg-body-transition);
55+
border-bottom: 1px solid var(--rtg-body-divide-color);
56+
}
57+
58+
.rtg-body-row:hover {
59+
background-color: var(--rtg-body-hover-bg);
60+
}
61+
62+
.rtg-body-row:nth-child(even) {
63+
background-color: var(--rtg-body-even-bg);
64+
}
65+
66+
/* Dark mode support */
67+
@media (prefers-color-scheme: dark) {
68+
.rtg-body {
69+
--rtg-body-bg: #111827;
70+
--rtg-body-text: #e5e7eb;
71+
--rtg-body-border: #374151;
72+
--rtg-body-divide-color: #374151;
73+
--rtg-body-hover-bg: rgba(55, 65, 81, 0.5);
74+
--rtg-body-even-bg: rgba(31, 41, 55, 0.3);
75+
}
76+
77+
.rtg-body-loading {
78+
--rtg-loading-bg: #111827;
79+
--rtg-loading-text: #9ca3af;
80+
}
81+
82+
.rtg-body-empty {
83+
--rtg-empty-bg: #111827;
84+
--rtg-empty-text: #9ca3af;
85+
}
86+
}
87+
88+
/* Loading spinner animation */
89+
.rtg-loading-spinner {
90+
--rtg-spinner-size: 2rem;
91+
--rtg-spinner-border: 2px;
92+
--rtg-spinner-color: #6b7280;
93+
94+
width: var(--rtg-spinner-size);
95+
height: var(--rtg-spinner-size);
96+
border: var(--rtg-spinner-border) solid var(--rtg-spinner-color);
97+
border-right-color: transparent;
98+
border-radius: 50%;
99+
animation: spin 1s linear infinite;
100+
}
101+
102+
@keyframes spin {
103+
from {
104+
transform: rotate(0deg);
105+
}
106+
to {
107+
transform: rotate(360deg);
108+
}
109+
}
Lines changed: 69 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
1-
import { cn } from '@/utils/cn'
2-
import { tableStyles } from '@/styles/table.style'
3-
import { TableRow } from '@/components/core/table-row/table-row'
4-
import { LoadingSpinner } from '@/components/ui/loading-spinner'
5-
import { Empty } from '@/components/ui/empty'
6-
import type { useTableGrid } from '@/hooks/use-table-grid'
1+
import { cn } from "@/utils/cn";
2+
import { tableStyles } from "@/styles/table.style";
3+
import { TableRow } from "@/components/core/table-row/table-row";
4+
import { LoadingSpinner } from "@/components/ui/loading-spinner";
5+
import { Empty } from "@/components/ui/empty";
6+
import type { useTableGrid } from "@/hooks/use-table-grid";
7+
import "./table-body.css";
78

89
interface TableBodyProps<T extends Record<string, unknown>> {
9-
className?: string
10-
style?: React.CSSProperties
11-
bodyRowClassName?: string
12-
bodyCellClassName?: string
13-
tableInstance: ReturnType<typeof useTableGrid<T>>
10+
className?: string;
11+
style?: React.CSSProperties;
12+
bodyRowClassName?: string;
13+
bodyCellClassName?: string;
14+
tableInstance: ReturnType<typeof useTableGrid<T>>;
1415
components?: {
15-
EmptyState?: React.ComponentType
16-
LoadingState?: React.ComponentType
17-
}
16+
EmptyState?: React.ComponentType;
17+
LoadingState?: React.ComponentType;
18+
};
1819

1920
customRender?: {
20-
empty?: () => React.ReactNode
21-
loading?: () => React.ReactNode
22-
row?: (props: { row: T; rowIndex: number }) => React.ReactNode
23-
}
21+
empty?: () => React.ReactNode;
22+
loading?: () => React.ReactNode;
23+
row?: (props: { row: T; rowIndex: number }) => React.ReactNode;
24+
};
25+
withoutTailwind?: boolean;
2426
}
2527

2628
export function TableBody<T extends Record<string, unknown>>({
@@ -31,66 +33,97 @@ export function TableBody<T extends Record<string, unknown>>({
3133
customRender,
3234
bodyRowClassName,
3335
bodyCellClassName,
36+
withoutTailwind = false,
3437
}: TableBodyProps<T>) {
35-
const styles = tableStyles()
38+
const styles = tableStyles();
3639
const {
37-
3840
filteredData,
3941
state: { loading },
4042
} = tableInstance;
4143

4244
if (loading) {
4345
if (customRender?.loading) {
44-
return customRender.loading()
46+
return customRender.loading();
4547
}
4648

4749
if (components?.LoadingState) {
48-
return <components.LoadingState />
50+
return <components.LoadingState />;
4951
}
5052

5153
return (
52-
<div
53-
className={cn("rtg-loading flex items-center justify-center p-8", className)}
54+
<div
55+
className={cn(
56+
withoutTailwind
57+
? "rtg-body-loading"
58+
: cn("rtg-loading flex items-center justify-center p-8", className),
59+
className
60+
)}
5461
style={style}
62+
role="status"
63+
aria-busy="true"
64+
aria-live="polite"
5565
>
56-
<LoadingSpinner />
66+
<div className={withoutTailwind ? "rtg-loading-spinner" : undefined}>
67+
<LoadingSpinner />
68+
</div>
5769
</div>
58-
)
70+
);
5971
}
6072

6173
if (filteredData.length === 0) {
6274
if (customRender?.empty) {
63-
return customRender.empty()
75+
return customRender.empty();
6476
}
6577

6678
if (components?.EmptyState) {
67-
return <components.EmptyState />
79+
return <components.EmptyState />;
6880
}
6981

7082
return (
71-
<div
72-
className={cn("flex items-center justify-center p-8", className)}
83+
<div
84+
className={cn(
85+
withoutTailwind
86+
? "rtg-body-empty"
87+
: cn("flex items-center justify-center p-8", className),
88+
className
89+
)}
7390
style={style}
91+
role="status"
92+
aria-label="No data available"
7493
>
7594
<Empty text="No data found" />
7695
</div>
77-
)
96+
);
7897
}
7998

80-
const RowComponent = customRender?.row || TableRow
99+
const RowComponent = customRender?.row || TableRow;
81100

82101
return (
83-
<div className={cn("rtg-table-body", styles.body(), className)} style={style}>
102+
<div
103+
className={cn(
104+
withoutTailwind
105+
? "rtg-body"
106+
: cn("rtg-table-body", styles.body()),
107+
className
108+
)}
109+
style={style}
110+
role="rowgroup"
111+
aria-label="Table body"
112+
>
84113
{filteredData.map((row: T, index: number) => (
85114
<RowComponent
86-
key={`row-${index}-${(row as { id?: string }).id || ''}`}
115+
key={`row-${index}-${(row as { id?: string }).id || ""}`}
87116
row={row}
88117
rowIndex={index}
89118
tableInstance={tableInstance}
90-
className={bodyRowClassName}
119+
className={cn(
120+
withoutTailwind ? "rtg-body-row" : undefined,
121+
bodyRowClassName
122+
)}
91123
cellClassName={bodyCellClassName}
124+
withoutTailwind={withoutTailwind}
92125
/>
93126
))}
94127
</div>
95-
)
128+
);
96129
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* Custom theme variables */
2+
:root {
3+
--rtg-virtualized-height: 520px;
4+
--rtg-virtualized-scrollbar-width: 6px;
5+
--rtg-virtualized-scrollbar-track: #f1f1f1;
6+
--rtg-virtualized-scrollbar-thumb: #c1c1c1;
7+
--rtg-virtualized-scrollbar-thumb-hover: #a1a1a1;
8+
}
9+
10+
.rtg-virtualized-body {
11+
--rtg-virtualized-height: 520px;
12+
--rtg-virtualized-scrollbar-width: 6px;
13+
--rtg-virtualized-scrollbar-track: #f1f1f1;
14+
--rtg-virtualized-scrollbar-thumb: #c1c1c1;
15+
--rtg-virtualized-scrollbar-thumb-hover: #a1a1a1;
16+
17+
position: relative;
18+
overflow: auto;
19+
height: var(--rtg-virtualized-height);
20+
overscroll-behavior: contain;
21+
-webkit-overflow-scrolling: touch;
22+
}
23+
24+
/* Scrollbar styling */
25+
.rtg-virtualized-body::-webkit-scrollbar {
26+
width: var(--rtg-virtualized-scrollbar-width);
27+
}
28+
29+
.rtg-virtualized-body::-webkit-scrollbar-track {
30+
background: var(--rtg-virtualized-scrollbar-track);
31+
}
32+
33+
.rtg-virtualized-body::-webkit-scrollbar-thumb {
34+
background: var(--rtg-virtualized-scrollbar-thumb);
35+
border-radius: var(--rtg-virtualized-scrollbar-width);
36+
}
37+
38+
.rtg-virtualized-body::-webkit-scrollbar-thumb:hover {
39+
background: var(--rtg-virtualized-scrollbar-thumb-hover);
40+
}
41+
42+
.rtg-virtualized-content {
43+
position: relative;
44+
will-change: transform;
45+
contain: strict;
46+
content-visibility: auto;
47+
}
48+
49+
.rtg-virtualized-loading {
50+
display: flex;
51+
align-items: center;
52+
justify-content: center;
53+
padding: 2rem;
54+
background-color: var(--rtg-body-bg, #ffffff);
55+
color: var(--rtg-body-text, #6b7280);
56+
}
57+
58+
/* Dark mode support */
59+
@media (prefers-color-scheme: dark) {
60+
.rtg-virtualized-body {
61+
--rtg-virtualized-scrollbar-track: #374151;
62+
--rtg-virtualized-scrollbar-thumb: #4b5563;
63+
--rtg-virtualized-scrollbar-thumb-hover: #6b7280;
64+
}
65+
66+
.rtg-virtualized-loading {
67+
background-color: var(--rtg-body-bg, #1f2937);
68+
color: var(--rtg-body-text, #9ca3af);
69+
}
70+
}

0 commit comments

Comments
 (0)