@pagopa/io-app-design-system 3.0.1 → 3.1.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.
Files changed (50) hide show
  1. package/lib/commonjs/components/index.js +11 -0
  2. package/lib/commonjs/components/index.js.map +1 -1
  3. package/lib/commonjs/components/tooltip/Arrows.js +49 -0
  4. package/lib/commonjs/components/tooltip/Arrows.js.map +1 -0
  5. package/lib/commonjs/components/tooltip/Tooltip.js +174 -0
  6. package/lib/commonjs/components/tooltip/Tooltip.js.map +1 -0
  7. package/lib/commonjs/components/tooltip/index.js +17 -0
  8. package/lib/commonjs/components/tooltip/index.js.map +1 -0
  9. package/lib/commonjs/components/tooltip/styles.js +50 -0
  10. package/lib/commonjs/components/tooltip/styles.js.map +1 -0
  11. package/lib/commonjs/components/tooltip/utils/index.js +180 -0
  12. package/lib/commonjs/components/tooltip/utils/index.js.map +1 -0
  13. package/lib/commonjs/components/tooltip/utils/types.js +2 -0
  14. package/lib/commonjs/components/tooltip/utils/types.js.map +1 -0
  15. package/lib/module/components/index.js +1 -0
  16. package/lib/module/components/index.js.map +1 -1
  17. package/lib/module/components/tooltip/Arrows.js +36 -0
  18. package/lib/module/components/tooltip/Arrows.js.map +1 -0
  19. package/lib/module/components/tooltip/Tooltip.js +165 -0
  20. package/lib/module/components/tooltip/Tooltip.js.map +1 -0
  21. package/lib/module/components/tooltip/index.js +2 -0
  22. package/lib/module/components/tooltip/index.js.map +1 -0
  23. package/lib/module/components/tooltip/styles.js +43 -0
  24. package/lib/module/components/tooltip/styles.js.map +1 -0
  25. package/lib/module/components/tooltip/utils/index.js +163 -0
  26. package/lib/module/components/tooltip/utils/index.js.map +1 -0
  27. package/lib/module/components/tooltip/utils/types.js +2 -0
  28. package/lib/module/components/tooltip/utils/types.js.map +1 -0
  29. package/lib/typescript/components/index.d.ts +1 -0
  30. package/lib/typescript/components/index.d.ts.map +1 -1
  31. package/lib/typescript/components/tooltip/Arrows.d.ts +14 -0
  32. package/lib/typescript/components/tooltip/Arrows.d.ts.map +1 -0
  33. package/lib/typescript/components/tooltip/Tooltip.d.ts +64 -0
  34. package/lib/typescript/components/tooltip/Tooltip.d.ts.map +1 -0
  35. package/lib/typescript/components/tooltip/index.d.ts +2 -0
  36. package/lib/typescript/components/tooltip/index.d.ts.map +1 -0
  37. package/lib/typescript/components/tooltip/styles.d.ts +41 -0
  38. package/lib/typescript/components/tooltip/styles.d.ts.map +1 -0
  39. package/lib/typescript/components/tooltip/utils/index.d.ts +89 -0
  40. package/lib/typescript/components/tooltip/utils/index.d.ts.map +1 -0
  41. package/lib/typescript/components/tooltip/utils/types.d.ts +10 -0
  42. package/lib/typescript/components/tooltip/utils/types.d.ts.map +1 -0
  43. package/package.json +1 -1
  44. package/src/components/index.tsx +1 -0
  45. package/src/components/tooltip/Arrows.tsx +36 -0
  46. package/src/components/tooltip/Tooltip.tsx +313 -0
  47. package/src/components/tooltip/index.ts +1 -0
  48. package/src/components/tooltip/styles.ts +44 -0
  49. package/src/components/tooltip/utils/index.ts +179 -0
  50. package/src/components/tooltip/utils/types.ts +9 -0
