@teleporthq/teleport-plugin-next-workflows 0.43.70 → 0.43.71
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/__tests__/chapter-reached-trigger.test.ts +141 -0
- package/__tests__/element-video-control.test.ts +102 -0
- package/__tests__/element-visible-settings.test.ts +128 -0
- package/__tests__/element-visible-trigger-scoping.test.ts +22 -0
- package/__tests__/scrolled-past-trigger.test.ts +189 -0
- package/dist/cjs/api-route-generator.d.ts.map +1 -1
- package/dist/cjs/api-route-generator.js +1 -0
- package/dist/cjs/api-route-generator.js.map +1 -1
- package/dist/cjs/element-visible-trigger.d.ts +32 -0
- package/dist/cjs/element-visible-trigger.d.ts.map +1 -0
- package/dist/cjs/element-visible-trigger.js +42 -0
- package/dist/cjs/element-visible-trigger.js.map +1 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/nodes/element/element-video-control.d.ts +3 -0
- package/dist/cjs/nodes/element/element-video-control.d.ts.map +1 -0
- package/dist/cjs/nodes/element/element-video-control.js +94 -0
- package/dist/cjs/nodes/element/element-video-control.js.map +1 -0
- package/dist/cjs/nodes/index.d.ts.map +1 -1
- package/dist/cjs/nodes/index.js +2 -1
- package/dist/cjs/nodes/index.js.map +1 -1
- package/dist/cjs/scroll-points.d.ts +14 -0
- package/dist/cjs/scroll-points.d.ts.map +1 -0
- package/dist/cjs/scroll-points.js +23 -0
- package/dist/cjs/scroll-points.js.map +1 -0
- package/dist/cjs/trigger-generator.d.ts.map +1 -1
- package/dist/cjs/trigger-generator.js +3 -15
- package/dist/cjs/trigger-generator.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/workflow-component-plugin.d.ts.map +1 -1
- package/dist/cjs/workflow-component-plugin.js +121 -13
- package/dist/cjs/workflow-component-plugin.js.map +1 -1
- package/dist/esm/api-route-generator.d.ts.map +1 -1
- package/dist/esm/api-route-generator.js +1 -0
- package/dist/esm/api-route-generator.js.map +1 -1
- package/dist/esm/element-visible-trigger.d.ts +32 -0
- package/dist/esm/element-visible-trigger.d.ts.map +1 -0
- package/dist/esm/element-visible-trigger.js +37 -0
- package/dist/esm/element-visible-trigger.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/nodes/element/element-video-control.d.ts +3 -0
- package/dist/esm/nodes/element/element-video-control.d.ts.map +1 -0
- package/dist/esm/nodes/element/element-video-control.js +91 -0
- package/dist/esm/nodes/element/element-video-control.js.map +1 -0
- package/dist/esm/nodes/index.d.ts.map +1 -1
- package/dist/esm/nodes/index.js +2 -1
- package/dist/esm/nodes/index.js.map +1 -1
- package/dist/esm/scroll-points.d.ts +14 -0
- package/dist/esm/scroll-points.d.ts.map +1 -0
- package/dist/esm/scroll-points.js +19 -0
- package/dist/esm/scroll-points.js.map +1 -0
- package/dist/esm/trigger-generator.d.ts.map +1 -1
- package/dist/esm/trigger-generator.js +3 -15
- package/dist/esm/trigger-generator.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/workflow-component-plugin.d.ts.map +1 -1
- package/dist/esm/workflow-component-plugin.js +121 -13
- package/dist/esm/workflow-component-plugin.js.map +1 -1
- package/package.json +4 -4
- package/src/api-route-generator.ts +1 -0
- package/src/element-visible-trigger.ts +63 -0
- package/src/index.ts +1 -0
- package/src/nodes/element/element-video-control.ts +95 -0
- package/src/nodes/index.ts +2 -0
- package/src/scroll-points.ts +21 -0
- package/src/trigger-generator.ts +7 -16
- package/src/workflow-component-plugin.ts +135 -13
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { NodeHandlerGenerator, handlerToString } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* "Control a Video": play / pause / play-or-pause / restart / mute / unmute
|
|
5
|
+
* the bound Video element (or the first <video> inside the bound element).
|
|
6
|
+
* A play the browser blocks for sound (autoplay policy: no user gesture yet,
|
|
7
|
+
* e.g. a chapter-reached trigger) is retried muted, so the video still
|
|
8
|
+
* starts, and the result says so. Client-only: it needs the DOM.
|
|
9
|
+
*/
|
|
10
|
+
async function element_video_control(config: any, context: Record<string, unknown>) {
|
|
11
|
+
const action = typeof config.action === 'string' ? config.action : 'play'
|
|
12
|
+
const host =
|
|
13
|
+
document.getElementById(config.nodeId) ||
|
|
14
|
+
(config.elementHtmlId ? document.getElementById(config.elementHtmlId) : null)
|
|
15
|
+
const video = host
|
|
16
|
+
? host.tagName === 'VIDEO'
|
|
17
|
+
? (host as HTMLVideoElement)
|
|
18
|
+
: (host.querySelector('video') as HTMLVideoElement | null)
|
|
19
|
+
: null
|
|
20
|
+
if (!video) {
|
|
21
|
+
return { success: false, action, reason: 'video-not-found' }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const state = (extra?: Record<string, unknown>) => {
|
|
25
|
+
const result: Record<string, unknown> = {
|
|
26
|
+
success: true,
|
|
27
|
+
action,
|
|
28
|
+
playing: !video.paused && !video.ended,
|
|
29
|
+
currentTime: video.currentTime,
|
|
30
|
+
muted: video.muted,
|
|
31
|
+
}
|
|
32
|
+
if (extra) {
|
|
33
|
+
Object.keys(extra).forEach((key) => {
|
|
34
|
+
result[key] = extra[key]
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
return result
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const play = async () => {
|
|
41
|
+
try {
|
|
42
|
+
await video.play()
|
|
43
|
+
return null
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (video.muted) {
|
|
46
|
+
return 'blocked'
|
|
47
|
+
}
|
|
48
|
+
video.muted = true
|
|
49
|
+
try {
|
|
50
|
+
await video.play()
|
|
51
|
+
return 'muted'
|
|
52
|
+
} catch (retryError) {
|
|
53
|
+
return 'blocked'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const playAndReport = async () => {
|
|
59
|
+
const outcome = await play()
|
|
60
|
+
if (outcome === 'blocked') {
|
|
61
|
+
return state({ success: false, reason: 'autoplay-blocked' })
|
|
62
|
+
}
|
|
63
|
+
return outcome === 'muted' ? state({ mutedFallback: true }) : state()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (action === 'pause') {
|
|
67
|
+
video.pause()
|
|
68
|
+
return state()
|
|
69
|
+
}
|
|
70
|
+
if (action === 'mute') {
|
|
71
|
+
video.muted = true
|
|
72
|
+
return state()
|
|
73
|
+
}
|
|
74
|
+
if (action === 'unmute') {
|
|
75
|
+
video.muted = false
|
|
76
|
+
return state()
|
|
77
|
+
}
|
|
78
|
+
if (action === 'restart') {
|
|
79
|
+
video.currentTime = 0
|
|
80
|
+
return playAndReport()
|
|
81
|
+
}
|
|
82
|
+
if (action === 'toggle' && !video.paused && !video.ended) {
|
|
83
|
+
video.pause()
|
|
84
|
+
return state()
|
|
85
|
+
}
|
|
86
|
+
return playAndReport()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const elementVideoControl: NodeHandlerGenerator = {
|
|
90
|
+
nodeType: 'element-video-control',
|
|
91
|
+
executionEnv: 'client',
|
|
92
|
+
generateHandler(): string {
|
|
93
|
+
return handlerToString(element_video_control)
|
|
94
|
+
},
|
|
95
|
+
}
|
package/src/nodes/index.ts
CHANGED
|
@@ -69,6 +69,7 @@ import { elementGetInputValue } from './element/element-get-input-value'
|
|
|
69
69
|
import { elementHide } from './element/element-hide'
|
|
70
70
|
import { elementRemoveClass } from './element/element-remove-class'
|
|
71
71
|
import { elementScrollTo } from './element/element-scroll-to'
|
|
72
|
+
import { elementVideoControl } from './element/element-video-control'
|
|
72
73
|
import { elementSetAttribute } from './element/element-set-attribute'
|
|
73
74
|
import { elementSetText } from './element/element-set-text'
|
|
74
75
|
import { elementShow } from './element/element-show'
|
|
@@ -241,6 +242,7 @@ export const nodeRegistry: Record<string, NodeHandlerGenerator> = {
|
|
|
241
242
|
'element-hide': elementHide,
|
|
242
243
|
'element-remove-class': elementRemoveClass,
|
|
243
244
|
'element-scroll-to': elementScrollTo,
|
|
245
|
+
'element-video-control': elementVideoControl,
|
|
244
246
|
'element-set-attribute': elementSetAttribute,
|
|
245
247
|
'element-set-text': elementSetText,
|
|
246
248
|
'element-show': elementShow,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plain choices of the "scrolled past a point" triggers and the progress
|
|
3
|
+
* (0..1) each stands for. The published Scroll Scene runtime carries the same
|
|
4
|
+
* list (scenePointList in teleport-project-generator-next, pinned by a test);
|
|
5
|
+
* the page trigger reads it here directly.
|
|
6
|
+
*/
|
|
7
|
+
export const SCROLL_POINT_PROGRESS: Readonly<Record<string, number>> = {
|
|
8
|
+
quarter: 0.25,
|
|
9
|
+
half: 0.5,
|
|
10
|
+
'three-quarters': 0.75,
|
|
11
|
+
end: 1,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_SCROLL_POINT = 'half'
|
|
15
|
+
|
|
16
|
+
export const resolveScrollPoint = (value: unknown): { key: string; at: number; rank: number } => {
|
|
17
|
+
const keys = Object.keys(SCROLL_POINT_PROGRESS)
|
|
18
|
+
const key =
|
|
19
|
+
typeof value === 'string' && value in SCROLL_POINT_PROGRESS ? value : DEFAULT_SCROLL_POINT
|
|
20
|
+
return { key, at: SCROLL_POINT_PROGRESS[key], rank: keys.indexOf(key) + 1 }
|
|
21
|
+
}
|
package/src/trigger-generator.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { UIDLWorkflow } from '@teleporthq/teleport-types'
|
|
2
|
+
import { elementVisibleObserverCode } from './element-visible-trigger'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* For DOM events whose `target` is a form control (select, input, textarea,
|
|
@@ -171,27 +172,17 @@ const generateElementVisibleTrigger = (
|
|
|
171
172
|
workflowId: string
|
|
172
173
|
): string => {
|
|
173
174
|
const elementId = (config.elementHtmlId || config.nodeId) as string
|
|
174
|
-
const threshold = (config.threshold as number) || 0
|
|
175
|
-
const once = config.once as boolean
|
|
176
|
-
|
|
177
175
|
const safeId = workflowId.replace(/[^a-zA-Z0-9]/g, '_')
|
|
176
|
+
const observer = elementVisibleObserverCode(
|
|
177
|
+
`obs_${safeId}`,
|
|
178
|
+
config,
|
|
179
|
+
`const triggerContext = { elementId: '${elementId}', timestamp: Date.now(), intersectionRatio: entry.intersectionRatio };\n ${executionCall};`
|
|
180
|
+
)
|
|
178
181
|
return `
|
|
179
182
|
// Workflow trigger: element visible (${workflowId})
|
|
180
183
|
const el_${safeId} = document.getElementById('${elementId}');
|
|
181
184
|
if (el_${safeId}) {
|
|
182
|
-
|
|
183
|
-
entries.forEach(function(entry) {
|
|
184
|
-
if (entry.isIntersecting) {
|
|
185
|
-
const triggerContext = {
|
|
186
|
-
elementId: '${elementId}',
|
|
187
|
-
timestamp: Date.now(),
|
|
188
|
-
intersectionRatio: entry.intersectionRatio
|
|
189
|
-
};
|
|
190
|
-
${executionCall};
|
|
191
|
-
${once ? `obs_${safeId}.disconnect();` : ''}
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
}, { threshold: ${threshold} });
|
|
185
|
+
${observer.trimEnd().replace(/^/gm, ' ')}
|
|
195
186
|
obs_${safeId}.observe(el_${safeId});
|
|
196
187
|
return function() { obs_${safeId}.disconnect(); };
|
|
197
188
|
}`
|
|
@@ -30,7 +30,9 @@ import { getAPIRouteFileName, hasStreamingAINode } from './api-route-generator'
|
|
|
30
30
|
import { REALTIME_TRIGGER_TYPES, REALTIME_NODE_TYPES } from './graph-utils'
|
|
31
31
|
import { neutraliseIsLoggedInGates } from './is-logged-in-gate'
|
|
32
32
|
import { formControlPropertyReads } from './trigger-generator'
|
|
33
|
+
import { elementVisibleObserverCode } from './element-visible-trigger'
|
|
33
34
|
import { restoreControlledSelectValue } from './controlled-select'
|
|
35
|
+
import { resolveScrollPoint } from './scroll-points'
|
|
34
36
|
import { findFormStateKeyForHydration } from './admin-form-hydration'
|
|
35
37
|
|
|
36
38
|
interface WorkflowPluginConfig {
|
|
@@ -96,7 +98,11 @@ const resolveLifecycleTriggerElementId = (
|
|
|
96
98
|
triggerType: string,
|
|
97
99
|
config: Record<string, unknown>
|
|
98
100
|
): string | null => {
|
|
99
|
-
if (
|
|
101
|
+
if (
|
|
102
|
+
triggerType !== 'event-element-visible' &&
|
|
103
|
+
triggerType !== 'event-chapter-reached' &&
|
|
104
|
+
triggerType !== 'event-scene-scrolled-past'
|
|
105
|
+
) {
|
|
100
106
|
return null
|
|
101
107
|
}
|
|
102
108
|
return ((config.elementHtmlId || config.nodeId) as string) || null
|
|
@@ -2198,27 +2204,143 @@ const generateLifecycleTrigger = (wf: UIDLWorkflow, safeId: string): string => {
|
|
|
2198
2204
|
// so the observer was never constructed and the cookie-consent banner
|
|
2199
2205
|
// could never appear on any page (run a15472af: 408 dead lookups).
|
|
2200
2206
|
const elementId = (config.elementHtmlId || config.nodeId) as string
|
|
2201
|
-
const
|
|
2202
|
-
|
|
2207
|
+
const observer = elementVisibleObserverCode(
|
|
2208
|
+
`__obs_${safeId}`,
|
|
2209
|
+
config,
|
|
2210
|
+
`const triggerContext = { elementId: '${elementId}', timestamp: Date.now(), intersectionRatio: entry.intersectionRatio };\n ${execCall};`
|
|
2211
|
+
)
|
|
2203
2212
|
return (
|
|
2204
2213
|
` // Element visible (${wf.name || wf.id})\n` +
|
|
2205
2214
|
` const __visEl_${safeId} = document.getElementById('${elementId}');\n` +
|
|
2206
2215
|
` if (__visEl_${safeId}) {\n` +
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
` if (entry.isIntersecting) {\n` +
|
|
2210
|
-
` const triggerContext = { elementId: '${elementId}', timestamp: Date.now(), intersectionRatio: entry.intersectionRatio };\n` +
|
|
2211
|
-
` ${execCall};\n` +
|
|
2212
|
-
(once ? ` __obs_${safeId}.disconnect();\n` : '') +
|
|
2213
|
-
` }\n` +
|
|
2214
|
-
` });\n` +
|
|
2215
|
-
` }, { threshold: ${threshold} });\n` +
|
|
2216
|
-
` __obs_${safeId}.observe(__visEl_${safeId});\n` +
|
|
2216
|
+
observer.trimEnd().replace(/^/gm, ' ') +
|
|
2217
|
+
`\n __obs_${safeId}.observe(__visEl_${safeId});\n` +
|
|
2217
2218
|
` cleanups.push(function() { __obs_${safeId}.disconnect(); });\n` +
|
|
2218
2219
|
` }`
|
|
2219
2220
|
)
|
|
2220
2221
|
}
|
|
2221
2222
|
|
|
2223
|
+
case 'event-chapter-reached': {
|
|
2224
|
+
// The published TqScrollScene announces the chapter on stage as a
|
|
2225
|
+
// `tq-chapter-reached` event on the CHAPTER element (a direct child of the
|
|
2226
|
+
// scene) and stamps `data-chapter-active` / `data-chapter-count` on it.
|
|
2227
|
+
// The bound element is resolved to its chapter root, so a descendant the
|
|
2228
|
+
// author picked still listens where the event lands; events bubbling up
|
|
2229
|
+
// from a scene nested inside the chapter are ignored; and a listener that
|
|
2230
|
+
// attaches after the announcement reads the stamp instead of waiting for
|
|
2231
|
+
// a change that already happened.
|
|
2232
|
+
const elementId = resolveLifecycleTriggerElementId(trigger.type, config) ?? ''
|
|
2233
|
+
const once = config.once !== false
|
|
2234
|
+
return (
|
|
2235
|
+
` // Chapter reached (${wf.name || wf.id})\n` +
|
|
2236
|
+
` const __chEl_${safeId} = document.getElementById('${elementId}');\n` +
|
|
2237
|
+
` if (__chEl_${safeId}) {\n` +
|
|
2238
|
+
` const __chRoot_${safeId} = __chEl_${safeId}.closest('[data-scene-stage] > *, [data-scene-track] > *') || __chEl_${safeId};\n` +
|
|
2239
|
+
` const __chFire_${safeId} = function(detail) {\n` +
|
|
2240
|
+
` const triggerContext = { elementId: '${elementId}', chapterIndex: detail.chapterIndex, chapterCount: detail.chapterCount, progress: detail.progress, direction: detail.direction, timestamp: Date.now() };\n` +
|
|
2241
|
+
` ${execCall};\n` +
|
|
2242
|
+
` };\n` +
|
|
2243
|
+
` const __chH_${safeId} = function(event) {\n` +
|
|
2244
|
+
` if (event.target !== __chRoot_${safeId}) { return; }\n` +
|
|
2245
|
+
(once
|
|
2246
|
+
? ` __chRoot_${safeId}.removeEventListener('tq-chapter-reached', __chH_${safeId});\n`
|
|
2247
|
+
: '') +
|
|
2248
|
+
` __chFire_${safeId}(event.detail || {});\n` +
|
|
2249
|
+
` };\n` +
|
|
2250
|
+
` const __chStamp_${safeId} = __chRoot_${safeId}.getAttribute('data-chapter-active');\n` +
|
|
2251
|
+
` if (__chStamp_${safeId}) {\n` +
|
|
2252
|
+
` __chFire_${safeId}({ chapterIndex: Number(__chStamp_${safeId}), chapterCount: Number(__chRoot_${safeId}.getAttribute('data-chapter-count')) || undefined });\n` +
|
|
2253
|
+
` }\n` +
|
|
2254
|
+
(once ? ` if (!__chStamp_${safeId}) {\n` : ` {\n`) +
|
|
2255
|
+
` __chRoot_${safeId}.addEventListener('tq-chapter-reached', __chH_${safeId});\n` +
|
|
2256
|
+
` cleanups.push(function() { __chRoot_${safeId}.removeEventListener('tq-chapter-reached', __chH_${safeId}); });\n` +
|
|
2257
|
+
` }\n` +
|
|
2258
|
+
` }`
|
|
2259
|
+
)
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
case 'event-scene-scrolled-past': {
|
|
2263
|
+
// The published TqScrollScene announces every point passed on the way
|
|
2264
|
+
// down as `tq-scene-point-passed` on its track (the element with the
|
|
2265
|
+
// scene's id) and stamps the furthest point passed as `data-scene-point`.
|
|
2266
|
+
// The bound element is resolved to its track; announcements bubbling up
|
|
2267
|
+
// from a scene nested inside it are ignored; a listener that attaches
|
|
2268
|
+
// after the point was passed fires from the stamp instead of waiting.
|
|
2269
|
+
const elementId = resolveLifecycleTriggerElementId(trigger.type, config) ?? ''
|
|
2270
|
+
const point = resolveScrollPoint(config.point)
|
|
2271
|
+
const once = config.once !== false
|
|
2272
|
+
return (
|
|
2273
|
+
` // Scene scrolled past (${wf.name || wf.id})\n` +
|
|
2274
|
+
` const __spEl_${safeId} = document.getElementById('${elementId}');\n` +
|
|
2275
|
+
` if (__spEl_${safeId}) {\n` +
|
|
2276
|
+
` const __spRoot_${safeId} = __spEl_${safeId}.closest('[data-scene-track]') || __spEl_${safeId};\n` +
|
|
2277
|
+
` const __spFire_${safeId} = function(progress) {\n` +
|
|
2278
|
+
` const triggerContext = { elementId: '${elementId}', point: '${point.key}', progress: progress, timestamp: Date.now() };\n` +
|
|
2279
|
+
` ${execCall};\n` +
|
|
2280
|
+
` };\n` +
|
|
2281
|
+
` const __spH_${safeId} = function(event) {\n` +
|
|
2282
|
+
` if (event.target !== __spRoot_${safeId}) { return; }\n` +
|
|
2283
|
+
` const detail = event.detail || {};\n` +
|
|
2284
|
+
` if (detail.point !== '${point.key}') { return; }\n` +
|
|
2285
|
+
(once
|
|
2286
|
+
? ` __spRoot_${safeId}.removeEventListener('tq-scene-point-passed', __spH_${safeId});\n`
|
|
2287
|
+
: '') +
|
|
2288
|
+
` __spFire_${safeId}(detail.progress);\n` +
|
|
2289
|
+
` };\n` +
|
|
2290
|
+
` const __spStamp_${safeId} = ['quarter', 'half', 'three-quarters', 'end'].indexOf(__spRoot_${safeId}.getAttribute('data-scene-point') || '') + 1;\n` +
|
|
2291
|
+
` if (__spStamp_${safeId} >= ${point.rank}) {\n` +
|
|
2292
|
+
` __spFire_${safeId}(${point.at});\n` +
|
|
2293
|
+
` }\n` +
|
|
2294
|
+
(once ? ` if (__spStamp_${safeId} < ${point.rank}) {\n` : ` {\n`) +
|
|
2295
|
+
` __spRoot_${safeId}.addEventListener('tq-scene-point-passed', __spH_${safeId});\n` +
|
|
2296
|
+
` cleanups.push(function() { __spRoot_${safeId}.removeEventListener('tq-scene-point-passed', __spH_${safeId}); });\n` +
|
|
2297
|
+
` }\n` +
|
|
2298
|
+
` }`
|
|
2299
|
+
)
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
case 'event-page-scrolled-past': {
|
|
2303
|
+
// Page progress = scrollY over the scrollable height; a page that cannot
|
|
2304
|
+
// scroll is entirely in view, so every point counts as passed. Checked
|
|
2305
|
+
// once on attach (an anchor link or a restored scroll may open the page
|
|
2306
|
+
// already past the point), then on scroll and resize, one check per
|
|
2307
|
+
// frame. Going back above the point re-arms it; `once` detaches after
|
|
2308
|
+
// the first pass.
|
|
2309
|
+
const point = resolveScrollPoint(config.point)
|
|
2310
|
+
const once = config.once !== false
|
|
2311
|
+
return (
|
|
2312
|
+
` // Page scrolled past (${wf.name || wf.id})\n` +
|
|
2313
|
+
` const __ppProgress_${safeId} = function() {\n` +
|
|
2314
|
+
` const max = document.documentElement.scrollHeight - window.innerHeight;\n` +
|
|
2315
|
+
` return max <= 0 ? 1 : Math.min(1, Math.max(0, window.scrollY / max));\n` +
|
|
2316
|
+
` };\n` +
|
|
2317
|
+
` let __ppPassed_${safeId} = false;\n` +
|
|
2318
|
+
` let __ppTick_${safeId} = false;\n` +
|
|
2319
|
+
` const __ppOnScroll_${safeId} = function() {\n` +
|
|
2320
|
+
` if (__ppTick_${safeId}) { return; }\n` +
|
|
2321
|
+
` __ppTick_${safeId} = true;\n` +
|
|
2322
|
+
` requestAnimationFrame(function() { __ppTick_${safeId} = false; __ppCheck_${safeId}(); });\n` +
|
|
2323
|
+
` };\n` +
|
|
2324
|
+
` const __ppStop_${safeId} = function() {\n` +
|
|
2325
|
+
` window.removeEventListener('scroll', __ppOnScroll_${safeId});\n` +
|
|
2326
|
+
` window.removeEventListener('resize', __ppOnScroll_${safeId});\n` +
|
|
2327
|
+
` };\n` +
|
|
2328
|
+
` const __ppCheck_${safeId} = function() {\n` +
|
|
2329
|
+
` const progress = __ppProgress_${safeId}();\n` +
|
|
2330
|
+
` if (progress < ${point.at} - 0.000001) { __ppPassed_${safeId} = false; return; }\n` +
|
|
2331
|
+
` if (__ppPassed_${safeId}) { return; }\n` +
|
|
2332
|
+
` __ppPassed_${safeId} = true;\n` +
|
|
2333
|
+
(once ? ` __ppStop_${safeId}();\n` : '') +
|
|
2334
|
+
` const triggerContext = { point: '${point.key}', progress: progress, url: window.location.href, timestamp: Date.now() };\n` +
|
|
2335
|
+
` ${execCall};\n` +
|
|
2336
|
+
` };\n` +
|
|
2337
|
+
` window.addEventListener('scroll', __ppOnScroll_${safeId}, { passive: true });\n` +
|
|
2338
|
+
` window.addEventListener('resize', __ppOnScroll_${safeId});\n` +
|
|
2339
|
+
` cleanups.push(__ppStop_${safeId});\n` +
|
|
2340
|
+
` __ppCheck_${safeId}();`
|
|
2341
|
+
)
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2222
2344
|
case 'event-user-logged-in':
|
|
2223
2345
|
case 'event-user-logged-out': {
|
|
2224
2346
|
const eventName =
|