Remove input file from helper plugin

Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
soupette 2020-03-27 06:32:57 +01:00
parent 3658e31dda
commit f8f4f82693
15 changed files with 0 additions and 943 deletions

View File

@ -1,56 +0,0 @@
import React from 'react';
const IconUpload = () => {
return (
<svg
width="105"
height="84"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
style={{ margin: 'auto' }}
>
<defs>
<rect id="a" y="1.354" width="77.333" height="62.292" rx="4" />
<rect id="b" y="1.354" width="77.333" height="62.292" rx="4" />
</defs>
<g fill="none" fillRule="evenodd" opacity=".05">
<g transform="rotate(-12 75.8 12.214)">
<use fill="#FAFAFB" xlinkHref="#a" />
<rect
stroke="#979797"
x=".5"
y="1.854"
width="76.333"
height="61.292"
rx="4"
/>
</g>
<path
d="M74.255 36.05l3.942 18.544-57.385 12.198-1.689-7.948L29.35 42.827l7.928 5.236 16.363-25.628L74.255 36.05zM71.974 6.078l-65.21 13.86a1.272 1.272 0 0 0-.833.589 1.311 1.311 0 0 0-.19 1.014l10.7 50.334c.076.358.27.641.584.849.314.207.648.273 1.001.198l65.21-13.86c.353-.076.63-.272.833-.589.203-.317.266-.655.19-1.014l-10.7-50.334a1.311 1.311 0 0 0-.584-.849 1.272 1.272 0 0 0-1.001-.198zm6.803-.061L89.475 56.35c.387 1.822.08 3.517-.921 5.085-1.001 1.568-2.399 2.543-4.192 2.924L19.152 78.22c-1.793.381-3.466.06-5.019-.966-1.552-1.026-2.522-2.45-2.91-4.27L.525 22.65c-.387-1.822-.08-3.517.921-5.085 1.001-1.568 2.399-2.543 4.192-2.924L70.848.78c1.793-.381 3.466-.06 5.019.966 1.552 1.026 2.522 2.45 2.91 4.27z"
fill="#333740"
fillRule="nonzero"
/>
<g>
<g transform="rotate(15 7.723 110.16)">
<use fill="#FAFAFB" xlinkHref="#b" />
<rect
stroke="#979797"
x=".5"
y="1.854"
width="76.333"
height="61.292"
rx="4"
/>
</g>
<path
d="M49.626 26.969c-.584 2.18-1.832 3.832-3.744 4.955-1.911 1.123-3.94 1.398-6.086.822-2.147-.575-3.767-1.827-4.86-3.755-1.094-1.929-1.35-3.983-.765-6.163.584-2.18 1.832-3.832 3.743-4.955 1.912-1.124 3.94-1.398 6.087-.823s3.767 1.827 4.86 3.756c1.094 1.928 1.349 3.983.765 6.163zm37.007 26.74L81.726 72.02 25.058 56.836l2.103-7.848 16.384-9.63 4.687 8.266 26.214-15.406 12.187 21.49zm11.574-27.742L33.812 8.712a1.272 1.272 0 0 0-1.01.146c-.324.19-.533.463-.628.817L18.855 59.38c-.095.354-.05.695.136 1.022.186.327.453.538.802.631l64.395 17.255c.349.093.685.045 1.01-.146.324-.19.533-.463.628-.817L99.145 27.62c.095-.354.05-.695-.136-1.022a1.272 1.272 0 0 0-.802-.631zm6.09 3.033l-13.32 49.705c-.481 1.799-1.524 3.17-3.128 4.112-1.605.943-3.292 1.177-5.063.703L18.39 66.265c-1.771-.474-3.115-1.52-4.033-3.14-.918-1.618-1.136-3.327-.654-5.125L27.022 8.295c.482-1.799 1.525-3.17 3.13-4.112 1.604-.943 3.291-1.177 5.062-.703L99.61 20.735c1.771.474 3.115 1.52 4.033 3.14.918 1.618 1.136 3.327.654 5.125z"
fill="#333740"
fillRule="nonzero"
/>
</g>
</g>
</svg>
);
};
export default IconUpload;

View File

@ -1,53 +0,0 @@
import styled from 'styled-components';
const Wrapper = styled.div`
position: relative;
width: 100%;
height: 162px;
z-index: 1 !important;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
text-align: center;
vertical-align: middle;
background-color: #333740;
background-position: center;
background-repeat: no-repeat !important;
white-space: nowrap;
> img {
position: absolute;
top: 0;
left: 0;
right: 0;
margin: auto;
max-width: 100%;
max-height: 100%;
z-index: 3;
}
.fileIcon {
display: flex;
flex-direction: column;
height: 100%;
color: #fff;
justify-content: space-around;
font-size: 30px;
svg {
margin: auto;
}
}
.overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: 162px;
z-index: 999;
background: #333740;
opacity: 0.9;
}
`;
export default Wrapper;

