@robertlinde/react-tour-kit 0.8.9 → 1.0.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 (368) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/MIGRATION.md +205 -0
  3. package/README.md +401 -850
  4. package/dist/react-native-platform/hooks/use-tour-scroll-view.hook.d.ts +14 -0
  5. package/dist/react-native-platform/hooks/use-tour-scroll-view.hook.d.ts.map +1 -0
  6. package/dist/react-native-platform/hooks/use-tour-scroll-view.hook.js +21 -0
  7. package/dist/react-native-platform/hooks/use-tour-scroll-view.hook.js.map +1 -0
  8. package/dist/react-native-platform/hooks/use-tour-target.hook.d.ts +15 -0
  9. package/dist/react-native-platform/hooks/use-tour-target.hook.d.ts.map +1 -0
  10. package/dist/react-native-platform/hooks/use-tour-target.hook.js +28 -0
  11. package/dist/react-native-platform/hooks/use-tour-target.hook.js.map +1 -0
  12. package/dist/react-native-platform/host-store.d.ts +24 -0
  13. package/dist/react-native-platform/host-store.d.ts.map +1 -0
  14. package/dist/react-native-platform/host-store.js +63 -0
  15. package/dist/react-native-platform/host-store.js.map +1 -0
  16. package/dist/react-native-platform/insets.util.d.ts +11 -0
  17. package/dist/react-native-platform/insets.util.d.ts.map +1 -0
  18. package/dist/react-native-platform/insets.util.js +31 -0
  19. package/dist/react-native-platform/insets.util.js.map +1 -0
  20. package/dist/react-native-platform/layer/native-renderer.d.ts +7 -0
  21. package/dist/react-native-platform/layer/native-renderer.d.ts.map +1 -0
  22. package/dist/react-native-platform/layer/native-renderer.js +496 -0
  23. package/dist/react-native-platform/layer/native-renderer.js.map +1 -0
  24. package/dist/react-native-platform/layer/tour-host.d.ts +13 -0
  25. package/dist/react-native-platform/layer/tour-host.d.ts.map +1 -0
  26. package/dist/react-native-platform/layer/tour-host.js +70 -0
  27. package/dist/react-native-platform/layer/tour-host.js.map +1 -0
  28. package/dist/react-native-platform/measure.util.d.ts +20 -0
  29. package/dist/react-native-platform/measure.util.d.ts.map +1 -0
  30. package/dist/react-native-platform/measure.util.js +100 -0
  31. package/dist/react-native-platform/measure.util.js.map +1 -0
  32. package/dist/react-native-platform/native-context.d.ts +12 -0
  33. package/dist/react-native-platform/native-context.d.ts.map +1 -0
  34. package/dist/react-native-platform/native-context.js +4 -0
  35. package/dist/react-native-platform/native-context.js.map +1 -0
  36. package/dist/react-native-platform/native-environment.d.ts +14 -0
  37. package/dist/react-native-platform/native-environment.d.ts.map +1 -0
  38. package/dist/react-native-platform/native-environment.js +20 -0
  39. package/dist/react-native-platform/native-environment.js.map +1 -0
  40. package/dist/react-native-platform/native-platform.d.ts +19 -0
  41. package/dist/react-native-platform/native-platform.d.ts.map +1 -0
  42. package/dist/react-native-platform/native-platform.js +265 -0
  43. package/dist/react-native-platform/native-platform.js.map +1 -0
  44. package/dist/react-native-platform/reduced-motion.d.ts +4 -0
  45. package/dist/react-native-platform/reduced-motion.d.ts.map +1 -0
  46. package/dist/react-native-platform/reduced-motion.js +44 -0
  47. package/dist/react-native-platform/reduced-motion.js.map +1 -0
  48. package/dist/react-native-platform/registry.d.ts +18 -0
  49. package/dist/react-native-platform/registry.d.ts.map +1 -0
  50. package/dist/react-native-platform/registry.js +51 -0
  51. package/dist/react-native-platform/registry.js.map +1 -0
  52. package/dist/react-native-platform/tour-provider.d.ts +16 -3
  53. package/dist/react-native-platform/tour-provider.d.ts.map +1 -0
  54. package/dist/react-native-platform/tour-provider.js +92 -211
  55. package/dist/react-native-platform/tour-provider.js.map +1 -1
  56. package/dist/react-native.d.ts +14 -10
  57. package/dist/react-native.d.ts.map +1 -0
  58. package/dist/react-native.js +7 -7
  59. package/dist/react-native.js.map +1 -1
  60. package/dist/react-platform/dom/elements.util.d.ts +35 -0
  61. package/dist/react-platform/dom/elements.util.d.ts.map +1 -0
  62. package/dist/react-platform/dom/elements.util.js +155 -0
  63. package/dist/react-platform/dom/elements.util.js.map +1 -0
  64. package/dist/react-platform/dom/occluders.util.d.ts +13 -0
  65. package/dist/react-platform/dom/occluders.util.d.ts.map +1 -0
  66. package/dist/react-platform/dom/occluders.util.js +75 -0
  67. package/dist/react-platform/dom/occluders.util.js.map +1 -0
  68. package/dist/react-platform/dom/scroll-lock.util.d.ts +13 -0
  69. package/dist/react-platform/dom/scroll-lock.util.d.ts.map +1 -0
  70. package/dist/react-platform/dom/scroll-lock.util.js +219 -0
  71. package/dist/react-platform/dom/scroll-lock.util.js.map +1 -0
  72. package/dist/react-platform/dom/scroll.util.d.ts +44 -0
  73. package/dist/react-platform/dom/scroll.util.d.ts.map +1 -0
  74. package/dist/react-platform/dom/scroll.util.js +250 -0
  75. package/dist/react-platform/dom/scroll.util.js.map +1 -0
  76. package/dist/react-platform/dom/viewport.util.d.ts +16 -0
  77. package/dist/react-platform/dom/viewport.util.d.ts.map +1 -0
  78. package/dist/react-platform/dom/viewport.util.js +65 -0
  79. package/dist/react-platform/dom/viewport.util.js.map +1 -0
  80. package/dist/react-platform/dom/web-platform.d.ts +15 -0
  81. package/dist/react-platform/dom/web-platform.d.ts.map +1 -0
  82. package/dist/react-platform/dom/web-platform.js +169 -0
  83. package/dist/react-platform/dom/web-platform.js.map +1 -0
  84. package/dist/react-platform/layer/styles.d.ts +12 -0
  85. package/dist/react-platform/layer/styles.d.ts.map +1 -0
  86. package/dist/react-platform/layer/styles.js +285 -0
  87. package/dist/react-platform/layer/styles.js.map +1 -0
  88. package/dist/react-platform/layer/web-renderer.d.ts +4 -0
  89. package/dist/react-platform/layer/web-renderer.d.ts.map +1 -0
  90. package/dist/react-platform/layer/web-renderer.js +390 -0
  91. package/dist/react-platform/layer/web-renderer.js.map +1 -0
  92. package/dist/react-platform/tour-provider.d.ts +9 -3
  93. package/dist/react-platform/tour-provider.d.ts.map +1 -0
  94. package/dist/react-platform/tour-provider.js +28 -218
  95. package/dist/react-platform/tour-provider.js.map +1 -1
  96. package/dist/react.d.ts +10 -10
  97. package/dist/react.d.ts.map +1 -0
  98. package/dist/react.js +5 -7
  99. package/dist/react.js.map +1 -1
  100. package/dist/shared/engine/abort.util.d.ts +17 -0
  101. package/dist/shared/engine/abort.util.d.ts.map +1 -0
  102. package/dist/shared/engine/abort.util.js +72 -0
  103. package/dist/shared/engine/abort.util.js.map +1 -0
  104. package/dist/shared/engine/report.util.d.ts +12 -0
  105. package/dist/shared/engine/report.util.d.ts.map +1 -0
  106. package/dist/shared/engine/report.util.js +51 -0
  107. package/dist/shared/engine/report.util.js.map +1 -0
  108. package/dist/shared/engine/tour-adapter.type.d.ts +67 -0
  109. package/dist/shared/engine/tour-adapter.type.d.ts.map +1 -0
  110. package/dist/shared/engine/tour-adapter.type.js +2 -0
  111. package/dist/shared/engine/tour-adapter.type.js.map +1 -0
  112. package/dist/shared/engine/tour-engine.d.ts +101 -0
  113. package/dist/shared/engine/tour-engine.d.ts.map +1 -0
  114. package/dist/shared/engine/tour-engine.js +835 -0
  115. package/dist/shared/engine/tour-engine.js.map +1 -0
  116. package/dist/shared/geometry/box.util.d.ts +39 -0
  117. package/dist/shared/geometry/box.util.d.ts.map +1 -0
  118. package/dist/shared/geometry/box.util.js +146 -0
  119. package/dist/shared/geometry/box.util.js.map +1 -0
  120. package/dist/shared/geometry/compute-tooltip-layout.util.d.ts +60 -0
  121. package/dist/shared/geometry/compute-tooltip-layout.util.d.ts.map +1 -0
  122. package/dist/shared/geometry/compute-tooltip-layout.util.js +148 -0
  123. package/dist/shared/geometry/compute-tooltip-layout.util.js.map +1 -0
  124. package/dist/shared/geometry/geometry.type.d.ts +34 -0
  125. package/dist/shared/geometry/geometry.type.d.ts.map +1 -0
  126. package/dist/shared/geometry/geometry.type.js +2 -0
  127. package/dist/shared/geometry/geometry.type.js.map +1 -0
  128. package/dist/shared/geometry/reveal.util.d.ts +38 -0
  129. package/dist/shared/geometry/reveal.util.d.ts.map +1 -0
  130. package/dist/shared/geometry/reveal.util.js +135 -0
  131. package/dist/shared/geometry/reveal.util.js.map +1 -0
  132. package/dist/shared/geometry/target-visibility.util.d.ts +10 -0
  133. package/dist/shared/geometry/target-visibility.util.d.ts.map +1 -0
  134. package/dist/shared/geometry/target-visibility.util.js +29 -0
  135. package/dist/shared/geometry/target-visibility.util.js.map +1 -0
  136. package/dist/shared/platform/create-tour-adapter.d.ts +9 -0
  137. package/dist/shared/platform/create-tour-adapter.d.ts.map +1 -0
  138. package/dist/shared/platform/create-tour-adapter.js +145 -0
  139. package/dist/shared/platform/create-tour-adapter.js.map +1 -0
  140. package/dist/shared/platform/platform.type.d.ts +77 -0
  141. package/dist/shared/platform/platform.type.d.ts.map +1 -0
  142. package/dist/shared/platform/platform.type.js +2 -0
  143. package/dist/shared/platform/platform.type.js.map +1 -0
  144. package/dist/shared/platform/reveal-in-scroll-containers.d.ts +27 -0
  145. package/dist/shared/platform/reveal-in-scroll-containers.d.ts.map +1 -0
  146. package/dist/shared/platform/reveal-in-scroll-containers.js +49 -0
  147. package/dist/shared/platform/reveal-in-scroll-containers.js.map +1 -0
  148. package/dist/shared/react/tour-context.d.ts +7 -0
  149. package/dist/shared/react/tour-context.d.ts.map +1 -0
  150. package/dist/shared/react/tour-context.js +23 -0
  151. package/dist/shared/react/tour-context.js.map +1 -0
  152. package/dist/shared/react/tour-provider-core.d.ts +41 -0
  153. package/dist/shared/react/tour-provider-core.d.ts.map +1 -0
  154. package/dist/shared/react/tour-provider-core.js +24 -0
  155. package/dist/shared/react/tour-provider-core.js.map +1 -0
  156. package/dist/shared/react/tour-root.d.ts +14 -0
  157. package/dist/shared/react/tour-root.d.ts.map +1 -0
  158. package/dist/shared/react/tour-root.js +22 -0
  159. package/dist/shared/react/tour-root.js.map +1 -0
  160. package/dist/shared/react/use-isomorphic-layout-effect.hook.d.ts +4 -0
  161. package/dist/shared/react/use-isomorphic-layout-effect.hook.d.ts.map +1 -0
  162. package/dist/shared/react/use-isomorphic-layout-effect.hook.js +5 -0
  163. package/dist/shared/react/use-isomorphic-layout-effect.hook.js.map +1 -0
  164. package/dist/shared/react/use-tour-view.hook.d.ts +5 -0
  165. package/dist/shared/react/use-tour-view.hook.d.ts.map +1 -0
  166. package/dist/shared/react/use-tour-view.hook.js +14 -0
  167. package/dist/shared/react/use-tour-view.hook.js.map +1 -0
  168. package/dist/shared/react/use-tour.hook.d.ts +7 -0
  169. package/dist/shared/react/use-tour.hook.d.ts.map +1 -0
  170. package/dist/shared/react/use-tour.hook.js +10 -0
  171. package/dist/shared/react/use-tour.hook.js.map +1 -0
  172. package/dist/shared/renderer/default-tooltip.d.ts +5 -0
  173. package/dist/shared/renderer/default-tooltip.d.ts.map +1 -0
  174. package/dist/shared/renderer/default-tooltip.js +13 -0
  175. package/dist/shared/renderer/default-tooltip.js.map +1 -0
  176. package/dist/shared/renderer/motion.d.ts +29 -0
  177. package/dist/shared/renderer/motion.d.ts.map +1 -0
  178. package/dist/shared/renderer/motion.js +49 -0
  179. package/dist/shared/renderer/motion.js.map +1 -0
  180. package/dist/shared/renderer/renderer-context.d.ts +17 -0
  181. package/dist/shared/renderer/renderer-context.d.ts.map +1 -0
  182. package/dist/shared/renderer/renderer-context.js +16 -0
  183. package/dist/shared/renderer/renderer-context.js.map +1 -0
  184. package/dist/shared/renderer/renderer.type.d.ts +164 -0
  185. package/dist/shared/renderer/renderer.type.d.ts.map +1 -0
  186. package/dist/shared/renderer/renderer.type.js +2 -0
  187. package/dist/shared/renderer/renderer.type.js.map +1 -0
  188. package/dist/shared/renderer/tooltip-model.d.ts +32 -0
  189. package/dist/shared/renderer/tooltip-model.d.ts.map +1 -0
  190. package/dist/shared/renderer/tooltip-model.js +24 -0
  191. package/dist/shared/renderer/tooltip-model.js.map +1 -0
  192. package/dist/shared/renderer/tour-overlay.d.ts +24 -0
  193. package/dist/shared/renderer/tour-overlay.d.ts.map +1 -0
  194. package/dist/shared/renderer/tour-overlay.js +228 -0
  195. package/dist/shared/renderer/tour-overlay.js.map +1 -0
  196. package/dist/shared/types/tour-context.type.d.ts +21 -11
  197. package/dist/shared/types/tour-context.type.d.ts.map +1 -0
  198. package/dist/shared/types/tour-end-info.type.d.ts +19 -2
  199. package/dist/shared/types/tour-end-info.type.d.ts.map +1 -0
  200. package/dist/shared/types/tour-i18n.type.d.ts +8 -0
  201. package/dist/shared/types/tour-i18n.type.d.ts.map +1 -0
  202. package/dist/shared/types/tour-step.type.d.ts +35 -6
  203. package/dist/shared/types/tour-step.type.d.ts.map +1 -0
  204. package/dist/shared/types/tour-theme.type.d.ts +22 -2
  205. package/dist/shared/types/tour-theme.type.d.ts.map +1 -0
  206. package/dist/shared/types/tour-theme.type.js +1 -16
  207. package/dist/shared/types/tour-theme.type.js.map +1 -1
  208. package/dist/shared/types/tour-tooltip-props.type.d.ts +21 -17
  209. package/dist/shared/types/tour-tooltip-props.type.d.ts.map +1 -0
  210. package/dist/shared/utils/frame.util.d.ts +6 -0
  211. package/dist/shared/utils/frame.util.d.ts.map +1 -0
  212. package/dist/shared/utils/frame.util.js +20 -0
  213. package/dist/shared/utils/frame.util.js.map +1 -0
  214. package/dist/shared/utils/get-truncated-dots.util.d.ts +13 -7
  215. package/dist/shared/utils/get-truncated-dots.util.d.ts.map +1 -0
  216. package/dist/shared/utils/get-truncated-dots.util.js +30 -30
  217. package/dist/shared/utils/get-truncated-dots.util.js.map +1 -1
  218. package/dist/shared/utils/i18n.util.d.ts +7 -0
  219. package/dist/shared/utils/i18n.util.d.ts.map +1 -0
  220. package/dist/shared/utils/i18n.util.js +34 -0
  221. package/dist/shared/utils/i18n.util.js.map +1 -0
  222. package/dist/shared/utils/theme.util.d.ts +6 -0
  223. package/dist/shared/utils/theme.util.d.ts.map +1 -0
  224. package/dist/shared/utils/theme.util.js +31 -0
  225. package/dist/shared/utils/theme.util.js.map +1 -0
  226. package/package.json +72 -55
  227. package/src/react-native-platform/hooks/use-tour-scroll-view.hook.ts +24 -0
  228. package/src/react-native-platform/hooks/use-tour-target.hook.ts +30 -0
  229. package/src/react-native-platform/host-store.ts +92 -0
  230. package/src/react-native-platform/insets.util.ts +39 -0
  231. package/src/react-native-platform/layer/native-renderer.tsx +776 -0
  232. package/src/react-native-platform/layer/tour-host.tsx +105 -0
  233. package/src/react-native-platform/measure.util.ts +140 -0
  234. package/src/react-native-platform/native-context.ts +15 -0
  235. package/src/react-native-platform/native-environment.ts +33 -0
  236. package/src/react-native-platform/native-platform.ts +368 -0
  237. package/src/react-native-platform/reduced-motion.ts +47 -0
  238. package/src/react-native-platform/registry.ts +70 -0
  239. package/src/react-native-platform/tour-provider.tsx +148 -0
  240. package/src/react-native.ts +15 -0
  241. package/src/react-platform/dom/elements.util.ts +191 -0
  242. package/src/react-platform/dom/occluders.util.ts +88 -0
  243. package/src/react-platform/dom/scroll-lock.util.ts +261 -0
  244. package/src/react-platform/dom/scroll.util.ts +310 -0
  245. package/src/react-platform/dom/viewport.util.ts +74 -0
  246. package/src/react-platform/dom/web-platform.ts +208 -0
  247. package/src/react-platform/layer/styles.ts +286 -0
  248. package/src/react-platform/layer/web-renderer.tsx +594 -0
  249. package/src/react-platform/tour-provider.tsx +44 -0
  250. package/src/react.ts +13 -0
  251. package/src/shared/engine/abort.util.ts +84 -0
  252. package/src/shared/engine/report.util.ts +58 -0
  253. package/src/shared/engine/tour-adapter.type.ts +73 -0
  254. package/src/shared/engine/tour-engine.ts +1147 -0
  255. package/src/shared/geometry/box.util.ts +185 -0
  256. package/src/shared/geometry/compute-tooltip-layout.util.ts +238 -0
  257. package/src/shared/geometry/geometry.type.ts +38 -0
  258. package/src/shared/geometry/reveal.util.ts +190 -0
  259. package/src/shared/geometry/target-visibility.util.ts +44 -0
  260. package/src/shared/platform/create-tour-adapter.ts +169 -0
  261. package/src/shared/platform/platform.type.ts +81 -0
  262. package/src/shared/platform/reveal-in-scroll-containers.ts +78 -0
  263. package/src/shared/react/tour-context.ts +28 -0
  264. package/src/shared/react/tour-provider-core.ts +67 -0
  265. package/src/shared/react/tour-root.tsx +34 -0
  266. package/src/shared/react/use-isomorphic-layout-effect.hook.ts +7 -0
  267. package/src/shared/react/use-tour-view.hook.ts +17 -0
  268. package/src/shared/react/use-tour.hook.ts +11 -0
  269. package/src/shared/renderer/default-tooltip.tsx +52 -0
  270. package/src/shared/renderer/motion.ts +59 -0
  271. package/src/shared/renderer/renderer-context.ts +33 -0
  272. package/src/shared/renderer/renderer.type.ts +183 -0
  273. package/src/shared/renderer/tooltip-model.ts +44 -0
  274. package/src/shared/renderer/tour-overlay.tsx +453 -0
  275. package/src/shared/types/tour-context.type.ts +23 -0
  276. package/src/shared/types/tour-end-info.type.ts +32 -0
  277. package/src/shared/types/tour-i18n.type.ts +15 -0
  278. package/src/shared/types/tour-step.type.ts +40 -0
  279. package/src/shared/types/tour-theme.type.ts +28 -0
  280. package/src/shared/types/tour-tooltip-props.type.ts +27 -0
  281. package/src/shared/utils/frame.util.ts +21 -0
  282. package/src/shared/utils/get-truncated-dots.util.ts +46 -0
  283. package/src/shared/utils/i18n.util.ts +44 -0
  284. package/src/shared/utils/theme.util.ts +38 -0
  285. package/dist/react-native-platform/components/index.d.ts +0 -2
  286. package/dist/react-native-platform/components/index.js +0 -3
  287. package/dist/react-native-platform/components/index.js.map +0 -1
  288. package/dist/react-native-platform/components/tour-overlay.d.ts +0 -3
  289. package/dist/react-native-platform/components/tour-overlay.js +0 -26
  290. package/dist/react-native-platform/components/tour-overlay.js.map +0 -1
  291. package/dist/react-native-platform/components/tour-tooltip.d.ts +0 -3
  292. package/dist/react-native-platform/components/tour-tooltip.js +0 -167
  293. package/dist/react-native-platform/components/tour-tooltip.js.map +0 -1
  294. package/dist/react-native-platform/hooks/index.d.ts +0 -1
  295. package/dist/react-native-platform/hooks/index.js +0 -2
  296. package/dist/react-native-platform/hooks/index.js.map +0 -1
  297. package/dist/react-native-platform/hooks/use-tour-target.d.ts +0 -2
  298. package/dist/react-native-platform/hooks/use-tour-target.js +0 -13
  299. package/dist/react-native-platform/hooks/use-tour-target.js.map +0 -1
  300. package/dist/react-native-platform/index.d.ts +0 -7
  301. package/dist/react-native-platform/index.js +0 -7
  302. package/dist/react-native-platform/index.js.map +0 -1
  303. package/dist/react-native-platform/platform-adapter.d.ts +0 -2
  304. package/dist/react-native-platform/platform-adapter.js +0 -84
  305. package/dist/react-native-platform/platform-adapter.js.map +0 -1
  306. package/dist/react-native-platform/target-registry.d.ts +0 -10
  307. package/dist/react-native-platform/target-registry.js +0 -17
  308. package/dist/react-native-platform/target-registry.js.map +0 -1
  309. package/dist/react-native-platform/types/tour-provider.props.type.d.ts +0 -12
  310. package/dist/react-native-platform/types/tour-provider.props.type.js +0 -2
  311. package/dist/react-native-platform/types/tour-provider.props.type.js.map +0 -1
  312. package/dist/react-platform/components/index.d.ts +0 -2
  313. package/dist/react-platform/components/index.js +0 -3
  314. package/dist/react-platform/components/index.js.map +0 -1
  315. package/dist/react-platform/components/tour-overlay.d.ts +0 -3
  316. package/dist/react-platform/components/tour-overlay.js +0 -29
  317. package/dist/react-platform/components/tour-overlay.js.map +0 -1
  318. package/dist/react-platform/components/tour-tooltip.d.ts +0 -2
  319. package/dist/react-platform/components/tour-tooltip.js +0 -155
  320. package/dist/react-platform/components/tour-tooltip.js.map +0 -1
  321. package/dist/react-platform/index.d.ts +0 -6
  322. package/dist/react-platform/index.js +0 -6
  323. package/dist/react-platform/index.js.map +0 -1
  324. package/dist/react-platform/platform-adapter.d.ts +0 -2
  325. package/dist/react-platform/platform-adapter.js +0 -101
  326. package/dist/react-platform/platform-adapter.js.map +0 -1
  327. package/dist/react-platform/types/tour-provider.props.type.d.ts +0 -12
  328. package/dist/react-platform/types/tour-provider.props.type.js +0 -2
  329. package/dist/react-platform/types/tour-provider.props.type.js.map +0 -1
  330. package/dist/react-platform/utils/find-visible-element.util.d.ts +0 -1
  331. package/dist/react-platform/utils/find-visible-element.util.js +0 -11
  332. package/dist/react-platform/utils/find-visible-element.util.js.map +0 -1
  333. package/dist/react-platform/utils/index.d.ts +0 -1
  334. package/dist/react-platform/utils/index.js +0 -2
  335. package/dist/react-platform/utils/index.js.map +0 -1
  336. package/dist/shared/context/index.d.ts +0 -1
  337. package/dist/shared/context/index.js +0 -2
  338. package/dist/shared/context/index.js.map +0 -1
  339. package/dist/shared/context/tour-context.d.ts +0 -3
  340. package/dist/shared/context/tour-context.js +0 -14
  341. package/dist/shared/context/tour-context.js.map +0 -1
  342. package/dist/shared/hooks/index.d.ts +0 -1
  343. package/dist/shared/hooks/index.js +0 -2
  344. package/dist/shared/hooks/index.js.map +0 -1
  345. package/dist/shared/hooks/use-tour.hook.d.ts +0 -2
  346. package/dist/shared/hooks/use-tour.hook.js +0 -7
  347. package/dist/shared/hooks/use-tour.hook.js.map +0 -1
  348. package/dist/shared/index.d.ts +0 -7
  349. package/dist/shared/index.js +0 -6
  350. package/dist/shared/index.js.map +0 -1
  351. package/dist/shared/types/index.d.ts +0 -10
  352. package/dist/shared/types/index.js +0 -2
  353. package/dist/shared/types/index.js.map +0 -1
  354. package/dist/shared/types/platform-adapter.type.d.ts +0 -24
  355. package/dist/shared/types/platform-adapter.type.js +0 -2
  356. package/dist/shared/types/platform-adapter.type.js.map +0 -1
  357. package/dist/shared/types/rect.type.d.ts +0 -11
  358. package/dist/shared/types/rect.type.js +0 -2
  359. package/dist/shared/types/rect.type.js.map +0 -1
  360. package/dist/shared/types/tour-overlay-props.type.d.ts +0 -8
  361. package/dist/shared/types/tour-overlay-props.type.js +0 -2
  362. package/dist/shared/types/tour-overlay-props.type.js.map +0 -1
  363. package/dist/shared/utils/calculate-tooltip-position.util.d.ts +0 -14
  364. package/dist/shared/utils/calculate-tooltip-position.util.js +0 -49
  365. package/dist/shared/utils/calculate-tooltip-position.util.js.map +0 -1
  366. package/dist/shared/utils/index.d.ts +0 -3
  367. package/dist/shared/utils/index.js +0 -3
  368. package/dist/shared/utils/index.js.map +0 -1
