add FileUploader

This commit is contained in:
Sidney Alcantara
2020-02-18 18:01:46 +11:00
parent 49d5fd1f42
commit 7d604fa0bb
9 changed files with 211 additions and 26 deletions

View File

@@ -1,4 +1,7 @@
import React from "react";
import createMuiTheme from "@material-ui/core/styles/createMuiTheme";
import ClearIcon from "@material-ui/icons/Clear";
const HEADING_TEXT = "Europa, sans-serif";
const BODY_TEXT = '"Open Sans", sans-serif';
@@ -88,6 +91,11 @@ const Theme = createMuiTheme({
root: {
borderRadius: 4,
},
outlined: {
backgroundColor: "rgba(0, 0, 0, 0.08)",
borderColor: "rgba(0, 0, 0, 0.08)",
color: "rgba(0, 0, 0, 0.6)",
},
label: {
// overline style
fontFamily: HEADING_TEXT,
@@ -96,12 +104,14 @@ const Theme = createMuiTheme({
fontStyle: "normal",
lineHeight: 1.2,
letterSpacing: 1.125,
color: "rgba(0, 0, 0, 0.6)",
},
labelSmall: {
paddingLeft: 12,
paddingRight: 11,
},
deleteIcon: {
color: "inherit",
},
},
MuiPaper: {
rounded: {
@@ -151,7 +161,11 @@ const Theme = createMuiTheme({
transformOrigin: { vertical: "top", horizontal: "center" },
},
},
MuiChip: { size: "small" },
MuiChip: {
size: "small",
variant: "outlined",
deleteIcon: <ClearIcon />,
},
MuiButton: { color: "primary" },
},
});

View File

@@ -2,7 +2,7 @@ import React from "react";
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon";
import { mdiGestureTap } from "@mdi/js";
export default function SubTable(props: SvgIconProps) {
export default function Action(props: SvgIconProps) {
return (
<SvgIcon viewBox="0 2 24 24" {...props}>
<path d={mdiGestureTap} />

View File

@@ -0,0 +1,11 @@
import * as React from "react";
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon";
import { mdiUpload } from "@mdi/js";
export default function Upload(props: SvgIconProps) {
return (
<SvgIcon viewBox="0 0 24 24" {...props}>
<path d={mdiUpload} />
</SvgIcon>
);
}

View File

@@ -20,7 +20,7 @@ const useStyles = makeStyles(theme =>
})
);
interface Props {
export interface IConfirmationProps {
children: JSX.Element;
message?: {
title?: string;
@@ -30,9 +30,15 @@ interface Props {
confirm?: string | JSX.Element;
};
confirmationCommand?: string;
functionName?: string;
}
const Confirmation = (props: Props) => {
const { children, message, confirmationCommand } = props;
export default function Confirmation({
children,
message,
confirmationCommand,
functionName = "onClick",
}: IConfirmationProps) {
const classes = useStyles();
const [showDialog, setShowDialog] = useState(false);
const [dryText, setDryText] = useState("");
@@ -40,20 +46,16 @@ const Confirmation = (props: Props) => {
const handleClose = () => {
setShowDialog(false);
};
//Gets the function of the wrapped button to use for execution on conformation
const confirmHandler = children.props.onChange || children.props.onClick;
const confirmHandler = children.props[functionName];
const button = React.cloneElement(children, {
onClick: () => {
setShowDialog(true);
},
onChange: () => {
setShowDialog(true);
},
[functionName]: () => setShowDialog(true),
});
return (
<>
{button}
<Dialog open={showDialog} onClose={handleClose}>
<DialogTitle>
{(message && message.title) || "Are you sure?"}
@@ -108,6 +110,4 @@ const Confirmation = (props: Props) => {
</Dialog>
</>
);
};
export default Confirmation;
}

View File

@@ -26,6 +26,7 @@ const CheckBox = (props: Props) => {
row.firstName
),
}}
functionName="onChange"
>
<Grid container justify="center">
<Switch

View File

@@ -9,7 +9,7 @@ import {
IconButton,
CircularProgress,
} from "@material-ui/core";
import UploadIcon from "@material-ui/icons/Publish";
import UploadIcon from "assets/icons/Upload";
import useUploader, { FileValue } from "hooks/useFiretable/useUploader";
@@ -34,7 +34,7 @@ const useStyles = makeStyles(theme =>
chipList: { overflow: "hidden" },
chipGridItem: { maxWidth: "100%" },
chip: { cursor: "pointer", width: "100%" },
chip: { width: "100%" },
uploadButton: { marginLeft: "auto" },
})
);
@@ -87,17 +87,16 @@ const File = (props: Props) => {
<Chip
icon={<FileIcon />}
label={file.name}
component="a"
target="_blank"
rel="noopener noreferrer"
href={file.downloadURL}
onClick={e => {
window.open(file.downloadURL);
e.stopPropagation();
}}
onDelete={
config && config.isLocked
? undefined
: () => handleDelete(file.downloadURL)
}
className={classes.chip}
onClick={e => e.stopPropagation()}
/>
</Grid>
))

View File

@@ -0,0 +1,151 @@
import React, { useCallback, useState } from "react";
import clsx from "clsx";
import { FieldProps } from "formik";
import { useDropzone } from "react-dropzone";
import useUploader, { FileValue } from "hooks/useFiretable/useUploader";
import {
makeStyles,
createStyles,
ButtonBase,
Typography,
Grid,
Chip,
CircularProgress,
} from "@material-ui/core";
import UploadIcon from "assets/icons/Upload";
import { FileIcon } from "constants/fields";
import ErrorMessage from "../ErrorMessage";
import Confirmation from "components/Confirmation";
const useStyles = makeStyles(theme =>
createStyles({
dropzoneButton: {
backgroundColor:
theme.palette.type === "light"
? "rgba(0, 0, 0, 0.09)"
: "rgba(255, 255, 255, 0.09)",
borderRadius: theme.shape.borderRadius,
padding: theme.spacing(0, 2),
justifyContent: "flex-start",
margin: 0,
width: "100%",
height: 56,
color: theme.palette.text.secondary,
"& svg": { marginRight: theme.spacing(2) },
},
chipList: { marginTop: theme.spacing(1) },
chipGridItem: { maxWidth: "100%" },
chip: { width: "100%" },
})
);
export interface IFileUploaderProps extends FieldProps {
docRef?: firebase.firestore.DocumentReference;
}
export default function FileUploader({
form,
field,
docRef,
}: IFileUploaderProps) {
const classes = useStyles();
const [uploaderState, upload] = useUploader();
const { progress } = uploaderState;
// Store a preview image locally while uploading
const [localFile, setLocalFile] = useState<string>("");
const onDrop = useCallback(
(acceptedFiles: File[]) => {
const file = acceptedFiles[0];
if (docRef && file) {
upload({
docRef,
fieldName: field.name,
files: [file],
previousValue: field.value ?? [],
onComplete: newValue => {
form.setFieldValue(field.name, newValue);
setLocalFile("");
},
});
setLocalFile(file.name);
}
},
[docRef]
);
const handleDelete = (index: number) => {
const newValue = [...field.value];
newValue.splice(index, 1);
form.setFieldValue(field.name, newValue);
};
const { getRootProps, getInputProps } = useDropzone({
onDrop,
multiple: false,
});
return (
<>
<ButtonBase className={classes.dropzoneButton} {...getRootProps()}>
<input id={`sidemodal-field-${field.name}`} {...getInputProps()} />
<UploadIcon />
<Typography variant="body1" color="textSecondary">
Upload file
</Typography>
</ButtonBase>
<Grid container spacing={1} className={classes.chipList}>
{field.value.map((file: FileValue, i) => (
<Grid item key={file.name} className={classes.chipGridItem}>
<Confirmation
message={{
title: "Delete File",
body: "Are you sure you want to delete this file?",
confirm: "Delete",
}}
functionName="onDelete"
>
<Chip
size="medium"
icon={<FileIcon />}
label={file.name}
onClick={() => window.open(file.downloadURL)}
onDelete={() => handleDelete(i)}
className={classes.chip}
/>
</Confirmation>
</Grid>
))}
{localFile && (
<Grid item className={classes.chipGridItem}>
<Chip
size="medium"
icon={<FileIcon />}
label={localFile}
className={classes.chip}
onDelete={() => {}}
deleteIcon={
<CircularProgress size={20} thickness={4.5} color="inherit" />
}
/>
</Grid>
)}
</Grid>
<ErrorMessage name={field.name} />
</>
);
}

View File

@@ -82,7 +82,6 @@ const useStyles = makeStyles(theme =>
);
export interface IImageUploaderProps extends FieldProps {
label: React.ReactNode;
docRef?: firebase.firestore.DocumentReference;
}

View File

@@ -21,6 +21,7 @@ import Color from "./Fields/Color";
import Slider from "./Fields/Slider";
// import TextMulti from "./Fields/TextMulti";
import ImageUploader from "./Fields/ImageUploader";
import FileUploader from "./Fields/FileUploader";
import { FieldType } from "constants/fields";
// import Heading from "./Heading";
@@ -200,13 +201,22 @@ export default function Form({ fields, values }: IFormProps) {
);
break;
case FieldType.file:
renderedField = (
<Field
{...fieldProps}
component={FileUploader}
docRef={values.ref}
/>
);
break;
case FieldType.rating:
renderedField = (
<Field {...fieldProps} component={Rating} />
);
break;
// case FieldType.file:
// case FieldType.connectTable:
// case FieldType.subTable:
// case FieldType.action: