veo-sdk 0.3.15 → 0.4.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/dist/builder-ZDNVF6KG.mjs +5 -0
- package/dist/{builder-BKLDSVN5.mjs.map → builder-ZDNVF6KG.mjs.map} +1 -1
- package/dist/builder.cjs +1085 -204
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +38 -2
- package/dist/builder.d.ts +38 -2
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-EF4BZR5R.mjs → chunk-I55Z7UGJ.mjs} +269 -5
- package/dist/chunk-I55Z7UGJ.mjs.map +1 -0
- package/dist/{chunk-2INDL77W.mjs → chunk-ICKEQ7VC.mjs} +797 -180
- package/dist/chunk-ICKEQ7VC.mjs.map +1 -0
- package/dist/{chunk-WVLVN422.mjs → chunk-MA2BXLZ7.mjs} +67 -11
- package/dist/chunk-MA2BXLZ7.mjs.map +1 -0
- package/dist/{guide-preview-DewCTa-B.d.cts → guide-preview-D_xhRVLe.d.cts} +20 -2
- package/dist/{guide-preview-DewCTa-B.d.ts → guide-preview-D_xhRVLe.d.ts} +20 -2
- package/dist/index.cjs +1180 -211
- 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 +7 -6
- package/dist/index.mjs.map +1 -1
- package/dist/veo-builder.js +125 -9
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +86 -4
- package/dist/veo-guides.js.map +1 -1
- package/dist/veo.js +1 -1
- package/dist/veo.js.map +1 -1
- package/package.json +1 -1
- package/dist/builder-BKLDSVN5.mjs +0 -5
- package/dist/chunk-2INDL77W.mjs.map +0 -1
- package/dist/chunk-EF4BZR5R.mjs.map +0 -1
- package/dist/chunk-WVLVN422.mjs.map +0 -1
|
@@ -162,6 +162,112 @@ var WALKTHROUGH_STATE_KEY_PREFIX = "veo:walkthrough_state:";
|
|
|
162
162
|
var WALKTHROUGH_ABANDONMENT_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
163
163
|
var WALKTHROUGH_STEP_SELECTOR_TIMEOUT_MS = 5e3;
|
|
164
164
|
|
|
165
|
+
// src/plugins/guides/rich-text.ts
|
|
166
|
+
var ALLOWED_TAGS = {
|
|
167
|
+
P: "p",
|
|
168
|
+
BR: "br",
|
|
169
|
+
STRONG: "strong",
|
|
170
|
+
B: "strong",
|
|
171
|
+
EM: "em",
|
|
172
|
+
I: "em",
|
|
173
|
+
U: "u",
|
|
174
|
+
S: "s",
|
|
175
|
+
UL: "ul",
|
|
176
|
+
OL: "ol",
|
|
177
|
+
LI: "li",
|
|
178
|
+
A: "a"
|
|
179
|
+
};
|
|
180
|
+
var DROP_TAGS = /* @__PURE__ */ new Set([
|
|
181
|
+
"SCRIPT",
|
|
182
|
+
"STYLE",
|
|
183
|
+
"TEMPLATE",
|
|
184
|
+
"IFRAME",
|
|
185
|
+
"OBJECT",
|
|
186
|
+
"EMBED",
|
|
187
|
+
"NOSCRIPT",
|
|
188
|
+
"TITLE",
|
|
189
|
+
"TEXTAREA",
|
|
190
|
+
"SELECT",
|
|
191
|
+
"SVG",
|
|
192
|
+
"MATH"
|
|
193
|
+
]);
|
|
194
|
+
var MAX_INPUT_LEN = 16 * 1024;
|
|
195
|
+
var MAX_DEPTH = 20;
|
|
196
|
+
var MAX_NODES = 1e3;
|
|
197
|
+
function renderRichText(html, doc) {
|
|
198
|
+
const fragment = doc.createDocumentFragment();
|
|
199
|
+
if (typeof html !== "string" || html.length === 0) return fragment;
|
|
200
|
+
let parsed;
|
|
201
|
+
try {
|
|
202
|
+
parsed = new DOMParser().parseFromString(
|
|
203
|
+
html.length > MAX_INPUT_LEN ? html.slice(0, MAX_INPUT_LEN) : html,
|
|
204
|
+
"text/html"
|
|
205
|
+
);
|
|
206
|
+
} catch {
|
|
207
|
+
fragment.appendChild(doc.createTextNode(html));
|
|
208
|
+
return fragment;
|
|
209
|
+
}
|
|
210
|
+
const budget = { nodes: 0, exceeded: false };
|
|
211
|
+
for (const child of Array.from(parsed.body.childNodes)) {
|
|
212
|
+
const rebuilt = rebuildNode(child, doc, 0, budget);
|
|
213
|
+
if (rebuilt) fragment.appendChild(rebuilt);
|
|
214
|
+
}
|
|
215
|
+
if (budget.exceeded) {
|
|
216
|
+
const plain = doc.createDocumentFragment();
|
|
217
|
+
plain.appendChild(doc.createTextNode(parsed.body.textContent ?? ""));
|
|
218
|
+
return plain;
|
|
219
|
+
}
|
|
220
|
+
return fragment;
|
|
221
|
+
}
|
|
222
|
+
function rebuildNode(node, doc, depth, budget) {
|
|
223
|
+
if (budget.exceeded) return null;
|
|
224
|
+
if (++budget.nodes > MAX_NODES || depth > MAX_DEPTH) {
|
|
225
|
+
budget.exceeded = true;
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
229
|
+
return doc.createTextNode(node.textContent ?? "");
|
|
230
|
+
}
|
|
231
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return null;
|
|
232
|
+
const el = node;
|
|
233
|
+
if (DROP_TAGS.has(el.tagName)) return null;
|
|
234
|
+
const mapped = ALLOWED_TAGS[el.tagName];
|
|
235
|
+
if (!mapped) {
|
|
236
|
+
const frag = doc.createDocumentFragment();
|
|
237
|
+
for (const child of Array.from(el.childNodes)) {
|
|
238
|
+
const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
|
|
239
|
+
if (rebuilt2) frag.appendChild(rebuilt2);
|
|
240
|
+
}
|
|
241
|
+
return frag;
|
|
242
|
+
}
|
|
243
|
+
if (mapped === "a") {
|
|
244
|
+
const href = el.getAttribute("href") ?? "";
|
|
245
|
+
if (!isSafeUrl(href)) {
|
|
246
|
+
const frag = doc.createDocumentFragment();
|
|
247
|
+
for (const child of Array.from(el.childNodes)) {
|
|
248
|
+
const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
|
|
249
|
+
if (rebuilt2) frag.appendChild(rebuilt2);
|
|
250
|
+
}
|
|
251
|
+
return frag;
|
|
252
|
+
}
|
|
253
|
+
const a = doc.createElement("a");
|
|
254
|
+
a.setAttribute("href", href);
|
|
255
|
+
a.setAttribute("target", "_blank");
|
|
256
|
+
a.setAttribute("rel", "noopener noreferrer nofollow");
|
|
257
|
+
for (const child of Array.from(el.childNodes)) {
|
|
258
|
+
const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
|
|
259
|
+
if (rebuilt2) a.appendChild(rebuilt2);
|
|
260
|
+
}
|
|
261
|
+
return a;
|
|
262
|
+
}
|
|
263
|
+
const rebuilt = doc.createElement(mapped);
|
|
264
|
+
for (const child of Array.from(el.childNodes)) {
|
|
265
|
+
const childNode = rebuildNode(child, doc, depth + 1, budget);
|
|
266
|
+
if (childNode) rebuilt.appendChild(childNode);
|
|
267
|
+
}
|
|
268
|
+
return rebuilt;
|
|
269
|
+
}
|
|
270
|
+
|
|
165
271
|
// src/plugins/guides/block-builder.ts
|
|
166
272
|
function buildStepContent(step, doc, callbacks) {
|
|
167
273
|
const container = doc.createElement("div");
|
|
@@ -256,9 +362,13 @@ function buildContentBlock(block, doc) {
|
|
|
256
362
|
return el;
|
|
257
363
|
}
|
|
258
364
|
case "text": {
|
|
259
|
-
const el = doc.createElement("
|
|
365
|
+
const el = doc.createElement("div");
|
|
260
366
|
el.className = "veo-guide-text";
|
|
261
|
-
|
|
367
|
+
if (typeof block.html === "string" && block.html) {
|
|
368
|
+
el.appendChild(renderRichText(block.html, doc));
|
|
369
|
+
} else {
|
|
370
|
+
el.textContent = typeof block.text === "string" ? block.text : "";
|
|
371
|
+
}
|
|
262
372
|
applyBlockStyle(el, block.style, "text");
|
|
263
373
|
return el;
|
|
264
374
|
}
|
|
@@ -284,7 +394,11 @@ function buildButtonBlock(block, doc, callbacks) {
|
|
|
284
394
|
btn.addEventListener("click", () => {
|
|
285
395
|
const action = block.action ?? "dismiss";
|
|
286
396
|
const url = action === "url" && typeof block.url === "string" && isSafeUrl(block.url) ? block.url : void 0;
|
|
287
|
-
|
|
397
|
+
const meta = {
|
|
398
|
+
...typeof block.id === "string" && block.id ? { buttonId: block.id } : {},
|
|
399
|
+
...typeof block.text === "string" && block.text ? { buttonText: block.text } : {}
|
|
400
|
+
};
|
|
401
|
+
callbacks.onCtaClick(action, url, Object.keys(meta).length ? meta : void 0);
|
|
288
402
|
});
|
|
289
403
|
return btn;
|
|
290
404
|
}
|
|
@@ -299,6 +413,10 @@ function clampNum(n, min, max) {
|
|
|
299
413
|
function applyBlockStyle(el, style, kind) {
|
|
300
414
|
if (!style || typeof style !== "object") return;
|
|
301
415
|
const s = style;
|
|
416
|
+
if (kind === "image" && s.bleed === true) {
|
|
417
|
+
el.classList.add("veo-guide-image--bleed");
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
302
420
|
const align = typeof s.align === "string" ? s.align : null;
|
|
303
421
|
if (align && (align === "left" || align === "center" || align === "right")) {
|
|
304
422
|
if (kind === "text") el.style.textAlign = align;
|
|
@@ -346,78 +464,6 @@ function readCtaUrl(step) {
|
|
|
346
464
|
return isSafeUrl(candidate) ? candidate : void 0;
|
|
347
465
|
}
|
|
348
466
|
|
|
349
|
-
// src/plugins/guides/walkthrough-block-builder.ts
|
|
350
|
-
function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
|
|
351
|
-
const container = doc.createElement("div");
|
|
352
|
-
container.className = "veo-guide-content";
|
|
353
|
-
const counter = doc.createElement("div");
|
|
354
|
-
counter.className = "veo-walkthrough-counter";
|
|
355
|
-
counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
|
|
356
|
-
container.appendChild(counter);
|
|
357
|
-
const progress = doc.createElement("div");
|
|
358
|
-
progress.className = "veo-walkthrough-progress";
|
|
359
|
-
for (let i = 0; i < totalSteps; i++) {
|
|
360
|
-
const dot = doc.createElement("span");
|
|
361
|
-
dot.className = "veo-walkthrough-progress-dot";
|
|
362
|
-
if (i < stepIndex) dot.classList.add("completed");
|
|
363
|
-
if (i === stepIndex) dot.classList.add("active");
|
|
364
|
-
progress.appendChild(dot);
|
|
365
|
-
}
|
|
366
|
-
container.appendChild(progress);
|
|
367
|
-
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
368
|
-
const img = doc.createElement("img");
|
|
369
|
-
img.className = "veo-guide-image";
|
|
370
|
-
img.src = step.imageUrl;
|
|
371
|
-
img.alt = typeof step.title === "string" ? step.title : "";
|
|
372
|
-
container.appendChild(img);
|
|
373
|
-
}
|
|
374
|
-
if (typeof step.title === "string" && step.title) {
|
|
375
|
-
const heading = doc.createElement("h2");
|
|
376
|
-
heading.className = "veo-guide-title";
|
|
377
|
-
heading.textContent = step.title;
|
|
378
|
-
container.appendChild(heading);
|
|
379
|
-
}
|
|
380
|
-
if (typeof step.content === "string" && step.content) {
|
|
381
|
-
const paragraph = doc.createElement("p");
|
|
382
|
-
paragraph.className = "veo-guide-text";
|
|
383
|
-
paragraph.textContent = step.content;
|
|
384
|
-
container.appendChild(paragraph);
|
|
385
|
-
}
|
|
386
|
-
const actions = doc.createElement("div");
|
|
387
|
-
actions.className = "veo-walkthrough-actions";
|
|
388
|
-
const skipBtn = doc.createElement("button");
|
|
389
|
-
skipBtn.type = "button";
|
|
390
|
-
skipBtn.className = "veo-walkthrough-skip";
|
|
391
|
-
skipBtn.textContent = "Omitir";
|
|
392
|
-
skipBtn.addEventListener("click", () => callbacks.onSkip());
|
|
393
|
-
actions.appendChild(skipBtn);
|
|
394
|
-
const rightGroup = doc.createElement("div");
|
|
395
|
-
rightGroup.className = "veo-walkthrough-actions-right";
|
|
396
|
-
if (stepIndex > 0) {
|
|
397
|
-
const backBtn = doc.createElement("button");
|
|
398
|
-
backBtn.type = "button";
|
|
399
|
-
backBtn.className = "veo-walkthrough-btn-secondary";
|
|
400
|
-
backBtn.textContent = "Atr\xE1s";
|
|
401
|
-
backBtn.addEventListener("click", () => callbacks.onBack());
|
|
402
|
-
rightGroup.appendChild(backBtn);
|
|
403
|
-
}
|
|
404
|
-
const isLastStep = stepIndex === totalSteps - 1;
|
|
405
|
-
const primaryBtn = doc.createElement("button");
|
|
406
|
-
primaryBtn.type = "button";
|
|
407
|
-
primaryBtn.className = "veo-guide-cta";
|
|
408
|
-
const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
|
|
409
|
-
primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
|
|
410
|
-
primaryBtn.addEventListener("click", () => {
|
|
411
|
-
if (isLastStep) callbacks.onComplete();
|
|
412
|
-
else callbacks.onNext();
|
|
413
|
-
});
|
|
414
|
-
rightGroup.appendChild(primaryBtn);
|
|
415
|
-
actions.appendChild(rightGroup);
|
|
416
|
-
container.appendChild(actions);
|
|
417
|
-
container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
|
|
418
|
-
return container;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
467
|
// src/plugins/guides/guide-design.ts
|
|
422
468
|
var DARK_THEME = {
|
|
423
469
|
"--veo-bg": "#1f2937",
|
|
@@ -490,6 +536,9 @@ function applyDesignVars(host, style) {
|
|
|
490
536
|
if (typeof s.width === "number" && Number.isFinite(s.width)) {
|
|
491
537
|
host.style.setProperty("--veo-width", `${clamp(s.width, 220, 720)}px`);
|
|
492
538
|
}
|
|
539
|
+
if (typeof s.height === "number" && Number.isFinite(s.height)) {
|
|
540
|
+
host.style.setProperty("--veo-min-h", `${clamp(s.height, 120, 900)}px`);
|
|
541
|
+
}
|
|
493
542
|
if (s.align === "left" || s.align === "center" || s.align === "right") {
|
|
494
543
|
host.style.setProperty("--veo-actions-justify", ALIGN_JUSTIFY[s.align]);
|
|
495
544
|
}
|
|
@@ -575,6 +624,68 @@ function applyElementVars(host, prefix, raw) {
|
|
|
575
624
|
}
|
|
576
625
|
}
|
|
577
626
|
|
|
627
|
+
// src/plugins/guides/inline-host.ts
|
|
628
|
+
function readInlinePosition(style) {
|
|
629
|
+
const p = style?.inlinePosition;
|
|
630
|
+
return p === "before" || p === "prepend" || p === "append" ? p : "after";
|
|
631
|
+
}
|
|
632
|
+
function insertHost(anchor, host, position) {
|
|
633
|
+
switch (position) {
|
|
634
|
+
case "before":
|
|
635
|
+
anchor.before(host);
|
|
636
|
+
break;
|
|
637
|
+
case "after":
|
|
638
|
+
anchor.after(host);
|
|
639
|
+
break;
|
|
640
|
+
case "prepend":
|
|
641
|
+
anchor.prepend(host);
|
|
642
|
+
break;
|
|
643
|
+
case "append":
|
|
644
|
+
anchor.append(host);
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function keepHostAttached(host, selector, position) {
|
|
649
|
+
const id = window.setInterval(() => {
|
|
650
|
+
if (host.isConnected) return;
|
|
651
|
+
const anchor = document.querySelector(selector);
|
|
652
|
+
if (anchor) insertHost(anchor, host, position);
|
|
653
|
+
}, 1e3);
|
|
654
|
+
return () => window.clearInterval(id);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/plugins/guides/wait-for-element.ts
|
|
658
|
+
function waitForElement(selector, timeoutMs = DEFAULT_ANCHOR_WAIT_MS) {
|
|
659
|
+
return new Promise((resolve) => {
|
|
660
|
+
const safeQuery = () => {
|
|
661
|
+
try {
|
|
662
|
+
return document.querySelector(selector);
|
|
663
|
+
} catch {
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
const existing = safeQuery();
|
|
668
|
+
if (existing) {
|
|
669
|
+
resolve(existing);
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
let resolved = false;
|
|
673
|
+
const finish = (el) => {
|
|
674
|
+
if (resolved) return;
|
|
675
|
+
resolved = true;
|
|
676
|
+
observer.disconnect();
|
|
677
|
+
clearTimeout(timer);
|
|
678
|
+
resolve(el);
|
|
679
|
+
};
|
|
680
|
+
const observer = new MutationObserver(() => {
|
|
681
|
+
const el = safeQuery();
|
|
682
|
+
if (el) finish(el);
|
|
683
|
+
});
|
|
684
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
685
|
+
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
578
689
|
// src/plugins/guides/styles.ts
|
|
579
690
|
var GUIDE_STYLES = `
|
|
580
691
|
:host {
|
|
@@ -596,6 +707,13 @@ var GUIDE_STYLES = `
|
|
|
596
707
|
z-index: ${GUIDE_Z_INDEX};
|
|
597
708
|
animation: veo-fade-in 180ms ease-out;
|
|
598
709
|
}
|
|
710
|
+
/* Sin backdrop (style.backdrop === false): la app queda usable detr\xE1s; solo la
|
|
711
|
+
tarjeta captura el mouse. El click-en-backdrop deja de cerrar (no hay backdrop). */
|
|
712
|
+
.veo-modal-overlay--none {
|
|
713
|
+
background: transparent;
|
|
714
|
+
pointer-events: none;
|
|
715
|
+
}
|
|
716
|
+
.veo-modal-overlay--none .veo-modal-card { pointer-events: auto; }
|
|
599
717
|
/*
|
|
600
718
|
* Posici\xF3n libre/preset: --veo-pos-x/y son porcentajes (default 50% = centro).
|
|
601
719
|
* El truco translate(-pos) alinea la MISMA fracci\xF3n de la tarjeta con esa
|
|
@@ -612,6 +730,7 @@ var GUIDE_STYLES = `
|
|
|
612
730
|
border: var(--veo-border-width, 0) solid var(--veo-border-color, transparent);
|
|
613
731
|
padding: var(--veo-pad, 24px);
|
|
614
732
|
max-width: var(--veo-width); width: 90%;
|
|
733
|
+
min-height: var(--veo-min-h, auto);
|
|
615
734
|
box-shadow: var(--veo-shadow);
|
|
616
735
|
animation: veo-fade-in 180ms ease-out;
|
|
617
736
|
}
|
|
@@ -627,6 +746,14 @@ var GUIDE_STYLES = `
|
|
|
627
746
|
}
|
|
628
747
|
.veo-banner-top { top: 0; }
|
|
629
748
|
.veo-banner-bottom { bottom: 0; }
|
|
749
|
+
/* Banner EMBEBIDO en un contenedor (step.selector): fluye dentro del contenedor
|
|
750
|
+
y empuja su contenido, en vez de flotar fijo sobre la pantalla. */
|
|
751
|
+
.veo-banner-embedded {
|
|
752
|
+
position: static;
|
|
753
|
+
left: auto; right: auto;
|
|
754
|
+
width: 100%;
|
|
755
|
+
border-radius: var(--veo-radius, 0);
|
|
756
|
+
}
|
|
630
757
|
|
|
631
758
|
.veo-tooltip {
|
|
632
759
|
position: absolute;
|
|
@@ -680,6 +807,21 @@ var GUIDE_STYLES = `
|
|
|
680
807
|
border-radius: var(--veo-image-radius, 8px);
|
|
681
808
|
margin: var(--veo-image-mt, 0) 0 var(--veo-image-mb, 12px);
|
|
682
809
|
}
|
|
810
|
+
/* Imagen A SANGRE: rompe el padding de la tarjeta y ocupa el ancho completo
|
|
811
|
+
(hero estilo anuncio). Como primer bloque, hereda el redondeo superior. */
|
|
812
|
+
.veo-guide-image--bleed {
|
|
813
|
+
width: calc(100% + var(--veo-pad, 24px) * 2);
|
|
814
|
+
max-width: none;
|
|
815
|
+
max-height: 280px;
|
|
816
|
+
align-self: auto;
|
|
817
|
+
border-radius: 0;
|
|
818
|
+
margin: 0 calc(var(--veo-pad, 24px) * -1) 12px;
|
|
819
|
+
}
|
|
820
|
+
.veo-guide-content > .veo-guide-image--bleed:first-child {
|
|
821
|
+
margin-top: calc(var(--veo-pad, 24px) * -1);
|
|
822
|
+
border-radius: calc(var(--veo-radius, 12px) - var(--veo-border-width, 0px))
|
|
823
|
+
calc(var(--veo-radius, 12px) - var(--veo-border-width, 0px)) 0 0;
|
|
824
|
+
}
|
|
683
825
|
.veo-guide-title {
|
|
684
826
|
font-size: var(--veo-title-size, 18px); font-weight: 600; line-height: 1.3;
|
|
685
827
|
text-align: var(--veo-title-align, left);
|
|
@@ -692,6 +834,18 @@ var GUIDE_STYLES = `
|
|
|
692
834
|
margin: var(--veo-text-mt, 0) 0 var(--veo-text-mb, 16px);
|
|
693
835
|
color: var(--veo-text-color, var(--veo-text-secondary));
|
|
694
836
|
}
|
|
837
|
+
/* Rich text dentro de un bloque de texto (p/listas/links/\xE9nfasis). */
|
|
838
|
+
.veo-guide-text p { margin: 0 0 8px; }
|
|
839
|
+
.veo-guide-text p:last-child { margin-bottom: 0; }
|
|
840
|
+
.veo-guide-text ul, .veo-guide-text ol { margin: 0 0 8px; padding-left: 20px; }
|
|
841
|
+
.veo-guide-text ul { list-style: disc; }
|
|
842
|
+
.veo-guide-text ol { list-style: decimal; }
|
|
843
|
+
.veo-guide-text li { margin: 2px 0; display: list-item; }
|
|
844
|
+
.veo-guide-text a { color: var(--veo-primary); text-decoration: underline; cursor: pointer; }
|
|
845
|
+
.veo-guide-text strong { font-weight: 600; }
|
|
846
|
+
.veo-guide-text em { font-style: italic; }
|
|
847
|
+
.veo-guide-text u { text-decoration: underline; }
|
|
848
|
+
.veo-guide-text s { text-decoration: line-through; }
|
|
695
849
|
.veo-guide-actions {
|
|
696
850
|
display: flex; gap: 8px; justify-content: var(--veo-actions-justify);
|
|
697
851
|
}
|
|
@@ -830,6 +984,44 @@ var GUIDE_STYLES = `
|
|
|
830
984
|
}
|
|
831
985
|
.veo-walkthrough-skip:hover { color: var(--veo-text); }
|
|
832
986
|
|
|
987
|
+
/* \u2500\u2500 Badge (elemento inyectado junto al ancla que abre un tooltip) \u2500\u2500 */
|
|
988
|
+
.veo-badge {
|
|
989
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
990
|
+
border: none; padding: 0; margin: 0 4px;
|
|
991
|
+
background: none; cursor: pointer;
|
|
992
|
+
line-height: 1; vertical-align: middle;
|
|
993
|
+
font-family: inherit;
|
|
994
|
+
}
|
|
995
|
+
.veo-badge:focus-visible { outline: 2px solid var(--veo-primary); outline-offset: 2px; }
|
|
996
|
+
.veo-badge--icon {
|
|
997
|
+
border-radius: 50%;
|
|
998
|
+
background: color-mix(in srgb, var(--veo-primary) 14%, transparent);
|
|
999
|
+
color: var(--veo-primary);
|
|
1000
|
+
font-weight: 600;
|
|
1001
|
+
}
|
|
1002
|
+
.veo-badge--dot {
|
|
1003
|
+
border-radius: 50%;
|
|
1004
|
+
background: var(--veo-primary);
|
|
1005
|
+
animation: veo-badge-pulse 2s ease-out infinite;
|
|
1006
|
+
}
|
|
1007
|
+
.veo-badge--pill {
|
|
1008
|
+
border-radius: 999px;
|
|
1009
|
+
background: var(--veo-primary);
|
|
1010
|
+
color: #fff;
|
|
1011
|
+
font-weight: 600;
|
|
1012
|
+
padding: 3px 9px;
|
|
1013
|
+
white-space: nowrap;
|
|
1014
|
+
}
|
|
1015
|
+
.veo-badge--image img { display: block; border-radius: 4px; object-fit: cover; }
|
|
1016
|
+
/* El tooltip del badge usa strategy fixed (el host vive dentro del flujo del
|
|
1017
|
+
cliente; un absoluto se recortar\xEDa con overflow de ancestros). */
|
|
1018
|
+
.veo-badge-tooltip { position: fixed; }
|
|
1019
|
+
@keyframes veo-badge-pulse {
|
|
1020
|
+
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--veo-primary) 45%, transparent); }
|
|
1021
|
+
70% { box-shadow: 0 0 0 7px transparent; }
|
|
1022
|
+
100% { box-shadow: 0 0 0 0 transparent; }
|
|
1023
|
+
}
|
|
1024
|
+
|
|
833
1025
|
.veo-custom-floating {
|
|
834
1026
|
position: fixed;
|
|
835
1027
|
z-index: ${GUIDE_Z_INDEX};
|
|
@@ -874,6 +1066,37 @@ var BaseRenderer = class {
|
|
|
874
1066
|
this.host = null;
|
|
875
1067
|
this.shadow = null;
|
|
876
1068
|
this.cleanups = [];
|
|
1069
|
+
// ── Update in-place (preview en vivo del editor) ──
|
|
1070
|
+
// La subclase setea estas piezas en render() para habilitarlo: el contenedor
|
|
1071
|
+
// del contenido, el nodo de contenido actual y cómo reconstruirlo.
|
|
1072
|
+
this.liveContainer = null;
|
|
1073
|
+
this.liveContent = null;
|
|
1074
|
+
this.liveBuild = null;
|
|
1075
|
+
/** Clave de anclaje/estructura fijada en render(); si cambia → remontar. */
|
|
1076
|
+
this.liveKey = "";
|
|
1077
|
+
}
|
|
1078
|
+
/** Clave de anclaje derivada del step (override por subclase). */
|
|
1079
|
+
liveKeyOf(_step) {
|
|
1080
|
+
return "";
|
|
1081
|
+
}
|
|
1082
|
+
/** Hook post-swap (reposicionar tooltip, actualizar clase del banner…). */
|
|
1083
|
+
onLiveUpdate(_step) {
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Actualiza el contenido del step SIN remontar el host — así el preview del
|
|
1087
|
+
* editor no parpadea en cada tecla. Devuelve `false` cuando el renderer no
|
|
1088
|
+
* lo soporta o el cambio requiere remontar (cambió el ancla/estructura);
|
|
1089
|
+
* en ese caso el caller hace el re-render completo.
|
|
1090
|
+
*/
|
|
1091
|
+
updateStep(step) {
|
|
1092
|
+
if (!this.host || !this.liveContainer || !this.liveContent || !this.liveBuild) return false;
|
|
1093
|
+
if (this.liveKeyOf(step) !== this.liveKey) return false;
|
|
1094
|
+
const next = this.liveBuild(step);
|
|
1095
|
+
this.liveContainer.replaceChild(next, this.liveContent);
|
|
1096
|
+
this.liveContent = next;
|
|
1097
|
+
this.applyDesign(step.style);
|
|
1098
|
+
this.onLiveUpdate(step);
|
|
1099
|
+
return true;
|
|
877
1100
|
}
|
|
878
1101
|
/**
|
|
879
1102
|
* Crea un `<div data-veo-guide>` adjunto a `document.body`, le adjunta
|
|
@@ -909,6 +1132,14 @@ var BaseRenderer = class {
|
|
|
909
1132
|
registerCleanup(fn) {
|
|
910
1133
|
this.cleanups.push(fn);
|
|
911
1134
|
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Host de la guía montada (o `null` si aún no se montó / ya se destruyó).
|
|
1137
|
+
* Lo usa el modo builder para adjuntar manipulación directa (drag/resize)
|
|
1138
|
+
* sobre el shadow root abierto sin tocar los renderers.
|
|
1139
|
+
*/
|
|
1140
|
+
hostElement() {
|
|
1141
|
+
return this.host;
|
|
1142
|
+
}
|
|
912
1143
|
/** Remueve el host del DOM y corre todas las funciones de cleanup. */
|
|
913
1144
|
destroy() {
|
|
914
1145
|
for (const fn of this.cleanups) {
|
|
@@ -926,20 +1157,372 @@ var BaseRenderer = class {
|
|
|
926
1157
|
}
|
|
927
1158
|
};
|
|
928
1159
|
|
|
1160
|
+
// src/plugins/guides/renderers/floating-arrow.ts
|
|
1161
|
+
var STATIC_SIDE = {
|
|
1162
|
+
top: "bottom",
|
|
1163
|
+
bottom: "top",
|
|
1164
|
+
left: "right",
|
|
1165
|
+
right: "left"
|
|
1166
|
+
};
|
|
1167
|
+
function positionArrow(arrowEl, placement, data) {
|
|
1168
|
+
const side = STATIC_SIDE[placement.split("-")[0] ?? "bottom"] ?? "top";
|
|
1169
|
+
for (const prop of ["top", "bottom", "left", "right"]) {
|
|
1170
|
+
arrowEl.style.setProperty(prop, "");
|
|
1171
|
+
}
|
|
1172
|
+
if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
|
|
1173
|
+
if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
|
|
1174
|
+
arrowEl.style.setProperty(side, "-6px");
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// src/plugins/guides/renderers/badge-renderer.ts
|
|
1178
|
+
var HOVER_CLOSE_DELAY_MS = 150;
|
|
1179
|
+
var BadgeRenderer = class extends BaseRenderer {
|
|
1180
|
+
constructor() {
|
|
1181
|
+
super(...arguments);
|
|
1182
|
+
this.tooltip = null;
|
|
1183
|
+
this.arrowEl = null;
|
|
1184
|
+
this.badgeBtn = null;
|
|
1185
|
+
this.open = false;
|
|
1186
|
+
this.shownEmitted = false;
|
|
1187
|
+
this.trigger = "hover";
|
|
1188
|
+
this.side = "top";
|
|
1189
|
+
this.closeTimer = null;
|
|
1190
|
+
this.stopFloat = null;
|
|
1191
|
+
}
|
|
1192
|
+
async render(context) {
|
|
1193
|
+
const step = context.guide.guideSteps[0];
|
|
1194
|
+
if (!step) return;
|
|
1195
|
+
const selector = step.selector ?? context.guide.activationRules.selector;
|
|
1196
|
+
if (typeof selector !== "string" || selector.length === 0) return;
|
|
1197
|
+
const anchor = await waitForElement(selector);
|
|
1198
|
+
if (!anchor) return;
|
|
1199
|
+
const { host, root } = this.createHost();
|
|
1200
|
+
const ownerDocument = root.ownerDocument ?? document;
|
|
1201
|
+
this.applyDesign(step.style);
|
|
1202
|
+
this.trigger = step.style?.badgeTrigger === "click" ? "click" : "hover";
|
|
1203
|
+
const rawSide = step.style?.tooltipPlacement;
|
|
1204
|
+
this.side = rawSide === "bottom" || rawSide === "left" || rawSide === "right" ? rawSide : "top";
|
|
1205
|
+
const position = readInlinePosition(step.style);
|
|
1206
|
+
host.style.display = "inline-flex";
|
|
1207
|
+
host.style.verticalAlign = "middle";
|
|
1208
|
+
const badge = buildBadgeElement(readBadgeConfig(step), ownerDocument);
|
|
1209
|
+
root.appendChild(badge);
|
|
1210
|
+
this.badgeBtn = badge;
|
|
1211
|
+
insertHost(anchor, host, position);
|
|
1212
|
+
this.registerCleanup(keepHostAttached(host, selector, position));
|
|
1213
|
+
const tooltip = ownerDocument.createElement("div");
|
|
1214
|
+
tooltip.className = "veo-tooltip veo-badge-tooltip";
|
|
1215
|
+
tooltip.style.display = "none";
|
|
1216
|
+
const emit = (action, meta) => {
|
|
1217
|
+
context.onInteraction({
|
|
1218
|
+
guideId: context.guide.guideId,
|
|
1219
|
+
stepIndex: 0,
|
|
1220
|
+
action,
|
|
1221
|
+
...meta ? { metadata: meta } : {}
|
|
1222
|
+
});
|
|
1223
|
+
};
|
|
1224
|
+
const buildTooltipContent = (s) => buildStepContent(s, ownerDocument, {
|
|
1225
|
+
onCtaClick: (action, url, meta) => {
|
|
1226
|
+
emit("cta_clicked", meta);
|
|
1227
|
+
if (action === "url" && url) window.open(url, "_blank", "noopener,noreferrer");
|
|
1228
|
+
if (action === "dismiss") {
|
|
1229
|
+
emit("dismissed");
|
|
1230
|
+
context.onClose();
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
this.closeTooltip();
|
|
1234
|
+
},
|
|
1235
|
+
onDismiss: () => {
|
|
1236
|
+
emit("dismissed");
|
|
1237
|
+
context.onClose();
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
const content = buildTooltipContent(step);
|
|
1241
|
+
tooltip.appendChild(content);
|
|
1242
|
+
const arrowEl = ownerDocument.createElement("div");
|
|
1243
|
+
arrowEl.className = "veo-tooltip-arrow";
|
|
1244
|
+
tooltip.appendChild(arrowEl);
|
|
1245
|
+
root.appendChild(tooltip);
|
|
1246
|
+
this.tooltip = tooltip;
|
|
1247
|
+
this.arrowEl = arrowEl;
|
|
1248
|
+
this.liveContainer = tooltip;
|
|
1249
|
+
this.liveContent = content;
|
|
1250
|
+
this.liveKey = this.liveKeyOf(step);
|
|
1251
|
+
this.liveBuild = buildTooltipContent;
|
|
1252
|
+
const openNow = () => {
|
|
1253
|
+
this.cancelClose();
|
|
1254
|
+
if (!this.open) {
|
|
1255
|
+
this.openTooltip(badge);
|
|
1256
|
+
if (!this.shownEmitted) {
|
|
1257
|
+
this.shownEmitted = true;
|
|
1258
|
+
emit("shown");
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
if (this.trigger === "hover") {
|
|
1263
|
+
const scheduleClose = () => {
|
|
1264
|
+
this.cancelClose();
|
|
1265
|
+
this.closeTimer = window.setTimeout(() => this.closeTooltip(), HOVER_CLOSE_DELAY_MS);
|
|
1266
|
+
};
|
|
1267
|
+
badge.addEventListener("mouseenter", openNow);
|
|
1268
|
+
badge.addEventListener("focus", openNow);
|
|
1269
|
+
badge.addEventListener("mouseleave", scheduleClose);
|
|
1270
|
+
badge.addEventListener("blur", scheduleClose);
|
|
1271
|
+
tooltip.addEventListener("mouseenter", () => this.cancelClose());
|
|
1272
|
+
tooltip.addEventListener("mouseleave", scheduleClose);
|
|
1273
|
+
} else {
|
|
1274
|
+
badge.addEventListener("click", () => {
|
|
1275
|
+
if (this.open) this.closeTooltip();
|
|
1276
|
+
else openNow();
|
|
1277
|
+
});
|
|
1278
|
+
const onDocClick = (e) => {
|
|
1279
|
+
if (!this.open) return;
|
|
1280
|
+
if (e.composedPath().includes(host)) return;
|
|
1281
|
+
this.closeTooltip();
|
|
1282
|
+
};
|
|
1283
|
+
document.addEventListener("click", onDocClick, true);
|
|
1284
|
+
this.registerCleanup(() => document.removeEventListener("click", onDocClick, true));
|
|
1285
|
+
}
|
|
1286
|
+
const onKey = (e) => {
|
|
1287
|
+
if (e.key === "Escape" && this.open) this.closeTooltip();
|
|
1288
|
+
};
|
|
1289
|
+
document.addEventListener("keydown", onKey);
|
|
1290
|
+
this.registerCleanup(() => document.removeEventListener("keydown", onKey));
|
|
1291
|
+
if (context.isPreview) openNow();
|
|
1292
|
+
}
|
|
1293
|
+
/** Cambiar de ancla/posición/trigger requiere re-montar y re-cablear. */
|
|
1294
|
+
liveKeyOf(step) {
|
|
1295
|
+
const selector = step.selector ?? "";
|
|
1296
|
+
const position = readInlinePosition(step.style);
|
|
1297
|
+
const trigger = step.style?.badgeTrigger === "click" ? "click" : "hover";
|
|
1298
|
+
return `${selector}|${position}|${trigger}`;
|
|
1299
|
+
}
|
|
1300
|
+
onLiveUpdate(step) {
|
|
1301
|
+
if (this.badgeBtn) {
|
|
1302
|
+
const doc = this.badgeBtn.ownerDocument;
|
|
1303
|
+
const next = buildBadgeElement(readBadgeConfig(step), doc);
|
|
1304
|
+
this.badgeBtn.className = next.className;
|
|
1305
|
+
this.badgeBtn.setAttribute("style", next.getAttribute("style") ?? "");
|
|
1306
|
+
this.badgeBtn.replaceChildren(...Array.from(next.childNodes));
|
|
1307
|
+
}
|
|
1308
|
+
const rawSide = step.style?.tooltipPlacement;
|
|
1309
|
+
this.side = rawSide === "bottom" || rawSide === "left" || rawSide === "right" ? rawSide : "top";
|
|
1310
|
+
if (this.open && this.badgeBtn) this.position(this.badgeBtn);
|
|
1311
|
+
}
|
|
1312
|
+
destroy() {
|
|
1313
|
+
this.cancelClose();
|
|
1314
|
+
this.stopFloat?.();
|
|
1315
|
+
this.stopFloat = null;
|
|
1316
|
+
this.tooltip = null;
|
|
1317
|
+
this.arrowEl = null;
|
|
1318
|
+
this.badgeBtn = null;
|
|
1319
|
+
this.open = false;
|
|
1320
|
+
super.destroy();
|
|
1321
|
+
}
|
|
1322
|
+
openTooltip(badge) {
|
|
1323
|
+
if (!this.tooltip) return;
|
|
1324
|
+
this.tooltip.style.display = "block";
|
|
1325
|
+
this.open = true;
|
|
1326
|
+
void this.position(badge);
|
|
1327
|
+
const reposition = () => {
|
|
1328
|
+
void this.position(badge);
|
|
1329
|
+
};
|
|
1330
|
+
window.addEventListener("scroll", reposition, true);
|
|
1331
|
+
window.addEventListener("resize", reposition);
|
|
1332
|
+
this.stopFloat = () => {
|
|
1333
|
+
window.removeEventListener("scroll", reposition, true);
|
|
1334
|
+
window.removeEventListener("resize", reposition);
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
closeTooltip() {
|
|
1338
|
+
this.cancelClose();
|
|
1339
|
+
if (!this.tooltip || !this.open) return;
|
|
1340
|
+
this.tooltip.style.display = "none";
|
|
1341
|
+
this.open = false;
|
|
1342
|
+
this.stopFloat?.();
|
|
1343
|
+
this.stopFloat = null;
|
|
1344
|
+
}
|
|
1345
|
+
cancelClose() {
|
|
1346
|
+
if (this.closeTimer !== null) {
|
|
1347
|
+
window.clearTimeout(this.closeTimer);
|
|
1348
|
+
this.closeTimer = null;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
async position(badge) {
|
|
1352
|
+
const tooltip = this.tooltip;
|
|
1353
|
+
const arrowEl = this.arrowEl;
|
|
1354
|
+
if (!tooltip || !arrowEl) return;
|
|
1355
|
+
const { x, y, placement, middlewareData } = await computePosition(badge, tooltip, {
|
|
1356
|
+
strategy: "fixed",
|
|
1357
|
+
placement: this.side,
|
|
1358
|
+
middleware: [offset(8), flip(), shift({ padding: 8 }), arrow({ element: arrowEl })]
|
|
1359
|
+
});
|
|
1360
|
+
tooltip.style.left = `${x}px`;
|
|
1361
|
+
tooltip.style.top = `${y}px`;
|
|
1362
|
+
positionArrow(arrowEl, placement, middlewareData.arrow);
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
function readBadgeConfig(step) {
|
|
1366
|
+
const raw = step.style?.badge;
|
|
1367
|
+
if (!raw || typeof raw !== "object") return { kind: "icon" };
|
|
1368
|
+
const b = raw;
|
|
1369
|
+
const kind = b.kind === "dot" || b.kind === "pill" || b.kind === "image" || b.kind === "icon" ? b.kind : "icon";
|
|
1370
|
+
return {
|
|
1371
|
+
kind,
|
|
1372
|
+
icon: typeof b.icon === "string" ? b.icon : null,
|
|
1373
|
+
text: typeof b.text === "string" ? b.text : null,
|
|
1374
|
+
imageUrl: typeof b.imageUrl === "string" ? b.imageUrl : null,
|
|
1375
|
+
color: typeof b.color === "string" && COLOR_RE.test(b.color.trim()) ? b.color.trim() : null,
|
|
1376
|
+
size: typeof b.size === "number" && Number.isFinite(b.size) ? b.size : null
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
function clamp2(n, min, max) {
|
|
1380
|
+
return Math.min(max, Math.max(min, n));
|
|
1381
|
+
}
|
|
1382
|
+
function buildBadgeElement(config, doc) {
|
|
1383
|
+
const btn = doc.createElement("button");
|
|
1384
|
+
btn.type = "button";
|
|
1385
|
+
btn.className = `veo-badge veo-badge--${config.kind}`;
|
|
1386
|
+
btn.setAttribute("aria-label", "M\xE1s informaci\xF3n");
|
|
1387
|
+
const size = clamp2(config.size ?? 18, 8, 48);
|
|
1388
|
+
switch (config.kind) {
|
|
1389
|
+
case "dot":
|
|
1390
|
+
btn.style.width = `${size}px`;
|
|
1391
|
+
btn.style.height = `${size}px`;
|
|
1392
|
+
if (config.color) btn.style.background = config.color;
|
|
1393
|
+
break;
|
|
1394
|
+
case "pill": {
|
|
1395
|
+
btn.textContent = config.text?.slice(0, 24) || "Nuevo";
|
|
1396
|
+
btn.style.fontSize = `${clamp2(Math.round(size * 0.62), 8, 24)}px`;
|
|
1397
|
+
if (config.color) btn.style.background = config.color;
|
|
1398
|
+
break;
|
|
1399
|
+
}
|
|
1400
|
+
case "image": {
|
|
1401
|
+
if (config.imageUrl && isSafeUrl(config.imageUrl)) {
|
|
1402
|
+
const img = doc.createElement("img");
|
|
1403
|
+
img.src = config.imageUrl;
|
|
1404
|
+
img.alt = "";
|
|
1405
|
+
img.style.width = `${size}px`;
|
|
1406
|
+
img.style.height = `${size}px`;
|
|
1407
|
+
btn.appendChild(img);
|
|
1408
|
+
} else {
|
|
1409
|
+
btn.className = "veo-badge veo-badge--icon";
|
|
1410
|
+
btn.textContent = "?";
|
|
1411
|
+
btn.style.width = `${size}px`;
|
|
1412
|
+
btn.style.height = `${size}px`;
|
|
1413
|
+
btn.style.fontSize = `${clamp2(Math.round(size * 0.62), 8, 32)}px`;
|
|
1414
|
+
}
|
|
1415
|
+
break;
|
|
1416
|
+
}
|
|
1417
|
+
default: {
|
|
1418
|
+
btn.textContent = (config.icon || "\u2139\uFE0F").slice(0, 4);
|
|
1419
|
+
btn.style.width = `${size}px`;
|
|
1420
|
+
btn.style.height = `${size}px`;
|
|
1421
|
+
btn.style.fontSize = `${clamp2(Math.round(size * 0.62), 8, 32)}px`;
|
|
1422
|
+
if (config.color) btn.style.color = config.color;
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return btn;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
// src/plugins/guides/walkthrough-block-builder.ts
|
|
1430
|
+
function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
|
|
1431
|
+
const container = doc.createElement("div");
|
|
1432
|
+
container.className = "veo-guide-content";
|
|
1433
|
+
const counter = doc.createElement("div");
|
|
1434
|
+
counter.className = "veo-walkthrough-counter";
|
|
1435
|
+
counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
|
|
1436
|
+
container.appendChild(counter);
|
|
1437
|
+
const progress = doc.createElement("div");
|
|
1438
|
+
progress.className = "veo-walkthrough-progress";
|
|
1439
|
+
for (let i = 0; i < totalSteps; i++) {
|
|
1440
|
+
const dot = doc.createElement("span");
|
|
1441
|
+
dot.className = "veo-walkthrough-progress-dot";
|
|
1442
|
+
if (i < stepIndex) dot.classList.add("completed");
|
|
1443
|
+
if (i === stepIndex) dot.classList.add("active");
|
|
1444
|
+
progress.appendChild(dot);
|
|
1445
|
+
}
|
|
1446
|
+
container.appendChild(progress);
|
|
1447
|
+
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
1448
|
+
const img = doc.createElement("img");
|
|
1449
|
+
img.className = "veo-guide-image";
|
|
1450
|
+
img.src = step.imageUrl;
|
|
1451
|
+
img.alt = typeof step.title === "string" ? step.title : "";
|
|
1452
|
+
container.appendChild(img);
|
|
1453
|
+
}
|
|
1454
|
+
if (typeof step.title === "string" && step.title) {
|
|
1455
|
+
const heading = doc.createElement("h2");
|
|
1456
|
+
heading.className = "veo-guide-title";
|
|
1457
|
+
heading.textContent = step.title;
|
|
1458
|
+
container.appendChild(heading);
|
|
1459
|
+
}
|
|
1460
|
+
if (typeof step.content === "string" && step.content) {
|
|
1461
|
+
const paragraph = doc.createElement("p");
|
|
1462
|
+
paragraph.className = "veo-guide-text";
|
|
1463
|
+
paragraph.textContent = step.content;
|
|
1464
|
+
container.appendChild(paragraph);
|
|
1465
|
+
}
|
|
1466
|
+
const actions = doc.createElement("div");
|
|
1467
|
+
actions.className = "veo-walkthrough-actions";
|
|
1468
|
+
const skipBtn = doc.createElement("button");
|
|
1469
|
+
skipBtn.type = "button";
|
|
1470
|
+
skipBtn.className = "veo-walkthrough-skip";
|
|
1471
|
+
skipBtn.textContent = "Omitir";
|
|
1472
|
+
skipBtn.addEventListener("click", () => callbacks.onSkip());
|
|
1473
|
+
actions.appendChild(skipBtn);
|
|
1474
|
+
const rightGroup = doc.createElement("div");
|
|
1475
|
+
rightGroup.className = "veo-walkthrough-actions-right";
|
|
1476
|
+
if (stepIndex > 0) {
|
|
1477
|
+
const backBtn = doc.createElement("button");
|
|
1478
|
+
backBtn.type = "button";
|
|
1479
|
+
backBtn.className = "veo-walkthrough-btn-secondary";
|
|
1480
|
+
backBtn.textContent = "Atr\xE1s";
|
|
1481
|
+
backBtn.addEventListener("click", () => callbacks.onBack());
|
|
1482
|
+
rightGroup.appendChild(backBtn);
|
|
1483
|
+
}
|
|
1484
|
+
const isLastStep = stepIndex === totalSteps - 1;
|
|
1485
|
+
const primaryBtn = doc.createElement("button");
|
|
1486
|
+
primaryBtn.type = "button";
|
|
1487
|
+
primaryBtn.className = "veo-guide-cta";
|
|
1488
|
+
const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
|
|
1489
|
+
primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
|
|
1490
|
+
primaryBtn.addEventListener("click", () => {
|
|
1491
|
+
if (isLastStep) callbacks.onComplete();
|
|
1492
|
+
else callbacks.onNext();
|
|
1493
|
+
});
|
|
1494
|
+
rightGroup.appendChild(primaryBtn);
|
|
1495
|
+
actions.appendChild(rightGroup);
|
|
1496
|
+
container.appendChild(actions);
|
|
1497
|
+
container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
|
|
1498
|
+
return container;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
929
1501
|
// src/plugins/guides/renderers/banner-renderer.ts
|
|
930
1502
|
var BannerRenderer = class extends BaseRenderer {
|
|
931
|
-
render(context) {
|
|
1503
|
+
async render(context) {
|
|
932
1504
|
const idx = context.nav?.stepIndex ?? 0;
|
|
933
1505
|
const step = context.guide.guideSteps[idx];
|
|
934
1506
|
if (!step) return;
|
|
935
|
-
const
|
|
1507
|
+
const position = step.style?.position === "bottom" ? "bottom" : "top";
|
|
1508
|
+
const selector = context.nav ? null : step.selector?.trim() ?? null;
|
|
1509
|
+
let anchor = null;
|
|
1510
|
+
if (selector) {
|
|
1511
|
+
anchor = await waitForElement(selector);
|
|
1512
|
+
if (!anchor) return;
|
|
1513
|
+
}
|
|
1514
|
+
const { host, root } = this.createHost();
|
|
936
1515
|
this.applyDesign(step.style);
|
|
937
1516
|
const ownerDocument = root.ownerDocument ?? document;
|
|
938
|
-
const position = step.style?.position === "bottom" ? "bottom" : "top";
|
|
939
1517
|
const banner = ownerDocument.createElement("div");
|
|
940
|
-
banner.className =
|
|
941
|
-
const dismiss = (action, ctaUrl) => {
|
|
942
|
-
context.onInteraction({
|
|
1518
|
+
banner.className = bannerClassOf(position, Boolean(anchor));
|
|
1519
|
+
const dismiss = (action, ctaUrl, meta) => {
|
|
1520
|
+
context.onInteraction({
|
|
1521
|
+
guideId: context.guide.guideId,
|
|
1522
|
+
stepIndex: idx,
|
|
1523
|
+
action,
|
|
1524
|
+
...meta ? { metadata: meta } : {}
|
|
1525
|
+
});
|
|
943
1526
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
944
1527
|
context.onClose();
|
|
945
1528
|
};
|
|
@@ -950,47 +1533,45 @@ var BannerRenderer = class extends BaseRenderer {
|
|
|
950
1533
|
ownerDocument,
|
|
951
1534
|
context.nav.callbacks
|
|
952
1535
|
) : buildStepContent(step, ownerDocument, {
|
|
953
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
1536
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
954
1537
|
onDismiss: () => dismiss("dismissed")
|
|
955
1538
|
});
|
|
956
1539
|
banner.appendChild(content);
|
|
957
1540
|
root.appendChild(banner);
|
|
1541
|
+
if (selector && anchor) {
|
|
1542
|
+
host.style.display = "block";
|
|
1543
|
+
const insertAt = position === "top" ? "prepend" : "append";
|
|
1544
|
+
insertHost(anchor, host, insertAt);
|
|
1545
|
+
this.registerCleanup(keepHostAttached(host, selector, insertAt));
|
|
1546
|
+
}
|
|
958
1547
|
if (!context.nav) {
|
|
1548
|
+
this.liveContainer = banner;
|
|
1549
|
+
this.liveContent = content;
|
|
1550
|
+
this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
|
|
1551
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
1552
|
+
onDismiss: () => dismiss("dismissed")
|
|
1553
|
+
});
|
|
1554
|
+
this.liveKey = this.liveKeyOf(step);
|
|
959
1555
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
960
1556
|
}
|
|
961
1557
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
const existing = safeQuery();
|
|
975
|
-
if (existing) {
|
|
976
|
-
resolve(existing);
|
|
977
|
-
return;
|
|
1558
|
+
/** Cambiar de contenedor o de top/bottom embebido requiere re-insertar → remontar. */
|
|
1559
|
+
liveKeyOf(step) {
|
|
1560
|
+
const selector = step.selector?.trim() ?? "";
|
|
1561
|
+
if (!selector) return "";
|
|
1562
|
+
const position = step.style?.position === "bottom" ? "bottom" : "top";
|
|
1563
|
+
return `${selector}|${position}`;
|
|
1564
|
+
}
|
|
1565
|
+
onLiveUpdate(step) {
|
|
1566
|
+
if (this.liveContainer) {
|
|
1567
|
+
const position = step.style?.position === "bottom" ? "bottom" : "top";
|
|
1568
|
+
const embedded = Boolean(step.selector?.trim());
|
|
1569
|
+
this.liveContainer.className = bannerClassOf(position, embedded);
|
|
978
1570
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
observer.disconnect();
|
|
984
|
-
clearTimeout(timer);
|
|
985
|
-
resolve(el);
|
|
986
|
-
};
|
|
987
|
-
const observer = new MutationObserver(() => {
|
|
988
|
-
const el = safeQuery();
|
|
989
|
-
if (el) finish(el);
|
|
990
|
-
});
|
|
991
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
992
|
-
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
993
|
-
});
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
function bannerClassOf(position, embedded) {
|
|
1574
|
+
return embedded ? "veo-banner veo-banner-embedded" : `veo-banner veo-banner-${position}`;
|
|
994
1575
|
}
|
|
995
1576
|
|
|
996
1577
|
// src/utils/uuid.ts
|
|
@@ -1525,36 +2106,6 @@ var FormRenderer = class extends BaseRenderer {
|
|
|
1525
2106
|
}
|
|
1526
2107
|
};
|
|
1527
2108
|
|
|
1528
|
-
// src/plugins/guides/inline-host.ts
|
|
1529
|
-
function readInlinePosition(style) {
|
|
1530
|
-
const p = style?.inlinePosition;
|
|
1531
|
-
return p === "before" || p === "prepend" || p === "append" ? p : "after";
|
|
1532
|
-
}
|
|
1533
|
-
function insertHost(anchor, host, position) {
|
|
1534
|
-
switch (position) {
|
|
1535
|
-
case "before":
|
|
1536
|
-
anchor.before(host);
|
|
1537
|
-
break;
|
|
1538
|
-
case "after":
|
|
1539
|
-
anchor.after(host);
|
|
1540
|
-
break;
|
|
1541
|
-
case "prepend":
|
|
1542
|
-
anchor.prepend(host);
|
|
1543
|
-
break;
|
|
1544
|
-
case "append":
|
|
1545
|
-
anchor.append(host);
|
|
1546
|
-
break;
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
function keepHostAttached(host, selector, position) {
|
|
1550
|
-
const id = window.setInterval(() => {
|
|
1551
|
-
if (host.isConnected) return;
|
|
1552
|
-
const anchor = document.querySelector(selector);
|
|
1553
|
-
if (anchor) insertHost(anchor, host, position);
|
|
1554
|
-
}, 1e3);
|
|
1555
|
-
return () => window.clearInterval(id);
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
2109
|
// src/plugins/guides/renderers/inline-custom-renderer.ts
|
|
1559
2110
|
var InlineCustomRenderer = class extends BaseRenderer {
|
|
1560
2111
|
async render(context) {
|
|
@@ -1622,23 +2173,45 @@ var InlineRenderer = class extends BaseRenderer {
|
|
|
1622
2173
|
const ownerDocument = root.ownerDocument ?? document;
|
|
1623
2174
|
const card = ownerDocument.createElement("div");
|
|
1624
2175
|
card.className = "veo-inline";
|
|
1625
|
-
const dismiss = (action, ctaUrl) => {
|
|
1626
|
-
context.onInteraction({
|
|
2176
|
+
const dismiss = (action, ctaUrl, meta) => {
|
|
2177
|
+
context.onInteraction({
|
|
2178
|
+
guideId: context.guide.guideId,
|
|
2179
|
+
stepIndex: 0,
|
|
2180
|
+
action,
|
|
2181
|
+
...meta ? { metadata: meta } : {}
|
|
2182
|
+
});
|
|
1627
2183
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
1628
2184
|
context.onClose();
|
|
1629
2185
|
};
|
|
1630
2186
|
const content = buildStepContent(step, ownerDocument, {
|
|
1631
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
2187
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
1632
2188
|
onDismiss: () => dismiss("dismissed")
|
|
1633
2189
|
});
|
|
1634
2190
|
card.appendChild(content);
|
|
1635
2191
|
root.appendChild(card);
|
|
2192
|
+
this.liveContainer = card;
|
|
2193
|
+
this.liveContent = content;
|
|
2194
|
+
this.liveKey = `${selector}|${position}`;
|
|
2195
|
+
this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
|
|
2196
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
2197
|
+
onDismiss: () => dismiss("dismissed")
|
|
2198
|
+
});
|
|
1636
2199
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
1637
2200
|
}
|
|
2201
|
+
liveKeyOf(step) {
|
|
2202
|
+
return `${step.selector ?? ""}|${readInlinePosition(step.style)}`;
|
|
2203
|
+
}
|
|
1638
2204
|
};
|
|
1639
2205
|
|
|
1640
2206
|
// src/plugins/guides/renderers/modal-renderer.ts
|
|
2207
|
+
function overlayClassOf(step) {
|
|
2208
|
+
return step.style?.backdrop === false ? "veo-modal-overlay veo-modal-overlay--none" : "veo-modal-overlay";
|
|
2209
|
+
}
|
|
1641
2210
|
var ModalRenderer = class extends BaseRenderer {
|
|
2211
|
+
constructor() {
|
|
2212
|
+
super(...arguments);
|
|
2213
|
+
this.overlay = null;
|
|
2214
|
+
}
|
|
1642
2215
|
render(context) {
|
|
1643
2216
|
const idx = context.nav?.stepIndex ?? 0;
|
|
1644
2217
|
const step = context.guide.guideSteps[idx];
|
|
@@ -1647,11 +2220,17 @@ var ModalRenderer = class extends BaseRenderer {
|
|
|
1647
2220
|
this.applyDesign(step.style);
|
|
1648
2221
|
const ownerDocument = root.ownerDocument ?? document;
|
|
1649
2222
|
const overlay = ownerDocument.createElement("div");
|
|
1650
|
-
overlay.className =
|
|
2223
|
+
overlay.className = overlayClassOf(step);
|
|
2224
|
+
this.overlay = overlay;
|
|
1651
2225
|
const card = ownerDocument.createElement("div");
|
|
1652
2226
|
card.className = "veo-modal-card";
|
|
1653
|
-
const dismiss = (action, ctaUrl) => {
|
|
1654
|
-
context.onInteraction({
|
|
2227
|
+
const dismiss = (action, ctaUrl, meta) => {
|
|
2228
|
+
context.onInteraction({
|
|
2229
|
+
guideId: context.guide.guideId,
|
|
2230
|
+
stepIndex: idx,
|
|
2231
|
+
action,
|
|
2232
|
+
...meta ? { metadata: meta } : {}
|
|
2233
|
+
});
|
|
1655
2234
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
1656
2235
|
context.onClose();
|
|
1657
2236
|
};
|
|
@@ -1666,12 +2245,20 @@ var ModalRenderer = class extends BaseRenderer {
|
|
|
1666
2245
|
ownerDocument,
|
|
1667
2246
|
context.nav.callbacks
|
|
1668
2247
|
) : buildStepContent(step, ownerDocument, {
|
|
1669
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
2248
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
1670
2249
|
onDismiss: () => dismiss("dismissed")
|
|
1671
2250
|
});
|
|
1672
2251
|
card.appendChild(content);
|
|
1673
2252
|
overlay.appendChild(card);
|
|
1674
2253
|
root.appendChild(overlay);
|
|
2254
|
+
if (!context.nav) {
|
|
2255
|
+
this.liveContainer = card;
|
|
2256
|
+
this.liveContent = content;
|
|
2257
|
+
this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
|
|
2258
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
2259
|
+
onDismiss: () => dismiss("dismissed")
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
1675
2262
|
overlay.addEventListener("click", (e) => {
|
|
1676
2263
|
if (e.target === overlay) onBackdropOrEsc();
|
|
1677
2264
|
});
|
|
@@ -1684,27 +2271,21 @@ var ModalRenderer = class extends BaseRenderer {
|
|
|
1684
2271
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
1685
2272
|
}
|
|
1686
2273
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
// src/plugins/guides/renderers/floating-arrow.ts
|
|
1690
|
-
var STATIC_SIDE = {
|
|
1691
|
-
top: "bottom",
|
|
1692
|
-
bottom: "top",
|
|
1693
|
-
left: "right",
|
|
1694
|
-
right: "left"
|
|
1695
|
-
};
|
|
1696
|
-
function positionArrow(arrowEl, placement, data) {
|
|
1697
|
-
const side = STATIC_SIDE[placement.split("-")[0] ?? "bottom"] ?? "top";
|
|
1698
|
-
for (const prop of ["top", "bottom", "left", "right"]) {
|
|
1699
|
-
arrowEl.style.setProperty(prop, "");
|
|
2274
|
+
onLiveUpdate(step) {
|
|
2275
|
+
if (this.overlay) this.overlay.className = overlayClassOf(step);
|
|
1700
2276
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
// src/plugins/guides/renderers/tooltip-renderer.ts
|
|
2277
|
+
destroy() {
|
|
2278
|
+
this.overlay = null;
|
|
2279
|
+
super.destroy();
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
1707
2282
|
var TooltipRenderer = class extends BaseRenderer {
|
|
2283
|
+
constructor() {
|
|
2284
|
+
super(...arguments);
|
|
2285
|
+
/** Lado preferido (mutable: el update in-place puede cambiarlo). */
|
|
2286
|
+
this.side = "bottom";
|
|
2287
|
+
this.reposition = null;
|
|
2288
|
+
}
|
|
1708
2289
|
async render(context) {
|
|
1709
2290
|
const step = context.guide.guideSteps[0];
|
|
1710
2291
|
if (!step) return;
|
|
@@ -1713,24 +2294,25 @@ var TooltipRenderer = class extends BaseRenderer {
|
|
|
1713
2294
|
const anchor = await waitForElement(selector);
|
|
1714
2295
|
if (!anchor) return;
|
|
1715
2296
|
const rawSide = step.style?.tooltipPlacement;
|
|
1716
|
-
|
|
2297
|
+
this.side = rawSide === "top" || rawSide === "left" || rawSide === "right" ? rawSide : "bottom";
|
|
1717
2298
|
const { root } = this.createHost();
|
|
1718
2299
|
this.applyDesign(step.style);
|
|
1719
2300
|
const ownerDocument = root.ownerDocument ?? document;
|
|
1720
2301
|
const tooltip = ownerDocument.createElement("div");
|
|
1721
2302
|
tooltip.className = "veo-tooltip";
|
|
1722
|
-
const dismiss = (action, ctaUrl) => {
|
|
2303
|
+
const dismiss = (action, ctaUrl, meta) => {
|
|
1723
2304
|
context.onInteraction({
|
|
1724
2305
|
guideId: context.guide.guideId,
|
|
1725
2306
|
stepIndex: 0,
|
|
1726
|
-
action
|
|
2307
|
+
action,
|
|
2308
|
+
...meta ? { metadata: meta } : {}
|
|
1727
2309
|
});
|
|
1728
2310
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
1729
2311
|
context.onClose();
|
|
1730
2312
|
};
|
|
1731
2313
|
const content = buildStepContent(step, ownerDocument, {
|
|
1732
|
-
onCtaClick: (action, url) => {
|
|
1733
|
-
dismiss("cta_clicked", action === "url" ? url : void 0);
|
|
2314
|
+
onCtaClick: (action, url, meta) => {
|
|
2315
|
+
dismiss("cta_clicked", action === "url" ? url : void 0, meta);
|
|
1734
2316
|
},
|
|
1735
2317
|
onDismiss: () => dismiss("dismissed")
|
|
1736
2318
|
});
|
|
@@ -1741,7 +2323,7 @@ var TooltipRenderer = class extends BaseRenderer {
|
|
|
1741
2323
|
root.appendChild(tooltip);
|
|
1742
2324
|
const updatePosition = async () => {
|
|
1743
2325
|
const { x, y, placement, middlewareData } = await computePosition(anchor, tooltip, {
|
|
1744
|
-
placement: side,
|
|
2326
|
+
placement: this.side,
|
|
1745
2327
|
middleware: [offset(10), flip(), shift({ padding: 8 }), arrow({ element: arrowEl })]
|
|
1746
2328
|
});
|
|
1747
2329
|
tooltip.style.left = `${x}px`;
|
|
@@ -1752,18 +2334,34 @@ var TooltipRenderer = class extends BaseRenderer {
|
|
|
1752
2334
|
const reposition = () => {
|
|
1753
2335
|
void updatePosition();
|
|
1754
2336
|
};
|
|
2337
|
+
this.reposition = reposition;
|
|
1755
2338
|
window.addEventListener("scroll", reposition, true);
|
|
1756
2339
|
window.addEventListener("resize", reposition);
|
|
1757
2340
|
this.registerCleanup(() => {
|
|
1758
2341
|
window.removeEventListener("scroll", reposition, true);
|
|
1759
2342
|
window.removeEventListener("resize", reposition);
|
|
1760
2343
|
});
|
|
2344
|
+
this.liveContainer = tooltip;
|
|
2345
|
+
this.liveContent = content;
|
|
2346
|
+
this.liveKey = selector;
|
|
2347
|
+
this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
|
|
2348
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
2349
|
+
onDismiss: () => dismiss("dismissed")
|
|
2350
|
+
});
|
|
1761
2351
|
context.onInteraction({
|
|
1762
2352
|
guideId: context.guide.guideId,
|
|
1763
2353
|
stepIndex: 0,
|
|
1764
2354
|
action: "shown"
|
|
1765
2355
|
});
|
|
1766
2356
|
}
|
|
2357
|
+
liveKeyOf(step) {
|
|
2358
|
+
return step.selector ?? "";
|
|
2359
|
+
}
|
|
2360
|
+
onLiveUpdate(step) {
|
|
2361
|
+
const raw = step.style?.tooltipPlacement;
|
|
2362
|
+
this.side = raw === "top" || raw === "left" || raw === "right" ? raw : "bottom";
|
|
2363
|
+
this.reposition?.();
|
|
2364
|
+
}
|
|
1767
2365
|
};
|
|
1768
2366
|
var WalkthroughRenderer = class extends BaseRenderer {
|
|
1769
2367
|
async render(context) {
|
|
@@ -1828,12 +2426,29 @@ var GuidePreviewController = class {
|
|
|
1828
2426
|
this.input = null;
|
|
1829
2427
|
}
|
|
1830
2428
|
preview(input) {
|
|
2429
|
+
const nextStep = input.guideSteps[0];
|
|
2430
|
+
if (this.singleStep && this.input && input.guideType === this.input.guideType && input.guideType !== "walkthrough" && nextStep && this.singleStep.updateStep(nextStep)) {
|
|
2431
|
+
this.input = input;
|
|
2432
|
+
return {
|
|
2433
|
+
close: () => this.close(),
|
|
2434
|
+
ready: Promise.resolve({ rendered: true }),
|
|
2435
|
+
host: () => this.activeHost()
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
1831
2438
|
this.close();
|
|
1832
2439
|
this.input = input;
|
|
1833
2440
|
const guide = normalize(input);
|
|
1834
2441
|
const start = typeof input.startStepIndex === "number" ? input.startStepIndex : 0;
|
|
1835
2442
|
const ready = guide.guideType === "walkthrough" ? this.startWalkthrough(guide, start) : this.renderSingleStep(guide);
|
|
1836
|
-
return { close: () => this.close(), ready };
|
|
2443
|
+
return { close: () => this.close(), ready, host: () => this.activeHost() };
|
|
2444
|
+
}
|
|
2445
|
+
/** Host de la guía montada (single-step o paso de walkthrough activo). */
|
|
2446
|
+
activeHost() {
|
|
2447
|
+
if (this.singleStep) return this.singleStep.hostElement();
|
|
2448
|
+
if (this.walkthrough instanceof BaseRenderer) {
|
|
2449
|
+
return this.walkthrough.hostElement();
|
|
2450
|
+
}
|
|
2451
|
+
return null;
|
|
1837
2452
|
}
|
|
1838
2453
|
close() {
|
|
1839
2454
|
if (this.singleStep) {
|
|
@@ -1954,7 +2569,7 @@ var activeController = null;
|
|
|
1954
2569
|
function previewGuide(input) {
|
|
1955
2570
|
if (!hasDocument()) {
|
|
1956
2571
|
return { close: () => {
|
|
1957
|
-
}, ready: Promise.resolve({ rendered: false }) };
|
|
2572
|
+
}, ready: Promise.resolve({ rendered: false }), host: () => null };
|
|
1958
2573
|
}
|
|
1959
2574
|
if (!activeController) activeController = new GuidePreviewController();
|
|
1960
2575
|
return activeController.preview(input);
|
|
@@ -1997,6 +2612,8 @@ function createSingleStepRenderer(type) {
|
|
|
1997
2612
|
return new FormRenderer();
|
|
1998
2613
|
case "inline-form":
|
|
1999
2614
|
return new InlineFormRenderer();
|
|
2615
|
+
case "badge":
|
|
2616
|
+
return new BadgeRenderer();
|
|
2000
2617
|
case "walkthrough":
|
|
2001
2618
|
return null;
|
|
2002
2619
|
}
|
|
@@ -2141,6 +2758,6 @@ function buildSelectorPath(element, maxAncestors = 5) {
|
|
|
2141
2758
|
return parts.join(" > ");
|
|
2142
2759
|
}
|
|
2143
2760
|
|
|
2144
|
-
export { ALWAYS_BLOCK_SELECTORS, BannerRenderer, CustomRenderer, DEFAULT_AUTOCAPTURE_CONFIG, DEFAULT_TRACKER_BATCH_SIZE, DEFAULT_TRACKER_FLUSH_INTERVAL_MS, DEFAULT_TRACKER_MAX_RETRIES, FREQUENCY_CACHE_KEY_PREFIX, FREQUENCY_CACHE_MAX_ENTRIES, FREQUENCY_CACHE_TTL_MS, FormRenderer, InlineCustomRenderer, InlineFormRenderer, InlineRenderer, MAX_ANCESTORS, ModalRenderer, PRIORITY_ATTRIBUTES, SENSITIVE_ATTRIBUTES, TooltipRenderer, WALKTHROUGH_ABANDONMENT_TIMEOUT_MS, WALKTHROUGH_STATE_KEY_PREFIX, WalkthroughRenderer, buildSelectorPath, clearBuilderToken, closeGuidePreview, filterHumanClasses, hasDocument, hasLocalStorage, hasNavigatorSendBeacon, hasWindow, isBuilderMode, previewGuide, resolveBuilderContext, resolveBuilderToken, uuidv7 };
|
|
2145
|
-
//# sourceMappingURL=chunk-
|
|
2146
|
-
//# sourceMappingURL=chunk-
|
|
2761
|
+
export { ALWAYS_BLOCK_SELECTORS, BadgeRenderer, BannerRenderer, CustomRenderer, DEFAULT_AUTOCAPTURE_CONFIG, DEFAULT_TRACKER_BATCH_SIZE, DEFAULT_TRACKER_FLUSH_INTERVAL_MS, DEFAULT_TRACKER_MAX_RETRIES, FREQUENCY_CACHE_KEY_PREFIX, FREQUENCY_CACHE_MAX_ENTRIES, FREQUENCY_CACHE_TTL_MS, FormRenderer, InlineCustomRenderer, InlineFormRenderer, InlineRenderer, MAX_ANCESTORS, ModalRenderer, PRIORITY_ATTRIBUTES, SENSITIVE_ATTRIBUTES, TooltipRenderer, WALKTHROUGH_ABANDONMENT_TIMEOUT_MS, WALKTHROUGH_STATE_KEY_PREFIX, WalkthroughRenderer, buildSelectorPath, clearBuilderToken, closeGuidePreview, filterHumanClasses, hasDocument, hasLocalStorage, hasNavigatorSendBeacon, hasWindow, isBuilderMode, previewGuide, resolveBuilderContext, resolveBuilderToken, uuidv7 };
|
|
2762
|
+
//# sourceMappingURL=chunk-ICKEQ7VC.mjs.map
|
|
2763
|
+
//# sourceMappingURL=chunk-ICKEQ7VC.mjs.map
|