@widergy/mobile-ui 2.1.4 → 2.3.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 +14 -0
- package/lib/components/Carousel/README.md +2 -0
- package/lib/components/Carousel/components/CarouselComponent/index.js +165 -29
- package/lib/components/Carousel/components/CarouselComponent/utils.js +2 -0
- package/lib/components/Carousel/propTypes.js +4 -0
- package/lib/components/UTTabs/index.js +5 -3
- package/lib/components/UTTabs/styles.js +1 -0
- package/lib/components/UTWalkthroughStep/index.js +108 -0
- package/lib/components/UTWalkthroughStep/styles.js +41 -0
- package/lib/index.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [2.3.0](https://github.com/widergy/mobile-ui/compare/v2.2.0...v2.3.0) (2025-12-01)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* [UGGC-23] carousel autoplay ([#464](https://github.com/widergy/mobile-ui/issues/464)) ([5a81315](https://github.com/widergy/mobile-ui/commit/5a81315a79571e3b833e99b6a96f40ecf4684571))
|
|
7
|
+
|
|
8
|
+
# [2.2.0](https://github.com/widergy/mobile-ui/compare/v2.1.4...v2.2.0) (2025-11-28)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* [EVEP-52] onboarding component ([#467](https://github.com/widergy/mobile-ui/issues/467)) ([277ff19](https://github.com/widergy/mobile-ui/commit/277ff194b397ca08001d151a9dd52ec17a4e63df))
|
|
14
|
+
|
|
1
15
|
## [2.1.4](https://github.com/widergy/mobile-ui/compare/v2.1.3...v2.1.4) (2025-11-19)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -11,6 +11,8 @@ For example, if `contentHeight` and `contentWidth` are not set, the carousel wil
|
|
|
11
11
|
|
|
12
12
|
| NAME | TYPE | REQUIRED | DESCRIPTION | DEFAULT |
|
|
13
13
|
| --- | --- | --- | --- | --- |
|
|
14
|
+
| autoPlay | bool | No | Whether the carousel should automatically scroll to the next item. | * `false` |
|
|
15
|
+
| autoPlayDelay | number | No | The delay between each auto scroll. | * `3000` |
|
|
14
16
|
| ...[ScrollView props]('https://facebook.github.io/react-native/docs/scrollview#props') | ScrollViewProps | No | Excluded `alwaysBounceHorizontal`, `alwaysBounceVertical`, `automaticallyAdjustContentInsets`, `pagingEnabled`, `scrollEventThrottle` & `scrollsToTop`. | `ScrollView.defaultProps` |
|
|
15
17
|
| horizontal | bool | No* | The orientation of the carousel. | * `true` |
|
|
16
18
|
| contentHeight | number | No* | The `height` of each carousel item. Also the height of the carousel itself, if the prop `horizontal` is `true`. | * `0` |
|
|
@@ -6,23 +6,30 @@ import styles from '../../styles';
|
|
|
6
6
|
import propTypes, { carouselForcedProps } from '../../propTypes';
|
|
7
7
|
import { checkSizeProp, CAROUSEL_MOUNT_DELAY, EXTRA_CAROUSEL_ITEMS } from '../../constants';
|
|
8
8
|
|
|
9
|
+
import { validateIndex } from './utils';
|
|
10
|
+
|
|
9
11
|
class CarouselComponent extends Component {
|
|
10
12
|
constructor(props) {
|
|
11
13
|
super(props);
|
|
12
14
|
this.ref = createRef();
|
|
13
|
-
const { initialIndex
|
|
15
|
+
const { initialIndex, spacing, visibleEdge } = props;
|
|
14
16
|
checkSizeProp(spacing, 'spacing');
|
|
15
17
|
checkSizeProp(visibleEdge, 'visibleEdge');
|
|
16
18
|
this.state = {
|
|
17
|
-
|
|
19
|
+
autoPlayTimeout: null,
|
|
20
|
+
autoScrollRestartTimeout: null,
|
|
21
|
+
currentIndex: validateIndex(initialIndex, 0),
|
|
22
|
+
isAutoScrolling: false,
|
|
18
23
|
opacity: 0,
|
|
19
24
|
opacityTimeout: null,
|
|
25
|
+
resumeAutoPlayTimeout: null,
|
|
20
26
|
timeout: null
|
|
21
27
|
};
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
componentDidMount() {
|
|
25
|
-
const { initialIndex } = this.props;
|
|
31
|
+
const { initialIndex, autoPlay } = this.props;
|
|
32
|
+
const validInitialIndex = validateIndex(initialIndex, 0);
|
|
26
33
|
/**
|
|
27
34
|
* These timeouts are required to scroll to the initial index on mount.
|
|
28
35
|
* They are assigned to state variables because state is asynchronous.
|
|
@@ -30,23 +37,29 @@ class CarouselComponent extends Component {
|
|
|
30
37
|
* We can then cancel the timeouts in case the component unmounts before they were called.
|
|
31
38
|
*/
|
|
32
39
|
const timeout = setTimeout(() => {
|
|
33
|
-
this.scrollToIndex(
|
|
40
|
+
this.scrollToIndex(validInitialIndex);
|
|
41
|
+
if (autoPlay) {
|
|
42
|
+
this.startAutoPlay();
|
|
43
|
+
}
|
|
34
44
|
}, CAROUSEL_MOUNT_DELAY);
|
|
45
|
+
|
|
35
46
|
const opacityTimeout = setTimeout(() => this.setState({ opacity: 1 }), CAROUSEL_MOUNT_DELAY + 10);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
timeout
|
|
39
|
-
});
|
|
47
|
+
|
|
48
|
+
this.setState({ opacityTimeout, timeout });
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
componentWillUnmount() {
|
|
43
|
-
const { opacityTimeout, timeout } =
|
|
52
|
+
const { opacityTimeout, timeout, autoPlayTimeout, resumeAutoPlayTimeout, autoScrollRestartTimeout } =
|
|
53
|
+
this.state;
|
|
44
54
|
/**
|
|
45
55
|
* Here we cancel the timeouts in case they were not executed before the component was unmounted.
|
|
46
56
|
* This avoids memory leaks (because if we don't do it, it will set state to an unmounted component).
|
|
47
57
|
*/
|
|
48
58
|
clearTimeout(opacityTimeout);
|
|
49
59
|
clearTimeout(timeout);
|
|
60
|
+
clearTimeout(autoPlayTimeout);
|
|
61
|
+
clearTimeout(resumeAutoPlayTimeout);
|
|
62
|
+
clearTimeout(autoScrollRestartTimeout);
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
changeCurrentIndex = index => {
|
|
@@ -58,36 +71,145 @@ class CarouselComponent extends Component {
|
|
|
58
71
|
|
|
59
72
|
changeInternalIndex = index => {
|
|
60
73
|
const { currentIndex } = this.state;
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
const { items, loop } = this.props;
|
|
75
|
+
const validIndex = validateIndex(index, currentIndex);
|
|
76
|
+
|
|
77
|
+
if (validIndex !== currentIndex) {
|
|
78
|
+
this.setState({ currentIndex: validIndex }, () => {
|
|
79
|
+
let indexToReport = validIndex;
|
|
80
|
+
if (loop) {
|
|
81
|
+
indexToReport = ((validIndex % items.length) + items.length) % items.length;
|
|
82
|
+
}
|
|
83
|
+
this.changeCurrentIndex(indexToReport);
|
|
84
|
+
});
|
|
63
85
|
}
|
|
64
86
|
};
|
|
65
87
|
|
|
88
|
+
startAutoPlay = () => {
|
|
89
|
+
const { autoPlay, autoPlayDelay, items } = this.props;
|
|
90
|
+
if (!autoPlay || items.length <= 1) return;
|
|
91
|
+
|
|
92
|
+
this.clearAutoPlay();
|
|
93
|
+
const autoPlayTimeout = setTimeout(this.autoPlayNext, autoPlayDelay);
|
|
94
|
+
this.setState({ autoPlayTimeout });
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
clearAutoPlay = () => {
|
|
98
|
+
const { autoPlayTimeout } = this.state;
|
|
99
|
+
if (autoPlayTimeout) {
|
|
100
|
+
clearTimeout(autoPlayTimeout);
|
|
101
|
+
this.setState({ autoPlayTimeout: null });
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
autoPlayNext = () => {
|
|
106
|
+
const { currentIndex } = this.state;
|
|
107
|
+
const { items, loop } = this.props;
|
|
108
|
+
|
|
109
|
+
if (!items || items.length <= 1) return;
|
|
110
|
+
|
|
111
|
+
const nextIndex = loop
|
|
112
|
+
? (currentIndex + 1) % items.length
|
|
113
|
+
: currentIndex + 1 >= items.length
|
|
114
|
+
? 0
|
|
115
|
+
: currentIndex + 1;
|
|
116
|
+
|
|
117
|
+
this.setState({ isAutoScrolling: true });
|
|
118
|
+
this.scrollToIndex(nextIndex, true, false, true);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
pauseAutoPlay = () => {
|
|
122
|
+
const { autoPlay } = this.props;
|
|
123
|
+
if (autoPlay) {
|
|
124
|
+
this.clearAutoPlay();
|
|
125
|
+
const { resumeAutoPlayTimeout } = this.state;
|
|
126
|
+
if (resumeAutoPlayTimeout) {
|
|
127
|
+
clearTimeout(resumeAutoPlayTimeout);
|
|
128
|
+
this.setState({ resumeAutoPlayTimeout: null });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
resumeAutoPlay = (delay = 1000) => {
|
|
134
|
+
const { autoPlay } = this.props;
|
|
135
|
+
if (autoPlay) {
|
|
136
|
+
const { resumeAutoPlayTimeout } = this.state;
|
|
137
|
+
if (resumeAutoPlayTimeout) {
|
|
138
|
+
clearTimeout(resumeAutoPlayTimeout);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const newResumeTimeout = setTimeout(() => {
|
|
142
|
+
this.startAutoPlay();
|
|
143
|
+
this.setState({ resumeAutoPlayTimeout: null });
|
|
144
|
+
}, delay);
|
|
145
|
+
|
|
146
|
+
this.setState({ resumeAutoPlayTimeout: newResumeTimeout });
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
handleTouchStart = () => this.pauseAutoPlay();
|
|
151
|
+
|
|
152
|
+
handleTouchEnd = () => this.resumeAutoPlay();
|
|
153
|
+
|
|
66
154
|
handleScroll = ({ nativeEvent: { contentOffset } }) => {
|
|
67
155
|
const { horizontal, contentHeight, contentWidth, items, loop, spacing, visibleEdge } = this.props;
|
|
156
|
+
const { isAutoScrolling } = this.state;
|
|
68
157
|
const offset = horizontal ? contentOffset.x : contentOffset.y;
|
|
69
158
|
const size = (horizontal ? contentWidth : contentHeight) - visibleEdge * 2 - spacing;
|
|
70
159
|
const index = Math.round(offset / size);
|
|
71
160
|
const last = items.length - 1;
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
161
|
+
const isLast = index <= 1;
|
|
162
|
+
const isFirst = index >= last + 3;
|
|
163
|
+
|
|
164
|
+
if (!isAutoScrolling) {
|
|
165
|
+
this.pauseAutoPlay();
|
|
166
|
+
this.resumeAutoPlay(1500);
|
|
167
|
+
}
|
|
168
|
+
|
|
75
169
|
if (loop) {
|
|
76
|
-
if (isLast)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
170
|
+
if (isLast) {
|
|
171
|
+
this.scrollToIndex(last, false, false, true);
|
|
172
|
+
this.setState({ currentIndex: last }, () => {
|
|
173
|
+
this.changeCurrentIndex(last);
|
|
174
|
+
});
|
|
175
|
+
} else if (isFirst) {
|
|
176
|
+
this.scrollToIndex(0, false, false, true);
|
|
177
|
+
this.setState({ currentIndex: 0 }, () => {
|
|
178
|
+
this.changeCurrentIndex(0);
|
|
179
|
+
});
|
|
180
|
+
} else {
|
|
181
|
+
const actualIndex = index - EXTRA_CAROUSEL_ITEMS;
|
|
182
|
+
this.changeInternalIndex(actualIndex);
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
this.changeInternalIndex(index);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (isAutoScrolling) {
|
|
189
|
+
this.setState({ isAutoScrolling: false });
|
|
190
|
+
|
|
191
|
+
const { autoScrollRestartTimeout } = this.state;
|
|
192
|
+
if (autoScrollRestartTimeout) {
|
|
193
|
+
clearTimeout(autoScrollRestartTimeout);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const newAutoScrollRestartTimeout = setTimeout(() => {
|
|
197
|
+
this.startAutoPlay();
|
|
198
|
+
this.setState({ autoScrollRestartTimeout: null });
|
|
199
|
+
}, 100);
|
|
200
|
+
this.setState({ autoScrollRestartTimeout: newAutoScrollRestartTimeout });
|
|
201
|
+
}
|
|
80
202
|
};
|
|
81
203
|
|
|
82
204
|
renderCarouselItem = (item, index, array) => {
|
|
83
205
|
const {
|
|
84
206
|
contentHeight,
|
|
85
207
|
contentWidth,
|
|
208
|
+
horizontal,
|
|
209
|
+
itemStyle,
|
|
86
210
|
keyExtractor,
|
|
87
211
|
keyPrefix,
|
|
88
|
-
horizontal,
|
|
89
212
|
loop,
|
|
90
|
-
itemStyle,
|
|
91
213
|
renderItem,
|
|
92
214
|
spacing,
|
|
93
215
|
visibleEdge
|
|
@@ -102,11 +224,19 @@ class CarouselComponent extends Component {
|
|
|
102
224
|
visibleEdge
|
|
103
225
|
};
|
|
104
226
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
227
|
+
let key;
|
|
228
|
+
if (loop) {
|
|
229
|
+
if (index < EXTRA_CAROUSEL_ITEMS) {
|
|
230
|
+
key = `${keyPrefix}loop-start-${index}`;
|
|
231
|
+
} else if (index >= array.length - EXTRA_CAROUSEL_ITEMS) {
|
|
232
|
+
key = `${keyPrefix}loop-end-${index}`;
|
|
233
|
+
} else {
|
|
234
|
+
const originalIndex = index - EXTRA_CAROUSEL_ITEMS;
|
|
235
|
+
key = `${keyPrefix}${keyExtractor(item, originalIndex, array)}`;
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
key = `${keyPrefix}${keyExtractor(item, index, array)}`;
|
|
239
|
+
}
|
|
110
240
|
|
|
111
241
|
return (
|
|
112
242
|
<CarouselItem {...itemProps} key={key}>
|
|
@@ -124,19 +254,23 @@ class CarouselComponent extends Component {
|
|
|
124
254
|
return children.map(this.renderCarouselItem);
|
|
125
255
|
};
|
|
126
256
|
|
|
127
|
-
scrollToIndex = (index, animated = false, disableThreshold = false) => {
|
|
257
|
+
scrollToIndex = (index, animated = false, disableThreshold = false, skipOnChange = false) => {
|
|
128
258
|
if (this.ref && this.ref.current) {
|
|
129
|
-
this.changeCurrentIndex(index);
|
|
130
259
|
const { contentHeight, contentWidth, horizontal, items, loop, spacing, visibleEdge } = this.props;
|
|
131
260
|
if (index >= 0 - disableThreshold && index < items.length + disableThreshold) {
|
|
261
|
+
if (!skipOnChange) {
|
|
262
|
+
this.changeCurrentIndex(index);
|
|
263
|
+
}
|
|
132
264
|
const indexToScrollTo = loop ? index + EXTRA_CAROUSEL_ITEMS : index;
|
|
133
265
|
const scrollConfig = horizontal
|
|
134
266
|
? { x: indexToScrollTo * (contentWidth - visibleEdge * 2 - spacing) }
|
|
135
267
|
: { y: indexToScrollTo * (contentHeight - visibleEdge * 2 - spacing) };
|
|
136
268
|
this.ref.current.scrollTo({ ...scrollConfig, animated });
|
|
269
|
+
} else {
|
|
270
|
+
// eslint-disable-next-line no-console
|
|
271
|
+
console.warn('The index specified is not in the item list!', index, items.length);
|
|
137
272
|
}
|
|
138
|
-
|
|
139
|
-
} else console.warn('The index specified is not in the item list!');
|
|
273
|
+
}
|
|
140
274
|
};
|
|
141
275
|
|
|
142
276
|
// eslint-disable-next-line react/no-unused-class-component-methods
|
|
@@ -179,6 +313,8 @@ class CarouselComponent extends Component {
|
|
|
179
313
|
style={[styles.contentStyle, forcedStyles, { opacity }]}
|
|
180
314
|
horizontal={horizontal}
|
|
181
315
|
onScroll={this.handleScroll}
|
|
316
|
+
onTouchStart={this.handleTouchStart}
|
|
317
|
+
onTouchEnd={this.handleTouchEnd}
|
|
182
318
|
overScrollMode={loop ? 'never' : 'auto'}
|
|
183
319
|
pagingEnabled
|
|
184
320
|
ref={this.ref}
|
|
@@ -13,6 +13,8 @@ export const carouselForcedProps = {
|
|
|
13
13
|
|
|
14
14
|
export const carouselDefaultProps = {
|
|
15
15
|
...ScrollView.defaultProps,
|
|
16
|
+
autoPlay: false,
|
|
17
|
+
autoPlayDelay: 3000,
|
|
16
18
|
contentHeight: 0,
|
|
17
19
|
contentWidth: 0,
|
|
18
20
|
horizontal: true, // ScrollView prop
|
|
@@ -31,6 +33,8 @@ export const carouselDefaultProps = {
|
|
|
31
33
|
|
|
32
34
|
const propTypes = {
|
|
33
35
|
...ScrollView.propTypes,
|
|
36
|
+
autoPlay: bool,
|
|
37
|
+
autoPlayDelay: number,
|
|
34
38
|
contentHeight: number,
|
|
35
39
|
contentWidth: number,
|
|
36
40
|
initialIndex: number.isRequired,
|
|
@@ -140,7 +140,7 @@ const UTTabs = ({
|
|
|
140
140
|
onScroll={handleScroll}
|
|
141
141
|
scrollEventThrottle={16}
|
|
142
142
|
>
|
|
143
|
-
{tabs.map(({ badge, label, icon }, index) => {
|
|
143
|
+
{tabs.map(({ badge, label, icon, walkthrough }, index) => {
|
|
144
144
|
const selected = selectedTab === index;
|
|
145
145
|
const colorThemeToUse = selected ? colorTheme : 'gray';
|
|
146
146
|
return (
|
|
@@ -150,6 +150,7 @@ const UTTabs = ({
|
|
|
150
150
|
key={`tab-${label || icon}`}
|
|
151
151
|
onPress={() => setSelectedTab(index)}
|
|
152
152
|
style={styles.scrollableTab}
|
|
153
|
+
{...walkthrough}
|
|
153
154
|
>
|
|
154
155
|
{icon && <UTIcon colorTheme={colorThemeToUse} name={icon} />}
|
|
155
156
|
<UTLabel colorTheme={colorThemeToUse} shade="04" weight="medium" {...labelProps}>
|
|
@@ -162,7 +163,7 @@ const UTTabs = ({
|
|
|
162
163
|
</ScrollView>
|
|
163
164
|
) : (
|
|
164
165
|
<View style={styles.container}>
|
|
165
|
-
{tabs.map(({ badge, label, icon }, index) => {
|
|
166
|
+
{tabs.map(({ badge, label, icon, walkthrough, walkthroughStyle }, index) => {
|
|
166
167
|
const selected = selectedTab === index;
|
|
167
168
|
const colorThemeToUse = selected ? colorTheme : 'gray';
|
|
168
169
|
return (
|
|
@@ -170,7 +171,8 @@ const UTTabs = ({
|
|
|
170
171
|
disabled={selected}
|
|
171
172
|
key={`tab-${label || icon}`}
|
|
172
173
|
onPress={() => setSelectedTab(index)}
|
|
173
|
-
style={styles.tabContainer(tabs.length, index)}
|
|
174
|
+
style={() => [styles.tabContainer(tabs.length, index)(), ...(walkthroughStyle || [])]}
|
|
175
|
+
{...walkthrough}
|
|
174
176
|
>
|
|
175
177
|
<View style={styles.tabContent(selected)}>
|
|
176
178
|
{icon && <UTIcon colorTheme={colorThemeToUse} name={icon} />}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { func, object, array } from 'prop-types';
|
|
4
|
+
|
|
5
|
+
import UTLabel from '../UTLabel';
|
|
6
|
+
import UTButton from '../UTButton';
|
|
7
|
+
|
|
8
|
+
import ownStyles from './styles';
|
|
9
|
+
|
|
10
|
+
const UTWalkthroughStep = ({
|
|
11
|
+
buttonTexts,
|
|
12
|
+
containerStyle,
|
|
13
|
+
colors,
|
|
14
|
+
handleFinishWalkthrough,
|
|
15
|
+
next,
|
|
16
|
+
previous,
|
|
17
|
+
step,
|
|
18
|
+
stepsConfig,
|
|
19
|
+
walkthroughStepTestIds
|
|
20
|
+
}) => {
|
|
21
|
+
const styles = ownStyles(colors);
|
|
22
|
+
const stepNumber = stepsConfig.findIndex(s => s.identifier === step.identifier) + 1;
|
|
23
|
+
|
|
24
|
+
const currentStep = stepsConfig.find(stepConfig => stepConfig.identifier === step.identifier);
|
|
25
|
+
const isFirstStep = currentStep.identifier === stepsConfig[0].identifier;
|
|
26
|
+
const isLastStep = currentStep.identifier === stepsConfig[stepsConfig.length - 1].identifier;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<View style={[styles.container, containerStyle]}>
|
|
30
|
+
<View style={styles.titleContainer}>
|
|
31
|
+
<UTLabel
|
|
32
|
+
colorTheme="light"
|
|
33
|
+
dataTestId={walkthroughStepTestIds.title}
|
|
34
|
+
style={styles.grow}
|
|
35
|
+
weight="medium"
|
|
36
|
+
withMarkdown
|
|
37
|
+
>
|
|
38
|
+
{currentStep.title}
|
|
39
|
+
</UTLabel>
|
|
40
|
+
<View style={styles.buttons}>
|
|
41
|
+
<UTButton
|
|
42
|
+
colorTheme="negative"
|
|
43
|
+
dataTestId={walkthroughStepTestIds.closeButton}
|
|
44
|
+
Icon="IconX"
|
|
45
|
+
onPress={handleFinishWalkthrough}
|
|
46
|
+
size="small"
|
|
47
|
+
variant="text"
|
|
48
|
+
/>
|
|
49
|
+
</View>
|
|
50
|
+
</View>
|
|
51
|
+
<View style={styles.content}>
|
|
52
|
+
<UTLabel
|
|
53
|
+
colorTheme="light"
|
|
54
|
+
dataTestId={walkthroughStepTestIds.description}
|
|
55
|
+
variant="small"
|
|
56
|
+
withMarkdown
|
|
57
|
+
>
|
|
58
|
+
{currentStep.description}
|
|
59
|
+
</UTLabel>
|
|
60
|
+
</View>
|
|
61
|
+
<View style={styles.footer}>
|
|
62
|
+
<View style={styles.page}>
|
|
63
|
+
<UTLabel colorTheme="light" variant="small" withMarkdown>
|
|
64
|
+
{`${stepNumber} de ${stepsConfig.length}`}
|
|
65
|
+
</UTLabel>
|
|
66
|
+
</View>
|
|
67
|
+
<View style={styles.buttons}>
|
|
68
|
+
{!isFirstStep && (
|
|
69
|
+
<UTButton
|
|
70
|
+
colorTheme="negative"
|
|
71
|
+
dataTestId={walkthroughStepTestIds.previousButton}
|
|
72
|
+
onPress={previous}
|
|
73
|
+
size="small"
|
|
74
|
+
variant="text"
|
|
75
|
+
weight="medium"
|
|
76
|
+
>
|
|
77
|
+
{buttonTexts.previous}
|
|
78
|
+
</UTButton>
|
|
79
|
+
)}
|
|
80
|
+
<UTButton
|
|
81
|
+
colorTheme="negative"
|
|
82
|
+
dataTestId={walkthroughStepTestIds.nextButton}
|
|
83
|
+
onPress={isLastStep ? handleFinishWalkthrough : next}
|
|
84
|
+
size="small"
|
|
85
|
+
variant="filled"
|
|
86
|
+
weight="medium"
|
|
87
|
+
>
|
|
88
|
+
{isLastStep ? buttonTexts.finish : isFirstStep ? buttonTexts.start : buttonTexts.next}
|
|
89
|
+
</UTButton>
|
|
90
|
+
</View>
|
|
91
|
+
</View>
|
|
92
|
+
</View>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
UTWalkthroughStep.propTypes = {
|
|
97
|
+
buttonTexts: object,
|
|
98
|
+
colors: object,
|
|
99
|
+
containerStyle: object,
|
|
100
|
+
handleFinishWalkthrough: func,
|
|
101
|
+
next: func,
|
|
102
|
+
previous: func,
|
|
103
|
+
step: object,
|
|
104
|
+
stepsConfig: array,
|
|
105
|
+
walkthroughStepTestIds: object
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default UTWalkthroughStep;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export default StyleSheet.create(colors => {
|
|
4
|
+
return {
|
|
5
|
+
container: {
|
|
6
|
+
borderRadius: 4,
|
|
7
|
+
backgroundColor: colors.dark04,
|
|
8
|
+
display: 'flex',
|
|
9
|
+
width: 296,
|
|
10
|
+
padding: 16,
|
|
11
|
+
position: 'absolute'
|
|
12
|
+
},
|
|
13
|
+
titleContainer: {
|
|
14
|
+
flexDirection: 'row',
|
|
15
|
+
justifyContent: 'space-between',
|
|
16
|
+
alignItems: 'flex-start'
|
|
17
|
+
},
|
|
18
|
+
grow: {
|
|
19
|
+
flexShrink: 1,
|
|
20
|
+
flexGrow: 1,
|
|
21
|
+
marginRight: 8
|
|
22
|
+
},
|
|
23
|
+
buttons: {
|
|
24
|
+
flexDirection: 'row',
|
|
25
|
+
gap: 8
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
content: {
|
|
29
|
+
marginBottom: 8
|
|
30
|
+
},
|
|
31
|
+
footer: {
|
|
32
|
+
flexDirection: 'row',
|
|
33
|
+
justifyContent: 'space-between',
|
|
34
|
+
marginTop: 8,
|
|
35
|
+
alignItems: 'flex-end'
|
|
36
|
+
},
|
|
37
|
+
page: {
|
|
38
|
+
alignItems: 'center'
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
});
|
package/lib/index.js
CHANGED
|
@@ -77,6 +77,7 @@ export { default as UTTooltip } from './components/UTTooltip';
|
|
|
77
77
|
export { default as UTTracker } from './components/UTTracker';
|
|
78
78
|
export { default as UTValidation } from './components/UTValidation';
|
|
79
79
|
export { default as UTWorkflowContainer } from './components/UTWorkflowContainer';
|
|
80
|
+
export { default as UTWalkthroughStep } from './components/UTWalkthroughStep';
|
|
80
81
|
|
|
81
82
|
// Theming
|
|
82
83
|
export * from './theming';
|
package/package.json
CHANGED