veo-sdk 0.3.6 → 0.3.7
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/dist/builder-7MBMYL26.mjs +5 -0
- package/dist/{builder-MIPJRGOD.mjs.map → builder-7MBMYL26.mjs.map} +1 -1
- package/dist/builder.cjs +249 -110
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +2 -2
- package/dist/builder.d.ts +2 -2
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-FUMGOQJR.mjs → chunk-4VJIILXF.mjs} +251 -112
- package/dist/chunk-4VJIILXF.mjs.map +1 -0
- package/dist/{chunk-A73VSAZP.mjs → chunk-734ORVMM.mjs} +3 -3
- package/dist/{chunk-A73VSAZP.mjs.map → chunk-734ORVMM.mjs.map} +1 -1
- package/dist/{chunk-B5GO6PTO.mjs → chunk-RNHOBE2D.mjs} +34 -4
- package/dist/chunk-RNHOBE2D.mjs.map +1 -0
- package/dist/{guide-preview-CO5ShtNf.d.cts → guide-preview-DewCTa-B.d.cts} +26 -1
- package/dist/{guide-preview-CO5ShtNf.d.ts → guide-preview-DewCTa-B.d.ts} +26 -1
- package/dist/index.cjs +288 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +5 -5
- package/dist/veo-builder.js +9 -9
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +4 -4
- package/dist/veo-guides.js.map +1 -1
- package/package.json +1 -1
- package/dist/builder-MIPJRGOD.mjs +0 -5
- package/dist/chunk-B5GO6PTO.mjs.map +0 -1
- package/dist/chunk-FUMGOQJR.mjs.map +0 -1
|
@@ -47,6 +47,25 @@ interface ActivationRules {
|
|
|
47
47
|
eventName?: string;
|
|
48
48
|
delayMs?: number;
|
|
49
49
|
}
|
|
50
|
+
/** Tipo de un bloque de contenido (para pasos con varios títulos/textos/botones). */
|
|
51
|
+
type StepBlockType = 'title' | 'text' | 'button' | 'image';
|
|
52
|
+
/**
|
|
53
|
+
* Un bloque de contenido dentro de un paso. Permite componer un paso con VARIOS
|
|
54
|
+
* títulos/textos/botones/imágenes en orden (no solo un título + un texto + un
|
|
55
|
+
* CTA). Si `step.blocks` está presente, el renderer lo usa; si no, cae a los
|
|
56
|
+
* campos escalares (`title`/`content`/`ctaText`/`imageUrl`) — retrocompatible.
|
|
57
|
+
*/
|
|
58
|
+
interface StepBlock {
|
|
59
|
+
type: StepBlockType;
|
|
60
|
+
/** title/text/button: el texto. */
|
|
61
|
+
text?: string | null;
|
|
62
|
+
/** image: la URL; button con acción `url`: el destino. */
|
|
63
|
+
url?: string | null;
|
|
64
|
+
/** button: qué hace al hacer click. */
|
|
65
|
+
action?: CtaAction | null;
|
|
66
|
+
/** Estilo por-bloque (align/color/fontSize/margin…). Se aplica inline. */
|
|
67
|
+
style?: Record<string, unknown> | null;
|
|
68
|
+
}
|
|
50
69
|
interface GuideStep {
|
|
51
70
|
title: string;
|
|
52
71
|
content: string;
|
|
@@ -57,9 +76,15 @@ interface GuideStep {
|
|
|
57
76
|
/**
|
|
58
77
|
* Free-form JSONB. Convención: si `ctaAction === 'url'`, la URL destino
|
|
59
78
|
* se lee de `style.ctaUrl`. Otras keys que puede traer: `position`
|
|
60
|
-
* (`top` | `bottom` para banners), `theme
|
|
79
|
+
* (`top` | `bottom` para banners), `theme`, y `render` (tipo de presentación
|
|
80
|
+
* del paso dentro de un walkthrough heterogéneo: modal/banner/tooltip).
|
|
61
81
|
*/
|
|
62
82
|
style?: Record<string, unknown> | null;
|
|
83
|
+
/**
|
|
84
|
+
* Bloques de contenido ordenados (varios títulos/textos/botones/imágenes).
|
|
85
|
+
* Si está presente y no vacío, tiene prioridad sobre los campos escalares.
|
|
86
|
+
*/
|
|
87
|
+
blocks?: StepBlock[] | null;
|
|
63
88
|
/** Solo guías `custom`: código a renderizar en un iframe sandbox + ubicación. */
|
|
64
89
|
html?: string | null;
|
|
65
90
|
css?: string | null;
|
|
@@ -47,6 +47,25 @@ interface ActivationRules {
|
|
|
47
47
|
eventName?: string;
|
|
48
48
|
delayMs?: number;
|
|
49
49
|
}
|
|
50
|
+
/** Tipo de un bloque de contenido (para pasos con varios títulos/textos/botones). */
|
|
51
|
+
type StepBlockType = 'title' | 'text' | 'button' | 'image';
|
|
52
|
+
/**
|
|
53
|
+
* Un bloque de contenido dentro de un paso. Permite componer un paso con VARIOS
|
|
54
|
+
* títulos/textos/botones/imágenes en orden (no solo un título + un texto + un
|
|
55
|
+
* CTA). Si `step.blocks` está presente, el renderer lo usa; si no, cae a los
|
|
56
|
+
* campos escalares (`title`/`content`/`ctaText`/`imageUrl`) — retrocompatible.
|
|
57
|
+
*/
|
|
58
|
+
interface StepBlock {
|
|
59
|
+
type: StepBlockType;
|
|
60
|
+
/** title/text/button: el texto. */
|
|
61
|
+
text?: string | null;
|
|
62
|
+
/** image: la URL; button con acción `url`: el destino. */
|
|
63
|
+
url?: string | null;
|
|
64
|
+
/** button: qué hace al hacer click. */
|
|
65
|
+
action?: CtaAction | null;
|
|
66
|
+
/** Estilo por-bloque (align/color/fontSize/margin…). Se aplica inline. */
|
|
67
|
+
style?: Record<string, unknown> | null;
|
|
68
|
+
}
|
|
50
69
|
interface GuideStep {
|
|
51
70
|
title: string;
|
|
52
71
|
content: string;
|
|
@@ -57,9 +76,15 @@ interface GuideStep {
|
|
|
57
76
|
/**
|
|
58
77
|
* Free-form JSONB. Convención: si `ctaAction === 'url'`, la URL destino
|
|
59
78
|
* se lee de `style.ctaUrl`. Otras keys que puede traer: `position`
|
|
60
|
-
* (`top` | `bottom` para banners), `theme
|
|
79
|
+
* (`top` | `bottom` para banners), `theme`, y `render` (tipo de presentación
|
|
80
|
+
* del paso dentro de un walkthrough heterogéneo: modal/banner/tooltip).
|
|
61
81
|
*/
|
|
62
82
|
style?: Record<string, unknown> | null;
|
|
83
|
+
/**
|
|
84
|
+
* Bloques de contenido ordenados (varios títulos/textos/botones/imágenes).
|
|
85
|
+
* Si está presente y no vacío, tiene prioridad sobre los campos escalares.
|
|
86
|
+
*/
|
|
87
|
+
blocks?: StepBlock[] | null;
|
|
63
88
|
/** Solo guías `custom`: código a renderizar en un iframe sandbox + ubicación. */
|
|
64
89
|
html?: string | null;
|
|
65
90
|
css?: string | null;
|
package/dist/index.cjs
CHANGED
|
@@ -378,6 +378,16 @@ var init_constants2 = __esm({
|
|
|
378
378
|
function buildStepContent(step, doc, callbacks) {
|
|
379
379
|
const container = doc.createElement("div");
|
|
380
380
|
container.className = "veo-guide-content";
|
|
381
|
+
const blocks = normalizeBlocks(step);
|
|
382
|
+
if (blocks) {
|
|
383
|
+
renderBlocks(container, blocks, doc, callbacks);
|
|
384
|
+
} else {
|
|
385
|
+
renderLegacyStep(container, step, doc, callbacks);
|
|
386
|
+
}
|
|
387
|
+
container.appendChild(createCloseButton(doc, () => callbacks.onDismiss()));
|
|
388
|
+
return container;
|
|
389
|
+
}
|
|
390
|
+
function renderLegacyStep(container, step, doc, callbacks) {
|
|
381
391
|
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
382
392
|
const img = doc.createElement("img");
|
|
383
393
|
img.className = "veo-guide-image";
|
|
@@ -412,8 +422,105 @@ function buildStepContent(step, doc, callbacks) {
|
|
|
412
422
|
actions.appendChild(cta);
|
|
413
423
|
}
|
|
414
424
|
container.appendChild(actions);
|
|
415
|
-
|
|
416
|
-
|
|
425
|
+
}
|
|
426
|
+
function normalizeBlocks(step) {
|
|
427
|
+
if (!Array.isArray(step.blocks) || step.blocks.length === 0) return null;
|
|
428
|
+
const valid = step.blocks.filter((b) => {
|
|
429
|
+
if (!b || typeof b !== "object") return false;
|
|
430
|
+
if (b.type === "image") return typeof b.url === "string" && isSafeUrl(b.url);
|
|
431
|
+
return b.type === "title" || b.type === "text" || b.type === "button";
|
|
432
|
+
});
|
|
433
|
+
return valid.length > 0 ? valid : null;
|
|
434
|
+
}
|
|
435
|
+
function renderBlocks(container, blocks, doc, callbacks) {
|
|
436
|
+
let i = 0;
|
|
437
|
+
while (i < blocks.length) {
|
|
438
|
+
const block = blocks[i];
|
|
439
|
+
if (block?.type === "button") {
|
|
440
|
+
const actions = doc.createElement("div");
|
|
441
|
+
actions.className = "veo-guide-actions";
|
|
442
|
+
while (i < blocks.length && blocks[i]?.type === "button") {
|
|
443
|
+
const btnBlock = blocks[i];
|
|
444
|
+
actions.appendChild(buildButtonBlock(btnBlock, doc, callbacks));
|
|
445
|
+
i++;
|
|
446
|
+
}
|
|
447
|
+
container.appendChild(actions);
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
const node = buildContentBlock(block, doc);
|
|
451
|
+
if (node) container.appendChild(node);
|
|
452
|
+
i++;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function buildContentBlock(block, doc) {
|
|
456
|
+
switch (block.type) {
|
|
457
|
+
case "title": {
|
|
458
|
+
const el = doc.createElement("h2");
|
|
459
|
+
el.className = "veo-guide-title";
|
|
460
|
+
el.textContent = typeof block.text === "string" ? block.text : "";
|
|
461
|
+
applyBlockStyle(el, block.style, "text");
|
|
462
|
+
return el;
|
|
463
|
+
}
|
|
464
|
+
case "text": {
|
|
465
|
+
const el = doc.createElement("p");
|
|
466
|
+
el.className = "veo-guide-text";
|
|
467
|
+
el.textContent = typeof block.text === "string" ? block.text : "";
|
|
468
|
+
applyBlockStyle(el, block.style, "text");
|
|
469
|
+
return el;
|
|
470
|
+
}
|
|
471
|
+
case "image": {
|
|
472
|
+
if (typeof block.url !== "string" || !isSafeUrl(block.url)) return null;
|
|
473
|
+
const el = doc.createElement("img");
|
|
474
|
+
el.className = "veo-guide-image";
|
|
475
|
+
el.src = block.url;
|
|
476
|
+
el.alt = "";
|
|
477
|
+
applyBlockStyle(el, block.style, "image");
|
|
478
|
+
return el;
|
|
479
|
+
}
|
|
480
|
+
default:
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
function buildButtonBlock(block, doc, callbacks) {
|
|
485
|
+
const btn = doc.createElement("button");
|
|
486
|
+
btn.type = "button";
|
|
487
|
+
btn.className = "veo-guide-cta";
|
|
488
|
+
btn.textContent = typeof block.text === "string" && block.text ? block.text : "OK";
|
|
489
|
+
applyBlockStyle(btn, block.style, "button");
|
|
490
|
+
btn.addEventListener("click", () => {
|
|
491
|
+
const action = block.action ?? "dismiss";
|
|
492
|
+
const url = action === "url" && typeof block.url === "string" && isSafeUrl(block.url) ? block.url : void 0;
|
|
493
|
+
callbacks.onCtaClick(action, url);
|
|
494
|
+
});
|
|
495
|
+
return btn;
|
|
496
|
+
}
|
|
497
|
+
function clampNum(n, min, max) {
|
|
498
|
+
return Math.min(max, Math.max(min, n));
|
|
499
|
+
}
|
|
500
|
+
function applyBlockStyle(el, style, kind) {
|
|
501
|
+
if (!style || typeof style !== "object") return;
|
|
502
|
+
const s = style;
|
|
503
|
+
const align = typeof s.align === "string" ? s.align : null;
|
|
504
|
+
if (align && (align === "left" || align === "center" || align === "right")) {
|
|
505
|
+
if (kind === "text") el.style.textAlign = align;
|
|
506
|
+
else el.style.alignSelf = BLOCK_ALIGN_SELF[align];
|
|
507
|
+
}
|
|
508
|
+
if (kind !== "image" && typeof s.color === "string") el.style.color = s.color;
|
|
509
|
+
if (kind === "text" && typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
|
|
510
|
+
el.style.fontSize = `${clampNum(s.fontSize, 8, 72)}px`;
|
|
511
|
+
}
|
|
512
|
+
if (kind === "image" && typeof s.width === "number" && Number.isFinite(s.width)) {
|
|
513
|
+
el.style.width = `${clampNum(s.width, 10, 100)}%`;
|
|
514
|
+
}
|
|
515
|
+
if (kind === "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
|
|
516
|
+
el.style.borderRadius = `${clampNum(s.radius, 0, 48)}px`;
|
|
517
|
+
}
|
|
518
|
+
if (typeof s.marginTop === "number" && Number.isFinite(s.marginTop)) {
|
|
519
|
+
el.style.marginTop = `${clampNum(s.marginTop, 0, 64)}px`;
|
|
520
|
+
}
|
|
521
|
+
if (typeof s.marginBottom === "number" && Number.isFinite(s.marginBottom)) {
|
|
522
|
+
el.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
|
|
523
|
+
}
|
|
417
524
|
}
|
|
418
525
|
function createCloseButton(doc, onClose, className = "veo-guide-close") {
|
|
419
526
|
const closeBtn = doc.createElement("button");
|
|
@@ -439,9 +546,92 @@ function readCtaUrl(step) {
|
|
|
439
546
|
if (typeof candidate !== "string") return void 0;
|
|
440
547
|
return isSafeUrl(candidate) ? candidate : void 0;
|
|
441
548
|
}
|
|
549
|
+
var BLOCK_ALIGN_SELF;
|
|
442
550
|
var init_block_builder = __esm({
|
|
443
551
|
"src/plugins/guides/block-builder.ts"() {
|
|
444
552
|
init_constants2();
|
|
553
|
+
BLOCK_ALIGN_SELF = {
|
|
554
|
+
left: "flex-start",
|
|
555
|
+
center: "center",
|
|
556
|
+
right: "flex-end"
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// src/plugins/guides/walkthrough-block-builder.ts
|
|
562
|
+
function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
|
|
563
|
+
const container = doc.createElement("div");
|
|
564
|
+
container.className = "veo-guide-content";
|
|
565
|
+
const counter = doc.createElement("div");
|
|
566
|
+
counter.className = "veo-walkthrough-counter";
|
|
567
|
+
counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
|
|
568
|
+
container.appendChild(counter);
|
|
569
|
+
const progress = doc.createElement("div");
|
|
570
|
+
progress.className = "veo-walkthrough-progress";
|
|
571
|
+
for (let i = 0; i < totalSteps; i++) {
|
|
572
|
+
const dot = doc.createElement("span");
|
|
573
|
+
dot.className = "veo-walkthrough-progress-dot";
|
|
574
|
+
if (i < stepIndex) dot.classList.add("completed");
|
|
575
|
+
if (i === stepIndex) dot.classList.add("active");
|
|
576
|
+
progress.appendChild(dot);
|
|
577
|
+
}
|
|
578
|
+
container.appendChild(progress);
|
|
579
|
+
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
580
|
+
const img = doc.createElement("img");
|
|
581
|
+
img.className = "veo-guide-image";
|
|
582
|
+
img.src = step.imageUrl;
|
|
583
|
+
img.alt = typeof step.title === "string" ? step.title : "";
|
|
584
|
+
container.appendChild(img);
|
|
585
|
+
}
|
|
586
|
+
if (typeof step.title === "string" && step.title) {
|
|
587
|
+
const heading = doc.createElement("h2");
|
|
588
|
+
heading.className = "veo-guide-title";
|
|
589
|
+
heading.textContent = step.title;
|
|
590
|
+
container.appendChild(heading);
|
|
591
|
+
}
|
|
592
|
+
if (typeof step.content === "string" && step.content) {
|
|
593
|
+
const paragraph = doc.createElement("p");
|
|
594
|
+
paragraph.className = "veo-guide-text";
|
|
595
|
+
paragraph.textContent = step.content;
|
|
596
|
+
container.appendChild(paragraph);
|
|
597
|
+
}
|
|
598
|
+
const actions = doc.createElement("div");
|
|
599
|
+
actions.className = "veo-walkthrough-actions";
|
|
600
|
+
const skipBtn = doc.createElement("button");
|
|
601
|
+
skipBtn.type = "button";
|
|
602
|
+
skipBtn.className = "veo-walkthrough-skip";
|
|
603
|
+
skipBtn.textContent = "Omitir";
|
|
604
|
+
skipBtn.addEventListener("click", () => callbacks.onSkip());
|
|
605
|
+
actions.appendChild(skipBtn);
|
|
606
|
+
const rightGroup = doc.createElement("div");
|
|
607
|
+
rightGroup.className = "veo-walkthrough-actions-right";
|
|
608
|
+
if (stepIndex > 0) {
|
|
609
|
+
const backBtn = doc.createElement("button");
|
|
610
|
+
backBtn.type = "button";
|
|
611
|
+
backBtn.className = "veo-walkthrough-btn-secondary";
|
|
612
|
+
backBtn.textContent = "Atr\xE1s";
|
|
613
|
+
backBtn.addEventListener("click", () => callbacks.onBack());
|
|
614
|
+
rightGroup.appendChild(backBtn);
|
|
615
|
+
}
|
|
616
|
+
const isLastStep = stepIndex === totalSteps - 1;
|
|
617
|
+
const primaryBtn = doc.createElement("button");
|
|
618
|
+
primaryBtn.type = "button";
|
|
619
|
+
primaryBtn.className = "veo-guide-cta";
|
|
620
|
+
const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
|
|
621
|
+
primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
|
|
622
|
+
primaryBtn.addEventListener("click", () => {
|
|
623
|
+
if (isLastStep) callbacks.onComplete();
|
|
624
|
+
else callbacks.onNext();
|
|
625
|
+
});
|
|
626
|
+
rightGroup.appendChild(primaryBtn);
|
|
627
|
+
actions.appendChild(rightGroup);
|
|
628
|
+
container.appendChild(actions);
|
|
629
|
+
container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
|
|
630
|
+
return container;
|
|
631
|
+
}
|
|
632
|
+
var init_walkthrough_block_builder = __esm({
|
|
633
|
+
"src/plugins/guides/walkthrough-block-builder.ts"() {
|
|
634
|
+
init_block_builder();
|
|
445
635
|
}
|
|
446
636
|
});
|
|
447
637
|
|
|
@@ -455,6 +645,9 @@ function applyDesignVars(host, style) {
|
|
|
455
645
|
if (typeof s.accentColor === "string" && COLOR_RE.test(s.accentColor.trim())) {
|
|
456
646
|
host.style.setProperty("--veo-primary", s.accentColor.trim());
|
|
457
647
|
}
|
|
648
|
+
if (typeof s.fontFamily === "string" && s.fontFamily.length <= 200 && !/[<>{}]/.test(s.fontFamily)) {
|
|
649
|
+
host.style.setProperty("--veo-font", s.fontFamily.trim());
|
|
650
|
+
}
|
|
458
651
|
if (s.theme === "dark") {
|
|
459
652
|
for (const [key, value] of Object.entries(DARK_THEME)) {
|
|
460
653
|
host.style.setProperty(key, value);
|
|
@@ -596,7 +789,7 @@ var init_styles = __esm({
|
|
|
596
789
|
--veo-actions-justify: flex-end;
|
|
597
790
|
all: initial;
|
|
598
791
|
}
|
|
599
|
-
* { box-sizing: border-box; font-family: -apple-system, system-ui, "Segoe UI", Roboto, sans-serif; }
|
|
792
|
+
* { box-sizing: border-box; font-family: var(--veo-font, -apple-system, system-ui, "Segoe UI", Roboto, sans-serif); }
|
|
600
793
|
|
|
601
794
|
.veo-modal-overlay {
|
|
602
795
|
position: fixed; inset: 0;
|
|
@@ -939,10 +1132,12 @@ var BannerRenderer;
|
|
|
939
1132
|
var init_banner_renderer = __esm({
|
|
940
1133
|
"src/plugins/guides/renderers/banner-renderer.ts"() {
|
|
941
1134
|
init_block_builder();
|
|
1135
|
+
init_walkthrough_block_builder();
|
|
942
1136
|
init_base_renderer();
|
|
943
1137
|
BannerRenderer = class extends BaseRenderer {
|
|
944
1138
|
render(context) {
|
|
945
|
-
const
|
|
1139
|
+
const idx = context.nav?.stepIndex ?? 0;
|
|
1140
|
+
const step = context.guide.guideSteps[idx];
|
|
946
1141
|
if (!step) return;
|
|
947
1142
|
const { root } = this.createHost();
|
|
948
1143
|
this.applyDesign(step.style);
|
|
@@ -951,27 +1146,25 @@ var init_banner_renderer = __esm({
|
|
|
951
1146
|
const banner = ownerDocument.createElement("div");
|
|
952
1147
|
banner.className = `veo-banner veo-banner-${position}`;
|
|
953
1148
|
const dismiss = (action, ctaUrl) => {
|
|
954
|
-
context.onInteraction({
|
|
955
|
-
guideId: context.guide.guideId,
|
|
956
|
-
stepIndex: 0,
|
|
957
|
-
action
|
|
958
|
-
});
|
|
1149
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action });
|
|
959
1150
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
960
1151
|
context.onClose();
|
|
961
1152
|
};
|
|
962
|
-
const content =
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1153
|
+
const content = context.nav ? buildWalkthroughStepContent(
|
|
1154
|
+
step,
|
|
1155
|
+
context.nav.stepIndex,
|
|
1156
|
+
context.nav.totalSteps,
|
|
1157
|
+
ownerDocument,
|
|
1158
|
+
context.nav.callbacks
|
|
1159
|
+
) : buildStepContent(step, ownerDocument, {
|
|
1160
|
+
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
966
1161
|
onDismiss: () => dismiss("dismissed")
|
|
967
1162
|
});
|
|
968
1163
|
banner.appendChild(content);
|
|
969
1164
|
root.appendChild(banner);
|
|
970
|
-
context.
|
|
971
|
-
guideId: context.guide.guideId,
|
|
972
|
-
|
|
973
|
-
action: "shown"
|
|
974
|
-
});
|
|
1165
|
+
if (!context.nav) {
|
|
1166
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
1167
|
+
}
|
|
975
1168
|
}
|
|
976
1169
|
};
|
|
977
1170
|
}
|
|
@@ -1693,10 +1886,12 @@ var ModalRenderer;
|
|
|
1693
1886
|
var init_modal_renderer = __esm({
|
|
1694
1887
|
"src/plugins/guides/renderers/modal-renderer.ts"() {
|
|
1695
1888
|
init_block_builder();
|
|
1889
|
+
init_walkthrough_block_builder();
|
|
1696
1890
|
init_base_renderer();
|
|
1697
1891
|
ModalRenderer = class extends BaseRenderer {
|
|
1698
1892
|
render(context) {
|
|
1699
|
-
const
|
|
1893
|
+
const idx = context.nav?.stepIndex ?? 0;
|
|
1894
|
+
const step = context.guide.guideSteps[idx];
|
|
1700
1895
|
if (!step) return;
|
|
1701
1896
|
const { root } = this.createHost();
|
|
1702
1897
|
this.applyDesign(step.style);
|
|
@@ -1706,36 +1901,38 @@ var init_modal_renderer = __esm({
|
|
|
1706
1901
|
const card = ownerDocument.createElement("div");
|
|
1707
1902
|
card.className = "veo-modal-card";
|
|
1708
1903
|
const dismiss = (action, ctaUrl) => {
|
|
1709
|
-
context.onInteraction({
|
|
1710
|
-
guideId: context.guide.guideId,
|
|
1711
|
-
stepIndex: 0,
|
|
1712
|
-
action
|
|
1713
|
-
});
|
|
1904
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action });
|
|
1714
1905
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
1715
1906
|
context.onClose();
|
|
1716
1907
|
};
|
|
1717
|
-
const
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1908
|
+
const onBackdropOrEsc = () => {
|
|
1909
|
+
if (context.nav) context.nav.callbacks.onSkip();
|
|
1910
|
+
else dismiss("dismissed");
|
|
1911
|
+
};
|
|
1912
|
+
const content = context.nav ? buildWalkthroughStepContent(
|
|
1913
|
+
step,
|
|
1914
|
+
context.nav.stepIndex,
|
|
1915
|
+
context.nav.totalSteps,
|
|
1916
|
+
ownerDocument,
|
|
1917
|
+
context.nav.callbacks
|
|
1918
|
+
) : buildStepContent(step, ownerDocument, {
|
|
1919
|
+
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
1721
1920
|
onDismiss: () => dismiss("dismissed")
|
|
1722
1921
|
});
|
|
1723
1922
|
card.appendChild(content);
|
|
1724
1923
|
overlay.appendChild(card);
|
|
1725
1924
|
root.appendChild(overlay);
|
|
1726
1925
|
overlay.addEventListener("click", (e) => {
|
|
1727
|
-
if (e.target === overlay)
|
|
1926
|
+
if (e.target === overlay) onBackdropOrEsc();
|
|
1728
1927
|
});
|
|
1729
1928
|
const onKey = (e) => {
|
|
1730
|
-
if (e.key === "Escape")
|
|
1929
|
+
if (e.key === "Escape") onBackdropOrEsc();
|
|
1731
1930
|
};
|
|
1732
1931
|
document.addEventListener("keydown", onKey);
|
|
1733
1932
|
this.registerCleanup(() => document.removeEventListener("keydown", onKey));
|
|
1734
|
-
context.
|
|
1735
|
-
guideId: context.guide.guideId,
|
|
1736
|
-
|
|
1737
|
-
action: "shown"
|
|
1738
|
-
});
|
|
1933
|
+
if (!context.nav) {
|
|
1934
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
1935
|
+
}
|
|
1739
1936
|
}
|
|
1740
1937
|
};
|
|
1741
1938
|
}
|
|
@@ -1830,83 +2027,6 @@ var init_tooltip_renderer = __esm({
|
|
|
1830
2027
|
};
|
|
1831
2028
|
}
|
|
1832
2029
|
});
|
|
1833
|
-
|
|
1834
|
-
// src/plugins/guides/walkthrough-block-builder.ts
|
|
1835
|
-
function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
|
|
1836
|
-
const container = doc.createElement("div");
|
|
1837
|
-
container.className = "veo-guide-content";
|
|
1838
|
-
const counter = doc.createElement("div");
|
|
1839
|
-
counter.className = "veo-walkthrough-counter";
|
|
1840
|
-
counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
|
|
1841
|
-
container.appendChild(counter);
|
|
1842
|
-
const progress = doc.createElement("div");
|
|
1843
|
-
progress.className = "veo-walkthrough-progress";
|
|
1844
|
-
for (let i = 0; i < totalSteps; i++) {
|
|
1845
|
-
const dot = doc.createElement("span");
|
|
1846
|
-
dot.className = "veo-walkthrough-progress-dot";
|
|
1847
|
-
if (i < stepIndex) dot.classList.add("completed");
|
|
1848
|
-
if (i === stepIndex) dot.classList.add("active");
|
|
1849
|
-
progress.appendChild(dot);
|
|
1850
|
-
}
|
|
1851
|
-
container.appendChild(progress);
|
|
1852
|
-
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
1853
|
-
const img = doc.createElement("img");
|
|
1854
|
-
img.className = "veo-guide-image";
|
|
1855
|
-
img.src = step.imageUrl;
|
|
1856
|
-
img.alt = typeof step.title === "string" ? step.title : "";
|
|
1857
|
-
container.appendChild(img);
|
|
1858
|
-
}
|
|
1859
|
-
if (typeof step.title === "string" && step.title) {
|
|
1860
|
-
const heading = doc.createElement("h2");
|
|
1861
|
-
heading.className = "veo-guide-title";
|
|
1862
|
-
heading.textContent = step.title;
|
|
1863
|
-
container.appendChild(heading);
|
|
1864
|
-
}
|
|
1865
|
-
if (typeof step.content === "string" && step.content) {
|
|
1866
|
-
const paragraph = doc.createElement("p");
|
|
1867
|
-
paragraph.className = "veo-guide-text";
|
|
1868
|
-
paragraph.textContent = step.content;
|
|
1869
|
-
container.appendChild(paragraph);
|
|
1870
|
-
}
|
|
1871
|
-
const actions = doc.createElement("div");
|
|
1872
|
-
actions.className = "veo-walkthrough-actions";
|
|
1873
|
-
const skipBtn = doc.createElement("button");
|
|
1874
|
-
skipBtn.type = "button";
|
|
1875
|
-
skipBtn.className = "veo-walkthrough-skip";
|
|
1876
|
-
skipBtn.textContent = "Omitir";
|
|
1877
|
-
skipBtn.addEventListener("click", () => callbacks.onSkip());
|
|
1878
|
-
actions.appendChild(skipBtn);
|
|
1879
|
-
const rightGroup = doc.createElement("div");
|
|
1880
|
-
rightGroup.className = "veo-walkthrough-actions-right";
|
|
1881
|
-
if (stepIndex > 0) {
|
|
1882
|
-
const backBtn = doc.createElement("button");
|
|
1883
|
-
backBtn.type = "button";
|
|
1884
|
-
backBtn.className = "veo-walkthrough-btn-secondary";
|
|
1885
|
-
backBtn.textContent = "Atr\xE1s";
|
|
1886
|
-
backBtn.addEventListener("click", () => callbacks.onBack());
|
|
1887
|
-
rightGroup.appendChild(backBtn);
|
|
1888
|
-
}
|
|
1889
|
-
const isLastStep = stepIndex === totalSteps - 1;
|
|
1890
|
-
const primaryBtn = doc.createElement("button");
|
|
1891
|
-
primaryBtn.type = "button";
|
|
1892
|
-
primaryBtn.className = "veo-guide-cta";
|
|
1893
|
-
const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
|
|
1894
|
-
primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
|
|
1895
|
-
primaryBtn.addEventListener("click", () => {
|
|
1896
|
-
if (isLastStep) callbacks.onComplete();
|
|
1897
|
-
else callbacks.onNext();
|
|
1898
|
-
});
|
|
1899
|
-
rightGroup.appendChild(primaryBtn);
|
|
1900
|
-
actions.appendChild(rightGroup);
|
|
1901
|
-
container.appendChild(actions);
|
|
1902
|
-
container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
|
|
1903
|
-
return container;
|
|
1904
|
-
}
|
|
1905
|
-
var init_walkthrough_block_builder = __esm({
|
|
1906
|
-
"src/plugins/guides/walkthrough-block-builder.ts"() {
|
|
1907
|
-
init_block_builder();
|
|
1908
|
-
}
|
|
1909
|
-
});
|
|
1910
2030
|
var WalkthroughRenderer;
|
|
1911
2031
|
var init_walkthrough_renderer = __esm({
|
|
1912
2032
|
"src/plugins/guides/renderers/walkthrough-renderer.ts"() {
|
|
@@ -2039,6 +2159,7 @@ var init_guide_preview = __esm({
|
|
|
2039
2159
|
GuidePreviewController = class {
|
|
2040
2160
|
constructor() {
|
|
2041
2161
|
this.singleStep = null;
|
|
2162
|
+
/** Renderer del paso de walkthrough activo (walkthrough tooltip o modal/banner). */
|
|
2042
2163
|
this.walkthrough = null;
|
|
2043
2164
|
this.walkthroughGuide = null;
|
|
2044
2165
|
this.walkthroughIndex = 0;
|
|
@@ -2111,7 +2232,6 @@ var init_guide_preview = __esm({
|
|
|
2111
2232
|
safeDestroy(this.walkthrough);
|
|
2112
2233
|
this.walkthrough = null;
|
|
2113
2234
|
}
|
|
2114
|
-
const renderer = new WalkthroughRenderer();
|
|
2115
2235
|
const callbacks = {
|
|
2116
2236
|
onNext: () => {
|
|
2117
2237
|
const next = this.walkthroughIndex + 1;
|
|
@@ -2131,6 +2251,30 @@ var init_guide_preview = __esm({
|
|
|
2131
2251
|
onSkip: () => this.close(),
|
|
2132
2252
|
onComplete: () => this.close()
|
|
2133
2253
|
};
|
|
2254
|
+
const step = guide.guideSteps[index];
|
|
2255
|
+
const render = step?.style?.render;
|
|
2256
|
+
if (render === "modal" || render === "banner") {
|
|
2257
|
+
const renderer2 = render === "modal" ? new ModalRenderer() : new BannerRenderer();
|
|
2258
|
+
try {
|
|
2259
|
+
renderer2.render({
|
|
2260
|
+
guide,
|
|
2261
|
+
onInteraction: () => {
|
|
2262
|
+
},
|
|
2263
|
+
onClose: () => this.close(),
|
|
2264
|
+
nav: { stepIndex: index, totalSteps: guide.guideSteps.length, callbacks }
|
|
2265
|
+
});
|
|
2266
|
+
} catch {
|
|
2267
|
+
safeDestroy(renderer2);
|
|
2268
|
+
return false;
|
|
2269
|
+
}
|
|
2270
|
+
if (this.walkthroughGuide !== guide) {
|
|
2271
|
+
safeDestroy(renderer2);
|
|
2272
|
+
return false;
|
|
2273
|
+
}
|
|
2274
|
+
this.walkthrough = renderer2;
|
|
2275
|
+
return true;
|
|
2276
|
+
}
|
|
2277
|
+
const renderer = new WalkthroughRenderer();
|
|
2134
2278
|
let success = false;
|
|
2135
2279
|
try {
|
|
2136
2280
|
success = await renderer.render({ guide, currentStepIndex: index, callbacks });
|
|
@@ -4198,6 +4342,11 @@ var GuidesController = class {
|
|
|
4198
4342
|
*/
|
|
4199
4343
|
this.activeByGuideId = /* @__PURE__ */ new Map();
|
|
4200
4344
|
this.dispatched = /* @__PURE__ */ new Set();
|
|
4345
|
+
/**
|
|
4346
|
+
* Renderer del paso de walkthrough activo. Puede ser el `WalkthroughRenderer`
|
|
4347
|
+
* (tooltip anclado) o un Modal/Banner con navegación (walkthrough heterogéneo);
|
|
4348
|
+
* tipamos por `destroy()`, lo único que el controller necesita.
|
|
4349
|
+
*/
|
|
4201
4350
|
this.activeWalkthroughRenderer = null;
|
|
4202
4351
|
this.activeWalkthroughGuide = null;
|
|
4203
4352
|
this.debug = config.debug === true;
|
|
@@ -4378,7 +4527,6 @@ var GuidesController = class {
|
|
|
4378
4527
|
this.activeWalkthroughRenderer.destroy();
|
|
4379
4528
|
this.activeWalkthroughRenderer = null;
|
|
4380
4529
|
}
|
|
4381
|
-
const renderer = new WalkthroughRenderer();
|
|
4382
4530
|
this.activeWalkthroughGuide = guide;
|
|
4383
4531
|
const callbacks = {
|
|
4384
4532
|
onNext: () => this.handleWalkthroughNext(endUserId, sessionId, pageUrl),
|
|
@@ -4386,6 +4534,28 @@ var GuidesController = class {
|
|
|
4386
4534
|
onSkip: () => this.handleWalkthroughSkip(endUserId, sessionId, pageUrl),
|
|
4387
4535
|
onComplete: () => this.handleWalkthroughComplete(endUserId, sessionId, pageUrl)
|
|
4388
4536
|
};
|
|
4537
|
+
const step = guide.guideSteps[stepIndex];
|
|
4538
|
+
const render = readStepRender(step);
|
|
4539
|
+
if (render === "modal" || render === "banner") {
|
|
4540
|
+
const renderer2 = render === "modal" ? new ModalRenderer() : new BannerRenderer();
|
|
4541
|
+
try {
|
|
4542
|
+
renderer2.render({
|
|
4543
|
+
guide,
|
|
4544
|
+
onInteraction: () => {
|
|
4545
|
+
},
|
|
4546
|
+
onClose: () => {
|
|
4547
|
+
},
|
|
4548
|
+
nav: { stepIndex, totalSteps: guide.guideSteps.length, callbacks }
|
|
4549
|
+
});
|
|
4550
|
+
} catch (err) {
|
|
4551
|
+
if (this.debug) console.error("[veo] sequence renderer threw:", err);
|
|
4552
|
+
this.tearDownWalkthrough();
|
|
4553
|
+
return false;
|
|
4554
|
+
}
|
|
4555
|
+
this.activeWalkthroughRenderer = renderer2;
|
|
4556
|
+
return true;
|
|
4557
|
+
}
|
|
4558
|
+
const renderer = new WalkthroughRenderer();
|
|
4389
4559
|
let success = false;
|
|
4390
4560
|
try {
|
|
4391
4561
|
success = await renderer.render({ guide, currentStepIndex: stepIndex, callbacks });
|
|
@@ -4608,6 +4778,10 @@ function extractPath(url) {
|
|
|
4608
4778
|
return "/";
|
|
4609
4779
|
}
|
|
4610
4780
|
}
|
|
4781
|
+
function readStepRender(step) {
|
|
4782
|
+
const r = step?.style?.render;
|
|
4783
|
+
return typeof r === "string" ? r : "walkthrough";
|
|
4784
|
+
}
|
|
4611
4785
|
|
|
4612
4786
|
// src/plugins/guides/index.ts
|
|
4613
4787
|
init_guide_preview();
|