@@ -0,0 +1,89 @@
1
+ import { ScaledSize } from 'react-native';
2
+ import { ChildrenCoords, DisplayInsets, Placement } from './types';
3
+ export declare const ARROW_WIDTH = 24;
4
+ export declare const ARROW_HEIGHT = 14;
5
+ export declare const EMPTY_SPACE = 8;
6
+ /**
7
+ * @param displayInsets custom display insets
8
+ * @returns An `object` based on `DEFAULT_INSETS` and `displayInsets`
9
+ */
10
+ export declare const getDisplayInsets: (displayInsets: Partial<DisplayInsets>) => DisplayInsets;
11
+ /**
12
+ *
13
+ * @param placement The `Tooltip` placement
14
+ * @returns The `Arrow` box `width` and `height` based on `placement` value
15
+ */
16
+ export declare const getArrowBoxByPlacement: (placement: Placement) => {
17
+ width: number;
18
+ height: number;
19
+ };
20
+ /**
21
+ * A utility function to calculate the `Tooltip` coordinates and dimensions
22
+ * @param placement The `Tooltip` placement in relation of its children
23
+ * @param childrenCoords The measures in screen of the `Tooltip` children
24
+ * @param displayInsets The active display insets
25
+ * @param screenDimensions The dimensions of the device screen
26
+ * @returns The `Tooltip` coordinates
27
+ */
28
+ export declare const getTooltipCoords: (placement: Placement, childrenCoords: ChildrenCoords, displayInsets: DisplayInsets, screenDimensions: ScaledSize) => {
29
+ bottom: number;
30
+ left: number;
31
+ width: number;
32
+ top?: undefined;
33
+ } | {
34
+ top: number;
35
+ left: number;
36
+ width: number;
37
+ bottom?: undefined;
38
+ } | {
39
+ bottom?: undefined;
40
+ left?: undefined;
41
+ width?: undefined;
42
+ top?: undefined;
43
+ };
44
+ /**
45
+ * A utility function to calculate the `Tooltip`'s `Arrow` coordinates
46
+ * @param placement The `Arrow` placement in relation of the `Tooltip` children
47
+ * @param childrenCoords The measures in screen of the `Tooltip` children
48
+ * @param screenDimensions The active display insets
49
+ * @returns The `Tooltip`'s Arrow coordinates
50
+ */
51
+ export declare const getArrowCoords: (placement: Placement, childrenCoords: ChildrenCoords, screenDimensions: ScaledSize) => {
52
+ bottom: number;
53
+ left: number;
54
+ top?: undefined;
55
+ } | {
56
+ top: number;
57
+ left: number;
58
+ bottom?: undefined;
59
+ } | {
60
+ bottom?: undefined;
61
+ left?: undefined;
62
+ top?: undefined;
63
+ };
64
+ /**
65
+ * A utility function to calculate the `Tooltip` vertical alignment
66
+ * @param placement The `Tooltip` placement in relation of its children
67
+ * @param childrenHeight The `Tooltip`'s children height
68
+ * @param tooltipHeight The `Tooltip`'s height
69
+ * @returns If placement is `left` or `right` it returns the vertical tranlsation to align the `Tooltip` center with its `children` center,
70
+ * otherwise `null` is returned
71
+ */
72
+ export declare const getTooltipVerticalAlignment: (placement: Placement, childrenHeight: number, tooltipHeight?: number) => {
73
+ transform: {
74
+ translateY: number;
75
+ }[];
76
+ } | null;
77
+ /**
78
+ * A utility function to calculate the `Arrow` vertical alignment
79
+ * @param placement The `Tooltip` placement in relation of its children
80
+ * @param childrenHeight The `Tooltip`'s children height
81
+ */
82
+ export declare const getArrowVerticalAlignment: (placement: Placement, childrenHeight: number) => {
83
+ transform: {
84
+ translateY: number;
85
+ }[];
86
+ } | null;
87
+ export declare const isDefined: <T>(v: T) => boolean;
88
+ export declare const isNotZero: (v: number) => boolean;
89
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/tooltip/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEnE,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC;AAQ7B;;;GAGG;AACH,eAAO,MAAM,gBAAgB,kBACZ,QAAQ,aAAa,CAAC,KACpC,aAA0D,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,cAAe,SAAS;;;CAc1D,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,cAChB,SAAS,kBACJ,cAAc,iBACf,aAAa,oBACV,UAAU;;;;;;;;;;;;;;;CAsC7B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,cACd,SAAS,kBACJ,cAAc,oBACZ,UAAU;;;;;;;;;;;;CA6B7B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,cAAe,SAAS,kBAAkB,MAAM,kBAAkB,MAAM;;;;QAY/G,CAAC;AAEF;;;;EAIE;AACF,eAAO,MAAM,yBAAyB,cAAe,SAAS,kBAAkB,MAAM;;;;QAYrF,CAAC;AAEF,eAAO,MAAM,SAAS,SAAU,CAAC,YAAoB,CAAC;AACtD,eAAO,MAAM,SAAS,MAAO,MAAM,YAAY,CAAC"}
@@ -0,0 +1,10 @@
1
+ export type DisplayInsets = Record<Placement, number>;
2
+ export type Placement = "top" | "bottom" | "left" | "right";
3
+ export type ChildrenCoords = {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ };
9
+ export type TooltipLayout = ChildrenCoords;
10
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/components/tooltip/utils/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AACtD,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAC5D,MAAM,MAAM,cAAc,GAAG;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AACF,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/io-app-design-system",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "description": "The library defining the core components of the design system of @pagopa/io-app",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -30,4 +30,5 @@ export * from "./tag";
30
30
  export * from "./textInput";
