posthog-js 1.303.1 → 1.305.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 (50) hide show
  1. package/dist/array.full.es5.js +1 -1
  2. package/dist/array.full.es5.js.map +1 -1
  3. package/dist/array.full.js +1 -1
  4. package/dist/array.full.js.map +1 -1
  5. package/dist/array.full.no-external.js +1 -1
  6. package/dist/array.full.no-external.js.map +1 -1
  7. package/dist/array.js +1 -1
  8. package/dist/array.js.map +1 -1
  9. package/dist/array.no-external.js +1 -1
  10. package/dist/array.no-external.js.map +1 -1
  11. package/dist/customizations.full.js +1 -1
  12. package/dist/lazy-recorder.js +1 -1
  13. package/dist/main.js +1 -1
  14. package/dist/main.js.map +1 -1
  15. package/dist/module.d.ts +20 -2
  16. package/dist/module.full.d.ts +20 -2
  17. package/dist/module.full.js +1 -1
  18. package/dist/module.full.js.map +1 -1
  19. package/dist/module.full.no-external.d.ts +20 -2
  20. package/dist/module.full.no-external.js +1 -1
  21. package/dist/module.full.no-external.js.map +1 -1
  22. package/dist/module.js +1 -1
  23. package/dist/module.js.map +1 -1
  24. package/dist/module.no-external.d.ts +20 -2
  25. package/dist/module.no-external.js +1 -1
  26. package/dist/module.no-external.js.map +1 -1
  27. package/dist/posthog-recorder.js +1 -1
  28. package/dist/product-tours.js +1 -1
  29. package/dist/product-tours.js.map +1 -1
  30. package/dist/src/extensions/product-tours/components/ProductTourTooltip.d.ts +1 -1
  31. package/dist/src/extensions/product-tours/product-tours.d.ts +7 -1
  32. package/dist/src/posthog-product-tours-types.d.ts +19 -1
  33. package/dist/surveys-preview.d.ts +19 -1
  34. package/lib/package.json +1 -1
  35. package/lib/src/extensions/product-tours/components/ProductTourTooltip.d.ts +1 -1
  36. package/lib/src/extensions/product-tours/components/ProductTourTooltip.js +31 -6
  37. package/lib/src/extensions/product-tours/components/ProductTourTooltip.js.map +1 -1
  38. package/lib/src/extensions/product-tours/product-tours.d.ts +7 -1
  39. package/lib/src/extensions/product-tours/product-tours.js +183 -8
  40. package/lib/src/extensions/product-tours/product-tours.js.map +1 -1
  41. package/lib/src/posthog-product-tours-types.d.ts +19 -1
  42. package/lib/src/posthog-product-tours-types.js.map +1 -1
  43. package/lib/src/posthog-product-tours.js +10 -42
  44. package/lib/src/posthog-product-tours.js.map +1 -1
  45. package/lib/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +1 -1
  47. package/dist/src/utils/product-tour-utils.d.ts +0 -5
  48. package/lib/src/utils/product-tour-utils.d.ts +0 -5
  49. package/lib/src/utils/product-tour-utils.js +0 -67
  50. package/lib/src/utils/product-tour-utils.js.map +0 -1
