mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
Merge pull request #5 from AntlerEngineering/react-data-grid
React data grid
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
"@types/node": "12.7.4",
|
||||
"@types/ramda": "^0.26.21",
|
||||
"@types/react": "^16.9.2",
|
||||
"@types/react-data-grid": "^4.0.3",
|
||||
"@types/react-dom": "16.9.0",
|
||||
"@types/react-router-dom": "^4.3.5",
|
||||
"@types/react-sortable-hoc": "^0.6.5",
|
||||
@@ -25,6 +26,8 @@
|
||||
"lodash": "^4.17.15",
|
||||
"ramda": "^0.26.1",
|
||||
"react": "^16.9.0",
|
||||
"react-data-grid": "^6.1.0",
|
||||
"react-data-grid-addons": "^6.1.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"react-dropzone": "^10.1.8",
|
||||
"react-router-dom": "^5.0.1",
|
||||
|
||||
@@ -4,17 +4,13 @@ import { Checkbox } from "@material-ui/core";
|
||||
|
||||
// TODO: Create an interface for props
|
||||
const CheckBox = (props: any) => {
|
||||
const { columnData, cellData, cellActions, rowData, rowIndex } = props;
|
||||
const { value, row, onSubmit } = props;
|
||||
return (
|
||||
<Checkbox
|
||||
checked={cellData}
|
||||
name={`checkBox-controlled-${row.id}`}
|
||||
checked={value}
|
||||
onChange={e => {
|
||||
cellActions.updateFirestore({
|
||||
rowIndex,
|
||||
value: !cellData,
|
||||
docRef: rowData.ref,
|
||||
fieldName: columnData.fieldName,
|
||||
});
|
||||
onSubmit(row.ref, !value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,45 +1,40 @@
|
||||
import React from "react";
|
||||
import DateFnsUtils from "@date-io/date-fns";
|
||||
|
||||
import { Button } from "@material-ui/core";
|
||||
import { FieldType } from ".";
|
||||
import {
|
||||
MuiPickersUtilsProvider,
|
||||
// KeyboardTimePicker,
|
||||
// KeyboardDatePicker,
|
||||
DatePicker,
|
||||
DateTimePicker,
|
||||
} from "@material-ui/pickers";
|
||||
|
||||
// TODO: Create an interface for props
|
||||
const Date = (props: any) => {
|
||||
const {
|
||||
isFocusedCell,
|
||||
columnData,
|
||||
cellData,
|
||||
cellActions,
|
||||
rowData,
|
||||
rowIndex,
|
||||
} = props;
|
||||
const { value, row, onSubmit, fieldType } = props;
|
||||
function handleDateChange(date: Date | null) {
|
||||
if (date) {
|
||||
const cell = {
|
||||
rowIndex,
|
||||
value: date,
|
||||
docRef: rowData.ref,
|
||||
fieldName: columnData.fieldName,
|
||||
};
|
||||
cellActions.updateFirestore(cell);
|
||||
onSubmit(row.ref, date);
|
||||
}
|
||||
}
|
||||
if (!cellData && !isFocusedCell) return <Button>click to set</Button>;
|
||||
else
|
||||
return (
|
||||
<MuiPickersUtilsProvider utils={DateFnsUtils}>
|
||||
|
||||
return (
|
||||
<MuiPickersUtilsProvider utils={DateFnsUtils}>
|
||||
{fieldType === FieldType.date ? (
|
||||
<DatePicker
|
||||
value={cellData && cellData.toDate()}
|
||||
value={value ? value.toDate() : null}
|
||||
onChange={handleDateChange}
|
||||
emptyLabel="select a date"
|
||||
/>
|
||||
</MuiPickersUtilsProvider>
|
||||
);
|
||||
) : (
|
||||
<DateTimePicker
|
||||
value={value ? value.toDate() : null}
|
||||
onChange={handleDateChange}
|
||||
emptyLabel="select a time"
|
||||
/>
|
||||
)}
|
||||
</MuiPickersUtilsProvider>
|
||||
);
|
||||
};
|
||||
export default Date;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import React from "react";
|
||||
import DateFnsUtils from "@date-io/date-fns";
|
||||
|
||||
import { Button } from "@material-ui/core";
|
||||
import {
|
||||
MuiPickersUtilsProvider,
|
||||
// KeyboardTimePicker,
|
||||
// KeyboardDatePicker,
|
||||
DateTimePicker,
|
||||
} from "@material-ui/pickers";
|
||||
|
||||
// TODO: Create an interface for props
|
||||
const DateTime = (props: any) => {
|
||||
const {
|
||||
isFocusedCell,
|
||||
columnData,
|
||||
cellData,
|
||||
cellActions,
|
||||
rowData,
|
||||
rowIndex,
|
||||
} = props;
|
||||
function handleDateChange(date: Date | null) {
|
||||
if (date) {
|
||||
const cell = {
|
||||
rowIndex,
|
||||
value: date,
|
||||
docRef: rowData.ref,
|
||||
fieldName: columnData.fieldName,
|
||||
};
|
||||
cellActions.updateFirestore(cell);
|
||||
}
|
||||
}
|
||||
if (!cellData && !isFocusedCell) return <Button>click to set</Button>;
|
||||
else
|
||||
return (
|
||||
<MuiPickersUtilsProvider utils={DateFnsUtils}>
|
||||
<DateTimePicker
|
||||
value={cellData && cellData.toDate()}
|
||||
onChange={handleDateChange}
|
||||
emptyLabel="select a date"
|
||||
/>
|
||||
</MuiPickersUtilsProvider>
|
||||
);
|
||||
};
|
||||
export default DateTime;
|
||||
16
src/components/Fields/LongText.tsx
Normal file
16
src/components/Fields/LongText.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
import ExpandIcon from "@material-ui/icons/AspectRatio";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
|
||||
const UrlLink = (props: any) => {
|
||||
const { value, cellActions } = props;
|
||||
return value ? (
|
||||
<>
|
||||
<IconButton>
|
||||
<ExpandIcon />
|
||||
</IconButton>
|
||||
<p>{value}</p>
|
||||
</>
|
||||
) : null;
|
||||
};
|
||||
export default UrlLink;
|
||||
@@ -4,18 +4,15 @@ import { TextField } from "@material-ui/core";
|
||||
|
||||
// TODO: Create an interface for props
|
||||
const Number = (props: any) => {
|
||||
const { isFocusedCell, cellData, cellActions } = props;
|
||||
if (isFocusedCell)
|
||||
return (
|
||||
<TextField
|
||||
autoFocus
|
||||
type="number"
|
||||
defaultValue={cellData}
|
||||
onChange={e => {
|
||||
cellActions.update(e.target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
else return <p>{cellData}</p>;
|
||||
const { value, cellActions } = props;
|
||||
return (
|
||||
<TextField
|
||||
autoFocus
|
||||
type="number"
|
||||
defaultValue={value}
|
||||
onChange={e => {}}
|
||||
/>
|
||||
);
|
||||
// else return <p>{cellData}</p>;
|
||||
};
|
||||
export default Number;
|
||||
|
||||
@@ -3,19 +3,14 @@ import MuiRating from "@material-ui/lab/Rating";
|
||||
|
||||
// TODO: Create an interface for props
|
||||
const Rating = (props: any) => {
|
||||
const { columnData, cellData, cellActions, rowData, rowIndex } = props;
|
||||
const { value, row, onSubmit } = props;
|
||||
return (
|
||||
<MuiRating
|
||||
name={`rating-controlled-${columnData.fieldName}-${rowIndex}`}
|
||||
value={cellData}
|
||||
// TODO: make it unique for each
|
||||
name={`rating-controlled-${row.id}`}
|
||||
value={value}
|
||||
onChange={(event, newValue) => {
|
||||
const cell = {
|
||||
rowIndex,
|
||||
value: newValue,
|
||||
docRef: rowData.ref,
|
||||
fieldName: columnData.fieldName,
|
||||
};
|
||||
cellActions.updateFirestore(cell);
|
||||
onSubmit(row.ref, newValue);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
import { TextField } from "@material-ui/core";
|
||||
|
||||
// TODO: Create an interface for props
|
||||
const SimpleText = (props: any) => {
|
||||
const { isFocusedCell, cellData, cellActions } = props;
|
||||
|
||||
if (isFocusedCell)
|
||||
return (
|
||||
<TextField
|
||||
autoFocus
|
||||
defaultValue={cellData}
|
||||
onChange={e => {
|
||||
cellActions.update(e.target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
else return <p>{cellData}</p>;
|
||||
};
|
||||
export default SimpleText;
|
||||
17
src/components/Fields/UrlLink.tsx
Normal file
17
src/components/Fields/UrlLink.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from "react";
|
||||
import EditIcon from "@material-ui/icons/Edit";
|
||||
// TODO: regex validating url
|
||||
// ^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$
|
||||
|
||||
const UrlLink = (props: any) => {
|
||||
const { value, cellActions } = props;
|
||||
return value ? (
|
||||
<>
|
||||
<EditIcon />
|
||||
<a href={value} target="_blank">
|
||||
{value}
|
||||
</a>
|
||||
</>
|
||||
) : null;
|
||||
};
|
||||
export default UrlLink;
|
||||
@@ -1,262 +0,0 @@
|
||||
import React, { useEffect } from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { Theme, WithStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
TableCell as MuiTableCell,
|
||||
createStyles,
|
||||
withStyles,
|
||||
Paper,
|
||||
Button,
|
||||
} from "@material-ui/core";
|
||||
|
||||
import {
|
||||
AutoSizer,
|
||||
Column,
|
||||
Table as MuiTable,
|
||||
TableCellRenderer,
|
||||
TableHeaderProps,
|
||||
} from "react-virtualized";
|
||||
|
||||
import { FieldType, getFieldIcon } from "./Fields";
|
||||
import ColumnDrawer from "./ColumnDrawer";
|
||||
import TableCell from "../components/TableCell";
|
||||
|
||||
import useCell, { Cell } from "../hooks/useFiretable/useCell";
|
||||
import useFiretable from "../hooks/useFiretable";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
flexContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
boxSizing: "border-box",
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
tableRowHover: {
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
},
|
||||
},
|
||||
tableCell: {
|
||||
flex: 1,
|
||||
},
|
||||
noClick: {
|
||||
cursor: "initial",
|
||||
},
|
||||
});
|
||||
|
||||
interface ColumnData {
|
||||
columnData: any;
|
||||
dataKey: string;
|
||||
label: string;
|
||||
numeric?: boolean;
|
||||
width: number;
|
||||
}
|
||||
|
||||
interface Row {
|
||||
index: number;
|
||||
}
|
||||
|
||||
interface MuiVirtualizedTableProps extends WithStyles<typeof styles> {
|
||||
columns: ColumnData[];
|
||||
focusedCell: Cell | null;
|
||||
cellActions: any;
|
||||
headerHeight?: number;
|
||||
onRowClick?: () => void;
|
||||
rowCount: number;
|
||||
rowGetter: (row: Row) => any;
|
||||
rowHeight?: number;
|
||||
}
|
||||
|
||||
class MuiVirtualizedTable extends React.PureComponent<
|
||||
MuiVirtualizedTableProps
|
||||
> {
|
||||
static defaultProps = {
|
||||
headerHeight: 48,
|
||||
rowHeight: 40,
|
||||
};
|
||||
|
||||
getRowClassName = ({ index }: Row) => {
|
||||
const { classes, onRowClick } = this.props;
|
||||
|
||||
return clsx(classes.tableRow, classes.flexContainer, {
|
||||
[classes.tableRowHover]: index !== -1 && onRowClick != null,
|
||||
});
|
||||
};
|
||||
|
||||
cellRenderer: TableCellRenderer = ({
|
||||
cellData,
|
||||
columnData,
|
||||
columnIndex,
|
||||
dataKey,
|
||||
isScrolling,
|
||||
rowData,
|
||||
rowIndex,
|
||||
}) => {
|
||||
const {
|
||||
columns,
|
||||
classes,
|
||||
rowHeight,
|
||||
onRowClick,
|
||||
cellActions,
|
||||
focusedCell,
|
||||
} = this.props;
|
||||
const fieldType = columnData.fieldType;
|
||||
return (
|
||||
<TableCell
|
||||
fieldType={fieldType}
|
||||
rowIndex={rowIndex}
|
||||
rowData={rowData}
|
||||
columnData={columnData}
|
||||
classes={classes}
|
||||
cellActions={cellActions}
|
||||
cellData={cellData}
|
||||
onRowClick={onRowClick}
|
||||
rowHeight={rowHeight}
|
||||
columnIndex={columnIndex}
|
||||
columns={columns}
|
||||
focusedCell={focusedCell}
|
||||
/>
|
||||
);
|
||||
};
|
||||
headerRenderer = ({
|
||||
label,
|
||||
columnData,
|
||||
dataKey,
|
||||
columnIndex,
|
||||
}: TableHeaderProps & { columnIndex: number }) => {
|
||||
const { headerHeight, columns, classes } = this.props;
|
||||
|
||||
return (
|
||||
<MuiTableCell
|
||||
component="div"
|
||||
className={clsx(
|
||||
classes.tableCell,
|
||||
classes.flexContainer,
|
||||
classes.noClick
|
||||
)}
|
||||
variant="head"
|
||||
style={{ height: headerHeight }}
|
||||
align={columns[columnIndex].numeric || false ? "right" : "left"}
|
||||
>
|
||||
{dataKey === "add" ? (
|
||||
<ColumnDrawer
|
||||
columns={columns}
|
||||
addColumn={columnData.actions.addColumn}
|
||||
/>
|
||||
) : (
|
||||
<Button size="small">
|
||||
{getFieldIcon(columnData.fieldType)} {label}
|
||||
</Button>
|
||||
)}
|
||||
</MuiTableCell>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
classes,
|
||||
columns,
|
||||
rowHeight,
|
||||
headerHeight,
|
||||
...tableProps
|
||||
} = this.props;
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<>
|
||||
<MuiTable
|
||||
height={height}
|
||||
width={width}
|
||||
rowHeight={rowHeight!}
|
||||
headerHeight={headerHeight!}
|
||||
{...tableProps}
|
||||
rowClassName={this.getRowClassName}
|
||||
>
|
||||
{[
|
||||
...columns.map(({ dataKey, ...other }, index) => {
|
||||
return (
|
||||
<Column
|
||||
key={dataKey}
|
||||
headerRenderer={headerProps =>
|
||||
this.headerRenderer({
|
||||
...headerProps,
|
||||
columnIndex: index,
|
||||
})
|
||||
}
|
||||
className={classes.flexContainer}
|
||||
cellRenderer={this.cellRenderer}
|
||||
dataKey={dataKey}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
]}
|
||||
</MuiTable>
|
||||
</>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const VirtualizedTable = withStyles(styles)(MuiVirtualizedTable);
|
||||
|
||||
// TODO: Create an interface for props
|
||||
export default function Table(props: any) {
|
||||
const { collection } = props;
|
||||
const { tableState, tableActions } = useFiretable(collection);
|
||||
|
||||
useEffect(() => {
|
||||
tableActions.table.set(collection);
|
||||
}, [collection]);
|
||||
|
||||
if (tableState.columns)
|
||||
return (
|
||||
<>
|
||||
<Paper style={{ height: 400, width: "100%" }}>
|
||||
<VirtualizedTable
|
||||
focusedCell={tableState.cell}
|
||||
cellActions={tableActions.cell}
|
||||
rowCount={tableState.rows.length}
|
||||
rowGetter={({ index }) => tableState.rows[index]}
|
||||
columns={[
|
||||
...tableState.columns.map(
|
||||
(column: {
|
||||
fieldName: string;
|
||||
columnName: string;
|
||||
type: FieldType;
|
||||
}) => ({
|
||||
width: 200,
|
||||
label: column.columnName,
|
||||
dataKey: column.fieldName,
|
||||
columnData: {
|
||||
fieldType: column.type,
|
||||
fieldName: column.fieldName,
|
||||
actions: {},
|
||||
},
|
||||
})
|
||||
),
|
||||
{
|
||||
width: 80,
|
||||
label: "add",
|
||||
dataKey: "add",
|
||||
columnData: {
|
||||
fieldType: "DELETE",
|
||||
actions: {
|
||||
addColumn: tableActions.column.add,
|
||||
deleteRow: tableActions.row.delete,
|
||||
},
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Paper>
|
||||
<Button onClick={tableActions.row.add}>Add Row</Button>
|
||||
</>
|
||||
);
|
||||
else return <>insert loading Skeleton here</>;
|
||||
}
|
||||
109
src/components/Table/HeaderPopper.tsx
Normal file
109
src/components/Table/HeaderPopper.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import React from "react";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import Popper from "@material-ui/core/Popper";
|
||||
import Fade from "@material-ui/core/Fade";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import { createStyles, Theme, makeStyles } from "@material-ui/core/styles";
|
||||
import { TextField, Grid } from "@material-ui/core";
|
||||
import { FIELDS } from "../Fields";
|
||||
const useStyles = makeStyles(Theme =>
|
||||
createStyles({
|
||||
container: {
|
||||
padding: 10
|
||||
},
|
||||
typography: {
|
||||
padding: 1
|
||||
},
|
||||
header: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0
|
||||
//zIndex: 100000
|
||||
},
|
||||
button: {
|
||||
// margin: theme.spacing(1)
|
||||
},
|
||||
root: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap"
|
||||
},
|
||||
formControl: {
|
||||
margin: Theme.spacing(1),
|
||||
minWidth: 120
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: Theme.spacing(2)
|
||||
}
|
||||
})
|
||||
);
|
||||
const HeaderPopper = (props: any) => {
|
||||
const { anchorEl, column, handleClose } = props;
|
||||
console.log(column);
|
||||
const [values, setValues] = React.useState({
|
||||
age: "",
|
||||
name: "hai"
|
||||
});
|
||||
console.log(props);
|
||||
const classes = useStyles();
|
||||
function handleChange(
|
||||
event: React.ChangeEvent<{ name?: string; value: unknown }>
|
||||
) {
|
||||
setValues(oldValues => ({
|
||||
...oldValues,
|
||||
[event.target.name as string]: event.target.value
|
||||
}));
|
||||
}
|
||||
|
||||
if (column) {
|
||||
return (
|
||||
<Popper
|
||||
id={`id-${column.name}`}
|
||||
open={!!anchorEl}
|
||||
anchorEl={anchorEl}
|
||||
transition
|
||||
>
|
||||
{({ TransitionProps }) => (
|
||||
<Fade {...TransitionProps} timeout={350}>
|
||||
<Paper className={classes.container}>
|
||||
<Grid container direction="column">
|
||||
<TextField label="Column name" defaultValue={column.name} />
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel htmlFor="age-simple">Field Type</InputLabel>
|
||||
<Select
|
||||
value={FIELDS[0].type}
|
||||
onChange={handleChange}
|
||||
inputProps={{
|
||||
name: "age",
|
||||
id: "age-simple"
|
||||
}}
|
||||
>
|
||||
{FIELDS.map((field: any) => {
|
||||
return (
|
||||
<MenuItem value={field.type}>
|
||||
{field.icon} {field.name}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<Button>Add</Button>
|
||||
<Button color="secondary" onClick={handleClose}>
|
||||
cancel
|
||||
</Button>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Fade>
|
||||
)}
|
||||
</Popper>
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
};
|
||||
|
||||
export default HeaderPopper;
|
||||
85
src/components/Table/NewColumnPopper.tsx
Normal file
85
src/components/Table/NewColumnPopper.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import React from "react";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputLabel from "@material-ui/core/InputLabel";
|
||||
import MenuItem from "@material-ui/core/MenuItem";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import FormControl from "@material-ui/core/FormControl";
|
||||
import Select from "@material-ui/core/Select";
|
||||
import Popper from "@material-ui/core/Popper";
|
||||
import Fade from "@material-ui/core/Fade";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import { createStyles, Theme, makeStyles } from "@material-ui/core/styles";
|
||||
import { TextField } from "@material-ui/core";
|
||||
const useStyles = makeStyles(Theme =>
|
||||
createStyles({
|
||||
typography: {
|
||||
padding: 1
|
||||
},
|
||||
header: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0
|
||||
//zIndex: 100000
|
||||
},
|
||||
button: {
|
||||
// margin: theme.spacing(1)
|
||||
},
|
||||
root: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap"
|
||||
},
|
||||
formControl: {
|
||||
margin: Theme.spacing(1),
|
||||
minWidth: 120
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: Theme.spacing(2)
|
||||
}
|
||||
})
|
||||
);
|
||||
const NewColumnPopper = (props: any) => {
|
||||
const { anchorEl, column } = props;
|
||||
const [values, setValues] = React.useState({
|
||||
age: "",
|
||||
name: "hai"
|
||||
});
|
||||
console.log(props);
|
||||
const classes = useStyles();
|
||||
function handleChange(
|
||||
event: React.ChangeEvent<{ name?: string; value: unknown }>
|
||||
) {
|
||||
setValues(oldValues => ({
|
||||
...oldValues,
|
||||
[event.target.name as string]: event.target.value
|
||||
}));
|
||||
}
|
||||
return (
|
||||
<Popper id={"id"} open={!!anchorEl} anchorEl={anchorEl} transition>
|
||||
{({ TransitionProps }) => (
|
||||
<Fade {...TransitionProps} timeout={350}>
|
||||
<Paper>
|
||||
<TextField label="Column name" />
|
||||
<FormControl className={classes.formControl}>
|
||||
<InputLabel htmlFor="age-simple">Age</InputLabel>
|
||||
<Select
|
||||
value={values.age}
|
||||
onChange={handleChange}
|
||||
inputProps={{
|
||||
name: "age",
|
||||
id: "age-simple"
|
||||
}}
|
||||
>
|
||||
<MenuItem value={10}>Ten</MenuItem>
|
||||
<MenuItem value={20}>Twenty</MenuItem>
|
||||
<MenuItem value={30}>Thirty</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Paper>
|
||||
</Fade>
|
||||
)}
|
||||
</Popper>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewColumnPopper;
|
||||
181
src/components/Table/index.tsx
Normal file
181
src/components/Table/index.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import React, { useState } from "react";
|
||||
import ReactDataGrid from "react-data-grid";
|
||||
|
||||
import useFiretable from "../../hooks/useFiretable";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
|
||||
import { createStyles, Theme, makeStyles } from "@material-ui/core/styles";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import EditIcon from "@material-ui/icons/Edit";
|
||||
import HeaderPopper from "./HeaderPopper";
|
||||
import { FieldType } from "../Fields";
|
||||
|
||||
import Date from "../Fields/Date";
|
||||
import Rating from "../Fields/Rating";
|
||||
import CheckBox from "../Fields/CheckBox";
|
||||
import UrlLink from "../Fields/UrlLink";
|
||||
const useStyles = makeStyles(Theme =>
|
||||
createStyles({
|
||||
typography: {
|
||||
padding: 1
|
||||
},
|
||||
header: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
headerButton: {
|
||||
width: "100%"
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const copyPaste = (props: any) => {
|
||||
console.log(props);
|
||||
};
|
||||
const editable = (fieldType: FieldType) => {
|
||||
switch (fieldType) {
|
||||
case FieldType.date:
|
||||
case FieldType.dateTime:
|
||||
case FieldType.rating:
|
||||
case FieldType.checkBox:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
const onSubmit = (fieldName: string) => (
|
||||
ref: firebase.firestore.DocumentReference,
|
||||
value: any
|
||||
) => {
|
||||
if (value !== null || value !== undefined) {
|
||||
ref.update({ [fieldName]: value });
|
||||
}
|
||||
};
|
||||
|
||||
const DateFormatter = (fieldName: string, fieldType: FieldType) => (
|
||||
props: any
|
||||
) => {
|
||||
return (
|
||||
<Date {...props} onSubmit={onSubmit(fieldName)} fieldType={fieldType} />
|
||||
);
|
||||
};
|
||||
|
||||
const formatter = (fieldType: FieldType, fieldName: string) => {
|
||||
switch (fieldType) {
|
||||
case FieldType.date:
|
||||
case FieldType.dateTime:
|
||||
return DateFormatter(fieldName, fieldType);
|
||||
case FieldType.rating:
|
||||
return (props: any) => {
|
||||
return <Rating {...props} onSubmit={onSubmit(fieldName)} />;
|
||||
};
|
||||
case FieldType.checkBox:
|
||||
return (props: any) => {
|
||||
return <CheckBox {...props} onSubmit={onSubmit(fieldName)} />;
|
||||
};
|
||||
case FieldType.url:
|
||||
return (props: any) => {
|
||||
return <UrlLink {...props} onSubmit={onSubmit(fieldName)} />;
|
||||
};
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function Table(props: any) {
|
||||
const { collection } = props;
|
||||
const { tableState, tableActions } = useFiretable(collection);
|
||||
const classes = useStyles();
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||
|
||||
const [header, setHeader] = useState<any | null>();
|
||||
|
||||
const handleCloseHeader = () => {
|
||||
setHeader(null);
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const handleClick = (headerProps: any) => (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
handleCloseHeader();
|
||||
setAnchorEl(event.currentTarget);
|
||||
setHeader(headerProps);
|
||||
};
|
||||
|
||||
const onGridRowsUpdated = (props: any) => {
|
||||
const { fromRowData, updated } = props;
|
||||
fromRowData.ref.update(updated);
|
||||
};
|
||||
const onCellSelected = (args: any) => {
|
||||
handleCloseHeader();
|
||||
console.log(args);
|
||||
};
|
||||
const headerRenderer = (props: any) => {
|
||||
switch (props.column.key) {
|
||||
case "new":
|
||||
return (
|
||||
<Button onClick={handleClick(props)} className={classes.header}>
|
||||
{props.column.name}
|
||||
</Button>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<div className={classes.header}>
|
||||
<Button
|
||||
className={classes.headerButton}
|
||||
onClick={handleClick(props)}
|
||||
aria-label="edit"
|
||||
>
|
||||
{props.column.name} <EditIcon />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (tableState.columns) {
|
||||
let columns = tableState.columns.map((column: any) => ({
|
||||
key: column.fieldName,
|
||||
name: column.columnName,
|
||||
editable: editable(column.type),
|
||||
resizeable: true,
|
||||
// frozen: column.fieldName === "cohort",
|
||||
headerRenderer: headerRenderer,
|
||||
formatter: formatter(column.type, column.fieldName),
|
||||
width: 200,
|
||||
...column
|
||||
}));
|
||||
columns.push({
|
||||
key: "new",
|
||||
name: "Add column",
|
||||
width: 160,
|
||||
headerRenderer: headerRenderer
|
||||
});
|
||||
const rows = tableState.rows;
|
||||
return (
|
||||
<>
|
||||
<ReactDataGrid
|
||||
columns={columns}
|
||||
rowGetter={i => rows[i]}
|
||||
rowsCount={rows.length}
|
||||
onGridRowsUpdated={onGridRowsUpdated}
|
||||
enableCellSelect={true}
|
||||
onCellCopyPaste={copyPaste}
|
||||
minHeight={500}
|
||||
onCellSelected={onCellSelected}
|
||||
/>
|
||||
<Button onClick={tableActions.row.add}>Add Row</Button>
|
||||
<HeaderPopper
|
||||
handleClose={handleCloseHeader}
|
||||
anchorEl={anchorEl}
|
||||
column={header && header.column}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
} else return <p>Loading</p>;
|
||||
}
|
||||
|
||||
export default Table;
|
||||
@@ -1,157 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
import {
|
||||
TableCell as MuiTableCell,
|
||||
Switch,
|
||||
IconButton,
|
||||
} from "@material-ui/core";
|
||||
import DeleteIcon from "@material-ui/icons/Delete";
|
||||
|
||||
import { FieldType } from "./Fields";
|
||||
import SimpleText from "./Fields/SimpleText";
|
||||
import CheckBox from "./Fields/CheckBox";
|
||||
import Number from "./Fields/Number";
|
||||
import Rating from "./Fields/Rating";
|
||||
import Date from "./Fields/Date";
|
||||
import DateTime from "./Fields/DateTime";
|
||||
import Image from "./Fields/Image";
|
||||
|
||||
// TODO: Create an interface for props
|
||||
const TableCell = (props: any) => {
|
||||
const {
|
||||
fieldType,
|
||||
rowIndex,
|
||||
rowData,
|
||||
columnData,
|
||||
classes,
|
||||
cellActions,
|
||||
cellData,
|
||||
onRowClick,
|
||||
rowHeight,
|
||||
columnIndex,
|
||||
columns,
|
||||
focusedCell,
|
||||
} = props;
|
||||
const isFocusedCell =
|
||||
focusedCell &&
|
||||
focusedCell.fieldName === columnData.fieldName &&
|
||||
focusedCell.rowIndex === rowIndex;
|
||||
|
||||
const renderCell = () => {
|
||||
switch (fieldType) {
|
||||
case FieldType.checkBox:
|
||||
return (
|
||||
<CheckBox
|
||||
rowIndex={rowIndex}
|
||||
rowData={rowData}
|
||||
isFocusedCell={isFocusedCell}
|
||||
cellData={cellData}
|
||||
cellActions={cellActions}
|
||||
columnData={columnData}
|
||||
/>
|
||||
);
|
||||
case FieldType.rating:
|
||||
return (
|
||||
<Rating
|
||||
rowIndex={rowIndex}
|
||||
rowData={rowData}
|
||||
isFocusedCell={isFocusedCell}
|
||||
cellData={cellData}
|
||||
cellActions={cellActions}
|
||||
columnData={columnData}
|
||||
/>
|
||||
);
|
||||
case FieldType.image:
|
||||
return (
|
||||
<Image
|
||||
rowIndex={rowIndex}
|
||||
rowData={rowData}
|
||||
isFocusedCell={isFocusedCell}
|
||||
cellData={cellData}
|
||||
cellActions={cellActions}
|
||||
columnData={columnData}
|
||||
/>
|
||||
);
|
||||
case FieldType.date:
|
||||
return (
|
||||
<Date
|
||||
rowIndex={rowIndex}
|
||||
rowData={rowData}
|
||||
isFocusedCell={isFocusedCell}
|
||||
cellData={cellData}
|
||||
cellActions={cellActions}
|
||||
columnData={columnData}
|
||||
/>
|
||||
);
|
||||
case FieldType.dateTime:
|
||||
return (
|
||||
<DateTime
|
||||
rowIndex={rowIndex}
|
||||
rowData={rowData}
|
||||
isFocusedCell={isFocusedCell}
|
||||
cellData={cellData}
|
||||
cellActions={cellActions}
|
||||
columnData={columnData}
|
||||
/>
|
||||
);
|
||||
case FieldType.number:
|
||||
return (
|
||||
<Number
|
||||
isFocusedCell={isFocusedCell}
|
||||
cellData={cellData}
|
||||
cellActions={cellActions}
|
||||
columnData={columnData}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<SimpleText
|
||||
isFocusedCell={isFocusedCell}
|
||||
cellData={cellData}
|
||||
cellActions={cellActions}
|
||||
columnData={columnData}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
if (fieldType === "DELETE")
|
||||
return (
|
||||
<IconButton
|
||||
aria-label="delete"
|
||||
onClick={() => {
|
||||
columnData.actions.deleteRow(rowIndex, rowData.id);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
);
|
||||
|
||||
return (
|
||||
<MuiTableCell
|
||||
component="div"
|
||||
className={clsx(classes.tableCell, classes.flexContainer, {
|
||||
[classes.noClick]: onRowClick == null,
|
||||
})}
|
||||
variant="body"
|
||||
onClick={() => {
|
||||
// set focusedCell on click
|
||||
cellActions.set({
|
||||
rowIndex,
|
||||
docRef: rowData.ref,
|
||||
fieldName: columnData.fieldName,
|
||||
value: cellData,
|
||||
});
|
||||
}}
|
||||
style={{ height: rowHeight }}
|
||||
align={
|
||||
(columnIndex != null && columns[columnIndex].numeric) || false
|
||||
? "right"
|
||||
: "left"
|
||||
}
|
||||
>
|
||||
{renderCell()}
|
||||
</MuiTableCell>
|
||||
);
|
||||
};
|
||||
export default TableCell;
|
||||
Reference in New Issue
Block a user