@echothink-ui/todo 0.1.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/README.md +5 -0
- package/dist/components/KanbanBoard.d.ts +8 -0
- package/dist/components/KanbanColumn.d.ts +8 -0
- package/dist/components/TaskCard.d.ts +6 -0
- package/dist/components/TaskDependencyList.d.ts +6 -0
- package/dist/components/TaskTable.d.ts +9 -0
- package/dist/components/TaskTimeline.d.ts +6 -0
- package/dist/components/TodoItem.d.ts +7 -0
- package/dist/components/TodoList.d.ts +8 -0
- package/dist/index.cjs +590 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +500 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +545 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +49 -0
- package/dist/utils.d.ts +3 -0
- package/package.json +43 -0
- package/src/components/KanbanBoard.tsx +161 -0
- package/src/components/KanbanColumn.tsx +105 -0
- package/src/components/TaskCard.tsx +74 -0
- package/src/components/TaskDependencyList.tsx +56 -0
- package/src/components/TaskTable.tsx +69 -0
- package/src/components/TaskTimeline.tsx +40 -0
- package/src/components/TodoItem.test.tsx +46 -0
- package/src/components/TodoItem.tsx +62 -0
- package/src/components/TodoList.tsx +65 -0
- package/src/index.test.tsx +46 -0
- package/src/index.tsx +26 -0
- package/src/styles.css +591 -0
- package/src/types.ts +56 -0
- package/src/utils.ts +26 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
// src/components/TodoList.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Button, Panel, TextInput } from "@echothink-ui/core";
|
|
4
|
+
import { PlusIcon } from "@echothink-ui/icons";
|
|
5
|
+
|
|
6
|
+
// src/components/TodoItem.tsx
|
|
7
|
+
import { Badge, Checkbox, Tag } from "@echothink-ui/core";
|
|
8
|
+
|
|
9
|
+
// src/utils.ts
|
|
10
|
+
function formatDateTime(value) {
|
|
11
|
+
if (!value) return "";
|
|
12
|
+
const date = new Date(value);
|
|
13
|
+
if (Number.isNaN(date.getTime())) return value;
|
|
14
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
15
|
+
month: "short",
|
|
16
|
+
day: "numeric",
|
|
17
|
+
hour: "numeric",
|
|
18
|
+
minute: "2-digit"
|
|
19
|
+
}).format(date);
|
|
20
|
+
}
|
|
21
|
+
function priorityLabel(priority) {
|
|
22
|
+
return priority ?? "normal";
|
|
23
|
+
}
|
|
24
|
+
function prioritySeverity(priority) {
|
|
25
|
+
const normalized = priority?.toLowerCase();
|
|
26
|
+
if (normalized === "urgent" || normalized === "critical" || normalized === "high")
|
|
27
|
+
return "danger";
|
|
28
|
+
if (normalized === "medium") return "warning";
|
|
29
|
+
if (normalized === "low") return "info";
|
|
30
|
+
return "neutral";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/components/TodoItem.tsx
|
|
34
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
35
|
+
function validDateTimeAttribute(value) {
|
|
36
|
+
if (!value) return void 0;
|
|
37
|
+
return Number.isNaN(new Date(value).getTime()) ? void 0 : value;
|
|
38
|
+
}
|
|
39
|
+
function TodoItem({ item, onToggle, className, ...props }) {
|
|
40
|
+
const hasMeta = Boolean(item.assignee || item.priority || item.dueAt);
|
|
41
|
+
const dueDateTime = validDateTimeAttribute(item.dueAt);
|
|
42
|
+
const itemClassName = [
|
|
43
|
+
"eth-todo-item",
|
|
44
|
+
hasMeta ? "eth-todo-item--has-meta" : "",
|
|
45
|
+
item.done ? "eth-todo-item--done" : "",
|
|
46
|
+
className ?? ""
|
|
47
|
+
].filter(Boolean).join(" ");
|
|
48
|
+
return /* @__PURE__ */ jsxs(
|
|
49
|
+
"div",
|
|
50
|
+
{
|
|
51
|
+
...props,
|
|
52
|
+
className: itemClassName,
|
|
53
|
+
"data-priority": item.priority,
|
|
54
|
+
"data-state": item.done ? "done" : "open",
|
|
55
|
+
"data-eth-component": "TodoItem",
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ jsx("div", { className: "eth-todo-item__content eth-todo-item__primary", children: /* @__PURE__ */ jsx(
|
|
58
|
+
Checkbox,
|
|
59
|
+
{
|
|
60
|
+
checked: item.done,
|
|
61
|
+
className: "eth-todo-item__checkbox",
|
|
62
|
+
label: /* @__PURE__ */ jsx("span", { className: "eth-todo-item__label", children: item.label }),
|
|
63
|
+
onChange: () => onToggle?.(item.id)
|
|
64
|
+
}
|
|
65
|
+
) }),
|
|
66
|
+
hasMeta ? /* @__PURE__ */ jsxs("div", { className: "eth-todo-item__meta", role: "group", "aria-label": "Task details", children: [
|
|
67
|
+
item.priority ? /* @__PURE__ */ jsx(Badge, { className: "eth-todo-item__tag", severity: prioritySeverity(item.priority), children: priorityLabel(item.priority) }) : null,
|
|
68
|
+
item.assignee ? /* @__PURE__ */ jsx(Tag, { className: "eth-todo-item__tag", children: item.assignee }) : null,
|
|
69
|
+
item.dueAt ? /* @__PURE__ */ jsxs("span", { className: "eth-todo-item__due", children: [
|
|
70
|
+
/* @__PURE__ */ jsx("span", { className: "eth-todo-item__due-label", children: "Due" }),
|
|
71
|
+
/* @__PURE__ */ jsx("time", { dateTime: dueDateTime, children: formatDateTime(item.dueAt) })
|
|
72
|
+
] }) : null
|
|
73
|
+
] }) : null
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/components/TodoList.tsx
|
|
80
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
81
|
+
function TodoList({ items = [], onToggle, onAdd, className, ...props }) {
|
|
82
|
+
const [label, setLabel] = React.useState("");
|
|
83
|
+
const completedCount = items.filter((item) => item.done).length;
|
|
84
|
+
const openCount = items.length - completedCount;
|
|
85
|
+
const summary = items.length ? `${openCount} open / ${completedCount} done` : "No tasks yet";
|
|
86
|
+
const submit = (event) => {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
if (!label.trim() || !onAdd) return;
|
|
89
|
+
onAdd(label.trim());
|
|
90
|
+
setLabel("");
|
|
91
|
+
};
|
|
92
|
+
return /* @__PURE__ */ jsxs2(
|
|
93
|
+
Panel,
|
|
94
|
+
{
|
|
95
|
+
...props,
|
|
96
|
+
className: `eth-todo-list ${className ?? ""}`,
|
|
97
|
+
title: "To-do",
|
|
98
|
+
subtitle: summary,
|
|
99
|
+
"data-eth-component": "TodoList",
|
|
100
|
+
children: [
|
|
101
|
+
items.length ? /* @__PURE__ */ jsx2(
|
|
102
|
+
"div",
|
|
103
|
+
{
|
|
104
|
+
className: "eth-todo-list__items",
|
|
105
|
+
role: "list",
|
|
106
|
+
"aria-label": "Tasks",
|
|
107
|
+
children: items.map((item) => /* @__PURE__ */ jsx2(TodoItem, { item, onToggle, role: "listitem" }, item.id))
|
|
108
|
+
}
|
|
109
|
+
) : /* @__PURE__ */ jsx2("p", { className: "eth-todo-list__empty", children: "No tasks have been added." }),
|
|
110
|
+
onAdd ? /* @__PURE__ */ jsxs2("form", { className: "eth-todo-list__add", "aria-label": "Add task", onSubmit: submit, children: [
|
|
111
|
+
/* @__PURE__ */ jsx2(
|
|
112
|
+
TextInput,
|
|
113
|
+
{
|
|
114
|
+
value: label,
|
|
115
|
+
labelText: "New task",
|
|
116
|
+
hideLabel: true,
|
|
117
|
+
placeholder: "Add a task",
|
|
118
|
+
onChange: (event) => setLabel(event.currentTarget.value)
|
|
119
|
+
}
|
|
120
|
+
),
|
|
121
|
+
/* @__PURE__ */ jsx2(Button, { type: "submit", icon: /* @__PURE__ */ jsx2(PlusIcon, {}), disabled: !label.trim(), children: "Add" })
|
|
122
|
+
] }) : null
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/components/KanbanBoard.tsx
|
|
129
|
+
import * as React3 from "react";
|
|
130
|
+
|
|
131
|
+
// src/components/KanbanColumn.tsx
|
|
132
|
+
import * as React2 from "react";
|
|
133
|
+
import { Badge as Badge2, IconButton } from "@echothink-ui/core";
|
|
134
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
135
|
+
function KanbanColumn({
|
|
136
|
+
column,
|
|
137
|
+
children,
|
|
138
|
+
onAddCard,
|
|
139
|
+
className,
|
|
140
|
+
"aria-label": ariaLabel,
|
|
141
|
+
...props
|
|
142
|
+
}) {
|
|
143
|
+
const itemCount = column.items.length;
|
|
144
|
+
const taskLabel = `${itemCount} ${itemCount === 1 ? "task" : "tasks"}`;
|
|
145
|
+
const wipLimit = column.wipLimit;
|
|
146
|
+
const hasWipLimit = typeof wipLimit === "number";
|
|
147
|
+
const overLimit = hasWipLimit ? itemCount > wipLimit : false;
|
|
148
|
+
const atLimit = hasWipLimit ? itemCount === wipLimit : false;
|
|
149
|
+
const hasRenderedChildren = React2.Children.count(children) > 0;
|
|
150
|
+
const titleId = React2.useId();
|
|
151
|
+
const { "aria-labelledby": ariaLabelledBy, ...sectionProps } = props;
|
|
152
|
+
let wipPercent = 0;
|
|
153
|
+
let wipStatusLabel = "";
|
|
154
|
+
let wipSeverity = "neutral";
|
|
155
|
+
if (hasWipLimit) {
|
|
156
|
+
wipPercent = wipLimit > 0 ? Math.min(100, itemCount / wipLimit * 100) : itemCount > 0 ? 100 : 0;
|
|
157
|
+
const remaining = wipLimit - itemCount;
|
|
158
|
+
wipStatusLabel = overLimit ? `${Math.abs(remaining)} over limit` : remaining === 0 ? "At limit" : `${remaining} ${remaining === 1 ? "slot" : "slots"} open`;
|
|
159
|
+
if (overLimit) wipSeverity = "danger";
|
|
160
|
+
else if (atLimit) wipSeverity = "warning";
|
|
161
|
+
}
|
|
162
|
+
return /* @__PURE__ */ jsxs3(
|
|
163
|
+
"section",
|
|
164
|
+
{
|
|
165
|
+
...sectionProps,
|
|
166
|
+
"aria-label": ariaLabel,
|
|
167
|
+
"aria-labelledby": ariaLabel ? void 0 : ariaLabelledBy ?? titleId,
|
|
168
|
+
className: `eth-kanban-column ${overLimit ? "eth-kanban-column--over-limit" : ""} ${className ?? ""}`,
|
|
169
|
+
"data-eth-component": "KanbanColumn",
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ jsxs3("header", { className: "eth-kanban-column__header", children: [
|
|
172
|
+
/* @__PURE__ */ jsxs3("div", { className: "eth-kanban-column__header-main", children: [
|
|
173
|
+
/* @__PURE__ */ jsxs3("div", { className: "eth-kanban-column__heading", children: [
|
|
174
|
+
/* @__PURE__ */ jsx3("h3", { id: titleId, children: column.title }),
|
|
175
|
+
/* @__PURE__ */ jsx3("span", { className: "eth-kanban-column__count", children: taskLabel })
|
|
176
|
+
] }),
|
|
177
|
+
onAddCard ? /* @__PURE__ */ jsx3("div", { className: "eth-kanban-column__actions", children: /* @__PURE__ */ jsx3(
|
|
178
|
+
IconButton,
|
|
179
|
+
{
|
|
180
|
+
className: "eth-kanban-column__add",
|
|
181
|
+
intent: "ghost",
|
|
182
|
+
density: "compact",
|
|
183
|
+
label: `Add task to ${column.title}`,
|
|
184
|
+
icon: /* @__PURE__ */ jsx3("span", { className: "eth-kanban-column__add-glyph", "aria-hidden": "true", children: "+" }),
|
|
185
|
+
onClick: () => onAddCard(column.id)
|
|
186
|
+
}
|
|
187
|
+
) }) : null
|
|
188
|
+
] }),
|
|
189
|
+
hasWipLimit ? /* @__PURE__ */ jsx3("div", { className: "eth-kanban-column__metrics", children: /* @__PURE__ */ jsxs3(
|
|
190
|
+
"div",
|
|
191
|
+
{
|
|
192
|
+
className: "eth-kanban-column__limit",
|
|
193
|
+
"aria-label": `Work in progress ${itemCount} of ${wipLimit}`,
|
|
194
|
+
children: [
|
|
195
|
+
/* @__PURE__ */ jsxs3("div", { className: "eth-kanban-column__limit-row", children: [
|
|
196
|
+
/* @__PURE__ */ jsxs3(Badge2, { severity: wipSeverity, children: [
|
|
197
|
+
"WIP ",
|
|
198
|
+
itemCount,
|
|
199
|
+
"/",
|
|
200
|
+
wipLimit
|
|
201
|
+
] }),
|
|
202
|
+
/* @__PURE__ */ jsx3("span", { className: "eth-kanban-column__limit-note", children: wipStatusLabel })
|
|
203
|
+
] }),
|
|
204
|
+
/* @__PURE__ */ jsx3("span", { className: "eth-kanban-column__meter", "aria-hidden": "true", children: /* @__PURE__ */ jsx3("span", { style: { inlineSize: `${wipPercent}%` } }) })
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
) }) : null
|
|
208
|
+
] }),
|
|
209
|
+
/* @__PURE__ */ jsx3("div", { className: "eth-kanban-column__body", role: "list", "aria-label": `${column.title} tasks`, children: hasRenderedChildren ? children : itemCount === 0 ? /* @__PURE__ */ jsx3("div", { className: "eth-kanban-column__empty", children: "No tasks in this column" }) : null })
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/components/TaskCard.tsx
|
|
216
|
+
import { Badge as Badge3, Surface, StatusDot, Tag as Tag2 } from "@echothink-ui/core";
|
|
217
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
218
|
+
var operationalStatuses = /* @__PURE__ */ new Set([
|
|
219
|
+
"queued",
|
|
220
|
+
"running",
|
|
221
|
+
"paused",
|
|
222
|
+
"blocked",
|
|
223
|
+
"failed",
|
|
224
|
+
"succeeded",
|
|
225
|
+
"warning",
|
|
226
|
+
"stale",
|
|
227
|
+
"synced",
|
|
228
|
+
"pending-approval",
|
|
229
|
+
"approval-required",
|
|
230
|
+
"in-progress",
|
|
231
|
+
"not-started",
|
|
232
|
+
"completed",
|
|
233
|
+
"active",
|
|
234
|
+
"inactive"
|
|
235
|
+
]);
|
|
236
|
+
function isOperationalStatus(status) {
|
|
237
|
+
return Boolean(status && operationalStatuses.has(status));
|
|
238
|
+
}
|
|
239
|
+
function statusLabel(status) {
|
|
240
|
+
return status.replace(/-/g, " ");
|
|
241
|
+
}
|
|
242
|
+
function TaskCard({ card, className, ...props }) {
|
|
243
|
+
return /* @__PURE__ */ jsxs4(
|
|
244
|
+
Surface,
|
|
245
|
+
{
|
|
246
|
+
...props,
|
|
247
|
+
className: `eth-todo-task-card ${className ?? ""}`,
|
|
248
|
+
"data-priority": card.priority,
|
|
249
|
+
"data-status": card.status,
|
|
250
|
+
"data-eth-component": "TaskCard",
|
|
251
|
+
children: [
|
|
252
|
+
/* @__PURE__ */ jsxs4("div", { className: "eth-todo-task-card__header", children: [
|
|
253
|
+
/* @__PURE__ */ jsx4("strong", { children: card.title }),
|
|
254
|
+
card.priority ? /* @__PURE__ */ jsx4(Badge3, { severity: prioritySeverity(card.priority), children: priorityLabel(card.priority) }) : null
|
|
255
|
+
] }),
|
|
256
|
+
/* @__PURE__ */ jsxs4("div", { className: "eth-todo-task-card__meta", children: [
|
|
257
|
+
card.assignee ? /* @__PURE__ */ jsx4(Tag2, { children: card.assignee }) : null,
|
|
258
|
+
isOperationalStatus(card.status) ? /* @__PURE__ */ jsx4(StatusDot, { status: card.status, label: statusLabel(card.status) }) : card.status ? /* @__PURE__ */ jsx4(Tag2, { children: card.status }) : null,
|
|
259
|
+
card.dueAt ? /* @__PURE__ */ jsx4("time", { className: "eth-todo-task-card__due", dateTime: card.dueAt, children: formatDateTime(card.dueAt) }) : null
|
|
260
|
+
] }),
|
|
261
|
+
card.labels?.length ? /* @__PURE__ */ jsx4("div", { className: "eth-todo-task-card__labels", children: card.labels.map((label) => /* @__PURE__ */ jsx4(Tag2, { children: label }, label)) }) : null
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/components/KanbanBoard.tsx
|
|
268
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
269
|
+
function KanbanBoard({
|
|
270
|
+
columns: columns2 = [],
|
|
271
|
+
onCardMove,
|
|
272
|
+
onAddCard,
|
|
273
|
+
className,
|
|
274
|
+
...props
|
|
275
|
+
}) {
|
|
276
|
+
const [announcement, setAnnouncement] = React3.useState("");
|
|
277
|
+
const [lifted, setLifted] = React3.useState(null);
|
|
278
|
+
const findCard = (cardId) => {
|
|
279
|
+
for (const column of columns2) {
|
|
280
|
+
const index = column.items.findIndex((item) => item.id === cardId);
|
|
281
|
+
if (index >= 0) return { column, index, card: column.items[index] };
|
|
282
|
+
}
|
|
283
|
+
return void 0;
|
|
284
|
+
};
|
|
285
|
+
const announce = (message) => setAnnouncement(message);
|
|
286
|
+
const moveLifted = (direction) => {
|
|
287
|
+
if (!lifted) return;
|
|
288
|
+
const currentColumnIndex = columns2.findIndex((column) => column.id === lifted.toColumn);
|
|
289
|
+
if (currentColumnIndex < 0) return;
|
|
290
|
+
if (direction === "left" || direction === "right") {
|
|
291
|
+
const offset = direction === "left" ? -1 : 1;
|
|
292
|
+
const nextColumn = columns2[currentColumnIndex + offset];
|
|
293
|
+
if (!nextColumn) return;
|
|
294
|
+
const nextIndex2 = Math.min(lifted.index, Math.max(0, nextColumn.items.length));
|
|
295
|
+
setLifted({ ...lifted, toColumn: nextColumn.id, index: nextIndex2 });
|
|
296
|
+
announce(`Moved ${lifted.cardId} to ${nextColumn.title}, position ${nextIndex2 + 1}`);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const currentColumn = columns2[currentColumnIndex];
|
|
300
|
+
const maxIndex = Math.max(0, currentColumn.items.length - 1);
|
|
301
|
+
const nextIndex = direction === "up" ? Math.max(0, lifted.index - 1) : Math.min(maxIndex, lifted.index + 1);
|
|
302
|
+
setLifted({ ...lifted, index: nextIndex });
|
|
303
|
+
announce(`Moved ${lifted.cardId} to position ${nextIndex + 1}`);
|
|
304
|
+
};
|
|
305
|
+
const handleCardKeyDown = (event, card, column, index) => {
|
|
306
|
+
if (event.key === " ") {
|
|
307
|
+
event.preventDefault();
|
|
308
|
+
setLifted({ cardId: card.id, fromColumn: column.id, toColumn: column.id, index });
|
|
309
|
+
announce(`Lifted ${card.title}`);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (!lifted) return;
|
|
313
|
+
if (event.key === "ArrowLeft") {
|
|
314
|
+
event.preventDefault();
|
|
315
|
+
moveLifted("left");
|
|
316
|
+
}
|
|
317
|
+
if (event.key === "ArrowRight") {
|
|
318
|
+
event.preventDefault();
|
|
319
|
+
moveLifted("right");
|
|
320
|
+
}
|
|
321
|
+
if (event.key === "ArrowUp") {
|
|
322
|
+
event.preventDefault();
|
|
323
|
+
moveLifted("up");
|
|
324
|
+
}
|
|
325
|
+
if (event.key === "ArrowDown") {
|
|
326
|
+
event.preventDefault();
|
|
327
|
+
moveLifted("down");
|
|
328
|
+
}
|
|
329
|
+
if (event.key === "Enter") {
|
|
330
|
+
event.preventDefault();
|
|
331
|
+
onCardMove?.(lifted.cardId, lifted.fromColumn, lifted.toColumn, lifted.index);
|
|
332
|
+
announce(`Dropped ${lifted.cardId}`);
|
|
333
|
+
setLifted(null);
|
|
334
|
+
}
|
|
335
|
+
if (event.key === "Escape") {
|
|
336
|
+
event.preventDefault();
|
|
337
|
+
announce(`Cancelled move for ${lifted.cardId}`);
|
|
338
|
+
setLifted(null);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
const handleDrop = (event, toColumn, index) => {
|
|
342
|
+
event.preventDefault();
|
|
343
|
+
const cardId = event.dataTransfer.getData("text/plain");
|
|
344
|
+
const source = findCard(cardId);
|
|
345
|
+
if (!source) return;
|
|
346
|
+
onCardMove?.(cardId, source.column.id, toColumn.id, index);
|
|
347
|
+
announce(`Moved ${source.card.title} to ${toColumn.title}`);
|
|
348
|
+
};
|
|
349
|
+
return /* @__PURE__ */ jsxs5(
|
|
350
|
+
"div",
|
|
351
|
+
{
|
|
352
|
+
...props,
|
|
353
|
+
className: `eth-kanban-board ${className ?? ""}`,
|
|
354
|
+
"data-eth-component": "KanbanBoard",
|
|
355
|
+
children: [
|
|
356
|
+
/* @__PURE__ */ jsx5("div", { className: "eth-kanban-board__columns", role: "list", "aria-label": "Kanban columns", children: columns2.map((column) => /* @__PURE__ */ jsx5(
|
|
357
|
+
KanbanColumn,
|
|
358
|
+
{
|
|
359
|
+
column,
|
|
360
|
+
role: "listitem",
|
|
361
|
+
onAddCard,
|
|
362
|
+
onDragOver: (event) => event.preventDefault(),
|
|
363
|
+
onDrop: (event) => handleDrop(event, column, column.items.length),
|
|
364
|
+
children: column.items.map((card, index) => /* @__PURE__ */ jsx5(
|
|
365
|
+
"div",
|
|
366
|
+
{
|
|
367
|
+
className: `eth-kanban-card-shell ${lifted?.cardId === card.id ? "eth-kanban-card-shell--lifted" : ""}`,
|
|
368
|
+
draggable: true,
|
|
369
|
+
tabIndex: 0,
|
|
370
|
+
role: "listitem",
|
|
371
|
+
"aria-label": `${card.title}, ${column.title}, position ${index + 1} of ${column.items.length}`,
|
|
372
|
+
"aria-grabbed": lifted?.cardId === card.id,
|
|
373
|
+
"aria-roledescription": "Draggable task card",
|
|
374
|
+
"aria-keyshortcuts": "Space ArrowLeft ArrowRight ArrowUp ArrowDown Enter Escape",
|
|
375
|
+
onDragStart: (event) => {
|
|
376
|
+
event.dataTransfer.setData("text/plain", card.id);
|
|
377
|
+
event.dataTransfer.effectAllowed = "move";
|
|
378
|
+
},
|
|
379
|
+
onDragOver: (event) => event.preventDefault(),
|
|
380
|
+
onDrop: (event) => handleDrop(event, column, index),
|
|
381
|
+
onKeyDown: (event) => handleCardKeyDown(event, card, column, index),
|
|
382
|
+
children: /* @__PURE__ */ jsx5(TaskCard, { card })
|
|
383
|
+
},
|
|
384
|
+
card.id
|
|
385
|
+
))
|
|
386
|
+
},
|
|
387
|
+
column.id
|
|
388
|
+
)) }),
|
|
389
|
+
/* @__PURE__ */ jsx5("div", { className: "eth-kanban-board__live", "aria-live": "polite", "aria-atomic": "true", children: announcement })
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/components/TaskTable.tsx
|
|
396
|
+
import { Badge as Badge4, StatusDot as StatusDot2, Tag as Tag3 } from "@echothink-ui/core";
|
|
397
|
+
import { DataTable } from "@echothink-ui/data";
|
|
398
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
399
|
+
var columns = [
|
|
400
|
+
{
|
|
401
|
+
key: "title",
|
|
402
|
+
header: "Task",
|
|
403
|
+
render: (task) => /* @__PURE__ */ jsxs6("div", { className: "eth-todo-task-table__title", children: [
|
|
404
|
+
/* @__PURE__ */ jsx6("strong", { children: task.title }),
|
|
405
|
+
task.labels?.map((label) => /* @__PURE__ */ jsx6(Tag3, { children: label }, label))
|
|
406
|
+
] })
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
key: "status",
|
|
410
|
+
header: "Status",
|
|
411
|
+
render: (task) => task.status ? /* @__PURE__ */ jsx6(StatusDot2, { status: task.status, label: task.status }) : null
|
|
412
|
+
},
|
|
413
|
+
{ key: "assignee", header: "Assignee" },
|
|
414
|
+
{
|
|
415
|
+
key: "priority",
|
|
416
|
+
header: "Priority",
|
|
417
|
+
render: (task) => task.priority ? /* @__PURE__ */ jsx6(Badge4, { children: task.priority }) : null
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
key: "dueAt",
|
|
421
|
+
header: "Due",
|
|
422
|
+
render: (task) => task.dueAt ? /* @__PURE__ */ jsx6("time", { dateTime: task.dueAt, children: formatDateTime(task.dueAt) }) : null
|
|
423
|
+
}
|
|
424
|
+
];
|
|
425
|
+
function TaskTable({
|
|
426
|
+
tasks = [],
|
|
427
|
+
density = "default",
|
|
428
|
+
selectable,
|
|
429
|
+
rowActions,
|
|
430
|
+
className,
|
|
431
|
+
...props
|
|
432
|
+
}) {
|
|
433
|
+
return /* @__PURE__ */ jsx6(
|
|
434
|
+
"div",
|
|
435
|
+
{
|
|
436
|
+
...props,
|
|
437
|
+
className: `eth-todo-task-table ${className ?? ""}`,
|
|
438
|
+
"data-eth-component": "TaskTable",
|
|
439
|
+
children: /* @__PURE__ */ jsx6(
|
|
440
|
+
DataTable,
|
|
441
|
+
{
|
|
442
|
+
rows: tasks,
|
|
443
|
+
columns,
|
|
444
|
+
rowKey: "id",
|
|
445
|
+
density,
|
|
446
|
+
selectable,
|
|
447
|
+
rowActions
|
|
448
|
+
}
|
|
449
|
+
)
|
|
450
|
+
}
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// src/components/TaskDependencyList.tsx
|
|
455
|
+
import { Badge as Badge5, Panel as Panel2 } from "@echothink-ui/core";
|
|
456
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
457
|
+
function TaskDependencyList({
|
|
458
|
+
dependencies = [],
|
|
459
|
+
className,
|
|
460
|
+
...props
|
|
461
|
+
}) {
|
|
462
|
+
const groups = {
|
|
463
|
+
"blocked-by": dependencies.filter((dependency) => dependency.relation === "blocked-by"),
|
|
464
|
+
blocks: dependencies.filter((dependency) => dependency.relation === "blocks")
|
|
465
|
+
};
|
|
466
|
+
return /* @__PURE__ */ jsxs7(
|
|
467
|
+
Panel2,
|
|
468
|
+
{
|
|
469
|
+
...props,
|
|
470
|
+
className: `eth-todo-dependencies ${className ?? ""}`,
|
|
471
|
+
title: "Dependencies",
|
|
472
|
+
"data-eth-component": "TaskDependencyList",
|
|
473
|
+
children: [
|
|
474
|
+
/* @__PURE__ */ jsx7(DependencyGroup, { title: "Blocked by", dependencies: groups["blocked-by"] }),
|
|
475
|
+
/* @__PURE__ */ jsx7(DependencyGroup, { title: "Blocks", dependencies: groups.blocks })
|
|
476
|
+
]
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
function DependencyGroup({
|
|
481
|
+
title,
|
|
482
|
+
dependencies
|
|
483
|
+
}) {
|
|
484
|
+
return /* @__PURE__ */ jsxs7("section", { className: "eth-todo-dependencies__group", children: [
|
|
485
|
+
/* @__PURE__ */ jsx7("h3", { children: title }),
|
|
486
|
+
dependencies.length ? /* @__PURE__ */ jsx7("ul", { children: dependencies.map((dependency) => /* @__PURE__ */ jsxs7("li", { children: [
|
|
487
|
+
/* @__PURE__ */ jsx7("span", { children: dependency.title }),
|
|
488
|
+
/* @__PURE__ */ jsx7(Badge5, { children: dependency.status })
|
|
489
|
+
] }, dependency.id)) }) : /* @__PURE__ */ jsx7("p", { children: "None" })
|
|
490
|
+
] });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// src/components/TaskTimeline.tsx
|
|
494
|
+
import * as React4 from "react";
|
|
495
|
+
import { Panel as Panel3, Tag as Tag4 } from "@echothink-ui/core";
|
|
496
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
497
|
+
function TaskTimeline({ events = [], className, ...props }) {
|
|
498
|
+
const sortedEvents = React4.useMemo(
|
|
499
|
+
() => [...events].sort(
|
|
500
|
+
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
501
|
+
),
|
|
502
|
+
[events]
|
|
503
|
+
);
|
|
504
|
+
return /* @__PURE__ */ jsx8(
|
|
505
|
+
Panel3,
|
|
506
|
+
{
|
|
507
|
+
...props,
|
|
508
|
+
className: `eth-todo-timeline ${className ?? ""}`,
|
|
509
|
+
title: "Task timeline",
|
|
510
|
+
"data-eth-component": "TaskTimeline",
|
|
511
|
+
children: /* @__PURE__ */ jsx8("ol", { className: "eth-todo-timeline__events", children: sortedEvents.map((event) => /* @__PURE__ */ jsxs8("li", { className: "eth-todo-timeline__event", children: [
|
|
512
|
+
/* @__PURE__ */ jsx8("time", { dateTime: event.timestamp, children: formatDateTime(event.timestamp) }),
|
|
513
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
514
|
+
/* @__PURE__ */ jsx8(Tag4, { children: event.kind }),
|
|
515
|
+
event.actor ? /* @__PURE__ */ jsx8("span", { children: event.actor }) : null,
|
|
516
|
+
/* @__PURE__ */ jsx8("p", { children: event.summary })
|
|
517
|
+
] })
|
|
518
|
+
] }, event.id)) })
|
|
519
|
+
}
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/index.tsx
|
|
524
|
+
var TodoComponentNames = [
|
|
525
|
+
"TodoList",
|
|
526
|
+
"TodoItem",
|
|
527
|
+
"KanbanBoard",
|
|
528
|
+
"KanbanColumn",
|
|
529
|
+
"TaskCard",
|
|
530
|
+
"TaskTable",
|
|
531
|
+
"TaskDependencyList",
|
|
532
|
+
"TaskTimeline"
|
|
533
|
+
];
|
|
534
|
+
export {
|
|
535
|
+
KanbanBoard,
|
|
536
|
+
KanbanColumn,
|
|
537
|
+
TaskCard,
|
|
538
|
+
TaskDependencyList,
|
|
539
|
+
TaskTable,
|
|
540
|
+
TaskTimeline,
|
|
541
|
+
TodoComponentNames,
|
|
542
|
+
TodoItem,
|
|
543
|
+
TodoList
|
|
544
|
+
};
|
|
545
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/TodoList.tsx","../src/components/TodoItem.tsx","../src/utils.ts","../src/components/KanbanBoard.tsx","../src/components/KanbanColumn.tsx","../src/components/TaskCard.tsx","../src/components/TaskTable.tsx","../src/components/TaskDependencyList.tsx","../src/components/TaskTimeline.tsx","../src/index.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Button, Panel, TextInput } from \"@echothink-ui/core\";\nimport { PlusIcon } from \"@echothink-ui/icons\";\nimport type { TodoTaskItem } from \"../types\";\nimport { TodoItem } from \"./TodoItem\";\n\nexport interface TodoListProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onToggle\"> {\n items?: TodoTaskItem[];\n onToggle?: (id: string) => void;\n onAdd?: (label: string) => void;\n}\n\nexport function TodoList({ items = [], onToggle, onAdd, className, ...props }: TodoListProps) {\n const [label, setLabel] = React.useState(\"\");\n const completedCount = items.filter((item) => item.done).length;\n const openCount = items.length - completedCount;\n const summary = items.length\n ? `${openCount} open / ${completedCount} done`\n : \"No tasks yet\";\n\n const submit = (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!label.trim() || !onAdd) return;\n onAdd(label.trim());\n setLabel(\"\");\n };\n\n return (\n <Panel\n {...props}\n className={`eth-todo-list ${className ?? \"\"}`}\n title=\"To-do\"\n subtitle={summary}\n data-eth-component=\"TodoList\"\n >\n {items.length ? (\n <div\n className=\"eth-todo-list__items\"\n role=\"list\"\n aria-label=\"Tasks\"\n >\n {items.map((item) => (\n <TodoItem key={item.id} item={item} onToggle={onToggle} role=\"listitem\" />\n ))}\n </div>\n ) : (\n <p className=\"eth-todo-list__empty\">No tasks have been added.</p>\n )}\n {onAdd ? (\n <form className=\"eth-todo-list__add\" aria-label=\"Add task\" onSubmit={submit}>\n <TextInput\n value={label}\n labelText=\"New task\"\n hideLabel\n placeholder=\"Add a task\"\n onChange={(event) => setLabel(event.currentTarget.value)}\n />\n <Button type=\"submit\" icon={<PlusIcon />} disabled={!label.trim()}>\n Add\n </Button>\n </form>\n ) : null}\n </Panel>\n );\n}\n","import * as React from \"react\";\nimport { Badge, Checkbox, Tag } from \"@echothink-ui/core\";\nimport type { TodoTaskItem } from \"../types\";\nimport { formatDateTime, priorityLabel, prioritySeverity } from \"../utils\";\n\nexport interface TodoItemProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onToggle\"> {\n item: TodoTaskItem;\n onToggle?: (id: string) => void;\n}\n\nfunction validDateTimeAttribute(value: string | undefined) {\n if (!value) return undefined;\n return Number.isNaN(new Date(value).getTime()) ? undefined : value;\n}\n\nexport function TodoItem({ item, onToggle, className, ...props }: TodoItemProps) {\n const hasMeta = Boolean(item.assignee || item.priority || item.dueAt);\n const dueDateTime = validDateTimeAttribute(item.dueAt);\n const itemClassName = [\n \"eth-todo-item\",\n hasMeta ? \"eth-todo-item--has-meta\" : \"\",\n item.done ? \"eth-todo-item--done\" : \"\",\n className ?? \"\"\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n {...props}\n className={itemClassName}\n data-priority={item.priority}\n data-state={item.done ? \"done\" : \"open\"}\n data-eth-component=\"TodoItem\"\n >\n <div className=\"eth-todo-item__content eth-todo-item__primary\">\n <Checkbox\n checked={item.done}\n className=\"eth-todo-item__checkbox\"\n label={<span className=\"eth-todo-item__label\">{item.label}</span>}\n onChange={() => onToggle?.(item.id)}\n />\n </div>\n {hasMeta ? (\n <div className=\"eth-todo-item__meta\" role=\"group\" aria-label=\"Task details\">\n {item.priority ? (\n <Badge className=\"eth-todo-item__tag\" severity={prioritySeverity(item.priority)}>\n {priorityLabel(item.priority)}\n </Badge>\n ) : null}\n {item.assignee ? <Tag className=\"eth-todo-item__tag\">{item.assignee}</Tag> : null}\n {item.dueAt ? (\n <span className=\"eth-todo-item__due\">\n <span className=\"eth-todo-item__due-label\">Due</span>\n <time dateTime={dueDateTime}>{formatDateTime(item.dueAt)}</time>\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n );\n}\n","export function formatDateTime(value: string | undefined) {\n if (!value) return \"\";\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return value;\n return new Intl.DateTimeFormat(undefined, {\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\"\n }).format(date);\n}\n\nexport function priorityLabel(priority: string | undefined) {\n return priority ?? \"normal\";\n}\n\nexport function prioritySeverity(\n priority: string | undefined\n): \"danger\" | \"warning\" | \"info\" | \"neutral\" {\n const normalized = priority?.toLowerCase();\n if (normalized === \"urgent\" || normalized === \"critical\" || normalized === \"high\")\n return \"danger\";\n if (normalized === \"medium\") return \"warning\";\n if (normalized === \"low\") return \"info\";\n return \"neutral\";\n}\n","import * as React from \"react\";\nimport type { KanbanCard, KanbanColumnModel } from \"../types\";\nimport { KanbanColumn } from \"./KanbanColumn\";\nimport { TaskCard } from \"./TaskCard\";\n\nexport interface KanbanBoardProps extends React.HTMLAttributes<HTMLDivElement> {\n columns?: KanbanColumnModel[];\n onCardMove?: (cardId: string, fromColumn: string, toColumn: string, index: number) => void;\n onAddCard?: (columnId: string) => void;\n}\n\ninterface LiftedCard {\n cardId: string;\n fromColumn: string;\n toColumn: string;\n index: number;\n}\n\nexport function KanbanBoard({\n columns = [],\n onCardMove,\n onAddCard,\n className,\n ...props\n}: KanbanBoardProps) {\n const [announcement, setAnnouncement] = React.useState(\"\");\n const [lifted, setLifted] = React.useState<LiftedCard | null>(null);\n\n const findCard = (cardId: string) => {\n for (const column of columns) {\n const index = column.items.findIndex((item) => item.id === cardId);\n if (index >= 0) return { column, index, card: column.items[index] };\n }\n return undefined;\n };\n\n const announce = (message: string) => setAnnouncement(message);\n\n const moveLifted = (direction: \"left\" | \"right\" | \"up\" | \"down\") => {\n if (!lifted) return;\n const currentColumnIndex = columns.findIndex((column) => column.id === lifted.toColumn);\n if (currentColumnIndex < 0) return;\n if (direction === \"left\" || direction === \"right\") {\n const offset = direction === \"left\" ? -1 : 1;\n const nextColumn = columns[currentColumnIndex + offset];\n if (!nextColumn) return;\n const nextIndex = Math.min(lifted.index, Math.max(0, nextColumn.items.length));\n setLifted({ ...lifted, toColumn: nextColumn.id, index: nextIndex });\n announce(`Moved ${lifted.cardId} to ${nextColumn.title}, position ${nextIndex + 1}`);\n return;\n }\n const currentColumn = columns[currentColumnIndex];\n const maxIndex = Math.max(0, currentColumn.items.length - 1);\n const nextIndex =\n direction === \"up\" ? Math.max(0, lifted.index - 1) : Math.min(maxIndex, lifted.index + 1);\n setLifted({ ...lifted, index: nextIndex });\n announce(`Moved ${lifted.cardId} to position ${nextIndex + 1}`);\n };\n\n const handleCardKeyDown = (\n event: React.KeyboardEvent<HTMLDivElement>,\n card: KanbanCard,\n column: KanbanColumnModel,\n index: number\n ) => {\n if (event.key === \" \") {\n event.preventDefault();\n setLifted({ cardId: card.id, fromColumn: column.id, toColumn: column.id, index });\n announce(`Lifted ${card.title}`);\n return;\n }\n if (!lifted) return;\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n moveLifted(\"left\");\n }\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n moveLifted(\"right\");\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n moveLifted(\"up\");\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n moveLifted(\"down\");\n }\n if (event.key === \"Enter\") {\n event.preventDefault();\n onCardMove?.(lifted.cardId, lifted.fromColumn, lifted.toColumn, lifted.index);\n announce(`Dropped ${lifted.cardId}`);\n setLifted(null);\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n announce(`Cancelled move for ${lifted.cardId}`);\n setLifted(null);\n }\n };\n\n const handleDrop = (event: React.DragEvent, toColumn: KanbanColumnModel, index: number) => {\n event.preventDefault();\n const cardId = event.dataTransfer.getData(\"text/plain\");\n const source = findCard(cardId);\n if (!source) return;\n onCardMove?.(cardId, source.column.id, toColumn.id, index);\n announce(`Moved ${source.card.title} to ${toColumn.title}`);\n };\n\n return (\n <div\n {...props}\n className={`eth-kanban-board ${className ?? \"\"}`}\n data-eth-component=\"KanbanBoard\"\n >\n <div className=\"eth-kanban-board__columns\" role=\"list\" aria-label=\"Kanban columns\">\n {columns.map((column) => (\n <KanbanColumn\n key={column.id}\n column={column}\n role=\"listitem\"\n onAddCard={onAddCard}\n onDragOver={(event) => event.preventDefault()}\n onDrop={(event) => handleDrop(event, column, column.items.length)}\n >\n {column.items.map((card, index) => (\n <div\n key={card.id}\n className={`eth-kanban-card-shell ${\n lifted?.cardId === card.id ? \"eth-kanban-card-shell--lifted\" : \"\"\n }`}\n draggable\n tabIndex={0}\n role=\"listitem\"\n aria-label={`${card.title}, ${column.title}, position ${index + 1} of ${\n column.items.length\n }`}\n aria-grabbed={lifted?.cardId === card.id}\n aria-roledescription=\"Draggable task card\"\n aria-keyshortcuts=\"Space ArrowLeft ArrowRight ArrowUp ArrowDown Enter Escape\"\n onDragStart={(event) => {\n event.dataTransfer.setData(\"text/plain\", card.id);\n event.dataTransfer.effectAllowed = \"move\";\n }}\n onDragOver={(event) => event.preventDefault()}\n onDrop={(event) => handleDrop(event, column, index)}\n onKeyDown={(event) => handleCardKeyDown(event, card, column, index)}\n >\n <TaskCard card={card} />\n </div>\n ))}\n </KanbanColumn>\n ))}\n </div>\n <div className=\"eth-kanban-board__live\" aria-live=\"polite\" aria-atomic=\"true\">\n {announcement}\n </div>\n </div>\n );\n}\n","import * as React from \"react\";\nimport { Badge, IconButton } from \"@echothink-ui/core\";\nimport type { KanbanColumnModel } from \"../types\";\n\nexport interface KanbanColumnProps extends React.HTMLAttributes<HTMLDivElement> {\n column: KanbanColumnModel;\n children?: React.ReactNode;\n onAddCard?: (columnId: string) => void;\n}\n\nexport function KanbanColumn({\n column,\n children,\n onAddCard,\n className,\n \"aria-label\": ariaLabel,\n ...props\n}: KanbanColumnProps) {\n const itemCount = column.items.length;\n const taskLabel = `${itemCount} ${itemCount === 1 ? \"task\" : \"tasks\"}`;\n const wipLimit = column.wipLimit;\n const hasWipLimit = typeof wipLimit === \"number\";\n const overLimit = hasWipLimit ? itemCount > wipLimit : false;\n const atLimit = hasWipLimit ? itemCount === wipLimit : false;\n const hasRenderedChildren = React.Children.count(children) > 0;\n const titleId = React.useId();\n const { \"aria-labelledby\": ariaLabelledBy, ...sectionProps } = props;\n\n let wipPercent = 0;\n let wipStatusLabel = \"\";\n let wipSeverity: \"danger\" | \"warning\" | \"neutral\" = \"neutral\";\n\n if (hasWipLimit) {\n wipPercent =\n wipLimit > 0 ? Math.min(100, (itemCount / wipLimit) * 100) : itemCount > 0 ? 100 : 0;\n const remaining = wipLimit - itemCount;\n wipStatusLabel = overLimit\n ? `${Math.abs(remaining)} over limit`\n : remaining === 0\n ? \"At limit\"\n : `${remaining} ${remaining === 1 ? \"slot\" : \"slots\"} open`;\n if (overLimit) wipSeverity = \"danger\";\n else if (atLimit) wipSeverity = \"warning\";\n }\n\n return (\n <section\n {...sectionProps}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabel ? undefined : (ariaLabelledBy ?? titleId)}\n className={`eth-kanban-column ${overLimit ? \"eth-kanban-column--over-limit\" : \"\"} ${\n className ?? \"\"\n }`}\n data-eth-component=\"KanbanColumn\"\n >\n <header className=\"eth-kanban-column__header\">\n <div className=\"eth-kanban-column__header-main\">\n <div className=\"eth-kanban-column__heading\">\n <h3 id={titleId}>{column.title}</h3>\n <span className=\"eth-kanban-column__count\">{taskLabel}</span>\n </div>\n {onAddCard ? (\n <div className=\"eth-kanban-column__actions\">\n <IconButton\n className=\"eth-kanban-column__add\"\n intent=\"ghost\"\n density=\"compact\"\n label={`Add task to ${column.title}`}\n icon={\n <span className=\"eth-kanban-column__add-glyph\" aria-hidden=\"true\">\n +\n </span>\n }\n onClick={() => onAddCard(column.id)}\n />\n </div>\n ) : null}\n </div>\n {hasWipLimit ? (\n <div className=\"eth-kanban-column__metrics\">\n <div\n className=\"eth-kanban-column__limit\"\n aria-label={`Work in progress ${itemCount} of ${wipLimit}`}\n >\n <div className=\"eth-kanban-column__limit-row\">\n <Badge severity={wipSeverity}>WIP {itemCount}/{wipLimit}</Badge>\n <span className=\"eth-kanban-column__limit-note\">{wipStatusLabel}</span>\n </div>\n <span className=\"eth-kanban-column__meter\" aria-hidden=\"true\">\n <span style={{ inlineSize: `${wipPercent}%` }} />\n </span>\n </div>\n </div>\n ) : null}\n </header>\n <div className=\"eth-kanban-column__body\" role=\"list\" aria-label={`${column.title} tasks`}>\n {hasRenderedChildren ? (\n children\n ) : itemCount === 0 ? (\n <div className=\"eth-kanban-column__empty\">No tasks in this column</div>\n ) : null}\n </div>\n </section>\n );\n}\n","import * as React from \"react\";\nimport { Badge, Surface, StatusDot, Tag, type EthOperationalStatus } from \"@echothink-ui/core\";\nimport type { KanbanCard } from \"../types\";\nimport { formatDateTime, priorityLabel, prioritySeverity } from \"../utils\";\n\nexport interface TaskCardProps extends React.HTMLAttributes<HTMLElement> {\n card: KanbanCard;\n}\n\nconst operationalStatuses = new Set<string>([\n \"queued\",\n \"running\",\n \"paused\",\n \"blocked\",\n \"failed\",\n \"succeeded\",\n \"warning\",\n \"stale\",\n \"synced\",\n \"pending-approval\",\n \"approval-required\",\n \"in-progress\",\n \"not-started\",\n \"completed\",\n \"active\",\n \"inactive\"\n]);\n\nfunction isOperationalStatus(status: string | undefined): status is EthOperationalStatus {\n return Boolean(status && operationalStatuses.has(status));\n}\n\nfunction statusLabel(status: string) {\n return status.replace(/-/g, \" \");\n}\n\nexport function TaskCard({ card, className, ...props }: TaskCardProps) {\n return (\n <Surface\n {...props}\n className={`eth-todo-task-card ${className ?? \"\"}`}\n data-priority={card.priority}\n data-status={card.status}\n data-eth-component=\"TaskCard\"\n >\n <div className=\"eth-todo-task-card__header\">\n <strong>{card.title}</strong>\n {card.priority ? (\n <Badge severity={prioritySeverity(card.priority)}>{priorityLabel(card.priority)}</Badge>\n ) : null}\n </div>\n <div className=\"eth-todo-task-card__meta\">\n {card.assignee ? <Tag>{card.assignee}</Tag> : null}\n {isOperationalStatus(card.status) ? (\n <StatusDot status={card.status} label={statusLabel(card.status)} />\n ) : card.status ? (\n <Tag>{card.status}</Tag>\n ) : null}\n {card.dueAt ? (\n <time className=\"eth-todo-task-card__due\" dateTime={card.dueAt}>\n {formatDateTime(card.dueAt)}\n </time>\n ) : null}\n </div>\n {card.labels?.length ? (\n <div className=\"eth-todo-task-card__labels\">\n {card.labels.map((label) => (\n <Tag key={label}>{label}</Tag>\n ))}\n </div>\n ) : null}\n </Surface>\n );\n}\n","import * as React from \"react\";\nimport { Badge, StatusDot, Tag } from \"@echothink-ui/core\";\nimport { DataTable, type DataColumn } from \"@echothink-ui/data\";\nimport type { TaskRowActions, TaskTableTask } from \"../types\";\nimport { formatDateTime } from \"../utils\";\n\nexport interface TaskTableProps extends React.HTMLAttributes<HTMLDivElement> {\n tasks?: TaskTableTask[];\n density?: \"compact\" | \"default\" | \"comfortable\";\n selectable?: boolean;\n rowActions?: TaskRowActions;\n}\n\nconst columns: DataColumn<TaskTableTask>[] = [\n {\n key: \"title\",\n header: \"Task\",\n render: (task) => (\n <div className=\"eth-todo-task-table__title\">\n <strong>{task.title}</strong>\n {task.labels?.map((label) => (\n <Tag key={label}>{label}</Tag>\n ))}\n </div>\n )\n },\n {\n key: \"status\",\n header: \"Status\",\n render: (task) => (task.status ? <StatusDot status={task.status} label={task.status} /> : null)\n },\n { key: \"assignee\", header: \"Assignee\" },\n {\n key: \"priority\",\n header: \"Priority\",\n render: (task) => (task.priority ? <Badge>{task.priority}</Badge> : null)\n },\n {\n key: \"dueAt\",\n header: \"Due\",\n render: (task) => (task.dueAt ? <time dateTime={task.dueAt}>{formatDateTime(task.dueAt)}</time> : null)\n }\n];\n\nexport function TaskTable({\n tasks = [],\n density = \"default\",\n selectable,\n rowActions,\n className,\n ...props\n}: TaskTableProps) {\n return (\n <div\n {...props}\n className={`eth-todo-task-table ${className ?? \"\"}`}\n data-eth-component=\"TaskTable\"\n >\n <DataTable\n rows={tasks}\n columns={columns}\n rowKey=\"id\"\n density={density}\n selectable={selectable}\n rowActions={rowActions}\n />\n </div>\n );\n}\n","import * as React from \"react\";\nimport { Badge, Panel } from \"@echothink-ui/core\";\nimport type { TaskDependency } from \"../types\";\n\nexport interface TaskDependencyListProps extends React.HTMLAttributes<HTMLDivElement> {\n dependencies?: TaskDependency[];\n}\n\nexport function TaskDependencyList({\n dependencies = [],\n className,\n ...props\n}: TaskDependencyListProps) {\n const groups = {\n \"blocked-by\": dependencies.filter((dependency) => dependency.relation === \"blocked-by\"),\n blocks: dependencies.filter((dependency) => dependency.relation === \"blocks\")\n };\n\n return (\n <Panel\n {...props}\n className={`eth-todo-dependencies ${className ?? \"\"}`}\n title=\"Dependencies\"\n data-eth-component=\"TaskDependencyList\"\n >\n <DependencyGroup title=\"Blocked by\" dependencies={groups[\"blocked-by\"]} />\n <DependencyGroup title=\"Blocks\" dependencies={groups.blocks} />\n </Panel>\n );\n}\n\nfunction DependencyGroup({\n title,\n dependencies\n}: {\n title: string;\n dependencies: TaskDependency[];\n}) {\n return (\n <section className=\"eth-todo-dependencies__group\">\n <h3>{title}</h3>\n {dependencies.length ? (\n <ul>\n {dependencies.map((dependency) => (\n <li key={dependency.id}>\n <span>{dependency.title}</span>\n <Badge>{dependency.status}</Badge>\n </li>\n ))}\n </ul>\n ) : (\n <p>None</p>\n )}\n </section>\n );\n}\n","import * as React from \"react\";\nimport { Panel, Tag } from \"@echothink-ui/core\";\nimport type { TaskTimelineEvent } from \"../types\";\nimport { formatDateTime } from \"../utils\";\n\nexport interface TaskTimelineProps extends React.HTMLAttributes<HTMLDivElement> {\n events?: TaskTimelineEvent[];\n}\n\nexport function TaskTimeline({ events = [], className, ...props }: TaskTimelineProps) {\n const sortedEvents = React.useMemo(\n () =>\n [...events].sort(\n (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()\n ),\n [events]\n );\n\n return (\n <Panel\n {...props}\n className={`eth-todo-timeline ${className ?? \"\"}`}\n title=\"Task timeline\"\n data-eth-component=\"TaskTimeline\"\n >\n <ol className=\"eth-todo-timeline__events\">\n {sortedEvents.map((event) => (\n <li key={event.id} className=\"eth-todo-timeline__event\">\n <time dateTime={event.timestamp}>{formatDateTime(event.timestamp)}</time>\n <div>\n <Tag>{event.kind}</Tag>\n {event.actor ? <span>{event.actor}</span> : null}\n <p>{event.summary}</p>\n </div>\n </li>\n ))}\n </ol>\n </Panel>\n );\n}\n","import \"./styles.css\";\n\nexport * from \"./types\";\nexport { TodoList, type TodoListProps } from \"./components/TodoList\";\nexport { TodoItem, type TodoItemProps } from \"./components/TodoItem\";\nexport { KanbanBoard, type KanbanBoardProps } from \"./components/KanbanBoard\";\nexport { KanbanColumn, type KanbanColumnProps } from \"./components/KanbanColumn\";\nexport { TaskCard, type TaskCardProps } from \"./components/TaskCard\";\nexport { TaskTable, type TaskTableProps } from \"./components/TaskTable\";\nexport {\n TaskDependencyList,\n type TaskDependencyListProps\n} from \"./components/TaskDependencyList\";\nexport { TaskTimeline, type TaskTimelineProps } from \"./components/TaskTimeline\";\n\nexport const TodoComponentNames = [\n \"TodoList\",\n \"TodoItem\",\n \"KanbanBoard\",\n \"KanbanColumn\",\n \"TaskCard\",\n \"TaskTable\",\n \"TaskDependencyList\",\n \"TaskTimeline\"\n] as const;\nexport type TodoComponentName = (typeof TodoComponentNames)[number];\n"],"mappings":"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","names":["jsx","jsxs","React","React","Badge","jsx","jsxs","Badge","Tag","jsx","jsxs","Badge","Tag","jsx","jsxs","columns","nextIndex","Badge","StatusDot","Tag","jsx","jsxs","Tag","StatusDot","Badge","Badge","Panel","jsx","jsxs","React","Panel","Tag","jsx","jsxs","Panel","Tag"]}
|