@robertlinde/react-tour-kit 0.8.9 → 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.
- package/CHANGELOG.md +40 -0
- package/MIGRATION.md +205 -0
- package/README.md +401 -850
- package/dist/react-native-platform/hooks/use-tour-scroll-view.hook.d.ts +14 -0
- package/dist/react-native-platform/hooks/use-tour-scroll-view.hook.d.ts.map +1 -0
- package/dist/react-native-platform/hooks/use-tour-scroll-view.hook.js +21 -0
- package/dist/react-native-platform/hooks/use-tour-scroll-view.hook.js.map +1 -0
- package/dist/react-native-platform/hooks/use-tour-target.hook.d.ts +15 -0
- package/dist/react-native-platform/hooks/use-tour-target.hook.d.ts.map +1 -0
- package/dist/react-native-platform/hooks/use-tour-target.hook.js +28 -0
- package/dist/react-native-platform/hooks/use-tour-target.hook.js.map +1 -0
- package/dist/react-native-platform/host-store.d.ts +24 -0
- package/dist/react-native-platform/host-store.d.ts.map +1 -0
- package/dist/react-native-platform/host-store.js +63 -0
- package/dist/react-native-platform/host-store.js.map +1 -0
- package/dist/react-native-platform/insets.util.d.ts +11 -0
- package/dist/react-native-platform/insets.util.d.ts.map +1 -0
- package/dist/react-native-platform/insets.util.js +31 -0
- package/dist/react-native-platform/insets.util.js.map +1 -0
- package/dist/react-native-platform/layer/native-renderer.d.ts +7 -0
- package/dist/react-native-platform/layer/native-renderer.d.ts.map +1 -0
- package/dist/react-native-platform/layer/native-renderer.js +496 -0
- package/dist/react-native-platform/layer/native-renderer.js.map +1 -0
- package/dist/react-native-platform/layer/tour-host.d.ts +13 -0
- package/dist/react-native-platform/layer/tour-host.d.ts.map +1 -0
- package/dist/react-native-platform/layer/tour-host.js +70 -0
- package/dist/react-native-platform/layer/tour-host.js.map +1 -0
- package/dist/react-native-platform/measure.util.d.ts +20 -0
- package/dist/react-native-platform/measure.util.d.ts.map +1 -0
- package/dist/react-native-platform/measure.util.js +100 -0
- package/dist/react-native-platform/measure.util.js.map +1 -0
- package/dist/react-native-platform/native-context.d.ts +12 -0
- package/dist/react-native-platform/native-context.d.ts.map +1 -0
- package/dist/react-native-platform/native-context.js +4 -0
- package/dist/react-native-platform/native-context.js.map +1 -0
- package/dist/react-native-platform/native-environment.d.ts +14 -0
- package/dist/react-native-platform/native-environment.d.ts.map +1 -0
- package/dist/react-native-platform/native-environment.js +20 -0
- package/dist/react-native-platform/native-environment.js.map +1 -0
- package/dist/react-native-platform/native-platform.d.ts +19 -0
- package/dist/react-native-platform/native-platform.d.ts.map +1 -0
- package/dist/react-native-platform/native-platform.js +265 -0
- package/dist/react-native-platform/native-platform.js.map +1 -0
- package/dist/react-native-platform/reduced-motion.d.ts +4 -0
- package/dist/react-native-platform/reduced-motion.d.ts.map +1 -0
- package/dist/react-native-platform/reduced-motion.js +44 -0
- package/dist/react-native-platform/reduced-motion.js.map +1 -0
- package/dist/react-native-platform/registry.d.ts +18 -0
- package/dist/react-native-platform/registry.d.ts.map +1 -0
- package/dist/react-native-platform/registry.js +51 -0
- package/dist/react-native-platform/registry.js.map +1 -0
- package/dist/react-native-platform/tour-provider.d.ts +16 -3
- package/dist/react-native-platform/tour-provider.d.ts.map +1 -0
- package/dist/react-native-platform/tour-provider.js +92 -211
- package/dist/react-native-platform/tour-provider.js.map +1 -1
- package/dist/react-native.d.ts +14 -10
- package/dist/react-native.d.ts.map +1 -0
- package/dist/react-native.js +7 -7
- package/dist/react-native.js.map +1 -1
- package/dist/react-platform/dom/elements.util.d.ts +35 -0
- package/dist/react-platform/dom/elements.util.d.ts.map +1 -0
- package/dist/react-platform/dom/elements.util.js +155 -0
- package/dist/react-platform/dom/elements.util.js.map +1 -0
- package/dist/react-platform/dom/occluders.util.d.ts +13 -0
- package/dist/react-platform/dom/occluders.util.d.ts.map +1 -0
- package/dist/react-platform/dom/occluders.util.js +75 -0
- package/dist/react-platform/dom/occluders.util.js.map +1 -0
- package/dist/react-platform/dom/scroll-lock.util.d.ts +13 -0
- package/dist/react-platform/dom/scroll-lock.util.d.ts.map +1 -0
- package/dist/react-platform/dom/scroll-lock.util.js +219 -0
- package/dist/react-platform/dom/scroll-lock.util.js.map +1 -0
- package/dist/react-platform/dom/scroll.util.d.ts +44 -0
- package/dist/react-platform/dom/scroll.util.d.ts.map +1 -0
- package/dist/react-platform/dom/scroll.util.js +250 -0
- package/dist/react-platform/dom/scroll.util.js.map +1 -0
- package/dist/react-platform/dom/viewport.util.d.ts +16 -0
- package/dist/react-platform/dom/viewport.util.d.ts.map +1 -0
- package/dist/react-platform/dom/viewport.util.js +65 -0
- package/dist/react-platform/dom/viewport.util.js.map +1 -0
- package/dist/react-platform/dom/web-platform.d.ts +15 -0
- package/dist/react-platform/dom/web-platform.d.ts.map +1 -0
- package/dist/react-platform/dom/web-platform.js +169 -0
- package/dist/react-platform/dom/web-platform.js.map +1 -0
- package/dist/react-platform/layer/styles.d.ts +12 -0
- package/dist/react-platform/layer/styles.d.ts.map +1 -0
- package/dist/react-platform/layer/styles.js +285 -0
- package/dist/react-platform/layer/styles.js.map +1 -0
- package/dist/react-platform/layer/web-renderer.d.ts +4 -0
- package/dist/react-platform/layer/web-renderer.d.ts.map +1 -0
- package/dist/react-platform/layer/web-renderer.js +390 -0
- package/dist/react-platform/layer/web-renderer.js.map +1 -0
- package/dist/react-platform/tour-provider.d.ts +9 -3
- package/dist/react-platform/tour-provider.d.ts.map +1 -0
- package/dist/react-platform/tour-provider.js +28 -218
- package/dist/react-platform/tour-provider.js.map +1 -1
- package/dist/react.d.ts +10 -10
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +5 -7
- package/dist/react.js.map +1 -1
- package/dist/shared/engine/abort.util.d.ts +17 -0
- package/dist/shared/engine/abort.util.d.ts.map +1 -0
- package/dist/shared/engine/abort.util.js +72 -0
- package/dist/shared/engine/abort.util.js.map +1 -0
- package/dist/shared/engine/report.util.d.ts +12 -0
- package/dist/shared/engine/report.util.d.ts.map +1 -0
- package/dist/shared/engine/report.util.js +51 -0
- package/dist/shared/engine/report.util.js.map +1 -0
- package/dist/shared/engine/tour-adapter.type.d.ts +60 -0
- package/dist/shared/engine/tour-adapter.type.d.ts.map +1 -0
- package/dist/shared/engine/tour-adapter.type.js +2 -0
- package/dist/shared/engine/tour-adapter.type.js.map +1 -0
- package/dist/shared/engine/tour-engine.d.ts +92 -0
- package/dist/shared/engine/tour-engine.d.ts.map +1 -0
- package/dist/shared/engine/tour-engine.js +787 -0
- package/dist/shared/engine/tour-engine.js.map +1 -0
- package/dist/shared/geometry/box.util.d.ts +39 -0
- package/dist/shared/geometry/box.util.d.ts.map +1 -0
- package/dist/shared/geometry/box.util.js +146 -0
- package/dist/shared/geometry/box.util.js.map +1 -0
- package/dist/shared/geometry/compute-tooltip-layout.util.d.ts +60 -0
- package/dist/shared/geometry/compute-tooltip-layout.util.d.ts.map +1 -0
- package/dist/shared/geometry/compute-tooltip-layout.util.js +148 -0
- package/dist/shared/geometry/compute-tooltip-layout.util.js.map +1 -0
- package/dist/shared/geometry/geometry.type.d.ts +34 -0
- package/dist/shared/geometry/geometry.type.d.ts.map +1 -0
- package/dist/shared/geometry/geometry.type.js +2 -0
- package/dist/shared/geometry/geometry.type.js.map +1 -0
- package/dist/shared/geometry/reveal.util.d.ts +38 -0
- package/dist/shared/geometry/reveal.util.d.ts.map +1 -0
- package/dist/shared/geometry/reveal.util.js +135 -0
- package/dist/shared/geometry/reveal.util.js.map +1 -0
- package/dist/shared/geometry/target-visibility.util.d.ts +10 -0
- package/dist/shared/geometry/target-visibility.util.d.ts.map +1 -0
- package/dist/shared/geometry/target-visibility.util.js +29 -0
- package/dist/shared/geometry/target-visibility.util.js.map +1 -0
- package/dist/shared/platform/create-tour-adapter.d.ts +9 -0
- package/dist/shared/platform/create-tour-adapter.d.ts.map +1 -0
- package/dist/shared/platform/create-tour-adapter.js +130 -0
- package/dist/shared/platform/create-tour-adapter.js.map +1 -0
- package/dist/shared/platform/platform.type.d.ts +77 -0
- package/dist/shared/platform/platform.type.d.ts.map +1 -0
- package/dist/shared/platform/platform.type.js +2 -0
- package/dist/shared/platform/platform.type.js.map +1 -0
- package/dist/shared/platform/reveal-in-scroll-containers.d.ts +27 -0
- package/dist/shared/platform/reveal-in-scroll-containers.d.ts.map +1 -0
- package/dist/shared/platform/reveal-in-scroll-containers.js +49 -0
- package/dist/shared/platform/reveal-in-scroll-containers.js.map +1 -0
- package/dist/shared/react/tour-context.d.ts +7 -0
- package/dist/shared/react/tour-context.d.ts.map +1 -0
- package/dist/shared/react/tour-context.js +23 -0
- package/dist/shared/react/tour-context.js.map +1 -0
- package/dist/shared/react/tour-provider-core.d.ts +41 -0
- package/dist/shared/react/tour-provider-core.d.ts.map +1 -0
- package/dist/shared/react/tour-provider-core.js +24 -0
- package/dist/shared/react/tour-provider-core.js.map +1 -0
- package/dist/shared/react/tour-root.d.ts +14 -0
- package/dist/shared/react/tour-root.d.ts.map +1 -0
- package/dist/shared/react/tour-root.js +22 -0
- package/dist/shared/react/tour-root.js.map +1 -0
- package/dist/shared/react/use-isomorphic-layout-effect.hook.d.ts +4 -0
- package/dist/shared/react/use-isomorphic-layout-effect.hook.d.ts.map +1 -0
- package/dist/shared/react/use-isomorphic-layout-effect.hook.js +5 -0
- package/dist/shared/react/use-isomorphic-layout-effect.hook.js.map +1 -0
- package/dist/shared/react/use-tour-view.hook.d.ts +5 -0
- package/dist/shared/react/use-tour-view.hook.d.ts.map +1 -0
- package/dist/shared/react/use-tour-view.hook.js +14 -0
- package/dist/shared/react/use-tour-view.hook.js.map +1 -0
- package/dist/shared/react/use-tour.hook.d.ts +7 -0
- package/dist/shared/react/use-tour.hook.d.ts.map +1 -0
- package/dist/shared/react/use-tour.hook.js +10 -0
- package/dist/shared/react/use-tour.hook.js.map +1 -0
- package/dist/shared/renderer/default-tooltip.d.ts +5 -0
- package/dist/shared/renderer/default-tooltip.d.ts.map +1 -0
- package/dist/shared/renderer/default-tooltip.js +13 -0
- package/dist/shared/renderer/default-tooltip.js.map +1 -0
- package/dist/shared/renderer/motion.d.ts +29 -0
- package/dist/shared/renderer/motion.d.ts.map +1 -0
- package/dist/shared/renderer/motion.js +49 -0
- package/dist/shared/renderer/motion.js.map +1 -0
- package/dist/shared/renderer/renderer-context.d.ts +17 -0
- package/dist/shared/renderer/renderer-context.d.ts.map +1 -0
- package/dist/shared/renderer/renderer-context.js +16 -0
- package/dist/shared/renderer/renderer-context.js.map +1 -0
- package/dist/shared/renderer/renderer.type.d.ts +164 -0
- package/dist/shared/renderer/renderer.type.d.ts.map +1 -0
- package/dist/shared/renderer/renderer.type.js +2 -0
- package/dist/shared/renderer/renderer.type.js.map +1 -0
- package/dist/shared/renderer/tooltip-model.d.ts +32 -0
- package/dist/shared/renderer/tooltip-model.d.ts.map +1 -0
- package/dist/shared/renderer/tooltip-model.js +24 -0
- package/dist/shared/renderer/tooltip-model.js.map +1 -0
- package/dist/shared/renderer/tour-overlay.d.ts +24 -0
- package/dist/shared/renderer/tour-overlay.d.ts.map +1 -0
- package/dist/shared/renderer/tour-overlay.js +228 -0
- package/dist/shared/renderer/tour-overlay.js.map +1 -0
- package/dist/shared/types/tour-context.type.d.ts +21 -11
- package/dist/shared/types/tour-context.type.d.ts.map +1 -0
- package/dist/shared/types/tour-end-info.type.d.ts +19 -2
- package/dist/shared/types/tour-end-info.type.d.ts.map +1 -0
- package/dist/shared/types/tour-i18n.type.d.ts +8 -0
- package/dist/shared/types/tour-i18n.type.d.ts.map +1 -0
- package/dist/shared/types/tour-step.type.d.ts +35 -6
- package/dist/shared/types/tour-step.type.d.ts.map +1 -0
- package/dist/shared/types/tour-theme.type.d.ts +22 -2
- package/dist/shared/types/tour-theme.type.d.ts.map +1 -0
- package/dist/shared/types/tour-theme.type.js +1 -16
- package/dist/shared/types/tour-theme.type.js.map +1 -1
- package/dist/shared/types/tour-tooltip-props.type.d.ts +21 -17
- package/dist/shared/types/tour-tooltip-props.type.d.ts.map +1 -0
- package/dist/shared/utils/frame.util.d.ts +6 -0
- package/dist/shared/utils/frame.util.d.ts.map +1 -0
- package/dist/shared/utils/frame.util.js +20 -0
- package/dist/shared/utils/frame.util.js.map +1 -0
- package/dist/shared/utils/get-truncated-dots.util.d.ts +13 -7
- package/dist/shared/utils/get-truncated-dots.util.d.ts.map +1 -0
- package/dist/shared/utils/get-truncated-dots.util.js +30 -30
- package/dist/shared/utils/get-truncated-dots.util.js.map +1 -1
- package/dist/shared/utils/i18n.util.d.ts +7 -0
- package/dist/shared/utils/i18n.util.d.ts.map +1 -0
- package/dist/shared/utils/i18n.util.js +34 -0
- package/dist/shared/utils/i18n.util.js.map +1 -0
- package/dist/shared/utils/theme.util.d.ts +6 -0
- package/dist/shared/utils/theme.util.d.ts.map +1 -0
- package/dist/shared/utils/theme.util.js +31 -0
- package/dist/shared/utils/theme.util.js.map +1 -0
- package/package.json +72 -55
- package/src/react-native-platform/hooks/use-tour-scroll-view.hook.ts +24 -0
- package/src/react-native-platform/hooks/use-tour-target.hook.ts +30 -0
- package/src/react-native-platform/host-store.ts +92 -0
- package/src/react-native-platform/insets.util.ts +39 -0
- package/src/react-native-platform/layer/native-renderer.tsx +776 -0
- package/src/react-native-platform/layer/tour-host.tsx +105 -0
- package/src/react-native-platform/measure.util.ts +140 -0
- package/src/react-native-platform/native-context.ts +15 -0
- package/src/react-native-platform/native-environment.ts +33 -0
- package/src/react-native-platform/native-platform.ts +368 -0
- package/src/react-native-platform/reduced-motion.ts +47 -0
- package/src/react-native-platform/registry.ts +70 -0
- package/src/react-native-platform/tour-provider.tsx +148 -0
- package/src/react-native.ts +15 -0
- package/src/react-platform/dom/elements.util.ts +191 -0
- package/src/react-platform/dom/occluders.util.ts +88 -0
- package/src/react-platform/dom/scroll-lock.util.ts +261 -0
- package/src/react-platform/dom/scroll.util.ts +310 -0
- package/src/react-platform/dom/viewport.util.ts +74 -0
- package/src/react-platform/dom/web-platform.ts +208 -0
- package/src/react-platform/layer/styles.ts +286 -0
- package/src/react-platform/layer/web-renderer.tsx +594 -0
- package/src/react-platform/tour-provider.tsx +44 -0
- package/src/react.ts +13 -0
- package/src/shared/engine/abort.util.ts +84 -0
- package/src/shared/engine/report.util.ts +58 -0
- package/src/shared/engine/tour-adapter.type.ts +62 -0
- package/src/shared/engine/tour-engine.ts +1068 -0
- package/src/shared/geometry/box.util.ts +185 -0
- package/src/shared/geometry/compute-tooltip-layout.util.ts +238 -0
- package/src/shared/geometry/geometry.type.ts +38 -0
- package/src/shared/geometry/reveal.util.ts +190 -0
- package/src/shared/geometry/target-visibility.util.ts +44 -0
- package/src/shared/platform/create-tour-adapter.ts +151 -0
- package/src/shared/platform/platform.type.ts +81 -0
- package/src/shared/platform/reveal-in-scroll-containers.ts +78 -0
- package/src/shared/react/tour-context.ts +28 -0
- package/src/shared/react/tour-provider-core.ts +67 -0
- package/src/shared/react/tour-root.tsx +34 -0
- package/src/shared/react/use-isomorphic-layout-effect.hook.ts +7 -0
- package/src/shared/react/use-tour-view.hook.ts +17 -0
- package/src/shared/react/use-tour.hook.ts +11 -0
- package/src/shared/renderer/default-tooltip.tsx +52 -0
- package/src/shared/renderer/motion.ts +59 -0
- package/src/shared/renderer/renderer-context.ts +33 -0
- package/src/shared/renderer/renderer.type.ts +183 -0
- package/src/shared/renderer/tooltip-model.ts +44 -0
- package/src/shared/renderer/tour-overlay.tsx +453 -0
- package/src/shared/types/tour-context.type.ts +23 -0
- package/src/shared/types/tour-end-info.type.ts +32 -0
- package/src/shared/types/tour-i18n.type.ts +15 -0
- package/src/shared/types/tour-step.type.ts +40 -0
- package/src/shared/types/tour-theme.type.ts +28 -0
- package/src/shared/types/tour-tooltip-props.type.ts +27 -0
- package/src/shared/utils/frame.util.ts +21 -0
- package/src/shared/utils/get-truncated-dots.util.ts +46 -0
- package/src/shared/utils/i18n.util.ts +44 -0
- package/src/shared/utils/theme.util.ts +38 -0
- package/dist/react-native-platform/components/index.d.ts +0 -2
- package/dist/react-native-platform/components/index.js +0 -3
- package/dist/react-native-platform/components/index.js.map +0 -1
- package/dist/react-native-platform/components/tour-overlay.d.ts +0 -3
- package/dist/react-native-platform/components/tour-overlay.js +0 -26
- package/dist/react-native-platform/components/tour-overlay.js.map +0 -1
- package/dist/react-native-platform/components/tour-tooltip.d.ts +0 -3
- package/dist/react-native-platform/components/tour-tooltip.js +0 -167
- package/dist/react-native-platform/components/tour-tooltip.js.map +0 -1
- package/dist/react-native-platform/hooks/index.d.ts +0 -1
- package/dist/react-native-platform/hooks/index.js +0 -2
- package/dist/react-native-platform/hooks/index.js.map +0 -1
- package/dist/react-native-platform/hooks/use-tour-target.d.ts +0 -2
- package/dist/react-native-platform/hooks/use-tour-target.js +0 -13
- package/dist/react-native-platform/hooks/use-tour-target.js.map +0 -1
- package/dist/react-native-platform/index.d.ts +0 -7
- package/dist/react-native-platform/index.js +0 -7
- package/dist/react-native-platform/index.js.map +0 -1
- package/dist/react-native-platform/platform-adapter.d.ts +0 -2
- package/dist/react-native-platform/platform-adapter.js +0 -84
- package/dist/react-native-platform/platform-adapter.js.map +0 -1
- package/dist/react-native-platform/target-registry.d.ts +0 -10
- package/dist/react-native-platform/target-registry.js +0 -17
- package/dist/react-native-platform/target-registry.js.map +0 -1
- package/dist/react-native-platform/types/tour-provider.props.type.d.ts +0 -12
- package/dist/react-native-platform/types/tour-provider.props.type.js +0 -2
- package/dist/react-native-platform/types/tour-provider.props.type.js.map +0 -1
- package/dist/react-platform/components/index.d.ts +0 -2
- package/dist/react-platform/components/index.js +0 -3
- package/dist/react-platform/components/index.js.map +0 -1
- package/dist/react-platform/components/tour-overlay.d.ts +0 -3
- package/dist/react-platform/components/tour-overlay.js +0 -29
- package/dist/react-platform/components/tour-overlay.js.map +0 -1
- package/dist/react-platform/components/tour-tooltip.d.ts +0 -2
- package/dist/react-platform/components/tour-tooltip.js +0 -155
- package/dist/react-platform/components/tour-tooltip.js.map +0 -1
- package/dist/react-platform/index.d.ts +0 -6
- package/dist/react-platform/index.js +0 -6
- package/dist/react-platform/index.js.map +0 -1
- package/dist/react-platform/platform-adapter.d.ts +0 -2
- package/dist/react-platform/platform-adapter.js +0 -101
- package/dist/react-platform/platform-adapter.js.map +0 -1
- package/dist/react-platform/types/tour-provider.props.type.d.ts +0 -12
- package/dist/react-platform/types/tour-provider.props.type.js +0 -2
- package/dist/react-platform/types/tour-provider.props.type.js.map +0 -1
- package/dist/react-platform/utils/find-visible-element.util.d.ts +0 -1
- package/dist/react-platform/utils/find-visible-element.util.js +0 -11
- package/dist/react-platform/utils/find-visible-element.util.js.map +0 -1
- package/dist/react-platform/utils/index.d.ts +0 -1
- package/dist/react-platform/utils/index.js +0 -2
- package/dist/react-platform/utils/index.js.map +0 -1
- package/dist/shared/context/index.d.ts +0 -1
- package/dist/shared/context/index.js +0 -2
- package/dist/shared/context/index.js.map +0 -1
- package/dist/shared/context/tour-context.d.ts +0 -3
- package/dist/shared/context/tour-context.js +0 -14
- package/dist/shared/context/tour-context.js.map +0 -1
- package/dist/shared/hooks/index.d.ts +0 -1
- package/dist/shared/hooks/index.js +0 -2
- package/dist/shared/hooks/index.js.map +0 -1
- package/dist/shared/hooks/use-tour.hook.d.ts +0 -2
- package/dist/shared/hooks/use-tour.hook.js +0 -7
- package/dist/shared/hooks/use-tour.hook.js.map +0 -1
- package/dist/shared/index.d.ts +0 -7
- package/dist/shared/index.js +0 -6
- package/dist/shared/index.js.map +0 -1
- package/dist/shared/types/index.d.ts +0 -10
- package/dist/shared/types/index.js +0 -2
- package/dist/shared/types/index.js.map +0 -1
- package/dist/shared/types/platform-adapter.type.d.ts +0 -24
- package/dist/shared/types/platform-adapter.type.js +0 -2
- package/dist/shared/types/platform-adapter.type.js.map +0 -1
- package/dist/shared/types/rect.type.d.ts +0 -11
- package/dist/shared/types/rect.type.js +0 -2
- package/dist/shared/types/rect.type.js.map +0 -1
- package/dist/shared/types/tour-overlay-props.type.d.ts +0 -8
- package/dist/shared/types/tour-overlay-props.type.js +0 -2
- package/dist/shared/types/tour-overlay-props.type.js.map +0 -1
- package/dist/shared/utils/calculate-tooltip-position.util.d.ts +0 -14
- package/dist/shared/utils/calculate-tooltip-position.util.js +0 -49
- package/dist/shared/utils/calculate-tooltip-position.util.js.map +0 -1
- package/dist/shared/utils/index.d.ts +0 -3
- package/dist/shared/utils/index.js +0 -3
- package/dist/shared/utils/index.js.map +0 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {AccessibilityInfo} from 'react-native';
|
|
2
|
+
import type {ExternalValue} from '../shared/platform/platform.type.js';
|
|
3
|
+
|
|
4
|
+
/** The system's "reduce motion" setting, live. Listens to the system only while someone is subscribed. */
|
|
5
|
+
export function createReducedMotionSetting(): ExternalValue<boolean> {
|
|
6
|
+
let value = false;
|
|
7
|
+
const listeners = new Set<() => void>();
|
|
8
|
+
let stop: (() => void) | undefined;
|
|
9
|
+
const set = (next: boolean): void => {
|
|
10
|
+
if (next !== value) {
|
|
11
|
+
value = next;
|
|
12
|
+
// Snapshot: listeners may unsubscribe while being notified.
|
|
13
|
+
const snapshot = [...listeners];
|
|
14
|
+
for (const listener of snapshot) {
|
|
15
|
+
listener();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
get: () => value,
|
|
22
|
+
subscribe(listener) {
|
|
23
|
+
listeners.add(listener);
|
|
24
|
+
if (!stop) {
|
|
25
|
+
let isListening = true;
|
|
26
|
+
void AccessibilityInfo.isReduceMotionEnabled().then((enabled) => {
|
|
27
|
+
if (isListening) {
|
|
28
|
+
set(enabled);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
const subscription = AccessibilityInfo.addEventListener('reduceMotionChanged', set);
|
|
32
|
+
stop = (): void => {
|
|
33
|
+
isListening = false;
|
|
34
|
+
subscription.remove();
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return () => {
|
|
39
|
+
listeners.delete(listener);
|
|
40
|
+
if (listeners.size === 0) {
|
|
41
|
+
stop?.();
|
|
42
|
+
stop = undefined;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type {RefObject} from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Targets and scroll views registered by `useTourTarget` / `useTourScrollView`, one registry per
|
|
5
|
+
* `TourProvider`. A target id can be registered by several components at once (two screens of a
|
|
6
|
+
* stack during a transition, tabs that stay mounted); unregistering only removes the caller's own
|
|
7
|
+
* registration, and the newest registration comes first.
|
|
8
|
+
*/
|
|
9
|
+
export type TourRegistry = {
|
|
10
|
+
readonly registerTarget: (id: string, ref: RefObject<unknown>) => () => void;
|
|
11
|
+
/** Refs registered for `id`, newest first. */
|
|
12
|
+
readonly targets: (id: string) => ReadonlyArray<RefObject<unknown>>;
|
|
13
|
+
readonly registerScrollView: (ref: RefObject<unknown>) => () => void;
|
|
14
|
+
readonly scrollViews: () => ReadonlyArray<RefObject<unknown>>;
|
|
15
|
+
/** Called after any registration change. */
|
|
16
|
+
readonly subscribe: (listener: () => void) => () => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function createRegistry(): TourRegistry {
|
|
20
|
+
const targets = new Map<string, Array<RefObject<unknown>>>();
|
|
21
|
+
const scrollViews = new Set<RefObject<unknown>>();
|
|
22
|
+
const listeners = new Set<() => void>();
|
|
23
|
+
const notify = (): void => {
|
|
24
|
+
// Snapshot: listeners may unsubscribe while being notified.
|
|
25
|
+
const snapshot = [...listeners];
|
|
26
|
+
for (const listener of snapshot) {
|
|
27
|
+
listener();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
registerTarget(id, ref) {
|
|
33
|
+
const entries = targets.get(id) ?? [];
|
|
34
|
+
entries.push(ref);
|
|
35
|
+
targets.set(id, entries);
|
|
36
|
+
notify();
|
|
37
|
+
return () => {
|
|
38
|
+
const current = targets.get(id);
|
|
39
|
+
const index = current?.lastIndexOf(ref) ?? -1;
|
|
40
|
+
if (current && index !== -1) {
|
|
41
|
+
current.splice(index, 1);
|
|
42
|
+
if (current.length === 0) {
|
|
43
|
+
targets.delete(id);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
notify();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
// Newest first. (Not `toReversed`: ES2023 is not available on every supported React Native version.)
|
|
51
|
+
// eslint-disable-next-line unicorn/no-array-reverse
|
|
52
|
+
targets: (id) => [...(targets.get(id) ?? [])].reverse(),
|
|
53
|
+
registerScrollView(ref) {
|
|
54
|
+
scrollViews.add(ref);
|
|
55
|
+
notify();
|
|
56
|
+
return () => {
|
|
57
|
+
if (scrollViews.delete(ref)) {
|
|
58
|
+
notify();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
scrollViews: () => [...scrollViews],
|
|
63
|
+
subscribe(listener) {
|
|
64
|
+
listeners.add(listener);
|
|
65
|
+
return () => {
|
|
66
|
+
listeners.delete(listener);
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {type ReactNode, useEffect, useMemo, useState, useSyncExternalStore} from 'react';
|
|
2
|
+
import {Keyboard, PixelRatio, Platform, StyleSheet, View} from 'react-native';
|
|
3
|
+
import {useIsomorphicLayoutEffect} from '../shared/react/use-isomorphic-layout-effect.hook.js';
|
|
4
|
+
import {TourRoot} from '../shared/react/tour-root.js';
|
|
5
|
+
import {type TourProviderBaseProps, useTourCore} from '../shared/react/tour-provider-core.js';
|
|
6
|
+
import {createHostStore} from './host-store.js';
|
|
7
|
+
import type {TourInsets} from './insets.util.js';
|
|
8
|
+
import {nativeRenderer} from './layer/native-renderer.js';
|
|
9
|
+
import {HostView} from './layer/tour-host.js';
|
|
10
|
+
import {NativeTourContext, type NativeTourContextValue} from './native-context.js';
|
|
11
|
+
import {createNativeEnvironment} from './native-environment.js';
|
|
12
|
+
import {createNativePlatform} from './native-platform.js';
|
|
13
|
+
import {createReducedMotionSetting} from './reduced-motion.js';
|
|
14
|
+
import {createRegistry} from './registry.js';
|
|
15
|
+
|
|
16
|
+
export type TourProviderProps = TourProviderBaseProps & {
|
|
17
|
+
/**
|
|
18
|
+
* Safe-area insets, for example `useSafeAreaInsets()` from react-native-safe-area-context.
|
|
19
|
+
* Without them, conservative defaults keep the tooltip clear of notches and system bars.
|
|
20
|
+
*/
|
|
21
|
+
readonly insets?: TourInsets;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Provides `useTour()` and renders the tour above your app. Place it at the root, around your
|
|
26
|
+
* navigation container and outside any `SafeAreaView`; pass safe-area `insets` instead.
|
|
27
|
+
*/
|
|
28
|
+
export function TourProvider({insets, ...props}: TourProviderProps): ReactNode {
|
|
29
|
+
const [registry] = useState(createRegistry);
|
|
30
|
+
const [hosts] = useState(createHostStore);
|
|
31
|
+
const [environment] = useState(createNativeEnvironment);
|
|
32
|
+
const [platform] = useState(() =>
|
|
33
|
+
createNativePlatform({
|
|
34
|
+
registry,
|
|
35
|
+
getHost: hosts.activeHost,
|
|
36
|
+
getInsets: () => environment.getInsets(hosts.activeHost()?.size ?? {width: 0, height: 0}),
|
|
37
|
+
subscribeLayout: hosts.subscribeLayout,
|
|
38
|
+
reducedMotion: createReducedMotionSetting(),
|
|
39
|
+
getPixelRatio: () => PixelRatio.get(),
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
const {engine, overlay} = useTourCore(props, platform, nativeRenderer);
|
|
43
|
+
const {isActive} = useSyncExternalStore(engine.subscribe, engine.getPublicState, engine.getPublicState);
|
|
44
|
+
const isRootActive = useSyncExternalStore(hosts.subscribe, hosts.isRootActive, hosts.isRootActive);
|
|
45
|
+
|
|
46
|
+
// New insets (rotation, split screen) move the safe area.
|
|
47
|
+
useIsomorphicLayoutEffect(() => {
|
|
48
|
+
environment.setInsets(insets);
|
|
49
|
+
hosts.notifyLayout();
|
|
50
|
+
}, [environment, hosts, insets?.top, insets?.right, insets?.bottom, insets?.left]);
|
|
51
|
+
|
|
52
|
+
// The keyboard shrinks the area the tooltip may use.
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const show = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
|
|
55
|
+
const hide = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
|
|
56
|
+
let keyboard: {readonly top: number; readonly height: number} | undefined;
|
|
57
|
+
const apply = (overlap: number): void => {
|
|
58
|
+
if (environment.setKeyboardOverlap(overlap)) {
|
|
59
|
+
hosts.notifyLayout();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const update = (): void => {
|
|
64
|
+
const current = keyboard;
|
|
65
|
+
if (!current) {
|
|
66
|
+
apply(0);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Android apps that resize with the keyboard (`adjustResize` without edge-to-edge) have
|
|
71
|
+
// already made room for it: only the part still covering the layer counts.
|
|
72
|
+
const host = hosts.activeHost()?.instance;
|
|
73
|
+
if (Platform.OS !== 'android' || !canMeasureInWindow(host)) {
|
|
74
|
+
apply(current.height);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
host.measureInWindow((_x, y, _width, height) => {
|
|
79
|
+
if (keyboard === current) {
|
|
80
|
+
apply(y + height - current.top);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const subscriptions = [
|
|
86
|
+
Keyboard.addListener(show, (event) => {
|
|
87
|
+
keyboard = {top: event.endCoordinates.screenY, height: event.endCoordinates.height};
|
|
88
|
+
update();
|
|
89
|
+
}),
|
|
90
|
+
Keyboard.addListener(hide, () => {
|
|
91
|
+
keyboard = undefined;
|
|
92
|
+
update();
|
|
93
|
+
}),
|
|
94
|
+
];
|
|
95
|
+
// The layer resizing for the keyboard changes how much of it the keyboard covers.
|
|
96
|
+
const unsubscribeLayout = hosts.subscribeLayout(() => {
|
|
97
|
+
if (keyboard) {
|
|
98
|
+
update();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return (): void => {
|
|
102
|
+
unsubscribeLayout();
|
|
103
|
+
for (const subscription of subscriptions) {
|
|
104
|
+
subscription.remove();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}, [environment, hosts]);
|
|
108
|
+
|
|
109
|
+
const context = useMemo<NativeTourContextValue>(() => ({registry, hosts, overlay}), [registry, hosts, overlay]);
|
|
110
|
+
const hidesApp = isActive && isRootActive;
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<TourRoot engine={engine}>
|
|
114
|
+
<NativeTourContext.Provider value={context}>
|
|
115
|
+
<View collapsable={false} style={styles.root}>
|
|
116
|
+
{/* Hidden from screen readers while the tour (a modal) is open over it. Not while a
|
|
117
|
+
TourHost draws the tour: that host can be inside this view (modal screens on Android). */}
|
|
118
|
+
<View
|
|
119
|
+
collapsable={false}
|
|
120
|
+
style={styles.root}
|
|
121
|
+
importantForAccessibility={hidesApp ? 'no-hide-descendants' : 'auto'}
|
|
122
|
+
accessibilityElementsHidden={hidesApp}
|
|
123
|
+
>
|
|
124
|
+
{props.children}
|
|
125
|
+
</View>
|
|
126
|
+
<HostView isRoot />
|
|
127
|
+
</View>
|
|
128
|
+
</NativeTourContext.Provider>
|
|
129
|
+
</TourRoot>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
type WindowMeasurable = {
|
|
134
|
+
readonly measureInWindow: (callback: (x: number, y: number, width: number, height: number) => void) => void;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
function canMeasureInWindow(value: unknown): value is WindowMeasurable {
|
|
138
|
+
return (
|
|
139
|
+
typeof value === 'object' &&
|
|
140
|
+
value !== null &&
|
|
141
|
+
'measureInWindow' in value &&
|
|
142
|
+
typeof value.measureInWindow === 'function'
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const styles = StyleSheet.create({
|
|
147
|
+
root: {flex: 1},
|
|
148
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// React Native entry point: import from '@robertlinde/react-tour-kit/react-native'.
|
|
2
|
+
|
|
3
|
+
export {TourProvider, type TourProviderProps} from './react-native-platform/tour-provider.js';
|
|
4
|
+
export {TourHost} from './react-native-platform/layer/tour-host.js';
|
|
5
|
+
export {useTourTarget} from './react-native-platform/hooks/use-tour-target.hook.js';
|
|
6
|
+
export {useTourScrollView} from './react-native-platform/hooks/use-tour-scroll-view.hook.js';
|
|
7
|
+
export {useTour} from './shared/react/use-tour.hook.js';
|
|
8
|
+
export {getTruncatedDots, type DotItem} from './shared/utils/get-truncated-dots.util.js';
|
|
9
|
+
export type {TourInsets} from './react-native-platform/insets.util.js';
|
|
10
|
+
export type {TourContextType} from './shared/types/tour-context.type.js';
|
|
11
|
+
export type {StartTourOptions, TourEndCallback, TourEndInfo, TourEndReason} from './shared/types/tour-end-info.type.js';
|
|
12
|
+
export type {TourI18n} from './shared/types/tour-i18n.type.js';
|
|
13
|
+
export type {TourPlacement, TourStep, TourStepContext, TourTarget} from './shared/types/tour-step.type.js';
|
|
14
|
+
export type {TourTheme} from './shared/types/tour-theme.type.js';
|
|
15
|
+
export type {TourTooltipProps} from './shared/types/tour-tooltip-props.type.js';
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type {Box} from '../../shared/geometry/geometry.type.js';
|
|
2
|
+
import type {TourTarget} from '../../shared/types/tour-step.type.js';
|
|
3
|
+
|
|
4
|
+
export function toBox(rect: DOMRectReadOnly): Box {
|
|
5
|
+
return {x: rect.left, y: rect.top, width: rect.width, height: rect.height};
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* False in documents without a layout engine (jsdom, happy-dom in test suites): every size there is
|
|
10
|
+
* zero. A real page always has a size, also a zero-sized frame (its window is zero-sized too).
|
|
11
|
+
*/
|
|
12
|
+
export function hasLayout(): boolean {
|
|
13
|
+
const root = document.documentElement;
|
|
14
|
+
return root.clientWidth > 0 || root.clientHeight > 0 || globalThis.innerWidth === 0 || globalThis.innerHeight === 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type VisibilityCheck = (options?: {visibilityProperty?: boolean; opacityProperty?: boolean}) => boolean;
|
|
18
|
+
|
|
19
|
+
/** `checkVisibility()` where the browser has it. */
|
|
20
|
+
export function checkVisibility(
|
|
21
|
+
element: Element,
|
|
22
|
+
options: {visibilityProperty?: boolean; opacityProperty?: boolean},
|
|
23
|
+
): boolean | undefined {
|
|
24
|
+
const check = (element as Element & {checkVisibility?: VisibilityCheck}).checkVisibility;
|
|
25
|
+
return typeof check === 'function' ? check.call(element, options) : undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** True when the element takes up space and is not hidden (`display: none`, `visibility: hidden`, closed `<details>`). */
|
|
29
|
+
export function isRendered(element: Element): boolean {
|
|
30
|
+
if (!hasLayout()) {
|
|
31
|
+
// Nothing has a size: count every element that is not explicitly hidden.
|
|
32
|
+
return element.isConnected && !element.closest('[hidden]') && getComputedStyle(element).display !== 'none';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const rect = element.getBoundingClientRect();
|
|
36
|
+
if (rect.width <= 0 && rect.height <= 0) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return checkVisibility(element, {visibilityProperty: true}) ?? getComputedStyle(element).visibility !== 'hidden';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolves a target to an element. For selectors, the first rendered match wins, which handles
|
|
45
|
+
* layouts that duplicate markup (for example a mobile and a desktop navigation).
|
|
46
|
+
*/
|
|
47
|
+
export function resolveTarget(target: TourTarget): Element | undefined {
|
|
48
|
+
if (typeof target === 'string') {
|
|
49
|
+
let matches: NodeListOf<Element>;
|
|
50
|
+
try {
|
|
51
|
+
matches = document.querySelectorAll(target);
|
|
52
|
+
} catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const element of matches) {
|
|
57
|
+
if (isRendered(element)) {
|
|
58
|
+
return element;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return matches[0] ?? undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const {current} = target;
|
|
66
|
+
return current instanceof Element ? current : undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The parent element, crossing shadow-root boundaries. */
|
|
70
|
+
export function parentOf(element: Element): Element | undefined {
|
|
71
|
+
if (element.parentElement) {
|
|
72
|
+
return element.parentElement;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const root = element.getRootNode();
|
|
76
|
+
return root instanceof ShadowRoot ? root.host : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** `ancestor.contains(element)`, also for elements inside shadow roots of the ancestor's descendants. */
|
|
80
|
+
export function containsComposed(ancestor: Element, element: Element): boolean {
|
|
81
|
+
for (let current: Element | undefined = element; current; current = parentOf(current)) {
|
|
82
|
+
if (current === ancestor) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The page's direction. Browsers take it from `<body>` (CSS calls it the principal writing mode),
|
|
92
|
+
* so `<body dir="rtl">` turns the page right-to-left even when `<html>` is left-to-right.
|
|
93
|
+
*/
|
|
94
|
+
export function pageDirection(): 'ltr' | 'rtl' {
|
|
95
|
+
const element = document.body ?? document.documentElement;
|
|
96
|
+
return getComputedStyle(element).direction === 'rtl' ? 'rtl' : 'ltr';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const textInputTypes = new Set([
|
|
100
|
+
'text',
|
|
101
|
+
'search',
|
|
102
|
+
'email',
|
|
103
|
+
'url',
|
|
104
|
+
'tel',
|
|
105
|
+
'password',
|
|
106
|
+
'number',
|
|
107
|
+
'date',
|
|
108
|
+
'datetime-local',
|
|
109
|
+
'month',
|
|
110
|
+
'week',
|
|
111
|
+
'time',
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
/** Inputs that a space press activates or toggles. */
|
|
115
|
+
const pressableInputTypes = new Set(['button', 'submit', 'reset', 'image', 'checkbox', 'file', 'color']);
|
|
116
|
+
|
|
117
|
+
const arrowKeys = new Set(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']);
|
|
118
|
+
const rangeKeys = new Set([...arrowKeys, 'PageUp', 'PageDown', 'Home', 'End']);
|
|
119
|
+
|
|
120
|
+
/** Widgets whose arrow, page, Home and End keys move a selection or change a value. */
|
|
121
|
+
const keyedWidgetSelector = [
|
|
122
|
+
'slider',
|
|
123
|
+
'spinbutton',
|
|
124
|
+
'radio',
|
|
125
|
+
'radiogroup',
|
|
126
|
+
'tab',
|
|
127
|
+
'tablist',
|
|
128
|
+
'listbox',
|
|
129
|
+
'option',
|
|
130
|
+
'menu',
|
|
131
|
+
'menubar',
|
|
132
|
+
'menuitem',
|
|
133
|
+
'menuitemcheckbox',
|
|
134
|
+
'menuitemradio',
|
|
135
|
+
'tree',
|
|
136
|
+
'treeitem',
|
|
137
|
+
'treegrid',
|
|
138
|
+
'grid',
|
|
139
|
+
'gridcell',
|
|
140
|
+
'combobox',
|
|
141
|
+
'textbox',
|
|
142
|
+
'searchbox',
|
|
143
|
+
'scrollbar',
|
|
144
|
+
]
|
|
145
|
+
.map((role) => `[role="${role}"]`)
|
|
146
|
+
.join(', ');
|
|
147
|
+
|
|
148
|
+
/** Widgets that a space press activates (links do not: space scrolls the page there). */
|
|
149
|
+
const pressableSelector =
|
|
150
|
+
'button, summary, [role="button"], [role="checkbox"], [role="switch"], [role="radio"], [role="option"], [role="tab"], [role="treeitem"], [role^="menuitem"]';
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* True when the focused element handles `key` itself (typing, toggling, choosing, adjusting a
|
|
154
|
+
* value), so the tour neither navigates nor blocks scrolling with it.
|
|
155
|
+
*/
|
|
156
|
+
export function handlesKey(target: EventTarget | null, key: string): boolean {
|
|
157
|
+
if (!(target instanceof HTMLElement)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (
|
|
162
|
+
target.isContentEditable ||
|
|
163
|
+
target instanceof HTMLTextAreaElement ||
|
|
164
|
+
target instanceof HTMLSelectElement ||
|
|
165
|
+
((target instanceof HTMLAudioElement || target instanceof HTMLVideoElement) && target.controls)
|
|
166
|
+
) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (target instanceof HTMLInputElement) {
|
|
171
|
+
if (textInputTypes.has(target.type)) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (target.type === 'radio') {
|
|
176
|
+
return key === ' ' || arrowKeys.has(key);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (target.type === 'range') {
|
|
180
|
+
return rangeKeys.has(key);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return key === ' ' && pressableInputTypes.has(target.type);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (key === ' ') {
|
|
187
|
+
return target.closest(pressableSelector) !== null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return rangeKeys.has(key) && target.closest(keyedWidgetSelector) !== null;
|
|
191
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {intersect} from '../../shared/geometry/box.util.js';
|
|
2
|
+
import type {Box} from '../../shared/geometry/geometry.type.js';
|
|
3
|
+
import {checkVisibility, containsComposed, toBox} from './elements.util.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Elements that stay put while the page scrolls (`position: fixed` or `sticky`): navigation bars,
|
|
7
|
+
* headers, floating buttons, chat widgets, cookie banners, toasts. Anything scrolled underneath
|
|
8
|
+
* them is hidden, so the tour scrolls targets clear of them where it can.
|
|
9
|
+
*
|
|
10
|
+
* Hit-testing (`elementsFromPoint`) cannot find them while the tour is open: the tour is a modal
|
|
11
|
+
* dialog and the inert page is excluded from hit-testing. They are found by computed style instead,
|
|
12
|
+
* also inside open shadow roots (web components). The list is cached briefly so following a
|
|
13
|
+
* scrolling target does not rescan the page every frame.
|
|
14
|
+
*/
|
|
15
|
+
const rescanAfterMs = 500;
|
|
16
|
+
let cache: {readonly at: number; readonly elements: readonly Element[]} | undefined;
|
|
17
|
+
|
|
18
|
+
/** Forgets the cached elements, so a page that has been navigated away from is not kept in memory. */
|
|
19
|
+
export function clearOccluderCache(): void {
|
|
20
|
+
cache = undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function stayingPutElements(layer: Element | undefined): readonly Element[] {
|
|
24
|
+
const now = performance.now();
|
|
25
|
+
if (cache && now - cache.at < rescanAfterMs) {
|
|
26
|
+
return cache.elements;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const elements: Element[] = [];
|
|
30
|
+
const scan = (root: ParentNode): void => {
|
|
31
|
+
for (const element of root.querySelectorAll('*')) {
|
|
32
|
+
if (element.shadowRoot) {
|
|
33
|
+
scan(element.shadowRoot);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {position} = getComputedStyle(element);
|
|
37
|
+
if (position === 'fixed' || position === 'sticky') {
|
|
38
|
+
elements.push(element);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
scan(document.body);
|
|
44
|
+
const found = elements.filter((element) => !layer?.contains(element) && !element.matches('[data-rtk-probe]'));
|
|
45
|
+
cache = {at: now, elements: found};
|
|
46
|
+
return found;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** False for elements that are there but cannot be seen: closed menus faded out with `visibility` or `opacity`. */
|
|
50
|
+
function isSeen(element: Element): boolean {
|
|
51
|
+
const checked = checkVisibility(element, {visibilityProperty: true, opacityProperty: true});
|
|
52
|
+
if (checked !== undefined) {
|
|
53
|
+
return checked;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const style = getComputedStyle(element);
|
|
57
|
+
return style.visibility !== 'hidden' && style.opacity !== '0';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Rectangles (viewport coordinates) of elements that can cover the target. Excluded: elements
|
|
62
|
+
* containing the target (highlighting a link in a sticky navigation) or inside it, empty or
|
|
63
|
+
* invisible ones, and ones covering more than half the screen (app shells, full-screen
|
|
64
|
+
* backgrounds), which cannot be scrolled away from.
|
|
65
|
+
*/
|
|
66
|
+
export function findOccluders(target: Element | undefined, layer: Element | undefined): Box[] {
|
|
67
|
+
const screen = {width: document.documentElement.clientWidth, height: document.documentElement.clientHeight};
|
|
68
|
+
const occluders: Box[] = [];
|
|
69
|
+
for (const element of stayingPutElements(layer)) {
|
|
70
|
+
if (!element.isConnected || (target && (containsComposed(element, target) || containsComposed(target, element)))) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const rect = toBox(element.getBoundingClientRect());
|
|
75
|
+
const isCovering =
|
|
76
|
+
rect.width > 0 && rect.height > 0 && rect.width * rect.height <= (screen.width * screen.height) / 2;
|
|
77
|
+
if (isCovering && isSeen(element)) {
|
|
78
|
+
occluders.push(rect);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return occluders;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The occluders overlapping `box`. */
|
|
86
|
+
export function occludersOver(box: Box, occluders: readonly Box[]): Box[] {
|
|
87
|
+
return occluders.filter((occluder) => intersect(occluder, box) !== undefined);
|
|
88
|
+
}
|