@widergy/mobile-ui 1.6.0 → 1.7.0

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.
Files changed (26) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/lib/components/MultipleFilePicker/components/Input/README.md +77 -0
  3. package/lib/components/MultipleFilePicker/components/Input/components/ShowPassword/constants.js +2 -0
  4. package/lib/components/MultipleFilePicker/components/Input/components/ShowPassword/index.js +27 -0
  5. package/lib/components/MultipleFilePicker/components/Input/components/ShowPassword/propTypes.js +8 -0
  6. package/lib/components/MultipleFilePicker/components/Input/components/ShowPassword/styles.js +15 -0
  7. package/lib/components/MultipleFilePicker/components/Input/components/Title/index.js +78 -0
  8. package/lib/components/MultipleFilePicker/components/Input/components/Title/propTypes.js +14 -0
  9. package/lib/components/MultipleFilePicker/components/Input/components/Title/styles.js +42 -0
  10. package/lib/components/MultipleFilePicker/components/Input/components/Underline/index.js +80 -0
  11. package/lib/components/MultipleFilePicker/components/Input/components/Underline/styles.js +39 -0
  12. package/lib/components/MultipleFilePicker/components/Input/constants.js +2 -0
  13. package/lib/components/MultipleFilePicker/components/Input/index.js +299 -0
  14. package/lib/components/MultipleFilePicker/components/Input/propTypes.js +43 -0
  15. package/lib/components/MultipleFilePicker/components/Input/styles.js +47 -0
  16. package/lib/components/MultipleFilePicker/components/Picker/index.js +95 -0
  17. package/lib/components/MultipleFilePicker/components/Picker/styles.js +47 -0
  18. package/lib/components/MultipleFilePicker/components/UploadedFiles/index.js +140 -0
  19. package/lib/components/MultipleFilePicker/components/UploadedFiles/styles.js +65 -0
  20. package/lib/components/MultipleFilePicker/components/UploadedFiles/utils.js +6 -0
  21. package/lib/components/MultipleFilePicker/constants.js +18 -0
  22. package/lib/components/MultipleFilePicker/index.js +162 -0
  23. package/lib/components/MultipleFilePicker/propTypes.js +17 -0
  24. package/lib/components/MultipleFilePicker/utils.js +41 -0
  25. package/lib/index.js +1 -0
  26. package/package.json +1 -1
