@dxos/react-ui-task 0.11.1 → 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264

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.
Files changed (27) hide show
  1. package/dist/lib/index.mjs +627 -96
  2. package/dist/lib/index.mjs.map +1 -1
  3. package/dist/lib/translations.mjs +17 -0
  4. package/dist/lib/translations.mjs.map +1 -0
  5. package/dist/types/src/components/TaskList/TaskList.d.ts +41 -12
  6. package/dist/types/src/components/TaskList/TaskList.d.ts.map +1 -1
  7. package/dist/types/src/components/TaskList/TaskList.stories.d.ts +31 -3
  8. package/dist/types/src/components/TaskList/TaskList.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/TaskList/dnd.d.ts +27 -0
  10. package/dist/types/src/components/TaskList/dnd.d.ts.map +1 -0
  11. package/dist/types/src/components/TaskList/hierarchy.d.ts +73 -0
  12. package/dist/types/src/components/TaskList/hierarchy.d.ts.map +1 -0
  13. package/dist/types/src/components/TaskList/hierarchy.test.d.ts +2 -0
  14. package/dist/types/src/components/TaskList/hierarchy.test.d.ts.map +1 -0
  15. package/dist/types/src/components/TaskList/index.d.ts +1 -0
  16. package/dist/types/src/components/TaskList/index.d.ts.map +1 -1
  17. package/dist/types/src/translations.d.ts +17 -0
  18. package/dist/types/src/translations.d.ts.map +1 -0
  19. package/dist/types/tsconfig.tsbuildinfo +1 -1
  20. package/package.json +26 -14
  21. package/src/components/TaskList/TaskList.stories.tsx +258 -14
  22. package/src/components/TaskList/TaskList.tsx +743 -100
  23. package/src/components/TaskList/dnd.ts +46 -0
  24. package/src/components/TaskList/hierarchy.test.ts +155 -0
  25. package/src/components/TaskList/hierarchy.ts +191 -0
  26. package/src/components/TaskList/index.ts +1 -0
  27. package/src/translations.ts +25 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/components/TaskList/TaskList.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { createContext } from '@radix-ui/react-context';\nimport React, { Fragment, type KeyboardEvent, type PropsWithChildren, useCallback, useMemo, useState } from 'react';\n\nimport { useObject } from '@dxos/echo-react';\nimport { Icon, IconButton, Input, Tag, composable, composableProps } from '@dxos/react-ui';\nimport { Listbox } from '@dxos/react-ui-list';\nimport { type Actor, type Task } from '@dxos/types';\nimport { mx } from '@dxos/ui-theme';\nimport { type ComposableProps } from '@dxos/ui-types';\n\nconst TASK_LIST_NAME = 'TaskList.Root';\n\nexport type TaskStatus = NonNullable<Task.Task['status']>;\n\n/** Linear-style status groups, most active first. */\nexport const STATUS_ORDER: TaskStatus[] = ['in-progress', 'todo', 'done', 'failed', 'cancelled'];\n\nconst DEFAULT_STATUS_LABELS: Record<TaskStatus, string> = {\n 'in-progress': 'In progress',\n 'todo': 'Todo',\n 'done': 'Done',\n 'failed': 'Failed',\n 'cancelled': 'Cancelled',\n};\n\nexport type TaskPatch = Partial<Pick<Task.Task, 'title' | 'status' | 'priority' | 'estimate' | 'assignee'>>;\n\n/**\n * One row geometry shared by `Item` and `Create` so their affordances land in the same columns:\n * a fixed icon gutter (the status toggle above the add `+`), the flexible label, then trailing\n * chips/actions pinned to the far edge. `w-full` is load-bearing — the listbox item is a flex\n * container, so without it the grid shrinks to its content and the trailing actions float\n * mid-row. `px-3` matches the listbox item's own inset, which `Create` does not inherit.\n */\nconst ROW_GRID = 'grid grid-cols-[1.5rem_1fr_auto] items-center gap-2 w-full min-w-0 px-3 h-8';\n\n//\n// Context — plain Radix context (un-scoped); nesting task lists has no meaning today.\n//\n\ntype TaskListContextValue = {\n tasks: readonly Task.Task[];\n groupByStatus: boolean;\n statusLabel: (status: TaskStatus) => string;\n onTaskCreate?: (title: string) => void;\n onTaskUpdate?: (task: Task.Task, patch: TaskPatch) => void;\n onTaskDelete?: (task: Task.Task) => void;\n onTaskSelect?: (task: Task.Task) => void;\n};\n\nconst [TaskListProvider, useTaskListContext] = createContext<TaskListContextValue>(TASK_LIST_NAME);\n\n//\n// Root — headless context provider. Renders no DOM.\n//\n\ntype TaskListRootProps = PropsWithChildren<{\n tasks: readonly Task.Task[];\n /** Group rows into status sections (Linear order); flat list otherwise. */\n groupByStatus?: boolean;\n /** i18n hook for group headings; defaults to English labels. */\n statusLabel?: (status: TaskStatus) => string;\n /** Enables `Create`; called with the trimmed title. */\n onTaskCreate?: (title: string) => void;\n /** Enables the done toggle. Every mutation is delegated — the list never writes. */\n onTaskUpdate?: (task: Task.Task, patch: TaskPatch) => void;\n /** Enables the per-row delete affordance. */\n onTaskDelete?: (task: Task.Task) => void;\n /** Row click. */\n onTaskSelect?: (task: Task.Task) => void;\n}>;\n\nconst TaskListRoot = ({\n children,\n tasks,\n groupByStatus = true,\n statusLabel = (status) => DEFAULT_STATUS_LABELS[status],\n onTaskCreate,\n onTaskUpdate,\n onTaskDelete,\n onTaskSelect,\n}: TaskListRootProps) => (\n <TaskListProvider\n tasks={tasks}\n groupByStatus={groupByStatus}\n statusLabel={statusLabel}\n onTaskCreate={onTaskCreate}\n onTaskUpdate={onTaskUpdate}\n onTaskDelete={onTaskDelete}\n onTaskSelect={onTaskSelect}\n >\n {/* Both roots are headless, so the pair renders no DOM of its own. */}\n <Listbox.Root>{children}</Listbox.Root>\n </TaskListProvider>\n);\n\nTaskListRoot.displayName = 'TaskList.Root';\n\n//\n// Viewport — the scrolling region (the listbox's own viewport). `Create` sits outside it, so the\n// add row stays pinned while the rows scroll.\n//\n\ntype TaskListViewportProps = ComposableProps;\n\nconst TaskListViewport = composable<HTMLDivElement>(({ children, ...props }, forwardedRef) => {\n const { className, ...rest } = composableProps(props);\n return (\n <Listbox.Viewport {...rest} classNames={mx('min-w-0', className)} ref={forwardedRef}>\n {children}\n </Listbox.Viewport>\n );\n});\n\nTaskListViewport.displayName = 'TaskList.Viewport';\n\n//\n// Content — the rows, grouped by status when the root says so.\n//\n\ntype TaskListContentProps = ComposableProps;\n\nconst TaskListContent = composable<HTMLUListElement>((props, forwardedRef) => {\n const { tasks, groupByStatus, statusLabel } = useTaskListContext('TaskList.Content');\n const { className, ...rest } = composableProps(props);\n const groups = useMemo(() => {\n if (!groupByStatus) {\n return tasks.length > 0 ? [{ status: undefined, tasks }] : [];\n }\n\n return STATUS_ORDER.map((status) => ({\n status,\n tasks: tasks.filter((task) => (task.status ?? 'todo') === status),\n })).filter((group) => group.tasks.length > 0);\n }, [tasks, groupByStatus]);\n\n return (\n <Listbox.Content {...rest} aria-label='Tasks' classNames={className} ref={forwardedRef}>\n {groups.map(({ status, tasks }) => (\n <Fragment key={status ?? 'all'}>\n {status && <TaskListGroupLabel>{statusLabel(status)}</TaskListGroupLabel>}\n {tasks.map((task) => (\n <TaskListItem key={task.id} task={task} />\n ))}\n </Fragment>\n ))}\n </Listbox.Content>\n );\n});\n\nTaskListContent.displayName = 'TaskList.Content';\n\n//\n// GroupLabel\n//\n\ntype TaskListGroupLabelProps = ComposableProps;\n\nconst TaskListGroupLabel = composable<HTMLDivElement>(({ children, ...props }, forwardedRef) => {\n const { className, ...rest } = composableProps(props);\n return (\n <div {...rest} className={mx('px-2 pt-3 pb-1 text-xs text-subdued uppercase', className)} ref={forwardedRef}>\n {children}\n </div>\n );\n});\n\nTaskListGroupLabel.displayName = 'TaskList.GroupLabel';\n\n//\n// Item — one row. Exported so a host can render its own selection of tasks.\n//\n\ntype TaskListItemProps = ComposableProps<{ task: Task.Task }>;\n\nconst TaskListItem = composable<HTMLLIElement, { task: Task.Task }>(({ task, ...props }, forwardedRef) => {\n const { onTaskUpdate, onTaskDelete, onTaskSelect } = useTaskListContext('TaskList.Item');\n const { className, ...rest } = composableProps(props);\n // Subscribe per row: a query re-emits when membership changes, not when a task's own fields do,\n // so a rename elsewhere (task form, agent, sync) would otherwise leave the row stale.\n const [snapshot] = useObject(task);\n const current = snapshot ?? task;\n const done = current.status === 'done';\n const handleToggle = useCallback(\n () => onTaskUpdate?.(task, { status: done ? 'todo' : 'done' }),\n [onTaskUpdate, task, done],\n );\n\n return (\n // `px-0` hands the horizontal inset to `ROW_GRID`, so the row shares the create row's columns.\n <Listbox.Item {...rest} id={task.id} classNames={mx('px-0 py-0 group', className)} ref={forwardedRef}>\n <div role='none' data-testid='taskList.item' className={ROW_GRID}>\n {onTaskUpdate ? (\n <IconButton\n variant='ghost'\n density='sm'\n icon={done ? 'ph--check--regular' : 'ph--circle--regular'}\n iconOnly\n label={done ? 'Mark todo' : 'Mark done'}\n classNames={mx('justify-self-center text-subdued', done && 'text-success-text')}\n onClick={handleToggle}\n />\n ) : (\n <Icon\n icon={done ? 'ph--check--regular' : 'ph--circle--regular'}\n classNames={mx('justify-self-center', done && 'text-success-text')}\n size={4}\n />\n )}\n <span\n className={onTaskSelect ? 'truncate cursor-pointer' : 'truncate'}\n onClick={onTaskSelect ? () => onTaskSelect(task) : undefined}\n >\n {current.title}\n </span>\n <div className='flex items-center gap-1'>\n {current.priority && current.priority !== 'none' && <Tag hue='neutral'>{current.priority}</Tag>}\n {current.assignee && <TaskListAssignee assignee={current.assignee} />}\n {onTaskDelete && (\n <IconButton\n variant='ghost'\n density='sm'\n icon='ph--x--regular'\n iconOnly\n label='Delete task'\n classNames='invisible group-hover:visible'\n onClick={() => onTaskDelete(task)}\n />\n )}\n </div>\n </div>\n </Listbox.Item>\n );\n});\n\nTaskListItem.displayName = 'TaskList.Item';\n\n//\n// Create — the add row; renders nothing unless the root supplies `onTaskCreate`.\n//\n\ntype TaskListCreateProps = ComposableProps<{ placeholder?: string }>;\n\nconst TaskListCreate = composable<HTMLDivElement, { placeholder?: string }>(\n ({ placeholder = 'Add task', ...props }, forwardedRef) => {\n const { onTaskCreate } = useTaskListContext('TaskList.Create');\n const { className, ...rest } = composableProps(props);\n const [title, setTitle] = useState('');\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n const trimmed = title.trim();\n if (event.key === 'Enter' && trimmed.length > 0) {\n onTaskCreate?.(trimmed);\n setTitle('');\n }\n },\n [title, onTaskCreate],\n );\n\n if (!onTaskCreate) {\n return null;\n }\n\n return (\n <div {...rest} data-testid='taskList.create' className={mx(ROW_GRID, className)} ref={forwardedRef}>\n <Icon icon='ph--plus--regular' size={4} classNames='justify-self-center text-subdued' />\n <Input.Root>\n <Input.TextInput\n variant='subdued'\n placeholder={placeholder}\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n onKeyDown={handleKeyDown}\n />\n </Input.Root>\n </div>\n );\n },\n);\n\nTaskListCreate.displayName = 'TaskList.Create';\n\n//\n// Assignee — actor-aware chip: a Person ref resolves to the contact's name; otherwise fall back to\n// name, email, or a shortened DID; agents (`role: 'assistant'`) are marked with a sparkle.\n//\n\ntype TaskListAssigneeProps = { assignee: Actor.Actor };\n\nconst TaskListAssignee = composable<HTMLSpanElement, TaskListAssigneeProps>(({ assignee }, _forwardedRef) => {\n const [contact] = useObject(assignee.contact);\n const label =\n contact?.fullName ??\n assignee.name ??\n assignee.email ??\n (assignee.identityDid ? shortDid(assignee.identityDid) : undefined);\n const agent = assignee.role === 'assistant';\n if (!label && !agent) {\n return null;\n }\n\n return (\n <Tag hue={agent ? 'purple' : 'indigo'}>\n {agent && <Icon icon='ph--sparkle--regular' size={3} classNames='inline-block me-1' />}\n {label ?? 'agent'}\n </Tag>\n );\n});\n\nTaskListAssignee.displayName = 'TaskList.Assignee';\n\nconst shortDid = (did: string): string => `${did.slice(0, 12)}…`;\n\n//\n// TaskList\n//\n\nexport const TaskList = {\n Root: TaskListRoot,\n Viewport: TaskListViewport,\n Content: TaskListContent,\n GroupLabel: TaskListGroupLabel,\n Item: TaskListItem,\n Create: TaskListCreate,\n Assignee: TaskListAssignee,\n};\n\nexport type {\n TaskListAssigneeProps,\n TaskListContentProps,\n TaskListCreateProps,\n TaskListGroupLabelProps,\n TaskListItemProps,\n TaskListRootProps,\n TaskListViewportProps,\n};\n"],"mappings":";;;;;;;;AAcA,IAAM,iBAAiB;;AAKvB,IAAa,eAA6B;CAAC;CAAe;CAAQ;CAAQ;CAAU;AAAW;AAE/F,IAAM,wBAAoD;CACxD,eAAe;CACf,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,aAAa;AACf;;;;;;;;AAWA,IAAM,WAAW;AAgBjB,IAAM,CAAC,kBAAkB,sBAAsB,cAAoC,cAAc;AAsBjG,IAAM,gBAAgB,EACpB,UACA,OACA,gBAAgB,MAChB,eAAe,WAAW,sBAAsB,SAChD,cACA,cACA,cACA,mBAEA,oBAAC,kBAAD;CACS;CACQ;CACF;CACC;CACA;CACA;CACA;WAGd,oBAAC,QAAQ,MAAT,EAAe,SAAuB,CAAA;AACtB,CAAA;AAGpB,aAAa,cAAc;AAS3B,IAAM,mBAAmB,YAA4B,EAAE,UAAU,GAAG,SAAS,iBAAiB;CAC5F,MAAM,EAAE,WAAW,GAAG,SAAS,gBAAgB,KAAK;CACpD,OACE,oBAAC,QAAQ,UAAT;EAAkB,GAAI;EAAM,YAAY,GAAG,WAAW,SAAS;EAAG,KAAK;EACpE;CACe,CAAA;AAEtB,CAAC;AAED,iBAAiB,cAAc;AAQ/B,IAAM,kBAAkB,YAA8B,OAAO,iBAAiB;CAC5E,MAAM,EAAE,OAAO,eAAe,gBAAgB,mBAAmB,kBAAkB;CACnF,MAAM,EAAE,WAAW,GAAG,SAAS,gBAAgB,KAAK;CACpD,MAAM,SAAS,cAAc;EAC3B,IAAI,CAAC,eACH,OAAO,MAAM,SAAS,IAAI,CAAC;GAAE,QAAQ,KAAA;GAAW;EAAM,CAAC,IAAI,CAAC;EAG9D,OAAO,aAAa,KAAK,YAAY;GACnC;GACA,OAAO,MAAM,QAAQ,UAAU,KAAK,UAAU,YAAY,MAAM;EAClE,EAAE,CAAC,CAAC,QAAQ,UAAU,MAAM,MAAM,SAAS,CAAC;CAC9C,GAAG,CAAC,OAAO,aAAa,CAAC;CAEzB,OACE,oBAAC,QAAQ,SAAT;EAAiB,GAAI;EAAM,cAAW;EAAQ,YAAY;EAAW,KAAK;YACvE,OAAO,KAAK,EAAE,QAAQ,YACrB,qBAAC,UAAD,EAAA,UAAA,CACG,UAAU,oBAAC,oBAAD,EAAA,UAAqB,YAAY,MAAM,EAAsB,CAAA,GACvE,MAAM,KAAK,SACV,oBAAC,cAAD,EAAkC,KAAO,GAAtB,KAAK,EAAiB,CAC1C,CACO,EAAA,GALK,UAAU,KAKf,CACX;CACc,CAAA;AAErB,CAAC;AAED,gBAAgB,cAAc;AAQ9B,IAAM,qBAAqB,YAA4B,EAAE,UAAU,GAAG,SAAS,iBAAiB;CAC9F,MAAM,EAAE,WAAW,GAAG,SAAS,gBAAgB,KAAK;CACpD,OACE,oBAAC,OAAD;EAAK,GAAI;EAAM,WAAW,GAAG,iDAAiD,SAAS;EAAG,KAAK;EAC5F;CACE,CAAA;AAET,CAAC;AAED,mBAAmB,cAAc;AAQjC,IAAM,eAAe,YAAgD,EAAE,MAAM,GAAG,SAAS,iBAAiB;CACxG,MAAM,EAAE,cAAc,cAAc,iBAAiB,mBAAmB,eAAe;CACvF,MAAM,EAAE,WAAW,GAAG,SAAS,gBAAgB,KAAK;CAGpD,MAAM,CAAC,YAAY,UAAU,IAAI;CACjC,MAAM,UAAU,YAAY;CAC5B,MAAM,OAAO,QAAQ,WAAW;CAChC,MAAM,eAAe,kBACb,eAAe,MAAM,EAAE,QAAQ,OAAO,SAAS,OAAO,CAAC,GAC7D;EAAC;EAAc;EAAM;CAAI,CAC3B;CAEA,OAEE,oBAAC,QAAQ,MAAT;EAAc,GAAI;EAAM,IAAI,KAAK;EAAI,YAAY,GAAG,mBAAmB,SAAS;EAAG,KAAK;YACtF,qBAAC,OAAD;GAAK,MAAK;GAAO,eAAY;GAAgB,WAAW;aAAxD;IACG,eACC,oBAAC,YAAD;KACE,SAAQ;KACR,SAAQ;KACR,MAAM,OAAO,uBAAuB;KACpC,UAAA;KACA,OAAO,OAAO,cAAc;KAC5B,YAAY,GAAG,oCAAoC,QAAQ,mBAAmB;KAC9E,SAAS;IACV,CAAA,IAED,oBAAC,MAAD;KACE,MAAM,OAAO,uBAAuB;KACpC,YAAY,GAAG,uBAAuB,QAAQ,mBAAmB;KACjE,MAAM;IACP,CAAA;IAEH,oBAAC,QAAD;KACE,WAAW,eAAe,4BAA4B;KACtD,SAAS,qBAAqB,aAAa,IAAI,IAAI,KAAA;eAElD,QAAQ;IACL,CAAA;IACN,qBAAC,OAAD;KAAK,WAAU;eAAf;MACG,QAAQ,YAAY,QAAQ,aAAa,UAAU,oBAAC,KAAD;OAAK,KAAI;iBAAW,QAAQ;MAAc,CAAA;MAC7F,QAAQ,YAAY,oBAAC,kBAAD,EAAkB,UAAU,QAAQ,SAAW,CAAA;MACnE,gBACC,oBAAC,YAAD;OACE,SAAQ;OACR,SAAQ;OACR,MAAK;OACL,UAAA;OACA,OAAM;OACN,YAAW;OACX,eAAe,aAAa,IAAI;MACjC,CAAA;KAEA;;GACF;;CACO,CAAA;AAElB,CAAC;AAED,aAAa,cAAc;AAQ3B,IAAM,iBAAiB,YACpB,EAAE,cAAc,YAAY,GAAG,SAAS,iBAAiB;CACxD,MAAM,EAAE,iBAAiB,mBAAmB,iBAAiB;CAC7D,MAAM,EAAE,WAAW,GAAG,SAAS,gBAAgB,KAAK;CACpD,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CACrC,MAAM,gBAAgB,aACnB,UAA2C;EAC1C,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,MAAM,QAAQ,WAAW,QAAQ,SAAS,GAAG;GAC/C,eAAe,OAAO;GACtB,SAAS,EAAE;EACb;CACF,GACA,CAAC,OAAO,YAAY,CACtB;CAEA,IAAI,CAAC,cACH,OAAO;CAGT,OACE,qBAAC,OAAD;EAAK,GAAI;EAAM,eAAY;EAAkB,WAAW,GAAG,UAAU,SAAS;EAAG,KAAK;YAAtF,CACE,oBAAC,MAAD;GAAM,MAAK;GAAoB,MAAM;GAAG,YAAW;EAAoC,CAAA,GACvF,oBAAC,MAAM,MAAP,EAAA,UACE,oBAAC,MAAM,WAAP;GACE,SAAQ;GACK;GACb,OAAO;GACP,WAAW,UAAU,SAAS,MAAM,OAAO,KAAK;GAChD,WAAW;EACZ,CAAA,EACS,CAAA,CACT;;AAET,CACF;AAEA,eAAe,cAAc;AAS7B,IAAM,mBAAmB,YAAoD,EAAE,YAAY,kBAAkB;CAC3G,MAAM,CAAC,WAAW,UAAU,SAAS,OAAO;CAC5C,MAAM,QACJ,SAAS,YACT,SAAS,QACT,SAAS,UACR,SAAS,cAAc,SAAS,SAAS,WAAW,IAAI,KAAA;CAC3D,MAAM,QAAQ,SAAS,SAAS;CAChC,IAAI,CAAC,SAAS,CAAC,OACb,OAAO;CAGT,OACE,qBAAC,KAAD;EAAK,KAAK,QAAQ,WAAW;YAA7B,CACG,SAAS,oBAAC,MAAD;GAAM,MAAK;GAAuB,MAAM;GAAG,YAAW;EAAqB,CAAA,GACpF,SAAS,OACP;;AAET,CAAC;AAED,iBAAiB,cAAc;AAE/B,IAAM,YAAY,QAAwB,GAAG,IAAI,MAAM,GAAG,EAAE,EAAE;AAM9D,IAAa,WAAW;CACtB,MAAM;CACN,UAAU;CACV,SAAS;CACT,YAAY;CACZ,MAAM;CACN,QAAQ;CACR,UAAU;AACZ"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/components/TaskList/hierarchy.ts","../../src/components/TaskList/dnd.ts","../../src/components/TaskList/TaskList.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Task, TaskSet } from '@dxos/types';\n\n/**\n * The tree the list renders, and where a gesture puts a task in it. Kept out of the components\n * because this is the part a regression would break: a drop cannot be driven synthetically\n * (pragmatic-drag-and-drop uses native HTML5 drag events), so the placement calculation is tested\n * directly instead.\n *\n * `TaskSet.tasks` is flat and holds every task; `Task.parentTask` is the hierarchy. The array is\n * NOT a pre-order traversal and is deliberately not made into one: array order decides SIBLING\n * order only, and the walk supplies the rest. A parent therefore moves without dragging its\n * subtree's entries along, and a peer reordering one branch cannot corrupt another.\n */\n\n/** One rendered row: the task, its depth, and where it sits among its siblings. */\nexport type TaskTreeRow = {\n task: Task.Task;\n /** 1 for a root task, incrementing per level. */\n level: number;\n /** Whether the task has sub-tasks — a leaf renders its ordinal where a branch renders a toggle. */\n branch: boolean;\n /** 1-based position among its siblings, and how many siblings there are (`aria-posinset`/`setsize`). */\n position: number;\n setSize: number;\n /** Ids from the root down to (and excluding) the task, so a row knows what to collapse into. */\n ancestors: string[];\n};\n\n/**\n * Walks the set's tree in sibling order, omitting the descendants of any collapsed task.\n * Cycle-safe: a malformed `parentTask` loop is visited once and then skipped, so a corrupt set\n * renders short rather than hanging the list.\n */\nexport const walkTaskTree = (tasks: readonly Task.Task[], collapsed?: ReadonlySet<string>): TaskTreeRow[] => {\n const rows: TaskTreeRow[] = [];\n const seen = new Set<string>();\n\n const visit = (siblings: Task.Task[], level: number, ancestors: string[]): void => {\n siblings.forEach((task, index) => {\n if (seen.has(task.id)) {\n return;\n }\n seen.add(task.id);\n const children = TaskSet.subTasks(tasks, task);\n rows.push({\n task,\n level,\n branch: children.length > 0,\n position: index + 1,\n setSize: siblings.length,\n ancestors,\n });\n if (children.length > 0 && !collapsed?.has(task.id)) {\n visit(children, level + 1, [...ancestors, task.id]);\n }\n });\n };\n\n visit(TaskSet.rootTasks(tasks), 1, []);\n return rows;\n};\n\n/** Every task transitively under `task`, including `task` itself. Cycle-safe. */\nexport const subtreeIds = (tasks: readonly Task.Task[], task: Task.Task): Set<string> => {\n const ids = new Set<string>();\n const visit = (current: Task.Task): void => {\n if (ids.has(current.id)) {\n return;\n }\n ids.add(current.id);\n for (const child of TaskSet.subTasks(tasks, current)) {\n visit(child);\n }\n };\n visit(task);\n return ids;\n};\n\n/** The three tree-item intents, matching `@atlaskit/pragmatic-drag-and-drop-hitbox`. */\nexport type TaskDropIntent = 'reorder-above' | 'reorder-below' | 'make-child';\n\n/**\n * Where a task lands, in the two terms `MoveTask` takes. `parentTask` is `null` for a root task\n * (matching the verb's convention) and `before` is undefined when the task goes last — which is\n * also last among its new siblings, since nothing after it in the array shares the parent.\n */\nexport type TaskPlacement = {\n parentTask: Task.Task | null;\n before?: Task.Task;\n};\n\n/**\n * Resolves a drop into a single `MoveTask` call, or `undefined` when the gesture is rejected.\n *\n * Rejected rather than silently ignored, so the caller can say no with the cursor:\n * - onto itself, or into its own subtree (the cycle `UpdateTask` rejects);\n * - onto a task from another set — a membership transfer, not a reorder.\n */\nexport const resolveTaskPlacement = ({\n tasks,\n source,\n target,\n intent,\n}: {\n tasks: readonly Task.Task[];\n source: Task.Task;\n target: Task.Task;\n intent: TaskDropIntent;\n}): TaskPlacement | undefined => {\n const present = new Set(tasks.map((task) => task.id));\n if (!present.has(source.id) || !present.has(target.id)) {\n return undefined;\n }\n if (subtreeIds(tasks, source).has(target.id)) {\n return undefined;\n }\n\n if (intent === 'make-child') {\n // Appended as the last child (the navtree's convention): with no anchor the task goes to the\n // end of the array, where nothing following it can be an earlier sibling.\n return { parentTask: target, before: undefined };\n }\n\n const parentId = TaskSet.parentTaskId(target);\n const parent = parentId === undefined ? null : (tasks.find((task) => task.id === parentId) ?? null);\n const siblings = parent === null ? TaskSet.rootTasks(tasks) : TaskSet.subTasks(tasks, parent);\n const index = siblings.findIndex((task) => task.id === target.id);\n\n if (intent === 'reorder-above') {\n return { parentTask: parent, before: target };\n }\n\n // Below the target is before the sibling that follows it; with none, the task goes last.\n const next = siblings.slice(index + 1).find((task) => task.id !== source.id);\n return { parentTask: parent, before: next };\n};\n\n/** A task's siblings (in sibling order) and its index among them. */\nconst siblingsOf = (\n tasks: readonly Task.Task[],\n task: Task.Task,\n): { parent: Task.Task | null; siblings: Task.Task[]; index: number } => {\n const parentId = TaskSet.parentTaskId(task);\n const parent = parentId === undefined ? null : (tasks.find((candidate) => candidate.id === parentId) ?? null);\n const siblings = parent === null ? TaskSet.rootTasks(tasks) : TaskSet.subTasks(tasks, parent);\n return { parent, siblings, index: siblings.findIndex((candidate) => candidate.id === task.id) };\n};\n\n/**\n * Indent: the task becomes the last child of its previous sibling — the outliner's `Tab`. Undefined\n * when it is the first among its siblings and so has nothing to indent under.\n */\nexport const resolveIndent = (tasks: readonly Task.Task[], task: Task.Task): TaskPlacement | undefined => {\n const { siblings, index } = siblingsOf(tasks, task);\n const previous = index > 0 ? siblings[index - 1] : undefined;\n return previous ? { parentTask: previous, before: undefined } : undefined;\n};\n\n/**\n * Outdent: the task becomes the next sibling of its parent — the outliner's `Shift-Tab`. Undefined\n * for a task already at the root.\n */\nexport const resolveOutdent = (tasks: readonly Task.Task[], task: Task.Task): TaskPlacement | undefined => {\n const { parent } = siblingsOf(tasks, task);\n if (!parent) {\n return undefined;\n }\n const { parent: grandparent, siblings, index } = siblingsOf(tasks, parent);\n return { parentTask: grandparent, before: siblings[index + 1] };\n};\n\n/**\n * Move within the current parent — `Alt-ArrowUp` / `Alt-ArrowDown`. Undefined at either end, so the\n * key is a no-op rather than silently jumping the task out of its branch.\n */\nexport const resolveNudge = (\n tasks: readonly Task.Task[],\n task: Task.Task,\n direction: 'up' | 'down',\n): TaskPlacement | undefined => {\n const { parent, siblings, index } = siblingsOf(tasks, task);\n if (direction === 'up') {\n return index > 0 ? { parentTask: parent, before: siblings[index - 1] } : undefined;\n }\n // Past the next sibling: anchored on the one after it, or unanchored to land last.\n return index < siblings.length - 1 ? { parentTask: parent, before: siblings[index + 2] } : undefined;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Instruction, type ItemMode } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';\n\nimport { type TaskDropIntent } from './hierarchy';\n\n/**\n * Drag payload for a task row. Tagged so a drop target can tell a task from any other draggable\n * sharing the page (the navtree, a mosaic tile) rather than reading a bare `id` off it.\n */\nexport const TASK_DRAG_TYPE = 'dxos/task';\n\nexport type TaskDragData = {\n type: typeof TASK_DRAG_TYPE;\n taskId: string;\n};\n\nexport const isTaskDragData = (data: Record<string | symbol, unknown>): data is TaskDragData =>\n data.type === TASK_DRAG_TYPE && typeof data.taskId === 'string';\n\n/** Indentation per level, matching the navtree so the two trees step identically. */\nexport const INDENT_PER_LEVEL = 8;\n\n/**\n * The hitbox's item mode, which decides where its bands sit: the last row of a group gets a\n * shallower `reorder-below` band (there is nothing beneath it to reorder against), and an expanded\n * branch gets none at all — below it is its own first child.\n */\nexport const itemMode = ({ branch, open, last }: { branch: boolean; open: boolean; last: boolean }): ItemMode =>\n branch && open ? 'expanded' : last ? 'last-in-group' : 'standard';\n\n/** The drop intent an instruction expresses, or undefined when it carries none (blocked). */\nexport const dropIntent = (instruction: Instruction | null): TaskDropIntent | undefined => {\n switch (instruction?.type) {\n case 'reorder-above':\n case 'reorder-below':\n case 'make-child':\n return instruction.type;\n default:\n // `reparent` (dropping past the end of a branch onto an outer level) is not offered: the list\n // has no gutter to aim at, and `make-child` plus a nudge expresses the same move.\n return undefined;\n }\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport {\n type Instruction,\n attachInstruction,\n extractInstruction,\n} from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';\nimport { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';\nimport { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';\nimport { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';\nimport { useComposedRefs } from '@radix-ui/react-compose-refs';\nimport { createContext } from '@radix-ui/react-context';\nimport React, {\n type CSSProperties,\n Fragment,\n type KeyboardEvent,\n type PropsWithChildren,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { useObject } from '@dxos/echo-react';\nimport {\n Icon,\n IconButton,\n IconButtonProps,\n Input,\n Tag,\n composable,\n composableProps,\n useTranslation,\n} from '@dxos/react-ui';\nimport { Listbox, TreeDropIndicator, TreeItemToggle, paddingIndentation, useListDisclosure } from '@dxos/react-ui-list';\nimport { MarkdownView } from '@dxos/react-ui-markdown';\nimport { type Actor, type Task, TaskSet } from '@dxos/types';\nimport { mx } from '@dxos/ui-theme';\nimport { type ComposableProps } from '@dxos/ui-types';\n\nimport { translationKey } from '#translations';\n\nimport { INDENT_PER_LEVEL, TASK_DRAG_TYPE, type TaskDragData, dropIntent, isTaskDragData, itemMode } from './dnd';\nimport {\n type TaskPlacement,\n type TaskTreeRow,\n resolveIndent,\n resolveNudge,\n resolveOutdent,\n resolveTaskPlacement,\n subtreeIds,\n walkTaskTree,\n} from './hierarchy';\n\nconst TASK_LIST_NAME = 'TaskList.Root';\n\n/** Linear-style status groups, most active first. */\nexport const STATUS_ORDER: Task.Status[] = ['started', 'todo', 'done', 'failed', 'cancelled'];\n\n/** Fallback status labels. */\nconst DEFAULT_STATUS_LABELS: Record<Task.Status, string> = {\n started: 'Started',\n todo: 'To Do',\n done: 'Done',\n failed: 'Failed',\n cancelled: 'Cancelled',\n};\n\nexport type TaskPatch = Partial<Pick<Task.Task, 'title' | 'status' | 'priority' | 'estimate' | 'assignee'>>;\n\n//\n// Context — plain Radix context (un-scoped); nesting task lists has no meaning today.\n//\n\ntype TaskListContextValue = {\n tasks: readonly Task.Task[];\n groupByStatus: boolean;\n showGroupLabels: boolean;\n showOrdinals: boolean;\n showDescriptions: boolean;\n hierarchical: boolean;\n /** Whether the leading gutter is rendered at all — it holds the ordinal and the drag handle. */\n showGutter: boolean;\n statusLabel: (status: Task.Status) => string;\n selected?: string;\n /** Whether a branch's sub-tasks are hidden, and the toggle that flips it. */\n isCollapsed: (id: string) => boolean;\n onCollapseToggle: (id: string) => void;\n /** Ids of the task being dragged and its sub-tasks — lifted out of the list for the drag's duration. */\n dragging: ReadonlySet<string>;\n onDraggingChange: (task: Task.Task | undefined) => void;\n onTaskCreate?: (title: string) => void;\n onTaskUpdate?: (task: Task.Task, patch: TaskPatch) => void;\n onTaskDelete?: (task: Task.Task) => void;\n onTaskSelect?: (task: Task.Task) => void;\n onTaskMove?: (task: Task.Task, placement: TaskPlacement) => void;\n};\n\nconst [TaskListProvider, useTaskListContext] = createContext<TaskListContextValue>(TASK_LIST_NAME);\n\n/** Shared empty set, so a list with nothing in flight does not allocate one per render. */\nconst EMPTY_IDS: ReadonlySet<string> = new Set<string>();\n\n//\n// Root — headless context provider. Renders no DOM.\n//\n\ntype TaskListRootProps = PropsWithChildren<{\n tasks: readonly Task.Task[];\n /** Group rows into status sections (Linear order); flat list otherwise. */\n groupByStatus?: boolean;\n /** Render the status heading above each group; grouping order is kept either way. */\n showGroupLabels?: boolean;\n /** Number rows by their position in `tasks` (set order), so tasks can be referenced by ordinal. */\n showOrdinals?: boolean;\n /** Render each task's description under its title; rows grow to fit. Off by default, so a\n * single-line list (e.g. the chat strip) keeps one row per task. */\n showDescriptions?: boolean;\n /** i18n hook for group headings; defaults to English labels. */\n statusLabel?: (status: Task.Status) => string;\n /** Enables `Create`; called with the trimmed title. */\n onTaskCreate?: (title: string) => void;\n /** Enables the done toggle. Every mutation is delegated — the list never writes. */\n onTaskUpdate?: (task: Task.Task, patch: TaskPatch) => void;\n /** Enables the per-row delete affordance. */\n onTaskDelete?: (task: Task.Task) => void;\n /** Row click. Wiring it (or `selected`) makes the list selectable, so the row shows as selected. */\n onTaskSelect?: (task: Task.Task) => void;\n /** Selected task id (controlled); omit to let the list track the last row clicked. */\n selected?: string;\n /**\n * Render the set as the tree it stores (`Task.parentTask`), not as status groups — the two are\n * mutually exclusive, since a tree regrouped by status is no longer a tree.\n */\n hierarchical?: boolean;\n /**\n * Enables restructuring by drag and by keyboard; called with the one move the gesture means.\n * `MoveTask` takes exactly this pair, so a drop is a single mutation rather than a re-parent\n * followed by a reposition.\n */\n onTaskMove?: (task: Task.Task, placement: TaskPlacement) => void;\n /**\n * Ids of the branches whose sub-tasks are hidden (controlled). Collapsed rather than expanded\n * ids, because a branch is open by default: tracking the expanded set would render a task's new\n * first sub-task hidden, the moment adding it made its parent a branch. Per viewer and per list —\n * a collapsed branch is not a property of the work — so this is state, not stored on the object.\n */\n collapsed?: ReadonlySet<string>;\n onCollapsedChange?: (collapsed: ReadonlySet<string>) => void;\n}>;\n\nconst TaskListRoot = ({\n children,\n tasks,\n groupByStatus = true,\n showGroupLabels = true,\n showOrdinals = false,\n showDescriptions = false,\n statusLabel = (status) => DEFAULT_STATUS_LABELS[status],\n onTaskCreate,\n onTaskUpdate,\n onTaskDelete,\n onTaskSelect,\n onTaskMove,\n selected: selectedProp,\n hierarchical = false,\n collapsed,\n onCollapsedChange,\n}: TaskListRootProps) => {\n // Uncontrolled by default: a host that only wants the click callback still gets the selected\n // styling, and one that owns the selection passes `selected`.\n const [selectedState, setSelectedState] = useState<string | undefined>(selectedProp);\n const selected = selectedProp ?? selectedState;\n const selectable = !!onTaskSelect || selectedProp !== undefined;\n const handleValueChange = useCallback(\n (id: string) => {\n setSelectedState(id);\n const task = tasks.find((task) => task.id === id);\n if (task) {\n onTaskSelect?.(task);\n }\n },\n [tasks, onTaskSelect],\n );\n\n // The hook owns the controlled/uncontrolled Set state machine; its trigger/panel ids are not\n // used, because a sub-task is a sibling row in the same grid rather than a region the toggle\n // could point `aria-controls` at — `aria-expanded` on the row carries the disclosure instead.\n const disclosure = useListDisclosure({\n mode: 'multi',\n ...(collapsed !== undefined || onCollapsedChange ? { value: collapsed } : {}),\n defaultValue: new Set<string>(),\n onValueChange: onCollapsedChange,\n });\n const isCollapsed = useCallback((id: string) => disclosure.bind(id).expanded, [disclosure]);\n const onCollapseToggle = useCallback((id: string) => disclosure.bind(id).toggle(), [disclosure]);\n\n // A dragged task leaves the list while it is in flight — it is in the reader's hand, shown in the\n // drag preview — so the rows close up over the gap it came from. Its sub-tasks go with it: they\n // travel with their parent and are part of the same preview.\n const [draggingTask, setDraggingTask] = useState<Task.Task>();\n const dragging = useMemo(() => (draggingTask ? subtreeIds(tasks, draggingTask) : EMPTY_IDS), [tasks, draggingTask]);\n\n return (\n <TaskListProvider\n tasks={tasks}\n groupByStatus={groupByStatus && !hierarchical}\n showGroupLabels={showGroupLabels}\n showOrdinals={showOrdinals}\n showDescriptions={showDescriptions}\n hierarchical={hierarchical}\n // The handle lives in the ordinal's gutter, so a movable list reserves the track even when it\n // shows no numbers.\n showGutter={showOrdinals || !!onTaskMove}\n statusLabel={statusLabel}\n isCollapsed={isCollapsed}\n onCollapseToggle={onCollapseToggle}\n dragging={dragging}\n onDraggingChange={setDraggingTask}\n onTaskCreate={onTaskCreate}\n onTaskUpdate={onTaskUpdate}\n onTaskDelete={onTaskDelete}\n onTaskSelect={onTaskSelect}\n onTaskMove={onTaskMove}\n selected={selected}\n >\n {/* Both roots are headless, so the pair renders no DOM of its own. */}\n <Listbox.Root {...(selectable ? { value: selected, onValueChange: handleValueChange } : {})}>\n {children}\n </Listbox.Root>\n </TaskListProvider>\n );\n};\n\nTaskListRoot.displayName = 'TaskList.Root';\n\n//\n// Viewport — the scrolling region (the listbox's own viewport). `Create` sits outside it, so the\n// add row stays pinned while the rows scroll.\n//\n\ntype TaskListViewportProps = ComposableProps;\n\nconst TaskListViewport = composable<HTMLDivElement>(({ children, ...props }, forwardedRef) => {\n const { className, ...rest } = composableProps(props);\n return (\n <Listbox.Viewport {...rest} classNames={mx('min-w-0 min-h-0', className)} ref={forwardedRef}>\n {children}\n </Listbox.Viewport>\n );\n});\n\nTaskListViewport.displayName = 'TaskList.Viewport';\n\n//\n// Content — the rows, grouped by status when the root says so.\n//\n\n/**\n * The rows and the create row are separate grids (the create row sits outside the scrolling\n * viewport), so their leading gutters — ordinal, then status — are declared once here: only\n * matching templates keep the `+` under the status control and the input under the titles.\n * Tailwind scans for whole class names, hence four literals rather than a composed prefix. The\n * status gutter is `1.5rem` because that is the width of the `density='sm'` icon button it holds;\n * a narrower track makes the button overflow it and align left instead of centring.\n */\nconst GRID_COLS = {\n content: 'grid-cols-[1.5rem_1fr_min-content_min-content_2rem]',\n contentWithOrdinals: 'grid-cols-[2rem_1.5rem_1fr_min-content_min-content_2rem]',\n create: 'grid-cols-[1.5rem_1fr]',\n createWithOrdinals: 'grid-cols-[2rem_1.5rem_1fr]',\n};\n\ntype TaskListContentProps = ComposableProps;\n\nconst TaskListContent = composable<HTMLUListElement>((props, forwardedRef) => {\n const {\n tasks,\n groupByStatus,\n showGroupLabels,\n showOrdinals,\n showDescriptions,\n hierarchical,\n showGutter,\n statusLabel,\n isCollapsed,\n } = useTaskListContext('TaskList.Content');\n // Ordinals follow the set's canonical order, not the display order, so a task keeps its number as\n // it moves between status groups — and, in a tree, as branches collapse around it. Flat by\n // design: an ordinal names a task (\"run 3\"), and a `1.2.1` path renumbers a whole branch every\n // time anything above it changes.\n const ordinals = useMemo(() => new Map(tasks.map((task, index) => [task.id, index + 1])), [tasks]);\n // Collapsed ids are read through the context callback rather than held here, so the walk still\n // re-runs when one flips; the set itself lives in `Root`.\n const collapsed = useMemo(() => new Set(tasks.map((task) => task.id).filter(isCollapsed)), [tasks, isCollapsed]);\n const rows = useMemo(\n () => (hierarchical ? walkTaskTree(tasks, collapsed) : undefined),\n [hierarchical, tasks, collapsed],\n );\n const groups = useMemo(() => {\n if (!groupByStatus) {\n return tasks.length > 0 ? [{ status: undefined, tasks }] : [];\n }\n\n return STATUS_ORDER.map((status) => ({\n status,\n tasks: tasks.filter((task) => (task.status ?? 'todo') === status),\n })).filter((group) => group.tasks.length > 0);\n }, [tasks, groupByStatus]);\n\n return (\n <Listbox.Content\n {...composableProps(props, {\n // Row height lives on the auto rows — an `h-8` on the grid itself would size the whole\n // list to one row and let the rest overflow invisibly. A described row is taller than one\n // line, so the tracks size to content and the cells align to the first line rather than to\n // the middle of a two-line row.\n classNames: mx(\n 'group grid gap-x-1 w-full min-w-0',\n showDescriptions ? 'auto-rows-min items-start' : 'auto-rows-[2rem] items-center',\n showGutter ? GRID_COLS.contentWithOrdinals : GRID_COLS.content,\n ),\n })}\n aria-label='Tasks'\n ref={forwardedRef}\n >\n {rows\n ? rows.map((row) => (\n <TaskListItem\n key={row.task.id}\n task={row.task}\n ordinal={showOrdinals ? ordinals.get(row.task.id) : undefined}\n row={row}\n />\n ))\n : groups.map(({ status, tasks }) => (\n <Fragment key={status ?? 'all'}>\n {status && showGroupLabels && <TaskListGroupLabel>{statusLabel(status)}</TaskListGroupLabel>}\n {tasks.map((task) => (\n <TaskListItem key={task.id} task={task} ordinal={showOrdinals ? ordinals.get(task.id) : undefined} />\n ))}\n </Fragment>\n ))}\n </Listbox.Content>\n );\n});\n\nTaskListContent.displayName = 'TaskList.Content';\n\n//\n// GroupLabel\n//\n\ntype TaskListGroupLabelProps = ComposableProps;\n\nconst TaskListGroupLabel = composable<HTMLDivElement>(({ children, ...props }, forwardedRef) => {\n const { className, ...rest } = composableProps(props);\n return (\n <div\n {...rest}\n className={mx('col-span-full min-h-(--dx-control) flex items-center text-sm text-description', className)}\n ref={forwardedRef}\n >\n <span>{children}</span>\n </div>\n );\n});\n\nTaskListGroupLabel.displayName = 'TaskList.GroupLabel';\n\n//\n// Drag and drop. The mechanics come from `react-ui-list` (the tree-item hitbox, its instructions,\n// and the drop indicator), so a task tree behaves like the navtree; the row itself stays a listbox\n// option rather than being re-expressed as a treegrid row.\n//\n\n/** How long the cursor must rest on a collapsed branch before it opens, so crossing one does not. */\nconst EXPAND_DWELL = 600;\n\n/**\n * Distance from the title cell's own inline start to the title text: the disclosure toggle (`w-6`)\n * plus the cell's `gap-1`. The description sits in a different grid row and has to clear the same\n * distance to line up under the title.\n */\nconst TOGGLE_INSET = '1.75rem';\n\nconst useTaskDrag = ({\n task,\n row,\n tasks,\n onTaskMove,\n isCollapsed,\n onCollapseToggle,\n onDraggingChange,\n}: {\n task: Task.Task;\n row?: TaskTreeRow;\n tasks: readonly Task.Task[];\n onTaskMove?: (task: Task.Task, placement: TaskPlacement) => void;\n isCollapsed: (id: string) => boolean;\n onCollapseToggle: (id: string) => void;\n onDraggingChange: (task: Task.Task | undefined) => void;\n}) => {\n const rowRef = useRef<HTMLLIElement | null>(null);\n const dragHandleRef = useRef<HTMLSpanElement | null>(null);\n const [instruction, setInstruction] = useState<Instruction | null>(null);\n const expandTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const liftFrame = useRef<number | null>(null);\n\n // Read through a ref so the listeners are registered once per row rather than re-registered on\n // every keystroke elsewhere in the list; a drag in flight must not lose its target.\n const latest = useRef({ tasks, onTaskMove, isCollapsed, onCollapseToggle, onDraggingChange, row });\n latest.current = { tasks, onTaskMove, isCollapsed, onCollapseToggle, onDraggingChange, row };\n\n const cancelLift = useCallback(() => {\n if (liftFrame.current !== null) {\n cancelAnimationFrame(liftFrame.current);\n liftFrame.current = null;\n }\n }, []);\n\n const cancelExpand = useCallback(() => {\n if (expandTimeout.current) {\n clearTimeout(expandTimeout.current);\n expandTimeout.current = null;\n }\n }, []);\n\n const enabled = !!onTaskMove && !!row;\n\n useEffect(() => {\n const element = rowRef.current;\n const handle = dragHandleRef.current;\n if (!enabled || !element || !handle) {\n return;\n }\n\n const data: TaskDragData = { type: TASK_DRAG_TYPE, taskId: task.id };\n\n /** The placement a drag over this row currently means, or undefined when the drop is refused. */\n const placementFor = (instruction: Instruction | null): TaskPlacement | undefined => {\n const intent = dropIntent(instruction);\n const source = latest.current.tasks.find(({ id }) => id === draggingId.current);\n if (!intent || !source) {\n return undefined;\n }\n return resolveTaskPlacement({ tasks: latest.current.tasks, source, target: task, intent });\n };\n\n return combine(\n draggable({\n // Dragging is started from the handle alone: a whole-row drag would fight text selection and\n // the row's own click-to-select.\n element: handle,\n getInitialData: () => data,\n // ...but what is dragged is the task AND its sub-tasks, which travel with it, so the preview\n // is the whole subtree. The browser's default drag image is the dragged element, which here\n // would be the grip alone.\n onGenerateDragPreview: ({ location, nativeSetDragImage }) => {\n const rows = subtreeRows(element, latest.current.tasks, task);\n const { left, top } = element.getBoundingClientRect();\n const { clientX, clientY } = location.initial.input;\n // Offset by where the grip was grabbed, so the preview does not jump under the cursor.\n const offset = { x: clientX - left, y: clientY - top };\n if (rows.length < 2) {\n // A leaf (or a collapsed branch) is one row: the live element is a better image than a\n // clone, since it keeps the subgrid tracks it inherits from the list.\n nativeSetDragImage?.(element, offset.x, offset.y);\n return;\n }\n setCustomNativeDragPreview({\n nativeSetDragImage,\n getOffset: () => offset,\n render: ({ container }) => renderSubtreePreview(container, element, rows),\n });\n },\n onDragStart: () => {\n draggingId.current = task.id;\n // Deferred a frame: the browser snapshots the drag image as it finishes dispatching\n // `dragstart`, and hiding the rows before that would hand it an empty picture.\n liftFrame.current = requestAnimationFrame(() => latest.current.onDraggingChange(task));\n },\n // Also fires when the drag is cancelled, so the rows always come back.\n onDrop: () => {\n draggingId.current = undefined;\n cancelLift();\n latest.current.onDraggingChange(undefined);\n },\n }),\n dropTargetForElements({\n element,\n getData: ({ input, element }) =>\n attachInstruction(data, {\n input,\n element,\n indentPerLevel: INDENT_PER_LEVEL,\n currentLevel: (latest.current.row?.level ?? 1) - 1,\n mode: itemMode({\n branch: latest.current.row?.branch ?? false,\n open: !latest.current.isCollapsed(task.id),\n last: (latest.current.row?.position ?? 1) === (latest.current.row?.setSize ?? 1),\n }),\n // `make-child` is offered on a leaf too: dropping onto one is how a sub-task is made.\n block: [],\n }),\n canDrop: ({ source }) => isTaskDragData(source.data) && source.data.taskId !== task.id,\n getIsSticky: () => true,\n onDrag: ({ self }) => {\n const desired = extractInstruction(self.data);\n // Refused drops are shown as blocked rather than silently ignored, so the cursor says no.\n const next: Instruction | null =\n desired !== null && desired.type !== 'instruction-blocked' && !placementFor(desired)\n ? { type: 'instruction-blocked', desired }\n : desired;\n setInstruction(next);\n\n const { row, isCollapsed, onCollapseToggle } = latest.current;\n if (next?.type === 'make-child' && row?.branch && isCollapsed(task.id) && !expandTimeout.current) {\n expandTimeout.current = setTimeout(() => {\n expandTimeout.current = null;\n onCollapseToggle(task.id);\n }, EXPAND_DWELL);\n } else if (next?.type !== 'make-child') {\n cancelExpand();\n }\n },\n onDragLeave: () => {\n cancelExpand();\n setInstruction(null);\n },\n onDrop: ({ self, source }) => {\n cancelExpand();\n setInstruction(null);\n if (!isTaskDragData(source.data)) {\n return;\n }\n const moved = latest.current.tasks.find(({ id }) => id === source.data.taskId);\n const intent = dropIntent(extractInstruction(self.data));\n if (!moved || !intent) {\n return;\n }\n const placement = resolveTaskPlacement({ tasks: latest.current.tasks, source: moved, target: task, intent });\n if (placement) {\n latest.current.onTaskMove?.(moved, placement);\n }\n },\n }),\n );\n }, [enabled, task.id, cancelExpand, cancelLift]);\n\n useEffect(\n () => () => {\n cancelExpand();\n cancelLift();\n },\n [cancelExpand, cancelLift],\n );\n\n return { instruction, rowRef, dragHandleRef, dragHandle: enabled };\n};\n\n/**\n * The rendered rows of `task`'s subtree, in document order: the row itself plus every visible\n * descendant. A collapsed branch contributes only its own row, which is what the reader sees.\n */\nconst subtreeRows = (element: HTMLElement, tasks: readonly Task.Task[], task: Task.Task): HTMLElement[] => {\n const list = element.parentElement;\n const ids = subtreeIds(tasks, task);\n return list\n ? Array.from(list.querySelectorAll<HTMLElement>('[data-task-id]')).filter((row) =>\n ids.has(row.dataset.taskId ?? ''),\n )\n : [element];\n};\n\n/**\n * Clones the subtree into the drag preview. The container is made a grid carrying the list's own\n * track sizes: the rows are `grid-cols-subgrid`, so without them a detached copy collapses to its\n * content width and the columns no longer line up.\n */\nconst renderSubtreePreview = (container: HTMLElement, element: HTMLElement, rows: HTMLElement[]): void => {\n const list = element.parentElement;\n const listStyle = list && getComputedStyle(list);\n container.style.display = 'grid';\n container.style.width = `${element.getBoundingClientRect().width}px`;\n if (listStyle) {\n container.style.gridTemplateColumns = listStyle.gridTemplateColumns;\n container.style.columnGap = listStyle.columnGap;\n container.style.gridAutoRows = listStyle.gridAutoRows;\n container.style.alignItems = listStyle.alignItems;\n }\n // The preview is torn off the page, so it needs its own ground to sit on.\n container.classList.add('bg-base-surface');\n for (const row of rows) {\n container.appendChild(row.cloneNode(true));\n }\n};\n\n/**\n * Id of the task currently being dragged. Module-level because the drag source and the row under\n * the cursor are different components, and the hitbox reports only the target's own data mid-drag;\n * a rejected drop has to be refused while the cursor is over it, not after the drop.\n */\nconst draggingId = { current: undefined as string | undefined };\n\n//\n// Item — one row. Exported so a host can render its own selection of tasks.\n//\n\n/** A description is a line in a row, not a document: no paragraph block, no heading scale. */\nconst DESCRIPTION_COMPONENTS = {\n p: ({ children }: PropsWithChildren) => <span>{children}</span>,\n};\n\nconst STATUS_ICONS: Record<Task.Status, { icon: string; classNames?: string }> = {\n todo: { icon: 'ph--square--regular', classNames: 'text-subdued' },\n started: { icon: 'ph--hourglass--regular', classNames: 'text-info-text' },\n done: { icon: 'ph--check--regular', classNames: 'text-success-text' },\n failed: { icon: 'ph--x--regular', classNames: 'text-error-text' },\n cancelled: { icon: 'ph--x--regular', classNames: 'text-error-text' },\n};\n\ntype TaskListItemProps = ComposableProps<{ task: Task.Task; ordinal?: number; row?: TaskTreeRow }>;\n\nconst TaskListItem = composable<HTMLLIElement, { task: Task.Task; ordinal?: number; row?: TaskTreeRow }>(\n ({ task, ordinal, row, ...props }, forwardedRef) => {\n const { t } = useTranslation(translationKey);\n const {\n tasks,\n showDescriptions,\n showGutter,\n onTaskUpdate,\n onTaskDelete,\n onTaskSelect,\n onTaskMove,\n isCollapsed,\n onCollapseToggle,\n dragging,\n onDraggingChange,\n } = useTaskListContext('TaskList.Item');\n const { className, ...rest } = composableProps(props);\n const { instruction, rowRef, dragHandleRef, dragHandle } = useTaskDrag({\n task,\n row,\n tasks,\n onTaskMove,\n onCollapseToggle,\n isCollapsed,\n onDraggingChange,\n });\n const open = row ? !isCollapsed(task.id) : undefined;\n\n // Subscribe per row: a query re-emits when membership changes, not when a task's own fields do,\n // so a rename elsewhere (task form, agent, sync) would otherwise leave the row stale.\n const [snapshot] = useObject(task);\n const current = snapshot ?? task;\n\n const done = current.status === 'done';\n const error = current.status === 'failed';\n\n // Only when the list asks for it, and only when there is something to show — an empty second\n // line would make every row taller for nothing.\n const description = showDescriptions ? current.description?.trim() || undefined : undefined;\n\n // Virtual: an open task whose dependencies (resolved within the set) are not all done.\n const blocked = (current.status ?? 'todo') === 'todo' && !TaskSet.isTaskReady(tasks, task);\n // A started agent task is actively being worked by a sub-agent (started is stamped at spawn),\n // so it spins; a human-started task keeps the static glyph.\n const active = current.status === 'started' && current.assignee?.role === 'assistant';\n const { icon, classNames: iconClassNames } = active\n ? { icon: 'ph--spinner--regular', classNames: 'text-info-text animate-spin' }\n : STATUS_ICONS[current.status ?? 'todo'];\n\n const handleToggle = useCallback(\n () => onTaskUpdate?.(task, { status: done ? 'todo' : 'done' }),\n [onTaskUpdate, task, done],\n );\n\n // Restructuring keys. `Alt` rather than the outliner's bare `Tab`/`Shift-Tab`: a row is a\n // listbox option, not a text field, and consuming `Tab` there would remove the only way to move\n // focus out of the list. One modifier covers all four moves.\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLLIElement>) => {\n if (!onTaskMove || !row || !event.altKey) {\n return;\n }\n const placement = (() => {\n switch (event.key) {\n case 'ArrowRight':\n return resolveIndent(tasks, task);\n case 'ArrowLeft':\n return resolveOutdent(tasks, task);\n case 'ArrowUp':\n return resolveNudge(tasks, task, 'up');\n case 'ArrowDown':\n return resolveNudge(tasks, task, 'down');\n default:\n return undefined;\n }\n })();\n if (placement) {\n event.preventDefault();\n event.stopPropagation();\n onTaskMove(task, placement);\n }\n },\n [onTaskMove, row, tasks, task],\n );\n\n return (\n <Listbox.Item\n {...rest}\n id={task.id}\n data-testid='taskList.item'\n // The drag preview has to find this row's descendants in the DOM, and `Listbox.Item`'s `id`\n // is its selection key rather than a DOM id.\n data-task-id={task.id}\n // `px-0`: a subgrid's own inline padding shrinks its first and last tracks, so the listbox\n // item's default inset would push the status control off the column the create row's `+`\n // sits in. The list's inset belongs to the host, not the row.\n classNames={mx(\n 'group/row col-span-full grid grid-cols-subgrid px-0 items-start relative',\n // Hidden rather than unmounted: the drag is anchored to this row's handle, and removing it\n // from the DOM mid-flight would cancel the gesture in some browsers.\n dragging.has(task.id) && 'hidden',\n className,\n )}\n // A row stays `role=option` (that is what carries selection and roving focus), so nesting is\n // announced by these rather than by treegrid semantics.\n aria-level={row?.level}\n aria-posinset={row && row.position}\n aria-setsize={row && row.setSize}\n aria-expanded={row?.branch ? open : undefined}\n // Depth is published once as variables: the title cell and the description sit in different\n // grid rows and both step in by it, and the description additionally clears the toggle so it\n // starts under the title text rather than under the toggle.\n style={\n row\n ? ({\n '--task-indent': paddingIndentation(row.level, INDENT_PER_LEVEL).paddingInlineStart,\n '--task-title-inset': `calc(var(--task-indent) + ${TOGGLE_INSET})`,\n } as CSSProperties)\n : undefined\n }\n onKeyDown={handleKeyDown}\n ref={useComposedRefs(rowRef, forwardedRef)}\n >\n {showGutter && (\n // Handle and ordinal share one cell: the handle takes the ordinal's place on hover rather\n // than claiming a column of its own, so no row shifts when the cursor crosses it.\n <div className='relative flex h-8 items-center justify-center'>\n {ordinal !== undefined && (\n <Tag\n hue={done ? 'green' : error ? 'rose' : 'neutral'}\n classNames={mx(\n 'tabular-nums',\n dragHandle && 'group-hover/row:invisible group-has-[:focus-visible]/row:invisible',\n )}\n >\n {ordinal}\n </Tag>\n )}\n {dragHandle && (\n <span\n data-testid='taskList.dragHandle'\n aria-hidden\n className={mx(\n 'absolute inset-0 grid place-items-center text-subdued cursor-grab active:cursor-grabbing',\n // A handle with no ordinal beneath it is the cell's only content, so it stays put;\n // otherwise it appears only while the row is under the cursor or holds focus.\n ordinal !== undefined && 'invisible group-hover/row:visible group-has-[:focus-visible]/row:visible',\n )}\n ref={dragHandleRef}\n >\n <Icon icon='ph--dots-six-vertical--regular' size={4} />\n </span>\n )}\n </div>\n )}\n {onTaskUpdate ? (\n <IconButton\n classNames={mx('justify-self-center my-1', iconClassNames)}\n variant='ghost'\n density='sm'\n icon={icon}\n iconOnly\n label={done ? t('mark-todo.label') : t('mark-done.label')}\n onClick={handleToggle}\n />\n ) : (\n <span className='grid h-8 place-items-center justify-self-center'>\n <Icon icon={icon} classNames={iconClassNames} size={4} />\n <span className='sr-only'>{t(`status-${current.status ?? 'todo'}.label`)}</span>\n </span>\n )}\n <span\n // Depth pads the title cell alone, so the status control and every trailing cell stay in\n // their subgrid columns and the rows keep one geometry however deep the tree goes.\n className={mx(\n 'flex h-8 items-center gap-1 min-w-0',\n row && 'ps-(--task-indent)',\n onTaskSelect && 'cursor-pointer',\n )}\n >\n {row && (\n <TreeItemToggle\n isBranch={row.branch}\n open={open}\n onClick={(event) => {\n // The row is the selection target; toggling a branch must not also re-select it.\n event.stopPropagation();\n onCollapseToggle(task.id);\n }}\n />\n )}\n <span className='truncate'>{current.title}</span>\n </span>\n {current.assignee ? (\n <span className='flex h-8 items-center'>\n <TaskListAssignee assignee={current.assignee} />\n </span>\n ) : (\n <div />\n )}\n <div className='flex h-8 items-center gap-1'>\n {blocked && <Tag hue='indigo'>{t('task-blocked.label')}</Tag>}\n {current.priority && current.priority !== 'none' && <Tag hue='neutral'>{current.priority}</Tag>}\n </div>\n {onTaskDelete && (\n <CompactIconButton\n variant='ghost'\n icon='ph--x--regular'\n label={t('delete-task.label')}\n classNames='invisible group-hover/row:visible group-has-[:focus-visible]/row:visible'\n onClick={() => onTaskDelete(task)}\n />\n )}\n {instruction && <TreeDropIndicator instruction={instruction} gap={0} />}\n {description && (\n // Its own row in the subgrid, starting under the title and spanning the label columns.\n <MarkdownView\n content={description}\n classNames={mx(\n showGutter ? 'col-start-3' : 'col-start-2',\n // Aligned under its own title — which sits past the disclosure toggle — so a\n // sub-task's description does not read as belonging to the row above it.\n row && 'ps-(--task-title-inset)',\n 'col-span-3 pb-1 text-sm text-description line-clamp-3',\n )}\n // The row supplies the type scale and the clamp, so the description renders as one\n // inline run rather than the block paragraph the default component wraps it in.\n components={DESCRIPTION_COMPONENTS}\n />\n )}\n </Listbox.Item>\n );\n },\n);\n\nTaskListItem.displayName = 'TaskList.Item';\n\n// TODO(burdon): Reconcile with `CompactIconButton` from `react-ui-form`.\nconst CompactIconButton = (props: IconButtonProps) => {\n return (\n <span className='grid size-8 shrink-0 place-items-center'>\n <IconButton variant='ghost' iconOnly density='sm' {...props} />\n </span>\n );\n};\n\n//\n// Create — the add row; renders nothing unless the root supplies `onTaskCreate`.\n//\n\ntype TaskListCreateProps = ComposableProps<{ placeholder?: string }>;\n\nconst TaskListCreate = composable<HTMLDivElement, { placeholder?: string }>(\n ({ placeholder = 'Add task', ...props }, forwardedRef) => {\n const { onTaskCreate, showGutter } = useTaskListContext('TaskList.Create');\n const { className, ...rest } = composableProps(props);\n const [title, setTitle] = useState('');\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n const trimmed = title.trim();\n if (event.key === 'Enter' && trimmed.length > 0) {\n onTaskCreate?.(trimmed);\n setTitle('');\n }\n },\n [title, onTaskCreate],\n );\n\n if (!onTaskCreate) {\n return null;\n }\n\n return (\n <div\n {...rest}\n data-testid='taskList.create'\n className={mx(\n 'grid gap-x-1 items-center w-full min-w-0 h-8 shrink-0',\n showGutter ? GRID_COLS.createWithOrdinals : GRID_COLS.create,\n className,\n )}\n ref={forwardedRef}\n >\n {showGutter && <span />}\n <Icon icon='ph--plus--regular' size={4} classNames='justify-self-center text-subdued' />\n <Input.Root>\n <Input.TextInput\n variant='subdued'\n classNames='px-0'\n placeholder={placeholder}\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n onKeyDown={handleKeyDown}\n />\n </Input.Root>\n </div>\n );\n },\n);\n\nTaskListCreate.displayName = 'TaskList.Create';\n\n//\n// Assignee — actor-aware chip: a Person ref resolves to the contact's name; otherwise fall back to\n// name, email, or a shortened DID; agents (`role: 'assistant'`) are marked with a sparkle.\n//\n\ntype TaskListAssigneeProps = { assignee: Actor.Actor };\n\nconst TaskListAssignee = composable<HTMLSpanElement, TaskListAssigneeProps>(({ assignee }, _forwardedRef) => {\n const [contact] = useObject(assignee.contact);\n const label =\n contact?.fullName ??\n assignee.name ??\n assignee.email ??\n (assignee.identityDid ? shortDid(assignee.identityDid) : undefined);\n const agent = assignee.role === 'assistant';\n if (!label && !agent) {\n return null;\n }\n\n return (\n <Tag hue={agent ? 'purple' : 'indigo'}>\n {agent && <Icon icon='ph--sparkle--regular' size={3} classNames='inline-block me-1' />}\n {label ?? 'agent'}\n </Tag>\n );\n});\n\nTaskListAssignee.displayName = 'TaskList.Assignee';\n\nconst shortDid = (did: string): string => `${did.slice(0, 12)}…`;\n\n//\n// TaskList\n//\n\nexport const TaskList = {\n Root: TaskListRoot,\n Viewport: TaskListViewport,\n Content: TaskListContent,\n GroupLabel: TaskListGroupLabel,\n Item: TaskListItem,\n Create: TaskListCreate,\n Assignee: TaskListAssignee,\n};\n\nexport type {\n TaskListAssigneeProps,\n TaskListContentProps,\n TaskListCreateProps,\n TaskListGroupLabelProps,\n TaskListItemProps,\n TaskListRootProps,\n TaskListViewportProps,\n};\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ //#region src/translations.ts
