adds detail and structure to File, FileDetails components
This commit is contained in:
@@ -74,6 +74,7 @@
|
||||
|
||||
&--style-none {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&--round {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
color: white;
|
||||
line-height: base(.75);
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user