create columns,column settings flags toggles setup

This commit is contained in:
shams mosowi
2019-09-23 14:45:15 +10:00
parent 26310382d4
commit 06fc0e8c18
6 changed files with 180 additions and 47 deletions

View File

@@ -18,7 +18,7 @@
- Create columns (fields) ✅
- Create rows(documents) ✅
- Edit cells ✅
- Authenicate ✅
- Authenticate ✅
- Delete rows ✅
## MVP
@@ -27,12 +27,12 @@
- single select(string)
- [https://material-ui.com/components/chips/#chip-array] Multiple select(array of strings)
- date(Firebase timestamp)
- time(Firebase timestamp)
- date(Firebase timestamp)
- time(Firebase timestamp)
- file(firebase storage url string)
- image(firebase storage url string)
- single select reference(DocRefrence)
- mulit select reference(DocRefrence)
- single select reference(DocReference)
- multi select reference(DocReference)
- rating ✅
### Functionality:
@@ -44,10 +44,11 @@
- Edit tables
- Hide tables
- Fixed column
- resizable column ✅
- keyboard Navigation:
- Up key to move to the cell above ✅
- Down key to move to the cell bellow, if last cell create a new row ✅
- Tab to go to the next cell
- Tab to go to the next cell
- column / table Create/edit validation
## V1
@@ -64,7 +65,7 @@
- Sort rows
- Locked columns
- Table view only mode
- Subcollection tables
- SubCollection tables
- Permissions
- Duplicate columns
- Filters:

View File

@@ -4,13 +4,13 @@ import MuiRating from "@material-ui/lab/Rating";
// TODO: Create an interface for props
interface Props {
value: number | null;
value: number;
row: any;
onSubmit: Function;
//fieldType: FieldType;
}
const Rating = (props: Props) => {
const { value, row, onSubmit } = props;
return (
<MuiRating
// TODO: make it unique for each

View File

@@ -13,6 +13,8 @@ import URLIcon from "@material-ui/icons/Explore";
import NumberIcon from "@material-ui/icons/Looks3";
import propEq from "ramda/es/propEq";
import find from "ramda/es/find";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
export enum FieldType {
simpleText = "SIMPLE_TEXT",
longText = "LONG_TEXT",
@@ -46,3 +48,31 @@ export const FIELDS = [
export const getFieldIcon = (type: FieldType) => {
return find(propEq("type", type))(FIELDS).icon;
};
export const isFieldType = (fieldType: any) => {
const fieldTypes = FIELDS.map(field => field.type);
return fieldTypes.includes(fieldType);
};
export const FieldsDropDown = (value: FieldType | null, onChange: any) => {
return (
<Select
value={value}
onChange={onChange}
inputProps={{
name: "type",
id: "type",
}}
>
{FIELDS.map(
(field: { icon: JSX.Element; name: string; type: FieldType }) => {
return (
<MenuItem id={`select-field-${field.type}`} value={field.type}>
<>{field.name}</>
</MenuItem>
);
}
)}
</Select>
);
};

View File

@@ -1,22 +1,33 @@
import React from "react";
import React, { useEffect } 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 ClickAwayListener from "@material-ui/core/ClickAwayListener";
import { createStyles, Theme, makeStyles } from "@material-ui/core/styles";
import { createStyles, makeStyles } from "@material-ui/core/styles";
import { TextField, Grid } from "@material-ui/core";
import { FIELDS } from "../Fields";
import { FieldsDropDown, isFieldType } from "../Fields";
import ToggleButton from "@material-ui/lab/ToggleButton";
import ToggleButtonGroup from "@material-ui/lab/ToggleButtonGroup";
import LockIcon from "@material-ui/icons/Lock";
import LockOpenIcon from "@material-ui/icons/LockOpen";
import VisibilityIcon from "@material-ui/icons/Visibility";
import VisibilityOffIcon from "@material-ui/icons/VisibilityOff";
import FormatItalicIcon from "@material-ui/icons/FormatItalic";
import FormatUnderlinedIcon from "@material-ui/icons/FormatUnderlined";
import FormatColorFillIcon from "@material-ui/icons/FormatColorFill";
const useStyles = makeStyles(Theme =>
createStyles({
container: {
padding: 10,
padding: 15,
},
typography: {
padding: 1,
@@ -41,17 +52,29 @@ const useStyles = makeStyles(Theme =>
selectEmpty: {
marginTop: Theme.spacing(2),
},
toggleGrouped: {
margin: Theme.spacing(0.5),
border: "none",
padding: Theme.spacing(0, 1),
"&:not(:first-child)": {
borderRadius: Theme.shape.borderRadius,
},
"&:first-child": {
borderRadius: Theme.shape.borderRadius,
},
},
})
);
const HeaderPopper = (props: any) => {
const { anchorEl, column, handleClose } = props;
console.log(column);
const { anchorEl, column, handleClose, actions } = props;
const [values, setValues] = React.useState({
age: "",
name: "hai",
type: null,
name: "",
});
console.log(props);
const [flags, setFlags] = React.useState(() => [""]);
const classes = useStyles();
function handleChange(
event: React.ChangeEvent<{ name?: string; value: unknown }>
) {
@@ -60,10 +83,45 @@ const HeaderPopper = (props: any) => {
[event.target.name as string]: event.target.value,
}));
}
console.log(column);
const setValue = (key: string, value: any) => {
setValues(oldValues => ({
...oldValues,
[key]: value,
}));
};
useEffect(() => {
if (column && !column.isNew)
setValues(oldValues => ({
...oldValues,
name: column.name,
type: column.type,
key: column.key,
isNew: column.isNew,
}));
}, [column]);
const onClickAway = (event: any) => {
const dropDownClicked = isFieldType(event.target.dataset.value);
if (!dropDownClicked) {
handleClose();
}
};
const handleToggle = (
event: React.MouseEvent<HTMLElement>,
newFlags: string[]
) => {
setFlags(newFlags);
};
const createNewColumn = () => {
const { name, type } = values;
actions.add(name, type);
handleClose();
};
if (column) {
return (
<ClickAwayListener onClickAway={handleClose}>
<ClickAwayListener onClickAway={onClickAway}>
<Popper
id={`id-${column.name}`}
open={!!anchorEl}
@@ -74,26 +132,51 @@ const HeaderPopper = (props: any) => {
<Fade {...TransitionProps} timeout={350}>
<Paper className={classes.container}>
<Grid container direction="column">
<TextField label="Column name" defaultValue={column.name} />
<ToggleButtonGroup
size="small"
value={flags}
className={classes.toggleGrouped}
onChange={handleToggle}
arial-label="column settings"
>
<ToggleButton value="editable" aria-label="bold">
{flags.includes("editable") ? (
<LockOpenIcon />
) : (
<LockIcon />
)}
</ToggleButton>
<ToggleButton value="visible" aria-label="italic">
{flags.includes("visible") ? (
<VisibilityIcon />
) : (
<VisibilityOffIcon />
)}
</ToggleButton>
<ToggleButton value="freeze" aria-label="underlined">
<FormatUnderlinedIcon />
</ToggleButton>
<ToggleButton value="resize" aria-label="color">
<FormatColorFillIcon />
</ToggleButton>
</ToggleButtonGroup>
<TextField
label="Column name"
name="name"
defaultValue={values.name}
// onChange={handleChange}
onChange={e => {
setValue("name", e.target.value);
}}
/>
<FormControl className={classes.formControl}>
<InputLabel htmlFor="age-simple">Field Type</InputLabel>
<Select
value={column.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>
<InputLabel htmlFor="Field-select">Field Type</InputLabel>
{FieldsDropDown(values.type, handleChange)}
{column.isNew && (
<Button onClick={createNewColumn}>Add</Button>
)}
<Button color="secondary" onClick={handleClose}>
cancel
</Button>

View File

@@ -67,7 +67,13 @@ const formatter = (fieldType: FieldType, key: string) => {
return DateFormatter(key, fieldType);
case FieldType.rating:
return (props: any) => {
return <Rating {...props} onSubmit={onSubmit(key)} />;
return (
<Rating
{...props}
onSubmit={onSubmit(key)}
value={typeof props.value === "number" ? props.value : 0}
/>
);
};
case FieldType.checkBox:
return (props: any) => {
@@ -112,23 +118,31 @@ function Table(props: any) {
console.log(args);
};
const headerRenderer = (props: any) => {
switch (props.column.key) {
const { column } = props;
switch (column.key) {
case "new":
return (
<Button onClick={handleClick(props)} className={classes.header}>
{props.column.name}
</Button>
<div className={classes.header}>
<Button
onClick={handleClick(props)}
style={{ width: column.width }}
>
{column.name}
</Button>
</div>
);
default:
return (
<div className={classes.header}>
<Button
style={{ width: column.width }}
className={classes.headerButton}
onClick={handleClick(props)}
aria-label="edit"
>
{getFieldIcon(props.column.type)}
{props.column.name} <EditIcon />
{props.column.name}
{/* <EditIcon /> */}
</Button>
</div>
);
@@ -148,6 +162,7 @@ function Table(props: any) {
...column,
}));
columns.push({
isNew: true,
key: "new",
name: "Add column",
width: 160,
@@ -174,6 +189,7 @@ function Table(props: any) {
handleClose={handleCloseHeader}
anchorEl={anchorEl}
column={header && header.column}
actions={tableActions.column}
/>
</>
);

View File

@@ -1,6 +1,7 @@
import { useEffect } from "react";
import useDoc, { DocActions } from "../useDoc";
import { FieldType } from "../../components/Fields";
import _camelCase from "lodash/camelCase";
const useTableConfig = (tablePath: string) => {
const [tableConfigState, documentDispatch] = useDoc({
path: `${tablePath}/_FIRETABLE_`,
@@ -14,8 +15,10 @@ const useTableConfig = (tablePath: string) => {
const setTable = (table: string) => {
documentDispatch({ path: `${table}/_FIRETABLE_`, columns: [], doc: null });
};
const add = (name: string, key: string, type: FieldType) => {
const add = (name: string, type: FieldType) => {
//TODO: validation
const { columns } = tableConfigState;
const key = _camelCase(name);
documentDispatch({
action: DocActions.update,
data: { columns: [...columns, { name, key, type }] },