View File

@ -1,250 +0,0 @@
/**
*
*
* ImgPreview
*
*/
/* eslint-disable no-console */
import React from 'react';
import PropTypes from 'prop-types';
import { get, has, isArray, isEmpty, startsWith, size } from 'lodash';
import cn from 'classnames';
import ImgPreviewArrow from '../ImgPreviewArrow';
import ImgPreviewHint from '../ImgPreviewHint';
import IconUpload from './IconUpload';
import Wrapper from './Wrapper';
/* eslint-disable react/no-unused-state */
class ImgPreview extends React.Component {
state = {
imgURL: '',
isDraging: false,
isOver: false,
isOverArrow: false,
isImg: false,
isInitValue: false,
};
componentDidMount() {
// We don't need the generateImgURL function here since the compo will
// always have an init value here
const file = this.props.multiple
? get(this.props.files, ['0', 'name'], '')
: get(this.props.files, 'name');
this.setState({
imgURL:
get(this.props.files, ['0', 'url'], '') ||
get(this.props.files, 'url', ''),
isImg: this.isPictureType(file),
});
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.isUploading !== this.props.isUploading) {
const lastFile = this.props.multiple
? nextProps.files.slice(-1)[0]
: nextProps.files[0] || nextProps.files;
this.generateImgURL(lastFile);
if (this.props.multiple) {
this.updateFilePosition(nextProps.files.length - 1);
}
}
// Update the preview or slide pictures or init the component
if (
nextProps.didDeleteFile !== this.props.didDeleteFile ||
nextProps.position !== this.props.position ||
(size(nextProps.files) !== size(this.props.files) &&
!this.state.isInitValue)
) {
const file = nextProps.files[nextProps.position] || nextProps.files || '';
this.generateImgURL(file);
if (!this.state.isInitValue) {
this.setState({ isInitValue: true });
}
}
if (isEmpty(nextProps.files)) {
this.setState({ isImg: false, imgURL: null });
}
}
componentDidCatch(error, info) {
console.log('An error occured in ImgPreview', info);
}
getFileType = fileName => fileName.split('.').slice(-1)[0];
/**
* [generateImgURL description]
* @param {FileList} files
* @return {URL}
*/
generateImgURL = file => {
if (this.isPictureType(file.name) && !has(file, 'url')) {
const reader = new FileReader();
reader.onloadend = () => {
this.setState({
imgURL: reader.result,
isImg: true,
});
};
reader.readAsDataURL(file);
} else if (has(file, 'url')) {
const isImg = this.isPictureType(file.name);
const imgURL =
file.url[0] === '/' ? `${strapi.backendURL}${file.url}` : file.url;
this.setState({ isImg, imgURL });
} else {
this.setState({ isImg: false, imgURL: file.name });
}
};
handleClick = type => {
const { files, position } = this.props;
let file; // eslint-disable-line no-unused-vars
let nextPosition;
switch (type) {
case 'right':
file = files[position + 1] || files[0];
nextPosition = files[position + 1] ? position + 1 : 0;
break;
case 'left':
file = files[position - 1] || files[files.length - 1];
nextPosition = files[position - 1] ? position - 1 : files.length - 1;
break;
default:
// Do nothing
}
// Update the parent position
this.updateFilePosition(nextPosition);
};
handleDragEnter = e => {
e.preventDefault();
e.stopPropagation();
this.setState({ isDraging: true });
};
handleDragLeave = e => {
e.preventDefault();
e.stopPropagation();
this.setState({ isDraging: false });
};
handleDragOver = e => {
e.preventDefault();
e.stopPropagation();
};
handleDrop = e => {
this.setState({ isDraging: false });
this.props.onDrop(e);
};
// TODO change logic to depend on the type
isPictureType = fileName => /\.(jpe?g|png|gif)$/i.test(fileName);
updateFilePosition = newPosition => {
this.props.updateFilePosition(newPosition);
};
renderContent = () => {
const fileType = this.getFileType(this.state.imgURL);
if (this.state.isImg) {
const imgURL = startsWith(this.state.imgURL, '/')
? `${strapi.backendURL}${this.state.imgURL}`
: this.state.imgURL;
return <img src={imgURL} alt="" />;
}
return (
<div className="fileIcon" onDrop={this.handleDrop}>
<i className={`far fa-file-${fileType}`} />
</div>
);
};
render() {
const { files, onBrowseClick } = this.props;
const { imgURL } = this.state;
const containerStyle = isEmpty(imgURL)
? {
display: 'flex',
zIndex: 9999,
}
: {};
return (
<Wrapper
onDragOver={this.handleDragOver}
onDragEnter={this.handleDragEnter}
style={containerStyle}
>
{isEmpty(imgURL) && <IconUpload />}
<div
className={cn(this.state.isDraging && 'overlay')}
onDragLeave={this.handleDragLeave}
onDragOver={this.handleDragOver}
onDrop={this.handleDrop}
/>
<ImgPreviewHint
displayHint={isEmpty(files)}
onClick={onBrowseClick}
onDrop={this.handleDrop}
showWhiteHint={this.state.isDraging || isEmpty(files)}
/>
{!isEmpty(imgURL) && this.renderContent()}
<ImgPreviewArrow
enable={isArray(files) && size(files) > 1}
onClick={this.handleClick}
onMouseEnter={() => this.setState({ isOverArrow: true })}
onMouseLeave={() => this.setState({ isOverArrow: false })}
show={isArray(files) && size(files) > 1}
type="right"
/>
<ImgPreviewArrow
enable={isArray(files) && size(files) > 1}
onClick={this.handleClick}
onMouseEnter={() => this.setState({ isOverArrow: true })}
onMouseLeave={() => this.setState({ isOverArrow: false })}
show={isArray(files) && size(files) > 1}
/>
</Wrapper>
);
}
}
ImgPreview.defaultProps = {
didDeleteFile: false,
files: [],
isUploading: false,
multiple: false,
onBrowseClick: () => {},
onDrop: () => {},
position: 0,
updateFilePosition: () => {},
};
ImgPreview.propTypes = {
didDeleteFile: PropTypes.bool,
files: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
isUploading: PropTypes.bool,
multiple: PropTypes.bool,
onBrowseClick: PropTypes.func,
onDrop: PropTypes.func,
position: PropTypes.number,
updateFilePosition: PropTypes.func,
};
export default ImgPreview;

