improve table empty & loading states

This commit is contained in:
Sidney Alcantara
2022-05-27 15:10:06 +10:00
parent c85098ae6a
commit 8286aa8147
11 changed files with 91 additions and 41 deletions

View File

@@ -4,6 +4,8 @@ import { alpha } from "@mui/material/styles";
import { FieldType } from "@src/constants/fields";
import { getFieldProp } from "@src/components/fields";
export const COLUMN_HEADER_HEIGHT = 42;
export interface IColumnProps extends Partial<GridProps> {
label: string;
type?: FieldType;
@@ -29,7 +31,7 @@ export default function Column({
sx={[
{
width: "100%",
height: 42,
height: COLUMN_HEADER_HEIGHT,
border: (theme) => `1px solid ${theme.palette.divider}`,
backgroundColor: "background.default",

View File

@@ -26,10 +26,12 @@ import {
import { tableScope, updateColumnAtom } from "@src/atoms/tableScope";
import { FieldType } from "@src/constants/fields";
import { getFieldProp } from "@src/components/fields";
import { DEFAULT_ROW_HEIGHT } from "@src/components/Table";
import { COLUMN_HEADER_HEIGHT } from "@src/components/Table/Column";
import { ColumnConfig } from "@src/types/table";
import useKeyPress from "@src/hooks/useKeyPress";
export { COLUMN_HEADER_HEIGHT };
const LightTooltip = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} classes={{ popper: className }} />
))(({ theme }) => ({
@@ -37,7 +39,7 @@ const LightTooltip = styled(({ className, ...props }: TooltipProps) => (
backgroundColor: theme.palette.background.default,
color: theme.palette.text.primary,
margin: `-${DEFAULT_ROW_HEIGHT - 2}px 0 0 !important`,
margin: `-${COLUMN_HEADER_HEIGHT - 1 - 2}px 0 0 !important`,
padding: 0,
paddingRight: theme.spacing(1.5),
},
@@ -83,6 +85,7 @@ export default function DraggableHeaderRenderer({
return (
<Grid
key={column.key}
ref={(ref) => {
dragRef(ref);
dropRef(ref);
@@ -164,7 +167,7 @@ export default function DraggableHeaderRenderer({
sx={{
typography: "caption",
fontWeight: "fontWeightMedium",
lineHeight: `${DEFAULT_ROW_HEIGHT - 1 - 4}px`,
lineHeight: `${COLUMN_HEADER_HEIGHT - 2 - 4}px`,
textOverflow: "clip",
}}
color="inherit"
@@ -182,7 +185,7 @@ export default function DraggableHeaderRenderer({
sx={{
typography: "caption",
fontWeight: "fontWeightMedium",
lineHeight: `${DEFAULT_ROW_HEIGHT + 1}px`,
lineHeight: `${COLUMN_HEADER_HEIGHT}px`,
textOverflow: "clip",
}}
component="div"

View File

@@ -1,9 +1,10 @@
import { useAtom } from "jotai";
import { useAtom, useSetAtom } from "jotai";
import { Grid, Stack, Typography, Button, Divider } from "@mui/material";
import ImportIcon from "@src/assets/icons/Import";
import AddColumnIcon from "@src/assets/icons/AddColumn";
import { globalScope, columnModalAtom } from "@src/atoms/globalScope";
import {
tableScope,
tableSettingsAtom,
@@ -17,6 +18,8 @@ import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
// import ImportCSV from "@src/components/TableToolbar/ImportCsv";
export default function EmptyTable() {
const openColumnModal = useSetAtom(columnModalAtom, globalScope);
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
const [tableRows] = useAtom(tableRowsAtom, tableScope);
// const { tableState, importWizardRef, columnMenuRef } = useProjectContext();
@@ -115,19 +118,10 @@ export default function EmptyTable() {
variant="contained"
color="primary"
startIcon={<AddColumnIcon />}
// onClick={(event) =>
// columnMenuRef?.current?.setSelectedColumnHeader({
// column: { isNew: true, key: "new", type: "LAST" } as any,
// anchorEl: event.currentTarget,
// })
// }
// disabled={!columnMenuRef?.current}
disabled
onClick={() => openColumnModal({ type: "new" })}
>
Add column
</Button>
{/* <ColumnMenu /> */}
</Grid>
</Grid>
</>

View File

@@ -1,8 +1,7 @@
import React, { useRef, useMemo, useState } from "react";
import { find, difference, get } from "lodash-es";
import { find, difference } from "lodash-es";
import { useAtom, useSetAtom } from "jotai";
import { useDebouncedCallback } from "use-debounce";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
@@ -11,14 +10,18 @@ import DataGrid, {
Column,
// SelectColumn as _SelectColumn,
} from "react-data-grid";
import { LinearProgress } from "@mui/material";
import TableContainer, { OUT_OF_ORDER_MARGIN } from "./TableContainer";
import ColumnHeader from "./ColumnHeader";
import ColumnHeader, { COLUMN_HEADER_HEIGHT } from "./ColumnHeader";
// import ContextMenu from "./ContextMenu";
import FinalColumnHeader from "./FinalColumnHeader";
import FinalColumn from "./formatters/FinalColumn";
import TableRow from "./TableRow";
import EmptyState from "@src/components/EmptyState";
// import BulkActions from "./BulkActions";
import AddRow from "@src/components/TableToolbar/AddRow";
import AddRowIcon from "@src/assets/icons/AddRow";
import {
globalScope,
@@ -152,7 +155,7 @@ export default function Table() {
// { ...row }
// )
// );
}, [columns, tableRows]) ?? [];
}, [tableRows]) ?? [];
const rowsContainerRef = useRef<HTMLDivElement>(null);
const [selectedRowsSet, setSelectedRowsSet] = useState<Set<React.Key>>();
@@ -166,8 +169,6 @@ export default function Table() {
const isAtBottom =
target.clientHeight + target.scrollTop >= target.scrollHeight - offset;
if (!isAtBottom) return;
// Prevent calling more rows when theyve already been called
if (tableLoadingMore) return;
// Call for the next page
setTablePageAtom((p) => p + 1);
};
@@ -271,6 +272,26 @@ export default function Table() {
// }
/>
</DndProvider>
{tableRows.length === 0 && (
<EmptyState
Icon={AddRowIcon}
message="Add a row to get started"
description={
<div>
<br />
<AddRow />
</div>
}
style={{
position: "absolute",
inset: 0,
top: COLUMN_HEADER_HEIGHT,
height: "auto",
}}
/>
)}
{tableLoadingMore && <LinearProgress />}
</TableContainer>
{/* <ContextMenu />

View File

@@ -13,6 +13,7 @@ export const TableContainer = styled("div", {
shouldForwardProp: (prop) => prop !== "rowHeight",
})<{ rowHeight: number }>(({ theme, rowHeight }) => ({
display: "flex",
position: "relative",
flexDirection: "column",
height: `calc(100vh - ${APP_BAR_HEIGHT}px - ${TABLE_TOOLBAR_HEIGHT}px)`,

View File

@@ -85,7 +85,6 @@ export default function Filters() {
// Set the local table filter
useEffect(() => {
console.log("Filters effect");
// Set local state for UI
setTableQuery(
Array.isArray(tableFilters) && tableFilters[0]

View File

@@ -180,6 +180,8 @@ export default function ImportCsv({ render, PopoverProps }: IImportCsvProps) {
title="Import CSV or TSV"
onClick={handleOpen}
icon={<ImportIcon />}
// FIXME:
disabled
/>
)}

View File

@@ -1,6 +1,7 @@
import { Suspense, forwardRef } from "react";
import { useAtom } from "jotai";
import { Tooltip, Typography } from "@mui/material";
import { Tooltip, Typography, TypographyProps } from "@mui/material";
import {
tableScope,
@@ -10,7 +11,23 @@ import {
} from "@src/atoms/tableScope";
import { COLLECTION_PAGE_SIZE } from "@src/config/db";
export default function LoadedRowsStatus() {
const StatusText = forwardRef(function StatusText(
props: TypographyProps,
ref: React.Ref<HTMLButtonElement>
) {
return (
<Typography
ref={ref}
variant="body2"
color="text.disabled"
display="block"
{...props}
style={{ userSelect: "none", ...props.style }}
/>
);
});
function LoadedRowsStatus() {
const [tableRows] = useAtom(tableRowsAtom, tableScope);
const [tableLoadingMore] = useAtom(tableLoadingMoreAtom, tableScope);
const [tablePage] = useAtom(tablePageAtom, tableScope);
@@ -18,23 +35,28 @@ export default function LoadedRowsStatus() {
const allLoaded =
!tableLoadingMore && tableRows.length < COLLECTION_PAGE_SIZE * tablePage;
if (tableLoadingMore) return <StatusText>Loading more</StatusText>;
return (
<Tooltip
title={
allLoaded
? "All rows have been loaded in this table"
: `Scroll to the bottom to load more rows`
: "Scroll to the bottom to load more rows"
}
>
<Typography
variant="body2"
color="text.disabled"
display="block"
style={{ userSelect: "none" }}
>
<StatusText>
Loaded {allLoaded && "all "}
{tableRows.length} row{tableRows.length !== 1 && "s"}
</Typography>
</StatusText>
</Tooltip>
);
}
export default function SuspendedLoadedRowsStatus() {
return (
<Suspense fallback={<StatusText>Loading</StatusText>}>
<LoadedRowsStatus />
</Suspense>
);
}

View File

@@ -130,6 +130,7 @@ export function useFirestoreCollectionWithAtom<T = TableRow>(
isLastPage || queryEqual(next?.query as any, prev?.query as any)
);
// Create listener
useEffect(() => {
// If path is invalid and no memoizedQuery was created, dont continue
if (!memoizedQuery) return;
@@ -244,7 +245,6 @@ export function useFirestoreCollectionWithAtom<T = TableRow>(
setUpdateDocAtom,
deleteDocAtom,
setDeleteDocAtom,
loadingMoreAtom,
]);
}

View File

@@ -28,11 +28,17 @@ function TablePage() {
if (isEmpty(tableSchema.columns))
return (
<Fade style={{ transitionDelay: "500ms" }}>
<div>
<EmptyTable />
</div>
</Fade>
<Suspense fallback={null}>
<Fade in style={{ transitionDelay: "500ms" }}>
<div>
<EmptyTable />
<Suspense fallback={null}>
<ColumnModals />
</Suspense>
</div>
</Fade>
</Suspense>
);
return (
@@ -45,7 +51,7 @@ function TablePage() {
<Table />
</Suspense>
<Suspense>
<Suspense fallback={null}>
<ColumnMenu />
<ColumnModals />
</Suspense>

View File

@@ -1224,7 +1224,7 @@ export const components = (theme: Theme): ThemeOptions => {
MuiAvatar: {
styleOverrides: {
root: {
...theme.typography.button,
fontWeight: theme.typography.fontWeightMedium,
},
colorDefault: {
backgroundColor: theme.palette.action.selected,