react-native-ui-lib 7.36.0 → 7.37.0-snapshot.6102

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 (29) hide show
  1. package/README.md +7 -0
  2. package/package.json +2 -2
  3. package/src/components/floatingButton/index.js +0 -1
  4. package/src/components/hint/Hint.driver.new.d.ts +19 -0
  5. package/src/components/hint/Hint.driver.new.js +19 -0
  6. package/src/components/hint/HintAnchor.d.ts +13 -0
  7. package/src/components/hint/HintAnchor.js +47 -0
  8. package/src/components/hint/HintBubble.d.ts +12 -0
  9. package/src/components/hint/HintBubble.js +64 -0
  10. package/src/components/hint/HintMockChildren.d.ts +8 -0
  11. package/src/components/hint/HintMockChildren.js +49 -0
  12. package/src/components/hint/HintOld.d.ts +196 -0
  13. package/src/components/hint/HintOld.js +549 -0
  14. package/src/components/hint/hooks/useHintAccessibility.d.ts +10 -0
  15. package/src/components/hint/hooks/useHintAccessibility.js +26 -0
  16. package/src/components/hint/hooks/useHintLayout.d.ts +13 -0
  17. package/src/components/hint/hooks/useHintLayout.js +66 -0
  18. package/src/components/hint/hooks/useHintPosition.d.ts +29 -0
  19. package/src/components/hint/hooks/useHintPosition.js +119 -0
  20. package/src/components/hint/hooks/useHintVisibility.d.ts +6 -0
  21. package/src/components/hint/hooks/useHintVisibility.js +26 -0
  22. package/src/components/hint/index.d.ts +17 -186
  23. package/src/components/hint/index.js +150 -403
  24. package/src/components/hint/types.d.ts +106 -0
  25. package/src/components/hint/types.js +11 -0
  26. package/src/components/picker/PickerItemsList.js +12 -3
  27. package/src/components/view/View.driver.new.js +2 -1
  28. package/src/incubator/expandableOverlay/index.d.ts +1 -0
  29. package/src/incubator/expandableOverlay/index.js +17 -5