31
31
  export * from "./searchInput";
32
32
  export * from "./toast";
33
+ export * from "./tooltip";
33
34
  export * from "./typography";
@@ -0,0 +1,36 @@
1
+ import Svg, { Path } from 'react-native-svg';
2
+ import React from 'react';
3
+ import { IOColors } from '../../core';
4
+
5
+ export const LeftArrow = ({ color = IOColors.white }: { color?: string }) => (
6
+ <Svg fill="none" >
7
+ <Path
8
+ d="M12.6955 15.2C14.8289 13.6 14.8289 10.4 12.6955 8.8L0.962204 0V24L12.6955 15.2Z"
9
+ fill={color}
10
+ />
11
+ </Svg>
12
+ );
13
+ export const RightArrow = ({ color = IOColors.white }: { color?: string }) => (
14
+ <Svg fill={color}>
15
+ <Path
16
+ d="M2.30448 15.2031C0.171145 13.6031 0.171145 10.4031 2.30448 8.80314L14.0378 0.00314331V24.0031L2.30448 15.2031Z"
17
+ fill={color}
18
+ />
19
+ </Svg>
20
+ );
21
+ export const BottomArrow = ({ color = IOColors.white }: { color?: string }) => (
22
+ <Svg fill={color}>
23
+ <Path
24
+ d="M15.2 2.26667C13.6 0.133334 10.4 0.133333 8.8 2.26667L0 14L24 14L15.2 2.26667Z"
25
+ fill={color}
26
+ />
27
+ </Svg>
28
+ );
29
+ export const TopArrow = ({ color = IOColors.white }: { color?: string }) => (
30
+ <Svg fill={color}>
31
+ <Path
32
+ d="M15.2 11.7333C13.6 13.8667 10.4 13.8667 8.8 11.7333L0 0L24 0L15.2 11.7333Z"
33
+ fill={color}
34
+ />
35
+ </Svg>
36
+ );
@@ -0,0 +1,313 @@
1
+ import React, {
2
+ useState,
3
+ useRef,
4
+ PropsWithChildren,
5
+ useEffect,
6
+ useCallback,
7
+ JSXElementConstructor,
8
+ useMemo,
9
+ ReactElement
10
+ } from "react";
11
+ import {
12
+ View,
13
+ Modal,
14
+ Dimensions,
15
+ LayoutChangeEvent,
16
+ TouchableWithoutFeedback
17
+ } from "react-native";
18
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
19
+ import { every, some } from "lodash";
20
+ import { IOColors } from "../../core";
21
+ import { Body, H6 } from "../typography";
22
+ import { IconButton } from "../buttons";
23
+ import { BottomArrow, LeftArrow, RightArrow, TopArrow } from "./Arrows";
24
+ import {
25
+ ARROW_HEIGHT,
26
+ EMPTY_SPACE,
27
+ getArrowBoxByPlacement,
28
+ getArrowCoords,
29
+ getArrowVerticalAlignment,
30
+ getDisplayInsets,
31
+ getTooltipCoords,
32
+ getTooltipVerticalAlignment,
33
+ isDefined,
34
+ isNotZero
35
+ } from "./utils";
36
+ import { getChildrenPosition, tooltipStyles } from "./styles";
37
+ import {
38
+ ChildrenCoords,
39
+ DisplayInsets,
40
+ Placement,
41
+ TooltipLayout
42
+ } from "./utils/types";
43
+
44
+ const screenDimensions = Dimensions.get("window");
45
+ const INITIAL_COORDS: ChildrenCoords = {
46
+ x: 0,
47
+ y: 0,
48
+ width: 0,
49
+ height: 0
50
+ };
51
+ const ARROWS_BY_PLACEMENT: Record<
52
+ Placement,
53
+ JSXElementConstructor<{ color: string }>
54
+ > = {
55
+ top: TopArrow,
56
+ bottom: BottomArrow,
57
+ left: LeftArrow,
58
+ right: RightArrow
59
+ };
60
+
61
+ type CommonProps = {
62
+ /**
63
+ * The title text displayed at the top of the tooltip.
64
+ */
65
+ title: string;
66
+ /**
67
+ * The tooltip text content.
68
+ */
69
+ content: string;
70
+ /**
71
+ * Controls the visibility of the tooltip.
72
+ */
73
+ isVisible: boolean;
74
+ /**
75
+ * Initial tooltip position; can be 'top', 'bottom', 'left', or 'right'.
76
+ * @default top
77
+ */
78
+ placement?: Placement;
79
+ /**
80
+ * Insets for adjusting tooltip position within screen boundaries.
81
+ * @default {}
82
+ */
83
+ displayInsets?: Partial<DisplayInsets>;
84
+ /**
85
+ * Accessibility label for the close icon button.
86
+ */
87
+ closeIconAccessibilityLabel: string;
88
+ /**
89
+ * Determines whether interactions with the tooltip's children are allowed when `isVisible` is set to true.
90
+ * @default false
91
+ */
92
+ childrenInteractionsEnabled?: boolean;
93
+ /**
94
+ * Callback function triggered when the tooltip is closed.
95
+ */
96
+ onClose: () => void;
97
+ };
98
+ type CloseWithTapOnBackground = {
99
+ /**
100
+ * Allows closing the tooltip by tapping outside of it.
101
+ */
102
+ allowCloseOnBackgroundTap: true;
103
+ /**
104
+ * Accessibility label for the tooltip background mask.
105
+ */
106
+ backgroundAccessibilityLabel: string;
107
+ };
108
+ type CloseWithBackgroundTapDisabled = {
109
+ allowCloseOnBackgroundTap?: false;
110
+ };
111
+ type Props = CommonProps & (CloseWithTapOnBackground | CloseWithBackgroundTapDisabled);
112
+
113
+ /**
114
+ * Tooltip component that displays a contextual tooltip around its children.
115
+ * The tooltip position is controlled by the `placement` prop and can adjust
116
+ * dynamically if there is insufficient space.
117
+ * @param {Props} props - The component props
118
+ *
119
+ * @returns {ReactElement} A tooltip component rendered around the specified children.
120
+ */
121
+ export const Tooltip = ({
122
+ children,
123
+ title,
124
+ content,
125
+ placement: initialPlacement = "top",
126
+ closeIconAccessibilityLabel,
127
+ isVisible,
128
+ displayInsets = {},
129
+ allowCloseOnBackgroundTap,
130
+ childrenInteractionsEnabled = false,
131
+ onClose
132
+ }: PropsWithChildren<Props>): ReactElement => {
133
+ const insets = useSafeAreaInsets();
134
+ const [currentPlacement, setCurrentPlacement] =
135
+ useState<Placement>(initialPlacement);
136
+ const [childrenCoords, setChildrenCoords] = useState<ChildrenCoords>(INITIAL_COORDS);
137
+ const [tooltipLayout, setTooltipLayout] = useState<TooltipLayout>();
138
+ const childRef = useRef<View>(null);
139
+ const titleRef = useRef<View>(null);
140
+ const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
141
+
142
+ const Arrow = useMemo(
143
+ () => ARROWS_BY_PLACEMENT[currentPlacement],
144
+ [currentPlacement]
145
+ );
146
+ const isChildrenMeasurementFinished =
147
+ every(childrenCoords, isDefined)
148
+ && some(childrenCoords, isNotZero);
149
+ const isTooltipMeasurementCompleted = isDefined(tooltipLayout);
150
+ const tooltipVisibility = { opacity: isTooltipMeasurementCompleted ? 1 : 0 };
151
+
152
+ /**
153
+ * This function sets the `Tooltip` children coordinates
154
+ */
155
+ const measureChildrenCoords = useCallback(() => {
156
+ if (childRef.current && typeof childRef.current.measure === "function") {
157
+ childRef.current.measure((_, __, width, height, px, py) => {
158
+ const coords = {
159
+ x: px,
160
+ y: py,
161
+ width,
162
+ height
163
+ };
164
+ if (every(coords, isDefined)) {
165
+ setChildrenCoords(coords);
166
+ }
167
+ });
168
+ }
169
+ }, []);
170
+
171
+ useEffect(() => {
172
+ if (isVisible) {
173
+ // A new measure is executed every time the `Tooltip` is visible
174
+ // This is required for use within ScrollView components.
175
+ // eslint-disable-next-line functional/immutable-data
176
+ timeoutRef.current = setTimeout(measureChildrenCoords, 100);
177
+ } else {
178
+ setChildrenCoords(INITIAL_COORDS);
179
+ setCurrentPlacement(initialPlacement);
180
+ }
181
+
182
+ return () => {
183
+ if (isVisible) {
184
+ clearTimeout(timeoutRef.current);
185
+ }
186
+ };
187
+ }, [isVisible, initialPlacement, measureChildrenCoords]);
188
+
189
+ /**
190
+ * This function works with `top` and `bottom` placement and sets the current placement to their opposite value
191
+ * if in the selected one there is no space to prompt the tooltip
192
+ */
193
+ const invertPlacementIfNeeded = useCallback(
194
+ (nativeEvent: LayoutChangeEvent["nativeEvent"]) => {
195
+ if (initialPlacement === "top") {
196
+ const hasSpace = nativeEvent.layout.y >= insets.top;
197
+
198
+ if (!hasSpace) {
199
+ setCurrentPlacement("bottom");
200
+ }
201
+ }
202
+ if (initialPlacement === "bottom") {
203
+ const remainingSpace =
204
+ screenDimensions.height - nativeEvent.layout.y - insets.bottom;
205
+ const tooltipMinHeight =
206
+ nativeEvent.layout.height + ARROW_HEIGHT + EMPTY_SPACE;
207
+ const hasSpace = remainingSpace >= tooltipMinHeight;
208
+
209
+ if (!hasSpace) {
210
+ setCurrentPlacement("top");
211
+ }
212
+ }
213
+ },
214
+ [insets.bottom, insets.top, initialPlacement]
215
+ );
216
+
217
+ const handleTooltipOnLayout = useCallback(
218
+ ({ nativeEvent }: LayoutChangeEvent) => {
219
+ invertPlacementIfNeeded(nativeEvent);
220
+ setTooltipLayout(nativeEvent.layout);
221
+ },
222
+ [invertPlacementIfNeeded]
223
+ );
224
+
225
+ const handleTapOnBackground = useCallback(() => {
226
+ if (allowCloseOnBackgroundTap) {
227
+ onClose();
228
+ }
229
+ }, [allowCloseOnBackgroundTap, onClose]);
230
+
231
+ return (
232
+ <>
233
+ <View
234
+ // This prop is necessary for .measure to work correctly, as explained here: https://github.com/facebook/react-native/issues/29712
235
+ collapsable={false}
236
+ ref={childRef}
237
+ >
238
+ {children}
239
+ </View>
240
+ <Modal transparent visible={isVisible && isChildrenMeasurementFinished}>
241
+ <View
242
+ accessibilityElementsHidden={!childrenInteractionsEnabled}
243
+ importantForAccessibility={childrenInteractionsEnabled ? 'auto' : 'no-hide-descendants'}
244
+ pointerEvents={childrenInteractionsEnabled ? "auto" : "box-only"}
245
+ style={[
246
+ tooltipStyles.childrenContainer,
247
+ getChildrenPosition(childrenCoords)
248
+ ]}
249
+ >
250
+ {children}
251
+ </View>
252
+ <TouchableWithoutFeedback
253
+ accessible={allowCloseOnBackgroundTap}
254
+ accessibilityRole={allowCloseOnBackgroundTap ? "button" : "none"}
255
+ importantForAccessibility={allowCloseOnBackgroundTap ? 'yes' : 'no'}
256
+ accessibilityElementsHidden={!allowCloseOnBackgroundTap}
257
+ onPress={handleTapOnBackground}
258
+ >
259
+ <View
260
+ style={[
261
+ tooltipStyles.overlay,
262
+ { height: screenDimensions.height }
263
+ ]}
264
+ />
265
+ </TouchableWithoutFeedback>
266
+ <View
267
+ onLayout={handleTooltipOnLayout}
268
+ style={[
269
+ tooltipStyles.tooltipContainer,
270
+ getTooltipCoords(
271
+ currentPlacement,
272
+ childrenCoords,
273
+ getDisplayInsets(displayInsets),
274
+ screenDimensions
275
+ ),
276
+ getTooltipVerticalAlignment(
277
+ currentPlacement,
278
+ childrenCoords.height,
279
+ tooltipLayout?.height
280
+ ),
281
+ tooltipVisibility
282
+ ]}
283
+ >
284
+ <H6 ref={titleRef}>{title}</H6>
285
+ <View style={tooltipStyles.closeIcon}>
286
+ <IconButton
287
+ color="neutral"
288
+ icon="closeSmall"
289
+ accessibilityLabel={closeIconAccessibilityLabel}
290
+ onPress={onClose}
291
+ />
292
+ </View>
293
+ <Body>{content}</Body>
294
+ </View>
295
+ <View
296
+ style={[
297
+ tooltipStyles.arrowContainer,
298
+ getArrowBoxByPlacement(currentPlacement),
299
+ getArrowCoords(
300
+ currentPlacement,
301
+ childrenCoords,
302
+ screenDimensions
303
+ ),
304
+ getArrowVerticalAlignment(currentPlacement, childrenCoords.height),
305
+ tooltipVisibility
306
+ ]}
307
+ >
308
+ <Arrow color={IOColors.white} />
309
+ </View>
310
+ </Modal>
311
+ </>
312
+ );
313
+ };
@@ -0,0 +1 @@
1
+ export * from "./Tooltip";
@@ -0,0 +1,44 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { IOColors } from '../../core';
3
+ import { ChildrenCoords } from './utils/types';
4
+
5
+ export const tooltipStyles = StyleSheet.create({
6
+ overlay: {
7
+ position: "absolute",
8
+ width: "100%",
9
+ height: "100%",
10
+ backgroundColor: IOColors["grey-850"],
11
+ opacity: 0.6,
12
+ zIndex: 997
13
+ },
14
+ childrenContainer: {
15
+ position: "absolute",
16
+ zIndex: 1000
17
+ },
18
+ tooltipContainer: {
19
+ position: "absolute",
20
+ paddingHorizontal: 16,
21
+ paddingVertical: 16,
22
+ backgroundColor: IOColors.white,
23
+ borderRadius: 8,
24
+ zIndex: 2000,
25
+ overflow: "visible"
26
+ },
27
+ arrowContainer: {
28
+ position: "absolute",
29
+ display: 'flex',
30
+ zIndex: 3000
31
+ },
32
+ closeIcon: {
33
+ position: 'absolute',
34
+ right: 8,
35
+ top: 9 // It's been used `9` instead of `8` to fix accessibility focus order. In this way title is read before close icon.
36
+ }
37
+ });
38
+
39
+ export const getChildrenPosition = (childrenCoords: ChildrenCoords) => ({
40
+ top: childrenCoords.y,
41
+ left: childrenCoords.x,
42
+ width: childrenCoords.width,
43
+ height: childrenCoords.height
44
+ });
@@ -0,0 +1,179 @@
1
+ import { ScaledSize } from 'react-native';
2
+ import { IOVisualCostants } from '../../../core';
3
+ import { ChildrenCoords, DisplayInsets, Placement } from './types';
4
+
5
+ export const ARROW_WIDTH = 24;
6
+ export const ARROW_HEIGHT = 14;
7
+ export const EMPTY_SPACE = 8;
8
+ const DEFAULT_INSETS: DisplayInsets = {
9
+ top: 0,
10
+ bottom: 0,
11
+ left: IOVisualCostants.appMarginDefault,
12
+ right: IOVisualCostants.appMarginDefault,
13
+ };
14
+
15
+ /**
16
+ * @param displayInsets custom display insets
17
+ * @returns An `object` based on `DEFAULT_INSETS` and `displayInsets`
18
+ */
19
+ export const getDisplayInsets = (
20
+ displayInsets: Partial<DisplayInsets>
21
+ ): DisplayInsets => ({ ...DEFAULT_INSETS, ...displayInsets });
22
+
23
+ /**
24
+ *
25
+ * @param placement The `Tooltip` placement
26
+ * @returns The `Arrow` box `width` and `height` based on `placement` value
27
+ */
28
+ export const getArrowBoxByPlacement = (placement: Placement) => {
29
+ switch (placement) {
30
+ case 'left':
31
+ case 'right':
32
+ return {
33
+ width: ARROW_HEIGHT,
34
+ height: ARROW_WIDTH,
35
+ };
36
+ default:
37
+ return {
38
+ height: ARROW_HEIGHT,
39
+ width: ARROW_WIDTH,
40
+ };
41
+ }
42
+ };
43
+
44
+ /**
45
+ * A utility function to calculate the `Tooltip` coordinates and dimensions
46
+ * @param placement The `Tooltip` placement in relation of its children
47
+ * @param childrenCoords The measures in screen of the `Tooltip` children
48
+ * @param displayInsets The active display insets
49
+ * @param screenDimensions The dimensions of the device screen
50
+ * @returns The `Tooltip` coordinates
51
+ */
52
+ export const getTooltipCoords = (
53
+ placement: Placement,
54
+ childrenCoords: ChildrenCoords,
55
+ displayInsets: DisplayInsets,
56
+ screenDimensions: ScaledSize
57
+ ) => {
58
+ const { width: screenWidth, height: screenHeight } = screenDimensions;
59
+
60
+ switch (placement) {
61
+ case "top":
62
+ return {
63
+ bottom: screenHeight - childrenCoords.y + ARROW_HEIGHT + EMPTY_SPACE,
64
+ left: displayInsets.left,
65
+ width: screenWidth - displayInsets.left - displayInsets.right
66
+ };
67
+ case "bottom":
68
+ return {
69
+ top: childrenCoords.y + childrenCoords.height + ARROW_HEIGHT + EMPTY_SPACE,
70
+ left: displayInsets.left,
71
+ width: screenWidth - displayInsets.left - displayInsets.right
72
+ };
73
+ case "left":
74
+ return {
75
+ top: childrenCoords.y,
76
+ left: displayInsets.left,
77
+ width:
78
+ screenWidth - (screenWidth - childrenCoords.x) - ARROW_HEIGHT - displayInsets.left - EMPTY_SPACE
79
+ };
80
+ case "right":
81
+ const elementSize = childrenCoords.width + childrenCoords.x + ARROW_HEIGHT + EMPTY_SPACE;
82
+
83
+ return {
84
+ top: childrenCoords.y,
85
+ left: elementSize,
86
+ width:
87
+ screenWidth -
88
+ (elementSize + displayInsets.right)
89
+ };
90
+ // TODO: provide a default center position in case of Tooltip without children
91
+ default:
92
+ return {};
93
+ }
94
+ };
95
+
96
+ /**
97
+ * A utility function to calculate the `Tooltip`'s `Arrow` coordinates
98
+ * @param placement The `Arrow` placement in relation of the `Tooltip` children
99
+ * @param childrenCoords The measures in screen of the `Tooltip` children
100
+ * @param screenDimensions The active display insets
101
+ * @returns The `Tooltip`'s Arrow coordinates
102
+ */
103
+ export const getArrowCoords = (
104
+ placement: Placement,
105
+ childrenCoords: ChildrenCoords,
106
+ screenDimensions: ScaledSize
107
+ ) => {
108
+ const { width: screenWidth, height: screenHeight } = screenDimensions;
109
+
110
+ switch (placement) {
111
+ case "top":
112
+ return {
113
+ bottom: screenHeight - childrenCoords.y + EMPTY_SPACE,
114
+ left: childrenCoords.x + childrenCoords.width / 2 - ARROW_WIDTH / 2
115
+ };
116
+ case "bottom":
117
+ return {
118
+ top: childrenCoords.y + childrenCoords.height + EMPTY_SPACE,
119
+ left: childrenCoords.x + childrenCoords.width / 2 - ARROW_WIDTH / 2
120
+ };
121
+ case "left":
122
+ return {
123
+ top: childrenCoords.y,
124
+ left: screenWidth - (screenWidth - childrenCoords.x) - ARROW_HEIGHT - EMPTY_SPACE - 1, // FIXME -> This `-1` is necessary because of the Svg size doesn't match the box size
125
+ };
126
+ case "right":
127
+ return {
128
+ top: childrenCoords.y,
129
+ left: childrenCoords.width + childrenCoords.x + EMPTY_SPACE
130
+ };
131
+ default:
132
+ // TODO: provide a default center position in case of Tooltip without children
133
+ return {};
134
+ }
135
+ };
136
+
137
+ /**
138
+ * A utility function to calculate the `Tooltip` vertical alignment
139
+ * @param placement The `Tooltip` placement in relation of its children
140
+ * @param childrenHeight The `Tooltip`'s children height
141
+ * @param tooltipHeight The `Tooltip`'s height
142
+ * @returns If placement is `left` or `right` it returns the vertical tranlsation to align the `Tooltip` center with its `children` center,
143
+ * otherwise `null` is returned
144
+ */
145
+ export const getTooltipVerticalAlignment = (placement: Placement, childrenHeight: number, tooltipHeight?: number) => {
146
+ if ((placement === "left" || placement === "right") && tooltipHeight) {
147
+ return {
148
+ transform: [
149
+ {
150
+ translateY:
151
+ -tooltipHeight / 2 + childrenHeight / 2
152
+ }
153
+ ]
154
+ };
155
+ }
156
+ return null;
157
+ };
158
+
159
+ /**
160
+ * A utility function to calculate the `Arrow` vertical alignment
161
+ * @param placement The `Tooltip` placement in relation of its children
162
+ * @param childrenHeight The `Tooltip`'s children height
163
+ */
164
+ export const getArrowVerticalAlignment = (placement: Placement, childrenHeight: number) => {
165
+ if (placement === "left" || placement === "right") {
166
+ return {
167
+ transform: [
168
+ {
169
+ translateY:
170
+ -ARROW_WIDTH / 2 + childrenHeight / 2
171
+ }
172
+ ]
173
+ };
174
+ }
175
+ return null;
176
+ };
177
+
178
+ export const isDefined = <T>(v: T) => v !== undefined;
179
+ export const isNotZero = (v: number) => v !== 0;