@qaiddev/quests-embed 1.0.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.
@@ -0,0 +1,752 @@
1
+ const k = '*{box-sizing:border-box}button{cursor:pointer;font-family:inherit}.qaid-q-root{font-family:var(--qaid-font-family, system-ui, -apple-system, sans-serif);font-size:var(--qaid-font-size, 16px);color:light-dark(#1f2937,#f9fafb)}.qaid-q-backdrop{position:fixed;inset:0;z-index:45;background:rgba(0,0,0,var(--qaid-backdrop-opacity, .4));animation:qaid-q-fade .18s ease-out}@keyframes qaid-q-fade{0%{opacity:0}to{opacity:1}}.qaid-q-modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:50;width:var(--qaid-modal-width, 480px);max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);pointer-events:auto;display:flex;flex-direction:column}.qaid-q-card{background:light-dark(#ffffff,#1f2937);color:light-dark(#1f2937,#f9fafb);border-radius:1rem;box-shadow:0 25px 50px -12px light-dark(rgba(0,0,0,.25),rgba(0,0,0,.6));padding:1.5rem;overflow:hidden;display:flex;flex-direction:column;min-height:0}:where(.qaid-q-root.qaid-q-inline) .qaid-q-card{box-shadow:none;border:1px solid light-dark(#e5e7eb,#374151);flex:1;min-height:0}:where(.qaid-q-root.qaid-q-inline){height:100%;display:flex;flex-direction:column;min-height:0}.qaid-q-header{display:flex;align-items:center;justify-content:space-between;gap:.75rem;margin-bottom:1rem}.qaid-q-title{font-size:1.125em;font-weight:700;margin:0;color:light-dark(#1f2937,#f9fafb)}.qaid-q-step-counter{font-size:.875em;color:light-dark(#6b7280,#9ca3af);font-variant-numeric:tabular-nums;white-space:nowrap}.qaid-q-close{width:28px;height:28px;padding:0;border:none;border-radius:50%;background:transparent;color:light-dark(#6b7280,#9ca3af);display:inline-flex;align-items:center;justify-content:center;-webkit-appearance:none;appearance:none}.qaid-q-close:hover{background:light-dark(#f3f4f6,#374151);color:light-dark(#1f2937,#f9fafb)}.qaid-q-close:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker, #6366f1) 30%,transparent)}.qaid-q-progress{position:relative;height:4px;background:light-dark(#e5e7eb,#374151);border-radius:9999px;overflow:hidden;margin-bottom:1.25rem}.qaid-q-progress--inline{flex:1;min-width:60px;margin-bottom:0}.qaid-q-progress-fill{position:absolute;inset:0 auto 0 0;background:var(--qaid-positive, #10b981);border-radius:9999px;transition:width .3s ease;width:0%}.qaid-q-body{flex:1;min-height:0;overflow-y:auto}.qaid-q-step{display:flex;flex-direction:column;gap:.75rem;animation:qaid-q-slide .22s ease-out}@keyframes qaid-q-slide{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.qaid-q-label{font-size:1.125em;font-weight:600;margin:0;color:light-dark(#1f2937,#f9fafb);line-height:1.35}.qaid-q-label .qaid-q-required{color:var(--qaid-negative, #ef4444);margin-left:.25rem}.qaid-q-description{font-size:.875em;color:light-dark(#6b7280,#9ca3af);margin:0;line-height:1.4}.qaid-q-error{font-size:.8125em;color:var(--qaid-negative, #ef4444);margin:0}.qaid-q-error:empty{display:none}.qaid-q-input,.qaid-q-textarea{width:100%;padding:.75rem .875rem;border:1px solid light-dark(#d1d5db,#374151);border-radius:.625rem;font-family:inherit;font-size:1em;background:light-dark(#ffffff,#111827);color:light-dark(#1f2937,#f9fafb);transition:border-color .15s,box-shadow .15s;-webkit-appearance:none;appearance:none}.qaid-q-textarea{min-height:7rem;resize:vertical;font-family:inherit}.qaid-q-input:focus,.qaid-q-textarea:focus{outline:none;border-color:var(--qaid-marker, #6366f1);box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker, #6366f1) 20%,transparent)}.qaid-q-input::placeholder,.qaid-q-textarea::placeholder{color:light-dark(#9ca3af,#6b7280)}.qaid-q-currency{position:relative}.qaid-q-currency-prefix{position:absolute;left:.875rem;top:50%;transform:translateY(-50%);color:light-dark(#6b7280,#9ca3af);font-size:1em;font-weight:500;pointer-events:none}.qaid-q-currency .qaid-q-input{padding-left:2.25rem}.qaid-q-range-wrap{display:flex;flex-direction:column;gap:.5rem;padding:.5rem 0}.qaid-q-range-value{font-size:2em;font-weight:700;text-align:center;font-variant-numeric:tabular-nums;color:var(--qaid-positive, #10b981)}.qaid-q-range-value .qaid-q-range-unit{font-size:.5em;font-weight:500;color:light-dark(#6b7280,#9ca3af);margin-left:.25rem}.qaid-q-range{width:100%;-webkit-appearance:none;appearance:none;height:6px;border-radius:9999px;background:light-dark(#e5e7eb,#374151);outline:none}.qaid-q-range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:22px;height:22px;border-radius:50%;background:var(--qaid-positive, #10b981);border:3px solid light-dark(#ffffff,#1f2937);box-shadow:0 2px 6px #0003;cursor:pointer;transition:transform .1s}.qaid-q-range::-moz-range-thumb{width:22px;height:22px;border-radius:50%;background:var(--qaid-positive, #10b981);border:3px solid light-dark(#ffffff,#1f2937);box-shadow:0 2px 6px #0003;cursor:pointer}.qaid-q-range:focus-visible::-webkit-slider-thumb{box-shadow:0 0 0 6px color-mix(in srgb,var(--qaid-marker, #6366f1) 30%,transparent)}.qaid-q-range:focus-visible::-moz-range-thumb{box-shadow:0 0 0 6px color-mix(in srgb,var(--qaid-marker, #6366f1) 30%,transparent)}.qaid-q-range-bounds{display:flex;justify-content:space-between;font-size:.8125em;color:light-dark(#6b7280,#9ca3af);font-variant-numeric:tabular-nums}.qaid-q-options{display:flex;flex-direction:column;gap:.5rem}.qaid-q-option{display:flex;align-items:flex-start;gap:.625rem;padding:.75rem .875rem;border:1px solid light-dark(#e5e7eb,#374151);border-radius:.625rem;background:light-dark(#ffffff,#111827);text-align:left;font-size:1em;color:light-dark(#1f2937,#f9fafb);cursor:pointer;transition:border-color .12s,background .12s,box-shadow .12s;-webkit-appearance:none;appearance:none;width:100%}.qaid-q-option:hover{border-color:light-dark(#9ca3af,#4b5563);background:light-dark(#f9fafb,#1f2937)}.qaid-q-option:focus-visible{outline:none;border-color:var(--qaid-marker, #6366f1);box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker, #6366f1) 25%,transparent)}.qaid-q-option.qaid-q-selected{border-color:var(--qaid-positive, #10b981);background:color-mix(in srgb,var(--qaid-positive, #10b981) 10%,transparent)}.qaid-q-option-marker{flex-shrink:0;width:1.25rem;height:1.25rem;border:2px solid light-dark(#d1d5db,#4b5563);border-radius:50%;display:inline-flex;align-items:center;justify-content:center;margin-top:.125rem;background:light-dark(#ffffff,#111827);transition:border-color .12s,background .12s}.qaid-q-option.qaid-q-multi .qaid-q-option-marker{border-radius:.25rem}.qaid-q-option.qaid-q-selected .qaid-q-option-marker{border-color:var(--qaid-positive, #10b981);background:var(--qaid-positive, #10b981)}.qaid-q-option.qaid-q-selected .qaid-q-option-marker:after{content:"";display:block;width:.5rem;height:.5rem;border-radius:50%;background:light-dark(#ffffff,#1f2937)}.qaid-q-option.qaid-q-multi.qaid-q-selected .qaid-q-option-marker:after{content:"";display:block;width:.625rem;height:.625rem;border-radius:0;background:transparent;border-right:2px solid light-dark(#ffffff,#1f2937);border-bottom:2px solid light-dark(#ffffff,#1f2937);transform:rotate(45deg) translate(-1px,-2px)}.qaid-q-option-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:.125rem}.qaid-q-option-image{display:block;width:48px;height:48px;aspect-ratio:1 / 1;object-fit:cover;border-radius:.375rem;background:light-dark(#f3f4f6,#374151);flex-shrink:0}.qaid-q-options--image-horizontal .qaid-q-option-body{flex-direction:row;align-items:center;gap:.75rem}.qaid-q-options--image-horizontal .qaid-q-option-body>:not(.qaid-q-option-image){flex:1;min-width:0;display:flex;flex-direction:column;gap:.125rem}.qaid-q-options--image-vertical{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.5rem}.qaid-q-options--image-vertical .qaid-q-option{flex-direction:column;align-items:stretch;text-align:center;padding:.75rem;gap:.5rem}.qaid-q-options--image-vertical .qaid-q-option-body{align-items:center;text-align:center}.qaid-q-options--image-vertical .qaid-q-option-image{width:100%;height:auto;max-width:100%}.qaid-q-options--image-vertical .qaid-q-option-marker{position:absolute;top:.5rem;right:.5rem;margin-top:0}.qaid-q-options--image-vertical .qaid-q-option{position:relative}.qaid-q-options--image-vertical .qaid-q-option-key{position:absolute;top:.5rem;left:.5rem;align-self:auto}.qaid-q-option-label{font-weight:500;line-height:1.3}.qaid-q-option-desc{font-size:.8125em;color:light-dark(#6b7280,#9ca3af);line-height:1.4}.qaid-q-option-key{flex-shrink:0;align-self:center;font-size:.7em;font-weight:600;padding:.125rem .375rem;border-radius:.25rem;background:light-dark(#f3f4f6,#374151);color:light-dark(#6b7280,#9ca3af);font-variant-numeric:tabular-nums}.qaid-q-footer{display:flex;align-items:center;justify-content:space-between;gap:.5rem;margin-top:1.25rem}.qaid-q-footer--inline-progress{justify-content:flex-start;gap:.75rem}.qaid-q-footer-left,.qaid-q-footer-right{display:flex;gap:.5rem}.qaid-q-btn{padding:.625rem 1.125rem;border-radius:.5rem;font-size:.9375em;font-weight:500;border:1px solid transparent;cursor:pointer;transition:filter .15s,background .15s,color .15s;-webkit-appearance:none;appearance:none}.qaid-q-btn:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--qaid-marker, #6366f1) 30%,transparent)}.qaid-q-btn-secondary{background:transparent;border-color:light-dark(#d1d5db,#4b5563);color:light-dark(#374151,#d1d5db)}.qaid-q-btn-secondary:hover{background:light-dark(#f3f4f6,#374151)}.qaid-q-btn-primary{background:var(--qaid-positive, #10b981);color:var(--qaid-positive-text, #ffffff);border-color:transparent}.qaid-q-btn-primary:hover{filter:brightness(.9)}.qaid-q-btn-primary:disabled{opacity:.5;cursor:not-allowed;filter:none}.qaid-q-hint{font-size:.75em;color:light-dark(#6b7280,#9ca3af)}.qaid-q-hint kbd{display:inline-block;padding:.05rem .35rem;border-radius:.25rem;background:light-dark(#f3f4f6,#374151);border:1px solid light-dark(#e5e7eb,#4b5563);font-family:inherit;font-size:.7em;font-weight:600;color:light-dark(#374151,#e5e7eb)}.qaid-q-done{display:flex;flex-direction:column;align-items:center;text-align:center;gap:.5rem;padding:1.5rem 0}.qaid-q-done-icon{width:48px;height:48px;border-radius:50%;background:color-mix(in srgb,var(--qaid-positive, #10b981) 18%,transparent);color:var(--qaid-positive, #10b981);display:inline-flex;align-items:center;justify-content:center;margin-bottom:.5rem}.qaid-q-done-title{font-size:1.25em;font-weight:700;margin:0}.qaid-q-done-message{color:light-dark(#6b7280,#9ca3af);margin:0}.qaid-q-saving{display:inline-flex;align-items:center;gap:.375rem;font-size:.75em;color:light-dark(#6b7280,#9ca3af);opacity:0;transition:opacity .2s}.qaid-q-saving.qaid-q-visible{opacity:1}.qaid-q-saving-dot{width:6px;height:6px;border-radius:50%;background:var(--qaid-positive, #10b981);animation:qaid-q-pulse 1.2s ease-in-out infinite}@keyframes qaid-q-pulse{0%,to{opacity:.3}50%{opacity:1}}';
2
+ function w(t, e, i) {
3
+ const [a, n, s] = [t, e, i].map((r) => (r = r / 255, r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4)));
4
+ return 0.2126 * a + 0.7152 * n + 0.0722 * s;
5
+ }
6
+ function C(t) {
7
+ if (t.startsWith("#")) {
8
+ const i = t.slice(1), a = i.length === 3 ? i.split("").map((s) => s + s).join("") : i, n = parseInt(a, 16);
9
+ return { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
10
+ }
11
+ const e = t.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
12
+ return e ? { r: parseInt(e[1]), g: parseInt(e[2]), b: parseInt(e[3]) } : null;
13
+ }
14
+ function y(t) {
15
+ const e = C(t);
16
+ return e && w(e.r, e.g, e.b) > 0.4 ? "black" : "white";
17
+ }
18
+ function S(t = {}) {
19
+ const {
20
+ positiveColor: e = "#10b981",
21
+ negativeColor: i = "#ef4444",
22
+ markerColor: a = "#6366f1",
23
+ modalWidth: n = 480,
24
+ backdropOpacity: s = 0.4,
25
+ fontFamily: r = "system-ui, -apple-system, sans-serif",
26
+ fontSize: p = 16
27
+ } = t;
28
+ return {
29
+ "--qaid-positive": e,
30
+ "--qaid-positive-text": y(e),
31
+ "--qaid-negative": i,
32
+ "--qaid-marker": a,
33
+ "--qaid-marker-text": y(a),
34
+ "--qaid-modal-width": `${n}px`,
35
+ "--qaid-backdrop-opacity": String(s),
36
+ "--qaid-font-family": r,
37
+ "--qaid-font-size": `${p}px`
38
+ };
39
+ }
40
+ function N(t, e) {
41
+ for (const [i, a] of Object.entries(e))
42
+ t.style.setProperty(i, a);
43
+ }
44
+ function A() {
45
+ return k;
46
+ }
47
+ function I(t) {
48
+ switch (t.question.type) {
49
+ case "text":
50
+ return L(t.question, t);
51
+ case "currency":
52
+ return V(t.question, t);
53
+ case "range":
54
+ return D(t.question, t);
55
+ case "date":
56
+ return H(t.question, t);
57
+ case "multiple-choice":
58
+ return O(t.question, t);
59
+ }
60
+ }
61
+ function L(t, e) {
62
+ const i = typeof e.initialValue == "string" ? e.initialValue : "";
63
+ let a;
64
+ if (t.multiline) {
65
+ const n = document.createElement("textarea");
66
+ n.className = "qaid-q-textarea", n.value = i, t.placeholder && (n.placeholder = t.placeholder), t.maxLength && (n.maxLength = t.maxLength), a = n;
67
+ } else {
68
+ const n = document.createElement("input");
69
+ n.className = "qaid-q-input", n.type = t.inputType ?? "text", n.value = i, t.placeholder && (n.placeholder = t.placeholder), t.maxLength && (n.maxLength = t.maxLength), n.setAttribute("autocomplete", z(t.inputType)), a = n;
70
+ }
71
+ return a.setAttribute("aria-label", t.label), a.addEventListener("input", () => {
72
+ e.onChange(a.value);
73
+ }), a.addEventListener("keydown", ((n) => {
74
+ if (n.key === "Enter") {
75
+ if (t.multiline) {
76
+ (n.metaKey || n.ctrlKey) && (n.preventDefault(), e.onSubmit());
77
+ return;
78
+ }
79
+ n.isComposing || (n.preventDefault(), e.onSubmit());
80
+ }
81
+ })), {
82
+ element: a,
83
+ focus: () => {
84
+ a.focus();
85
+ const n = a.value.length;
86
+ try {
87
+ a.setSelectionRange(n, n);
88
+ } catch {
89
+ }
90
+ },
91
+ getValue: () => a.value,
92
+ isValid: () => {
93
+ if (!t.required) return !0;
94
+ const n = a.value.trim();
95
+ return !(n.length === 0 || t.minLength && n.length < t.minLength);
96
+ }
97
+ };
98
+ }
99
+ function z(t) {
100
+ switch (t) {
101
+ case "email":
102
+ return "email";
103
+ case "tel":
104
+ return "tel";
105
+ case "url":
106
+ return "url";
107
+ default:
108
+ return "off";
109
+ }
110
+ }
111
+ function V(t, e) {
112
+ const i = document.createElement("div");
113
+ i.className = "qaid-q-currency";
114
+ const a = document.createElement("span");
115
+ a.className = "qaid-q-currency-prefix", a.textContent = T(t.currency ?? "USD", t.locale), i.appendChild(a);
116
+ const n = document.createElement("input");
117
+ return n.className = "qaid-q-input", n.type = "number", n.inputMode = "decimal", n.step = "0.01", typeof t.min == "number" && (n.min = String(t.min)), typeof t.max == "number" && (n.max = String(t.max)), t.placeholder && (n.placeholder = t.placeholder), typeof e.initialValue == "number" ? n.value = String(e.initialValue) : typeof e.initialValue == "string" && e.initialValue !== "" && (n.value = e.initialValue), n.setAttribute("aria-label", t.label), n.addEventListener("input", () => {
118
+ const s = n.value;
119
+ if (s === "")
120
+ e.onChange(null);
121
+ else {
122
+ const r = Number(s);
123
+ e.onChange(Number.isNaN(r) ? null : r);
124
+ }
125
+ }), n.addEventListener("keydown", (s) => {
126
+ s.key === "Enter" && !s.isComposing && (s.preventDefault(), e.onSubmit());
127
+ }), i.appendChild(n), {
128
+ element: i,
129
+ focus: () => n.focus(),
130
+ getValue: () => {
131
+ if (n.value === "") return null;
132
+ const s = Number(n.value);
133
+ return Number.isNaN(s) ? null : s;
134
+ },
135
+ isValid: () => {
136
+ const s = n.value === "" ? null : Number(n.value);
137
+ return !(t.required && (s === null || Number.isNaN(s)) || s !== null && !Number.isNaN(s) && (typeof t.min == "number" && s < t.min || typeof t.max == "number" && s > t.max));
138
+ }
139
+ };
140
+ }
141
+ function T(t, e) {
142
+ try {
143
+ return new Intl.NumberFormat(e, {
144
+ style: "currency",
145
+ currency: t,
146
+ currencyDisplay: "narrowSymbol",
147
+ maximumFractionDigits: 0
148
+ }).formatToParts(0).find((s) => s.type === "currency")?.value ?? t;
149
+ } catch {
150
+ return t;
151
+ }
152
+ }
153
+ function D(t, e) {
154
+ const i = document.createElement("div");
155
+ i.className = "qaid-q-range-wrap";
156
+ const a = document.createElement("div");
157
+ a.className = "qaid-q-range-value";
158
+ const n = document.createElement("span");
159
+ if (a.appendChild(n), t.unit) {
160
+ const o = document.createElement("span");
161
+ o.className = "qaid-q-range-unit", o.textContent = t.unit, a.appendChild(o);
162
+ }
163
+ const s = typeof e.initialValue == "number" ? e.initialValue : t.defaultValue ?? t.min, r = document.createElement("input");
164
+ r.className = "qaid-q-range", r.type = "range", r.min = String(t.min), r.max = String(t.max), r.step = String(t.step ?? 1), r.value = String(s), r.setAttribute("aria-label", t.label), r.setAttribute("aria-valuemin", String(t.min)), r.setAttribute("aria-valuemax", String(t.max));
165
+ const p = () => {
166
+ n.textContent = r.value, r.setAttribute("aria-valuenow", r.value);
167
+ };
168
+ p(), r.addEventListener("input", () => {
169
+ p(), e.onChange(Number(r.value));
170
+ }), r.addEventListener("keydown", (o) => {
171
+ o.key === "Enter" && (o.preventDefault(), e.onSubmit());
172
+ });
173
+ const h = document.createElement("div");
174
+ h.className = "qaid-q-range-bounds";
175
+ const f = document.createElement("span");
176
+ f.textContent = `${t.min}${t.unit ?? ""}`;
177
+ const m = document.createElement("span");
178
+ return m.textContent = `${t.max}${t.unit ?? ""}`, h.appendChild(f), h.appendChild(m), i.appendChild(a), i.appendChild(r), i.appendChild(h), queueMicrotask(() => e.onChange(Number(r.value))), {
179
+ element: i,
180
+ focus: () => r.focus(),
181
+ getValue: () => Number(r.value),
182
+ isValid: () => !0
183
+ };
184
+ }
185
+ function H(t, e) {
186
+ const i = document.createElement("input");
187
+ return i.className = "qaid-q-input", i.type = "date", t.min && (i.min = t.min), t.max && (i.max = t.max), typeof e.initialValue == "string" && (i.value = e.initialValue), i.setAttribute("aria-label", t.label), i.addEventListener("input", () => {
188
+ e.onChange(i.value || null);
189
+ }), i.addEventListener("keydown", (a) => {
190
+ a.key === "Enter" && !a.isComposing && (a.preventDefault(), e.onSubmit());
191
+ }), {
192
+ element: i,
193
+ focus: () => i.focus(),
194
+ getValue: () => i.value || null,
195
+ isValid: () => t.required ? i.value !== "" : !0
196
+ };
197
+ }
198
+ function O(t, e) {
199
+ const i = document.createElement("div"), a = t.imageAlignment ?? "horizontal", s = t.options.some((o) => !!o.image) ? a === "vertical" ? " qaid-q-options--image-vertical" : " qaid-q-options--image-horizontal" : "";
200
+ i.className = `qaid-q-options${s}`, i.setAttribute("role", t.multiple ? "group" : "radiogroup"), i.setAttribute("aria-label", t.label);
201
+ const r = /* @__PURE__ */ new Set();
202
+ if (t.multiple && Array.isArray(e.initialValue))
203
+ for (const o of e.initialValue) r.add(o);
204
+ else !t.multiple && typeof e.initialValue == "string" && r.add(e.initialValue);
205
+ const p = [];
206
+ t.options.forEach((o, c) => {
207
+ const u = document.createElement("button");
208
+ u.type = "button", u.className = `qaid-q-option${t.multiple ? " qaid-q-multi" : ""}`, u.dataset.value = o.value, u.setAttribute("role", t.multiple ? "checkbox" : "radio"), u.setAttribute("aria-checked", r.has(o.value) ? "true" : "false"), r.has(o.value) && u.classList.add("qaid-q-selected");
209
+ const g = document.createElement("span");
210
+ g.className = "qaid-q-option-marker", g.setAttribute("aria-hidden", "true");
211
+ const l = document.createElement("span");
212
+ if (l.className = "qaid-q-option-body", o.image) {
213
+ const d = document.createElement("img");
214
+ d.className = "qaid-q-option-image", d.src = o.image, d.alt = "", d.loading = "lazy", d.decoding = "async", l.appendChild(d);
215
+ }
216
+ const b = document.createElement("span");
217
+ if (b.className = "qaid-q-option-label", b.textContent = o.label, l.appendChild(b), o.description) {
218
+ const d = document.createElement("span");
219
+ d.className = "qaid-q-option-desc", d.textContent = o.description, l.appendChild(d);
220
+ }
221
+ if (u.appendChild(g), u.appendChild(l), c < 9) {
222
+ const d = document.createElement("span");
223
+ d.className = "qaid-q-option-key", d.textContent = String(c + 1), d.setAttribute("aria-hidden", "true"), u.appendChild(d);
224
+ }
225
+ u.addEventListener("click", () => f(c)), u.addEventListener("keydown", (d) => {
226
+ if (d.key === "ArrowDown" || d.key === "ArrowRight") {
227
+ d.preventDefault(), h((c + 1) % p.length);
228
+ return;
229
+ }
230
+ if (d.key === "ArrowUp" || d.key === "ArrowLeft") {
231
+ d.preventDefault(), h((c - 1 + p.length) % p.length);
232
+ return;
233
+ }
234
+ !t.multiple && d.key === "Enter" && r.size > 0 && (d.preventDefault(), e.onSubmit());
235
+ }), p.push(u), i.appendChild(u);
236
+ }), i.addEventListener("keydown", (o) => {
237
+ const c = o.key;
238
+ if (c.length === 1 && c >= "1" && c <= "9") {
239
+ const u = parseInt(c, 10) - 1;
240
+ u < p.length && (o.preventDefault(), f(u), h(u));
241
+ }
242
+ });
243
+ function h(o) {
244
+ p[o]?.focus();
245
+ }
246
+ function f(o) {
247
+ const c = t.options[o].value;
248
+ t.multiple ? r.has(c) ? r.delete(c) : r.add(c) : (r.clear(), r.add(c)), p.forEach((u) => {
249
+ const g = u.dataset.value, l = r.has(g);
250
+ u.classList.toggle("qaid-q-selected", l), u.setAttribute("aria-checked", l ? "true" : "false");
251
+ }), e.onChange(m()), !t.multiple && e.autoAdvance && setTimeout(() => e.onAutoAdvance(), 180);
252
+ }
253
+ function m() {
254
+ if (t.multiple) return Array.from(r);
255
+ const o = r.values().next();
256
+ return o.done ? null : o.value;
257
+ }
258
+ return {
259
+ element: i,
260
+ focus: () => {
261
+ (p.find((c) => c.classList.contains("qaid-q-selected")) ?? p[0])?.focus();
262
+ },
263
+ getValue: () => m(),
264
+ isValid: () => t.required ? r.size > 0 : !0
265
+ };
266
+ }
267
+ function F(t, e) {
268
+ return t.questions.filter(
269
+ (i) => i.visibleIf ? v(i.visibleIf, e) : !0
270
+ );
271
+ }
272
+ function v(t, e) {
273
+ if ("allOf" in t) {
274
+ for (const n of t.allOf)
275
+ if (!v(n, e)) return !1;
276
+ return !0;
277
+ }
278
+ if ("anyOf" in t) {
279
+ for (const n of t.anyOf)
280
+ if (v(n, e)) return !0;
281
+ return !1;
282
+ }
283
+ const i = e[t.questionId], a = i != null && !(typeof i == "string" && i.trim() === "") && !(Array.isArray(i) && i.length === 0);
284
+ return "answered" in t ? t.answered ? a : !a : a ? "equals" in t ? q(i, t.equals) : "notEquals" in t ? !q(i, t.notEquals) : "in" in t ? t.in.some((n) => q(i, n)) : !1 : !1;
285
+ }
286
+ function q(t, e) {
287
+ return Array.isArray(t) ? t.some((i) => i === e) : t === e;
288
+ }
289
+ const x = "qaid_visitor_id";
290
+ function P() {
291
+ try {
292
+ let t = localStorage.getItem(x);
293
+ return t || (t = crypto.randomUUID(), localStorage.setItem(x, t)), t;
294
+ } catch {
295
+ return crypto.randomUUID();
296
+ }
297
+ }
298
+ const Q = '<svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 2l10 10M12 2L2 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/></svg>', U = '<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12l4 4L19 7" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>';
299
+ class j {
300
+ config;
301
+ questionnaire = null;
302
+ inlineQuestionnaire;
303
+ configUrl;
304
+ state = "LOADING";
305
+ stepIndex = 0;
306
+ // Cached subset of questionnaire.questions that are currently visible
307
+ // given the answers so far. Recomputed on every answer change so
308
+ // stepIndex always indexes into THIS list, not the raw question array.
309
+ visibleQuestions = [];
310
+ // Latched goToStep request when called before init finishes. Applied
311
+ // once the questionnaire + visible list are ready.
312
+ pendingGoToStep = null;
313
+ // True after the first step has rendered. Used so the focus-on-step
314
+ // logic in renderStep() can distinguish initial mount (driven by the
315
+ // `autoFocus` config) from subsequent step changes (always focused so
316
+ // keyboard + screen-reader users land on the active input after Next
317
+ // / Back).
318
+ hasRenderedStep = !1;
319
+ answers = {};
320
+ responseId = null;
321
+ visitorId;
322
+ // Per-question pending autosave (text/currency are debounced)
323
+ pendingSaveTimer = null;
324
+ pendingSaveQuestionId = null;
325
+ pendingSaveValue = null;
326
+ inflightSaves = 0;
327
+ // Shadow DOM
328
+ shadowHost = null;
329
+ shadowRoot = null;
330
+ isUserContainer = !1;
331
+ // Mounted UI elements
332
+ rootEl = null;
333
+ cardEl = null;
334
+ titleEl = null;
335
+ stepCounterEl = null;
336
+ progressEl = null;
337
+ progressFillEl = null;
338
+ bodyEl = null;
339
+ footerEl = null;
340
+ savingEl = null;
341
+ backdropEl = null;
342
+ currentInput = null;
343
+ boundKeyDown;
344
+ cssVars = {};
345
+ constructor(e) {
346
+ this.config = {
347
+ endpoint: e.endpoint,
348
+ apiKey: e.apiKey ?? "",
349
+ container: e.container ?? "",
350
+ zIndex: e.zIndex ?? 50,
351
+ colors: {
352
+ positive: e.colors?.positive ?? "#10b981",
353
+ negative: e.colors?.negative ?? "#ef4444",
354
+ marker: e.colors?.marker ?? "#6366f1"
355
+ },
356
+ modalWidth: e.modalWidth ?? 480,
357
+ backdropOpacity: e.backdropOpacity ?? 0.4,
358
+ fontFamily: e.fontFamily ?? "system-ui, -apple-system, sans-serif",
359
+ fontSize: e.fontSize ?? 16,
360
+ css: e.css ?? "",
361
+ autoAdvance: e.autoAdvance ?? !1,
362
+ saveDebounceMs: e.saveDebounceMs ?? 500,
363
+ autoFocus: e.autoFocus ?? !0,
364
+ progressPosition: e.progressPosition ?? "top"
365
+ }, this.inlineQuestionnaire = e.questionnaire, this.configUrl = e.configUrl, this.visitorId = P(), this.boundKeyDown = this.handleKeyDown.bind(this), this.init();
366
+ }
367
+ async init() {
368
+ this.cssVars = S({
369
+ positiveColor: this.config.colors.positive,
370
+ negativeColor: this.config.colors.negative,
371
+ markerColor: this.config.colors.marker,
372
+ modalWidth: this.config.modalWidth,
373
+ backdropOpacity: this.config.backdropOpacity,
374
+ fontFamily: this.config.fontFamily,
375
+ fontSize: this.config.fontSize
376
+ }), this.mountShell(), this.renderLoading();
377
+ try {
378
+ const e = await this.loadQuestionnaire();
379
+ if (!e || !e.questions || e.questions.length === 0)
380
+ throw new Error("Questionnaire is empty");
381
+ if (this.questionnaire = e, this.recomputeVisible(), this.pendingGoToStep) {
382
+ const i = this.visibleQuestions.findIndex(
383
+ (a) => a.id === this.pendingGoToStep
384
+ );
385
+ i !== -1 && (this.stepIndex = i), this.pendingGoToStep = null;
386
+ }
387
+ this.createResponse(), this.state = "READY", this.renderHeader(), this.renderStep();
388
+ } catch (e) {
389
+ this.state = "ERROR", this.renderError(e);
390
+ }
391
+ }
392
+ // ------------------------------------------------------------------
393
+ // Loading the questionnaire JSON
394
+ // ------------------------------------------------------------------
395
+ async loadQuestionnaire() {
396
+ if (this.inlineQuestionnaire) return this.inlineQuestionnaire;
397
+ if (!this.configUrl)
398
+ throw new Error("No questionnaire or configUrl provided");
399
+ const e = await fetch(this.configUrl, {
400
+ headers: { Accept: "application/json" }
401
+ });
402
+ if (!e.ok)
403
+ throw new Error(`Failed to load questionnaire (${e.status})`);
404
+ return await e.json();
405
+ }
406
+ // ------------------------------------------------------------------
407
+ // Shell (shadow host + card chrome that's stable across steps)
408
+ // ------------------------------------------------------------------
409
+ mountShell() {
410
+ this.shadowHost = document.createElement("div"), this.shadowHost.setAttribute("data-qaid-quests", "");
411
+ const e = this.config.container ? document.querySelector(this.config.container) : null;
412
+ e ? (this.shadowHost.style.display = "block", this.shadowHost.style.height = "100%", this.shadowHost.style.minHeight = "0", e.appendChild(this.shadowHost), this.isUserContainer = !0) : (this.shadowHost.style.position = "fixed", this.shadowHost.style.inset = "0", this.shadowHost.style.zIndex = String(this.config.zIndex), this.shadowHost.style.pointerEvents = "none", document.body.appendChild(this.shadowHost)), this.shadowRoot = this.shadowHost.attachShadow({ mode: "open" });
413
+ const i = document.createElement("style");
414
+ if (i.textContent = A(), this.shadowRoot.appendChild(i), this.config.css) {
415
+ const a = document.createElement("style");
416
+ a.textContent = this.config.css, this.shadowRoot.appendChild(a);
417
+ }
418
+ if (this.rootEl = document.createElement("div"), this.rootEl.className = `qaid-q-root${this.isUserContainer ? " qaid-q-inline" : ""}`, N(this.rootEl, this.cssVars), this.isUserContainer)
419
+ this.cardEl = document.createElement("div"), this.cardEl.className = "qaid-q-card", this.rootEl.appendChild(this.cardEl);
420
+ else {
421
+ this.backdropEl = document.createElement("div"), this.backdropEl.className = "qaid-q-backdrop", this.backdropEl.addEventListener("click", () => this.close()), this.rootEl.appendChild(this.backdropEl);
422
+ const a = document.createElement("div");
423
+ a.className = "qaid-q-modal", this.cardEl = document.createElement("div"), this.cardEl.className = "qaid-q-card", a.appendChild(this.cardEl), this.rootEl.appendChild(a);
424
+ }
425
+ this.shadowRoot.appendChild(this.rootEl), !this.isUserContainer && this.shadowHost && (this.shadowHost.style.pointerEvents = "auto"), document.addEventListener("keydown", this.boundKeyDown);
426
+ }
427
+ renderLoading() {
428
+ if (!this.cardEl) return;
429
+ this.cardEl.replaceChildren();
430
+ const e = document.createElement("p");
431
+ e.className = "qaid-q-description", e.textContent = "Loading…", this.cardEl.appendChild(e);
432
+ }
433
+ renderError(e) {
434
+ if (!this.cardEl) return;
435
+ this.cardEl.replaceChildren();
436
+ const i = document.createElement("h3");
437
+ i.className = "qaid-q-title", i.textContent = "Couldn't load form";
438
+ const a = document.createElement("p");
439
+ a.className = "qaid-q-description", a.textContent = e instanceof Error ? e.message : String(e), this.cardEl.appendChild(i), this.cardEl.appendChild(a);
440
+ }
441
+ renderHeader() {
442
+ if (!this.cardEl || !this.questionnaire) return;
443
+ this.cardEl.replaceChildren();
444
+ const e = this.config.progressPosition === "bottom", i = !!this.questionnaire.title, a = !!this.questionnaire.description, n = !this.isUserContainer;
445
+ this.savingEl = document.createElement("div"), this.savingEl.className = "qaid-q-saving";
446
+ const s = document.createElement("span");
447
+ s.className = "qaid-q-saving-dot";
448
+ const r = document.createElement("span");
449
+ r.textContent = "Saving…", this.savingEl.appendChild(s), this.savingEl.appendChild(r), this.stepCounterEl = document.createElement("div"), this.stepCounterEl.className = "qaid-q-step-counter", this.progressEl = document.createElement("div"), this.progressEl.className = e ? "qaid-q-progress qaid-q-progress--inline" : "qaid-q-progress", this.progressFillEl = document.createElement("div"), this.progressFillEl.className = "qaid-q-progress-fill", this.progressEl.appendChild(this.progressFillEl);
450
+ const p = i || n || !e;
451
+ if (p) {
452
+ const h = document.createElement("div");
453
+ h.className = "qaid-q-header";
454
+ const f = document.createElement("div");
455
+ i && (this.titleEl = document.createElement("h2"), this.titleEl.className = "qaid-q-title", this.titleEl.textContent = this.questionnaire.title, f.appendChild(this.titleEl));
456
+ const m = document.createElement("div");
457
+ if (m.style.display = "flex", m.style.alignItems = "center", m.style.gap = "0.5rem", e || (m.appendChild(this.savingEl), m.appendChild(this.stepCounterEl)), n) {
458
+ const o = document.createElement("button");
459
+ o.type = "button", o.className = "qaid-q-close", o.setAttribute("aria-label", "Close form"), o.innerHTML = Q, o.addEventListener("click", () => this.close()), m.appendChild(o);
460
+ }
461
+ h.appendChild(f), h.appendChild(m), this.cardEl.appendChild(h);
462
+ }
463
+ if (a) {
464
+ const h = document.createElement("p");
465
+ h.className = "qaid-q-description", h.textContent = this.questionnaire.description, h.style.marginTop = p ? "-0.5rem" : "0", h.style.marginBottom = "0.75rem", this.cardEl.appendChild(h);
466
+ }
467
+ e || this.cardEl.appendChild(this.progressEl), this.bodyEl = document.createElement("div"), this.bodyEl.className = "qaid-q-body", this.cardEl.appendChild(this.bodyEl), this.footerEl = document.createElement("div"), this.footerEl.className = "qaid-q-footer", this.cardEl.appendChild(this.footerEl);
468
+ }
469
+ // ------------------------------------------------------------------
470
+ // Step rendering
471
+ // ------------------------------------------------------------------
472
+ renderStep() {
473
+ if (!this.questionnaire || !this.bodyEl || !this.footerEl) return;
474
+ const e = this.visibleQuestions.length;
475
+ if (e === 0) {
476
+ this.submit();
477
+ return;
478
+ }
479
+ this.stepIndex >= e && (this.stepIndex = e - 1);
480
+ const i = this.stepIndex, a = this.visibleQuestions[i];
481
+ if (this.stepCounterEl && (this.stepCounterEl.textContent = `${i + 1} / ${e}`), this.progressFillEl) {
482
+ const l = (i + 1) / e * 100;
483
+ this.progressFillEl.style.width = `${l}%`;
484
+ }
485
+ const n = document.createElement("div");
486
+ n.className = "qaid-q-step", n.setAttribute("role", "group"), n.setAttribute("aria-labelledby", `qaid-q-label-${i}`);
487
+ const s = document.createElement("label");
488
+ if (s.id = `qaid-q-label-${i}`, s.className = "qaid-q-label", s.textContent = a.label, a.required) {
489
+ const l = document.createElement("span");
490
+ l.className = "qaid-q-required", l.textContent = "*", l.setAttribute("aria-label", "required"), s.appendChild(l);
491
+ }
492
+ if (n.appendChild(s), a.description) {
493
+ const l = document.createElement("p");
494
+ l.className = "qaid-q-description", l.textContent = a.description, n.appendChild(l);
495
+ }
496
+ const r = document.createElement("p");
497
+ r.className = "qaid-q-error", r.setAttribute("aria-live", "polite");
498
+ const p = this.answers[a.id] ?? null, h = i === e - 1, f = I({
499
+ question: a,
500
+ initialValue: p,
501
+ onChange: (l) => {
502
+ this.handleAnswerChange(a, l), r.textContent && (r.textContent = "");
503
+ },
504
+ onSubmit: () => this.advance(a, r),
505
+ onAutoAdvance: () => this.advance(a, r),
506
+ autoAdvance: this.config.autoAdvance
507
+ });
508
+ this.currentInput = f, n.appendChild(f.element), n.appendChild(r), this.bodyEl.replaceChildren(n), this.footerEl.replaceChildren();
509
+ const m = this.config.progressPosition === "bottom", o = i > 0 ? (() => {
510
+ const l = document.createElement("button");
511
+ return l.type = "button", l.className = "qaid-q-btn qaid-q-btn-secondary", l.textContent = this.questionnaire.backLabel ?? "Back", l.addEventListener("click", () => this.back()), l;
512
+ })() : null, c = document.createElement("button");
513
+ if (c.type = "button", c.className = "qaid-q-btn qaid-q-btn-primary", c.textContent = h ? this.questionnaire.submitLabel ?? "Submit" : this.questionnaire.nextLabel ?? "Next", c.addEventListener("click", () => this.advance(a, r)), m)
514
+ this.footerEl.classList.add("qaid-q-footer--inline-progress"), o && this.footerEl.appendChild(o), this.progressEl && this.footerEl.appendChild(this.progressEl), this.savingEl && this.footerEl.appendChild(this.savingEl), this.stepCounterEl && this.footerEl.appendChild(this.stepCounterEl), this.footerEl.appendChild(c);
515
+ else {
516
+ this.footerEl.classList.remove("qaid-q-footer--inline-progress");
517
+ const l = document.createElement("div");
518
+ l.className = "qaid-q-footer-left";
519
+ const b = document.createElement("div");
520
+ b.className = "qaid-q-footer-right", o && l.appendChild(o);
521
+ const d = document.createElement("span");
522
+ d.className = "qaid-q-hint", d.innerHTML = h ? "<kbd>Enter</kbd> to submit" : "<kbd>Enter</kbd> to continue", b.appendChild(d), b.appendChild(c), this.footerEl.appendChild(l), this.footerEl.appendChild(b);
523
+ }
524
+ const u = !this.hasRenderedStep;
525
+ this.hasRenderedStep = !0, (!u || this.config.autoFocus) && requestAnimationFrame(() => f.focus());
526
+ }
527
+ renderDone() {
528
+ if (!this.cardEl || !this.questionnaire) return;
529
+ this.cardEl.replaceChildren();
530
+ const e = document.createElement("div");
531
+ e.className = "qaid-q-done";
532
+ const i = document.createElement("div");
533
+ i.className = "qaid-q-done-icon", i.innerHTML = U, e.appendChild(i);
534
+ const a = document.createElement("h3");
535
+ if (a.className = "qaid-q-done-title", a.textContent = this.questionnaire.thankYouTitle ?? "Thank you!", e.appendChild(a), this.questionnaire.thankYouMessage) {
536
+ const n = document.createElement("p");
537
+ n.className = "qaid-q-done-message", n.textContent = this.questionnaire.thankYouMessage, e.appendChild(n);
538
+ }
539
+ if (!this.isUserContainer) {
540
+ const n = document.createElement("button");
541
+ n.type = "button", n.className = "qaid-q-btn qaid-q-btn-primary", n.textContent = "Close", n.style.marginTop = "1rem", n.addEventListener("click", () => this.close()), e.appendChild(n);
542
+ }
543
+ this.cardEl.appendChild(e);
544
+ }
545
+ // ------------------------------------------------------------------
546
+ // Navigation
547
+ // ------------------------------------------------------------------
548
+ advance(e, i) {
549
+ if (!this.currentInput || !this.questionnaire) return;
550
+ if (!this.currentInput.isValid()) {
551
+ i.textContent = R(e), this.currentInput.focus();
552
+ return;
553
+ }
554
+ if (this.handleAnswerChange(e, this.currentInput.getValue(), { immediate: !0 }), this.stepIndex === this.visibleQuestions.length - 1) {
555
+ this.submit();
556
+ return;
557
+ }
558
+ this.stepIndex++, this.renderStep();
559
+ }
560
+ back() {
561
+ this.flushPendingSave(), this.stepIndex > 0 && (this.stepIndex--, this.recomputeVisible(), this.stepIndex >= this.visibleQuestions.length && (this.stepIndex = Math.max(0, this.visibleQuestions.length - 1)), this.renderStep());
562
+ }
563
+ // ------------------------------------------------------------------
564
+ // Answer handling + autosave
565
+ // ------------------------------------------------------------------
566
+ recomputeVisible() {
567
+ this.questionnaire && (this.visibleQuestions = F(this.questionnaire, this.answers));
568
+ }
569
+ handleAnswerChange(e, i, a = {}) {
570
+ this.answers[e.id] = i, this.recomputeVisible();
571
+ const n = e.type === "text" || e.type === "currency" || e.type === "range";
572
+ if (a.immediate || !n) {
573
+ this.flushPendingSave(), this.saveAnswer(e.id, i);
574
+ return;
575
+ }
576
+ this.pendingSaveQuestionId = e.id, this.pendingSaveValue = i, this.pendingSaveTimer && clearTimeout(this.pendingSaveTimer), this.pendingSaveTimer = setTimeout(() => {
577
+ this.flushPendingSave();
578
+ }, this.config.saveDebounceMs);
579
+ }
580
+ flushPendingSave() {
581
+ if (this.pendingSaveTimer && (clearTimeout(this.pendingSaveTimer), this.pendingSaveTimer = null), this.pendingSaveQuestionId !== null) {
582
+ const e = this.pendingSaveQuestionId, i = this.pendingSaveValue;
583
+ this.pendingSaveQuestionId = null, this.pendingSaveValue = null, this.saveAnswer(e, i);
584
+ }
585
+ }
586
+ saveAnswer(e, i) {
587
+ this.showSaving(!0), this.inflightSaves++, this.doSave(e, i).finally(() => {
588
+ this.inflightSaves--, this.inflightSaves === 0 && this.showSaving(!1);
589
+ });
590
+ }
591
+ async doSave(e, i) {
592
+ let a = 0;
593
+ for (; this.responseId === null && a < 5e3; )
594
+ await new Promise((n) => setTimeout(n, 50)), a += 50;
595
+ if (this.responseId !== null)
596
+ try {
597
+ await fetch(`${this.config.endpoint}/${this.responseId}`, {
598
+ method: "PATCH",
599
+ headers: this.jsonHeaders(),
600
+ body: JSON.stringify({ questionId: e, value: i })
601
+ });
602
+ } catch (n) {
603
+ console.error("[quests-embed] failed to save answer:", n);
604
+ }
605
+ }
606
+ async createResponse() {
607
+ try {
608
+ const e = await fetch(this.config.endpoint, {
609
+ method: "POST",
610
+ headers: this.jsonHeaders(),
611
+ body: JSON.stringify({
612
+ apiKey: this.config.apiKey || void 0,
613
+ questId: this.questionnaire?.id,
614
+ pageUrl: window.location.href,
615
+ visitorId: this.visitorId,
616
+ userAgent: navigator.userAgent
617
+ })
618
+ });
619
+ if (e.ok) {
620
+ const i = await e.json();
621
+ this.responseId = i.id;
622
+ }
623
+ } catch (e) {
624
+ console.error("[quests-embed] failed to create response:", e);
625
+ }
626
+ }
627
+ async submit() {
628
+ for (this.flushPendingSave(); this.inflightSaves > 0; )
629
+ await new Promise((e) => setTimeout(e, 50));
630
+ if (this.responseId !== null)
631
+ try {
632
+ await fetch(`${this.config.endpoint}/${this.responseId}/submit`, {
633
+ method: "POST",
634
+ headers: this.jsonHeaders(),
635
+ body: JSON.stringify({ answers: this.answers })
636
+ });
637
+ } catch (e) {
638
+ console.error("[quests-embed] failed to submit:", e);
639
+ }
640
+ this.state = "DONE", this.renderDone();
641
+ }
642
+ jsonHeaders() {
643
+ const e = {
644
+ "Content-Type": "application/json",
645
+ Accept: "application/json"
646
+ };
647
+ return this.config.apiKey && (e["X-API-Key"] = this.config.apiKey), e;
648
+ }
649
+ showSaving(e) {
650
+ this.savingEl && this.savingEl.classList.toggle("qaid-q-visible", e);
651
+ }
652
+ // ------------------------------------------------------------------
653
+ // Keyboard / lifecycle
654
+ // ------------------------------------------------------------------
655
+ handleKeyDown(e) {
656
+ e.key === "Escape" && !this.isUserContainer && this.state !== "DONE" && this.close();
657
+ }
658
+ close() {
659
+ this.flushPendingSave(), this.destroy();
660
+ }
661
+ /** Destroy the embed and clean up all resources */
662
+ destroy() {
663
+ document.removeEventListener("keydown", this.boundKeyDown), this.pendingSaveTimer && (clearTimeout(this.pendingSaveTimer), this.pendingSaveTimer = null), this.shadowHost && (this.shadowHost.remove(), this.shadowHost = null, this.shadowRoot = null), this.rootEl = null, this.cardEl = null, this.bodyEl = null, this.footerEl = null, this.titleEl = null, this.stepCounterEl = null, this.progressFillEl = null, this.savingEl = null, this.backdropEl = null, this.currentInput = null;
664
+ }
665
+ /** Read-only snapshot of current answers */
666
+ getAnswers() {
667
+ return { ...this.answers };
668
+ }
669
+ /**
670
+ * Jump to the visible question with the given id. Returns true if
671
+ * the question is currently visible (and the embed navigated to
672
+ * it), false if it's hidden by an unmet `visibleIf` predicate or
673
+ * unknown. If the embed is still initializing, the request is
674
+ * latched and applied as soon as the questionnaire is ready.
675
+ *
676
+ * Intended for editor previews and other host-driven step control.
677
+ */
678
+ goToStep(e) {
679
+ if (this.state !== "READY" || !this.questionnaire)
680
+ return this.pendingGoToStep = e, !1;
681
+ const i = this.visibleQuestions.findIndex((a) => a.id === e);
682
+ return i === -1 ? !1 : (i === this.stepIndex || (this.flushPendingSave(), this.stepIndex = i, this.renderStep()), !0);
683
+ }
684
+ }
685
+ function R(t) {
686
+ switch (t.type) {
687
+ case "text":
688
+ return t.minLength ? `Please enter at least ${t.minLength} characters.` : "This field is required.";
689
+ case "currency":
690
+ return "Please enter a valid amount.";
691
+ case "date":
692
+ return "Please pick a date.";
693
+ case "multiple-choice":
694
+ return t.multiple ? "Please select at least one option." : "Please select an option.";
695
+ case "range":
696
+ return "Please choose a value.";
697
+ }
698
+ }
699
+ function E(t) {
700
+ return document.querySelector(t)?.textContent?.trim() ?? "";
701
+ }
702
+ function M() {
703
+ const t = document.querySelector(
704
+ 'script[type="application/json"][data-quests-config]'
705
+ );
706
+ if (!t) return null;
707
+ const e = t.textContent?.trim();
708
+ if (!e) return null;
709
+ try {
710
+ const i = JSON.parse(e);
711
+ return i.cssSelector && !i.css && (i.css = E(i.cssSelector), delete i.cssSelector), i;
712
+ } catch {
713
+ return null;
714
+ }
715
+ }
716
+ function $(t) {
717
+ const e = t.getAttribute("data-endpoint");
718
+ if (!e) return null;
719
+ const i = t.getAttribute("data-config-url"), a = t.getAttribute("data-api-key"), n = t.getAttribute("data-container"), s = t.getAttribute("data-zindex"), r = t.getAttribute("data-positive-color"), p = t.getAttribute("data-negative-color"), h = t.getAttribute("data-marker-color"), f = t.getAttribute("data-modal-width"), m = t.getAttribute("data-backdrop-opacity"), o = t.getAttribute("data-font-family"), c = t.getAttribute("data-font-size"), u = t.getAttribute("data-css-selector"), g = t.getAttribute("data-auto-advance"), l = t.getAttribute("data-save-debounce-ms");
720
+ return {
721
+ endpoint: e,
722
+ configUrl: i ?? void 0,
723
+ apiKey: a ?? void 0,
724
+ container: n ?? void 0,
725
+ zIndex: s ? parseInt(s, 10) : void 0,
726
+ colors: {
727
+ positive: r ?? void 0,
728
+ negative: p ?? void 0,
729
+ marker: h ?? void 0
730
+ },
731
+ modalWidth: f ? parseInt(f, 10) : void 0,
732
+ backdropOpacity: m ? parseFloat(m) : void 0,
733
+ fontFamily: o ?? void 0,
734
+ fontSize: c ? parseInt(c, 10) : void 0,
735
+ css: u ? E(u) : void 0,
736
+ autoAdvance: g === "true" ? !0 : void 0,
737
+ saveDebounceMs: l ? parseInt(l, 10) : void 0
738
+ };
739
+ }
740
+ if (typeof document < "u") {
741
+ const t = () => {
742
+ const e = document.currentScript, i = M(), a = e ? $(e) : null, n = i ?? a;
743
+ n?.endpoint && (n.configUrl || n.questionnaire) && new j(n);
744
+ };
745
+ document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", t) : t();
746
+ }
747
+ export {
748
+ j as QaidQuests,
749
+ v as evaluateRule,
750
+ F as getVisibleQuestions
751
+ };
752
+ //# sourceMappingURL=qaid-quests.js.map