@widergy/mobile-ui 1.7.0 → 1.8.1

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 CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.8.1](https://github.com/widergy/mobile-ui/compare/v1.8.0...v1.8.1) (2024-05-09)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * safeareaview for utworkflowcontainer ([#283](https://github.com/widergy/mobile-ui/issues/283)) ([f3bcac4](https://github.com/widergy/mobile-ui/commit/f3bcac45899d6859800264ce7e24c4f09d65ebea))
7
+
8
+ # [1.8.0](https://github.com/widergy/mobile-ui/compare/v1.7.0...v1.8.0) (2024-05-07)
9
+
10
+
11
+ ### Features
12
+
13
+ * new component uttracker ([#280](https://github.com/widergy/mobile-ui/issues/280)) ([3560bee](https://github.com/widergy/mobile-ui/commit/3560bee47485bfd448198380396c4630b1549f40))
14
+
1
15
  # [1.7.0](https://github.com/widergy/mobile-ui/compare/v1.6.0...v1.7.0) (2024-05-06)
2
16
 
3
17
 
@@ -24,6 +24,7 @@ const UTTopbar = ({
24
24
 
25
25
  const ownTheme = theme.UTWorkflowContainer?.topbar?.[colorTheme];
26
26
 
27
+ // If you are trying to render this in the example of mobile-ui, comment this useFocusEffect block or it will crash
27
28
  useFocusEffect(
28
29
  useCallback(() => {
29
30
  const onBackPress = () => {
@@ -36,6 +37,7 @@ const UTTopbar = ({
36
37
  return () => subscription.remove();
37
38
  }, [goBack])
38
39
  );
40
+ // up to here
39
41
 
40
42
  return (
41
43
  <View>
@@ -0,0 +1,24 @@
1
+ # UTTracker
2
+
3
+ ### Description
4
+
5
+ This component displays a vertical stepper intended to track progress
6
+
7
+ ## Props
8
+
9
+ | Name | Type | Default | Description |
10
+ | ----------- | ----------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | title | string | | Displays the title above the tracker. |
12
+ | mode | card \| flat | card | Defines whether the component should display as a card or as the bare components without any background. |
13
+ | variant | standard \| error | standard | Defines the color scheme of the tracker steps and the icon on the active step. |
14
+ | steps | stepsType | | Displays a step with a rounded icon on the left for each object received. |
15
+ | currentStep | number | | Defines which step will be active, previous ones will be marked as completed while following ones will not. Use 1 for the first step |
16
+ | detailsTab | detailsType | | Defines whether the detailsTab is enabled or not, if it is, it will show the subSteps of each step. The title property shows a message along the tab (card mode only) |
17
+
18
+ ### Custom Types
19
+
20
+ | Type | Shape |
21
+ | ------------ | ------------------------------------------------------------------------------ |
22
+ | stepsType | `{ id: number, title: string, subtitle: string, subSteps: [...subStepsType] }` |
23
+ | subStepsType | `{ id: number, title: string, subtitle: string }` |
24
+ | detailsType | `{ enabled: bool, title: string }` |
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import { number } from 'prop-types';
4
+ import merge from 'lodash/merge';
5
+ import { ViewPropTypes } from 'deprecated-react-native-prop-types';
6
+
7
+ import ownStyles from './styles';
8
+
9
+ const Connectors = ({ firstStepPosition, lastStepPosition, stepperHeight, style }) => {
10
+ const themedStyles = merge({}, ownStyles, style);
11
+
12
+ return (
13
+ <View style={themedStyles.wrapper(firstStepPosition, lastStepPosition, stepperHeight)}>
14
+ <View style={themedStyles.connectors} />
15
+ </View>
16
+ );
17
+ };
18
+
19
+ Connectors.propTypes = {
20
+ firstStepPosition: number,
21
+ lastStepPosition: number,
22
+ stepperHeight: number,
23
+ style: ViewPropTypes.style
24
+ };
25
+
26
+ export default Connectors;
@@ -0,0 +1,20 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ import { OVAL_SIZE } from '../../styles';
4
+
5
+ export default StyleSheet.create({
6
+ connectors: {
7
+ borderColor: 'gray',
8
+ borderLeftWidth: 2,
9
+ borderStyle: 'dashed',
10
+ flexGrow: 1
11
+ },
12
+ wrapper: (firstStepPosition, lastStepHeight, stepperHeight) => ({
13
+ height: '100%',
14
+ paddingBottom: stepperHeight - lastStepHeight,
15
+ paddingLeft: OVAL_SIZE / 2 - 1,
16
+ paddingTop: OVAL_SIZE + 4 + firstStepPosition,
17
+ position: 'absolute',
18
+ width: 2
19
+ })
20
+ });
@@ -0,0 +1,114 @@
1
+ import React, { useState } from 'react';
2
+ import { View } from 'react-native';
3
+ import { bool, func } from 'prop-types';
4
+ import merge from 'lodash/merge';
5
+
6
+ import Label from '../../../Label';
7
+ import { StepPropTypes, VariantPropTypes } from '../../propTypes';
8
+ import Icon from '../../../Icon';
9
+ import { ERROR } from '../../constants';
10
+
11
+ import ownStyles, { ownVariantStyles } from './styles';
12
+
13
+ const ERROR_ICON_SIZE = 16;
14
+
15
+ const Step = ({
16
+ first,
17
+ isActive,
18
+ isCompleted,
19
+ last,
20
+ setFirstStepPosition,
21
+ setLastStepPosition,
22
+ step,
23
+ style = {},
24
+ variant
25
+ }) => {
26
+ const stepCompleted = isCompleted(step.id);
27
+ const stepActive = isActive(step.id);
28
+
29
+ const themedStyles = merge({}, ownStyles, ownVariantStyles[variant], style[variant]);
30
+
31
+ const [stepIconOffset, setStepIconOffset] = useState(0);
32
+
33
+ return (
34
+ <View
35
+ onLayout={e => {
36
+ if (first) setFirstStepPosition(e.nativeEvent.layout.y + stepIconOffset);
37
+ if (last) setLastStepPosition(e.nativeEvent.layout.y + stepIconOffset);
38
+ }}
39
+ style={[
40
+ themedStyles.outerContainer,
41
+ stepCompleted && themedStyles.completedOuterContainer,
42
+ stepActive && themedStyles.activeOuterContainer,
43
+ !first && ownStyles.stepMargin
44
+ ]}
45
+ >
46
+ <View
47
+ onLayout={e => (first || last) && setStepIconOffset(e.nativeEvent.layout.y)}
48
+ style={[
49
+ themedStyles.container,
50
+ stepCompleted && themedStyles.completedContainer,
51
+ stepActive && themedStyles.activeContainer
52
+ ]}
53
+ >
54
+ <View
55
+ style={[
56
+ themedStyles.innerContainer,
57
+ stepCompleted && themedStyles.completedInnerContainer,
58
+ stepActive && themedStyles.activeInnerContainer
59
+ ]}
60
+ >
61
+ {stepActive && variant === ERROR && (
62
+ <Icon
63
+ color="white"
64
+ height={ERROR_ICON_SIZE}
65
+ name="close"
66
+ size={ERROR_ICON_SIZE}
67
+ style={themedStyles.icon}
68
+ type="antdesign"
69
+ width={ERROR_ICON_SIZE}
70
+ />
71
+ )}
72
+ </View>
73
+ </View>
74
+ <View style={themedStyles.textContainer}>
75
+ {step.title && (
76
+ <Label
77
+ color={
78
+ stepCompleted
79
+ ? themedStyles.completedTitleColor
80
+ : stepActive
81
+ ? themedStyles.activeTitleColor
82
+ : themedStyles.titleColor
83
+ }
84
+ >
85
+ {step.title}
86
+ </Label>
87
+ )}
88
+ {step.subtitle && (
89
+ <Label color={themedStyles.subtitleColor} style={themedStyles.subtitle}>
90
+ {step.subtitle}
91
+ </Label>
92
+ )}
93
+ {step.description && (
94
+ <Label color={themedStyles.descriptionColor} style={themedStyles.description}>
95
+ {step.description}
96
+ </Label>
97
+ )}
98
+ </View>
99
+ </View>
100
+ );
101
+ };
102
+
103
+ Step.propTypes = {
104
+ first: bool,
105
+ isActive: func,
106
+ isCompleted: func,
107
+ last: bool,
108
+ setFirstStepPosition: func,
109
+ setLastStepPosition: func,
110
+ step: StepPropTypes,
111
+ variant: VariantPropTypes
112
+ };
113
+
114
+ export default Step;
@@ -0,0 +1,80 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ import { OVAL_SIZE } from '../../styles';
4
+ import { ERROR, STANDARD } from '../../constants';
5
+
6
+ export const ownVariantStyles = {
7
+ [ERROR]: {
8
+ activeContainer: {
9
+ backgroundColor: 'red',
10
+ borderColor: 'red'
11
+ },
12
+ activeInnerContainer: {
13
+ backgroundColor: 'red'
14
+ },
15
+ activeTitleColor: 'red',
16
+ completedContainer: {
17
+ borderColor: '#F38595'
18
+ },
19
+ completedInnerContainer: {
20
+ backgroundColor: '#F38595'
21
+ },
22
+ completedTitleColor: '#F38595'
23
+ },
24
+ [STANDARD]: {
25
+ activeContainer: {
26
+ borderColor: 'blue'
27
+ },
28
+ activeInnerContainer: {
29
+ backgroundColor: 'blue'
30
+ },
31
+ activeTitleColor: 'blue',
32
+ completedContainer: {
33
+ borderColor: '#93ACFF'
34
+ },
35
+ completedInnerContainer: {
36
+ backgroundColor: '#93ACFF'
37
+ },
38
+ completedTitleColor: '#93ACFF'
39
+ }
40
+ };
41
+
42
+ export default StyleSheet.create({
43
+ container: {
44
+ alignItems: 'center',
45
+ backgroundColor: 'white',
46
+ borderColor: 'gray',
47
+ borderRadius: OVAL_SIZE / 2,
48
+ borderWidth: OVAL_SIZE / 10,
49
+ height: OVAL_SIZE,
50
+ justifyContent: 'center',
51
+ marginRight: 16,
52
+ width: OVAL_SIZE
53
+ },
54
+ description: {
55
+ marginTop: 4
56
+ },
57
+ descriptionColor: 'gray',
58
+ icon: {
59
+ position: 'absolute',
60
+ right: -3,
61
+ top: -3
62
+ },
63
+ innerContainer: {
64
+ backgroundColor: 'white',
65
+ borderRadius: OVAL_SIZE / 4,
66
+ height: OVAL_SIZE / 2,
67
+ width: OVAL_SIZE / 2
68
+ },
69
+ outerContainer: {
70
+ alignItems: 'center',
71
+ flexDirection: 'row'
72
+ },
73
+ stepMargin: { marginTop: 24 },
74
+ subtitle: {
75
+ marginTop: 4
76
+ },
77
+ textContainer: {
78
+ width: '90%'
79
+ }
80
+ });
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import { string } from 'prop-types';
4
+ import merge from 'lodash/merge';
5
+ import { ViewPropTypes } from 'deprecated-react-native-prop-types';
6
+
7
+ import Label from '../../../Label';
8
+
9
+ import ownStyles from './styles';
10
+
11
+ const SubStep = ({ style, subtitle, title }) => {
12
+ const themedStyles = merge({}, ownStyles, style);
13
+
14
+ return (
15
+ <View style={themedStyles.container}>
16
+ <Label color={themedStyles.titleColor}>{title}</Label>
17
+ <Label color={themedStyles.subtitleColor}>{subtitle}</Label>
18
+ </View>
19
+ );
20
+ };
21
+
22
+ SubStep.propTypes = {
23
+ style: ViewPropTypes.style,
24
+ subtitle: string,
25
+ title: string
26
+ };
27
+
28
+ export default SubStep;
@@ -0,0 +1,10 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export default StyleSheet.create({
4
+ container: {
5
+ marginLeft: 36,
6
+ marginTop: 8
7
+ },
8
+ subtitleColor: 'lightgray',
9
+ titleColor: 'gray'
10
+ });
@@ -0,0 +1,4 @@
1
+ export const CARD = 'card';
2
+ export const FLAT = 'flat';
3
+ export const STANDARD = 'standard';
4
+ export const ERROR = 'error';
@@ -0,0 +1,120 @@
1
+ import React, { Fragment, useState } from 'react';
2
+ import { View } from 'react-native';
3
+ import merge from 'lodash/merge';
4
+ import { number, string } from 'prop-types';
5
+ import Collapsible from 'react-native-collapsible';
6
+ import { ViewPropTypes } from 'deprecated-react-native-prop-types';
7
+
8
+ import Icon from '../Icon';
9
+ import Touchable from '../Touchable';
10
+ import Label from '../Label';
11
+ import { useTheme } from '../../theming';
12
+
13
+ import ownStyles, { DETAILS_ICON_CARD_SIZE, DETAILS_ICON_FLAT_SIZE, ownModeStyles } from './styles';
14
+ import Step from './components/Step';
15
+ import Connectors from './components/Connectors';
16
+ import SubStep from './components/SubStep';
17
+ import { DetailsTabPropTypes, ModePropTypes, StepsPropTypes, VariantPropTypes } from './propTypes';
18
+ import { CARD, FLAT, STANDARD } from './constants';
19
+
20
+ const UTTracker = ({ currentStep, detailsTab, mode = CARD, steps, style, title, variant = STANDARD }) => {
21
+ const theme = useTheme();
22
+ const themedStyles = merge({}, ownStyles, ownModeStyles[mode], theme.UTTracker, style);
23
+
24
+ const [stepperHeight, setStepperHeight] = useState(0);
25
+ const [lastStepPosition, setLastStepPosition] = useState(0);
26
+ const [firstStepPosition, setFirstStepPosition] = useState(0);
27
+
28
+ const [isCollapsed, setIsCollapsed] = useState(true);
29
+
30
+ const isCompleted = stepNumber => stepNumber < currentStep;
31
+ const isActive = stepNumber => stepNumber === currentStep;
32
+
33
+ return (
34
+ <View style={themedStyles.outerContainer}>
35
+ <View style={[themedStyles.container, !detailsTab && ownStyles.roundedBorders]}>
36
+ {title && (
37
+ <Touchable
38
+ disabled={mode !== FLAT}
39
+ onPress={() => setIsCollapsed(!isCollapsed)}
40
+ style={themedStyles.titleTouchable}
41
+ >
42
+ <View style={themedStyles.titleContainer}>
43
+ <Label color={themedStyles.titleColor} style={themedStyles.title}>
44
+ {title}
45
+ </Label>
46
+ {detailsTab?.enabled && mode === FLAT && (
47
+ <Icon
48
+ color={themedStyles.detailsIconColor || 'black'}
49
+ height={DETAILS_ICON_FLAT_SIZE}
50
+ name={isCollapsed ? 'chevron-down' : 'chevron-up'}
51
+ size={DETAILS_ICON_FLAT_SIZE}
52
+ type="feather"
53
+ width={DETAILS_ICON_FLAT_SIZE}
54
+ />
55
+ )}
56
+ </View>
57
+ </Touchable>
58
+ )}
59
+ {steps && (
60
+ <View onLayout={e => setStepperHeight(e.nativeEvent.layout.height)}>
61
+ <Connectors
62
+ style={themedStyles.connectors}
63
+ {...{ firstStepPosition, lastStepPosition, stepperHeight }}
64
+ />
65
+ {steps.map((step, index) => (
66
+ <Fragment key={step.id}>
67
+ <Step
68
+ first={index === 0}
69
+ last={index === steps.length - 1}
70
+ style={themedStyles.step}
71
+ {...{
72
+ isActive,
73
+ isCompleted,
74
+ setFirstStepPosition,
75
+ setLastStepPosition,
76
+ step,
77
+ variant
78
+ }}
79
+ />
80
+ <Collapsible collapsed={isCollapsed}>
81
+ {step.subSteps &&
82
+ step.subSteps.map(subStep => (
83
+ <SubStep key={subStep.id} {...subStep} style={themedStyles.subStep} />
84
+ ))}
85
+ </Collapsible>
86
+ </Fragment>
87
+ ))}
88
+ </View>
89
+ )}
90
+ </View>
91
+ {detailsTab?.enabled && mode === CARD && (
92
+ <Touchable onPress={() => setIsCollapsed(!isCollapsed)} style={themedStyles.detailsTab}>
93
+ <Fragment>
94
+ <Label>{detailsTab.title}</Label>
95
+ <Icon
96
+ color={themedStyles.detailsIconColor || 'black'}
97
+ height={DETAILS_ICON_CARD_SIZE}
98
+ name={isCollapsed ? 'chevron-down' : 'chevron-up'}
99
+ size={DETAILS_ICON_CARD_SIZE}
100
+ type="feather"
101
+ width={DETAILS_ICON_CARD_SIZE}
102
+ />
103
+ </Fragment>
104
+ </Touchable>
105
+ )}
106
+ </View>
107
+ );
108
+ };
109
+
110
+ UTTracker.propTypes = {
111
+ currentStep: number,
112
+ detailsTab: DetailsTabPropTypes,
113
+ mode: ModePropTypes,
114
+ steps: StepsPropTypes,
115
+ style: ViewPropTypes.style,
116
+ title: string,
117
+ variant: VariantPropTypes
118
+ };
119
+
120
+ export default UTTracker;
@@ -0,0 +1,19 @@
1
+ import { arrayOf, bool, number, oneOf, shape, string } from 'prop-types';
2
+
3
+ import { CARD, ERROR, FLAT, STANDARD } from './constants';
4
+
5
+ export const SubStepsPropTypes = arrayOf(shape({ id: number, subtitle: string, title: string }));
6
+
7
+ export const StepPropTypes = shape({
8
+ id: number,
9
+ subSteps: arrayOf(SubStepsPropTypes),
10
+ subtitle: string,
11
+ title: string
12
+ });
13
+
14
+ export const StepsPropTypes = arrayOf(StepPropTypes);
15
+
16
+ export const DetailsTabPropTypes = shape({ enabled: bool, title: string });
17
+
18
+ export const ModePropTypes = oneOf([CARD, FLAT]);
19
+ export const VariantPropTypes = oneOf([STANDARD, ERROR]);
@@ -0,0 +1,57 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ import { CARD, FLAT } from './constants';
4
+
5
+ export const OVAL_SIZE = 20;
6
+ export const DETAILS_ICON_CARD_SIZE = 20;
7
+ export const DETAILS_ICON_FLAT_SIZE = 24;
8
+
9
+ export const ownModeStyles = {
10
+ [CARD]: {
11
+ container: {
12
+ backgroundColor: 'white',
13
+ elevation: 2,
14
+ shadowColor: 'black',
15
+ shadowOffset: {
16
+ height: 3,
17
+ width: 0
18
+ },
19
+ shadowOpacity: 0.1
20
+ }
21
+ },
22
+ [FLAT]: {
23
+ container: {
24
+ backgroundColor: 'transparent'
25
+ }
26
+ }
27
+ };
28
+
29
+ export default StyleSheet.create({
30
+ container: {
31
+ borderTopLeftRadius: 8,
32
+ borderTopRightRadius: 8,
33
+ padding: 16
34
+ },
35
+ detailsTab: {
36
+ backgroundColor: 'white',
37
+ borderBottomLeftRadius: 8,
38
+ borderBottomRightRadius: 8,
39
+ borderColor: 'lightgray',
40
+ borderTopWidth: 1,
41
+ flexDirection: 'row',
42
+ justifyContent: 'space-between',
43
+ paddingHorizontal: 16,
44
+ paddingVertical: 12
45
+ },
46
+ roundedBorders: {
47
+ borderBottomLeftRadius: 8,
48
+ borderBottomRightRadius: 8
49
+ },
50
+ titleContainer: {
51
+ flexDirection: 'row',
52
+ justifyContent: 'space-between'
53
+ },
54
+ titleTouchable: {
55
+ marginBottom: 24
56
+ }
57
+ });
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect } from 'react';
2
- import { View, ScrollView } from 'react-native';
2
+ import { SafeAreaView, ScrollView } from 'react-native';
3
3
  import { number, func, shape, bool, string, element } from 'prop-types';
4
4
  import { merge } from 'lodash';
5
5
 
@@ -40,7 +40,7 @@ const UTWorkflowContainer = ({
40
40
  const themedStyles = merge({}, ownStyles, theme?.UTWorkflowContainer, style);
41
41
 
42
42
  return (
43
- <View style={themedStyles.container}>
43
+ <SafeAreaView style={themedStyles.container}>
44
44
  {topbar && <UTTopbar {...{ currentStage, currentStep, stages, stepsCount, theme, topbar }} />}
45
45
  <ScrollView contentContainerStyle={themedStyles.content}>
46
46
  {title && (
@@ -70,7 +70,7 @@ const UTWorkflowContainer = ({
70
70
  }}
71
71
  />
72
72
  )}
73
- </View>
73
+ </SafeAreaView>
74
74
  );
75
75
  };
76
76
  UTWorkflowContainer.propTypes = {
package/lib/index.js CHANGED
@@ -51,6 +51,7 @@ export { default as UTTextInput } from './components/UTTextInput';
51
51
  export { default as UTSelect } from './components/UTSelect';
52
52
  export { default as UTStepFeedback } from './components/UTStepFeedback';
53
53
  export { default as UTAutocomplete } from './components/UTAutocomplete';
54
+ export { default as UTTracker } from './components/UTTracker';
54
55
  export { default as ImageRadio } from './components/ImageRadio';
55
56
  // Loading
56
57
  export { default as Loading } from './components/Loading';
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.7.0",
5
+ "version": "1.8.1",
6
6
  "repository": "https://github.com/widergy/mobile-ui.git",
7
7
  "main": "lib/index.js",
8
8
  "files": [
@@ -43,7 +43,8 @@
43
43
  "pdf-lib": "^1.17.1",
44
44
  "react-native-markdown-display": "^7.0.0-alpha.2",
45
45
  "react-native-modal": "^13.0.1",
46
- "react-native-pager-view": "^6.2.1"
46
+ "react-native-pager-view": "^6.2.1",
47
+ "react-native-collapsible": "^1.6.1"
47
48
  },
48
49
  "devDependencies": {
49
50
  "@babel/cli": "^7.22.10",