Skip to content

Commit e2ca757

Browse files
authored
Merge pull request #3658 from ilandikov/feat-copy-button
feat: Add 'Copy results' button to search results
2 parents dc5f865 + c567537 commit e2ca757

15 files changed

+83
-170
lines changed

docs/snippets-embedded-in-multiple-pages/Sample HTML - Full mode.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
-->
66

77
<div>
8+
<button class="plugin-tasks-copy-button">Copy results</button>
89
<ul class="contains-task-list plugin-tasks-query-result">
910
<li
1011
class="task-list-item plugin-tasks-list-item"

docs/snippets-embedded-in-multiple-pages/Sample HTML - Short mode.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
-->
66

77
<div>
8+
<button class="plugin-tasks-copy-button">Copy results</button>
89
<ul class="contains-task-list plugin-tasks-query-result tasks-layout-short-mode">
910
<li
1011
class="task-list-item plugin-tasks-list-item"

src/Query/Group/TaskGroup.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,17 @@ export class TaskGroup {
9797
* A human-readable representation of this task group, including names
9898
* and headings that should be displayed.
9999
*
100-
* Note that this is used in snapshot testing, so if the format is
101-
* changed, the snapshots will need to be updated.
100+
* Note that this is used in the 'Copy results' facility and snapshot testing, so if the format is
101+
* changed, the documentation and snapshots will need to be updated.
102102
*/
103103
public toString(): string {
104104
let output = '\n';
105-
output += `Group names: [${this.groups}]\n`;
106105

107106
for (const heading of this.groupHeadings) {
108107
// These headings mimic the behaviour of QueryRenderer,
109108
// which uses 'h4', 'h5' and 'h6' for nested groups.
110109
const headingPrefix = '#'.repeat(4 + heading.nestingLevel);
111-
output += `${headingPrefix} [${heading.property}] ${heading.displayName}\n`;
110+
output += `${headingPrefix} ${heading.displayName}\n\n`;
112111
}
113112

114113
output += this.tasksAsStringOfLines();

src/Query/Group/TaskGroups.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,22 +75,16 @@ export class TaskGroups {
7575
/**
7676
* A human-readable representation of all the task groups.
7777
*
78-
* Note that this is used in snapshot testing, so if the format is
79-
* changed, the snapshots will need to be updated.
78+
* Note that this is used in the 'Copy results' facility and snapshot testing, so if the format is
79+
* changed, the documentation and snapshots will need to be updated.
8080
*/
8181
public toString(): string {
8282
let output = '';
83-
output += 'Groupers (if any):\n';
84-
for (const grouper of this._groupers) {
85-
const reverseText = grouper.reverse ? ' reverse' : '';
86-
output += `- ${grouper.property}${reverseText}\n`;
87-
}
83+
8884
for (const taskGroup of this.groups) {
8985
output += taskGroup.toString();
90-
output += '\n---\n';
9186
}
92-
const totalTasksCount = this.totalTasksCount();
93-
output += `\n${totalTasksCount} tasks\n`;
87+
9488
return output;
9589
}
9690

src/Renderer/QueryResultsRenderer.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { App, Component, TFile } from 'obsidian';
1+
import { type App, type Component, Notice, type TFile } from 'obsidian';
22
import { GlobalFilter } from '../Config/GlobalFilter';
33
import { GlobalQuery } from '../Config/GlobalQuery';
44
import { postponeButtonTitle, shouldShowPostponeButton } from '../DateTime/Postponer';
@@ -202,6 +202,8 @@ export class QueryResultsRenderer {
202202
const measureRender = new PerformanceTracker(`Render: ${this.query.queryId} - ${this.filePath}`);
203203
measureRender.start();
204204

205+
this.addCopyButton(content, queryResult.taskGroups);
206+
205207
await this.addAllTaskGroups(queryResult.taskGroups, content, queryRendererParameters);
206208

207209
const totalTasksCount = queryResult.totalTasksCount;
@@ -235,6 +237,16 @@ export class QueryResultsRenderer {
235237
content.appendChild(explanationsBlock);
236238
}
237239

240+
private addCopyButton(content: HTMLDivElement, taskGroups: TaskGroups) {
241+
const copyButton = createAndAppendElement('button', content);
242+
copyButton.textContent = 'Copy results';
243+
copyButton.classList.add('plugin-tasks-copy-button');
244+
copyButton.addEventListener('click', async () => {
245+
await navigator.clipboard.writeText(taskGroups.toString());
246+
new Notice('Results copied to clipboard');
247+
});
248+
}
249+
238250
private async addAllTaskGroups(
239251
tasksSortedLimitedGrouped: TaskGroups,
240252
content: HTMLDivElement,

src/Renderer/Renderer.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@
1313
--code-white-space: pre;
1414
}
1515

16+
.plugin-tasks-copy-button {
17+
position: absolute;
18+
right: 0;
19+
transform: translate(-50%, -50%);
20+
z-index: 1;
21+
22+
margin-top: 16px;
23+
}
24+
1625
.tasks-count {
1726
color: var(--text-faint);
1827
padding-left: 20px;

tests/Query/Filter/TagsField.test.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -659,22 +659,14 @@ describe('grouping by tag', () => {
659659

660660
// Assert
661661
expect(groups.toString()).toMatchInlineSnapshot(`
662-
"Groupers (if any):
663-
- tags reverse
662+
"
663+
#### #tag2
664664
665-
Group names: [#tag2]
666-
#### [tags] #tag2
667665
- [ ] b #tag2
668666
669-
---
667+
#### #tag1
670668
671-
Group names: [#tag1]
672-
#### [tags] #tag1
673669
- [ ] a #tag1
674-
675-
---
676-
677-
2 tasks
678670
"
679671
`);
680672
});

0 commit comments

Comments
 (0)