react-native-ui-lib 7.36.0-snapshot.6096 → 7.36.0-snapshot.6098

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 (28) hide show
  1. package/package.json +1 -1
  2. package/src/components/hint/index.d.ts +186 -17
  3. package/src/components/hint/index.js +403 -150
  4. package/src/components/tabController/TabBar.js +2 -1
  5. package/src/components/tabController/TabBarContext.d.ts +1 -0
  6. package/src/components/tabController/TabBarItem.js +5 -2
  7. package/src/components/tabController/index.js +4 -1
  8. package/src/components/view/View.driver.new.js +1 -2
  9. package/src/components/hint/Hint.driver.new.d.ts +0 -19
  10. package/src/components/hint/Hint.driver.new.js +0 -19
  11. package/src/components/hint/HintAnchor.d.ts +0 -13
  12. package/src/components/hint/HintAnchor.js +0 -47
  13. package/src/components/hint/HintBubble.d.ts +0 -12
  14. package/src/components/hint/HintBubble.js +0 -64
  15. package/src/components/hint/HintMockChildren.d.ts +0 -8
  16. package/src/components/hint/HintMockChildren.js +0 -49
  17. package/src/components/hint/HintOld.d.ts +0 -196
  18. package/src/components/hint/HintOld.js +0 -549
  19. package/src/components/hint/hooks/useHintAccessibility.d.ts +0 -10
  20. package/src/components/hint/hooks/useHintAccessibility.js +0 -26
  21. package/src/components/hint/hooks/useHintLayout.d.ts +0 -13
  22. package/src/components/hint/hooks/useHintLayout.js +0 -66
  23. package/src/components/hint/hooks/useHintPosition.d.ts +0 -29
  24. package/src/components/hint/hooks/useHintPosition.js +0 -119
  25. package/src/components/hint/hooks/useHintVisibility.d.ts +0 -6
  26. package/src/components/hint/hooks/useHintVisibility.js +0 -26
  27. package/src/components/hint/types.d.ts +0 -106
  28. package/src/components/hint/types.js +0 -11
@@ -1,133 +1,324 @@
1
- import React, { isValidElement, useMemo, useCallback, useRef, useEffect } from 'react';
2
- import { Animated, StyleSheet, TouchableWithoutFeedback } from 'react-native';
1
+ import _isUndefined from "lodash/isUndefined";
2
+ import _isEqual from "lodash/isEqual";
3
+ import _isString from "lodash/isString";
4
+ import React, { Component, isValidElement } from 'react';
5
+ import { Animated, StyleSheet, AccessibilityInfo, findNodeHandle, TouchableWithoutFeedback } from 'react-native';
3
6
  import { Typography, Spacings, Colors, BorderRadiuses, Shadows } from "../../style";
4
7
  import { Constants, asBaseComponent } from "../../commons/new";
5
8
  import View from "../view";
9
+ import Text from "../text";
6
10
  import Image from "../image";
7
11
  import Modal from "../modal";
8
12
  import TouchableOpacity from "../touchableOpacity";
9
- import { HintPositions, HintProps, TargetAlignments } from "./types";
10
- import useHintVisibility from "./hooks/useHintVisibility";
11
- import useHintLayout from "./hooks/useHintLayout";
12
- import useHintAccessibility from "./hooks/useHintAccessibility";
13
- import useHintPosition from "./hooks/useHintPosition";
14
- import HintMockChildren from "./HintMockChildren";
15
- import HintAnchor from "./HintAnchor";
16
- import HintBubble from "./HintBubble";
17
13
  const sideTip = require("./assets/hintTipSide.png");
18
14
  const middleTip = require("./assets/hintTipMiddle.png");
19
15
  const DEFAULT_COLOR = Colors.$backgroundPrimaryHeavy;
20
16
  const DEFAULT_HINT_OFFSET = Spacings.s4;
21
17
  const DEFAULT_EDGE_MARGINS = Spacings.s5;