2
+ var translationKey = "@dxos/react-ui-task";
3
+ var translations = [{ "en-US": { [translationKey]: {
4
+ "mark-done.label": "Mark done",
5
+ "mark-todo.label": "Mark todo",
6
+ "status-todo.label": "To Do",
7
+ "status-started.label": "Started",
8
+ "status-done.label": "Done",
9
+ "status-failed.label": "Failed",
10
+ "status-cancelled.label": "Cancelled",
11
+ "task-blocked.label": "Blocked",
12
+ "delete-task.label": "Delete task"
13
+ } } }];
14
+ //#endregion
15
+ export { translationKey, translations };
16
+
17
+ //# sourceMappingURL=translations.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translations.mjs","names":[],"sources":["../../src/translations.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Resource } from '@dxos/react-ui';\n\nexport const translationKey = '@dxos/react-ui-task';\n\nexport const translations = [\n {\n 'en-US': {\n [translationKey]: {\n 'mark-done.label': 'Mark done',\n 'mark-todo.label': 'Mark todo',\n 'status-todo.label': 'To Do',\n 'status-started.label': 'Started',\n 'status-done.label': 'Done',\n 'status-failed.label': 'Failed',\n 'status-cancelled.label': 'Cancelled',\n 'task-blocked.label': 'Blocked',\n 'delete-task.label': 'Delete task',\n },\n },\n },\n] as const satisfies Resource[];\n"],"mappings":";AAMA,IAAa,iBAAiB;AAE9B,IAAa,eAAe,CAC1B,EACE,SAAS,GACN,iBAAiB;CAChB,mBAAmB;CACnB,mBAAmB;CACnB,qBAAqB;CACrB,wBAAwB;CACxB,qBAAqB;CACrB,uBAAuB;CACvB,0BAA0B;CAC1B,sBAAsB;CACtB,qBAAqB;AACvB,EACF,EACF,CACF"}
@@ -1,34 +1,64 @@
1
1
  import React, { type PropsWithChildren } from 'react';
2
2
  import { type Actor, type Task } from '@dxos/types';
3
3
  import { type ComposableProps } from '@dxos/ui-types';
4
- export type TaskStatus = NonNullable<Task.Task['status']>;
4
+ import { type TaskPlacement, type TaskTreeRow } from './hierarchy';
5
5
  /** Linear-style status groups, most active first. */
6
- export declare const STATUS_ORDER: TaskStatus[];
6
+ export declare const STATUS_ORDER: Task.Status[];
7
7
  export type TaskPatch = Partial<Pick<Task.Task, 'title' | 'status' | 'priority' | 'estimate' | 'assignee'>>;
8
8
  type TaskListRootProps = PropsWithChildren<{
9
9
  tasks: readonly Task.Task[];
10
10
  /** Group rows into status sections (Linear order); flat list otherwise. */
11
11
  groupByStatus?: boolean;
12
+ /** Render the status heading above each group; grouping order is kept either way. */
13
+ showGroupLabels?: boolean;
14
+ /** Number rows by their position in `tasks` (set order), so tasks can be referenced by ordinal. */
15
+ showOrdinals?: boolean;
16
+ /** Render each task's description under its title; rows grow to fit. Off by default, so a
17
+ * single-line list (e.g. the chat strip) keeps one row per task. */
18
+ showDescriptions?: boolean;
12
19
  /** i18n hook for group headings; defaults to English labels. */
13
- statusLabel?: (status: TaskStatus) => string;
20
+ statusLabel?: (status: Task.Status) => string;
14
21
  /** Enables `Create`; called with the trimmed title. */
15
22
  onTaskCreate?: (title: string) => void;
16
23
  /** Enables the done toggle. Every mutation is delegated — the list never writes. */
17
24
  onTaskUpdate?: (task: Task.Task, patch: TaskPatch) => void;
18
25
  /** Enables the per-row delete affordance. */
19
26
  onTaskDelete?: (task: Task.Task) => void;
20
- /** Row click. */
27
+ /** Row click. Wiring it (or `selected`) makes the list selectable, so the row shows as selected. */
21
28
  onTaskSelect?: (task: Task.Task) => void;
29
+ /** Selected task id (controlled); omit to let the list track the last row clicked. */
30
+ selected?: string;
31
+ /**
32
+ * Render the set as the tree it stores (`Task.parentTask`), not as status groups — the two are
33
+ * mutually exclusive, since a tree regrouped by status is no longer a tree.
34
+ */
35
+ hierarchical?: boolean;
36
+ /**
37
+ * Enables restructuring by drag and by keyboard; called with the one move the gesture means.
38
+ * `MoveTask` takes exactly this pair, so a drop is a single mutation rather than a re-parent
39
+ * followed by a reposition.
40
+ */
41
+ onTaskMove?: (task: Task.Task, placement: TaskPlacement) => void;
42
+ /**
43
+ * Ids of the branches whose sub-tasks are hidden (controlled). Collapsed rather than expanded
44
+ * ids, because a branch is open by default: tracking the expanded set would render a task's new
45
+ * first sub-task hidden, the moment adding it made its parent a branch. Per viewer and per list —
46
+ * a collapsed branch is not a property of the work — so this is state, not stored on the object.
47
+ */
48
+ collapsed?: ReadonlySet<string>;
49
+ onCollapsedChange?: (collapsed: ReadonlySet<string>) => void;
22
50
  }>;
