@telefonica/mistica 16.58.0-beta.2 → 16.59.0-beta.1

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 (539) hide show
  1. package/css/mistica.css +1 -1
  2. package/dist/accordion.css-mistica.js +6 -6
  3. package/dist/align.css-mistica.js +1 -1
  4. package/dist/autocomplete.css-mistica.js +1 -1
  5. package/dist/avatar.css-mistica.js +1 -1
  6. package/dist/badge.css-mistica.js +1 -1
  7. package/dist/box.css-mistica.js +13 -13
  8. package/dist/boxed.css-mistica.js +24 -24
  9. package/dist/button-group.css-mistica.js +1 -1
  10. package/dist/button-layout.css-mistica.js +14 -14
  11. package/dist/button.css-mistica.js +30 -30
  12. package/dist/callout.css-mistica.js +11 -11
  13. package/dist/card-internal.css-mistica.js +15 -15
  14. package/dist/carousel.css-mistica.js +8 -8
  15. package/dist/checkbox.css-mistica.js +11 -11
  16. package/dist/chip.css-mistica.js +15 -15
  17. package/dist/circle.css-mistica.js +1 -1
  18. package/dist/community/advanced-data-card.css-mistica.js +6 -6
  19. package/dist/community/blocks.css-mistica.js +1 -1
  20. package/dist/community/example-component.css-mistica.js +1 -1
  21. package/dist/counter.css-mistica.js +1 -1
  22. package/dist/cover-hero.css-mistica.js +2 -2
  23. package/dist/credit-card-number-field.css-mistica.js +3 -3
  24. package/dist/date-field.css-mistica.js +1 -1
  25. package/dist/date-time-picker.css-mistica.js +1 -1
  26. package/dist/dialog.css-mistica.js +4 -4
  27. package/dist/divider.css-mistica.js +5 -5
  28. package/dist/double-field.css-mistica.js +2 -2
  29. package/dist/drawer.css-mistica.js +1 -1
  30. package/dist/empty-state-card.css-mistica.js +1 -1
  31. package/dist/empty-state.css-mistica.js +5 -5
  32. package/dist/fade-in.css-mistica.js +1 -1
  33. package/dist/feedback.css-mistica.js +1 -1
  34. package/dist/file-upload.css-mistica.js +7 -7
  35. package/dist/fixed-footer-layout.css-mistica.js +2 -2
  36. package/dist/form.css-mistica.js +1 -1
  37. package/dist/grid-layout.css-mistica.js +3 -3
  38. package/dist/grid.css-mistica.js +120 -120
  39. package/dist/header.css-mistica.js +1 -1
  40. package/dist/hero.css-mistica.js +2 -2
  41. package/dist/horizontal-scroll.css-mistica.js +1 -1
  42. package/dist/icon-button.css-mistica.js +53 -53
  43. package/dist/icons/icon-chevron.css-mistica.js +2 -2
  44. package/dist/icons/icon-error.css-mistica.js +1 -1
  45. package/dist/image.css-mistica.js +2 -2
  46. package/dist/image.js +31 -30
  47. package/dist/inline.css-mistica.js +9 -9
  48. package/dist/list.css-mistica.js +1 -1
  49. package/dist/loading-bar.css-mistica.js +1 -1
  50. package/dist/loading-screen.css-mistica.js +4 -4
  51. package/dist/logo.css-mistica.js +5 -5
  52. package/dist/menu.css-mistica.js +13 -13
  53. package/dist/mosaic.css-mistica.js +1 -1
  54. package/dist/navigation-bar.css-mistica.js +18 -18
  55. package/dist/navigation-breadcrumbs.css-mistica.js +1 -1
  56. package/dist/package-version.js +1 -1
  57. package/dist/pin-field.css-mistica.js +1 -1
  58. package/dist/popover.css-mistica.js +1 -1
  59. package/dist/progress-bar.css-mistica.js +6 -6
  60. package/dist/radio-button.css-mistica.js +19 -19
  61. package/dist/rating.css-mistica.js +2 -2
  62. package/dist/responsive-layout.css-mistica.js +6 -6
  63. package/dist/screen-reader-only.css-mistica.js +1 -1
  64. package/dist/select.css-mistica.js +15 -15
  65. package/dist/sheet-action-row.css-mistica.js +1 -1
  66. package/dist/sheet-common.css-mistica.js +1 -1
  67. package/dist/sheet-info.css-mistica.js +1 -1
  68. package/dist/skeletons.css-mistica.js +6 -6
  69. package/dist/skins/skin-contract.css-mistica.js +684 -684
  70. package/dist/skip-link.css-mistica.js +1 -1
  71. package/dist/slider.css-mistica.js +18 -18
  72. package/dist/snackbar.css-mistica.js +4 -4
  73. package/dist/spinner.css-mistica.js +1 -1
  74. package/dist/square.css-mistica.js +1 -1
  75. package/dist/stack.css-mistica.js +5 -5
  76. package/dist/stacking-group.css-mistica.js +1 -1
  77. package/dist/stepper.css-mistica.js +2 -2
  78. package/dist/switch-component.css-mistica.js +35 -35
  79. package/dist/table.css-mistica.js +9 -9
  80. package/dist/tabs.css-mistica.js +17 -17
  81. package/dist/tag.css-mistica.js +1 -1
  82. package/dist/text-field-base.css-mistica.js +15 -15
  83. package/dist/text-field-components.css-mistica.js +3 -3
  84. package/dist/text-link.css-mistica.js +6 -6
  85. package/dist/text.css-mistica.js +6 -6
  86. package/dist/theme-context.css-mistica.js +1 -1
  87. package/dist/timeline.css-mistica.js +9 -9
  88. package/dist/timer.css-mistica.js +6 -6
  89. package/dist/tooltip.css-mistica.js +1 -1
  90. package/dist/touchable.css-mistica.js +1 -1
  91. package/dist/utils/aspect-ratio-support.css-mistica.js +2 -2
  92. package/dist/video.css-mistica.js +1 -1
  93. package/dist/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +1 -1
  94. package/dist-es/accordion.css-mistica.js +6 -6
  95. package/dist-es/align.css-mistica.js +1 -1
  96. package/dist-es/autocomplete.css-mistica.js +1 -1
  97. package/dist-es/avatar.css-mistica.js +1 -1
  98. package/dist-es/badge.css-mistica.js +1 -1
  99. package/dist-es/box.css-mistica.js +13 -13
  100. package/dist-es/boxed.css-mistica.js +23 -23
  101. package/dist-es/button-group.css-mistica.js +1 -1
  102. package/dist-es/button-layout.css-mistica.js +14 -14
  103. package/dist-es/button.css-mistica.js +30 -30
  104. package/dist-es/callout.css-mistica.js +11 -11
  105. package/dist-es/card-internal.css-mistica.js +15 -15
  106. package/dist-es/carousel.css-mistica.js +8 -8
  107. package/dist-es/checkbox.css-mistica.js +11 -11
  108. package/dist-es/chip.css-mistica.js +15 -15
  109. package/dist-es/circle.css-mistica.js +1 -1
  110. package/dist-es/community/advanced-data-card.css-mistica.js +6 -6
  111. package/dist-es/community/blocks.css-mistica.js +1 -1
  112. package/dist-es/community/example-component.css-mistica.js +1 -1
  113. package/dist-es/counter.css-mistica.js +1 -1
  114. package/dist-es/cover-hero.css-mistica.js +2 -2
  115. package/dist-es/credit-card-number-field.css-mistica.js +3 -3
  116. package/dist-es/date-field.css-mistica.js +1 -1
  117. package/dist-es/date-time-picker.css-mistica.js +1 -1
  118. package/dist-es/dialog.css-mistica.js +4 -4
  119. package/dist-es/divider.css-mistica.js +5 -5
  120. package/dist-es/double-field.css-mistica.js +2 -2
  121. package/dist-es/drawer.css-mistica.js +1 -1
  122. package/dist-es/empty-state-card.css-mistica.js +1 -1
  123. package/dist-es/empty-state.css-mistica.js +5 -5
  124. package/dist-es/fade-in.css-mistica.js +1 -1
  125. package/dist-es/feedback.css-mistica.js +1 -1
  126. package/dist-es/file-upload.css-mistica.js +7 -7
  127. package/dist-es/fixed-footer-layout.css-mistica.js +2 -2
  128. package/dist-es/form.css-mistica.js +1 -1
  129. package/dist-es/grid-layout.css-mistica.js +3 -3
  130. package/dist-es/grid.css-mistica.js +120 -120
  131. package/dist-es/header.css-mistica.js +1 -1
  132. package/dist-es/hero.css-mistica.js +2 -2
  133. package/dist-es/horizontal-scroll.css-mistica.js +1 -1
  134. package/dist-es/icon-button.css-mistica.js +53 -53
  135. package/dist-es/icons/icon-chevron.css-mistica.js +2 -2
  136. package/dist-es/icons/icon-error.css-mistica.js +1 -1
  137. package/dist-es/image.css-mistica.js +2 -2
  138. package/dist-es/image.js +33 -32
  139. package/dist-es/inline.css-mistica.js +9 -9
  140. package/dist-es/list.css-mistica.js +1 -1
  141. package/dist-es/loading-bar.css-mistica.js +1 -1
  142. package/dist-es/loading-screen.css-mistica.js +4 -4
  143. package/dist-es/logo.css-mistica.js +5 -5
  144. package/dist-es/menu.css-mistica.js +13 -13
  145. package/dist-es/mosaic.css-mistica.js +1 -1
  146. package/dist-es/navigation-bar.css-mistica.js +18 -18
  147. package/dist-es/navigation-breadcrumbs.css-mistica.js +1 -1
  148. package/dist-es/package-version.js +1 -1
  149. package/dist-es/pin-field.css-mistica.js +1 -1
  150. package/dist-es/popover.css-mistica.js +1 -1
  151. package/dist-es/progress-bar.css-mistica.js +6 -6
  152. package/dist-es/radio-button.css-mistica.js +19 -19
  153. package/dist-es/rating.css-mistica.js +2 -2
  154. package/dist-es/responsive-layout.css-mistica.js +6 -6
  155. package/dist-es/screen-reader-only.css-mistica.js +1 -1
  156. package/dist-es/select.css-mistica.js +15 -15
  157. package/dist-es/sheet-action-row.css-mistica.js +1 -1
  158. package/dist-es/sheet-common.css-mistica.js +1 -1
  159. package/dist-es/sheet-info.css-mistica.js +1 -1
  160. package/dist-es/skeletons.css-mistica.js +6 -6
  161. package/dist-es/skins/skin-contract.css-mistica.js +684 -684
  162. package/dist-es/skip-link.css-mistica.js +1 -1
  163. package/dist-es/slider.css-mistica.js +18 -18
  164. package/dist-es/snackbar.css-mistica.js +4 -4
  165. package/dist-es/spinner.css-mistica.js +1 -1
  166. package/dist-es/square.css-mistica.js +1 -1
  167. package/dist-es/stack.css-mistica.js +5 -5
  168. package/dist-es/stacking-group.css-mistica.js +1 -1
  169. package/dist-es/stepper.css-mistica.js +2 -2
  170. package/dist-es/style.css +1 -1
  171. package/dist-es/switch-component.css-mistica.js +35 -35
  172. package/dist-es/table.css-mistica.js +9 -9
  173. package/dist-es/tabs.css-mistica.js +17 -17
  174. package/dist-es/tag.css-mistica.js +1 -1
  175. package/dist-es/text-field-base.css-mistica.js +15 -15
  176. package/dist-es/text-field-components.css-mistica.js +3 -3
  177. package/dist-es/text-link.css-mistica.js +6 -6
  178. package/dist-es/text.css-mistica.js +6 -6
  179. package/dist-es/theme-context.css-mistica.js +1 -1
  180. package/dist-es/timeline.css-mistica.js +9 -9
  181. package/dist-es/timer.css-mistica.js +6 -6
  182. package/dist-es/tooltip.css-mistica.js +1 -1
  183. package/dist-es/touchable.css-mistica.js +1 -1
  184. package/dist-es/utils/aspect-ratio-support.css-mistica.js +2 -2
  185. package/dist-es/video.css-mistica.js +1 -1
  186. package/dist-es/vivinho-loading-animation/vivinho-loading-animation.css-mistica.js +1 -1
  187. package/doc/llms.md +35 -11
  188. package/doc/patterns.md +22 -20
  189. package/package.json +13 -1
  190. package/src/accordion.css.ts +121 -0
  191. package/src/accordion.tsx +366 -0
  192. package/src/align.css.ts +7 -0
  193. package/src/align.tsx +32 -0
  194. package/src/autocomplete.css.ts +62 -0
  195. package/src/autocomplete.tsx +239 -0
  196. package/src/avatar.css.ts +14 -0
  197. package/src/avatar.tsx +120 -0
  198. package/src/badge.css.ts +51 -0
  199. package/src/badge.tsx +79 -0
  200. package/src/box.css.ts +51 -0
  201. package/src/box.tsx +114 -0
  202. package/src/boxed.css.ts +132 -0
  203. package/src/boxed.tsx +153 -0
  204. package/src/button-fixed-footer-layout.tsx +62 -0
  205. package/src/button-group.css.ts +75 -0
  206. package/src/button-group.tsx +91 -0
  207. package/src/button-layout.css.ts +162 -0
  208. package/src/button-layout.tsx +91 -0
  209. package/src/button.css.ts +758 -0
  210. package/src/button.tsx +632 -0
  211. package/src/callout.css.ts +50 -0
  212. package/src/callout.tsx +147 -0
  213. package/src/card-cover.tsx +242 -0
  214. package/src/card-data.tsx +152 -0
  215. package/src/card-internal.css.ts +271 -0
  216. package/src/card-internal.tsx +1724 -0
  217. package/src/card-media.tsx +157 -0
  218. package/src/card-naked.tsx +63 -0
  219. package/src/carousel.css.ts +522 -0
  220. package/src/carousel.tsx +1300 -0
  221. package/src/checkbox.css.ts +94 -0
  222. package/src/checkbox.tsx +192 -0
  223. package/src/chip.css.ts +204 -0
  224. package/src/chip.tsx +191 -0
  225. package/src/circle.css.ts +14 -0
  226. package/src/circle.tsx +52 -0
  227. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-actions-button-and-link-footer-image-false-1-snap.png +0 -0
  228. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-actions-button-and-link-footer-image-true-1-snap.png +0 -0
  229. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-actions-button-footer-image-false-1-snap.png +0 -0
  230. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-actions-button-footer-image-true-1-snap.png +0 -0
  231. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-actions-link-footer-image-false-1-snap.png +0 -0
  232. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-actions-link-footer-image-true-1-snap.png +0 -0
  233. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-actions-none-footer-image-false-1-snap.png +0 -0
  234. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-actions-none-footer-image-true-1-snap.png +0 -0
  235. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-extras-0-1-snap.png +0 -0
  236. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-extras-1-1-snap.png +0 -0
  237. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-extras-3-1-snap.png +0 -0
  238. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-extras-3-no-divider-1-snap.png +0 -0
  239. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-inside-carousel-1-snap.png +0 -0
  240. package/src/community/__screenshot_tests__/__image_snapshots__/advanced-data-card-screenshot-test-tsx-advanced-data-card-without-stacking-group-with-top-actions-and-too-long-title-1-snap.png +0 -0
  241. package/src/community/__screenshot_tests__/__image_snapshots__/blocks-screenshot-test-tsx-blocks-highlighted-value-block-1-snap.png +0 -0
  242. package/src/community/__screenshot_tests__/__image_snapshots__/blocks-screenshot-test-tsx-blocks-information-block-1-snap.png +0 -0
  243. package/src/community/__screenshot_tests__/__image_snapshots__/blocks-screenshot-test-tsx-blocks-progress-block-1-snap.png +0 -0
  244. package/src/community/__screenshot_tests__/__image_snapshots__/blocks-screenshot-test-tsx-blocks-progress-block-2-snap.png +0 -0
  245. package/src/community/__screenshot_tests__/__image_snapshots__/blocks-screenshot-test-tsx-blocks-row-block-1-snap.png +0 -0
  246. package/src/community/__screenshot_tests__/__image_snapshots__/blocks-screenshot-test-tsx-blocks-simple-block-1-snap.png +0 -0
  247. package/src/community/__screenshot_tests__/__image_snapshots__/blocks-screenshot-test-tsx-blocks-value-block-1-snap.png +0 -0
  248. package/src/community/__screenshot_tests__/advanced-data-card-screenshot-test.tsx +84 -0
  249. package/src/community/__screenshot_tests__/blocks-screenshot-test.tsx +72 -0
  250. package/src/community/__stories__/advanced-data-card-carousel-story.tsx +66 -0
  251. package/src/community/__stories__/advanced-data-card-story.tsx +158 -0
  252. package/src/community/__stories__/blocks-story.tsx +272 -0
  253. package/src/community/__stories__/example-component-story.tsx +15 -0
  254. package/src/community/__stories__/index-story.tsx +154 -0
  255. package/src/community/__type_tests__/advanced-data-card-type-test.tsx +40 -0
  256. package/src/community/advanced-data-card.css.ts +271 -0
  257. package/src/community/advanced-data-card.tsx +431 -0
  258. package/src/community/blocks.css.ts +12 -0
  259. package/src/community/blocks.tsx +290 -0
  260. package/src/community/example-component.css.ts +7 -0
  261. package/src/community/example-component.tsx +17 -0
  262. package/src/community/index.tsx +10 -0
  263. package/src/counter.css.ts +150 -0
  264. package/src/counter.tsx +215 -0
  265. package/src/cover-hero-media.tsx +39 -0
  266. package/src/cover-hero.css.ts +133 -0
  267. package/src/cover-hero.tsx +262 -0
  268. package/src/credit-card-expiration-field.tsx +187 -0
  269. package/src/credit-card-fields.tsx +56 -0
  270. package/src/credit-card-number-field.css.ts +47 -0
  271. package/src/credit-card-number-field.tsx +245 -0
  272. package/src/cvv-field.tsx +169 -0
  273. package/src/date-field.css.ts +14 -0
  274. package/src/date-field.tsx +130 -0
  275. package/src/date-time-field.tsx +141 -0
  276. package/src/date-time-picker.css.ts +126 -0
  277. package/src/date-time-picker.tsx +188 -0
  278. package/src/decimal-field.tsx +160 -0
  279. package/src/desktop-container-type-context.tsx +15 -0
  280. package/src/dialog-context.tsx +81 -0
  281. package/src/dialog.css.ts +155 -0
  282. package/src/dialog.tsx +423 -0
  283. package/src/divider.css.ts +10 -0
  284. package/src/divider.tsx +11 -0
  285. package/src/double-field.css.ts +33 -0
  286. package/src/double-field.tsx +71 -0
  287. package/src/drawer.css.ts +123 -0
  288. package/src/drawer.tsx +304 -0
  289. package/src/email-field.tsx +76 -0
  290. package/src/empty-state-card.css.ts +40 -0
  291. package/src/empty-state-card.tsx +92 -0
  292. package/src/empty-state.css.ts +119 -0
  293. package/src/empty-state.tsx +141 -0
  294. package/src/fade-in.css.ts +12 -0
  295. package/src/fade-in.tsx +40 -0
  296. package/src/feedback.css.ts +119 -0
  297. package/src/feedback.tsx +432 -0
  298. package/src/file-upload.css.ts +156 -0
  299. package/src/file-upload.tsx +612 -0
  300. package/src/fixed-footer-layout.css.ts +96 -0
  301. package/src/fixed-footer-layout.tsx +215 -0
  302. package/src/fixed-to-top.tsx +21 -0
  303. package/src/focus-trap.tsx +17 -0
  304. package/src/form-context.tsx +198 -0
  305. package/src/form.css.ts +5 -0
  306. package/src/form.tsx +287 -0
  307. package/src/grid-layout.css.ts +68 -0
  308. package/src/grid-layout.tsx +201 -0
  309. package/src/grid.css.ts +203 -0
  310. package/src/grid.tsx +241 -0
  311. package/src/header.css.ts +30 -0
  312. package/src/header.tsx +319 -0
  313. package/src/hero.css.ts +71 -0
  314. package/src/hero.tsx +318 -0
  315. package/src/hooks.tsx +313 -0
  316. package/src/horizontal-scroll.css.ts +43 -0
  317. package/src/horizontal-scroll.tsx +18 -0
  318. package/src/iban-field.tsx +218 -0
  319. package/src/icon-button.css.ts +561 -0
  320. package/src/icon-button.tsx +221 -0
  321. package/src/icons/__stories__/mistica-icons-story.tsx +192 -0
  322. package/src/icons/icon-amex.tsx +40 -0
  323. package/src/icons/icon-chevron.css.ts +23 -0
  324. package/src/icons/icon-chevron.tsx +150 -0
  325. package/src/icons/icon-cvv-amex.tsx +31 -0
  326. package/src/icons/icon-cvv-visa-mc.tsx +31 -0
  327. package/src/icons/icon-error.css.ts +27 -0
  328. package/src/icons/icon-error.tsx +207 -0
  329. package/src/icons/icon-info.tsx +86 -0
  330. package/src/icons/icon-mastercard.tsx +36 -0
  331. package/src/icons/icon-success-vivo-new.tsx +51 -0
  332. package/src/icons/icon-success-vivo.tsx +36 -0
  333. package/src/icons/icon-success.tsx +211 -0
  334. package/src/icons/icon-visa.tsx +32 -0
  335. package/src/image.css.ts +48 -0
  336. package/src/image.tsx +345 -0
  337. package/src/index.tsx +2466 -0
  338. package/src/inline.css.ts +131 -0
  339. package/src/inline.tsx +135 -0
  340. package/src/integer-field.tsx +93 -0
  341. package/src/list.css.ts +281 -0
  342. package/src/list.tsx +963 -0
  343. package/src/loading-bar.css.ts +69 -0
  344. package/src/loading-bar.tsx +25 -0
  345. package/src/loading-screen.css.ts +114 -0
  346. package/src/loading-screen.tsx +376 -0
  347. package/src/logo-blau-shell.tsx +30 -0
  348. package/src/logo-blau.tsx +60 -0
  349. package/src/logo-common.tsx +29 -0
  350. package/src/logo-esimflag-shell.tsx +30 -0
  351. package/src/logo-esimflag.tsx +56 -0
  352. package/src/logo-movistar-new-shell.tsx +30 -0
  353. package/src/logo-movistar-new.tsx +85 -0
  354. package/src/logo-movistar-shell.tsx +30 -0
  355. package/src/logo-movistar.tsx +63 -0
  356. package/src/logo-o2-new-shell.tsx +26 -0
  357. package/src/logo-o2-new.tsx +27 -0
  358. package/src/logo-o2-shell.tsx +26 -0
  359. package/src/logo-o2.tsx +27 -0
  360. package/src/logo-telefonica-shell.tsx +30 -0
  361. package/src/logo-telefonica.tsx +95 -0
  362. package/src/logo-tu-shell.tsx +26 -0
  363. package/src/logo-tu.tsx +28 -0
  364. package/src/logo-vivo-shell.tsx +30 -0
  365. package/src/logo-vivo.tsx +53 -0
  366. package/src/logo.css.ts +33 -0
  367. package/src/logo.tsx +313 -0
  368. package/src/master-detail-layout.tsx +28 -0
  369. package/src/maybe-dismissable.css.ts +37 -0
  370. package/src/maybe-dismissable.tsx +58 -0
  371. package/src/media-queries.css.ts +67 -0
  372. package/src/menu.css.ts +132 -0
  373. package/src/menu.tsx +468 -0
  374. package/src/meter.tsx +516 -0
  375. package/src/modal-context-provider.tsx +45 -0
  376. package/src/month-field.tsx +124 -0
  377. package/src/mosaic.css.ts +73 -0
  378. package/src/mosaic.tsx +205 -0
  379. package/src/navigation-bar.css.ts +558 -0
  380. package/src/navigation-bar.tsx +1637 -0
  381. package/src/navigation-breadcrumbs.css.ts +22 -0
  382. package/src/navigation-breadcrumbs.tsx +69 -0
  383. package/src/negative-box.tsx +15 -0
  384. package/src/nestable-context.tsx +139 -0
  385. package/src/overlay.tsx +86 -0
  386. package/src/overscroll-color-context.tsx +141 -0
  387. package/src/package-version.tsx +2 -0
  388. package/src/password-field.tsx +126 -0
  389. package/src/phone-number-field-lite.tsx +265 -0
  390. package/src/phone-number-field.tsx +171 -0
  391. package/src/pin-field.css.ts +90 -0
  392. package/src/pin-field.tsx +346 -0
  393. package/src/placeholder.tsx +41 -0
  394. package/src/popover.css.ts +8 -0
  395. package/src/popover.tsx +85 -0
  396. package/src/portal.tsx +43 -0
  397. package/src/progress-bar.css.ts +61 -0
  398. package/src/progress-bar.tsx +174 -0
  399. package/src/radio-button.css.ts +174 -0
  400. package/src/radio-button.tsx +322 -0
  401. package/src/rating.css.ts +128 -0
  402. package/src/rating.tsx +351 -0
  403. package/src/responsive-layout.css.ts +162 -0
  404. package/src/responsive-layout.tsx +106 -0
  405. package/src/screen-reader-only.css.ts +27 -0
  406. package/src/screen-reader-only.tsx +33 -0
  407. package/src/screen-size-context-provider.tsx +96 -0
  408. package/src/screen-size-context.tsx +23 -0
  409. package/src/search-field.tsx +126 -0
  410. package/src/select.css.ts +226 -0
  411. package/src/select.tsx +513 -0
  412. package/src/sheet-action-row.css.ts +33 -0
  413. package/src/sheet-actions-list.tsx +113 -0
  414. package/src/sheet-actions.tsx +95 -0
  415. package/src/sheet-common.css.ts +254 -0
  416. package/src/sheet-common.tsx +402 -0
  417. package/src/sheet-info.css.ts +19 -0
  418. package/src/sheet-info.tsx +127 -0
  419. package/src/sheet-native.tsx +189 -0
  420. package/src/sheet-radio-list.tsx +118 -0
  421. package/src/sheet-root.tsx +127 -0
  422. package/src/sheet-types.tsx +94 -0
  423. package/src/sheet-web.tsx +140 -0
  424. package/src/skeleton-base.tsx +38 -0
  425. package/src/skeletons.css.ts +56 -0
  426. package/src/skeletons.tsx +133 -0
  427. package/src/skins/blau.tsx +724 -0
  428. package/src/skins/constants.tsx +10 -0
  429. package/src/skins/defaults.tsx +104 -0
  430. package/src/skins/esimflag.tsx +728 -0
  431. package/src/skins/movistar-new.tsx +735 -0
  432. package/src/skins/movistar.tsx +740 -0
  433. package/src/skins/o2-new.tsx +731 -0
  434. package/src/skins/o2.tsx +727 -0
  435. package/src/skins/skin-contract.css.ts +380 -0
  436. package/src/skins/telefonica.tsx +768 -0
  437. package/src/skins/tu.tsx +741 -0
  438. package/src/skins/types/colors.tsx +286 -0
  439. package/src/skins/types/index.tsx +153 -0
  440. package/src/skins/utils.tsx +66 -0
  441. package/src/skins/vivo-new.tsx +745 -0
  442. package/src/skins/vivo.tsx +720 -0
  443. package/src/skip-link.css.ts +34 -0
  444. package/src/skip-link.tsx +52 -0
  445. package/src/slider.css.ts +181 -0
  446. package/src/slider.tsx +384 -0
  447. package/src/snackbar-context.tsx +98 -0
  448. package/src/snackbar-native.ts +37 -0
  449. package/src/snackbar.css.ts +176 -0
  450. package/src/snackbar.tsx +258 -0
  451. package/src/spinner.css.ts +66 -0
  452. package/src/spinner.tsx +136 -0
  453. package/src/sprinkles.css.ts +83 -0
  454. package/src/square.css.ts +15 -0
  455. package/src/square.tsx +55 -0
  456. package/src/stack.css.ts +44 -0
  457. package/src/stack.tsx +79 -0
  458. package/src/stacking-group.css.ts +15 -0
  459. package/src/stacking-group.tsx +82 -0
  460. package/src/stepper.css.ts +233 -0
  461. package/src/stepper.tsx +156 -0
  462. package/src/switch-component.css.ts +181 -0
  463. package/src/switch-component.tsx +187 -0
  464. package/src/tab-focus.tsx +68 -0
  465. package/src/table-actions-header.tsx +21 -0
  466. package/src/table-cell-text.tsx +35 -0
  467. package/src/table.css.ts +297 -0
  468. package/src/table.tsx +398 -0
  469. package/src/tabs.css.ts +212 -0
  470. package/src/tabs.tsx +263 -0
  471. package/src/tag.css.ts +42 -0
  472. package/src/tag.tsx +161 -0
  473. package/src/test-utils/environment/setup-ssr.tsx +10 -0
  474. package/src/test-utils/fail-test-on-console-error.tsx +22 -0
  475. package/src/test-utils/index.tsx +341 -0
  476. package/src/test-utils/setup-ssr-test-env.tsx +13 -0
  477. package/src/test-utils/ssr.tsx +197 -0
  478. package/src/text-field-base.css.ts +416 -0
  479. package/src/text-field-base.tsx +628 -0
  480. package/src/text-field-components.css.ts +159 -0
  481. package/src/text-field-components.tsx +225 -0
  482. package/src/text-field.tsx +118 -0
  483. package/src/text-link.css.ts +83 -0
  484. package/src/text-link.tsx +85 -0
  485. package/src/text-tokens.tsx +708 -0
  486. package/src/text.css.ts +60 -0
  487. package/src/text.tsx +516 -0
  488. package/src/theme-context-provider.tsx +370 -0
  489. package/src/theme-context.css.ts +3 -0
  490. package/src/theme-context.tsx +8 -0
  491. package/src/theme-variant-context.tsx +51 -0
  492. package/src/theme.tsx +184 -0
  493. package/src/time-field.tsx +99 -0
  494. package/src/timeline.css.ts +135 -0
  495. package/src/timeline.tsx +250 -0
  496. package/src/timer.css.ts +99 -0
  497. package/src/timer.tsx +420 -0
  498. package/src/title.tsx +119 -0
  499. package/src/tooltip-context-provider.tsx +57 -0
  500. package/src/tooltip.css.ts +106 -0
  501. package/src/tooltip.tsx +649 -0
  502. package/src/touchable.css.ts +56 -0
  503. package/src/touchable.tsx +355 -0
  504. package/src/types/font-face.d.ts +47 -0
  505. package/src/types/libs.d.ts +3 -0
  506. package/src/utils/__tests__/analytics-test.tsx +35 -0
  507. package/src/utils/__tests__/browser-test.tsx +28 -0
  508. package/src/utils/__tests__/dom-test.tsx +23 -0
  509. package/src/utils/__tests__/helpers-test.tsx +166 -0
  510. package/src/utils/analytics.tsx +28 -0
  511. package/src/utils/animation.tsx +201 -0
  512. package/src/utils/aspect-ratio-support.css.ts +28 -0
  513. package/src/utils/aspect-ratio-support.tsx +141 -0
  514. package/src/utils/browser.tsx +9 -0
  515. package/src/utils/color.tsx +46 -0
  516. package/src/utils/common.tsx +27 -0
  517. package/src/utils/credit-card.tsx +46 -0
  518. package/src/utils/css.tsx +25 -0
  519. package/src/utils/document-visibility.tsx +52 -0
  520. package/src/utils/dom.tsx +155 -0
  521. package/src/utils/environment.tsx +6 -0
  522. package/src/utils/headings.tsx +18 -0
  523. package/src/utils/helpers.tsx +182 -0
  524. package/src/utils/keys.tsx +8 -0
  525. package/src/utils/locale.tsx +27 -0
  526. package/src/utils/platform.tsx +94 -0
  527. package/src/utils/region-code.tsx +1 -0
  528. package/src/utils/renders-element.tsx +6 -0
  529. package/src/utils/time.tsx +22 -0
  530. package/src/utils/types.tsx +19 -0
  531. package/src/utils/utility-types.tsx +8 -0
  532. package/src/video.css.ts +11 -0
  533. package/src/video.tsx +355 -0
  534. package/src/vivinho-loading-animation/in-lottie.json +402 -0
  535. package/src/vivinho-loading-animation/index.tsx +90 -0
  536. package/src/vivinho-loading-animation/out-lottie.json +575 -0
  537. package/src/vivinho-loading-animation/pulse-lottie.json +551 -0
  538. package/src/vivinho-loading-animation/vivinho-loading-animation.css.ts +15 -0
  539. package/src/vivinho-loading-animation/wave-lottie.json +2829 -0