22
- const HINT_MIN_WIDTH = 68;
23
- const Hint = props => {
24
- const {
25
- visible,
26
- useModal,
27
- position,
28
- children,
29
- message,
30
- containerWidth = Constants.windowWidth,
31
- offset = DEFAULT_HINT_OFFSET,
32
- edgeMargins = DEFAULT_EDGE_MARGINS,
33
- targetFrame,
34
- useSideTip,
35
- onPress,
36
- onBackgroundPress,
37
- backdropColor,
38
- color = DEFAULT_COLOR,
39
- icon,
40
- iconStyle,
41
- messageStyle,
42
- removePaddings,
43
- enableShadow,
44
- borderRadius,
45
- customContent,
46
- testID
47
- } = props;
48
- const hintRef = useRef(null);
49
- const isUsingModal = Boolean(onBackgroundPress && useModal);
50
- const {
51
- hintUnmounted,
52
- visibilityProgress
53
- } = useHintVisibility(visible);
54
- const {
55
- targetLayoutState,
56
- targetLayoutInWindowState,
57
- hintMessageWidth,
58
- targetRef,
59
- onTargetLayout,
60
- setHintLayout
61
- } = useHintLayout({
62
- onBackgroundPress,
63
- targetFrame
64
- });
65
- const {
66
- focusAccessibilityOnHint,
67
- accessibilityInfo
68
- } = useHintAccessibility(message);
69
- useEffect(() => {
70
- if (targetRef.current && hintRef.current) {
71
- focusAccessibilityOnHint(targetRef.current, hintRef.current);
72
- }
73
- }, []);
74
- const targetLayout = useMemo(() => {
75
- return isUsingModal ? targetLayoutInWindowState : targetLayoutState;
76
- }, [isUsingModal, targetLayoutState, targetLayoutInWindowState]);
77
- const setTargetRef = useCallback(ref => {
78
- targetRef.current = ref;
79
- if (hintRef.current) {
80
- focusAccessibilityOnHint(targetRef.current, hintRef.current);
81
- }
82
- }, []);
83
- const showHint = !!targetLayout;
84
- const {
85
- targetAlignmentOnScreen,
86
- hintContainerLayout,
87
- tipPositionStyle,
88
- hintPadding,
89
- hintPositionStyle,
90
- targetScreenToRelativeOffset
91
- } = useHintPosition({
92
- isUsingModal,
93
- useSideTip,
94
- position,
95
- offset,
96
- targetLayoutState,
97
- targetLayoutInWindowState,
98
- containerWidth,
99
- edgeMargins,
100
- hintMessageWidth
101
- });
102
- const hintAnimatedStyle = useMemo(() => {
18
+ var TARGET_POSITIONS = /*#__PURE__*/function (TARGET_POSITIONS) {
19
+ TARGET_POSITIONS["LEFT"] = "left";
20
+ TARGET_POSITIONS["RIGHT"] = "right";
21
+ TARGET_POSITIONS["CENTER"] = "center";
22
+ return TARGET_POSITIONS;
23
+ }(TARGET_POSITIONS || {});
24
+ var HintPositions = /*#__PURE__*/function (HintPositions) {
25
+ HintPositions["TOP"] = "top";
26
+ HintPositions["BOTTOM"] = "bottom";
27
+ return HintPositions;
28
+ }(HintPositions || {}); // TODO: unify with FeatureHighlightFrame
29
+ /**
30
+ * @description: Hint component for displaying a tooltip over wrapped component
31
+ * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/HintsScreen.tsx
32
+ * @notes: You can either wrap a component or pass a specific targetFrame
33
+ * @gif: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Hint/Hint.gif?raw=true
34
+ */
35
+ class Hint extends Component {
36
+ static displayName = 'Hint';
37
+ static defaultProps = {
38
+ position: HintPositions.BOTTOM,
39
+ useModal: true
40
+ };
41
+ static positions = HintPositions;
42
+ targetRef = null;
43
+ hintRef = React.createRef();
44
+ animationDuration = 170;
45
+ state = {
46
+ targetLayoutInWindow: undefined,
47
+ targetLayout: this.props.targetFrame,
48
+ hintUnmounted: !this.props.visible
49
+ };
50
+ visibleAnimated = new Animated.Value(Number(!!this.props.visible));
51
+ componentDidMount() {
52
+ this.focusAccessibilityOnHint();
53
+ }
54
+ componentDidUpdate(prevProps) {
55
+ if (prevProps.visible !== this.props.visible) {
56
+ this.animateHint();
57
+ }
58
+ }
59
+ animateHint = () => {
60
+ Animated.timing(this.visibleAnimated, {
61
+ toValue: Number(!!this.props.visible),
62
+ duration: this.animationDuration,
63
+ useNativeDriver: true
64
+ }).start(this.toggleAnimationEndedToRemoveHint);
65
+ };
66
+ toggleAnimationEndedToRemoveHint = () => {
67
+ this.setState({
68
+ hintUnmounted: !this.props.visible
69
+ });
70
+ };
71
+ focusAccessibilityOnHint = () => {
72
+ const {
73
+ message
74
+ } = this.props;
75
+ const targetRefTag = findNodeHandle(this.targetRef);
76
+ const hintRefTag = findNodeHandle(this.hintRef.current);
77
+ if (targetRefTag && _isString(message)) {
78
+ AccessibilityInfo.setAccessibilityFocus(targetRefTag);
79
+ } else if (hintRefTag) {
80
+ AccessibilityInfo.setAccessibilityFocus(hintRefTag);
81
+ }
82
+ };
83
+ setTargetRef = ref => {
84
+ this.targetRef = ref;
85
+ this.focusAccessibilityOnHint();
86
+ };
87
+ onTargetLayout = ({
88
+ nativeEvent: {
89
+ layout
90
+ }
91
+ }) => {
92
+ if (!_isEqual(this.state.targetLayout, layout)) {
93
+ this.setState({
94
+ targetLayout: layout
95
+ });
96
+ }
97
+ if (!this.state.targetLayoutInWindow || this.props.onBackgroundPress) {
98
+ setTimeout(() => {
99
+ this.targetRef?.measureInWindow?.((x, y, width, height) => {
100
+ const targetLayoutInWindow = {
101
+ x,
102
+ y,
103
+ width,
104
+ height
105
+ };
106
+ this.setState({
107
+ targetLayoutInWindow
108
+ });
109
+ });
110
+ });
111
+ }
112
+ };
113
+ getAccessibilityInfo() {
114
+ const {
115
+ visible,
116
+ message
117
+ } = this.props;
118
+ if (visible && _isString(message)) {
119
+ return {
120
+ accessible: true,
121
+ accessibilityLabel: `hint: ${message}`
122
+ };
123
+ }
124
+ }
125
+ get containerWidth() {
126
+ const {
127
+ containerWidth = Constants.windowWidth
128
+ } = this.props;
129
+ return containerWidth;
130
+ }
131
+ get targetLayout() {
132
+ const {
133
+ onBackgroundPress,
134
+ useModal,
135
+ targetFrame
136
+ } = this.props;
137
+ const {
138
+ targetLayout,
139
+ targetLayoutInWindow
140
+ } = this.state;
141
+ if (targetFrame) {
142
+ return targetFrame;
143
+ }
144
+ return onBackgroundPress && useModal ? targetLayoutInWindow : targetLayout;
145
+ }
146
+ get showHint() {
147
+ return !!this.targetLayout;
148
+ }
149
+ get tipSize() {
150
+ return this.useSideTip ? {
151
+ width: 14,
152
+ height: 7
153
+ } : {
154
+ width: 20,
155
+ height: 7
156
+ };
157
+ }
158
+ get hintOffset() {
159
+ const {
160
+ offset = DEFAULT_HINT_OFFSET
161
+ } = this.props;
162
+ return offset;
163
+ }
164
+ get edgeMargins() {
165
+ const {
166
+ edgeMargins = DEFAULT_EDGE_MARGINS
167
+ } = this.props;
168
+ return edgeMargins;
169
+ }
170
+ get useSideTip() {
171
+ const {
172
+ useSideTip
173
+ } = this.props;
174
+ if (!_isUndefined(useSideTip)) {
175
+ return useSideTip;
176
+ }
177
+ return this.getTargetPositionOnScreen() !== TARGET_POSITIONS.CENTER;
178
+ }
179
+ getTargetPositionOnScreen() {
180
+ if (this.targetLayout?.x !== undefined && this.targetLayout?.width) {
181
+ const targetMidPosition = this.targetLayout.x + this.targetLayout.width / 2;
182
+ if (targetMidPosition > this.containerWidth * (2 / 3)) {
183
+ return TARGET_POSITIONS.RIGHT;
184
+ } else if (targetMidPosition < this.containerWidth * (1 / 3)) {
185
+ return TARGET_POSITIONS.LEFT;
186
+ }
187
+ }
188
+ return TARGET_POSITIONS.CENTER;
189
+ }
190
+ getContainerPosition() {
191
+ if (this.targetLayout) {
192
+ return {
193
+ top: this.targetLayout.y,
194
+ left: this.targetLayout.x
195
+ };
196
+ }
197
+ }
198
+ getHintPosition() {
199
+ const {
200
+ position
201
+ } = this.props;
202
+ const hintPositionStyle = {
203
+ alignItems: 'center'
204
+ };
205
+ if (this.targetLayout?.x !== undefined) {
206
+ hintPositionStyle.left = -this.targetLayout.x;
207
+ }
208
+ if (position === HintPositions.TOP) {
209
+ hintPositionStyle.bottom = 0;
210
+ } else if (this.targetLayout?.height) {
211
+ hintPositionStyle.top = this.targetLayout.height;
212
+ }
213
+ const targetPositionOnScreen = this.getTargetPositionOnScreen();
214
+ if (targetPositionOnScreen === TARGET_POSITIONS.RIGHT) {
215
+ hintPositionStyle.alignItems = Constants.isRTL ? 'flex-start' : 'flex-end';
216
+ } else if (targetPositionOnScreen === TARGET_POSITIONS.LEFT) {
217
+ hintPositionStyle.alignItems = Constants.isRTL ? 'flex-end' : 'flex-start';
218
+ }
219
+ return hintPositionStyle;
220
+ }
221
+ getHintPadding() {
222
+ const paddings = {
223
+ paddingVertical: this.hintOffset,
224
+ paddingHorizontal: this.edgeMargins
225
+ };
226
+ if (this.useSideTip && this.targetLayout?.x !== undefined) {
227
+ const targetPositionOnScreen = this.getTargetPositionOnScreen();
228
+ if (targetPositionOnScreen === TARGET_POSITIONS.LEFT) {
229
+ paddings.paddingLeft = this.targetLayout.x;
230
+ } else if (targetPositionOnScreen === TARGET_POSITIONS.RIGHT && this.targetLayout?.width) {
231
+ paddings.paddingRight = this.containerWidth - this.targetLayout.x - this.targetLayout.width;
232
+ }
233
+ }
234
+ return paddings;
235
+ }
236
+ getHintAnimatedStyle = () => {
237
+ const {
238
+ position
239
+ } = this.props;
103
240
  const translateY = position === HintPositions.TOP ? -10 : 10;
104
241
  return {
105
- opacity: visibilityProgress,
242
+ opacity: this.visibleAnimated,
106
243
  transform: [{
107
- translateY: visibilityProgress.interpolate({
244
+ translateY: this.visibleAnimated.interpolate({
108
245
  inputRange: [0, 1],
109
246
  outputRange: [translateY, 0]
110
247
  })
111
248
  }]
112
249
  };
113
- }, [position, visibilityProgress]);
114
- const renderOverlay = () => {
115
- if (targetLayoutInWindowState && targetScreenToRelativeOffset?.top !== undefined && targetScreenToRelativeOffset?.left !== undefined) {
250
+ };
251
+ getTipPosition() {
252
+ const {
253
+ position
254
+ } = this.props;
255
+ const tipPositionStyle = {};
256
+ if (position === HintPositions.TOP) {
257
+ tipPositionStyle.bottom = this.hintOffset - this.tipSize.height;
258
+ !this.useSideTip ? tipPositionStyle.bottom += 1 : undefined;
259
+ } else {
260
+ tipPositionStyle.top = this.hintOffset - this.tipSize.height;
261
+ }
262
+ const layoutWidth = this.targetLayout?.width || 0;
263
+ if (this.targetLayout?.x !== undefined) {
264
+ const targetMidWidth = layoutWidth / 2;
265
+ const tipMidWidth = this.tipSize.width / 2;
266
+ const leftPosition = this.useSideTip ? this.targetLayout.x : this.targetLayout.x + targetMidWidth - tipMidWidth;
267
+ const rightPosition = this.useSideTip ? this.containerWidth - this.targetLayout.x - layoutWidth : this.containerWidth - this.targetLayout.x - targetMidWidth - tipMidWidth;
268
+ const targetPositionOnScreen = this.getTargetPositionOnScreen();
269
+ switch (targetPositionOnScreen) {
270
+ case TARGET_POSITIONS.LEFT:
271
+ tipPositionStyle.left = Constants.isRTL ? rightPosition : leftPosition;
272
+ break;
273
+ case TARGET_POSITIONS.RIGHT:
274
+ tipPositionStyle.right = Constants.isRTL ? leftPosition : rightPosition;
275
+ break;
276
+ case TARGET_POSITIONS.CENTER:
277
+ default:
278
+ tipPositionStyle.left = this.targetLayout.x + targetMidWidth - tipMidWidth;
279
+ break;
280
+ }
281
+ }
282
+ return tipPositionStyle;
283
+ }
284
+ isUsingModal = () => {
285
+ const {
286
+ onBackgroundPress,
287
+ useModal
288
+ } = this.props;
289
+ return onBackgroundPress && useModal;
290
+ };
291
+ renderOverlay() {
292
+ const {
293
+ targetLayoutInWindow
294
+ } = this.state;
295
+ const {
296
+ onBackgroundPress,
297
+ backdropColor,
298
+ testID
299
+ } = this.props;
300
+ if (targetLayoutInWindow) {
301
+ const containerPosition = this.getContainerPosition();
116
302
  return <Animated.View style={[styles.overlay, {
117
- ...targetScreenToRelativeOffset,
303
+ top: containerPosition.top - targetLayoutInWindow.y,
304
+ left: containerPosition.left - targetLayoutInWindow.x,
118
305
  backgroundColor: backdropColor,
119
- opacity: visibilityProgress
306
+ opacity: this.visibleAnimated
120
307
  }]} pointerEvents="box-none" testID={`${testID}.overlay`}>
121
308
  {onBackgroundPress && <TouchableWithoutFeedback style={StyleSheet.absoluteFillObject} onPress={onBackgroundPress}>
122
309
  <View flex />
123
310
  </TouchableWithoutFeedback>}
124
311
  </Animated.View>;
125
312
  }
126
- };
127
- const renderHintTip = () => {
128
- const source = useSideTip ? sideTip : middleTip;
313
+ }
314
+ renderHintTip() {
315
+ const {
316
+ position,
317
+ color = DEFAULT_COLOR
318
+ } = this.props;
319
+ const source = this.useSideTip ? sideTip : middleTip;
129
320
  const flipVertically = position === HintPositions.TOP;
130
- const flipHorizontally = targetAlignmentOnScreen === TargetAlignments.RIGHT;
321
+ const flipHorizontally = this.getTargetPositionOnScreen() === TARGET_POSITIONS.RIGHT;
131
322
  const flipStyle = {
132
323
  transform: [{
133
324
  scaleY: flipVertically ? -1 : 1
@@ -135,49 +326,121 @@ const Hint = props => {
135
326
  scaleX: flipHorizontally ? -1 : 1
136
327
  }]
137
328
  };
138
- return <Image tintColor={color} source={source} style={[styles.hintTip, tipPositionStyle, flipStyle]} />;
139
- };
140
- const renderHint = () => {
141
- return <HintBubble visible={visible} message={message} messageStyle={messageStyle} color={color} removePaddings={removePaddings} enableShadow={enableShadow} borderRadius={borderRadius} icon={icon} iconStyle={iconStyle} customContent={customContent} testID={testID} hintRef={hintRef} setHintLayout={setHintLayout} hintPositionStyle={hintPositionStyle} />;
142
- };
143
- const renderHintAnchor = () => {
329
+ return <Image tintColor={color} source={source} style={[styles.hintTip, this.getTipPosition(), flipStyle]} />;
330
+ }
331
+ renderContent() {
332
+ const {
333
+ message,
334
+ messageStyle,
335
+ icon,
336
+ iconStyle,
337
+ borderRadius,
338
+ color = DEFAULT_COLOR,
339
+ customContent,
340
+ removePaddings,
341
+ enableShadow,
342
+ visible,
343
+ testID
344
+ } = this.props;
345
+ return <View testID={`${testID}.message`} row centerV style={[styles.hint, !removePaddings && styles.hintPaddings, visible && enableShadow && styles.containerShadow, {
346
+ backgroundColor: color
347
+ }, !_isUndefined(borderRadius) && {
348
+ borderRadius
349
+ }]} ref={this.hintRef}>
350
+ {customContent}
351
+ {!customContent && icon && <Image source={icon} style={[styles.icon, iconStyle]} />}
352
+ {!customContent && <Text recorderTag={'unmask'} style={[styles.hintMessage, messageStyle]} testID={`${testID}.message.text`}>
353
+ {message}
354
+ </Text>}
355
+ </View>;
356
+ }
357
+ renderHint() {
358
+ const {
359
+ onPress,
360
+ testID
361
+ } = this.props;
144
362
  const opacity = onPress ? 0.9 : 1.0;
145
- return <HintAnchor {...props} showHint={showHint} containerWidth={containerWidth} hintContainerLayout={hintContainerLayout} hintPadding={hintPadding} hintAnimatedStyle={hintAnimatedStyle} isUsingModal={isUsingModal} targetLayout={targetLayout}>
146
- <TouchableOpacity activeOpacity={opacity} onPress={onPress}>
147
- {renderHint()}
148
- </TouchableOpacity>
149
- {renderHintTip()}
150
- </HintAnchor>;
151
- };
152
- const renderMockChildren = () => {
153
- return <HintMockChildren children={children} backdropColor={backdropColor} targetLayout={targetLayout} />;
154
- };
155
- const renderChildren = () => {
156
- if (!targetFrame && isValidElement(children)) {
157
- return React.cloneElement(children, {
363
+ if (this.showHint) {
364
+ return <View animated style={[{
365
+ width: this.containerWidth
366
+ }, styles.animatedContainer, this.getHintPosition(), this.getHintPadding(), this.getHintAnimatedStyle()]} pointerEvents="box-none" testID={testID}>
367
+ <TouchableOpacity activeOpacity={opacity} onPress={onPress}>
368
+ {this.renderContent()}
369
+ </TouchableOpacity>
370
+ {this.renderHintTip()}
371
+ </View>;
372
+ }
373
+ }
374
+ renderHintContainer() {
375
+ const {
376
+ style,
377
+ ...others
378
+ } = this.props;
379
+ return <View {...others}
380
+ // this view must be collapsable, don't pass testID or backgroundColor etc'.
381
+ collapsable testID={undefined} style={[styles.container, style, this.getContainerPosition(), !this.isUsingModal() && styles.overlayContainer]}>
382
+ {this.renderHint()}
383
+ </View>;
384
+ }
385
+ renderMockChildren() {
386
+ const {
387
+ children,
388
+ backdropColor
389
+ } = this.props;
390
+ const isBackdropColorPassed = backdropColor !== undefined;
391
+ if (children && React.isValidElement(children)) {
392
+ const layout = {
393
+ ...this.getContainerPosition(),
394
+ width: this.targetLayout?.width,
395
+ height: this.targetLayout?.height,
396
+ right: Constants.isRTL ? this.targetLayout?.x : undefined,
397
+ left: Constants.isRTL ? undefined : this.targetLayout?.x
398
+ };
399
+ return <View style={[styles.mockChildrenContainer, layout, !isBackdropColorPassed && styles.hidden]}>
400
+ {React.cloneElement(children, {
401
+ collapsable: false,
402
+ key: 'mock',
403
+ style: [children.props.style, styles.mockChildren]
404
+ })}
405
+ </View>;
406
+ }
407
+ }
408
+ renderChildren() {
409
+ const {
410
+ targetFrame
411
+ } = this.props;
412
+ if (!targetFrame && isValidElement(this.props.children)) {
413
+ return React.cloneElement(this.props.children, {
158
414
  key: 'clone',
159
415
  collapsable: false,
160
- onLayout: onTargetLayout,
161
- ref: setTargetRef,
162
- ...accessibilityInfo
416
+ onLayout: this.onTargetLayout,
417
+ ref: this.setTargetRef,
418
+ ...this.getAccessibilityInfo()
163
419
  });
164
420
  }
165
- };
166
- if (!visible && hintUnmounted) {
167
- return children || null;
168
- }
169
- return <>
170
- {renderChildren()}
171
- {isUsingModal ? <Modal visible={showHint} animationType={backdropColor ? 'fade' : 'none'} overlayBackgroundColor={backdropColor} transparent onBackgroundPress={onBackgroundPress} onRequestClose={onBackgroundPress} testID={`${testID}.modal`}>
172
- {renderMockChildren()}
173
- {renderHintAnchor()}
174
- </Modal> : <>
175
- {renderOverlay()}
176
- {renderMockChildren()}
177
- {renderHintAnchor()}
178
- </>}
179
- </>;
180
- };
421
+ }
422
+ render() {
423
+ const {
424
+ onBackgroundPress,
425
+ backdropColor,
426
+ testID
427
+ } = this.props;
428
+ if (!this.props.visible && this.state.hintUnmounted) {
429
+ return this.props.children || null;
430
+ }
431
+ return <>
432
+ {this.renderChildren()}
433
+ {this.isUsingModal() ? <Modal visible={this.showHint} animationType={backdropColor ? 'fade' : 'none'} overlayBackgroundColor={backdropColor} transparent onBackgroundPress={onBackgroundPress} onRequestClose={onBackgroundPress} testID={`${testID}.modal`}>
434
+ {this.renderMockChildren()}
435
+ {this.renderHintContainer()}
436
+ </Modal> : <>
437
+ {this.renderOverlay()}
438
+ {this.renderMockChildren()}
439
+ {this.renderHintContainer()}
440
+ </>}
441
+ </>;
442
+ }
443
+ }
181
444
  const styles = StyleSheet.create({
182
445
  container: {
183
446
  position: 'absolute'
@@ -217,7 +480,6 @@ const styles = StyleSheet.create({
217
480
  position: 'absolute'
218
481
  },
219
482
  hint: {
220
- minWidth: HINT_MIN_WIDTH,
221
483
  maxWidth: Math.min(Constants.windowWidth - 2 * Spacings.s4, 400),
222
484
  borderRadius: BorderRadiuses.br60,
223
485
  backgroundColor: DEFAULT_COLOR
@@ -240,13 +502,4 @@ const styles = StyleSheet.create({
240
502
  tintColor: Colors.white
241
503
  }
242
504
  });
243
- Hint.displayName = 'Hint';
244
- Hint.defaultProps = {
245
- position: HintPositions.BOTTOM,
246
- useModal: true
247
- };
248
- Hint.positions = HintPositions;
249
- export { HintProps, Hint };
250
-
251
- // @ts-expect-error
252
505
  export default asBaseComponent(Hint);
@@ -149,7 +149,8 @@ const TabBar = props => {
149
149
  }, [labelColor, selectedLabelColor]);
150
150
  return <View style={_containerStyle} key={key} bg-$backgroundElevated>
151
151
  <FadedScrollView ref={tabBar} horizontal showsHorizontalScrollIndicator={false} showStartFader startFaderProps={faderProps} showEndFader endFaderProps={faderProps} contentContainerStyle={scrollViewContainerStyle} testID={testID} onContentSizeChange={onContentSizeChange} onLayout={onLayout}>
152
- <View style={tabBarContainerStyle}>{tabBarItems}</View>
152
+ {/* TODO: Might need to change role to tablist, when upgrading rn77. It didn't work now (rn73) */}
153
+ <View style={tabBarContainerStyle} accessibilityRole="tabbar">{tabBarItems}</View>
153
154
  {itemsCount > 1 && <Reanimated.View style={[styles.selectedIndicator, indicatorStyle, _indicatorTransitionStyle]} />}
154
155
  </FadedScrollView>
155
156
  </View>;
@@ -13,6 +13,7 @@ interface TabControllerContext {
13
13
  /** transition page index (can be a fraction when transitioning between pages) */
14
14
  targetPage: Reanimated.SharedValue<number>;
15
15
  setCurrentIndex: (index: number) => void;
16
+ selectedIndex: number;
16
17
  }
17
18
  declare const TabBarContext: React.Context<TabControllerContext>;
18
19
  export default TabBarContext;
@@ -44,7 +44,8 @@ export default function TabBarItem({
44
44
  }) {
45
45
  const {
46
46
  currentPage,
47
- setCurrentIndex
47
+ setCurrentIndex,
48
+ selectedIndex
48
49
  } = useContext(TabBarContext);
49
50
  const itemRef = useRef();
50
51
  const itemWidth = useRef(props.width);
@@ -133,7 +134,9 @@ export default function TabBarItem({
133
134
  return <GestureDetector gesture={gesture}>
134
135
  <View reanimated
135
136
  // @ts-expect-error
136
- ref={itemRef} style={_style} onLayout={onLayout} testID={testID}>
137
+ ref={itemRef} style={_style} onLayout={onLayout} testID={testID} accessible accessibilityRole="tab" accessibilityState={{
138
+ selected: selectedIndex === index
139
+ }}>
137
140
  {leadingAccessory}
138
141
  {icon && <Reanimated.Image source={icon} style={[!_isUndefined(label) && styles.tabItemIconWithLabel, animatedIconStyle]} />}
139
142
  {!_isEmpty(label) && <Reanimated.Text {...labelProps} fsTagName={'unmask'} style={[styles.tabItemLabel, labelStyle, animatedLabelStyle, animatedLabelColorStyle]}>
@@ -38,6 +38,7 @@ const TabController = React.forwardRef((props, ref) => {
38
38
  children
39
39
  } = themeProps;
40
40
  const [screenWidth, setScreenWidth] = useState(getScreenWidth(useSafeArea));
41
+ const [selectedIndex, setSelectedIndex] = useState(initialIndex);
41
42
  if (items?.length < 2) {
42
43
  console.warn('TabController component expect a minimum of 2 items');
43
44
  }
@@ -61,6 +62,7 @@ const TabController = React.forwardRef((props, ref) => {
61
62
  'worklet';
62
63
 
63
64
  currentPage.value = index;
65
+ runOnJS(setSelectedIndex)(index);
64
66
  }, []);
65
67
  useEffect(() => {
66
68
  setCurrentIndex(initialIndex);
@@ -88,12 +90,13 @@ const TabController = React.forwardRef((props, ref) => {
88
90
  /* Animated Values */
89
91
  targetPage,
90
92
  currentPage,
93
+ selectedIndex,
91
94
  containerWidth: screenWidth,
92
95
  /* Callbacks */
93
96
  onChangeIndex,
94
97
  setCurrentIndex
95
98
  };
96
- }, [initialIndex, asCarousel, items, onChangeIndex, screenWidth, nestedInScrollView]);
99
+ }, [initialIndex, asCarousel, items, onChangeIndex, screenWidth, nestedInScrollView, selectedIndex]);
97
100
  return <TabBarContext.Provider value={context}>{children}</TabBarContext.Provider>;
98
101
  });
99
102