@posthog/browser-common 0.9.5 → 0.9.6

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 (86) hide show
  1. package/dist/client.d.ts +6 -0
  2. package/dist/config.js +1 -1
  3. package/dist/config.mjs +1 -1
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +58 -10
  6. package/dist/index.mjs +1 -0
  7. package/dist/survey-action-matcher.d.ts +27 -0
  8. package/dist/survey-action-matcher.js +169 -0
  9. package/dist/survey-action-matcher.mjs +131 -0
  10. package/dist/survey-constants.d.ts +86 -0
  11. package/dist/survey-constants.js +144 -0
  12. package/dist/survey-constants.mjs +76 -0
  13. package/dist/survey-event-host.d.ts +15 -0
  14. package/dist/survey-event-host.js +18 -0
  15. package/dist/survey-event-host.mjs +1 -0
  16. package/dist/survey-event-receiver-base.d.ts +180 -0
  17. package/dist/survey-event-receiver-base.js +354 -0
  18. package/dist/survey-event-receiver-base.mjs +316 -0
  19. package/dist/survey-event-receiver.d.ts +36 -0
  20. package/dist/survey-event-receiver.js +133 -0
  21. package/dist/survey-event-receiver.mjs +95 -0
  22. package/dist/survey-render-context.d.ts +13 -0
  23. package/dist/survey-render-context.js +45 -0
  24. package/dist/survey-render-context.mjs +7 -0
  25. package/dist/surveys/components/BottomSection.d.ts +12 -0
  26. package/dist/surveys/components/BottomSection.js +82 -0
  27. package/dist/surveys/components/BottomSection.mjs +44 -0
  28. package/dist/surveys/components/ConfirmationMessage.d.ts +10 -0
  29. package/dist/surveys/components/ConfirmationMessage.js +92 -0
  30. package/dist/surveys/components/ConfirmationMessage.mjs +54 -0
  31. package/dist/surveys/components/IntroScreen.d.ts +10 -0
  32. package/dist/surveys/components/IntroScreen.js +78 -0
  33. package/dist/surveys/components/IntroScreen.mjs +40 -0
  34. package/dist/surveys/components/PostHogLogo.d.ts +5 -0
  35. package/dist/surveys/components/PostHogLogo.js +54 -0
  36. package/dist/surveys/components/PostHogLogo.mjs +16 -0
  37. package/dist/surveys/components/QuestionHeader.d.ts +10 -0
  38. package/dist/surveys/components/QuestionHeader.js +82 -0
  39. package/dist/surveys/components/QuestionHeader.mjs +41 -0
  40. package/dist/surveys/components/QuestionTypes.d.ts +31 -0
  41. package/dist/surveys/components/QuestionTypes.js +519 -0
  42. package/dist/surveys/components/QuestionTypes.mjs +469 -0
  43. package/dist/surveys/icons.d.ts +12 -0
  44. package/dist/surveys/icons.js +212 -0
  45. package/dist/surveys/icons.mjs +147 -0
  46. package/dist/surveys/survey-branching.d.ts +12 -0
  47. package/dist/surveys/survey-branching.js +101 -0
  48. package/dist/surveys/survey-branching.mjs +60 -0
  49. package/dist/surveys/survey-styles.d.ts +1 -0
  50. package/dist/surveys/survey-styles.js +40 -0
  51. package/dist/surveys/survey-styles.mjs +2 -0
  52. package/dist/surveys/survey-translations.d.ts +22 -0
  53. package/dist/surveys/survey-translations.js +68 -0
  54. package/dist/surveys/survey-translations.mjs +27 -0
  55. package/dist/surveys/survey-url-prefill.d.ts +36 -0
  56. package/dist/surveys/survey-url-prefill.js +162 -0
  57. package/dist/surveys/survey-url-prefill.mjs +118 -0
  58. package/dist/surveys/surveys-extension-utils.d.ts +133 -0
  59. package/dist/surveys/surveys-extension-utils.js +739 -0
  60. package/dist/surveys/surveys-extension-utils.mjs +608 -0
  61. package/dist/surveys-config.d.ts +60 -0
  62. package/dist/surveys-config.js +68 -0
  63. package/dist/surveys-config.mjs +9 -0
  64. package/dist/surveys-renderer.d.ts +248 -0
  65. package/dist/surveys-renderer.js +1299 -0
  66. package/dist/surveys-renderer.mjs +1230 -0
  67. package/dist/surveys.d.ts +117 -0
  68. package/dist/surveys.js +566 -0
  69. package/dist/surveys.mjs +495 -0
  70. package/dist/types/surveys.d.ts +83 -86
  71. package/dist/types/surveys.js +1 -127
  72. package/dist/types/surveys.mjs +1 -76
  73. package/dist/utils/autocapture-utils.js +8 -9
  74. package/dist/utils/autocapture-utils.mjs +8 -9
  75. package/dist/utils/matcher-utils.d.ts +1 -1
  76. package/dist/utils/matcher-utils.js +13 -10
  77. package/dist/utils/matcher-utils.mjs +10 -7
  78. package/dist/utils/survey-utils.d.ts +16 -0
  79. package/dist/utils/survey-utils.js +121 -0
  80. package/dist/utils/survey-utils.mjs +38 -0
  81. package/dist/utils/url-targeting-utils.d.ts +3 -1
  82. package/dist/utils/url-targeting-utils.js +1 -2
  83. package/dist/utils/url-targeting-utils.mjs +1 -2
  84. package/dist/utils/uuidv7.js +4 -4
  85. package/dist/utils/uuidv7.mjs +4 -4
  86. package/package.json +96 -2