View File

@ -1,44 +0,0 @@
import styled, { css } from 'styled-components';
const Wrapper = styled.div`
position: absolute;
top: 56px;
height: 32px;
width: 28px;
background: rgba(0, 0, 0, 0.2);
border-radius: 2px;
text-align: center;
color: #fff;
cursor: pointer;
z-index: 99;
${({ type }) => {
if (type === 'left') {
return css`
left: 0;
&:before {
content: '\f104';
vertical-align: middle;
text-align: center;
font-family: 'FontAwesome';
font-size: 20px;
font-weight: 800;
}
`;
}
return css`
right: 0;
&:before {
content: '\f105';
vertical-align: middle;
text-align: center;
font-family: 'FontAwesome';
font-size: 20px;
font-weight: 800;
}
`;
}}
`;
export default Wrapper;

View File

@ -1,51 +0,0 @@
/**
*
* ImgPreviewArrow
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import Wrapper from './Wrapper';
function ImgPreviewArrow(props) {
let divStyle = props.show ? {} : { display: 'none' };
if (props.enable) {
divStyle = { zIndex: 99999 };
}
return (
<Wrapper
type={props.type}
style={divStyle}
onClick={e => {
e.preventDefault();
e.stopPropagation();
props.onClick(props.type);
}}
onMouseEnter={props.onMouseEnter}
onMouseLeave={props.onMouseLeave}
/>
);
}
ImgPreviewArrow.defaultProps = {
enable: false,
onClick: () => {},
onMouseEnter: () => {},
onMouseLeave: () => {},
show: false,
type: 'left',
};
ImgPreviewArrow.propTypes = {
enable: PropTypes.bool,
onClick: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
show: PropTypes.bool,
type: PropTypes.string,
};
export default ImgPreviewArrow;

View File

@ -1,21 +0,0 @@
import styled from 'styled-components';
const P = styled.p`
position: absolute;
top: 52px;
display: block;
width: 100%;
padding: 12px 75px 0 75px;
white-space: pre-line;
color: #333740;
line-height: 18px;
font-size: 13px;
> span {
display: block;
> u {
cursor: pointer;
}
}
`;
export default P;

View File

@ -1,60 +0,0 @@
/**
*
* ImgPreviewHint
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import P from './P';
function ImgPreviewHint(props) {
let pStyle;
switch (true) {
case props.showWhiteHint:
pStyle = { zIndex: 999, color: '#fff' };
break;
case props.displayHint:
pStyle = { zIndex: 4 };
break;
default:
pStyle = { display: 'none' };
}
const browse = (
<FormattedMessage id="app.components.ImgPreview.hint.browse">
{message => <u onClick={props.onClick}>{message}</u>}
</FormattedMessage>
);
return (
<P
style={pStyle}
onDragEnter={e => e.stopPropagation()}
onDrop={props.onDrop}
>
<FormattedMessage
id="app.components.ImgPreview.hint"
values={{ browse }}
/>
</P>
);
}
ImgPreviewHint.defaultProps = {
displayHint: false,
onClick: () => {},
onDrop: () => {},
showWhiteHint: false,
};
ImgPreviewHint.propTypes = {
displayHint: PropTypes.bool,
onClick: PropTypes.func,
onDrop: PropTypes.func,
showWhiteHint: PropTypes.bool,
};
export default ImgPreviewHint;

View File

@ -1,15 +0,0 @@
import styled from 'styled-components';
const Div = styled.div`
position: absolute;
top: 5px;
right: 3px;
width: 20px;
height: 20px;
z-index: 999;
color: #fff;
font-size: 11px;
cursor: pointer;
`;
export default Div;

View File

@ -1,32 +0,0 @@
/**
*
* ImgPreviewRemoveIcon
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import Div from './Div';
function ImgPreviewRemoveIcon(props) {
const divStyle = props.show ? {} : { display: 'none' };
return (
<Div onClick={props.onClick} style={divStyle}>
<FontAwesomeIcon icon="times" />
</Div>
);
}
ImgPreviewRemoveIcon.defaultProps = {
onClick: () => {},
show: false,
};
ImgPreviewRemoveIcon.propTypes = {
onClick: PropTypes.func,
show: PropTypes.bool,
};
export default ImgPreviewRemoveIcon;

View File

@ -1,43 +0,0 @@
import styled from 'styled-components';
const Wrapper = styled.div`
.inputFile {
overflow: hidden;
position: absolute;
z-index: -1;
opacity: 0;
}
.buttonContainer {
width: 100%;
height: 34px;
line-height: 34px;
text-align: center;
background-color: #fafafb;
border-top: 0;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
color: #333740;
font-size: 12px;
font-weight: 700;
-webkit-font-smoothing: antialiased;
cursor: pointer;
text-transform: uppercase;
> i,
> svg {
margin-right: 10px;
}
}
.copy {
cursor: copy !important;
}
.inputFileControlForm {
padding: 0;
height: auto;
}
`;
export default Wrapper;

View File

@ -1,188 +0,0 @@
/**
*
*
* InputFile
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { cloneDeep, isArray, isObject } from 'lodash';
import cn from 'classnames';
import ImgPreview from '../ImgPreview';
import InputFileDetails from '../InputFileDetails';
import Wrapper from './Wrapper';
/* eslint-disable react/jsx-handler-names */
class InputFile extends React.Component {
state = {
didDeleteFile: false,
isUploading: false,
position: 0,
};
onDrop = e => {
e.preventDefault();
this.addFilesToProps(e.dataTransfer.files);
};
handleClick = e => {
e.preventDefault();
e.stopPropagation();
this.inputFile.click();
};
handleChange = ({ target }) => this.addFilesToProps(target.files);
addFilesToProps = files => {
if (files.length === 0) {
return;
}
const initAcc = this.props.multiple ? cloneDeep(this.props.value) : {};
const value = Object.keys(files).reduce((acc, current) => {
if (this.props.multiple) {
acc.push(files[current]);
} else if (current === '0') {
acc[0] = files[0];
}
return acc;
}, initAcc);
const target = {
name: this.props.name,
type: 'file',
value,
};
this.inputFile.value = '';
this.setState({ isUploading: !this.state.isUploading });
this.props.onChange({ target });
};
handleFileDelete = e => {
e.preventDefault();
e.stopPropagation();
// Remove the file from props
const value = this.props.multiple ? cloneDeep(this.props.value) : {};
// Remove the file from the array if multiple files upload is enable
if (this.props.multiple) {
value.splice(this.state.position, 1);
}
// Update the parent's props
const target = {
name: this.props.name,
type: 'file',
value: Object.keys(value).length === 0 ? '' : value,
};
this.props.onChange({ target });
// Update the position of the children
if (this.props.multiple) {
const newPosition = value.length === 0 ? 0 : value.length - 1;
this.updateFilePosition(newPosition, value.length);
}
this.setState({ didDeleteFile: !this.state.didDeleteFile });
};
updateFilePosition = (newPosition, size = this.props.value.length) => {
const label = size === 0 ? false : newPosition + 1;
this.props.setLabel(label);
this.setState({ position: newPosition });
};
isVisibleDetails = () => {
const { value } = this.props;
if (
!value ||
(isArray(value) && value.length === 0) ||
(isObject(value) && Object.keys(value).length === 0)
) {
return false;
}
return true;
};
render() {
const { multiple, name, onChange, value } = this.props;
return (
<Wrapper>
<div
className={cn(
'form-control',
'inputFileControlForm',
this.props.error && 'is-invalid'
)}
>
<ImgPreview
didDeleteFile={this.state.didDeleteFile}
files={value}
isUploading={this.state.isUploading}
multiple={multiple}
name={name}
onChange={onChange}
onBrowseClick={this.handleClick}
onDrop={this.onDrop}
position={this.state.position}
updateFilePosition={this.updateFilePosition}
/>
<label style={{ marginBottom: 0, width: '100%' }}>
<input
className="inputFile"
multiple={multiple}
name={name}
onChange={this.handleChange}
type="file"
ref={input => (this.inputFile = input)}
/>
<div className="buttonContainer">
<i className="fa fa-plus" />
<FormattedMessage id="app.components.InputFile.newFile" />
</div>
</label>
</div>
{this.isVisibleDetails() && (
<InputFileDetails
file={value[this.state.position] || value[0] || value}
multiple={multiple}
number={value.length}
onFileDelete={this.handleFileDelete}
/>
)}
</Wrapper>
);
}
}
InputFile.defaultProps = {
multiple: false,
setLabel: () => {},
value: [],
error: false,
};
InputFile.propTypes = {
error: PropTypes.bool,
multiple: PropTypes.bool,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
setLabel: PropTypes.func,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
PropTypes.array,
]),
};
export default InputFile;