@@ -0,0 +1,65 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ import { portraitHorizontalScale, portraitVerticalScale } from '../../../../utils/portraitScalingUtils';
4
+
5
+ export default StyleSheet.create({
6
+ container: {
7
+ flexDirection: 'row'
8
+ },
9
+ containerInput: {
10
+ height: '100%',
11
+ width: '100%'
12
+ },
13
+ containerStyle: {
14
+ flex: 1,
15
+ shadowColor: '#E4E6EA',
16
+ elevation: 4
17
+ },
18
+ containerButtonPadding: {
19
+ padding: portraitHorizontalScale(65)
20
+ },
21
+ containerPadding: {
22
+ padding: portraitHorizontalScale(18)
23
+ },
24
+ fieldInput: {
25
+ borderRadius: 5,
26
+ borderStyle: 'dashed',
27
+ borderWidth: 1,
28
+ flex: 1,
29
+ height: 'auto',
30
+ paddingBottom: portraitVerticalScale(24),
31
+ paddingTop: portraitVerticalScale(16),
32
+ width: '100%',
33
+ marginVertical: 10
34
+ },
35
+ helpText: {
36
+ textAlign: 'center',
37
+ marginBottom: 10
38
+ },
39
+ pickerText: {
40
+ textAlign: 'center'
41
+ },
42
+ textStyles: {
43
+ marginLeft: 5,
44
+ marginTop: 16
45
+ },
46
+ textButtonStyles: {
47
+ flex: 1,
48
+ padding: 0,
49
+ textAlignVertical: 'center',
50
+ margin: 15
51
+ },
52
+ touchable: {
53
+ borderRadius: 4,
54
+ paddingHorizontal: portraitHorizontalScale(12),
55
+ height: 80,
56
+ width: '100%'
57
+ },
58
+ uploadTouchable: {
59
+ borderRadius: 4,
60
+ marginHorizontal: 16,
61
+ marginVertical: 10,
62
+ height: 38,
63
+ width: '90%'
64
+ }
65
+ });
@@ -0,0 +1,6 @@
1
+ export const bytesFormater = bytes => {
2
+ const lista = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
3
+ if (bytes === 0) return '0 Byte';
4
+ const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
5
+ return `${Math.round(bytes / 1024 ** i, 2)} ${lista[i]}`;
6
+ };
@@ -0,0 +1,18 @@
1
+ // eslint-disable-next-line import/no-unresolved
2
+ import DocumentPicker from 'react-native-document-picker';
3
+
4
+ import { MEGABYTE } from '../../utils/fileUtils.js';
5
+
6
+ export const FILE_UPLOAD_ICON = 'file-upload';
7
+
8
+ export const UPLOAD_ICON = 'upload';
9
+
10
+ export const CHECK_ICON = 'check';
11
+
12
+ export const TRASH_ICON = 'trash-2';
13
+
14
+ export const TYPE_ICON = 'feather';
15
+
16
+ export const DEFAULT_ALLOWED_TYPES = [DocumentPicker.types.allFiles];
17
+
18
+ export const DEFAULT_MAX_SIZE = 10 * MEGABYTE;
@@ -0,0 +1,162 @@
1
+ import React, { Component } from 'react';
2
+ import { View } from 'react-native';
3
+ // eslint-disable-next-line import/no-unresolved
4
+ import DocumentPicker from 'react-native-document-picker';
5
+ import { isEmpty } from '@widergy/web-utils/lib/array';
6
+
7
+ import { retrieveFile, isImageByUri, blobToFile } from '../../utils/fileUtils.js';
8
+
9
+ import Picker from './components/Picker';
10
+ import UploadedFiles from './components/UploadedFiles';
11
+ import { UPLOAD_ICON, DEFAULT_MAX_SIZE } from './constants';
12
+ import filePickerPropTypes from './propTypes';
13
+ import { onlyPDFAllowed, pdfAspectRatioValidation } from './utils';
14
+
15
+ class MultipleFilePicker extends Component {
16
+ constructor(props) {
17
+ super(props);
18
+ this.state = {
19
+ uploadedFiles: []
20
+ };
21
+ }
22
+
23
+ handleShowPicker = async () => {
24
+ const {
25
+ allowedTypes,
26
+ onMaxSizeError,
27
+ onError,
28
+ onChange,
29
+ maxFileByteSize,
30
+ maxFiles,
31
+ minFiles,
32
+ avoidRetrieveFile,
33
+ fileTypeError,
34
+ allowedPDFUploadSizes,
35
+ pdfFormatError
36
+ } = this.props;
37
+ try {
38
+ const documents = await DocumentPicker.pick({
39
+ allowMultiSelection: true,
40
+ type: allowedTypes && !onlyPDFAllowed(allowedTypes) ? allowedTypes : DocumentPicker.types.allFiles
41
+ });
42
+ const { uploadedFiles } = this.state;
43
+
44
+ if (uploadedFiles.length + documents.length > maxFiles) {
45
+ throw new Error(fileTypeError || 'La cantidad de archivos supera al máximo permitido');
46
+ }
47
+ if (uploadedFiles.length + documents.length < minFiles) {
48
+ throw new Error(fileTypeError || 'La cantidad de archivos es menor al mínimo permitido');
49
+ }
50
+ documents.map(async document => {
51
+ const isPDF = document.type.includes('pdf');
52
+ const isImage = document.type.includes('image');
53
+ if (onlyPDFAllowed(allowedTypes) && !isPDF) {
54
+ throw new Error(fileTypeError || 'El tipo de archivo debe ser PDF.');
55
+ }
56
+ if (allowedTypes && !isImage && !isImageByUri(document.uri) && !isPDF) {
57
+ throw new Error(fileTypeError || 'Tipo de archivo inválido.');
58
+ }
59
+ if (maxFileByteSize && document.size > maxFileByteSize) {
60
+ onMaxSizeError(document.size, maxFileByteSize);
61
+ return;
62
+ }
63
+
64
+ const file = !avoidRetrieveFile && (await retrieveFile(document.uri, document.type));
65
+
66
+ if (file && isPDF && !isEmpty(allowedPDFUploadSizes)) {
67
+ const isWrongFormat = await pdfAspectRatioValidation(file, allowedPDFUploadSizes);
68
+ if (isWrongFormat) {
69
+ throw new Error(
70
+ pdfFormatError ||
71
+ `El formato de archivo es inválido. (Válidos: ${allowedPDFUploadSizes
72
+ ?.map(aspectRatio => aspectRatio.name)
73
+ .join(' - ')})`
74
+ );
75
+ }
76
+ }
77
+
78
+ if (onChange) {
79
+ onChange(avoidRetrieveFile ? { document } : { file: blobToFile(file, document.type) });
80
+ }
81
+ this.setState(prevState => ({
82
+ uploadedFiles: [...prevState.uploadedFiles, { name: document.name, size: document.size }]
83
+ }));
84
+ });
85
+ } catch (err) {
86
+ if (!DocumentPicker.isCancel(err)) {
87
+ onError(err.message);
88
+ }
89
+ }
90
+ };
91
+
92
+ handleDeleteFile = index => {
93
+ const { onChange } = this.props;
94
+ if (onChange) {
95
+ onChange(null);
96
+ }
97
+ this.setState(prevState => ({
98
+ uploadedFiles: prevState.uploadedFiles.filter((a, i) => i !== index)
99
+ }));
100
+ };
101
+
102
+ render() {
103
+ const {
104
+ error,
105
+ title,
106
+ filePlaceholder,
107
+ style,
108
+ withMarkdownTitle,
109
+ markdownStyles,
110
+ disabled,
111
+ helpText,
112
+ pickerText,
113
+ UploadIcon
114
+ } = this.props;
115
+ const { uploadedFiles } = this.state;
116
+
117
+ return uploadedFiles.length !== 0 ? (
118
+ <View style={style}>
119
+ <UploadedFiles
120
+ icon={UPLOAD_ICON}
121
+ error={error}
122
+ onAdd={this.handleShowPicker}
123
+ onDelete={this.handleDeleteFile}
124
+ uploadedFiles={uploadedFiles}
125
+ filePlaceholder={filePlaceholder}
126
+ title={title}
127
+ withMarkdownTitle={withMarkdownTitle}
128
+ markdownStyles={markdownStyles}
129
+ disabled={disabled}
130
+ pickerText={pickerText}
131
+ helpText={helpText}
132
+ />
133
+ </View>
134
+ ) : (
135
+ <View style={style}>
136
+ <Picker
137
+ icon={UPLOAD_ICON}
138
+ error={error}
139
+ onAdd={this.handleShowPicker}
140
+ onDelete={this.handleDeleteFile}
141
+ uploadedFiles={uploadedFiles}
142
+ filePlaceholder={filePlaceholder}
143
+ title={title}
144
+ withMarkdownTitle={withMarkdownTitle}
145
+ markdownStyles={markdownStyles}
146
+ disabled={disabled}
147
+ pickerText={pickerText}
148
+ helpText={helpText}
149
+ UploadIcon={UploadIcon}
150
+ />
151
+ </View>
152
+ );
153
+ }
154
+ }
155
+
156
+ MultipleFilePicker.defaultProps = {
157
+ maxFileByteSize: DEFAULT_MAX_SIZE
158
+ };
159
+
160
+ MultipleFilePicker.propTypes = filePickerPropTypes;
161
+
162
+ export default MultipleFilePicker;
@@ -0,0 +1,17 @@
1
+ import { oneOfType, string, bool, func, number, arrayOf, shape } from 'prop-types';
2
+
3
+ export default {
4
+ error: oneOfType([string, bool]),
5
+ onChange: func,
6
+ allowedTypes: arrayOf(string),
7
+ filePlaceholder: string,
8
+ title: string,
9
+ onMaxSizeError: func,
10
+ maxFileByteSize: number,
11
+ onError: func,
12
+ fileTypeError: string,
13
+ allowedPDFUploadSizes: arrayOf(
14
+ shape({ name: string, heightInPt: number, widthInPt: number, tolerance: number })
15
+ ),
16
+ disabled: bool
17
+ };
@@ -0,0 +1,41 @@
1
+ import { PDFDocument } from 'pdf-lib';
2
+
3
+ export const onlyPDFAllowed = allowedTypes => allowedTypes.length === 1 && allowedTypes[0].includes('pdf');
4
+
5
+ const lengthMatches = (length1, length2, toleranceInPercentage) => {
6
+ const delta = length1 * (toleranceInPercentage / 100);
7
+ return Math.abs(length1 - length2) < delta;
8
+ };
9
+
10
+ const pageMatches = (pageSize, allowedPDFUploadSizes) =>
11
+ allowedPDFUploadSizes.some(
12
+ size =>
13
+ (lengthMatches(size.heightInPt, pageSize.height, size.toleranceInPercentage) &&
14
+ lengthMatches(size.widthInPt, pageSize.width, size.toleranceInPercentage)) ||
15
+ (lengthMatches(size.heightInPt, pageSize.width, size.toleranceInPercentage) &&
16
+ lengthMatches(size.widthInPt, pageSize.height, size.toleranceInPercentage))
17
+ );
18
+
19
+ const blobToBase64 = blob =>
20
+ new Promise((resolve, reject) => {
21
+ const reader = new FileReader();
22
+ reader.onload = function () {
23
+ const result = reader.result.replace(/^data:.+;base64,/, '');
24
+ resolve(result);
25
+ };
26
+ reader.onerror = function () {
27
+ reject(new Error('No es posible leer el archivo.'));
28
+ };
29
+ reader.readAsDataURL(blob);
30
+ });
31
+
32
+ export const pdfAspectRatioValidation = async (file, allowedPDFUploadSizes) => {
33
+ const base64 = await blobToBase64(file);
34
+ const pdf = await PDFDocument.load(base64);
35
+ const pages = pdf.getPages();
36
+
37
+ return pages.some(page => {
38
+ const { width, height } = page.getSize();
39
+ return !pageMatches({ width, height }, allowedPDFUploadSizes);
40
+ });
41
+ };
package/lib/index.js CHANGED
@@ -16,6 +16,7 @@ export { default as RadioGroup } from './components/RadioGroup';
16
16
  export { default as Input } from './components/Input';
17
17
  export { default as ImagePicker } from './components/ImagePicker';
18
18
  export { default as FilePicker } from './components/FilePicker';
19
+ export { default as MultipleFilePicker } from './components/MultipleFilePicker';
19
20
  export { default as Picker } from './components/Picker';
20
21
  export { default as ImageLabel } from './components/ImageLabel';
21
22
  export { default as CheckList } from './components/CheckList';
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@widergy/mobile-ui",
3
3
  "description": "Widergy Mobile Components",
4
4
  "author": "widergy",
5
- "version": "1.6.0",
5
+ "version": "1.7.0",
6
6
  "repository": "https://github.com/widergy/mobile-ui.git",
7
7
  "main": "lib/index.js",
8
8
  "files": [