@syntrologie/adapt-faq 2.16.0 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-5WRI5ZAA.js +31 -0
- package/dist/chunk-5WRI5ZAA.js.map +7 -0
- package/dist/chunk-S6WIENQP.js +578 -0
- package/dist/chunk-S6WIENQP.js.map +7 -0
- package/dist/editor.d.ts +35 -33
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +4821 -308
- package/dist/editor.js.map +7 -0
- package/dist/runtime.d.ts +3 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +848 -91
- package/dist/runtime.js.map +7 -0
- package/dist/schema.d.ts +609 -77
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +444 -206
- package/dist/schema.js.map +7 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -20
- package/dist/FAQWidget.d.ts +0 -33
- package/dist/FAQWidget.d.ts.map +0 -1
- package/dist/FAQWidget.js +0 -375
- package/dist/FAQWidgetLit.js +0 -534
- package/dist/cdn.d.ts +0 -70
- package/dist/cdn.d.ts.map +0 -1
- package/dist/cdn.js +0 -46
- package/dist/editor-lit.d.ts +0 -37
- package/dist/editor-lit.d.ts.map +0 -1
- package/dist/editor-lit.js +0 -195
- package/dist/executors.js +0 -150
- package/dist/faq-styles.js +0 -204
- package/dist/faq-types.js +0 -7
- package/dist/runtime-lit.d.ts +0 -85
- package/dist/runtime-lit.d.ts.map +0 -1
- package/dist/runtime-lit.js +0 -94
- package/dist/state.js +0 -132
- package/dist/summarize.js +0 -62
- package/dist/types.js +0 -17
- package/node_modules/@syntrologie/sdk-contracts/dist/index.d.ts +0 -129
- package/node_modules/@syntrologie/sdk-contracts/dist/index.js +0 -17
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.d.ts +0 -2296
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.js +0 -361
- package/node_modules/@syntrologie/sdk-contracts/package.json +0 -33
package/dist/FAQWidgetLit.js
DELETED
|
@@ -1,534 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Adaptive FAQ - FAQWidgetLit
|
|
3
|
-
*
|
|
4
|
-
* Lit web component equivalent of FAQWidget.tsx.
|
|
5
|
-
* Renders a collapsible Q&A accordion with search, category grouping,
|
|
6
|
-
* feedback, and markdown rendering — all as a custom element with no
|
|
7
|
-
* Shadow DOM (light DOM via createRenderRoot).
|
|
8
|
-
*
|
|
9
|
-
* Tag name: <syntro-faq-accordion>
|
|
10
|
-
*
|
|
11
|
-
* Decorator-free: uses `static override properties` (tsconfig has no
|
|
12
|
-
* experimentalDecorators).
|
|
13
|
-
*/
|
|
14
|
-
import { purple } from '@syntro/design-system/tokens';
|
|
15
|
-
import { html, LitElement, nothing } from 'lit';
|
|
16
|
-
import { styleMap } from 'lit/directives/style-map.js';
|
|
17
|
-
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
18
|
-
import { Marked } from 'marked';
|
|
19
|
-
import { baseStyles, themeStyles } from './faq-styles';
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// Utility — styleMap accepts Record<string, string | number | undefined>
|
|
22
|
-
// but its TypeScript signature is overly narrow in some Lit versions.
|
|
23
|
-
// Cast through unknown to avoid false positives when style values include
|
|
24
|
-
// numeric CSS properties (fontWeight, lineHeight, etc.).
|
|
25
|
-
// ============================================================================
|
|
26
|
-
function sm(styles) {
|
|
27
|
-
return styles;
|
|
28
|
-
}
|
|
29
|
-
const marked = new Marked({ async: false, gfm: true, breaks: true });
|
|
30
|
-
// ============================================================================
|
|
31
|
-
// Helpers (mirrored from FAQWidget.tsx)
|
|
32
|
-
// ============================================================================
|
|
33
|
-
function getAnswerText(answer) {
|
|
34
|
-
if (typeof answer === 'string')
|
|
35
|
-
return answer;
|
|
36
|
-
if (answer.type === 'rich')
|
|
37
|
-
return answer.html;
|
|
38
|
-
return answer.content;
|
|
39
|
-
}
|
|
40
|
-
function renderAnswerHtml(answer) {
|
|
41
|
-
if (typeof answer === 'string') {
|
|
42
|
-
return marked.parse(answer);
|
|
43
|
-
}
|
|
44
|
-
if (answer.type === 'rich') {
|
|
45
|
-
return answer.html;
|
|
46
|
-
}
|
|
47
|
-
return marked.parse(answer.content);
|
|
48
|
-
}
|
|
49
|
-
function resolveFeedbackConfig(feedback) {
|
|
50
|
-
if (!feedback)
|
|
51
|
-
return null;
|
|
52
|
-
if (feedback === true)
|
|
53
|
-
return { style: 'thumbs' };
|
|
54
|
-
return feedback;
|
|
55
|
-
}
|
|
56
|
-
function getFeedbackPrompt(feedbackConfig) {
|
|
57
|
-
return feedbackConfig.prompt ?? 'Was this helpful?';
|
|
58
|
-
}
|
|
59
|
-
function resolveTheme(theme) {
|
|
60
|
-
if (theme && theme !== 'auto')
|
|
61
|
-
return theme;
|
|
62
|
-
if (typeof window !== 'undefined') {
|
|
63
|
-
return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
64
|
-
}
|
|
65
|
-
return 'light';
|
|
66
|
-
}
|
|
67
|
-
// ============================================================================
|
|
68
|
-
// FAQAccordionElement — Lit element
|
|
69
|
-
// ============================================================================
|
|
70
|
-
/**
|
|
71
|
-
* <syntro-faq-accordion> — light-DOM Lit web component.
|
|
72
|
-
*
|
|
73
|
-
* Set properties imperatively (no attribute serialisation for objects):
|
|
74
|
-
* el.faqConfig = { expandBehavior: 'single', ... };
|
|
75
|
-
* el.runtime = runtimeInstance;
|
|
76
|
-
* el.instanceId = 'my-faq';
|
|
77
|
-
*/
|
|
78
|
-
export class FAQAccordionElement extends LitElement {
|
|
79
|
-
constructor() {
|
|
80
|
-
// -----------------------------------------------------------------------
|
|
81
|
-
// Reactive properties (no decorators — tsconfig forbids experimentalDecorators)
|
|
82
|
-
// -----------------------------------------------------------------------
|
|
83
|
-
super(...arguments);
|
|
84
|
-
// -----------------------------------------------------------------------
|
|
85
|
-
// Property declarations
|
|
86
|
-
// -----------------------------------------------------------------------
|
|
87
|
-
this.faqConfig = {
|
|
88
|
-
expandBehavior: 'single',
|
|
89
|
-
searchable: false,
|
|
90
|
-
theme: 'auto',
|
|
91
|
-
actions: [],
|
|
92
|
-
};
|
|
93
|
-
this.runtime = null;
|
|
94
|
-
this.instanceId = 'faq-widget';
|
|
95
|
-
// Internal state
|
|
96
|
-
this._expandedIds = new Set();
|
|
97
|
-
this._highlightId = null;
|
|
98
|
-
this._searchQuery = '';
|
|
99
|
-
this._feedbackState = new Map();
|
|
100
|
-
this._hoveredId = null;
|
|
101
|
-
// Subscription cleanup handles
|
|
102
|
-
this._unsubContext = null;
|
|
103
|
-
this._unsubAccumulator = null;
|
|
104
|
-
this._unsubCta = null;
|
|
105
|
-
this._unsubDeepLink = null;
|
|
106
|
-
this._unsubSessionMetrics = null;
|
|
107
|
-
this._highlightTimer = null;
|
|
108
|
-
}
|
|
109
|
-
// -----------------------------------------------------------------------
|
|
110
|
-
// Light DOM — no Shadow DOM so CSS variables from the host page apply
|
|
111
|
-
// -----------------------------------------------------------------------
|
|
112
|
-
createRenderRoot() {
|
|
113
|
-
return this;
|
|
114
|
-
}
|
|
115
|
-
// -----------------------------------------------------------------------
|
|
116
|
-
// Lifecycle
|
|
117
|
-
// -----------------------------------------------------------------------
|
|
118
|
-
connectedCallback() {
|
|
119
|
-
super.connectedCallback();
|
|
120
|
-
this._subscribeAll();
|
|
121
|
-
}
|
|
122
|
-
disconnectedCallback() {
|
|
123
|
-
super.disconnectedCallback();
|
|
124
|
-
this._unsubscribeAll();
|
|
125
|
-
if (this._highlightTimer !== null) {
|
|
126
|
-
clearTimeout(this._highlightTimer);
|
|
127
|
-
this._highlightTimer = null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
// Re-subscribe when runtime changes (property may be set after connectedCallback)
|
|
131
|
-
updated(changedProps) {
|
|
132
|
-
if (changedProps.has('runtime')) {
|
|
133
|
-
this._unsubscribeAll();
|
|
134
|
-
this._subscribeAll();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// -----------------------------------------------------------------------
|
|
138
|
-
// Subscription management
|
|
139
|
-
// -----------------------------------------------------------------------
|
|
140
|
-
_subscribeAll() {
|
|
141
|
-
if (!this.runtime)
|
|
142
|
-
return;
|
|
143
|
-
// Context changes → force re-render
|
|
144
|
-
this._unsubContext = this.runtime.context.subscribe(() => {
|
|
145
|
-
this.requestUpdate();
|
|
146
|
-
});
|
|
147
|
-
// Accumulator changes → force re-render (for event_count triggerWhen)
|
|
148
|
-
if (this.runtime.accumulator?.subscribe) {
|
|
149
|
-
this._unsubAccumulator = this.runtime.accumulator.subscribe(() => {
|
|
150
|
-
this.requestUpdate();
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
// Session metric changes → force re-render (for session_metric triggerWhen)
|
|
154
|
-
if (this.runtime.sessionMetrics?.subscribe) {
|
|
155
|
-
this._unsubSessionMetrics = this.runtime.sessionMetrics.subscribe(() => {
|
|
156
|
-
this.requestUpdate();
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
// faq:open:* events from overlay CTA clicks
|
|
160
|
-
if (this.runtime.events.subscribe) {
|
|
161
|
-
// Check EventBus history for pending faq:open events
|
|
162
|
-
if (this.runtime.events.getRecent) {
|
|
163
|
-
const recentEvents = this.runtime.events.getRecent({ patterns: ['^action\\.tooltip_cta_clicked$', '^action\\.modal_cta_clicked$'] }, 10);
|
|
164
|
-
const pendingEvent = recentEvents
|
|
165
|
-
.filter((e) => {
|
|
166
|
-
const actionId = e.props?.actionId;
|
|
167
|
-
return typeof actionId === 'string' && actionId.startsWith('faq:open:');
|
|
168
|
-
})
|
|
169
|
-
.pop();
|
|
170
|
-
if (pendingEvent && Date.now() - pendingEvent.ts < 10000) {
|
|
171
|
-
const questionId = pendingEvent.props.actionId.replace('faq:open:', '');
|
|
172
|
-
this._expandedIds = new Set([questionId]);
|
|
173
|
-
requestAnimationFrame(() => {
|
|
174
|
-
const el = document.querySelector(`[data-faq-item-id="${questionId}"]`);
|
|
175
|
-
if (el)
|
|
176
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
this._unsubCta = this.runtime.events.subscribe({ patterns: ['^action\\.tooltip_cta_clicked$', '^action\\.modal_cta_clicked$'] }, (event) => {
|
|
181
|
-
const actionId = event.props?.actionId;
|
|
182
|
-
if (typeof actionId !== 'string' || !actionId.startsWith('faq:open:'))
|
|
183
|
-
return;
|
|
184
|
-
const questionId = actionId.replace('faq:open:', '');
|
|
185
|
-
this._expandedIds = new Set([questionId]);
|
|
186
|
-
requestAnimationFrame(() => {
|
|
187
|
-
const el = document.querySelector(`[data-faq-item-id="${questionId}"]`);
|
|
188
|
-
if (el)
|
|
189
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
190
|
-
});
|
|
191
|
-
this.runtime?.events.publish('canvas.requestOpen');
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
// notification.deep_link events
|
|
195
|
-
if (this.runtime.events.subscribe) {
|
|
196
|
-
const handleDeepLink = (event) => {
|
|
197
|
-
const tileId = event.props?.tileId;
|
|
198
|
-
const itemId = event.props?.itemId;
|
|
199
|
-
if (tileId !== this.instanceId)
|
|
200
|
-
return;
|
|
201
|
-
if (!itemId)
|
|
202
|
-
return;
|
|
203
|
-
this._expandedIds = new Set([itemId]);
|
|
204
|
-
this._highlightId = itemId;
|
|
205
|
-
if (this._highlightTimer !== null)
|
|
206
|
-
clearTimeout(this._highlightTimer);
|
|
207
|
-
this._highlightTimer = setTimeout(() => {
|
|
208
|
-
this._highlightId = null;
|
|
209
|
-
this._highlightTimer = null;
|
|
210
|
-
}, 1500);
|
|
211
|
-
requestAnimationFrame(() => {
|
|
212
|
-
const el = document.querySelector(`[data-faq-item-id="${itemId}"]`);
|
|
213
|
-
if (el)
|
|
214
|
-
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
215
|
-
});
|
|
216
|
-
};
|
|
217
|
-
// Check recent events (may have fired before widget mounted)
|
|
218
|
-
if (this.runtime.events.getRecent) {
|
|
219
|
-
const recent = this.runtime.events.getRecent({ names: ['notification.deep_link'] }, 5);
|
|
220
|
-
const pending = recent
|
|
221
|
-
.filter((e) => e.props?.tileId === this.instanceId && e.props?.itemId)
|
|
222
|
-
.pop();
|
|
223
|
-
if (pending && Date.now() - pending.ts < 10000) {
|
|
224
|
-
handleDeepLink(pending);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
this._unsubDeepLink = this.runtime.events.subscribe({ names: ['notification.deep_link'] }, handleDeepLink);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
_unsubscribeAll() {
|
|
231
|
-
this._unsubContext?.();
|
|
232
|
-
this._unsubAccumulator?.();
|
|
233
|
-
this._unsubSessionMetrics?.();
|
|
234
|
-
this._unsubCta?.();
|
|
235
|
-
this._unsubDeepLink?.();
|
|
236
|
-
this._unsubContext = null;
|
|
237
|
-
this._unsubAccumulator = null;
|
|
238
|
-
this._unsubSessionMetrics = null;
|
|
239
|
-
this._unsubCta = null;
|
|
240
|
-
this._unsubDeepLink = null;
|
|
241
|
-
}
|
|
242
|
-
// -----------------------------------------------------------------------
|
|
243
|
-
// Handlers
|
|
244
|
-
// -----------------------------------------------------------------------
|
|
245
|
-
_handleToggle(id) {
|
|
246
|
-
const prev = this._expandedIds;
|
|
247
|
-
let next;
|
|
248
|
-
if (this.faqConfig.expandBehavior === 'single') {
|
|
249
|
-
next = prev.has(id) ? new Set() : new Set([id]);
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
next = new Set(prev);
|
|
253
|
-
if (prev.has(id)) {
|
|
254
|
-
next.delete(id);
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
next.add(id);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
const willBeExpanded = !prev.has(id);
|
|
261
|
-
this._expandedIds = next;
|
|
262
|
-
this.runtime?.events.publish('faq:toggled', {
|
|
263
|
-
instanceId: this.instanceId,
|
|
264
|
-
questionId: id,
|
|
265
|
-
expanded: willBeExpanded,
|
|
266
|
-
timestamp: Date.now(),
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
_handleFeedback(itemId, question, value) {
|
|
270
|
-
const next = new Map(this._feedbackState);
|
|
271
|
-
next.set(itemId, value);
|
|
272
|
-
this._feedbackState = next;
|
|
273
|
-
this.runtime?.events.publish('faq:feedback', { itemId, question, value });
|
|
274
|
-
}
|
|
275
|
-
// -----------------------------------------------------------------------
|
|
276
|
-
// Computed helpers
|
|
277
|
-
// -----------------------------------------------------------------------
|
|
278
|
-
_visibleQuestions() {
|
|
279
|
-
return (this.faqConfig.actions ?? []).filter((q) => {
|
|
280
|
-
if (!q.triggerWhen)
|
|
281
|
-
return true;
|
|
282
|
-
if (!this.runtime)
|
|
283
|
-
return true;
|
|
284
|
-
const result = this.runtime.evaluateSync(q.triggerWhen);
|
|
285
|
-
return result.value;
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
_orderedQuestions(visible) {
|
|
289
|
-
if (this.faqConfig.ordering === 'priority') {
|
|
290
|
-
return [...visible].sort((a, b) => (b.config.priority ?? 0) - (a.config.priority ?? 0));
|
|
291
|
-
}
|
|
292
|
-
return visible;
|
|
293
|
-
}
|
|
294
|
-
_filteredQuestions(ordered) {
|
|
295
|
-
const q = this._searchQuery.trim().toLowerCase();
|
|
296
|
-
if (!this.faqConfig.searchable || !q)
|
|
297
|
-
return ordered;
|
|
298
|
-
return ordered.filter((item) => item.config.question.toLowerCase().includes(q) ||
|
|
299
|
-
getAnswerText(item.config.answer).toLowerCase().includes(q) ||
|
|
300
|
-
item.config.category?.toLowerCase().includes(q));
|
|
301
|
-
}
|
|
302
|
-
_categoryGroups(filtered) {
|
|
303
|
-
const groups = new Map();
|
|
304
|
-
for (const item of filtered) {
|
|
305
|
-
const cat = item.config.category;
|
|
306
|
-
if (!groups.has(cat))
|
|
307
|
-
groups.set(cat, []);
|
|
308
|
-
groups.get(cat).push(item);
|
|
309
|
-
}
|
|
310
|
-
return groups;
|
|
311
|
-
}
|
|
312
|
-
// -----------------------------------------------------------------------
|
|
313
|
-
// Render helpers
|
|
314
|
-
// -----------------------------------------------------------------------
|
|
315
|
-
_renderAnswer(answer) {
|
|
316
|
-
const html_str = renderAnswerHtml(answer);
|
|
317
|
-
return html `<div style="margin:0" data-faq-markdown="">${unsafeHTML(html_str)}</div>`;
|
|
318
|
-
}
|
|
319
|
-
_renderFeedback(item, feedbackConfig, feedbackValue, theme) {
|
|
320
|
-
const colors = themeStyles[theme];
|
|
321
|
-
const feedbackStyle = { ...baseStyles.feedback, ...colors.feedbackPrompt };
|
|
322
|
-
return html `
|
|
323
|
-
<div style=${styleMap(sm(feedbackStyle))}>
|
|
324
|
-
<span>${getFeedbackPrompt(feedbackConfig)}</span>
|
|
325
|
-
<button
|
|
326
|
-
type="button"
|
|
327
|
-
style=${styleMap(sm({
|
|
328
|
-
...baseStyles.feedbackButton,
|
|
329
|
-
...(feedbackValue === 'up' ? baseStyles.feedbackButtonSelected : {}),
|
|
330
|
-
}))}
|
|
331
|
-
aria-label="Thumbs up"
|
|
332
|
-
@click=${() => this._handleFeedback(item.config.id, item.config.question, 'up')}
|
|
333
|
-
>\uD83D\uDC4D</button>
|
|
334
|
-
<button
|
|
335
|
-
type="button"
|
|
336
|
-
style=${styleMap(sm({
|
|
337
|
-
...baseStyles.feedbackButton,
|
|
338
|
-
...(feedbackValue === 'down' ? baseStyles.feedbackButtonSelected : {}),
|
|
339
|
-
}))}
|
|
340
|
-
aria-label="Thumbs down"
|
|
341
|
-
@click=${() => this._handleFeedback(item.config.id, item.config.question, 'down')}
|
|
342
|
-
>\uD83D\uDC4E</button>
|
|
343
|
-
</div>
|
|
344
|
-
`;
|
|
345
|
-
}
|
|
346
|
-
_renderItem(item, isLast, theme, feedbackConfig) {
|
|
347
|
-
const colors = themeStyles[theme];
|
|
348
|
-
const isExpanded = this._expandedIds.has(item.config.id);
|
|
349
|
-
const isHighlighted = this._highlightId === item.config.id;
|
|
350
|
-
const isHovered = this._hoveredId === item.config.id;
|
|
351
|
-
const itemStyle = {
|
|
352
|
-
...baseStyles.item,
|
|
353
|
-
...colors.item,
|
|
354
|
-
...(isExpanded ? colors.itemExpanded : {}),
|
|
355
|
-
...(isHighlighted
|
|
356
|
-
? {
|
|
357
|
-
boxShadow: `0 0 0 2px ${purple[4]}, 0 0 12px rgba(106, 89, 206, 0.4)`,
|
|
358
|
-
transition: 'box-shadow 0.3s ease',
|
|
359
|
-
}
|
|
360
|
-
: {}),
|
|
361
|
-
...(!isLast ? { borderBottom: 'var(--sc-content-item-divider, none)' } : {}),
|
|
362
|
-
};
|
|
363
|
-
const questionStyle = {
|
|
364
|
-
...baseStyles.question,
|
|
365
|
-
...colors.question,
|
|
366
|
-
...(isHovered ? colors.questionHover : {}),
|
|
367
|
-
};
|
|
368
|
-
const chevronStyle = {
|
|
369
|
-
...baseStyles.chevron,
|
|
370
|
-
transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
|
|
371
|
-
};
|
|
372
|
-
const answerStyle = {
|
|
373
|
-
...baseStyles.answer,
|
|
374
|
-
...colors.answer,
|
|
375
|
-
maxHeight: isExpanded ? '500px' : '0',
|
|
376
|
-
paddingBottom: isExpanded ? '16px' : '0',
|
|
377
|
-
};
|
|
378
|
-
return html `
|
|
379
|
-
<div
|
|
380
|
-
style=${styleMap(sm(itemStyle))}
|
|
381
|
-
data-faq-item-id=${item.config.id}
|
|
382
|
-
>
|
|
383
|
-
<button
|
|
384
|
-
type="button"
|
|
385
|
-
style=${styleMap(sm(questionStyle))}
|
|
386
|
-
aria-expanded=${isExpanded}
|
|
387
|
-
@click=${() => this._handleToggle(item.config.id)}
|
|
388
|
-
@mouseenter=${() => {
|
|
389
|
-
this._hoveredId = item.config.id;
|
|
390
|
-
}}
|
|
391
|
-
@mouseleave=${() => {
|
|
392
|
-
this._hoveredId = null;
|
|
393
|
-
}}
|
|
394
|
-
>
|
|
395
|
-
<span>${item.config.question}</span>
|
|
396
|
-
<span style=${styleMap(sm(chevronStyle))}>\u203A</span>
|
|
397
|
-
</button>
|
|
398
|
-
|
|
399
|
-
<div
|
|
400
|
-
style=${styleMap(sm(answerStyle))}
|
|
401
|
-
aria-hidden=${!isExpanded}
|
|
402
|
-
>
|
|
403
|
-
${this._renderAnswer(item.config.answer)}
|
|
404
|
-
${isExpanded && feedbackConfig
|
|
405
|
-
? this._renderFeedback(item, feedbackConfig, this._feedbackState.get(item.config.id), theme)
|
|
406
|
-
: nothing}
|
|
407
|
-
</div>
|
|
408
|
-
</div>
|
|
409
|
-
`;
|
|
410
|
-
}
|
|
411
|
-
_renderItems(items, theme, feedbackConfig) {
|
|
412
|
-
return items.map((item, index) => this._renderItem(item, index === items.length - 1, theme, feedbackConfig));
|
|
413
|
-
}
|
|
414
|
-
// -----------------------------------------------------------------------
|
|
415
|
-
// Render
|
|
416
|
-
// -----------------------------------------------------------------------
|
|
417
|
-
render() {
|
|
418
|
-
const theme = resolveTheme(this.faqConfig.theme);
|
|
419
|
-
const colors = themeStyles[theme];
|
|
420
|
-
const feedbackConfig = resolveFeedbackConfig(this.faqConfig.feedback);
|
|
421
|
-
const visible = this._visibleQuestions();
|
|
422
|
-
const ordered = this._orderedQuestions(visible);
|
|
423
|
-
const filtered = this._filteredQuestions(ordered);
|
|
424
|
-
const hasCategories = filtered.some((q) => q.config.category);
|
|
425
|
-
const groups = hasCategories ? this._categoryGroups(filtered) : null;
|
|
426
|
-
const containerStyle = {
|
|
427
|
-
...baseStyles.container,
|
|
428
|
-
...colors.container,
|
|
429
|
-
};
|
|
430
|
-
const emptyStateStyle = {
|
|
431
|
-
...baseStyles.emptyState,
|
|
432
|
-
...colors.emptyState,
|
|
433
|
-
};
|
|
434
|
-
const categoryHeaderStyle = {
|
|
435
|
-
...baseStyles.categoryHeader,
|
|
436
|
-
...colors.categoryHeader,
|
|
437
|
-
};
|
|
438
|
-
const searchInputStyle = {
|
|
439
|
-
...baseStyles.searchInput,
|
|
440
|
-
...colors.searchInput,
|
|
441
|
-
};
|
|
442
|
-
// Empty state — no visible questions at all
|
|
443
|
-
if (visible.length === 0) {
|
|
444
|
-
return html `
|
|
445
|
-
<div
|
|
446
|
-
style=${styleMap(sm(containerStyle))}
|
|
447
|
-
data-adaptive-id=${this.instanceId}
|
|
448
|
-
data-adaptive-type="adaptive-faq"
|
|
449
|
-
>
|
|
450
|
-
<div style=${styleMap(sm(emptyStateStyle))}>
|
|
451
|
-
You're all set for now! We'll surface answers here when they're relevant to what
|
|
452
|
-
you're doing.
|
|
453
|
-
</div>
|
|
454
|
-
</div>
|
|
455
|
-
`;
|
|
456
|
-
}
|
|
457
|
-
return html `
|
|
458
|
-
<div
|
|
459
|
-
style=${styleMap(sm(containerStyle))}
|
|
460
|
-
data-adaptive-id=${this.instanceId}
|
|
461
|
-
data-adaptive-type="adaptive-faq"
|
|
462
|
-
>
|
|
463
|
-
${this.faqConfig.searchable
|
|
464
|
-
? html `
|
|
465
|
-
<div style=${styleMap(sm(baseStyles.searchWrapper))}>
|
|
466
|
-
<style>
|
|
467
|
-
[data-adaptive-id="${this.instanceId}"] input::placeholder {
|
|
468
|
-
color: var(--sc-content-search-color, inherit);
|
|
469
|
-
opacity: 0.7;
|
|
470
|
-
}
|
|
471
|
-
</style>
|
|
472
|
-
<input
|
|
473
|
-
type="text"
|
|
474
|
-
placeholder="Search questions..."
|
|
475
|
-
.value=${this._searchQuery}
|
|
476
|
-
style=${styleMap(sm(searchInputStyle))}
|
|
477
|
-
@input=${(e) => {
|
|
478
|
-
this._searchQuery = e.target.value;
|
|
479
|
-
}}
|
|
480
|
-
/>
|
|
481
|
-
</div>
|
|
482
|
-
`
|
|
483
|
-
: nothing}
|
|
484
|
-
|
|
485
|
-
<div style=${styleMap(sm(baseStyles.accordion))}>
|
|
486
|
-
${groups
|
|
487
|
-
? Array.from(groups.entries()).map(([category, items]) => html `
|
|
488
|
-
${category
|
|
489
|
-
? html `
|
|
490
|
-
<div
|
|
491
|
-
style=${styleMap(sm(categoryHeaderStyle))}
|
|
492
|
-
data-category-header=${category}
|
|
493
|
-
>
|
|
494
|
-
${category}
|
|
495
|
-
</div>
|
|
496
|
-
`
|
|
497
|
-
: nothing}
|
|
498
|
-
${this._renderItems(items, theme, feedbackConfig)}
|
|
499
|
-
`)
|
|
500
|
-
: this._renderItems(filtered, theme, feedbackConfig)}
|
|
501
|
-
</div>
|
|
502
|
-
|
|
503
|
-
${this.faqConfig.searchable && filtered.length === 0 && this._searchQuery
|
|
504
|
-
? html `
|
|
505
|
-
<div
|
|
506
|
-
style=${styleMap(sm({ ...baseStyles.noResults, ...colors.emptyState }))}
|
|
507
|
-
>
|
|
508
|
-
No questions found matching "${this._searchQuery}"
|
|
509
|
-
</div>
|
|
510
|
-
`
|
|
511
|
-
: nothing}
|
|
512
|
-
</div>
|
|
513
|
-
`;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
FAQAccordionElement.properties = {
|
|
517
|
-
// Public API — set from the outside
|
|
518
|
-
faqConfig: { attribute: false },
|
|
519
|
-
runtime: { attribute: false },
|
|
520
|
-
instanceId: { type: String },
|
|
521
|
-
// Internal reactive state (prefixed with _ to signal "private")
|
|
522
|
-
_expandedIds: { state: true },
|
|
523
|
-
_highlightId: { state: true },
|
|
524
|
-
_searchQuery: { state: true },
|
|
525
|
-
_feedbackState: { state: true },
|
|
526
|
-
_hoveredId: { state: true },
|
|
527
|
-
};
|
|
528
|
-
// ============================================================================
|
|
529
|
-
// Custom element registration
|
|
530
|
-
// ============================================================================
|
|
531
|
-
if (!customElements.get('syntro-faq-accordion')) {
|
|
532
|
-
customElements.define('syntro-faq-accordion', FAQAccordionElement);
|
|
533
|
-
}
|
|
534
|
-
export default FAQAccordionElement;
|
package/dist/cdn.d.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CDN Entry Point for Adaptive FAQ
|
|
3
|
-
*
|
|
4
|
-
* This module is bundled for CDN delivery and self-registers with the global
|
|
5
|
-
* SynOS app registry when loaded dynamically via the AppLoader.
|
|
6
|
-
*/
|
|
7
|
-
import FAQEditor from './editor';
|
|
8
|
-
/**
|
|
9
|
-
* App manifest for registry registration.
|
|
10
|
-
* Follows the AppManifest interface expected by AppLoader/AppRegistry.
|
|
11
|
-
*/
|
|
12
|
-
export declare const manifest: {
|
|
13
|
-
id: string;
|
|
14
|
-
version: string;
|
|
15
|
-
name: string;
|
|
16
|
-
description: string;
|
|
17
|
-
runtime: {
|
|
18
|
-
actions: readonly [{
|
|
19
|
-
readonly kind: "faq:scroll_to";
|
|
20
|
-
readonly executor: typeof import("./executors").executeScrollToFaq;
|
|
21
|
-
}, {
|
|
22
|
-
readonly kind: "faq:toggle_item";
|
|
23
|
-
readonly executor: typeof import("./executors").executeToggleFaqItem;
|
|
24
|
-
}, {
|
|
25
|
-
readonly kind: "faq:update";
|
|
26
|
-
readonly executor: typeof import("./executors").executeUpdateFaq;
|
|
27
|
-
}];
|
|
28
|
-
widgets: {
|
|
29
|
-
id: string;
|
|
30
|
-
component: {
|
|
31
|
-
mount(container: HTMLElement, config?: import("./types").FAQConfig & {
|
|
32
|
-
runtime?: import("./faq-types").FAQWidgetRuntime;
|
|
33
|
-
instanceId?: string;
|
|
34
|
-
}): (() => void) | undefined;
|
|
35
|
-
};
|
|
36
|
-
metadata: {
|
|
37
|
-
name: string;
|
|
38
|
-
description: string;
|
|
39
|
-
icon: string;
|
|
40
|
-
subtitle: string;
|
|
41
|
-
};
|
|
42
|
-
}[];
|
|
43
|
-
notifyWatchers: (props: Record<string, unknown>) => {
|
|
44
|
-
id: string;
|
|
45
|
-
strategy: import("./types").DecisionStrategy<boolean>;
|
|
46
|
-
eventName: string;
|
|
47
|
-
eventProps: {
|
|
48
|
-
questionId: string;
|
|
49
|
-
question: string;
|
|
50
|
-
title: string | undefined;
|
|
51
|
-
body: string | undefined;
|
|
52
|
-
icon: string | undefined;
|
|
53
|
-
};
|
|
54
|
-
}[];
|
|
55
|
-
};
|
|
56
|
-
editor: {
|
|
57
|
-
component: typeof FAQEditor;
|
|
58
|
-
panel: {
|
|
59
|
-
title: string;
|
|
60
|
-
icon: string;
|
|
61
|
-
description: string;
|
|
62
|
-
};
|
|
63
|
-
getActionLabel(action: Record<string, unknown>): string;
|
|
64
|
-
};
|
|
65
|
-
metadata: {
|
|
66
|
-
isBuiltIn: boolean;
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
export default manifest;
|
|
70
|
-
//# sourceMappingURL=cdn.d.ts.map
|
package/dist/cdn.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,SAA0B,MAAM,UAAU,CAAC;AAGlD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;2BAoCg4iB,CAAC;8BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAtB15iB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;CAQjD,CAAC;AAaF,eAAe,QAAQ,CAAC"}
|
package/dist/cdn.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CDN Entry Point for Adaptive FAQ
|
|
3
|
-
*
|
|
4
|
-
* This module is bundled for CDN delivery and self-registers with the global
|
|
5
|
-
* SynOS app registry when loaded dynamically via the AppLoader.
|
|
6
|
-
*/
|
|
7
|
-
import FAQEditor, { editorPanel } from './editor';
|
|
8
|
-
import { runtime } from './runtime';
|
|
9
|
-
/**
|
|
10
|
-
* App manifest for registry registration.
|
|
11
|
-
* Follows the AppManifest interface expected by AppLoader/AppRegistry.
|
|
12
|
-
*/
|
|
13
|
-
export const manifest = {
|
|
14
|
-
id: 'adaptive-faq',
|
|
15
|
-
version: runtime.version,
|
|
16
|
-
name: runtime.name,
|
|
17
|
-
description: runtime.description,
|
|
18
|
-
// biome-ignore lint: manifest shape maps runtime fields to specific interface properties
|
|
19
|
-
runtime: {
|
|
20
|
-
actions: runtime.executors,
|
|
21
|
-
widgets: runtime.widgets,
|
|
22
|
-
notifyWatchers: runtime.notifyWatchers,
|
|
23
|
-
},
|
|
24
|
-
editor: {
|
|
25
|
-
component: FAQEditor,
|
|
26
|
-
panel: editorPanel,
|
|
27
|
-
getActionLabel(action) {
|
|
28
|
-
const config = action.config || {};
|
|
29
|
-
return config.question || action.kind || 'faq:update';
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
metadata: {
|
|
33
|
-
isBuiltIn: false,
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
/**
|
|
37
|
-
* Self-register with global registry if available.
|
|
38
|
-
* This happens when loaded via script tag (UMD).
|
|
39
|
-
*/
|
|
40
|
-
if (typeof window !== 'undefined') {
|
|
41
|
-
const registry = window.SynOS?.appRegistry;
|
|
42
|
-
if (registry && typeof registry.register === 'function') {
|
|
43
|
-
registry.register(manifest);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
export default manifest;
|
package/dist/editor-lit.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Adaptive FAQ - Lit Editor Component
|
|
3
|
-
*
|
|
4
|
-
* Lit web component port of the React FAQ editor (editor.tsx).
|
|
5
|
-
* Displays FAQ question cards with inline editing, detection,
|
|
6
|
-
* dismiss/restore, and rationale display.
|
|
7
|
-
*
|
|
8
|
-
* Custom events:
|
|
9
|
-
* navigate-home — user clicked back
|
|
10
|
-
* dirty-change — { dirty: boolean }
|
|
11
|
-
*/
|
|
12
|
-
import { LitElement } from 'lit';
|
|
13
|
-
import { type FAQConfig } from './types';
|
|
14
|
-
export declare class FAQEditorLit extends LitElement {
|
|
15
|
-
static properties: {
|
|
16
|
-
config: {
|
|
17
|
-
attribute: boolean;
|
|
18
|
-
};
|
|
19
|
-
onChange: {
|
|
20
|
-
attribute: boolean;
|
|
21
|
-
};
|
|
22
|
-
_editingKey: {
|
|
23
|
-
state: boolean;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
config: FAQConfig | null;
|
|
27
|
-
onChange: ((updated: Record<string, unknown>) => void) | null;
|
|
28
|
-
private _editingKey;
|
|
29
|
-
createRenderRoot(): this;
|
|
30
|
-
private _handleBack;
|
|
31
|
-
private _handleItemClick;
|
|
32
|
-
private _handleFieldChange;
|
|
33
|
-
private _renderEditMode;
|
|
34
|
-
private _renderListMode;
|
|
35
|
-
render(): import("lit-html").TemplateResult<1>;
|
|
36
|
-
}
|
|
37
|
-
//# sourceMappingURL=editor-lit.d.ts.map
|
package/dist/editor-lit.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"editor-lit.d.ts","sourceRoot":"","sources":["../src/editor-lit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAQ,UAAU,EAAW,MAAM,KAAK,CAAC;AAGhD,OAAO,EAAkB,KAAK,SAAS,EAAuC,MAAM,SAAS,CAAC;AAqC9F,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAgB,UAAU;;;;;;;;;;MAIxB;IAEF,MAAM,EAAE,SAAS,GAAG,IAAI,CAAQ;IAChC,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAErE,OAAO,CAAC,WAAW,CAAuB;IAEjC,gBAAgB;IAQzB,OAAO,CAAC,WAAW,CAMjB;IAEF,OAAO,CAAC,gBAAgB,CAEtB;IAEF,OAAO,CAAC,kBAAkB,CAaxB;IAIF,OAAO,CAAC,eAAe,CAsDrB;IAEF,OAAO,CAAC,eAAe,CAoCrB;IAEO,MAAM;CAoChB"}
|