View File

@ -1,9 +0,0 @@
import styled from 'styled-components';
const EmptyWrapper = styled.div`
width: 100%;
padding-top: 6px;
margin-bottom: 23px;
`;
export default EmptyWrapper;

View File

@ -1,49 +0,0 @@
import styled from 'styled-components';
const Wrapper = styled.div`
width: 100%;
padding-top: 6px;
margin-bottom: -13px;
.detailBanner {
display: flex;
justify-content: space-between;
line-height: 23px;
-webkit-font-smoothing: antialiased;
> div:first-child {
display: flex;
> div:nth-child(2) {
color: #333740;
font-size: 13px;
font-weight: 400;
}
}
}
.externalLink {
color: #333740;
text-decoration: none;
&:hover,
&:active {
color: #333740;
text-decoration: none;
}
> i,
> svg {
margin-right: 7px;
color: #b3b5b9;
}
}
.removeContainer {
color: #ff3000;
font-size: 13px;
font-weight: 400;
cursor: pointer;
}
`;
export default Wrapper;

View File

@ -1,71 +0,0 @@
/**
*
* InputFileDetails
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { get, startsWith } from 'lodash';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import EmptyWrapper from './EmptyWrapper';
import Wrapper from './Wrapper';
function InputFileDetails(props) {
if (props.number === 0 && props.multiple) {
return <EmptyWrapper />;
}
// TODO improve logic
if (!get(props.file, 'name') && !props.multiple) {
return <EmptyWrapper />;
}
const url = startsWith(props.file.url, '/')
? `${strapi.backendURL}${props.file.url}`
: props.file.url;
return (
<Wrapper>
<div className="detailBanner">
<div>
{props.file.url && (
<a
href={url}
className="externalLink"
target="_blank"
rel="noopener noreferrer"
>
<FontAwesomeIcon icon="external-link-alt" />
<FormattedMessage id="app.components.InputFileDetails.open" />
</a>
)}
</div>
<div
className="removeContainer"
onClick={props.onFileDelete}
style={{ marginBottom: '-2px', paddingTop: '4px' }}
>
<FormattedMessage id="app.components.InputFileDetails.remove" />
</div>
</div>
</Wrapper>
);
}
InputFileDetails.defaultProps = {
file: {},
multiple: false,
number: 0,
onFileDelete: () => {},
};
InputFileDetails.propTypes = {
file: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
multiple: PropTypes.bool,
number: PropTypes.number,
onFileDelete: PropTypes.func,
};
export default InputFileDetails;

View File

@ -33,7 +33,6 @@ export { default as InputDescription } from './components/InputDescription';
export { default as InputEmail } from './components/InputEmail';
export { default as InputEmailWithErrors } from './components/InputEmailWithErrors';
export { default as InputErrors } from './components/InputErrors';
export { default as InputFile } from './components/InputFile';
export { default as InputNumber } from './components/InputNumber';
export { default as InputNumberWithErrors } from './components/InputNumberWithErrors';
export { default as InputPassword } from './components/InputPassword';