mirror of
https://github.com/strapi/strapi.git
synced 2025-09-25 08:19:07 +00:00
Remove input file from helper plugin
Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
parent
3658e31dda
commit
f8f4f82693
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -1,9 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const EmptyWrapper = styled.div`
|
||||
width: 100%;
|
||||
padding-top: 6px;
|
||||
margin-bottom: 23px;
|
||||
`;
|
||||
|
||||
export default EmptyWrapper;
|
@ -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;
|
@ -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;
|
@ -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';
|
||||
|
Loading…
x
Reference in New Issue
Block a user