tiptap/packages/extension-task-item/src/task-item.ts

142 lines
3.0 KiB
TypeScript
Raw Normal View History

import { Node, mergeAttributes } from '@tiptap/core'
import { wrappingInputRule } from 'prosemirror-inputrules'
export interface TaskItemOptions {
nested: boolean,
2020-11-13 23:44:22 +08:00
HTMLAttributes: {
[key: string]: any
},
}
2020-11-13 23:44:22 +08:00
export const inputRegex = /^\s*(\[([ |x])\])\s$/
2021-02-11 01:25:08 +08:00
export const TaskItem = Node.create<TaskItemOptions>({
2020-11-04 03:57:09 +08:00
name: 'taskItem',
2021-02-11 01:25:08 +08:00
defaultOptions: {
2020-11-13 23:44:22 +08:00
nested: false,
HTMLAttributes: {},
},
content() {
2021-01-30 04:45:51 +08:00
return this.options.nested ? 'paragraph block*' : 'paragraph+'
},
defining: true,
addAttributes() {
return {
2020-10-30 21:55:48 +08:00
checked: {
default: false,
2020-10-30 21:55:48 +08:00
parseHTML: element => ({
checked: element.getAttribute('data-checked') === 'true',
}),
renderHTML: attributes => ({
'data-checked': attributes.checked,
}),
keepOnSplit: false,
},
}
},
parseHTML() {
return [
{
2020-11-04 03:57:09 +08:00
tag: 'li[data-type="taskItem"]',
priority: 51,
},
]
},
2020-11-13 23:07:20 +08:00
renderHTML({ HTMLAttributes }) {
2020-11-25 16:50:54 +08:00
return ['li', mergeAttributes(
this.options.HTMLAttributes,
HTMLAttributes,
{ 'data-type': 'taskItem' },
), 0]
},
addKeyboardShortcuts() {
2020-10-30 21:55:48 +08:00
const shortcuts = {
Enter: () => this.editor.commands.splitListItem('taskItem'),
'Shift-Tab': () => this.editor.commands.liftListItem('taskItem'),
2020-10-30 21:55:48 +08:00
}
if (!this.options.nested) {
return shortcuts
}
return {
...shortcuts,
Tab: () => this.editor.commands.sinkListItem('taskItem'),
}
},
2020-10-30 21:55:48 +08:00
addNodeView() {
2020-12-03 01:13:16 +08:00
return ({
node,
HTMLAttributes,
getPos,
editor,
}) => {
2020-10-30 21:55:48 +08:00
const { view } = editor
const listItem = document.createElement('li')
const checkbox = document.createElement('input')
const content = document.createElement('div')
checkbox.type = 'checkbox'
2020-11-25 18:24:10 +08:00
checkbox.contentEditable = 'false'
2020-10-30 21:55:48 +08:00
checkbox.addEventListener('change', event => {
const { checked } = event.target as any
if (typeof getPos === 'function') {
view.dispatch(view.state.tr.setNodeMarkup(getPos(), undefined, {
checked,
}))
editor.commands.focus()
2020-10-30 21:55:48 +08:00
}
})
2020-12-03 01:13:16 +08:00
if (node.attrs.checked) {
2020-10-30 21:55:48 +08:00
checkbox.setAttribute('checked', 'checked')
}
listItem.append(checkbox, content)
2020-11-13 23:07:20 +08:00
Object.entries(HTMLAttributes).forEach(([key, value]) => {
2020-10-30 21:55:48 +08:00
listItem.setAttribute(key, value)
})
return {
dom: listItem,
contentDOM: content,
2020-12-03 01:13:16 +08:00
update: updatedNode => {
if (updatedNode.type !== this.type) {
return false
}
2020-12-03 01:13:16 +08:00
if (updatedNode.attrs.checked) {
checkbox.setAttribute('checked', 'checked')
} else {
checkbox.removeAttribute('checked')
}
return true
},
2020-10-30 21:55:48 +08:00
}
}
},
addInputRules() {
return [
wrappingInputRule(
inputRegex,
this.type,
match => ({
checked: match[match.length - 1] === 'x',
}),
),
]
},
})