adds width and height to uploads that match proper mime type, continues build to FileDetails
This commit is contained in:
@@ -1,30 +1,93 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import FileGraphic from '../../graphics/File';
|
||||
import config from '../../../config';
|
||||
import getThumbnail from '../../../../uploads/getThumbnail';
|
||||
import Button from '../Button';
|
||||
import formatFilesize from '../../../../uploads/formatFilesize';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const { serverURL } = config;
|
||||
|
||||
const baseClass = 'file-details';
|
||||
|
||||
const FileDetails = (props) => {
|
||||
const { filename, mimeType, filesize } = props;
|
||||
const {
|
||||
filename, mimeType, filesize, staticURL, adminThumbnail, sizes, handleRemove, width, height,
|
||||
} = props;
|
||||
|
||||
const [moreInfoOpen, setMoreInfoOpen] = useState(false);
|
||||
|
||||
const thumbnail = getThumbnail(mimeType, staticURL, filename, sizes, adminThumbnail);
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<FileGraphic />
|
||||
<header>
|
||||
{!thumbnail && (
|
||||
<FileGraphic />
|
||||
)}
|
||||
{thumbnail && (
|
||||
<div className={`${baseClass}__thumbnail`}>
|
||||
<img
|
||||
src={`${serverURL}${thumbnail}`}
|
||||
alt={filename}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={`${baseClass}__main-detail`}>
|
||||
<div className={`${baseClass}__url`}>
|
||||
<a
|
||||
href={`${serverURL}${staticURL}/${filename}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{filename}
|
||||
</a>
|
||||
</div>
|
||||
<div className={`${baseClass}__meta`}>
|
||||
{formatFilesize(filesize)}
|
||||
{(width && height) && (
|
||||
<>
|
||||
-
|
||||
{width}
|
||||
x
|
||||
{height}
|
||||
</>
|
||||
)}
|
||||
{mimeType && (
|
||||
<>
|
||||
-
|
||||
{mimeType}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
icon="x"
|
||||
round
|
||||
buttonStyle="icon-label"
|
||||
iconStyle="with-border"
|
||||
onClick={handleRemove}
|
||||
/>
|
||||
</header>
|
||||
<div className={`${baseClass}__more-info`}>
|
||||
test
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
FileDetails.defaultProps = {
|
||||
sizes: null,
|
||||
adminThumbnail: undefined,
|
||||
};
|
||||
|
||||
FileDetails.propTypes = {
|
||||
filename: PropTypes.string.isRequired,
|
||||
mimeType: PropTypes.string.isRequired,
|
||||
filesize: PropTypes.number.isRequired,
|
||||
sizes: PropTypes.shape({}),
|
||||
staticURL: PropTypes.string.isRequired,
|
||||
adminThumbnail: PropTypes.string,
|
||||
};
|
||||
|
||||
export default FileDetails;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
@import '../../../scss/styles.scss';
|
||||
|
||||
.file-details {
|
||||
header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
.btn {
|
||||
margin: $baseline;
|
||||
}
|
||||
}
|
||||
|
||||
&__thumbnail {
|
||||
width: base(6);
|
||||
height: base(4.8);
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
&__main-detail {
|
||||
padding: $baseline;
|
||||
width: auto;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import './index.scss';
|
||||
|
||||
const baseClass = 'form';
|
||||
|
||||
const reduceFieldsToValues = (fields) => {
|
||||
const reduceFieldsToValues = (fields, flatten) => {
|
||||
const data = {};
|
||||
|
||||
Object.keys(fields).forEach((key) => {
|
||||
@@ -26,8 +26,11 @@ const reduceFieldsToValues = (fields) => {
|
||||
}
|
||||
});
|
||||
|
||||
const unflattened = unflatten(data, { safe: true });
|
||||
return unflattened;
|
||||
if (flatten) {
|
||||
return unflatten(data, { safe: true });
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const Form = (props) => {
|
||||
@@ -62,7 +65,7 @@ const Form = (props) => {
|
||||
}, [fields]);
|
||||
|
||||
const getData = useCallback(() => {
|
||||
return reduceFieldsToValues(fields);
|
||||
return reduceFieldsToValues(fields, true);
|
||||
}, [fields]);
|
||||
|
||||
const getSiblingData = useCallback((path) => {
|
||||
@@ -84,7 +87,7 @@ const Form = (props) => {
|
||||
}, {});
|
||||
}
|
||||
|
||||
return reduceFieldsToValues(siblingFields);
|
||||
return reduceFieldsToValues(siblingFields, true);
|
||||
}, [fields]);
|
||||
|
||||
const getDataByPath = useCallback((path) => {
|
||||
@@ -102,7 +105,7 @@ const Form = (props) => {
|
||||
return matchedData;
|
||||
}, {});
|
||||
|
||||
const values = reduceFieldsToValues(data);
|
||||
const values = reduceFieldsToValues(data, true);
|
||||
const unflattenedData = unflatten(values);
|
||||
return unflattenedData?.[name];
|
||||
}, [fields]);
|
||||
|
||||
@@ -30,7 +30,10 @@ const File = (props) => {
|
||||
const [selectingFile, setSelectingFile] = useState(false);
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const [dragCounter, setDragCounter] = useState(0);
|
||||
const { initialData = {} } = props;
|
||||
const {
|
||||
initialData = {}, adminThumbnail, imageSizes, staticURL,
|
||||
} = props;
|
||||
|
||||
const { filename } = initialData;
|
||||
|
||||
const {
|
||||
@@ -129,7 +132,12 @@ const File = (props) => {
|
||||
return (
|
||||
<div className={classes}>
|
||||
{filename && (
|
||||
<FileDetails {...initialData} />
|
||||
<FileDetails
|
||||
{...initialData}
|
||||
staticURL={staticURL}
|
||||
imageSizes={imageSizes}
|
||||
adminThumbnail={adminThumbnail}
|
||||
/>
|
||||
)}
|
||||
{!filename && (
|
||||
<div className={`${baseClass}__upload`}>
|
||||
@@ -178,6 +186,7 @@ const File = (props) => {
|
||||
|
||||
File.defaultProps = {
|
||||
initialData: undefined,
|
||||
adminThumbnail: undefined,
|
||||
};
|
||||
|
||||
File.propTypes = {
|
||||
@@ -187,6 +196,8 @@ File.propTypes = {
|
||||
mimeType: PropTypes.string,
|
||||
filesize: PropTypes.number,
|
||||
}),
|
||||
staticURL: PropTypes.string.isRequired,
|
||||
adminThumbnail: PropTypes.string,
|
||||
};
|
||||
|
||||
export default File;
|
||||
|
||||
@@ -17,6 +17,7 @@ const formatFields = (config, isEditing) => {
|
||||
const uploadFields = [
|
||||
{
|
||||
type: 'file',
|
||||
...config.upload,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ const executePolicy = require('../../auth/executePolicy');
|
||||
const { MissingFile } = require('../../errors');
|
||||
const resizeAndSave = require('../../uploads/imageResizer');
|
||||
const getSafeFilename = require('../../uploads/getSafeFilename');
|
||||
const getImageSize = require('../../uploads/getImageSize');
|
||||
const imageMIMETypes = require('../../uploads/imageMIMETypes');
|
||||
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
|
||||
@@ -39,7 +41,7 @@ const create = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.config.upload) {
|
||||
const { staticDir, imageSizes } = options.req.collection.config.upload;
|
||||
const { staticDir } = options.req.collection.config.upload;
|
||||
|
||||
const fileData = {};
|
||||
|
||||
@@ -53,14 +55,18 @@ const create = async (args) => {
|
||||
|
||||
await options.req.files.file.mv(`${staticDir}/${fsSafeName}`);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
fileData.filename = fsSafeName;
|
||||
fileData.filesize = options.req.files.file.size;
|
||||
fileData.mimeType = options.req.files.file.mimetype;
|
||||
|
||||
if (imageSizes) {
|
||||
fileData.sizes = await resizeAndSave(options.config, fsSafeName, fileData.mimeType);
|
||||
}
|
||||
|
||||
options.data = {
|
||||
...options.data,
|
||||
...fileData,
|
||||
|
||||
@@ -3,6 +3,9 @@ const overwriteMerge = require('../../utilities/overwriteMerge');
|
||||
const executePolicy = require('../../auth/executePolicy');
|
||||
const { NotFound, Forbidden } = require('../../errors');
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
const imageMIMETypes = require('../../uploads/imageMIMETypes');
|
||||
const getImageSize = require('../../uploads/getImageSize');
|
||||
const getSafeFilename = require('../../uploads/getSafeFilename');
|
||||
|
||||
const resizeAndSave = require('../../uploads/imageResizer');
|
||||
|
||||
@@ -79,15 +82,21 @@ const update = async (args) => {
|
||||
if (args.config.upload) {
|
||||
const fileData = {};
|
||||
|
||||
const { staticDir, imageSizes } = args.config.upload;
|
||||
const { staticDir } = args.config.upload;
|
||||
|
||||
if (args.req.files || args.req.files.file) {
|
||||
await options.req.files.file.mv(`${staticDir}/${options.req.files.file.name}`);
|
||||
const fsSafeName = await getSafeFilename(staticDir, options.req.files.file.name);
|
||||
|
||||
fileData.filename = options.req.files.file.name;
|
||||
await options.req.files.file.mv(`${staticDir}/${fsSafeName}`);
|
||||
|
||||
if (imageSizes) {
|
||||
fileData.sizes = await resizeAndSave(options.config, options.req.files.file.name);
|
||||
fileData.filename = fsSafeName;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
options.data = {
|
||||
|
||||
@@ -3,7 +3,7 @@ const express = require('express');
|
||||
function initStatic() {
|
||||
this.config.collections.forEach((collection) => {
|
||||
if (collection.upload) {
|
||||
this.express.use(`${collection.upload.staticURL}*`, express.static(collection.upload.staticDir));
|
||||
this.express.use(`${collection.upload.staticURL}`, express.static(collection.upload.staticDir));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
13
src/uploads/formatFilesize.js
Normal file
13
src/uploads/formatFilesize.js
Normal file
@@ -0,0 +1,13 @@
|
||||
function formatBytes(bytes, decimals = 0) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / (k ** i)).toFixed(dm))} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
module.exports = formatBytes;
|
||||
6
src/uploads/getImageSize.js
Normal file
6
src/uploads/getImageSize.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const imageSize = require('image-size');
|
||||
const { promisify } = require('util');
|
||||
|
||||
const getImageSize = promisify(imageSize);
|
||||
|
||||
module.exports = getImageSize;
|
||||
15
src/uploads/getThumbnail.js
Normal file
15
src/uploads/getThumbnail.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const imageMIMETypes = require('./imageMIMETypes');
|
||||
|
||||
const getThumbnail = (mimeType, staticURL, filename, sizes, adminThumbnail) => {
|
||||
if (imageMIMETypes.indexOf(mimeType) > -1) {
|
||||
if (sizes?.[adminThumbnail]?.filename) {
|
||||
return `${staticURL}/${sizes[adminThumbnail].filename}`;
|
||||
}
|
||||
|
||||
return `${staticURL}/${filename}`;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
module.exports = getThumbnail;
|
||||
3
src/uploads/imageMIMETypes.js
Normal file
3
src/uploads/imageMIMETypes.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const types = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'];
|
||||
|
||||
module.exports = types;
|
||||
@@ -1,12 +1,9 @@
|
||||
const fs = require('fs');
|
||||
const sharp = require('sharp');
|
||||
const sanitize = require('sanitize-filename');
|
||||
const { promisify } = require('util');
|
||||
const imageSize = require('image-size');
|
||||
const getImageSize = require('./getImageSize');
|
||||
const fileExists = require('./fileExists');
|
||||
|
||||
const sizeOf = promisify(imageSize);
|
||||
|
||||
function getOutputImage(sourceImage, size) {
|
||||
const extension = sourceImage.split('.').pop();
|
||||
const name = sanitize(sourceImage.substr(0, sourceImage.lastIndexOf('.')) || sourceImage);
|
||||
@@ -33,7 +30,7 @@ module.exports = async function resizeAndSave(config, savedFilename, mimeType) {
|
||||
const sourceImage = `${staticDir}/${savedFilename}`;
|
||||
let sizes;
|
||||
try {
|
||||
const dimensions = await sizeOf(sourceImage);
|
||||
const dimensions = await getImageSize(sourceImage);
|
||||
sizes = imageSizes
|
||||
.filter(desiredSize => desiredSize.width < dimensions.width)
|
||||
.map(async (desiredSize) => {
|
||||
|
||||
Reference in New Issue
Block a user