Skip to content

Commit f2615b3

Browse files
authored
Merge pull request #3671 from obsidian-tasks-group/refactor-task-line-renderer
refactor: Allow TaskLineRenderer to render to multiple UL elements
2 parents ee071a1 + b6e70bd commit f2615b3

File tree

5 files changed

+40
-43
lines changed

5 files changed

+40
-43
lines changed

src/Obsidian/InlineRenderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@ export class InlineRenderer {
120120
const taskLineRenderer = new TaskLineRenderer({
121121
obsidianApp: this.app,
122122
obsidianComponent: childComponent,
123-
parentUlElement: element,
124123
taskLayoutOptions: new TaskLayoutOptions(),
125124
queryLayoutOptions: new QueryLayoutOptions(),
126125
});
@@ -139,6 +138,7 @@ export class InlineRenderer {
139138
const dataLine: string = renderedElement.getAttr('data-line') ?? '0';
140139
const taskIndex: number = Number.parseInt(dataLine, 10);
141140
const taskElement = await taskLineRenderer.renderTaskLine({
141+
parentUlElement: element,
142142
task,
143143
taskIndex,
144144
isTaskInQueryFile: true,

src/Renderer/HtmlQueryResultsRenderer.ts

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export class HtmlQueryResultsRenderer {
4242
// TODO access this via getContent() for now
4343
public content: HTMLDivElement | null = null;
4444

45+
private readonly taskLineRenderer: TaskLineRenderer;
46+
4547
private readonly ulElementStack: HTMLUListElement[] = [];
4648
private readonly renderedListItems: Set<ListItem> = new Set<ListItem>();
4749

@@ -63,6 +65,14 @@ export class HtmlQueryResultsRenderer {
6365
this.obsidianApp = obsidianApp;
6466
this.textRenderer = textRenderer;
6567
this.getters = getters;
68+
69+
this.taskLineRenderer = new TaskLineRenderer({
70+
textRenderer: this.textRenderer,
71+
obsidianApp: this.obsidianApp,
72+
obsidianComponent: this.obsidianComponent,
73+
taskLayoutOptions: this.getters.query().taskLayoutOptions,
74+
queryLayoutOptions: this.getters.query().queryLayoutOptions,
75+
});
6676
}
6777

6878
public get filePath(): string | undefined {
@@ -213,15 +223,6 @@ export class HtmlQueryResultsRenderer {
213223
const groupingAttribute = this.getGroupingAttribute();
214224
if (groupingAttribute && groupingAttribute.length > 0) taskList.dataset.taskGroupBy = groupingAttribute;
215225

216-
const taskLineRenderer = new TaskLineRenderer({
217-
textRenderer: this.textRenderer,
218-
obsidianApp: this.obsidianApp,
219-
obsidianComponent: this.obsidianComponent,
220-
parentUlElement: taskList,
221-
taskLayoutOptions: this.getters.query().taskLayoutOptions,
222-
queryLayoutOptions: this.getters.query().queryLayoutOptions,
223-
});
224-
225226
for (const [listItemIndex, listItem] of listItems.entries()) {
226227
if (this.getters.query().queryLayoutOptions.hideTree) {
227228
/* Old-style rendering of tasks:
@@ -231,7 +232,7 @@ export class HtmlQueryResultsRenderer {
231232
* - Tasks are rendered in the order specified in 'sort by' instructions and default sort order.
232233
*/
233234
if (listItem instanceof Task) {
234-
await this.addTask(taskLineRenderer, listItem, listItemIndex, queryRendererParameters, []);
235+
await this.addTask(listItem, listItemIndex, queryRendererParameters, []);
235236
}
236237
} else {
237238
/* New-style rendering of tasks:
@@ -244,13 +245,7 @@ export class HtmlQueryResultsRenderer {
244245
* instructions and default sort order.
245246
* - Child tasks (and list items) are shown in their original order in their Markdown file.
246247
*/
247-
await this.addTaskOrListItemAndChildren(
248-
taskLineRenderer,
249-
listItem,
250-
listItemIndex,
251-
queryRendererParameters,
252-
listItems,
253-
);
248+
await this.addTaskOrListItemAndChildren(listItem, listItemIndex, queryRendererParameters, listItems);
254249
}
255250
}
256251
}
@@ -277,7 +272,6 @@ export class HtmlQueryResultsRenderer {
277272
}
278273

279274
private async addTaskOrListItemAndChildren(
280-
taskLineRenderer: TaskLineRenderer,
281275
listItem: ListItem,
282276
taskIndex: number,
283277
queryRendererParameters: QueryRendererParameters,
@@ -291,7 +285,7 @@ export class HtmlQueryResultsRenderer {
291285
return;
292286
}
293287

294-
await this.createTaskOrListItem(taskLineRenderer, listItem, taskIndex, queryRendererParameters);
288+
await this.createTaskOrListItem(listItem, taskIndex, queryRendererParameters);
295289
this.renderedListItems.add(listItem);
296290

297291
for (const childTask of listItem.children) {
@@ -300,26 +294,28 @@ export class HtmlQueryResultsRenderer {
300294
}
301295

302296
private async createTaskOrListItem(
303-
taskLineRenderer: TaskLineRenderer,
304297
listItem: ListItem,
305298
taskIndex: number,
306299
queryRendererParameters: QueryRendererParameters,
307300
): Promise<void> {
308301
if (listItem instanceof Task) {
309-
await this.addTask(taskLineRenderer, listItem, taskIndex, queryRendererParameters, listItem.children);
302+
await this.addTask(listItem, taskIndex, queryRendererParameters, listItem.children);
310303
} else {
311-
await this.addListItem(taskLineRenderer, listItem, taskIndex, listItem.children, queryRendererParameters);
304+
await this.addListItem(listItem, taskIndex, listItem.children, queryRendererParameters);
312305
}
313306
}
314307

315308
private async addListItem(
316-
taskLineRenderer: TaskLineRenderer,
317309
listItem: ListItem,
318310
listItemIndex: number,
319311
children: ListItem[],
320312
queryRendererParameters: QueryRendererParameters,
321313
): Promise<void> {
322-
const listItemElement = await taskLineRenderer.renderListItem(this.currentULElement(), listItem, listItemIndex);
314+
const listItemElement = await this.taskLineRenderer.renderListItem(
315+
this.currentULElement(),
316+
listItem,
317+
listItemIndex,
318+
);
323319

324320
if (children.length > 0) {
325321
// TODO re-extract the method to include this back
@@ -334,14 +330,14 @@ export class HtmlQueryResultsRenderer {
334330
}
335331

336332
private async addTask(
337-
taskLineRenderer: TaskLineRenderer,
338333
task: Task,
339334
taskIndex: number,
340335
queryRendererParameters: QueryRendererParameters,
341336
children: ListItem[],
342337
): Promise<void> {
343338
const isFilenameUnique = this.isFilenameUnique({ task }, queryRendererParameters.allMarkdownFiles);
344-
const listItem = await taskLineRenderer.renderTaskLine({
339+
const listItem = await this.taskLineRenderer.renderTaskLine({
340+
parentUlElement: this.currentULElement(),
345341
task,
346342
taskIndex,
347343
isTaskInQueryFile: this.filePath === task.path,

src/Renderer/QueryResultsRenderer.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,6 @@ export class QueryResultsRenderer {
7070
obsidianApp: App,
7171
textRenderer: TextRenderer = TaskLineRenderer.obsidianMarkdownRenderer,
7272
) {
73-
this.htmlRenderer = new HtmlQueryResultsRenderer(renderMarkdown, obsidianComponent, obsidianApp, textRenderer, {
74-
source: () => this.source,
75-
tasksFile: () => this._tasksFile,
76-
query: () => this.query,
77-
});
78-
7973
this.source = source;
8074
this._tasksFile = tasksFile;
8175

@@ -93,6 +87,12 @@ export class QueryResultsRenderer {
9387
this.queryType = 'tasks';
9488
break;
9589
}
90+
91+
this.htmlRenderer = new HtmlQueryResultsRenderer(renderMarkdown, obsidianComponent, obsidianApp, textRenderer, {
92+
source: () => this.source,
93+
tasksFile: () => this._tasksFile,
94+
query: () => this.query,
95+
});
9696
}
9797

9898
private makeQueryFromSourceAndTasksFile() {

src/Renderer/TaskLineRenderer.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ export class TaskLineRenderer {
6868
private readonly textRenderer: TextRenderer;
6969
private readonly obsidianApp: App;
7070
private readonly obsidianComponent: Component | null;
71-
private readonly parentUlElement: HTMLElement;
7271
private readonly taskLayoutOptions: TaskLayoutOptions;
7372
private readonly queryLayoutOptions: QueryLayoutOptions;
7473

@@ -95,8 +94,6 @@ export class TaskLineRenderer {
9594
* @param obsidianComponent One of the parameters needed by `MarkdownRenderer.renderMarkdown()` Obsidian API,
9695
* that is called by the Obsidian renderer. Set this to null in test code.
9796
*
98-
* @param parentUlElement HTML element where the task shall be rendered.
99-
*
10097
* @param taskLayoutOptions See {@link TaskLayoutOptions}.
10198
*
10299
* @param queryLayoutOptions See {@link QueryLayoutOptions}.
@@ -105,21 +102,18 @@ export class TaskLineRenderer {
105102
textRenderer = TaskLineRenderer.obsidianMarkdownRenderer,
106103
obsidianApp,
107104
obsidianComponent,
108-
parentUlElement,
109105
taskLayoutOptions,
110106
queryLayoutOptions,
111107
}: {
112108
textRenderer?: TextRenderer;
113109
obsidianApp: App;
114110
obsidianComponent: Component | null;
115-
parentUlElement: HTMLElement;
116111
taskLayoutOptions: TaskLayoutOptions;
117112
queryLayoutOptions: QueryLayoutOptions;
118113
}) {
119114
this.textRenderer = textRenderer;
120115
this.obsidianApp = obsidianApp;
121116
this.obsidianComponent = obsidianComponent;
122-
this.parentUlElement = parentUlElement;
123117
this.taskLayoutOptions = taskLayoutOptions;
124118
this.queryLayoutOptions = queryLayoutOptions;
125119
}
@@ -132,6 +126,7 @@ export class TaskLineRenderer {
132126
*
133127
* @returns an HTML rendered List Item element (LI) for a task.
134128
* @note Output is based on the {@link DefaultTaskSerializer}'s format, with default (emoji) symbols
129+
* @param parentUlElement HTML element where the task shall be rendered.
135130
* @param task The task to be rendered.
136131
* @param taskIndex Task's index in the list. This affects `data-line` data attributes of the list item.
137132
* @param isTaskInQueryFile
@@ -140,17 +135,19 @@ export class TaskLineRenderer {
140135
* the file name only. If set to `true`, the full path will be returned.
141136
*/
142137
public async renderTaskLine({
138+
parentUlElement,
143139
task,
144140
taskIndex,
145141
isTaskInQueryFile,
146142
isFilenameUnique,
147143
}: {
144+
parentUlElement: HTMLElement;
148145
task: Task;
149146
taskIndex: number;
150147
isTaskInQueryFile: boolean;
151148
isFilenameUnique?: boolean;
152149
}): Promise<HTMLLIElement> {
153-
const li = createAndAppendElement('li', this.parentUlElement);
150+
const li = createAndAppendElement('li', parentUlElement);
154151
li.classList.add('task-list-item', 'plugin-tasks-list-item');
155152

156153
const textSpan = createAndAppendElement('span', li);

tests/Renderer/TaskLineRenderer.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,15 @@ async function renderListItem(
4444
textRenderer: testRenderer ?? mockTextRenderer,
4545
obsidianApp: mockApp,
4646
obsidianComponent: null,
47-
parentUlElement: document.createElement('div'),
4847
taskLayoutOptions: taskLayoutOptions ?? new TaskLayoutOptions(),
4948
queryLayoutOptions: queryLayoutOptions ?? new QueryLayoutOptions(),
5049
});
51-
return await taskLineRenderer.renderTaskLine({ task: task, taskIndex: 0, isTaskInQueryFile: true });
50+
return await taskLineRenderer.renderTaskLine({
51+
parentUlElement: document.createElement('div'),
52+
task: task,
53+
taskIndex: 0,
54+
isTaskInQueryFile: true,
55+
});
5256
}
5357

5458
function getTextSpan(listItem: HTMLElement) {
@@ -88,11 +92,11 @@ describe('task line rendering - HTML', () => {
8892
textRenderer: mockTextRenderer,
8993
obsidianApp: mockApp,
9094
obsidianComponent: null,
91-
parentUlElement: ulElement,
9295
taskLayoutOptions: new TaskLayoutOptions(),
9396
queryLayoutOptions: new QueryLayoutOptions(),
9497
});
9598
const listItem = await taskLineRenderer.renderTaskLine({
99+
parentUlElement: ulElement,
96100
task: new TaskBuilder().build(),
97101
taskIndex: 0,
98102
isTaskInQueryFile: true,

0 commit comments

Comments
 (0)