@robertlinde/react-tour-kit 0.8.8 → 1.0.0

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