@@ -0,0 +1,608 @@
1
+ import { FeatureFlagsCommonExtension } from "../extension-tokens.mjs";
2
+ import { getTargetingUrl } from "../utils/url-targeting-utils.mjs";
3
+ import { getSurveyReplayUrl } from "../survey-render-context.mjs";
4
+ import { cloneElement, createContext } from "preact";
5
+ import { SurveyEventName, SurveyEventProperties, SurveyPosition, SurveyType, SurveyWidgetType } from "../survey-constants.mjs";
6
+ import { SURVEY_IN_PROGRESS_PREFIX, SURVEY_LOGGER, getSurveyAbandonedKey, getSurveyInteractionProperty, getSurveySeenKey, getSurveyStorageKey } from "../utils/survey-utils.mjs";
7
+ import { isNullish, isUndefined } from "@posthog/core";
8
+ import { buildSurveyResponseEventProperties, canSurveyActivateRepeatedly, getDisplayOrderChoices, getSurveyResponseKey, propertyComparisons, shuffle } from "@posthog/core/surveys";
9
+ import { prepareStylesheet } from "../utils/stylesheet-loader.mjs";
10
+ import { surveyStyles } from "./survey-styles.mjs";
11
+ import { useContext } from "preact/hooks";
12
+ import { doesDeviceTypeMatch, hasPeriodPassed } from "../utils/matcher-utils.mjs";
13
+ const Z_INDEX_SURVEYS = 2147483645;
14
+ function getFontFamily(fontFamily) {
15
+ if ('inherit' === fontFamily) return 'inherit';
16
+ const defaultFontStack = 'BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
17
+ return fontFamily ? `${fontFamily}, ${defaultFontStack}` : `-apple-system, ${defaultFontStack}`;
18
+ }
19
+ const BLACK_TEXT_COLOR = '#020617';
20
+ const defaultSurveyAppearance = {
21
+ fontFamily: 'inherit',
22
+ backgroundColor: '#eeeded',
23
+ submitButtonColor: 'black',
24
+ submitButtonTextColor: 'white',
25
+ ratingButtonColor: 'white',
26
+ ratingButtonActiveColor: 'black',
27
+ borderColor: '#c9c6c6',
28
+ whiteLabel: false,
29
+ displayThankYouMessage: true,
30
+ thankYouMessageHeader: 'Thank you for your feedback!',
31
+ position: SurveyPosition.Right,
32
+ widgetType: SurveyWidgetType.Tab,
33
+ widgetLabel: 'Feedback',
34
+ widgetColor: 'black',
35
+ zIndex: String(Z_INDEX_SURVEYS),
36
+ disabledButtonOpacity: '0.6',
37
+ maxWidth: '300px',
38
+ textSubtleColor: '#939393',
39
+ boxPadding: '20px 24px',
40
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
41
+ borderRadius: '10px',
42
+ shuffleQuestions: false,
43
+ outlineColor: 'rgba(59, 130, 246, 0.8)',
44
+ inputBackground: 'white',
45
+ inputTextColor: BLACK_TEXT_COLOR,
46
+ scrollbarThumbColor: 'var(--ph-survey-border-color)',
47
+ scrollbarTrackColor: 'var(--ph-survey-background-color)'
48
+ };
49
+ const BOTTOM_BORDER_SURVEY_POSITIONS = [
50
+ SurveyPosition.Center,
51
+ SurveyPosition.Left,
52
+ SurveyPosition.Right
53
+ ];
54
+ const addSurveyCSSVariablesToElement = (element, type, appearance)=>{
55
+ const effectiveAppearance = {
56
+ ...defaultSurveyAppearance,
57
+ ...appearance
58
+ };
59
+ const hostStyle = element.style;
60
+ const surveyHasBottomBorder = !BOTTOM_BORDER_SURVEY_POSITIONS.includes(effectiveAppearance.position) || type === SurveyType.Widget && appearance?.widgetType === SurveyWidgetType.Tab;
61
+ hostStyle.setProperty('--ph-survey-font-family', getFontFamily(effectiveAppearance.fontFamily));
62
+ hostStyle.setProperty('--ph-survey-box-padding', effectiveAppearance.boxPadding);
63
+ hostStyle.setProperty('--ph-survey-max-width', effectiveAppearance.maxWidth);
64
+ hostStyle.setProperty('--ph-survey-z-index', effectiveAppearance.zIndex);
65
+ hostStyle.setProperty('--ph-survey-border-color', effectiveAppearance.borderColor);
66
+ if (surveyHasBottomBorder) {
67
+ hostStyle.setProperty('--ph-survey-border-radius', effectiveAppearance.borderRadius);
68
+ hostStyle.setProperty('--ph-survey-border-bottom', '1.5px solid var(--ph-survey-border-color)');
69
+ } else {
70
+ hostStyle.setProperty('--ph-survey-border-bottom', 'none');
71
+ hostStyle.setProperty('--ph-survey-border-radius', `${effectiveAppearance.borderRadius} ${effectiveAppearance.borderRadius} 0 0`);
72
+ }
73
+ hostStyle.setProperty('--ph-survey-background-color', effectiveAppearance.backgroundColor);
74
+ hostStyle.setProperty('--ph-survey-box-shadow', effectiveAppearance.boxShadow);
75
+ hostStyle.setProperty('--ph-survey-disabled-button-opacity', effectiveAppearance.disabledButtonOpacity);
76
+ hostStyle.setProperty('--ph-survey-submit-button-color', effectiveAppearance.submitButtonColor);
77
+ hostStyle.setProperty('--ph-survey-submit-button-text-color', appearance?.submitButtonTextColor || getContrastingTextColor(effectiveAppearance.submitButtonColor));
78
+ hostStyle.setProperty('--ph-survey-rating-bg-color', effectiveAppearance.ratingButtonColor);
79
+ hostStyle.setProperty('--ph-survey-rating-active-bg-color', effectiveAppearance.ratingButtonActiveColor);
80
+ hostStyle.setProperty('--ph-survey-rating-active-text-color', getContrastingTextColor(effectiveAppearance.ratingButtonActiveColor));
81
+ hostStyle.setProperty('--ph-survey-text-primary-color', appearance?.textColor || getContrastingTextColor(effectiveAppearance.backgroundColor));
82
+ hostStyle.setProperty('--ph-survey-text-subtle-color', effectiveAppearance.textSubtleColor);
83
+ hostStyle.setProperty('--ph-widget-color', effectiveAppearance.widgetColor);
84
+ hostStyle.setProperty('--ph-widget-text-color', getContrastingTextColor(effectiveAppearance.widgetColor));
85
+ hostStyle.setProperty('--ph-widget-z-index', effectiveAppearance.zIndex);
86
+ const userInputBg = appearance?.inputBackground || appearance?.inputBackgroundColor;
87
+ let inputBgColor = userInputBg || effectiveAppearance.inputBackground;
88
+ if (!userInputBg && 'white' === effectiveAppearance.backgroundColor) inputBgColor = '#f8f8f8';
89
+ hostStyle.setProperty('--ph-survey-input-background', inputBgColor);
90
+ const inputTextColor = appearance?.inputTextColor || getContrastingTextColor(inputBgColor);
91
+ hostStyle.setProperty('--ph-survey-input-text-color', inputTextColor);
92
+ hostStyle.setProperty('--ph-survey-rating-text-color', inputTextColor);
93
+ hostStyle.setProperty('--ph-survey-scrollbar-thumb-color', effectiveAppearance.scrollbarThumbColor);
94
+ hostStyle.setProperty('--ph-survey-scrollbar-track-color', effectiveAppearance.scrollbarTrackColor);
95
+ hostStyle.setProperty('--ph-survey-outline-color', effectiveAppearance.outlineColor);
96
+ };
97
+ function nameToHex(name) {
98
+ return ({
99
+ aliceblue: '#f0f8ff',
100
+ antiquewhite: '#faebd7',
101
+ aqua: '#00ffff',
102
+ aquamarine: '#7fffd4',
103
+ azure: '#f0ffff',
104
+ beige: '#f5f5dc',
105
+ bisque: '#ffe4c4',
106
+ black: '#000000',
107
+ blanchedalmond: '#ffebcd',
108
+ blue: '#0000ff',
109
+ blueviolet: '#8a2be2',
110
+ brown: '#a52a2a',
111
+ burlywood: '#deb887',
112
+ cadetblue: '#5f9ea0',
113
+ chartreuse: '#7fff00',
114
+ chocolate: '#d2691e',
115
+ coral: '#ff7f50',
116
+ cornflowerblue: '#6495ed',
117
+ cornsilk: '#fff8dc',
118
+ crimson: '#dc143c',
119
+ cyan: '#00ffff',
120
+ darkblue: '#00008b',
121
+ darkcyan: '#008b8b',
122
+ darkgoldenrod: '#b8860b',
123
+ darkgray: '#a9a9a9',
124
+ darkgreen: '#006400',
125
+ darkkhaki: '#bdb76b',
126
+ darkmagenta: '#8b008b',
127
+ darkolivegreen: '#556b2f',
128
+ darkorange: '#ff8c00',
129
+ darkorchid: '#9932cc',
130
+ darkred: '#8b0000',
131
+ darksalmon: '#e9967a',
132
+ darkseagreen: '#8fbc8f',
133
+ darkslateblue: '#483d8b',
134
+ darkslategray: '#2f4f4f',
135
+ darkturquoise: '#00ced1',
136
+ darkviolet: '#9400d3',
137
+ deeppink: '#ff1493',
138
+ deepskyblue: '#00bfff',
139
+ dimgray: '#696969',
140
+ dodgerblue: '#1e90ff',
141
+ firebrick: '#b22222',
142
+ floralwhite: '#fffaf0',
143
+ forestgreen: '#228b22',
144
+ fuchsia: '#ff00ff',
145
+ gainsboro: '#dcdcdc',
146
+ ghostwhite: '#f8f8ff',
147
+ gold: '#ffd700',
148
+ goldenrod: '#daa520',
149
+ gray: '#808080',
150
+ green: '#008000',
151
+ greenyellow: '#adff2f',
152
+ honeydew: '#f0fff0',
153
+ hotpink: '#ff69b4',
154
+ 'indianred ': '#cd5c5c',
155
+ indigo: '#4b0082',
156
+ ivory: '#fffff0',
157
+ khaki: '#f0e68c',
158
+ lavender: '#e6e6fa',
159
+ lavenderblush: '#fff0f5',
160
+ lawngreen: '#7cfc00',
161
+ lemonchiffon: '#fffacd',
162
+ lightblue: '#add8e6',
163
+ lightcoral: '#f08080',
164
+ lightcyan: '#e0ffff',
165
+ lightgoldenrodyellow: '#fafad2',
166
+ lightgrey: '#d3d3d3',
167
+ lightgreen: '#90ee90',
168
+ lightpink: '#ffb6c1',
169
+ lightsalmon: '#ffa07a',
170
+ lightseagreen: '#20b2aa',
171
+ lightskyblue: '#87cefa',
172
+ lightslategray: '#778899',
173
+ lightsteelblue: '#b0c4de',
174
+ lightyellow: '#ffffe0',
175
+ lime: '#00ff00',
176
+ limegreen: '#32cd32',
177
+ linen: '#faf0e6',
178
+ magenta: '#ff00ff',
179
+ maroon: '#800000',
180
+ mediumaquamarine: '#66cdaa',
181
+ mediumblue: '#0000cd',
182
+ mediumorchid: '#ba55d3',
183
+ mediumpurple: '#9370d8',
184
+ mediumseagreen: '#3cb371',
185
+ mediumslateblue: '#7b68ee',
186
+ mediumspringgreen: '#00fa9a',
187
+ mediumturquoise: '#48d1cc',
188
+ mediumvioletred: '#c71585',
189
+ midnightblue: '#191970',
190
+ mintcream: '#f5fffa',
191
+ mistyrose: '#ffe4e1',
192
+ moccasin: '#ffe4b5',
193
+ navajowhite: '#ffdead',
194
+ navy: '#000080',
195
+ oldlace: '#fdf5e6',
196
+ olive: '#808000',
197
+ olivedrab: '#6b8e23',
198
+ orange: '#ffa500',
199
+ orangered: '#ff4500',
200
+ orchid: '#da70d6',
201
+ palegoldenrod: '#eee8aa',
202
+ palegreen: '#98fb98',
203
+ paleturquoise: '#afeeee',
204
+ palevioletred: '#d87093',
205
+ papayawhip: '#ffefd5',
206
+ peachpuff: '#ffdab9',
207
+ peru: '#cd853f',
208
+ pink: '#ffc0cb',
209
+ plum: '#dda0dd',
210
+ powderblue: '#b0e0e6',
211
+ purple: '#800080',
212
+ red: '#ff0000',
213
+ rosybrown: '#bc8f8f',
214
+ royalblue: '#4169e1',
215
+ saddlebrown: '#8b4513',
216
+ salmon: '#fa8072',
217
+ sandybrown: '#f4a460',
218
+ seagreen: '#2e8b57',
219
+ seashell: '#fff5ee',
220
+ sienna: '#a0522d',
221
+ silver: '#c0c0c0',
222
+ skyblue: '#87ceeb',
223
+ slateblue: '#6a5acd',
224
+ slategray: '#708090',
225
+ snow: '#fffafa',
226
+ springgreen: '#00ff7f',
227
+ steelblue: '#4682b4',
228
+ tan: '#d2b48c',
229
+ teal: '#008080',
230
+ thistle: '#d8bfd8',
231
+ tomato: '#ff6347',
232
+ turquoise: '#40e0d0',
233
+ violet: '#ee82ee',
234
+ wheat: '#f5deb3',
235
+ white: '#ffffff',
236
+ whitesmoke: '#f5f5f5',
237
+ yellow: '#ffff00',
238
+ yellowgreen: '#9acd32'
239
+ })[name.toLowerCase()];
240
+ }
241
+ function hex2rgb(c) {
242
+ if (c.startsWith('#')) {
243
+ let hexColor = c.replace(/^#/, '');
244
+ if (/^[0-9A-Fa-f]{3}$/.test(hexColor)) hexColor = hexColor[0] + hexColor[0] + hexColor[1] + hexColor[1] + hexColor[2] + hexColor[2];
245
+ if (!/^[0-9A-Fa-f]{6}$/.test(hexColor)) return 'rgb(255, 255, 255)';
246
+ const r = parseInt(hexColor.slice(0, 2), 16);
247
+ const g = parseInt(hexColor.slice(2, 4), 16);
248
+ const b = parseInt(hexColor.slice(4, 6), 16);
249
+ return `rgb(${r},${g},${b})`;
250
+ }
251
+ return 'rgb(255, 255, 255)';
252
+ }
253
+ function hexToRgba(hex, opacity) {
254
+ const rgb = hex2rgb(hex);
255
+ const match = rgb.match(/^rgb\((\d+),(\d+),(\d+)\)$/);
256
+ if (!match) return hex;
257
+ return `rgba(${match[1]}, ${match[2]}, ${match[3]}, ${opacity})`;
258
+ }
259
+ function getContrastingTextColor(color = defaultSurveyAppearance.backgroundColor) {
260
+ let rgb;
261
+ if ('#' === color[0]) rgb = hex2rgb(color);
262
+ if (color.startsWith('rgb')) rgb = color;
263
+ const nameColorToHex = nameToHex(color);
264
+ if (nameColorToHex) rgb = hex2rgb(nameColorToHex);
265
+ if (!rgb) return BLACK_TEXT_COLOR;
266
+ const colorMatch = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/);
267
+ if (colorMatch) {
268
+ const r = parseInt(colorMatch[1]);
269
+ const g = parseInt(colorMatch[2]);
270
+ const b = parseInt(colorMatch[3]);
271
+ const hsp = Math.sqrt(r * r * 0.299 + g * g * 0.587 + b * b * 0.114);
272
+ return hsp > 127.5 ? BLACK_TEXT_COLOR : 'white';
273
+ }
274
+ return BLACK_TEXT_COLOR;
275
+ }
276
+ function getSurveyStylesheet(posthog) {
277
+ const stylesheet = prepareStylesheet(document, 'string' == typeof surveyStyles ? surveyStyles : '', posthog?.config.prepareStylesheet);
278
+ stylesheet?.setAttribute('data-ph-survey-style', 'true');
279
+ return stylesheet;
280
+ }
281
+ const retrieveSurveyShadow = (survey, posthog, element)=>{
282
+ const widgetClassName = getSurveyContainerClass(survey);
283
+ const existingDiv = document.querySelector(`.${widgetClassName}`);
284
+ if (existingDiv && existingDiv.shadowRoot) return {
285
+ shadow: existingDiv.shadowRoot,
286
+ isNewlyCreated: false
287
+ };
288
+ const div = document.createElement('div');
289
+ addSurveyCSSVariablesToElement(div, survey.type, survey.appearance);
290
+ div.className = widgetClassName;
291
+ const shadow = div.attachShadow({
292
+ mode: 'open'
293
+ });
294
+ const stylesheet = getSurveyStylesheet(posthog);
295
+ if (stylesheet) {
296
+ const existingStylesheet = shadow.querySelector('style');
297
+ if (existingStylesheet) shadow.removeChild(existingStylesheet);
298
+ shadow.appendChild(stylesheet);
299
+ }
300
+ (element ? element : document.body).appendChild(div);
301
+ return {
302
+ shadow,
303
+ isNewlyCreated: true
304
+ };
305
+ };
306
+ const setSurveySeen = (survey)=>{
307
+ try {
308
+ const key = getSurveySeenKey(survey);
309
+ if (!localStorage.getItem(key)) localStorage.setItem(key, 'true');
310
+ } catch (error) {
311
+ SURVEY_LOGGER.error('Failed to persist survey seen state', error);
312
+ }
313
+ };
314
+ const sendSurveyEvent = ({ responses, survey, surveySubmissionId, posthog, isSurveyCompleted, properties, surveyLanguage, questionSnapshots })=>{
315
+ if (!posthog) return void SURVEY_LOGGER.error('[survey sent] event not captured, PostHog instance not found.');
316
+ if (!posthog.client?.canCapture) return;
317
+ setSurveySeen(survey);
318
+ if (isSurveyCompleted) clearInProgressSurveyState(survey);
319
+ posthog.client?.capture(SurveyEventName.SENT, {
320
+ [SurveyEventProperties.SURVEY_NAME]: survey.name,
321
+ [SurveyEventProperties.SURVEY_ID]: survey.id,
322
+ [SurveyEventProperties.SURVEY_ITERATION]: survey.current_iteration,
323
+ [SurveyEventProperties.SURVEY_ITERATION_START_DATE]: survey.current_iteration_start_date,
324
+ sessionRecordingUrl: getSurveyReplayUrl(posthog),
325
+ ...buildSurveyResponseEventProperties({
326
+ event: 'sent',
327
+ survey,
328
+ responses,
329
+ submissionId: surveySubmissionId,
330
+ completed: isSurveyCompleted,
331
+ ...!isUndefined(surveyLanguage) && {
332
+ surveyLanguage
333
+ },
334
+ ...!isUndefined(questionSnapshots) && {
335
+ questionSnapshots
336
+ }
337
+ }),
338
+ ...properties,
339
+ $set: {
340
+ [getSurveyInteractionProperty(survey, 'responded')]: true
341
+ }
342
+ });
343
+ if (isSurveyCompleted) {
344
+ window.dispatchEvent(new CustomEvent('PHSurveySent', {
345
+ detail: {
346
+ surveyId: survey.id
347
+ }
348
+ }));
349
+ posthog.client?.getExtension(FeatureFlagsCommonExtension)?.reloadFeatureFlags();
350
+ }
351
+ };
352
+ const _buildSurveyEventProperties = (event, survey, inProgressSurvey, posthog)=>({
353
+ [SurveyEventProperties.SURVEY_NAME]: survey.name,
354
+ [SurveyEventProperties.SURVEY_ID]: survey.id,
355
+ [SurveyEventProperties.SURVEY_ITERATION]: survey.current_iteration,
356
+ [SurveyEventProperties.SURVEY_ITERATION_START_DATE]: survey.current_iteration_start_date,
357
+ sessionRecordingUrl: getSurveyReplayUrl(posthog),
358
+ ...buildSurveyResponseEventProperties({
359
+ event,
360
+ survey,
361
+ ...!isUndefined(inProgressSurvey?.responses) && {
362
+ responses: inProgressSurvey.responses
363
+ },
364
+ ...!isUndefined(inProgressSurvey?.surveySubmissionId) && {
365
+ submissionId: inProgressSurvey.surveySubmissionId
366
+ },
367
+ ...!isUndefined(inProgressSurvey?.surveyLanguage) && {
368
+ surveyLanguage: inProgressSurvey.surveyLanguage
369
+ },
370
+ ...!isUndefined(inProgressSurvey?.questionSnapshots) && {
371
+ questionSnapshots: inProgressSurvey.questionSnapshots
372
+ }
373
+ })
374
+ });
375
+ const dismissedSurveyEvent = (survey, posthog, readOnly, surveyLanguage)=>{
376
+ if (!posthog) return void SURVEY_LOGGER.error('[survey dismissed] event not captured, PostHog instance not found.');
377
+ if (readOnly) return;
378
+ const inProgressSurvey = getInProgressSurveyState(survey);
379
+ const effectiveLanguage = inProgressSurvey ? inProgressSurvey.surveyLanguage : surveyLanguage;
380
+ const properties = {
381
+ ..._buildSurveyEventProperties('dismissed', survey, inProgressSurvey, posthog),
382
+ ...effectiveLanguage && {
383
+ [SurveyEventProperties.SURVEY_LANGUAGE]: effectiveLanguage
384
+ },
385
+ $set: {
386
+ [getSurveyInteractionProperty(survey, 'dismissed')]: true
387
+ }
388
+ };
389
+ clearInProgressSurveyState(survey);
390
+ setSurveySeen(survey);
391
+ posthog.client?.capture(SurveyEventName.DISMISSED, properties);
392
+ window.dispatchEvent(new CustomEvent('PHSurveyClosed', {
393
+ detail: {
394
+ surveyId: survey.id
395
+ }
396
+ }));
397
+ };
398
+ const sendSurveyAbandonedEvent = (survey, posthog)=>{
399
+ if (!posthog) return void SURVEY_LOGGER.error('[survey abandoned] event not captured, PostHog instance not found.');
400
+ if (!posthog.client?.canCapture) return;
401
+ const abandonedKey = getSurveyAbandonedKey(survey);
402
+ try {
403
+ if ('true' === localStorage.getItem(abandonedKey)) return;
404
+ } catch {
405
+ return;
406
+ }
407
+ const inProgressSurvey = getInProgressSurveyState(survey);
408
+ if (!inProgressSurvey) return;
409
+ try {
410
+ localStorage.setItem(abandonedKey, 'true');
411
+ } catch {}
412
+ posthog.client?.capture(SurveyEventName.ABANDONED, _buildSurveyEventProperties('abandoned', survey, inProgressSurvey, posthog), {
413
+ delivery: 'unload'
414
+ });
415
+ };
416
+ const reverseIfUnshuffled = (unshuffled, shuffled)=>{
417
+ if (unshuffled.length === shuffled.length && unshuffled.every((val, index)=>val === shuffled[index])) return shuffled.reverse();
418
+ return shuffled;
419
+ };
420
+ const hasBranching = (survey)=>survey.questions.some((question)=>!!question.branching?.type);
421
+ const restorePersistedOrder = (survey, questionOrder)=>{
422
+ if (!questionOrder || questionOrder.length !== survey.questions.length) return null;
423
+ const questionsById = new Map(survey.questions.map((question)=>[
424
+ question.id,
425
+ question
426
+ ]));
427
+ const restored = [];
428
+ for (const id of questionOrder){
429
+ const question = questionsById.get(id);
430
+ if (!question) return null;
431
+ restored.push(question);
432
+ }
433
+ return restored;
434
+ };
435
+ const getQuestionOrder = (questions)=>{
436
+ const ids = questions.map((question)=>question.id);
437
+ return ids.every((id)=>!!id) ? ids : void 0;
438
+ };
439
+ const getDisplayOrderQuestions = (survey, inProgressState)=>{
440
+ if (hasBranching(survey)) return survey.questions;
441
+ if (inProgressState) return restorePersistedOrder(survey, inProgressState.questionOrder) ?? survey.questions;
442
+ if (!survey.appearance || !survey.appearance.shuffleQuestions || survey.enable_partial_responses) return survey.questions;
443
+ return reverseIfUnshuffled(survey.questions, shuffle(survey.questions));
444
+ };
445
+ const canActivateRepeatedly = (survey)=>canSurveyActivateRepeatedly(survey) || isSurveyInProgress(survey);
446
+ const getSurveySeen = (survey)=>{
447
+ let surveySeen = null;
448
+ try {
449
+ surveySeen = localStorage.getItem(getSurveySeenKey(survey));
450
+ } catch (error) {
451
+ SURVEY_LOGGER.error('localStorage error: ' + error);
452
+ }
453
+ if (surveySeen) return !canActivateRepeatedly(survey);
454
+ return false;
455
+ };
456
+ const LAST_SEEN_SURVEY_DATE_KEY = 'lastSeenSurveyDate';
457
+ const hasWaitPeriodPassed = (waitPeriodInDays)=>{
458
+ let lastSeenSurveyDate = null;
459
+ try {
460
+ lastSeenSurveyDate = localStorage.getItem(LAST_SEEN_SURVEY_DATE_KEY);
461
+ } catch (error) {
462
+ SURVEY_LOGGER.error('localStorage error: ' + error);
463
+ }
464
+ return hasPeriodPassed(waitPeriodInDays, lastSeenSurveyDate);
465
+ };
466
+ const SurveyContext = createContext({
467
+ isPreviewMode: false,
468
+ previewPageIndex: 0,
469
+ onPopupSurveyDismissed: ()=>{},
470
+ isPopup: true,
471
+ onPreviewSubmit: ()=>{},
472
+ onPreviewBack: ()=>{},
473
+ surveySubmissionId: '',
474
+ properties: void 0,
475
+ surveyLanguage: null
476
+ });
477
+ const useSurveyContext = ()=>useContext(SurveyContext);
478
+ const renderChildrenAsTextOrHtml = ({ component, children, renderAsHtml, style })=>renderAsHtml ? cloneElement(component, {
479
+ dangerouslySetInnerHTML: {
480
+ __html: children
481
+ },
482
+ style
483
+ }) : cloneElement(component, {
484
+ children,
485
+ style
486
+ });
487
+ function defaultMatchType(matchType) {
488
+ return matchType ?? 'icontains';
489
+ }
490
+ function doesSurveyUrlMatch(survey, posthog) {
491
+ if (!survey.conditions?.url) return true;
492
+ const href = getTargetingUrl(posthog) ?? ("u" > typeof window ? window.location.href : void 0);
493
+ if (!href) return false;
494
+ const targets = [
495
+ survey.conditions.url
496
+ ];
497
+ const matchType = defaultMatchType(survey.conditions?.urlMatchType);
498
+ return propertyComparisons[matchType](targets, [
499
+ href
500
+ ]);
501
+ }
502
+ function doesSurveyDeviceTypesMatch(survey) {
503
+ return doesDeviceTypeMatch(survey.conditions?.deviceTypes, survey.conditions?.deviceTypesMatchType);
504
+ }
505
+ function doesSurveyMatchSelector(survey) {
506
+ if (!survey.conditions?.selector) return true;
507
+ return !!document?.querySelector(survey.conditions.selector);
508
+ }
509
+ const getInProgressSurveyStateKey = (survey)=>getSurveyStorageKey(SURVEY_IN_PROGRESS_PREFIX, survey);
510
+ const inMemoryInProgressSurveyState = {};
511
+ const clearAllInMemoryInProgressSurveyState = ()=>{
512
+ for (const key of Object.keys(inMemoryInProgressSurveyState))delete inMemoryInProgressSurveyState[key];
513
+ };
514
+ const setInProgressSurveyState = (survey, state)=>{
515
+ const key = getInProgressSurveyStateKey(survey);
516
+ try {
517
+ localStorage.setItem(key, JSON.stringify(state));
518
+ delete inMemoryInProgressSurveyState[key];
519
+ } catch (e) {
520
+ SURVEY_LOGGER.error('Error setting in-progress survey state in localStorage', e);
521
+ inMemoryInProgressSurveyState[key] = state;
522
+ }
523
+ };
524
+ const getInProgressSurveyState = (survey)=>{
525
+ const key = getInProgressSurveyStateKey(survey);
526
+ const inMemoryState = inMemoryInProgressSurveyState[key];
527
+ if (inMemoryState) return inMemoryState;
528
+ try {
529
+ const stateString = localStorage.getItem(key);
530
+ if (stateString) return JSON.parse(stateString);
531
+ } catch (e) {
532
+ SURVEY_LOGGER.error('Error getting in-progress survey state from localStorage', e);
533
+ }
534
+ return null;
535
+ };
536
+ const isSurveyInProgress = (survey)=>{
537
+ const state = getInProgressSurveyState(survey);
538
+ return !isNullish(state?.surveySubmissionId);
539
+ };
540
+ const clearInProgressSurveyState = (survey)=>{
541
+ const key = getInProgressSurveyStateKey(survey);
542
+ delete inMemoryInProgressSurveyState[key];
543
+ try {
544
+ localStorage.removeItem(key);
545
+ } catch (e) {
546
+ SURVEY_LOGGER.error('Error clearing in-progress survey state from localStorage', e);
547
+ }
548
+ };
549
+ function getSurveyContainerClass(survey, asSelector = false) {
550
+ const className = `PostHogSurvey-${survey.id}`;
551
+ return asSelector ? `.${className}` : className;
552
+ }
553
+ function getPopoverPosition(type, position = SurveyPosition.Right, surveyWidgetType) {
554
+ if (type === SurveyType.ExternalSurvey) return {};
555
+ switch(position){
556
+ case SurveyPosition.TopLeft:
557
+ return {
558
+ top: '0',
559
+ left: '0',
560
+ transform: 'translate(30px, 30px)'
561
+ };
562
+ case SurveyPosition.TopRight:
563
+ return {
564
+ top: '0',
565
+ right: '0',
566
+ transform: 'translate(-30px, 30px)'
567
+ };
568
+ case SurveyPosition.TopCenter:
569
+ return {
570
+ top: '0',
571
+ left: '50%',
572
+ transform: 'translate(-50%, 30px)'
573
+ };
574
+ case SurveyPosition.MiddleLeft:
575
+ return {
576
+ top: '50%',
577
+ left: '0',
578
+ transform: 'translate(30px, -50%)'
579
+ };
580
+ case SurveyPosition.MiddleRight:
581
+ return {
582
+ top: '50%',
583
+ right: '0',
584
+ transform: 'translate(-30px, -50%)'
585
+ };
586
+ case SurveyPosition.MiddleCenter:
587
+ return {
588
+ top: '50%',
589
+ left: '50%',
590
+ transform: 'translate(-50%, -50%)'
591
+ };
592
+ case SurveyPosition.Left:
593
+ return {
594
+ left: '30px'
595
+ };
596
+ case SurveyPosition.Center:
597
+ return {
598
+ left: '50%',
599
+ transform: 'translateX(-50%)'
600
+ };
601
+ default:
602
+ case SurveyPosition.Right:
603
+ return {
604
+ right: type === SurveyType.Widget && surveyWidgetType === SurveyWidgetType.Tab ? '60px' : '30px'
605
+ };
606
+ }
607
+ }
608
+ export { SurveyContext, addSurveyCSSVariablesToElement, canActivateRepeatedly, clearAllInMemoryInProgressSurveyState, clearInProgressSurveyState, defaultSurveyAppearance, dismissedSurveyEvent, doesSurveyDeviceTypesMatch, doesSurveyMatchSelector, doesSurveyUrlMatch, getContrastingTextColor, getDisplayOrderChoices, getDisplayOrderQuestions, getFontFamily, getInProgressSurveyState, getPopoverPosition, getQuestionOrder, getSurveyContainerClass, getSurveyResponseKey, getSurveySeen, getSurveyStylesheet, hasWaitPeriodPassed, hex2rgb, hexToRgba, isSurveyInProgress, renderChildrenAsTextOrHtml, retrieveSurveyShadow, sendSurveyAbandonedEvent, sendSurveyEvent, setInProgressSurveyState, shuffle, useSurveyContext };
@@ -0,0 +1,60 @@
1
+ import type { Properties } from '@posthog/types';
2
+ import type { Survey, SurveyCallback, DisplaySurveyPopoverOptions } from './types/surveys';
3
+ export declare const SURVEYS = "$surveys";
4
+ export declare const SURVEYS_LOADED_AT = "$surveys_loaded_at";
5
+ export declare const SURVEYS_CACHE_TTL_MS: number;
6
+ export declare const SURVEYS_REFRESH_BACKOFF_MS: number;
7
+ export declare const LOAD_EXT_NOT_FOUND = "PostHog loadExternalDependency extension not found.";
8
+ export interface SurveysConfig {
9
+ disableSurveys: boolean;
10
+ cookielessMode: boolean;
11
+ advancedEnableSurveys: boolean;
12
+ requestTimeoutMs: number;
13
+ prefillFromUrl?: boolean | undefined;
14
+ automaticDisplay?: boolean | undefined;
15
+ featureFlagEvaluation?: boolean | undefined;
16
+ overrideLanguage?: string | null | undefined;
17
+ uiHost?: string | undefined;
18
+ get_current_url?: ((url: string) => string) | undefined;
19
+ prepareStylesheet?: ((stylesheet: HTMLStyleElement) => HTMLStyleElement | null) | undefined;
20
+ }
21
+ /** Rendering and eligibility operations used by the surveys lifecycle. */
22
+ export interface SurveysManager {
23
+ clearInMemoryInProgressSurveyState?(): void;
24
+ getActiveMatchingSurveys(callback: SurveyCallback, forceReload?: boolean): void;
25
+ checkSurveyEligibility(survey: Survey): {
26
+ eligible: boolean;
27
+ reason?: string | undefined;
28
+ };
29
+ checkSurveyRenderability(survey: Survey): {
30
+ eligible: boolean;
31
+ reason?: string | undefined;
32
+ };
33
+ renderSurvey(survey: Survey, element: Element, properties?: Properties): void;
34
+ handlePopoverSurvey(survey: Survey, options?: DisplaySurveyPopoverOptions): void;
35
+ cancelSurvey(surveyId: string): void;
36
+ handlePageUnload?(): void;
37
+ startAutomaticDisplay?(): void;
38
+ dispose?(): void;
39
+ }
40
+ /** Trigger state owned by the host's event/action receiver. */
41
+ export interface SurveysEventReceiver {
42
+ register(surveys: Survey[]): void;
43
+ replace(surveys: Survey[]): void;
44
+ reset(): void;
45
+ dispose(): void;
46
+ getSurveys(): string[];
47
+ getActivationTimestamp(surveyId: string): number | undefined;
48
+ }
49
+ export interface SurveysExtensionHost {
50
+ generateSurveys?: ((isSurveysEnabled: boolean) => SurveysManager) | undefined;
51
+ loadExternalDependency?: ((callback: (error?: unknown) => void) => void) | undefined;
52
+ }
53
+ export interface SurveysConfigSource {
54
+ get(): Readonly<SurveysConfig>;
55
+ getExtensions(): SurveysExtensionHost | undefined;
56
+ onMatchingConditionsChanged?(callback: () => void): () => void;
57
+ }
58
+ export declare const SURVEYS_ACTIVATED = "$surveys_activated";
59
+ export declare const SURVEYS_ACTIVATED_SESSION = "$surveys_activated_session";
60
+ export declare const SURVEYS_ACTIVATED_TIMESTAMPS = "$surveys_activated_timestamps";