feat(import/markdown): support todo lists in the CKEditor style

This commit is contained in:
Elian Doran 2025-04-17 18:34:40 +03:00
parent 2edaa2c4d4
commit c2b5f0a5a3
No known key found for this signature in database
3 changed files with 50 additions and 2 deletions

View File

@ -37,7 +37,7 @@
* Basic Touch Bar support for macOS. * Basic Touch Bar support for macOS.
* [Support Bearer Token](https://github.com/TriliumNext/Notes/issues/1701) * [Support Bearer Token](https://github.com/TriliumNext/Notes/issues/1701)
* The tab bar is now scrollable when there are many tabs by @SiriusXT * The tab bar is now scrollable when there are many tabs by @SiriusXT
* Markdown export: support todo lists * Markdown import/export: support todo lists
## 🌍 Internationalization ## 🌍 Internationalization

View File

@ -233,4 +233,12 @@ second line 2</code></pre><ul><li>Hello</li><li>world</li></ul><ol><li>Hello</li
expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected); expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected);
}); });
it("imports todo lists properly", () => {
const input = trimIndentation`\
- [x] Hello
- [ ] World`;
const expected = `<ul class="todo-list"><li><label class="todo-list__label"><input type="checkbox" checked="checked" disabled="disabled"><span class="todo-list__label__description">Hello</span></label></li><li><label class="todo-list__label"><input type="checkbox" disabled="disabled"><span class="todo-list__label__description">World</span></label></li></ul>`;
expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected);
});
}); });

View File

@ -48,12 +48,52 @@ class CustomMarkdownRenderer extends Renderer {
} }
list(token: Tokens.List): string { list(token: Tokens.List): string {
return super.list(token) let result = super.list(token)
.replace("\n", "") // we replace the first one only. .replace("\n", "") // we replace the first one only.
.trimEnd(); .trimEnd();
// Handle todo-list in the CKEditor format.
if (token.items.some(item => item.task)) {
result = result.replace(/^<ul>/, "<ul class=\"todo-list\">");
}
return result;
}
checkbox({ checked }: Tokens.Checkbox): string {
return '<input type="checkbox"'
+ (checked ? 'checked="checked" ' : '')
+ 'disabled="disabled">';
} }
listitem(item: Tokens.ListItem): string { listitem(item: Tokens.ListItem): string {
// Handle todo-list in the CKEditor format.
if (item.task) {
let itemBody = '';
const checkbox = this.checkbox({ checked: !!item.checked });
if (item.loose) {
if (item.tokens[0]?.type === 'paragraph') {
item.tokens[0].text = checkbox + item.tokens[0].text;
if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
item.tokens[0].tokens[0].text = checkbox + escape(item.tokens[0].tokens[0].text);
item.tokens[0].tokens[0].escaped = true;
}
} else {
item.tokens.unshift({
type: 'text',
raw: checkbox,
text: checkbox,
escaped: true,
});
}
} else {
itemBody += checkbox;
}
itemBody += `<span class="todo-list__label__description">${this.parser.parse(item.tokens, !!item.loose)}</span>`;
return `<li><label class="todo-list__label">${itemBody}</label></li>`;
}
return super.listitem(item).trimEnd(); return super.listitem(item).trimEnd();
} }