@widergy/mobile-ui 1.51.0 → 2.0.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 +10 -0
- package/lib/components/Carousel/components/CarouselContainer/index.js +5 -3
- package/lib/components/FilePicker/constants.js +1 -4
- package/lib/components/FilePicker/index.js +33 -17
- package/lib/components/ImagePicker/ModalSelectionOption/index.js +14 -2
- package/lib/components/ImagePicker/index.js +5 -1
- package/lib/components/ImagePicker/layout.js +65 -5
- package/lib/components/MultipleFilePicker/index.js +275 -35
- package/lib/components/MultipleFilePicker/propTypes.js +16 -1
- package/lib/components/MultipleFilePicker/utils.js +44 -13
- package/lib/components/Overlay/index.js +8 -3
- package/lib/components/PhotoAlbum/index.js +28 -4
- package/lib/components/RateChart/components/RateStagesGraph/components/Bars/index.js +5 -4
- package/lib/components/UTCheckBox/index.js +3 -15
- package/lib/components/UTCheckBox/theme.js +4 -18
- package/lib/components/UTCheckList/index.js +11 -7
- package/lib/components/UTDetailDrawer/index.js +4 -4
- package/lib/components/UTIcon/index.js +0 -1
- package/lib/components/UTIcon/utils.js +3 -3
- package/lib/components/UTModal/index.js +2 -0
- package/lib/components/UTRating/index.js +4 -3
- package/lib/components/UTRating/styles.js +3 -1
- package/lib/components/UTTabs/index.js +14 -6
- package/lib/components/UTTabs/styles.js +51 -19
- package/lib/components/UTTopbar/index.js +3 -5
- package/lib/components/UTTracker/components/Step/styles.js +8 -4
- package/lib/components/UTWorkflowContainer/versions/V1/index.js +19 -4
- package/lib/reactotronConfig.js +36 -3
- package/lib/utils/fileUtils.js/index.js +15 -10
- package/package.json +12 -10
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { isEmpty } from 'lodash';
|
|
2
2
|
import { PDFDocument } from 'pdf-lib';
|
|
3
|
-
// eslint-disable-next-line import/no-unresolved
|
|
4
|
-
import DocumentPicker from 'react-native-document-picker';
|
|
5
3
|
|
|
6
4
|
const mimeTypes = {
|
|
7
5
|
allFiles: '*/*',
|
|
@@ -57,11 +55,11 @@ const pageMatches = (pageSize, allowedPDFUploadSizes) =>
|
|
|
57
55
|
const blobToBase64 = blob =>
|
|
58
56
|
new Promise((resolve, reject) => {
|
|
59
57
|
const reader = new FileReader();
|
|
60
|
-
reader.onload = function () {
|
|
58
|
+
reader.onload = function onload() {
|
|
61
59
|
const result = reader.result.replace(/^data:.+;base64,/, '');
|
|
62
60
|
resolve(result);
|
|
63
61
|
};
|
|
64
|
-
reader.onerror = function () {
|
|
62
|
+
reader.onerror = function onerror() {
|
|
65
63
|
reject(new Error('No es posible leer el archivo.'));
|
|
66
64
|
};
|
|
67
65
|
reader.readAsDataURL(blob);
|
|
@@ -78,19 +76,52 @@ const pdfAspectRatioValidation = async (file, allowedPDFUploadSizes) => {
|
|
|
78
76
|
});
|
|
79
77
|
};
|
|
80
78
|
|
|
81
|
-
export const getFileType =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
79
|
+
export const getFileType = extensionOrCategory => {
|
|
80
|
+
const input = extensionOrCategory;
|
|
81
|
+
if (!input) return undefined;
|
|
82
|
+
if (extensions[input]) return input;
|
|
83
|
+
if (input === 'image') return 'images';
|
|
84
|
+
if (input === 'video') return 'video';
|
|
85
|
+
if (input === 'audio') return 'audio';
|
|
86
|
+
if (typeof input === 'string' && input.startsWith('.')) {
|
|
87
|
+
const entry = Object.entries(extensions).find(([, value]) => value.includes(input));
|
|
88
|
+
return entry ? entry[0] : undefined;
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
88
91
|
};
|
|
89
92
|
|
|
90
|
-
export const
|
|
93
|
+
export const getMimeType = value => {
|
|
94
|
+
const input = value;
|
|
95
|
+
if (!input) return mimeTypes.allFiles;
|
|
96
|
+
if (typeof input === 'string' && input.includes('/')) return input;
|
|
97
|
+
const key = getFileType(input);
|
|
98
|
+
if (key && mimeTypes[key]) return mimeTypes[key];
|
|
99
|
+
if (input === 'image') return 'image/*';
|
|
100
|
+
if (input === 'video') return 'video/*';
|
|
101
|
+
if (input === 'audio') return 'audio/*';
|
|
102
|
+
return mimeTypes.allFiles;
|
|
103
|
+
};
|
|
91
104
|
|
|
92
105
|
export const isFileTypeInvalid = (document, allowedTypes, fileTypeError, onError) => {
|
|
93
|
-
const
|
|
106
|
+
const normalizedAllowed = (
|
|
107
|
+
Array.isArray(allowedTypes) && allowedTypes.length > 0 ? allowedTypes : ['allFiles']
|
|
108
|
+
)
|
|
109
|
+
.map(getMimeType)
|
|
110
|
+
.filter(Boolean);
|
|
111
|
+
|
|
112
|
+
const docTypeRaw = document?.type || '';
|
|
113
|
+
const docType = docTypeRaw.includes('/') ? docTypeRaw : `${docTypeRaw}/*`;
|
|
114
|
+
|
|
115
|
+
const matches = (allowed, actual) => {
|
|
116
|
+
if (allowed === mimeTypes.allFiles) return true;
|
|
117
|
+
if (allowed.endsWith('/*')) {
|
|
118
|
+
const prefix = `${allowed.split('/')[0]}/`;
|
|
119
|
+
return actual.startsWith(prefix) || actual === allowed;
|
|
120
|
+
}
|
|
121
|
+
return allowed === actual;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const isInvalid = !normalizedAllowed.some(a => matches(a, docType));
|
|
94
125
|
if (isInvalid) onError(fileTypeError || 'Tipo de archivo inválido.');
|
|
95
126
|
return isInvalid;
|
|
96
127
|
};
|
|
@@ -19,7 +19,8 @@ class Overlay extends Component {
|
|
|
19
19
|
|
|
20
20
|
this.state = {
|
|
21
21
|
opacity: new Value(0),
|
|
22
|
-
backgroundOpacity: new Value(props.transparentBackground ? 0 : 1)
|
|
22
|
+
backgroundOpacity: new Value(props.transparentBackground ? 0 : 1),
|
|
23
|
+
backSubscription: null
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -58,7 +59,10 @@ class Overlay extends Component {
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
setBack() {
|
|
61
|
-
|
|
62
|
+
this.setState(prevState => ({
|
|
63
|
+
...prevState,
|
|
64
|
+
backSubscription: BackHandler.addEventListener('hardwareBackPress', this.dismiss)
|
|
65
|
+
}));
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
dismiss = () => {
|
|
@@ -94,7 +98,8 @@ class Overlay extends Component {
|
|
|
94
98
|
};
|
|
95
99
|
|
|
96
100
|
removeBack() {
|
|
97
|
-
|
|
101
|
+
const { backSubscription } = this.state;
|
|
102
|
+
backSubscription?.remove?.();
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
render() {
|
|
@@ -3,6 +3,7 @@ import React, { Fragment, useEffect, useState } from 'react';
|
|
|
3
3
|
import { View, TouchableOpacity, PermissionsAndroid } from 'react-native';
|
|
4
4
|
// eslint-disable-next-line import/no-unresolved
|
|
5
5
|
import { launchCamera } from 'react-native-image-picker';
|
|
6
|
+
import * as ExpoImagePicker from 'expo-image-picker';
|
|
6
7
|
|
|
7
8
|
import UTLabel from '../UTLabel';
|
|
8
9
|
import Portal from '../Portal';
|
|
@@ -59,20 +60,43 @@ const PhotoAlbum = ({
|
|
|
59
60
|
};
|
|
60
61
|
|
|
61
62
|
const openCamera = async () => {
|
|
63
|
+
if (!ExpoImagePicker || typeof ExpoImagePicker.launchCameraAsync !== 'function') {
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
console.error('No se encontró un ExpoImagePicker válido. instala "expo-image-picker".');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
62
68
|
// eslint-disable-next-line no-useless-catch
|
|
63
69
|
try {
|
|
64
|
-
if (
|
|
70
|
+
if (ExpoImagePicker?.launchCameraAsync) {
|
|
71
|
+
const perm = await ExpoImagePicker.requestCameraPermissionsAsync?.();
|
|
72
|
+
if (!perm || perm.status !== 'granted') {
|
|
73
|
+
throw messageErrorPermissionCamera;
|
|
74
|
+
}
|
|
75
|
+
const mediaTypes = ExpoImagePicker.MediaType ? [ExpoImagePicker.MediaType.image] : undefined;
|
|
76
|
+
const result = await ExpoImagePicker.launchCameraAsync({
|
|
77
|
+
mediaTypes,
|
|
78
|
+
allowsMultipleSelection: false,
|
|
79
|
+
quality: 1
|
|
80
|
+
});
|
|
81
|
+
if (result?.canceled) return;
|
|
82
|
+
const asset = Array.isArray(result?.assets) && result.assets[0];
|
|
83
|
+
if (asset?.uri) {
|
|
84
|
+
onPressAddAnotherPhoto({ newImage: { uri: asset.uri }, numberOfPhotosInTheAlbumHasExceeded });
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
65
88
|
|
|
89
|
+
if (IS_ANDROID) await requestPermission();
|
|
66
90
|
const response = await launchCamera({});
|
|
67
91
|
const { didCancel, errorCode, errorMessage, assets } = response;
|
|
68
92
|
if (didCancel || errorCode) {
|
|
69
93
|
if (errorCode) {
|
|
70
94
|
onUnknownCameraError(`[${errorCode}] : ${errorMessage}`);
|
|
71
95
|
}
|
|
72
|
-
|
|
73
|
-
const { uri } = assets[0];
|
|
74
|
-
onPressAddAnotherPhoto({ newImage: { uri }, numberOfPhotosInTheAlbumHasExceeded });
|
|
96
|
+
return;
|
|
75
97
|
}
|
|
98
|
+
const { uri } = assets[0];
|
|
99
|
+
onPressAddAnotherPhoto({ newImage: { uri }, numberOfPhotosInTheAlbumHasExceeded });
|
|
76
100
|
} catch (error) {
|
|
77
101
|
throw error;
|
|
78
102
|
}
|
|
@@ -10,14 +10,15 @@ import SubStage from './components/Stage/components/SubStage';
|
|
|
10
10
|
import styles from './styles';
|
|
11
11
|
|
|
12
12
|
const Bars = ({ rateStages, withoutStages }) => {
|
|
13
|
-
const
|
|
13
|
+
const rateStagesToUse = [...(rateStages || [])];
|
|
14
|
+
const totalSubStages = rateStagesToUse.reduce(
|
|
14
15
|
(previous, current) => previous + current.sub_rate_stages.length,
|
|
15
16
|
0
|
|
16
17
|
);
|
|
17
18
|
|
|
18
|
-
const totalRange = getTotalRange(
|
|
19
|
+
const totalRange = getTotalRange(rateStagesToUse);
|
|
19
20
|
// eslint-disable-next-line camelcase
|
|
20
|
-
const subStages =
|
|
21
|
+
const subStages = rateStagesToUse.flatMap(rate => rate?.sub_rate_stages);
|
|
21
22
|
|
|
22
23
|
return (
|
|
23
24
|
<View style={styles.container}>
|
|
@@ -35,7 +36,7 @@ const Bars = ({ rateStages, withoutStages }) => {
|
|
|
35
36
|
isLast={index === subStages.length - 1}
|
|
36
37
|
/>
|
|
37
38
|
))
|
|
38
|
-
:
|
|
39
|
+
: rateStagesToUse.map((stage, index) => (
|
|
39
40
|
<Stage
|
|
40
41
|
key={stage.group}
|
|
41
42
|
stage={stage}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
2
|
import { View, Pressable } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../../theming';
|
|
@@ -33,7 +33,6 @@ const UTCheckBox = ({
|
|
|
33
33
|
dataTestId
|
|
34
34
|
}) => {
|
|
35
35
|
const theme = useTheme();
|
|
36
|
-
const [pressed, setPressed] = useState(false);
|
|
37
36
|
|
|
38
37
|
const { containerStyles, iconContainerStyles, boxStyles, pressableStyles, titleStyles } = useMemo(
|
|
39
38
|
() =>
|
|
@@ -42,14 +41,13 @@ const UTCheckBox = ({
|
|
|
42
41
|
disabled,
|
|
43
42
|
error,
|
|
44
43
|
indeterminate,
|
|
45
|
-
pressed,
|
|
46
44
|
reversed,
|
|
47
45
|
spacing,
|
|
48
46
|
style,
|
|
49
47
|
theme,
|
|
50
48
|
variant
|
|
51
49
|
}),
|
|
52
|
-
[value, disabled, error, indeterminate, reversed, spacing, style, theme, variant
|
|
50
|
+
[value, disabled, error, indeterminate, reversed, spacing, style, theme, variant]
|
|
53
51
|
);
|
|
54
52
|
|
|
55
53
|
const iconName = useMemo(
|
|
@@ -57,9 +55,6 @@ const UTCheckBox = ({
|
|
|
57
55
|
[indeterminate, value]
|
|
58
56
|
);
|
|
59
57
|
|
|
60
|
-
const handlePressIn = useCallback(() => setPressed(true), []);
|
|
61
|
-
const handlePressOut = useCallback(() => setPressed(false), []);
|
|
62
|
-
|
|
63
58
|
const handlePress = useCallback(() => {
|
|
64
59
|
if (!disabled && onChange) {
|
|
65
60
|
onChange(!value);
|
|
@@ -71,14 +66,7 @@ const UTCheckBox = ({
|
|
|
71
66
|
const validationData = useMemo(() => error && formatErrorToValidation(error), [error]);
|
|
72
67
|
|
|
73
68
|
return (
|
|
74
|
-
<Pressable
|
|
75
|
-
style={pressableStyles}
|
|
76
|
-
disabled={disabled}
|
|
77
|
-
onPress={handlePress}
|
|
78
|
-
onPressIn={handlePressIn}
|
|
79
|
-
onPressOut={handlePressOut}
|
|
80
|
-
testID={dataTestId}
|
|
81
|
-
>
|
|
69
|
+
<Pressable style={pressableStyles} disabled={disabled} onPress={handlePress} testID={dataTestId}>
|
|
82
70
|
<View style={styles.container}>
|
|
83
71
|
<View style={containerStyles}>
|
|
84
72
|
{isSimple ? (
|
|
@@ -33,7 +33,6 @@ const conditionalStyles = ({
|
|
|
33
33
|
reversed,
|
|
34
34
|
spacing,
|
|
35
35
|
variant,
|
|
36
|
-
pressed,
|
|
37
36
|
theme
|
|
38
37
|
}) => {
|
|
39
38
|
const spacingValue = spacing === SPACING.SMALL ? SMALL_SPACING : MEDIUM_SPACING;
|
|
@@ -63,24 +62,13 @@ const conditionalStyles = ({
|
|
|
63
62
|
marginLeft: spacingValue,
|
|
64
63
|
marginRight: 0
|
|
65
64
|
}
|
|
66
|
-
: { marginRight: spacingValue })
|
|
67
|
-
...(pressed &&
|
|
68
|
-
variant !== BUTTON_VARIANT && {
|
|
69
|
-
backgroundColor: theme.Palette.accent['02'],
|
|
70
|
-
borderColor: theme.Palette.accent['03'],
|
|
71
|
-
borderWidth: 1,
|
|
72
|
-
overflow: 'hidden',
|
|
73
|
-
padding: 5
|
|
74
|
-
})
|
|
65
|
+
: { marginRight: spacingValue })
|
|
75
66
|
},
|
|
76
67
|
pressable: {
|
|
77
68
|
...(variant === BUTTON_VARIANT && {
|
|
78
69
|
borderRadius: 8,
|
|
79
70
|
paddingHorizontal: 16,
|
|
80
71
|
paddingVertical: 12,
|
|
81
|
-
...(pressed && {
|
|
82
|
-
backgroundColor: theme.Palette.light['04']
|
|
83
|
-
}),
|
|
84
72
|
...(error && {
|
|
85
73
|
backgroundColor: theme.Palette.error['02']
|
|
86
74
|
})
|
|
@@ -99,8 +87,7 @@ export const retrieveStyle = ({
|
|
|
99
87
|
spacing,
|
|
100
88
|
style,
|
|
101
89
|
theme,
|
|
102
|
-
variant
|
|
103
|
-
pressed
|
|
90
|
+
variant
|
|
104
91
|
}) => {
|
|
105
92
|
const baseThemeStyles = baseCheckBoxTheme(theme);
|
|
106
93
|
|
|
@@ -112,7 +99,6 @@ export const retrieveStyle = ({
|
|
|
112
99
|
reversed,
|
|
113
100
|
spacing,
|
|
114
101
|
variant,
|
|
115
|
-
pressed,
|
|
116
102
|
theme
|
|
117
103
|
});
|
|
118
104
|
|
|
@@ -124,7 +110,7 @@ export const retrieveStyle = ({
|
|
|
124
110
|
containerStyles: {
|
|
125
111
|
...baseThemeStyles.container,
|
|
126
112
|
...container,
|
|
127
|
-
...style
|
|
113
|
+
...style?.root
|
|
128
114
|
},
|
|
129
115
|
iconContainerStyles: {
|
|
130
116
|
...baseThemeStyles.iconContainer,
|
|
@@ -132,7 +118,7 @@ export const retrieveStyle = ({
|
|
|
132
118
|
},
|
|
133
119
|
titleStyles: {
|
|
134
120
|
...baseThemeStyles.title,
|
|
135
|
-
...style
|
|
121
|
+
...style?.title
|
|
136
122
|
},
|
|
137
123
|
pressableStyles: pressable
|
|
138
124
|
};
|
|
@@ -51,7 +51,7 @@ const UTCheckList = ({
|
|
|
51
51
|
}
|
|
52
52
|
}, [value, onChange]);
|
|
53
53
|
|
|
54
|
-
const enabledOptions = useMemo(() => options.filter(option => !option.disabled), [options]);
|
|
54
|
+
const enabledOptions = useMemo(() => [...options].filter(option => !option.disabled), [options]);
|
|
55
55
|
|
|
56
56
|
const areAllSelected = useMemo(
|
|
57
57
|
() => enabledOptions.every(option => value?.includes(option.value)),
|
|
@@ -83,9 +83,13 @@ const UTCheckList = ({
|
|
|
83
83
|
|
|
84
84
|
return (
|
|
85
85
|
<View
|
|
86
|
-
style={[
|
|
86
|
+
style={[
|
|
87
|
+
styles.container,
|
|
88
|
+
verticalSpacing === SPACING.SMALL && styles.smallVerticalSpacing,
|
|
89
|
+
style?.root
|
|
90
|
+
]}
|
|
87
91
|
>
|
|
88
|
-
<View style={[styles.headerContainer, style
|
|
92
|
+
<View style={[styles.headerContainer, style?.header]}>
|
|
89
93
|
{title && (
|
|
90
94
|
<UTFieldLabel
|
|
91
95
|
required={required}
|
|
@@ -106,7 +110,7 @@ const UTCheckList = ({
|
|
|
106
110
|
styles.checkboxesContainer,
|
|
107
111
|
verticalSpacingBasedOnVariant === SPACING.SMALL && styles.smallVerticalSpacing,
|
|
108
112
|
variant === BUTTON_VARIANT && styles.buttonVariant,
|
|
109
|
-
style
|
|
113
|
+
style?.checkboxesContainer
|
|
110
114
|
]}
|
|
111
115
|
>
|
|
112
116
|
{showSelectAll && !isSimple && (
|
|
@@ -117,12 +121,12 @@ const UTCheckList = ({
|
|
|
117
121
|
onChange={handleCheckAll}
|
|
118
122
|
reversed={reversedBasedOnVariant}
|
|
119
123
|
spacing={horizontalSpacing}
|
|
120
|
-
style={style
|
|
124
|
+
style={style?.selectAll}
|
|
121
125
|
variant={variant}
|
|
122
126
|
dataTestId={dataTestId ? `${dataTestId}.${selectAllTestId}` : undefined}
|
|
123
127
|
/>
|
|
124
128
|
)}
|
|
125
|
-
{options?.map((item, index) => (
|
|
129
|
+
{[...options]?.map((item, index) => (
|
|
126
130
|
<UTCheckBox
|
|
127
131
|
isSimple={isSimple}
|
|
128
132
|
value={isChecked(item, value)}
|
|
@@ -132,7 +136,7 @@ const UTCheckList = ({
|
|
|
132
136
|
onChange={() => handleChange(item.value)}
|
|
133
137
|
reversed={reversedBasedOnVariant}
|
|
134
138
|
spacing={horizontalSpacing}
|
|
135
|
-
style={style
|
|
139
|
+
style={style?.item}
|
|
136
140
|
variant={variant}
|
|
137
141
|
dataTestId={dataTestId ? `${dataTestId}.${optionTestId}.${index}` : undefined}
|
|
138
142
|
/>
|
|
@@ -76,16 +76,16 @@ const UTDetailDrawer = ({
|
|
|
76
76
|
dataTestId={dataTestId ? `${dataTestId}.${close}` : undefined}
|
|
77
77
|
onPress={onClose}
|
|
78
78
|
Icon="IconX"
|
|
79
|
-
variant={CloseButtonProps
|
|
80
|
-
colorTheme={CloseButtonProps
|
|
79
|
+
variant={CloseButtonProps?.variant || 'text'}
|
|
80
|
+
colorTheme={CloseButtonProps?.colorTheme || null}
|
|
81
81
|
style={{
|
|
82
82
|
root: {
|
|
83
83
|
...mergedStyles.closeButton,
|
|
84
|
-
...(CloseButtonProps
|
|
84
|
+
...(CloseButtonProps?.root ? CloseButtonProps.root : null)
|
|
85
85
|
},
|
|
86
86
|
icon: {
|
|
87
87
|
...mergedStyles.icon,
|
|
88
|
-
...(CloseButtonProps
|
|
88
|
+
...(CloseButtonProps?.icon ? CloseButtonProps.icon : null)
|
|
89
89
|
}
|
|
90
90
|
}}
|
|
91
91
|
/>
|
|
@@ -29,9 +29,9 @@ export const getIconProps = ({
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
return {
|
|
32
|
-
|
|
33
|
-
...(
|
|
34
|
-
...(
|
|
32
|
+
...(isEnergyIcon || isBrandIcon ? customIconProps : defaultIconProps),
|
|
33
|
+
...(filled ? { color: themeColor, ...iconStyles } : {}),
|
|
34
|
+
...(internalFill && { fill: internalFill })
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
37
|
|
|
@@ -32,8 +32,9 @@ const UTRating = ({
|
|
|
32
32
|
() => validations || (error && formatErrorToValidation(error)),
|
|
33
33
|
[error, validations]
|
|
34
34
|
);
|
|
35
|
+
const optionsToUse = [...options];
|
|
35
36
|
|
|
36
|
-
const valueIndex =
|
|
37
|
+
const valueIndex = optionsToUse.map(({ value: optionValue }) => optionValue).indexOf(value);
|
|
37
38
|
const isSelected = index =>
|
|
38
39
|
valueIndex !== -1 && variant === RATING_VARIANTS.STAR ? index <= valueIndex : index === valueIndex;
|
|
39
40
|
|
|
@@ -52,7 +53,7 @@ const UTRating = ({
|
|
|
52
53
|
</UTFieldLabel>
|
|
53
54
|
)}
|
|
54
55
|
<View style={styles.optionsContainer} onLayout={onLayout}>
|
|
55
|
-
{
|
|
56
|
+
{optionsToUse.map((option, index) => (
|
|
56
57
|
<Option
|
|
57
58
|
{...option}
|
|
58
59
|
disabled={disabled}
|
|
@@ -61,7 +62,7 @@ const UTRating = ({
|
|
|
61
62
|
key={option.value}
|
|
62
63
|
onChange={onChange}
|
|
63
64
|
variant={variant}
|
|
64
|
-
wrapperStyle={styles.option(containerWidth, error,
|
|
65
|
+
wrapperStyle={styles.option(containerWidth, error, optionsToUse, theme)}
|
|
65
66
|
/>
|
|
66
67
|
))}
|
|
67
68
|
</View>
|
|
@@ -23,6 +23,7 @@ export default StyleSheet.create({
|
|
|
23
23
|
option: (containerWidth, error, options, theme) => {
|
|
24
24
|
const topItems = Math.ceil(options.length / 2);
|
|
25
25
|
const breakOptions = options.length > 5;
|
|
26
|
+
|
|
26
27
|
return {
|
|
27
28
|
alignContent: 'center',
|
|
28
29
|
display: 'flex',
|
|
@@ -30,7 +31,8 @@ export default StyleSheet.create({
|
|
|
30
31
|
flexDirection: 'row',
|
|
31
32
|
flexGrow: breakOptions ? 0 : 1,
|
|
32
33
|
flexShrink: 0,
|
|
33
|
-
|
|
34
|
+
minHeight: 48,
|
|
35
|
+
minWidth: 48,
|
|
34
36
|
justifyContent: 'center',
|
|
35
37
|
...(error ? { borderColor: theme.Palette.error['05'], borderWidth: 2 } : {})
|
|
36
38
|
};
|
|
@@ -170,13 +170,21 @@ const UTTabs = ({
|
|
|
170
170
|
disabled={selected}
|
|
171
171
|
key={`tab-${label || icon}`}
|
|
172
172
|
onPress={() => setSelectedTab(index)}
|
|
173
|
-
style={styles.
|
|
173
|
+
style={styles.tabContainer(tabs.length, index)}
|
|
174
174
|
>
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
175
|
+
<View style={styles.tabContent(selected)}>
|
|
176
|
+
{icon && <UTIcon colorTheme={colorThemeToUse} name={icon} />}
|
|
177
|
+
<UTLabel
|
|
178
|
+
colorTheme={colorThemeToUse}
|
|
179
|
+
shade="04"
|
|
180
|
+
weight="medium"
|
|
181
|
+
style={styles.tabLabel}
|
|
182
|
+
{...fixedLabelProps}
|
|
183
|
+
>
|
|
184
|
+
{label}
|
|
185
|
+
</UTLabel>
|
|
186
|
+
{badge && <UTBadge colorTheme="accent" />}
|
|
187
|
+
</View>
|
|
180
188
|
</Pressable>
|
|
181
189
|
);
|
|
182
190
|
})}
|
|
@@ -2,6 +2,16 @@ import { StyleSheet } from 'react-native';
|
|
|
2
2
|
|
|
3
3
|
import { COLOR_THEMES, HIERARCHIES } from './constants';
|
|
4
4
|
|
|
5
|
+
const getTabDistribution = length => {
|
|
6
|
+
const equalWidth = `${100 / length}%`;
|
|
7
|
+
return {
|
|
8
|
+
widths: Array(length).fill(equalWidth),
|
|
9
|
+
positions: Array(length)
|
|
10
|
+
.fill(0)
|
|
11
|
+
.map((_, i) => `${(100 / length) * i}%`)
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
|
|
5
15
|
export default StyleSheet.create(({ Palette: { accent, neutral, negative, light } }) => ({
|
|
6
16
|
border: {
|
|
7
17
|
position: 'absolute',
|
|
@@ -80,13 +90,24 @@ export default StyleSheet.create(({ Palette: { accent, neutral, negative, light
|
|
|
80
90
|
}
|
|
81
91
|
: {};
|
|
82
92
|
|
|
93
|
+
const distribution = getTabDistribution(length);
|
|
94
|
+
const inputRange =
|
|
95
|
+
length === 3
|
|
96
|
+
? [0, 1, 2]
|
|
97
|
+
: Array(length)
|
|
98
|
+
.fill(0)
|
|
99
|
+
.map((_, i) => i);
|
|
100
|
+
|
|
83
101
|
const positioningStyles = isScrollable
|
|
84
102
|
? {}
|
|
85
103
|
: {
|
|
86
|
-
width:
|
|
104
|
+
width: position.interpolate({
|
|
105
|
+
inputRange,
|
|
106
|
+
outputRange: distribution.widths
|
|
107
|
+
}),
|
|
87
108
|
left: position.interpolate({
|
|
88
|
-
inputRange
|
|
89
|
-
outputRange:
|
|
109
|
+
inputRange,
|
|
110
|
+
outputRange: distribution.positions
|
|
90
111
|
})
|
|
91
112
|
};
|
|
92
113
|
|
|
@@ -98,22 +119,33 @@ export default StyleSheet.create(({ Palette: { accent, neutral, negative, light
|
|
|
98
119
|
...positioningStyles
|
|
99
120
|
};
|
|
100
121
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
tabContainer: (tabs, index) => () => {
|
|
123
|
+
const distribution = getTabDistribution(tabs);
|
|
124
|
+
const width = distribution.widths[index] || 'auto';
|
|
125
|
+
return {
|
|
126
|
+
width,
|
|
127
|
+
zIndex: 3
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
tabContent: selected => ({
|
|
131
|
+
alignItems: 'center',
|
|
132
|
+
display: 'flex',
|
|
133
|
+
flexDirection: 'row',
|
|
134
|
+
paddingHorizontal: 8,
|
|
135
|
+
gap: 8,
|
|
136
|
+
height: 48,
|
|
137
|
+
justifyContent: 'center',
|
|
138
|
+
backgroundColor: selected ? 'white' : 'transparent',
|
|
139
|
+
borderRadius: 4,
|
|
140
|
+
width: '100%',
|
|
141
|
+
minWidth: '100%',
|
|
142
|
+
overflow: 'hidden',
|
|
143
|
+
position: 'relative'
|
|
144
|
+
}),
|
|
145
|
+
tabLabel: {
|
|
146
|
+
flex: 1,
|
|
147
|
+
textAlign: 'center'
|
|
148
|
+
},
|
|
117
149
|
scrollableTab: ({ pressed }) => ({
|
|
118
150
|
alignItems: 'center',
|
|
119
151
|
display: 'flex',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import React, { useCallback } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect } from 'react';
|
|
2
2
|
import { BackHandler, View } from 'react-native';
|
|
3
3
|
import { number } from 'prop-types';
|
|
4
|
-
import { useFocusEffect } from '@react-navigation/native';
|
|
5
4
|
|
|
6
5
|
import { TEST_IDS } from '../../constants/testIds';
|
|
7
6
|
import UTButton from '../UTButton';
|
|
@@ -25,8 +24,8 @@ const UTTopbar = ({
|
|
|
25
24
|
}) => {
|
|
26
25
|
const ownTheme = theme.UTWorkflowContainer?.topbar?.[colorTheme];
|
|
27
26
|
|
|
28
|
-
//
|
|
29
|
-
|
|
27
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
28
|
+
useEffect(
|
|
30
29
|
useCallback(() => {
|
|
31
30
|
const onBackPress = () => {
|
|
32
31
|
goBack();
|
|
@@ -38,7 +37,6 @@ const UTTopbar = ({
|
|
|
38
37
|
return () => subscription.remove();
|
|
39
38
|
}, [goBack])
|
|
40
39
|
);
|
|
41
|
-
// up to here
|
|
42
40
|
|
|
43
41
|
return (
|
|
44
42
|
<View>
|
|
@@ -10,13 +10,15 @@ export const getVariantStyles = theme => ({
|
|
|
10
10
|
borderColor: theme.Palette.error['04']
|
|
11
11
|
},
|
|
12
12
|
activeInnerContainer: {
|
|
13
|
-
backgroundColor: theme.Palette.error['04']
|
|
13
|
+
backgroundColor: theme.Palette.error['04'],
|
|
14
|
+
borderRadius: OVAL_SIZE / 4
|
|
14
15
|
},
|
|
15
16
|
completedContainer: {
|
|
16
17
|
borderColor: theme.Palette.error['04']
|
|
17
18
|
},
|
|
18
19
|
completedInnerContainer: {
|
|
19
|
-
backgroundColor: theme.Palette.error['04']
|
|
20
|
+
backgroundColor: theme.Palette.error['04'],
|
|
21
|
+
borderRadius: OVAL_SIZE / 4
|
|
20
22
|
}
|
|
21
23
|
},
|
|
22
24
|
[STANDARD]: {
|
|
@@ -24,13 +26,15 @@ export const getVariantStyles = theme => ({
|
|
|
24
26
|
borderColor: theme.Palette.accent['04']
|
|
25
27
|
},
|
|
26
28
|
activeInnerContainer: {
|
|
27
|
-
backgroundColor: theme.Palette.accent['04']
|
|
29
|
+
backgroundColor: theme.Palette.accent['04'],
|
|
30
|
+
borderRadius: OVAL_SIZE / 4
|
|
28
31
|
},
|
|
29
32
|
completedContainer: {
|
|
30
33
|
borderColor: theme.Palette.accent['04']
|
|
31
34
|
},
|
|
32
35
|
completedInnerContainer: {
|
|
33
|
-
backgroundColor: theme.Palette.accent['04']
|
|
36
|
+
backgroundColor: theme.Palette.accent['04'],
|
|
37
|
+
borderRadius: OVAL_SIZE / 4
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
});
|