@cognior/iap-sdk 0.1.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.
Potentially problematic release.
This version of @cognior/iap-sdk might be problematic. Click here for more details.
- package/.github/copilot-instructions.md +95 -0
- package/README.md +79 -0
- package/TRACKING.md +105 -0
- package/USER_CONTEXT_README.md +284 -0
- package/package.json +154 -0
- package/src/config.ts +25 -0
- package/src/core/flowEngine.ts +1833 -0
- package/src/core/triggerManager.ts +1011 -0
- package/src/experiences/banner.ts +366 -0
- package/src/experiences/beacon.ts +668 -0
- package/src/experiences/hotspotTour.ts +654 -0
- package/src/experiences/hotspots.ts +566 -0
- package/src/experiences/modal.ts +1337 -0
- package/src/experiences/modalSequence.ts +1247 -0
- package/src/experiences/popover.ts +652 -0
- package/src/experiences/registry.ts +21 -0
- package/src/experiences/survey.ts +1639 -0
- package/src/experiences/taskList.ts +625 -0
- package/src/experiences/tooltip.ts +740 -0
- package/src/experiences/types.ts +395 -0
- package/src/experiences/walkthrough.ts +670 -0
- package/src/flow-sequence.ts +177 -0
- package/src/flows.ts +512 -0
- package/src/http.ts +61 -0
- package/src/index.ts +355 -0
- package/src/services/flowManager.ts +905 -0
- package/src/services/flowNormalizer.ts +74 -0
- package/src/services/locationContextService.ts +189 -0
- package/src/services/pageContextService.ts +221 -0
- package/src/services/userContextService.ts +286 -0
- package/src/state/appState.ts +0 -0
- package/src/state/hooks.ts +0 -0
- package/src/state/index.ts +0 -0
- package/src/state/migration.ts +0 -0
- package/src/state/store.ts +0 -0
- package/src/styles/banner.css.ts +0 -0
- package/src/styles/hotspot.css.ts +0 -0
- package/src/styles/hotspotTour.css.ts +0 -0
- package/src/styles/modal.css.ts +564 -0
- package/src/styles/survey.css.ts +1013 -0
- package/src/styles/taskList.css.ts +0 -0
- package/src/styles/tooltip.css.ts +149 -0
- package/src/styles/walkthrough.css.ts +0 -0
- package/src/tourUtils.ts +0 -0
- package/src/tracking.ts +223 -0
- package/src/utils/debounce.ts +66 -0
- package/src/utils/eventSequenceValidator.ts +124 -0
- package/src/utils/flowTrackingSystem.ts +524 -0
- package/src/utils/idGenerator.ts +155 -0
- package/src/utils/immediateValidationPrevention.ts +184 -0
- package/src/utils/normalize.ts +50 -0
- package/src/utils/privacyManager.ts +166 -0
- package/src/utils/ruleEvaluator.ts +199 -0
- package/src/utils/sanitize.ts +79 -0
- package/src/utils/selectors.ts +107 -0
- package/src/utils/stepExecutor.ts +345 -0
- package/src/utils/triggerNormalizer.ts +149 -0
- package/src/utils/validationInterceptor.ts +650 -0
- package/tsconfig.json +13 -0
- package/tsup.config.ts +13 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
// src/experiences/banner.ts
|
|
2
|
+
// Banner notification experience renderer
|
|
3
|
+
|
|
4
|
+
import { sanitizeHtml } from "../utils/sanitize";
|
|
5
|
+
import { register } from "./registry";
|
|
6
|
+
import type { BannerPayload } from "./types";
|
|
7
|
+
|
|
8
|
+
type BannerFlow = { id: string; type: "banner"; payload: BannerPayload };
|
|
9
|
+
|
|
10
|
+
const bannerCssText = `
|
|
11
|
+
:root {
|
|
12
|
+
--dap-z-banner: 2147483620;
|
|
13
|
+
--dap-banner-bg-info: #eff6ff;
|
|
14
|
+
--dap-banner-bg-warning: #fefce8;
|
|
15
|
+
--dap-banner-bg-error: #fef2f2;
|
|
16
|
+
--dap-banner-bg-success: #f0f9ff;
|
|
17
|
+
--dap-banner-text-info: #1e40af;
|
|
18
|
+
--dap-banner-text-warning: #92400e;
|
|
19
|
+
--dap-banner-text-error: #dc2626;
|
|
20
|
+
--dap-banner-text-success: #059669;
|
|
21
|
+
--dap-banner-border-info: #3b82f6;
|
|
22
|
+
--dap-banner-border-warning: #f59e0b;
|
|
23
|
+
--dap-banner-border-error: #ef4444;
|
|
24
|
+
--dap-banner-border-success: #10b981;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.dap-banner-wrap {
|
|
28
|
+
position: fixed;
|
|
29
|
+
left: 0;
|
|
30
|
+
right: 0;
|
|
31
|
+
z-index: var(--dap-z-banner);
|
|
32
|
+
padding: 0 16px;
|
|
33
|
+
pointer-events: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.dap-banner-wrap.top {
|
|
37
|
+
top: 16px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.dap-banner-wrap.bottom {
|
|
41
|
+
bottom: 16px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.dap-banner {
|
|
45
|
+
max-width: 800px;
|
|
46
|
+
margin: 0 auto;
|
|
47
|
+
padding: 16px 20px;
|
|
48
|
+
border-radius: 8px;
|
|
49
|
+
border-left: 4px solid;
|
|
50
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: 12px;
|
|
54
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
55
|
+
line-height: 1.5;
|
|
56
|
+
pointer-events: auto;
|
|
57
|
+
animation: bannerSlideIn 0.3s ease-out;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@keyframes bannerSlideIn {
|
|
61
|
+
from {
|
|
62
|
+
opacity: 0;
|
|
63
|
+
transform: translateY(-20px);
|
|
64
|
+
}
|
|
65
|
+
to {
|
|
66
|
+
opacity: 1;
|
|
67
|
+
transform: translateY(0);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.dap-banner.bottom {
|
|
72
|
+
animation: bannerSlideInBottom 0.3s ease-out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@keyframes bannerSlideInBottom {
|
|
76
|
+
from {
|
|
77
|
+
opacity: 0;
|
|
78
|
+
transform: translateY(20px);
|
|
79
|
+
}
|
|
80
|
+
to {
|
|
81
|
+
opacity: 1;
|
|
82
|
+
transform: translateY(0);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.dap-banner.info {
|
|
87
|
+
background: var(--dap-banner-bg-info);
|
|
88
|
+
color: var(--dap-banner-text-info);
|
|
89
|
+
border-color: var(--dap-banner-border-info);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.dap-banner.warning {
|
|
93
|
+
background: var(--dap-banner-bg-warning);
|
|
94
|
+
color: var(--dap-banner-text-warning);
|
|
95
|
+
border-color: var(--dap-banner-border-warning);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.dap-banner.error {
|
|
99
|
+
background: var(--dap-banner-bg-error);
|
|
100
|
+
color: var(--dap-banner-text-error);
|
|
101
|
+
border-color: var(--dap-banner-border-error);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.dap-banner.success {
|
|
105
|
+
background: var(--dap-banner-bg-success);
|
|
106
|
+
color: var(--dap-banner-text-success);
|
|
107
|
+
border-color: var(--dap-banner-border-success);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.dap-banner-icon {
|
|
111
|
+
width: 20px;
|
|
112
|
+
height: 20px;
|
|
113
|
+
flex-shrink: 0;
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
justify-content: center;
|
|
117
|
+
font-size: 16px;
|
|
118
|
+
font-weight: bold;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.dap-banner-message {
|
|
122
|
+
flex: 1;
|
|
123
|
+
font-size: 14px;
|
|
124
|
+
font-weight: 500;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.dap-banner-actions {
|
|
128
|
+
display: flex;
|
|
129
|
+
gap: 8px;
|
|
130
|
+
align-items: center;
|
|
131
|
+
flex-shrink: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.dap-banner-btn {
|
|
135
|
+
padding: 6px 12px;
|
|
136
|
+
border: 1px solid currentColor;
|
|
137
|
+
border-radius: 4px;
|
|
138
|
+
background: transparent;
|
|
139
|
+
color: inherit;
|
|
140
|
+
font-size: 12px;
|
|
141
|
+
font-weight: 500;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
text-decoration: none;
|
|
144
|
+
display: inline-flex;
|
|
145
|
+
align-items: center;
|
|
146
|
+
transition: all 0.15s ease;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.dap-banner-btn:hover {
|
|
150
|
+
background: currentColor;
|
|
151
|
+
color: white;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.dap-banner-close {
|
|
155
|
+
background: none;
|
|
156
|
+
border: none;
|
|
157
|
+
color: inherit;
|
|
158
|
+
cursor: pointer;
|
|
159
|
+
padding: 4px;
|
|
160
|
+
border-radius: 4px;
|
|
161
|
+
display: flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
justify-content: center;
|
|
164
|
+
width: 24px;
|
|
165
|
+
height: 24px;
|
|
166
|
+
font-size: 14px;
|
|
167
|
+
opacity: 0.7;
|
|
168
|
+
transition: opacity 0.15s ease;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.dap-banner-close:hover {
|
|
172
|
+
opacity: 1;
|
|
173
|
+
background: rgba(0, 0, 0, 0.1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@media (max-width: 640px) {
|
|
177
|
+
.dap-banner-wrap {
|
|
178
|
+
padding: 0 12px;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.dap-banner {
|
|
182
|
+
padding: 14px 16px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.dap-banner-actions {
|
|
186
|
+
flex-direction: column;
|
|
187
|
+
gap: 6px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.dap-banner-btn {
|
|
191
|
+
font-size: 11px;
|
|
192
|
+
padding: 4px 8px;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
export function registerBanner() {
|
|
198
|
+
register("banner", renderBanner);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export async function renderBanner(flow: BannerFlow): Promise<void> {
|
|
202
|
+
const { payload, id } = flow;
|
|
203
|
+
|
|
204
|
+
// Ensure we have a root container
|
|
205
|
+
const root = ensureRoot();
|
|
206
|
+
|
|
207
|
+
// Extract completion tracker if provided
|
|
208
|
+
const completionTracker = payload._completionTracker;
|
|
209
|
+
|
|
210
|
+
// Create banner shell
|
|
211
|
+
const shell = createBannerShell(payload);
|
|
212
|
+
|
|
213
|
+
// Add message content
|
|
214
|
+
shell.messageEl.innerHTML = sanitizeHtml(payload.message);
|
|
215
|
+
|
|
216
|
+
// Add actions if provided
|
|
217
|
+
if (payload.actions?.length) {
|
|
218
|
+
payload.actions.forEach(action => {
|
|
219
|
+
const btn = document.createElement(action.action === "navigate" ? "a" : "button");
|
|
220
|
+
btn.className = "dap-banner-btn";
|
|
221
|
+
btn.textContent = action.label;
|
|
222
|
+
|
|
223
|
+
if (action.action === "navigate" && action.href) {
|
|
224
|
+
(btn as HTMLAnchorElement).href = action.href;
|
|
225
|
+
(btn as HTMLAnchorElement).target = "_blank";
|
|
226
|
+
(btn as HTMLAnchorElement).rel = "noopener noreferrer";
|
|
227
|
+
} else {
|
|
228
|
+
btn.addEventListener("click", () => {
|
|
229
|
+
if (action.action === "dismiss") {
|
|
230
|
+
dismissBanner();
|
|
231
|
+
} else if (action.action === "custom" && action.customAction) {
|
|
232
|
+
// Trigger custom action event
|
|
233
|
+
window.dispatchEvent(new CustomEvent("dap-banner-action", {
|
|
234
|
+
detail: { action: action.customAction, bannerId: id }
|
|
235
|
+
}));
|
|
236
|
+
dismissBanner();
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
shell.actionsEl.appendChild(btn);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Auto-hide functionality
|
|
246
|
+
let autoHideTimer: number | undefined;
|
|
247
|
+
if (payload.autoHide && payload.autoHide > 0) {
|
|
248
|
+
autoHideTimer = window.setTimeout(() => {
|
|
249
|
+
dismissBanner();
|
|
250
|
+
}, payload.autoHide * 1000);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function dismissBanner() {
|
|
254
|
+
if (autoHideTimer) {
|
|
255
|
+
clearTimeout(autoHideTimer);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
shell.wrap.style.animation = payload.position === "bottom" ? "bannerSlideOutBottom 0.2s ease-in" : "bannerSlideOut 0.2s ease-in";
|
|
259
|
+
|
|
260
|
+
setTimeout(() => {
|
|
261
|
+
shell.wrap.remove();
|
|
262
|
+
|
|
263
|
+
// Signal completion if tracker provided
|
|
264
|
+
if (completionTracker?.onComplete) {
|
|
265
|
+
console.debug(`[DAP] Completing banner flow: ${id}`);
|
|
266
|
+
completionTracker.onComplete();
|
|
267
|
+
}
|
|
268
|
+
}, 200);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Close button functionality (if dismissible)
|
|
272
|
+
if (payload.dismissible !== false) {
|
|
273
|
+
shell.closeBtn.addEventListener("click", dismissBanner);
|
|
274
|
+
} else {
|
|
275
|
+
shell.closeBtn.style.display = "none";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Add dismiss animation styles
|
|
279
|
+
if (!document.getElementById("dap-banner-dismiss-styles")) {
|
|
280
|
+
const style = document.createElement("style");
|
|
281
|
+
style.id = "dap-banner-dismiss-styles";
|
|
282
|
+
style.textContent = `
|
|
283
|
+
@keyframes bannerSlideOut {
|
|
284
|
+
from { opacity: 1; transform: translateY(0); }
|
|
285
|
+
to { opacity: 0; transform: translateY(-20px); }
|
|
286
|
+
}
|
|
287
|
+
@keyframes bannerSlideOutBottom {
|
|
288
|
+
from { opacity: 1; transform: translateY(0); }
|
|
289
|
+
to { opacity: 0; transform: translateY(20px); }
|
|
290
|
+
}
|
|
291
|
+
`;
|
|
292
|
+
document.head.appendChild(style);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Append to root and focus for accessibility
|
|
296
|
+
root.appendChild(shell.wrap);
|
|
297
|
+
shell.banner.setAttribute("role", "alert");
|
|
298
|
+
shell.banner.setAttribute("aria-live", "polite");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function createBannerShell(payload: BannerPayload) {
|
|
302
|
+
const wrap = document.createElement("div");
|
|
303
|
+
wrap.className = `dap-banner-wrap ${payload.position || "top"}`;
|
|
304
|
+
|
|
305
|
+
const banner = document.createElement("div");
|
|
306
|
+
banner.className = `dap-banner ${payload.variant || "info"}`;
|
|
307
|
+
|
|
308
|
+
// Apply theme if provided
|
|
309
|
+
if (payload.theme) {
|
|
310
|
+
for (const [key, value] of Object.entries(payload.theme)) {
|
|
311
|
+
banner.style.setProperty(key, value);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const iconEl = document.createElement("div");
|
|
316
|
+
iconEl.className = "dap-banner-icon";
|
|
317
|
+
|
|
318
|
+
// Set icon based on variant
|
|
319
|
+
const icons = {
|
|
320
|
+
info: "ℹ",
|
|
321
|
+
warning: "⚠",
|
|
322
|
+
error: "✕",
|
|
323
|
+
success: "✓"
|
|
324
|
+
};
|
|
325
|
+
iconEl.textContent = icons[payload.variant || "info"];
|
|
326
|
+
|
|
327
|
+
const messageEl = document.createElement("div");
|
|
328
|
+
messageEl.className = "dap-banner-message";
|
|
329
|
+
|
|
330
|
+
const actionsEl = document.createElement("div");
|
|
331
|
+
actionsEl.className = "dap-banner-actions";
|
|
332
|
+
|
|
333
|
+
const closeBtn = document.createElement("button");
|
|
334
|
+
closeBtn.className = "dap-banner-close";
|
|
335
|
+
closeBtn.innerHTML = "×";
|
|
336
|
+
closeBtn.setAttribute("aria-label", "Close banner");
|
|
337
|
+
|
|
338
|
+
banner.appendChild(iconEl);
|
|
339
|
+
banner.appendChild(messageEl);
|
|
340
|
+
banner.appendChild(actionsEl);
|
|
341
|
+
banner.appendChild(closeBtn);
|
|
342
|
+
wrap.appendChild(banner);
|
|
343
|
+
|
|
344
|
+
return { wrap, banner, iconEl, messageEl, actionsEl, closeBtn };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function ensureRoot(): HTMLElement {
|
|
348
|
+
let host = document.querySelector("dap-banner-root") as HTMLElement | null;
|
|
349
|
+
if (!host) {
|
|
350
|
+
host = document.createElement("dap-banner-root");
|
|
351
|
+
host.style.position = "fixed";
|
|
352
|
+
host.style.zIndex = "2147483620";
|
|
353
|
+
host.style.inset = "0";
|
|
354
|
+
host.style.pointerEvents = "none";
|
|
355
|
+
document.documentElement.appendChild(host);
|
|
356
|
+
|
|
357
|
+
// Inject styles
|
|
358
|
+
if (!host.shadowRoot && !document.getElementById("dap-banner-style")) {
|
|
359
|
+
const style = document.createElement("style");
|
|
360
|
+
style.id = "dap-banner-style";
|
|
361
|
+
style.textContent = bannerCssText;
|
|
362
|
+
document.head.appendChild(style);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return host;
|
|
366
|
+
}
|