@widergy/mobile-ui 1.5.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.
- package/CHANGELOG.md +39 -0
- package/lib/components/MultipleFilePicker/components/Input/README.md +77 -0
- package/lib/components/MultipleFilePicker/components/Input/components/ShowPassword/constants.js +2 -0
- package/lib/components/MultipleFilePicker/components/Input/components/ShowPassword/index.js +27 -0
- package/lib/components/MultipleFilePicker/components/Input/components/ShowPassword/propTypes.js +8 -0
- package/lib/components/MultipleFilePicker/components/Input/components/ShowPassword/styles.js +15 -0
- package/lib/components/MultipleFilePicker/components/Input/components/Title/index.js +78 -0
- package/lib/components/MultipleFilePicker/components/Input/components/Title/propTypes.js +14 -0
- package/lib/components/MultipleFilePicker/components/Input/components/Title/styles.js +42 -0
- package/lib/components/MultipleFilePicker/components/Input/components/Underline/index.js +80 -0
- package/lib/components/MultipleFilePicker/components/Input/components/Underline/styles.js +39 -0
- package/lib/components/MultipleFilePicker/components/Input/constants.js +2 -0
- package/lib/components/MultipleFilePicker/components/Input/index.js +299 -0
- package/lib/components/MultipleFilePicker/components/Input/propTypes.js +43 -0
- package/lib/components/MultipleFilePicker/components/Input/styles.js +47 -0
- package/lib/components/MultipleFilePicker/components/Picker/index.js +95 -0
- package/lib/components/MultipleFilePicker/components/Picker/styles.js +47 -0
- package/lib/components/MultipleFilePicker/components/UploadedFiles/index.js +140 -0
- package/lib/components/MultipleFilePicker/components/UploadedFiles/styles.js +65 -0
- package/lib/components/MultipleFilePicker/components/UploadedFiles/utils.js +6 -0
- package/lib/components/MultipleFilePicker/constants.js +18 -0
- package/lib/components/MultipleFilePicker/index.js +162 -0
- package/lib/components/MultipleFilePicker/propTypes.js +17 -0
- package/lib/components/MultipleFilePicker/utils.js +41 -0
- package/lib/index.js +1 -0
- package/lib/utils/analyticsUtils/index.js +7 -1
- 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,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';
|
|
@@ -60,10 +60,16 @@ const createAnalyticsMiddleware = (eventsMapper, analytics) => store => next =>
|
|
|
60
60
|
return result;
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
+
export const multiTracking = trackerArgumentsArray =>
|
|
64
|
+
trackerArgumentsArray.map(trackerArguments =>
|
|
65
|
+
createAnalyticsMiddleware(trackerArguments.eventsMapper, trackerArguments.analytics)
|
|
66
|
+
);
|
|
67
|
+
|
|
63
68
|
export default {
|
|
64
69
|
createAnalyticsMiddleware,
|
|
65
70
|
tracker,
|
|
66
71
|
trackEvent,
|
|
67
72
|
trackScreen,
|
|
68
|
-
setUserTracking
|
|
73
|
+
setUserTracking,
|
|
74
|
+
multiTracking
|
|
69
75
|
};
|
package/package.json
CHANGED