@syntrologie/adapt-faq 2.28.0 → 2.29.1

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/runtime.js CHANGED
@@ -1,838 +1,11 @@
1
1
  import {
2
- stripMountPlumbing
3
- } from "./chunk-IMSLXYXR.js";
4
- import {
5
- getAnswerText,
6
- purple,
7
- renderAnswerHtml,
8
- slateGrey
9
- } from "./chunk-KRKRB4OL.js";
10
- import "./chunk-5WRI5ZAA.js";
11
-
12
- // src/executors.ts
13
- function resolveItem(store, itemId, itemQuestion) {
14
- if (itemId) {
15
- const found = store.getState().items.find((i) => i.config.id === itemId);
16
- if (found) return found;
17
- }
18
- if (itemQuestion) {
19
- const found = store.findByQuestion(itemQuestion);
20
- if (found) return found;
21
- }
22
- throw new Error("FAQ item not found");
23
- }
24
- async function executeScrollToFaq(action, context, store) {
25
- const item = resolveItem(store, action.itemId, action.itemQuestion);
26
- const { id } = item.config;
27
- if (action.expand !== false) {
28
- store.expand(id);
29
- }
30
- const el = document.querySelector(`[data-faq-item-id="${id}"]`);
31
- if (el) {
32
- el.scrollIntoView({
33
- behavior: action.behavior ?? "smooth"
34
- });
35
- }
36
- context.publishEvent("faq:scroll_to", { itemId: id });
37
- return {
38
- cleanup: () => {
39
- }
40
- };
41
- }
42
- async function executeToggleFaqItem(action, context, store) {
43
- const item = resolveItem(store, action.itemId, action.itemQuestion);
44
- const { id } = item.config;
45
- const desiredState = action.state ?? "toggle";
46
- let newState;
47
- switch (desiredState) {
48
- case "open":
49
- store.expand(id);
50
- newState = "open";
51
- break;
52
- case "closed":
53
- store.collapse(id);
54
- newState = "closed";
55
- break;
56
- default: {
57
- const wasExpanded = store.getState().expandedItems.has(id);
58
- store.toggle(id);
59
- newState = wasExpanded ? "closed" : "open";
60
- break;
61
- }
62
- }
63
- context.publishEvent("faq:toggle", { itemId: id, newState });
64
- return {
65
- cleanup: () => {
66
- }
67
- };
68
- }
69
- async function executeUpdateFaq(action, context, store) {
70
- switch (action.operation) {
71
- case "add": {
72
- const items = action.items ?? [];
73
- const position = action.position === "prepend" ? "prepend" : "append";
74
- store.addItems(items, position);
75
- break;
76
- }
77
- case "remove": {
78
- if (!action.itemId) {
79
- throw new Error("FAQ item not found");
80
- }
81
- const exists = store.getState().items.some((i) => i.config.id === action.itemId);
82
- if (!exists) {
83
- throw new Error("FAQ item not found");
84
- }
85
- store.removeItem(action.itemId);
86
- break;
87
- }
88
- case "reorder": {
89
- const order = action.order ?? [];
90
- store.reorderItems(order);
91
- break;
92
- }
93
- case "replace": {
94
- const items = action.items ?? [];
95
- store.replaceItems(items);
96
- break;
97
- }
98
- }
99
- context.publishEvent("faq:update", { operation: action.operation });
100
- return {
101
- cleanup: () => {
102
- }
103
- };
104
- }
105
- var executorDefinitions = [
106
- { kind: "faq:scroll_to", executor: executeScrollToFaq },
107
- { kind: "faq:toggle_item", executor: executeToggleFaqItem },
108
- { kind: "faq:update", executor: executeUpdateFaq }
109
- ];
110
-
111
- // src/FAQWidgetLit.ts
112
- import { html, LitElement, nothing } from "lit";
113
- import { styleMap } from "lit/directives/style-map.js";
114
- import { unsafeHTML } from "lit/directives/unsafe-html.js";
115
-
116
- // src/faq-styles.ts
117
- var baseStyles = {
118
- container: {
119
- fontFamily: "var(--sc-font-family, system-ui, -apple-system, sans-serif)",
120
- maxWidth: "800px",
121
- margin: "0 auto"
122
- },
123
- searchWrapper: {
124
- marginBottom: "8px"
125
- },
126
- searchInput: {
127
- width: "100%",
128
- padding: "12px 16px",
129
- borderRadius: "8px",
130
- fontSize: "14px",
131
- outline: "none",
132
- transition: "border-color 0.15s ease",
133
- backgroundColor: "var(--sc-content-search-background)",
134
- color: "var(--sc-content-search-color)"
135
- },
136
- accordion: {
137
- display: "flex",
138
- flexDirection: "column",
139
- gap: "var(--sc-content-item-gap, 6px)"
140
- },
141
- item: {
142
- borderRadius: "var(--sc-content-border-radius, 8px)",
143
- overflow: "hidden",
144
- transition: "box-shadow 0.15s ease"
145
- },
146
- question: {
147
- width: "100%",
148
- padding: "var(--sc-content-item-padding, 12px 16px)",
149
- display: "flex",
150
- alignItems: "center",
151
- justifyContent: "space-between",
152
- border: "none",
153
- cursor: "pointer",
154
- fontSize: "var(--sc-content-item-font-size, 15px)",
155
- fontWeight: 500,
156
- textAlign: "left",
157
- transition: "background-color 0.15s ease"
158
- },
159
- chevron: {
160
- fontSize: "20px",
161
- transition: "transform 0.2s ease",
162
- color: "var(--sc-content-chevron-color, currentColor)"
163
- },
164
- answer: {
165
- padding: "var(--sc-content-body-padding, 0 16px 12px 16px)",
166
- fontSize: "var(--sc-content-body-font-size, 14px)",
167
- lineHeight: 1.6,
168
- overflow: "hidden",
169
- transition: "max-height 0.2s ease, padding 0.2s ease"
170
- },
171
- category: {
172
- display: "inline-block",
173
- fontSize: "11px",
174
- fontWeight: 600,
175
- textTransform: "uppercase",
176
- letterSpacing: "0.05em",
177
- padding: "4px 8px",
178
- borderRadius: "4px",
179
- marginBottom: "8px"
180
- },
181
- categoryHeader: {
182
- fontSize: "var(--sc-content-category-font-size, 12px)",
183
- fontWeight: 700,
184
- textTransform: "uppercase",
185
- letterSpacing: "0.05em",
186
- padding: "var(--sc-content-category-padding, 8px 4px 4px 4px)",
187
- marginTop: "var(--sc-content-category-gap, 4px)"
188
- },
189
- feedback: {
190
- display: "flex",
191
- alignItems: "center",
192
- gap: "8px",
193
- marginTop: "12px",
194
- paddingTop: "10px",
195
- borderTop: "1px solid rgba(0, 0, 0, 0.08)",
196
- fontSize: "13px"
197
- },
198
- feedbackButton: {
199
- background: "none",
200
- border: "1px solid transparent",
201
- cursor: "pointer",
202
- fontSize: "16px",
203
- padding: "4px 8px",
204
- borderRadius: "4px",
205
- transition: "background-color 0.15s ease, border-color 0.15s ease"
206
- },
207
- feedbackButtonSelected: {
208
- borderColor: "rgba(0, 0, 0, 0.2)",
209
- backgroundColor: "rgba(0, 0, 0, 0.04)"
210
- },
211
- emptyState: {
212
- textAlign: "center",
213
- padding: "48px 24px",
214
- fontSize: "14px"
215
- },
216
- noResults: {
217
- textAlign: "center",
218
- padding: "32px 16px",
219
- fontSize: "14px"
220
- }
221
- };
222
- var themeStyles = {
223
- light: {
224
- container: {
225
- backgroundColor: "transparent",
226
- color: "inherit"
227
- },
228
- searchInput: {
229
- border: `1px solid ${slateGrey[11]}`
230
- },
231
- item: {
232
- backgroundColor: "var(--sc-content-background)",
233
- borderTop: "var(--sc-content-border)",
234
- borderRight: "var(--sc-content-border)",
235
- borderBottom: "var(--sc-content-border)",
236
- borderLeft: "var(--sc-content-border)"
237
- },
238
- itemExpanded: {
239
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)"
240
- },
241
- question: {
242
- backgroundColor: "transparent",
243
- color: "var(--sc-content-text-color)"
244
- },
245
- questionHover: {
246
- backgroundColor: "var(--sc-content-background-hover)"
247
- },
248
- answer: {
249
- color: "var(--sc-content-text-secondary-color)"
250
- },
251
- category: {
252
- backgroundColor: purple[8],
253
- color: purple[2]
254
- },
255
- categoryHeader: {
256
- color: slateGrey[7]
257
- },
258
- emptyState: {
259
- color: slateGrey[8]
260
- },
261
- feedbackPrompt: {
262
- color: slateGrey[7]
263
- }
264
- },
265
- dark: {
266
- container: {
267
- backgroundColor: "transparent",
268
- color: "inherit"
269
- },
270
- searchInput: {
271
- border: `1px solid ${slateGrey[5]}`
272
- },
273
- item: {
274
- backgroundColor: "var(--sc-content-background)",
275
- borderTop: "var(--sc-content-border)",
276
- borderRight: "var(--sc-content-border)",
277
- borderBottom: "var(--sc-content-border)",
278
- borderLeft: "var(--sc-content-border)"
279
- },
280
- itemExpanded: {
281
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)"
282
- },
283
- question: {
284
- backgroundColor: "transparent",
285
- color: "var(--sc-content-text-color)"
286
- },
287
- questionHover: {
288
- backgroundColor: "var(--sc-content-background-hover)"
289
- },
290
- answer: {
291
- color: "var(--sc-content-text-secondary-color)"
292
- },
293
- category: {
294
- backgroundColor: purple[0],
295
- color: purple[6]
296
- },
297
- categoryHeader: {
298
- color: slateGrey[8]
299
- },
300
- emptyState: {
301
- color: slateGrey[7]
302
- },
303
- feedbackPrompt: {
304
- color: slateGrey[8]
305
- }
306
- }
307
- };
308
-
309
- // src/FAQWidgetLit.ts
310
- function sm(styles) {
311
- return styles;
312
- }
313
- function resolveFeedbackConfig(feedback) {
314
- if (!feedback) return null;
315
- if (feedback === true) return { style: "thumbs" };
316
- return feedback;
317
- }
318
- function getFeedbackPrompt(feedbackConfig) {
319
- return feedbackConfig.prompt ?? "Was this helpful?";
320
- }
321
- function resolveTheme(theme) {
322
- if (theme && theme !== "auto") return theme;
323
- if (typeof window !== "undefined") {
324
- return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
325
- }
326
- return "light";
327
- }
328
- var FAQAccordionElement = class extends LitElement {
329
- constructor() {
330
- super(...arguments);
331
- // -----------------------------------------------------------------------
332
- // Property declarations
333
- // -----------------------------------------------------------------------
334
- this.faqConfig = {
335
- expandBehavior: "single",
336
- searchable: false,
337
- theme: "auto",
338
- actions: []
339
- };
340
- this.runtime = null;
341
- this.instanceId = "faq-widget";
342
- // Internal state
343
- this._expandedIds = /* @__PURE__ */ new Set();
344
- this._highlightId = null;
345
- this._searchQuery = "";
346
- this._feedbackState = /* @__PURE__ */ new Map();
347
- this._hoveredId = null;
348
- // Subscription cleanup handles
349
- this._unsubContext = null;
350
- this._unsubAccumulator = null;
351
- this._unsubCta = null;
352
- this._unsubDeepLink = null;
353
- this._unsubSessionMetrics = null;
354
- this._highlightTimer = null;
355
- }
356
- // -----------------------------------------------------------------------
357
- // Light DOM — no Shadow DOM so CSS variables from the host page apply
358
- // -----------------------------------------------------------------------
359
- createRenderRoot() {
360
- return this;
361
- }
362
- // -----------------------------------------------------------------------
363
- // Lifecycle
364
- // -----------------------------------------------------------------------
365
- connectedCallback() {
366
- super.connectedCallback();
367
- this._subscribeAll();
368
- }
369
- disconnectedCallback() {
370
- super.disconnectedCallback();
371
- this._unsubscribeAll();
372
- if (this._highlightTimer !== null) {
373
- clearTimeout(this._highlightTimer);
374
- this._highlightTimer = null;
375
- }
376
- }
377
- // Re-subscribe when runtime changes (property may be set after connectedCallback)
378
- updated(changedProps) {
379
- if (changedProps.has("runtime")) {
380
- this._unsubscribeAll();
381
- this._subscribeAll();
382
- }
383
- }
384
- // -----------------------------------------------------------------------
385
- // Subscription management
386
- // -----------------------------------------------------------------------
387
- _subscribeAll() {
388
- if (!this.runtime) return;
389
- this._unsubContext = this.runtime.context.subscribe(() => {
390
- this.requestUpdate();
391
- });
392
- if (this.runtime.accumulator?.subscribe) {
393
- this._unsubAccumulator = this.runtime.accumulator.subscribe(() => {
394
- this.requestUpdate();
395
- });
396
- }
397
- if (this.runtime.sessionMetrics?.subscribe) {
398
- this._unsubSessionMetrics = this.runtime.sessionMetrics.subscribe(() => {
399
- this.requestUpdate();
400
- });
401
- }
402
- if (this.runtime.events.subscribe) {
403
- if (this.runtime.events.getRecent) {
404
- const recentEvents = this.runtime.events.getRecent(
405
- { patterns: ["^action\\.tooltip_cta_clicked$", "^action\\.modal_cta_clicked$"] },
406
- 10
407
- );
408
- const pendingEvent = recentEvents.filter((e) => {
409
- const actionId = e.props?.actionId;
410
- return typeof actionId === "string" && actionId.startsWith("faq:open:");
411
- }).pop();
412
- if (pendingEvent && Date.now() - pendingEvent.ts < 1e4) {
413
- const questionId = pendingEvent.props.actionId.replace("faq:open:", "");
414
- this._expandedIds = /* @__PURE__ */ new Set([questionId]);
415
- requestAnimationFrame(() => {
416
- const el = document.querySelector(`[data-faq-item-id="${questionId}"]`);
417
- if (el) el.scrollIntoView({ behavior: "smooth", block: "center" });
418
- });
419
- }
420
- }
421
- this._unsubCta = this.runtime.events.subscribe(
422
- { patterns: ["^action\\.tooltip_cta_clicked$", "^action\\.modal_cta_clicked$"] },
423
- (event) => {
424
- const actionId = event.props?.actionId;
425
- if (typeof actionId !== "string" || !actionId.startsWith("faq:open:")) return;
426
- const questionId = actionId.replace("faq:open:", "");
427
- this._expandedIds = /* @__PURE__ */ new Set([questionId]);
428
- requestAnimationFrame(() => {
429
- const el = document.querySelector(`[data-faq-item-id="${questionId}"]`);
430
- if (el) el.scrollIntoView({ behavior: "smooth", block: "center" });
431
- });
432
- this.runtime?.events.publish("canvas.requestOpen");
433
- }
434
- );
435
- }
436
- if (this.runtime.events.subscribe) {
437
- const handleDeepLink = (event) => {
438
- const tileId = event.props?.tileId;
439
- const itemId = event.props?.itemId;
440
- if (tileId !== this.instanceId) return;
441
- if (!itemId) return;
442
- this._expandedIds = /* @__PURE__ */ new Set([itemId]);
443
- this._highlightId = itemId;
444
- if (this._highlightTimer !== null) clearTimeout(this._highlightTimer);
445
- this._highlightTimer = setTimeout(() => {
446
- this._highlightId = null;
447
- this._highlightTimer = null;
448
- }, 1500);
449
- requestAnimationFrame(() => {
450
- const el = document.querySelector(`[data-faq-item-id="${itemId}"]`);
451
- if (el) el.scrollIntoView({ behavior: "smooth", block: "center" });
452
- });
453
- };
454
- if (this.runtime.events.getRecent) {
455
- const recent = this.runtime.events.getRecent({ names: ["notification.deep_link"] }, 5);
456
- const pending = recent.filter((e) => e.props?.tileId === this.instanceId && e.props?.itemId).pop();
457
- if (pending && Date.now() - pending.ts < 1e4) {
458
- handleDeepLink(pending);
459
- }
460
- }
461
- this._unsubDeepLink = this.runtime.events.subscribe(
462
- { names: ["notification.deep_link"] },
463
- handleDeepLink
464
- );
465
- }
466
- }
467
- _unsubscribeAll() {
468
- this._unsubContext?.();
469
- this._unsubAccumulator?.();
470
- this._unsubSessionMetrics?.();
471
- this._unsubCta?.();
472
- this._unsubDeepLink?.();
473
- this._unsubContext = null;
474
- this._unsubAccumulator = null;
475
- this._unsubSessionMetrics = null;
476
- this._unsubCta = null;
477
- this._unsubDeepLink = null;
478
- }
479
- // -----------------------------------------------------------------------
480
- // Handlers
481
- // -----------------------------------------------------------------------
482
- _handleToggle(id) {
483
- const prev = this._expandedIds;
484
- let next;
485
- if (this.faqConfig.expandBehavior === "single") {
486
- next = prev.has(id) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([id]);
487
- } else {
488
- next = new Set(prev);
489
- if (prev.has(id)) {
490
- next.delete(id);
491
- } else {
492
- next.add(id);
493
- }
494
- }
495
- const willBeExpanded = !prev.has(id);
496
- this._expandedIds = next;
497
- this.runtime?.events.publish("faq:toggled", {
498
- instanceId: this.instanceId,
499
- questionId: id,
500
- expanded: willBeExpanded,
501
- timestamp: Date.now()
502
- });
503
- }
504
- _handleFeedback(itemId, question, value) {
505
- const next = new Map(this._feedbackState);
506
- next.set(itemId, value);
507
- this._feedbackState = next;
508
- this.runtime?.events.publish("faq:feedback", { itemId, question, value });
509
- }
510
- // -----------------------------------------------------------------------
511
- // Computed helpers
512
- // -----------------------------------------------------------------------
513
- _visibleQuestions() {
514
- return (this.faqConfig.actions ?? []).filter((q) => {
515
- if (!q.triggerWhen) return true;
516
- if (!this.runtime) return true;
517
- const result = this.runtime.evaluateSync(q.triggerWhen);
518
- return result.value;
519
- });
520
- }
521
- _orderedQuestions(visible) {
522
- if (this.faqConfig.ordering === "priority") {
523
- return [...visible].sort((a, b) => (b.config.priority ?? 0) - (a.config.priority ?? 0));
524
- }
525
- return visible;
526
- }
527
- _filteredQuestions(ordered) {
528
- const q = this._searchQuery.trim().toLowerCase();
529
- if (!this.faqConfig.searchable || !q) return ordered;
530
- return ordered.filter(
531
- (item) => item.config.question.toLowerCase().includes(q) || getAnswerText(item.config.answer).toLowerCase().includes(q) || item.config.category?.toLowerCase().includes(q)
532
- );
533
- }
534
- _categoryGroups(filtered) {
535
- const groups = /* @__PURE__ */ new Map();
536
- for (const item of filtered) {
537
- const cat = item.config.category;
538
- if (!groups.has(cat)) groups.set(cat, []);
539
- groups.get(cat).push(item);
540
- }
541
- return groups;
542
- }
543
- // -----------------------------------------------------------------------
544
- // Render helpers
545
- // -----------------------------------------------------------------------
546
- _renderAnswer(answer) {
547
- const html_str = renderAnswerHtml(answer);
548
- return html`<div style="margin:0" data-faq-markdown="">${unsafeHTML(html_str)}</div>`;
549
- }
550
- _renderFeedback(item, feedbackConfig, feedbackValue, theme) {
551
- const colors = themeStyles[theme];
552
- const feedbackStyle = { ...baseStyles.feedback, ...colors.feedbackPrompt };
553
- return html`
554
- <div style=${styleMap(sm(feedbackStyle))}>
555
- <span>${getFeedbackPrompt(feedbackConfig)}</span>
556
- <button
557
- type="button"
558
- style=${styleMap(
559
- sm({
560
- ...baseStyles.feedbackButton,
561
- ...feedbackValue === "up" ? baseStyles.feedbackButtonSelected : {}
562
- })
563
- )}
564
- aria-label="Thumbs up"
565
- @click=${() => this._handleFeedback(item.config.id, item.config.question, "up")}
566
- >\uD83D\uDC4D</button>
567
- <button
568
- type="button"
569
- style=${styleMap(
570
- sm({
571
- ...baseStyles.feedbackButton,
572
- ...feedbackValue === "down" ? baseStyles.feedbackButtonSelected : {}
573
- })
574
- )}
575
- aria-label="Thumbs down"
576
- @click=${() => this._handleFeedback(item.config.id, item.config.question, "down")}
577
- >\uD83D\uDC4E</button>
578
- </div>
579
- `;
580
- }
581
- _renderItem(item, isLast, theme, feedbackConfig) {
582
- const colors = themeStyles[theme];
583
- const isExpanded = this._expandedIds.has(item.config.id);
584
- const isHighlighted = this._highlightId === item.config.id;
585
- const isHovered = this._hoveredId === item.config.id;
586
- const itemStyle = {
587
- ...baseStyles.item,
588
- ...colors.item,
589
- ...isExpanded ? colors.itemExpanded : {},
590
- ...isHighlighted ? {
591
- boxShadow: `0 0 0 2px ${purple[4]}, 0 0 12px rgba(106, 89, 206, 0.4)`,
592
- transition: "box-shadow 0.3s ease"
593
- } : {},
594
- ...!isLast ? { borderBottom: "var(--sc-content-item-divider, none)" } : {}
595
- };
596
- const questionStyle = {
597
- ...baseStyles.question,
598
- ...colors.question,
599
- ...isHovered ? colors.questionHover : {}
600
- };
601
- const chevronStyle = {
602
- ...baseStyles.chevron,
603
- transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)"
604
- };
605
- const answerStyle = {
606
- ...baseStyles.answer,
607
- ...colors.answer,
608
- maxHeight: isExpanded ? "500px" : "0",
609
- paddingBottom: isExpanded ? "16px" : "0"
610
- };
611
- return html`
612
- <div
613
- style=${styleMap(sm(itemStyle))}
614
- data-faq-item-id=${item.config.id}
615
- >
616
- <button
617
- type="button"
618
- style=${styleMap(sm(questionStyle))}
619
- aria-expanded=${isExpanded}
620
- @click=${() => this._handleToggle(item.config.id)}
621
- @mouseenter=${() => {
622
- this._hoveredId = item.config.id;
623
- }}
624
- @mouseleave=${() => {
625
- this._hoveredId = null;
626
- }}
627
- >
628
- <span>${item.config.question}</span>
629
- <span style=${styleMap(sm(chevronStyle))}>\u203A</span>
630
- </button>
631
-
632
- <div
633
- style=${styleMap(sm(answerStyle))}
634
- aria-hidden=${!isExpanded}
635
- >
636
- ${this._renderAnswer(item.config.answer)}
637
- ${isExpanded && feedbackConfig ? this._renderFeedback(
638
- item,
639
- feedbackConfig,
640
- this._feedbackState.get(item.config.id),
641
- theme
642
- ) : nothing}
643
- </div>
644
- </div>
645
- `;
646
- }
647
- _renderItems(items, theme, feedbackConfig) {
648
- return items.map(
649
- (item, index) => this._renderItem(item, index === items.length - 1, theme, feedbackConfig)
650
- );
651
- }
652
- // -----------------------------------------------------------------------
653
- // Render
654
- // -----------------------------------------------------------------------
655
- render() {
656
- const theme = resolveTheme(this.faqConfig.theme);
657
- const colors = themeStyles[theme];
658
- const feedbackConfig = resolveFeedbackConfig(this.faqConfig.feedback);
659
- const visible = this._visibleQuestions();
660
- const ordered = this._orderedQuestions(visible);
661
- const filtered = this._filteredQuestions(ordered);
662
- const hasCategories = filtered.some((q) => q.config.category);
663
- const groups = hasCategories ? this._categoryGroups(filtered) : null;
664
- const containerStyle = {
665
- ...baseStyles.container,
666
- ...colors.container
667
- };
668
- const emptyStateStyle = {
669
- ...baseStyles.emptyState,
670
- ...colors.emptyState
671
- };
672
- const categoryHeaderStyle = {
673
- ...baseStyles.categoryHeader,
674
- ...colors.categoryHeader
675
- };
676
- const searchInputStyle = {
677
- ...baseStyles.searchInput,
678
- ...colors.searchInput
679
- };
680
- if (visible.length === 0) {
681
- return html`
682
- <div
683
- style=${styleMap(sm(containerStyle))}
684
- data-adaptive-id=${this.instanceId}
685
- data-adaptive-type="adaptive-faq"
686
- >
687
- <div style=${styleMap(sm(emptyStateStyle))}>
688
- You're all set for now! We'll surface answers here when they're relevant to what
689
- you're doing.
690
- </div>
691
- </div>
692
- `;
693
- }
694
- return html`
695
- <div
696
- style=${styleMap(sm(containerStyle))}
697
- data-adaptive-id=${this.instanceId}
698
- data-adaptive-type="adaptive-faq"
699
- >
700
- ${this.faqConfig.searchable ? html`
701
- <div style=${styleMap(sm(baseStyles.searchWrapper))}>
702
- <style>
703
- [data-adaptive-id="${this.instanceId}"] input::placeholder {
704
- color: var(--sc-content-search-color, inherit);
705
- opacity: 0.7;
706
- }
707
- </style>
708
- <input
709
- type="text"
710
- placeholder="Search questions..."
711
- .value=${this._searchQuery}
712
- style=${styleMap(sm(searchInputStyle))}
713
- @input=${(e) => {
714
- this._searchQuery = e.target.value;
715
- }}
716
- />
717
- </div>
718
- ` : nothing}
719
-
720
- <div style=${styleMap(sm(baseStyles.accordion))}>
721
- ${groups ? Array.from(groups.entries()).map(
722
- ([category, items]) => html`
723
- ${category ? html`
724
- <div
725
- style=${styleMap(sm(categoryHeaderStyle))}
726
- data-category-header=${category}
727
- >
728
- ${category}
729
- </div>
730
- ` : nothing}
731
- ${this._renderItems(items, theme, feedbackConfig)}
732
- `
733
- ) : this._renderItems(filtered, theme, feedbackConfig)}
734
- </div>
735
-
736
- ${this.faqConfig.searchable && filtered.length === 0 && this._searchQuery ? html`
737
- <div
738
- style=${styleMap(sm({ ...baseStyles.noResults, ...colors.emptyState }))}
739
- >
740
- No questions found matching &quot;${this._searchQuery}&quot;
741
- </div>
742
- ` : nothing}
743
- </div>
744
- `;
745
- }
746
- };
747
- // -----------------------------------------------------------------------
748
- // Reactive properties (no decorators — tsconfig forbids experimentalDecorators)
749
- // -----------------------------------------------------------------------
750
- FAQAccordionElement.properties = {
751
- // Public API — set from the outside
752
- faqConfig: { attribute: false },
753
- runtime: { attribute: false },
754
- instanceId: { type: String },
755
- // Internal reactive state (prefixed with _ to signal "private")
756
- _expandedIds: { state: true },
757
- _highlightId: { state: true },
758
- _searchQuery: { state: true },
759
- _feedbackState: { state: true },
760
- _hoveredId: { state: true }
761
- };
762
- if (!customElements.get("syntro-faq-accordion")) {
763
- customElements.define("syntro-faq-accordion", FAQAccordionElement);
764
- }
765
-
766
- // src/runtime.ts
767
- var DEFAULT_FAQ_CONFIG = {
768
- expandBehavior: "single",
769
- searchable: false,
770
- theme: "auto",
771
- actions: []
772
- };
773
- var FAQWidgetLitMountable = {
774
- mount(container, config) {
775
- const incoming = config ?? null;
776
- const stripped = stripMountPlumbing(incoming);
777
- const runtime2 = incoming?.runtime;
778
- const instanceId = incoming?.instanceId ?? "faq-widget";
779
- const faqConfig = incoming ? stripped : { ...DEFAULT_FAQ_CONFIG };
780
- const el = document.createElement("syntro-faq-accordion");
781
- Object.assign(el, {
782
- faqConfig,
783
- runtime: runtime2 ?? null,
784
- instanceId
785
- });
786
- container.appendChild(el);
787
- return () => el.remove();
788
- }
789
- };
790
- var runtime = {
791
- id: "adaptive-faq",
792
- version: "2.0.0",
793
- name: "FAQ Accordion",
794
- description: "Collapsible Q&A accordion with actions, rich content, feedback, and personalization",
795
- /**
796
- * Action executors for programmatic FAQ interaction.
797
- */
798
- executors: executorDefinitions,
799
- /**
800
- * Widget definitions for the runtime's WidgetRegistry.
801
- */
802
- widgets: [
803
- {
804
- id: "adaptive-faq:accordion",
805
- component: FAQWidgetLitMountable,
806
- metadata: {
807
- name: "FAQ Accordion",
808
- description: "Collapsible Q&A accordion with search, categories, and feedback",
809
- icon: "\u2753",
810
- subtitle: "Curated just for you."
811
- }
812
- }
813
- ],
814
- /**
815
- * Extract notify watcher entries from tile config props.
816
- * The runtime evaluates these continuously (even with drawer closed)
817
- * and publishes faq:question_revealed when triggerWhen transitions false → true.
818
- */
819
- notifyWatchers(props) {
820
- const actions = props.actions ?? [];
821
- return actions.filter((a) => a.notify && a.triggerWhen).map((a) => ({
822
- id: `faq:${a.config.id}`,
823
- strategy: a.triggerWhen,
824
- eventName: "faq:question_revealed",
825
- eventProps: {
826
- questionId: a.config.id,
827
- question: a.config.question,
828
- title: a.notify.title,
829
- body: a.notify.body,
830
- icon: a.notify.icon
831
- }
832
- }));
833
- }
834
- };
835
- var runtime_default = runtime;
2
+ FAQWidgetLitMountable,
3
+ runtime,
4
+ runtime_default
5
+ } from "./chunk-AJELMQH5.js";
6
+ import "./chunk-YNDAVOD7.js";
7
+ import "./chunk-KRKRB4OL.js";
8
+ import "./chunk-7DTOSQNC.js";
836
9
  export {
837
10
  FAQWidgetLitMountable,
838
11
  runtime_default as default,