1
0

feat: move from easymde to tiptap editor (#2222)

Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/2222
This commit is contained in:
konrad
2023-10-22 13:48:57 +00:00
28 changed files with 2834 additions and 1380 deletions

View File

@ -10,48 +10,56 @@ describe('Find checklists in text', () => {
expect(checkboxes).toHaveLength(0)
})
it('should find multiple checkboxes', () => {
const text: string = `* [ ] Lorem Ipsum
* [ ] Dolor sit amet
Here's some text in between
* [x] Dolor sit amet
- [ ] Dolor sit amet`
const text: string = `
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Another task</p>
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>subtask</p></div>
</li>
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"
checked="checked"><span></span></label>
<div><p>done</p></div>
</li>
</ul>
</div>
</li>
</ul>`
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(4)
expect(checkboxes[0]).toBe(0)
expect(checkboxes[1]).toBe(18)
expect(checkboxes[2]).toBe(69)
expect(checkboxes[3]).toBe(90)
expect(checkboxes[0]).toBe(32)
expect(checkboxes[1]).toBe(163)
expect(checkboxes[2]).toBe(321)
expect(checkboxes[3]).toBe(464)
})
it('should find one checkbox with *', () => {
const text: string = '* [ ] Lorem Ipsum'
it('should find one unchecked checkbox', () => {
const text: string = `
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
</ul>`
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(1)
expect(checkboxes[0]).toBe(0)
expect(checkboxes[0]).toBe(32)
})
it('should find one checkbox with -', () => {
const text: string = '- [ ] Lorem Ipsum'
it('should find one checked checkbox', () => {
const text: string = `
<ul data-type="taskList">
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
</ul>`
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(1)
expect(checkboxes[0]).toBe(0)
})
it('should find one checked checkbox with *', () => {
const text: string = '* [x] Lorem Ipsum'
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(1)
expect(checkboxes[0]).toBe(0)
})
it('should find one checked checkbox with -', () => {
const text: string = '- [x] Lorem Ipsum'
const checkboxes = findCheckboxesInText(text)
expect(checkboxes).toHaveLength(1)
expect(checkboxes[0]).toBe(0)
expect(checkboxes[0]).toBe(32)
})
})
@ -63,32 +71,60 @@ describe('Get Checklist Statistics in a Text', () => {
expect(stats.total).toBe(0)
})
it('should find one checkbox', () => {
const text: string = '* [ ] Lorem Ipsum'
const text: string = `
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
</ul>`
const stats = getChecklistStatistics(text)
expect(stats.total).toBe(1)
expect(stats.checked).toBe(0)
})
it('should find one checked checkbox', () => {
const text: string = '* [x] Lorem Ipsum'
const text: string = `
<ul data-type="taskList">
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
</ul>`
const stats = getChecklistStatistics(text)
expect(stats.total).toBe(1)
expect(stats.checked).toBe(1)
})
it('should find multiple mixed and matched', () => {
const text: string = `* [ ] Lorem Ipsum
* [ ] Dolor sit amet
* [x] Dolor sit amet
- [x] Dolor sit amet
const text: string = `
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Task</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>Another task</p>
<ul data-type="taskList">
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>subtask</p></div>
</li>
<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
<div><p>subtask 2</p></div>
</li>
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"
checked="checked"><span></span></label>
<div><p>done</p></div>
</li>
<li data-checked="true" data-type="taskItem"><label><input type="checkbox"
checked="checked"><span></span></label>
<div><p>also done</p></div>
</li>
</ul>
</div>
</li>
</ul>`
Here's some text in between
* [x] Dolor sit amet
- [ ] Dolor sit amet`
const stats = getChecklistStatistics(text)
expect(stats.total).toBe(6)
expect(stats.checked).toBe(3)
expect(stats.checked).toBe(2)
})
})

View File

@ -1,5 +1,3 @@
const checked = '[x]'
interface CheckboxStatistics {
total: number
checked: number
@ -11,7 +9,7 @@ interface MatchedCheckboxes {
}
const getCheckboxesInText = (text: string): MatchedCheckboxes => {
const regex = /[*-] \[[ x]]/g
const regex = /data-checked="(true|false)"/g
let match
const checkboxes: MatchedCheckboxes = {
checked: [],
@ -19,7 +17,7 @@ const getCheckboxesInText = (text: string): MatchedCheckboxes => {
}
while ((match = regex.exec(text)) !== null) {
if (match[0].endsWith(checked)) {
if (match[1] === 'true') {
checkboxes.checked.push(match.index)
} else {
checkboxes.unchecked.push(match.index)

View File

@ -1,45 +0,0 @@
import {marked} from 'marked'
import hljs from 'highlight.js/lib/common'
export function setupMarkdownRenderer(checkboxId: string) {
const renderer = new marked.Renderer()
const linkRenderer = renderer.link
let checkboxNum = -1
marked.use({
renderer: {
image(src: string, title: string, text: string) {
title = title ? ` title="${title}` : ''
// If the url starts with the api url, the image is likely an attachment and
// we'll need to download and parse it properly.
if (src.slice(0, window.API_URL.length + 7) === `${window.API_URL}/tasks/`) {
return `<img data-src="${src}" alt="${text}" ${title} class="attachment-image"/>`
}
return `<img src="${src}" alt="${text}" ${title}/>`
},
checkbox(checked: boolean) {
let checkedString = ''
if (checked) {
checkedString = 'checked'
}
checkboxNum++
return `<input type="checkbox" data-checkbox-num="${checkboxNum}" ${checkedString} class="text-checkbox-${checkboxId}"/>`
},
link(href: string, title: string, text: string) {
const isLocal = href.startsWith(`${location.protocol}//${location.hostname}`)
const html = linkRenderer.call(renderer, href, title, text)
return isLocal ? html : html.replace(/^<a /, '<a target="_blank" rel="noreferrer noopener nofollow" ')
},
},
highlight(code: string, language: string) {
const validLanguage = hljs.getLanguage(language) ? language : 'plaintext'
return hljs.highlight(code, {language: validLanguage}).value
},
})
return renderer
}