Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Virtualization with Dynamic Row Heights and Accessibility #4

Merged
merged 27 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
81e2926
Refactor Column Pinning: Update Imports and Enhance Pinned Column Sty…
shakibdshy Feb 9, 2025
4aa6dcd
Improve TableBody Accessibility: Add ARIA Attributes and Semantic Roles
shakibdshy Feb 9, 2025
e9eb3d3
Enhance VirtualizedBody Accessibility: Add ARIA Roles and Semantic At…
shakibdshy Feb 9, 2025
18b4a70
Improve Table Container Accessibility: Add ARIA Roles and Semantic At…
shakibdshy Feb 9, 2025
64ff021
Improve TableHeaderGroup Accessibility: Add ARIA Roles and Semantic A…
shakibdshy Feb 9, 2025
b1c2d1f
Improve HeaderCell Accessibility: Add ARIA Roles and Sorting Attributes
shakibdshy Feb 9, 2025
193e3ba
Enhance TableHeader Accessibility: Add ARIA Roles for Header Row and …
shakibdshy Feb 9, 2025
72d789b
Improve TableRow Accessibility: Add ARIA and Keyboard Navigation Support
shakibdshy Feb 9, 2025
6468a0b
Improve TableSearch Accessibility: Add ARIA Roles and Descriptive Labels
shakibdshy Feb 9, 2025
5f958df
Improve LoadingSpinner Accessibility: Enhance ARIA Roles and Screen R…
shakibdshy Feb 9, 2025
9b959af
Improve TableCell Accessibility: Add ARIA Roles and Column Identifica…
shakibdshy Feb 9, 2025
b242396
Enhance TableResizer Accessibility: Add ARIA Roles, Keyboard Interact…
shakibdshy Feb 9, 2025
c9895a4
Enhance Virtualization with Dynamic Row Heights, Scrolling, and Infin…
shakibdshy Feb 10, 2025
39e64ce
Refactor TableContainer: Rename and Relocate Component to TableGrid
shakibdshy Feb 12, 2025
97c2d0d
Refactor: Rename HeaderCell to TableColumn across components
shakibdshy Feb 12, 2025
4928b67
Add TableColumn CSS and Component for Flexible Table Header Styling
shakibdshy Feb 12, 2025
f03284f
Add Tailwind-free CSS for Table Header with Pinned Column Support
shakibdshy Feb 12, 2025
2262dd4
Add Tailwind-free CSS for Table Column Resizer
shakibdshy Feb 12, 2025
1714f6c
Add CSS classes for table grid wrapper and table grid
shakibdshy Feb 12, 2025
41c9b15
Add Tailwind-free CSS and Enhanced Features for Table Search Component
shakibdshy Feb 12, 2025
1052dd2
Add Tailwind-free CSS for Table Header Group with Optional Styling
shakibdshy Feb 12, 2025
67d1dce
Add Tailwind-free CSS for Table Body with Dark Mode Support
shakibdshy Feb 12, 2025
8b5aac4
Add Tailwind-free CSS for Table Row with Dark Mode and Selection Support
shakibdshy Feb 12, 2025
801cedf
Add Tailwind-free CSS and Editing Support for Table Cell Component
shakibdshy Feb 12, 2025
5e0899c
Add Tailwind-free CSS for Virtualized Table Body with Customization S…
shakibdshy Feb 12, 2025
adaf275
Enhance Table Column and Header CSS with Dark Mode and Customization
shakibdshy Feb 12, 2025
645a925
Add Tailwind-free CSS and Customization Option for Table Grid Component
shakibdshy Feb 12, 2025
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
109 changes: 109 additions & 0 deletions src/components/containers/table-body/table-body.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/* Custom theme variables */
:root {
--rtg-body-bg: #ffffff;
--rtg-body-text: #374151;
--rtg-body-border: #e5e7eb;
--rtg-body-divide-color: #e5e7eb;
--rtg-body-hover-bg: rgba(243, 244, 246, 0.5);
--rtg-body-even-bg: rgba(249, 250, 251, 0.3);
}

.rtg-body {
--rtg-body-bg: #ffffff;
--rtg-body-text: #374151;
--rtg-body-border: #e5e7eb;
--rtg-body-transition: all 0.2s ease;
--rtg-body-divide-color: #e5e7eb;
--rtg-body-hover-bg: rgba(243, 244, 246, 0.5);
--rtg-body-even-bg: rgba(249, 250, 251, 0.3);

position: relative;
isolation: isolate;
transition: var(--rtg-body-transition);
border-top: 1px solid var(--rtg-body-divide-color);
}

