UNPKG

posthog-js

Version:

Posthog-js allows you to automatically capture usage and send events to PostHog.

214 lines (213 loc) • 10.4 kB
import { type JSX, type RefObject } from 'preact'; import { PostHog } from '../posthog-core'; import { DisplaySurveyPopoverOptions, Survey, SurveyCallback, SurveyWithTypeAndAppearance } from '../posthog-surveys-types'; import { Properties } from '../types'; import { getNextSurveyStep } from '../utils/survey-branching'; export { getNextSurveyStep }; /** * `previewPageIndex` sentinel for the intro screen — the leading mirror of the confirmation * page's `survey.questions.length` sentinel. Also serves as a capability marker for the main * app: bundles without this export cannot render the intro screen preview. */ export declare const INTRO_SCREEN_PREVIEW_INDEX = -1; export declare class SurveyManager { private _posthog; private _surveyInFocus; private _surveyTimeouts; private _widgetSelectorListeners; private _renderedTargets; private _prefillHandledSurveys; private _automaticDisplayDispose?; private _currentLanguage; private _surveyIsRendered; private _languageChangeListener; private _unsubscribeFeatureFlags; private _surveyPopupProps; private _displayOptions; constructor(posthog: PostHog); private _onLanguageChange; private get _featureFlags(); private _applyDisplayOverrides; handlePageUnload: () => void; private _clearSurveyTimeout; /** * cancel a "pending" survey, i.e. one that is waiting to be displayed in * the _surveyTimeouts queue */ cancelSurvey(surveyId: string): void; setAutomaticDisplayDispose(dispose: () => void): void; dispose(): void; /** * `resumeDelayFromActivation` is internal to the display loop: a survey armed by an * event/action trigger resumes its popup delay from when the trigger fired, so navigating * mid-delay does not restart the countdown. An explicit `displaySurvey()` call carries its own * `ignoreDelay` option instead, so it must always honor the full configured delay. */ handlePopoverSurvey: (surveyParam: Survey, options?: DisplaySurveyPopoverOptions, { resumeDelayFromActivation }?: { resumeDelayFromActivation?: boolean; }) => void; private _handleWidget; private _detachWidgetSelectorListener; private _removeWidgetSelectorListener; private _isWidgetSurveyOpen; private _manageWidgetSelectorListener; /** * Sorts surveys by their appearance delay in ascending order. If a survey does not have an appearance delay, * it is considered to have a delay of 0. * @param surveys * @returns The surveys sorted by their appearance delay */ private _sortSurveysByAppearanceDelay; renderPopover: (survey: Survey) => void; renderSurvey: (survey: Survey, selector: Element, properties?: Properties) => void; private _translateSurveyForRendering; private _handleUrlPrefill; /** * On load, capture a response for prefilled questions only when: * 1) the survey is complete, OR * 2) partial responses are enabled AND the skipped questions were set to auto-submit. */ private _autoSubmitPrefilledResponses; /** * process prefilled responses for non-hosted surveys * * @returns boolean, true if the survey is completed */ private _handleInitialResponses; private _processPrefillData; private _isSurveyFeatureFlagEnabled; private _isSurveyConditionMatched; private _internalFlagCheckSatisfied; checkSurveyEligibility(survey: Survey): { eligible: boolean; reason?: string; }; /** * Renderability = eligibility (running, type, flags, wait period, already-seen) plus the * survey's event/action activation trigger. Used by the programmatic `canRenderSurvey` / * `canRenderSurveyAsync` checks so they match the display loop. * * The trigger is intentionally kept out of `checkSurveyEligibility`: that method is also * used by the explicit `displaySurvey` path, and the trigger state only lives in memory * (a reload clears it, server-side events never set it). Gating eligibility on it would * make explicit `displaySurvey('id')` calls silently show nothing. */ checkSurveyRenderability(survey: Survey): { eligible: boolean; reason?: string; }; /** * Surveys can be activated by events or actions. This method checks if the survey has events and actions, * and if so, it checks if the survey has been activated. * @param survey */ private _hasActionOrEventTriggeredSurvey; private _checkFlags; /** * The full predicate for "should this survey display right now": eligibility (running, * type, linked/targeting/internal flags, wait period, already-seen) plus the URL/device/ * selector conditions, the event/action trigger, and any feature-flag dependencies. * * Shared by the display loop and re-checked when a popover's delay timer fires, so a * survey that became ineligible *during* the delay (e.g. an identify() reloaded flags and * the internal targeting flag is now false) is not shown. Note this is purely an AND gate: * adding it can only ever suppress a display, never cause an extra one. */ private _shouldDisplaySurvey; getActiveMatchingSurveys: (callback: SurveyCallback, forceReload?: boolean) => void; callSurveysAndEvaluateDisplayLogic: (forceReload?: boolean) => void; private _addSurveyToFocus; private _removeSurveyFromDom; private _removeSurveyFromFocus; getTestAPI(): { addSurveyToFocus: (survey: Pick<Survey, "id">) => void; removeSurveyFromFocus: (survey: SurveyWithTypeAndAppearance) => void; surveyInFocus: string | null; surveyTimeouts: Map<string, number>; handleWidget: (survey: Survey) => void; handlePopoverSurvey: (surveyParam: Survey, options?: DisplaySurveyPopoverOptions, { resumeDelayFromActivation }?: { resumeDelayFromActivation?: boolean; }) => void; manageWidgetSelectorListener: (survey: Survey, selector: string) => void; sortSurveysByAppearanceDelay: (surveys: Survey[]) => Survey[]; checkFlags: (survey: Survey) => boolean; isSurveyFeatureFlagEnabled: (flagKey: string | null, flagVariant?: string | undefined) => boolean; onLanguageChange: () => void; currentLanguage: string | null; surveyIsRendered: boolean; }; } export declare const renderSurveysPreview: ({ survey, parentElement, previewPageIndex, forceDisableHtml, onPreviewSubmit, onPreviewBack, positionStyles, }: { survey: Survey; parentElement: HTMLElement; previewPageIndex: number; forceDisableHtml?: boolean; onPreviewSubmit?: (res: string | string[] | number | null) => void; onPreviewBack?: () => void; posthog?: PostHog; positionStyles?: JSX.CSSProperties; }) => void; export declare const renderFeedbackWidgetPreview: ({ survey, root, forceDisableHtml, }: { survey: Survey; root: HTMLElement; forceDisableHtml?: boolean; }) => void; export declare function generateSurveys(posthog: PostHog, isSurveysEnabled: boolean | undefined): SurveyManager | undefined; type UseHideSurveyOnURLChangeProps = { survey: Pick<Survey, 'id' | 'conditions' | 'type' | 'appearance'>; removeSurveyFromFocus?: (survey: SurveyWithTypeAndAppearance) => void; setSurveyVisible: (visible: boolean) => void; isPreviewMode?: boolean; posthog?: PostHog; }; /** * This hook handles URL-based survey visibility after the initial mount. * The initial URL check is handled by the `getActiveMatchingSurveys` method in the `PostHogSurveys` class, * which ensures the URL matches before displaying a survey for the first time. * That is the method that is called every second to see if there's a matching survey. * * This separation of concerns means: * 1. Initial URL matching is done by `getActiveMatchingSurveys` before displaying the survey * 2. Subsequent URL changes are handled here to hide the survey as the user navigates */ export declare function useHideSurveyOnURLChange({ survey, removeSurveyFromFocus, setSurveyVisible, isPreviewMode, posthog, }: UseHideSurveyOnURLChangeProps): void; export declare function usePopupVisibility(survey: Survey, posthog: PostHog | undefined, millisecondDelay: number, isPreviewMode: boolean, removeSurveyFromFocus: (survey: SurveyWithTypeAndAppearance) => void, isPopup: boolean, surveyContainerRef?: RefObject<HTMLDivElement>, skipShownEvent?: boolean, surveyLanguage?: string | null): { isPopupVisible: boolean; isSurveySent: boolean; setIsPopupVisible: import("preact/hooks").Dispatch<import("preact/hooks").StateUpdater<boolean>>; hidePopupWithAnimation: () => void; }; interface SurveyPopupProps { survey: Survey; forceDisableHtml?: boolean; posthog?: PostHog; style?: JSX.CSSProperties; previewPageIndex?: number | undefined; removeSurveyFromFocus?: (survey: SurveyWithTypeAndAppearance) => void; isPopup?: boolean; onPreviewSubmit?: (res: string | string[] | number | null) => void; onPreviewBack?: () => void; onPopupSurveyDismissed?: () => void; onCloseConfirmationMessage?: () => void; /** Additional properties to include in all survey events */ properties?: Properties; /** if true, survey popup will only render if there is a thank-you message */ isSurveyCompleted?: boolean; /** When true, `survey shown` events will not be emitted automatically */ skipShownEvent?: boolean; /** The language that was applied to the survey. */ surveyLanguage?: string | null; } export declare function SurveyPopup({ survey, forceDisableHtml, posthog, style, previewPageIndex, removeSurveyFromFocus, isPopup, onPreviewSubmit, onPreviewBack, onPopupSurveyDismissed, onCloseConfirmationMessage, properties, isSurveyCompleted, skipShownEvent, surveyLanguage, }: SurveyPopupProps): JSX.Element | null; export declare function Questions({ survey, forceDisableHtml, posthog, }: { survey: Survey; forceDisableHtml: boolean; posthog?: PostHog; }): JSX.Element | null; export declare function FeedbackWidget({ survey, forceDisableHtml, posthog, readOnly, surveyLanguage, }: { survey: Survey; forceDisableHtml?: boolean; posthog?: PostHog; readOnly?: boolean; surveyLanguage?: string | null; }): JSX.Element | null;