@flanksource/clicky-ui 0.3.30 → 0.3.31
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/comments/CommentCard.cjs +110 -262
- package/dist/comments/CommentCard.cjs.map +1 -1
- package/dist/comments/CommentCard.d.ts +3 -1
- package/dist/comments/CommentCard.d.ts.map +1 -1
- package/dist/comments/CommentCard.js +105 -257
- package/dist/comments/CommentCard.js.map +1 -1
- package/dist/comments/CommentCardParts.cjs +95 -0
- package/dist/comments/CommentCardParts.cjs.map +1 -0
- package/dist/comments/CommentCardParts.d.ts +17 -0
- package/dist/comments/CommentCardParts.d.ts.map +1 -0
- package/dist/comments/CommentCardParts.js +95 -0
- package/dist/comments/CommentCardParts.js.map +1 -0
- package/dist/comments/CommentSidePanel.cjs +14 -7
- package/dist/comments/CommentSidePanel.cjs.map +1 -1
- package/dist/comments/CommentSidePanel.d.ts +3 -1
- package/dist/comments/CommentSidePanel.d.ts.map +1 -1
- package/dist/comments/CommentSidePanel.js +14 -7
- package/dist/comments/CommentSidePanel.js.map +1 -1
- package/dist/comments/CommentThread.cjs +2 -0
- package/dist/comments/CommentThread.cjs.map +1 -1
- package/dist/comments/CommentThread.d.ts +2 -1
- package/dist/comments/CommentThread.d.ts.map +1 -1
- package/dist/comments/CommentThread.js +2 -0
- package/dist/comments/CommentThread.js.map +1 -1
- package/dist/comments/CommentThreadList.cjs +50 -34
- package/dist/comments/CommentThreadList.cjs.map +1 -1
- package/dist/comments/CommentThreadList.d.ts +3 -1
- package/dist/comments/CommentThreadList.d.ts.map +1 -1
- package/dist/comments/CommentThreadList.js +50 -34
- package/dist/comments/CommentThreadList.js.map +1 -1
- package/dist/components/ComboboxMenu.cjs +4 -1
- package/dist/components/ComboboxMenu.cjs.map +1 -1
- package/dist/components/ComboboxMenu.d.ts.map +1 -1
- package/dist/components/ComboboxMenu.js +5 -2
- package/dist/components/ComboboxMenu.js.map +1 -1
- package/dist/components/ComboboxTags.cjs +5 -1
- package/dist/components/ComboboxTags.cjs.map +1 -1
- package/dist/components/ComboboxTags.d.ts.map +1 -1
- package/dist/components/ComboboxTags.js +6 -2
- package/dist/components/ComboboxTags.js.map +1 -1
- package/dist/components/ConnectionLoggingPolicy.cjs +24 -35
- package/dist/components/ConnectionLoggingPolicy.cjs.map +1 -1
- package/dist/components/ConnectionLoggingPolicy.d.ts.map +1 -1
- package/dist/components/ConnectionLoggingPolicy.js +24 -35
- package/dist/components/ConnectionLoggingPolicy.js.map +1 -1
- package/dist/components/MultiSelect.cjs.map +1 -1
- package/dist/components/MultiSelect.d.ts +7 -0
- package/dist/components/MultiSelect.d.ts.map +1 -1
- package/dist/components/MultiSelect.js.map +1 -1
- package/dist/components/filter-bar-multi.cjs +10 -1
- package/dist/components/filter-bar-multi.cjs.map +1 -1
- package/dist/components/filter-bar-multi.d.ts.map +1 -1
- package/dist/components/filter-bar-multi.js +11 -2
- package/dist/components/filter-bar-multi.js.map +1 -1
- package/dist/data/ai/SessionViewer.model.cjs +23 -8
- package/dist/data/ai/SessionViewer.model.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.model.d.ts +8 -0
- package/dist/data/ai/SessionViewer.model.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.model.js +23 -8
- package/dist/data/ai/SessionViewer.model.js.map +1 -1
- package/dist/data/ai/SessionViewer.rows.cjs +9 -1
- package/dist/data/ai/SessionViewer.rows.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.rows.js +9 -1
- package/dist/data/ai/SessionViewer.rows.js.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.cjs +24 -2
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.cjs.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.d.ts.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.js +25 -3
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.js.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/summaries.cjs.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/summaries.d.ts.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/summaries.js.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor.model.cjs +4 -0
- package/dist/data/ai/SpecRuntimeEditor.model.cjs.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor.model.d.ts +4 -0
- package/dist/data/ai/SpecRuntimeEditor.model.d.ts.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor.model.js +4 -0
- package/dist/data/ai/SpecRuntimeEditor.model.js.map +1 -1
- package/dist/data/test-runner/TestRunner.cjs +6 -4
- package/dist/data/test-runner/TestRunner.cjs.map +1 -1
- package/dist/data/test-runner/TestRunner.d.ts +10 -1
- package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
- package/dist/data/test-runner/TestRunner.js +6 -4
- package/dist/data/test-runner/TestRunner.js.map +1 -1
- package/dist/data/verification/VerificationResults.cjs +99 -0
- package/dist/data/verification/VerificationResults.cjs.map +1 -0
- package/dist/data/verification/VerificationResults.d.ts +27 -0
- package/dist/data/verification/VerificationResults.d.ts.map +1 -0
- package/dist/data/verification/VerificationResults.js +99 -0
- package/dist/data/verification/VerificationResults.js.map +1 -0
- package/dist/data/verification/adapterViews.cjs +116 -0
- package/dist/data/verification/adapterViews.cjs.map +1 -0
- package/dist/data/verification/adapterViews.d.ts +13 -0
- package/dist/data/verification/adapterViews.d.ts.map +1 -0
- package/dist/data/verification/adapterViews.js +116 -0
- package/dist/data/verification/adapterViews.js.map +1 -0
- package/dist/data/verification/adapters.cjs +26 -0
- package/dist/data/verification/adapters.cjs.map +1 -0
- package/dist/data/verification/adapters.d.ts +6 -0
- package/dist/data/verification/adapters.d.ts.map +1 -0
- package/dist/data/verification/adapters.js +26 -0
- package/dist/data/verification/adapters.js.map +1 -0
- package/dist/data/verification/index.d.ts +5 -0
- package/dist/data/verification/index.d.ts.map +1 -0
- package/dist/data/verification/verify-report.cjs +58 -0
- package/dist/data/verification/verify-report.cjs.map +1 -0
- package/dist/data/verification/verify-report.d.ts +96 -0
- package/dist/data/verification/verify-report.d.ts.map +1 -0
- package/dist/data/verification/verify-report.js +58 -0
- package/dist/data/verification/verify-report.js.map +1 -0
- package/dist/data/verification/verifyReportTests.cjs +83 -0
- package/dist/data/verification/verifyReportTests.cjs.map +1 -0
- package/dist/data/verification/verifyReportTests.d.ts +13 -0
- package/dist/data/verification/verifyReportTests.d.ts.map +1 -0
- package/dist/data/verification/verifyReportTests.js +83 -0
- package/dist/data/verification/verifyReportTests.js.map +1 -0
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/data.cjs +10 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +1 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +10 -0
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +10 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -3,20 +3,19 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const react = require("react");
|
|
5
5
|
const utils = require("../lib/utils.cjs");
|
|
6
|
-
const Badge = require("../data/Badge.cjs");
|
|
7
6
|
const Icon = require("../data/Icon.cjs");
|
|
8
7
|
const DropdownMenu = require("../overlay/DropdownMenu.cjs");
|
|
9
8
|
const timestampFormat = require("../data/cells/timestamp-format.cjs");
|
|
10
9
|
const CommentAuthor = require("./CommentAuthor.cjs");
|
|
10
|
+
const CommentCardParts = require("./CommentCardParts.cjs");
|
|
11
11
|
const CommentMarkdown = require("./CommentMarkdown.cjs");
|
|
12
12
|
const commentUtils = require("./comment-utils.cjs");
|
|
13
|
-
const UiCheck = require("../icons/components/UiCheck.cjs");
|
|
14
|
-
const UiFullscreen = require("../icons/components/UiFullscreen.cjs");
|
|
15
13
|
const UiDotsVertical = require("../icons/components/UiDotsVertical.cjs");
|
|
16
|
-
const UiThumbsUp = require("../icons/components/UiThumbsUp.cjs");
|
|
17
|
-
const UiThumbsDown = require("../icons/components/UiThumbsDown.cjs");
|
|
18
|
-
const UiClose = require("../icons/components/UiClose.cjs");
|
|
19
14
|
const UiTrash = require("../icons/components/UiTrash.cjs");
|
|
15
|
+
const UiCheck = require("../icons/components/UiCheck.cjs");
|
|
16
|
+
const UiRestart = require("../icons/components/UiRestart.cjs");
|
|
17
|
+
const UiCopy = require("../icons/components/UiCopy.cjs");
|
|
18
|
+
const UiFullscreen = require("../icons/components/UiFullscreen.cjs");
|
|
20
19
|
function completionStatus(config) {
|
|
21
20
|
for (const completionName of ["resolved", "closed"]) {
|
|
22
21
|
const status = config.statuses.find(
|
|
@@ -26,204 +25,82 @@ function completionStatus(config) {
|
|
|
26
25
|
}
|
|
27
26
|
return void 0;
|
|
28
27
|
}
|
|
29
|
-
function
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
function RatingChip({ rating }) {
|
|
33
|
-
if (!rating) return null;
|
|
34
|
-
const positive = rating === "positive";
|
|
35
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36
|
-
Badge.Badge,
|
|
37
|
-
{
|
|
38
|
-
variant: "soft",
|
|
39
|
-
tone: positive ? "success" : "danger",
|
|
40
|
-
size: "xs",
|
|
41
|
-
icon: positive ? UiThumbsUp.UiThumbsUp : UiThumbsDown.UiThumbsDown,
|
|
42
|
-
children: positive ? "Positive rating" : "Negative rating"
|
|
43
|
-
}
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
function StatusChip({
|
|
47
|
-
status,
|
|
48
|
-
config,
|
|
49
|
-
showLabel
|
|
50
|
-
}) {
|
|
51
|
-
const cfg = commentUtils.resolveStatusConfig(config, status);
|
|
52
|
-
if (!cfg) return null;
|
|
53
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
54
|
-
Badge.Badge,
|
|
55
|
-
{
|
|
56
|
-
variant: "soft",
|
|
57
|
-
tone: commentUtils.toneToBadgeTone(cfg.tone),
|
|
58
|
-
size: "xs",
|
|
59
|
-
...cfg.icon ? { icon: cfg.icon } : {},
|
|
60
|
-
children: showLabel ? cfg.label : null
|
|
61
|
-
}
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
function FacetBadges({
|
|
65
|
-
comment,
|
|
66
|
-
config,
|
|
67
|
-
compact
|
|
68
|
-
}) {
|
|
69
|
-
const facets = config.facets ?? [];
|
|
70
|
-
const chips = facets.flatMap((facet) => {
|
|
71
|
-
var _a;
|
|
72
|
-
const value = (_a = comment.facets) == null ? void 0 : _a[facet.key];
|
|
73
|
-
const option = commentUtils.resolveFacetOption(facet, value);
|
|
74
|
-
if (!option) return [];
|
|
75
|
-
return [
|
|
76
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
77
|
-
Badge.Badge,
|
|
78
|
-
{
|
|
79
|
-
variant: "soft",
|
|
80
|
-
tone: commentUtils.toneToBadgeTone(option.tone),
|
|
81
|
-
size: "xs",
|
|
82
|
-
children: compact ? option.short ?? option.label : option.label
|
|
83
|
-
},
|
|
84
|
-
facet.key
|
|
85
|
-
)
|
|
86
|
-
];
|
|
87
|
-
});
|
|
88
|
-
return chips.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: chips }) : null;
|
|
28
|
+
function reopenStatus(config) {
|
|
29
|
+
return config.statuses.find((status) => status.unresolved);
|
|
89
30
|
}
|
|
90
|
-
function
|
|
91
|
-
comment,
|
|
92
|
-
onChecklistToggle
|
|
93
|
-
}) {
|
|
94
|
-
if (!comment.checklist || comment.checklist.length === 0) return null;
|
|
95
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: comment.checklist.map((item, i) => {
|
|
96
|
-
const done = /done|complete|resolved/i.test(item.status);
|
|
97
|
-
const active = /progress|doing/i.test(item.status);
|
|
98
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
99
|
-
"button",
|
|
100
|
-
{
|
|
101
|
-
type: "button",
|
|
102
|
-
onClick: () => onChecklistToggle == null ? void 0 : onChecklistToggle(i),
|
|
103
|
-
disabled: !onChecklistToggle,
|
|
104
|
-
className: utils.cn(
|
|
105
|
-
"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] transition-opacity",
|
|
106
|
-
onChecklistToggle && "cursor-pointer hover:opacity-80",
|
|
107
|
-
done ? "bg-green-100 text-green-800 line-through dark:bg-green-500/20 dark:text-green-300" : active ? "bg-yellow-100 text-yellow-800 dark:bg-yellow-500/20 dark:text-yellow-300" : "bg-muted text-muted-foreground"
|
|
108
|
-
),
|
|
109
|
-
children: item.label
|
|
110
|
-
},
|
|
111
|
-
i
|
|
112
|
-
);
|
|
113
|
-
}) });
|
|
114
|
-
}
|
|
115
|
-
function RefChips({ comment }) {
|
|
116
|
-
if (!comment.refs || comment.refs.length === 0) return null;
|
|
117
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5", children: comment.refs.map((group, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
118
|
-
"span",
|
|
119
|
-
{
|
|
120
|
-
className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground",
|
|
121
|
-
children: [
|
|
122
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
|
|
123
|
-
group.label,
|
|
124
|
-
":"
|
|
125
|
-
] }),
|
|
126
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: group.mono ? "font-mono" : void 0, children: group.items.join(", ") })
|
|
127
|
-
]
|
|
128
|
-
},
|
|
129
|
-
i
|
|
130
|
-
)) });
|
|
131
|
-
}
|
|
132
|
-
function statusMenuItems(comment, config, isReply, opts) {
|
|
31
|
+
function actionMenuItems(isReply, opts) {
|
|
133
32
|
const items = [];
|
|
33
|
+
if (opts.onDelete) {
|
|
34
|
+
items.push({ label: "Delete", icon: UiTrash.UiTrash, onSelect: opts.onDelete });
|
|
35
|
+
}
|
|
36
|
+
if (!isReply && opts.onToggleStatus && opts.toggleStatusLabel) {
|
|
37
|
+
items.push({
|
|
38
|
+
label: opts.toggleStatusLabel,
|
|
39
|
+
icon: opts.toggleStatusLabel === "Resolve" ? UiCheck.UiCheck : UiRestart.UiRestart,
|
|
40
|
+
onSelect: opts.onToggleStatus
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (opts.onCopy) {
|
|
44
|
+
items.push({ label: "Copy", icon: UiCopy.UiCopy, onSelect: opts.onCopy });
|
|
45
|
+
}
|
|
134
46
|
if (opts.onMaximize) {
|
|
135
47
|
items.push({
|
|
136
|
-
label: "
|
|
48
|
+
label: "Maximise",
|
|
137
49
|
icon: UiFullscreen.UiFullscreen,
|
|
138
50
|
onSelect: opts.onMaximize
|
|
139
51
|
});
|
|
140
52
|
}
|
|
141
|
-
if (opts.onCollapse) {
|
|
142
|
-
items.push({ label: "Collapse", icon: UiClose.UiClose, onSelect: opts.onCollapse });
|
|
143
|
-
}
|
|
144
|
-
if (!isReply && opts.onUpdateStatus) {
|
|
145
|
-
for (const status of config.statuses) {
|
|
146
|
-
if (status.value === opts.completionStatus) continue;
|
|
147
|
-
const current = status.value === comment.status;
|
|
148
|
-
items.push({
|
|
149
|
-
label: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
150
|
-
"span",
|
|
151
|
-
{
|
|
152
|
-
className: utils.cn(
|
|
153
|
-
"inline-flex items-center gap-1.5",
|
|
154
|
-
current && "font-semibold"
|
|
155
|
-
),
|
|
156
|
-
children: [
|
|
157
|
-
/* @__PURE__ */ jsxRuntime.jsx(StatusChip, { status: status.value, config }),
|
|
158
|
-
status.label
|
|
159
|
-
]
|
|
160
|
-
}
|
|
161
|
-
),
|
|
162
|
-
onSelect: () => {
|
|
163
|
-
var _a;
|
|
164
|
-
return (_a = opts.onUpdateStatus) == null ? void 0 : _a.call(opts, status.value);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if (opts.onDelete) {
|
|
170
|
-
items.push({ label: "Delete", icon: UiTrash.UiTrash, onSelect: opts.onDelete });
|
|
171
|
-
}
|
|
172
53
|
return items;
|
|
173
54
|
}
|
|
174
|
-
function IconAction({
|
|
175
|
-
icon,
|
|
176
|
-
label,
|
|
177
|
-
onClick,
|
|
178
|
-
className
|
|
179
|
-
}) {
|
|
180
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
181
|
-
"button",
|
|
182
|
-
{
|
|
183
|
-
type: "button",
|
|
184
|
-
onClick,
|
|
185
|
-
"aria-label": label,
|
|
186
|
-
title: label,
|
|
187
|
-
className: utils.cn(
|
|
188
|
-
"rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
189
|
-
className
|
|
190
|
-
),
|
|
191
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon, className: "text-sm" })
|
|
192
|
-
}
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
55
|
function CommentBody({
|
|
196
56
|
comment,
|
|
197
57
|
config,
|
|
198
58
|
renderBody,
|
|
199
59
|
onUpdateStatus,
|
|
200
60
|
onDelete,
|
|
61
|
+
onCopy,
|
|
201
62
|
onReply,
|
|
202
63
|
onChecklistToggle,
|
|
203
|
-
onCollapse,
|
|
204
64
|
onMaximize
|
|
205
65
|
}) {
|
|
206
|
-
const [
|
|
66
|
+
const [actionError, setActionError] = react.useState("");
|
|
207
67
|
const isReply = Boolean(comment.parentId);
|
|
208
68
|
const date = timestampFormat.parseTimestamp(comment.createdAt);
|
|
209
69
|
const completion = completionStatus(config);
|
|
210
|
-
const
|
|
70
|
+
const reopen = reopenStatus(config);
|
|
71
|
+
const unresolved = comment.status == null || commentUtils.isUnresolved(config, comment.status);
|
|
211
72
|
const updateStatus = async (status) => {
|
|
212
|
-
|
|
73
|
+
setActionError("");
|
|
213
74
|
try {
|
|
214
75
|
await (onUpdateStatus == null ? void 0 : onUpdateStatus(status));
|
|
215
76
|
} catch (error) {
|
|
216
|
-
|
|
217
|
-
error instanceof Error ? error.message : "Unexpected error"
|
|
77
|
+
setActionError(
|
|
78
|
+
`Couldn't update comment: ${error instanceof Error ? error.message : "Unexpected error"}`
|
|
218
79
|
);
|
|
219
80
|
}
|
|
220
81
|
};
|
|
221
|
-
const
|
|
222
|
-
|
|
82
|
+
const copy = async () => {
|
|
83
|
+
setActionError("");
|
|
84
|
+
try {
|
|
85
|
+
await (onCopy == null ? void 0 : onCopy());
|
|
86
|
+
} catch (error) {
|
|
87
|
+
setActionError(
|
|
88
|
+
`Couldn't copy comment: ${error instanceof Error ? error.message : "Unexpected error"}`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const menuItems = actionMenuItems(isReply, {
|
|
223
93
|
...onDelete ? { onDelete } : {},
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
94
|
+
...!isReply && onUpdateStatus && completion && unresolved ? {
|
|
95
|
+
onToggleStatus: () => void updateStatus(completion.value),
|
|
96
|
+
toggleStatusLabel: "Resolve"
|
|
97
|
+
} : {},
|
|
98
|
+
...!isReply && onUpdateStatus && reopen && !unresolved ? {
|
|
99
|
+
onToggleStatus: () => void updateStatus(reopen.value),
|
|
100
|
+
toggleStatusLabel: "Reopen"
|
|
101
|
+
} : {},
|
|
102
|
+
...onCopy ? { onCopy: () => void copy() } : {},
|
|
103
|
+
...onMaximize ? { onMaximize } : {}
|
|
227
104
|
});
|
|
228
105
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2.5", children: [
|
|
229
106
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2.5", children: [
|
|
@@ -241,60 +118,46 @@ function CommentBody({
|
|
|
241
118
|
)
|
|
242
119
|
] }),
|
|
243
120
|
!isReply && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex flex-wrap gap-1", children: [
|
|
244
|
-
/* @__PURE__ */ jsxRuntime.jsx(RatingChip, { rating: comment.rating }),
|
|
245
|
-
/* @__PURE__ */ jsxRuntime.jsx(FacetBadges, { comment, config })
|
|
121
|
+
/* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.RatingChip, { rating: comment.rating }),
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.FacetBadges, { comment, config })
|
|
246
123
|
] })
|
|
247
124
|
] }),
|
|
248
|
-
/* @__PURE__ */ jsxRuntime.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
role: "button",
|
|
267
|
-
tabIndex: 0,
|
|
268
|
-
"aria-label": "Comment actions",
|
|
269
|
-
className: "inline-flex rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
270
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiDotsVertical.UiDotsVertical, className: "text-sm" })
|
|
271
|
-
}
|
|
272
|
-
),
|
|
273
|
-
items: menuItems
|
|
274
|
-
}
|
|
275
|
-
)
|
|
276
|
-
] })
|
|
125
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-0.5", children: menuItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
126
|
+
DropdownMenu.DropdownMenu,
|
|
127
|
+
{
|
|
128
|
+
align: "right",
|
|
129
|
+
menuClassName: "min-w-[150px]",
|
|
130
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
131
|
+
"span",
|
|
132
|
+
{
|
|
133
|
+
role: "button",
|
|
134
|
+
tabIndex: 0,
|
|
135
|
+
"aria-label": "Comment actions",
|
|
136
|
+
className: "inline-flex rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
137
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiDotsVertical.UiDotsVertical, className: "text-sm" })
|
|
138
|
+
}
|
|
139
|
+
),
|
|
140
|
+
items: menuItems
|
|
141
|
+
}
|
|
142
|
+
) })
|
|
277
143
|
] }),
|
|
278
144
|
comment.body.trim() && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[13px] leading-5 text-foreground", children: renderBody ? renderBody(comment.body) : /* @__PURE__ */ jsxRuntime.jsx(CommentMarkdown.CommentMarkdown, { text: comment.body }) }),
|
|
279
|
-
|
|
145
|
+
actionError && /* @__PURE__ */ jsxRuntime.jsx(
|
|
280
146
|
"div",
|
|
281
147
|
{
|
|
282
148
|
role: "alert",
|
|
283
149
|
className: "rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive",
|
|
284
|
-
children:
|
|
285
|
-
"Couldn't update comment: ",
|
|
286
|
-
statusError
|
|
287
|
-
]
|
|
150
|
+
children: actionError
|
|
288
151
|
}
|
|
289
152
|
),
|
|
290
153
|
!isReply && /* @__PURE__ */ jsxRuntime.jsx(
|
|
291
|
-
ChecklistChips,
|
|
154
|
+
CommentCardParts.ChecklistChips,
|
|
292
155
|
{
|
|
293
156
|
comment,
|
|
294
157
|
...onChecklistToggle ? { onChecklistToggle } : {}
|
|
295
158
|
}
|
|
296
159
|
),
|
|
297
|
-
!isReply && /* @__PURE__ */ jsxRuntime.jsx(RefChips, { comment }),
|
|
160
|
+
!isReply && /* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.RefChips, { comment }),
|
|
298
161
|
onReply && /* @__PURE__ */ jsxRuntime.jsx(
|
|
299
162
|
"button",
|
|
300
163
|
{
|
|
@@ -307,31 +170,37 @@ function CommentBody({
|
|
|
307
170
|
] });
|
|
308
171
|
}
|
|
309
172
|
function CommentCard(props) {
|
|
310
|
-
const { comment, config, compact, defaultExpanded
|
|
173
|
+
const { comment, config, compact, defaultExpanded } = props;
|
|
311
174
|
const [expanded, setExpanded] = react.useState(defaultExpanded ?? false);
|
|
312
|
-
const [
|
|
175
|
+
const [collapsedActionError, setCollapsedActionError] = react.useState("");
|
|
313
176
|
const isReply = Boolean(comment.parentId);
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
const completeCollapsed = async () => {
|
|
177
|
+
const body = /* @__PURE__ */ jsxRuntime.jsx(CommentBody, { ...props });
|
|
178
|
+
async function updateCollapsedStatus() {
|
|
317
179
|
var _a;
|
|
318
|
-
|
|
319
|
-
|
|
180
|
+
const completion = completionStatus(config);
|
|
181
|
+
const unresolved = comment.status == null || commentUtils.isUnresolved(config, comment.status);
|
|
182
|
+
const next = unresolved ? completion : reopenStatus(config);
|
|
183
|
+
if (!next) return;
|
|
184
|
+
setCollapsedActionError("");
|
|
320
185
|
try {
|
|
321
|
-
await ((_a = props.onUpdateStatus) == null ? void 0 : _a.call(props,
|
|
186
|
+
await ((_a = props.onUpdateStatus) == null ? void 0 : _a.call(props, next.value));
|
|
322
187
|
} catch (error) {
|
|
323
|
-
|
|
324
|
-
error instanceof Error ? error.message : "Unexpected error"
|
|
188
|
+
setCollapsedActionError(
|
|
189
|
+
`Couldn't update comment: ${error instanceof Error ? error.message : "Unexpected error"}`
|
|
325
190
|
);
|
|
326
191
|
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
192
|
+
}
|
|
193
|
+
async function copyCollapsed() {
|
|
194
|
+
var _a;
|
|
195
|
+
setCollapsedActionError("");
|
|
196
|
+
try {
|
|
197
|
+
await ((_a = props.onCopy) == null ? void 0 : _a.call(props));
|
|
198
|
+
} catch (error) {
|
|
199
|
+
setCollapsedActionError(
|
|
200
|
+
`Couldn't copy comment: ${error instanceof Error ? error.message : "Unexpected error"}`
|
|
201
|
+
);
|
|
333
202
|
}
|
|
334
|
-
|
|
203
|
+
}
|
|
335
204
|
if (!expanded) {
|
|
336
205
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
337
206
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -358,27 +227,10 @@ function CommentCard(props) {
|
|
|
358
227
|
/* @__PURE__ */ jsxRuntime.jsx(CommentAuthor.CommentAuthorAvatar, { author: comment.author, bare: true }),
|
|
359
228
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[11px] font-semibold text-foreground", children: commentUtils.authorDisplayName(comment.author) }),
|
|
360
229
|
!isReply && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
361
|
-
/* @__PURE__ */ jsxRuntime.jsx(RatingChip, { rating: comment.rating }),
|
|
362
|
-
/* @__PURE__ */ jsxRuntime.jsx(FacetBadges, { comment, config, compact: true })
|
|
230
|
+
/* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.RatingChip, { rating: comment.rating }),
|
|
231
|
+
/* @__PURE__ */ jsxRuntime.jsx(CommentCardParts.FacetBadges, { comment, config, compact: true })
|
|
363
232
|
] }),
|
|
364
233
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-[12px] text-foreground/80", children: comment.body.trim() ? commentUtils.truncatePlain(comment.body) : comment.rating === "positive" ? "Positive rating" : comment.rating === "negative" ? "Negative rating" : "Empty comment" }),
|
|
365
|
-
canComplete && /* @__PURE__ */ jsxRuntime.jsx(
|
|
366
|
-
"span",
|
|
367
|
-
{
|
|
368
|
-
role: "presentation",
|
|
369
|
-
onClick: (e) => e.stopPropagation(),
|
|
370
|
-
onKeyDown: (e) => e.stopPropagation(),
|
|
371
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
372
|
-
IconAction,
|
|
373
|
-
{
|
|
374
|
-
icon: UiCheck.UiCheck,
|
|
375
|
-
label: completionActionLabel(completion),
|
|
376
|
-
onClick: () => void completeCollapsed(),
|
|
377
|
-
className: "hover:bg-green-100 hover:text-green-700 dark:hover:bg-green-500/20 dark:hover:text-green-300"
|
|
378
|
-
}
|
|
379
|
-
)
|
|
380
|
-
}
|
|
381
|
-
),
|
|
382
234
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
383
235
|
"span",
|
|
384
236
|
{
|
|
@@ -399,16 +251,15 @@ function CommentCard(props) {
|
|
|
399
251
|
children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiDotsVertical.UiDotsVertical, className: "text-sm" })
|
|
400
252
|
}
|
|
401
253
|
),
|
|
402
|
-
items:
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
]
|
|
254
|
+
items: actionMenuItems(isReply, {
|
|
255
|
+
...props.onDelete ? { onDelete: props.onDelete } : {},
|
|
256
|
+
...!isReply && props.onUpdateStatus && completionStatus(config) ? {
|
|
257
|
+
onToggleStatus: () => void updateCollapsedStatus(),
|
|
258
|
+
toggleStatusLabel: comment.status == null || commentUtils.isUnresolved(config, comment.status) ? "Resolve" : "Reopen"
|
|
259
|
+
} : {},
|
|
260
|
+
...props.onCopy ? { onCopy: () => void copyCollapsed() } : {},
|
|
261
|
+
...props.onMaximize ? { onMaximize: props.onMaximize } : {}
|
|
262
|
+
})
|
|
412
263
|
}
|
|
413
264
|
)
|
|
414
265
|
}
|
|
@@ -416,15 +267,12 @@ function CommentCard(props) {
|
|
|
416
267
|
]
|
|
417
268
|
}
|
|
418
269
|
),
|
|
419
|
-
|
|
270
|
+
collapsedActionError && /* @__PURE__ */ jsxRuntime.jsx(
|
|
420
271
|
"div",
|
|
421
272
|
{
|
|
422
273
|
role: "alert",
|
|
423
274
|
className: "rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive",
|
|
424
|
-
children:
|
|
425
|
-
"Couldn't update comment: ",
|
|
426
|
-
collapsedStatusError
|
|
427
|
-
]
|
|
275
|
+
children: collapsedActionError
|
|
428
276
|
}
|
|
429
277
|
)
|
|
430
278
|
] });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentCard.cjs","sources":["../../src/comments/CommentCard.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Badge } from \"../data/Badge\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { DropdownMenu, type DropdownMenuItem } from \"../overlay/DropdownMenu\";\nimport {\n parseTimestamp,\n formatRelativeTime,\n} from \"../data/cells/timestamp-format\";\nimport {\n UiCheck,\n UiClose,\n UiDotsVertical,\n UiFullscreen,\n UiThumbsDown,\n UiThumbsUp,\n UiTrash,\n} from \"../icons\";\nimport { CommentAuthorAvatar } from \"./CommentAuthor\";\nimport { CommentMarkdown } from \"./CommentMarkdown\";\nimport {\n authorDisplayName,\n isUnresolved,\n resolveFacetOption,\n resolveStatusConfig,\n toneToBadgeTone,\n truncatePlain,\n} from \"./comment-utils\";\nimport type {\n Comment,\n CommentConfig,\n CommentRating,\n CommentStatusConfig,\n} from \"./comment-types\";\n\nfunction completionStatus(config: CommentConfig) {\n for (const completionName of [\"resolved\", \"closed\"]) {\n const status = config.statuses.find(\n (candidate) =>\n candidate.value.toLowerCase() === completionName ||\n candidate.label.toLowerCase() === completionName,\n );\n if (status) return status;\n }\n return undefined;\n}\n\nfunction completionActionLabel(status: CommentStatusConfig) {\n return status.value.toLowerCase() === \"closed\" ||\n status.label.toLowerCase() === \"closed\"\n ? \"Close comment\"\n : \"Resolve comment\";\n}\n\nexport type CommentCardProps = {\n comment: Comment;\n config: CommentConfig;\n /** Tighter padding for dense rails. */\n compact?: boolean;\n /** Render expanded on first mount. */\n defaultExpanded?: boolean;\n /** Custom body renderer; defaults to the lightweight CommentMarkdown. */\n renderBody?: (body: string) => ReactNode;\n /** Change this comment's status (roots only). */\n onUpdateStatus?: (status: string) => void | Promise<void>;\n /** Delete this comment. */\n onDelete?: () => void;\n /** Begin a reply to this comment. */\n onReply?: () => void;\n /** Advance the checklist item at `index` to its next status. */\n onChecklistToggle?: (index: number) => void;\n /**\n * Open this card's thread in a larger view. Owned by the caller because a\n * card knows only itself, while maximizing shows the root and its replies.\n * Omit to drop the action (e.g. inside an already-maximized thread).\n */\n onMaximize?: () => void;\n};\n\nfunction RatingChip({ rating }: { rating: CommentRating | undefined }) {\n if (!rating) return null;\n const positive = rating === \"positive\";\n return (\n <Badge\n variant=\"soft\"\n tone={positive ? \"success\" : \"danger\"}\n size=\"xs\"\n icon={positive ? UiThumbsUp : UiThumbsDown}\n >\n {positive ? \"Positive rating\" : \"Negative rating\"}\n </Badge>\n );\n}\n\nfunction StatusChip({\n status,\n config,\n showLabel,\n}: {\n status: string | undefined;\n config: CommentConfig;\n showLabel?: boolean;\n}) {\n const cfg = resolveStatusConfig(config, status);\n if (!cfg) return null;\n return (\n <Badge\n variant=\"soft\"\n tone={toneToBadgeTone(cfg.tone)}\n size=\"xs\"\n {...(cfg.icon ? { icon: cfg.icon } : {})}\n >\n {showLabel ? cfg.label : null}\n </Badge>\n );\n}\n\nfunction FacetBadges({\n comment,\n config,\n compact,\n}: {\n comment: Comment;\n config: CommentConfig;\n compact?: boolean;\n}) {\n const facets = config.facets ?? [];\n const chips = facets.flatMap((facet) => {\n const value = comment.facets?.[facet.key];\n const option = resolveFacetOption(facet, value);\n if (!option) return [];\n return [\n <Badge\n key={facet.key}\n variant=\"soft\"\n tone={toneToBadgeTone(option.tone)}\n size=\"xs\"\n >\n {compact ? (option.short ?? option.label) : option.label}\n </Badge>,\n ];\n });\n return chips.length > 0 ? <>{chips}</> : null;\n}\n\nfunction ChecklistChips({\n comment,\n onChecklistToggle,\n}: {\n comment: Comment;\n onChecklistToggle?: (index: number) => void;\n}) {\n if (!comment.checklist || comment.checklist.length === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-1\">\n {comment.checklist.map((item, i) => {\n const done = /done|complete|resolved/i.test(item.status);\n const active = /progress|doing/i.test(item.status);\n return (\n <button\n key={i}\n type=\"button\"\n onClick={() => onChecklistToggle?.(i)}\n disabled={!onChecklistToggle}\n className={cn(\n \"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] transition-opacity\",\n onChecklistToggle && \"cursor-pointer hover:opacity-80\",\n done\n ? \"bg-green-100 text-green-800 line-through dark:bg-green-500/20 dark:text-green-300\"\n : active\n ? \"bg-yellow-100 text-yellow-800 dark:bg-yellow-500/20 dark:text-yellow-300\"\n : \"bg-muted text-muted-foreground\",\n )}\n >\n {item.label}\n </button>\n );\n })}\n </div>\n );\n}\n\nfunction RefChips({ comment }: { comment: Comment }) {\n if (!comment.refs || comment.refs.length === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-1.5\">\n {comment.refs.map((group, i) => (\n <span\n key={i}\n className=\"inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground\"\n >\n <span className=\"font-medium\">{group.label}:</span>\n <span className={group.mono ? \"font-mono\" : undefined}>\n {group.items.join(\", \")}\n </span>\n </span>\n ))}\n </div>\n );\n}\n\nfunction statusMenuItems(\n comment: Comment,\n config: CommentConfig,\n isReply: boolean,\n opts: {\n onUpdateStatus?: (status: string) => void;\n onDelete?: () => void;\n onCollapse?: () => void;\n onMaximize?: () => void;\n completionStatus?: string;\n },\n): DropdownMenuItem[] {\n const items: DropdownMenuItem[] = [];\n if (opts.onMaximize) {\n items.push({\n label: \"Maximize\",\n icon: UiFullscreen,\n onSelect: opts.onMaximize,\n });\n }\n if (opts.onCollapse) {\n items.push({ label: \"Collapse\", icon: UiClose, onSelect: opts.onCollapse });\n }\n if (!isReply && opts.onUpdateStatus) {\n for (const status of config.statuses) {\n if (status.value === opts.completionStatus) continue;\n const current = status.value === comment.status;\n items.push({\n label: (\n <span\n className={cn(\n \"inline-flex items-center gap-1.5\",\n current && \"font-semibold\",\n )}\n >\n <StatusChip status={status.value} config={config} />\n {status.label}\n </span>\n ),\n onSelect: () => opts.onUpdateStatus?.(status.value),\n });\n }\n }\n if (opts.onDelete) {\n items.push({ label: \"Delete\", icon: UiTrash, onSelect: opts.onDelete });\n }\n return items;\n}\n\nfunction IconAction({\n icon,\n label,\n onClick,\n className,\n}: {\n icon: StaticIconComponent;\n label: string;\n onClick: () => void;\n className?: string;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={label}\n title={label}\n className={cn(\n \"rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\",\n className,\n )}\n >\n <Icon icon={icon} className=\"text-sm\" />\n </button>\n );\n}\n\nfunction CommentBody({\n comment,\n config,\n renderBody,\n onUpdateStatus,\n onDelete,\n onReply,\n onChecklistToggle,\n onCollapse,\n onMaximize,\n}: CommentCardProps & { onCollapse?: () => void }) {\n const [statusError, setStatusError] = useState(\"\");\n const isReply = Boolean(comment.parentId);\n const date = parseTimestamp(comment.createdAt);\n const completion = completionStatus(config);\n const canComplete =\n !isReply &&\n onUpdateStatus != null &&\n completion != null &&\n isUnresolved(config, comment.status);\n const updateStatus = async (status: string) => {\n setStatusError(\"\");\n try {\n await onUpdateStatus?.(status);\n } catch (error) {\n setStatusError(\n error instanceof Error ? error.message : \"Unexpected error\",\n );\n }\n };\n const menuItems = statusMenuItems(comment, config, isReply, {\n ...(onUpdateStatus\n ? { onUpdateStatus: (status) => void updateStatus(status) }\n : {}),\n ...(onDelete ? { onDelete } : {}),\n ...(onCollapse ? { onCollapse } : {}),\n ...(onMaximize ? { onMaximize } : {}),\n ...(completion ? { completionStatus: completion.value } : {}),\n });\n\n return (\n <div className=\"space-y-2.5\">\n <div className=\"flex items-start gap-2.5\">\n <CommentAuthorAvatar author={comment.author} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-baseline gap-1.5\">\n <span className=\"truncate text-xs font-semibold text-foreground\">\n {authorDisplayName(comment.author)}\n </span>\n {date && (\n <span\n title={date.toLocaleString()}\n className=\"shrink-0 text-[10px] text-muted-foreground\"\n >\n {formatRelativeTime(date)}\n </span>\n )}\n </div>\n {!isReply && (\n <div className=\"mt-1 flex flex-wrap gap-1\">\n <RatingChip rating={comment.rating} />\n <FacetBadges comment={comment} config={config} />\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-0.5\">\n {canComplete && (\n <IconAction\n icon={UiCheck}\n label={completionActionLabel(completion)}\n onClick={() => void updateStatus(completion.value)}\n className=\"hover:bg-green-100 hover:text-green-700 dark:hover:bg-green-500/20 dark:hover:text-green-300\"\n />\n )}\n {menuItems.length > 0 && (\n <DropdownMenu\n align=\"right\"\n menuClassName=\"min-w-[150px]\"\n trigger={\n <span\n role=\"button\"\n tabIndex={0}\n aria-label=\"Comment actions\"\n className=\"inline-flex rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </span>\n }\n items={menuItems}\n />\n )}\n </div>\n </div>\n {comment.body.trim() && (\n <div className=\"text-[13px] leading-5 text-foreground\">\n {renderBody ? (\n renderBody(comment.body)\n ) : (\n <CommentMarkdown text={comment.body} />\n )}\n </div>\n )}\n {statusError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive\"\n >\n Couldn't update comment: {statusError}\n </div>\n )}\n {!isReply && (\n <ChecklistChips\n comment={comment}\n {...(onChecklistToggle ? { onChecklistToggle } : {})}\n />\n )}\n {!isReply && <RefChips comment={comment} />}\n {onReply && (\n <button\n type=\"button\"\n onClick={onReply}\n className=\"-ml-2 rounded px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-primary/10\"\n >\n Reply\n </button>\n )}\n </div>\n );\n}\n\n/**\n * A single comment, collapsible between a compact one-line preview and a full\n * card. Offers a Maximize action when `onMaximize` is supplied — the owner\n * enlarges the whole thread. Fully controlled — all mutations are delegated to\n * the supplied callbacks.\n */\nexport function CommentCard(props: CommentCardProps) {\n const { comment, config, compact, defaultExpanded, onMaximize } = props;\n const [expanded, setExpanded] = useState(defaultExpanded ?? false);\n const [collapsedStatusError, setCollapsedStatusError] = useState(\"\");\n const isReply = Boolean(comment.parentId);\n const completion = completionStatus(config);\n const canComplete =\n !isReply &&\n props.onUpdateStatus != null &&\n completion != null &&\n isUnresolved(config, comment.status);\n const completeCollapsed = async () => {\n if (!completion) return;\n setCollapsedStatusError(\"\");\n try {\n await props.onUpdateStatus?.(completion.value);\n } catch (error) {\n setCollapsedStatusError(\n error instanceof Error ? error.message : \"Unexpected error\",\n );\n }\n };\n\n const body = (\n <CommentBody\n {...props}\n {...(expanded ? { onCollapse: () => setExpanded(false) } : {})}\n />\n );\n\n if (!expanded) {\n return (\n <>\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => setExpanded(true)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n setExpanded(true);\n }\n }}\n data-testid=\"comment-card\"\n data-comment-kind={isReply ? \"reply\" : \"root\"}\n data-comment-id={comment.id}\n className={cn(\n \"group/comment flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-xs transition-colors hover:bg-muted/50\",\n !isReply && \"border border-border bg-background shadow-sm\",\n isReply && \"ml-4 border-l-2 border-l-border bg-muted/30\",\n )}\n >\n <CommentAuthorAvatar author={comment.author} bare />\n <span className=\"shrink-0 text-[11px] font-semibold text-foreground\">\n {authorDisplayName(comment.author)}\n </span>\n {!isReply && (\n <>\n <RatingChip rating={comment.rating} />\n <FacetBadges comment={comment} config={config} compact />\n </>\n )}\n <span className=\"min-w-0 flex-1 truncate text-[12px] text-foreground/80\">\n {comment.body.trim()\n ? truncatePlain(comment.body)\n : comment.rating === \"positive\"\n ? \"Positive rating\"\n : comment.rating === \"negative\"\n ? \"Negative rating\"\n : \"Empty comment\"}\n </span>\n {canComplete && (\n <span\n role=\"presentation\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <IconAction\n icon={UiCheck}\n label={completionActionLabel(completion)}\n onClick={() => void completeCollapsed()}\n className=\"hover:bg-green-100 hover:text-green-700 dark:hover:bg-green-500/20 dark:hover:text-green-300\"\n />\n </span>\n )}\n <span\n role=\"presentation\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <DropdownMenu\n align=\"right\"\n trigger={\n <span\n role=\"button\"\n tabIndex={0}\n aria-label=\"Comment actions\"\n className=\"rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:opacity-100 group-hover/comment:opacity-70\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </span>\n }\n items={[\n { label: \"Expand\", onSelect: () => setExpanded(true) },\n ...(onMaximize\n ? [\n {\n label: \"Maximize\",\n icon: UiFullscreen,\n onSelect: onMaximize,\n },\n ]\n : []),\n ]}\n />\n </span>\n </div>\n {collapsedStatusError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive\"\n >\n Couldn't update comment: {collapsedStatusError}\n </div>\n )}\n </>\n );\n }\n\n return (\n <>\n <div\n data-testid=\"comment-card\"\n data-comment-kind={isReply ? \"reply\" : \"root\"}\n data-comment-id={comment.id}\n className={cn(\n \"group/comment rounded-xl bg-background px-3.5 py-3 text-xs\",\n !isReply &&\n \"border border-border/80 shadow-sm transition-shadow hover:shadow-md\",\n compact && \"px-2.5 py-2.5\",\n isReply && \"ml-4 border-l-2 border-l-border bg-muted/20 shadow-none\",\n )}\n >\n {body}\n </div>\n </>\n );\n}\n"],"names":["jsx","Badge","UiThumbsUp","UiThumbsDown","resolveStatusConfig","toneToBadgeTone","resolveFacetOption","Fragment","cn","jsxs","UiFullscreen","UiClose","UiTrash","Icon","useState","parseTimestamp","isUnresolved","CommentAuthorAvatar","authorDisplayName","UiCheck","DropdownMenu","UiDotsVertical","CommentMarkdown","truncatePlain"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CommentCard.cjs","sources":["../../src/comments/CommentCard.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { DropdownMenu, type DropdownMenuItem } from \"../overlay/DropdownMenu\";\nimport {\n parseTimestamp,\n formatRelativeTime,\n} from \"../data/cells/timestamp-format\";\nimport {\n UiCheck,\n UiCopy,\n UiDotsVertical,\n UiFullscreen,\n UiRestart,\n UiTrash,\n} from \"../icons\";\nimport { CommentAuthorAvatar } from \"./CommentAuthor\";\nimport {\n ChecklistChips,\n FacetBadges,\n RatingChip,\n RefChips,\n} from \"./CommentCardParts\";\nimport { CommentMarkdown } from \"./CommentMarkdown\";\nimport {\n authorDisplayName,\n isUnresolved,\n truncatePlain,\n} from \"./comment-utils\";\nimport type { Comment, CommentConfig } from \"./comment-types\";\n\nfunction completionStatus(config: CommentConfig) {\n for (const completionName of [\"resolved\", \"closed\"]) {\n const status = config.statuses.find(\n (candidate) =>\n candidate.value.toLowerCase() === completionName ||\n candidate.label.toLowerCase() === completionName,\n );\n if (status) return status;\n }\n return undefined;\n}\n\nfunction reopenStatus(config: CommentConfig) {\n return config.statuses.find((status) => status.unresolved);\n}\n\nexport type CommentCardProps = {\n comment: Comment;\n config: CommentConfig;\n /** Tighter padding for dense rails. */\n compact?: boolean;\n /** Render expanded on first mount. */\n defaultExpanded?: boolean;\n /** Custom body renderer; defaults to the lightweight CommentMarkdown. */\n renderBody?: (body: string) => ReactNode;\n /** Change this comment's status (roots only). */\n onUpdateStatus?: (status: string) => void | Promise<void>;\n /** Delete this comment. */\n onDelete?: () => void;\n /** Copy this comment's whole thread in the caller's canonical format. */\n onCopy?: () => void | Promise<void>;\n /** Begin a reply to this comment. */\n onReply?: () => void;\n /** Advance the checklist item at `index` to its next status. */\n onChecklistToggle?: (index: number) => void;\n /**\n * Open this card's thread in a larger view. Owned by the caller because a\n * card knows only itself, while maximizing shows the root and its replies.\n * Omit to drop the action (e.g. inside an already-maximized thread).\n */\n onMaximize?: () => void;\n};\n\nfunction actionMenuItems(\n isReply: boolean,\n opts: {\n onDelete?: () => void;\n onToggleStatus?: () => void;\n toggleStatusLabel?: \"Resolve\" | \"Reopen\";\n onCopy?: () => void;\n onMaximize?: () => void;\n },\n): DropdownMenuItem[] {\n const items: DropdownMenuItem[] = [];\n if (opts.onDelete) {\n items.push({ label: \"Delete\", icon: UiTrash, onSelect: opts.onDelete });\n }\n if (!isReply && opts.onToggleStatus && opts.toggleStatusLabel) {\n items.push({\n label: opts.toggleStatusLabel,\n icon: opts.toggleStatusLabel === \"Resolve\" ? UiCheck : UiRestart,\n onSelect: opts.onToggleStatus,\n });\n }\n if (opts.onCopy) {\n items.push({ label: \"Copy\", icon: UiCopy, onSelect: opts.onCopy });\n }\n if (opts.onMaximize) {\n items.push({\n label: \"Maximise\",\n icon: UiFullscreen,\n onSelect: opts.onMaximize,\n });\n }\n return items;\n}\n\nfunction CommentBody({\n comment,\n config,\n renderBody,\n onUpdateStatus,\n onDelete,\n onCopy,\n onReply,\n onChecklistToggle,\n onMaximize,\n}: CommentCardProps) {\n const [actionError, setActionError] = useState(\"\");\n const isReply = Boolean(comment.parentId);\n const date = parseTimestamp(comment.createdAt);\n const completion = completionStatus(config);\n const reopen = reopenStatus(config);\n const unresolved =\n comment.status == null || isUnresolved(config, comment.status);\n const updateStatus = async (status: string) => {\n setActionError(\"\");\n try {\n await onUpdateStatus?.(status);\n } catch (error) {\n setActionError(\n `Couldn't update comment: ${error instanceof Error ? error.message : \"Unexpected error\"}`,\n );\n }\n };\n const copy = async () => {\n setActionError(\"\");\n try {\n await onCopy?.();\n } catch (error) {\n setActionError(\n `Couldn't copy comment: ${error instanceof Error ? error.message : \"Unexpected error\"}`,\n );\n }\n };\n const menuItems = actionMenuItems(isReply, {\n ...(onDelete ? { onDelete } : {}),\n ...(!isReply && onUpdateStatus && completion && unresolved\n ? {\n onToggleStatus: () => void updateStatus(completion.value),\n toggleStatusLabel: \"Resolve\" as const,\n }\n : {}),\n ...(!isReply && onUpdateStatus && reopen && !unresolved\n ? {\n onToggleStatus: () => void updateStatus(reopen.value),\n toggleStatusLabel: \"Reopen\" as const,\n }\n : {}),\n ...(onCopy ? { onCopy: () => void copy() } : {}),\n ...(onMaximize ? { onMaximize } : {}),\n });\n\n return (\n <div className=\"space-y-2.5\">\n <div className=\"flex items-start gap-2.5\">\n <CommentAuthorAvatar author={comment.author} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-baseline gap-1.5\">\n <span className=\"truncate text-xs font-semibold text-foreground\">\n {authorDisplayName(comment.author)}\n </span>\n {date && (\n <span\n title={date.toLocaleString()}\n className=\"shrink-0 text-[10px] text-muted-foreground\"\n >\n {formatRelativeTime(date)}\n </span>\n )}\n </div>\n {!isReply && (\n <div className=\"mt-1 flex flex-wrap gap-1\">\n <RatingChip rating={comment.rating} />\n <FacetBadges comment={comment} config={config} />\n </div>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-0.5\">\n {menuItems.length > 0 && (\n <DropdownMenu\n align=\"right\"\n menuClassName=\"min-w-[150px]\"\n trigger={\n <span\n role=\"button\"\n tabIndex={0}\n aria-label=\"Comment actions\"\n className=\"inline-flex rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </span>\n }\n items={menuItems}\n />\n )}\n </div>\n </div>\n {comment.body.trim() && (\n <div className=\"text-[13px] leading-5 text-foreground\">\n {renderBody ? (\n renderBody(comment.body)\n ) : (\n <CommentMarkdown text={comment.body} />\n )}\n </div>\n )}\n {actionError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive\"\n >\n {actionError}\n </div>\n )}\n {!isReply && (\n <ChecklistChips\n comment={comment}\n {...(onChecklistToggle ? { onChecklistToggle } : {})}\n />\n )}\n {!isReply && <RefChips comment={comment} />}\n {onReply && (\n <button\n type=\"button\"\n onClick={onReply}\n className=\"-ml-2 rounded px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-primary/10\"\n >\n Reply\n </button>\n )}\n </div>\n );\n}\n\n/**\n * A single comment, collapsible between a compact one-line preview and a full\n * card. Offers a Maximise action when `onMaximize` is supplied — the owner\n * enlarges the whole thread. Fully controlled — all mutations are delegated to\n * the supplied callbacks.\n */\nexport function CommentCard(props: CommentCardProps) {\n const { comment, config, compact, defaultExpanded } = props;\n const [expanded, setExpanded] = useState(defaultExpanded ?? false);\n const [collapsedActionError, setCollapsedActionError] = useState(\"\");\n const isReply = Boolean(comment.parentId);\n const body = <CommentBody {...props} />;\n\n async function updateCollapsedStatus() {\n const completion = completionStatus(config);\n const unresolved =\n comment.status == null || isUnresolved(config, comment.status);\n const next = unresolved ? completion : reopenStatus(config);\n if (!next) return;\n setCollapsedActionError(\"\");\n try {\n await props.onUpdateStatus?.(next.value);\n } catch (error) {\n setCollapsedActionError(\n `Couldn't update comment: ${error instanceof Error ? error.message : \"Unexpected error\"}`,\n );\n }\n }\n\n async function copyCollapsed() {\n setCollapsedActionError(\"\");\n try {\n await props.onCopy?.();\n } catch (error) {\n setCollapsedActionError(\n `Couldn't copy comment: ${error instanceof Error ? error.message : \"Unexpected error\"}`,\n );\n }\n }\n\n if (!expanded) {\n return (\n <>\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => setExpanded(true)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n setExpanded(true);\n }\n }}\n data-testid=\"comment-card\"\n data-comment-kind={isReply ? \"reply\" : \"root\"}\n data-comment-id={comment.id}\n className={cn(\n \"group/comment flex cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-xs transition-colors hover:bg-muted/50\",\n !isReply && \"border border-border bg-background shadow-sm\",\n isReply && \"ml-4 border-l-2 border-l-border bg-muted/30\",\n )}\n >\n <CommentAuthorAvatar author={comment.author} bare />\n <span className=\"shrink-0 text-[11px] font-semibold text-foreground\">\n {authorDisplayName(comment.author)}\n </span>\n {!isReply && (\n <>\n <RatingChip rating={comment.rating} />\n <FacetBadges comment={comment} config={config} compact />\n </>\n )}\n <span className=\"min-w-0 flex-1 truncate text-[12px] text-foreground/80\">\n {comment.body.trim()\n ? truncatePlain(comment.body)\n : comment.rating === \"positive\"\n ? \"Positive rating\"\n : comment.rating === \"negative\"\n ? \"Negative rating\"\n : \"Empty comment\"}\n </span>\n <span\n role=\"presentation\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <DropdownMenu\n align=\"right\"\n trigger={\n <span\n role=\"button\"\n tabIndex={0}\n aria-label=\"Comment actions\"\n className=\"rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:opacity-100 group-hover/comment:opacity-70\"\n >\n <Icon icon={UiDotsVertical} className=\"text-sm\" />\n </span>\n }\n items={actionMenuItems(isReply, {\n ...(props.onDelete ? { onDelete: props.onDelete } : {}),\n ...(!isReply && props.onUpdateStatus && completionStatus(config)\n ? {\n onToggleStatus: () => void updateCollapsedStatus(),\n toggleStatusLabel:\n comment.status == null ||\n isUnresolved(config, comment.status)\n ? \"Resolve\"\n : \"Reopen\",\n }\n : {}),\n ...(props.onCopy ? { onCopy: () => void copyCollapsed() } : {}),\n ...(props.onMaximize ? { onMaximize: props.onMaximize } : {}),\n })}\n />\n </span>\n </div>\n {collapsedActionError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/30 bg-destructive/5 px-2.5 py-2 text-xs text-destructive\"\n >\n {collapsedActionError}\n </div>\n )}\n </>\n );\n }\n\n return (\n <>\n <div\n data-testid=\"comment-card\"\n data-comment-kind={isReply ? \"reply\" : \"root\"}\n data-comment-id={comment.id}\n className={cn(\n \"group/comment rounded-xl bg-background px-3.5 py-3 text-xs\",\n !isReply &&\n \"border border-border/80 shadow-sm transition-shadow hover:shadow-md\",\n compact && \"px-2.5 py-2.5\",\n isReply && \"ml-4 border-l-2 border-l-border bg-muted/20 shadow-none\",\n )}\n >\n {body}\n </div>\n </>\n );\n}\n"],"names":["UiTrash","UiCheck","UiRestart","UiCopy","UiFullscreen","useState","parseTimestamp","isUnresolved","jsxs","jsx","CommentAuthorAvatar","authorDisplayName","RatingChip","FacetBadges","DropdownMenu","Icon","UiDotsVertical","CommentMarkdown","ChecklistChips","RefChips","Fragment","cn","truncatePlain"],"mappings":"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"}
|
|
@@ -13,6 +13,8 @@ export type CommentCardProps = {
|
|
|
13
13
|
onUpdateStatus?: (status: string) => void | Promise<void>;
|
|
14
14
|
/** Delete this comment. */
|
|
15
15
|
onDelete?: () => void;
|
|
16
|
+
/** Copy this comment's whole thread in the caller's canonical format. */
|
|
17
|
+
onCopy?: () => void | Promise<void>;
|
|
16
18
|
/** Begin a reply to this comment. */
|
|
17
19
|
onReply?: () => void;
|
|
18
20
|
/** Advance the checklist item at `index` to its next status. */
|
|
@@ -26,7 +28,7 @@ export type CommentCardProps = {
|
|
|
26
28
|
};
|
|
27
29
|
/**
|
|
28
30
|
* A single comment, collapsible between a compact one-line preview and a full
|
|
29
|
-
* card. Offers a
|
|
31
|
+
* card. Offers a Maximise action when `onMaximize` is supplied — the owner
|
|
30
32
|
* enlarges the whole thread. Fully controlled — all mutations are delegated to
|
|
31
33
|
* the supplied callbacks.
|
|
32
34
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentCard.d.ts","sourceRoot":"","sources":["../../src/comments/CommentCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"CommentCard.d.ts","sourceRoot":"","sources":["../../src/comments/CommentCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA6BjD,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAkB9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;IACtB,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,yEAAyE;IACzE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IACzC,iDAAiD;IACjD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AA8KF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BA4IlD"}
|