veo-sdk 0.5.6 → 0.5.11
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-324AW5Z7.mjs +5 -0
- package/dist/{builder-AMCPHL4K.mjs.map → builder-324AW5Z7.mjs.map} +1 -1
- package/dist/builder.cjs +534 -250
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-A4RHMCCM.mjs → chunk-JXRYBP77.mjs} +3 -3
- package/dist/{chunk-A4RHMCCM.mjs.map → chunk-JXRYBP77.mjs.map} +1 -1
- package/dist/{chunk-CPSHNLZG.mjs → chunk-OTZH2CV2.mjs} +119 -3
- package/dist/chunk-OTZH2CV2.mjs.map +1 -0
- package/dist/{chunk-764FSEHS.mjs → chunk-Z5EBMOZJ.mjs} +536 -252
- package/dist/chunk-Z5EBMOZJ.mjs.map +1 -0
- package/dist/index.cjs +758 -349
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +5 -5
- package/dist/veo-builder.js +10 -10
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +5 -5
- package/dist/veo-guides.js.map +1 -1
- package/package.json +1 -1
- package/dist/builder-AMCPHL4K.mjs +0 -5
- package/dist/chunk-764FSEHS.mjs.map +0 -1
- package/dist/chunk-CPSHNLZG.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -696,6 +696,42 @@ function applyBlockStyle(el, style, kind) {
|
|
|
696
696
|
if (typeof s.marginBottom === "number" && Number.isFinite(s.marginBottom)) {
|
|
697
697
|
el.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
|
|
698
698
|
}
|
|
699
|
+
if (kind !== "image") {
|
|
700
|
+
if (typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
|
|
701
|
+
el.style.fontSize = `${clampNum(s.fontSize, 8, 96)}px`;
|
|
702
|
+
}
|
|
703
|
+
if (typeof s.fontFamily === "string" && s.fontFamily && s.fontFamily !== "inherit") {
|
|
704
|
+
el.style.fontFamily = s.fontFamily;
|
|
705
|
+
}
|
|
706
|
+
if (typeof s.fontWeight === "string" && s.fontWeight) el.style.fontWeight = s.fontWeight;
|
|
707
|
+
if (typeof s.textTransform === "string" && s.textTransform && s.textTransform !== "none") {
|
|
708
|
+
el.style.textTransform = s.textTransform;
|
|
709
|
+
}
|
|
710
|
+
if (typeof s.lineHeight === "number" && Number.isFinite(s.lineHeight)) {
|
|
711
|
+
el.style.lineHeight = `${clampNum(s.lineHeight, 50, 300)}%`;
|
|
712
|
+
}
|
|
713
|
+
if (typeof s.letterSpacing === "number" && Number.isFinite(s.letterSpacing)) {
|
|
714
|
+
el.style.letterSpacing = `${clampNum(s.letterSpacing, -5, 20)}px`;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (typeof s.background === "string" && s.background) el.style.background = s.background;
|
|
718
|
+
if (typeof s.borderWidth === "number" && s.borderWidth > 0) {
|
|
719
|
+
const bc = typeof s.borderColor === "string" && s.borderColor ? s.borderColor : "#bbbbbb";
|
|
720
|
+
el.style.border = `${clampNum(s.borderWidth, 0, 20)}px solid ${bc}`;
|
|
721
|
+
}
|
|
722
|
+
if (kind !== "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
|
|
723
|
+
el.style.borderRadius = `${clampNum(s.radius, 0, 64)}px`;
|
|
724
|
+
}
|
|
725
|
+
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
726
|
+
const p = s[`padding${side}`];
|
|
727
|
+
if (typeof p === "number" && Number.isFinite(p)) {
|
|
728
|
+
el.style.setProperty(`padding-${side.toLowerCase()}`, `${clampNum(p, 0, 128)}px`);
|
|
729
|
+
}
|
|
730
|
+
const m = s[`margin${side}`];
|
|
731
|
+
if (typeof m === "number" && Number.isFinite(m)) {
|
|
732
|
+
el.style.setProperty(`margin-${side.toLowerCase()}`, `${clampNum(m, 0, 128)}px`);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
699
735
|
}
|
|
700
736
|
function createCloseButton(doc, onClose, className = "veo-guide-close") {
|
|
701
737
|
const closeBtn = doc.createElement("button");
|
|
@@ -1950,191 +1986,6 @@ var init_banner_renderer = __esm({
|
|
|
1950
1986
|
};
|
|
1951
1987
|
}
|
|
1952
1988
|
});
|
|
1953
|
-
function inlinePosOf(p) {
|
|
1954
|
-
return p === "before" || p === "prepend" || p === "append" ? p : "after";
|
|
1955
|
-
}
|
|
1956
|
-
function applyViewportPosition(card, position) {
|
|
1957
|
-
const s = card.style;
|
|
1958
|
-
s.position = "fixed";
|
|
1959
|
-
const M = "16px";
|
|
1960
|
-
const v = position.includes("top") ? "top" : position.includes("bottom") ? "bottom" : "middle";
|
|
1961
|
-
const h = position.includes("left") ? "left" : position.includes("right") ? "right" : "middle";
|
|
1962
|
-
if (position === "center") {
|
|
1963
|
-
s.top = "50%";
|
|
1964
|
-
s.left = "50%";
|
|
1965
|
-
s.transform = "translate(-50%, -50%)";
|
|
1966
|
-
return;
|
|
1967
|
-
}
|
|
1968
|
-
if (v === "top") s.top = M;
|
|
1969
|
-
else if (v === "bottom") s.bottom = M;
|
|
1970
|
-
else {
|
|
1971
|
-
s.top = "50%";
|
|
1972
|
-
s.transform = "translateY(-50%)";
|
|
1973
|
-
}
|
|
1974
|
-
if (h === "left") s.left = M;
|
|
1975
|
-
else if (h === "right") s.right = M;
|
|
1976
|
-
else {
|
|
1977
|
-
s.left = "50%";
|
|
1978
|
-
s.transform = `${s.transform} translateX(-50%)`.trim();
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
async function anchorPosition(anchor, wrapper) {
|
|
1982
|
-
const update = async () => {
|
|
1983
|
-
const { x, y } = await dom.computePosition(anchor, wrapper, {
|
|
1984
|
-
placement: "bottom",
|
|
1985
|
-
strategy: "fixed",
|
|
1986
|
-
middleware: [dom.offset(8), dom.flip(), dom.shift({ padding: 8 })]
|
|
1987
|
-
});
|
|
1988
|
-
wrapper.style.left = `${x}px`;
|
|
1989
|
-
wrapper.style.top = `${y}px`;
|
|
1990
|
-
};
|
|
1991
|
-
await update();
|
|
1992
|
-
const reposition = () => void update();
|
|
1993
|
-
window.addEventListener("scroll", reposition, true);
|
|
1994
|
-
window.addEventListener("resize", reposition);
|
|
1995
|
-
return () => {
|
|
1996
|
-
window.removeEventListener("scroll", reposition, true);
|
|
1997
|
-
window.removeEventListener("resize", reposition);
|
|
1998
|
-
};
|
|
1999
|
-
}
|
|
2000
|
-
function applyCompositeDesign(card, design) {
|
|
2001
|
-
const s = card.style;
|
|
2002
|
-
const px = (v) => typeof v === "number" && Number.isFinite(v) ? `${v}px` : null;
|
|
2003
|
-
const strv = (v) => typeof v === "string" && v ? v : null;
|
|
2004
|
-
if (strv(design.background)) s.background = design.background;
|
|
2005
|
-
if (strv(design.textColor)) s.color = design.textColor;
|
|
2006
|
-
if (px(design.fontSize)) s.fontSize = px(design.fontSize);
|
|
2007
|
-
if (strv(design.fontWeight)) s.fontWeight = design.fontWeight;
|
|
2008
|
-
if (strv(design.align)) s.textAlign = design.align;
|
|
2009
|
-
if (typeof design.lineHeight === "number") s.lineHeight = `${design.lineHeight}%`;
|
|
2010
|
-
if (px(design.radius)) s.borderRadius = px(design.radius);
|
|
2011
|
-
if (px(design.width)) s.width = px(design.width);
|
|
2012
|
-
const bw = px(design.borderWidth);
|
|
2013
|
-
if (bw) s.border = `${bw} solid ${strv(design.borderColor) ?? "#e5e5e5"}`;
|
|
2014
|
-
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
2015
|
-
const p = px(design[`padding${side}`]);
|
|
2016
|
-
if (p) s.setProperty(`padding-${side.toLowerCase()}`, p);
|
|
2017
|
-
const m = px(design[`margin${side}`]);
|
|
2018
|
-
if (m) s.setProperty(`margin-${side.toLowerCase()}`, m);
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
var CompositeRenderer;
|
|
2022
|
-
var init_composite_renderer = __esm({
|
|
2023
|
-
"src/plugins/guides/renderers/composite-renderer.ts"() {
|
|
2024
|
-
init_block_builder();
|
|
2025
|
-
init_inline_host();
|
|
2026
|
-
init_wait_for_element();
|
|
2027
|
-
init_base_renderer();
|
|
2028
|
-
CompositeRenderer = class extends BaseRenderer {
|
|
2029
|
-
async render(context) {
|
|
2030
|
-
const idx = context.nav?.stepIndex ?? 0;
|
|
2031
|
-
const step = context.guide.guideSteps[idx];
|
|
2032
|
-
if (!step) return;
|
|
2033
|
-
const anchorSelector = step.position === "relative-to-element" || step.presentation === "inline" ? step.anchorSelector?.trim() || void 0 : void 0;
|
|
2034
|
-
let anchor = null;
|
|
2035
|
-
if (anchorSelector) {
|
|
2036
|
-
anchor = await waitForElement(anchorSelector);
|
|
2037
|
-
if (!anchor) return;
|
|
2038
|
-
}
|
|
2039
|
-
const { host, root } = this.createHost();
|
|
2040
|
-
const doc = root.ownerDocument ?? document;
|
|
2041
|
-
const card = doc.createElement("div");
|
|
2042
|
-
card.className = "veo-card";
|
|
2043
|
-
applyCompositeDesign(card, step.design ?? {});
|
|
2044
|
-
const dismiss = (action, meta) => {
|
|
2045
|
-
context.onInteraction({
|
|
2046
|
-
guideId: context.guide.guideId,
|
|
2047
|
-
stepIndex: idx,
|
|
2048
|
-
action,
|
|
2049
|
-
...meta?.id ? { metadata: { buttonId: meta.id } } : {}
|
|
2050
|
-
});
|
|
2051
|
-
};
|
|
2052
|
-
const synthetic = { blocks: step.blocks ?? [] };
|
|
2053
|
-
for (const node of buildContentNodes(synthetic, doc)) card.appendChild(node);
|
|
2054
|
-
const buttons = (step.blocks ?? []).filter((b) => b.type === "button");
|
|
2055
|
-
if (buttons.length) {
|
|
2056
|
-
const row = doc.createElement("div");
|
|
2057
|
-
row.className = "veo-guide-actions";
|
|
2058
|
-
for (const b of buttons) row.appendChild(this.buildButton(b, doc, context, dismiss));
|
|
2059
|
-
card.appendChild(row);
|
|
2060
|
-
}
|
|
2061
|
-
card.appendChild(
|
|
2062
|
-
createCloseButton(doc, () => {
|
|
2063
|
-
dismiss("dismissed");
|
|
2064
|
-
this.closeOrSkip(context);
|
|
2065
|
-
})
|
|
2066
|
-
);
|
|
2067
|
-
if (step.presentation === "inline" && anchor) {
|
|
2068
|
-
host.style.display = "block";
|
|
2069
|
-
const pos = inlinePosOf(step.inlinePosition);
|
|
2070
|
-
root.appendChild(card);
|
|
2071
|
-
insertHost(anchor, host, pos);
|
|
2072
|
-
this.registerCleanup(keepHostAttached(host, anchorSelector, pos));
|
|
2073
|
-
} else if (step.position === "relative-to-element" && anchor) {
|
|
2074
|
-
const wrapper = doc.createElement("div");
|
|
2075
|
-
wrapper.className = "veo-composite-anchored";
|
|
2076
|
-
wrapper.appendChild(card);
|
|
2077
|
-
root.appendChild(wrapper);
|
|
2078
|
-
this.registerCleanup(await anchorPosition(anchor, wrapper));
|
|
2079
|
-
} else {
|
|
2080
|
-
const overlay = doc.createElement("div");
|
|
2081
|
-
overlay.className = "veo-composite-overlay";
|
|
2082
|
-
if (step.position === "center" && step.design?.backdrop !== false) {
|
|
2083
|
-
overlay.classList.add("veo-composite-overlay--dim");
|
|
2084
|
-
overlay.addEventListener("click", (e) => {
|
|
2085
|
-
if (e.target === overlay) {
|
|
2086
|
-
dismiss("dismissed");
|
|
2087
|
-
this.closeOrSkip(context);
|
|
2088
|
-
}
|
|
2089
|
-
});
|
|
2090
|
-
}
|
|
2091
|
-
applyViewportPosition(card, step.position);
|
|
2092
|
-
overlay.appendChild(card);
|
|
2093
|
-
root.appendChild(overlay);
|
|
2094
|
-
}
|
|
2095
|
-
context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action: "shown" });
|
|
2096
|
-
}
|
|
2097
|
-
/** Botón con enrutado de acción (navegación / cierre / url / guía). */
|
|
2098
|
-
buildButton(block, doc, context, emit) {
|
|
2099
|
-
const btn = doc.createElement("button");
|
|
2100
|
-
btn.type = "button";
|
|
2101
|
-
btn.className = "veo-guide-cta";
|
|
2102
|
-
btn.textContent = block.text?.trim() || "OK";
|
|
2103
|
-
btn.addEventListener("click", () => {
|
|
2104
|
-
emit("cta_clicked", block);
|
|
2105
|
-
const nav = context.nav?.callbacks;
|
|
2106
|
-
switch (block.action) {
|
|
2107
|
-
case "next":
|
|
2108
|
-
case "submit-forms":
|
|
2109
|
-
case "submit-forms-advance":
|
|
2110
|
-
if (nav) nav.onNext();
|
|
2111
|
-
else context.onClose();
|
|
2112
|
-
break;
|
|
2113
|
-
case "prev":
|
|
2114
|
-
nav?.onBack();
|
|
2115
|
-
break;
|
|
2116
|
-
case "url":
|
|
2117
|
-
if (typeof block.url === "string" && isSafeUrl(block.url)) {
|
|
2118
|
-
window.open(block.url, "_blank", "noopener,noreferrer");
|
|
2119
|
-
}
|
|
2120
|
-
break;
|
|
2121
|
-
case "guide":
|
|
2122
|
-
if (block.targetGuideId) context.onOpenGuide?.(block.targetGuideId);
|
|
2123
|
-
this.closeOrSkip(context);
|
|
2124
|
-
break;
|
|
2125
|
-
default:
|
|
2126
|
-
this.closeOrSkip(context);
|
|
2127
|
-
}
|
|
2128
|
-
});
|
|
2129
|
-
return btn;
|
|
2130
|
-
}
|
|
2131
|
-
closeOrSkip(context) {
|
|
2132
|
-
if (context.nav?.callbacks) context.nav.callbacks.onSkip();
|
|
2133
|
-
else context.onClose();
|
|
2134
|
-
}
|
|
2135
|
-
};
|
|
2136
|
-
}
|
|
2137
|
-
});
|
|
2138
1989
|
|
|
2139
1990
|
// src/plugins/guides/renderers/custom-frame.ts
|
|
2140
1991
|
function mountCustomFrame(doc, step, context, opts) {
|
|
@@ -2273,91 +2124,58 @@ var init_custom_frame = __esm({
|
|
|
2273
2124
|
MAX_HOST_CSS = 800 * 1024;
|
|
2274
2125
|
}
|
|
2275
2126
|
});
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
return;
|
|
2286
|
-
}
|
|
2287
|
-
const
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2127
|
+
|
|
2128
|
+
// src/plugins/guides/code-block.ts
|
|
2129
|
+
function mountCodeBlock(block, doc, context) {
|
|
2130
|
+
if (block.sandboxed) {
|
|
2131
|
+
const step = { html: block.html ?? "", css: block.css, js: block.js };
|
|
2132
|
+
const { iframe, cleanup } = mountCustomFrame(doc, step, context, { width: "100%" });
|
|
2133
|
+
const wrapper = doc.createElement("div");
|
|
2134
|
+
wrapper.className = "veo-code-sandbox";
|
|
2135
|
+
wrapper.appendChild(iframe);
|
|
2136
|
+
return { node: wrapper, cleanup };
|
|
2137
|
+
}
|
|
2138
|
+
const hostDoc = doc;
|
|
2139
|
+
const injected = [];
|
|
2140
|
+
if (typeof block.css === "string" && block.css.trim()) {
|
|
2141
|
+
const style = hostDoc.createElement("style");
|
|
2142
|
+
style.textContent = block.css;
|
|
2143
|
+
hostDoc.head.appendChild(style);
|
|
2144
|
+
injected.push(style);
|
|
2145
|
+
}
|
|
2146
|
+
let node = null;
|
|
2147
|
+
if (typeof block.html === "string" && block.html.trim()) {
|
|
2148
|
+
node = doc.createElement("div");
|
|
2149
|
+
node.className = "veo-code-html";
|
|
2150
|
+
node.innerHTML = block.html;
|
|
2151
|
+
for (const old of Array.from(node.querySelectorAll("script"))) {
|
|
2152
|
+
injected.push(runScript(old, hostDoc));
|
|
2153
|
+
old.remove();
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
if (typeof block.js === "string" && block.js.trim()) {
|
|
2157
|
+
const s = hostDoc.createElement("script");
|
|
2158
|
+
s.textContent = block.js;
|
|
2159
|
+
hostDoc.body.appendChild(s);
|
|
2160
|
+
injected.push(s);
|
|
2297
2161
|
}
|
|
2162
|
+
return {
|
|
2163
|
+
node,
|
|
2164
|
+
cleanup: () => {
|
|
2165
|
+
for (const n of injected) n.parentNode?.removeChild(n);
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2298
2168
|
}
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2169
|
+
function runScript(old, hostDoc) {
|
|
2170
|
+
const s = hostDoc.createElement("script");
|
|
2171
|
+
for (const attr of Array.from(old.attributes)) s.setAttribute(attr.name, attr.value);
|
|
2172
|
+
if (!old.src) s.textContent = old.textContent;
|
|
2173
|
+
hostDoc.body.appendChild(s);
|
|
2174
|
+
return s;
|
|
2175
|
+
}
|
|
2176
|
+
var init_code_block = __esm({
|
|
2177
|
+
"src/plugins/guides/code-block.ts"() {
|
|
2304
2178
|
init_custom_frame();
|
|
2305
|
-
DEFAULT_PLACEMENT = { mode: "floating", position: "bottom-right" };
|
|
2306
|
-
DEFAULT_OFFSET = 16;
|
|
2307
|
-
DEFAULT_WIDTH = 360;
|
|
2308
|
-
CustomRenderer = class extends BaseRenderer {
|
|
2309
|
-
async render(context) {
|
|
2310
|
-
const step = context.guide.guideSteps[0];
|
|
2311
|
-
if (!step || typeof step.html !== "string" || step.html.length === 0) return;
|
|
2312
|
-
const placement = step.placement ?? DEFAULT_PLACEMENT;
|
|
2313
|
-
let anchor = null;
|
|
2314
|
-
if (placement.mode === "anchored") {
|
|
2315
|
-
const selector = placement.selector ?? step.selector ?? context.guide.activationRules.selector;
|
|
2316
|
-
if (typeof selector !== "string" || selector.length === 0) return;
|
|
2317
|
-
anchor = await waitForElement(selector);
|
|
2318
|
-
if (!anchor) return;
|
|
2319
|
-
}
|
|
2320
|
-
const { root } = this.createHost();
|
|
2321
|
-
const doc = root.ownerDocument ?? document;
|
|
2322
|
-
const wrapper = doc.createElement("div");
|
|
2323
|
-
wrapper.className = placement.mode === "anchored" ? "veo-custom-anchored" : "veo-custom-floating";
|
|
2324
|
-
const { iframe, cleanup } = mountCustomFrame(doc, step, context, {
|
|
2325
|
-
width: toCssSize(placement.width ?? DEFAULT_WIDTH),
|
|
2326
|
-
fixedHeight: placement.height ?? null
|
|
2327
|
-
});
|
|
2328
|
-
this.registerCleanup(cleanup);
|
|
2329
|
-
wrapper.appendChild(iframe);
|
|
2330
|
-
root.appendChild(wrapper);
|
|
2331
|
-
if (placement.mode === "floating") {
|
|
2332
|
-
applyFloatingPosition(wrapper, placement);
|
|
2333
|
-
} else if (anchor) {
|
|
2334
|
-
this.registerCleanup(await this.setupAnchoredPosition(anchor, wrapper, placement));
|
|
2335
|
-
}
|
|
2336
|
-
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
2337
|
-
}
|
|
2338
|
-
async setupAnchoredPosition(anchor, wrapper, placement) {
|
|
2339
|
-
const side = placement.side ?? "bottom";
|
|
2340
|
-
const update = async () => {
|
|
2341
|
-
const { x, y } = await dom.computePosition(anchor, wrapper, {
|
|
2342
|
-
placement: side,
|
|
2343
|
-
strategy: "fixed",
|
|
2344
|
-
middleware: [dom.offset(placement.offsetY ?? 8), dom.flip(), dom.shift({ padding: 8 })]
|
|
2345
|
-
});
|
|
2346
|
-
wrapper.style.left = `${x}px`;
|
|
2347
|
-
wrapper.style.top = `${y}px`;
|
|
2348
|
-
};
|
|
2349
|
-
await update();
|
|
2350
|
-
const reposition = () => {
|
|
2351
|
-
void update();
|
|
2352
|
-
};
|
|
2353
|
-
window.addEventListener("scroll", reposition, true);
|
|
2354
|
-
window.addEventListener("resize", reposition);
|
|
2355
|
-
return () => {
|
|
2356
|
-
window.removeEventListener("scroll", reposition, true);
|
|
2357
|
-
window.removeEventListener("resize", reposition);
|
|
2358
|
-
};
|
|
2359
|
-
}
|
|
2360
|
-
};
|
|
2361
2179
|
}
|
|
2362
2180
|
});
|
|
2363
2181
|
|
|
@@ -2541,93 +2359,568 @@ function buildField(field, doc) {
|
|
|
2541
2359
|
optionLabel.appendChild(text);
|
|
2542
2360
|
group.appendChild(optionLabel);
|
|
2543
2361
|
}
|
|
2544
|
-
wrapper.appendChild(group);
|
|
2545
|
-
return {
|
|
2546
|
-
wrapper,
|
|
2547
|
-
read: () => {
|
|
2548
|
-
const values = inputs.filter((i) => i.checked).map((i) => i.value);
|
|
2549
|
-
return values.length > 0 ? values : void 0;
|
|
2362
|
+
wrapper.appendChild(group);
|
|
2363
|
+
return {
|
|
2364
|
+
wrapper,
|
|
2365
|
+
read: () => {
|
|
2366
|
+
const values = inputs.filter((i) => i.checked).map((i) => i.value);
|
|
2367
|
+
return values.length > 0 ? values : void 0;
|
|
2368
|
+
}
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
case "checkbox": {
|
|
2372
|
+
const optionLabel = doc.createElement("label");
|
|
2373
|
+
optionLabel.className = "veo-form-option";
|
|
2374
|
+
const checkbox = doc.createElement("input");
|
|
2375
|
+
checkbox.type = "checkbox";
|
|
2376
|
+
const text = doc.createElement("span");
|
|
2377
|
+
text.textContent = field.placeholder || "S\xED";
|
|
2378
|
+
optionLabel.appendChild(checkbox);
|
|
2379
|
+
optionLabel.appendChild(text);
|
|
2380
|
+
wrapper.appendChild(optionLabel);
|
|
2381
|
+
return { wrapper, read: () => checkbox.checked ? true : field.required ? "" : false };
|
|
2382
|
+
}
|
|
2383
|
+
case "yesno": {
|
|
2384
|
+
const group = doc.createElement("div");
|
|
2385
|
+
group.className = "veo-form-options";
|
|
2386
|
+
const name = `veo-${field.key}`;
|
|
2387
|
+
const [yesLabel, noLabel] = field.options?.length === 2 ? field.options : ["S\xED", "No"];
|
|
2388
|
+
const inputs = [];
|
|
2389
|
+
for (const { label: optText, value } of [
|
|
2390
|
+
{ label: yesLabel, value: true },
|
|
2391
|
+
{ label: noLabel, value: false }
|
|
2392
|
+
]) {
|
|
2393
|
+
const optionLabel = doc.createElement("label");
|
|
2394
|
+
optionLabel.className = "veo-form-option";
|
|
2395
|
+
const radio = doc.createElement("input");
|
|
2396
|
+
radio.type = "radio";
|
|
2397
|
+
radio.name = name;
|
|
2398
|
+
inputs.push({ input: radio, value });
|
|
2399
|
+
const text = doc.createElement("span");
|
|
2400
|
+
text.textContent = optText;
|
|
2401
|
+
optionLabel.appendChild(radio);
|
|
2402
|
+
optionLabel.appendChild(text);
|
|
2403
|
+
group.appendChild(optionLabel);
|
|
2404
|
+
}
|
|
2405
|
+
wrapper.appendChild(group);
|
|
2406
|
+
return { wrapper, read: () => inputs.find((i) => i.input.checked)?.value };
|
|
2407
|
+
}
|
|
2408
|
+
case "nps":
|
|
2409
|
+
return buildScale(wrapper, doc, 0, 10, "veo-nps-btn");
|
|
2410
|
+
case "rating":
|
|
2411
|
+
return buildScale(wrapper, doc, 1, 5, "veo-rating-btn", "\u2605");
|
|
2412
|
+
default: {
|
|
2413
|
+
const input = doc.createElement("input");
|
|
2414
|
+
input.type = "text";
|
|
2415
|
+
input.className = "veo-form-input";
|
|
2416
|
+
if (field.placeholder) input.placeholder = field.placeholder;
|
|
2417
|
+
wrapper.appendChild(input);
|
|
2418
|
+
return { wrapper, read: () => input.value.trim() };
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
function buildScale(wrapper, doc, min, max, btnClass, symbol) {
|
|
2423
|
+
const row = doc.createElement("div");
|
|
2424
|
+
row.className = "veo-form-scale";
|
|
2425
|
+
let selected;
|
|
2426
|
+
const buttons = [];
|
|
2427
|
+
for (let n = min; n <= max; n++) {
|
|
2428
|
+
const btn = doc.createElement("button");
|
|
2429
|
+
btn.type = "button";
|
|
2430
|
+
btn.className = btnClass;
|
|
2431
|
+
btn.textContent = symbol ?? String(n);
|
|
2432
|
+
btn.setAttribute("aria-label", String(n));
|
|
2433
|
+
btn.addEventListener("click", () => {
|
|
2434
|
+
selected = n;
|
|
2435
|
+
buttons.forEach((b, i) => {
|
|
2436
|
+
const active2 = symbol ? i + min <= n : i + min === n;
|
|
2437
|
+
b.classList.toggle("veo-scale-active", active2);
|
|
2438
|
+
});
|
|
2439
|
+
});
|
|
2440
|
+
buttons.push(btn);
|
|
2441
|
+
row.appendChild(btn);
|
|
2442
|
+
}
|
|
2443
|
+
wrapper.appendChild(row);
|
|
2444
|
+
return { wrapper, read: () => selected };
|
|
2445
|
+
}
|
|
2446
|
+
var init_form_content = __esm({
|
|
2447
|
+
"src/plugins/guides/form-content.ts"() {
|
|
2448
|
+
init_block_builder();
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
function inlinePosOf(p) {
|
|
2452
|
+
return p === "before" || p === "prepend" || p === "append" ? p : "after";
|
|
2453
|
+
}
|
|
2454
|
+
function applyViewportPosition(card, position, design) {
|
|
2455
|
+
const s = card.style;
|
|
2456
|
+
s.position = "fixed";
|
|
2457
|
+
const M = "16px";
|
|
2458
|
+
const hAlign = design.hAlign === "left" ? "left" : design.hAlign === "right" ? "right" : null;
|
|
2459
|
+
const v = position.includes("top") ? "top" : position.includes("bottom") ? "bottom" : "middle";
|
|
2460
|
+
const h = hAlign ?? (position.includes("left") ? "left" : position.includes("right") ? "right" : "middle");
|
|
2461
|
+
if (v === "top") s.top = M;
|
|
2462
|
+
else if (v === "bottom") s.bottom = M;
|
|
2463
|
+
else s.top = "50%";
|
|
2464
|
+
if (h === "left") s.left = M;
|
|
2465
|
+
else if (h === "right") s.right = M;
|
|
2466
|
+
else s.left = "50%";
|
|
2467
|
+
const tx = h === "middle" ? "-50%" : "0";
|
|
2468
|
+
const ty = v === "middle" ? "-50%" : "0";
|
|
2469
|
+
s.transform = tx === "0" && ty === "0" ? "" : `translate(${tx}, ${ty})`;
|
|
2470
|
+
}
|
|
2471
|
+
function makeCaret(doc, design) {
|
|
2472
|
+
const caret = doc.createElement("div");
|
|
2473
|
+
caret.className = "veo-composite-caret";
|
|
2474
|
+
const w = typeof design.caretWidth === "number" ? design.caretWidth : 12;
|
|
2475
|
+
const h = typeof design.caretHeight === "number" ? design.caretHeight : 12;
|
|
2476
|
+
const s = caret.style;
|
|
2477
|
+
s.position = "absolute";
|
|
2478
|
+
s.width = `${w}px`;
|
|
2479
|
+
s.height = `${h}px`;
|
|
2480
|
+
s.transform = "rotate(45deg)";
|
|
2481
|
+
s.background = typeof design.background === "string" ? design.background : "#ffffff";
|
|
2482
|
+
if (typeof design.borderWidth === "number" && design.borderWidth > 0) {
|
|
2483
|
+
const bc = typeof design.borderColor === "string" ? design.borderColor : "#bbbbbb";
|
|
2484
|
+
s.border = `${design.borderWidth}px solid ${bc}`;
|
|
2485
|
+
}
|
|
2486
|
+
return caret;
|
|
2487
|
+
}
|
|
2488
|
+
function liftTarget(anchor, zIndex) {
|
|
2489
|
+
const el = anchor;
|
|
2490
|
+
const prevPosition = el.style.position;
|
|
2491
|
+
const prevZ = el.style.zIndex;
|
|
2492
|
+
if (getComputedStyle(el).position === "static") el.style.position = "relative";
|
|
2493
|
+
el.style.zIndex = String(zIndex);
|
|
2494
|
+
return () => {
|
|
2495
|
+
el.style.position = prevPosition;
|
|
2496
|
+
el.style.zIndex = prevZ;
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
async function anchorPosition(anchor, wrapper, caret) {
|
|
2500
|
+
const update = async () => {
|
|
2501
|
+
const { x, y, placement, middlewareData } = await dom.computePosition(anchor, wrapper, {
|
|
2502
|
+
placement: "bottom",
|
|
2503
|
+
strategy: "fixed",
|
|
2504
|
+
middleware: [
|
|
2505
|
+
dom.offset(caret ? 10 : 8),
|
|
2506
|
+
dom.flip(),
|
|
2507
|
+
dom.shift({ padding: 8 }),
|
|
2508
|
+
...caret ? [dom.arrow({ element: caret })] : []
|
|
2509
|
+
]
|
|
2510
|
+
});
|
|
2511
|
+
wrapper.style.left = `${x}px`;
|
|
2512
|
+
wrapper.style.top = `${y}px`;
|
|
2513
|
+
if (caret) positionArrow(caret, placement, middlewareData.arrow);
|
|
2514
|
+
};
|
|
2515
|
+
await update();
|
|
2516
|
+
const reposition = () => void update();
|
|
2517
|
+
window.addEventListener("scroll", reposition, true);
|
|
2518
|
+
window.addEventListener("resize", reposition);
|
|
2519
|
+
return () => {
|
|
2520
|
+
window.removeEventListener("scroll", reposition, true);
|
|
2521
|
+
window.removeEventListener("resize", reposition);
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
function applyCompositeDesign(card, design) {
|
|
2525
|
+
const s = card.style;
|
|
2526
|
+
const px = (v) => typeof v === "number" && Number.isFinite(v) ? `${v}px` : null;
|
|
2527
|
+
const strv = (v) => typeof v === "string" && v ? v : null;
|
|
2528
|
+
if (strv(design.background)) s.background = design.background;
|
|
2529
|
+
const textColor = strv(design.color) ?? strv(design.textColor);
|
|
2530
|
+
if (textColor) s.color = textColor;
|
|
2531
|
+
if (px(design.fontSize)) s.fontSize = px(design.fontSize);
|
|
2532
|
+
if (strv(design.fontWeight)) s.fontWeight = design.fontWeight;
|
|
2533
|
+
if (strv(design.fontFamily) && design.fontFamily !== "inherit") {
|
|
2534
|
+
s.fontFamily = design.fontFamily;
|
|
2535
|
+
}
|
|
2536
|
+
if (strv(design.textTransform) && design.textTransform !== "none") {
|
|
2537
|
+
s.textTransform = design.textTransform;
|
|
2538
|
+
}
|
|
2539
|
+
if (typeof design.letterSpacing === "number") s.letterSpacing = `${design.letterSpacing}px`;
|
|
2540
|
+
if (strv(design.align)) s.textAlign = design.align;
|
|
2541
|
+
if (typeof design.lineHeight === "number") s.lineHeight = `${design.lineHeight}%`;
|
|
2542
|
+
if (px(design.radius)) s.borderRadius = px(design.radius);
|
|
2543
|
+
const bw = px(design.borderWidth);
|
|
2544
|
+
if (bw) s.border = `${bw} solid ${strv(design.borderColor) ?? "#e5e5e5"}`;
|
|
2545
|
+
if (strv(design.shadowColor) || typeof design.shadowBlur === "number") {
|
|
2546
|
+
const n = (k) => typeof design[k] === "number" ? design[k] : 0;
|
|
2547
|
+
s.boxShadow = `${n("shadowX")}px ${n("shadowY")}px ${n("shadowBlur")}px ${n("shadowSpread")}px ${strv(design.shadowColor) ?? "rgba(0,0,0,0.2)"}`;
|
|
2548
|
+
}
|
|
2549
|
+
if (design.fullWidth === true) {
|
|
2550
|
+
s.width = "100%";
|
|
2551
|
+
} else if (design.widthType === "fluid") {
|
|
2552
|
+
s.width = "auto";
|
|
2553
|
+
if (px(design.width)) s.maxWidth = px(design.width);
|
|
2554
|
+
} else if (px(design.width)) {
|
|
2555
|
+
s.width = px(design.width);
|
|
2556
|
+
}
|
|
2557
|
+
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
2558
|
+
const p = px(design[`padding${side}`]);
|
|
2559
|
+
if (p) s.setProperty(`padding-${side.toLowerCase()}`, p);
|
|
2560
|
+
const m = px(design[`margin${side}`]);
|
|
2561
|
+
if (m) s.setProperty(`margin-${side.toLowerCase()}`, m);
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
var CompositeRenderer;
|
|
2565
|
+
var init_composite_renderer = __esm({
|
|
2566
|
+
"src/plugins/guides/renderers/composite-renderer.ts"() {
|
|
2567
|
+
init_block_builder();
|
|
2568
|
+
init_code_block();
|
|
2569
|
+
init_form_content();
|
|
2570
|
+
init_inline_host();
|
|
2571
|
+
init_wait_for_element();
|
|
2572
|
+
init_badge_renderer();
|
|
2573
|
+
init_base_renderer();
|
|
2574
|
+
init_floating_arrow();
|
|
2575
|
+
CompositeRenderer = class extends BaseRenderer {
|
|
2576
|
+
constructor() {
|
|
2577
|
+
super(...arguments);
|
|
2578
|
+
/** Delegado para pasos `badge` (reusa el BadgeRenderer con un step sintético). */
|
|
2579
|
+
this.badgeDelegate = null;
|
|
2580
|
+
}
|
|
2581
|
+
async render(context) {
|
|
2582
|
+
const idx = context.nav?.stepIndex ?? 0;
|
|
2583
|
+
const step = context.guide.guideSteps[idx];
|
|
2584
|
+
if (!step) return;
|
|
2585
|
+
if (step.presentation === "badge") {
|
|
2586
|
+
const syntheticStep = {
|
|
2587
|
+
title: "",
|
|
2588
|
+
content: "",
|
|
2589
|
+
selector: step.anchorSelector ?? void 0,
|
|
2590
|
+
blocks: step.blocks ?? [],
|
|
2591
|
+
style: {
|
|
2592
|
+
...step.design ?? {},
|
|
2593
|
+
...step.badge ? { badge: step.badge } : {},
|
|
2594
|
+
...step.badgeTrigger ? { badgeTrigger: step.badgeTrigger } : {}
|
|
2595
|
+
}
|
|
2596
|
+
};
|
|
2597
|
+
const syntheticGuide = {
|
|
2598
|
+
...context.guide,
|
|
2599
|
+
guideSteps: [syntheticStep]
|
|
2600
|
+
};
|
|
2601
|
+
const { nav: _nav, ...rest } = context;
|
|
2602
|
+
this.badgeDelegate = new BadgeRenderer();
|
|
2603
|
+
await this.badgeDelegate.render({ ...rest, guide: syntheticGuide });
|
|
2604
|
+
return;
|
|
2605
|
+
}
|
|
2606
|
+
const anchorSelector = step.position === "relative-to-element" || step.presentation === "inline" ? step.anchorSelector?.trim() || void 0 : void 0;
|
|
2607
|
+
let anchor = null;
|
|
2608
|
+
if (anchorSelector) {
|
|
2609
|
+
anchor = await waitForElement(anchorSelector);
|
|
2610
|
+
if (!anchor) return;
|
|
2611
|
+
}
|
|
2612
|
+
const { host, root } = this.createHost();
|
|
2613
|
+
const doc = root.ownerDocument ?? document;
|
|
2614
|
+
const design = step.design ?? {};
|
|
2615
|
+
if (typeof design.zIndex === "number") host.style.zIndex = String(design.zIndex);
|
|
2616
|
+
const card = doc.createElement("div");
|
|
2617
|
+
card.className = "veo-card";
|
|
2618
|
+
applyCompositeDesign(card, design);
|
|
2619
|
+
const dismiss = (action, meta) => {
|
|
2620
|
+
context.onInteraction({
|
|
2621
|
+
guideId: context.guide.guideId,
|
|
2622
|
+
stepIndex: idx,
|
|
2623
|
+
action,
|
|
2624
|
+
...{}
|
|
2625
|
+
});
|
|
2626
|
+
};
|
|
2627
|
+
const readers = [];
|
|
2628
|
+
this.buildContent(card, step.blocks ?? [], doc, context, idx, readers);
|
|
2629
|
+
if (design.closeButton !== false) {
|
|
2630
|
+
const closeBtn = createCloseButton(doc, () => {
|
|
2631
|
+
dismiss("dismissed");
|
|
2632
|
+
this.closeOrSkip(context);
|
|
2633
|
+
});
|
|
2634
|
+
const closeColor = typeof design.closeColor === "string" ? design.closeColor : null;
|
|
2635
|
+
const hoverColor = typeof design.closeHoverColor === "string" ? design.closeHoverColor : null;
|
|
2636
|
+
if (closeColor) closeBtn.style.color = closeColor;
|
|
2637
|
+
if (hoverColor) {
|
|
2638
|
+
closeBtn.addEventListener("mouseenter", () => {
|
|
2639
|
+
closeBtn.style.color = hoverColor;
|
|
2640
|
+
});
|
|
2641
|
+
closeBtn.addEventListener("mouseleave", () => {
|
|
2642
|
+
closeBtn.style.color = closeColor ?? "";
|
|
2643
|
+
});
|
|
2644
|
+
}
|
|
2645
|
+
card.appendChild(closeBtn);
|
|
2646
|
+
}
|
|
2647
|
+
if (step.presentation === "inline" && anchor) {
|
|
2648
|
+
host.style.display = "block";
|
|
2649
|
+
const pos = inlinePosOf(step.inlinePosition);
|
|
2650
|
+
root.appendChild(card);
|
|
2651
|
+
insertHost(anchor, host, pos);
|
|
2652
|
+
this.registerCleanup(keepHostAttached(host, anchorSelector, pos));
|
|
2653
|
+
} else if (step.position === "relative-to-element" && anchor) {
|
|
2654
|
+
if (design.backdrop === true) {
|
|
2655
|
+
const bg = doc.createElement("div");
|
|
2656
|
+
bg.className = "veo-composite-overlay veo-composite-overlay--dim";
|
|
2657
|
+
if (typeof design.backdropColor === "string") bg.style.background = design.backdropColor;
|
|
2658
|
+
const bz = typeof design.backdropZIndex === "number" ? design.backdropZIndex : 2e5;
|
|
2659
|
+
bg.style.zIndex = String(bz);
|
|
2660
|
+
root.appendChild(bg);
|
|
2661
|
+
if (design.targetAbove === true) this.registerCleanup(liftTarget(anchor, bz + 1));
|
|
2662
|
+
}
|
|
2663
|
+
const wrapper = doc.createElement("div");
|
|
2664
|
+
wrapper.className = "veo-composite-anchored";
|
|
2665
|
+
const caretEl = design.caret === false ? null : makeCaret(doc, design);
|
|
2666
|
+
if (caretEl) wrapper.appendChild(caretEl);
|
|
2667
|
+
wrapper.appendChild(card);
|
|
2668
|
+
root.appendChild(wrapper);
|
|
2669
|
+
this.registerCleanup(await anchorPosition(anchor, wrapper, caretEl));
|
|
2670
|
+
} else {
|
|
2671
|
+
const overlay = doc.createElement("div");
|
|
2672
|
+
overlay.className = "veo-composite-overlay";
|
|
2673
|
+
if (step.position === "center" && design.backdrop !== false) {
|
|
2674
|
+
overlay.classList.add("veo-composite-overlay--dim");
|
|
2675
|
+
if (typeof design.backdropColor === "string") {
|
|
2676
|
+
overlay.style.background = design.backdropColor;
|
|
2677
|
+
}
|
|
2678
|
+
if (typeof design.backdropZIndex === "number") {
|
|
2679
|
+
overlay.style.zIndex = String(design.backdropZIndex);
|
|
2680
|
+
}
|
|
2681
|
+
overlay.addEventListener("click", (e) => {
|
|
2682
|
+
if (e.target === overlay) {
|
|
2683
|
+
dismiss("dismissed");
|
|
2684
|
+
this.closeOrSkip(context);
|
|
2685
|
+
}
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
applyViewportPosition(card, step.position, design);
|
|
2689
|
+
overlay.appendChild(card);
|
|
2690
|
+
root.appendChild(overlay);
|
|
2691
|
+
}
|
|
2692
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action: "shown" });
|
|
2693
|
+
}
|
|
2694
|
+
/** Recorre los bloques en orden y los pinta en la card. */
|
|
2695
|
+
buildContent(card, blocks, doc, context, idx, readers) {
|
|
2696
|
+
const emit = (block) => {
|
|
2697
|
+
context.onInteraction({
|
|
2698
|
+
guideId: context.guide.guideId,
|
|
2699
|
+
stepIndex: idx,
|
|
2700
|
+
action: "cta_clicked",
|
|
2701
|
+
...block.id ? { metadata: { buttonId: block.id } } : {}
|
|
2702
|
+
});
|
|
2703
|
+
};
|
|
2704
|
+
let i = 0;
|
|
2705
|
+
while (i < blocks.length) {
|
|
2706
|
+
const b = blocks[i];
|
|
2707
|
+
if (!b) {
|
|
2708
|
+
i++;
|
|
2709
|
+
continue;
|
|
2710
|
+
}
|
|
2711
|
+
if (b.type === "button") {
|
|
2712
|
+
const row = doc.createElement("div");
|
|
2713
|
+
row.className = "veo-guide-actions";
|
|
2714
|
+
while (i < blocks.length && blocks[i]?.type === "button") {
|
|
2715
|
+
row.appendChild(
|
|
2716
|
+
this.buildButton(blocks[i], doc, context, idx, readers, emit)
|
|
2717
|
+
);
|
|
2718
|
+
i++;
|
|
2719
|
+
}
|
|
2720
|
+
card.appendChild(row);
|
|
2721
|
+
continue;
|
|
2722
|
+
}
|
|
2723
|
+
if (b.type === "code") {
|
|
2724
|
+
const { node: node2, cleanup } = mountCodeBlock(b, doc, context);
|
|
2725
|
+
if (node2) card.appendChild(node2);
|
|
2726
|
+
this.registerCleanup(cleanup);
|
|
2727
|
+
i++;
|
|
2728
|
+
continue;
|
|
2729
|
+
}
|
|
2730
|
+
if (b.type === "form") {
|
|
2731
|
+
const form = doc.createElement("div");
|
|
2732
|
+
form.className = "veo-form";
|
|
2733
|
+
for (const field of b.fields ?? []) {
|
|
2734
|
+
const { wrapper, read } = buildField(field, doc);
|
|
2735
|
+
readers.push({ field, read });
|
|
2736
|
+
form.appendChild(wrapper);
|
|
2737
|
+
}
|
|
2738
|
+
card.appendChild(form);
|
|
2739
|
+
i++;
|
|
2740
|
+
continue;
|
|
2741
|
+
}
|
|
2742
|
+
const node = buildContentBlock(b, doc);
|
|
2743
|
+
if (node) card.appendChild(node);
|
|
2744
|
+
i++;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
/** Botón con enrutado de acción (navegación / cierre / url / guía / envío de forms). */
|
|
2748
|
+
buildButton(block, doc, context, idx, readers, emit) {
|
|
2749
|
+
const btn = doc.createElement("button");
|
|
2750
|
+
btn.type = "button";
|
|
2751
|
+
btn.className = "veo-guide-cta";
|
|
2752
|
+
btn.textContent = block.text?.trim() || "OK";
|
|
2753
|
+
const nav = context.nav?.callbacks;
|
|
2754
|
+
btn.addEventListener("click", () => {
|
|
2755
|
+
switch (block.action) {
|
|
2756
|
+
case "next":
|
|
2757
|
+
emit(block);
|
|
2758
|
+
if (nav) nav.onNext();
|
|
2759
|
+
else context.onClose();
|
|
2760
|
+
break;
|
|
2761
|
+
case "prev":
|
|
2762
|
+
emit(block);
|
|
2763
|
+
nav?.onBack();
|
|
2764
|
+
break;
|
|
2765
|
+
case "url":
|
|
2766
|
+
emit(block);
|
|
2767
|
+
if (typeof block.url === "string" && isSafeUrl(block.url)) {
|
|
2768
|
+
window.open(block.url, "_blank", "noopener,noreferrer");
|
|
2769
|
+
}
|
|
2770
|
+
break;
|
|
2771
|
+
case "guide":
|
|
2772
|
+
emit(block);
|
|
2773
|
+
if (block.targetGuideId) context.onOpenGuide?.(block.targetGuideId);
|
|
2774
|
+
this.closeOrSkip(context);
|
|
2775
|
+
break;
|
|
2776
|
+
case "submit-forms":
|
|
2777
|
+
case "submit-forms-advance":
|
|
2778
|
+
case "submit-forms-goto":
|
|
2779
|
+
this.submitForms(readers, context, idx, btn, () => {
|
|
2780
|
+
emit(block);
|
|
2781
|
+
if (block.action === "submit-forms" || !nav) {
|
|
2782
|
+
context.onClose();
|
|
2783
|
+
} else if (block.action === "submit-forms-goto" && typeof block.targetStep === "number" && nav.onGoto) {
|
|
2784
|
+
nav.onGoto(block.targetStep);
|
|
2785
|
+
} else {
|
|
2786
|
+
nav.onNext();
|
|
2787
|
+
}
|
|
2788
|
+
});
|
|
2789
|
+
break;
|
|
2790
|
+
default:
|
|
2791
|
+
emit(block);
|
|
2792
|
+
this.closeOrSkip(context);
|
|
2793
|
+
}
|
|
2794
|
+
});
|
|
2795
|
+
return btn;
|
|
2796
|
+
}
|
|
2797
|
+
/** Recoge respuestas de todos los form-blocks del paso, valida y envía. */
|
|
2798
|
+
submitForms(readers, context, idx, btn, onDone) {
|
|
2799
|
+
const answers = {};
|
|
2800
|
+
for (const { field, read } of readers) {
|
|
2801
|
+
const value = read();
|
|
2802
|
+
const empty = value === void 0 || value === null || value === "";
|
|
2803
|
+
if (empty) {
|
|
2804
|
+
if (field.required) {
|
|
2805
|
+
btn.textContent = `Completa "${field.label}"`;
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2808
|
+
continue;
|
|
2809
|
+
}
|
|
2810
|
+
answers[field.key] = value;
|
|
2550
2811
|
}
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
}
|
|
2565
|
-
case "yesno": {
|
|
2566
|
-
const group = doc.createElement("div");
|
|
2567
|
-
group.className = "veo-form-options";
|
|
2568
|
-
const name = `veo-${field.key}`;
|
|
2569
|
-
const [yesLabel, noLabel] = field.options?.length === 2 ? field.options : ["S\xED", "No"];
|
|
2570
|
-
const inputs = [];
|
|
2571
|
-
for (const { label: optText, value } of [
|
|
2572
|
-
{ label: yesLabel, value: true },
|
|
2573
|
-
{ label: noLabel, value: false }
|
|
2574
|
-
]) {
|
|
2575
|
-
const optionLabel = doc.createElement("label");
|
|
2576
|
-
optionLabel.className = "veo-form-option";
|
|
2577
|
-
const radio = doc.createElement("input");
|
|
2578
|
-
radio.type = "radio";
|
|
2579
|
-
radio.name = name;
|
|
2580
|
-
inputs.push({ input: radio, value });
|
|
2581
|
-
const text = doc.createElement("span");
|
|
2582
|
-
text.textContent = optText;
|
|
2583
|
-
optionLabel.appendChild(radio);
|
|
2584
|
-
optionLabel.appendChild(text);
|
|
2585
|
-
group.appendChild(optionLabel);
|
|
2812
|
+
if (readers.length > 0 && Object.keys(answers).length === 0) return;
|
|
2813
|
+
btn.disabled = true;
|
|
2814
|
+
const submitFn = context.onFormSubmit ?? (() => Promise.resolve(true));
|
|
2815
|
+
void submitFn(answers).then((ok) => {
|
|
2816
|
+
btn.disabled = false;
|
|
2817
|
+
if (!ok) return;
|
|
2818
|
+
context.onInteraction({
|
|
2819
|
+
guideId: context.guide.guideId,
|
|
2820
|
+
stepIndex: idx,
|
|
2821
|
+
action: "completed"
|
|
2822
|
+
});
|
|
2823
|
+
onDone();
|
|
2824
|
+
});
|
|
2586
2825
|
}
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
if (field.placeholder) input.placeholder = field.placeholder;
|
|
2599
|
-
wrapper.appendChild(input);
|
|
2600
|
-
return { wrapper, read: () => input.value.trim() };
|
|
2601
|
-
}
|
|
2826
|
+
closeOrSkip(context) {
|
|
2827
|
+
if (context.nav?.callbacks) context.nav.callbacks.onSkip();
|
|
2828
|
+
else context.onClose();
|
|
2829
|
+
}
|
|
2830
|
+
/** Además del host propio, destruye el delegado de badge si lo hubo. */
|
|
2831
|
+
destroy() {
|
|
2832
|
+
this.badgeDelegate?.destroy();
|
|
2833
|
+
this.badgeDelegate = null;
|
|
2834
|
+
super.destroy();
|
|
2835
|
+
}
|
|
2836
|
+
};
|
|
2602
2837
|
}
|
|
2603
|
-
}
|
|
2604
|
-
function
|
|
2605
|
-
const
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
const
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2838
|
+
});
|
|
2839
|
+
function applyFloatingPosition(wrapper, placement) {
|
|
2840
|
+
const position = placement.position ?? "bottom-right";
|
|
2841
|
+
const ox = `${placement.offsetX ?? DEFAULT_OFFSET}px`;
|
|
2842
|
+
const oy = `${placement.offsetY ?? DEFAULT_OFFSET}px`;
|
|
2843
|
+
const s = wrapper.style;
|
|
2844
|
+
if (position === "center") {
|
|
2845
|
+
s.top = "50%";
|
|
2846
|
+
s.left = "50%";
|
|
2847
|
+
s.transform = "translate(-50%, -50%)";
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
const [vertical, horizontal] = position.split("-");
|
|
2851
|
+
if (vertical === "top") s.top = oy;
|
|
2852
|
+
else s.bottom = oy;
|
|
2853
|
+
if (horizontal === "left") {
|
|
2854
|
+
s.left = ox;
|
|
2855
|
+
} else if (horizontal === "right") {
|
|
2856
|
+
s.right = ox;
|
|
2857
|
+
} else {
|
|
2858
|
+
s.left = "50%";
|
|
2859
|
+
s.transform = "translateX(-50%)";
|
|
2624
2860
|
}
|
|
2625
|
-
wrapper.appendChild(row);
|
|
2626
|
-
return { wrapper, read: () => selected };
|
|
2627
2861
|
}
|
|
2628
|
-
var
|
|
2629
|
-
|
|
2630
|
-
|
|
2862
|
+
var DEFAULT_PLACEMENT, DEFAULT_OFFSET, DEFAULT_WIDTH, CustomRenderer;
|
|
2863
|
+
var init_custom_renderer = __esm({
|
|
2864
|
+
"src/plugins/guides/renderers/custom-renderer.ts"() {
|
|
2865
|
+
init_wait_for_element();
|
|
2866
|
+
init_base_renderer();
|
|
2867
|
+
init_custom_frame();
|
|
2868
|
+
DEFAULT_PLACEMENT = { mode: "floating", position: "bottom-right" };
|
|
2869
|
+
DEFAULT_OFFSET = 16;
|
|
2870
|
+
DEFAULT_WIDTH = 360;
|
|
2871
|
+
CustomRenderer = class extends BaseRenderer {
|
|
2872
|
+
async render(context) {
|
|
2873
|
+
const step = context.guide.guideSteps[0];
|
|
2874
|
+
if (!step || typeof step.html !== "string" || step.html.length === 0) return;
|
|
2875
|
+
const placement = step.placement ?? DEFAULT_PLACEMENT;
|
|
2876
|
+
let anchor = null;
|
|
2877
|
+
if (placement.mode === "anchored") {
|
|
2878
|
+
const selector = placement.selector ?? step.selector ?? context.guide.activationRules.selector;
|
|
2879
|
+
if (typeof selector !== "string" || selector.length === 0) return;
|
|
2880
|
+
anchor = await waitForElement(selector);
|
|
2881
|
+
if (!anchor) return;
|
|
2882
|
+
}
|
|
2883
|
+
const { root } = this.createHost();
|
|
2884
|
+
const doc = root.ownerDocument ?? document;
|
|
2885
|
+
const wrapper = doc.createElement("div");
|
|
2886
|
+
wrapper.className = placement.mode === "anchored" ? "veo-custom-anchored" : "veo-custom-floating";
|
|
2887
|
+
const { iframe, cleanup } = mountCustomFrame(doc, step, context, {
|
|
2888
|
+
width: toCssSize(placement.width ?? DEFAULT_WIDTH),
|
|
2889
|
+
fixedHeight: placement.height ?? null
|
|
2890
|
+
});
|
|
2891
|
+
this.registerCleanup(cleanup);
|
|
2892
|
+
wrapper.appendChild(iframe);
|
|
2893
|
+
root.appendChild(wrapper);
|
|
2894
|
+
if (placement.mode === "floating") {
|
|
2895
|
+
applyFloatingPosition(wrapper, placement);
|
|
2896
|
+
} else if (anchor) {
|
|
2897
|
+
this.registerCleanup(await this.setupAnchoredPosition(anchor, wrapper, placement));
|
|
2898
|
+
}
|
|
2899
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
2900
|
+
}
|
|
2901
|
+
async setupAnchoredPosition(anchor, wrapper, placement) {
|
|
2902
|
+
const side = placement.side ?? "bottom";
|
|
2903
|
+
const update = async () => {
|
|
2904
|
+
const { x, y } = await dom.computePosition(anchor, wrapper, {
|
|
2905
|
+
placement: side,
|
|
2906
|
+
strategy: "fixed",
|
|
2907
|
+
middleware: [dom.offset(placement.offsetY ?? 8), dom.flip(), dom.shift({ padding: 8 })]
|
|
2908
|
+
});
|
|
2909
|
+
wrapper.style.left = `${x}px`;
|
|
2910
|
+
wrapper.style.top = `${y}px`;
|
|
2911
|
+
};
|
|
2912
|
+
await update();
|
|
2913
|
+
const reposition = () => {
|
|
2914
|
+
void update();
|
|
2915
|
+
};
|
|
2916
|
+
window.addEventListener("scroll", reposition, true);
|
|
2917
|
+
window.addEventListener("resize", reposition);
|
|
2918
|
+
return () => {
|
|
2919
|
+
window.removeEventListener("scroll", reposition, true);
|
|
2920
|
+
window.removeEventListener("resize", reposition);
|
|
2921
|
+
};
|
|
2922
|
+
}
|
|
2923
|
+
};
|
|
2631
2924
|
}
|
|
2632
2925
|
});
|
|
2633
2926
|
|
|
@@ -5671,6 +5964,26 @@ init_modal_renderer();
|
|
|
5671
5964
|
init_tooltip_renderer();
|
|
5672
5965
|
init_walkthrough_renderer();
|
|
5673
5966
|
|
|
5967
|
+
// src/plugins/guides/url-match.ts
|
|
5968
|
+
function matchesUrl(matcher, pageUrl) {
|
|
5969
|
+
switch (matcher.type) {
|
|
5970
|
+
case "exact":
|
|
5971
|
+
return matcher.pattern === pageUrl;
|
|
5972
|
+
case "prefix":
|
|
5973
|
+
return pageUrl.startsWith(matcher.pattern);
|
|
5974
|
+
case "regex":
|
|
5975
|
+
try {
|
|
5976
|
+
return new RegExp(matcher.pattern).test(pageUrl);
|
|
5977
|
+
} catch {
|
|
5978
|
+
return false;
|
|
5979
|
+
}
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5982
|
+
function stepMatchesPage(loc, pageUrl) {
|
|
5983
|
+
if (!loc || loc.scope === "sitewide" || !loc.pattern) return true;
|
|
5984
|
+
return matchesUrl({ type: loc.matchType ?? "prefix", pattern: loc.pattern }, pageUrl);
|
|
5985
|
+
}
|
|
5986
|
+
|
|
5674
5987
|
// src/plugins/guides/walkthrough-state.ts
|
|
5675
5988
|
init_constants2();
|
|
5676
5989
|
var WalkthroughStateManager = class {
|
|
@@ -5825,6 +6138,12 @@ var GuidesController = class {
|
|
|
5825
6138
|
* resolve fresco manda) y se limpian en `destroy()`.
|
|
5826
6139
|
*/
|
|
5827
6140
|
this.armedTriggers = /* @__PURE__ */ new Map();
|
|
6141
|
+
/**
|
|
6142
|
+
* Tour composite ESPERANDO navegación: el usuario avanzó a un paso cuya
|
|
6143
|
+
* `pageLocation` apunta a otra página. Se resume en `checkGuides` cuando llega
|
|
6144
|
+
* a esa página (tours multi-página estilo Pendo).
|
|
6145
|
+
*/
|
|
6146
|
+
this.pendingComposite = null;
|
|
5828
6147
|
this.debug = config.debug === true;
|
|
5829
6148
|
this.apiUrl = config.apiUrl;
|
|
5830
6149
|
this.apiKey = config.apiKey;
|
|
@@ -5874,6 +6193,7 @@ var GuidesController = class {
|
|
|
5874
6193
|
if (await this.tryResumeWalkthrough(endUserId, sessionId, pageUrl)) {
|
|
5875
6194
|
return;
|
|
5876
6195
|
}
|
|
6196
|
+
this.tryResumeComposite(sessionId, pageUrl);
|
|
5877
6197
|
let guides;
|
|
5878
6198
|
try {
|
|
5879
6199
|
guides = await this.resolveGuides(endUserId, pageUrl);
|
|
@@ -5915,6 +6235,10 @@ var GuidesController = class {
|
|
|
5915
6235
|
await this.startWalkthrough(guide, endUserId, sessionId, pageUrl);
|
|
5916
6236
|
return;
|
|
5917
6237
|
}
|
|
6238
|
+
if (guide.guideType === "composite") {
|
|
6239
|
+
this.startComposite(guide, sessionId, pageUrl);
|
|
6240
|
+
return;
|
|
6241
|
+
}
|
|
5918
6242
|
this.runSingleStepRender(guide, sessionId, pageUrl);
|
|
5919
6243
|
}
|
|
5920
6244
|
/**
|
|
@@ -6022,6 +6346,91 @@ ${pageUrl}`;
|
|
|
6022
6346
|
delayMs
|
|
6023
6347
|
);
|
|
6024
6348
|
}
|
|
6349
|
+
/**
|
|
6350
|
+
* Camino del modelo unificado (`composite`). Elige el PRIMER paso cuya
|
|
6351
|
+
* `pageLocation` matchea la URL actual (targeting por-paso) y lo pinta con el
|
|
6352
|
+
* `CompositeRenderer`. Si ningún paso aplica a esta página, no muestra nada.
|
|
6353
|
+
*/
|
|
6354
|
+
startComposite(guide, sessionId, pageUrl) {
|
|
6355
|
+
if (this.pendingComposite?.guide.guideId === guide.guideId) return;
|
|
6356
|
+
const steps = guide.guideSteps;
|
|
6357
|
+
const start = steps.findIndex((s) => stepMatchesPage(s.pageLocation, pageUrl));
|
|
6358
|
+
if (start === -1) return;
|
|
6359
|
+
this.renderCompositeStep(guide, start, sessionId, pageUrl);
|
|
6360
|
+
}
|
|
6361
|
+
/**
|
|
6362
|
+
* Resume un tour composite que quedó esperando navegación: si el paso
|
|
6363
|
+
* pendiente aplica a la página actual, lo pinta. Devuelve `true` si resumió.
|
|
6364
|
+
*/
|
|
6365
|
+
tryResumeComposite(sessionId, pageUrl) {
|
|
6366
|
+
const pending = this.pendingComposite;
|
|
6367
|
+
if (!pending) return false;
|
|
6368
|
+
const steps = pending.guide.guideSteps;
|
|
6369
|
+
const step = steps[pending.stepIndex];
|
|
6370
|
+
if (!step) {
|
|
6371
|
+
this.pendingComposite = null;
|
|
6372
|
+
return false;
|
|
6373
|
+
}
|
|
6374
|
+
if (!stepMatchesPage(step.pageLocation, pageUrl)) return false;
|
|
6375
|
+
this.pendingComposite = null;
|
|
6376
|
+
this.renderCompositeStep(pending.guide, pending.stepIndex, sessionId, pageUrl);
|
|
6377
|
+
return true;
|
|
6378
|
+
}
|
|
6379
|
+
/**
|
|
6380
|
+
* Pinta UN paso composite. En guías multi-paso pasa `nav` al renderer para
|
|
6381
|
+
* que los botones next/prev avancen (misma página); en single-step los
|
|
6382
|
+
* botones cierran. Cada avance destruye el paso anterior y monta el siguiente.
|
|
6383
|
+
*/
|
|
6384
|
+
renderCompositeStep(guide, idx, sessionId, pageUrl) {
|
|
6385
|
+
const steps = guide.guideSteps;
|
|
6386
|
+
const renderer = new CompositeRenderer();
|
|
6387
|
+
this.activeRenderers.add(renderer);
|
|
6388
|
+
this.activeByGuideId.set(guide.guideId, renderer);
|
|
6389
|
+
const cleanup = () => {
|
|
6390
|
+
renderer.destroy();
|
|
6391
|
+
this.activeRenderers.delete(renderer);
|
|
6392
|
+
if (this.activeByGuideId.get(guide.guideId) === renderer) {
|
|
6393
|
+
this.activeByGuideId.delete(guide.guideId);
|
|
6394
|
+
}
|
|
6395
|
+
};
|
|
6396
|
+
const goToStep = (target) => {
|
|
6397
|
+
cleanup();
|
|
6398
|
+
if (target < 0 || target >= steps.length) return;
|
|
6399
|
+
const step = steps[target];
|
|
6400
|
+
if (step && stepMatchesPage(step.pageLocation, pageUrl)) {
|
|
6401
|
+
this.renderCompositeStep(guide, target, sessionId, pageUrl);
|
|
6402
|
+
} else {
|
|
6403
|
+
this.pendingComposite = { guide, stepIndex: target };
|
|
6404
|
+
}
|
|
6405
|
+
};
|
|
6406
|
+
const nav = steps.length > 1 ? {
|
|
6407
|
+
stepIndex: idx,
|
|
6408
|
+
totalSteps: steps.length,
|
|
6409
|
+
callbacks: {
|
|
6410
|
+
onNext: () => goToStep(idx + 1),
|
|
6411
|
+
onBack: () => goToStep(idx - 1),
|
|
6412
|
+
onGoto: (target) => goToStep(target),
|
|
6413
|
+
onSkip: cleanup,
|
|
6414
|
+
onComplete: cleanup
|
|
6415
|
+
}
|
|
6416
|
+
} : void 0;
|
|
6417
|
+
try {
|
|
6418
|
+
void renderer.render({
|
|
6419
|
+
guide,
|
|
6420
|
+
onInteraction: (e) => this.handleInteraction(guide, sessionId, pageUrl, e),
|
|
6421
|
+
onClose: cleanup,
|
|
6422
|
+
onTrack: (name, props) => this.client.track(name, props),
|
|
6423
|
+
onFormSubmit: (answers) => this.submitFormResponse(guide.guideId, answers),
|
|
6424
|
+
onOpenGuide: (targetGuideId) => {
|
|
6425
|
+
void this.openGuideById(targetGuideId, sessionId, pageUrl);
|
|
6426
|
+
},
|
|
6427
|
+
...nav ? { nav } : {}
|
|
6428
|
+
});
|
|
6429
|
+
} catch (err) {
|
|
6430
|
+
if (this.debug) console.error("[veo] composite renderer threw:", err);
|
|
6431
|
+
cleanup();
|
|
6432
|
+
}
|
|
6433
|
+
}
|
|
6025
6434
|
mountSingleStepRenderer(guide, renderer, sessionId, pageUrl) {
|
|
6026
6435
|
this.activeRenderers.add(renderer);
|
|
6027
6436
|
this.activeByGuideId.set(guide.guideId, renderer);
|