responsive table now has optional mobile render prop; users now has functional mobile view
This commit is contained in:
parent
6234f13eb0
commit
3f96724422
5 changed files with 238 additions and 8 deletions
|
|
@ -1,9 +1,10 @@
|
|||
import { Box, CircularProgress, Collapse, darken, Grid2, IconButton, InputAdornment, Paper, SxProps, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Theme, Typography } from "@mui/material";
|
||||
import { Box, Button, Card, CircularProgress, Collapse, darken, Divider, Grid2, IconButton, InputAdornment, List, ListItem, Paper, SxProps, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Theme, Typography } from "@mui/material";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { ChevronRight, Close, Search } from "@mui/icons-material"
|
||||
import { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import { useMobile } from "hooks";
|
||||
import { render } from "react-dom";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => {
|
||||
// const isMobile = useMobile()
|
||||
|
|
@ -29,13 +30,22 @@ const useStyles = makeStyles((theme: Theme) => {
|
|||
border: "none",
|
||||
textAlign: "right",
|
||||
},
|
||||
mobileTitleBox: {
|
||||
margin: theme.spacing(1),
|
||||
marginBottom: theme.spacing(1.5),
|
||||
},
|
||||
card: {
|
||||
// padding: theme.spacing(1),
|
||||
margin: theme.spacing(1),
|
||||
|
||||
}
|
||||
})},
|
||||
);
|
||||
|
||||
export interface Column<T> {
|
||||
title: string | JSX.Element;
|
||||
cellStyle?: SxProps<Theme> | undefined
|
||||
render?: (row: T) => JSX.Element;
|
||||
cellStyle?: SxProps<Theme>;
|
||||
render: (row: T) => JSX.Element;
|
||||
}
|
||||
|
||||
interface Props<T> {
|
||||
|
|
@ -53,6 +63,7 @@ interface Props<T> {
|
|||
multiGutter?: boolean;
|
||||
rowsPerPageOptions?: number[];
|
||||
noDataMessage?: string;
|
||||
renderMobile?: (row: T) => JSX.Element;
|
||||
}
|
||||
|
||||
export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
||||
|
|
@ -71,6 +82,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
columns,
|
||||
rowsPerPageOptions,
|
||||
noDataMessage,
|
||||
renderMobile,
|
||||
} = props
|
||||
const classes = useStyles();
|
||||
|
||||
|
|
@ -125,6 +137,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
const searchBar = () => {
|
||||
return (
|
||||
<TextField
|
||||
fullWidth
|
||||
variant="standard"
|
||||
placeholder="Search..."
|
||||
className={classes.title}
|
||||
|
|
@ -149,10 +162,116 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
)
|
||||
}
|
||||
|
||||
const renderTitle = () => {
|
||||
if (typeof(title) === "string" ) return (
|
||||
<Typography variant="h5" className={classes.title}>
|
||||
{title}
|
||||
</Typography>
|
||||
)
|
||||
if (title) return (
|
||||
<Box className={classes.title}>
|
||||
title
|
||||
</Box>
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
function capitalize(val: any) {
|
||||
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
||||
}
|
||||
|
||||
const renderMobileRow = (row: T) => {
|
||||
if (renderMobile) return (
|
||||
renderMobile(row)
|
||||
)
|
||||
if (columns) {
|
||||
return (
|
||||
<Grid2 container direction={"row"} spacing={2} padding={2}>
|
||||
{columns.map((column, index) => {
|
||||
return (
|
||||
<React.Fragment key={"mobile-row-column-"+index}>
|
||||
<Grid2 size={{ xs: 3 }} alignContent={"center"}>
|
||||
<Typography fontWeight={"bold"} >
|
||||
{column.title+": "}
|
||||
</Typography>
|
||||
</Grid2>
|
||||
<Grid2 size={{ xs: 9 }} alignContent={"center"}>
|
||||
{column.render(row)}
|
||||
</Grid2>
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</Grid2>
|
||||
)
|
||||
}
|
||||
return (
|
||||
Object.keys(row).map((key, index) => {
|
||||
let value = row[key as keyof typeof row]
|
||||
|
||||
return (
|
||||
<Grid2 container direction={"row"} key={"mobile-list-"+index} spacing={1} padding={1}>
|
||||
<Grid2 size={{ xs: 4 }} alignContent={"center"} >
|
||||
<Typography fontWeight={"bold"} >
|
||||
{capitalize(key)+": "}
|
||||
</Typography>
|
||||
</Grid2>
|
||||
<Grid2 size={{ xs: 8 }}>
|
||||
<Typography>
|
||||
{value+""}
|
||||
</Typography>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (isMobile) return (
|
||||
<Typography>
|
||||
Hello!
|
||||
</Typography>
|
||||
<Box>
|
||||
<Box className={classes.mobileTitleBox}>
|
||||
{renderTitle()}
|
||||
{setSearchText && searchBar()}
|
||||
</Box>
|
||||
|
||||
{rows.map((row, index) => {
|
||||
return (
|
||||
<Card className={classes.card} key={"mobile-card-"+index}>
|
||||
{renderMobileRow(row)}
|
||||
{renderGutter &&
|
||||
<>
|
||||
<Divider/>
|
||||
<Collapse className={classes.gutter} in={openGutters.includes(index)} >
|
||||
{renderGutter(row)}
|
||||
</Collapse>
|
||||
{openGutters.includes(index) && <Divider/>}
|
||||
<IconButton
|
||||
onClick={() => handleCollapse(index)}
|
||||
sx={{
|
||||
transform: openGutters.includes(index) ?
|
||||
'rotate(-90deg)' : 'rotate(90deg)',
|
||||
transition: 'transform 0.3s ease',
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<ChevronRight/>
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
{rows.length < 1 &&
|
||||
(!isLoading ?
|
||||
<Typography sx={{ textAlign: "center" }} color="textSecondary">
|
||||
{ noDataMessage ? noDataMessage : "No data."}
|
||||
</Typography>
|
||||
:
|
||||
<Box sx={{ width: "100%", textAlign: "center" }} >
|
||||
<CircularProgress/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
</Box>
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
@ -222,6 +341,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
|||
transform: openGutters.includes(index) ?
|
||||
'rotate(90deg)' : 'rotate(0deg)',
|
||||
transition: 'transform 0.3s ease',
|
||||
marginRight: -1
|
||||
}}
|
||||
onClick={() => handleCollapse(index)}>
|
||||
<ChevronRight/>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Chip,
|
||||
darken,
|
||||
|
|
@ -326,9 +327,28 @@ export default function Users() {
|
|||
);
|
||||
};
|
||||
|
||||
const renderMobile = (user: User) => {
|
||||
const avatarURL = user.settings.avatar;
|
||||
const avatar = avatarURL ? (
|
||||
<Avatar alt={user.name()} src={avatarURL} />
|
||||
) : (
|
||||
<Avatar alt={user.name()}>
|
||||
<Face />
|
||||
</Avatar>
|
||||
);
|
||||
return (
|
||||
<Box sx={{padding: 1}}>
|
||||
<ListItem>
|
||||
<ListItemAvatar>{avatar}</ListItemAvatar>
|
||||
<ListItemText primary={user.name()} secondary={user.settings.email} />
|
||||
</ListItem>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<ResponsiveTable<User>
|
||||
<ResponsiveTable<User>
|
||||
title="Users"
|
||||
rows={rows}
|
||||
columns={columns()}
|
||||
|
|
@ -340,6 +360,7 @@ export default function Users() {
|
|||
setSearchText={setSearchText}
|
||||
isLoading={isLoading}
|
||||
renderGutter={details}
|
||||
renderMobile={renderMobile}
|
||||
// multiGutter
|
||||
/>
|
||||
<ResponsiveDialog
|
||||
|
|
|
|||
|
|
@ -509,7 +509,8 @@ export default function ShareObject(props: Props) {
|
|||
// inputRef={props => <TextField {...props} helperText="" />}
|
||||
label="Expiration Date"
|
||||
value={newLinkExpiration}
|
||||
onChange={date => setNewLinkExpiration(date as Moment)}
|
||||
// onChange={(date: moment.Moment) => setNewLinkExpiration(date as Moment)}
|
||||
onChange={(value) => setNewLinkExpiration(value as Moment)}
|
||||
disablePast
|
||||
/>
|
||||
<FormControlLabel
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue