@dxos/react-ui-task 0.11.0 → 0.12.0-next-2800d0376abcc62d5544afdd744b16b2b960434e
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/lib/index.mjs +627 -96
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/translations.mjs +17 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/types/src/components/TaskList/TaskList.d.ts +41 -12
- package/dist/types/src/components/TaskList/TaskList.d.ts.map +1 -1
- package/dist/types/src/components/TaskList/TaskList.stories.d.ts +31 -3
- package/dist/types/src/components/TaskList/TaskList.stories.d.ts.map +1 -1
- package/dist/types/src/components/TaskList/dnd.d.ts +27 -0
- package/dist/types/src/components/TaskList/dnd.d.ts.map +1 -0
- package/dist/types/src/components/TaskList/hierarchy.d.ts +73 -0
- package/dist/types/src/components/TaskList/hierarchy.d.ts.map +1 -0
- package/dist/types/src/components/TaskList/hierarchy.test.d.ts +2 -0
- package/dist/types/src/components/TaskList/hierarchy.test.d.ts.map +1 -0
- package/dist/types/src/components/TaskList/index.d.ts +1 -0
- package/dist/types/src/components/TaskList/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +17 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +26 -14
- package/src/components/TaskList/TaskList.stories.tsx +258 -14
- package/src/components/TaskList/TaskList.tsx +743 -100
- package/src/components/TaskList/dnd.ts +46 -0
- package/src/components/TaskList/hierarchy.test.ts +155 -0
- package/src/components/TaskList/hierarchy.ts +191 -0
- package/src/components/TaskList/index.ts +1 -0
- package/src/translations.ts +25 -0
package/dist/lib/index.mjs
CHANGED
|
@@ -1,60 +1,262 @@
|
|
|
1
|
+
import { TaskSet } from "@dxos/types";
|
|
2
|
+
import { attachInstruction, extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
|
|
3
|
+
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
|
|
4
|
+
import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
|
|
5
|
+
import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
|
|
6
|
+
import { useComposedRefs } from "@radix-ui/react-compose-refs";
|
|
1
7
|
import { createContext } from "@radix-ui/react-context";
|
|
2
|
-
import { Fragment, useCallback, useMemo, useState } from "react";
|
|
8
|
+
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
9
|
import { useObject } from "@dxos/echo-react";
|
|
4
|
-
import { Icon, IconButton, Input, Tag, composable, composableProps } from "@dxos/react-ui";
|
|
5
|
-
import { Listbox } from "@dxos/react-ui-list";
|
|
10
|
+
import { Icon, IconButton, Input, Tag, composable, composableProps, useTranslation } from "@dxos/react-ui";
|
|
11
|
+
import { Listbox, TreeDropIndicator, TreeItemToggle, paddingIndentation, useListDisclosure } from "@dxos/react-ui-list";
|
|
12
|
+
import { MarkdownView } from "@dxos/react-ui-markdown";
|
|
6
13
|
import { mx } from "@dxos/ui-theme";
|
|
14
|
+
import { translationKey } from "#translations";
|
|
7
15
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
+
//#region src/components/TaskList/hierarchy.ts
|
|
17
|
+
/**
|
|
18
|
+
* Walks the set's tree in sibling order, omitting the descendants of any collapsed task.
|
|
19
|
+
* Cycle-safe: a malformed `parentTask` loop is visited once and then skipped, so a corrupt set
|
|
20
|
+
* renders short rather than hanging the list.
|
|
21
|
+
*/
|
|
22
|
+
var walkTaskTree = (tasks, collapsed) => {
|
|
23
|
+
const rows = [];
|
|
24
|
+
const seen = /* @__PURE__ */ new Set();
|
|
25
|
+
const visit = (siblings, level, ancestors) => {
|
|
26
|
+
siblings.forEach((task, index) => {
|
|
27
|
+
if (seen.has(task.id)) return;
|
|
28
|
+
seen.add(task.id);
|
|
29
|
+
const children = TaskSet.subTasks(tasks, task);
|
|
30
|
+
rows.push({
|
|
31
|
+
task,
|
|
32
|
+
level,
|
|
33
|
+
branch: children.length > 0,
|
|
34
|
+
position: index + 1,
|
|
35
|
+
setSize: siblings.length,
|
|
36
|
+
ancestors
|
|
37
|
+
});
|
|
38
|
+
if (children.length > 0 && !collapsed?.has(task.id)) visit(children, level + 1, [...ancestors, task.id]);
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
visit(TaskSet.rootTasks(tasks), 1, []);
|
|
42
|
+
return rows;
|
|
43
|
+
};
|
|
44
|
+
/** Every task transitively under `task`, including `task` itself. Cycle-safe. */
|
|
45
|
+
var subtreeIds = (tasks, task) => {
|
|
46
|
+
const ids = /* @__PURE__ */ new Set();
|
|
47
|
+
const visit = (current) => {
|
|
48
|
+
if (ids.has(current.id)) return;
|
|
49
|
+
ids.add(current.id);
|
|
50
|
+
for (const child of TaskSet.subTasks(tasks, current)) visit(child);
|
|
51
|
+
};
|
|
52
|
+
visit(task);
|
|
53
|
+
return ids;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Resolves a drop into a single `MoveTask` call, or `undefined` when the gesture is rejected.
|
|
57
|
+
*
|
|
58
|
+
* Rejected rather than silently ignored, so the caller can say no with the cursor:
|
|
59
|
+
* - onto itself, or into its own subtree (the cycle `UpdateTask` rejects);
|
|
60
|
+
* - onto a task from another set — a membership transfer, not a reorder.
|
|
61
|
+
*/
|
|
62
|
+
var resolveTaskPlacement = ({ tasks, source, target, intent }) => {
|
|
63
|
+
const present = new Set(tasks.map((task) => task.id));
|
|
64
|
+
if (!present.has(source.id) || !present.has(target.id)) return;
|
|
65
|
+
if (subtreeIds(tasks, source).has(target.id)) return;
|
|
66
|
+
if (intent === "make-child") return {
|
|
67
|
+
parentTask: target,
|
|
68
|
+
before: void 0
|
|
69
|
+
};
|
|
70
|
+
const parentId = TaskSet.parentTaskId(target);
|
|
71
|
+
const parent = parentId === void 0 ? null : tasks.find((task) => task.id === parentId) ?? null;
|
|
72
|
+
const siblings = parent === null ? TaskSet.rootTasks(tasks) : TaskSet.subTasks(tasks, parent);
|
|
73
|
+
const index = siblings.findIndex((task) => task.id === target.id);
|
|
74
|
+
if (intent === "reorder-above") return {
|
|
75
|
+
parentTask: parent,
|
|
76
|
+
before: target
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
parentTask: parent,
|
|
80
|
+
before: siblings.slice(index + 1).find((task) => task.id !== source.id)
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
/** A task's siblings (in sibling order) and its index among them. */
|
|
84
|
+
var siblingsOf = (tasks, task) => {
|
|
85
|
+
const parentId = TaskSet.parentTaskId(task);
|
|
86
|
+
const parent = parentId === void 0 ? null : tasks.find((candidate) => candidate.id === parentId) ?? null;
|
|
87
|
+
const siblings = parent === null ? TaskSet.rootTasks(tasks) : TaskSet.subTasks(tasks, parent);
|
|
88
|
+
return {
|
|
89
|
+
parent,
|
|
90
|
+
siblings,
|
|
91
|
+
index: siblings.findIndex((candidate) => candidate.id === task.id)
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Indent: the task becomes the last child of its previous sibling — the outliner's `Tab`. Undefined
|
|
96
|
+
* when it is the first among its siblings and so has nothing to indent under.
|
|
97
|
+
*/
|
|
98
|
+
var resolveIndent = (tasks, task) => {
|
|
99
|
+
const { siblings, index } = siblingsOf(tasks, task);
|
|
100
|
+
const previous = index > 0 ? siblings[index - 1] : void 0;
|
|
101
|
+
return previous ? {
|
|
102
|
+
parentTask: previous,
|
|
103
|
+
before: void 0
|
|
104
|
+
} : void 0;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Outdent: the task becomes the next sibling of its parent — the outliner's `Shift-Tab`. Undefined
|
|
108
|
+
* for a task already at the root.
|
|
109
|
+
*/
|
|
110
|
+
var resolveOutdent = (tasks, task) => {
|
|
111
|
+
const { parent } = siblingsOf(tasks, task);
|
|
112
|
+
if (!parent) return;
|
|
113
|
+
const { parent: grandparent, siblings, index } = siblingsOf(tasks, parent);
|
|
114
|
+
return {
|
|
115
|
+
parentTask: grandparent,
|
|
116
|
+
before: siblings[index + 1]
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Move within the current parent — `Alt-ArrowUp` / `Alt-ArrowDown`. Undefined at either end, so the
|
|
121
|
+
* key is a no-op rather than silently jumping the task out of its branch.
|
|
122
|
+
*/
|
|
123
|
+
var resolveNudge = (tasks, task, direction) => {
|
|
124
|
+
const { parent, siblings, index } = siblingsOf(tasks, task);
|
|
125
|
+
if (direction === "up") return index > 0 ? {
|
|
126
|
+
parentTask: parent,
|
|
127
|
+
before: siblings[index - 1]
|
|
128
|
+
} : void 0;
|
|
129
|
+
return index < siblings.length - 1 ? {
|
|
130
|
+
parentTask: parent,
|
|
131
|
+
before: siblings[index + 2]
|
|
132
|
+
} : void 0;
|
|
133
|
+
};
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/components/TaskList/dnd.ts
|
|
136
|
+
/**
|
|
137
|
+
* Drag payload for a task row. Tagged so a drop target can tell a task from any other draggable
|
|
138
|
+
* sharing the page (the navtree, a mosaic tile) rather than reading a bare `id` off it.
|
|
139
|
+
*/
|
|
140
|
+
var TASK_DRAG_TYPE = "dxos/task";
|
|
141
|
+
var isTaskDragData = (data) => data.type === "dxos/task" && typeof data.taskId === "string";
|
|
142
|
+
/**
|
|
143
|
+
* The hitbox's item mode, which decides where its bands sit: the last row of a group gets a
|
|
144
|
+
* shallower `reorder-below` band (there is nothing beneath it to reorder against), and an expanded
|
|
145
|
+
* branch gets none at all — below it is its own first child.
|
|
146
|
+
*/
|
|
147
|
+
var itemMode = ({ branch, open, last }) => branch && open ? "expanded" : last ? "last-in-group" : "standard";
|
|
148
|
+
/** The drop intent an instruction expresses, or undefined when it carries none (blocked). */
|
|
149
|
+
var dropIntent = (instruction) => {
|
|
150
|
+
switch (instruction?.type) {
|
|
151
|
+
case "reorder-above":
|
|
152
|
+
case "reorder-below":
|
|
153
|
+
case "make-child": return instruction.type;
|
|
154
|
+
default: return;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
//#endregion
|
|
8
158
|
//#region src/components/TaskList/TaskList.tsx
|
|
9
159
|
var TASK_LIST_NAME = "TaskList.Root";
|
|
10
160
|
/** Linear-style status groups, most active first. */
|
|
11
161
|
var STATUS_ORDER = [
|
|
12
|
-
"
|
|
162
|
+
"started",
|
|
13
163
|
"todo",
|
|
14
164
|
"done",
|
|
15
165
|
"failed",
|
|
16
166
|
"cancelled"
|
|
17
167
|
];
|
|
168
|
+
/** Fallback status labels. */
|
|
18
169
|
var DEFAULT_STATUS_LABELS = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
170
|
+
started: "Started",
|
|
171
|
+
todo: "To Do",
|
|
172
|
+
done: "Done",
|
|
173
|
+
failed: "Failed",
|
|
174
|
+
cancelled: "Cancelled"
|
|
24
175
|
};
|
|
25
|
-
/**
|
|
26
|
-
* One row geometry shared by `Item` and `Create` so their affordances land in the same columns:
|
|
27
|
-
* a fixed icon gutter (the status toggle above the add `+`), the flexible label, then trailing
|
|
28
|
-
* chips/actions pinned to the far edge. `w-full` is load-bearing — the listbox item is a flex
|
|
29
|
-
* container, so without it the grid shrinks to its content and the trailing actions float
|
|
30
|
-
* mid-row. `px-3` matches the listbox item's own inset, which `Create` does not inherit.
|
|
31
|
-
*/
|
|
32
|
-
var ROW_GRID = "grid grid-cols-[1.5rem_1fr_auto] items-center gap-2 w-full min-w-0 px-3 h-8";
|
|
33
176
|
var [TaskListProvider, useTaskListContext] = createContext(TASK_LIST_NAME);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
177
|
+
/** Shared empty set, so a list with nothing in flight does not allocate one per render. */
|
|
178
|
+
var EMPTY_IDS = /* @__PURE__ */ new Set();
|
|
179
|
+
var TaskListRoot = ({ children, tasks, groupByStatus = true, showGroupLabels = true, showOrdinals = false, showDescriptions = false, statusLabel = (status) => DEFAULT_STATUS_LABELS[status], onTaskCreate, onTaskUpdate, onTaskDelete, onTaskSelect, onTaskMove, selected: selectedProp, hierarchical = false, collapsed, onCollapsedChange }) => {
|
|
180
|
+
const [selectedState, setSelectedState] = useState(selectedProp);
|
|
181
|
+
const selected = selectedProp ?? selectedState;
|
|
182
|
+
const selectable = !!onTaskSelect || selectedProp !== void 0;
|
|
183
|
+
const handleValueChange = useCallback((id) => {
|
|
184
|
+
setSelectedState(id);
|
|
185
|
+
const task = tasks.find((task) => task.id === id);
|
|
186
|
+
if (task) onTaskSelect?.(task);
|
|
187
|
+
}, [tasks, onTaskSelect]);
|
|
188
|
+
const disclosure = useListDisclosure({
|
|
189
|
+
mode: "multi",
|
|
190
|
+
...collapsed !== void 0 || onCollapsedChange ? { value: collapsed } : {},
|
|
191
|
+
defaultValue: /* @__PURE__ */ new Set(),
|
|
192
|
+
onValueChange: onCollapsedChange
|
|
193
|
+
});
|
|
194
|
+
const isCollapsed = useCallback((id) => disclosure.bind(id).expanded, [disclosure]);
|
|
195
|
+
const onCollapseToggle = useCallback((id) => disclosure.bind(id).toggle(), [disclosure]);
|
|
196
|
+
const [draggingTask, setDraggingTask] = useState();
|
|
197
|
+
const dragging = useMemo(() => draggingTask ? subtreeIds(tasks, draggingTask) : EMPTY_IDS, [tasks, draggingTask]);
|
|
198
|
+
return /* @__PURE__ */ jsx(TaskListProvider, {
|
|
199
|
+
tasks,
|
|
200
|
+
groupByStatus: groupByStatus && !hierarchical,
|
|
201
|
+
showGroupLabels,
|
|
202
|
+
showOrdinals,
|
|
203
|
+
showDescriptions,
|
|
204
|
+
hierarchical,
|
|
205
|
+
showGutter: showOrdinals || !!onTaskMove,
|
|
206
|
+
statusLabel,
|
|
207
|
+
isCollapsed,
|
|
208
|
+
onCollapseToggle,
|
|
209
|
+
dragging,
|
|
210
|
+
onDraggingChange: setDraggingTask,
|
|
211
|
+
onTaskCreate,
|
|
212
|
+
onTaskUpdate,
|
|
213
|
+
onTaskDelete,
|
|
214
|
+
onTaskSelect,
|
|
215
|
+
onTaskMove,
|
|
216
|
+
selected,
|
|
217
|
+
children: /* @__PURE__ */ jsx(Listbox.Root, {
|
|
218
|
+
...selectable ? {
|
|
219
|
+
value: selected,
|
|
220
|
+
onValueChange: handleValueChange
|
|
221
|
+
} : {},
|
|
222
|
+
children
|
|
223
|
+
})
|
|
224
|
+
});
|
|
225
|
+
};
|
|
44
226
|
TaskListRoot.displayName = "TaskList.Root";
|
|
45
227
|
var TaskListViewport = composable(({ children, ...props }, forwardedRef) => {
|
|
46
228
|
const { className, ...rest } = composableProps(props);
|
|
47
229
|
return /* @__PURE__ */ jsx(Listbox.Viewport, {
|
|
48
230
|
...rest,
|
|
49
|
-
classNames: mx("min-w-0", className),
|
|
231
|
+
classNames: mx("min-w-0 min-h-0", className),
|
|
50
232
|
ref: forwardedRef,
|
|
51
233
|
children
|
|
52
234
|
});
|
|
53
235
|
});
|
|
54
236
|
TaskListViewport.displayName = "TaskList.Viewport";
|
|
237
|
+
/**
|
|
238
|
+
* The rows and the create row are separate grids (the create row sits outside the scrolling
|
|
239
|
+
* viewport), so their leading gutters — ordinal, then status — are declared once here: only
|
|
240
|
+
* matching templates keep the `+` under the status control and the input under the titles.
|
|
241
|
+
* Tailwind scans for whole class names, hence four literals rather than a composed prefix. The
|
|
242
|
+
* status gutter is `1.5rem` because that is the width of the `density='sm'` icon button it holds;
|
|
243
|
+
* a narrower track makes the button overflow it and align left instead of centring.
|
|
244
|
+
*/
|
|
245
|
+
var GRID_COLS = {
|
|
246
|
+
content: "grid-cols-[1.5rem_1fr_min-content_min-content_2rem]",
|
|
247
|
+
contentWithOrdinals: "grid-cols-[2rem_1.5rem_1fr_min-content_min-content_2rem]",
|
|
248
|
+
create: "grid-cols-[1.5rem_1fr]",
|
|
249
|
+
createWithOrdinals: "grid-cols-[2rem_1.5rem_1fr]"
|
|
250
|
+
};
|
|
55
251
|
var TaskListContent = composable((props, forwardedRef) => {
|
|
56
|
-
const { tasks, groupByStatus, statusLabel } = useTaskListContext("TaskList.Content");
|
|
57
|
-
const
|
|
252
|
+
const { tasks, groupByStatus, showGroupLabels, showOrdinals, showDescriptions, hierarchical, showGutter, statusLabel, isCollapsed } = useTaskListContext("TaskList.Content");
|
|
253
|
+
const ordinals = useMemo(() => new Map(tasks.map((task, index) => [task.id, index + 1])), [tasks]);
|
|
254
|
+
const collapsed = useMemo(() => new Set(tasks.map((task) => task.id).filter(isCollapsed)), [tasks, isCollapsed]);
|
|
255
|
+
const rows = useMemo(() => hierarchical ? walkTaskTree(tasks, collapsed) : void 0, [
|
|
256
|
+
hierarchical,
|
|
257
|
+
tasks,
|
|
258
|
+
collapsed
|
|
259
|
+
]);
|
|
58
260
|
const groups = useMemo(() => {
|
|
59
261
|
if (!groupByStatus) return tasks.length > 0 ? [{
|
|
60
262
|
status: void 0,
|
|
@@ -66,11 +268,17 @@ var TaskListContent = composable((props, forwardedRef) => {
|
|
|
66
268
|
})).filter((group) => group.tasks.length > 0);
|
|
67
269
|
}, [tasks, groupByStatus]);
|
|
68
270
|
return /* @__PURE__ */ jsx(Listbox.Content, {
|
|
69
|
-
...
|
|
271
|
+
...composableProps(props, { classNames: mx("group grid gap-x-1 w-full min-w-0", showDescriptions ? "auto-rows-min items-start" : "auto-rows-[2rem] items-center", showGutter ? GRID_COLS.contentWithOrdinals : GRID_COLS.content) }),
|
|
70
272
|
"aria-label": "Tasks",
|
|
71
|
-
classNames: className,
|
|
72
273
|
ref: forwardedRef,
|
|
73
|
-
children:
|
|
274
|
+
children: rows ? rows.map((row) => /* @__PURE__ */ jsx(TaskListItem, {
|
|
275
|
+
task: row.task,
|
|
276
|
+
ordinal: showOrdinals ? ordinals.get(row.task.id) : void 0,
|
|
277
|
+
row
|
|
278
|
+
}, row.task.id)) : groups.map(({ status, tasks }) => /* @__PURE__ */ jsxs(Fragment, { children: [status && showGroupLabels && /* @__PURE__ */ jsx(TaskListGroupLabel, { children: statusLabel(status) }), tasks.map((task) => /* @__PURE__ */ jsx(TaskListItem, {
|
|
279
|
+
task,
|
|
280
|
+
ordinal: showOrdinals ? ordinals.get(task.id) : void 0
|
|
281
|
+
}, task.id))] }, status ?? "all"))
|
|
74
282
|
});
|
|
75
283
|
});
|
|
76
284
|
TaskListContent.displayName = "TaskList.Content";
|
|
@@ -78,77 +286,395 @@ var TaskListGroupLabel = composable(({ children, ...props }, forwardedRef) => {
|
|
|
78
286
|
const { className, ...rest } = composableProps(props);
|
|
79
287
|
return /* @__PURE__ */ jsx("div", {
|
|
80
288
|
...rest,
|
|
81
|
-
className: mx("
|
|
289
|
+
className: mx("col-span-full min-h-(--dx-control) flex items-center text-sm text-description", className),
|
|
82
290
|
ref: forwardedRef,
|
|
83
|
-
children
|
|
291
|
+
children: /* @__PURE__ */ jsx("span", { children })
|
|
84
292
|
});
|
|
85
293
|
});
|
|
86
294
|
TaskListGroupLabel.displayName = "TaskList.GroupLabel";
|
|
87
|
-
|
|
88
|
-
|
|
295
|
+
/** How long the cursor must rest on a collapsed branch before it opens, so crossing one does not. */
|
|
296
|
+
var EXPAND_DWELL = 600;
|
|
297
|
+
/**
|
|
298
|
+
* Distance from the title cell's own inline start to the title text: the disclosure toggle (`w-6`)
|
|
299
|
+
* plus the cell's `gap-1`. The description sits in a different grid row and has to clear the same
|
|
300
|
+
* distance to line up under the title.
|
|
301
|
+
*/
|
|
302
|
+
var TOGGLE_INSET = "1.75rem";
|
|
303
|
+
var useTaskDrag = ({ task, row, tasks, onTaskMove, isCollapsed, onCollapseToggle, onDraggingChange }) => {
|
|
304
|
+
const rowRef = useRef(null);
|
|
305
|
+
const dragHandleRef = useRef(null);
|
|
306
|
+
const [instruction, setInstruction] = useState(null);
|
|
307
|
+
const expandTimeout = useRef(null);
|
|
308
|
+
const liftFrame = useRef(null);
|
|
309
|
+
const latest = useRef({
|
|
310
|
+
tasks,
|
|
311
|
+
onTaskMove,
|
|
312
|
+
isCollapsed,
|
|
313
|
+
onCollapseToggle,
|
|
314
|
+
onDraggingChange,
|
|
315
|
+
row
|
|
316
|
+
});
|
|
317
|
+
latest.current = {
|
|
318
|
+
tasks,
|
|
319
|
+
onTaskMove,
|
|
320
|
+
isCollapsed,
|
|
321
|
+
onCollapseToggle,
|
|
322
|
+
onDraggingChange,
|
|
323
|
+
row
|
|
324
|
+
};
|
|
325
|
+
const cancelLift = useCallback(() => {
|
|
326
|
+
if (liftFrame.current !== null) {
|
|
327
|
+
cancelAnimationFrame(liftFrame.current);
|
|
328
|
+
liftFrame.current = null;
|
|
329
|
+
}
|
|
330
|
+
}, []);
|
|
331
|
+
const cancelExpand = useCallback(() => {
|
|
332
|
+
if (expandTimeout.current) {
|
|
333
|
+
clearTimeout(expandTimeout.current);
|
|
334
|
+
expandTimeout.current = null;
|
|
335
|
+
}
|
|
336
|
+
}, []);
|
|
337
|
+
const enabled = !!onTaskMove && !!row;
|
|
338
|
+
useEffect(() => {
|
|
339
|
+
const element = rowRef.current;
|
|
340
|
+
const handle = dragHandleRef.current;
|
|
341
|
+
if (!enabled || !element || !handle) return;
|
|
342
|
+
const data = {
|
|
343
|
+
type: TASK_DRAG_TYPE,
|
|
344
|
+
taskId: task.id
|
|
345
|
+
};
|
|
346
|
+
/** The placement a drag over this row currently means, or undefined when the drop is refused. */
|
|
347
|
+
const placementFor = (instruction) => {
|
|
348
|
+
const intent = dropIntent(instruction);
|
|
349
|
+
const source = latest.current.tasks.find(({ id }) => id === draggingId.current);
|
|
350
|
+
if (!intent || !source) return;
|
|
351
|
+
return resolveTaskPlacement({
|
|
352
|
+
tasks: latest.current.tasks,
|
|
353
|
+
source,
|
|
354
|
+
target: task,
|
|
355
|
+
intent
|
|
356
|
+
});
|
|
357
|
+
};
|
|
358
|
+
return combine(draggable({
|
|
359
|
+
element: handle,
|
|
360
|
+
getInitialData: () => data,
|
|
361
|
+
onGenerateDragPreview: ({ location, nativeSetDragImage }) => {
|
|
362
|
+
const rows = subtreeRows(element, latest.current.tasks, task);
|
|
363
|
+
const { left, top } = element.getBoundingClientRect();
|
|
364
|
+
const { clientX, clientY } = location.initial.input;
|
|
365
|
+
const offset = {
|
|
366
|
+
x: clientX - left,
|
|
367
|
+
y: clientY - top
|
|
368
|
+
};
|
|
369
|
+
if (rows.length < 2) {
|
|
370
|
+
nativeSetDragImage?.(element, offset.x, offset.y);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
setCustomNativeDragPreview({
|
|
374
|
+
nativeSetDragImage,
|
|
375
|
+
getOffset: () => offset,
|
|
376
|
+
render: ({ container }) => renderSubtreePreview(container, element, rows)
|
|
377
|
+
});
|
|
378
|
+
},
|
|
379
|
+
onDragStart: () => {
|
|
380
|
+
draggingId.current = task.id;
|
|
381
|
+
liftFrame.current = requestAnimationFrame(() => latest.current.onDraggingChange(task));
|
|
382
|
+
},
|
|
383
|
+
onDrop: () => {
|
|
384
|
+
draggingId.current = void 0;
|
|
385
|
+
cancelLift();
|
|
386
|
+
latest.current.onDraggingChange(void 0);
|
|
387
|
+
}
|
|
388
|
+
}), dropTargetForElements({
|
|
389
|
+
element,
|
|
390
|
+
getData: ({ input, element }) => attachInstruction(data, {
|
|
391
|
+
input,
|
|
392
|
+
element,
|
|
393
|
+
indentPerLevel: 8,
|
|
394
|
+
currentLevel: (latest.current.row?.level ?? 1) - 1,
|
|
395
|
+
mode: itemMode({
|
|
396
|
+
branch: latest.current.row?.branch ?? false,
|
|
397
|
+
open: !latest.current.isCollapsed(task.id),
|
|
398
|
+
last: (latest.current.row?.position ?? 1) === (latest.current.row?.setSize ?? 1)
|
|
399
|
+
}),
|
|
400
|
+
block: []
|
|
401
|
+
}),
|
|
402
|
+
canDrop: ({ source }) => isTaskDragData(source.data) && source.data.taskId !== task.id,
|
|
403
|
+
getIsSticky: () => true,
|
|
404
|
+
onDrag: ({ self }) => {
|
|
405
|
+
const desired = extractInstruction(self.data);
|
|
406
|
+
const next = desired !== null && desired.type !== "instruction-blocked" && !placementFor(desired) ? {
|
|
407
|
+
type: "instruction-blocked",
|
|
408
|
+
desired
|
|
409
|
+
} : desired;
|
|
410
|
+
setInstruction(next);
|
|
411
|
+
const { row, isCollapsed, onCollapseToggle } = latest.current;
|
|
412
|
+
if (next?.type === "make-child" && row?.branch && isCollapsed(task.id) && !expandTimeout.current) expandTimeout.current = setTimeout(() => {
|
|
413
|
+
expandTimeout.current = null;
|
|
414
|
+
onCollapseToggle(task.id);
|
|
415
|
+
}, EXPAND_DWELL);
|
|
416
|
+
else if (next?.type !== "make-child") cancelExpand();
|
|
417
|
+
},
|
|
418
|
+
onDragLeave: () => {
|
|
419
|
+
cancelExpand();
|
|
420
|
+
setInstruction(null);
|
|
421
|
+
},
|
|
422
|
+
onDrop: ({ self, source }) => {
|
|
423
|
+
cancelExpand();
|
|
424
|
+
setInstruction(null);
|
|
425
|
+
if (!isTaskDragData(source.data)) return;
|
|
426
|
+
const moved = latest.current.tasks.find(({ id }) => id === source.data.taskId);
|
|
427
|
+
const intent = dropIntent(extractInstruction(self.data));
|
|
428
|
+
if (!moved || !intent) return;
|
|
429
|
+
const placement = resolveTaskPlacement({
|
|
430
|
+
tasks: latest.current.tasks,
|
|
431
|
+
source: moved,
|
|
432
|
+
target: task,
|
|
433
|
+
intent
|
|
434
|
+
});
|
|
435
|
+
if (placement) latest.current.onTaskMove?.(moved, placement);
|
|
436
|
+
}
|
|
437
|
+
}));
|
|
438
|
+
}, [
|
|
439
|
+
enabled,
|
|
440
|
+
task.id,
|
|
441
|
+
cancelExpand,
|
|
442
|
+
cancelLift
|
|
443
|
+
]);
|
|
444
|
+
useEffect(() => () => {
|
|
445
|
+
cancelExpand();
|
|
446
|
+
cancelLift();
|
|
447
|
+
}, [cancelExpand, cancelLift]);
|
|
448
|
+
return {
|
|
449
|
+
instruction,
|
|
450
|
+
rowRef,
|
|
451
|
+
dragHandleRef,
|
|
452
|
+
dragHandle: enabled
|
|
453
|
+
};
|
|
454
|
+
};
|
|
455
|
+
/**
|
|
456
|
+
* The rendered rows of `task`'s subtree, in document order: the row itself plus every visible
|
|
457
|
+
* descendant. A collapsed branch contributes only its own row, which is what the reader sees.
|
|
458
|
+
*/
|
|
459
|
+
var subtreeRows = (element, tasks, task) => {
|
|
460
|
+
const list = element.parentElement;
|
|
461
|
+
const ids = subtreeIds(tasks, task);
|
|
462
|
+
return list ? Array.from(list.querySelectorAll("[data-task-id]")).filter((row) => ids.has(row.dataset.taskId ?? "")) : [element];
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* Clones the subtree into the drag preview. The container is made a grid carrying the list's own
|
|
466
|
+
* track sizes: the rows are `grid-cols-subgrid`, so without them a detached copy collapses to its
|
|
467
|
+
* content width and the columns no longer line up.
|
|
468
|
+
*/
|
|
469
|
+
var renderSubtreePreview = (container, element, rows) => {
|
|
470
|
+
const list = element.parentElement;
|
|
471
|
+
const listStyle = list && getComputedStyle(list);
|
|
472
|
+
container.style.display = "grid";
|
|
473
|
+
container.style.width = `${element.getBoundingClientRect().width}px`;
|
|
474
|
+
if (listStyle) {
|
|
475
|
+
container.style.gridTemplateColumns = listStyle.gridTemplateColumns;
|
|
476
|
+
container.style.columnGap = listStyle.columnGap;
|
|
477
|
+
container.style.gridAutoRows = listStyle.gridAutoRows;
|
|
478
|
+
container.style.alignItems = listStyle.alignItems;
|
|
479
|
+
}
|
|
480
|
+
container.classList.add("bg-base-surface");
|
|
481
|
+
for (const row of rows) container.appendChild(row.cloneNode(true));
|
|
482
|
+
};
|
|
483
|
+
/**
|
|
484
|
+
* Id of the task currently being dragged. Module-level because the drag source and the row under
|
|
485
|
+
* the cursor are different components, and the hitbox reports only the target's own data mid-drag;
|
|
486
|
+
* a rejected drop has to be refused while the cursor is over it, not after the drop.
|
|
487
|
+
*/
|
|
488
|
+
var draggingId = { current: void 0 };
|
|
489
|
+
/** A description is a line in a row, not a document: no paragraph block, no heading scale. */
|
|
490
|
+
var DESCRIPTION_COMPONENTS = { p: ({ children }) => /* @__PURE__ */ jsx("span", { children }) };
|
|
491
|
+
var STATUS_ICONS = {
|
|
492
|
+
todo: {
|
|
493
|
+
icon: "ph--square--regular",
|
|
494
|
+
classNames: "text-subdued"
|
|
495
|
+
},
|
|
496
|
+
started: {
|
|
497
|
+
icon: "ph--hourglass--regular",
|
|
498
|
+
classNames: "text-info-text"
|
|
499
|
+
},
|
|
500
|
+
done: {
|
|
501
|
+
icon: "ph--check--regular",
|
|
502
|
+
classNames: "text-success-text"
|
|
503
|
+
},
|
|
504
|
+
failed: {
|
|
505
|
+
icon: "ph--x--regular",
|
|
506
|
+
classNames: "text-error-text"
|
|
507
|
+
},
|
|
508
|
+
cancelled: {
|
|
509
|
+
icon: "ph--x--regular",
|
|
510
|
+
classNames: "text-error-text"
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var TaskListItem = composable(({ task, ordinal, row, ...props }, forwardedRef) => {
|
|
514
|
+
const { t } = useTranslation(translationKey);
|
|
515
|
+
const { tasks, showDescriptions, showGutter, onTaskUpdate, onTaskDelete, onTaskSelect, onTaskMove, isCollapsed, onCollapseToggle, dragging, onDraggingChange } = useTaskListContext("TaskList.Item");
|
|
89
516
|
const { className, ...rest } = composableProps(props);
|
|
517
|
+
const { instruction, rowRef, dragHandleRef, dragHandle } = useTaskDrag({
|
|
518
|
+
task,
|
|
519
|
+
row,
|
|
520
|
+
tasks,
|
|
521
|
+
onTaskMove,
|
|
522
|
+
onCollapseToggle,
|
|
523
|
+
isCollapsed,
|
|
524
|
+
onDraggingChange
|
|
525
|
+
});
|
|
526
|
+
const open = row ? !isCollapsed(task.id) : void 0;
|
|
90
527
|
const [snapshot] = useObject(task);
|
|
91
528
|
const current = snapshot ?? task;
|
|
92
529
|
const done = current.status === "done";
|
|
530
|
+
const error = current.status === "failed";
|
|
531
|
+
const description = showDescriptions ? current.description?.trim() || void 0 : void 0;
|
|
532
|
+
const blocked = (current.status ?? "todo") === "todo" && !TaskSet.isTaskReady(tasks, task);
|
|
533
|
+
const { icon, classNames: iconClassNames } = current.status === "started" && current.assignee?.role === "assistant" ? {
|
|
534
|
+
icon: "ph--spinner--regular",
|
|
535
|
+
classNames: "text-info-text animate-spin"
|
|
536
|
+
} : STATUS_ICONS[current.status ?? "todo"];
|
|
93
537
|
const handleToggle = useCallback(() => onTaskUpdate?.(task, { status: done ? "todo" : "done" }), [
|
|
94
538
|
onTaskUpdate,
|
|
95
539
|
task,
|
|
96
540
|
done
|
|
97
541
|
]);
|
|
98
|
-
|
|
542
|
+
const handleKeyDown = useCallback((event) => {
|
|
543
|
+
if (!onTaskMove || !row || !event.altKey) return;
|
|
544
|
+
const placement = (() => {
|
|
545
|
+
switch (event.key) {
|
|
546
|
+
case "ArrowRight": return resolveIndent(tasks, task);
|
|
547
|
+
case "ArrowLeft": return resolveOutdent(tasks, task);
|
|
548
|
+
case "ArrowUp": return resolveNudge(tasks, task, "up");
|
|
549
|
+
case "ArrowDown": return resolveNudge(tasks, task, "down");
|
|
550
|
+
default: return;
|
|
551
|
+
}
|
|
552
|
+
})();
|
|
553
|
+
if (placement) {
|
|
554
|
+
event.preventDefault();
|
|
555
|
+
event.stopPropagation();
|
|
556
|
+
onTaskMove(task, placement);
|
|
557
|
+
}
|
|
558
|
+
}, [
|
|
559
|
+
onTaskMove,
|
|
560
|
+
row,
|
|
561
|
+
tasks,
|
|
562
|
+
task
|
|
563
|
+
]);
|
|
564
|
+
return /* @__PURE__ */ jsxs(Listbox.Item, {
|
|
99
565
|
...rest,
|
|
100
566
|
id: task.id,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
567
|
+
"data-testid": "taskList.item",
|
|
568
|
+
"data-task-id": task.id,
|
|
569
|
+
classNames: mx("group/row col-span-full grid grid-cols-subgrid px-0 items-start relative", dragging.has(task.id) && "hidden", className),
|
|
570
|
+
"aria-level": row?.level,
|
|
571
|
+
"aria-posinset": row && row.position,
|
|
572
|
+
"aria-setsize": row && row.setSize,
|
|
573
|
+
"aria-expanded": row?.branch ? open : void 0,
|
|
574
|
+
style: row ? {
|
|
575
|
+
"--task-indent": paddingIndentation(row.level, 8).paddingInlineStart,
|
|
576
|
+
"--task-title-inset": `calc(var(--task-indent) + ${TOGGLE_INSET})`
|
|
577
|
+
} : void 0,
|
|
578
|
+
onKeyDown: handleKeyDown,
|
|
579
|
+
ref: useComposedRefs(rowRef, forwardedRef),
|
|
580
|
+
children: [
|
|
581
|
+
showGutter && /* @__PURE__ */ jsxs("div", {
|
|
582
|
+
className: "relative flex h-8 items-center justify-center",
|
|
583
|
+
children: [ordinal !== void 0 && /* @__PURE__ */ jsx(Tag, {
|
|
584
|
+
hue: done ? "green" : error ? "rose" : "neutral",
|
|
585
|
+
classNames: mx("tabular-nums", dragHandle && "group-hover/row:invisible group-has-[:focus-visible]/row:invisible"),
|
|
586
|
+
children: ordinal
|
|
587
|
+
}), dragHandle && /* @__PURE__ */ jsx("span", {
|
|
588
|
+
"data-testid": "taskList.dragHandle",
|
|
589
|
+
"aria-hidden": true,
|
|
590
|
+
className: mx("absolute inset-0 grid place-items-center text-subdued cursor-grab active:cursor-grabbing", ordinal !== void 0 && "invisible group-hover/row:visible group-has-[:focus-visible]/row:visible"),
|
|
591
|
+
ref: dragHandleRef,
|
|
592
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
593
|
+
icon: "ph--dots-six-vertical--regular",
|
|
594
|
+
size: 4
|
|
595
|
+
})
|
|
596
|
+
})]
|
|
597
|
+
}),
|
|
598
|
+
onTaskUpdate ? /* @__PURE__ */ jsx(IconButton, {
|
|
599
|
+
classNames: mx("justify-self-center my-1", iconClassNames),
|
|
600
|
+
variant: "ghost",
|
|
601
|
+
density: "sm",
|
|
602
|
+
icon,
|
|
603
|
+
iconOnly: true,
|
|
604
|
+
label: done ? t("mark-todo.label") : t("mark-done.label"),
|
|
605
|
+
onClick: handleToggle
|
|
606
|
+
}) : /* @__PURE__ */ jsxs("span", {
|
|
607
|
+
className: "grid h-8 place-items-center justify-self-center",
|
|
608
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
609
|
+
icon,
|
|
610
|
+
classNames: iconClassNames,
|
|
119
611
|
size: 4
|
|
120
|
-
}),
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
612
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
613
|
+
className: "sr-only",
|
|
614
|
+
children: t(`status-${current.status ?? "todo"}.label`)
|
|
615
|
+
})]
|
|
616
|
+
}),
|
|
617
|
+
/* @__PURE__ */ jsxs("span", {
|
|
618
|
+
className: mx("flex h-8 items-center gap-1 min-w-0", row && "ps-(--task-indent)", onTaskSelect && "cursor-pointer"),
|
|
619
|
+
children: [row && /* @__PURE__ */ jsx(TreeItemToggle, {
|
|
620
|
+
isBranch: row.branch,
|
|
621
|
+
open,
|
|
622
|
+
onClick: (event) => {
|
|
623
|
+
event.stopPropagation();
|
|
624
|
+
onCollapseToggle(task.id);
|
|
625
|
+
}
|
|
626
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
627
|
+
className: "truncate",
|
|
124
628
|
children: current.title
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
629
|
+
})]
|
|
630
|
+
}),
|
|
631
|
+
current.assignee ? /* @__PURE__ */ jsx("span", {
|
|
632
|
+
className: "flex h-8 items-center",
|
|
633
|
+
children: /* @__PURE__ */ jsx(TaskListAssignee, { assignee: current.assignee })
|
|
634
|
+
}) : /* @__PURE__ */ jsx("div", {}),
|
|
635
|
+
/* @__PURE__ */ jsxs("div", {
|
|
636
|
+
className: "flex h-8 items-center gap-1",
|
|
637
|
+
children: [blocked && /* @__PURE__ */ jsx(Tag, {
|
|
638
|
+
hue: "indigo",
|
|
639
|
+
children: t("task-blocked.label")
|
|
640
|
+
}), current.priority && current.priority !== "none" && /* @__PURE__ */ jsx(Tag, {
|
|
641
|
+
hue: "neutral",
|
|
642
|
+
children: current.priority
|
|
643
|
+
})]
|
|
644
|
+
}),
|
|
645
|
+
onTaskDelete && /* @__PURE__ */ jsx(CompactIconButton, {
|
|
646
|
+
variant: "ghost",
|
|
647
|
+
icon: "ph--x--regular",
|
|
648
|
+
label: t("delete-task.label"),
|
|
649
|
+
classNames: "invisible group-hover/row:visible group-has-[:focus-visible]/row:visible",
|
|
650
|
+
onClick: () => onTaskDelete(task)
|
|
651
|
+
}),
|
|
652
|
+
instruction && /* @__PURE__ */ jsx(TreeDropIndicator, {
|
|
653
|
+
instruction,
|
|
654
|
+
gap: 0
|
|
655
|
+
}),
|
|
656
|
+
description && /* @__PURE__ */ jsx(MarkdownView, {
|
|
657
|
+
content: description,
|
|
658
|
+
classNames: mx(showGutter ? "col-start-3" : "col-start-2", row && "ps-(--task-title-inset)", "col-span-3 pb-1 text-sm text-description line-clamp-3"),
|
|
659
|
+
components: DESCRIPTION_COMPONENTS
|
|
660
|
+
})
|
|
661
|
+
]
|
|
147
662
|
});
|
|
148
663
|
});
|
|
149
664
|
TaskListItem.displayName = "TaskList.Item";
|
|
665
|
+
var CompactIconButton = (props) => {
|
|
666
|
+
return /* @__PURE__ */ jsx("span", {
|
|
667
|
+
className: "grid size-8 shrink-0 place-items-center",
|
|
668
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
669
|
+
variant: "ghost",
|
|
670
|
+
iconOnly: true,
|
|
671
|
+
density: "sm",
|
|
672
|
+
...props
|
|
673
|
+
})
|
|
674
|
+
});
|
|
675
|
+
};
|
|
150
676
|
var TaskListCreate = composable(({ placeholder = "Add task", ...props }, forwardedRef) => {
|
|
151
|
-
const { onTaskCreate } = useTaskListContext("TaskList.Create");
|
|
677
|
+
const { onTaskCreate, showGutter } = useTaskListContext("TaskList.Create");
|
|
152
678
|
const { className, ...rest } = composableProps(props);
|
|
153
679
|
const [title, setTitle] = useState("");
|
|
154
680
|
const handleKeyDown = useCallback((event) => {
|
|
@@ -162,19 +688,24 @@ var TaskListCreate = composable(({ placeholder = "Add task", ...props }, forward
|
|
|
162
688
|
return /* @__PURE__ */ jsxs("div", {
|
|
163
689
|
...rest,
|
|
164
690
|
"data-testid": "taskList.create",
|
|
165
|
-
className: mx(
|
|
691
|
+
className: mx("grid gap-x-1 items-center w-full min-w-0 h-8 shrink-0", showGutter ? GRID_COLS.createWithOrdinals : GRID_COLS.create, className),
|
|
166
692
|
ref: forwardedRef,
|
|
167
|
-
children: [
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
693
|
+
children: [
|
|
694
|
+
showGutter && /* @__PURE__ */ jsx("span", {}),
|
|
695
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
696
|
+
icon: "ph--plus--regular",
|
|
697
|
+
size: 4,
|
|
698
|
+
classNames: "justify-self-center text-subdued"
|
|
699
|
+
}),
|
|
700
|
+
/* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.TextInput, {
|
|
701
|
+
variant: "subdued",
|
|
702
|
+
classNames: "px-0",
|
|
703
|
+
placeholder,
|
|
704
|
+
value: title,
|
|
705
|
+
onChange: (event) => setTitle(event.target.value),
|
|
706
|
+
onKeyDown: handleKeyDown
|
|
707
|
+
}) })
|
|
708
|
+
]
|
|
178
709
|
});
|
|
179
710
|
});
|
|
180
711
|
TaskListCreate.displayName = "TaskList.Create";
|
|
@@ -204,6 +735,6 @@ var TaskList = {
|
|
|
204
735
|
Assignee: TaskListAssignee
|
|
205
736
|
};
|
|
206
737
|
//#endregion
|
|
207
|
-
export { STATUS_ORDER, TaskList };
|
|
738
|
+
export { STATUS_ORDER, TaskList, resolveIndent, resolveNudge, resolveOutdent, resolveTaskPlacement, subtreeIds, walkTaskTree };
|
|
208
739
|
|
|
209
740
|
//# sourceMappingURL=index.mjs.map
|