23
- declare function TaskListRoot({ children, tasks, groupByStatus, statusLabel, onTaskCreate, onTaskUpdate, onTaskDelete, onTaskSelect }: TaskListRootProps): React.JSX.Element;
24
- declare namespace TaskListRoot {
25
- var displayName: string;
26
- }
51
+ declare const TaskListRoot: {
52
+ ({ children, tasks, groupByStatus, showGroupLabels, showOrdinals, showDescriptions, statusLabel, onTaskCreate, onTaskUpdate, onTaskDelete, onTaskSelect, onTaskMove, selected: selectedProp, hierarchical, collapsed, onCollapsedChange, }: TaskListRootProps): React.JSX.Element;
53
+ displayName: string;
54
+ };
27
55
  type TaskListViewportProps = ComposableProps;
28
56
  type TaskListContentProps = ComposableProps;
29
57
  type TaskListGroupLabelProps = ComposableProps;
30
58
  type TaskListItemProps = ComposableProps<{
31
59
  task: Task.Task;
60
+ ordinal?: number;
61
+ row?: TaskTreeRow;
32
62
  }>;
33
63
  type TaskListCreateProps = ComposableProps<{
34
64
  placeholder?: string;
@@ -37,10 +67,7 @@ type TaskListAssigneeProps = {
37
67
  assignee: Actor.Actor;
38
68
  };
39
69
  export declare const TaskList: {
40
- Root: {
41
- ({ children, tasks, groupByStatus, statusLabel, onTaskCreate, onTaskUpdate, onTaskDelete, onTaskSelect, }: TaskListRootProps): React.JSX.Element;
42
- displayName: string;
43
- };
70
+ Root: typeof TaskListRoot;
44
71
  Viewport: React.ForwardRefExoticComponent<Omit<{}, "className"> & {
45
72
  classNames?: import("@dxos/ui-types").ClassNameValue;
46
73
  } & Pick<React.HTMLAttributes<Element>, "children" | "role" | "style"> & React.RefAttributes<HTMLDivElement>>;
@@ -52,6 +79,8 @@ export declare const TaskList: {
52
79
  } & Pick<React.HTMLAttributes<Element>, "children" | "role" | "style"> & React.RefAttributes<HTMLDivElement>>;
53
80
  Item: React.ForwardRefExoticComponent<Omit<{
54
81
  task: Task.Task;
82
+ ordinal?: number;
83
+ row?: TaskTreeRow;
55
84
  }, "className"> & {
56
85
  classNames?: import("@dxos/ui-types").ClassNameValue;
57
86
  } & Pick<React.HTMLAttributes<Element>, "children" | "role" | "style"> & React.RefAttributes<HTMLLIElement>>;
@@ -1 +1 @@
1
- {"version":3,"file":"TaskList.d.ts","sourceRoot":"","sources":["../../../../../src/components/TaskList/TaskList.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAgC,KAAK,iBAAiB,EAAkC,MAAM,OAAO,CAAC;AAKpH,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAEpD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAItD,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AAE1D,qDAAqD;AACrD,eAAO,MAAM,YAAY,EAAE,UAAU,EAA2D,CAAC;AAUjG,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC;AA+B5G,KAAK,iBAAiB,GAAG,iBAAiB,CAAC;IACzC,KAAK,EAAE,SAAS,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,CAAC;IAC7C,uDAAuD;IACvD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,oFAAoF;IACpF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC3D,6CAA6C;IAC7C,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;IACzC,iBAAiB;IACjB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;CAC1C,CAAC,CAAC;8BAEmB,EACpB,QAAQ,EACR,KAAK,EACL,aAAoB,EACpB,WAAuD,EACvD,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,EAAE,iBAAiB;;;;AAsBpB,KAAK,qBAAqB,GAAG,eAAe,CAAC;AAiB7C,KAAK,oBAAoB,GAAG,eAAe,CAAC;AAoC5C,KAAK,uBAAuB,GAAG,eAAe,CAAC;AAiB/C,KAAK,iBAAiB,GAAG,eAAe,CAAC;IAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAA;CAAE,CAAC,CAAC;AAoE9D,KAAK,mBAAmB,GAAG,eAAe,CAAC;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AA8CrE,KAAK,qBAAqB,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAA;CAAE,CAAC;AA8BvD,eAAO,MAAM,QAAQ;IACnB,IAAI;mHA7OH,iBAAiB;;;IA8OlB,QAAQ;;;IACR,OAAO;;;IACP,UAAU;;;IACV,IAAI;cAnJiD,IAAI,CAAC,IAAI;;;;IAoJ9D,MAAM;sBAhF0D,MAAM;;;;IAiFtE,QAAQ;;;CACT,CAAC;AAEF,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,GACtB,CAAC"}
1
+ {"version":3,"file":"TaskList.d.ts","sourceRoot":"","sources":["../../../../../src/components/TaskList/TaskList.tsx"],"names":[],"mappings":"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"}
@@ -1,14 +1,42 @@
1
1
  import { type StoryObj } from '@storybook/react-vite';
2
2
  import React from 'react';
3
+ declare const DefaultStory: ({ readonly, showGroupLabels, showOrdinals, showDescriptions, hierarchical, }: {
4
+ readonly?: boolean;
5
+ showGroupLabels?: boolean;
6
+ showOrdinals?: boolean;
7
+ showDescriptions?: boolean;
8
+ hierarchical?: boolean;
9
+ }) => React.JSX.Element;
3
10
  declare const meta: {
4
11
  title: string;
5
- render: ({ readonly }: {
6
- readonly?: boolean;
7
- }) => React.JSX.Element;
12
+ render: typeof DefaultStory;
8
13
  decorators: import("@storybook/react").Decorator[];
14
+ parameters: {
15
+ translations: [{
16
+ readonly 'en-US': {
17
+ readonly "@dxos/react-ui-task": {
18
+ readonly 'mark-done.label': 'Mark done';
19
+ readonly 'mark-todo.label': 'Mark todo';
20
+ readonly 'status-todo.label': 'To Do';
21
+ readonly 'status-started.label': 'Started';
22
+ readonly 'status-done.label': 'Done';
23
+ readonly 'status-failed.label': 'Failed';
24
+ readonly 'status-cancelled.label': 'Cancelled';
25
+ readonly 'task-blocked.label': 'Blocked';
26
+ readonly 'delete-task.label': 'Delete task';
27
+ };
28
+ };
29
+ }];
30
+ };
9
31
  };
10
32
  export default meta;
11
33
  type Story = StoryObj<typeof meta>;
12
34
  export declare const Default: Story;
13
35
  export declare const Readonly: Story;
36
+ export declare const WithoutGroupLabels: Story;
37
+ export declare const WithOrdinals: Story;
38
+ export declare const WithDescriptions: Story;
39
+ export declare const Hierarchical: Story;
40
+ export declare const TestHierarchy: Story;
41
+ export declare const Test: Story;
14
42
  //# sourceMappingURL=TaskList.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TaskList.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/TaskList/TaskList.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAgC,MAAM,OAAO,CAAC;AAkDrD,QAAA,MAAM,IAAI;;2BAjC0B;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE;;CAqCrB,CAAC;eAEvB,IAAI;AAEnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KA8BrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAEtB,CAAC"}
1
+ {"version":3,"file":"TaskList.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/TaskList/TaskList.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAgC,MAAM,OAAO,CAAC;AAwErD,QAAA,MAAM,YAAY,iFAMf;IACD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,sBAqDA,CAAC;AAEF,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;CAK2B,CAAC;eAEvB,IAAI;AAEnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAAU,CAAC;AAEjC,eAAO,MAAM,QAAQ,EAAE,KAItB,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAIhC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAK1B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAM9B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAsH3B,CAAC;AAEF,eAAO,MAAM,IAAI,EAAE,KA+BlB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { type Instruction, type ItemMode } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
2
+ import { type TaskDropIntent } from './hierarchy';
3
+ /**
4
+ * Drag payload for a task row. Tagged so a drop target can tell a task from any other draggable
5
+ * sharing the page (the navtree, a mosaic tile) rather than reading a bare `id` off it.
6
+ */
7
+ export declare const TASK_DRAG_TYPE = "dxos/task";
8
+ export type TaskDragData = {
9
+ type: typeof TASK_DRAG_TYPE;
10
+ taskId: string;
11
+ };
12
+ export declare const isTaskDragData: (data: Record<string | symbol, unknown>) => data is TaskDragData;
13
+ /** Indentation per level, matching the navtree so the two trees step identically. */
14
+ export declare const INDENT_PER_LEVEL = 8;
15
+ /**
16
+ * The hitbox's item mode, which decides where its bands sit: the last row of a group gets a
17
+ * shallower `reorder-below` band (there is nothing beneath it to reorder against), and an expanded
18
+ * branch gets none at all — below it is its own first child.
19
+ */
20
+ export declare const itemMode: ({ branch, open, last }: {
21
+ branch: boolean;
22
+ open: boolean;
23
+ last: boolean;
24
+ }) => ItemMode;
25
+ /** The drop intent an instruction expresses, or undefined when it carries none (blocked). */
26
+ export declare const dropIntent: (instruction: Instruction | null) => TaskDropIntent | undefined;
27
+ //# sourceMappingURL=dnd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dnd.d.ts","sourceRoot":"","sources":["../../../../../src/components/TaskList/dnd.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,oDAAoD,CAAC;AAErG,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,cAAc,cAAc,CAAC;AAE1C,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,cAAc,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,cAAc,SAAU,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI,IAAI,YACf,CAAC;AAElE,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC;;;;GAIG;AACH,eAAO,MAAM,QAAQ,2BAA4B;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,KAAG,QAClC,CAAC;AAEpE,6FAA6F;AAC7F,eAAO,MAAM,UAAU,gBAAiB,WAAW,GAAG,IAAI,KAAG,cAAc,GAAG,SAW7E,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { type Task } from '@dxos/types';
2
+ /**
3
+ * The tree the list renders, and where a gesture puts a task in it. Kept out of the components
4
+ * because this is the part a regression would break: a drop cannot be driven synthetically
5
+ * (pragmatic-drag-and-drop uses native HTML5 drag events), so the placement calculation is tested
6
+ * directly instead.
7
+ *
8
+ * `TaskSet.tasks` is flat and holds every task; `Task.parentTask` is the hierarchy. The array is
9
+ * NOT a pre-order traversal and is deliberately not made into one: array order decides SIBLING
10
+ * order only, and the walk supplies the rest. A parent therefore moves without dragging its
11
+ * subtree's entries along, and a peer reordering one branch cannot corrupt another.
12
+ */
13
+ /** One rendered row: the task, its depth, and where it sits among its siblings. */
14
+ export type TaskTreeRow = {
15
+ task: Task.Task;
16
+ /** 1 for a root task, incrementing per level. */
17
+ level: number;
18
+ /** Whether the task has sub-tasks — a leaf renders its ordinal where a branch renders a toggle. */
19
+ branch: boolean;
20
+ /** 1-based position among its siblings, and how many siblings there are (`aria-posinset`/`setsize`). */
21
+ position: number;
22
+ setSize: number;
23
+ /** Ids from the root down to (and excluding) the task, so a row knows what to collapse into. */
24
+ ancestors: string[];
25
+ };
26
+ /**
27
+ * Walks the set's tree in sibling order, omitting the descendants of any collapsed task.
28
+ * Cycle-safe: a malformed `parentTask` loop is visited once and then skipped, so a corrupt set
29
+ * renders short rather than hanging the list.
30
+ */
31
+ export declare const walkTaskTree: (tasks: readonly Task.Task[], collapsed?: ReadonlySet<string>) => TaskTreeRow[];
32
+ /** Every task transitively under `task`, including `task` itself. Cycle-safe. */
33
+ export declare const subtreeIds: (tasks: readonly Task.Task[], task: Task.Task) => Set<string>;
34
+ /** The three tree-item intents, matching `@atlaskit/pragmatic-drag-and-drop-hitbox`. */
35
+ export type TaskDropIntent = 'reorder-above' | 'reorder-below' | 'make-child';
36
+ /**
37
+ * Where a task lands, in the two terms `MoveTask` takes. `parentTask` is `null` for a root task
38
+ * (matching the verb's convention) and `before` is undefined when the task goes last — which is
39
+ * also last among its new siblings, since nothing after it in the array shares the parent.
40
+ */
41
+ export type TaskPlacement = {
42
+ parentTask: Task.Task | null;
43
+ before?: Task.Task;
44
+ };
45
+ /**
46
+ * Resolves a drop into a single `MoveTask` call, or `undefined` when the gesture is rejected.
47
+ *
48
+ * Rejected rather than silently ignored, so the caller can say no with the cursor:
49
+ * - onto itself, or into its own subtree (the cycle `UpdateTask` rejects);
50
+ * - onto a task from another set — a membership transfer, not a reorder.
51
+ */
52
+ export declare const resolveTaskPlacement: ({ tasks, source, target, intent, }: {
53
+ tasks: readonly Task.Task[];
54
+ source: Task.Task;
55
+ target: Task.Task;
56
+ intent: TaskDropIntent;
57
+ }) => TaskPlacement | undefined;
58
+ /**
59
+ * Indent: the task becomes the last child of its previous sibling — the outliner's `Tab`. Undefined
60
+ * when it is the first among its siblings and so has nothing to indent under.
61
+ */
62
+ export declare const resolveIndent: (tasks: readonly Task.Task[], task: Task.Task) => TaskPlacement | undefined;
63
+ /**
64
+ * Outdent: the task becomes the next sibling of its parent — the outliner's `Shift-Tab`. Undefined
65
+ * for a task already at the root.
66
+ */
67
+ export declare const resolveOutdent: (tasks: readonly Task.Task[], task: Task.Task) => TaskPlacement | undefined;
68
+ /**
69
+ * Move within the current parent — `Alt-ArrowUp` / `Alt-ArrowDown`. Undefined at either end, so the
70
+ * key is a no-op rather than silently jumping the task out of its branch.
71
+ */
72
+ export declare const resolveNudge: (tasks: readonly Task.Task[], task: Task.Task, direction: 'up' | 'down') => TaskPlacement | undefined;
73
+ //# sourceMappingURL=hierarchy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hierarchy.d.ts","sourceRoot":"","sources":["../../../../../src/components/TaskList/hierarchy.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,IAAI,EAAW,MAAM,aAAa,CAAC;AAEjD;;;;;;;;;;GAUG;AAEH,mFAAmF;AACnF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;IAChB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,mGAAmG;IACnG,MAAM,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gGAAgG;IAChG,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,UAAW,SAAS,IAAI,CAAC,IAAI,EAAE,cAAc,WAAW,CAAC,MAAM,CAAC,KAAG,WAAW,EA2BtG,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,UAAU,UAAW,SAAS,IAAI,CAAC,IAAI,EAAE,QAAQ,IAAI,CAAC,IAAI,KAAG,GAAG,CAAC,MAAM,CAanF,CAAC;AAEF,wFAAwF;AACxF,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,eAAe,GAAG,YAAY,CAAC;AAE9E;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;CACpB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,uCAK9B;IACD,KAAK,EAAE,SAAS,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;IAClB,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;CACxB,KAAG,aAAa,GAAG,SA2BnB,CAAC;AAaF;;;GAGG;AACH,eAAO,MAAM,aAAa,UAAW,SAAS,IAAI,CAAC,IAAI,EAAE,QAAQ,IAAI,CAAC,IAAI,KAAG,aAAa,GAAG,SAI5F,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,UAAW,SAAS,IAAI,CAAC,IAAI,EAAE,QAAQ,IAAI,CAAC,IAAI,KAAG,aAAa,GAAG,SAO7F,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,UAChB,SAAS,IAAI,CAAC,IAAI,EAAE,QACrB,IAAI,CAAC,IAAI,aACJ,IAAI,GAAG,MAAM,KACvB,aAAa,GAAG,SAOlB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hierarchy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hierarchy.test.d.ts","sourceRoot":"","sources":["../../../../../src/components/TaskList/hierarchy.test.ts"],"names":[],"mappings":""}
@@ -1,2 +1,3 @@
1
+ export * from './hierarchy';
1
2
  export * from './TaskList';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/TaskList/index.ts"],"names":[],"mappings":"AAIA,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/TaskList/index.ts"],"names":[],"mappings":"AAIA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,17 @@
1
+ export declare const translationKey = "@dxos/react-ui-task";
2
+ export declare const translations: [{
3
+ readonly 'en-US': {
4
+ readonly "@dxos/react-ui-task": {
5
+ readonly 'mark-done.label': 'Mark done';
6
+ readonly 'mark-todo.label': 'Mark todo';
7
+ readonly 'status-todo.label': 'To Do';
8
+ readonly 'status-started.label': 'Started';
9
+ readonly 'status-done.label': 'Done';
10
+ readonly 'status-failed.label': 'Failed';
11
+ readonly 'status-cancelled.label': 'Cancelled';
12
+ readonly 'task-blocked.label': 'Blocked';
13
+ readonly 'delete-task.label': 'Delete task';
14
+ };
15
+ };
16
+ }];
17
+ //# sourceMappingURL=translations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,wBAAwB,CAAC;AAEpD,eAAO,MAAM,YAAY;;;qBAIjB,iBAAiB,EAAE,WAAW;qBAC9B,iBAAiB,EAAE,WAAW;qBAC9B,mBAAmB,EAAE,OAAO;qBAC5B,sBAAsB,EAAE,SAAS;qBACjC,mBAAmB,EAAE,MAAM;qBAC3B,qBAAqB,EAAE,QAAQ;qBAC/B,wBAAwB,EAAE,WAAW;qBACrC,oBAAoB,EAAE,SAAS;qBAC/B,mBAAmB,EAAE,aAAa;;;EAIX,CAAC"}