diff --git a/docs/api/nodes/task-item.md b/docs/api/nodes/task-item.md
index e62bb0415..64004d65d 100644
--- a/docs/api/nodes/task-item.md
+++ b/docs/api/nodes/task-item.md
@@ -4,6 +4,7 @@ icon: task-line
---
# TaskItem
+
[![Version](https://img.shields.io/npm/v/@tiptap/extension-task-item.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-task-item)
[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-task-item.svg)](https://npmcharts.com/compare/@tiptap/extension-task-item?minimal=true)
@@ -12,6 +13,7 @@ This extension renders a task item list element, which is a `
` tag with a `d
This extension doesn’t require any JavaScript framework, it’s based on Vanilla JavaScript.
## Installation
+
```bash
npm install @tiptap/extension-task-list @tiptap/extension-task-item
```
@@ -21,6 +23,7 @@ This extension requires the [`TaskList`](/api/nodes/task-list) node.
## Settings
### HTMLAttributes
+
Custom HTML attributes that should be added to the rendered HTML tag.
```js
@@ -31,7 +34,32 @@ TaskItem.configure({
})
```
+### nested
+
+Whether the task items are allowed to be nested within each other.
+
+```js
+TaskItem.configure({
+ nested: true,
+})
+```
+
+### onReadOnlyChecked
+
+A handler for when the task item is checked or unchecked while the editor is set to `readOnly`.
+
+If this is not supplied, the task items are immutable while the editor is `readOnly`.
+
+```js
+TaskItem.configure({
+ onReadOnlyChecked: (node, checked) => {
+ // do something
+ },
+})
+```
+
## Keyboard shortcuts
+
| Command | Windows/Linux | macOS |
| --------------- | ------------------ | ------------------ |
| splitListItem() | `Enter` | `Enter` |
@@ -39,7 +67,9 @@ TaskItem.configure({
| liftListItem() | `Shift` `Tab` | `Shift` `Tab` |
## Source code
+
[packages/extension-task-item/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-task-item/)
## Usage
+
https://embed.tiptap.dev/preview/Nodes/TaskItem
diff --git a/packages/extension-task-item/package.json b/packages/extension-task-item/package.json
index 6f3d7ab94..18b0d2f3e 100644
--- a/packages/extension-task-item/package.json
+++ b/packages/extension-task-item/package.json
@@ -23,6 +23,9 @@
"peerDependencies": {
"@tiptap/core": "^2.0.0-beta.1"
},
+ "dependencies": {
+ "prosemirror-model": "^1.16.1"
+ },
"repository": {
"type": "git",
"url": "https://github.com/ueberdosis/tiptap",
diff --git a/packages/extension-task-item/src/task-item.ts b/packages/extension-task-item/src/task-item.ts
index 40f507a2c..3ededf17a 100644
--- a/packages/extension-task-item/src/task-item.ts
+++ b/packages/extension-task-item/src/task-item.ts
@@ -1,9 +1,10 @@
import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core'
+import { Node as ProseMirrorNode } from 'prosemirror-model'
export interface TaskItemOptions {
- nested: boolean,
- checkable: boolean,
- HTMLAttributes: Record,
+ onReadOnlyChecked?: (node: ProseMirrorNode, checked: boolean) => void
+ nested: boolean
+ HTMLAttributes: Record
}
export const inputRegex = /^\s*(\[([( |x])?\])\s$/
@@ -14,7 +15,6 @@ export const TaskItem = Node.create({
addOptions() {
return {
nested: false,
- checkable: false,
HTMLAttributes: {},
}
},
@@ -30,8 +30,8 @@ export const TaskItem = Node.create({
checked: {
default: false,
keepOnSplit: false,
- parseHTML: element => element.getAttribute('data-checked') === 'true',
- renderHTML: attributes => ({
+ parseHTML: (element) => element.getAttribute('data-checked') === 'true',
+ renderHTML: (attributes) => ({
'data-checked': attributes.checked,
}),
},
@@ -50,28 +50,21 @@ export const TaskItem = Node.create({
renderHTML({ node, HTMLAttributes }) {
return [
'li',
- mergeAttributes(
- this.options.HTMLAttributes,
- HTMLAttributes,
- { 'data-type': this.name },
- ),
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
+ 'data-type': this.name,
+ }),
[
'label',
[
'input',
{
type: 'checkbox',
- checked: node.attrs.checked
- ? 'checked'
- : null,
+ checked: node.attrs.checked ? 'checked' : null,
},
],
['span'],
],
- [
- 'div',
- 0,
- ],
+ ['div', 0],
]
},
@@ -92,12 +85,7 @@ export const TaskItem = Node.create({
},
addNodeView() {
- return ({
- node,
- HTMLAttributes,
- getPos,
- editor,
- }) => {
+ return ({ node, HTMLAttributes, getPos, editor }) => {
const listItem = document.createElement('li')
const checkboxWrapper = document.createElement('label')
const checkboxStyler = document.createElement('span')
@@ -106,10 +94,10 @@ export const TaskItem = Node.create({
checkboxWrapper.contentEditable = 'false'
checkbox.type = 'checkbox'
- checkbox.addEventListener('change', event => {
- // if the editor isn’t editable and the item isn't checkable
- // we have to undo the latest change
- if (!editor.isEditable && !this.options.checkable) {
+ checkbox.addEventListener('change', (event) => {
+ // if the editor isn’t editable and we don't have a handler for
+ // readonly checks we have to undo the latest change
+ if (!editor.isEditable && !this.options.onReadOnlyChecked) {
checkbox.checked = !checkbox.checked
return
@@ -134,6 +122,9 @@ export const TaskItem = Node.create({
})
.run()
}
+ if (!editor.isEditable && this.options.onReadOnlyChecked) {
+ this.options.onReadOnlyChecked(node, checked)
+ }
})
Object.entries(this.options.HTMLAttributes).forEach(([key, value]) => {
@@ -148,16 +139,14 @@ export const TaskItem = Node.create({
checkboxWrapper.append(checkbox, checkboxStyler)
listItem.append(checkboxWrapper, content)
- Object
- .entries(HTMLAttributes)
- .forEach(([key, value]) => {
- listItem.setAttribute(key, value)
- })
+ Object.entries(HTMLAttributes).forEach(([key, value]) => {
+ listItem.setAttribute(key, value)
+ })
return {
dom: listItem,
contentDOM: content,
- update: updatedNode => {
+ update: (updatedNode) => {
if (updatedNode.type !== this.type) {
return false
}
@@ -180,7 +169,7 @@ export const TaskItem = Node.create({
wrappingInputRule({
find: inputRegex,
type: this.type,
- getAttributes: match => ({
+ getAttributes: (match) => ({
checked: match[match.length - 1] === 'x',
}),
}),