fireworks-ai 0.4.1 → 0.4.2
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.
Potentially problematic release.
This version of fireworks-ai might be problematic. Click here for more details.
- package/dist/react/UserQuestionCard.js +45 -15
- package/package.json +1 -1
|
@@ -3,9 +3,14 @@ import { useEffect, useRef, useState } from "react";
|
|
|
3
3
|
import { cn } from "./cn.js";
|
|
4
4
|
const OTHER_LABEL = "__other__";
|
|
5
5
|
export function UserQuestionCard({ request, onSubmit, className, }) {
|
|
6
|
+
const wrapperRef = useRef(null);
|
|
6
7
|
const [answers, setAnswers] = useState({});
|
|
7
8
|
const [otherText, setOtherText] = useState({});
|
|
8
9
|
const otherInputRefs = useRef({});
|
|
10
|
+
// Auto-focus wrapper so number-key shortcuts work immediately
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
wrapperRef.current?.focus();
|
|
13
|
+
}, []);
|
|
9
14
|
const allAnswered = request.questions.every((q) => {
|
|
10
15
|
const val = answers[q.question];
|
|
11
16
|
if (!val)
|
|
@@ -28,34 +33,42 @@ export function UserQuestionCard({ request, onSubmit, className, }) {
|
|
|
28
33
|
return { ...prev, [question]: label };
|
|
29
34
|
const current = prev[question] ?? "";
|
|
30
35
|
const labels = current ? current.split(", ") : [];
|
|
31
|
-
const next = labels.includes(label)
|
|
32
|
-
? labels.filter((l) => l !== label)
|
|
33
|
-
: [...labels, label];
|
|
36
|
+
const next = labels.includes(label) ? labels.filter((l) => l !== label) : [...labels, label];
|
|
34
37
|
return { ...prev, [question]: next.join(", ") };
|
|
35
38
|
});
|
|
36
39
|
if (label === OTHER_LABEL) {
|
|
37
40
|
setTimeout(() => otherInputRefs.current[question]?.focus(), 0);
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
|
-
// Keyboard shortcuts: number keys select options
|
|
43
|
+
// Keyboard shortcuts: number keys select options, Enter submits
|
|
41
44
|
useEffect(() => {
|
|
42
45
|
function handleKeyDown(e) {
|
|
43
46
|
const tag = e.target?.tagName;
|
|
44
47
|
if (tag === "INPUT" || tag === "TEXTAREA" || e.target?.isContentEditable) {
|
|
45
48
|
return;
|
|
46
49
|
}
|
|
47
|
-
// Only handle single-question cards for keyboard shortcuts
|
|
48
50
|
if (request.questions.length !== 1)
|
|
49
51
|
return;
|
|
50
52
|
const q = request.questions[0];
|
|
51
53
|
if (!q.options?.length)
|
|
52
54
|
return;
|
|
55
|
+
if (e.key === "Enter" && allAnswered) {
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
onSubmit(resolvedAnswers());
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
53
60
|
const totalOptions = q.options.length + 1; // +1 for "Other"
|
|
54
61
|
const num = parseInt(e.key, 10);
|
|
55
62
|
if (num >= 1 && num <= totalOptions) {
|
|
56
63
|
e.preventDefault();
|
|
57
64
|
if (num <= q.options.length) {
|
|
58
|
-
|
|
65
|
+
if (!q.multiSelect) {
|
|
66
|
+
// Single-select: submit immediately (matches ApprovalButtons)
|
|
67
|
+
onSubmit({ [q.question]: q.options[num - 1].label });
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
selectOption(q.question, q.options[num - 1].label, true);
|
|
71
|
+
}
|
|
59
72
|
}
|
|
60
73
|
else {
|
|
61
74
|
selectOption(q.question, OTHER_LABEL, q.multiSelect);
|
|
@@ -65,22 +78,39 @@ export function UserQuestionCard({ request, onSubmit, className, }) {
|
|
|
65
78
|
document.addEventListener("keydown", handleKeyDown);
|
|
66
79
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
67
80
|
});
|
|
68
|
-
return (_jsxs("div", { className: cn("rounded-md border border-blue-500/40 bg-blue-500/5 px-3 py-2.5 text-xs", className), children: [request.questions.map((q) => {
|
|
81
|
+
return (_jsxs("div", { ref: wrapperRef, tabIndex: -1, className: cn("rounded-md border border-blue-500/40 bg-blue-500/5 px-3 py-2.5 text-xs focus:outline-none", className), children: [request.questions.map((q) => {
|
|
69
82
|
const hasOptions = q.options && q.options.length > 0;
|
|
70
83
|
const isSingleQuestion = request.questions.length === 1;
|
|
71
|
-
return (_jsxs("div", { className: "mb-3 last:mb-0", children: [q.header && (_jsx("span", { className: "text-[10px] uppercase tracking-wider text-muted-foreground/70", children: q.header })), _jsx("p", { className: "mt-0.5 text-foreground", children: q.question }), hasOptions && (_jsxs("div", { className: "mt-1.5 flex flex-col gap-1", children: [q.options
|
|
72
|
-
const selected = (answers[q.question] ?? "")
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
return (_jsxs("div", { className: "mb-3 last:mb-0", children: [q.header && (_jsx("span", { className: "text-[10px] uppercase tracking-wider text-muted-foreground/70", children: q.header })), _jsx("p", { className: "mt-0.5 text-foreground", children: q.question }), hasOptions && (_jsxs("div", { className: "mt-1.5 flex flex-col gap-1", children: [q.options?.map((opt, idx) => {
|
|
85
|
+
const selected = (answers[q.question] ?? "").split(", ").includes(opt.label);
|
|
86
|
+
return (_jsxs("button", { type: "button", onClick: () => {
|
|
87
|
+
if (!q.multiSelect && isSingleQuestion) {
|
|
88
|
+
onSubmit({ [q.question]: opt.label });
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
selectOption(q.question, opt.label, q.multiSelect);
|
|
92
|
+
}
|
|
93
|
+
}, className: cn("flex items-start gap-2 rounded border px-2 py-1.5 text-left transition-colors", selected
|
|
76
94
|
? "border-blue-500 bg-blue-500/15 text-foreground ring-1 ring-blue-500/30"
|
|
77
95
|
: "border-border text-muted-foreground hover:bg-accent"), children: [_jsx("span", { className: "mt-px shrink-0", children: q.multiSelect ? (selected ? "☑" : "☐") : selected ? "●" : "○" }), _jsxs("span", { className: "flex-1", children: [_jsx("span", { className: "font-medium", children: opt.label }), opt.description && (_jsxs("span", { className: "ml-1 opacity-70", children: ["\u2014 ", opt.description] }))] }), isSingleQuestion && (_jsx("kbd", { className: "ml-auto shrink-0 text-[9px] opacity-40", children: idx + 1 }))] }, opt.label));
|
|
78
96
|
}), (() => {
|
|
79
97
|
const isOther = answers[q.question] === OTHER_LABEL;
|
|
80
|
-
const otherIdx = q.options
|
|
98
|
+
const otherIdx = (q.options?.length ?? 0) + 1;
|
|
81
99
|
return (_jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => selectOption(q.question, OTHER_LABEL, q.multiSelect), className: cn("flex w-full items-start gap-2 rounded border px-2 py-1.5 text-left transition-colors", isOther
|
|
82
100
|
? "border-blue-500 bg-blue-500/15 text-foreground ring-1 ring-blue-500/30"
|
|
83
|
-
: "border-border text-muted-foreground hover:bg-accent"), children: [_jsx("span", { className: "mt-px shrink-0", children: q.multiSelect ? (isOther ? "☑" : "☐") : isOther ? "●" : "○" }), _jsx("span", { className: "font-medium", children: "Other" }), isSingleQuestion && (_jsx("kbd", { className: "ml-auto shrink-0 text-[9px] opacity-40", children: otherIdx }))] }), isOther && (_jsx("input", { ref: (el) => {
|
|
84
|
-
|
|
101
|
+
: "border-border text-muted-foreground hover:bg-accent"), children: [_jsx("span", { className: "mt-px shrink-0", children: q.multiSelect ? (isOther ? "☑" : "☐") : isOther ? "●" : "○" }), _jsx("span", { className: "font-medium", children: "Other" }), isSingleQuestion && (_jsx("kbd", { className: "ml-auto shrink-0 text-[9px] opacity-40", children: otherIdx }))] }), isOther && (_jsx("input", { ref: (el) => {
|
|
102
|
+
otherInputRefs.current[q.question] = el;
|
|
103
|
+
}, type: "text", className: "mt-1 w-full rounded border border-border bg-background px-2 py-1 text-foreground focus:outline-none focus:ring-1 focus:ring-blue-500/50", placeholder: "Type your answer\u2026", value: otherText[q.question] ?? "", onChange: (e) => setOtherText((prev) => ({ ...prev, [q.question]: e.target.value })), onKeyDown: (e) => {
|
|
104
|
+
if (e.key === "Enter" && otherText[q.question]?.trim()) {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
onSubmit(resolvedAnswers());
|
|
107
|
+
}
|
|
108
|
+
} }))] }));
|
|
109
|
+
})()] })), !hasOptions && (_jsx("input", { type: "text", className: "mt-1.5 w-full rounded border border-border bg-background px-2 py-1 text-foreground focus:outline-none focus:ring-1 focus:ring-blue-500/50", placeholder: "Type your answer\u2026", value: answers[q.question] ?? "", onChange: (e) => setAnswers((prev) => ({ ...prev, [q.question]: e.target.value })), onKeyDown: (e) => {
|
|
110
|
+
if (e.key === "Enter" && (answers[q.question] ?? "").trim()) {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
onSubmit(resolvedAnswers());
|
|
113
|
+
}
|
|
114
|
+
} }))] }, q.question));
|
|
85
115
|
}), _jsx("button", { type: "button", disabled: !allAnswered, onClick: () => onSubmit(resolvedAnswers()), className: cn("mt-2 rounded bg-primary px-3 py-1 text-primary-foreground transition-colors focus:outline-none focus:ring-2 focus:ring-primary/50", allAnswered ? "hover:bg-primary/90" : "opacity-50 cursor-not-allowed"), children: "Submit" })] }));
|
|
86
116
|
}
|