Skip to content

Commit

Permalink
Merge pull request obsidian-tasks-group#3118 from ilandikov/feat-rend…
Browse files Browse the repository at this point in the history
…er-parent-child

feat: add experimental rendering of child tasks and list items
  • Loading branch information
claremacrae authored Oct 14, 2024
2 parents 0128578 + 244ba7b commit 7bf7ae3
Show file tree
Hide file tree
Showing 5 changed files with 400 additions and 89 deletions.
85 changes: 79 additions & 6 deletions src/Renderer/QueryResultsRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Component, TFile } from 'obsidian';
import { GlobalFilter } from '../Config/GlobalFilter';
import { GlobalQuery } from '../Config/GlobalQuery';
import { postponeButtonTitle, shouldShowPostponeButton } from '../DateTime/Postponer';
import type { IQuery } from '../IQuery';
import { QueryLayout } from '../Layout/QueryLayout';
import { TaskLayout } from '../Layout/TaskLayout';
Expand All @@ -10,9 +11,9 @@ import { State } from '../Obsidian/Cache';
import type { GroupDisplayHeading } from '../Query/Group/GroupDisplayHeading';
import type { TaskGroups } from '../Query/Group/TaskGroups';
import type { QueryResult } from '../Query/QueryResult';
import { postponeButtonTitle, shouldShowPostponeButton } from '../DateTime/Postponer';
import type { TasksFile } from '../Scripting/TasksFile';
import type { Task } from '../Task/Task';
import type { ListItem } from '../Task/ListItem';
import { Task } from '../Task/Task';
import { PostponeMenu } from '../ui/Menus/PostponeMenu';
import { TaskLineRenderer, type TextRenderer, createAndAppendElement } from './TaskLineRenderer';

Expand Down Expand Up @@ -188,14 +189,16 @@ export class QueryResultsRenderer {
// will be empty, and no headings will be added.
await this.addGroupHeadings(content, group.groupHeadings);

await this.createTaskList(group.tasks, content, queryRendererParameters);
const renderedTasks: Set<ListItem> = new Set();
await this.createTaskList(group.tasks, content, queryRendererParameters, renderedTasks);
}
}

private async createTaskList(
tasks: Task[],
content: HTMLDivElement,
tasks: ListItem[],
content: HTMLElement,
queryRendererParameters: QueryRendererParameters,
renderedTasks: Set<ListItem>,
): Promise<void> {
const taskList = createAndAppendElement('ul', content);

Expand All @@ -217,12 +220,80 @@ export class QueryResultsRenderer {
});

for (const [taskIndex, task] of tasks.entries()) {
await this.addTask(taskList, taskLineRenderer, task, taskIndex, queryRendererParameters);
if (this.alreadyRendered(task, renderedTasks)) {
continue;
}

if (this.willBeRenderedLater(task, renderedTasks, tasks)) {
continue;
}

const listItem = await this.addTaskOrListItem(
taskList,
taskLineRenderer,
task,
taskIndex,
queryRendererParameters,
);
renderedTasks.add(task);

if (task.children.length > 0) {
await this.createTaskList(task.children, listItem, queryRendererParameters, renderedTasks);
task.children.forEach((childTask) => {
renderedTasks.add(childTask);
});
}
}

content.appendChild(taskList);
}

private willBeRenderedLater(task: ListItem, renderedTasks: Set<ListItem>, tasks: ListItem[]) {
// Try to find the closest parent that is a task
let closestParentTask = task.parent;
while (closestParentTask !== null && !(closestParentTask instanceof Task)) {
closestParentTask = closestParentTask.parent;
}

if (!closestParentTask) {
return false;
}

if (!renderedTasks.has(closestParentTask)) {
// This task is a direct or indirect child of another task that we are waiting to draw,
// so don't draw it yet, it will be done recursively later.
if (tasks.includes(closestParentTask)) {
return true;
}
}

return false;
}

private alreadyRendered(task: ListItem, renderedTasks: Set<ListItem>) {
return renderedTasks.has(task);
}

private async addTaskOrListItem(
taskList: HTMLUListElement,
taskLineRenderer: TaskLineRenderer,
task: ListItem,
taskIndex: number,
queryRendererParameters: QueryRendererParameters,
) {
if (task instanceof Task) {
return await this.addTask(taskList, taskLineRenderer, task, taskIndex, queryRendererParameters);
}

return this.addListItem(taskList, task);
}

private addListItem(taskList: HTMLUListElement, listItem: ListItem) {
const li = createAndAppendElement('li', taskList);
li.textContent = listItem.originalMarkdown;
return li;
}

private async addTask(
taskList: HTMLUListElement,
taskLineRenderer: TaskLineRenderer,
Expand Down Expand Up @@ -259,6 +330,8 @@ export class QueryResultsRenderer {
}

taskList.appendChild(listItem);

return listItem;
}