@@ -0,0 +1,835 @@
1
+ import { boxesEqual, containsBox, deflate, intersect, projectOnto, sanitizeBox, sanitizeInsets, sanitizeSize, sizesEqual, } from '../geometry/box.util.js';
2
+ import { computeTooltipLayout, getSafeArea } from '../geometry/compute-tooltip-layout.util.js';
3
+ import { buildRevealMargins } from '../geometry/reveal.util.js';
4
+ import { getTargetVisibility } from '../geometry/target-visibility.util.js';
5
+ import { withTimeout } from './abort.util.js';
6
+ import { devWarn, reportError, safeInvoke } from './report.util.js';
7
+ /** Spacing used for layout, shared by both platforms. */
8
+ export const tourLayout = {
9
+ /** Distance between the highlighted area and the tooltip (arrows are at most this long). */
10
+ gap: 12,
11
+ /** Space between the target and the edge of the highlighted area. */
12
+ highlightPadding: 8,
13
+ /** Minimum distance between the tooltip and the edge of the safe area. */
14
+ viewportPadding: 12,
15
+ /** Minimum distance between an arrow and the tooltip's corners. */
16
+ arrowPadding: 16,
17
+ };
18
+ /** How long to wait for a step's target to appear before skipping it. */
19
+ export const targetTimeoutMs = 5000;
20
+ /** Safety cap for the renderer to report a tooltip's size; it normally reports within the same frame. */
21
+ const measureTimeoutMs = 1000;
22
+ /** Safety cap for scrolling a target into view. */
23
+ const revealTimeoutMs = 2000;
24
+ /**
25
+ * Animations can move a target without any event (React Native's native-driver animations, such as
26
+ * a screen sliding in, and the Web Animations API): until one ends, the target is measured where it
27
+ * started. So right after a step appears, and before a target out of sight is skipped, the target is
28
+ * checked every frame until it has not moved for this long...
29
+ */
30
+ export const silentMoveQuietMs = 1000;
31
+ /** ...or for this long at most. */
32
+ export const silentMoveMaxMs = 3000;
33
+ /**
34
+ * Safety cap for `onBeforeStep`. The tour covers the app meanwhile, so it cannot be waiting for the
35
+ * user; one that never settles would otherwise leave a dimmed screen with no way out.
36
+ */
37
+ export const beforeStepTimeoutMs = 30_000;
38
+ /** How many tours started from `onTourEnd` callbacks are ended before a new tour starts. */
39
+ const maxChainedEnds = 10;
40
+ /** Size assumed when the renderer never reports one (for example in a test environment without layout). */
41
+ const fallbackTooltipSize = { width: 320, height: 180 };
42
+ const emptySteps = Object.freeze([]);
43
+ const sideOrder = {
44
+ top: ['top', 'bottom', 'right', 'left'],
45
+ bottom: ['bottom', 'top', 'right', 'left'],
46
+ left: ['left', 'right', 'bottom', 'top'],
47
+ right: ['right', 'left', 'bottom', 'top'],
48
+ };
49
+ /** The part of the viewport that is not covered by insets (notch, keyboard, system bars). */
50
+ function unobstructed(viewport) {
51
+ return deflate(sanitizeBox(viewport.rect), sanitizeInsets(viewport.insets));
52
+ }
53
+ /** The visible part of the target: clipped by its scroll containers. */
54
+ /**
55
+ * The visible part of the target: clipped by its scroll containers. A target scrolled completely out
56
+ * of its container collapses onto the container's nearest edge, where it will appear.
57
+ */
58
+ function highlightOf(measurement) {
59
+ if (!measurement.clip) {
60
+ return measurement.rect;
61
+ }
62
+ return intersect(measurement.rect, measurement.clip) ?? projectOnto(measurement.rect, measurement.clip);
63
+ }
64
+ /**
65
+ * How much of the target the user can see: inside its scroll containers and the screen, and not
66
+ * covered by elements that stay put (sticky headers, floating buttons). Covered completely counts as
67
+ * not visible; covered partly as partly visible.
68
+ */
69
+ function visibilityOf(measurement, screen) {
70
+ const area = measurement.clip ? intersect(measurement.clip, screen) : screen;
71
+ const visibility = area ? getTargetVisibility(measurement.rect, area) : 'none';
72
+ const occluders = measurement.occluders ?? [];
73
+ if (visibility === 'none' || occluders.length === 0) {
74
+ return visibility;
75
+ }
76
+ if (occluders.some((occluder) => containsBox(occluder, measurement.rect))) {
77
+ return 'none';
78
+ }
79
+ return occluders.some((occluder) => intersect(occluder, measurement.rect) !== undefined) ? 'partial' : visibility;
80
+ }
81
+ function layoutsEqual(a, b) {
82
+ return (a.placement === b.placement &&
83
+ Math.abs(a.x - b.x) < 0.01 &&
84
+ Math.abs(a.y - b.y) < 0.01 &&
85
+ Math.abs(a.width - b.width) < 0.01 &&
86
+ Math.abs(a.height - b.height) < 0.01 &&
87
+ Math.abs(a.maxWidth - b.maxWidth) < 0.01 &&
88
+ Math.abs(a.maxHeight - b.maxHeight) < 0.01 &&
89
+ (a.arrowOffset === b.arrowOffset ||
90
+ (a.arrowOffset !== undefined && b.arrowOffset !== undefined && Math.abs(a.arrowOffset - b.arrowOffset) < 0.01)));
91
+ }
92
+ /**
93
+ * Creates the platform-independent tour engine: a small store plus the step lifecycle
94
+ * (`onBeforeStep` → wait for target → measure tooltip → scroll into view → verify → show).
95
+ *
96
+ * Every step change is driven by an action, never by a re-render, so user callbacks run exactly
97
+ * once per navigation no matter how often React renders. Every asynchronous step is cancellable.
98
+ */
99
+ export function createTourEngine(initialOptions) {
100
+ let options = initialOptions;
101
+ const listeners = new Set();
102
+ let state = {
103
+ phase: 'idle',
104
+ session: undefined,
105
+ currentStep: 0,
106
+ pendingStepIndex: undefined,
107
+ shown: undefined,
108
+ measure: undefined,
109
+ revealHighlight: undefined,
110
+ };
111
+ let publicState = { isActive: false, currentStep: 0, steps: emptySteps, currentTourId: undefined };
112
+ let viewState = {
113
+ phase: 'idle',
114
+ revealHighlight: undefined,
115
+ followsScroll: initialOptions.adapter.measuresWhileScrolling,
116
+ tourKey: 0,
117
+ steps: emptySteps,
118
+ shown: undefined,
119
+ measure: undefined,
120
+ };
121
+ let sessionCounter = 0;
122
+ let idCounter = 0;
123
+ /** Preparation of a step in flight. */
124
+ let transition;
125
+ /** Observation of the step on screen. */
126
+ let stepScope;
127
+ let shownDetails;
128
+ let measureSequence = 0;
129
+ /** A scroll that brings the shown target back into view is running. */
130
+ let isKeepingInView = false;
131
+ /** The target moved again while keepInView was scrolling: check once more when it is done. */
132
+ let isKeepInViewStale = false;
133
+ /** The scroll of keepInView in flight, stopped when the next step is prepared. */
134
+ let keepInViewScope;
135
+ let lastTooltipSize;
136
+ const reportedSizes = new Map();
137
+ const sizeWaiters = new Map();
138
+ let lifecycle = 'live';
139
+ // -------------------------------------------------------------------------------------------
140
+ // Store
141
+ // -------------------------------------------------------------------------------------------
142
+ /** The public state for `useTour()`; the previous object while nothing in it changed. */
143
+ function derivePublicState(previous) {
144
+ const next = {
145
+ isActive: state.phase !== 'idle',
146
+ currentStep: state.currentStep,
147
+ steps: state.session?.source ?? emptySteps,
148
+ currentTourId: state.session?.tourId,
149
+ };
150
+ const isUnchanged = next.isActive === previous.isActive &&
151
+ next.currentStep === previous.currentStep &&
152
+ next.steps === previous.steps &&
153
+ next.currentTourId === previous.currentTourId;
154
+ return isUnchanged ? previous : next;
155
+ }
156
+ /** The state the renderer draws; the previous object while nothing in it changed. */
157
+ function deriveViewState(previous) {
158
+ const next = {
159
+ phase: state.phase,
160
+ revealHighlight: state.phase === 'revealing' ? state.revealHighlight : undefined,
161
+ followsScroll: options.adapter.measuresWhileScrolling,
162
+ tourKey: state.session?.key ?? previous.tourKey,
163
+ steps: state.session?.steps ?? emptySteps,
164
+ shown: state.shown,
165
+ measure: state.measure,
166
+ };
167
+ const isUnchanged = next.phase === previous.phase &&
168
+ next.revealHighlight === previous.revealHighlight &&
169
+ next.followsScroll === previous.followsScroll &&
170
+ next.tourKey === previous.tourKey &&
171
+ next.steps === previous.steps &&
172
+ next.shown === previous.shown &&
173
+ next.measure === previous.measure;
174
+ return isUnchanged ? previous : next;
175
+ }
176
+ function setState(patch) {
177
+ state = { ...state, ...patch };
178
+ const nextPublic = derivePublicState(publicState);
179
+ const nextView = deriveViewState(viewState);
180
+ if (nextPublic === publicState && nextView === viewState) {
181
+ return;
182
+ }
183
+ publicState = nextPublic;
184
+ viewState = nextView;
185
+ // Snapshot: listeners may subscribe or unsubscribe while being notified.
186
+ const snapshot = [...listeners];
187
+ for (const listener of snapshot) {
188
+ listener();
189
+ }
190
+ }
191
+ // -------------------------------------------------------------------------------------------
192
+ // Layout
193
+ // -------------------------------------------------------------------------------------------
194
+ function computeLayout(step, highlight, size, previous) {
195
+ return computeTooltipLayout({
196
+ ...tourLayout,
197
+ target: highlight,
198
+ tooltip: size,
199
+ viewport: options.adapter.getViewport(step.target),
200
+ placement: step.placement ?? 'bottom',
201
+ previousPlacement: previous?.placement,
202
+ pixelRatio: options.adapter.getPixelRatio?.(),
203
+ });
204
+ }
205
+ function updateShown(measurement, size) {
206
+ const { shown } = state;
207
+ if (!shown) {
208
+ return;
209
+ }
210
+ shownDetails = { measurement, size };
211
+ const highlight = measurement ? highlightOf(measurement) : undefined;
212
+ const layout = computeLayout(shown.step, highlight, size, shown.layout);
213
+ if (boxesEqual(highlight, shown.highlight) && layoutsEqual(layout, shown.layout)) {
214
+ return;
215
+ }
216
+ setState({ shown: { ...shown, highlight, layout } });
217
+ }
218
+ // -------------------------------------------------------------------------------------------
219
+ // Step lifecycle
220
+ // -------------------------------------------------------------------------------------------
221
+ async function waitForTooltipSize(id, signal) {
222
+ const reported = reportedSizes.get(id);
223
+ if (reported) {
224
+ return reported;
225
+ }
226
+ return new Promise((resolve) => {
227
+ sizeWaiters.set(id, resolve);
228
+ signal.addEventListener('abort', () => {
229
+ sizeWaiters.delete(id);
230
+ resolve(undefined);
231
+ });
232
+ });
233
+ }
234
+ async function measureTooltip(index, step, signal) {
235
+ const id = ++idCounter;
236
+ const safe = getSafeArea(options.adapter.getViewport(step.target), tourLayout.viewportPadding);
237
+ setState({ measure: { id, stepIndex: index, step, maxWidth: safe.width, maxHeight: safe.height } });
238
+ const size = await withTimeout(async (childSignal) => waitForTooltipSize(id, childSignal), signal, measureTimeoutMs);
239
+ reportedSizes.delete(id);
240
+ return { id, size: size ?? lastTooltipSize ?? fallbackTooltipSize };
241
+ }
242
+ /** Waits up to `ms` for the target to move away from `from`; resolves `undefined` if it does not. */
243
+ async function waitForMove(target, from, signal, ms) {
244
+ return withTimeout(async (childSignal) => {
245
+ const to = await options.adapter.waitForMove(target, from, childSignal);
246
+ return childSignal.aborted ? undefined : { to };
247
+ }, signal, ms);
248
+ }
249
+ /** Scrolls the target into view and returns its measurement if it is (at least partly) visible. */
250
+ async function reveal(input) {
251
+ const { target, signal } = input;
252
+ // While scrolling, the highlight follows the target frame by frame so the user sees where the tour goes.
253
+ const following = new AbortController();
254
+ const stopFollowing = () => {
255
+ following.abort();
256
+ };
257
+ signal.addEventListener('abort', stopFollowing);
258
+ let hasStarted = false;
259
+ const startFollowing = () => {
260
+ if (!hasStarted && !signal.aborted) {
261
+ hasStarted = true;
262
+ followTarget(target, following.signal).catch(reportError);
263
+ }
264
+ };
265
+ try {
266
+ let outcome = await scrollIntoViewAndVerify({ ...input, startFollowing });
267
+ // Out of sight, the target may still be on its way (see `silentMoveQuietMs`): give it a moment
268
+ // to arrive before it is skipped.
269
+ const deadline = Date.now() + silentMoveMaxMs;
270
+ while (outcome && !outcome.isInSight && Date.now() < deadline) {
271
+ // eslint-disable-next-line no-await-in-loop -- each attempt starts where the target moved to
272
+ const move = await waitForMove(target, outcome.measurement, signal, Math.min(silentMoveQuietMs, deadline - Date.now()));
273
+ if (!move?.to || signal.aborted) {
274
+ return undefined;
275
+ }
276
+ // eslint-disable-next-line no-await-in-loop -- see above
277
+ outcome = await scrollIntoViewAndVerify({ ...input, measurement: move.to, startFollowing });
278
+ }
279
+ return outcome?.isInSight ? outcome.measurement : undefined;
280
+ }
281
+ finally {
282
+ signal.removeEventListener('abort', stopFollowing);
283
+ stopFollowing();
284
+ }
285
+ }
286
+ /** Moves the reveal highlight with the target, once per frame, until `signal` aborts. */
287
+ async function followTarget(target, signal) {
288
+ const { adapter } = options;
289
+ while (!signal.aborted) {
290
+ // eslint-disable-next-line no-await-in-loop -- one measurement per frame
291
+ await adapter.nextFrame(signal);
292
+ // eslint-disable-next-line no-await-in-loop -- see above
293
+ const current = signal.aborted ? undefined : await adapter.measure(target);
294
+ if (!signal.aborted && state.phase === 'revealing' && current) {
295
+ const highlight = highlightOf(current);
296
+ if (!boxesEqual(highlight, state.revealHighlight)) {
297
+ setState({ revealHighlight: highlight });
298
+ }
299
+ }
300
+ }
301
+ }
302
+ /** Resolves `undefined` when aborted, or when the target is gone. */
303
+ async function scrollIntoViewAndVerify({ step, target, measurement, size, signal, startFollowing, }) {
304
+ const { adapter } = options;
305
+ const margins = buildRevealMargins({
306
+ ...tourLayout,
307
+ target: highlightOf(measurement),
308
+ tooltip: size,
309
+ area: unobstructed(adapter.getViewport(target)),
310
+ sides: sideOrder[step.placement ?? 'bottom'],
311
+ });
312
+ // Where the target is now: the second attempt starts where the first one ended.
313
+ const latest = { measurement };
314
+ for (const behavior of ['smooth', 'instant']) {
315
+ // eslint-disable-next-line no-await-in-loop -- the retry depends on the first attempt's outcome
316
+ await withTimeout(async (childSignal) => {
317
+ await adapter.reveal(target, {
318
+ margins,
319
+ behavior,
320
+ signal: childSignal,
321
+ onScrollStart(destination) {
322
+ if (signal.aborted) {
323
+ return;
324
+ }
325
+ if (adapter.measuresWhileScrolling) {
326
+ setState({ phase: 'revealing', revealHighlight: highlightOf(latest.measurement) });
327
+ startFollowing();
328
+ }
329
+ else {
330
+ // Positions only update once scrolling ends: move the highlight to where the target is
331
+ // going, together with the scroll.
332
+ const screen = unobstructed(adapter.getViewport(target));
333
+ setState({ phase: 'revealing', revealHighlight: highlightOf({ rect: destination, clip: screen }) });
334
+ }
335
+ },
336
+ });
337
+ return true;
338
+ }, signal, revealTimeoutMs);
339
+ if (signal.aborted) {
340
+ return undefined;
341
+ }
342
+ // eslint-disable-next-line no-await-in-loop -- see above
343
+ const current = await adapter.measure(target);
344
+ if (signal.aborted || !current) {
345
+ return undefined;
346
+ }
347
+ latest.measurement = current;
348
+ const visibility = visibilityOf(current, unobstructed(adapter.getViewport(target)));
349
+ if (visibility === 'full') {
350
+ return { measurement: current, isInSight: true };
351
+ }
352
+ // The first attempt can be cut short (the user scrolled, or the page moved while scrolling);
353
+ // the second one is instant. Partly visible targets are shown, invisible ones are not.
354
+ if (behavior === 'instant') {
355
+ return { measurement: current, isInSight: visibility === 'partial' };
356
+ }
357
+ }
358
+ return undefined;
359
+ }
360
+ function beginTransition() {
361
+ transition?.abort();
362
+ // The step on screen stays observed (and laid out) until the next one is shown; only an
363
+ // automatic scroll of it stops, so it cannot fight the next step's.
364
+ keepInViewScope?.abort();
365
+ keepInViewScope = undefined;
366
+ const controller = new AbortController();
367
+ transition = controller;
368
+ return controller;
369
+ }
370
+ /**
371
+ * Prepares the steps from `requested` on, in the direction of travel, until one can be shown.
372
+ * Steps whose target cannot be found or brought into view are skipped.
373
+ */
374
+ async function prepare(requested, initialDirection, prepareOptions = {}) {
375
+ const { session } = state;
376
+ if (!session || lifecycle === 'suspended') {
377
+ return;
378
+ }
379
+ const controller = beginTransition();
380
+ let index = requested;
381
+ let direction = initialDirection;
382
+ let returnedToOrigin = false;
383
+ while (!controller.signal.aborted) {
384
+ if (index < 0 || index >= session.steps.length) {
385
+ if (direction === -1 && prepareOptions.origin !== undefined && !returnedToOrigin) {
386
+ // Nothing to show before this step: go back to where the user came from.
387
+ returnedToOrigin = true;
388
+ index = prepareOptions.origin;
389
+ direction = 1;
390
+ continue;
391
+ }
392
+ finish('target-missing', { stepIndex: Math.min(Math.max(index - direction, 0), session.steps.length - 1) });
393
+ return;
394
+ }
395
+ // eslint-disable-next-line no-await-in-loop -- steps are tried one after another
396
+ const result = await prepareStep(controller, index, prepareOptions.skipBeforeStepFor !== index);
397
+ if (result === 'done') {
398
+ return;
399
+ }
400
+ index += direction;
401
+ }
402
+ }
403
+ /**
404
+ * Runs a step's `onBeforeStep`. Resolves `true` once it has finished, `false` if it did not within
405
+ * {@link beforeStepTimeoutMs} (its signal is then aborted); rejects with what it throws.
406
+ */
407
+ async function callBeforeStep(step, onBeforeStep, parent) {
408
+ const child = new AbortController();
409
+ const abortChild = () => {
410
+ child.abort();
411
+ };
412
+ parent.addEventListener('abort', abortChild);
413
+ let timer;
414
+ const timedOut = new Promise((resolve) => {
415
+ timer = setTimeout(() => {
416
+ child.abort();
417
+ resolve(false);
418
+ }, beforeStepTimeoutMs);
419
+ });
420
+ try {
421
+ const finished = (async () => {
422
+ await onBeforeStep(step.target, { signal: child.signal });
423
+ return true;
424
+ })();
425
+ return await Promise.race([finished, timedOut]);
426
+ }
427
+ finally {
428
+ clearTimeout(timer);
429
+ parent.removeEventListener('abort', abortChild);
430
+ }
431
+ }
432
+ /** Runs one step's lifecycle. Resolves `'skip'` when its target cannot be found or shown. */
433
+ async function prepareStep(controller, index, runBeforeStep) {
434
+ const { signal } = controller;
435
+ const step = state.session?.steps[index];
436
+ if (!step) {
437
+ return 'done';
438
+ }
439
+ setState({ phase: 'preparing', pendingStepIndex: index });
440
+ try {
441
+ if (runBeforeStep && step.onBeforeStep) {
442
+ const isSettled = await callBeforeStep(step, step.onBeforeStep, signal);
443
+ if (signal.aborted) {
444
+ return 'done';
445
+ }
446
+ if (!isSettled) {
447
+ finish('error', {
448
+ stepIndex: index,
449
+ error: new Error(`onBeforeStep of step ${index + 1} did not finish within ${beforeStepTimeoutMs / 1000} seconds.`),
450
+ });
451
+ return 'done';
452
+ }
453
+ }
454
+ const { adapter } = options;
455
+ const { target } = step;
456
+ let measurement;
457
+ if (target !== undefined) {
458
+ measurement = await withTimeout(async (childSignal) => adapter.waitForTarget(target, childSignal), signal, targetTimeoutMs);
459
+ if (signal.aborted) {
460
+ return 'done';
461
+ }
462
+ if (!measurement) {
463
+ devWarn(`Skipping step ${index + 1}: its target was not found within ${targetTimeoutMs / 1000} seconds.`);
464
+ return 'skip';
465
+ }
466
+ }
467
+ const { id, size } = await measureTooltip(index, step, signal);
468
+ if (signal.aborted) {
469
+ return 'done';
470
+ }
471
+ if (target !== undefined && measurement) {
472
+ measurement = await reveal({ step, target, measurement, size, signal });
473
+ if (signal.aborted) {
474
+ return 'done';
475
+ }
476
+ if (!measurement) {
477
+ devWarn(`Skipping step ${index + 1}: its target could not be scrolled into view.`);
478
+ return 'skip';
479
+ }
480
+ }
481
+ show({ controller, id, index, step, measurement, size });
482
+ return 'done';
483
+ }
484
+ catch (error) {
485
+ if (!signal.aborted) {
486
+ finish('error', { stepIndex: index, error });
487
+ }
488
+ return 'done';
489
+ }
490
+ }
491
+ function show({ controller, id, index, step, measurement, size }) {
492
+ if (transition === controller) {
493
+ transition = undefined;
494
+ }
495
+ shownDetails = { measurement, size };
496
+ lastTooltipSize = size;
497
+ const highlight = measurement ? highlightOf(measurement) : undefined;
498
+ setState({
499
+ phase: 'showing',
500
+ currentStep: index,
501
+ pendingStepIndex: undefined,
502
+ measure: undefined,
503
+ shown: {
504
+ transitionId: id,
505
+ stepIndex: index,
506
+ step,
507
+ highlight,
508
+ layout: computeLayout(step, highlight, size, undefined),
509
+ },
510
+ });
511
+ observeShown();
512
+ }
513
+ function observeShown() {
514
+ stepScope?.abort();
515
+ const { shown } = state;
516
+ if (!shown) {
517
+ return undefined;
518
+ }
519
+ const scope = new AbortController();
520
+ stepScope = scope;
521
+ const { target } = shown.step;
522
+ options.adapter.observe(target, () => {
523
+ void remeasure(scope.signal);
524
+ }, scope.signal);
525
+ if (target !== undefined) {
526
+ followSilentMoves(target, shownDetails?.measurement, scope.signal).catch(reportError);
527
+ }
528
+ return scope.signal;
529
+ }
530
+ /** Follows the shown target while it moves without any event (see `silentMoveQuietMs`). */
531
+ async function followSilentMoves(target, from, signal) {
532
+ const deadline = Date.now() + silentMoveMaxMs;
533
+ let last = from;
534
+ while (!signal.aborted && Date.now() < deadline) {
535
+ // eslint-disable-next-line no-await-in-loop -- one move at a time
536
+ const move = await waitForMove(target, last, signal, Math.min(silentMoveQuietMs, deadline - Date.now()));
537
+ if (!move || signal.aborted) {
538
+ return;
539
+ }
540
+ last = move.to;
541
+ void remeasure(signal);
542
+ }
543
+ }
544
+ /** `canKeepInView: false` only follows the target (after keepInView's own scroll: no second attempt). */
545
+ async function remeasure(signal, { canKeepInView = true } = {}) {
546
+ const sequence = ++measureSequence;
547
+ const { shown } = state;
548
+ // While the next step is prepared, the step on screen still follows layout changes (rotation,
549
+ // resizing), but is neither scrolled back into view nor looked for again: it is about to go.
550
+ const isShowing = state.phase === 'showing';
551
+ if (!shown || !shownDetails || (!isShowing && state.phase !== 'preparing')) {
552
+ return;
553
+ }
554
+ const { target } = shown.step;
555
+ if (target === undefined) {
556
+ updateShown(undefined, shownDetails.size);
557
+ return;
558
+ }
559
+ const measurement = await options.adapter.measure(target);
560
+ // The tooltip may have reported a new size meanwhile: use the latest.
561
+ const size = shownDetails?.size;
562
+ if (signal.aborted || sequence !== measureSequence || !size || state.shown !== shown) {
563
+ return;
564
+ }
565
+ if (!measurement) {
566
+ if (state.phase === 'showing') {
567
+ // The target disappeared (re-mounted, for example): find it again without re-running onBeforeStep.
568
+ void prepare(shown.stepIndex, 1, { skipBeforeStepFor: shown.stepIndex });
569
+ }
570
+ return;
571
+ }
572
+ updateShown(measurement, size);
573
+ if (state.phase === 'showing' && canKeepInView) {
574
+ void keepInView({ step: shown.step, target, measurement, size, signal });
575
+ }
576
+ }
577
+ /**
578
+ * When the app moves the shown target out of view (content inserted above it, a resize), scrolls
579
+ * it back. The step stays on screen and follows the target while it scrolls; nothing is re-run.
580
+ * Users cannot scroll the page themselves during a tour, so this never fights the user.
581
+ */
582
+ async function keepInView({ step, target, measurement, size, signal }) {
583
+ if (isKeepingInView) {
584
+ isKeepInViewStale = true;
585
+ return;
586
+ }
587
+ const { adapter } = options;
588
+ const screen = unobstructed(adapter.getViewport(target));
589
+ // Only targets that can be seen whole are brought back (larger ones would be scrolled to and fro).
590
+ // Measured against the screen, not against what is left on screen of the lists around the
591
+ // target: after a rotation, a list can lie entirely off screen, and that is what scrolling fixes.
592
+ const fits = measurement.rect.width <= screen.width && measurement.rect.height <= screen.height;
593
+ if (!fits || visibilityOf(measurement, screen) === 'full') {
594
+ return;
595
+ }
596
+ isKeepingInView = true;
597
+ isKeepInViewStale = false;
598
+ let hasScrolled = false;
599
+ const scope = new AbortController();
600
+ keepInViewScope = scope;
601
+ const stop = () => {
602
+ scope.abort();
603
+ };
604
+ signal.addEventListener('abort', stop);
605
+ try {
606
+ const margins = buildRevealMargins({
607
+ ...tourLayout,
608
+ target: highlightOf(measurement),
609
+ tooltip: size,
610
+ area: screen,
611
+ sides: sideOrder[step.placement ?? 'bottom'],
612
+ });
613
+ await withTimeout(async (childSignal) => {
614
+ await adapter.reveal(target, {
615
+ margins,
616
+ behavior: 'smooth',
617
+ signal: childSignal,
618
+ onScrollStart() {
619
+ hasScrolled = true;
620
+ },
621
+ });
622
+ return true;
623
+ }, scope.signal, revealTimeoutMs);
624
+ }
625
+ finally {
626
+ signal.removeEventListener('abort', stop);
627
+ isKeepingInView = false;
628
+ if (keepInViewScope === scope) {
629
+ keepInViewScope = undefined;
630
+ }
631
+ if (!scope.signal.aborted) {
632
+ if (isKeepInViewStale) {
633
+ // Pushed out again while scrolling (more content loaded): check once more.
634
+ isKeepInViewStale = false;
635
+ void remeasure(signal);
636
+ }
637
+ else if (hasScrolled) {
638
+ // Where the target ended up: platforms whose scrolling reports no layout events (React
639
+ // Native) would otherwise keep the highlight where the target was.
640
+ void remeasure(signal, { canKeepInView: false });
641
+ }
642
+ }
643
+ }
644
+ }
645
+ function finish(reason, extra = {}) {
646
+ const { session } = state;
647
+ if (!session) {
648
+ return;
649
+ }
650
+ const stepIndex = extra.stepIndex ?? state.shown?.stepIndex ?? state.pendingStepIndex ?? state.currentStep;
651
+ const step = session.steps[stepIndex] ?? session.steps[0];
652
+ if (!step) {
653
+ return;
654
+ }
655
+ const info = {
656
+ reason,
657
+ stepIndex,
658
+ step,
659
+ totalSteps: session.steps.length,
660
+ ...('error' in extra ? { error: extra.error } : {}),
661
+ };
662
+ transition?.abort();
663
+ transition = undefined;
664
+ stepScope?.abort();
665
+ stepScope = undefined;
666
+ shownDetails = undefined;
667
+ for (const resolve of sizeWaiters.values()) {
668
+ resolve(lastTooltipSize ?? fallbackTooltipSize);
669
+ }
670
+ sizeWaiters.clear();
671
+ reportedSizes.clear();
672
+ // Reset before running user code, so a throwing callback can never leave the tour half-open.
673
+ setState({
674
+ phase: 'idle',
675
+ session: undefined,
676
+ currentStep: 0,
677
+ pendingStepIndex: undefined,
678
+ shown: undefined,
679
+ measure: undefined,
680
+ });
681
+ safeInvoke(() => session.onTourEnd?.(session.tourId, info));
682
+ safeInvoke(() => options.onTourEnd?.(session.tourId, info));
683
+ }
684
+ // -------------------------------------------------------------------------------------------
685
+ // Actions
686
+ // -------------------------------------------------------------------------------------------
687
+ const actions = {
688
+ startTour(steps, tourId, startOptions) {
689
+ if (lifecycle === 'suspended') {
690
+ return;
691
+ }
692
+ if (steps.length === 0) {
693
+ devWarn('startTour was called without steps; nothing to show.');
694
+ return;
695
+ }
696
+ const current = state.session;
697
+ if (current && (tourId === undefined ? steps === current.source : tourId === current.tourId)) {
698
+ return;
699
+ }
700
+ // End the running tour, and any tour its onTourEnd starts, before starting this one.
701
+ for (let ended = 0; state.session && ended < maxChainedEnds; ended++) {
702
+ finish('closed');
703
+ }
704
+ if (state.session) {
705
+ devWarn('onTourEnd keeps starting new tours; the last one is replaced without ending it.');
706
+ }
707
+ const session = {
708
+ key: ++sessionCounter,
709
+ tourId,
710
+ steps: [...steps],
711
+ source: steps,
712
+ onTourEnd: startOptions?.onTourEnd,
713
+ };
714
+ lastTooltipSize = undefined;
715
+ setState({
716
+ session,
717
+ phase: 'preparing',
718
+ currentStep: 0,
719
+ pendingStepIndex: 0,
720
+ shown: undefined,
721
+ measure: undefined,
722
+ });
723
+ void prepare(0, 1);
724
+ },
725
+ endTour() {
726
+ finish('closed');
727
+ },
728
+ nextStep() {
729
+ if (lifecycle === 'suspended') {
730
+ return;
731
+ }
732
+ const { session } = state;
733
+ if (!session || state.phase !== 'showing') {
734
+ return;
735
+ }
736
+ if (state.currentStep >= session.steps.length - 1) {
737
+ finish('completed');
738
+ return;
739
+ }
740
+ void prepare(state.currentStep + 1, 1, { origin: state.currentStep });
741
+ },
742
+ prevStep() {
743
+ if (lifecycle === 'suspended') {
744
+ return;
745
+ }
746
+ if (!state.session || state.phase !== 'showing' || state.currentStep === 0) {
747
+ return;
748
+ }
749
+ void prepare(state.currentStep - 1, -1, { origin: state.currentStep });
750
+ },
751
+ goToStep(index) {
752
+ if (lifecycle === 'suspended') {
753
+ return;
754
+ }
755
+ const { session } = state;
756
+ if (!session || !Number.isInteger(index) || index < 0 || index >= session.steps.length) {
757
+ return;
758
+ }
759
+ if (state.phase === 'showing' ? index === state.currentStep : index === state.pendingStepIndex) {
760
+ return;
761
+ }
762
+ const from = state.shown?.stepIndex ?? state.currentStep;
763
+ void prepare(index, index >= from ? 1 : -1, { origin: state.shown ? from : undefined });
764
+ },
765
+ };
766
+ // -------------------------------------------------------------------------------------------
767
+ // Lifecycle
768
+ // -------------------------------------------------------------------------------------------
769
+ function suspend() {
770
+ lifecycle = 'suspended';
771
+ transition?.abort();
772
+ transition = undefined;
773
+ stepScope?.abort();
774
+ stepScope = undefined;
775
+ }
776
+ function resume() {
777
+ lifecycle = 'live';
778
+ if (state.phase === 'showing') {
779
+ const signal = observeShown();
780
+ if (signal) {
781
+ void remeasure(signal);
782
+ }
783
+ }
784
+ else if (state.phase !== 'idle') {
785
+ void prepare(state.pendingStepIndex ?? state.currentStep, 1);
786
+ }
787
+ }
788
+ return {
789
+ subscribe(listener) {
790
+ listeners.add(listener);
791
+ return () => {
792
+ listeners.delete(listener);
793
+ };
794
+ },
795
+ getPublicState: () => publicState,
796
+ getViewState: () => viewState,
797
+ actions,
798
+ configure(next) {
799
+ options = next;
800
+ },
801
+ mount() {
802
+ if (lifecycle === 'unmounting') {
803
+ lifecycle = 'live';
804
+ }
805
+ else if (lifecycle === 'suspended') {
806
+ resume();
807
+ }
808
+ return () => {
809
+ lifecycle = 'unmounting';
810
+ void Promise.resolve().then(() => {
811
+ if (lifecycle === 'unmounting') {
812
+ suspend();
813
+ }
814
+ });
815
+ };
816
+ },
817
+ reportTooltipSize(id, rawSize) {
818
+ const size = sanitizeSize(rawSize);
819
+ if (state.measure?.id === id) {
820
+ reportedSizes.set(id, size);
821
+ sizeWaiters.get(id)?.(size);
822
+ sizeWaiters.delete(id);
823
+ return;
824
+ }
825
+ if (state.shown?.transitionId === id && shownDetails && !sizesEqual(shownDetails.size, size)) {
826
+ lastTooltipSize = size;
827
+ updateShown(shownDetails.measurement, size);
828
+ }
829
+ },
830
+ dismiss(reason) {
831
+ finish(reason);
832
+ },
833
+ };
834
+ }
835
+ //# sourceMappingURL=tour-engine.js.map