.rtg-body-loading {
--rtg-loading-bg: #ffffff;
--rtg-loading-text: #6b7280;

display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
background-color: var(--rtg-loading-bg);
color: var(--rtg-loading-text);
}

.rtg-body-empty {
--rtg-empty-bg: #ffffff;
--rtg-empty-text: #6b7280;

display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
background-color: var(--rtg-empty-bg);
color: var(--rtg-empty-text);
text-align: center;
}

.rtg-body-row {
display: grid;
align-items: center;
transition: var(--rtg-body-transition);
border-bottom: 1px solid var(--rtg-body-divide-color);
}

.rtg-body-row:hover {
background-color: var(--rtg-body-hover-bg);
}

.rtg-body-row:nth-child(even) {
background-color: var(--rtg-body-even-bg);
}

/* Dark mode support */
@media (prefers-color-scheme: dark) {
.rtg-body {
--rtg-body-bg: #111827;
--rtg-body-text: #e5e7eb;
--rtg-body-border: #374151;
--rtg-body-divide-color: #374151;
--rtg-body-hover-bg: rgba(55, 65, 81, 0.5);
--rtg-body-even-bg: rgba(31, 41, 55, 0.3);
}

.rtg-body-loading {
--rtg-loading-bg: #111827;
--rtg-loading-text: #9ca3af;
}

.rtg-body-empty {
--rtg-empty-bg: #111827;
--rtg-empty-text: #9ca3af;
}
}

/* Loading spinner animation */
.rtg-loading-spinner {
--rtg-spinner-size: 2rem;
--rtg-spinner-border: 2px;
--rtg-spinner-color: #6b7280;

width: var(--rtg-spinner-size);
height: var(--rtg-spinner-size);
border: var(--rtg-spinner-border) solid var(--rtg-spinner-color);
border-right-color: transparent;
border-radius: 50%;
animation: spin 1s linear infinite;
}

@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
105 changes: 69 additions & 36 deletions src/components/containers/table-body/table-body.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import { cn } from '@/utils/cn'
import { tableStyles } from '@/styles/table.style'
import { TableRow } from '@/components/core/table-row/table-row'
import { LoadingSpinner } from '@/components/ui/loading-spinner'
import { Empty } from '@/components/ui/empty'
import type { useTableGrid } from '@/hooks/use-table-grid'
import { cn } from "@/utils/cn";
import { tableStyles } from "@/styles/table.style";
import { TableRow } from "@/components/core/table-row/table-row";
import { LoadingSpinner } from "@/components/ui/loading-spinner";
import { Empty } from "@/components/ui/empty";
import type { useTableGrid } from "@/hooks/use-table-grid";
import "./table-body.css";

interface TableBodyProps<T extends Record<string, unknown>> {
className?: string
style?: React.CSSProperties
bodyRowClassName?: string
bodyCellClassName?: string
tableInstance: ReturnType<typeof useTableGrid<T>>
className?: string;
style?: React.CSSProperties;
bodyRowClassName?: string;
bodyCellClassName?: string;
tableInstance: ReturnType<typeof useTableGrid<T>>;
components?: {
EmptyState?: React.ComponentType
LoadingState?: React.ComponentType
}
EmptyState?: React.ComponentType;
LoadingState?: React.ComponentType;
};

customRender?: {
empty?: () => React.ReactNode
loading?: () => React.ReactNode
row?: (props: { row: T; rowIndex: number }) => React.ReactNode
}
empty?: () => React.ReactNode;
loading?: () => React.ReactNode;
row?: (props: { row: T; rowIndex: number }) => React.ReactNode;
};
withoutTailwind?: boolean;
}