private addEditButton(listItem: HTMLElement, task: Task, queryRendererParameters: QueryRendererParameters) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,86 +19,104 @@
</span>
<a class="tasks-edit" title="Edit task" href="#"></a>
</span>
</li>
<li
class="task-list-item plugin-tasks-list-item"
data-task-priority="normal"
data-task=""
data-line="1"
data-task-status-name="Todo"
data-task-status-type="TODO">
<input class="task-list-item-checkbox" type="checkbox" title="Right-click for options" data-line="1" />
<span class="tasks-list-text">
<span class="task-description"><span>child 1</span></span>
</span>
<span class="task-extras">
<span class="tasks-backlink">
(
<a rel="noopener" target="_blank" class="internal-link">inheritance_rendering_sample</a>
)
</span>
<a class="tasks-edit" title="Edit task" href="#"></a>
</span>
</li>
<li
class="task-list-item plugin-tasks-list-item"
data-task-priority="normal"
data-task=""
data-line="2"
data-task-status-name="Todo"
data-task-status-type="TODO">
<input class="task-list-item-checkbox" type="checkbox" title="Right-click for options" data-line="2" />
<span class="tasks-list-text">
<span class="task-description"><span>grandchild 1</span></span>
</span>
<span class="task-extras">
<span class="tasks-backlink">
(
<a rel="noopener" target="_blank" class="internal-link">inheritance_rendering_sample</a>
)
</span>
<a class="tasks-edit" title="Edit task" href="#"></a>
</span>
</li>
<li
class="task-list-item plugin-tasks-list-item"
data-task-priority="normal"
data-task=""
data-line="3"
data-task-status-name="Todo"
data-task-status-type="TODO">
<input class="task-list-item-checkbox" type="checkbox" title="Right-click for options" data-line="3" />
<span class="tasks-list-text">
<span class="task-description"><span>child 2</span></span>
</span>
<span class="task-extras">
<span class="tasks-backlink">
(
<a rel="noopener" target="_blank" class="internal-link">inheritance_rendering_sample</a>
)
</span>
<a class="tasks-edit" title="Edit task" href="#"></a>
</span>
</li>
<li
class="task-list-item plugin-tasks-list-item"
data-task-priority="normal"
data-task=""
data-line="4"
data-task-status-name="Todo"
data-task-status-type="TODO">
<input class="task-list-item-checkbox" type="checkbox" title="Right-click for options" data-line="4" />
<span class="tasks-list-text">
<span class="task-description"><span>grand grand child</span></span>
</span>
<span class="task-extras">
<span class="tasks-backlink">
(
<a rel="noopener" target="_blank" class="internal-link">inheritance_rendering_sample</a>
)
</span>
<a class="tasks-edit" title="Edit task" href="#"></a>
</span>
<ul class="contains-task-list plugin-tasks-query-result tasks-layout-hide-urgency">
<li
class="task-list-item plugin-tasks-list-item"
data-task-priority="normal"
data-task=""
data-line="0"
data-task-status-name="Todo"
data-task-status-type="TODO">
<input class="task-list-item-checkbox" type="checkbox" title="Right-click for options" data-line="0" />
<span class="tasks-list-text">
<span class="task-description"><span>child 1</span></span>
</span>
<span class="task-extras">
<span class="tasks-backlink">
(
<a rel="noopener" target="_blank" class="internal-link">inheritance_rendering_sample</a>
)
</span>
<a class="tasks-edit" title="Edit task" href="#"></a>
</span>
<ul class="contains-task-list plugin-tasks-query-result tasks-layout-hide-urgency">
<li
class="task-list-item plugin-tasks-list-item"
data-task-priority="normal"
data-task=""
data-line="0"
data-task-status-name="Todo"
data-task-status-type="TODO">
<input class="task-list-item-checkbox" type="checkbox" title="Right-click for options" data-line="0" />
<span class="tasks-list-text">
<span class="task-description"><span>grandchild 1</span></span>
</span>
<span class="task-extras">
<span class="tasks-backlink">
(
<a rel="noopener" target="_blank" class="internal-link">inheritance_rendering_sample</a>
)
</span>
<a class="tasks-edit" title="Edit task" href="#"></a>
</span>
<ul class="contains-task-list plugin-tasks-query-result tasks-layout-hide-urgency">
<li>- list item grand grand child</li>
</ul>
</li>
</ul>
</li>
<li
class="task-list-item plugin-tasks-list-item"
data-task-priority="normal"
data-task=""
data-line="1"
data-task-status-name="Todo"
data-task-status-type="TODO">
<input class="task-list-item-checkbox" type="checkbox" title="Right-click for options" data-line="1" />
<span class="tasks-list-text">
<span class="task-description"><span>child 2</span></span>
</span>
<span class="task-extras">
<span class="tasks-backlink">
(
<a rel="noopener" target="_blank" class="internal-link">inheritance_rendering_sample</a>
)
</span>
<a class="tasks-edit" title="Edit task" href="#"></a>
</span>
<ul class="contains-task-list plugin-tasks-query-result tasks-layout-hide-urgency">
<li>
- non task grandchild
<ul class="contains-task-list plugin-tasks-query-result tasks-layout-hide-urgency">
<li
class="task-list-item plugin-tasks-list-item"
data-task-priority="normal"
data-task=""
data-line="0"
data-task-status-name="Todo"
data-task-status-type="TODO">
<input
class="task-list-item-checkbox"
type="checkbox"
title="Right-click for options"
data-line="0" />
<span class="tasks-list-text">
<span class="task-description"><span>grand grand child</span></span>
</span>
<span class="task-extras">
<span class="tasks-backlink">
(
<a rel="noopener" target="_blank" class="internal-link">inheritance_rendering_sample</a>
)
</span>
<a class="tasks-edit" title="Edit task" href="#"></a>
</span>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li
class="task-list-item plugin-tasks-list-item"
Expand Down
Loading

0 comments on commit 7bf7ae3

Please sign in to comment.