@pushwoosh/websdk-common 0.0.0 → 6.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +30 -5
- package/subscription-widget/SubscriptionWidgetReader.d.ts +23 -0
- package/subscription-widget/SubscriptionWidgetReader.js +307 -0
- package/subscription-widget/constants.d.ts +19 -0
- package/subscription-widget/constants.js +61 -0
- package/subscription-widget/helpers.d.ts +16 -0
- package/subscription-widget/helpers.js +110 -0
- package/subscription-widget/index.d.ts +4 -0
- package/subscription-widget/index.js +3 -0
- package/subscription-widget/types.d.ts +90 -0
- package/subscription-widget/types.js +1 -0
- package/web-popups/WebPopupReader.d.ts +4 -0
- package/web-popups/WebPopupReader.js +1394 -0
- package/web-popups/colorScheme.d.ts +2 -0
- package/web-popups/colorScheme.js +22 -0
- package/web-popups/constants.d.ts +71 -0
- package/web-popups/constants.js +216 -0
- package/web-popups/glyphs.d.ts +8 -0
- package/web-popups/glyphs.js +30 -0
- package/web-popups/helpers.d.ts +84 -0
- package/web-popups/helpers.js +687 -0
- package/web-popups/index.d.ts +9 -0
- package/web-popups/index.js +8 -0
- package/web-popups/layout.d.ts +39 -0
- package/web-popups/layout.js +290 -0
- package/web-popups/palette.d.ts +34 -0
- package/web-popups/palette.js +157 -0
- package/web-popups/templates.d.ts +3 -0
- package/web-popups/templates.js +125 -0
- package/web-popups/timer.d.ts +16 -0
- package/web-popups/timer.js +194 -0
- package/web-popups/types.d.ts +660 -0
- package/web-popups/types.js +10 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** Text styles a slot can use ('icon' = emoji line, 'eyebrow' = small caps kicker). */
|
|
3
|
+
export type WebPopupTextVariant = 'icon' | 'eyebrow' | 'h1' | 'h2' | 'h3' | 'body' | 'caption';
|
|
4
|
+
export type WebPopupTextAlign = 'left' | 'center' | 'right';
|
|
5
|
+
export type WebPopupButtonVariant = 'primary' | 'secondary';
|
|
6
|
+
/**
|
|
7
|
+
* What a button does: open a URL, dismiss the popup, switch to another page
|
|
8
|
+
* (`page` = WebPopupPageParams.key), or ask for push permission.
|
|
9
|
+
*/
|
|
10
|
+
export type WebPopupButtonAction =
|
|
11
|
+
/** `target` absent / anything but '_self' = a new tab (the historical behavior). */
|
|
12
|
+
{
|
|
13
|
+
type: 'href';
|
|
14
|
+
url: string;
|
|
15
|
+
target?: '_blank' | '_self';
|
|
16
|
+
} | {
|
|
17
|
+
type: 'close';
|
|
18
|
+
} | {
|
|
19
|
+
type: 'page';
|
|
20
|
+
page: string;
|
|
21
|
+
}
|
|
22
|
+
/** Ask the browser for notification permission and subscribe the visitor. */
|
|
23
|
+
| {
|
|
24
|
+
type: 'subscribe';
|
|
25
|
+
};
|
|
26
|
+
/** What the host's subscribe handler settled on — the browser's permission after the ask. */
|
|
27
|
+
export type WebPopupSubscribeResult = 'granted' | 'denied' | 'default';
|
|
28
|
+
/**
|
|
29
|
+
* Input kinds an email-subscription-form slot renders — the same set the
|
|
30
|
+
* subscription-form service accepts in a submission ('custom' entries land in
|
|
31
|
+
* the `customFields` map under the field's `tag`).
|
|
32
|
+
*/
|
|
33
|
+
export type WebPopupFormFieldKind = 'email' | 'firstName' | 'lastName' | 'phone' | 'custom';
|
|
34
|
+
export type WebPopupFormField = {
|
|
35
|
+
/** Stable id within the slot ('field-N'), not a display name. */
|
|
36
|
+
key: string;
|
|
37
|
+
kind: WebPopupFormFieldKind;
|
|
38
|
+
/** Caption above the input; empty = no caption. */
|
|
39
|
+
label: string;
|
|
40
|
+
placeholder: string;
|
|
41
|
+
required: boolean;
|
|
42
|
+
/** The tag a 'custom' field submits under; other kinds ignore it. */
|
|
43
|
+
tag?: string;
|
|
44
|
+
};
|
|
45
|
+
/** The consent row of a form slot: a required checkbox with a policy link. */
|
|
46
|
+
export type WebPopupFormPrivacy = {
|
|
47
|
+
checkboxLabel: string;
|
|
48
|
+
/** Policy page URL, rendered through safeUrl like every popup link. */
|
|
49
|
+
url: string;
|
|
50
|
+
linkLabel: string;
|
|
51
|
+
};
|
|
52
|
+
/** What a `timer` slot does once its deadline has passed. */
|
|
53
|
+
export type WebPopupTimerExpireAction = 'keep' | 'hide-popup' | 'page';
|
|
54
|
+
/** A countdown to a fixed deadline; the field contract is the email builder's timer block. */
|
|
55
|
+
export type WebPopupTimerSlot = {
|
|
56
|
+
type: 'timer';
|
|
57
|
+
key: string;
|
|
58
|
+
/** Countdown target as local wall-clock time in `timezone`. */
|
|
59
|
+
endTime: string;
|
|
60
|
+
/** IANA zone name; empty = UTC. */
|
|
61
|
+
timezone: string;
|
|
62
|
+
/** Unit-label language (ISO 639-1); empty/unknown = 'en'. */
|
|
63
|
+
language: string;
|
|
64
|
+
showLabels: boolean;
|
|
65
|
+
/** Behavior past the deadline; 'hide-popup' is the host's call, see webPopupExpired. */
|
|
66
|
+
onExpire: WebPopupTimerExpireAction;
|
|
67
|
+
/** Target page key for onExpire 'page'; normalization drops a pageless 'page' to 'keep'. */
|
|
68
|
+
expirePage?: string;
|
|
69
|
+
};
|
|
70
|
+
/** How a `choice` slot renders its options. */
|
|
71
|
+
export type WebPopupChoiceControl = 'checkbox' | 'chips';
|
|
72
|
+
export type WebPopupChoiceOption = {
|
|
73
|
+
/** Stable value within the slot — and what is written to the tag when `tagValue` is absent. */
|
|
74
|
+
value: string;
|
|
75
|
+
label: string;
|
|
76
|
+
/** Tag value this option submits, when it differs from `value`. */
|
|
77
|
+
tagValue?: string;
|
|
78
|
+
};
|
|
79
|
+
/** A topic picker written to a LIST device tag; no callback or no `tagName` = inert. */
|
|
80
|
+
export type WebPopupChoiceSlot = {
|
|
81
|
+
type: 'choice';
|
|
82
|
+
key: string;
|
|
83
|
+
control: WebPopupChoiceControl;
|
|
84
|
+
/** The device tag the choice is written to; '' = the slot is inert. */
|
|
85
|
+
tagName: string;
|
|
86
|
+
options: WebPopupChoiceOption[];
|
|
87
|
+
/** Selection bounds against the option count; 0 = unbounded. */
|
|
88
|
+
min: number;
|
|
89
|
+
max: number;
|
|
90
|
+
submitLabel: string;
|
|
91
|
+
submitVariant: WebPopupButtonVariant;
|
|
92
|
+
/** Replaces the slot after a successful submit. */
|
|
93
|
+
successMessage: string;
|
|
94
|
+
};
|
|
95
|
+
/** A rating slot's scale: 11 buttons (0..10), 5 stars, or 2 thumbs. */
|
|
96
|
+
export type WebPopupRatingScale = 'nps' | 'stars' | 'thumbs';
|
|
97
|
+
/** 'instant' posts the score on tap; 'button' waits for the submit pill. */
|
|
98
|
+
export type WebPopupRatingSubmitMode = 'instant' | 'button';
|
|
99
|
+
/** A rating question; the score leaves as an EVENT, and no callback or no `eventName` = inert. */
|
|
100
|
+
export type WebPopupRatingSlot = {
|
|
101
|
+
type: 'rating';
|
|
102
|
+
key: string;
|
|
103
|
+
scale: WebPopupRatingScale;
|
|
104
|
+
/** Captions under the extremes of the scale; '' = none. */
|
|
105
|
+
labels: {
|
|
106
|
+
low: string;
|
|
107
|
+
high: string;
|
|
108
|
+
};
|
|
109
|
+
submitMode: WebPopupRatingSubmitMode;
|
|
110
|
+
submitLabel: string;
|
|
111
|
+
/** The event the score is posted as; '' = the slot is inert. */
|
|
112
|
+
eventName: string;
|
|
113
|
+
/** Replaces the slot after a successful submit. */
|
|
114
|
+
successMessage: string;
|
|
115
|
+
};
|
|
116
|
+
/** The icon whitelist: inline svg only — no icon package in the widget bundle, no svg from json. */
|
|
117
|
+
export type WebPopupGlyphName = 'bell' | 'gift' | 'cart' | 'chart' | 'lock' | 'calendar' | 'check' | 'star' | 'clock' | 'tag' | 'mail' | 'phone' | 'download' | 'play' | 'shield' | 'sparkle' | 'percent' | 'truck' | 'ticket' | 'user';
|
|
118
|
+
export type WebPopupGlyphSize = 24 | 32 | 48;
|
|
119
|
+
/** A single icon; named `glyph` because WebPopupTextVariant already spends `icon` on its emoji line. */
|
|
120
|
+
export type WebPopupGlyphSlot = {
|
|
121
|
+
type: 'glyph';
|
|
122
|
+
key: string;
|
|
123
|
+
/** Whitelisted name; an unknown one drops the slot (no geometry to draw). */
|
|
124
|
+
name: WebPopupGlyphName;
|
|
125
|
+
size: WebPopupGlyphSize;
|
|
126
|
+
/** Hex '#rrggbb'; '' = the palette accent. */
|
|
127
|
+
color: string;
|
|
128
|
+
};
|
|
129
|
+
/** One entry of a page's content stack, in display order. */
|
|
130
|
+
export type WebPopupSlot = {
|
|
131
|
+
type: 'text';
|
|
132
|
+
key: string;
|
|
133
|
+
variant: WebPopupTextVariant;
|
|
134
|
+
html: string;
|
|
135
|
+
/** Per-slot alignment override; absent = the page kind's layout.align. */
|
|
136
|
+
align?: WebPopupTextAlign;
|
|
137
|
+
} | {
|
|
138
|
+
type: 'button';
|
|
139
|
+
key: string;
|
|
140
|
+
variant: WebPopupButtonVariant;
|
|
141
|
+
label: string;
|
|
142
|
+
action: WebPopupButtonAction;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* An email-subscription form: input fields + a submit button, bound to a
|
|
146
|
+
* Pushwoosh subscription form entity by `formCode`. The fields are the
|
|
147
|
+
* slot's own (seeded from the form's configuration by the editor) — the
|
|
148
|
+
* runtime renders straight from the json and never fetches the form. The
|
|
149
|
+
* host submits via WebPopupReaderProps.onSubmitForm; without the callback
|
|
150
|
+
* (editor canvas, previews) the form renders inert. NOTE: engine builds
|
|
151
|
+
* that predate this slot type cannot render a popup containing it.
|
|
152
|
+
*/
|
|
153
|
+
| {
|
|
154
|
+
type: 'email-subscription-form';
|
|
155
|
+
key: string;
|
|
156
|
+
/** Bound subscription form code; '' until picked — the form is inert. */
|
|
157
|
+
formCode: string;
|
|
158
|
+
fields: WebPopupFormField[];
|
|
159
|
+
submitLabel: string;
|
|
160
|
+
submitVariant: WebPopupButtonVariant;
|
|
161
|
+
/** Absent = no consent checkbox (submits policyAccepted: true). */
|
|
162
|
+
privacy?: WebPopupFormPrivacy;
|
|
163
|
+
/** Replaces the form after a subscribed / already-subscribed result. */
|
|
164
|
+
successMessage: string;
|
|
165
|
+
/** Replaces the form after the double-opt-in confirm-email result. */
|
|
166
|
+
confirmEmailMessage: string;
|
|
167
|
+
} | WebPopupTimerSlot | WebPopupChoiceSlot | WebPopupRatingSlot | WebPopupGlyphSlot;
|
|
168
|
+
/** What a media param plays instead of drawing its image; the param's `src` stays the poster, so engines predating video degrade to it. */
|
|
169
|
+
export type WebPopupVideo = {
|
|
170
|
+
/** Video url, rendered through safeUrl like every popup link. */
|
|
171
|
+
src: string;
|
|
172
|
+
/** Absent = chromeless playback (no browser controls). */
|
|
173
|
+
controls?: boolean;
|
|
174
|
+
/** Absent = loop; false = play once. */
|
|
175
|
+
loop?: boolean;
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* An image parameter: the data plus its presentation. WHERE it renders is
|
|
179
|
+
* the layout tree's business (an `Image` node or a container's
|
|
180
|
+
* `backgroundImage` reference this by key); HOW it renders — fit, blur — is
|
|
181
|
+
* the image's own, per-popup editable like the src.
|
|
182
|
+
*/
|
|
183
|
+
export type WebPopupImage = {
|
|
184
|
+
key: string;
|
|
185
|
+
/** Shown as the field label in the editor rail. */
|
|
186
|
+
label: string;
|
|
187
|
+
src: string;
|
|
188
|
+
/** Drawn instead of `src` in the dark scheme (a light logo would stay light
|
|
189
|
+
* on a dark background); absent = `src` in both. */
|
|
190
|
+
srcDark?: string;
|
|
191
|
+
/** uuid of the media-gallery file `srcDark` was picked from; editor-only, like `mediaId`. */
|
|
192
|
+
mediaIdDark?: string;
|
|
193
|
+
/** Set = the param renders as a muted autoplaying video with `src` as its poster. */
|
|
194
|
+
video?: WebPopupVideo;
|
|
195
|
+
/**
|
|
196
|
+
* uuid of the media-gallery file `src` was picked from, kept alongside the
|
|
197
|
+
* url for dedup and gallery back-reference. Absent for externally-typed urls
|
|
198
|
+
* (no gallery entry) and legacy documents. The runtime ignores it — it
|
|
199
|
+
* renders by `src`; only the editor reads it.
|
|
200
|
+
*/
|
|
201
|
+
mediaId?: string;
|
|
202
|
+
/**
|
|
203
|
+
* How the image fills its box when drawn by an `Image` node; a container
|
|
204
|
+
* background always covers. Absent = natural flow (full width, auto height).
|
|
205
|
+
*/
|
|
206
|
+
fit?: 'cover' | 'contain';
|
|
207
|
+
/** Blur radius px applied when drawn (backdrop-filter over a container
|
|
208
|
+
* background, css filter on an image node). Absent / 0 = no blur. */
|
|
209
|
+
blur?: number;
|
|
210
|
+
/**
|
|
211
|
+
* Scrim: a dark translucent layer (0 = none … 1 = black) drawn over the
|
|
212
|
+
* image when it's a container BACKGROUND, keeping the copy above readable
|
|
213
|
+
* on light photos. `Image` nodes ignore it. Absent / 0 = no scrim.
|
|
214
|
+
*/
|
|
215
|
+
dim?: number;
|
|
216
|
+
};
|
|
217
|
+
export type WebPopupVAnchor = 'top' | 'middle' | 'bottom';
|
|
218
|
+
export type WebPopupHAnchor = 'left' | 'center' | 'right';
|
|
219
|
+
/** Viewport placement: a 3×3 anchor plus the gutter from the anchored edges. */
|
|
220
|
+
export type WebPopupPlacement = {
|
|
221
|
+
v: WebPopupVAnchor;
|
|
222
|
+
h: WebPopupHAnchor;
|
|
223
|
+
/** Distance from the anchored edges, px (0 = flush). */
|
|
224
|
+
offset: number;
|
|
225
|
+
};
|
|
226
|
+
/** The page dim behind the popup; absent on params = no dimming. */
|
|
227
|
+
export type WebPopupOverlay = {
|
|
228
|
+
color: string;
|
|
229
|
+
/** 0 (transparent) to 1 (opaque). */
|
|
230
|
+
opacity: number;
|
|
231
|
+
};
|
|
232
|
+
export type WebPopupAnimation = 'none' | 'fade' | 'slide';
|
|
233
|
+
/**
|
|
234
|
+
* The popup palette — every color that varies. What derives from it is fixed:
|
|
235
|
+
* buttons pair the accent with white (primary = accent fill + white label,
|
|
236
|
+
* secondary = white fill + accent outline).
|
|
237
|
+
*/
|
|
238
|
+
export type WebPopupColors = {
|
|
239
|
+
/** Popup background color. */
|
|
240
|
+
background: string;
|
|
241
|
+
/** Brand accent: primary button fill, secondary button outline. */
|
|
242
|
+
accent: string;
|
|
243
|
+
/** Main copy: h1–h3 and body ('icon' inherits it). */
|
|
244
|
+
text: string;
|
|
245
|
+
/** Secondary copy: caption and eyebrow. */
|
|
246
|
+
textSecondary: string;
|
|
247
|
+
/**
|
|
248
|
+
* Copy in inverse-tone zones (over a background image). Secondary variants
|
|
249
|
+
* (eyebrow/body/caption) derive their translucency from it per
|
|
250
|
+
* webPopupTextColor. Default white — set it dark for light photos.
|
|
251
|
+
*/
|
|
252
|
+
textInverse: string;
|
|
253
|
+
};
|
|
254
|
+
/** Which of the two authored palettes a popup is being drawn with. */
|
|
255
|
+
export type WebPopupColorScheme = 'light' | 'dark';
|
|
256
|
+
/** What a host asks the reader for; 'auto' follows the visitor's `prefers-color-scheme`. */
|
|
257
|
+
export type WebPopupColorSchemePref = WebPopupColorScheme | 'auto';
|
|
258
|
+
/** The dark-scheme palette; `enabled: false` keeps it around while the popup
|
|
259
|
+
* renders light, which is what the editor's checkbox needs. */
|
|
260
|
+
export type WebPopupDark = {
|
|
261
|
+
enabled: boolean;
|
|
262
|
+
colors: WebPopupColors;
|
|
263
|
+
/** Page dim for the dark scheme; absent = the chrome's own overlay. */
|
|
264
|
+
overlay?: WebPopupOverlay;
|
|
265
|
+
/** Box shadow for the dark scheme ('none' = drop it); absent = the chrome's own. */
|
|
266
|
+
boxShadow?: string;
|
|
267
|
+
};
|
|
268
|
+
/** What a renderer draws with: the scheme's five authored roles plus the ones
|
|
269
|
+
* derived from them, which used to be hardcoded light values. */
|
|
270
|
+
export type WebPopupResolvedColors = WebPopupColors & {
|
|
271
|
+
/** Which palette this resolved from — 'light' for a popup with dark mode off. */
|
|
272
|
+
scheme: WebPopupColorScheme;
|
|
273
|
+
/** Fill of the cards that sit ON the popup background: inputs, secondary buttons. */
|
|
274
|
+
surface: string;
|
|
275
|
+
/** Copy inside a `surface` card. */
|
|
276
|
+
surfaceText: string;
|
|
277
|
+
/** Idle border of an input card. */
|
|
278
|
+
border: string;
|
|
279
|
+
/** Label over an accent fill: a primary button, a selected option. */
|
|
280
|
+
onAccent: string;
|
|
281
|
+
/** Validation errors and the generic retry line. */
|
|
282
|
+
error: string;
|
|
283
|
+
/** Glyph on a service chip drawn over the popup — today the ✕ button. */
|
|
284
|
+
chromeIcon: string;
|
|
285
|
+
};
|
|
286
|
+
/**
|
|
287
|
+
* The popup-level look shared by all pages: size, colors, viewport placement,
|
|
288
|
+
* page dim, animation and the box chrome. The retired top-level
|
|
289
|
+
* `background`/`accent` fields are folded into `colors` by parseWebPopupJson
|
|
290
|
+
* and mirrored back by stringifyWebPopup (so older engine builds keep
|
|
291
|
+
* rendering newly-saved popups) — they never appear on this type.
|
|
292
|
+
*/
|
|
293
|
+
export type WebPopupChrome = {
|
|
294
|
+
/** Width px. Absent = stretch to the available viewport width. A page may
|
|
295
|
+
* override it (WebPopupPageParams.width). */
|
|
296
|
+
width?: number;
|
|
297
|
+
/** Height px. Absent = grow with content; a number = fixed, content
|
|
298
|
+
* scrolls; 'stretch' = fill the viewport height (both vertical edges pinned
|
|
299
|
+
* at the placement offset, like the width-absent horizontal stretch —
|
|
300
|
+
* engine builds that predate the value treat it as absent and grow with
|
|
301
|
+
* content). A page may override it. */
|
|
302
|
+
height?: number | 'stretch';
|
|
303
|
+
colors: WebPopupColors;
|
|
304
|
+
/** Dark-scheme palette; absent or disabled = the popup always renders light. */
|
|
305
|
+
dark?: WebPopupDark;
|
|
306
|
+
placement: WebPopupPlacement;
|
|
307
|
+
overlay?: WebPopupOverlay;
|
|
308
|
+
animation: WebPopupAnimation;
|
|
309
|
+
/** Corner radius px. 0 = sharp corners. */
|
|
310
|
+
borderRadius: number;
|
|
311
|
+
/** CSS box-shadow string; 'none' = no shadow. */
|
|
312
|
+
boxShadow: string;
|
|
313
|
+
};
|
|
314
|
+
/** A named group of slots a layout `Content` node binds to by key. */
|
|
315
|
+
export type WebPopupContentZone = {
|
|
316
|
+
/** Stable zone id (referenced by the layout tree), not a display name. */
|
|
317
|
+
key: string;
|
|
318
|
+
/** Shown as the zone's name in the editor. */
|
|
319
|
+
label: string;
|
|
320
|
+
slots: WebPopupSlot[];
|
|
321
|
+
};
|
|
322
|
+
/** A css length unit a `length` control accepts. */
|
|
323
|
+
export type WebPopupLengthUnit = 'px' | '%' | 'em' | 'rem' | 'fr';
|
|
324
|
+
/** Min/max bounds for a length amount — a control's default or one unit's override. */
|
|
325
|
+
export type WebPopupLengthRange = {
|
|
326
|
+
min?: number;
|
|
327
|
+
max?: number;
|
|
328
|
+
};
|
|
329
|
+
/**
|
|
330
|
+
* Which editor control a value param is edited with. Editor metadata (the
|
|
331
|
+
* runtime never reads it), stored in json like an image's label so
|
|
332
|
+
* API/AI-authored pages get knobs too. Absent / unknown = a plain text input.
|
|
333
|
+
*/
|
|
334
|
+
export type WebPopupValueControl =
|
|
335
|
+
/**
|
|
336
|
+
* A number input with a unit. `min`/`max` bound the amount; `ranges` narrows
|
|
337
|
+
* them per unit (e.g. 120–480px but 20–60%) — a unit falls back to the flat
|
|
338
|
+
* `min`/`max` when it has no entry.
|
|
339
|
+
*/
|
|
340
|
+
{
|
|
341
|
+
kind: 'length';
|
|
342
|
+
min?: number;
|
|
343
|
+
max?: number;
|
|
344
|
+
units?: WebPopupLengthUnit[];
|
|
345
|
+
ranges?: Partial<Record<WebPopupLengthUnit, WebPopupLengthRange>>;
|
|
346
|
+
}
|
|
347
|
+
/** A 0–100 slider writing 'NN%'. */
|
|
348
|
+
| {
|
|
349
|
+
kind: 'percent';
|
|
350
|
+
min?: number;
|
|
351
|
+
max?: number;
|
|
352
|
+
} | {
|
|
353
|
+
kind: 'select';
|
|
354
|
+
options: {
|
|
355
|
+
value: string;
|
|
356
|
+
label: string;
|
|
357
|
+
}[];
|
|
358
|
+
} | {
|
|
359
|
+
kind: 'text';
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* A named style value the page's layout tree references as a `$key` token
|
|
363
|
+
* (whole string prop or a whitespace-separated token inside a `sizes`
|
|
364
|
+
* template) — the third page-param family after images and contents: WHERE
|
|
365
|
+
* the value applies is the tree's business, the value itself is the param's
|
|
366
|
+
* own, per-popup editable. resolveWebPopupPages substitutes values BEFORE
|
|
367
|
+
* the layout normalization, so a substituted value passes the same
|
|
368
|
+
* whitelists/clamps as a literal one. Binding keys (`content`, `image`,
|
|
369
|
+
* `backgroundImage`) are never substituted.
|
|
370
|
+
*/
|
|
371
|
+
export type WebPopupValueParam = {
|
|
372
|
+
/** Stable id the tree references as `$key`; not a display name. */
|
|
373
|
+
key: string;
|
|
374
|
+
/** Shown as the field label in the editor rail. */
|
|
375
|
+
label: string;
|
|
376
|
+
/** The current value, substituted into the tree verbatim. */
|
|
377
|
+
value: string;
|
|
378
|
+
control?: WebPopupValueControl;
|
|
379
|
+
};
|
|
380
|
+
/** A px number or a whitelisted css length string ('12px', '55%', '1.5em'). */
|
|
381
|
+
export type WebPopupCssLength = string | number;
|
|
382
|
+
export type WebPopupBoxSides = {
|
|
383
|
+
top?: WebPopupCssLength;
|
|
384
|
+
right?: WebPopupCssLength;
|
|
385
|
+
bottom?: WebPopupCssLength;
|
|
386
|
+
left?: WebPopupCssLength;
|
|
387
|
+
};
|
|
388
|
+
export type WebPopupAlignItems = 'start' | 'center' | 'end' | 'stretch';
|
|
389
|
+
export type WebPopupJustifyContent = 'start' | 'center' | 'end' | 'space-between';
|
|
390
|
+
/**
|
|
391
|
+
* A `$key` reference to a page value (WebPopupValueParam) in an AUTHORED
|
|
392
|
+
* (template/catalog/stored) tree — resolveWebPopupPages substitutes it before
|
|
393
|
+
* normalization, so a normalized tree only carries literal values. Every
|
|
394
|
+
* enum container prop accepts it (substitution walks all string props).
|
|
395
|
+
*/
|
|
396
|
+
export type WebPopupValueRef = `$${string}`;
|
|
397
|
+
export type WebPopupContainerType = 'Vertical' | 'Horizontal' | 'Columns' | 'Rows';
|
|
398
|
+
export type WebPopupContainerProps = {
|
|
399
|
+
gap?: WebPopupCssLength;
|
|
400
|
+
padding?: WebPopupCssLength | WebPopupBoxSides;
|
|
401
|
+
alignItems?: WebPopupAlignItems | WebPopupValueRef;
|
|
402
|
+
justifyContent?: WebPopupJustifyContent | WebPopupValueRef;
|
|
403
|
+
/**
|
|
404
|
+
* Vertical distribution of the container's rows — visible only when the
|
|
405
|
+
* container is taller than its content (a 'stretch'-height page's root
|
|
406
|
+
* container fills the box). Same value set as justifyContent.
|
|
407
|
+
*/
|
|
408
|
+
alignContent?: WebPopupJustifyContent | WebPopupValueRef;
|
|
409
|
+
/** Key into page images: the container draws it as a cover background. */
|
|
410
|
+
backgroundImage?: string;
|
|
411
|
+
/**
|
|
412
|
+
* Columns/Rows track template: N equal tracks, or a whitelist-validated
|
|
413
|
+
* template string ('1fr 220px'). Absent = one equal track per child.
|
|
414
|
+
* Ignored on Vertical/Horizontal.
|
|
415
|
+
*/
|
|
416
|
+
sizes?: number | string;
|
|
417
|
+
/**
|
|
418
|
+
* 'reverse' renders the children in reverse order; on Columns/Rows the
|
|
419
|
+
* `sizes` template reverses with them, so each child keeps its track.
|
|
420
|
+
* Applied (and consumed) by normalizeWebPopupLayout — a normalized tree
|
|
421
|
+
* has the children already reordered and no `order` left.
|
|
422
|
+
*/
|
|
423
|
+
order?: 'normal' | 'reverse' | WebPopupValueRef;
|
|
424
|
+
};
|
|
425
|
+
/** Text palette of a Content zone; 'inverse' = white copy over a dark image. */
|
|
426
|
+
export type WebPopupContentTone = 'default' | 'inverse';
|
|
427
|
+
export type WebPopupContentNode = {
|
|
428
|
+
type: 'Content';
|
|
429
|
+
props: {
|
|
430
|
+
/** WebPopupContentZone key; an unknown key renders as an empty zone. */
|
|
431
|
+
content: string;
|
|
432
|
+
/** Default text alignment; a text slot's own `align` overrides it. */
|
|
433
|
+
align?: WebPopupTextAlign;
|
|
434
|
+
tone?: WebPopupContentTone;
|
|
435
|
+
};
|
|
436
|
+
};
|
|
437
|
+
export type WebPopupImageNode = {
|
|
438
|
+
type: 'Image';
|
|
439
|
+
props: {
|
|
440
|
+
/** WebPopupImage key; an unknown key renders nothing. How the image
|
|
441
|
+
* renders (fit, blur) lives on the image param itself. */
|
|
442
|
+
image: string;
|
|
443
|
+
};
|
|
444
|
+
};
|
|
445
|
+
export type WebPopupContainerNode = {
|
|
446
|
+
type: WebPopupContainerType;
|
|
447
|
+
props?: WebPopupContainerProps;
|
|
448
|
+
children: WebPopupLayoutNode[];
|
|
449
|
+
};
|
|
450
|
+
export type WebPopupLayoutNode = WebPopupContainerNode | WebPopupContentNode | WebPopupImageNode;
|
|
451
|
+
/**
|
|
452
|
+
* One page of the popup. `key` is unique within the popup; button `page`
|
|
453
|
+
* actions point at it. The layout tree is EMBEDDED (copied from the kind by
|
|
454
|
+
* the editor on save), so the display engine renders the page self-contained
|
|
455
|
+
* — no kind catalog at runtime, and new kinds render on older engine builds.
|
|
456
|
+
*/
|
|
457
|
+
export type WebPopupPageParams = {
|
|
458
|
+
key: string;
|
|
459
|
+
/** WebPopupKind id — editor metadata only; the runtime never resolves it. */
|
|
460
|
+
kind: string;
|
|
461
|
+
layout: WebPopupLayoutNode;
|
|
462
|
+
images: WebPopupImage[];
|
|
463
|
+
contents: WebPopupContentZone[];
|
|
464
|
+
/** Named style values the layout tree references as `$key` tokens. */
|
|
465
|
+
values?: WebPopupValueParam[];
|
|
466
|
+
/**
|
|
467
|
+
* The page's own box size, overriding the chrome's: a number = px, `null` =
|
|
468
|
+
* explicitly auto (needed when the chrome default IS a number), absent =
|
|
469
|
+
* the chrome's width/height. Engine builds that predate the field ignore it
|
|
470
|
+
* and show every page at the chrome size.
|
|
471
|
+
*/
|
|
472
|
+
width?: number | null;
|
|
473
|
+
/** See `width`; auto height = grow with content, a number = fixed
|
|
474
|
+
* (scrolls), 'stretch' = fill the viewport height. */
|
|
475
|
+
height?: number | 'stretch' | null;
|
|
476
|
+
};
|
|
477
|
+
export type WebPopupParams = WebPopupChrome & {
|
|
478
|
+
/** The pages, in order; at least one. Only one shows at a time. */
|
|
479
|
+
pages: WebPopupPageParams[];
|
|
480
|
+
/** Key of the page shown first; absent/unknown = the first page. */
|
|
481
|
+
startPage?: string;
|
|
482
|
+
};
|
|
483
|
+
export type WebPopupKind = {
|
|
484
|
+
id: string;
|
|
485
|
+
label: string;
|
|
486
|
+
layout: WebPopupLayoutNode;
|
|
487
|
+
/** The chrome a fresh popup of this kind opens with in the editor gallery. */
|
|
488
|
+
chrome: WebPopupChrome;
|
|
489
|
+
/** The default content a new page of this kind opens with. */
|
|
490
|
+
content: {
|
|
491
|
+
images: WebPopupImage[];
|
|
492
|
+
contents: WebPopupContentZone[];
|
|
493
|
+
};
|
|
494
|
+
/**
|
|
495
|
+
* The value params the layout references as `$key` tokens; `value` is the
|
|
496
|
+
* default a fresh page's `values` open with.
|
|
497
|
+
*/
|
|
498
|
+
params?: WebPopupValueParam[];
|
|
499
|
+
};
|
|
500
|
+
/**
|
|
501
|
+
* A system layout template — a blessed, parameterized arrangement the editor
|
|
502
|
+
* kinds instantiate instead of hand-writing trees (a kind may still carry its
|
|
503
|
+
* own tree where no template fits). The tree references `$key` value params
|
|
504
|
+
* and binds the canonical zone/image keys ('main', 'image'); the kind catalog
|
|
505
|
+
* may rebind those when instantiating. Editor-only, like the kind catalog.
|
|
506
|
+
*/
|
|
507
|
+
export type WebPopupTemplate = {
|
|
508
|
+
name: string;
|
|
509
|
+
layout: WebPopupLayoutNode;
|
|
510
|
+
/** The template's knobs; `value` holds the default. */
|
|
511
|
+
params: WebPopupValueParam[];
|
|
512
|
+
};
|
|
513
|
+
/** A page with its layout tree validated/normalized — the renderable unit. */
|
|
514
|
+
export type WebPopupResolvedPage = {
|
|
515
|
+
key: string;
|
|
516
|
+
layout: WebPopupLayoutNode;
|
|
517
|
+
images: WebPopupImage[];
|
|
518
|
+
contents: WebPopupContentZone[];
|
|
519
|
+
/** The page's FINAL box width px (page ?? chrome, validated); absent =
|
|
520
|
+
* stretch to the available viewport width. */
|
|
521
|
+
width?: number;
|
|
522
|
+
/** Final box height: px, or 'stretch' = fill the viewport height; absent =
|
|
523
|
+
* grow with content. */
|
|
524
|
+
height?: number | 'stretch';
|
|
525
|
+
};
|
|
526
|
+
/** The stored format — PopupFormContent.json parses into this. */
|
|
527
|
+
export type WebPopupJson = {
|
|
528
|
+
/** Format version; content without it predates this engine and is unrenderable. */
|
|
529
|
+
version: number;
|
|
530
|
+
params: WebPopupParams;
|
|
531
|
+
};
|
|
532
|
+
export type WebPopupReaderMode = 'live' | 'preview';
|
|
533
|
+
/**
|
|
534
|
+
* What an email-subscription-form slot submits — mirrors the
|
|
535
|
+
* subscription-form service's submit request (the host adds transport fields
|
|
536
|
+
* like userId itself). Custom fields land under their `tag` (falling back to
|
|
537
|
+
* the field key).
|
|
538
|
+
*/
|
|
539
|
+
export type WebPopupFormSubmitPayload = {
|
|
540
|
+
email: string;
|
|
541
|
+
firstName?: string;
|
|
542
|
+
lastName?: string;
|
|
543
|
+
phone?: string;
|
|
544
|
+
customFields?: Record<string, string>;
|
|
545
|
+
/** True when the slot has no privacy checkbox. */
|
|
546
|
+
policyAccepted: boolean;
|
|
547
|
+
};
|
|
548
|
+
/** A countdown's remaining time at some instant, broken down for display. */
|
|
549
|
+
export type WebPopupTimerRemaining = {
|
|
550
|
+
/** Whole milliseconds left; 0 = the deadline has passed (or can't be computed). */
|
|
551
|
+
total: number;
|
|
552
|
+
days: number;
|
|
553
|
+
hours: number;
|
|
554
|
+
minutes: number;
|
|
555
|
+
seconds: number;
|
|
556
|
+
};
|
|
557
|
+
/** The service's submit outcomes; 'confirm-email' = double-opt-in pending. */
|
|
558
|
+
export type WebPopupFormSubmitResult = 'subscribed' | 'confirm-email' | 'already-subscribed';
|
|
559
|
+
/**
|
|
560
|
+
* Submits a form slot's payload to the bound subscription form. Rejects on
|
|
561
|
+
* network/server failure — the slot shows a generic retryable error.
|
|
562
|
+
*/
|
|
563
|
+
export type WebPopupFormSubmitHandler = (formCode: string, payload: WebPopupFormSubmitPayload) => Promise<WebPopupFormSubmitResult>;
|
|
564
|
+
/** What a `choice` slot submits: the picked options' tag values, in option order. */
|
|
565
|
+
export type WebPopupChoiceSubmitPayload = {
|
|
566
|
+
slotKey: string;
|
|
567
|
+
tagName: string;
|
|
568
|
+
values: string[];
|
|
569
|
+
};
|
|
570
|
+
/** Writes a choice slot's selection to the device tag; a rejection is what leaves the slot retryable. */
|
|
571
|
+
export type WebPopupChoiceSubmitHandler = (payload: WebPopupChoiceSubmitPayload) => Promise<void>;
|
|
572
|
+
/** What a `rating` slot submits: the score plus the scale it was given on. */
|
|
573
|
+
export type WebPopupRatingSubmitPayload = {
|
|
574
|
+
slotKey: string;
|
|
575
|
+
eventName: string;
|
|
576
|
+
score: number;
|
|
577
|
+
scale: WebPopupRatingScale;
|
|
578
|
+
};
|
|
579
|
+
/** Posts a rating slot's score as an event. Rejects on failure — see above. */
|
|
580
|
+
export type WebPopupRatingSubmitHandler = (payload: WebPopupRatingSubmitPayload) => Promise<void>;
|
|
581
|
+
/**
|
|
582
|
+
* Editor-only overrides for the tree's data-binding LEAVES. The container
|
|
583
|
+
* interpretation (grid divs, background images, dim, blur) is deliberately
|
|
584
|
+
* NOT overridable — rendering it here is what guarantees an editor canvas
|
|
585
|
+
* matches the runtime pixel for pixel. The runtime never passes these.
|
|
586
|
+
*/
|
|
587
|
+
export type WebPopupLeafRenderers = {
|
|
588
|
+
/** Replaces a Content leaf's slot stack. Unknown zone keys still render nothing. */
|
|
589
|
+
renderZone?: (zone: WebPopupContentZone, node: WebPopupContentNode) => ReactNode;
|
|
590
|
+
/**
|
|
591
|
+
* Replaces an Image leaf's <img>. Called whenever the image param exists —
|
|
592
|
+
* including an empty src the runtime would hide (editors want a
|
|
593
|
+
* placeholder there). Unknown image keys still render nothing.
|
|
594
|
+
*/
|
|
595
|
+
renderImage?: (image: WebPopupImage, node: WebPopupImageNode) => ReactNode;
|
|
596
|
+
};
|
|
597
|
+
export type WebPopupReaderProps = {
|
|
598
|
+
params: WebPopupParams;
|
|
599
|
+
/**
|
|
600
|
+
* 'live' (default): fixed-position chrome — overlay, viewport placement,
|
|
601
|
+
* enter animation, close button. 'preview': the popup box rendered inline
|
|
602
|
+
* (relative), for editor/list previews.
|
|
603
|
+
*/
|
|
604
|
+
mode?: WebPopupReaderMode;
|
|
605
|
+
/** Palette to draw with; default 'auto' — the visitor's OS setting, watched live. */
|
|
606
|
+
colorScheme?: WebPopupColorSchemePref;
|
|
607
|
+
/** Close requests: the ✕ button, the overlay click and 'close'-action buttons. */
|
|
608
|
+
onRequestClose?: () => void;
|
|
609
|
+
/** Form-slot submits; absent = form slots render inert (previews, editor). */
|
|
610
|
+
onSubmitForm?: WebPopupFormSubmitHandler;
|
|
611
|
+
/** Choice-slot submits; absent = choice slots render inert. */
|
|
612
|
+
onSubmitChoice?: WebPopupChoiceSubmitHandler;
|
|
613
|
+
/** Rating-slot submits; absent = rating slots render inert. */
|
|
614
|
+
onSubmitRating?: WebPopupRatingSubmitHandler;
|
|
615
|
+
/** `subscribe`-action buttons; absent = they render inert (previews, editor). */
|
|
616
|
+
onRequestSubscribe?: () => Promise<WebPopupSubscribeResult>;
|
|
617
|
+
/** Editor-only leaf overrides — see WebPopupLeafRenderers. */
|
|
618
|
+
leafRenderers?: WebPopupLeafRenderers;
|
|
619
|
+
};
|
|
620
|
+
/** Props of WebPopupSlotView — one slot, drawn the runtime's way; the defaults are the inert ones an editor wants. */
|
|
621
|
+
export type WebPopupSlotViewProps = {
|
|
622
|
+
slot: WebPopupSlot;
|
|
623
|
+
align: WebPopupTextAlign;
|
|
624
|
+
/** Inverse tone (over a dark image) — the zone node's, not the slot's. */
|
|
625
|
+
inverse: boolean;
|
|
626
|
+
colors: WebPopupResolvedColors;
|
|
627
|
+
/** Default 'live'; an editor canvas or a preview card passes 'preview'. */
|
|
628
|
+
mode?: WebPopupReaderMode;
|
|
629
|
+
onRequestClose?: () => void;
|
|
630
|
+
onPageChange?: (page: string) => void;
|
|
631
|
+
onSubmitForm?: WebPopupFormSubmitHandler;
|
|
632
|
+
onSubmitChoice?: WebPopupChoiceSubmitHandler;
|
|
633
|
+
onSubmitRating?: WebPopupRatingSubmitHandler;
|
|
634
|
+
/** `subscribe`-action buttons; absent = they render inert (previews, editor). */
|
|
635
|
+
onRequestSubscribe?: () => Promise<WebPopupSubscribeResult>;
|
|
636
|
+
};
|
|
637
|
+
/** Props of WebPopupBox — one resolved page's box, no viewport chrome. */
|
|
638
|
+
export type WebPopupBoxProps = {
|
|
639
|
+
page: WebPopupResolvedPage;
|
|
640
|
+
params: WebPopupParams;
|
|
641
|
+
/** What the box is drawn for; default 'live'. 'preview' reaches the leaves: no autoplay, no interval. */
|
|
642
|
+
mode?: WebPopupReaderMode;
|
|
643
|
+
/** Palette to draw with; default 'light'. The reader resolves 'auto' before it gets here. */
|
|
644
|
+
colorScheme?: WebPopupColorScheme;
|
|
645
|
+
/** Editor-only leaf overrides — see WebPopupLeafRenderers. */
|
|
646
|
+
leafRenderers?: WebPopupLeafRenderers;
|
|
647
|
+
/** Play the page-switch fade on this render (the reader sets it after navigation). */
|
|
648
|
+
animatePage?: boolean;
|
|
649
|
+
onRequestClose?: () => void;
|
|
650
|
+
/** `page`-action buttons; absent = they render inert (editor canvas). */
|
|
651
|
+
onPageChange?: (page: string) => void;
|
|
652
|
+
/** Form-slot submits; absent = form slots render inert (previews, editor). */
|
|
653
|
+
onSubmitForm?: WebPopupFormSubmitHandler;
|
|
654
|
+
/** Choice-slot submits; absent = choice slots render inert. */
|
|
655
|
+
onSubmitChoice?: WebPopupChoiceSubmitHandler;
|
|
656
|
+
/** Rating-slot submits; absent = rating slots render inert. */
|
|
657
|
+
onSubmitRating?: WebPopupRatingSubmitHandler;
|
|
658
|
+
/** `subscribe`-action buttons; absent = they render inert (previews, editor). */
|
|
659
|
+
onRequestSubscribe?: () => Promise<WebPopupSubscribeResult>;
|
|
660
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The web-popup model. The stored/wire shape is WebPopupJson —
|
|
2
|
+
// { version: 2, params } serialized into PopupFormContent.json. A popup is a
|
|
3
|
+
// set of PAGES (each page = a layout TREE plus named content zones and
|
|
4
|
+
// images) under a shared CHROME (size, colors, placement, overlay,
|
|
5
|
+
// animation, ...). Only one page shows at a time; `page`-action buttons
|
|
6
|
+
// switch between them. The editor (smart-blocks) produces the json;
|
|
7
|
+
// WebPopupReader renders it on the customer's site (WebSDK) and in previews
|
|
8
|
+
// (content). Pure types only — constants live in constants.tsx, helpers in
|
|
9
|
+
// helpers.ts, layout-tree validation in layout.ts.
|
|
10
|
+
export {};
|