@dtdot/lego 0.19.5 → 0.19.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
export declare type ImageUploadMode = 'fill' | 'form';
|
|
3
3
|
interface ImageUploadProps {
|
|
4
|
-
name: string;
|
|
5
|
-
value?: string;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
'name': string;
|
|
5
|
+
'value'?: string;
|
|
6
|
+
'error'?: string;
|
|
7
|
+
'uploading'?: boolean;
|
|
8
|
+
'onChange'?: (value: string) => void;
|
|
9
|
+
'onSearch'?: () => void;
|
|
10
|
+
'uploadFn'?: (file: File) => Promise<string>;
|
|
11
|
+
'data-cy'?: string;
|
|
9
12
|
}
|
|
10
|
-
declare const ImageUpload: ({ name, value, onChange, onSearch }: ImageUploadProps) => JSX.Element;
|
|
13
|
+
declare const ImageUpload: ({ name, value, "error": propsError, "uploading": uploadingProp, onChange, onSearch, "data-cy": dataCy, }: ImageUploadProps) => JSX.Element;
|
|
11
14
|
export default ImageUpload;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import React, { useContext, useRef } from 'react';
|
|
2
|
-
import { faCloudUploadAlt, faSearch } from '@fortawesome/free-solid-svg-icons';
|
|
1
|
+
import React, { useContext, useRef, useState } from 'react';
|
|
2
|
+
import { faCloudUploadAlt, faExclamationCircle, faSearch } from '@fortawesome/free-solid-svg-icons';
|
|
3
3
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
5
|
import { motion } from 'framer-motion';
|
|
6
6
|
import FileContext from '../../contexts/File.context';
|
|
7
7
|
import useFormNode from '../Form/useFormNode.hook';
|
|
8
|
+
import Loader from '../Loader/Loader.component';
|
|
8
9
|
const UploadContainer = styled.div `
|
|
9
10
|
position: relative;
|
|
10
11
|
min-height: 144px;
|
|
@@ -24,6 +25,14 @@ const UploadInnerContainer = styled.div `
|
|
|
24
25
|
display: flex;
|
|
25
26
|
justify-content: space-evenly;
|
|
26
27
|
`;
|
|
28
|
+
const LoaderContainer = styled.div `
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100%;
|
|
31
|
+
min-height: 142px;
|
|
32
|
+
display: flex;
|
|
33
|
+
justify-content: center;
|
|
34
|
+
align-items: center;
|
|
35
|
+
`;
|
|
27
36
|
const IconContainer = styled(motion.div) `
|
|
28
37
|
flex-grow: 1;
|
|
29
38
|
margin: 8px;
|
|
@@ -55,10 +64,42 @@ const Image = styled.img `
|
|
|
55
64
|
height: 100%;
|
|
56
65
|
object-fit: cover;
|
|
57
66
|
`;
|
|
58
|
-
const
|
|
67
|
+
const ErrorMessage = styled.div `
|
|
68
|
+
padding-left: 6px;
|
|
69
|
+
|
|
70
|
+
font-family: ${(props) => props.theme.fonts.default.family};
|
|
71
|
+
font-size: ${(props) => props.theme.fonts.default.size};
|
|
72
|
+
color: ${(props) => props.theme.colours.statusDanger.main};
|
|
73
|
+
`;
|
|
74
|
+
const ErrorContainer = styled(motion.div) `
|
|
75
|
+
position: absolute;
|
|
76
|
+
left: 10px;
|
|
77
|
+
top: 10px;
|
|
78
|
+
width: 100%;
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
|
|
82
|
+
font-size: ${(props) => props.theme.fonts.default.size};
|
|
83
|
+
color: ${(props) => props.theme.colours.statusDanger.main};
|
|
84
|
+
`;
|
|
85
|
+
const ErrorInner = styled.div `
|
|
86
|
+
width: 24px;
|
|
87
|
+
height: 24px;
|
|
88
|
+
|
|
89
|
+
display: flex;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
align-items: center;
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
`;
|
|
94
|
+
const errorVariants = {
|
|
95
|
+
show: { opacity: 1 },
|
|
96
|
+
};
|
|
97
|
+
const ImageUpload = ({ name, value, 'error': propsError, 'uploading': uploadingProp, onChange, onSearch, 'data-cy': dataCy, }) => {
|
|
59
98
|
const { upload, getUrl } = useContext(FileContext);
|
|
60
|
-
const
|
|
99
|
+
const [uploading, setUploading] = useState(false);
|
|
100
|
+
const { value: contextValue, error: contextError, onChange: contextOnChange } = useFormNode(name);
|
|
61
101
|
const inputRef = useRef();
|
|
102
|
+
const error = contextError || propsError;
|
|
62
103
|
const handleUploadClicked = () => {
|
|
63
104
|
if (inputRef && inputRef.current) {
|
|
64
105
|
inputRef.current.click();
|
|
@@ -67,18 +108,29 @@ const ImageUpload = ({ name, value, onChange, onSearch }) => {
|
|
|
67
108
|
const handleUpload = async (event) => {
|
|
68
109
|
if (event.target.files && upload) {
|
|
69
110
|
const file = event.target.files[0];
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
111
|
+
setUploading(true);
|
|
112
|
+
try {
|
|
113
|
+
const url = await upload(file);
|
|
114
|
+
if (onChange) {
|
|
115
|
+
onChange(url);
|
|
116
|
+
}
|
|
117
|
+
if (contextOnChange) {
|
|
118
|
+
contextOnChange(url);
|
|
119
|
+
}
|
|
73
120
|
}
|
|
74
|
-
|
|
75
|
-
|
|
121
|
+
finally {
|
|
122
|
+
setUploading(false);
|
|
76
123
|
}
|
|
77
124
|
}
|
|
78
125
|
};
|
|
79
126
|
const internalValue = value ? value : contextValue;
|
|
127
|
+
if (uploading || uploadingProp) {
|
|
128
|
+
return (React.createElement(UploadContainer, { "data-cy": dataCy },
|
|
129
|
+
React.createElement(LoaderContainer, null,
|
|
130
|
+
React.createElement(Loader, null))));
|
|
131
|
+
}
|
|
80
132
|
if (!internalValue) {
|
|
81
|
-
return (React.createElement(UploadContainer,
|
|
133
|
+
return (React.createElement(UploadContainer, { "data-cy": dataCy },
|
|
82
134
|
React.createElement(UploadInnerContainer, null,
|
|
83
135
|
React.createElement(IconContainer, { whileHover: { scale: 1.05 }, onClick: handleUploadClicked, "data-cy": 'button-image-upload' },
|
|
84
136
|
React.createElement(FontAwesomeIcon, { icon: faCloudUploadAlt })),
|
|
@@ -86,8 +138,13 @@ const ImageUpload = ({ name, value, onChange, onSearch }) => {
|
|
|
86
138
|
React.createElement(UploadVerticalDivider, null),
|
|
87
139
|
React.createElement(IconContainer, { whileHover: { scale: 1.05 }, onClick: onSearch, "data-cy": 'button-image-search' },
|
|
88
140
|
React.createElement(FontAwesomeIcon, { icon: faSearch }))))),
|
|
89
|
-
React.createElement(HiddenInput, { value: '', ref: inputRef, type: 'file', onChange: handleUpload, "data-cy": 'input-image-hidden' })
|
|
141
|
+
React.createElement(HiddenInput, { value: '', ref: inputRef, type: 'file', onChange: handleUpload, "data-cy": 'input-image-hidden' }),
|
|
142
|
+
error && (React.createElement(ErrorContainer, { animate: error ? 'show' : undefined, style: { opacity: 0 }, variants: errorVariants, transition: { type: 'spring', duration: 0.3 }, "data-cy": 'error-indicator' },
|
|
143
|
+
React.createElement(ErrorInner, null,
|
|
144
|
+
React.createElement(FontAwesomeIcon, { icon: faExclamationCircle })),
|
|
145
|
+
React.createElement(ErrorMessage, { "data-cy": 'error-message' }, error)))));
|
|
90
146
|
}
|
|
91
|
-
return React.createElement(
|
|
147
|
+
return (React.createElement("div", { "data-cy": dataCy },
|
|
148
|
+
React.createElement(Image, { "data-cy": 'uploaded-image', src: getUrl(internalValue) })));
|
|
92
149
|
};
|
|
93
150
|
export default ImageUpload;
|
|
@@ -7,7 +7,7 @@ import styled, { css } from 'styled-components';
|
|
|
7
7
|
import getThemeControlColours from '../../theme/helpers/getThemeControlColours';
|
|
8
8
|
import useFormNode, { getValue } from '../Form/useFormNode.hook';
|
|
9
9
|
export const INPUT_HEIGHT = 48;
|
|
10
|
-
const InputContainer = styled
|
|
10
|
+
const InputContainer = styled.div `
|
|
11
11
|
position: relative;
|
|
12
12
|
margin: 2px 0;
|
|
13
13
|
|
|
@@ -117,13 +117,14 @@ const Input = React.forwardRef(function ForwardRefInput(props, ref) {
|
|
|
117
117
|
onBlur();
|
|
118
118
|
}
|
|
119
119
|
};
|
|
120
|
+
const animationVariant = error ? (isFocused ? 'errorFocus' : 'error') : undefined;
|
|
120
121
|
return (React.createElement("div", null,
|
|
121
122
|
label && React.createElement(InputLabel, { htmlFor: name }, label),
|
|
122
|
-
React.createElement(InputContainer, {
|
|
123
|
-
React.createElement(StyledInput, { ref: ref, variants: inputVariants, transition: { type: 'spring', duration: 0.3 }, type: type, name: name, placeholder: placeholder, value: getValue(value, contextValue), onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, autoFocus: autoFocus, "data-cy":
|
|
124
|
-
React.createElement(ErrorContainer, { animate: error ? 'show' : undefined, style: { opacity: 0 }, variants: errorVariants, transition: { type: 'spring', duration: 0.3 } },
|
|
123
|
+
React.createElement(InputContainer, { "data-cy": dataCy },
|
|
124
|
+
React.createElement(StyledInput, { ref: ref, animate: animationVariant, variants: inputVariants, transition: { type: 'spring', duration: 0.3 }, type: type, name: name, placeholder: placeholder, value: getValue(value, contextValue), onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, autoFocus: autoFocus, "data-cy": 'input' }),
|
|
125
|
+
React.createElement(ErrorContainer, { animate: error ? 'show' : undefined, style: { opacity: 0 }, variants: errorVariants, transition: { type: 'spring', duration: 0.3 }, "data-cy": 'error-indicator' },
|
|
125
126
|
React.createElement(ErrorInner, null,
|
|
126
127
|
React.createElement(FontAwesomeIcon, { icon: faExclamationCircle }))),
|
|
127
|
-
error && (React.createElement(ErrorMessage, { style: { opacity: 0, y: 0 }, variants: messageVariants, transition: { type: 'spring', duration: 0.3 } }, error)))));
|
|
128
|
+
error && (React.createElement(ErrorMessage, { style: { opacity: 0, y: 0 }, animate: animationVariant, variants: messageVariants, transition: { type: 'spring', duration: 0.3 }, "data-cy": 'error-message' }, error)))));
|
|
128
129
|
});
|
|
129
130
|
export default Input;
|