@@ -5,7 +5,7 @@ export interface ProductTourTooltipProps {
5
5
  step: ProductTourStep;
6
6
  stepIndex: number;
7
7
  totalSteps: number;
8
- targetElement: HTMLElement;
8
+ targetElement: HTMLElement | null;
9
9
  onNext: () => void;
10
10
  onPrevious: () => void;
11
11
  onDismiss: (reason: ProductTourDismissReason) => void;
@@ -1,5 +1,5 @@
1
1
  import { PostHog } from '../../posthog-core';
2
- import { ProductTour, ProductTourDismissReason, ProductTourRenderReason } from '../../posthog-product-tours-types';
2
+ import { ProductTour, ProductTourCallback, ProductTourDismissReason, ProductTourRenderReason } from '../../posthog-product-tours-types';
3
3
  export declare class ProductTourManager {
4
4
  private _instance;
5
5
  private _activeTour;
@@ -7,6 +7,7 @@ export declare class ProductTourManager {
7
7
  private _renderReason;
8
8
  private _checkInterval;
9
9
  private _triggerSelectorListeners;
10
+ private _surveyEventUnsubscribe;
10
11
  constructor(instance: PostHog);
11
12
  start(): void;
12
13
  stop(): void;
@@ -21,9 +22,14 @@ export declare class ProductTourManager {
21
22
  private _completeTour;
22
23
  private _renderCurrentStep;
23
24
  private _renderTooltipWithPreact;
25
+ private _renderSurveyStep;
26
+ private _cleanupSurveyListener;
24
27
  private _cleanup;
25
28
  private _manageTriggerSelectorListener;
26
29
  private _removeTriggerSelectorListener;
27
30
  private _removeAllTriggerListeners;
28
31
  private _captureEvent;
32
+ getActiveProductTours(callback: ProductTourCallback): void;
33
+ resetTour(tourId: string): void;
34
+ resetAllTours(): void;
29
35
  }
@@ -8,10 +8,27 @@ export interface JSONContent {
8
8
  }[];
9
9
  text?: string;
10
10
  }
11
+ export type ProductTourSurveyQuestionType = 'open' | 'rating';
12
+ export interface ProductTourSurveyQuestion {
13
+ type: ProductTourSurveyQuestionType;
14
+ questionText: string;
15
+ /** Rating display type - emoji or number */
16
+ display?: 'emoji' | 'number';
17
+ /** Rating scale - 3 or 5 for emoji, 5 or 10 for number */
18
+ scale?: 3 | 5 | 10;
19
+ /** Label for low end of rating scale (e.g., "Not likely") */
20
+ lowerBoundLabel?: string;
21
+ /** Label for high end of rating scale (e.g., "Very likely") */
22
+ upperBoundLabel?: string;
23
+ }
11
24
  export interface ProductTourStep {
12
25
  id: string;
13
- selector: string;
26
+ selector?: string;
14
27
  content: JSONContent | null;
28
+ /** Inline survey question config - if present, this is a survey step */
29
+ survey?: ProductTourSurveyQuestion;
30
+ /** ID of the auto-created survey for this step (set by backend) */
31
+ linkedSurveyId?: string;
15
32
  }
16
33
  export interface ProductTourConditions {
17
34
  url?: string;
@@ -32,6 +49,7 @@ export interface ProductTour {
32
49
  name: string;
33
50
  description?: string;
34
51
  type: 'product_tour';
52
+ auto_launch?: boolean;
35
53
  start_date: string | null;
36
54
  end_date: string | null;
37
55
  current_iteration?: number;
@@ -2944,10 +2944,27 @@ interface JSONContent {
2944
2944
  }[];
2945
2945
  text?: string;
2946
2946
  }
2947
+ type ProductTourSurveyQuestionType = 'open' | 'rating';
2948
+ interface ProductTourSurveyQuestion {
2949
+ type: ProductTourSurveyQuestionType;
2950
+ questionText: string;
2951
+ /** Rating display type - emoji or number */
2952
+ display?: 'emoji' | 'number';
2953
+ /** Rating scale - 3 or 5 for emoji, 5 or 10 for number */
2954
+ scale?: 3 | 5 | 10;
2955
+ /** Label for low end of rating scale (e.g., "Not likely") */
2956
+ lowerBoundLabel?: string;
2957
+ /** Label for high end of rating scale (e.g., "Very likely") */
2958
+ upperBoundLabel?: string;
2959
+ }
2947
2960
  interface ProductTourStep {
2948
2961
  id: string;
2949
- selector: string;
2962
+ selector?: string;
2950
2963
  content: JSONContent | null;
2964
+ /** Inline survey question config - if present, this is a survey step */
2965
+ survey?: ProductTourSurveyQuestion;
2966
+ /** ID of the auto-created survey for this step (set by backend) */
2967
+ linkedSurveyId?: string;
2951
2968
  }
2952
2969
  interface ProductTourConditions {
2953
2970
  url?: string;
@@ -2968,6 +2985,7 @@ interface ProductTour {
2968
2985
  name: string;
2969
2986
  description?: string;
2970
2987
  type: 'product_tour';
2988
+ auto_launch?: boolean;
2971
2989
  start_date: string | null;
2972
2990
  end_date: string | null;
2973
2991
  current_iteration?: number;
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "posthog-js",
3
- "version": "1.303.1",
3
+ "version": "1.305.0",
4
4
  "description": "Posthog-js allows you to automatically capture usage and send events to PostHog.",
5
5
  "repository": "https://github.com/PostHog/posthog-js",
6
6
  "author": "hey@posthog.com",
@@ -5,7 +5,7 @@ export interface ProductTourTooltipProps {
5
5
  step: ProductTourStep;
6
6
  stepIndex: number;
7
7
  totalSteps: number;
8
- targetElement: HTMLElement;
8
+ targetElement: HTMLElement | null;
9
9
  onNext: () => void;
10
10
  onPrevious: () => void;
11
11
  onDismiss: (reason: ProductTourDismissReason) => void;
@@ -88,11 +88,15 @@ function ProductTourTooltip(_a) {
88
88
  var previousStepRef = (0, hooks_1.useRef)(stepIndex);
89
89
  var isTransitioningRef = (0, hooks_1.useRef)(false);
90
90
  var isFirstRender = (0, hooks_1.useRef)(true);
91
+ var isModalStep = !targetElement;
91
92
  var updatePosition = (0, hooks_1.useCallback)(function () {
93
+ if (isModalStep) {
94
+ return;
95
+ }
92
96
  var rect = targetElement.getBoundingClientRect();
93
97
  setPosition((0, product_tours_utils_1.calculateTooltipPosition)(rect));
94
98
  setSpotlightStyle((0, product_tours_utils_1.getSpotlightStyle)(rect));
95
- }, [targetElement]);
99
+ }, [targetElement, isModalStep]);
96
100
  (0, hooks_1.useEffect)(function () {
97
101
  var isStepChange = previousStepRef.current !== stepIndex;
98
102
  var currentStepIndex = stepIndex;
@@ -100,6 +104,12 @@ function ProductTourTooltip(_a) {
100
104
  isFirstRender.current = false;
101
105
  previousStepRef.current = stepIndex;
102
106
  isTransitioningRef.current = true;
107
+ if (isModalStep) {
108
+ // Modal steps are just centered on screen - no positioning needed
109
+ setTransitionState('visible');
110
+ isTransitioningRef.current = false;
111
+ return;
112
+ }
103
113
  scrollToElement(targetElement, function () {
104
114
  if (previousStepRef.current !== currentStepIndex) {
105
115
  return;
@@ -123,6 +133,17 @@ function ProductTourTooltip(_a) {
123
133
  setDisplayedStep(step);
124
134
  setDisplayedStepIndex(stepIndex);
125
135
  setTransitionState('entering');
136
+ if (isModalStep) {
137
+ // Modal steps don't need scrolling or position calculation
138
+ setTimeout(function () {
139
+ if (previousStepRef.current !== currentStepIndex) {
140
+ return;
141
+ }
142
+ setTransitionState('visible');
143
+ isTransitioningRef.current = false;
144
+ }, 50);
145
+ return;
146
+ }
126
147
  scrollToElement(targetElement, function () {
127
148
  if (previousStepRef.current !== currentStepIndex) {
128
149
  return;
@@ -138,9 +159,9 @@ function ProductTourTooltip(_a) {
138
159
  });
139
160
  }, 150);
140
161
  }
141
- }, [targetElement, stepIndex, step, updatePosition]);
162
+ }, [targetElement, stepIndex, step, updatePosition, isModalStep]);
142
163
  (0, hooks_1.useEffect)(function () {
143
- if (transitionState !== 'visible') {
164
+ if (transitionState !== 'visible' || isModalStep) {
144
165
  return;
145
166
  }
146
167
  var handleUpdate = function () {
@@ -154,7 +175,7 @@ function ProductTourTooltip(_a) {
154
175
  window === null || window === void 0 ? void 0 : window.removeEventListener('scroll', handleUpdate, true);
155
176
  window === null || window === void 0 ? void 0 : window.removeEventListener('resize', handleUpdate);
156
177
  };
157
- }, [updatePosition, transitionState]);
178
+ }, [updatePosition, transitionState, isModalStep]);
158
179
  (0, hooks_1.useEffect)(function () {
159
180
  var handleKeyDown = function (e) {
160
181
  if (e.key === 'Escape') {
@@ -183,12 +204,16 @@ function ProductTourTooltip(_a) {
183
204
  '--ph-tour-border-radius': "".concat(appearance.borderRadius, "px"),
184
205
  '--ph-tour-border-color': appearance.borderColor,
185
206
  };
186
- var isReady = transitionState !== 'initializing' && position && spotlightStyle;
207
+ var isReady = isModalStep || (transitionState !== 'initializing' && position && spotlightStyle);
187
208
  var isVisible = transitionState === 'visible';
188
209
  if (!isReady) {
189
210
  return ((0, jsx_runtime_1.jsxs)("div", { class: "ph-tour-container", style: containerStyle, children: [(0, jsx_runtime_1.jsx)("div", { class: "ph-tour-click-overlay", onClick: handleOverlayClick }), (0, jsx_runtime_1.jsx)("div", { class: "ph-tour-spotlight", style: { top: '50%', left: '50%', width: '0px', height: '0px' } })] }));
190
211
  }
191
- return ((0, jsx_runtime_1.jsxs)("div", { class: "ph-tour-container", style: containerStyle, children: [(0, jsx_runtime_1.jsx)("div", { class: "ph-tour-click-overlay", onClick: handleOverlayClick }), (0, jsx_runtime_1.jsx)("div", { class: "ph-tour-spotlight", style: isVisible
212
+ // Modal step: centered on screen with overlay dimming, no spotlight/arrow
213
+ if (isModalStep) {
214
+ return ((0, jsx_runtime_1.jsxs)("div", { class: "ph-tour-container", style: containerStyle, children: [(0, jsx_runtime_1.jsx)("div", { class: "ph-tour-click-overlay", onClick: handleOverlayClick }), (0, jsx_runtime_1.jsx)("div", { class: "ph-tour-modal-overlay" }), (0, jsx_runtime_1.jsxs)("div", { class: "ph-tour-tooltip ph-tour-tooltip--modal", onClick: handleTooltipClick, children: [(0, jsx_runtime_1.jsx)("button", { class: "ph-tour-dismiss", onClick: function () { return onDismiss('user_clicked_skip'); }, "aria-label": "Close tour", children: icons_1.cancelSVG }), (0, jsx_runtime_1.jsx)("div", { class: "ph-tour-content", dangerouslySetInnerHTML: { __html: (0, product_tours_utils_1.renderTipTapContent)(displayedStep.content) } }), (0, jsx_runtime_1.jsxs)("div", { class: "ph-tour-footer", children: [(0, jsx_runtime_1.jsxs)("span", { class: "ph-tour-progress", children: [displayedStepIndex + 1, " of ", totalSteps] }), (0, jsx_runtime_1.jsxs)("div", { class: "ph-tour-buttons", children: [!isFirstStep && ((0, jsx_runtime_1.jsx)("button", { class: "ph-tour-button ph-tour-button--secondary", onClick: onPrevious, children: "Back" })), (0, jsx_runtime_1.jsx)("button", { class: "ph-tour-button ph-tour-button--primary", onClick: onNext, children: isLastStep ? 'Done' : 'Next' })] })] }), !appearance.whiteLabel && ((0, jsx_runtime_1.jsxs)("a", { href: "https://posthog.com/product-tours", target: "_blank", rel: "noopener noreferrer", class: "ph-tour-branding", children: ["Tour by ", icons_1.IconPosthogLogo] }))] })] }));
215
+ }
216
+ return ((0, jsx_runtime_1.jsxs)("div", { class: "ph-tour-container", style: containerStyle, children: [(0, jsx_runtime_1.jsx)("div", { class: "ph-tour-click-overlay", onClick: handleOverlayClick }), (0, jsx_runtime_1.jsx)("div", { class: "ph-tour-spotlight", style: isVisible && spotlightStyle
192
217
  ? spotlightStyle
193
218
  : {
194
219
  top: '50%',
@@ -1 +1 @@
1
- {"version":3,"file":"ProductTourTooltip.js","sourceRoot":"","sources":["../../../../../src/extensions/product-tours/components/ProductTourTooltip.tsx"],"names":[],"mappings":"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","sourcesContent":["import { h } from 'preact'\nimport { useEffect, useState, useCallback, useRef } from 'preact/hooks'\nimport { ProductTour, ProductTourStep, ProductTourDismissReason } from '../../../posthog-product-tours-types'\nimport {\n calculateTooltipPosition,\n getSpotlightStyle,\n mergeAppearance,\n renderTipTapContent,\n TooltipPosition,\n} from '../product-tours-utils'\nimport { addEventListener } from '../../../utils'\nimport { window as _window } from '../../../utils/globals'\nimport { IconPosthogLogo, cancelSVG } from '../../surveys/icons'\n\nconst window = _window as Window & typeof globalThis\n\ntype TransitionState = 'initializing' | 'entering' | 'visible' | 'exiting'\n\nexport interface ProductTourTooltipProps {\n tour: ProductTour\n step: ProductTourStep\n stepIndex: number\n totalSteps: number\n targetElement: HTMLElement\n onNext: () => void\n onPrevious: () => void\n onDismiss: (reason: ProductTourDismissReason) => void\n}\n\nfunction getOppositePosition(position: TooltipPosition): TooltipPosition {\n const opposites: Record<TooltipPosition, TooltipPosition> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n }\n return opposites[position]\n}\n\nfunction scrollToElement(element: HTMLElement, resolve: () => void): void {\n const initialRect = element.getBoundingClientRect()\n const viewportHeight = window.innerHeight\n const viewportWidth = window.innerWidth\n\n const safeMarginY = viewportHeight / 6\n const safeMarginX = viewportWidth / 6\n\n const isInSafeZone =\n initialRect.top >= safeMarginY &&\n initialRect.bottom <= viewportHeight - safeMarginY &&\n initialRect.left >= safeMarginX &&\n initialRect.right <= viewportWidth - safeMarginX\n\n if (isInSafeZone) {\n resolve()\n return\n }\n\n element.scrollIntoView({ behavior: 'smooth', block: 'center' })\n\n let lastTop = initialRect.top\n let stableCount = 0\n let resolved = false\n\n const checkStability = () => {\n if (resolved) return\n\n const currentRect = element.getBoundingClientRect()\n if (Math.abs(currentRect.top - lastTop) < 1) {\n stableCount++\n if (stableCount >= 3) {\n resolved = true\n resolve()\n return\n }\n } else {\n stableCount = 0\n }\n lastTop = currentRect.top\n setTimeout(checkStability, 50)\n }\n\n setTimeout(checkStability, 30)\n\n setTimeout(() => {\n if (!resolved) {\n resolved = true\n resolve()\n }\n }, 500)\n}\n\nexport function ProductTourTooltip({\n tour,\n step,\n stepIndex,\n totalSteps,\n targetElement,\n onNext,\n onPrevious,\n onDismiss,\n}: ProductTourTooltipProps): h.JSX.Element {\n const appearance = mergeAppearance(tour.appearance)\n const [transitionState, setTransitionState] = useState<TransitionState>('initializing')\n const [position, setPosition] = useState<ReturnType<typeof calculateTooltipPosition> | null>(null)\n const [spotlightStyle, setSpotlightStyle] = useState<ReturnType<typeof getSpotlightStyle> | null>(null)\n\n const [displayedStep, setDisplayedStep] = useState(step)\n const [displayedStepIndex, setDisplayedStepIndex] = useState(stepIndex)\n\n const previousStepRef = useRef(stepIndex)\n const isTransitioningRef = useRef(false)\n const isFirstRender = useRef(true)\n\n const updatePosition = useCallback(() => {\n const rect = targetElement.getBoundingClientRect()\n setPosition(calculateTooltipPosition(rect))\n setSpotlightStyle(getSpotlightStyle(rect))\n }, [targetElement])\n\n useEffect(() => {\n const isStepChange = previousStepRef.current !== stepIndex\n\n const currentStepIndex = stepIndex\n\n if (isFirstRender.current) {\n isFirstRender.current = false\n previousStepRef.current = stepIndex\n isTransitioningRef.current = true\n\n scrollToElement(targetElement, () => {\n if (previousStepRef.current !== currentStepIndex) {\n return\n }\n\n const rect = targetElement.getBoundingClientRect()\n setPosition(calculateTooltipPosition(rect))\n setSpotlightStyle(getSpotlightStyle(rect))\n setTransitionState('visible')\n isTransitioningRef.current = false\n })\n return\n }\n\n if (isStepChange) {\n previousStepRef.current = stepIndex\n isTransitioningRef.current = true\n\n setTransitionState('exiting')\n\n setTimeout(() => {\n if (previousStepRef.current !== currentStepIndex) {\n return\n }\n\n setDisplayedStep(step)\n setDisplayedStepIndex(stepIndex)\n setTransitionState('entering')\n\n scrollToElement(targetElement, () => {\n if (previousStepRef.current !== currentStepIndex) {\n return\n }\n\n updatePosition()\n setTimeout(() => {\n if (previousStepRef.current !== currentStepIndex) {\n return\n }\n setTransitionState('visible')\n isTransitioningRef.current = false\n }, 50)\n })\n }, 150)\n }\n }, [targetElement, stepIndex, step, updatePosition])\n\n useEffect(() => {\n if (transitionState !== 'visible') {\n return\n }\n\n const handleUpdate = () => {\n if (!isTransitioningRef.current) {\n updatePosition()\n }\n }\n\n addEventListener(window, 'scroll', handleUpdate as EventListener, { capture: true })\n addEventListener(window, 'resize', handleUpdate as EventListener)\n\n return () => {\n window?.removeEventListener('scroll', handleUpdate, true)\n window?.removeEventListener('resize', handleUpdate)\n }\n }, [updatePosition, transitionState])\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onDismiss('escape_key')\n }\n }\n addEventListener(window, 'keydown', handleKeyDown as EventListener)\n return () => {\n window?.removeEventListener('keydown', handleKeyDown)\n }\n }, [onDismiss])\n\n const handleOverlayClick = (e: MouseEvent) => {\n e.stopPropagation()\n onDismiss('user_clicked_outside')\n }\n\n const handleTooltipClick = (e: MouseEvent) => {\n e.stopPropagation()\n }\n\n const isLastStep = displayedStepIndex >= totalSteps - 1\n const isFirstStep = displayedStepIndex === 0\n\n const containerStyle = {\n '--ph-tour-background-color': appearance.backgroundColor,\n '--ph-tour-text-color': appearance.textColor,\n '--ph-tour-button-color': appearance.buttonColor,\n '--ph-tour-button-text-color': appearance.buttonTextColor,\n '--ph-tour-border-radius': `${appearance.borderRadius}px`,\n '--ph-tour-border-color': appearance.borderColor,\n } as h.JSX.CSSProperties\n\n const isReady = transitionState !== 'initializing' && position && spotlightStyle\n const isVisible = transitionState === 'visible'\n\n if (!isReady) {\n return (\n <div class=\"ph-tour-container\" style={containerStyle}>\n <div class=\"ph-tour-click-overlay\" onClick={handleOverlayClick} />\n <div class=\"ph-tour-spotlight\" style={{ top: '50%', left: '50%', width: '0px', height: '0px' }} />\n </div>\n )\n }\n\n return (\n <div class=\"ph-tour-container\" style={containerStyle}>\n <div class=\"ph-tour-click-overlay\" onClick={handleOverlayClick} />\n\n <div\n class=\"ph-tour-spotlight\"\n style={\n isVisible\n ? spotlightStyle\n : {\n top: '50%',\n left: '50%',\n width: '0px',\n height: '0px',\n }\n }\n />\n\n <div\n class={`ph-tour-tooltip ${isVisible ? 'ph-tour-tooltip--visible' : 'ph-tour-tooltip--hidden'}`}\n style={{\n top: `${position.top}px`,\n left: `${position.left}px`,\n opacity: isVisible ? 1 : 0,\n transform: isVisible ? 'translateY(0)' : 'translateY(10px)',\n transition: 'opacity 0.15s ease-out, transform 0.15s ease-out',\n }}\n onClick={handleTooltipClick}\n >\n <div class={`ph-tour-arrow ph-tour-arrow--${getOppositePosition(position.position)}`} />\n\n <button class=\"ph-tour-dismiss\" onClick={() => onDismiss('user_clicked_skip')} aria-label=\"Close tour\">\n {cancelSVG}\n </button>\n\n <div\n class=\"ph-tour-content\"\n dangerouslySetInnerHTML={{ __html: renderTipTapContent(displayedStep.content) }}\n />\n\n <div class=\"ph-tour-footer\">\n <span class=\"ph-tour-progress\">\n {displayedStepIndex + 1} of {totalSteps}\n </span>\n\n <div class=\"ph-tour-buttons\">\n {!isFirstStep && (\n <button class=\"ph-tour-button ph-tour-button--secondary\" onClick={onPrevious}>\n Back\n </button>\n )}\n <button class=\"ph-tour-button ph-tour-button--primary\" onClick={onNext}>\n {isLastStep ? 'Done' : 'Next'}\n </button>\n </div>\n </div>\n\n {!appearance.whiteLabel && (\n <a\n href=\"https://posthog.com/product-tours\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"ph-tour-branding\"\n >\n Tour by {IconPosthogLogo}\n </a>\n )}\n </div>\n </div>\n )\n}\n"]}
1
+ {"version":3,"file":"ProductTourTooltip.js","sourceRoot":"","sources":["../../../../../src/extensions/product-tours/components/ProductTourTooltip.tsx"],"names":[],"mappings":"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","sourcesContent":["import { h } from 'preact'\nimport { useEffect, useState, useCallback, useRef } from 'preact/hooks'\nimport { ProductTour, ProductTourStep, ProductTourDismissReason } from '../../../posthog-product-tours-types'\nimport {\n calculateTooltipPosition,\n getSpotlightStyle,\n mergeAppearance,\n renderTipTapContent,\n TooltipPosition,\n} from '../product-tours-utils'\nimport { addEventListener } from '../../../utils'\nimport { window as _window } from '../../../utils/globals'\nimport { IconPosthogLogo, cancelSVG } from '../../surveys/icons'\n\nconst window = _window as Window & typeof globalThis\n\ntype TransitionState = 'initializing' | 'entering' | 'visible' | 'exiting'\n\nexport interface ProductTourTooltipProps {\n tour: ProductTour\n step: ProductTourStep\n stepIndex: number\n totalSteps: number\n targetElement: HTMLElement | null\n onNext: () => void\n onPrevious: () => void\n onDismiss: (reason: ProductTourDismissReason) => void\n}\n\nfunction getOppositePosition(position: TooltipPosition): TooltipPosition {\n const opposites: Record<TooltipPosition, TooltipPosition> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n }\n return opposites[position]\n}\n\nfunction scrollToElement(element: HTMLElement, resolve: () => void): void {\n const initialRect = element.getBoundingClientRect()\n const viewportHeight = window.innerHeight\n const viewportWidth = window.innerWidth\n\n const safeMarginY = viewportHeight / 6\n const safeMarginX = viewportWidth / 6\n\n const isInSafeZone =\n initialRect.top >= safeMarginY &&\n initialRect.bottom <= viewportHeight - safeMarginY &&\n initialRect.left >= safeMarginX &&\n initialRect.right <= viewportWidth - safeMarginX\n\n if (isInSafeZone) {\n resolve()\n return\n }\n\n element.scrollIntoView({ behavior: 'smooth', block: 'center' })\n\n let lastTop = initialRect.top\n let stableCount = 0\n let resolved = false\n\n const checkStability = () => {\n if (resolved) return\n\n const currentRect = element.getBoundingClientRect()\n if (Math.abs(currentRect.top - lastTop) < 1) {\n stableCount++\n if (stableCount >= 3) {\n resolved = true\n resolve()\n return\n }\n } else {\n stableCount = 0\n }\n lastTop = currentRect.top\n setTimeout(checkStability, 50)\n }\n\n setTimeout(checkStability, 30)\n\n setTimeout(() => {\n if (!resolved) {\n resolved = true\n resolve()\n }\n }, 500)\n}\n\nexport function ProductTourTooltip({\n tour,\n step,\n stepIndex,\n totalSteps,\n targetElement,\n onNext,\n onPrevious,\n onDismiss,\n}: ProductTourTooltipProps): h.JSX.Element {\n const appearance = mergeAppearance(tour.appearance)\n const [transitionState, setTransitionState] = useState<TransitionState>('initializing')\n const [position, setPosition] = useState<ReturnType<typeof calculateTooltipPosition> | null>(null)\n const [spotlightStyle, setSpotlightStyle] = useState<ReturnType<typeof getSpotlightStyle> | null>(null)\n\n const [displayedStep, setDisplayedStep] = useState(step)\n const [displayedStepIndex, setDisplayedStepIndex] = useState(stepIndex)\n\n const previousStepRef = useRef(stepIndex)\n const isTransitioningRef = useRef(false)\n const isFirstRender = useRef(true)\n\n const isModalStep = !targetElement\n\n const updatePosition = useCallback(() => {\n if (isModalStep) {\n return\n }\n const rect = targetElement.getBoundingClientRect()\n setPosition(calculateTooltipPosition(rect))\n setSpotlightStyle(getSpotlightStyle(rect))\n }, [targetElement, isModalStep])\n\n useEffect(() => {\n const isStepChange = previousStepRef.current !== stepIndex\n\n const currentStepIndex = stepIndex\n\n if (isFirstRender.current) {\n isFirstRender.current = false\n previousStepRef.current = stepIndex\n isTransitioningRef.current = true\n\n if (isModalStep) {\n // Modal steps are just centered on screen - no positioning needed\n setTransitionState('visible')\n isTransitioningRef.current = false\n return\n }\n\n scrollToElement(targetElement, () => {\n if (previousStepRef.current !== currentStepIndex) {\n return\n }\n\n const rect = targetElement.getBoundingClientRect()\n setPosition(calculateTooltipPosition(rect))\n setSpotlightStyle(getSpotlightStyle(rect))\n setTransitionState('visible')\n isTransitioningRef.current = false\n })\n return\n }\n\n if (isStepChange) {\n previousStepRef.current = stepIndex\n isTransitioningRef.current = true\n\n setTransitionState('exiting')\n\n setTimeout(() => {\n if (previousStepRef.current !== currentStepIndex) {\n return\n }\n\n setDisplayedStep(step)\n setDisplayedStepIndex(stepIndex)\n setTransitionState('entering')\n\n if (isModalStep) {\n // Modal steps don't need scrolling or position calculation\n setTimeout(() => {\n if (previousStepRef.current !== currentStepIndex) {\n return\n }\n setTransitionState('visible')\n isTransitioningRef.current = false\n }, 50)\n return\n }\n\n scrollToElement(targetElement, () => {\n if (previousStepRef.current !== currentStepIndex) {\n return\n }\n\n updatePosition()\n setTimeout(() => {\n if (previousStepRef.current !== currentStepIndex) {\n return\n }\n setTransitionState('visible')\n isTransitioningRef.current = false\n }, 50)\n })\n }, 150)\n }\n }, [targetElement, stepIndex, step, updatePosition, isModalStep])\n\n useEffect(() => {\n if (transitionState !== 'visible' || isModalStep) {\n return\n }\n\n const handleUpdate = () => {\n if (!isTransitioningRef.current) {\n updatePosition()\n }\n }\n\n addEventListener(window, 'scroll', handleUpdate as EventListener, { capture: true })\n addEventListener(window, 'resize', handleUpdate as EventListener)\n\n return () => {\n window?.removeEventListener('scroll', handleUpdate, true)\n window?.removeEventListener('resize', handleUpdate)\n }\n }, [updatePosition, transitionState, isModalStep])\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onDismiss('escape_key')\n }\n }\n addEventListener(window, 'keydown', handleKeyDown as EventListener)\n return () => {\n window?.removeEventListener('keydown', handleKeyDown)\n }\n }, [onDismiss])\n\n const handleOverlayClick = (e: MouseEvent) => {\n e.stopPropagation()\n onDismiss('user_clicked_outside')\n }\n\n const handleTooltipClick = (e: MouseEvent) => {\n e.stopPropagation()\n }\n\n const isLastStep = displayedStepIndex >= totalSteps - 1\n const isFirstStep = displayedStepIndex === 0\n\n const containerStyle = {\n '--ph-tour-background-color': appearance.backgroundColor,\n '--ph-tour-text-color': appearance.textColor,\n '--ph-tour-button-color': appearance.buttonColor,\n '--ph-tour-button-text-color': appearance.buttonTextColor,\n '--ph-tour-border-radius': `${appearance.borderRadius}px`,\n '--ph-tour-border-color': appearance.borderColor,\n } as h.JSX.CSSProperties\n\n const isReady = isModalStep || (transitionState !== 'initializing' && position && spotlightStyle)\n const isVisible = transitionState === 'visible'\n\n if (!isReady) {\n return (\n <div class=\"ph-tour-container\" style={containerStyle}>\n <div class=\"ph-tour-click-overlay\" onClick={handleOverlayClick} />\n <div class=\"ph-tour-spotlight\" style={{ top: '50%', left: '50%', width: '0px', height: '0px' }} />\n </div>\n )\n }\n\n // Modal step: centered on screen with overlay dimming, no spotlight/arrow\n if (isModalStep) {\n return (\n <div class=\"ph-tour-container\" style={containerStyle}>\n <div class=\"ph-tour-click-overlay\" onClick={handleOverlayClick} />\n <div class=\"ph-tour-modal-overlay\" />\n <div class=\"ph-tour-tooltip ph-tour-tooltip--modal\" onClick={handleTooltipClick}>\n <button\n class=\"ph-tour-dismiss\"\n onClick={() => onDismiss('user_clicked_skip')}\n aria-label=\"Close tour\"\n >\n {cancelSVG}\n </button>\n\n <div\n class=\"ph-tour-content\"\n dangerouslySetInnerHTML={{ __html: renderTipTapContent(displayedStep.content) }}\n />\n\n <div class=\"ph-tour-footer\">\n <span class=\"ph-tour-progress\">\n {displayedStepIndex + 1} of {totalSteps}\n </span>\n\n <div class=\"ph-tour-buttons\">\n {!isFirstStep && (\n <button class=\"ph-tour-button ph-tour-button--secondary\" onClick={onPrevious}>\n Back\n </button>\n )}\n <button class=\"ph-tour-button ph-tour-button--primary\" onClick={onNext}>\n {isLastStep ? 'Done' : 'Next'}\n </button>\n </div>\n </div>\n\n {!appearance.whiteLabel && (\n <a\n href=\"https://posthog.com/product-tours\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"ph-tour-branding\"\n >\n Tour by {IconPosthogLogo}\n </a>\n )}\n </div>\n </div>\n )\n }\n\n return (\n <div class=\"ph-tour-container\" style={containerStyle}>\n <div class=\"ph-tour-click-overlay\" onClick={handleOverlayClick} />\n\n <div\n class=\"ph-tour-spotlight\"\n style={\n isVisible && spotlightStyle\n ? spotlightStyle\n : {\n top: '50%',\n left: '50%',\n width: '0px',\n height: '0px',\n }\n }\n />\n\n <div\n class={`ph-tour-tooltip ${isVisible ? 'ph-tour-tooltip--visible' : 'ph-tour-tooltip--hidden'}`}\n style={{\n top: `${position!.top}px`,\n left: `${position!.left}px`,\n opacity: isVisible ? 1 : 0,\n transform: isVisible ? 'translateY(0)' : 'translateY(10px)',\n transition: 'opacity 0.15s ease-out, transform 0.15s ease-out',\n }}\n onClick={handleTooltipClick}\n >\n <div class={`ph-tour-arrow ph-tour-arrow--${getOppositePosition(position!.position)}`} />\n\n <button class=\"ph-tour-dismiss\" onClick={() => onDismiss('user_clicked_skip')} aria-label=\"Close tour\">\n {cancelSVG}\n </button>\n\n <div\n class=\"ph-tour-content\"\n dangerouslySetInnerHTML={{ __html: renderTipTapContent(displayedStep.content) }}\n />\n\n <div class=\"ph-tour-footer\">\n <span class=\"ph-tour-progress\">\n {displayedStepIndex + 1} of {totalSteps}\n </span>\n\n <div class=\"ph-tour-buttons\">\n {!isFirstStep && (\n <button class=\"ph-tour-button ph-tour-button--secondary\" onClick={onPrevious}>\n Back\n </button>\n )}\n <button class=\"ph-tour-button ph-tour-button--primary\" onClick={onNext}>\n {isLastStep ? 'Done' : 'Next'}\n </button>\n </div>\n </div>\n\n {!appearance.whiteLabel && (\n <a\n href=\"https://posthog.com/product-tours\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"ph-tour-branding\"\n >\n Tour by {IconPosthogLogo}\n </a>\n )}\n </div>\n </div>\n )\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { PostHog } from '../../posthog-core';
2
- import { ProductTour, ProductTourDismissReason, ProductTourRenderReason } from '../../posthog-product-tours-types';
2
+ import { ProductTour, ProductTourCallback, ProductTourDismissReason, ProductTourRenderReason } from '../../posthog-product-tours-types';
3
3
  export declare class ProductTourManager {
4
4
  private _instance;
5
5
  private _activeTour;
@@ -7,6 +7,7 @@ export declare class ProductTourManager {
7
7
  private _renderReason;
8
8
  private _checkInterval;
9
9
  private _triggerSelectorListeners;
10
+ private _surveyEventUnsubscribe;
10
11
  constructor(instance: PostHog);
11
12
  start(): void;
12
13
  stop(): void;
@@ -21,9 +22,14 @@ export declare class ProductTourManager {
21
22
  private _completeTour;
22
23
  private _renderCurrentStep;
23
24
  private _renderTooltipWithPreact;
25
+ private _renderSurveyStep;
26
+ private _cleanupSurveyListener;
24
27
  private _cleanup;
25
28
  private _manageTriggerSelectorListener;
26
29
  private _removeTriggerSelectorListener;
27
30
  private _removeAllTriggerListeners;
28
31
  private _captureEvent;
32
+ getActiveProductTours(callback: ProductTourCallback): void;
33
+ resetTour(tourId: string): void;
34
+ resetAllTours(): void;
29
35
  }
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  var __values = (this && this.__values) || function(o) {
3
14
  var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
4
15
  if (m) return m.call(o);
@@ -14,15 +25,74 @@ Object.defineProperty(exports, "__esModule", { value: true });
14
25
  exports.ProductTourManager = void 0;
15
26
  var jsx_runtime_1 = require("preact/jsx-runtime");
16
27
  var preact_1 = require("preact");
17
- var product_tour_utils_1 = require("../../utils/product-tour-utils");
28
+ var posthog_surveys_types_1 = require("../../posthog-surveys-types");
18
29
  var product_tours_utils_1 = require("./product-tours-utils");
19
30
  var ProductTourTooltip_1 = require("./components/ProductTourTooltip");
20
31
  var logger_1 = require("../../utils/logger");
21
32
  var globals_1 = require("../../utils/globals");
22
33
  var storage_1 = require("../../storage");
23
34
  var utils_1 = require("../../utils");
35
+ var core_1 = require("@posthog/core");
24
36
  var logger = (0, logger_1.createLogger)('[Product Tours]');
25
37
  var document = globals_1.document;
38
+ var window = globals_1.window;
39
+ // Tour condition checking utilities (moved from utils/product-tour-utils.ts)
40
+ function doesTourUrlMatch(tour) {
41
+ var conditions = tour.conditions;
42
+ if (!(conditions === null || conditions === void 0 ? void 0 : conditions.url)) {
43
+ return true;
44
+ }
45
+ var currentUrl = window.location.href;
46
+ var targetUrl = conditions.url;
47
+ var matchType = conditions.urlMatchType || 'contains';
48
+ switch (matchType) {
49
+ case 'exact':
50
+ return currentUrl === targetUrl;
51
+ case 'contains':
52
+ return currentUrl.includes(targetUrl);
53
+ case 'regex':
54
+ try {
55
+ var regex = new RegExp(targetUrl);
56
+ return regex.test(currentUrl);
57
+ }
58
+ catch (_a) {
59
+ return false;
60
+ }
61
+ default:
62
+ return false;
63
+ }
64
+ }
65
+ function doesTourSelectorMatch(tour) {
66
+ var conditions = tour.conditions;
67
+ if (!(conditions === null || conditions === void 0 ? void 0 : conditions.selector)) {
68
+ return true;
69
+ }
70
+ try {
71
+ return !(0, core_1.isNull)(document.querySelector(conditions.selector));
72
+ }
73
+ catch (_a) {
74
+ return false;
75
+ }
76
+ }
77
+ function isTourInDateRange(tour) {
78
+ var now = new Date();
79
+ if (tour.start_date) {
80
+ var startDate = new Date(tour.start_date);
81
+ if (now < startDate) {
82
+ return false;
83
+ }
84
+ }
85
+ if (tour.end_date) {
86
+ var endDate = new Date(tour.end_date);
87
+ if (now > endDate) {
88
+ return false;
89
+ }
90
+ }
91
+ return true;
92
+ }
93
+ function checkTourConditions(tour) {
94
+ return isTourInDateRange(tour) && doesTourUrlMatch(tour) && doesTourSelectorMatch(tour);
95
+ }
26
96
  var CONTAINER_CLASS = 'ph-product-tour-container';
27
97
  var TRIGGER_LISTENER_ATTRIBUTE = 'data-ph-tour-trigger';
28
98
  var CHECK_INTERVAL_MS = 1000;
@@ -64,6 +134,7 @@ var ProductTourManager = /** @class */ (function () {
64
134
  this._renderReason = 'auto';
65
135
  this._checkInterval = null;
66
136
  this._triggerSelectorListeners = new Map();
137
+ this._surveyEventUnsubscribe = null;
67
138
  this._handleVisibilityChange = function () {
68
139
  if (document.hidden && _this._checkInterval) {
69
140
  clearInterval(_this._checkInterval);
@@ -145,6 +216,7 @@ var ProductTourManager = /** @class */ (function () {
145
216
  // should work even if completed/dismissed
146
217
  (_a = this._instance.productTours) === null || _a === void 0 ? void 0 : _a.getProductTours(function (tours) {
147
218
  var e_1, _a;
219
+ var _b;
148
220
  if (tours.length === 0) {
149
221
  _this._removeAllTriggerListeners();
150
222
  return;
@@ -153,15 +225,17 @@ var ProductTourManager = /** @class */ (function () {
153
225
  try {
154
226
  for (var tours_1 = __values(tours), tours_1_1 = tours_1.next(); !tours_1_1.done; tours_1_1 = tours_1.next()) {
155
227
  var tour = tours_1_1.value;
156
- // Tours with trigger_selector: always attach listener, skip eligibility
228
+ // Determine the trigger selector - explicit trigger_selector takes precedence,
229
+ // otherwise use conditions.selector for click-only tours (auto_launch=false)
230
+ var triggerSelector = tour.trigger_selector || (!tour.auto_launch ? (_b = tour.conditions) === null || _b === void 0 ? void 0 : _b.selector : null);
231
+ // Tours with a trigger selector: always attach listener
157
232
  // These are "on-demand" tours that show when clicked
158
- if (tour.trigger_selector) {
233
+ if (triggerSelector) {
159
234
  activeTriggerTourIds.add(tour.id);
160
- _this._manageTriggerSelectorListener(tour);
161
- continue;
235
+ _this._manageTriggerSelectorListener(__assign(__assign({}, tour), { trigger_selector: triggerSelector }));
162
236
  }
163
- // Tours without trigger_selector: check eligibility for auto-show
164
- if (!_this._activeTour && _this._isTourEligible(tour)) {
237
+ // Only auto-show if auto_launch is enabled
238
+ if (tour.auto_launch && !_this._activeTour && _this._isTourEligible(tour)) {
165
239
  _this.showTour(tour);
166
240
  }
167
241
  }
@@ -183,7 +257,7 @@ var ProductTourManager = /** @class */ (function () {
183
257
  };
184
258
  ProductTourManager.prototype._isTourEligible = function (tour) {
185
259
  var _a;
186
- if (!(0, product_tour_utils_1.checkTourConditions)(tour)) {
260
+ if (!checkTourConditions(tour)) {
187
261
  logger.info("Tour ".concat(tour.id, " failed conditions check"));
188
262
  return false;
189
263
  }
@@ -206,9 +280,14 @@ var ProductTourManager = /** @class */ (function () {
206
280
  var e_2, _a;
207
281
  if (reason === void 0) { reason = 'auto'; }
208
282
  // Validate all step selectors before showing the tour
283
+ // Steps without selectors are modal steps and don't need validation
209
284
  var selectorFailures = [];
210
285
  for (var i = 0; i < tour.steps.length; i++) {
211
286
  var step = tour.steps[i];
287
+ // Skip validation for modal steps (no selector)
288
+ if (!step.selector) {
289
+ continue;
290
+ }
212
291
  var result = (0, product_tours_utils_1.findElementBySelector)(step.selector);
213
292
  if (result.error === 'not_found' || result.error === 'not_visible') {
214
293
  selectorFailures.push({
@@ -295,6 +374,22 @@ var ProductTourManager = /** @class */ (function () {
295
374
  return;
296
375
  }
297
376
  var step = this._activeTour.steps[this._currentStepIndex];
377
+ // Survey step - display the linked survey and listen for completion
378
+ if (step.linkedSurveyId) {
379
+ this._renderSurveyStep(step.linkedSurveyId);
380
+ return;
381
+ }
382
+ // Modal step (no selector) - render without a target element
383
+ if (!step.selector) {
384
+ this._captureEvent('product tour step shown', {
385
+ $product_tour_id: this._activeTour.id,
386
+ $product_tour_step_id: step.id,
387
+ $product_tour_step_order: this._currentStepIndex,
388
+ $product_tour_step_type: 'modal',
389
+ });
390
+ this._renderTooltipWithPreact(null);
391
+ return;
392
+ }
298
393
  var result = (0, product_tours_utils_1.findElementBySelector)(step.selector);
299
394
  if (result.error === 'not_found' || result.error === 'not_visible') {
300
395
  this._captureEvent('product tour step selector failed', {
@@ -348,10 +443,59 @@ var ProductTourManager = /** @class */ (function () {
348
443
  var shadow = retrieveTourShadow(this._activeTour.id).shadow;
349
444
  (0, preact_1.render)((0, jsx_runtime_1.jsx)(ProductTourTooltip_1.ProductTourTooltip, { tour: this._activeTour, step: step, stepIndex: this._currentStepIndex, totalSteps: this._activeTour.steps.length, targetElement: element, onNext: this.nextStep, onPrevious: this.previousStep, onDismiss: this.dismissTour }), shadow);
350
445
  };
446
+ ProductTourManager.prototype._renderSurveyStep = function (surveyId) {
447
+ var _this = this;
448
+ var _a;
449
+ if (!this._activeTour) {
450
+ return;
451
+ }
452
+ var step = this._activeTour.steps[this._currentStepIndex];
453
+ // Emit tour step shown event
454
+ this._captureEvent('product tour step shown', {
455
+ $product_tour_id: this._activeTour.id,
456
+ $product_tour_step_id: step.id,
457
+ $product_tour_step_order: this._currentStepIndex,
458
+ $product_tour_step_type: 'survey',
459
+ $product_tour_linked_survey_id: surveyId,
460
+ });
461
+ // Clean up any existing survey event listener
462
+ this._cleanupSurveyListener();
463
+ // Set up listener for survey events
464
+ this._surveyEventUnsubscribe = this._instance.on('eventCaptured', function (data) {
465
+ var _a;
466
+ var eventSurveyId = (_a = data.properties) === null || _a === void 0 ? void 0 : _a[posthog_surveys_types_1.SurveyEventProperties.SURVEY_ID];
467
+ if (eventSurveyId !== surveyId) {
468
+ return;
469
+ }
470
+ if (data.event === posthog_surveys_types_1.SurveyEventName.SENT) {
471
+ logger.info("Survey ".concat(surveyId, " completed, advancing tour"));
472
+ _this._cleanupSurveyListener();
473
+ _this.nextStep();
474
+ }
475
+ else if (data.event === posthog_surveys_types_1.SurveyEventName.DISMISSED) {
476
+ logger.info("Survey ".concat(surveyId, " dismissed, dismissing tour"));
477
+ _this._cleanupSurveyListener();
478
+ _this.dismissTour('user_clicked_skip');
479
+ }
480
+ });
481
+ // Display the survey
482
+ (_a = this._instance.surveys) === null || _a === void 0 ? void 0 : _a.displaySurvey(surveyId, {
483
+ ignoreConditions: true,
484
+ ignoreDelay: true,
485
+ displayType: posthog_surveys_types_1.DisplaySurveyType.Popover,
486
+ });
487
+ logger.info("Displayed survey ".concat(surveyId, " for tour step ").concat(this._currentStepIndex));
488
+ };
489
+ ProductTourManager.prototype._cleanupSurveyListener = function () {
490
+ var _a;
491
+ (_a = this._surveyEventUnsubscribe) === null || _a === void 0 ? void 0 : _a.call(this);
492
+ this._surveyEventUnsubscribe = null;
493
+ };
351
494
  ProductTourManager.prototype._cleanup = function () {
352
495
  if (this._activeTour) {
353
496
  removeTourFromDom(this._activeTour.id);
354
497
  }
498
+ this._cleanupSurveyListener();
355
499
  this._activeTour = null;
356
500
  this._currentStepIndex = 0;
357
501
  this._renderReason = 'auto';
@@ -412,6 +556,37 @@ var ProductTourManager = /** @class */ (function () {
412
556
  ProductTourManager.prototype._captureEvent = function (eventName, properties) {
413
557
  this._instance.capture(eventName, properties);
414
558
  };
559
+ // Public API methods delegated from PostHogProductTours
560
+ ProductTourManager.prototype.getActiveProductTours = function (callback) {
561
+ var _this = this;
562
+ var _a;
563
+ (_a = this._instance.productTours) === null || _a === void 0 ? void 0 : _a.getProductTours(function (tours, context) {
564
+ if (!(context === null || context === void 0 ? void 0 : context.isLoaded)) {
565
+ callback([], context);
566
+ return;
567
+ }
568
+ var activeTours = tours.filter(function (tour) { return _this._isTourEligible(tour); });
569
+ callback(activeTours, context);
570
+ });
571
+ };
572
+ ProductTourManager.prototype.resetTour = function (tourId) {
573
+ storage_1.localStore._remove("ph_product_tour_completed_".concat(tourId));
574
+ storage_1.localStore._remove("ph_product_tour_dismissed_".concat(tourId));
575
+ };
576
+ ProductTourManager.prototype.resetAllTours = function () {
577
+ var storage = window === null || window === void 0 ? void 0 : window.localStorage;
578
+ if (!storage) {
579
+ return;
580
+ }
581
+ var keysToRemove = [];
582
+ for (var i = 0; i < storage.length; i++) {
583
+ var key = storage.key(i);
584
+ if ((key === null || key === void 0 ? void 0 : key.startsWith('ph_product_tour_completed_')) || (key === null || key === void 0 ? void 0 : key.startsWith('ph_product_tour_dismissed_'))) {
585
+ keysToRemove.push(key);
586
+ }
587
+ }
588
+ keysToRemove.forEach(function (key) { return storage_1.localStore._remove(key); });
589
+ };
415
590
  return ProductTourManager;
416
591
  }());
417
592
  exports.ProductTourManager = ProductTourManager;