adds detail and structure to File, FileDetails components

This commit is contained in:
James
2020-06-24 19:51:06 -04:00
parent a27f804b9c
commit 453d4a3a49
11 changed files with 135 additions and 45 deletions

View File

@@ -74,6 +74,7 @@
&--style-none {
padding: 0;
margin: 0;
}
&--round {

View File

@@ -7,7 +7,7 @@ import './index.scss';
const baseClass = 'copy-to-clipboard';
const CopyToClipboard = ({ value }) => {
const CopyToClipboard = ({ value, defaultMessage, successMessage }) => {
const ref = useRef(null);
const [copied, setCopied] = useState(false);
const [hovered, setHovered] = useState(false);
@@ -45,8 +45,8 @@ const CopyToClipboard = ({ value }) => {
>
<Copy />
<Tooltip>
{copied && 'Copied'}
{!copied && 'Copy'}
{copied && successMessage}
{!copied && defaultMessage}
</Tooltip>
<textarea
readOnly
@@ -62,10 +62,14 @@ const CopyToClipboard = ({ value }) => {
CopyToClipboard.defaultProps = {
value: '',
defaultMessage: 'Copy',
successMessage: 'Copied',
};
CopyToClipboard.propTypes = {
value: PropTypes.string,
defaultMessage: PropTypes.string,
successMessage: PropTypes.string,
};
export default CopyToClipboard;

View File

@@ -1,10 +1,13 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import AnimateHeight from 'react-animate-height';
import FileGraphic from '../../graphics/File';
import config from '../../../config';
import getThumbnail from '../../../../uploads/getThumbnail';
import Button from '../Button';
import formatFilesize from '../../../../uploads/formatFilesize';
import CopyToClipboard from '../CopyToClipboard';
import Chevron from '../../icons/Chevron';
import './index.scss';
@@ -21,29 +24,35 @@ const FileDetails = (props) => {
const thumbnail = getThumbnail(mimeType, staticURL, filename, sizes, adminThumbnail);
const imageURL = `${serverURL}${staticURL}/${filename}`;
return (
<div className={baseClass}>
<header>
{!thumbnail && (
<FileGraphic />
)}
{thumbnail && (
<div className={`${baseClass}__thumbnail`}>
<div className={`${baseClass}__thumbnail`}>
{thumbnail && (
<img
src={`${serverURL}${thumbnail}`}
alt={filename}
/>
</div>
)}
)}
{!thumbnail && (
<FileGraphic />
)}
</div>
<div className={`${baseClass}__main-detail`}>
<div className={`${baseClass}__url`}>
<a
href={`${serverURL}${staticURL}/${filename}`}
href={imageURL}
target="_blank"
rel="noopener noreferrer"
>
{filename}
</a>
<CopyToClipboard
value={imageURL}
defaultMessage="Copy URL"
/>
</div>
<div className={`${baseClass}__meta`}>
{formatFilesize(filesize)}
@@ -62,24 +71,54 @@ const FileDetails = (props) => {
</>
)}
</div>
{sizes && (
<Button
className={`${baseClass}__toggle-more-info${moreInfoOpen ? ' open' : ''}`}
buttonStyle="none"
onClick={() => setMoreInfoOpen(!moreInfoOpen)}
>
{!moreInfoOpen && (
<>
More info
<Chevron />
</>
)}
{moreInfoOpen && (
<>
Less info
<Chevron />
</>
)}
</Button>
)}
</div>
<Button
icon="x"
round
buttonStyle="icon-label"
iconStyle="with-border"
onClick={handleRemove}
/>
{handleRemove && (
<Button
icon="x"
round
buttonStyle="icon-label"
iconStyle="with-border"
onClick={handleRemove}
className={`${baseClass}__remove`}
/>
)}
</header>
<div className={`${baseClass}__more-info`}>
<AnimateHeight
className={`${baseClass}__more-info`}
height={moreInfoOpen ? 'auto' : 0}
>
test
</div>
</AnimateHeight>
</div>
);
};
FileDetails.defaultProps = {
adminThumbnail: undefined,
handleRemove: undefined,
width: undefined,
height: undefined,
sizes: undefined,
};
FileDetails.propTypes = {
@@ -87,7 +126,11 @@ FileDetails.propTypes = {
mimeType: PropTypes.string.isRequired,
filesize: PropTypes.number.isRequired,
staticURL: PropTypes.string.isRequired,
width: PropTypes.number,
height: PropTypes.number,
sizes: PropTypes.shape({}),
adminThumbnail: PropTypes.string,
handleRemove: PropTypes.func,
};
export default FileDetails;

View File

@@ -4,17 +4,18 @@
header {
display: flex;
align-items: flex-start;
}
.btn {
margin: $baseline;
}
&__remove {
margin: $baseline $baseline $baseline 0;
}
&__thumbnail {
width: base(6);
height: base(4.8);
min-height: base(5);
width: base(7);
align-self: stretch;
img {
img, svg {
width: 100%;
height: 100%;
object-fit: cover;
@@ -22,8 +23,36 @@
}
&__main-detail {
padding: $baseline;
padding: $baseline base(1.5);
width: auto;
flex-grow: 1;
min-width: 0;
align-self: center;
}
&__url {
display: flex;
a {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
&__url a,
&__toggle-more-info {
font-weight: 600;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
&__toggle-more-info.open {
.icon--chevron {
transform: rotate(180deg);
}
}
}

View File

@@ -11,6 +11,7 @@
color: white;
line-height: base(.75);
font-weight: normal;
white-space: nowrap;
span {
position: absolute;

View File

@@ -2,8 +2,8 @@
@import '../shared.scss';
.auth-fields {
margin: base(1.5) 0 base(2);
padding: base(2) base(2) base(1.5);
margin-bottom: base(2);
background: $color-background-gray;
.btn {

View File

@@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import useFieldType from '../../useFieldType';
import Button from '../../../elements/Button';
import FileDetails from '../../../elements/FileDetails';
import Error from '../../Error';
import './index.scss';
@@ -16,7 +17,7 @@ const handleDrag = (e) => {
};
const validate = (value) => {
if (!value) {
if (!value && value !== undefined) {
return 'A file is required.';
}
@@ -30,6 +31,7 @@ const File = (props) => {
const [selectingFile, setSelectingFile] = useState(false);
const [dragging, setDragging] = useState(false);
const [dragCounter, setDragCounter] = useState(0);
const {
initialData = {}, adminThumbnail, imageSizes, staticURL,
} = props;
@@ -39,6 +41,8 @@ const File = (props) => {
const {
value,
setValue,
showError,
errorMessage,
} = useFieldType({
path: 'file',
validate,
@@ -109,15 +113,6 @@ const File = (props) => {
return null;
}, [handleDragIn, handleDragOut, handleDrop, dropRef]);
useEffect(() => {
const input = inputRef.current;
input.addEventListener('change', handleInputChange, false);
return () => {
input.removeEventListener('change', handleInputChange);
};
}, [inputRef, handleInputChange]);
useEffect(() => {
if (inputRef.current && fileList !== undefined) {
inputRef.current.files = fileList;
@@ -127,15 +122,19 @@ const File = (props) => {
const classes = [
baseClass,
dragging && `${baseClass}--dragging`,
'field-type',
].filter(Boolean).join(' ');
return (
<div className={classes}>
<Error
showError={showError}
message={errorMessage}
/>
{filename && (
<FileDetails
{...initialData}
staticURL={staticURL}
imageSizes={imageSizes}
adminThumbnail={adminThumbnail}
/>
)}
@@ -177,6 +176,7 @@ const File = (props) => {
<input
ref={inputRef}
type="file"
onChange={handleInputChange}
/>
</div>
)}

View File

@@ -1,9 +1,15 @@
@import '../../../../scss/styles.scss';
.file-field {
margin-bottom: base(2);
position: relative;
margin: base(1.5) 0 base(2);
background: $color-background-gray;
.tooltip.error-message {
z-index: 3;
bottom: calc(100% - #{base(.5)});
}
&__upload {
position: relative;

View File

@@ -41,7 +41,7 @@ const create = async (args) => {
// /////////////////////////////////////
if (args.config.upload) {
const { staticDir } = options.req.collection.config.upload;
const { staticDir, imageSizes } = options.req.collection.config.upload;
const fileData = {};
@@ -60,7 +60,9 @@ const create = async (args) => {
fileData.width = dimensions.width;
fileData.height = dimensions.height;
fileData.sizes = await resizeAndSave(options.config, fsSafeName, fileData.mimeType);
if (Array.isArray(imageSizes)) {
fileData.sizes = await resizeAndSave(options.config, fsSafeName, fileData.mimeType);
}
}
fileData.filename = fsSafeName;

View File

@@ -82,7 +82,7 @@ const update = async (args) => {
if (args.config.upload) {
const fileData = {};
const { staticDir } = args.config.upload;
const { staticDir, imageSizes } = args.config.upload;
if (args.req.files || args.req.files.file) {
const fsSafeName = await getSafeFilename(staticDir, options.req.files.file.name);
@@ -90,13 +90,17 @@ const update = async (args) => {
await options.req.files.file.mv(`${staticDir}/${fsSafeName}`);
fileData.filename = fsSafeName;
fileData.filesize = options.req.files.file.size;
fileData.mimeType = options.req.files.file.mimetype;
if (imageMIMETypes.indexOf(options.req.files.file.mimetype) > -1) {
const dimensions = await getImageSize(`${staticDir}/${fsSafeName}`);
fileData.width = dimensions.width;
fileData.height = dimensions.height;
fileData.sizes = await resizeAndSave(options.config, fsSafeName, fileData.mimeType);
if (Array.isArray(imageSizes)) {
fileData.sizes = await resizeAndSave(options.config, fsSafeName, fileData.mimeType);
}
}
options.data = {