@mrrocket/sdk 0.0.0-stage → 0.2.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/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/README.md +284 -2
- package/dist/api.d.ts +113 -0
- package/dist/api.js +71 -0
- package/dist/api.js.map +1 -0
- package/dist/browser.d.ts +11 -0
- package/dist/browser.js +25 -0
- package/dist/browser.js.map +1 -0
- package/dist/capture.d.ts +50 -0
- package/dist/capture.js +91 -0
- package/dist/capture.js.map +1 -0
- package/dist/client.d.ts +83 -0
- package/dist/client.js +468 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +33 -0
- package/dist/context.js +15 -0
- package/dist/context.js.map +1 -0
- package/dist/core.d.ts +132 -0
- package/dist/core.js +104 -0
- package/dist/core.js.map +1 -0
- package/dist/feedback.d.ts +28 -0
- package/dist/feedback.js +459 -0
- package/dist/feedback.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy.d.ts +28 -0
- package/dist/proxy.js +12 -0
- package/dist/proxy.js.map +1 -0
- package/dist/referral.d.ts +86 -0
- package/dist/referral.js +128 -0
- package/dist/referral.js.map +1 -0
- package/dist/replay-core.d.ts +93 -0
- package/dist/replay-core.js +122 -0
- package/dist/replay-core.js.map +1 -0
- package/dist/replay.d.ts +34 -0
- package/dist/replay.js +200 -0
- package/dist/replay.js.map +1 -0
- package/dist/server.d.ts +47 -0
- package/dist/server.js +43 -0
- package/dist/server.js.map +1 -0
- package/dist/styles.d.ts +4 -0
- package/dist/styles.js +147 -0
- package/dist/styles.js.map +1 -0
- package/dist/survey.d.ts +4 -0
- package/dist/survey.js +201 -0
- package/dist/survey.js.map +1 -0
- package/dist/ui.d.ts +29 -0
- package/dist/ui.js +77 -0
- package/dist/ui.js.map +1 -0
- package/package.json +96 -5
package/dist/feedback.js
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/feedback.tsx
|
|
4
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
5
|
+
import { useApi } from "./api.js";
|
|
6
|
+
import { Chat, ChevronUp, Cross, Plus, Root, StatusPill, VoteButton, similarPosts, timeAgo, useEscape } from "./ui.js";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
var useFeedbackList = (opts) => {
|
|
9
|
+
const api = useApi();
|
|
10
|
+
const [posts, setPosts] = useState([]);
|
|
11
|
+
const [boards, setBoards] = useState([]);
|
|
12
|
+
const [loading, setLoading] = useState(true);
|
|
13
|
+
const [error, setError] = useState("");
|
|
14
|
+
const [tick, setTick] = useState(0);
|
|
15
|
+
const { board, sort, q } = opts;
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const ctl = new AbortController();
|
|
18
|
+
setLoading(true);
|
|
19
|
+
const wait = window.setTimeout(
|
|
20
|
+
() => {
|
|
21
|
+
api.list({ board, sort, q: q?.trim() || void 0 }, ctl.signal).then((r) => {
|
|
22
|
+
setPosts(r.posts);
|
|
23
|
+
setBoards(r.boards);
|
|
24
|
+
setError("");
|
|
25
|
+
setLoading(false);
|
|
26
|
+
}).catch((e) => {
|
|
27
|
+
if (e.name === "AbortError") return;
|
|
28
|
+
setError(e.message);
|
|
29
|
+
setLoading(false);
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
q ? 250 : 0
|
|
33
|
+
);
|
|
34
|
+
return () => {
|
|
35
|
+
window.clearTimeout(wait);
|
|
36
|
+
ctl.abort();
|
|
37
|
+
};
|
|
38
|
+
}, [api, board, sort, q, tick]);
|
|
39
|
+
const patch = useCallback((id, fn) => setPosts((all) => all.map((p) => p.id === id ? fn(p) : p)), []);
|
|
40
|
+
const vote = useCallback(
|
|
41
|
+
(post) => {
|
|
42
|
+
const flip = (p) => ({ ...p, voted: !p.voted, votes: p.votes + (p.voted ? -1 : 1) });
|
|
43
|
+
patch(post.id, flip);
|
|
44
|
+
api.vote(post.id).then((r) => patch(post.id, (p) => ({ ...p, voted: r.voted, votes: r.votes }))).catch((e) => {
|
|
45
|
+
patch(post.id, flip);
|
|
46
|
+
setError(`Your vote was not saved. ${e.message}`);
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
[api, patch]
|
|
50
|
+
);
|
|
51
|
+
return { api, posts, setPosts, patch, boards, loading, error, setError, vote, reload: () => setTick((t) => t + 1) };
|
|
52
|
+
};
|
|
53
|
+
var SubmitForm = ({
|
|
54
|
+
api,
|
|
55
|
+
boards,
|
|
56
|
+
posts,
|
|
57
|
+
onCreated,
|
|
58
|
+
onVote,
|
|
59
|
+
onCancel,
|
|
60
|
+
compact
|
|
61
|
+
}) => {
|
|
62
|
+
const id = useId();
|
|
63
|
+
const [title, setTitle] = useState("");
|
|
64
|
+
const [body, setBody] = useState("");
|
|
65
|
+
const [board, setBoard] = useState("");
|
|
66
|
+
const [busy, setBusy] = useState(false);
|
|
67
|
+
const [error, setError] = useState("");
|
|
68
|
+
const chosen = board || boards[0]?.id || "";
|
|
69
|
+
const dupes = similarPosts(title, posts);
|
|
70
|
+
const submit = async (e) => {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
if (!title.trim() || busy) return;
|
|
73
|
+
setBusy(true);
|
|
74
|
+
setError("");
|
|
75
|
+
try {
|
|
76
|
+
const created = await api.submit({ title: title.trim(), body: body.trim() || void 0, board: chosen });
|
|
77
|
+
setTitle("");
|
|
78
|
+
setBody("");
|
|
79
|
+
onCreated(created);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
setError(err.message);
|
|
82
|
+
} finally {
|
|
83
|
+
setBusy(false);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
return /* @__PURE__ */ jsxs("form", { className: "ckw-form", onSubmit: submit, children: [
|
|
87
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-field", children: [
|
|
88
|
+
/* @__PURE__ */ jsx("label", { className: "ckw-label", htmlFor: `${id}-t`, children: "Title" }),
|
|
89
|
+
/* @__PURE__ */ jsx("input", { id: `${id}-t`, className: "ckw-input", value: title, maxLength: 140, required: true, autoFocus: true, onChange: (e) => setTitle(e.target.value), placeholder: "Short, specific summary" })
|
|
90
|
+
] }),
|
|
91
|
+
dupes.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ckw-dupes", role: "status", children: [
|
|
92
|
+
/* @__PURE__ */ jsx("p", { children: "Similar requests. Vote instead of adding a duplicate:" }),
|
|
93
|
+
dupes.map((d) => /* @__PURE__ */ jsxs("div", { className: "ckw-dupe", children: [
|
|
94
|
+
/* @__PURE__ */ jsx("span", { title: d.title, children: d.title }),
|
|
95
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ckw-btn", disabled: d.voted, onClick: () => onVote(d), children: d.voted ? "Voted" : "Vote instead" })
|
|
96
|
+
] }, d.id))
|
|
97
|
+
] }),
|
|
98
|
+
!compact && /* @__PURE__ */ jsxs("div", { className: "ckw-field", children: [
|
|
99
|
+
/* @__PURE__ */ jsx("label", { className: "ckw-label", htmlFor: `${id}-b`, children: "Details (optional)" }),
|
|
100
|
+
/* @__PURE__ */ jsx("textarea", { id: `${id}-b`, className: "ckw-textarea", value: body, maxLength: 2e3, onChange: (e) => setBody(e.target.value), placeholder: "What are you trying to do?" })
|
|
101
|
+
] }),
|
|
102
|
+
boards.length > 1 && /* @__PURE__ */ jsxs("div", { className: "ckw-field", children: [
|
|
103
|
+
/* @__PURE__ */ jsx("label", { className: "ckw-label", htmlFor: `${id}-c`, children: "Board" }),
|
|
104
|
+
/* @__PURE__ */ jsx("select", { id: `${id}-c`, className: "ckw-select", value: chosen, onChange: (e) => setBoard(e.target.value), children: boards.map((b) => /* @__PURE__ */ jsx("option", { value: b.id, children: b.label }, b.id)) })
|
|
105
|
+
] }),
|
|
106
|
+
error && /* @__PURE__ */ jsx("div", { className: "ckw-error", role: "alert", children: error }),
|
|
107
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-actions", children: [
|
|
108
|
+
onCancel && /* @__PURE__ */ jsx("button", { type: "button", className: "ckw-btn ckw-btn-ghost", onClick: onCancel, children: "Cancel" }),
|
|
109
|
+
/* @__PURE__ */ jsx("button", { type: "submit", className: "ckw-btn ckw-btn-primary", disabled: busy || !title.trim(), children: busy ? "Sending\u2026" : "Submit request" })
|
|
110
|
+
] })
|
|
111
|
+
] });
|
|
112
|
+
};
|
|
113
|
+
var PostRow = ({ post, onOpen, onVote }) => /* @__PURE__ */ jsxs("li", { className: "ckw-row", children: [
|
|
114
|
+
/* @__PURE__ */ jsx(VoteButton, { post, onVote }),
|
|
115
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-main", children: [
|
|
116
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ckw-link", onClick: () => onOpen(post), children: post.title }),
|
|
117
|
+
post.body && /* @__PURE__ */ jsx("p", { className: "ckw-excerpt", children: post.body }),
|
|
118
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-meta", children: [
|
|
119
|
+
/* @__PURE__ */ jsx(StatusPill, { status: post.status }),
|
|
120
|
+
/* @__PURE__ */ jsxs("span", { className: "ckw-stat", children: [
|
|
121
|
+
/* @__PURE__ */ jsx(Chat, {}),
|
|
122
|
+
" ",
|
|
123
|
+
post.comments
|
|
124
|
+
] }),
|
|
125
|
+
/* @__PURE__ */ jsx("span", { children: timeAgo(post.createdAt) })
|
|
126
|
+
] })
|
|
127
|
+
] })
|
|
128
|
+
] });
|
|
129
|
+
var Skeleton = () => /* @__PURE__ */ jsx("div", { "aria-hidden": "true", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { className: "ckw-skel" }, i)) });
|
|
130
|
+
var ErrorBar = ({ message, onRetry, onClose }) => /* @__PURE__ */ jsxs("div", { className: "ckw-error", role: "alert", children: [
|
|
131
|
+
/* @__PURE__ */ jsx("span", { children: message }),
|
|
132
|
+
onRetry ? /* @__PURE__ */ jsx("button", { type: "button", className: "ckw-btn", onClick: onRetry, children: "Retry" }) : onClose && /* @__PURE__ */ jsx("button", { type: "button", className: "ckw-btn ckw-btn-ghost", onClick: onClose, "aria-label": "Dismiss", children: /* @__PURE__ */ jsx(Cross, {}) })
|
|
133
|
+
] });
|
|
134
|
+
var PostDetail = ({ post, onBack, onVote }) => {
|
|
135
|
+
const api = useApi();
|
|
136
|
+
const id = useId();
|
|
137
|
+
const [comments, setComments] = useState(null);
|
|
138
|
+
const [error, setError] = useState("");
|
|
139
|
+
const [text, setText] = useState("");
|
|
140
|
+
const [busy, setBusy] = useState(false);
|
|
141
|
+
const [added, setAdded] = useState(0);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
const ctl = new AbortController();
|
|
144
|
+
api.comments(post.id, ctl.signal).then((r) => setComments(r.comments)).catch((e) => e.name !== "AbortError" && setError(e.message));
|
|
145
|
+
return () => ctl.abort();
|
|
146
|
+
}, [api, post.id]);
|
|
147
|
+
const send = async (e) => {
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
const body = text.trim();
|
|
150
|
+
if (!body || busy) return;
|
|
151
|
+
setBusy(true);
|
|
152
|
+
setError("");
|
|
153
|
+
const temp = { id: `tmp-${Date.now()}`, body, author: "You", isTeam: false, postedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
154
|
+
setComments((c) => [...c ?? [], temp]);
|
|
155
|
+
setText("");
|
|
156
|
+
try {
|
|
157
|
+
const real = await api.comment(post.id, body);
|
|
158
|
+
setComments((c) => (c ?? []).map((x) => x.id === temp.id ? real : x));
|
|
159
|
+
setAdded((n) => n + 1);
|
|
160
|
+
} catch (err) {
|
|
161
|
+
setComments((c) => (c ?? []).filter((x) => x.id !== temp.id));
|
|
162
|
+
setText(body);
|
|
163
|
+
setError(err.message);
|
|
164
|
+
} finally {
|
|
165
|
+
setBusy(false);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
169
|
+
/* @__PURE__ */ jsxs("button", { type: "button", className: "ckw-btn ckw-btn-ghost ckw-back", onClick: onBack, children: [
|
|
170
|
+
/* @__PURE__ */ jsx("span", { style: { display: "inline-block", transform: "rotate(-90deg)", lineHeight: 0 }, children: /* @__PURE__ */ jsx(ChevronUp, {}) }),
|
|
171
|
+
"All requests"
|
|
172
|
+
] }),
|
|
173
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-detail-head", children: [
|
|
174
|
+
/* @__PURE__ */ jsx(VoteButton, { post, onVote }),
|
|
175
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-main", children: [
|
|
176
|
+
/* @__PURE__ */ jsx("h2", { children: post.title }),
|
|
177
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-meta", style: { marginTop: 0 }, children: [
|
|
178
|
+
/* @__PURE__ */ jsx(StatusPill, { status: post.status }),
|
|
179
|
+
/* @__PURE__ */ jsx("span", { children: timeAgo(post.createdAt) })
|
|
180
|
+
] })
|
|
181
|
+
] })
|
|
182
|
+
] }),
|
|
183
|
+
post.body && /* @__PURE__ */ jsx("p", { className: "ckw-body", children: post.body }),
|
|
184
|
+
/* @__PURE__ */ jsxs("h3", { className: "ckw-h", children: [
|
|
185
|
+
"Comments (",
|
|
186
|
+
Math.max(post.comments + added, comments?.length ?? 0),
|
|
187
|
+
")"
|
|
188
|
+
] }),
|
|
189
|
+
error && /* @__PURE__ */ jsx(ErrorBar, { message: error, onClose: () => setError("") }),
|
|
190
|
+
/* @__PURE__ */ jsxs("div", { "aria-live": "polite", children: [
|
|
191
|
+
comments === null && !error && /* @__PURE__ */ jsx(Skeleton, {}),
|
|
192
|
+
comments?.length === 0 && /* @__PURE__ */ jsx("p", { className: "ckw-sub", children: "No comments yet. Be the first." }),
|
|
193
|
+
comments?.map((c) => /* @__PURE__ */ jsxs("div", { className: "ckw-comment", children: [
|
|
194
|
+
/* @__PURE__ */ jsx("b", { children: c.author }),
|
|
195
|
+
c.isTeam && /* @__PURE__ */ jsx("span", { className: "ckw-team", children: "Team" }),
|
|
196
|
+
/* @__PURE__ */ jsx("span", { className: "ckw-time", children: timeAgo(c.postedAt) }),
|
|
197
|
+
/* @__PURE__ */ jsx("p", { children: c.body })
|
|
198
|
+
] }, c.id))
|
|
199
|
+
] }),
|
|
200
|
+
/* @__PURE__ */ jsxs("form", { className: "ckw-form", onSubmit: send, style: { marginTop: 12 }, children: [
|
|
201
|
+
/* @__PURE__ */ jsx("label", { className: "ckw-label", htmlFor: id, children: "Add a comment" }),
|
|
202
|
+
/* @__PURE__ */ jsx("textarea", { id, className: "ckw-textarea", value: text, maxLength: 2e3, onChange: (e) => setText(e.target.value) }),
|
|
203
|
+
/* @__PURE__ */ jsx("div", { className: "ckw-actions", children: /* @__PURE__ */ jsx("button", { type: "submit", className: "ckw-btn ckw-btn-primary", disabled: busy || !text.trim(), children: "Comment" }) })
|
|
204
|
+
] })
|
|
205
|
+
] });
|
|
206
|
+
};
|
|
207
|
+
var Roadmap = ({ onOpen }) => {
|
|
208
|
+
const api = useApi();
|
|
209
|
+
const [data, setData] = useState(null);
|
|
210
|
+
const [error, setError] = useState("");
|
|
211
|
+
const [tick, setTick] = useState(0);
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
const ctl = new AbortController();
|
|
214
|
+
api.roadmap(ctl.signal).then((r) => (setData(r), setError(""))).catch((e) => e.name !== "AbortError" && setError(e.message));
|
|
215
|
+
return () => ctl.abort();
|
|
216
|
+
}, [api, tick]);
|
|
217
|
+
if (error) return /* @__PURE__ */ jsx(ErrorBar, { message: error, onRetry: () => setTick((t) => t + 1) });
|
|
218
|
+
if (!data) return /* @__PURE__ */ jsx(Skeleton, {});
|
|
219
|
+
const cols = [
|
|
220
|
+
["planned", "Planned"],
|
|
221
|
+
["progress", "In progress"],
|
|
222
|
+
["shipped", "Shipped"]
|
|
223
|
+
];
|
|
224
|
+
return /* @__PURE__ */ jsx("div", { className: "ckw-cols", children: cols.map(([k, label]) => /* @__PURE__ */ jsxs("section", { className: "ckw-col", "aria-label": label, children: [
|
|
225
|
+
/* @__PURE__ */ jsxs("h3", { children: [
|
|
226
|
+
label,
|
|
227
|
+
" ",
|
|
228
|
+
/* @__PURE__ */ jsx("span", { children: data.columns[k].length })
|
|
229
|
+
] }),
|
|
230
|
+
data.columns[k].length === 0 && /* @__PURE__ */ jsx("p", { className: "ckw-sub", children: "Nothing here yet." }),
|
|
231
|
+
data.columns[k].map((p) => /* @__PURE__ */ jsxs("div", { className: "ckw-card", children: [
|
|
232
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ckw-link", onClick: () => onOpen(p), children: p.title }),
|
|
233
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-meta", style: { marginTop: 0 }, children: [
|
|
234
|
+
/* @__PURE__ */ jsxs("span", { className: "ckw-stat", children: [
|
|
235
|
+
/* @__PURE__ */ jsx(ChevronUp, {}),
|
|
236
|
+
" ",
|
|
237
|
+
p.votes
|
|
238
|
+
] }),
|
|
239
|
+
/* @__PURE__ */ jsxs("span", { className: "ckw-stat", children: [
|
|
240
|
+
/* @__PURE__ */ jsx(Chat, {}),
|
|
241
|
+
" ",
|
|
242
|
+
p.comments
|
|
243
|
+
] })
|
|
244
|
+
] })
|
|
245
|
+
] }, p.id))
|
|
246
|
+
] }, k)) });
|
|
247
|
+
};
|
|
248
|
+
var Changelog = () => {
|
|
249
|
+
const api = useApi();
|
|
250
|
+
const [entries, setEntries] = useState(null);
|
|
251
|
+
const [error, setError] = useState("");
|
|
252
|
+
const [tick, setTick] = useState(0);
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
const ctl = new AbortController();
|
|
255
|
+
api.changelog(ctl.signal).then((r) => (setEntries(r.entries), setError(""))).catch((e) => e.name !== "AbortError" && setError(e.message));
|
|
256
|
+
return () => ctl.abort();
|
|
257
|
+
}, [api, tick]);
|
|
258
|
+
if (error) return /* @__PURE__ */ jsx(ErrorBar, { message: error, onRetry: () => setTick((t) => t + 1) });
|
|
259
|
+
if (!entries) return /* @__PURE__ */ jsx(Skeleton, {});
|
|
260
|
+
if (entries.length === 0) return /* @__PURE__ */ jsx("div", { className: "ckw-empty", children: "No updates published yet." });
|
|
261
|
+
return /* @__PURE__ */ jsx("div", { children: entries.map((e) => /* @__PURE__ */ jsxs("article", { className: "ckw-entry", children: [
|
|
262
|
+
/* @__PURE__ */ jsx("time", { dateTime: e.publishedAt, children: new Date(e.publishedAt).toLocaleDateString(void 0, { month: "long", day: "numeric", year: "numeric" }) }),
|
|
263
|
+
/* @__PURE__ */ jsx("h3", { children: e.title }),
|
|
264
|
+
/* @__PURE__ */ jsx("p", { children: e.body })
|
|
265
|
+
] }, e.id)) });
|
|
266
|
+
};
|
|
267
|
+
var FeedbackBoard = ({ title = "Feedback", subtitle = "Tell us what to build next and vote on what matters to you.", theme, accent }) => {
|
|
268
|
+
const [tab, setTab] = useState("requests");
|
|
269
|
+
const [sort, setSort] = useState("top");
|
|
270
|
+
const [board, setBoard] = useState();
|
|
271
|
+
const [q, setQ] = useState("");
|
|
272
|
+
const [creating, setCreating] = useState(false);
|
|
273
|
+
const [opened, setOpened] = useState(null);
|
|
274
|
+
const list = useFeedbackList({ board, sort, q });
|
|
275
|
+
const { posts, setPosts, vote } = list;
|
|
276
|
+
const current = opened && (posts.find((p) => p.id === opened.id) ?? opened);
|
|
277
|
+
const voteOpened = (p) => {
|
|
278
|
+
if (!posts.some((x) => x.id === p.id)) {
|
|
279
|
+
setOpened((o) => o ? { ...o, voted: !o.voted, votes: o.votes + (o.voted ? -1 : 1) } : o);
|
|
280
|
+
list.api.vote(p.id).then((r) => setOpened((o) => o ? { ...o, voted: r.voted, votes: r.votes } : o)).catch((e) => {
|
|
281
|
+
setOpened((o) => o ? { ...o, voted: p.voted, votes: p.votes } : o);
|
|
282
|
+
list.setError(`Your vote was not saved. ${e.message}`);
|
|
283
|
+
});
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
vote(p);
|
|
287
|
+
};
|
|
288
|
+
const tabs = [
|
|
289
|
+
["requests", "Requests"],
|
|
290
|
+
["roadmap", "Roadmap"],
|
|
291
|
+
["changelog", "Changelog"]
|
|
292
|
+
];
|
|
293
|
+
const onTabKey = (e) => {
|
|
294
|
+
const i = tabs.findIndex(([k]) => k === tab);
|
|
295
|
+
const n = e.key === "ArrowRight" ? i + 1 : e.key === "ArrowLeft" ? i - 1 : null;
|
|
296
|
+
if (n === null) return;
|
|
297
|
+
e.preventDefault();
|
|
298
|
+
setTab(tabs[(n + tabs.length) % tabs.length][0]);
|
|
299
|
+
setOpened(null);
|
|
300
|
+
};
|
|
301
|
+
return /* @__PURE__ */ jsxs(Root, { theme, accent, className: "ckw-board", children: [
|
|
302
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-head", children: [
|
|
303
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
304
|
+
/* @__PURE__ */ jsx("h2", { className: "ckw-title", children: title }),
|
|
305
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "ckw-sub", children: subtitle })
|
|
306
|
+
] }),
|
|
307
|
+
/* @__PURE__ */ jsxs("button", { type: "button", className: "ckw-btn ckw-btn-primary", onClick: () => (setCreating((c) => !c), setOpened(null), setTab("requests")), "aria-expanded": creating, children: [
|
|
308
|
+
/* @__PURE__ */ jsx(Plus, {}),
|
|
309
|
+
" Request a feature"
|
|
310
|
+
] })
|
|
311
|
+
] }),
|
|
312
|
+
/* @__PURE__ */ jsx("div", { className: "ckw-tabs", role: "tablist", "aria-label": "Feedback sections", onKeyDown: onTabKey, children: tabs.map(([k, label]) => /* @__PURE__ */ jsx(
|
|
313
|
+
"button",
|
|
314
|
+
{
|
|
315
|
+
type: "button",
|
|
316
|
+
role: "tab",
|
|
317
|
+
id: `ckw-tab-${k}`,
|
|
318
|
+
"aria-selected": tab === k,
|
|
319
|
+
tabIndex: tab === k ? 0 : -1,
|
|
320
|
+
className: "ckw-tab",
|
|
321
|
+
onClick: () => (setTab(k), setOpened(null)),
|
|
322
|
+
children: label
|
|
323
|
+
},
|
|
324
|
+
k
|
|
325
|
+
)) }),
|
|
326
|
+
/* @__PURE__ */ jsxs("div", { role: "tabpanel", "aria-labelledby": `ckw-tab-${tab}`, children: [
|
|
327
|
+
list.error && /* @__PURE__ */ jsx(ErrorBar, { message: list.error, onRetry: tab === "requests" && !current ? list.reload : void 0, onClose: () => list.setError("") }),
|
|
328
|
+
current ? /* @__PURE__ */ jsx(PostDetail, { post: current, onBack: () => setOpened(null), onVote: voteOpened }) : tab === "requests" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
329
|
+
creating && /* @__PURE__ */ jsx(
|
|
330
|
+
SubmitForm,
|
|
331
|
+
{
|
|
332
|
+
api: list.api,
|
|
333
|
+
boards: list.boards,
|
|
334
|
+
posts,
|
|
335
|
+
onVote: vote,
|
|
336
|
+
onCancel: () => setCreating(false),
|
|
337
|
+
onCreated: (p) => {
|
|
338
|
+
setPosts((all) => [p, ...all]);
|
|
339
|
+
setCreating(false);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
),
|
|
343
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-tools", children: [
|
|
344
|
+
/* @__PURE__ */ jsx("input", { className: "ckw-input ckw-search", type: "search", "aria-label": "Search requests", placeholder: "Search requests", value: q, onChange: (e) => setQ(e.target.value) }),
|
|
345
|
+
/* @__PURE__ */ jsx("div", { className: "ckw-seg", role: "group", "aria-label": "Sort", children: ["top", "new"].map((s) => /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": sort === s, onClick: () => setSort(s), children: s === "top" ? "Top" : "New" }, s)) })
|
|
346
|
+
] }),
|
|
347
|
+
list.boards.length > 1 && /* @__PURE__ */ jsxs("div", { className: "ckw-chips", role: "group", "aria-label": "Boards", children: [
|
|
348
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ckw-chip", "aria-pressed": !board, onClick: () => setBoard(void 0), children: "All" }),
|
|
349
|
+
list.boards.map((b) => /* @__PURE__ */ jsx("button", { type: "button", className: "ckw-chip", "aria-pressed": board === b.id, onClick: () => setBoard(b.id), children: b.label }, b.id))
|
|
350
|
+
] }),
|
|
351
|
+
/* @__PURE__ */ jsx("div", { "aria-live": "polite", "aria-busy": list.loading, children: list.loading && posts.length === 0 ? /* @__PURE__ */ jsx(Skeleton, {}) : posts.length === 0 && !list.error ? /* @__PURE__ */ jsx("div", { className: "ckw-empty", children: q ? "No requests match your search." : "No requests yet. Be the first to suggest something." }) : /* @__PURE__ */ jsx("ul", { className: "ckw-list", children: posts.map((p) => /* @__PURE__ */ jsx(PostRow, { post: p, onOpen: setOpened, onVote: vote }, p.id)) }) })
|
|
352
|
+
] }) : tab === "roadmap" ? /* @__PURE__ */ jsx(Roadmap, { onOpen: setOpened }) : /* @__PURE__ */ jsx(Changelog, {})
|
|
353
|
+
] })
|
|
354
|
+
] });
|
|
355
|
+
};
|
|
356
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
357
|
+
var widgetOpen = false;
|
|
358
|
+
var setWidgetOpen = (v) => {
|
|
359
|
+
widgetOpen = v;
|
|
360
|
+
listeners.forEach((l) => l(v));
|
|
361
|
+
};
|
|
362
|
+
var useFeedback = () => ({ open: () => setWidgetOpen(true), close: () => setWidgetOpen(false) });
|
|
363
|
+
var WidgetPanel = ({ boardUrl, onClose }) => {
|
|
364
|
+
const [suggesting, setSuggesting] = useState(false);
|
|
365
|
+
const [thanks, setThanks] = useState(false);
|
|
366
|
+
const list = useFeedbackList({ sort: "top" });
|
|
367
|
+
const { posts, setPosts } = list;
|
|
368
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
369
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-pop-head", children: [
|
|
370
|
+
/* @__PURE__ */ jsx("span", { children: suggesting ? "Suggest something" : "Top requests" }),
|
|
371
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ckw-btn ckw-btn-ghost", onClick: onClose, "aria-label": "Close feedback", children: /* @__PURE__ */ jsx(Cross, {}) })
|
|
372
|
+
] }),
|
|
373
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-pop-body", children: [
|
|
374
|
+
list.error && /* @__PURE__ */ jsx(ErrorBar, { message: list.error, onClose: () => list.setError("") }),
|
|
375
|
+
thanks ? /* @__PURE__ */ jsx("div", { className: "ckw-empty", role: "status", children: "Thanks, your request is in." }) : suggesting ? /* @__PURE__ */ jsx(
|
|
376
|
+
SubmitForm,
|
|
377
|
+
{
|
|
378
|
+
compact: true,
|
|
379
|
+
api: list.api,
|
|
380
|
+
boards: list.boards,
|
|
381
|
+
posts,
|
|
382
|
+
onVote: list.vote,
|
|
383
|
+
onCancel: () => setSuggesting(false),
|
|
384
|
+
onCreated: (p) => {
|
|
385
|
+
setPosts((all) => [p, ...all]);
|
|
386
|
+
setSuggesting(false);
|
|
387
|
+
setThanks(true);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
) : list.loading && posts.length === 0 ? /* @__PURE__ */ jsx(Skeleton, {}) : posts.length === 0 && !list.error ? /* @__PURE__ */ jsx("div", { className: "ckw-empty", children: "No requests yet. Suggest the first one." }) : /* @__PURE__ */ jsx("ul", { className: "ckw-list", "aria-live": "polite", children: posts.slice(0, 5).map((p) => /* @__PURE__ */ jsxs("li", { className: "ckw-row", children: [
|
|
391
|
+
/* @__PURE__ */ jsx(VoteButton, { post: p, onVote: list.vote }),
|
|
392
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-main", children: [
|
|
393
|
+
/* @__PURE__ */ jsx("span", { style: { fontWeight: 600, overflowWrap: "anywhere" }, children: p.title }),
|
|
394
|
+
/* @__PURE__ */ jsx("div", { className: "ckw-meta", children: /* @__PURE__ */ jsx(StatusPill, { status: p.status }) })
|
|
395
|
+
] })
|
|
396
|
+
] }, p.id)) })
|
|
397
|
+
] }),
|
|
398
|
+
/* @__PURE__ */ jsxs("div", { className: "ckw-pop-foot", children: [
|
|
399
|
+
boardUrl ? /* @__PURE__ */ jsx("a", { href: boardUrl, children: "See all" }) : /* @__PURE__ */ jsx("span", {}),
|
|
400
|
+
!suggesting && !thanks && /* @__PURE__ */ jsxs("button", { type: "button", className: "ckw-btn ckw-btn-primary", onClick: () => setSuggesting(true), children: [
|
|
401
|
+
/* @__PURE__ */ jsx(Plus, {}),
|
|
402
|
+
" Suggest something"
|
|
403
|
+
] }),
|
|
404
|
+
thanks && /* @__PURE__ */ jsx("button", { type: "button", className: "ckw-btn", onClick: () => setThanks(false), children: "Back" })
|
|
405
|
+
] })
|
|
406
|
+
] });
|
|
407
|
+
};
|
|
408
|
+
var FeedbackWidget = ({ position = "bottom-right", label = "Feedback", boardUrl, theme, accent }) => {
|
|
409
|
+
const [open, setOpen] = useState(false);
|
|
410
|
+
const fab = useRef(null);
|
|
411
|
+
const pop = useRef(null);
|
|
412
|
+
useEffect(() => {
|
|
413
|
+
const l = (v) => setOpen(v);
|
|
414
|
+
listeners.add(l);
|
|
415
|
+
setOpen(widgetOpen);
|
|
416
|
+
const click = (e) => {
|
|
417
|
+
if (e.target?.closest("[data-ck-feedback]")) {
|
|
418
|
+
e.preventDefault();
|
|
419
|
+
setWidgetOpen(true);
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
document.addEventListener("click", click);
|
|
423
|
+
return () => {
|
|
424
|
+
listeners.delete(l);
|
|
425
|
+
document.removeEventListener("click", click);
|
|
426
|
+
};
|
|
427
|
+
}, []);
|
|
428
|
+
const close = useCallback(() => {
|
|
429
|
+
setWidgetOpen(false);
|
|
430
|
+
fab.current?.focus();
|
|
431
|
+
}, []);
|
|
432
|
+
useEscape(() => open && close());
|
|
433
|
+
useEffect(() => {
|
|
434
|
+
if (open) pop.current?.querySelector("button, input")?.focus();
|
|
435
|
+
}, [open]);
|
|
436
|
+
const trap = (e) => {
|
|
437
|
+
if (e.key !== "Tab" || !pop.current) return;
|
|
438
|
+
const f = [...pop.current.querySelectorAll("a[href], button:not([disabled]), input, select, textarea")];
|
|
439
|
+
if (!f.length) return;
|
|
440
|
+
const first = f[0];
|
|
441
|
+
const last = f[f.length - 1];
|
|
442
|
+
if (e.shiftKey && document.activeElement === first) e.preventDefault(), last.focus();
|
|
443
|
+
else if (!e.shiftKey && document.activeElement === last) e.preventDefault(), first.focus();
|
|
444
|
+
};
|
|
445
|
+
return /* @__PURE__ */ jsxs(Root, { theme, accent, children: [
|
|
446
|
+
/* @__PURE__ */ jsxs("button", { ref: fab, type: "button", className: "ckw-fab", "data-pos": position, "aria-haspopup": "dialog", "aria-expanded": open, onClick: () => open ? close() : setWidgetOpen(true), children: [
|
|
447
|
+
/* @__PURE__ */ jsx(Chat, {}),
|
|
448
|
+
" ",
|
|
449
|
+
label
|
|
450
|
+
] }),
|
|
451
|
+
open && /* @__PURE__ */ jsx("div", { ref: pop, className: "ckw-pop", "data-pos": position, role: "dialog", "aria-label": "Feedback", onKeyDown: trap, children: /* @__PURE__ */ jsx(WidgetPanel, { boardUrl, onClose: close }) })
|
|
452
|
+
] });
|
|
453
|
+
};
|
|
454
|
+
export {
|
|
455
|
+
FeedbackBoard,
|
|
456
|
+
FeedbackWidget,
|
|
457
|
+
useFeedback
|
|
458
|
+
};
|
|
459
|
+
//# sourceMappingURL=feedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/feedback.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FormEvent, useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport { type ChangelogEntry, type FeedbackBoardInfo, type FeedbackComment, type FeedbackPost, type FeedbackRoadmap, useApi } from \"./api\";\nimport { Chat, ChevronUp, Cross, Plus, Root, StatusPill, type Theme, VoteButton, similarPosts, timeAgo, useEscape } from \"./ui\";\n\n/** Loads the post list and owns optimistic voting, so the board and the widget behave the same. */\nconst useFeedbackList = (opts: { board?: string; sort?: \"top\" | \"new\"; q?: string; limit?: number }) => {\n const api = useApi();\n const [posts, setPosts] = useState<FeedbackPost[]>([]);\n const [boards, setBoards] = useState<FeedbackBoardInfo[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState(\"\");\n const [tick, setTick] = useState(0);\n const { board, sort, q } = opts;\n\n useEffect(() => {\n const ctl = new AbortController();\n setLoading(true);\n const wait = window.setTimeout(\n () => {\n api.list({ board, sort, q: q?.trim() || undefined }, ctl.signal)\n .then((r) => {\n setPosts(r.posts);\n setBoards(r.boards);\n setError(\"\");\n setLoading(false);\n })\n .catch((e: Error) => {\n if (e.name === \"AbortError\") return;\n setError(e.message);\n setLoading(false);\n });\n },\n q ? 250 : 0,\n );\n return () => {\n window.clearTimeout(wait);\n ctl.abort();\n };\n }, [api, board, sort, q, tick]);\n\n const patch = useCallback((id: string, fn: (p: FeedbackPost) => FeedbackPost) => setPosts((all) => all.map((p) => (p.id === id ? fn(p) : p))), []);\n\n /** Flips the vote at once, then confirms with the server; on failure it flips back. */\n const vote = useCallback(\n (post: FeedbackPost) => {\n const flip = (p: FeedbackPost) => ({ ...p, voted: !p.voted, votes: p.votes + (p.voted ? -1 : 1) });\n patch(post.id, flip);\n api.vote(post.id)\n .then((r) => patch(post.id, (p) => ({ ...p, voted: r.voted, votes: r.votes })))\n .catch((e: Error) => {\n patch(post.id, flip);\n setError(`Your vote was not saved. ${e.message}`);\n });\n },\n [api, patch],\n );\n\n return { api, posts, setPosts, patch, boards, loading, error, setError, vote, reload: () => setTick((t) => t + 1) };\n};\n\n/** Title, details and board, with up to three similar requests to vote on instead. */\nconst SubmitForm = ({\n api,\n boards,\n posts,\n onCreated,\n onVote,\n onCancel,\n compact,\n}: {\n api: ReturnType<typeof useApi>;\n boards: FeedbackBoardInfo[];\n posts: FeedbackPost[];\n onCreated: (p: FeedbackPost) => void;\n onVote: (p: FeedbackPost) => void;\n onCancel?: () => void;\n compact?: boolean;\n}) => {\n const id = useId();\n const [title, setTitle] = useState(\"\");\n const [body, setBody] = useState(\"\");\n const [board, setBoard] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState(\"\");\n const chosen = board || boards[0]?.id || \"\";\n const dupes = similarPosts(title, posts);\n\n const submit = async (e: FormEvent) => {\n e.preventDefault();\n if (!title.trim() || busy) return;\n setBusy(true);\n setError(\"\");\n try {\n const created = await api.submit({ title: title.trim(), body: body.trim() || undefined, board: chosen });\n setTitle(\"\");\n setBody(\"\");\n onCreated(created);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <form className=\"ckw-form\" onSubmit={submit}>\n <div className=\"ckw-field\">\n <label className=\"ckw-label\" htmlFor={`${id}-t`}>\n Title\n </label>\n <input id={`${id}-t`} className=\"ckw-input\" value={title} maxLength={140} required autoFocus onChange={(e) => setTitle(e.target.value)} placeholder=\"Short, specific summary\" />\n </div>\n {dupes.length > 0 && (\n <div className=\"ckw-dupes\" role=\"status\">\n <p>Similar requests. Vote instead of adding a duplicate:</p>\n {dupes.map((d) => (\n <div className=\"ckw-dupe\" key={d.id}>\n <span title={d.title}>{d.title}</span>\n <button type=\"button\" className=\"ckw-btn\" disabled={d.voted} onClick={() => onVote(d)}>\n {d.voted ? \"Voted\" : \"Vote instead\"}\n </button>\n </div>\n ))}\n </div>\n )}\n {!compact && (\n <div className=\"ckw-field\">\n <label className=\"ckw-label\" htmlFor={`${id}-b`}>\n Details (optional)\n </label>\n <textarea id={`${id}-b`} className=\"ckw-textarea\" value={body} maxLength={2000} onChange={(e) => setBody(e.target.value)} placeholder=\"What are you trying to do?\" />\n </div>\n )}\n {boards.length > 1 && (\n <div className=\"ckw-field\">\n <label className=\"ckw-label\" htmlFor={`${id}-c`}>\n Board\n </label>\n <select id={`${id}-c`} className=\"ckw-select\" value={chosen} onChange={(e) => setBoard(e.target.value)}>\n {boards.map((b) => (\n <option key={b.id} value={b.id}>\n {b.label}\n </option>\n ))}\n </select>\n </div>\n )}\n {error && (\n <div className=\"ckw-error\" role=\"alert\">\n {error}\n </div>\n )}\n <div className=\"ckw-actions\">\n {onCancel && (\n <button type=\"button\" className=\"ckw-btn ckw-btn-ghost\" onClick={onCancel}>\n Cancel\n </button>\n )}\n <button type=\"submit\" className=\"ckw-btn ckw-btn-primary\" disabled={busy || !title.trim()}>\n {busy ? \"Sending…\" : \"Submit request\"}\n </button>\n </div>\n </form>\n );\n};\n\nconst PostRow = ({ post, onOpen, onVote }: { post: FeedbackPost; onOpen: (p: FeedbackPost) => void; onVote: (p: FeedbackPost) => void }) => (\n <li className=\"ckw-row\">\n <VoteButton post={post} onVote={onVote} />\n <div className=\"ckw-main\">\n <button type=\"button\" className=\"ckw-link\" onClick={() => onOpen(post)}>\n {post.title}\n </button>\n {post.body && <p className=\"ckw-excerpt\">{post.body}</p>}\n <div className=\"ckw-meta\">\n <StatusPill status={post.status} />\n <span className=\"ckw-stat\">\n <Chat /> {post.comments}\n </span>\n <span>{timeAgo(post.createdAt)}</span>\n </div>\n </div>\n </li>\n);\n\nconst Skeleton = () => (\n <div aria-hidden=\"true\">\n {[0, 1, 2].map((i) => (\n <div className=\"ckw-skel\" key={i} />\n ))}\n </div>\n);\n\nconst ErrorBar = ({ message, onRetry, onClose }: { message: string; onRetry?: () => void; onClose?: () => void }) => (\n <div className=\"ckw-error\" role=\"alert\">\n <span>{message}</span>\n {onRetry ? (\n <button type=\"button\" className=\"ckw-btn\" onClick={onRetry}>\n Retry\n </button>\n ) : (\n onClose && (\n <button type=\"button\" className=\"ckw-btn ckw-btn-ghost\" onClick={onClose} aria-label=\"Dismiss\">\n <Cross />\n </button>\n )\n )}\n </div>\n);\n\nconst PostDetail = ({ post, onBack, onVote }: { post: FeedbackPost; onBack: () => void; onVote: (p: FeedbackPost) => void }) => {\n const api = useApi();\n const id = useId();\n const [comments, setComments] = useState<FeedbackComment[] | null>(null);\n const [error, setError] = useState(\"\");\n const [text, setText] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [added, setAdded] = useState(0);\n\n useEffect(() => {\n const ctl = new AbortController();\n api.comments(post.id, ctl.signal)\n .then((r) => setComments(r.comments))\n .catch((e: Error) => e.name !== \"AbortError\" && setError(e.message));\n return () => ctl.abort();\n }, [api, post.id]);\n\n const send = async (e: FormEvent) => {\n e.preventDefault();\n const body = text.trim();\n if (!body || busy) return;\n setBusy(true);\n setError(\"\");\n // Show it at once; take it back if the server says no.\n const temp: FeedbackComment = { id: `tmp-${Date.now()}`, body, author: \"You\", isTeam: false, postedAt: new Date().toISOString() };\n setComments((c) => [...(c ?? []), temp]);\n setText(\"\");\n try {\n const real = await api.comment(post.id, body);\n setComments((c) => (c ?? []).map((x) => (x.id === temp.id ? real : x)));\n setAdded((n) => n + 1);\n } catch (err) {\n setComments((c) => (c ?? []).filter((x) => x.id !== temp.id));\n setText(body);\n setError((err as Error).message);\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <div>\n <button type=\"button\" className=\"ckw-btn ckw-btn-ghost ckw-back\" onClick={onBack}>\n <span style={{ display: \"inline-block\", transform: \"rotate(-90deg)\", lineHeight: 0 }}>\n <ChevronUp />\n </span>\n All requests\n </button>\n <div className=\"ckw-detail-head\">\n <VoteButton post={post} onVote={onVote} />\n <div className=\"ckw-main\">\n <h2>{post.title}</h2>\n <div className=\"ckw-meta\" style={{ marginTop: 0 }}>\n <StatusPill status={post.status} />\n <span>{timeAgo(post.createdAt)}</span>\n </div>\n </div>\n </div>\n {post.body && <p className=\"ckw-body\">{post.body}</p>}\n <h3 className=\"ckw-h\">Comments ({Math.max(post.comments + added, comments?.length ?? 0)})</h3>\n {error && <ErrorBar message={error} onClose={() => setError(\"\")} />}\n <div aria-live=\"polite\">\n {comments === null && !error && <Skeleton />}\n {comments?.length === 0 && <p className=\"ckw-sub\">No comments yet. Be the first.</p>}\n {comments?.map((c) => (\n <div className=\"ckw-comment\" key={c.id}>\n <b>{c.author}</b>\n {c.isTeam && <span className=\"ckw-team\">Team</span>}\n <span className=\"ckw-time\">{timeAgo(c.postedAt)}</span>\n <p>{c.body}</p>\n </div>\n ))}\n </div>\n <form className=\"ckw-form\" onSubmit={send} style={{ marginTop: 12 }}>\n <label className=\"ckw-label\" htmlFor={id}>\n Add a comment\n </label>\n <textarea id={id} className=\"ckw-textarea\" value={text} maxLength={2000} onChange={(e) => setText(e.target.value)} />\n <div className=\"ckw-actions\">\n <button type=\"submit\" className=\"ckw-btn ckw-btn-primary\" disabled={busy || !text.trim()}>\n Comment\n </button>\n </div>\n </form>\n </div>\n );\n};\n\ntype Tab = \"requests\" | \"roadmap\" | \"changelog\";\n\nconst Roadmap = ({ onOpen }: { onOpen: (p: FeedbackPost) => void }) => {\n const api = useApi();\n const [data, setData] = useState<FeedbackRoadmap | null>(null);\n const [error, setError] = useState(\"\");\n const [tick, setTick] = useState(0);\n useEffect(() => {\n const ctl = new AbortController();\n api.roadmap(ctl.signal)\n .then((r) => (setData(r), setError(\"\")))\n .catch((e: Error) => e.name !== \"AbortError\" && setError(e.message));\n return () => ctl.abort();\n }, [api, tick]);\n if (error) return <ErrorBar message={error} onRetry={() => setTick((t) => t + 1)} />;\n if (!data) return <Skeleton />;\n const cols: [keyof FeedbackRoadmap[\"columns\"], string][] = [\n [\"planned\", \"Planned\"],\n [\"progress\", \"In progress\"],\n [\"shipped\", \"Shipped\"],\n ];\n return (\n <div className=\"ckw-cols\">\n {cols.map(([k, label]) => (\n <section className=\"ckw-col\" key={k} aria-label={label}>\n <h3>\n {label} <span>{data.columns[k].length}</span>\n </h3>\n {data.columns[k].length === 0 && <p className=\"ckw-sub\">Nothing here yet.</p>}\n {data.columns[k].map((p) => (\n <div className=\"ckw-card\" key={p.id}>\n <button type=\"button\" className=\"ckw-link\" onClick={() => onOpen(p)}>\n {p.title}\n </button>\n <div className=\"ckw-meta\" style={{ marginTop: 0 }}>\n <span className=\"ckw-stat\">\n <ChevronUp /> {p.votes}\n </span>\n <span className=\"ckw-stat\">\n <Chat /> {p.comments}\n </span>\n </div>\n </div>\n ))}\n </section>\n ))}\n </div>\n );\n};\n\nconst Changelog = () => {\n const api = useApi();\n const [entries, setEntries] = useState<ChangelogEntry[] | null>(null);\n const [error, setError] = useState(\"\");\n const [tick, setTick] = useState(0);\n useEffect(() => {\n const ctl = new AbortController();\n api.changelog(ctl.signal)\n .then((r) => (setEntries(r.entries), setError(\"\")))\n .catch((e: Error) => e.name !== \"AbortError\" && setError(e.message));\n return () => ctl.abort();\n }, [api, tick]);\n if (error) return <ErrorBar message={error} onRetry={() => setTick((t) => t + 1)} />;\n if (!entries) return <Skeleton />;\n if (entries.length === 0) return <div className=\"ckw-empty\">No updates published yet.</div>;\n return (\n <div>\n {entries.map((e) => (\n <article className=\"ckw-entry\" key={e.id}>\n <time dateTime={e.publishedAt}>{new Date(e.publishedAt).toLocaleDateString(undefined, { month: \"long\", day: \"numeric\", year: \"numeric\" })}</time>\n <h3>{e.title}</h3>\n <p>{e.body}</p>\n </article>\n ))}\n </div>\n );\n};\n\nexport interface FeedbackBoardProps {\n /** Heading above the board. */\n title?: string;\n /** Line under the heading. */\n subtitle?: string;\n theme?: Theme;\n /** Accent color for buttons and votes. Default #3f76ff. */\n accent?: string;\n}\n\n/** The full board for a /feedback page: requests, roadmap and changelog, with voting, comments and a submit form. */\nexport const FeedbackBoard = ({ title = \"Feedback\", subtitle = \"Tell us what to build next and vote on what matters to you.\", theme, accent }: FeedbackBoardProps) => {\n const [tab, setTab] = useState<Tab>(\"requests\");\n const [sort, setSort] = useState<\"top\" | \"new\">(\"top\");\n const [board, setBoard] = useState<string | undefined>();\n const [q, setQ] = useState(\"\");\n const [creating, setCreating] = useState(false);\n const [opened, setOpened] = useState<FeedbackPost | null>(null);\n const list = useFeedbackList({ board, sort, q });\n const { posts, setPosts, vote } = list;\n\n // The open post follows the list's optimistic votes when it is in the list.\n const current = opened && (posts.find((p) => p.id === opened.id) ?? opened);\n const voteOpened = (p: FeedbackPost) => {\n if (!posts.some((x) => x.id === p.id)) {\n // Opened from the roadmap: it is not in this list, so vote through the list's server call and mirror it locally.\n setOpened((o) => (o ? { ...o, voted: !o.voted, votes: o.votes + (o.voted ? -1 : 1) } : o));\n list.api\n .vote(p.id)\n .then((r) => setOpened((o) => (o ? { ...o, voted: r.voted, votes: r.votes } : o)))\n .catch((e: Error) => {\n setOpened((o) => (o ? { ...o, voted: p.voted, votes: p.votes } : o));\n list.setError(`Your vote was not saved. ${e.message}`);\n });\n return;\n }\n vote(p);\n };\n\n const tabs: [Tab, string][] = [\n [\"requests\", \"Requests\"],\n [\"roadmap\", \"Roadmap\"],\n [\"changelog\", \"Changelog\"],\n ];\n const onTabKey = (e: React.KeyboardEvent) => {\n const i = tabs.findIndex(([k]) => k === tab);\n const n = e.key === \"ArrowRight\" ? i + 1 : e.key === \"ArrowLeft\" ? i - 1 : null;\n if (n === null) return;\n e.preventDefault();\n setTab(tabs[(n + tabs.length) % tabs.length][0]);\n setOpened(null);\n };\n\n return (\n <Root theme={theme} accent={accent} className=\"ckw-board\">\n <div className=\"ckw-head\">\n <div>\n <h2 className=\"ckw-title\">{title}</h2>\n {subtitle && <p className=\"ckw-sub\">{subtitle}</p>}\n </div>\n <button type=\"button\" className=\"ckw-btn ckw-btn-primary\" onClick={() => (setCreating((c) => !c), setOpened(null), setTab(\"requests\"))} aria-expanded={creating}>\n <Plus /> Request a feature\n </button>\n </div>\n\n <div className=\"ckw-tabs\" role=\"tablist\" aria-label=\"Feedback sections\" onKeyDown={onTabKey}>\n {tabs.map(([k, label]) => (\n <button\n key={k}\n type=\"button\"\n role=\"tab\"\n id={`ckw-tab-${k}`}\n aria-selected={tab === k}\n tabIndex={tab === k ? 0 : -1}\n className=\"ckw-tab\"\n onClick={() => (setTab(k), setOpened(null))}\n >\n {label}\n </button>\n ))}\n </div>\n\n <div role=\"tabpanel\" aria-labelledby={`ckw-tab-${tab}`}>\n {list.error && <ErrorBar message={list.error} onRetry={tab === \"requests\" && !current ? list.reload : undefined} onClose={() => list.setError(\"\")} />}\n\n {current ? (\n <PostDetail post={current} onBack={() => setOpened(null)} onVote={voteOpened} />\n ) : tab === \"requests\" ? (\n <>\n {creating && (\n <SubmitForm\n api={list.api}\n boards={list.boards}\n posts={posts}\n onVote={vote}\n onCancel={() => setCreating(false)}\n onCreated={(p) => {\n setPosts((all) => [p, ...all]);\n setCreating(false);\n }}\n />\n )}\n <div className=\"ckw-tools\">\n <input className=\"ckw-input ckw-search\" type=\"search\" aria-label=\"Search requests\" placeholder=\"Search requests\" value={q} onChange={(e) => setQ(e.target.value)} />\n <div className=\"ckw-seg\" role=\"group\" aria-label=\"Sort\">\n {([\"top\", \"new\"] as const).map((s) => (\n <button key={s} type=\"button\" aria-pressed={sort === s} onClick={() => setSort(s)}>\n {s === \"top\" ? \"Top\" : \"New\"}\n </button>\n ))}\n </div>\n </div>\n {list.boards.length > 1 && (\n <div className=\"ckw-chips\" role=\"group\" aria-label=\"Boards\">\n <button type=\"button\" className=\"ckw-chip\" aria-pressed={!board} onClick={() => setBoard(undefined)}>\n All\n </button>\n {list.boards.map((b) => (\n <button key={b.id} type=\"button\" className=\"ckw-chip\" aria-pressed={board === b.id} onClick={() => setBoard(b.id)}>\n {b.label}\n </button>\n ))}\n </div>\n )}\n <div aria-live=\"polite\" aria-busy={list.loading}>\n {list.loading && posts.length === 0 ? (\n <Skeleton />\n ) : posts.length === 0 && !list.error ? (\n <div className=\"ckw-empty\">{q ? \"No requests match your search.\" : \"No requests yet. Be the first to suggest something.\"}</div>\n ) : (\n <ul className=\"ckw-list\">\n {posts.map((p) => (\n <PostRow key={p.id} post={p} onOpen={setOpened} onVote={vote} />\n ))}\n </ul>\n )}\n </div>\n </>\n ) : tab === \"roadmap\" ? (\n <Roadmap onOpen={setOpened} />\n ) : (\n <Changelog />\n )}\n </div>\n </Root>\n );\n};\n\n// ---------- Floating widget ----------\n\nconst listeners = new Set<(open: boolean) => void>();\nlet widgetOpen = false;\nconst setWidgetOpen = (v: boolean) => {\n widgetOpen = v;\n listeners.forEach((l) => l(v));\n};\n\n/** Open or close the feedback widget from your own button. */\nexport const useFeedback = () => ({ open: () => setWidgetOpen(true), close: () => setWidgetOpen(false) });\n\nexport interface FeedbackWidgetProps {\n position?: \"bottom-right\" | \"bottom-left\";\n /** Text on the floating button. */\n label?: string;\n /** Where \"See all\" goes, for example your /feedback page. */\n boardUrl?: string;\n theme?: Theme;\n accent?: string;\n}\n\n/** The popover content. Mounted only while open, so a closed widget costs no request. */\nconst WidgetPanel = ({ boardUrl, onClose }: { boardUrl?: string; onClose: () => void }) => {\n const [suggesting, setSuggesting] = useState(false);\n const [thanks, setThanks] = useState(false);\n const list = useFeedbackList({ sort: \"top\" });\n const { posts, setPosts } = list;\n return (\n <>\n <div className=\"ckw-pop-head\">\n <span>{suggesting ? \"Suggest something\" : \"Top requests\"}</span>\n <button type=\"button\" className=\"ckw-btn ckw-btn-ghost\" onClick={onClose} aria-label=\"Close feedback\">\n <Cross />\n </button>\n </div>\n <div className=\"ckw-pop-body\">\n {list.error && <ErrorBar message={list.error} onClose={() => list.setError(\"\")} />}\n {thanks ? (\n <div className=\"ckw-empty\" role=\"status\">\n Thanks, your request is in.\n </div>\n ) : suggesting ? (\n <SubmitForm\n compact\n api={list.api}\n boards={list.boards}\n posts={posts}\n onVote={list.vote}\n onCancel={() => setSuggesting(false)}\n onCreated={(p) => {\n setPosts((all) => [p, ...all]);\n setSuggesting(false);\n setThanks(true);\n }}\n />\n ) : list.loading && posts.length === 0 ? (\n <Skeleton />\n ) : posts.length === 0 && !list.error ? (\n <div className=\"ckw-empty\">No requests yet. Suggest the first one.</div>\n ) : (\n <ul className=\"ckw-list\" aria-live=\"polite\">\n {posts.slice(0, 5).map((p) => (\n <li className=\"ckw-row\" key={p.id}>\n <VoteButton post={p} onVote={list.vote} />\n <div className=\"ckw-main\">\n <span style={{ fontWeight: 600, overflowWrap: \"anywhere\" }}>{p.title}</span>\n <div className=\"ckw-meta\">\n <StatusPill status={p.status} />\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n <div className=\"ckw-pop-foot\">\n {boardUrl ? <a href={boardUrl}>See all</a> : <span />}\n {!suggesting && !thanks && (\n <button type=\"button\" className=\"ckw-btn ckw-btn-primary\" onClick={() => setSuggesting(true)}>\n <Plus /> Suggest something\n </button>\n )}\n {thanks && (\n <button type=\"button\" className=\"ckw-btn\" onClick={() => setThanks(false)}>\n Back\n </button>\n )}\n </div>\n </>\n );\n};\n\n/** A floating button that opens a small popover with the top requests and a quick suggest form. */\nexport const FeedbackWidget = ({ position = \"bottom-right\", label = \"Feedback\", boardUrl, theme, accent }: FeedbackWidgetProps) => {\n const [open, setOpen] = useState(false);\n const fab = useRef<HTMLButtonElement>(null);\n const pop = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const l = (v: boolean) => setOpen(v);\n listeners.add(l);\n setOpen(widgetOpen);\n const click = (e: MouseEvent) => {\n if ((e.target as HTMLElement | null)?.closest(\"[data-ck-feedback]\")) {\n e.preventDefault();\n setWidgetOpen(true);\n }\n };\n document.addEventListener(\"click\", click);\n return () => {\n listeners.delete(l);\n document.removeEventListener(\"click\", click);\n };\n }, []);\n\n const close = useCallback(() => {\n setWidgetOpen(false);\n fab.current?.focus();\n }, []);\n useEscape(() => open && close());\n\n // Move focus into the popover when it opens.\n useEffect(() => {\n if (open) pop.current?.querySelector<HTMLElement>(\"button, input\")?.focus();\n }, [open]);\n\n // Keep Tab inside the popover while it is open.\n const trap = (e: React.KeyboardEvent) => {\n if (e.key !== \"Tab\" || !pop.current) return;\n const f = [...pop.current.querySelectorAll<HTMLElement>(\"a[href], button:not([disabled]), input, select, textarea\")];\n if (!f.length) return;\n const first = f[0];\n const last = f[f.length - 1];\n if (e.shiftKey && document.activeElement === first) (e.preventDefault(), last.focus());\n else if (!e.shiftKey && document.activeElement === last) (e.preventDefault(), first.focus());\n };\n\n return (\n <Root theme={theme} accent={accent}>\n <button ref={fab} type=\"button\" className=\"ckw-fab\" data-pos={position} aria-haspopup=\"dialog\" aria-expanded={open} onClick={() => (open ? close() : setWidgetOpen(true))}>\n <Chat /> {label}\n </button>\n {open && (\n <div ref={pop} className=\"ckw-pop\" data-pos={position} role=\"dialog\" aria-label=\"Feedback\" onKeyDown={trap}>\n <WidgetPanel boardUrl={boardUrl} onClose={close} />\n </div>\n )}\n </Root>\n );\n};\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { type ChangelogEntry, type CockpitProviderProps, type CockpitUser, type FeedbackBoardInfo, type FeedbackBoardProps, type FeedbackComment, type FeedbackList, type FeedbackPost, type FeedbackRoadmap, type FeedbackWidgetProps, type PostStatus, type SurveyAnswer, type SurveyDefinition, type SurveyQuestion, type SurveyQuestionKind, CockpitProvider, Experiment, FeedbackBoard, FeedbackWidget, Survey, Text, WIDGET_CSS, createApi, useCockpit, useFeedback, useStyles, useVariant, } from "./browser.js";
|
|
2
|
+
export { type CockpitConfig, type CockpitEvent, type ConsentChoice, type ConsentMode, type ReplayConfig, type ExperimentConfig, type VariantConfig, type VariantId, EMBED_PARAM, ID_COOKIE, PREVIEW_PARAM, SESSION_COOKIE, assign, parsePreview, previewHref, } from "./core.js";
|
|
3
|
+
export { type AttributionMode, type CheckoutReferral, type Referral, REFERRAL_COOKIE, getReferral, readReferralCookie, referralForCheckout, referralLink, } from "./referral.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
CockpitProvider,
|
|
4
|
+
Experiment,
|
|
5
|
+
FeedbackBoard,
|
|
6
|
+
FeedbackWidget,
|
|
7
|
+
Survey,
|
|
8
|
+
Text,
|
|
9
|
+
WIDGET_CSS,
|
|
10
|
+
createApi,
|
|
11
|
+
useCockpit,
|
|
12
|
+
useFeedback,
|
|
13
|
+
useStyles,
|
|
14
|
+
useVariant
|
|
15
|
+
} from "./browser.js";
|
|
16
|
+
import {
|
|
17
|
+
EMBED_PARAM,
|
|
18
|
+
ID_COOKIE,
|
|
19
|
+
PREVIEW_PARAM,
|
|
20
|
+
SESSION_COOKIE,
|
|
21
|
+
assign,
|
|
22
|
+
parsePreview,
|
|
23
|
+
previewHref
|
|
24
|
+
} from "./core.js";
|
|
25
|
+
import {
|
|
26
|
+
REFERRAL_COOKIE,
|
|
27
|
+
getReferral,
|
|
28
|
+
readReferralCookie,
|
|
29
|
+
referralForCheckout,
|
|
30
|
+
referralLink
|
|
31
|
+
} from "./referral.js";
|
|
32
|
+
export {
|
|
33
|
+
CockpitProvider,
|
|
34
|
+
EMBED_PARAM,
|
|
35
|
+
Experiment,
|
|
36
|
+
FeedbackBoard,
|
|
37
|
+
FeedbackWidget,
|
|
38
|
+
ID_COOKIE,
|
|
39
|
+
PREVIEW_PARAM,
|
|
40
|
+
REFERRAL_COOKIE,
|
|
41
|
+
SESSION_COOKIE,
|
|
42
|
+
Survey,
|
|
43
|
+
Text,
|
|
44
|
+
WIDGET_CSS,
|
|
45
|
+
assign,
|
|
46
|
+
createApi,
|
|
47
|
+
getReferral,
|
|
48
|
+
parsePreview,
|
|
49
|
+
previewHref,
|
|
50
|
+
readReferralCookie,
|
|
51
|
+
referralForCheckout,
|
|
52
|
+
referralLink,
|
|
53
|
+
useCockpit,
|
|
54
|
+
useFeedback,
|
|
55
|
+
useStyles,
|
|
56
|
+
useVariant
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n type ChangelogEntry,\n type CockpitProviderProps,\n type CockpitUser,\n type FeedbackBoardInfo,\n type FeedbackBoardProps,\n type FeedbackComment,\n type FeedbackList,\n type FeedbackPost,\n type FeedbackRoadmap,\n type FeedbackWidgetProps,\n type PostStatus,\n type SurveyAnswer,\n type SurveyDefinition,\n type SurveyQuestion,\n type SurveyQuestionKind,\n CockpitProvider,\n Experiment,\n FeedbackBoard,\n FeedbackWidget,\n Survey,\n Text,\n WIDGET_CSS,\n createApi,\n useCockpit,\n useFeedback,\n useStyles,\n useVariant,\n} from \"./browser\";\nexport {\n type CockpitConfig,\n type CockpitEvent,\n type ConsentChoice,\n type ConsentMode,\n type ReplayConfig,\n type ExperimentConfig,\n type VariantConfig,\n type VariantId,\n EMBED_PARAM,\n ID_COOKIE,\n PREVIEW_PARAM,\n SESSION_COOKIE,\n assign,\n parsePreview,\n previewHref,\n} from \"./core\";\nexport {\n type AttributionMode,\n type CheckoutReferral,\n type Referral,\n REFERRAL_COOKIE,\n getReferral,\n readReferralCookie,\n referralForCheckout,\n referralLink,\n} from \"./referral\";\n"],"mappings":";AAAA;AAAA,EAgBI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP;AAAA,EASI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP;AAAA,EAII;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;","names":[]}
|