@@ -0,0 +1,549 @@
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';
6
+ import { Typography, Spacings, Colors, BorderRadiuses, Shadows } from "../../style";
7
+ import { Constants, asBaseComponent } from "../../commons/new";
8
+ import View from "../view";
9
+ import Text from "../text";
10
+ import Image from "../image";
11
+ import Modal from "../modal";
12
+ import TouchableOpacity from "../touchableOpacity";
13
+ const sideTip = require("./assets/hintTipSide.png");
14
+ const middleTip = require("./assets/hintTipMiddle.png");
15
+ const DEFAULT_COLOR = Colors.$backgroundPrimaryHeavy;
16
+ const DEFAULT_HINT_OFFSET = Spacings.s4;
17
+ const DEFAULT_EDGE_MARGINS = Spacings.s5;
18
+ const HINT_MIN_WIDTH = 68;
19
+ var TARGET_POSITIONS = /*#__PURE__*/function (TARGET_POSITIONS) {
20
+ TARGET_POSITIONS["LEFT"] = "left";
21
+ TARGET_POSITIONS["RIGHT"] = "right";
22
+ TARGET_POSITIONS["CENTER"] = "center";
23
+ return TARGET_POSITIONS;
24
+ }(TARGET_POSITIONS || {});
25
+ var HintPositions = /*#__PURE__*/function (HintPositions) {
26
+ HintPositions["TOP"] = "top";
27
+ HintPositions["BOTTOM"] = "bottom";
28
+ return HintPositions;
29
+ }(HintPositions || {}); // TODO: unify with FeatureHighlightFrame
30
+ /**
31
+ * @description: Hint component for displaying a tooltip over wrapped component
32
+ * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/HintsScreen.tsx
33
+ * @notes: You can either wrap a component or pass a specific targetFrame
34
+ * @gif: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Hint/Hint.gif?raw=true
35
+ */
36
+ class Hint extends Component {
37
+ static displayName = 'Hint';
38
+ static defaultProps = {
39
+ position: HintPositions.BOTTOM,
40
+ useModal: true
41
+ };
42
+ static positions = HintPositions;
43
+ targetRef = null;
44
+ hintRef = React.createRef();
45
+ animationDuration = 170;
46
+ state = {
47
+ targetLayoutInWindow: undefined,
48
+ targetLayout: this.props.targetFrame,
49
+ hintUnmounted: !this.props.visible,
50
+ hintMessageWidth: undefined
51
+ };
52
+ visibleAnimated = new Animated.Value(Number(!!this.props.visible));
53
+ componentDidMount() {
54
+ this.focusAccessibilityOnHint();
55
+ }
56
+ componentDidUpdate(prevProps) {
57
+ if (prevProps.visible !== this.props.visible) {
58
+ this.animateHint();
59
+ }
60
+ }
61
+ animateHint = () => {
62
+ Animated.timing(this.visibleAnimated, {
63
+ toValue: Number(!!this.props.visible),
64
+ duration: this.animationDuration,
65
+ useNativeDriver: true
66
+ }).start(this.toggleAnimationEndedToRemoveHint);
67
+ };
68
+ toggleAnimationEndedToRemoveHint = () => {
69
+ this.setState({
70
+ hintUnmounted: !this.props.visible
71
+ });
72
+ };
73
+ focusAccessibilityOnHint = () => {
74
+ const {
75
+ message
76
+ } = this.props;
77
+ const targetRefTag = findNodeHandle(this.targetRef);
78
+ const hintRefTag = findNodeHandle(this.hintRef.current);
79
+ if (targetRefTag && _isString(message)) {
80
+ AccessibilityInfo.setAccessibilityFocus(targetRefTag);
81
+ } else if (hintRefTag) {
82
+ AccessibilityInfo.setAccessibilityFocus(hintRefTag);
83
+ }
84
+ };
85
+ setTargetRef = ref => {
86
+ this.targetRef = ref;
87
+ this.focusAccessibilityOnHint();
88
+ };
89
+ setHintLayout = ({
90
+ nativeEvent: {
91
+ layout
92
+ }
93
+ }) => {
94
+ if (!this.state.hintMessageWidth) {
95
+ this.setState({
96
+ hintMessageWidth: layout.width
97
+ });
98
+ }
99
+ };
100
+ onTargetLayout = ({
101
+ nativeEvent: {
102
+ layout
103
+ }
104
+ }) => {
105
+ if (!_isEqual(this.state.targetLayout, layout)) {
106
+ this.setState({
107
+ targetLayout: layout
108
+ });
109
+ }
110
+ if (!this.state.targetLayoutInWindow || this.props.onBackgroundPress) {
111
+ setTimeout(() => {
112
+ this.targetRef?.measureInWindow?.((x, y, width, height) => {
113
+ const targetLayoutInWindow = {
114
+ x,
115
+ y,
116
+ width,
117
+ height
118
+ };
119
+ this.setState({
120
+ targetLayoutInWindow
121
+ });
122
+ });
123
+ });
124
+ }
125
+ };
126
+ getAccessibilityInfo() {
127
+ const {
128
+ visible,
129
+ message
130
+ } = this.props;
131
+ if (visible && _isString(message)) {
132
+ return {
133
+ accessible: true,
134
+ accessibilityLabel: `hint: ${message}`
135
+ };
136
+ }
137
+ }
138
+ get containerWidth() {
139
+ const {
140
+ containerWidth = Constants.windowWidth
141
+ } = this.props;
142
+ return containerWidth;
143
+ }
144
+ get targetLayout() {
145
+ const {
146
+ onBackgroundPress,
147
+ useModal,
148
+ targetFrame
149
+ } = this.props;
150
+ const {
151
+ targetLayout,
152
+ targetLayoutInWindow
153
+ } = this.state;
154
+ if (targetFrame) {
155
+ return targetFrame;
156
+ }
157
+ return onBackgroundPress && useModal ? targetLayoutInWindow : targetLayout;
158
+ }
159
+ get showHint() {
160
+ return !!this.targetLayout;
161
+ }
162
+ get tipSize() {
163
+ return this.shouldUseSideTip ? {
164
+ width: 14,
165
+ height: 7
166
+ } : {
167
+ width: 20,
168
+ height: 7
169
+ };
170
+ }
171
+ get hintOffset() {
172
+ const {
173
+ offset = DEFAULT_HINT_OFFSET
174
+ } = this.props;
175
+ return offset;
176
+ }
177
+ get edgeMargins() {
178
+ const {
179
+ edgeMargins = this.isUsingModal() ? DEFAULT_EDGE_MARGINS : 0
180
+ } = this.props;
181
+ return edgeMargins;
182
+ }
183
+ get shouldUseSideTip() {
184
+ const {
185
+ useSideTip
186
+ } = this.props;
187
+ if (!_isUndefined(useSideTip)) {
188
+ return useSideTip;
189
+ }
190
+ return this.getTargetPositionOnScreen() !== TARGET_POSITIONS.CENTER;
191
+ }
192
+ get isShortMessage() {
193
+ const {
194
+ hintMessageWidth
195
+ } = this.state;
196
+ return hintMessageWidth && hintMessageWidth < Constants.screenWidth / 2;
197
+ }
198
+ getTargetPositionOnScreen() {
199
+ if (this.targetLayout?.x !== undefined && this.targetLayout?.width) {
200
+ const targetMidPosition = this.targetLayout.x + this.targetLayout.width / 2;
201
+ if (targetMidPosition > this.containerWidth * (4 / 5)) {
202
+ return TARGET_POSITIONS.RIGHT;
203
+ } else if (targetMidPosition < this.containerWidth * (1 / 5)) {
204
+ return TARGET_POSITIONS.LEFT;
205
+ }
206
+ }
207
+ return TARGET_POSITIONS.CENTER;
208
+ }
209
+ getContainerPosition() {
210
+ if (this.targetLayout) {
211
+ return {
212
+ top: this.targetLayout.y,
213
+ left: this.targetLayout.x
214
+ };
215
+ }
216
+ }
217
+ getHintPosition() {
218
+ const {
219
+ position
220
+ } = this.props;
221
+ const hintPositionStyle = {
222
+ alignItems: 'center'
223
+ };
224
+ if (this.targetLayout?.x !== undefined) {
225
+ hintPositionStyle.left = -this.targetLayout.x;
226
+ }
227
+ if (position === HintPositions.TOP) {
228
+ hintPositionStyle.bottom = 0;
229
+ } else if (this.targetLayout?.height) {
230
+ hintPositionStyle.top = this.targetLayout.height;
231
+ }
232
+ const targetPositionOnScreen = this.getTargetPositionOnScreen();
233
+ if (targetPositionOnScreen === TARGET_POSITIONS.RIGHT) {
234
+ hintPositionStyle.alignItems = Constants.isRTL ? 'flex-start' : 'flex-end';
235
+ } else if (targetPositionOnScreen === TARGET_POSITIONS.LEFT) {
236
+ hintPositionStyle.alignItems = Constants.isRTL ? 'flex-end' : 'flex-start';
237
+ }
238
+ return hintPositionStyle;
239
+ }
240
+ getHintPadding() {
241
+ const paddings = {
242
+ paddingVertical: this.hintOffset,
243
+ paddingHorizontal: this.edgeMargins
244
+ };
245
+ if (this.shouldUseSideTip && this.targetLayout?.x !== undefined) {
246
+ const targetPositionOnScreen = this.getTargetPositionOnScreen();
247
+ if (targetPositionOnScreen === TARGET_POSITIONS.LEFT) {
248
+ paddings.paddingLeft = this.targetLayout.x;
249
+ } else if (targetPositionOnScreen === TARGET_POSITIONS.RIGHT && this.targetLayout?.width) {
250
+ paddings.paddingRight = this.containerWidth - this.targetLayout.x - this.targetLayout.width;
251
+ }
252
+ }
253
+ return paddings;
254
+ }
255
+ getHintAnimatedStyle = () => {
256
+ const {
257
+ position
258
+ } = this.props;
259
+ const translateY = position === HintPositions.TOP ? -10 : 10;
260
+ return {
261
+ opacity: this.visibleAnimated,
262
+ transform: [{
263
+ translateY: this.visibleAnimated.interpolate({
264
+ inputRange: [0, 1],
265
+ outputRange: [translateY, 0]
266
+ })
267
+ }]
268
+ };
269
+ };
270
+ getTipPosition() {
271
+ const {
272
+ position
273
+ } = this.props;
274
+ const tipPositionStyle = {};
275
+ if (position === HintPositions.TOP) {
276
+ tipPositionStyle.bottom = this.hintOffset - this.tipSize.height;
277
+ !this.shouldUseSideTip ? tipPositionStyle.bottom += 1 : undefined;
278
+ } else {
279
+ tipPositionStyle.top = this.hintOffset - this.tipSize.height;
280
+ }
281
+ const layoutWidth = this.targetLayout?.width || 0;
282
+ if (this.targetLayout?.x !== undefined) {
283
+ const targetMidWidth = layoutWidth / 2;
284
+ const tipMidWidth = this.tipSize.width / 2;
285
+ const leftPosition = this.shouldUseSideTip ? this.targetLayout.x : this.targetLayout.x + targetMidWidth - tipMidWidth;
286
+ const rightPosition = this.shouldUseSideTip ? this.containerWidth - this.targetLayout.x - layoutWidth : this.containerWidth - this.targetLayout.x - targetMidWidth - tipMidWidth;
287
+ const targetPositionOnScreen = this.getTargetPositionOnScreen();
288
+ switch (targetPositionOnScreen) {
289
+ case TARGET_POSITIONS.LEFT:
290
+ tipPositionStyle.left = Constants.isRTL ? rightPosition : leftPosition;
291
+ break;
292
+ case TARGET_POSITIONS.RIGHT:
293
+ tipPositionStyle.right = Constants.isRTL ? leftPosition : rightPosition;
294
+ break;
295
+ case TARGET_POSITIONS.CENTER:
296
+ default:
297
+ tipPositionStyle.left = this.targetLayout.x + targetMidWidth - tipMidWidth;
298
+ break;
299
+ }
300
+ }
301
+ return tipPositionStyle;
302
+ }
303
+ getHintOffsetForShortMessage = () => {
304
+ const {
305
+ hintMessageWidth = 0
306
+ } = this.state;
307
+ let hintMessageOffset = 0;
308
+ if (this.isShortMessage) {
309
+ const targetPosition = this.getTipPosition();
310
+ if (targetPosition?.right) {
311
+ hintMessageOffset = -targetPosition?.right + hintMessageWidth / 2;
312
+ }
313
+ if (targetPosition?.left) {
314
+ hintMessageOffset = targetPosition?.left;
315
+ if (this.getTargetPositionOnScreen() === TARGET_POSITIONS.CENTER) {
316
+ hintMessageOffset -= Constants.screenWidth / 2;
317
+ } else {
318
+ hintMessageOffset -= hintMessageWidth / 2;
319
+ }
320
+ }
321
+ }
322
+ return hintMessageOffset;
323
+ };
324
+ isUsingModal = () => {
325
+ const {
326
+ onBackgroundPress,
327
+ useModal
328
+ } = this.props;
329
+ return onBackgroundPress && useModal;
330
+ };
331
+ renderOverlay() {
332
+ const {
333
+ targetLayoutInWindow
334
+ } = this.state;
335
+ const {
336
+ onBackgroundPress,
337
+ backdropColor,
338
+ testID
339
+ } = this.props;
340
+ if (targetLayoutInWindow) {
341
+ const containerPosition = this.getContainerPosition();
342
+ return <Animated.View style={[styles.overlay, {
343
+ top: containerPosition.top - targetLayoutInWindow.y,
344
+ left: containerPosition.left - targetLayoutInWindow.x,
345
+ backgroundColor: backdropColor,
346
+ opacity: this.visibleAnimated
347
+ }]} pointerEvents="box-none" testID={`${testID}.overlay`}>
348
+ {onBackgroundPress && <TouchableWithoutFeedback style={StyleSheet.absoluteFillObject} onPress={onBackgroundPress}>
349
+ <View flex />
350
+ </TouchableWithoutFeedback>}
351
+ </Animated.View>;
352
+ }
353
+ }
354
+ renderHintTip() {
355
+ const {
356
+ position,
357
+ color = DEFAULT_COLOR
358
+ } = this.props;
359
+ const source = this.shouldUseSideTip ? sideTip : middleTip;
360
+ const flipVertically = position === HintPositions.TOP;
361
+ const flipHorizontally = this.getTargetPositionOnScreen() === TARGET_POSITIONS.RIGHT;
362
+ const flipStyle = {
363
+ transform: [{
364
+ scaleY: flipVertically ? -1 : 1
365
+ }, {
366
+ scaleX: flipHorizontally ? -1 : 1
367
+ }]
368
+ };
369
+ return <Image tintColor={color} source={source} style={[styles.hintTip, this.getTipPosition(), flipStyle]} />;
370
+ }
371
+ renderHint() {
372
+ const {
373
+ message,
374
+ messageStyle,
375
+ icon,
376
+ iconStyle,
377
+ borderRadius,
378
+ color = DEFAULT_COLOR,
379
+ customContent,
380
+ removePaddings,
381
+ enableShadow,
382
+ visible,
383
+ testID
384
+ } = this.props;
385
+ const hintMessageOffset = this.getHintOffsetForShortMessage();
386
+ return <View testID={`${testID}.message`} row centerV centerH={!!hintMessageOffset} style={[styles.hint, !removePaddings && styles.hintPaddings, visible && enableShadow && styles.containerShadow, {
387
+ backgroundColor: color
388
+ }, !_isUndefined(borderRadius) && {
389
+ borderRadius
390
+ }, hintMessageOffset ? {
391
+ left: hintMessageOffset
392
+ } : undefined]} onLayout={this.setHintLayout} ref={this.hintRef}>
393
+ {customContent}
394
+ {!customContent && icon && <Image source={icon} style={[styles.icon, iconStyle]} />}
395
+ {!customContent && <Text recorderTag={'unmask'} style={[styles.hintMessage, messageStyle]} testID={`${testID}.message.text`}>
396
+ {message}
397
+ </Text>}
398
+ </View>;
399
+ }
400
+ renderHintContainer() {
401
+ const {
402
+ onPress,
403
+ testID
404
+ } = this.props;
405
+ const opacity = onPress ? 0.9 : 1.0;
406
+ if (this.showHint) {
407
+ return <View animated style={[{
408
+ width: this.containerWidth
409
+ }, styles.animatedContainer, this.getHintPosition(), this.getHintPadding(), this.getHintAnimatedStyle()]} pointerEvents="box-none" testID={testID}>
410
+ <TouchableOpacity activeOpacity={opacity} onPress={onPress}>
411
+ {this.renderHint()}
412
+ </TouchableOpacity>
413
+ {this.renderHintTip()}
414
+ </View>;
415
+ }
416
+ }
417
+ renderHintAnchor() {
418
+ const {
419
+ style,
420
+ ...others
421
+ } = this.props;
422
+ return <View {...others}
423
+ // this view must be collapsable, don't pass testID or backgroundColor etc'.
424
+ collapsable testID={undefined} style={[styles.container, style, this.getContainerPosition(), !this.isUsingModal() && styles.overlayContainer]}>
425
+ {this.renderHintContainer()}
426
+ </View>;
427
+ }
428
+ renderMockChildren() {
429
+ const {
430
+ children,
431
+ backdropColor
432
+ } = this.props;
433
+ const isBackdropColorPassed = backdropColor !== undefined;
434
+ if (children && React.isValidElement(children)) {
435
+ const layout = {
436
+ ...this.getContainerPosition(),
437
+ width: this.targetLayout?.width,
438
+ height: this.targetLayout?.height,
439
+ right: Constants.isRTL ? this.targetLayout?.x : undefined,
440
+ left: Constants.isRTL ? undefined : this.targetLayout?.x
441
+ };
442
+ return <View style={[styles.mockChildrenContainer, layout, !isBackdropColorPassed && styles.hidden]}>
443
+ {React.cloneElement(children, {
444
+ collapsable: false,
445
+ key: 'mock',
446
+ style: [children.props.style, styles.mockChildren]
447
+ })}
448
+ </View>;
449
+ }
450
+ }
451
+ renderChildren() {
452
+ const {
453
+ targetFrame
454
+ } = this.props;
455
+ if (!targetFrame && isValidElement(this.props.children)) {
456
+ return React.cloneElement(this.props.children, {
457
+ key: 'clone',
458
+ collapsable: false,
459
+ onLayout: this.onTargetLayout,
460
+ ref: this.setTargetRef,
461
+ ...this.getAccessibilityInfo()
462
+ });
463
+ }
464
+ }
465
+ render() {
466
+ const {
467
+ onBackgroundPress,
468
+ backdropColor,
469
+ testID
470
+ } = this.props;
471
+ if (!this.props.visible && this.state.hintUnmounted) {
472
+ return this.props.children || null;
473
+ }
474
+ return <>
475
+ {this.renderChildren()}
476
+ {this.isUsingModal() ? <Modal visible={this.showHint} animationType={backdropColor ? 'fade' : 'none'} overlayBackgroundColor={backdropColor} transparent onBackgroundPress={onBackgroundPress} onRequestClose={onBackgroundPress} testID={`${testID}.modal`}>
477
+ {this.renderMockChildren()}
478
+ {this.renderHintAnchor()}
479
+ </Modal> : <>
480
+ {this.renderOverlay()}
481
+ {this.renderMockChildren()}
482
+ {this.renderHintAnchor()}
483
+ </>}
484
+ </>;
485
+ }
486
+ }
487
+ const styles = StyleSheet.create({
488
+ container: {
489
+ position: 'absolute'
490
+ },
491
+ hidden: {
492
+ opacity: 0
493
+ },
494
+ overlayContainer: {
495
+ zIndex: 10,
496
+ elevation: 10
497
+ },
498
+ mockChildrenContainer: {
499
+ position: 'absolute'
500
+ },
501
+ mockChildren: {
502
+ margin: undefined,
503
+ marginVertical: undefined,
504
+ marginHorizontal: undefined,
505
+ marginTop: undefined,
506
+ marginRight: undefined,
507
+ marginBottom: undefined,
508
+ marginLeft: undefined,
509
+ top: undefined,
510
+ left: undefined,
511
+ right: undefined,
512
+ bottom: undefined
513
+ },
514
+ overlay: {
515
+ position: 'absolute',
516
+ width: Constants.windowWidth,
517
+ height: Constants.screenHeight
518
+ },
519
+ animatedContainer: {
520
+ position: 'absolute'
521
+ },
522
+ hintTip: {
523
+ position: 'absolute'
524
+ },
525
+ hint: {
526
+ minWidth: HINT_MIN_WIDTH,
527
+ maxWidth: Math.min(Constants.windowWidth - 2 * Spacings.s4, 400),
528
+ borderRadius: BorderRadiuses.br60,
529
+ backgroundColor: DEFAULT_COLOR
530
+ },
531
+ hintPaddings: {
532
+ paddingHorizontal: Spacings.s5,
533
+ paddingTop: Spacings.s3,
534
+ paddingBottom: Spacings.s4
535
+ },
536
+ containerShadow: {
537
+ ...Shadows.sh30.bottom
538
+ },
539
+ hintMessage: {
540
+ ...Typography.text70,
541
+ color: Colors.white,
542
+ flexShrink: 1
543
+ },
544
+ icon: {
545
+ marginRight: Spacings.s4,
546
+ tintColor: Colors.white
547
+ }
548
+ });
549
+ export default asBaseComponent(Hint);
@@ -0,0 +1,10 @@
1
+ import { ElementRef } from 'react';
2
+ import { View as RNView } from 'react-native';
3
+ import { HintProps } from '../types';
4
+ export default function useHintAccessibility(message?: HintProps['message']): {
5
+ focusAccessibilityOnHint: (targetRef: ElementRef<typeof RNView>, hintRef: ElementRef<typeof RNView>) => void;
6
+ accessibilityInfo: {
7
+ accessible: boolean;
8
+ accessibilityLabel: string;
9
+ } | undefined;
10
+ };
@@ -0,0 +1,26 @@
1
+ import _isString from "lodash/isString";
2
+ import { useCallback, useMemo } from 'react';
3
+ import { AccessibilityInfo, findNodeHandle } from 'react-native';
4
+ export default function useHintAccessibility(message) {
5
+ const focusAccessibilityOnHint = useCallback((targetRef, hintRef) => {
6
+ const targetRefTag = findNodeHandle(targetRef);
7
+ const hintRefTag = findNodeHandle(hintRef);
8
+ if (targetRefTag && _isString(message)) {
9
+ AccessibilityInfo.setAccessibilityFocus(targetRefTag);
10
+ } else if (hintRefTag) {
11
+ AccessibilityInfo.setAccessibilityFocus(hintRefTag);
12
+ }
13
+ }, [message]);
14
+ const accessibilityInfo = useMemo(() => {
15
+ if (_isString(message)) {
16
+ return {
17
+ accessible: true,
18
+ accessibilityLabel: `hint: ${message}`
19
+ };
20
+ }
21
+ }, [message]);
22
+ return {
23
+ focusAccessibilityOnHint,
24
+ accessibilityInfo
25
+ };
26
+ }
@@ -0,0 +1,13 @@
1
+ /// <reference types="react" />
2
+ import type { LayoutChangeEvent, LayoutRectangle, View as RNView } from 'react-native';
3
+ import { HintProps } from '../types';
4
+ type UseHintLayoutProps = Pick<HintProps, 'onBackgroundPress' | 'targetFrame'>;
5
+ export default function useHintLayout({ onBackgroundPress, targetFrame }: UseHintLayoutProps): {
6
+ targetLayoutState: LayoutRectangle | undefined;
7
+ targetLayoutInWindowState: LayoutRectangle | undefined;
8
+ hintMessageWidth: number | undefined;
9
+ targetRef: import("react").MutableRefObject<RNView | null>;
10
+ onTargetLayout: ({ nativeEvent: { layout } }: LayoutChangeEvent) => void;
11
+ setHintLayout: ({ nativeEvent: { layout } }: LayoutChangeEvent) => void;
12
+ };
13
+ export {};
@@ -0,0 +1,66 @@
1
+ import _isEqual from "lodash/isEqual";
2
+ import { useState, useCallback, useRef, useEffect } from 'react';
3
+ import { SafeAreaInsetsManager } from 'uilib-native';
4
+ import { Constants } from "../../../commons/new";
5
+ export default function useHintLayout({
6
+ onBackgroundPress,
7
+ targetFrame
8
+ }) {
9
+ const [targetLayoutState, setTargetLayout] = useState(targetFrame);
10
+ const [targetLayoutInWindowState, setTargetLayoutInWindow] = useState();
11
+ const [hintMessageWidth, setHintMessageWidth] = useState();
12
+ const targetRef = useRef(null);
13
+ useEffect(() => {
14
+ if (targetFrame) {
15
+ if (!onBackgroundPress) {
16
+ setTargetLayoutInWindow(targetFrame);
17
+ } else {
18
+ SafeAreaInsetsManager.getSafeAreaInsets().then(insets => {
19
+ setTargetLayoutInWindow({
20
+ ...targetFrame,
21
+ y: targetFrame.y + (insets?.top ?? 0) + Constants.getSafeAreaInsets().top
22
+ });
23
+ });
24
+ }
25
+ }
26
+ }, []);
27
+ const onTargetLayout = useCallback(({
28
+ nativeEvent: {
29
+ layout
30
+ }
31
+ }) => {
32
+ if (!_isEqual(targetLayoutState, layout)) {
33
+ setTargetLayout(layout);
34
+ }
35
+ if (!targetLayoutInWindowState || !!onBackgroundPress) {
36
+ setTimeout(() => {
37
+ targetRef?.current?.measureInWindow?.((x, y, width, height) => {
38
+ const targetLayoutInWindow = {
39
+ x,
40
+ y,
41
+ width,
42
+ height
43
+ };
44
+ setTargetLayoutInWindow(targetLayoutInWindow);
45
+ });
46
+ });
47
+ }
48
+ }, [targetLayoutState, targetLayoutInWindowState, onBackgroundPress]);
49
+ const setHintLayout = useCallback(({
50
+ nativeEvent: {
51
+ layout
52
+ }
53
+ }) => {
54
+ if (!hintMessageWidth) {
55
+ setHintMessageWidth(layout.width);
56
+ }
57
+ }, [hintMessageWidth]);
58
+ return {
59
+ targetLayoutState,
60
+ targetLayoutInWindowState,
61
+ hintMessageWidth,
62
+ targetRef,
63
+ onTargetLayout,
64
+ setHintLayout
65
+ };
66
+ }
@@ -0,0 +1,29 @@
1
+ import { LayoutRectangle } from 'react-native';
2
+ import { LayoutStyle, PositionStyle, PaddingsStyle, TargetAlignments, HintProps } from '../types';
3
+ interface UseHintPositionProps extends Pick<HintProps, 'position' | 'useSideTip'> {
4
+ isUsingModal: boolean;
5
+ targetLayoutState?: LayoutRectangle;
6
+ targetLayoutInWindowState?: LayoutRectangle;
7
+ containerWidth: number;
8
+ edgeMargins: number;
9
+ offset: number;
10
+ hintMessageWidth?: number;
11
+ }
12
+ export default function useHintPosition({ isUsingModal, targetLayoutState, targetLayoutInWindowState, position, containerWidth, useSideTip, offset, edgeMargins, hintMessageWidth }: UseHintPositionProps): {
13
+ tipSize: {
14
+ width: number;
15
+ height: number;
16
+ };
17
+ targetAlignmentOnScreen: TargetAlignments;
18
+ hintContainerLayout: LayoutStyle;
19
+ tipPositionStyle: PositionStyle;
20
+ hintPadding: PaddingsStyle;
21
+ hintPositionStyle: {
22
+ left: number;
23
+ };
24
+ targetScreenToRelativeOffset: {
25
+ left: number;
26
+ top: number;
27
+ };
28
+ };
29
+ export {};