@feedvalue/vue 0.1.5 → 0.1.7
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/index.cjs +444 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +181 -4
- package/dist/index.d.ts +181 -4
- package/dist/index.js +445 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -107,10 +107,454 @@ function useFeedValue(widgetId, config) {
|
|
|
107
107
|
reset
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
|
+
function useReaction(widgetId) {
|
|
111
|
+
const injectedInstance = vue.inject(FEEDVALUE_KEY, null);
|
|
112
|
+
const injectedOptions = vue.inject(FEEDVALUE_OPTIONS_KEY, null);
|
|
113
|
+
const instance = vue.ref(null);
|
|
114
|
+
const isReady = vue.ref(false);
|
|
115
|
+
const showFollowUp = vue.ref(null);
|
|
116
|
+
const submitted = vue.ref(null);
|
|
117
|
+
const isSubmitting = vue.ref(false);
|
|
118
|
+
const error = vue.ref(null);
|
|
119
|
+
let ownsInstance = false;
|
|
120
|
+
let unsubscribe = null;
|
|
121
|
+
const syncState = () => {
|
|
122
|
+
const state = instance.value?.getSnapshot();
|
|
123
|
+
if (state) {
|
|
124
|
+
isReady.value = state.isReady;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
vue.onMounted(() => {
|
|
128
|
+
if (injectedInstance && !widgetId) {
|
|
129
|
+
instance.value = injectedInstance;
|
|
130
|
+
} else {
|
|
131
|
+
const effectiveWidgetId = widgetId ?? injectedOptions?.widgetId;
|
|
132
|
+
if (!effectiveWidgetId) {
|
|
133
|
+
console.error(
|
|
134
|
+
"[FeedValue] No widgetId provided. Either install the plugin with createFeedValue() or pass widgetId to useReaction()."
|
|
135
|
+
);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
instance.value = core.FeedValue.init({
|
|
139
|
+
widgetId: effectiveWidgetId,
|
|
140
|
+
apiBaseUrl: injectedOptions?.apiBaseUrl,
|
|
141
|
+
config: injectedOptions?.config,
|
|
142
|
+
headless: true
|
|
143
|
+
// Reaction widgets are always headless
|
|
144
|
+
});
|
|
145
|
+
ownsInstance = true;
|
|
146
|
+
}
|
|
147
|
+
if (instance.value) {
|
|
148
|
+
unsubscribe = instance.value.subscribe(syncState);
|
|
149
|
+
syncState();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
vue.onUnmounted(() => {
|
|
153
|
+
unsubscribe?.();
|
|
154
|
+
if (ownsInstance && instance.value) {
|
|
155
|
+
instance.value.destroy();
|
|
156
|
+
}
|
|
157
|
+
instance.value = null;
|
|
158
|
+
});
|
|
159
|
+
const options = vue.computed(() => {
|
|
160
|
+
if (!instance.value || !isReady.value) return null;
|
|
161
|
+
return instance.value.getReactionOptions();
|
|
162
|
+
});
|
|
163
|
+
const isReactionWidget = vue.computed(() => {
|
|
164
|
+
return instance.value?.isReaction() ?? false;
|
|
165
|
+
});
|
|
166
|
+
const reactionConfig = vue.computed(() => {
|
|
167
|
+
if (!instance.value || !isReady.value) return null;
|
|
168
|
+
return instance.value._widgetConfig?.config ?? null;
|
|
169
|
+
});
|
|
170
|
+
const showLabels = vue.computed(() => {
|
|
171
|
+
return reactionConfig.value?.showLabels ?? true;
|
|
172
|
+
});
|
|
173
|
+
const buttonSize = vue.computed(() => {
|
|
174
|
+
return reactionConfig.value?.buttonSize ?? "md";
|
|
175
|
+
});
|
|
176
|
+
const followUpTrigger = vue.computed(() => {
|
|
177
|
+
return reactionConfig.value?.followUpTrigger ?? "negative";
|
|
178
|
+
});
|
|
179
|
+
const template = vue.computed(() => {
|
|
180
|
+
return reactionConfig.value?.template;
|
|
181
|
+
});
|
|
182
|
+
const shouldShowFollowUp = (optionValue) => {
|
|
183
|
+
if (followUpTrigger.value === "none") return false;
|
|
184
|
+
if (followUpTrigger.value === "all") return true;
|
|
185
|
+
if (template.value && core.NEGATIVE_OPTIONS_MAP[template.value]) {
|
|
186
|
+
return core.NEGATIVE_OPTIONS_MAP[template.value].includes(optionValue);
|
|
187
|
+
}
|
|
188
|
+
const option = options.value?.find((o) => o.value === optionValue);
|
|
189
|
+
return option?.showFollowUp ?? false;
|
|
190
|
+
};
|
|
191
|
+
const react = async (value, followUp) => {
|
|
192
|
+
if (!instance.value) {
|
|
193
|
+
throw new Error("FeedValue not initialized");
|
|
194
|
+
}
|
|
195
|
+
isSubmitting.value = true;
|
|
196
|
+
error.value = null;
|
|
197
|
+
try {
|
|
198
|
+
await instance.value.react(value, followUp ? { followUp } : void 0);
|
|
199
|
+
submitted.value = value;
|
|
200
|
+
showFollowUp.value = null;
|
|
201
|
+
} catch (err) {
|
|
202
|
+
const reactionError = err instanceof Error ? err : new Error(String(err));
|
|
203
|
+
error.value = reactionError;
|
|
204
|
+
throw reactionError;
|
|
205
|
+
} finally {
|
|
206
|
+
isSubmitting.value = false;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
const setShowFollowUp = (value) => {
|
|
210
|
+
showFollowUp.value = value;
|
|
211
|
+
};
|
|
212
|
+
const clearSubmitted = () => {
|
|
213
|
+
submitted.value = null;
|
|
214
|
+
error.value = null;
|
|
215
|
+
};
|
|
216
|
+
return {
|
|
217
|
+
options,
|
|
218
|
+
isSubmitting: vue.readonly(isSubmitting),
|
|
219
|
+
submitted: vue.readonly(submitted),
|
|
220
|
+
error: vue.readonly(error),
|
|
221
|
+
showFollowUp: vue.readonly(showFollowUp),
|
|
222
|
+
react,
|
|
223
|
+
setShowFollowUp,
|
|
224
|
+
clearSubmitted,
|
|
225
|
+
isReactionWidget,
|
|
226
|
+
isReady: vue.readonly(isReady),
|
|
227
|
+
showLabels,
|
|
228
|
+
buttonSize,
|
|
229
|
+
followUpTrigger,
|
|
230
|
+
shouldShowFollowUp
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
var sizeStyles = {
|
|
234
|
+
sm: {
|
|
235
|
+
button: { padding: "0.375rem 0.75rem", fontSize: "0.75rem", gap: "0.375rem" },
|
|
236
|
+
icon: { fontSize: "1rem" },
|
|
237
|
+
label: { fontSize: "0.75rem" }
|
|
238
|
+
},
|
|
239
|
+
md: {
|
|
240
|
+
button: { padding: "0.5rem 1rem", fontSize: "0.875rem", gap: "0.5rem" },
|
|
241
|
+
icon: { fontSize: "1.125rem" },
|
|
242
|
+
label: { fontSize: "0.875rem" }
|
|
243
|
+
},
|
|
244
|
+
lg: {
|
|
245
|
+
button: { padding: "0.75rem 1.25rem", fontSize: "1rem", gap: "0.625rem" },
|
|
246
|
+
icon: { fontSize: "1.5rem" },
|
|
247
|
+
label: { fontSize: "1rem" }
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
var styles = {
|
|
251
|
+
container: {
|
|
252
|
+
display: "flex",
|
|
253
|
+
flexDirection: "column",
|
|
254
|
+
gap: "0.75rem",
|
|
255
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
256
|
+
},
|
|
257
|
+
buttonGroup: {
|
|
258
|
+
display: "flex",
|
|
259
|
+
flexWrap: "wrap",
|
|
260
|
+
gap: "0.5rem"
|
|
261
|
+
},
|
|
262
|
+
button: {
|
|
263
|
+
display: "inline-flex",
|
|
264
|
+
alignItems: "center",
|
|
265
|
+
fontWeight: "500",
|
|
266
|
+
color: "#374151",
|
|
267
|
+
backgroundColor: "#ffffff",
|
|
268
|
+
border: "1px solid #d1d5db",
|
|
269
|
+
borderRadius: "9999px",
|
|
270
|
+
cursor: "pointer",
|
|
271
|
+
transition: "background-color 0.15s, border-color 0.15s, transform 0.1s"
|
|
272
|
+
},
|
|
273
|
+
buttonActive: {
|
|
274
|
+
backgroundColor: "#eef2ff",
|
|
275
|
+
borderColor: "#6366f1",
|
|
276
|
+
color: "#4f46e5"
|
|
277
|
+
},
|
|
278
|
+
buttonDisabled: {
|
|
279
|
+
opacity: 0.7,
|
|
280
|
+
cursor: "not-allowed"
|
|
281
|
+
},
|
|
282
|
+
icon: {},
|
|
283
|
+
followUp: {
|
|
284
|
+
display: "flex",
|
|
285
|
+
flexDirection: "column",
|
|
286
|
+
gap: "0.5rem",
|
|
287
|
+
padding: "0.75rem",
|
|
288
|
+
backgroundColor: "#f9fafb",
|
|
289
|
+
border: "1px solid #e5e7eb",
|
|
290
|
+
borderRadius: "0.5rem"
|
|
291
|
+
},
|
|
292
|
+
input: {
|
|
293
|
+
width: "100%",
|
|
294
|
+
padding: "0.5rem 0.75rem",
|
|
295
|
+
fontSize: "0.875rem",
|
|
296
|
+
border: "1px solid #d1d5db",
|
|
297
|
+
borderRadius: "0.375rem",
|
|
298
|
+
backgroundColor: "#ffffff",
|
|
299
|
+
boxSizing: "border-box"
|
|
300
|
+
},
|
|
301
|
+
actions: {
|
|
302
|
+
display: "flex",
|
|
303
|
+
gap: "0.5rem",
|
|
304
|
+
justifyContent: "flex-end"
|
|
305
|
+
},
|
|
306
|
+
submitButton: {
|
|
307
|
+
padding: "0.375rem 0.75rem",
|
|
308
|
+
fontSize: "0.875rem",
|
|
309
|
+
fontWeight: "500",
|
|
310
|
+
color: "#ffffff",
|
|
311
|
+
backgroundColor: "#6366f1",
|
|
312
|
+
border: "none",
|
|
313
|
+
borderRadius: "0.375rem",
|
|
314
|
+
cursor: "pointer"
|
|
315
|
+
},
|
|
316
|
+
cancelButton: {
|
|
317
|
+
padding: "0.375rem 0.75rem",
|
|
318
|
+
fontSize: "0.875rem",
|
|
319
|
+
color: "#6b7280",
|
|
320
|
+
background: "none",
|
|
321
|
+
border: "none",
|
|
322
|
+
cursor: "pointer"
|
|
323
|
+
},
|
|
324
|
+
thankYou: {
|
|
325
|
+
display: "flex",
|
|
326
|
+
alignItems: "center",
|
|
327
|
+
gap: "0.5rem",
|
|
328
|
+
padding: "0.75rem 1rem",
|
|
329
|
+
fontSize: "0.875rem",
|
|
330
|
+
color: "#059669",
|
|
331
|
+
backgroundColor: "#ecfdf5",
|
|
332
|
+
border: "1px solid #a7f3d0",
|
|
333
|
+
borderRadius: "0.5rem"
|
|
334
|
+
},
|
|
335
|
+
resetButton: {
|
|
336
|
+
padding: "0.25rem 0.5rem",
|
|
337
|
+
fontSize: "0.875rem",
|
|
338
|
+
color: "#6b7280",
|
|
339
|
+
background: "none",
|
|
340
|
+
border: "none",
|
|
341
|
+
cursor: "pointer"
|
|
342
|
+
},
|
|
343
|
+
error: {
|
|
344
|
+
padding: "0.5rem 0.75rem",
|
|
345
|
+
fontSize: "0.875rem",
|
|
346
|
+
color: "#dc2626",
|
|
347
|
+
backgroundColor: "#fef2f2",
|
|
348
|
+
border: "1px solid #fecaca",
|
|
349
|
+
borderRadius: "0.375rem"
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
var ReactionButtons = vue.defineComponent({
|
|
353
|
+
name: "ReactionButtons",
|
|
354
|
+
props: {
|
|
355
|
+
/** Widget ID (optional if using FeedValue plugin) */
|
|
356
|
+
widgetId: {
|
|
357
|
+
type: String,
|
|
358
|
+
default: void 0
|
|
359
|
+
},
|
|
360
|
+
/** Custom thank you message (overrides widget config) */
|
|
361
|
+
thankYouMessage: {
|
|
362
|
+
type: String,
|
|
363
|
+
default: void 0
|
|
364
|
+
},
|
|
365
|
+
/** Custom class for the container */
|
|
366
|
+
containerClass: {
|
|
367
|
+
type: String,
|
|
368
|
+
default: ""
|
|
369
|
+
},
|
|
370
|
+
/** Custom class for buttons */
|
|
371
|
+
buttonClass: {
|
|
372
|
+
type: String,
|
|
373
|
+
default: ""
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
emits: {
|
|
377
|
+
/** Emitted when a reaction is submitted */
|
|
378
|
+
react: (value, _followUp) => typeof value === "string"
|
|
379
|
+
},
|
|
380
|
+
setup(props, { emit }) {
|
|
381
|
+
const {
|
|
382
|
+
options,
|
|
383
|
+
react,
|
|
384
|
+
isSubmitting,
|
|
385
|
+
submitted,
|
|
386
|
+
error,
|
|
387
|
+
showFollowUp,
|
|
388
|
+
setShowFollowUp,
|
|
389
|
+
clearSubmitted,
|
|
390
|
+
isReady,
|
|
391
|
+
showLabels,
|
|
392
|
+
buttonSize,
|
|
393
|
+
shouldShowFollowUp
|
|
394
|
+
} = useReaction(props.widgetId);
|
|
395
|
+
const followUpText = vue.ref("");
|
|
396
|
+
const getFollowUpOption = vue.computed(() => {
|
|
397
|
+
if (!showFollowUp.value || !options.value) return null;
|
|
398
|
+
return options.value.find((opt) => opt.value === showFollowUp.value) ?? null;
|
|
399
|
+
});
|
|
400
|
+
const handleOptionClick = (option) => {
|
|
401
|
+
if (shouldShowFollowUp(option.value)) {
|
|
402
|
+
setShowFollowUp(option.value);
|
|
403
|
+
} else {
|
|
404
|
+
submitReaction(option.value);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
const submitReaction = async (value, followUp) => {
|
|
408
|
+
try {
|
|
409
|
+
await react(value, followUp);
|
|
410
|
+
emit("react", value, followUp);
|
|
411
|
+
} catch {
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
const handleFollowUpSubmit = (e) => {
|
|
415
|
+
e.preventDefault();
|
|
416
|
+
if (showFollowUp.value) {
|
|
417
|
+
submitReaction(showFollowUp.value, followUpText.value || void 0);
|
|
418
|
+
followUpText.value = "";
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
const cancelFollowUp = () => {
|
|
422
|
+
setShowFollowUp(null);
|
|
423
|
+
followUpText.value = "";
|
|
424
|
+
};
|
|
425
|
+
return () => {
|
|
426
|
+
if (!isReady.value || !options.value) {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
if (submitted.value) {
|
|
430
|
+
return vue.h(
|
|
431
|
+
"div",
|
|
432
|
+
{
|
|
433
|
+
class: props.containerClass,
|
|
434
|
+
style: styles.thankYou
|
|
435
|
+
},
|
|
436
|
+
[
|
|
437
|
+
vue.h("span", props.thankYouMessage || "Thanks for your feedback!"),
|
|
438
|
+
vue.h(
|
|
439
|
+
"button",
|
|
440
|
+
{
|
|
441
|
+
type: "button",
|
|
442
|
+
style: styles.resetButton,
|
|
443
|
+
onClick: clearSubmitted,
|
|
444
|
+
"aria-label": "Submit another reaction"
|
|
445
|
+
},
|
|
446
|
+
"\u21BA"
|
|
447
|
+
)
|
|
448
|
+
]
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
const currentSizeStyles = sizeStyles[buttonSize.value] || sizeStyles.md;
|
|
452
|
+
const buttonElements = options.value.map((option) => {
|
|
453
|
+
const children = [
|
|
454
|
+
vue.h("span", { style: { ...styles.icon, ...currentSizeStyles.icon }, "aria-hidden": "true" }, option.icon)
|
|
455
|
+
];
|
|
456
|
+
if (showLabels.value) {
|
|
457
|
+
children.push(vue.h("span", { style: currentSizeStyles.label }, option.label));
|
|
458
|
+
}
|
|
459
|
+
return vue.h(
|
|
460
|
+
"button",
|
|
461
|
+
{
|
|
462
|
+
key: option.value,
|
|
463
|
+
type: "button",
|
|
464
|
+
class: props.buttonClass,
|
|
465
|
+
style: {
|
|
466
|
+
...styles.button,
|
|
467
|
+
...currentSizeStyles.button,
|
|
468
|
+
...showFollowUp.value === option.value ? styles.buttonActive : {},
|
|
469
|
+
...isSubmitting.value ? styles.buttonDisabled : {}
|
|
470
|
+
},
|
|
471
|
+
disabled: isSubmitting.value,
|
|
472
|
+
"aria-pressed": showFollowUp.value === option.value,
|
|
473
|
+
"aria-label": option.label,
|
|
474
|
+
onClick: () => handleOptionClick(option)
|
|
475
|
+
},
|
|
476
|
+
children
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
let followUpForm = null;
|
|
480
|
+
const followUpOption = getFollowUpOption.value;
|
|
481
|
+
if (showFollowUp.value && followUpOption) {
|
|
482
|
+
followUpForm = vue.h(
|
|
483
|
+
"form",
|
|
484
|
+
{
|
|
485
|
+
style: styles.followUp,
|
|
486
|
+
onSubmit: handleFollowUpSubmit
|
|
487
|
+
},
|
|
488
|
+
[
|
|
489
|
+
vue.h("input", {
|
|
490
|
+
type: "text",
|
|
491
|
+
style: styles.input,
|
|
492
|
+
value: followUpText.value,
|
|
493
|
+
placeholder: followUpOption.followUpPlaceholder || "Tell us more...",
|
|
494
|
+
disabled: isSubmitting.value,
|
|
495
|
+
maxlength: 500,
|
|
496
|
+
onInput: (e) => {
|
|
497
|
+
followUpText.value = e.target.value;
|
|
498
|
+
}
|
|
499
|
+
}),
|
|
500
|
+
vue.h("div", { style: styles.actions }, [
|
|
501
|
+
vue.h(
|
|
502
|
+
"button",
|
|
503
|
+
{
|
|
504
|
+
type: "submit",
|
|
505
|
+
style: {
|
|
506
|
+
...styles.submitButton,
|
|
507
|
+
...isSubmitting.value ? styles.buttonDisabled : {}
|
|
508
|
+
},
|
|
509
|
+
disabled: isSubmitting.value
|
|
510
|
+
},
|
|
511
|
+
isSubmitting.value ? "Sending..." : "Send"
|
|
512
|
+
),
|
|
513
|
+
vue.h(
|
|
514
|
+
"button",
|
|
515
|
+
{
|
|
516
|
+
type: "button",
|
|
517
|
+
style: styles.cancelButton,
|
|
518
|
+
disabled: isSubmitting.value,
|
|
519
|
+
onClick: cancelFollowUp
|
|
520
|
+
},
|
|
521
|
+
"Cancel"
|
|
522
|
+
)
|
|
523
|
+
])
|
|
524
|
+
]
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
let errorElement = null;
|
|
528
|
+
if (error.value) {
|
|
529
|
+
errorElement = vue.h(
|
|
530
|
+
"div",
|
|
531
|
+
{ style: styles.error, role: "alert" },
|
|
532
|
+
error.value.message
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
return vue.h(
|
|
536
|
+
"div",
|
|
537
|
+
{
|
|
538
|
+
class: props.containerClass,
|
|
539
|
+
style: styles.container,
|
|
540
|
+
role: "group",
|
|
541
|
+
"aria-label": "Reaction buttons"
|
|
542
|
+
},
|
|
543
|
+
[
|
|
544
|
+
vue.h("div", { style: styles.buttonGroup, role: "radiogroup" }, buttonElements),
|
|
545
|
+
followUpForm,
|
|
546
|
+
errorElement
|
|
547
|
+
]
|
|
548
|
+
);
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
});
|
|
110
552
|
|
|
111
553
|
exports.FEEDVALUE_KEY = FEEDVALUE_KEY;
|
|
112
554
|
exports.FEEDVALUE_OPTIONS_KEY = FEEDVALUE_OPTIONS_KEY;
|
|
555
|
+
exports.ReactionButtons = ReactionButtons;
|
|
113
556
|
exports.createFeedValue = createFeedValue;
|
|
114
557
|
exports.useFeedValue = useFeedValue;
|
|
558
|
+
exports.useReaction = useReaction;
|
|
115
559
|
//# sourceMappingURL=index.cjs.map
|
|
116
560
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/plugin.ts","../src/composables.ts"],"names":["FeedValue","inject","shallowRef","ref","onMounted","onUnmounted","readonly"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @feedvalue/vue - Plugin\n *\n * Vue plugin for FeedValue. Provides app-level configuration\n * and automatic initialization.\n */\n\nimport type { App, InjectionKey } from 'vue';\nimport { FeedValue, type FeedValueConfig, type FeedValueInstance } from '@feedvalue/core';\n\n/**\n * Plugin options\n */\nexport interface FeedValuePluginOptions {\n /** Widget ID from FeedValue dashboard */\n widgetId: string;\n /** API base URL (for self-hosted) */\n apiBaseUrl?: string;\n /** Configuration overrides */\n config?: Partial<FeedValueConfig>;\n /**\n * Headless mode - disables all DOM rendering.\n * Use this when you want full control over the UI.\n * The SDK will still fetch config and provide all API methods\n * but won't render any trigger button or modal.\n *\n * @default false\n */\n headless?: boolean;\n}\n\n/**\n * Injection key for FeedValue instance\n */\nexport const FEEDVALUE_KEY: InjectionKey<FeedValueInstance> = Symbol('feedvalue');\n\n/**\n * Injection key for widget ID (used by useFeedValue when no instance is injected)\n */\nexport const FEEDVALUE_OPTIONS_KEY: InjectionKey<FeedValuePluginOptions> = Symbol('feedvalue-options');\n\n/**\n * Create FeedValue Vue plugin\n *\n * @example\n * ```ts\n * // main.ts\n * import { createApp } from 'vue';\n * import { createFeedValue } from '@feedvalue/vue';\n * import App from './App.vue';\n *\n * const app = createApp(App);\n *\n * app.use(createFeedValue({\n * widgetId: 'your-widget-id',\n * config: { theme: 'dark' }\n * }));\n *\n * app.mount('#app');\n * ```\n */\nexport function createFeedValue(options: FeedValuePluginOptions) {\n let instance: FeedValueInstance | null = null;\n\n return {\n install(app: App) {\n // Only initialize on client side\n if (typeof window !== 'undefined') {\n instance = FeedValue.init({\n widgetId: options.widgetId,\n apiBaseUrl: options.apiBaseUrl,\n config: options.config,\n headless: options.headless,\n });\n\n // Provide instance to all components\n app.provide(FEEDVALUE_KEY, instance);\n\n // Also provide to global properties for Options API access\n app.config.globalProperties.$feedvalue = instance;\n }\n\n // Always provide options (for SSR where we don't initialize)\n app.provide(FEEDVALUE_OPTIONS_KEY, options);\n },\n };\n}\n\n/**\n * Type augmentation for global properties\n */\ndeclare module 'vue' {\n interface ComponentCustomProperties {\n $feedvalue: FeedValueInstance | undefined;\n }\n}\n","/**\n * @feedvalue/vue - Composables\n *\n * Vue composables for FeedValue. Provides reactive state and methods.\n */\n\nimport {\n ref,\n shallowRef,\n readonly,\n onMounted,\n onUnmounted,\n inject,\n type Ref,\n type ShallowRef,\n} from 'vue';\nimport {\n FeedValue,\n type FeedValueConfig,\n type FeedValueInstance,\n type FeedbackData,\n type UserTraits,\n} from '@feedvalue/core';\nimport { FEEDVALUE_KEY, FEEDVALUE_OPTIONS_KEY } from './plugin';\n\n/**\n * Return type for useFeedValue composable\n */\nexport interface UseFeedValueReturn {\n /** FeedValue instance (for advanced usage) */\n instance: Readonly<ShallowRef<FeedValueInstance | null>>;\n /** Widget is ready */\n isReady: Readonly<Ref<boolean>>;\n /** Modal is open */\n isOpen: Readonly<Ref<boolean>>;\n /** Widget is visible */\n isVisible: Readonly<Ref<boolean>>;\n /** Current error */\n error: Readonly<Ref<Error | null>>;\n /** Submission in progress */\n isSubmitting: Readonly<Ref<boolean>>;\n /** Running in headless mode (no default UI rendered) */\n isHeadless: Readonly<Ref<boolean>>;\n /** Open the feedback modal */\n open: () => void;\n /** Close the feedback modal */\n close: () => void;\n /** Toggle the feedback modal */\n toggle: () => void;\n /** Show the trigger button */\n show: () => void;\n /** Hide the trigger button */\n hide: () => void;\n /** Submit feedback programmatically */\n submit: (feedback: Partial<FeedbackData>) => Promise<void>;\n /** Identify user */\n identify: (userId: string, traits?: UserTraits) => void;\n /** Set user data */\n setData: (data: Record<string, string>) => void;\n /** Reset user data */\n reset: () => void;\n}\n\n/**\n * FeedValue composable\n *\n * Can be used with or without plugin. If plugin is installed, uses the\n * provided instance. Otherwise, creates a new instance.\n *\n * @example\n * ```vue\n * <script setup>\n * import { useFeedValue } from '@feedvalue/vue';\n *\n * // With plugin installed\n * const { open, isReady } = useFeedValue();\n *\n * // Or standalone with widgetId\n * const { open, isReady } = useFeedValue('your-widget-id');\n * </script>\n *\n * <template>\n * <button @click=\"open\" :disabled=\"!isReady\">\n * Give Feedback\n * </button>\n * </template>\n * ```\n */\nexport function useFeedValue(\n widgetId?: string,\n config?: Partial<FeedValueConfig>\n): UseFeedValueReturn {\n // Try to inject instance from plugin\n const injectedInstance = inject(FEEDVALUE_KEY, null);\n const injectedOptions = inject(FEEDVALUE_OPTIONS_KEY, null);\n\n // Refs for reactive state\n const instance = shallowRef<FeedValueInstance | null>(null);\n const isReady = ref(false);\n const isOpen = ref(false);\n const isVisible = ref(false);\n const error = ref<Error | null>(null);\n const isSubmitting = ref(false);\n const isHeadless = ref(false);\n\n // Track if we own the instance (need to destroy on unmount)\n let ownsInstance = false;\n let unsubscribe: (() => void) | null = null;\n\n /**\n * Sync reactive state from instance\n */\n const syncState = () => {\n const state = instance.value?.getSnapshot();\n if (state) {\n isReady.value = state.isReady;\n isOpen.value = state.isOpen;\n isVisible.value = state.isVisible;\n error.value = state.error;\n isSubmitting.value = state.isSubmitting;\n }\n };\n\n onMounted(() => {\n // Use injected instance if available and no widgetId override\n if (injectedInstance && !widgetId) {\n instance.value = injectedInstance;\n isHeadless.value = injectedInstance.isHeadless();\n } else {\n // Create new instance\n const effectiveWidgetId = widgetId ?? injectedOptions?.widgetId;\n\n if (!effectiveWidgetId) {\n console.error(\n '[FeedValue] No widgetId provided. Either install the plugin with createFeedValue() ' +\n 'or pass widgetId to useFeedValue().'\n );\n return;\n }\n\n instance.value = FeedValue.init({\n widgetId: effectiveWidgetId,\n apiBaseUrl: injectedOptions?.apiBaseUrl,\n config: config ?? injectedOptions?.config,\n headless: injectedOptions?.headless,\n });\n ownsInstance = true;\n }\n\n // Subscribe to state changes\n if (instance.value) {\n unsubscribe = instance.value.subscribe(syncState);\n isHeadless.value = instance.value.isHeadless();\n syncState(); // Initial sync\n }\n });\n\n onUnmounted(() => {\n unsubscribe?.();\n if (ownsInstance && instance.value) {\n instance.value.destroy();\n }\n instance.value = null;\n });\n\n // Methods that delegate to instance\n const open = () => instance.value?.open();\n const close = () => instance.value?.close();\n const toggle = () => instance.value?.toggle();\n const show = () => instance.value?.show();\n const hide = () => instance.value?.hide();\n const submit = (feedback: Partial<FeedbackData>) =>\n instance.value?.submit(feedback) ?? Promise.reject(new Error('Not initialized'));\n const identify = (userId: string, traits?: UserTraits) =>\n instance.value?.identify(userId, traits);\n const setData = (data: Record<string, string>) => instance.value?.setData(data);\n const reset = () => instance.value?.reset();\n\n return {\n instance: readonly(instance),\n isReady: readonly(isReady),\n isOpen: readonly(isOpen),\n isVisible: readonly(isVisible),\n error: readonly(error),\n isSubmitting: readonly(isSubmitting),\n isHeadless: readonly(isHeadless),\n open,\n close,\n toggle,\n show,\n hide,\n submit,\n identify,\n setData,\n reset,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts","../src/composables.ts","../src/use-reaction.ts","../src/ReactionButtons.ts"],"names":["FeedValue","inject","shallowRef","ref","onMounted","onUnmounted","readonly","computed","NEGATIVE_OPTIONS_MAP","defineComponent","h"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @feedvalue/vue - Plugin\n *\n * Vue plugin for FeedValue. Provides app-level configuration\n * and automatic initialization.\n */\n\nimport type { App, InjectionKey } from 'vue';\nimport { FeedValue, type FeedValueConfig, type FeedValueInstance } from '@feedvalue/core';\n\n/**\n * Plugin options\n */\nexport interface FeedValuePluginOptions {\n /** Widget ID from FeedValue dashboard */\n widgetId: string;\n /** API base URL (for self-hosted) */\n apiBaseUrl?: string;\n /** Configuration overrides */\n config?: Partial<FeedValueConfig>;\n /**\n * Headless mode - disables all DOM rendering.\n * Use this when you want full control over the UI.\n * The SDK will still fetch config and provide all API methods\n * but won't render any trigger button or modal.\n *\n * @default false\n */\n headless?: boolean;\n}\n\n/**\n * Injection key for FeedValue instance\n */\nexport const FEEDVALUE_KEY: InjectionKey<FeedValueInstance> = Symbol('feedvalue');\n\n/**\n * Injection key for widget ID (used by useFeedValue when no instance is injected)\n */\nexport const FEEDVALUE_OPTIONS_KEY: InjectionKey<FeedValuePluginOptions> = Symbol('feedvalue-options');\n\n/**\n * Create FeedValue Vue plugin\n *\n * @example\n * ```ts\n * // main.ts\n * import { createApp } from 'vue';\n * import { createFeedValue } from '@feedvalue/vue';\n * import App from './App.vue';\n *\n * const app = createApp(App);\n *\n * app.use(createFeedValue({\n * widgetId: 'your-widget-id',\n * config: { theme: 'dark' }\n * }));\n *\n * app.mount('#app');\n * ```\n */\nexport function createFeedValue(options: FeedValuePluginOptions) {\n let instance: FeedValueInstance | null = null;\n\n return {\n install(app: App) {\n // Only initialize on client side\n if (typeof window !== 'undefined') {\n instance = FeedValue.init({\n widgetId: options.widgetId,\n apiBaseUrl: options.apiBaseUrl,\n config: options.config,\n headless: options.headless,\n });\n\n // Provide instance to all components\n app.provide(FEEDVALUE_KEY, instance);\n\n // Also provide to global properties for Options API access\n app.config.globalProperties.$feedvalue = instance;\n }\n\n // Always provide options (for SSR where we don't initialize)\n app.provide(FEEDVALUE_OPTIONS_KEY, options);\n },\n };\n}\n\n/**\n * Type augmentation for global properties\n */\ndeclare module 'vue' {\n interface ComponentCustomProperties {\n $feedvalue: FeedValueInstance | undefined;\n }\n}\n","/**\n * @feedvalue/vue - Composables\n *\n * Vue composables for FeedValue. Provides reactive state and methods.\n */\n\nimport {\n ref,\n shallowRef,\n readonly,\n onMounted,\n onUnmounted,\n inject,\n type Ref,\n type ShallowRef,\n} from 'vue';\nimport {\n FeedValue,\n type FeedValueConfig,\n type FeedValueInstance,\n type FeedbackData,\n type UserTraits,\n} from '@feedvalue/core';\nimport { FEEDVALUE_KEY, FEEDVALUE_OPTIONS_KEY } from './plugin';\n\n/**\n * Return type for useFeedValue composable\n */\nexport interface UseFeedValueReturn {\n /** FeedValue instance (for advanced usage) */\n instance: Readonly<ShallowRef<FeedValueInstance | null>>;\n /** Widget is ready */\n isReady: Readonly<Ref<boolean>>;\n /** Modal is open */\n isOpen: Readonly<Ref<boolean>>;\n /** Widget is visible */\n isVisible: Readonly<Ref<boolean>>;\n /** Current error */\n error: Readonly<Ref<Error | null>>;\n /** Submission in progress */\n isSubmitting: Readonly<Ref<boolean>>;\n /** Running in headless mode (no default UI rendered) */\n isHeadless: Readonly<Ref<boolean>>;\n /** Open the feedback modal */\n open: () => void;\n /** Close the feedback modal */\n close: () => void;\n /** Toggle the feedback modal */\n toggle: () => void;\n /** Show the trigger button */\n show: () => void;\n /** Hide the trigger button */\n hide: () => void;\n /** Submit feedback programmatically */\n submit: (feedback: Partial<FeedbackData>) => Promise<void>;\n /** Identify user */\n identify: (userId: string, traits?: UserTraits) => void;\n /** Set user data */\n setData: (data: Record<string, string>) => void;\n /** Reset user data */\n reset: () => void;\n}\n\n/**\n * FeedValue composable\n *\n * Can be used with or without plugin. If plugin is installed, uses the\n * provided instance. Otherwise, creates a new instance.\n *\n * @example\n * ```vue\n * <script setup>\n * import { useFeedValue } from '@feedvalue/vue';\n *\n * // With plugin installed\n * const { open, isReady } = useFeedValue();\n *\n * // Or standalone with widgetId\n * const { open, isReady } = useFeedValue('your-widget-id');\n * </script>\n *\n * <template>\n * <button @click=\"open\" :disabled=\"!isReady\">\n * Give Feedback\n * </button>\n * </template>\n * ```\n */\nexport function useFeedValue(\n widgetId?: string,\n config?: Partial<FeedValueConfig>\n): UseFeedValueReturn {\n // Try to inject instance from plugin\n const injectedInstance = inject(FEEDVALUE_KEY, null);\n const injectedOptions = inject(FEEDVALUE_OPTIONS_KEY, null);\n\n // Refs for reactive state\n const instance = shallowRef<FeedValueInstance | null>(null);\n const isReady = ref(false);\n const isOpen = ref(false);\n const isVisible = ref(false);\n const error = ref<Error | null>(null);\n const isSubmitting = ref(false);\n const isHeadless = ref(false);\n\n // Track if we own the instance (need to destroy on unmount)\n let ownsInstance = false;\n let unsubscribe: (() => void) | null = null;\n\n /**\n * Sync reactive state from instance\n */\n const syncState = () => {\n const state = instance.value?.getSnapshot();\n if (state) {\n isReady.value = state.isReady;\n isOpen.value = state.isOpen;\n isVisible.value = state.isVisible;\n error.value = state.error;\n isSubmitting.value = state.isSubmitting;\n }\n };\n\n onMounted(() => {\n // Use injected instance if available and no widgetId override\n if (injectedInstance && !widgetId) {\n instance.value = injectedInstance;\n isHeadless.value = injectedInstance.isHeadless();\n } else {\n // Create new instance\n const effectiveWidgetId = widgetId ?? injectedOptions?.widgetId;\n\n if (!effectiveWidgetId) {\n console.error(\n '[FeedValue] No widgetId provided. Either install the plugin with createFeedValue() ' +\n 'or pass widgetId to useFeedValue().'\n );\n return;\n }\n\n instance.value = FeedValue.init({\n widgetId: effectiveWidgetId,\n apiBaseUrl: injectedOptions?.apiBaseUrl,\n config: config ?? injectedOptions?.config,\n headless: injectedOptions?.headless,\n });\n ownsInstance = true;\n }\n\n // Subscribe to state changes\n if (instance.value) {\n unsubscribe = instance.value.subscribe(syncState);\n isHeadless.value = instance.value.isHeadless();\n syncState(); // Initial sync\n }\n });\n\n onUnmounted(() => {\n unsubscribe?.();\n if (ownsInstance && instance.value) {\n instance.value.destroy();\n }\n instance.value = null;\n });\n\n // Methods that delegate to instance\n const open = () => instance.value?.open();\n const close = () => instance.value?.close();\n const toggle = () => instance.value?.toggle();\n const show = () => instance.value?.show();\n const hide = () => instance.value?.hide();\n const submit = (feedback: Partial<FeedbackData>) =>\n instance.value?.submit(feedback) ?? Promise.reject(new Error('Not initialized'));\n const identify = (userId: string, traits?: UserTraits) =>\n instance.value?.identify(userId, traits);\n const setData = (data: Record<string, string>) => instance.value?.setData(data);\n const reset = () => instance.value?.reset();\n\n return {\n instance: readonly(instance),\n isReady: readonly(isReady),\n isOpen: readonly(isOpen),\n isVisible: readonly(isVisible),\n error: readonly(error),\n isSubmitting: readonly(isSubmitting),\n isHeadless: readonly(isHeadless),\n open,\n close,\n toggle,\n show,\n hide,\n submit,\n identify,\n setData,\n reset,\n };\n}\n","/**\n * @feedvalue/vue - useReaction Composable\n *\n * Composable for reaction widgets in Vue applications.\n * Provides a simple API for submitting reactions.\n */\n\nimport {\n ref,\n computed,\n readonly,\n onMounted,\n onUnmounted,\n inject,\n type Ref,\n type ComputedRef,\n} from 'vue';\nimport {\n FeedValue,\n NEGATIVE_OPTIONS_MAP,\n type ReactionOption,\n type FeedValueInstance,\n type ButtonSize,\n type FollowUpTrigger,\n type ReactionTemplate,\n} from '@feedvalue/core';\nimport { FEEDVALUE_KEY, FEEDVALUE_OPTIONS_KEY } from './plugin';\n\n/**\n * Return type for useReaction composable\n */\nexport interface UseReactionReturn {\n /** Available reaction options */\n options: ComputedRef<ReactionOption[] | null>;\n /** Currently submitting */\n isSubmitting: Readonly<Ref<boolean>>;\n /** Successfully submitted value (null if not yet submitted) */\n submitted: Readonly<Ref<string | null>>;\n /** Error if submission failed */\n error: Readonly<Ref<Error | null>>;\n /** Option value requiring follow-up input (null if none) */\n showFollowUp: Readonly<Ref<string | null>>;\n /** Submit a reaction */\n react: (value: string, followUp?: string) => Promise<void>;\n /** Set which option is showing follow-up input */\n setShowFollowUp: (value: string | null) => void;\n /** Clear the submitted state to allow re-submission */\n clearSubmitted: () => void;\n /** Check if widget is a reaction type */\n isReactionWidget: ComputedRef<boolean>;\n /** Widget configuration is ready */\n isReady: Readonly<Ref<boolean>>;\n /** Whether to show text labels next to icons */\n showLabels: ComputedRef<boolean>;\n /** Button size */\n buttonSize: ComputedRef<ButtonSize>;\n /** When to show follow-up input */\n followUpTrigger: ComputedRef<FollowUpTrigger>;\n /** Check if an option should show follow-up based on followUpTrigger */\n shouldShowFollowUp: (optionValue: string) => boolean;\n}\n\n/**\n * Composable for reaction widgets\n *\n * Provides reaction options, submission handling, and follow-up state management.\n * Can be used with or without the FeedValue plugin.\n *\n * @param widgetId - Optional widget ID override (uses plugin widget if not provided)\n *\n * @example\n * ```vue\n * <script setup>\n * import { useReaction } from '@feedvalue/vue';\n *\n * const {\n * options,\n * react,\n * isSubmitting,\n * submitted,\n * error,\n * showFollowUp,\n * setShowFollowUp,\n * isReady,\n * } = useReaction();\n *\n * const handleClick = (option) => {\n * if (option.showFollowUp) {\n * setShowFollowUp(option.value);\n * } else {\n * react(option.value);\n * }\n * };\n * </script>\n *\n * <template>\n * <div v-if=\"isReady && options\">\n * <div v-if=\"submitted\">Thanks for your feedback!</div>\n *\n * <template v-else>\n * <button\n * v-for=\"option in options\"\n * :key=\"option.value\"\n * @click=\"handleClick(option)\"\n * :disabled=\"isSubmitting\"\n * >\n * {{ option.icon }} {{ option.label }}\n * </button>\n *\n * <form v-if=\"showFollowUp\" @submit.prevent=\"react(showFollowUp, followUpText)\">\n * <input v-model=\"followUpText\" placeholder=\"Tell us more...\" />\n * <button type=\"submit\">Send</button>\n * </form>\n *\n * <div v-if=\"error\">Error: {{ error.message }}</div>\n * </template>\n * </div>\n * </template>\n * ```\n */\nexport function useReaction(widgetId?: string): UseReactionReturn {\n // Try to inject instance from plugin\n const injectedInstance = inject(FEEDVALUE_KEY, null);\n const injectedOptions = inject(FEEDVALUE_OPTIONS_KEY, null);\n\n // Local instance ref (may be created if no plugin or widgetId override)\n const instance = ref<FeedValueInstance | null>(null);\n const isReady = ref(false);\n\n // Local state for reaction UI\n const showFollowUp = ref<string | null>(null);\n const submitted = ref<string | null>(null);\n const isSubmitting = ref(false);\n const error = ref<Error | null>(null);\n\n // Track if we own the instance (need to destroy on unmount)\n let ownsInstance = false;\n let unsubscribe: (() => void) | null = null;\n\n /**\n * Sync ready state from instance\n */\n const syncState = () => {\n const state = instance.value?.getSnapshot();\n if (state) {\n isReady.value = state.isReady;\n }\n };\n\n onMounted(() => {\n // Use injected instance if available and no widgetId override\n if (injectedInstance && !widgetId) {\n instance.value = injectedInstance;\n } else {\n // Create new instance\n const effectiveWidgetId = widgetId ?? injectedOptions?.widgetId;\n\n if (!effectiveWidgetId) {\n console.error(\n '[FeedValue] No widgetId provided. Either install the plugin with createFeedValue() ' +\n 'or pass widgetId to useReaction().'\n );\n return;\n }\n\n instance.value = FeedValue.init({\n widgetId: effectiveWidgetId,\n apiBaseUrl: injectedOptions?.apiBaseUrl,\n config: injectedOptions?.config,\n headless: true, // Reaction widgets are always headless\n });\n ownsInstance = true;\n }\n\n // Subscribe to state changes\n if (instance.value) {\n unsubscribe = instance.value.subscribe(syncState);\n syncState(); // Initial sync\n }\n });\n\n onUnmounted(() => {\n unsubscribe?.();\n if (ownsInstance && instance.value) {\n instance.value.destroy();\n }\n instance.value = null;\n });\n\n // Computed: reaction options from instance\n const options = computed<ReactionOption[] | null>(() => {\n if (!instance.value || !isReady.value) return null;\n return instance.value.getReactionOptions();\n });\n\n // Computed: check if this is a reaction widget\n const isReactionWidget = computed(() => {\n return instance.value?.isReaction() ?? false;\n });\n\n // Computed: get config values with defaults\n const reactionConfig = computed(() => {\n if (!instance.value || !isReady.value) return null;\n // Access the widget config which includes reaction config\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (instance.value as any)._widgetConfig?.config ?? null;\n });\n\n const showLabels = computed(() => {\n return reactionConfig.value?.showLabels ?? true;\n });\n\n const buttonSize = computed<ButtonSize>(() => {\n return reactionConfig.value?.buttonSize ?? 'md';\n });\n\n const followUpTrigger = computed<FollowUpTrigger>(() => {\n return reactionConfig.value?.followUpTrigger ?? 'negative';\n });\n\n const template = computed<ReactionTemplate | undefined>(() => {\n return reactionConfig.value?.template;\n });\n\n /**\n * Check if an option should show follow-up based on followUpTrigger\n */\n const shouldShowFollowUp = (optionValue: string): boolean => {\n if (followUpTrigger.value === 'none') return false;\n if (followUpTrigger.value === 'all') return true;\n // followUpTrigger === 'negative'\n if (template.value && NEGATIVE_OPTIONS_MAP[template.value]) {\n return NEGATIVE_OPTIONS_MAP[template.value].includes(optionValue);\n }\n // For custom options, use the option's own showFollowUp setting\n const option = options.value?.find((o) => o.value === optionValue);\n return option?.showFollowUp ?? false;\n };\n\n /**\n * Submit a reaction\n */\n const react = async (value: string, followUp?: string): Promise<void> => {\n if (!instance.value) {\n throw new Error('FeedValue not initialized');\n }\n\n isSubmitting.value = true;\n error.value = null;\n\n try {\n await instance.value.react(value, followUp ? { followUp } : undefined);\n submitted.value = value;\n showFollowUp.value = null;\n } catch (err) {\n const reactionError = err instanceof Error ? err : new Error(String(err));\n error.value = reactionError;\n throw reactionError;\n } finally {\n isSubmitting.value = false;\n }\n };\n\n /**\n * Set which option is showing follow-up input\n */\n const setShowFollowUp = (value: string | null): void => {\n showFollowUp.value = value;\n };\n\n /**\n * Clear submitted state to allow re-submission\n */\n const clearSubmitted = (): void => {\n submitted.value = null;\n error.value = null;\n };\n\n return {\n options,\n isSubmitting: readonly(isSubmitting),\n submitted: readonly(submitted),\n error: readonly(error),\n showFollowUp: readonly(showFollowUp),\n react,\n setShowFollowUp,\n clearSubmitted,\n isReactionWidget,\n isReady: readonly(isReady),\n showLabels,\n buttonSize,\n followUpTrigger,\n shouldShowFollowUp,\n };\n}\n","/**\n * @feedvalue/vue - ReactionButtons Component\n *\n * Pre-built reaction buttons component for Vue applications.\n * Renders reaction options with optional follow-up input.\n */\n\nimport { defineComponent, ref, computed, h, type PropType } from 'vue';\nimport { useReaction } from './use-reaction';\nimport type { ReactionOption } from '@feedvalue/core';\n\nimport type { ButtonSize } from '@feedvalue/core';\n\n/**\n * Button size style variants\n */\nconst sizeStyles: Record<ButtonSize, { button: Record<string, string>; icon: Record<string, string>; label: Record<string, string> }> = {\n sm: {\n button: { padding: '0.375rem 0.75rem', fontSize: '0.75rem', gap: '0.375rem' },\n icon: { fontSize: '1rem' },\n label: { fontSize: '0.75rem' },\n },\n md: {\n button: { padding: '0.5rem 1rem', fontSize: '0.875rem', gap: '0.5rem' },\n icon: { fontSize: '1.125rem' },\n label: { fontSize: '0.875rem' },\n },\n lg: {\n button: { padding: '0.75rem 1.25rem', fontSize: '1rem', gap: '0.625rem' },\n icon: { fontSize: '1.5rem' },\n label: { fontSize: '1rem' },\n },\n};\n\n/**\n * Default CSS styles for the component\n */\nconst styles = {\n container: {\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '0.75rem',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n },\n buttonGroup: {\n display: 'flex',\n flexWrap: 'wrap' as const,\n gap: '0.5rem',\n },\n button: {\n display: 'inline-flex',\n alignItems: 'center',\n fontWeight: '500',\n color: '#374151',\n backgroundColor: '#ffffff',\n border: '1px solid #d1d5db',\n borderRadius: '9999px',\n cursor: 'pointer',\n transition: 'background-color 0.15s, border-color 0.15s, transform 0.1s',\n },\n buttonActive: {\n backgroundColor: '#eef2ff',\n borderColor: '#6366f1',\n color: '#4f46e5',\n },\n buttonDisabled: {\n opacity: 0.7,\n cursor: 'not-allowed',\n },\n icon: {},\n followUp: {\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '0.5rem',\n padding: '0.75rem',\n backgroundColor: '#f9fafb',\n border: '1px solid #e5e7eb',\n borderRadius: '0.5rem',\n },\n input: {\n width: '100%',\n padding: '0.5rem 0.75rem',\n fontSize: '0.875rem',\n border: '1px solid #d1d5db',\n borderRadius: '0.375rem',\n backgroundColor: '#ffffff',\n boxSizing: 'border-box' as const,\n },\n actions: {\n display: 'flex',\n gap: '0.5rem',\n justifyContent: 'flex-end',\n },\n submitButton: {\n padding: '0.375rem 0.75rem',\n fontSize: '0.875rem',\n fontWeight: '500',\n color: '#ffffff',\n backgroundColor: '#6366f1',\n border: 'none',\n borderRadius: '0.375rem',\n cursor: 'pointer',\n },\n cancelButton: {\n padding: '0.375rem 0.75rem',\n fontSize: '0.875rem',\n color: '#6b7280',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n },\n thankYou: {\n display: 'flex',\n alignItems: 'center',\n gap: '0.5rem',\n padding: '0.75rem 1rem',\n fontSize: '0.875rem',\n color: '#059669',\n backgroundColor: '#ecfdf5',\n border: '1px solid #a7f3d0',\n borderRadius: '0.5rem',\n },\n resetButton: {\n padding: '0.25rem 0.5rem',\n fontSize: '0.875rem',\n color: '#6b7280',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n },\n error: {\n padding: '0.5rem 0.75rem',\n fontSize: '0.875rem',\n color: '#dc2626',\n backgroundColor: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: '0.375rem',\n },\n};\n\n/**\n * ReactionButtons Component\n *\n * Pre-built reaction buttons with follow-up input support.\n *\n * @example\n * ```vue\n * <template>\n * <ReactionButtons widget-id=\"xxx\" @react=\"onReact\" />\n * </template>\n *\n * <script setup>\n * import { ReactionButtons } from '@feedvalue/vue';\n *\n * const onReact = (value, followUp) => {\n * console.log('Reacted:', value, followUp);\n * };\n * </script>\n * ```\n */\nexport const ReactionButtons = defineComponent({\n name: 'ReactionButtons',\n\n props: {\n /** Widget ID (optional if using FeedValue plugin) */\n widgetId: {\n type: String as PropType<string | undefined>,\n default: undefined,\n },\n /** Custom thank you message (overrides widget config) */\n thankYouMessage: {\n type: String as PropType<string | undefined>,\n default: undefined,\n },\n /** Custom class for the container */\n containerClass: {\n type: String,\n default: '',\n },\n /** Custom class for buttons */\n buttonClass: {\n type: String,\n default: '',\n },\n },\n\n emits: {\n /** Emitted when a reaction is submitted */\n react: (value: string, _followUp?: string) => typeof value === 'string',\n },\n\n setup(props, { emit }) {\n const {\n options,\n react,\n isSubmitting,\n submitted,\n error,\n showFollowUp,\n setShowFollowUp,\n clearSubmitted,\n isReady,\n showLabels,\n buttonSize,\n shouldShowFollowUp,\n } = useReaction(props.widgetId);\n\n // Local state for follow-up input\n const followUpText = ref('');\n\n // Get follow-up option\n const getFollowUpOption = computed<ReactionOption | null>(() => {\n if (!showFollowUp.value || !options.value) return null;\n return options.value.find((opt) => opt.value === showFollowUp.value) ?? null;\n });\n\n /**\n * Handle option click\n */\n const handleOptionClick = (option: ReactionOption) => {\n if (shouldShowFollowUp(option.value)) {\n setShowFollowUp(option.value);\n } else {\n submitReaction(option.value);\n }\n };\n\n /**\n * Submit reaction\n */\n const submitReaction = async (value: string, followUp?: string) => {\n try {\n await react(value, followUp);\n emit('react', value, followUp);\n } catch {\n // Error is already set in state\n }\n };\n\n /**\n * Handle follow-up form submit\n */\n const handleFollowUpSubmit = (e: Event) => {\n e.preventDefault();\n if (showFollowUp.value) {\n submitReaction(showFollowUp.value, followUpText.value || undefined);\n followUpText.value = '';\n }\n };\n\n /**\n * Cancel follow-up\n */\n const cancelFollowUp = () => {\n setShowFollowUp(null);\n followUpText.value = '';\n };\n\n return () => {\n // Don't render if not ready or no options\n if (!isReady.value || !options.value) {\n return null;\n }\n\n // Render thank you message after submission\n if (submitted.value) {\n return h(\n 'div',\n {\n class: props.containerClass,\n style: styles.thankYou,\n },\n [\n h('span', props.thankYouMessage || 'Thanks for your feedback!'),\n h(\n 'button',\n {\n type: 'button',\n style: styles.resetButton,\n onClick: clearSubmitted,\n 'aria-label': 'Submit another reaction',\n },\n '↺'\n ),\n ]\n );\n }\n\n // Build reaction buttons\n const currentSizeStyles = sizeStyles[buttonSize.value] || sizeStyles.md;\n const buttonElements = options.value.map((option) => {\n const children = [\n h('span', { style: { ...styles.icon, ...currentSizeStyles.icon }, 'aria-hidden': 'true' }, option.icon),\n ];\n if (showLabels.value) {\n children.push(h('span', { style: currentSizeStyles.label }, option.label));\n }\n\n return h(\n 'button',\n {\n key: option.value,\n type: 'button',\n class: props.buttonClass,\n style: {\n ...styles.button,\n ...currentSizeStyles.button,\n ...(showFollowUp.value === option.value ? styles.buttonActive : {}),\n ...(isSubmitting.value ? styles.buttonDisabled : {}),\n },\n disabled: isSubmitting.value,\n 'aria-pressed': showFollowUp.value === option.value,\n 'aria-label': option.label,\n onClick: () => handleOptionClick(option),\n },\n children\n );\n });\n\n // Build follow-up form if needed\n let followUpForm = null;\n const followUpOption = getFollowUpOption.value;\n if (showFollowUp.value && followUpOption) {\n followUpForm = h(\n 'form',\n {\n style: styles.followUp,\n onSubmit: handleFollowUpSubmit,\n },\n [\n h('input', {\n type: 'text',\n style: styles.input,\n value: followUpText.value,\n placeholder: followUpOption.followUpPlaceholder || 'Tell us more...',\n disabled: isSubmitting.value,\n maxlength: 500,\n onInput: (e: Event) => {\n followUpText.value = (e.target as HTMLInputElement).value;\n },\n }),\n h('div', { style: styles.actions }, [\n h(\n 'button',\n {\n type: 'submit',\n style: {\n ...styles.submitButton,\n ...(isSubmitting.value ? styles.buttonDisabled : {}),\n },\n disabled: isSubmitting.value,\n },\n isSubmitting.value ? 'Sending...' : 'Send'\n ),\n h(\n 'button',\n {\n type: 'button',\n style: styles.cancelButton,\n disabled: isSubmitting.value,\n onClick: cancelFollowUp,\n },\n 'Cancel'\n ),\n ]),\n ]\n );\n }\n\n // Build error message\n let errorElement = null;\n if (error.value) {\n errorElement = h(\n 'div',\n { style: styles.error, role: 'alert' },\n error.value.message\n );\n }\n\n // Return full component\n return h(\n 'div',\n {\n class: props.containerClass,\n style: styles.container,\n role: 'group',\n 'aria-label': 'Reaction buttons',\n },\n [\n h('div', { style: styles.buttonGroup, role: 'radiogroup' }, buttonElements),\n followUpForm,\n errorElement,\n ]\n );\n };\n },\n});\n\nexport default ReactionButtons;\n"]}
|