export function TableBody<T extends Record<string, unknown>>({
Expand All @@ -31,66 +33,97 @@ export function TableBody<T extends Record<string, unknown>>({
customRender,
bodyRowClassName,
bodyCellClassName,
withoutTailwind = false,
}: TableBodyProps<T>) {
const styles = tableStyles()
const styles = tableStyles();
const {

filteredData,
state: { loading },
} = tableInstance;

if (loading) {
if (customRender?.loading) {
return customRender.loading()
return customRender.loading();
}

if (components?.LoadingState) {
return <components.LoadingState />
return <components.LoadingState />;
}

return (
<div
className={cn("rtg-loading flex items-center justify-center p-8", className)}
<div
className={cn(
withoutTailwind
? "rtg-body-loading"
: cn("rtg-loading flex items-center justify-center p-8", className),
className
)}
style={style}
role="status"
aria-busy="true"
aria-live="polite"
>
<LoadingSpinner />
<div className={withoutTailwind ? "rtg-loading-spinner" : undefined}>
<LoadingSpinner />
</div>
</div>
)
);
}

if (filteredData.length === 0) {
if (customRender?.empty) {
return customRender.empty()
return customRender.empty();
}

if (components?.EmptyState) {
return <components.EmptyState />
return <components.EmptyState />;
}

return (
<div
className={cn("flex items-center justify-center p-8", className)}
<div
className={cn(
withoutTailwind
? "rtg-body-empty"
: cn("flex items-center justify-center p-8", className),
className
)}
style={style}
role="status"
aria-label="No data available"
>
<Empty text="No data found" />
</div>
)
);
}

const RowComponent = customRender?.row || TableRow
const RowComponent = customRender?.row || TableRow;

return (
<div className={cn("rtg-table-body", styles.body(), className)} style={style}>
<div
className={cn(
withoutTailwind
? "rtg-body"
: cn("rtg-table-body", styles.body()),
className
)}
style={style}
role="rowgroup"
aria-label="Table body"
>
{filteredData.map((row: T, index: number) => (
<RowComponent
key={`row-${index}-${(row as { id?: string }).id || ''}`}
key={`row-${index}-${(row as { id?: string }).id || ""}`}
row={row}
rowIndex={index}
tableInstance={tableInstance}
className={bodyRowClassName}
className={cn(
withoutTailwind ? "rtg-body-row" : undefined,
bodyRowClassName
)}
cellClassName={bodyCellClassName}
withoutTailwind={withoutTailwind}
/>
))}
</div>
)
);
}
70 changes: 70 additions & 0 deletions src/components/containers/table-body/virtualized-body.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* Custom theme variables */
:root {
--rtg-virtualized-height: 520px;
--rtg-virtualized-scrollbar-width: 6px;
--rtg-virtualized-scrollbar-track: #f1f1f1;
--rtg-virtualized-scrollbar-thumb: #c1c1c1;
--rtg-virtualized-scrollbar-thumb-hover: #a1a1a1;
}

.rtg-virtualized-body {
--rtg-virtualized-height: 520px;
--rtg-virtualized-scrollbar-width: 6px;
--rtg-virtualized-scrollbar-track: #f1f1f1;
--rtg-virtualized-scrollbar-thumb: #c1c1c1;
--rtg-virtualized-scrollbar-thumb-hover: #a1a1a1;

position: relative;
overflow: auto;
height: var(--rtg-virtualized-height);
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
}

/* Scrollbar styling */
.rtg-virtualized-body::-webkit-scrollbar {
width: var(--rtg-virtualized-scrollbar-width);
}

.rtg-virtualized-body::-webkit-scrollbar-track {
background: var(--rtg-virtualized-scrollbar-track);
}

.rtg-virtualized-body::-webkit-scrollbar-thumb {
background: var(--rtg-virtualized-scrollbar-thumb);
border-radius: var(--rtg-virtualized-scrollbar-width);
}

.rtg-virtualized-body::-webkit-scrollbar-thumb:hover {
background: var(--rtg-virtualized-scrollbar-thumb-hover);
}

.rtg-virtualized-content {
position: relative;
will-change: transform;
contain: strict;
content-visibility: auto;
}

.rtg-virtualized-loading {
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
background-color: var(--rtg-body-bg, #ffffff);
color: var(--rtg-body-text, #6b7280);
}

/* Dark mode support */
@media (prefers-color-scheme: dark) {
.rtg-virtualized-body {
--rtg-virtualized-scrollbar-track: #374151;
--rtg-virtualized-scrollbar-thumb: #4b5563;
--rtg-virtualized-scrollbar-thumb-hover: #6b7280;
}

.rtg-virtualized-loading {
background-color: var(--rtg-body-bg, #1f2937);
color: var(--rtg-body-text, #9ca3af);
}
}
Loading