diff --git a/src/components/table/Skeleton.tsx b/src/components/table/Skeleton.tsx
new file mode 100644
index 00000000..c8015af4
--- /dev/null
+++ b/src/components/table/Skeleton.tsx
@@ -0,0 +1,29 @@
+import styled from 'styled-components'
+
+function SkeletonUnstyled({ ...props }) {
+ return (
+
+
+
+ )
+}
+
+export const Skeleton = styled(SkeletonUnstyled)(({ theme }) => ({
+ '@keyframes moving-gradient': {
+ '0%': { backgroundPosition: '-250px 0' },
+ '100%': { backgroundPosition: '250px 0' },
+ },
+ maxWidth: '400px',
+ width: '100%',
+ span: {
+ borderRadius: theme.borderRadiuses.medium,
+ maxWidth: '400px',
+ width: 'unset',
+ minWidth: '150px',
+ display: 'block',
+ height: '12px',
+ background: `linear-gradient(to right, ${theme.colors.border} 20%, ${theme.colors['border-fill-two']} 50%, ${theme.colors.border} 80%)`,
+ backgroundSize: '500px 100px',
+ animation: 'moving-gradient 2s infinite linear forwards',
+ },
+}))
diff --git a/src/components/table/Table.tsx b/src/components/table/Table.tsx
index c6c4c67b..c15b8d91 100644
--- a/src/components/table/Table.tsx
+++ b/src/components/table/Table.tsx
@@ -39,6 +39,7 @@ import { Spinner } from '../Spinner'
import { tableFillLevelToBg, tableFillLevelToBorderColor } from './colors'
import { FillerRows } from './FillerRows'
import { useIsScrolling, useOnVirtualSliceChange } from './hooks'
+import { Skeleton } from './Skeleton'
import { SortIndicator } from './SortIndicator'
import { T } from './T'
import { Tbody } from './Tbody'
@@ -50,6 +51,8 @@ import { Tr } from './Tr'
export type TableProps = DivProps & {
data: any[]
columns: any[]
+ loading?: boolean
+ loadingSkeletonRows?: number
hideHeader?: boolean
padCells?: boolean
fillLevel?: TableFillLevel
@@ -126,6 +129,8 @@ function TableRef(
{
data,
columns,
+ loading = false,
+ loadingSkeletonRows = 10,
hideHeader = false,
getRowCanExpand,
renderExpanded,
@@ -260,11 +265,22 @@ function TableRef(
const headerGroups = useMemo(() => table.getHeaderGroups(), [table])
const rows = virtualizeRows ? virtualRows : tableRows
+
+ const skeletonRows = useMemo(
+ () => Array(loadingSkeletonRows).fill({}),
+ [loadingSkeletonRows]
+ )
+
const gridTemplateColumns = useMemo(
() => fixedGridTemplateColumns ?? getGridTemplateCols(columns),
[columns, fixedGridTemplateColumns]
)
+ const isRaised = useCallback(
+ (i: number) => rowBg === 'raised' || (rowBg === 'stripes' && i % 2 === 1),
+ [rowBg]
+ )
+
useEffect(() => {
const lastItem = virtualRows[virtualRows.length - 1]
@@ -362,111 +378,139 @@ function TableRef(
))}
- {paddingTop > 0 && (
-
- )}
- {rows.map((maybeRow) => {
- const i = maybeRow.index
- const isLoaderRow = i > tableRows.length - 1
- const row: Row | null = isRow(maybeRow)
- ? maybeRow
- : isLoaderRow
- ? null
- : tableRows[maybeRow.index]
- const key = row?.id ?? maybeRow.index
- const raised =
- rowBg === 'raised' || (rowBg === 'stripes' && i % 2 === 1)
-
- return (
-
+ {loading ? (
+ <>
+ {skeletonRows.map((_, i) => (
onRowClick?.(e, row)}
+ key={i}
$fillLevel={fillLevel}
- $raised={raised}
- $highlighted={row?.id === highlightedRowId}
- $selectable={row?.getCanSelect() ?? false}
- $selected={row?.getIsSelected() ?? false}
- $clickable={!!onRowClick}
- // data-index is required for virtual scrolling to work
- data-index={row?.index}
- {...(virtualizeRows
- ? { ref: rowVirtualizer.measureElement }
- : {})}
+ $raised={isRaised(i)}
>
- {isNil(row) && isLoaderRow ? (
- (
+
- Loading
-
-
- ) : (
- row?.getVisibleCells().map((cell) => (
- | |
+
+ ))}
+
+ ))}
+ >
+ ) : (
+ <>
+ {paddingTop > 0 && (
+
+ )}
+ {rows.map((maybeRow) => {
+ const i = maybeRow.index
+ const isLoaderRow = i > tableRows.length - 1
+ const row: Row | null = isRow(maybeRow)
+ ? maybeRow
+ : isLoaderRow
+ ? null
+ : tableRows[maybeRow.index]
+ const key = row?.id ?? maybeRow.index
+
+ return (
+
+ onRowClick?.(e, row)}
+ $fillLevel={fillLevel}
+ $raised={isRaised(i)}
+ $highlighted={row?.id === highlightedRowId}
+ $selectable={row?.getCanSelect() ?? false}
+ $selected={row?.getIsSelected() ?? false}
+ $clickable={!!onRowClick}
+ // data-index is required for virtual scrolling to work
+ data-index={row?.index}
+ {...(virtualizeRows
+ ? { ref: rowVirtualizer.measureElement }
+ : {})}
+ >
+ {isNil(row) && isLoaderRow ? (
+
+ Loading
+
+
+ ) : (
+ row?.getVisibleCells().map((cell) => (
+
+ {flexRender(
+ cell.column.columnDef.cell,
+ cell.getContext()
+ )}
+ |
+ ))
+ )}
+
+ {row?.getIsExpanded() && (
+
- {flexRender(
- cell.column.columnDef.cell,
- cell.getContext()
- )}
-
- ))
- )}
-
- {row?.getIsExpanded() && (
-
-
-
- {renderExpanded({ row })}
-
-
- )}
-
- )
- })}
- {paddingBottom > 0 && (
-
+
+
+ {renderExpanded({ row })}
+
+
+ )}
+
+ )
+ })}
+ {paddingBottom > 0 && (
+
+ )}
+ >
)}
- {isEmpty(rows) && (
+ {isEmpty(rows) && !loading && (