Make option a callback, update docs

This commit is contained in:
Kasper Nilsson 2022-02-10 16:58:26 -08:00 committed by Dominik
parent eda8d801b5
commit 2875fbe1c9
3 changed files with 57 additions and 35 deletions

View File

@ -4,6 +4,7 @@ icon: task-line
--- ---
# TaskItem # TaskItem
[![Version](https://img.shields.io/npm/v/@tiptap/extension-task-item.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-task-item) [![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) [![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 `<li>` tag with a `d
This extension doesnt require any JavaScript framework, its based on Vanilla JavaScript. This extension doesnt require any JavaScript framework, its based on Vanilla JavaScript.
## Installation ## Installation
```bash ```bash
npm install @tiptap/extension-task-list @tiptap/extension-task-item 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 ## Settings
### HTMLAttributes ### HTMLAttributes
Custom HTML attributes that should be added to the rendered HTML tag. Custom HTML attributes that should be added to the rendered HTML tag.
```js ```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 ## Keyboard shortcuts
| Command | Windows/Linux | macOS | | Command | Windows/Linux | macOS |
| --------------- | ------------------ | ------------------ | | --------------- | ------------------ | ------------------ |
| splitListItem() | `Enter` | `Enter` | | splitListItem() | `Enter` | `Enter` |
@ -39,7 +67,9 @@ TaskItem.configure({
| liftListItem() | `Shift`&nbsp;`Tab` | `Shift`&nbsp;`Tab` | | liftListItem() | `Shift`&nbsp;`Tab` | `Shift`&nbsp;`Tab` |
## Source code ## Source code
[packages/extension-task-item/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-task-item/) [packages/extension-task-item/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-task-item/)
## Usage ## Usage
https://embed.tiptap.dev/preview/Nodes/TaskItem https://embed.tiptap.dev/preview/Nodes/TaskItem

View File

@ -23,6 +23,9 @@
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.0.0-beta.1" "@tiptap/core": "^2.0.0-beta.1"
}, },
"dependencies": {
"prosemirror-model": "^1.16.1"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/ueberdosis/tiptap", "url": "https://github.com/ueberdosis/tiptap",

View File

@ -1,9 +1,10 @@
import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core' import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core'
import { Node as ProseMirrorNode } from 'prosemirror-model'
export interface TaskItemOptions { export interface TaskItemOptions {
nested: boolean, onReadOnlyChecked?: (node: ProseMirrorNode, checked: boolean) => void
checkable: boolean, nested: boolean
HTMLAttributes: Record<string, any>, HTMLAttributes: Record<string, any>
} }
export const inputRegex = /^\s*(\[([( |x])?\])\s$/ export const inputRegex = /^\s*(\[([( |x])?\])\s$/
@ -14,7 +15,6 @@ export const TaskItem = Node.create<TaskItemOptions>({
addOptions() { addOptions() {
return { return {
nested: false, nested: false,
checkable: false,
HTMLAttributes: {}, HTMLAttributes: {},
} }
}, },
@ -30,8 +30,8 @@ export const TaskItem = Node.create<TaskItemOptions>({
checked: { checked: {
default: false, default: false,
keepOnSplit: false, keepOnSplit: false,
parseHTML: element => element.getAttribute('data-checked') === 'true', parseHTML: (element) => element.getAttribute('data-checked') === 'true',
renderHTML: attributes => ({ renderHTML: (attributes) => ({
'data-checked': attributes.checked, 'data-checked': attributes.checked,
}), }),
}, },
@ -50,28 +50,21 @@ export const TaskItem = Node.create<TaskItemOptions>({
renderHTML({ node, HTMLAttributes }) { renderHTML({ node, HTMLAttributes }) {
return [ return [
'li', 'li',
mergeAttributes( mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
this.options.HTMLAttributes, 'data-type': this.name,
HTMLAttributes, }),
{ 'data-type': this.name },
),
[ [
'label', 'label',
[ [
'input', 'input',
{ {
type: 'checkbox', type: 'checkbox',
checked: node.attrs.checked checked: node.attrs.checked ? 'checked' : null,
? 'checked'
: null,
}, },
], ],
['span'], ['span'],
], ],
[ ['div', 0],
'div',
0,
],
] ]
}, },
@ -92,12 +85,7 @@ export const TaskItem = Node.create<TaskItemOptions>({
}, },
addNodeView() { addNodeView() {
return ({ return ({ node, HTMLAttributes, getPos, editor }) => {
node,
HTMLAttributes,
getPos,
editor,
}) => {
const listItem = document.createElement('li') const listItem = document.createElement('li')
const checkboxWrapper = document.createElement('label') const checkboxWrapper = document.createElement('label')
const checkboxStyler = document.createElement('span') const checkboxStyler = document.createElement('span')
@ -106,10 +94,10 @@ export const TaskItem = Node.create<TaskItemOptions>({
checkboxWrapper.contentEditable = 'false' checkboxWrapper.contentEditable = 'false'
checkbox.type = 'checkbox' checkbox.type = 'checkbox'
checkbox.addEventListener('change', event => { checkbox.addEventListener('change', (event) => {
// if the editor isnt editable and the item isn't checkable // if the editor isnt editable and we don't have a handler for
// we have to undo the latest change // readonly checks we have to undo the latest change
if (!editor.isEditable && !this.options.checkable) { if (!editor.isEditable && !this.options.onReadOnlyChecked) {
checkbox.checked = !checkbox.checked checkbox.checked = !checkbox.checked
return return
@ -134,6 +122,9 @@ export const TaskItem = Node.create<TaskItemOptions>({
}) })
.run() .run()
} }
if (!editor.isEditable && this.options.onReadOnlyChecked) {
this.options.onReadOnlyChecked(node, checked)
}
}) })
Object.entries(this.options.HTMLAttributes).forEach(([key, value]) => { Object.entries(this.options.HTMLAttributes).forEach(([key, value]) => {
@ -148,16 +139,14 @@ export const TaskItem = Node.create<TaskItemOptions>({
checkboxWrapper.append(checkbox, checkboxStyler) checkboxWrapper.append(checkbox, checkboxStyler)
listItem.append(checkboxWrapper, content) listItem.append(checkboxWrapper, content)
Object Object.entries(HTMLAttributes).forEach(([key, value]) => {
.entries(HTMLAttributes) listItem.setAttribute(key, value)
.forEach(([key, value]) => { })
listItem.setAttribute(key, value)
})
return { return {
dom: listItem, dom: listItem,
contentDOM: content, contentDOM: content,
update: updatedNode => { update: (updatedNode) => {
if (updatedNode.type !== this.type) { if (updatedNode.type !== this.type) {
return false return false
} }
@ -180,7 +169,7 @@ export const TaskItem = Node.create<TaskItemOptions>({
wrappingInputRule({ wrappingInputRule({
find: inputRegex, find: inputRegex,
type: this.type, type: this.type,
getAttributes: match => ({ getAttributes: (match) => ({
checked: match[match.length - 1] === 'x', checked: match[match.length - 1] === 'x',
}), }),
}), }),