@@ -0,0 +1,1637 @@
1
+ 'use client';
2
+ import * as React from 'react';
3
+ import {CSSTransition} from 'react-transition-group';
4
+ import classnames from 'classnames';
5
+ import ResponsiveLayout, {ResetResponsiveLayout} from './responsive-layout';
6
+ import Inline from './inline';
7
+ import Touchable, {BaseTouchable} from './touchable';
8
+ import {Text2, Text3} from './text';
9
+ import {useElementDimensions, useScreenSize, useTheme} from './hooks';
10
+ import IconMenuRegular from './generated/mistica-icons/icon-menu-regular';
11
+ import IconCloseRegular from './generated/mistica-icons/icon-close-regular';
12
+ import IconChevronLeftRegular from './generated/mistica-icons/icon-chevron-left-regular';
13
+ import {IconButton} from './icon-button';
14
+ import {Row, RowList} from './list';
15
+ import {ThemeVariant, normalizeVariant, useThemeVariant} from './theme-variant-context';
16
+ import FocusTrap from './focus-trap';
17
+ import {Portal} from './portal';
18
+ import GridLayout from './grid-layout';
19
+ import {useSetModalState} from './modal-context-provider';
20
+ import {Logo} from './logo';
21
+ import {vars} from './skins/skin-contract.css';
22
+ import * as styles from './navigation-bar.css';
23
+ import {cancelEvent, getPrefixedDataAttributes} from './utils/dom';
24
+ import Stack from './stack';
25
+ import Box from './box';
26
+ import {isRunningAcceptanceTest} from './utils/platform';
27
+ import * as tokens from './text-tokens';
28
+ import {NAVBAR_HEIGHT_DESKTOP, NAVBAR_HEIGHT_DESKTOP_LARGE, NAVBAR_HEIGHT_MOBILE} from './theme';
29
+ import TextLink from './text-link';
30
+ import {Title1, Title3} from './title';
31
+ import {ButtonLink} from './button';
32
+ import {Grid, GridItem} from './grid';
33
+ import {DOWN, ESC, UP} from './utils/keys';
34
+ import {debounce, isEqual} from './utils/helpers';
35
+ import NegativeBox from './negative-box';
36
+
37
+ import type {BoxProps} from './box';
38
+ import type {ExclusifyUnion} from './utils/utility-types';
39
+ import type {NonDeprecatedVariant, Variant} from './theme-variant-context';
40
+ import type {TouchableProps} from './touchable';
41
+ import type {DataAttributes, HeadingType, IconProps} from './utils/types';
42
+
43
+ const MAIN_NAVIGATION_BAR_MENU_DEBOUNCE_TIME = 120;
44
+
45
+ const MenuSectionArrow = ({size = 24, color = vars.colors.neutralHigh, style, className}: IconProps) => (
46
+ <svg width={size} height={size} viewBox="0 0 8 8" style={style} className={className}>
47
+ <path
48
+ d="M6.32595 3.46071L3.03801 0.158595L3.03292 0.153747L3.032 0.152903L3.02931 0.150463L3.02848 0.149738L3.02248 0.144353C2.88533 0.021206 2.71386 -0.0126731 2.56343 0.00394249C2.41648 0.0201739 2.27272 0.0856702 2.16886 0.18711C2.06893 0.281844 2.01209 0.42816 2.00175 0.567065C1.99083 0.71377 2.02925 0.889583 2.16869 1.02392L5.24446 4.00145L2.15859 6.96199L2.15375 6.96708L2.1529 6.968L2.15046 6.97069L2.14974 6.97152L2.14435 6.97752C2.02121 7.11467 1.98733 7.28614 2.00394 7.43657C2.02017 7.58352 2.08567 7.72728 2.18711 7.83114C2.28184 7.93107 2.42816 7.98791 2.56706 7.99825C2.71377 8.00917 2.88958 7.97075 3.02392 7.83132L6.32595 4.5422V4.5422C6.6246 4.24355 6.6246 3.75935 6.32595 3.46071V3.46071Z"
49
+ fill={color}
50
+ />
51
+ </svg>
52
+ );
53
+
54
+ const BurgerMenuIcon = ({isOpen}: {isOpen: boolean}) => {
55
+ return (
56
+ <div className={styles.burgerIconContainer} role="presentation" data-component-name="BurgerMenuIcon">
57
+ <div className={isOpen ? styles.iconCloseOpen : styles.iconCloseHidden}>
58
+ <IconCloseRegular />
59
+ </div>
60
+ <div className={isOpen ? styles.iconMenuHidden : styles.iconMenuOpen}>
61
+ <IconMenuRegular />
62
+ </div>
63
+ </div>
64
+ );
65
+ };
66
+
67
+ type HeaderProps = {
68
+ children: React.ReactNode;
69
+ topFixed?: boolean;
70
+ variant: NonDeprecatedVariant;
71
+ withBorder?: boolean;
72
+ isBurgerMenuOpen?: boolean;
73
+ dataAttributes?: DataAttributes;
74
+ isBottomRow?: boolean;
75
+ };
76
+
77
+ const Header = ({children, topFixed, withBorder, isBurgerMenuOpen, variant, dataAttributes}: HeaderProps) => {
78
+ const {isDarkMode} = useTheme();
79
+
80
+ const getBorderClass = () => {
81
+ const isBrandVariant = (variant === 'brand' || variant === 'negative') && !isDarkMode;
82
+ if (isBrandVariant || !withBorder) return styles.navbarBorderColorVariants.noBorder;
83
+ if (isBurgerMenuOpen) return styles.navbarBorderColorVariants.menuOpen;
84
+
85
+ return styles.navbarBorderColorVariants.default;
86
+ };
87
+
88
+ const backgroundColor = {
89
+ default: vars.colors.background,
90
+ brand: vars.colors.navigationBarBackground,
91
+ negative: vars.colors.backgroundNegative,
92
+ alternative: vars.colors.backgroundAlternative,
93
+ media: vars.colors.navigationBarBackground,
94
+ } as const;
95
+
96
+ return (
97
+ <header
98
+ className={classnames(getBorderClass(), {[styles.topFixed]: topFixed})}
99
+ style={{
100
+ background: backgroundColor[variant],
101
+ }}
102
+ {...getPrefixedDataAttributes(dataAttributes)}
103
+ >
104
+ {children}
105
+ </header>
106
+ );
107
+ };
108
+
109
+ type NavigationBarContentContainerProps = {
110
+ right?: React.ReactNode;
111
+ children?: React.ReactNode;
112
+ desktopOnly?: boolean;
113
+ expandRightContent?: boolean;
114
+ };
115
+
116
+ const NavigationBarContentContainer = React.forwardRef<HTMLDivElement, NavigationBarContentContainerProps>(
117
+ ({right, children, desktopOnly, expandRightContent}, ref) => {
118
+ return (
119
+ <div
120
+ ref={ref}
121
+ className={classnames(styles.navigationBarContent, {[styles.desktopOnly]: desktopOnly})}
122
+ >
123
+ {children}
124
+ {right && (
125
+ <div
126
+ className={
127
+ expandRightContent
128
+ ? styles.navigationBarContentRightExpanded
129
+ : styles.navigationBarContentRight
130
+ }
131
+ >
132
+ {right}
133
+ </div>
134
+ )}
135
+ </div>
136
+ );
137
+ }
138
+ );
139
+
140
+ type WideConfig = {
141
+ paddingX: BoxProps['paddingX'];
142
+ };
143
+
144
+ const NavigationBarSideMargins = ({
145
+ children,
146
+ wide,
147
+ backgroundColor,
148
+ }: {
149
+ children: React.ReactNode;
150
+ wide: boolean | WideConfig;
151
+ backgroundColor?: string;
152
+ }) => {
153
+ if (!wide) {
154
+ return <ResponsiveLayout backgroundColor={backgroundColor}>{children}</ResponsiveLayout>;
155
+ }
156
+
157
+ const defaultWidePaddingX: BoxProps['paddingX'] = {
158
+ mobile: 16,
159
+ tablet: 24,
160
+ desktop: 24,
161
+ };
162
+
163
+ return (
164
+ <Box
165
+ width="100%"
166
+ paddingX={
167
+ wide === true
168
+ ? defaultWidePaddingX
169
+ : typeof wide.paddingX === 'number'
170
+ ? wide.paddingX
171
+ : {
172
+ ...defaultWidePaddingX,
173
+ ...wide.paddingX,
174
+ }
175
+ }
176
+ background={backgroundColor}
177
+ >
178
+ {children}
179
+ </Box>
180
+ );
181
+ };
182
+
183
+ interface NavigationBarCommonProps {
184
+ variant?: Variant;
185
+ onBack?: () => void;
186
+ title?: string;
187
+ titleAs?: HeadingType;
188
+ right?: React.ReactElement;
189
+ withBorder?: boolean;
190
+ children?: undefined;
191
+ wide?: boolean | WideConfig;
192
+ }
193
+
194
+ interface NavigationBarTopFixedProps extends NavigationBarCommonProps {
195
+ topFixed?: true;
196
+ paddingX?: undefined;
197
+ }
198
+
199
+ interface NavigationBarNotFixedProps extends NavigationBarCommonProps {
200
+ topFixed: false;
201
+ /** @deprecated use wide */
202
+ paddingX?: number;
203
+ }
204
+
205
+ type NavigationBarProps = NavigationBarTopFixedProps | NavigationBarNotFixedProps;
206
+
207
+ export const NavigationBar = ({
208
+ onBack,
209
+ title,
210
+ titleAs,
211
+ right,
212
+ variant = 'default',
213
+ topFixed = true,
214
+ paddingX = 0,
215
+ withBorder = true,
216
+ wide = false,
217
+ }: NavigationBarProps): JSX.Element => {
218
+ const {texts, t} = useTheme();
219
+ const content = (
220
+ <NavigationBarContentContainer right={right} expandRightContent>
221
+ <Inline space={24} alignItems="center">
222
+ {onBack && (
223
+ <IconButton
224
+ aria-label={texts.backNavigationBar || t(tokens.backNavigationBar)}
225
+ onPress={onBack}
226
+ Icon={IconChevronLeftRegular}
227
+ bleedLeft
228
+ bleedRight
229
+ />
230
+ )}
231
+ <Text3 regular truncate as={titleAs}>
232
+ {title}
233
+ </Text3>
234
+ </Inline>
235
+ </NavigationBarContentContainer>
236
+ );
237
+
238
+ const calcPaddingXWhenNotTopFixed = (): BoxProps['paddingX'] => {
239
+ if (wide !== undefined) {
240
+ if (typeof wide !== 'object') {
241
+ return 0;
242
+ }
243
+ return wide.paddingX ?? 0;
244
+ }
245
+
246
+ return paddingX as BoxProps['paddingX'];
247
+ };
248
+
249
+ const normalizedVariant = normalizeVariant(variant);
250
+
251
+ return (
252
+ <ThemeVariant variant={normalizedVariant}>
253
+ <Header
254
+ topFixed={topFixed}
255
+ withBorder={withBorder}
256
+ variant={normalizedVariant}
257
+ dataAttributes={{'component-name': 'NavigationBar'}}
258
+ >
259
+ {topFixed ? (
260
+ <NavigationBarSideMargins wide={wide}>{content}</NavigationBarSideMargins>
261
+ ) : (
262
+ <Box width="100%" paddingX={calcPaddingXWhenNotTopFixed()}>
263
+ {content}
264
+ </Box>
265
+ )}
266
+ </Header>
267
+ {topFixed && <div className={styles.spacer} />}
268
+ </ThemeVariant>
269
+ );
270
+ };
271
+
272
+ type InteractiveProps = ExclusifyUnion<{href: string} | {to: string} | {onPress: () => void}>;
273
+ type MaybeInteractiveProps = ExclusifyUnion<{href?: string} | {to?: string} | {onPress?: () => void}>;
274
+
275
+ type SectionItem = {title: string} & InteractiveProps;
276
+
277
+ type SectionColumn = {
278
+ title: string;
279
+ items: ReadonlyArray<SectionItem>;
280
+ };
281
+
282
+ type SectionMenu = ExclusifyUnion<
283
+ | {columns: ReadonlyArray<SectionColumn>}
284
+ // Content prop can receive a function as value with the closeMenu callback as parameter.
285
+ // In this way, custom content can also force the menu to close programmatically.
286
+ | {content?: React.ReactElement | ((props: {closeMenu: () => void}) => React.ReactElement)}
287
+ >;
288
+
289
+ const getInteractivePropsWithCloseMenu = (interactiveProps: InteractiveProps, closeMenu: () => void) => {
290
+ if (
291
+ interactiveProps.href === undefined &&
292
+ interactiveProps.onPress === undefined &&
293
+ interactiveProps.to === undefined
294
+ ) {
295
+ return {onPress: closeMenu};
296
+ }
297
+
298
+ return interactiveProps.onPress
299
+ ? {
300
+ onPress: () => {
301
+ interactiveProps.onPress();
302
+ closeMenu();
303
+ },
304
+ }
305
+ : {...interactiveProps, onNavigate: () => closeMenu()};
306
+ };
307
+
308
+ type MainNavigationBarSection = {
309
+ title: string;
310
+ menu?: SectionMenu;
311
+ } & MaybeInteractiveProps;
312
+
313
+ type MainNavigationBarProps = {
314
+ sections?: ReadonlyArray<MainNavigationBarSection>;
315
+ selectedIndex?: number;
316
+ right?: React.ReactElement;
317
+ topSlot?: React.ReactElement;
318
+ topSlotBackgroundColor?: string;
319
+ logo?: React.ReactElement;
320
+ variant?: Variant;
321
+ children?: undefined;
322
+ topFixed?: boolean;
323
+ withBorder?: boolean;
324
+ burgerMenuExtra?: React.ReactNode;
325
+ large?: boolean;
326
+ desktopLargeMenu?: boolean;
327
+ wide?: boolean | WideConfig;
328
+ };
329
+
330
+ type MainNavigationBarMenuStatus = 'opening' | 'opened' | 'closing' | 'closed';
331
+ type MainNavigationBarMenuAction = 'open' | 'finishOpen' | 'close' | 'finishClose';
332
+
333
+ const mainNavigationBurgerMenuTranstions: Record<
334
+ MainNavigationBarMenuStatus,
335
+ Partial<Record<MainNavigationBarMenuAction, MainNavigationBarMenuStatus>>
336
+ > = {
337
+ opening: {
338
+ close: 'closing',
339
+ finishOpen: 'opened',
340
+ },
341
+ opened: {
342
+ close: 'closing',
343
+ },
344
+ closing: {
345
+ open: 'opening',
346
+ finishClose: 'closed',
347
+ },
348
+ closed: {
349
+ open: 'opening',
350
+ },
351
+ };
352
+
353
+ const burgerMenuReducer = (state: MainNavigationBarMenuStatus, action: MainNavigationBarMenuAction) => {
354
+ return mainNavigationBurgerMenuTranstions[state][action] || state;
355
+ };
356
+
357
+ const MainNavigationBarBurgerSection = ({
358
+ section,
359
+ closeSubMenu,
360
+ closeMenu,
361
+ }: {
362
+ section: MainNavigationBarSection;
363
+ closeSubMenu: () => void;
364
+ closeMenu: () => void;
365
+ }) => {
366
+ const {texts, t} = useTheme();
367
+
368
+ const {title, menu, ...interactiveProps} = section;
369
+
370
+ const columns = menu?.columns || [];
371
+ const customContent = menu?.content;
372
+ const hasCustomInteraction =
373
+ interactiveProps.href !== undefined ||
374
+ interactiveProps.onPress !== undefined ||
375
+ interactiveProps.to !== undefined;
376
+
377
+ return (
378
+ <ResponsiveLayout>
379
+ <Stack space={32}>
380
+ <Stack space={16}>
381
+ <NavigationBar
382
+ title={texts.backNavigationBar || t(tokens.backNavigationBar)}
383
+ onBack={closeSubMenu}
384
+ topFixed={false}
385
+ withBorder={false}
386
+ />
387
+ <Title3
388
+ right={
389
+ hasCustomInteraction ? (
390
+ <ButtonLink
391
+ small
392
+ bleedY
393
+ bleedRight
394
+ withChevron
395
+ // Close the menu when "See all" button is pressed
396
+ {...getInteractivePropsWithCloseMenu(
397
+ interactiveProps as InteractiveProps,
398
+ closeMenu
399
+ )}
400
+ >
401
+ {texts.mainNavigationBarSectionSeeAll ||
402
+ t(tokens.mainNavigationBarSectionSeeAll)}
403
+ </ButtonLink>
404
+ ) : undefined
405
+ }
406
+ >
407
+ {title}
408
+ </Title3>
409
+ </Stack>
410
+
411
+ {customContent ? (
412
+ <Box paddingBottom={16}>
413
+ {typeof customContent === 'function' ? customContent({closeMenu}) : customContent}
414
+ </Box>
415
+ ) : (
416
+ columns.map((column, columnIndex) => (
417
+ <Stack space={8} key={columnIndex}>
418
+ <Title1> {column.title}</Title1>
419
+ <NegativeBox>
420
+ <RowList>
421
+ {column.items.map(
422
+ ({title: itemTitle, ...itemInteractiveProps}, itemIndex) => (
423
+ <Row
424
+ key={itemIndex}
425
+ title={itemTitle}
426
+ // Close the menu when one of the rows is pressed
427
+ {...getInteractivePropsWithCloseMenu(
428
+ itemInteractiveProps,
429
+ closeMenu
430
+ )}
431
+ />
432
+ )
433
+ )}
434
+ </RowList>
435
+ </NegativeBox>
436
+ </Stack>
437
+ ))
438
+ )}
439
+ </Stack>
440
+ </ResponsiveLayout>
441
+ );
442
+ };
443
+
444
+ const MainNavigationBarBurgerMenu = ({
445
+ sections,
446
+ extra,
447
+ closeMenu,
448
+ open,
449
+ topSlotHeight,
450
+ id,
451
+ disableFocusTrap,
452
+ setDisableFocusTrap,
453
+ }: {
454
+ sections: ReadonlyArray<MainNavigationBarSection>;
455
+ extra: React.ReactNode;
456
+ closeMenu: () => void;
457
+ open: boolean;
458
+ topSlotHeight: number;
459
+ id: string;
460
+ disableFocusTrap: boolean;
461
+ setDisableFocusTrap: (value: boolean) => void;
462
+ }) => {
463
+ const {isDarkMode, texts, t} = useTheme();
464
+ const [openedSection, setOpenedSection] = React.useState(-1);
465
+ const [isSubMenuOpen, setIsSubMenuOpen] = React.useState(false);
466
+ const [subMenuStatus, dispatch] = React.useReducer(burgerMenuReducer, 'closed');
467
+ const menuRef = React.useRef<HTMLDivElement>(null);
468
+
469
+ const shadowAlpha = isDarkMode ? 1 : 0.2;
470
+ const menuAnimationDuration = isRunningAcceptanceTest() ? 0 : styles.BURGER_MENU_ANIMATION_DURATION_MS;
471
+
472
+ React.useEffect(() => {
473
+ let id: NodeJS.Timeout;
474
+
475
+ // menu starts opening or closing
476
+ if (isSubMenuOpen) {
477
+ dispatch('open');
478
+ id = setTimeout(() => dispatch('finishOpen'), menuAnimationDuration);
479
+ } else {
480
+ dispatch('close');
481
+ id = setTimeout(() => dispatch('finishClose'), menuAnimationDuration);
482
+ }
483
+
484
+ return () => clearTimeout(id);
485
+ }, [isSubMenuOpen, menuAnimationDuration]);
486
+
487
+ const sectionContainerRef = React.useRef<HTMLDivElement>(null);
488
+
489
+ React.useEffect(() => {
490
+ // Make screen reader focus on back button when opening any section's menu
491
+ if (subMenuStatus === 'opening') {
492
+ const sectionBackButtonElement = sectionContainerRef.current?.querySelector<HTMLButtonElement>(
493
+ `button[aria-label="${texts.backNavigationBar || t(tokens.backNavigationBar)}"]`
494
+ );
495
+
496
+ sectionBackButtonElement?.focus();
497
+ }
498
+ }, [subMenuStatus, t, texts]);
499
+
500
+ return (
501
+ <Portal>
502
+ <FocusTrap disabled={disableFocusTrap} group="burger-menu-lock">
503
+ <CSSTransition
504
+ onEntered={() => setDisableFocusTrap(false)}
505
+ onExiting={() => setDisableFocusTrap(true)}
506
+ onExited={() => {
507
+ setIsSubMenuOpen(false);
508
+ setOpenedSection(-1);
509
+ }}
510
+ classNames={styles.burgerMenuTransition}
511
+ in={open}
512
+ nodeRef={menuRef}
513
+ timeout={menuAnimationDuration}
514
+ mountOnEnter
515
+ unmountOnExit
516
+ >
517
+ <nav
518
+ className={styles.burgerMenu}
519
+ style={{
520
+ boxShadow: `6px 0 4px -4px rgba(0, 0, 0, ${shadowAlpha})`,
521
+ top: NAVBAR_HEIGHT_MOBILE + topSlotHeight,
522
+ }}
523
+ id={id}
524
+ ref={menuRef}
525
+ >
526
+ <div className={styles.burgerMenuContainer}>
527
+ <div
528
+ className={styles.burgerMenuContentContainer}
529
+ style={{
530
+ transform: `translate(${isSubMenuOpen ? '-100vw' : '0'})`,
531
+ }}
532
+ >
533
+ {subMenuStatus !== 'opened' && (
534
+ <ResponsiveLayout>
535
+ <ResetResponsiveLayout>
536
+ <RowList>
537
+ {sections.map(({title, menu, ...interactiveProps}, index) => (
538
+ <Row
539
+ key={index}
540
+ title={title}
541
+ {...(menu
542
+ ? {
543
+ onPress: () => {
544
+ setIsSubMenuOpen(true);
545
+ setOpenedSection(index);
546
+ },
547
+ }
548
+ : // Close the menu when one of the rows is pressed
549
+ getInteractivePropsWithCloseMenu(
550
+ interactiveProps as InteractiveProps,
551
+ closeMenu
552
+ ))}
553
+ />
554
+ ))}
555
+ </RowList>
556
+ </ResetResponsiveLayout>
557
+ {extra && <Box paddingY={16}>{extra}</Box>}
558
+ </ResponsiveLayout>
559
+ )}
560
+ </div>
561
+
562
+ <div
563
+ className={styles.burgerMenuContentContainer}
564
+ ref={sectionContainerRef}
565
+ style={{
566
+ transform: `translate(${isSubMenuOpen ? '0' : '100vw'})`,
567
+ }}
568
+ >
569
+ {subMenuStatus !== 'closed' && openedSection !== -1 && (
570
+ <MainNavigationBarBurgerSection
571
+ section={sections[openedSection]}
572
+ closeMenu={closeMenu}
573
+ closeSubMenu={() => setIsSubMenuOpen(false)}
574
+ />
575
+ )}
576
+ </div>
577
+ </div>
578
+ </nav>
579
+ </CSSTransition>
580
+ </FocusTrap>
581
+ </Portal>
582
+ );
583
+ };
584
+
585
+ const mainNavigationDesktopMenuTranstions: Record<
586
+ MainNavigationBarMenuStatus,
587
+ Partial<Record<MainNavigationBarMenuAction, MainNavigationBarMenuStatus>>
588
+ > = {
589
+ opening: {
590
+ close: 'closing',
591
+ finishOpen: 'opened',
592
+ },
593
+ opened: {
594
+ close: 'closing',
595
+ },
596
+ closing: {
597
+ // If a section was opened while the menu was closing, the menu should be considered as
598
+ // already open. This is useful for example to avoid the new content's fade-in animation
599
+ open: 'opened',
600
+ finishClose: 'closed',
601
+ },
602
+ closed: {
603
+ open: 'opening',
604
+ },
605
+ };
606
+
607
+ const desktopMenuReducer = (state: MainNavigationBarMenuStatus, action: MainNavigationBarMenuAction) => {
608
+ return mainNavigationDesktopMenuTranstions[state][action] || state;
609
+ };
610
+
611
+ type MainNavigationBarDesktopMenuState = {
612
+ isMenuOpen: boolean;
613
+ openedSection: number;
614
+ menuHeight: string;
615
+ menuStatus: MainNavigationBarMenuStatus;
616
+ openSectionMenu: (index: number) => void;
617
+ closeMenu: () => void;
618
+ setMenuHeight: (height: string) => void;
619
+ setIsMenuHovered: (value: boolean) => void;
620
+ setFocusedItem: (item?: {column: number; index: number}) => void;
621
+ debouncedOpenSectionMenu: (index: number) => void;
622
+ cancelDebouncedOpenSectionMenu: (index: number) => void;
623
+ };
624
+
625
+ const MainNavigationBarDesktopMenuContext = React.createContext<MainNavigationBarDesktopMenuState>({
626
+ isMenuOpen: false,
627
+ openedSection: -1,
628
+ menuHeight: '0px',
629
+ menuStatus: 'closed',
630
+ openSectionMenu: () => {},
631
+ closeMenu: () => {},
632
+ setMenuHeight: () => {},
633
+ setIsMenuHovered: () => {},
634
+ setFocusedItem: () => {},
635
+ debouncedOpenSectionMenu: () => {},
636
+ cancelDebouncedOpenSectionMenu: () => {},
637
+ });
638
+
639
+ const MainNavigationBarDesktopMenuContextProvider = ({
640
+ children,
641
+ sections,
642
+ isLargeMenu,
643
+ }: {
644
+ children: React.ReactNode;
645
+ sections?: ReadonlyArray<MainNavigationBarSection>;
646
+ isLargeMenu?: boolean;
647
+ }): JSX.Element => {
648
+ const {isTabletOrSmaller} = useScreenSize();
649
+ const [isMenuOpen, setIsMenuOpen] = React.useState(false);
650
+ const [menuHeight, setMenuHeight] = React.useState('0px');
651
+ const [menuStatus, dispatch] = React.useReducer(desktopMenuReducer, 'closed');
652
+ const [debouncedOpenSectionIndex, setDebouncedOpenSectionIndex] = React.useState(-1);
653
+
654
+ // Item that is currently focused inside a section. This state is used to handle pressing
655
+ // up/down arrows to navigate through the items of a section.
656
+ const [focusedItem, setFocusedItem] = React.useState<{column: number; index: number} | undefined>();
657
+
658
+ // State that indicated whether the menu container has been hovered
659
+ const [isMenuHovered, setIsMenuHovered] = React.useState(false);
660
+
661
+ // State that indicates whether the current rendered section has been hovered or its arrow button is focused
662
+ const [isSectionHovered, setIsSectionHovered] = React.useState(false);
663
+
664
+ // Section that is currently being rendered
665
+ const [openedSection, setOpenedSection] = React.useState(-1);
666
+
667
+ // Callback used when a section has been hovered or it's focused arrow has been pressed while it's closed
668
+ const openSectionMenu = React.useCallback(
669
+ (index: number) => {
670
+ setDebouncedOpenSectionIndex(-1);
671
+ if (sections?.[index]?.menu) {
672
+ setIsSectionHovered(true);
673
+ setOpenedSection(index);
674
+ } else {
675
+ // If the section has no menu, close the current opened one
676
+ setIsMenuHovered(false);
677
+ setIsSectionHovered(false);
678
+ }
679
+ },
680
+ [sections]
681
+ );
682
+
683
+ const debouncedOpenSectionMenu = React.useMemo(
684
+ () => debounce(openSectionMenu, MAIN_NAVIGATION_BAR_MENU_DEBOUNCE_TIME),
685
+ [openSectionMenu]
686
+ );
687
+
688
+ const closeMenu = React.useCallback(() => {
689
+ setIsMenuHovered(false);
690
+ setIsSectionHovered(false);
691
+ debouncedOpenSectionMenu.cancel();
692
+ setDebouncedOpenSectionIndex(-1);
693
+ }, [debouncedOpenSectionMenu]);
694
+
695
+ // Close menu when viewport is too small
696
+ React.useEffect(() => {
697
+ if (isTabletOrSmaller) {
698
+ closeMenu();
699
+ }
700
+ }, [isTabletOrSmaller, closeMenu]);
701
+
702
+ React.useEffect(() => {
703
+ if (!isSectionHovered && !isMenuHovered) {
704
+ setIsMenuOpen(false);
705
+ setMenuHeight('0px');
706
+ } else {
707
+ setIsMenuOpen(true);
708
+ }
709
+ }, [isMenuHovered, isSectionHovered]);
710
+
711
+ React.useEffect(() => {
712
+ const menuAnimationDuration =
713
+ isRunningAcceptanceTest() || !isLargeMenu ? 0 : styles.DESKTOP_MENU_ANIMATION_DURATION_MS;
714
+
715
+ let id: NodeJS.Timeout;
716
+
717
+ // menu starts opening or closing
718
+ if (!isMenuOpen) {
719
+ dispatch('close');
720
+ id = setTimeout(() => dispatch('finishClose'), menuAnimationDuration);
721
+ } else {
722
+ dispatch('open');
723
+ id = setTimeout(() => dispatch('finishOpen'), menuAnimationDuration);
724
+ }
725
+
726
+ return () => clearTimeout(id);
727
+ }, [isMenuOpen, isLargeMenu]);
728
+
729
+ React.useEffect(() => {
730
+ // reset openedSection when the menu has been closed
731
+ if (menuStatus === 'closed') {
732
+ setOpenedSection(-1);
733
+ }
734
+ }, [menuStatus]);
735
+
736
+ const focusItem = React.useCallback(
737
+ (item: {column: number; index: number} | undefined) => {
738
+ if (!isEqual(focusedItem, item)) {
739
+ setFocusedItem(item);
740
+ }
741
+ },
742
+ [focusedItem]
743
+ );
744
+
745
+ React.useEffect(() => {
746
+ // Find all the items of the section and focus the next (or previous) element
747
+ const focusNextItem = (reverse?: boolean) => {
748
+ if (focusedItem) {
749
+ const itemsContainer = document.querySelector('[data-navigation-bar-menu-items]');
750
+ const itemsList = Array.from(itemsContainer?.querySelectorAll('a,button') || []);
751
+
752
+ const currentIndex = itemsList.findIndex((el) =>
753
+ el.hasAttribute(
754
+ `data-navigation-bar-menu-item-${focusedItem.column}-${focusedItem.index}`
755
+ )
756
+ );
757
+
758
+ const nextIndex = reverse ? currentIndex - 1 : currentIndex + 1;
759
+ (itemsList[(nextIndex + itemsList.length) % itemsList.length] as HTMLElement).focus();
760
+ }
761
+ };
762
+
763
+ const handleKeyDown = (e: KeyboardEvent) => {
764
+ switch (e.key) {
765
+ case DOWN:
766
+ if (focusedItem) {
767
+ cancelEvent(e);
768
+ focusNextItem();
769
+ }
770
+ break;
771
+
772
+ case UP:
773
+ if (focusedItem) {
774
+ cancelEvent(e);
775
+ focusNextItem(true);
776
+ }
777
+ break;
778
+
779
+ case ESC:
780
+ closeMenu();
781
+ break;
782
+
783
+ default:
784
+ // Do nothing
785
+ }
786
+ };
787
+ // Close menu when ESC key is pressed
788
+ document.addEventListener('keydown', handleKeyDown, false);
789
+ return () => {
790
+ document.removeEventListener('keydown', handleKeyDown, false);
791
+ };
792
+ }, [closeMenu, focusedItem]);
793
+
794
+ React.useEffect(() => {
795
+ // Restore focusedItem when opened section changes (or when the menu is closed)
796
+ setFocusedItem(undefined);
797
+ }, [openedSection]);
798
+
799
+ return (
800
+ <MainNavigationBarDesktopMenuContext.Provider
801
+ value={{
802
+ isMenuOpen,
803
+ openedSection,
804
+ menuHeight,
805
+ menuStatus,
806
+ openSectionMenu,
807
+ debouncedOpenSectionMenu: (index: number) => {
808
+ setDebouncedOpenSectionIndex(index);
809
+ debouncedOpenSectionMenu(index);
810
+ },
811
+ cancelDebouncedOpenSectionMenu: (index: number) => {
812
+ if (index === debouncedOpenSectionIndex) {
813
+ debouncedOpenSectionMenu.cancel();
814
+ setDebouncedOpenSectionIndex(-1);
815
+ }
816
+ },
817
+ closeMenu,
818
+ setMenuHeight,
819
+ setIsMenuHovered,
820
+ setFocusedItem: focusItem,
821
+ }}
822
+ >
823
+ {children}
824
+ </MainNavigationBarDesktopMenuContext.Provider>
825
+ );
826
+ };
827
+
828
+ export const useMainNavigationBarDesktopMenuState = (): MainNavigationBarDesktopMenuState =>
829
+ React.useContext(MainNavigationBarDesktopMenuContext);
830
+
831
+ const MainNavigationBarDesktopMenuSectionColumn = ({
832
+ column,
833
+ columnIndex,
834
+ }: {
835
+ column: SectionColumn;
836
+ columnIndex: number;
837
+ }) => {
838
+ const {setFocusedItem, closeMenu} = useMainNavigationBarDesktopMenuState();
839
+
840
+ return (
841
+ <Stack space={24}>
842
+ <Text2 medium color={vars.colors.textSecondary} transform="uppercase">
843
+ {column.title}
844
+ </Text2>
845
+
846
+ <Stack space={16} role="list">
847
+ {column.items.map(({title, ...touchableProps}, itemIdx) => (
848
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
849
+ <div
850
+ key={itemIdx}
851
+ onFocus={() => setFocusedItem({column: columnIndex, index: itemIdx})}
852
+ onBlur={() => setFocusedItem(undefined)}
853
+ role="listitem"
854
+ >
855
+ <TextLink
856
+ underline="on hover"
857
+ className={styles.desktopMenuColumnItem}
858
+ dataAttributes={{
859
+ [`navigation-bar-menu-item-${columnIndex}-${itemIdx}`]: 'true',
860
+ }}
861
+ // Close the menu when one of the section items is pressed
862
+ {...getInteractivePropsWithCloseMenu(touchableProps, closeMenu)}
863
+ >
864
+ {title}
865
+ </TextLink>
866
+ </div>
867
+ ))}
868
+ </Stack>
869
+ </Stack>
870
+ );
871
+ };
872
+
873
+ const MainNavigationBarDesktopMenuContent = ({
874
+ section,
875
+ index,
876
+ isLargeNavigationBar,
877
+ topSlotHeight,
878
+ wide,
879
+ }: {
880
+ section: MainNavigationBarSection;
881
+ index: number;
882
+ isLargeNavigationBar: boolean;
883
+ topSlotHeight: number;
884
+ wide: boolean | WideConfig;
885
+ }): JSX.Element => {
886
+ const menuRef = React.useRef<HTMLDivElement>(null);
887
+ const [isMenuContentScrollable, setIsMenuContentScrollable] = React.useState(false);
888
+
889
+ const menuAnimationDuration = isRunningAcceptanceTest() ? 0 : styles.DESKTOP_MENU_ANIMATION_DURATION_MS;
890
+ const topSpace =
891
+ (isLargeNavigationBar ? NAVBAR_HEIGHT_DESKTOP_LARGE : NAVBAR_HEIGHT_DESKTOP) + topSlotHeight;
892
+ const bottomSpace = 40;
893
+
894
+ const {menuStatus, isMenuOpen, openedSection, closeMenu, setIsMenuHovered, setMenuHeight} =
895
+ useMainNavigationBarDesktopMenuState();
896
+
897
+ React.useEffect(() => {
898
+ // Scroll to top of the content if the opened section changed
899
+ if (menuRef.current && isMenuOpen) {
900
+ menuRef.current.scrollTop = 0;
901
+ }
902
+
903
+ // Disable scroll in menu content until height's animation is finished to avoid
904
+ // showing the scrollbar while the menu's container is changing it's height
905
+ setIsMenuContentScrollable(false);
906
+ if (isMenuOpen) {
907
+ const id = setTimeout(() => setIsMenuContentScrollable(true), menuAnimationDuration);
908
+ return () => clearTimeout(id);
909
+ }
910
+ }, [isMenuOpen, openedSection, menuAnimationDuration]);
911
+
912
+ const [isContentVisible, setIsContentVisible] = React.useState(true);
913
+
914
+ React.useEffect(() => {
915
+ if (openedSection === index) {
916
+ // If menu is opening, trigger the fade-in effect for current section
917
+ if (menuStatus === 'opening') {
918
+ setIsContentVisible(false);
919
+ const id = requestAnimationFrame(() => setIsContentVisible(true));
920
+ return () => cancelAnimationFrame(id);
921
+ } else {
922
+ setIsContentVisible(true);
923
+ }
924
+ }
925
+ }, [menuStatus, openedSection, index]);
926
+
927
+ React.useEffect(() => {
928
+ // disable scroll when menu is closing to avoid showing the scrollbar
929
+ if (menuStatus === 'closing') {
930
+ setIsMenuContentScrollable(false);
931
+ }
932
+ }, [menuStatus]);
933
+
934
+ const columns = section.menu?.columns || [];
935
+ const customContent = section?.menu?.content;
936
+
937
+ return (
938
+ <div className={styles.desktopOnly}>
939
+ <ThemeVariant variant="default">
940
+ {openedSection === index && (
941
+ <ResetResponsiveLayout>
942
+ <div
943
+ className={styles.desktopMenuContainer}
944
+ onMouseEnter={() => setIsMenuHovered(true)}
945
+ onMouseLeave={() => setIsMenuHovered(false)}
946
+ style={{
947
+ // disable pointer events when menu is closing
948
+ pointerEvents:
949
+ menuStatus === 'closed' || menuStatus === 'closing' ? 'none' : undefined,
950
+ top: topSpace,
951
+ maxHeight: `calc(100vh - ${topSpace}px - ${bottomSpace}px)`,
952
+ overflowY: isMenuContentScrollable ? 'auto' : 'hidden',
953
+ }}
954
+ >
955
+ <NavigationBarSideMargins wide={wide}>
956
+ <div
957
+ className={classnames(styles.desktopMenu, {
958
+ [styles.desktopMenuContentFadeIn]: isContentVisible,
959
+ })}
960
+ ref={(el) => {
961
+ if (el && isMenuOpen) {
962
+ setMenuHeight(
963
+ `min(${el.scrollHeight}px, calc(100vh - ${topSpace}px - ${bottomSpace}px))`
964
+ );
965
+ }
966
+ }}
967
+ >
968
+ {customContent ? (
969
+ typeof customContent === 'function' ? (
970
+ customContent({closeMenu})
971
+ ) : (
972
+ customContent
973
+ )
974
+ ) : (
975
+ <Grid
976
+ rows={1}
977
+ columns={12}
978
+ gap={[24, 40]}
979
+ dataAttributes={{'navigation-bar-menu-items': 'true'}}
980
+ >
981
+ {columns.map((column, columnIdx) => (
982
+ <GridItem key={columnIdx} columnSpan={2}>
983
+ <MainNavigationBarDesktopMenuSectionColumn
984
+ column={column}
985
+ columnIndex={columnIdx}
986
+ />
987
+ </GridItem>
988
+ ))}
989
+ </Grid>
990
+ )}
991
+ </div>
992
+ </NavigationBarSideMargins>
993
+ </div>
994
+ </ResetResponsiveLayout>
995
+ )}
996
+ </ThemeVariant>
997
+ </div>
998
+ );
999
+ };
1000
+
1001
+ // This is the menu's background panel. The menu content is rendered separately for each tab
1002
+ // in order to make the section's content follow the natural focus order of elements
1003
+ // when using the keyboard or a screen reader to navigate
1004
+ const MainNavigationBarDesktopMenuBackground = ({
1005
+ isLargeNavigationBar,
1006
+ topSlotHeight,
1007
+ }: {
1008
+ isLargeNavigationBar: boolean;
1009
+ topSlotHeight: number;
1010
+ }): JSX.Element => {
1011
+ const topSpace =
1012
+ (isLargeNavigationBar ? NAVBAR_HEIGHT_DESKTOP_LARGE : NAVBAR_HEIGHT_DESKTOP) + topSlotHeight;
1013
+ const {menuHeight} = useMainNavigationBarDesktopMenuState();
1014
+
1015
+ return (
1016
+ <div className={styles.desktopOnly}>
1017
+ <div className={styles.desktopMenuWrapper} style={{top: topSpace}}>
1018
+ <div
1019
+ className={styles.desktopMenuBackgroundContainer}
1020
+ style={{
1021
+ height: menuHeight,
1022
+ }}
1023
+ />
1024
+ </div>
1025
+ </div>
1026
+ );
1027
+ };
1028
+
1029
+ const MainNavigationBarDesktopSmallMenu = ({
1030
+ section,
1031
+ isLargeNavigationBar,
1032
+ topSlotHeight,
1033
+ leftPosition,
1034
+ index,
1035
+ }: {
1036
+ section: MainNavigationBarSection;
1037
+ isLargeNavigationBar: boolean;
1038
+ topSlotHeight: number;
1039
+ leftPosition: number;
1040
+ index: number;
1041
+ }): JSX.Element => {
1042
+ const topSpace =
1043
+ (isLargeNavigationBar ? NAVBAR_HEIGHT_DESKTOP_LARGE : NAVBAR_HEIGHT_DESKTOP) + topSlotHeight;
1044
+ const bottomSpace = 40;
1045
+
1046
+ const columns = section.menu?.columns || [];
1047
+ const customContent = section?.menu?.content;
1048
+
1049
+ const {openedSection, menuStatus, setIsMenuHovered, closeMenu} = useMainNavigationBarDesktopMenuState();
1050
+
1051
+ return (
1052
+ <div className={styles.desktopOnly}>
1053
+ {index === openedSection && (
1054
+ <ThemeVariant variant="default">
1055
+ <div
1056
+ className={styles.desktopSmallMenuContainer}
1057
+ onMouseEnter={() => setIsMenuHovered(true)}
1058
+ onMouseLeave={() => setIsMenuHovered(false)}
1059
+ style={{
1060
+ // disable pointer events when menu is closing
1061
+ pointerEvents:
1062
+ menuStatus === 'closed' || menuStatus === 'closing' ? 'none' : undefined,
1063
+ top: topSpace,
1064
+ left: leftPosition,
1065
+ maxHeight: `calc(100vh - ${topSpace}px - ${bottomSpace}px)`,
1066
+ }}
1067
+ >
1068
+ {customContent ? (
1069
+ typeof customContent === 'function' ? (
1070
+ customContent({closeMenu})
1071
+ ) : (
1072
+ customContent
1073
+ )
1074
+ ) : (
1075
+ <Stack space={40} dataAttributes={{'navigation-bar-menu-items': 'true'}}>
1076
+ {columns.map((column, columnIdx) => (
1077
+ <MainNavigationBarDesktopMenuSectionColumn
1078
+ key={columnIdx}
1079
+ column={column}
1080
+ columnIndex={columnIdx}
1081
+ />
1082
+ ))}
1083
+ </Stack>
1084
+ )}
1085
+ </div>
1086
+ </ThemeVariant>
1087
+ )}
1088
+ </div>
1089
+ );
1090
+ };
1091
+
1092
+ const MainNavigationBarDesktopSection = ({
1093
+ section,
1094
+ index,
1095
+ selectedIndex,
1096
+ isFirstSection,
1097
+ isLastSection,
1098
+ navigationBarRef,
1099
+ variant,
1100
+ isLargeNavigationBar,
1101
+ topSlotHeight,
1102
+ desktopLargeMenu,
1103
+ wide,
1104
+ }: {
1105
+ section: MainNavigationBarSection;
1106
+ index: number;
1107
+ selectedIndex?: number;
1108
+ isFirstSection: boolean;
1109
+ isLastSection: boolean;
1110
+ navigationBarRef: React.RefObject<HTMLDivElement | null>;
1111
+ variant: NonDeprecatedVariant;
1112
+ isLargeNavigationBar: boolean;
1113
+ topSlotHeight: number;
1114
+ desktopLargeMenu?: boolean;
1115
+ wide: boolean | WideConfig;
1116
+ }): JSX.Element => {
1117
+ const {texts, t} = useTheme();
1118
+ const {title, menu, ...touchableProps} = section;
1119
+ const sectionRef = React.useRef<HTMLDivElement>(null);
1120
+ const [smallMenuLeftPosition, setSmallMenuLeftPosition] = React.useState(0);
1121
+ const [isMenuControlFocused, setIsMenuControlFocused] = React.useState(false);
1122
+ const {
1123
+ isMenuOpen,
1124
+ openedSection,
1125
+ openSectionMenu,
1126
+ debouncedOpenSectionMenu,
1127
+ cancelDebouncedOpenSectionMenu,
1128
+ closeMenu,
1129
+ } = useMainNavigationBarDesktopMenuState();
1130
+
1131
+ const hasCustomInteraction =
1132
+ touchableProps.href !== undefined ||
1133
+ touchableProps.onPress !== undefined ||
1134
+ touchableProps.to !== undefined;
1135
+
1136
+ const openCurrentSectionMenu = React.useCallback(
1137
+ (withDebounce?: boolean) => {
1138
+ // Align small menu to left border of the section if it fits. Otherwise, align it to the right border
1139
+ const getSmallMenuLeftPosition = () => {
1140
+ const {left, right} = sectionRef.current?.getBoundingClientRect() || {left: 0, right: 0};
1141
+ const maxLeftOffset =
1142
+ (navigationBarRef.current?.getBoundingClientRect().right || 0) -
1143
+ styles.DESKTOP_SMALL_MENU_WIDTH;
1144
+
1145
+ return left <= maxLeftOffset ? left : right - styles.DESKTOP_SMALL_MENU_WIDTH;
1146
+ };
1147
+ if (!desktopLargeMenu) {
1148
+ setSmallMenuLeftPosition(getSmallMenuLeftPosition());
1149
+ }
1150
+
1151
+ if (withDebounce) {
1152
+ debouncedOpenSectionMenu(index);
1153
+ } else {
1154
+ openSectionMenu(index);
1155
+ }
1156
+ },
1157
+ [desktopLargeMenu, index, openSectionMenu, debouncedOpenSectionMenu, navigationBarRef]
1158
+ );
1159
+
1160
+ React.useEffect(() => {
1161
+ const handleKeyDown = (e: KeyboardEvent) => {
1162
+ switch (e.key) {
1163
+ // If arrow is focused and DOWN key is pressed, open the menu if it was closed
1164
+ case DOWN:
1165
+ if (isMenuControlFocused) {
1166
+ cancelEvent(e);
1167
+ openCurrentSectionMenu();
1168
+ }
1169
+ break;
1170
+
1171
+ // If arrow is focused and UP key is pressed, close the menu if it was opened
1172
+ case UP:
1173
+ if (isMenuControlFocused) {
1174
+ cancelEvent(e);
1175
+ closeMenu();
1176
+ }
1177
+ break;
1178
+
1179
+ default:
1180
+ // Do nothing
1181
+ }
1182
+ };
1183
+
1184
+ document.addEventListener('keydown', handleKeyDown, false);
1185
+ return () => {
1186
+ document.removeEventListener('keydown', handleKeyDown, false);
1187
+ };
1188
+ }, [index, isMenuControlFocused, openCurrentSectionMenu, closeMenu, menu, hasCustomInteraction]);
1189
+
1190
+ const isSectionMenuOpen = isMenuOpen && openedSection === index;
1191
+
1192
+ const menuButtonOnPress = React.useCallback(() => {
1193
+ if (isSectionMenuOpen) {
1194
+ closeMenu();
1195
+ } else {
1196
+ openCurrentSectionMenu();
1197
+ }
1198
+ }, [isSectionMenuOpen, openCurrentSectionMenu, closeMenu]);
1199
+
1200
+ const getSectionInteractiveProps = React.useCallback(
1201
+ (touchableProps: InteractiveProps) => {
1202
+ // Open or close the menu when a section without interaction is pressed
1203
+ if (!hasCustomInteraction) {
1204
+ return {
1205
+ onPress: menuButtonOnPress,
1206
+ };
1207
+ }
1208
+
1209
+ return touchableProps as InteractiveProps;
1210
+ },
1211
+ [hasCustomInteraction, menuButtonOnPress]
1212
+ );
1213
+
1214
+ return (
1215
+ <div className={styles.desktopMenuSectionWithArrowWrapper}>
1216
+ <div
1217
+ ref={sectionRef}
1218
+ className={classnames(styles.desktopMenuSectionContainer, {
1219
+ [styles.desktopMenuFirstSection]: isFirstSection,
1220
+ [styles.desktopMenuLastSection]: isLastSection,
1221
+ })}
1222
+ onMouseEnter={() => openCurrentSectionMenu(true)}
1223
+ onMouseLeave={() => cancelDebouncedOpenSectionMenu(index)}
1224
+ onFocus={() => {
1225
+ if (menu && !hasCustomInteraction) setIsMenuControlFocused(true);
1226
+ }}
1227
+ onBlur={() => {
1228
+ if (menu && !hasCustomInteraction) setIsMenuControlFocused(false);
1229
+ }}
1230
+ >
1231
+ <BaseTouchable
1232
+ {...getSectionInteractiveProps(touchableProps as InteractiveProps)}
1233
+ aria-label={
1234
+ !hasCustomInteraction
1235
+ ? `${section.title}, ${texts.mainNavigationBarOpenSectionMenu || t(tokens.mainNavigationBarOpenSectionMenu)}`
1236
+ : undefined
1237
+ }
1238
+ aria-haspopup={!hasCustomInteraction}
1239
+ aria-expanded={!hasCustomInteraction ? isSectionMenuOpen : undefined}
1240
+ className={classnames(
1241
+ styles.section,
1242
+ {
1243
+ [styles.selectedSectionVariants[variant === 'brand' ? 'brand' : 'default']]:
1244
+ index === selectedIndex,
1245
+ },
1246
+ styles.textWrapperVariants[variant === 'brand' ? 'brand' : 'default']
1247
+ )}
1248
+ >
1249
+ <Text3 regular color="inherit">
1250
+ {title}
1251
+ </Text3>
1252
+ </BaseTouchable>
1253
+ </div>
1254
+ {menu && (
1255
+ <>
1256
+ {hasCustomInteraction && (
1257
+ <div
1258
+ className={styles.desktopMenuSectionArrowContainer}
1259
+ onFocus={() => setIsMenuControlFocused(true)}
1260
+ onBlur={() => setIsMenuControlFocused(false)}
1261
+ >
1262
+ <BaseTouchable
1263
+ className={styles.desktopMenuSectionArrow}
1264
+ aria-label={`${section.title}, ${texts.mainNavigationBarOpenSectionMenu || t(tokens.mainNavigationBarOpenSectionMenu)}`}
1265
+ aria-haspopup
1266
+ aria-expanded={isSectionMenuOpen}
1267
+ onPress={() => {
1268
+ if (isMenuControlFocused) {
1269
+ menuButtonOnPress();
1270
+ }
1271
+ }}
1272
+ style={{
1273
+ pointerEvents: isMenuControlFocused ? 'auto' : 'none',
1274
+ opacity: isMenuControlFocused ? 1 : 0,
1275
+ // We are using the same chevron as in ButtonLink, and that svg has
1276
+ // some extra space on the left that we want to ignore in this case
1277
+ marginTop: -2,
1278
+ }}
1279
+ >
1280
+ <MenuSectionArrow
1281
+ size={8}
1282
+ style={{
1283
+ transform: `rotate(${isSectionMenuOpen ? -90 : 90}deg)`,
1284
+ }}
1285
+ />
1286
+ </BaseTouchable>
1287
+ </div>
1288
+ )}
1289
+ {desktopLargeMenu ? (
1290
+ <MainNavigationBarDesktopMenuContent
1291
+ section={section}
1292
+ isLargeNavigationBar={isLargeNavigationBar}
1293
+ topSlotHeight={topSlotHeight}
1294
+ index={index}
1295
+ wide={wide}
1296
+ />
1297
+ ) : (
1298
+ <MainNavigationBarDesktopSmallMenu
1299
+ section={section}
1300
+ isLargeNavigationBar={isLargeNavigationBar}
1301
+ topSlotHeight={topSlotHeight}
1302
+ leftPosition={smallMenuLeftPosition}
1303
+ index={index}
1304
+ />
1305
+ )}
1306
+ </>
1307
+ )}
1308
+ </div>
1309
+ );
1310
+ };
1311
+
1312
+ const MainNavigationBarDesktopSections = ({
1313
+ sections,
1314
+ selectedIndex,
1315
+ navigationBarRef,
1316
+ variant,
1317
+ isLargeNavigationBar,
1318
+ topSlotHeight,
1319
+ hasRightContent,
1320
+ desktopLargeMenu,
1321
+ wide,
1322
+ }: {
1323
+ sections: ReadonlyArray<MainNavigationBarSection>;
1324
+ selectedIndex?: number;
1325
+ navigationBarRef: React.RefObject<HTMLDivElement | null>;
1326
+ variant: NonDeprecatedVariant;
1327
+ hasRightContent: boolean;
1328
+ isLargeNavigationBar: boolean;
1329
+ topSlotHeight: number;
1330
+ desktopLargeMenu: boolean;
1331
+ wide: boolean | WideConfig;
1332
+ }): JSX.Element => {
1333
+ const {openSectionMenu, openedSection, closeMenu} = useMainNavigationBarDesktopMenuState();
1334
+
1335
+ return (
1336
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
1337
+ <nav
1338
+ className={classnames(styles.desktopOnly, styles.mainNavBarSectionsContainer)}
1339
+ style={{
1340
+ paddingLeft: !isLargeNavigationBar ? 48 : 0,
1341
+ paddingRight: !isLargeNavigationBar && hasRightContent ? 136 : 0,
1342
+ }}
1343
+ onMouseEnter={() => {
1344
+ // Mark current opened section as active to avoid closing the menu when moving the pointer
1345
+ // from the menu content to the navigation bar (without hovering the tab itself)
1346
+ if (openedSection !== -1) {
1347
+ openSectionMenu(openedSection);
1348
+ }
1349
+ }}
1350
+ onMouseLeave={closeMenu}
1351
+ >
1352
+ <Inline space={32}>
1353
+ {sections.map((section, idx) => (
1354
+ <MainNavigationBarDesktopSection
1355
+ key={idx}
1356
+ index={idx}
1357
+ selectedIndex={selectedIndex}
1358
+ navigationBarRef={navigationBarRef}
1359
+ isFirstSection={idx === 0}
1360
+ isLastSection={idx === sections.length - 1}
1361
+ variant={variant}
1362
+ section={section}
1363
+ isLargeNavigationBar={isLargeNavigationBar}
1364
+ topSlotHeight={topSlotHeight}
1365
+ desktopLargeMenu={desktopLargeMenu}
1366
+ wide={wide}
1367
+ />
1368
+ ))}
1369
+ </Inline>
1370
+ </nav>
1371
+ );
1372
+ };
1373
+
1374
+ // It's not easy to coordinate the animation of the menu content height when switching between opened
1375
+ // sections. This is because each section has it's own element where it displays the content. Instead,
1376
+ // the contents of the sections are rendered without any animation, and we keep this wrapper around
1377
+ // all of them, which "hides" the rendered smoothly by using animated clip-path
1378
+ const MainNavigationBarContentWrapper = ({
1379
+ children,
1380
+ isLargeNavigationBar,
1381
+ topSlotHeight,
1382
+ desktopLargeMenu,
1383
+ }: {
1384
+ children: React.ReactNode;
1385
+ isLargeNavigationBar: boolean;
1386
+ topSlotHeight: number;
1387
+ desktopLargeMenu: boolean;
1388
+ }): JSX.Element => {
1389
+ const {menuHeight} = useMainNavigationBarDesktopMenuState();
1390
+ const topSpace =
1391
+ (isLargeNavigationBar ? NAVBAR_HEIGHT_DESKTOP_LARGE : NAVBAR_HEIGHT_DESKTOP) + topSlotHeight;
1392
+
1393
+ return (
1394
+ <div
1395
+ className={styles.mainNavigationBarContentWrapper}
1396
+ style={
1397
+ desktopLargeMenu
1398
+ ? {
1399
+ clipPath: `rect(0 100% calc(${topSpace}px + ${menuHeight}) 0)`,
1400
+ WebkitClipPath: `rect(0 100% calc(${topSpace}px + ${menuHeight}) 0)`,
1401
+ }
1402
+ : undefined
1403
+ }
1404
+ >
1405
+ {children}
1406
+ </div>
1407
+ );
1408
+ };
1409
+
1410
+ export const MainNavigationBar = ({
1411
+ sections = [],
1412
+ selectedIndex,
1413
+ right,
1414
+ topSlot,
1415
+ topSlotBackgroundColor,
1416
+ variant = 'default',
1417
+ topFixed = true,
1418
+ withBorder = true,
1419
+ burgerMenuExtra,
1420
+ logo,
1421
+ large = false,
1422
+ desktopLargeMenu = false,
1423
+ wide = false,
1424
+ }: MainNavigationBarProps): JSX.Element => {
1425
+ const {texts, t} = useTheme();
1426
+ const menuId = React.useId();
1427
+ const {isTabletOrSmaller} = useScreenSize();
1428
+ const logoElement = logo || <Logo size={{mobile: 40, desktop: 48}} />;
1429
+ const hasBottomSections = large && sections.length > 0;
1430
+
1431
+ const [isBurgerMenuOpen, setIsBurgerMenuOpen] = React.useState(false);
1432
+ const [disableFocusTrap, setDisableFocusTrap] = React.useState(true);
1433
+ const navigationBarRef = React.useRef<HTMLDivElement>(null);
1434
+ const {height: topSlotHeight, ref: topSlotRef} = useElementDimensions();
1435
+ const setModalState = useSetModalState();
1436
+
1437
+ const normalizedVariant = normalizeVariant(variant);
1438
+
1439
+ const desktopSections = (
1440
+ <MainNavigationBarDesktopSections
1441
+ sections={sections}
1442
+ selectedIndex={selectedIndex}
1443
+ navigationBarRef={navigationBarRef}
1444
+ variant={normalizedVariant}
1445
+ hasRightContent={!!right}
1446
+ isLargeNavigationBar={hasBottomSections}
1447
+ topSlotHeight={topSlotHeight}
1448
+ desktopLargeMenu={desktopLargeMenu}
1449
+ wide={wide}
1450
+ />
1451
+ );
1452
+
1453
+ const openMenu = () => {
1454
+ setIsBurgerMenuOpen(true);
1455
+ setModalState({isModalOpen: true});
1456
+ };
1457
+
1458
+ const closeMenu = () => {
1459
+ setIsBurgerMenuOpen(false);
1460
+ setModalState({isModalOpen: false});
1461
+ };
1462
+
1463
+ const showBurger = sections.length > 1;
1464
+
1465
+ const mainNavBar = (
1466
+ <ThemeVariant variant={normalizedVariant}>
1467
+ <Header
1468
+ topFixed={topFixed}
1469
+ withBorder={withBorder}
1470
+ isBurgerMenuOpen={isBurgerMenuOpen}
1471
+ variant={normalizedVariant}
1472
+ dataAttributes={{'component-name': 'MainNavigationBar'}}
1473
+ >
1474
+ {desktopLargeMenu && (
1475
+ <MainNavigationBarDesktopMenuBackground
1476
+ isLargeNavigationBar={hasBottomSections}
1477
+ topSlotHeight={topSlotHeight}
1478
+ />
1479
+ )}
1480
+ <MainNavigationBarContentWrapper
1481
+ isLargeNavigationBar={hasBottomSections}
1482
+ topSlotHeight={topSlotHeight}
1483
+ desktopLargeMenu={desktopLargeMenu}
1484
+ >
1485
+ {topSlot && (
1486
+ <NavigationBarSideMargins wide={wide} backgroundColor={topSlotBackgroundColor}>
1487
+ <div ref={topSlotRef}>{topSlot}</div>
1488
+ </NavigationBarSideMargins>
1489
+ )}
1490
+ <NavigationBarSideMargins wide={wide}>
1491
+ <NavigationBarContentContainer
1492
+ ref={navigationBarRef}
1493
+ right={right}
1494
+ expandRightContent={hasBottomSections}
1495
+ >
1496
+ {showBurger && (
1497
+ <Touchable
1498
+ className={styles.burgerMenuButton}
1499
+ aria-live="polite"
1500
+ aria-label={
1501
+ isBurgerMenuOpen
1502
+ ? texts.closeNavigationMenu || t(tokens.closeNavigationMenu)
1503
+ : texts.openNavigationMenu || t(tokens.openNavigationMenu)
1504
+ }
1505
+ aria-expanded={isBurgerMenuOpen}
1506
+ aria-controls={menuId}
1507
+ onPress={isBurgerMenuOpen ? closeMenu : openMenu}
1508
+ >
1509
+ <BurgerMenuIcon isOpen={isBurgerMenuOpen} />
1510
+ </Touchable>
1511
+ )}
1512
+ <div className={styles.logoContainer}>{logoElement}</div>
1513
+ {!hasBottomSections && desktopSections}
1514
+ </NavigationBarContentContainer>
1515
+ {hasBottomSections && (
1516
+ <NavigationBarContentContainer desktopOnly>
1517
+ {desktopSections}
1518
+ </NavigationBarContentContainer>
1519
+ )}
1520
+ </NavigationBarSideMargins>
1521
+ </MainNavigationBarContentWrapper>
1522
+ </Header>
1523
+ {topFixed && topSlotHeight > 0 && <div style={{height: topSlotHeight}} />}
1524
+ {topFixed && <div className={hasBottomSections ? styles.spacerLarge : styles.spacer} />}
1525
+ </ThemeVariant>
1526
+ );
1527
+
1528
+ return (
1529
+ <MainNavigationBarDesktopMenuContextProvider sections={sections} isLargeMenu={desktopLargeMenu}>
1530
+ {!isTabletOrSmaller ? (
1531
+ mainNavBar
1532
+ ) : (
1533
+ <>
1534
+ <FocusTrap disabled={disableFocusTrap} group="burger-menu-lock">
1535
+ {mainNavBar}
1536
+ </FocusTrap>
1537
+ <MainNavigationBarBurgerMenu
1538
+ open={isBurgerMenuOpen}
1539
+ id={menuId}
1540
+ sections={sections}
1541
+ extra={burgerMenuExtra}
1542
+ closeMenu={closeMenu}
1543
+ topSlotHeight={topSlotHeight}
1544
+ disableFocusTrap={disableFocusTrap}
1545
+ setDisableFocusTrap={setDisableFocusTrap}
1546
+ />
1547
+ </>
1548
+ )}
1549
+ </MainNavigationBarDesktopMenuContextProvider>
1550
+ );
1551
+ };
1552
+
1553
+ type FunnelNavigationBarProps = {
1554
+ variant?: Variant;
1555
+ logo?: React.ReactElement;
1556
+ right?: React.ReactElement;
1557
+ topFixed?: boolean;
1558
+ children?: undefined;
1559
+ withBorder?: boolean;
1560
+ wide?: boolean | WideConfig;
1561
+ };
1562
+
1563
+ export const FunnelNavigationBar = ({
1564
+ logo,
1565
+ right,
1566
+ variant = 'default',
1567
+ topFixed = true,
1568
+ withBorder = true,
1569
+ wide = false,
1570
+ }: FunnelNavigationBarProps): JSX.Element => {
1571
+ logo = logo ?? <Logo size={{mobile: 40, desktop: 48}} />;
1572
+
1573
+ const normalizedVariant = normalizeVariant(variant);
1574
+
1575
+ return (
1576
+ <ThemeVariant variant={normalizedVariant}>
1577
+ <Header
1578
+ topFixed={topFixed}
1579
+ withBorder={withBorder}
1580
+ variant={normalizedVariant}
1581
+ dataAttributes={{'component-name': 'FunnelNavigationBar'}}
1582
+ >
1583
+ <NavigationBarSideMargins wide={wide}>
1584
+ <GridLayout template="10">
1585
+ <NavigationBarContentContainer right={right} expandRightContent>
1586
+ {logo}
1587
+ </NavigationBarContentContainer>
1588
+ </GridLayout>
1589
+ </NavigationBarSideMargins>
1590
+ </Header>
1591
+ {topFixed && <div className={styles.spacer} />}
1592
+ </ThemeVariant>
1593
+ );
1594
+ };
1595
+
1596
+ type NavigationBarActionGroupProps = {
1597
+ children: React.ReactNode;
1598
+ };
1599
+
1600
+ export const NavigationBarActionGroup = ({children}: NavigationBarActionGroupProps): JSX.Element => {
1601
+ return (
1602
+ <div className={styles.lineHeightFix} data-component-name="NavigationBarActionGroup">
1603
+ <Inline space={24} alignItems="center">
1604
+ {children}
1605
+ </Inline>
1606
+ </div>
1607
+ );
1608
+ };
1609
+
1610
+ type NavigationBarActionProps = TouchableProps;
1611
+
1612
+ export const NavigationBarAction = ({children, ...touchableProps}: NavigationBarActionProps): JSX.Element => {
1613
+ const themeVariant = useThemeVariant();
1614
+ return (
1615
+ <BaseTouchable
1616
+ {...touchableProps}
1617
+ className={classnames(
1618
+ styles.navigationBarAction,
1619
+ styles.lineHeightFix,
1620
+ styles.textWrapperVariants[themeVariant]
1621
+ )}
1622
+ dataAttributes={{'component-name': 'NavigationBarAction'}}
1623
+ >
1624
+ <Inline space={16} alignItems="center">
1625
+ {React.Children.map(children, (child) =>
1626
+ typeof child === 'string' ? (
1627
+ <Text2 regular color="inherit">
1628
+ {child}
1629
+ </Text2>
1630
+ ) : (
1631
+ child
1632
+ )
1633
+ )}
1634
+ </Inline>
1635
+ </BaseTouchable>
1636
+ );
1637
+ };