From acab7359fc9dcea2fb86f03e38e3ec778d9aa208 Mon Sep 17 00:00:00 2001
From: Cristian Matteu <94987118+ChrisMattew@users.noreply.github.com>
Date: Thu, 14 Nov 2024 17:49:51 +0100
Subject: [PATCH] [IOPID-2426] Tooltip component (#351)
## Short description
This PR adds the `Tooltip` component
## List of changes proposed in this pull request
- Created `Tooltip` component
- Added `Tooltips` screen in the Example app to show the `Tooltip`
behaviors
## Demo
|iOS|Android|
|----|-------|
|||
|A11Y iOS|A11Y Android|
|----|-------|
|||
>[!Important]
> The `left` and `right` positions are calculated after the component is
rendered. This implementation causes a minor glitch where the Tooltip
repositions itself to achieve a centered alignment relative to the
enclosing element.
> To avoid this behavior, the opacity value of the component depends on
the parameters returned by the `onLayout` prop. Once the `tooltipLayout`
is defined, the opacity is set to a value of 1.
## How to test
Run the example app and test the `Tooltip` different implementations
---------
Co-authored-by: Damiano Plebani
Co-authored-by: Alice Di Rico <83651704+Ladirico@users.noreply.github.com>
---
example/src/navigation/navigator.tsx | 8 +
example/src/navigation/params.ts | 1 +
example/src/navigation/routes.ts | 4 +
example/src/pages/Tooltips.tsx | 89 ++++++++
example/yarn.lock | 263 +++++++++++++---------
src/components/index.tsx | 1 +
src/components/tooltip/Arrows.tsx | 36 +++
src/components/tooltip/Tooltip.tsx | 313 ++++++++++++++++++++++++++
src/components/tooltip/index.ts | 1 +
src/components/tooltip/styles.ts | 44 ++++
src/components/tooltip/utils/index.ts | 179 +++++++++++++++
src/components/tooltip/utils/types.ts | 9 +
12 files changed, 847 insertions(+), 101 deletions(-)
create mode 100644 example/src/pages/Tooltips.tsx
create mode 100644 src/components/tooltip/Arrows.tsx
create mode 100644 src/components/tooltip/Tooltip.tsx
create mode 100644 src/components/tooltip/index.ts
create mode 100644 src/components/tooltip/styles.ts
create mode 100644 src/components/tooltip/utils/index.ts
create mode 100644 src/components/tooltip/utils/types.ts
diff --git a/example/src/navigation/navigator.tsx b/example/src/navigation/navigator.tsx
index 3860069a..124b3b68 100644
--- a/example/src/navigation/navigator.tsx
+++ b/example/src/navigation/navigator.tsx
@@ -55,6 +55,7 @@ import { TabNavigationScreen } from "../pages/TabNavigation";
import { TextInputs } from "../pages/TextInputs";
import { Toasts } from "../pages/Toasts";
import { Typography } from "../pages/Typography";
+import Tooltips from '../pages/Tooltips';
import { AppParamsList } from "./params";
import APP_ROUTES from "./routes";
@@ -426,6 +427,13 @@ const AppNavigator = () => {
headerTitle: APP_ROUTES.COMPONENTS.TOASTS.title
}}
/>
+
{
+ const [isTopVisible, setIsTopVisible] = useState(false);
+ const [isBottomVisible, setIsBottomVisible] = useState(false);
+ const [isRightVisible, setIsRightVisible] = useState(false);
+ const [isLeftVisible, setIsLeftVisible] = useState(false);
+ const [isTopLeftVisible, setIsTopLefttVisible] = useState(false);
+ const [isBottomRightVisible, setIsBottomRighttVisible] = useState(false);
+
+ return (
+
+
+ setIsBottomVisible(false)}
+ closeIconAccessibilityLabel=''
+ >
+ setIsBottomVisible(true)} />
+
+
+
+ setIsTopVisible(false)}
+ closeIconAccessibilityLabel=''
+ >
+ setIsTopVisible(true)} />
+
+
+
+ setIsRightVisible(false)}
+ closeIconAccessibilityLabel=''
+ >
+ setIsRightVisible(true)} />
+
+ setIsLeftVisible(false)}
+ closeIconAccessibilityLabel=''
+ >
+ setIsLeftVisible(true)} />
+
+
+
+
+ setIsTopLefttVisible(false)}
+ closeIconAccessibilityLabel=''
+ >
+ setIsTopLefttVisible(true)} />
+
+ setIsBottomRighttVisible(false)}
+ closeIconAccessibilityLabel=''
+ >
+ setIsBottomRighttVisible(true)} />
+
+
+
+
+ );
+};
+
+export default Tooltips;
\ No newline at end of file
diff --git a/example/yarn.lock b/example/yarn.lock
index 5e003319..576b567c 100644
--- a/example/yarn.lock
+++ b/example/yarn.lock
@@ -30,12 +30,13 @@
"@babel/highlight" "^7.22.13"
chalk "^2.4.2"
-"@babel/code-frame@^7.24.7":
- version "7.24.7"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465"
- integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==
+"@babel/code-frame@^7.25.9":
+ version "7.26.2"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
+ integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
dependencies:
- "@babel/highlight" "^7.24.7"
+ "@babel/helper-validator-identifier" "^7.25.9"
+ js-tokens "^4.0.0"
picocolors "^1.0.0"
"@babel/code-frame@^7.25.9":
@@ -98,15 +99,16 @@
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
-"@babel/generator@^7.25.0":
- version "7.25.0"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.25.0.tgz#f858ddfa984350bc3d3b7f125073c9af6988f18e"
- integrity sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==
+"@babel/generator@^7.25.9":
+ version "7.26.2"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.2.tgz#87b75813bec87916210e5e01939a4c823d6bb74f"
+ integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==
dependencies:
- "@babel/types" "^7.25.0"
+ "@babel/parser" "^7.26.2"
+ "@babel/types" "^7.26.0"
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.25"
- jsesc "^2.5.1"
+ jsesc "^3.0.2"
"@babel/generator@^7.25.9":
version "7.26.2"
@@ -267,6 +269,14 @@
dependencies:
"@babel/types" "^7.22.5"
+"@babel/helper-module-imports@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
+ integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
+ dependencies:
+ "@babel/traverse" "^7.25.9"
+ "@babel/types" "^7.25.9"
+
"@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129"
@@ -278,6 +288,15 @@
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/helper-validator-identifier" "^7.22.5"
+"@babel/helper-module-transforms@^7.25.9":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae"
+ integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==
+ dependencies:
+ "@babel/helper-module-imports" "^7.25.9"
+ "@babel/helper-validator-identifier" "^7.25.9"
+ "@babel/traverse" "^7.25.9"
+
"@babel/helper-optimise-call-expression@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e"
@@ -297,10 +316,10 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
-"@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.24.8":
- version "7.24.8"
- resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz#94ee67e8ec0e5d44ea7baeb51e571bd26af07878"
- integrity sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==
+"@babel/helper-plugin-utils@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46"
+ integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==
"@babel/helper-plugin-utils@^7.25.9":
version "7.25.9"
@@ -341,6 +360,14 @@
dependencies:
"@babel/types" "^7.22.5"
+"@babel/helper-simple-access@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz#6d51783299884a2c74618d6ef0f86820ec2e7739"
+ integrity sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==
+ dependencies:
+ "@babel/traverse" "^7.25.9"
+ "@babel/types" "^7.25.9"
+
"@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847"
@@ -348,13 +375,13 @@
dependencies:
"@babel/types" "^7.22.5"
-"@babel/helper-skip-transparent-expression-wrappers@^7.24.7":
- version "7.24.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz#5f8fa83b69ed5c27adc56044f8be2b3ea96669d9"
- integrity sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==
+"@babel/helper-skip-transparent-expression-wrappers@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9"
+ integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==
dependencies:
- "@babel/traverse" "^7.24.7"
- "@babel/types" "^7.24.7"
+ "@babel/traverse" "^7.25.9"
+ "@babel/types" "^7.25.9"
"@babel/helper-skip-transparent-expression-wrappers@^7.25.9":
version "7.25.9"
@@ -376,10 +403,10 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
-"@babel/helper-string-parser@^7.24.8":
- version "7.24.8"
- resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d"
- integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==
+"@babel/helper-string-parser@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
+ integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
"@babel/helper-string-parser@^7.25.9":
version "7.25.9"
@@ -396,10 +423,10 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193"
integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==
-"@babel/helper-validator-identifier@^7.24.7":
- version "7.24.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db"
- integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==
+"@babel/helper-validator-identifier@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
+ integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
"@babel/helper-validator-identifier@^7.25.9":
version "7.25.9"
@@ -452,16 +479,6 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/highlight@^7.24.7":
- version "7.24.7"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d"
- integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==
- dependencies:
- "@babel/helper-validator-identifier" "^7.24.7"
- chalk "^2.4.2"
- js-tokens "^4.0.0"
- picocolors "^1.0.0"
-
"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7":
version "7.22.7"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae"
@@ -472,10 +489,12 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
-"@babel/parser@^7.25.0":
- version "7.25.0"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.0.tgz#9fdc9237504d797b6e7b8f66e78ea7f570d256ad"
- integrity sha512-CzdIU9jdP0dg7HdyB+bHvDJGagUv+qtzZt5rYCWwW6tITNqV9odjp6Qu41gkG0ca5UfdDUWrKkiAnHHdGRnOrA==
+"@babel/parser@^7.25.9", "@babel/parser@^7.26.2":
+ version "7.26.2"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.2.tgz#fd7b6f487cfea09889557ef5d4eeb9ff9a5abd11"
+ integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==
+ dependencies:
+ "@babel/types" "^7.26.0"
"@babel/parser@^7.25.9", "@babel/parser@^7.26.2":
version "7.26.2"
@@ -617,6 +636,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
+"@babel/plugin-syntax-jsx@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290"
+ integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
"@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
@@ -673,6 +699,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
+"@babel/plugin-syntax-typescript@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399"
+ integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+
"@babel/plugin-transform-arrow-functions@^7.0.0":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958"
@@ -681,11 +714,11 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-arrow-functions@^7.0.0-0":
- version "7.24.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz#4f6886c11e423bd69f3ce51dbf42424a5f275514"
- integrity sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845"
+ integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==
dependencies:
- "@babel/helper-plugin-utils" "^7.24.7"
+ "@babel/helper-plugin-utils" "^7.25.9"
"@babel/plugin-transform-async-to-generator@^7.20.0":
version "7.22.5"
@@ -807,6 +840,15 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/helper-simple-access" "^7.22.5"
+"@babel/plugin-transform-modules-commonjs@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz#d165c8c569a080baf5467bda88df6425fc060686"
+ integrity sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-simple-access" "^7.25.9"
+
"@babel/plugin-transform-named-capturing-groups-regex@^7.0.0":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f"
@@ -816,12 +858,11 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-nullish-coalescing-operator@^7.0.0-0":
- version "7.24.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz#1de4534c590af9596f53d67f52a92f12db984120"
- integrity sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz#bcb1b0d9e948168102d5f7104375ca21c3266949"
+ integrity sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==
dependencies:
- "@babel/helper-plugin-utils" "^7.24.7"
- "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+ "@babel/helper-plugin-utils" "^7.25.9"
"@babel/plugin-transform-object-super@^7.0.0":
version "7.22.5"
@@ -832,13 +873,12 @@
"@babel/helper-replace-supers" "^7.22.5"
"@babel/plugin-transform-optional-chaining@^7.0.0-0":
- version "7.24.8"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz#bb02a67b60ff0406085c13d104c99a835cdf365d"
- integrity sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd"
+ integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==
dependencies:
- "@babel/helper-plugin-utils" "^7.24.8"
- "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7"
- "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7":
version "7.22.5"
@@ -906,11 +946,11 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-shorthand-properties@^7.0.0-0":
- version "7.24.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz#85448c6b996e122fa9e289746140aaa99da64e73"
- integrity sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2"
+ integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==
dependencies:
- "@babel/helper-plugin-utils" "^7.24.7"
+ "@babel/helper-plugin-utils" "^7.25.9"
"@babel/plugin-transform-spread@^7.0.0":
version "7.22.5"
@@ -935,11 +975,11 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-template-literals@^7.0.0-0":
- version "7.24.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz#a05debb4a9072ae8f985bcf77f3f215434c8f8c8"
- integrity sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1"
+ integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==
dependencies:
- "@babel/helper-plugin-utils" "^7.24.7"
+ "@babel/helper-plugin-utils" "^7.25.9"
"@babel/plugin-transform-typescript@^7.22.5", "@babel/plugin-transform-typescript@^7.5.0":
version "7.22.9"
@@ -951,6 +991,17 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-syntax-typescript" "^7.22.5"
+"@babel/plugin-transform-typescript@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.9.tgz#69267905c2b33c2ac6d8fe765e9dc2ddc9df3849"
+ integrity sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.25.9"
+ "@babel/helper-create-class-features-plugin" "^7.25.9"
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
+ "@babel/plugin-syntax-typescript" "^7.25.9"
+
"@babel/plugin-transform-unicode-regex@^7.0.0":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183"
@@ -976,7 +1027,7 @@
"@babel/helper-validator-option" "^7.22.5"
"@babel/plugin-transform-flow-strip-types" "^7.22.5"
-"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.16.7":
+"@babel/preset-typescript@^7.13.0":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz#16367d8b01d640e9a507577ed4ee54e0101e51c8"
integrity sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==
@@ -987,6 +1038,17 @@
"@babel/plugin-transform-modules-commonjs" "^7.22.5"
"@babel/plugin-transform-typescript" "^7.22.5"
+"@babel/preset-typescript@^7.16.7":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d"
+ integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.25.9"
+ "@babel/helper-validator-option" "^7.25.9"
+ "@babel/plugin-syntax-jsx" "^7.25.9"
+ "@babel/plugin-transform-modules-commonjs" "^7.25.9"
+ "@babel/plugin-transform-typescript" "^7.25.9"
+
"@babel/register@^7.13.16":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.22.5.tgz#e4d8d0f615ea3233a27b5c6ada6750ee59559939"
@@ -1028,14 +1090,14 @@
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
-"@babel/template@^7.25.0":
- version "7.25.0"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a"
- integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==
+"@babel/template@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016"
+ integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==
dependencies:
- "@babel/code-frame" "^7.24.7"
- "@babel/parser" "^7.25.0"
- "@babel/types" "^7.25.0"
+ "@babel/code-frame" "^7.25.9"
+ "@babel/parser" "^7.25.9"
+ "@babel/types" "^7.25.9"
"@babel/template@^7.25.9":
version "7.25.9"
@@ -1062,16 +1124,16 @@
debug "^4.1.0"
globals "^11.1.0"
-"@babel/traverse@^7.24.7":
- version "7.25.2"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.2.tgz#1a0a4aef53177bead359ccd0c89f4426c805b2ae"
- integrity sha512-s4/r+a7xTnny2O6FcZzqgT6nE4/GHEdcqj4qAeglbUOh0TeglEfmNJFAd/OLoVtGd6ZhAO8GCVvCNUO5t/VJVQ==
+"@babel/traverse@^7.25.9":
+ version "7.25.9"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84"
+ integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==
dependencies:
- "@babel/code-frame" "^7.24.7"
- "@babel/generator" "^7.25.0"
- "@babel/parser" "^7.25.0"
- "@babel/template" "^7.25.0"
- "@babel/types" "^7.25.2"
+ "@babel/code-frame" "^7.25.9"
+ "@babel/generator" "^7.25.9"
+ "@babel/parser" "^7.25.9"
+ "@babel/template" "^7.25.9"
+ "@babel/types" "^7.25.9"
debug "^4.3.1"
globals "^11.1.0"
@@ -1106,14 +1168,13 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
-"@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2":
- version "7.25.2"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.2.tgz#55fb231f7dc958cd69ea141a4c2997e819646125"
- integrity sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==
+"@babel/types@^7.25.9", "@babel/types@^7.26.0":
+ version "7.26.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff"
+ integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==
dependencies:
- "@babel/helper-string-parser" "^7.24.8"
- "@babel/helper-validator-identifier" "^7.24.7"
- to-fast-properties "^2.0.0"
+ "@babel/helper-string-parser" "^7.25.9"
+ "@babel/helper-validator-identifier" "^7.25.9"
"@babel/types@^7.25.9", "@babel/types@^7.26.0":
version "7.26.0"
@@ -2205,9 +2266,9 @@ caniuse-lite@^1.0.30001503:
integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==
caniuse-lite@^1.0.30001669:
- version "1.0.30001679"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz#18c573b72f72ba70822194f6c39e7888597f9e32"
- integrity sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==
+ version "1.0.30001680"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz#5380ede637a33b9f9f1fc6045ea99bd142f3da5e"
+ integrity sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==
chalk@^2.0.0, chalk@^2.4.2:
version "2.4.2"
@@ -2481,11 +2542,11 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.2:
ms "2.1.2"
debug@^4.3.1:
- version "4.3.6"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
- integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
+ version "4.3.7"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+ integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
dependencies:
- ms "2.1.2"
+ ms "^2.1.3"
decamelize@^1.2.0:
version "1.2.0"
@@ -2586,9 +2647,9 @@ electron-to-chromium@^1.4.431:
integrity sha512-TSkRvbXRXD8BwhcGlZXDsbI2lRoP8dvqR7LQnqQNk9KxXBc4tG8O+rTuXgTyIpEdiqSGKEBSqrxdqEntnjNncA==
electron-to-chromium@^1.5.41:
- version "1.5.55"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz#73684752aa2e1aa49cafb355a41386c6637e76a9"
- integrity sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==
+ version "1.5.56"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.56.tgz#3213f369efc3a41091c3b2c05bc0f406108ac1df"
+ integrity sha512-7lXb9dAvimCFdvUMTyucD4mnIndt/xhRKFAlky0CyFogdnNmdPQNoHI23msF/2V4mpTxMzgMdjK4+YRlFlRQZw==
emoji-regex@^8.0.0:
version "8.0.0"
@@ -4245,7 +4306,7 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-ms@2.1.3:
+ms@2.1.3, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
diff --git a/src/components/index.tsx b/src/components/index.tsx
index 4003d9ba..a350c1e8 100644
--- a/src/components/index.tsx
+++ b/src/components/index.tsx
@@ -30,4 +30,5 @@ export * from "./tag";
export * from "./textInput";
export * from "./searchInput";
export * from "./toast";
+export * from "./tooltip";
export * from "./typography";
diff --git a/src/components/tooltip/Arrows.tsx b/src/components/tooltip/Arrows.tsx
new file mode 100644
index 00000000..ae26a246
--- /dev/null
+++ b/src/components/tooltip/Arrows.tsx
@@ -0,0 +1,36 @@
+import Svg, { Path } from 'react-native-svg';
+import React from 'react';
+import { IOColors } from '../../core';
+
+export const LeftArrow = ({ color = IOColors.white }: { color?: string }) => (
+
+);
+export const RightArrow = ({ color = IOColors.white }: { color?: string }) => (
+
+);
+export const BottomArrow = ({ color = IOColors.white }: { color?: string }) => (
+
+);
+export const TopArrow = ({ color = IOColors.white }: { color?: string }) => (
+
+);
\ No newline at end of file
diff --git a/src/components/tooltip/Tooltip.tsx b/src/components/tooltip/Tooltip.tsx
new file mode 100644
index 00000000..543a11e4
--- /dev/null
+++ b/src/components/tooltip/Tooltip.tsx
@@ -0,0 +1,313 @@
+import React, {
+ useState,
+ useRef,
+ PropsWithChildren,
+ useEffect,
+ useCallback,
+ JSXElementConstructor,
+ useMemo,
+ ReactElement
+} from "react";
+import {
+ View,
+ Modal,
+ Dimensions,
+ LayoutChangeEvent,
+ TouchableWithoutFeedback
+} from "react-native";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+import { every, some } from "lodash";
+import { IOColors } from "../../core";
+import { Body, H6 } from "../typography";
+import { IconButton } from "../buttons";
+import { BottomArrow, LeftArrow, RightArrow, TopArrow } from "./Arrows";
+import {
+ ARROW_HEIGHT,
+ EMPTY_SPACE,
+ getArrowBoxByPlacement,
+ getArrowCoords,
+ getArrowVerticalAlignment,
+ getDisplayInsets,
+ getTooltipCoords,
+ getTooltipVerticalAlignment,
+ isDefined,
+ isNotZero
+} from "./utils";
+import { getChildrenPosition, tooltipStyles } from "./styles";
+import {
+ ChildrenCoords,
+ DisplayInsets,
+ Placement,
+ TooltipLayout
+} from "./utils/types";
+
+const screenDimensions = Dimensions.get("window");
+const INITIAL_COORDS: ChildrenCoords = {
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0
+};
+const ARROWS_BY_PLACEMENT: Record<
+ Placement,
+ JSXElementConstructor<{ color: string }>
+> = {
+ top: TopArrow,
+ bottom: BottomArrow,
+ left: LeftArrow,
+ right: RightArrow
+};
+
+type CommonProps = {
+ /**
+ * The title text displayed at the top of the tooltip.
+ */
+ title: string;
+ /**
+ * The tooltip text content.
+ */
+ content: string;
+ /**
+ * Controls the visibility of the tooltip.
+ */
+ isVisible: boolean;
+ /**
+ * Initial tooltip position; can be 'top', 'bottom', 'left', or 'right'.
+ * @default top
+ */
+ placement?: Placement;
+ /**
+ * Insets for adjusting tooltip position within screen boundaries.
+ * @default {}
+ */
+ displayInsets?: Partial;
+ /**
+ * Accessibility label for the close icon button.
+ */
+ closeIconAccessibilityLabel: string;
+ /**
+ * Determines whether interactions with the tooltip's children are allowed when `isVisible` is set to true.
+ * @default false
+ */
+ childrenInteractionsEnabled?: boolean;
+ /**
+ * Callback function triggered when the tooltip is closed.
+ */
+ onClose: () => void;
+};
+type CloseWithTapOnBackground = {
+ /**
+ * Allows closing the tooltip by tapping outside of it.
+ */
+ allowCloseOnBackgroundTap: true;
+ /**
+ * Accessibility label for the tooltip background mask.
+ */
+ backgroundAccessibilityLabel: string;
+};
+type CloseWithBackgroundTapDisabled = {
+ allowCloseOnBackgroundTap?: false;
+};
+type Props = CommonProps & (CloseWithTapOnBackground | CloseWithBackgroundTapDisabled);
+
+/**
+ * Tooltip component that displays a contextual tooltip around its children.
+ * The tooltip position is controlled by the `placement` prop and can adjust
+ * dynamically if there is insufficient space.
+ * @param {Props} props - The component props
+ *
+ * @returns {ReactElement} A tooltip component rendered around the specified children.
+ */
+export const Tooltip = ({
+ children,
+ title,
+ content,
+ placement: initialPlacement = "top",
+ closeIconAccessibilityLabel,
+ isVisible,
+ displayInsets = {},
+ allowCloseOnBackgroundTap,
+ childrenInteractionsEnabled = false,
+ onClose
+}: PropsWithChildren): ReactElement => {
+ const insets = useSafeAreaInsets();
+ const [currentPlacement, setCurrentPlacement] =
+ useState(initialPlacement);
+ const [childrenCoords, setChildrenCoords] = useState(INITIAL_COORDS);
+ const [tooltipLayout, setTooltipLayout] = useState();
+ const childRef = useRef(null);
+ const titleRef = useRef(null);
+ const timeoutRef = useRef>();
+
+ const Arrow = useMemo(
+ () => ARROWS_BY_PLACEMENT[currentPlacement],
+ [currentPlacement]
+ );
+ const isChildrenMeasurementFinished =
+ every(childrenCoords, isDefined)
+ && some(childrenCoords, isNotZero);
+ const isTooltipMeasurementCompleted = isDefined(tooltipLayout);
+ const tooltipVisibility = { opacity: isTooltipMeasurementCompleted ? 1 : 0 };
+
+ /**
+ * This function sets the `Tooltip` children coordinates
+ */
+ const measureChildrenCoords = useCallback(() => {
+ if (childRef.current && typeof childRef.current.measure === "function") {
+ childRef.current.measure((_, __, width, height, px, py) => {
+ const coords = {
+ x: px,
+ y: py,
+ width,
+ height
+ };
+ if (every(coords, isDefined)) {
+ setChildrenCoords(coords);
+ }
+ });
+ }
+ }, []);
+
+ useEffect(() => {
+ if (isVisible) {
+ // A new measure is executed every time the `Tooltip` is visible
+ // This is required for use within ScrollView components.
+ // eslint-disable-next-line functional/immutable-data
+ timeoutRef.current = setTimeout(measureChildrenCoords, 100);
+ } else {
+ setChildrenCoords(INITIAL_COORDS);
+ setCurrentPlacement(initialPlacement);
+ }
+
+ return () => {
+ if (isVisible) {
+ clearTimeout(timeoutRef.current);
+ }
+ };
+ }, [isVisible, initialPlacement, measureChildrenCoords]);
+
+ /**
+ * This function works with `top` and `bottom` placement and sets the current placement to their opposite value
+ * if in the selected one there is no space to prompt the tooltip
+ */
+ const invertPlacementIfNeeded = useCallback(
+ (nativeEvent: LayoutChangeEvent["nativeEvent"]) => {
+ if (initialPlacement === "top") {
+ const hasSpace = nativeEvent.layout.y >= insets.top;
+
+ if (!hasSpace) {
+ setCurrentPlacement("bottom");
+ }
+ }
+ if (initialPlacement === "bottom") {
+ const remainingSpace =
+ screenDimensions.height - nativeEvent.layout.y - insets.bottom;
+ const tooltipMinHeight =
+ nativeEvent.layout.height + ARROW_HEIGHT + EMPTY_SPACE;
+ const hasSpace = remainingSpace >= tooltipMinHeight;
+
+ if (!hasSpace) {
+ setCurrentPlacement("top");
+ }
+ }
+ },
+ [insets.bottom, insets.top, initialPlacement]
+ );
+
+ const handleTooltipOnLayout = useCallback(
+ ({ nativeEvent }: LayoutChangeEvent) => {
+ invertPlacementIfNeeded(nativeEvent);
+ setTooltipLayout(nativeEvent.layout);
+ },
+ [invertPlacementIfNeeded]
+ );
+
+ const handleTapOnBackground = useCallback(() => {
+ if (allowCloseOnBackgroundTap) {
+ onClose();
+ }
+ }, [allowCloseOnBackgroundTap, onClose]);
+
+ return (
+ <>
+
+ {children}
+
+
+
+ {children}
+
+
+
+
+
+ {title}
+
+
+
+ {content}
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/components/tooltip/index.ts b/src/components/tooltip/index.ts
new file mode 100644
index 00000000..734bb357
--- /dev/null
+++ b/src/components/tooltip/index.ts
@@ -0,0 +1 @@
+export * from "./Tooltip";
\ No newline at end of file
diff --git a/src/components/tooltip/styles.ts b/src/components/tooltip/styles.ts
new file mode 100644
index 00000000..0443a7dd
--- /dev/null
+++ b/src/components/tooltip/styles.ts
@@ -0,0 +1,44 @@
+import { StyleSheet } from 'react-native';
+import { IOColors } from '../../core';
+import { ChildrenCoords } from './utils/types';
+
+export const tooltipStyles = StyleSheet.create({
+ overlay: {
+ position: "absolute",
+ width: "100%",
+ height: "100%",
+ backgroundColor: IOColors["grey-850"],
+ opacity: 0.6,
+ zIndex: 997
+ },
+ childrenContainer: {
+ position: "absolute",
+ zIndex: 1000
+ },
+ tooltipContainer: {
+ position: "absolute",
+ paddingHorizontal: 16,
+ paddingVertical: 16,
+ backgroundColor: IOColors.white,
+ borderRadius: 8,
+ zIndex: 2000,
+ overflow: "visible"
+ },
+ arrowContainer: {
+ position: "absolute",
+ display: 'flex',
+ zIndex: 3000
+ },
+ closeIcon: {
+ position: 'absolute',
+ right: 8,
+ top: 9 // It's been used `9` instead of `8` to fix accessibility focus order. In this way title is read before close icon.
+ }
+});
+
+export const getChildrenPosition = (childrenCoords: ChildrenCoords) => ({
+ top: childrenCoords.y,
+ left: childrenCoords.x,
+ width: childrenCoords.width,
+ height: childrenCoords.height
+});
\ No newline at end of file
diff --git a/src/components/tooltip/utils/index.ts b/src/components/tooltip/utils/index.ts
new file mode 100644
index 00000000..4090aa02
--- /dev/null
+++ b/src/components/tooltip/utils/index.ts
@@ -0,0 +1,179 @@
+import { ScaledSize } from 'react-native';
+import { IOVisualCostants } from '../../../core';
+import { ChildrenCoords, DisplayInsets, Placement } from './types';
+
+export const ARROW_WIDTH = 24;
+export const ARROW_HEIGHT = 14;
+export const EMPTY_SPACE = 8;
+const DEFAULT_INSETS: DisplayInsets = {
+ top: 0,
+ bottom: 0,
+ left: IOVisualCostants.appMarginDefault,
+ right: IOVisualCostants.appMarginDefault,
+};
+
+/**
+ * @param displayInsets custom display insets
+ * @returns An `object` based on `DEFAULT_INSETS` and `displayInsets`
+ */
+export const getDisplayInsets = (
+ displayInsets: Partial
+): DisplayInsets => ({ ...DEFAULT_INSETS, ...displayInsets });
+
+/**
+ *
+ * @param placement The `Tooltip` placement
+ * @returns The `Arrow` box `width` and `height` based on `placement` value
+ */
+export const getArrowBoxByPlacement = (placement: Placement) => {
+ switch (placement) {
+ case 'left':
+ case 'right':
+ return {
+ width: ARROW_HEIGHT,
+ height: ARROW_WIDTH,
+ };
+ default:
+ return {
+ height: ARROW_HEIGHT,
+ width: ARROW_WIDTH,
+ };
+ }
+};
+
+/**
+ * A utility function to calculate the `Tooltip` coordinates and dimensions
+ * @param placement The `Tooltip` placement in relation of its children
+ * @param childrenCoords The measures in screen of the `Tooltip` children
+ * @param displayInsets The active display insets
+ * @param screenDimensions The dimensions of the device screen
+ * @returns The `Tooltip` coordinates
+ */
+export const getTooltipCoords = (
+ placement: Placement,
+ childrenCoords: ChildrenCoords,
+ displayInsets: DisplayInsets,
+ screenDimensions: ScaledSize
+) => {
+ const { width: screenWidth, height: screenHeight } = screenDimensions;
+
+ switch (placement) {
+ case "top":
+ return {
+ bottom: screenHeight - childrenCoords.y + ARROW_HEIGHT + EMPTY_SPACE,
+ left: displayInsets.left,
+ width: screenWidth - displayInsets.left - displayInsets.right
+ };
+ case "bottom":
+ return {
+ top: childrenCoords.y + childrenCoords.height + ARROW_HEIGHT + EMPTY_SPACE,
+ left: displayInsets.left,
+ width: screenWidth - displayInsets.left - displayInsets.right
+ };
+ case "left":
+ return {
+ top: childrenCoords.y,
+ left: displayInsets.left,
+ width:
+ screenWidth - (screenWidth - childrenCoords.x) - ARROW_HEIGHT - displayInsets.left - EMPTY_SPACE
+ };
+ case "right":
+ const elementSize = childrenCoords.width + childrenCoords.x + ARROW_HEIGHT + EMPTY_SPACE;
+
+ return {
+ top: childrenCoords.y,
+ left: elementSize,
+ width:
+ screenWidth -
+ (elementSize + displayInsets.right)
+ };
+ // TODO: provide a default center position in case of Tooltip without children
+ default:
+ return {};
+ }
+};
+
+/**
+ * A utility function to calculate the `Tooltip`'s `Arrow` coordinates
+ * @param placement The `Arrow` placement in relation of the `Tooltip` children
+ * @param childrenCoords The measures in screen of the `Tooltip` children
+ * @param screenDimensions The active display insets
+ * @returns The `Tooltip`'s Arrow coordinates
+ */
+export const getArrowCoords = (
+ placement: Placement,
+ childrenCoords: ChildrenCoords,
+ screenDimensions: ScaledSize
+) => {
+ const { width: screenWidth, height: screenHeight } = screenDimensions;
+
+ switch (placement) {
+ case "top":
+ return {
+ bottom: screenHeight - childrenCoords.y + EMPTY_SPACE,
+ left: childrenCoords.x + childrenCoords.width / 2 - ARROW_WIDTH / 2
+ };
+ case "bottom":
+ return {
+ top: childrenCoords.y + childrenCoords.height + EMPTY_SPACE,
+ left: childrenCoords.x + childrenCoords.width / 2 - ARROW_WIDTH / 2
+ };
+ case "left":
+ return {
+ top: childrenCoords.y,
+ 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
+ };
+ case "right":
+ return {
+ top: childrenCoords.y,
+ left: childrenCoords.width + childrenCoords.x + EMPTY_SPACE
+ };
+ default:
+ // TODO: provide a default center position in case of Tooltip without children
+ return {};
+ }
+};
+
+/**
+ * A utility function to calculate the `Tooltip` vertical alignment
+ * @param placement The `Tooltip` placement in relation of its children
+ * @param childrenHeight The `Tooltip`'s children height
+ * @param tooltipHeight The `Tooltip`'s height
+ * @returns If placement is `left` or `right` it returns the vertical tranlsation to align the `Tooltip` center with its `children` center,
+ * otherwise `null` is returned
+ */
+export const getTooltipVerticalAlignment = (placement: Placement, childrenHeight: number, tooltipHeight?: number) => {
+ if ((placement === "left" || placement === "right") && tooltipHeight) {
+ return {
+ transform: [
+ {
+ translateY:
+ -tooltipHeight / 2 + childrenHeight / 2
+ }
+ ]
+ };
+ }
+ return null;
+};
+
+/**
+ * A utility function to calculate the `Arrow` vertical alignment
+ * @param placement The `Tooltip` placement in relation of its children
+ * @param childrenHeight The `Tooltip`'s children height
+*/
+export const getArrowVerticalAlignment = (placement: Placement, childrenHeight: number) => {
+ if (placement === "left" || placement === "right") {
+ return {
+ transform: [
+ {
+ translateY:
+ -ARROW_WIDTH / 2 + childrenHeight / 2
+ }
+ ]
+ };
+ }
+ return null;
+};
+
+export const isDefined = (v: T) => v !== undefined;
+export const isNotZero = (v: number) => v !== 0;
\ No newline at end of file
diff --git a/src/components/tooltip/utils/types.ts b/src/components/tooltip/utils/types.ts
new file mode 100644
index 00000000..206713f4
--- /dev/null
+++ b/src/components/tooltip/utils/types.ts
@@ -0,0 +1,9 @@
+export type DisplayInsets = Record;
+export type Placement = "top" | "bottom" | "left" | "right";
+export type ChildrenCoords = {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+};
+export type TooltipLayout = ChildrenCoords;
\ No newline at end of file