@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
- onChange?: (value: string) => void;
7
- onSearch?: () => void;
8
- uploadFn?: (file: File) => Promise<string>;
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 ImageUpload = ({ name, value, onChange, onSearch }) => {
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 { value: contextValue, onChange: contextOnChange } = useFormNode(name);
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
- const url = await upload(file);
71
- if (onChange) {
72
- onChange(url);
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
- if (contextOnChange) {
75
- contextOnChange(url);
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, null,
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(Image, { "data-cy": 'uploaded-image', src: getUrl(internalValue) });
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(motion.div) `
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, { animate: error ? (isFocused ? 'errorFocus' : 'error') : undefined },
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": dataCy || 'input' }),
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dtdot/lego",
3
- "version": "0.19.5",
3
+ "version": "0.19.8",
4
4
  "description": "Some reusable components for building my applications",
5
5
  "main": "build/index.js",
6
6
  "scripts": {