diff --git a/bun.lockb b/bun.lockb index f91f653..a9d19e1 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 59062f8..2b102c7 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "@tiptap/extension-table-cell": "^3.14.0", "@tiptap/extension-table-header": "^3.14.0", "@tiptap/extension-table-row": "^3.14.0", + "@tiptap/extension-task-item": "^3.14.0", + "@tiptap/extension-task-list": "^3.14.0", "@tiptap/extension-text-style": "^3.14.0", "@tiptap/pm": "^3.14.0", "@tiptap/starter-kit": "^3.14.0", diff --git a/src/app.css b/src/app.css index df5de34..8d8aed7 100644 --- a/src/app.css +++ b/src/app.css @@ -809,3 +809,44 @@ a.hover-underline-animation:hover::after { background-color: var(--color-blue); pointer-events: none; } + +/* Task list styles */ +ul[data-type="taskList"] { + list-style: none; + padding: 0; + margin: 1rem 0; +} + +ul[data-type="taskList"] li { + display: flex; + align-items: flex-start; + margin: 0.5rem 0; +} + +ul[data-type="taskList"] li > label { + flex: 0 0 auto; + margin-right: 0.5rem; + user-select: none; +} + +ul[data-type="taskList"] li > div { + flex: 1 1 auto; +} + +ul[data-type="taskList"] input[type="checkbox"] { + cursor: pointer; + width: 1.2em; + height: 1.2em; + margin-top: 0.2em; + accent-color: var(--color-blue); +} + +ul[data-type="taskList"] li[data-checked="true"] > div { + text-decoration: line-through; + opacity: 0.6; +} + +/* Nested task lists */ +ul[data-type="taskList"] ul[data-type="taskList"] { + margin: 0.5rem 0 0.5rem 1.5rem; +} diff --git a/src/components/blog/TextEditor.tsx b/src/components/blog/TextEditor.tsx index 9d80ae1..174e2c0 100644 --- a/src/components/blog/TextEditor.tsx +++ b/src/components/blog/TextEditor.tsx @@ -8,6 +8,8 @@ import { Table } from "@tiptap/extension-table"; import { TableRow } from "@tiptap/extension-table-row"; import { TableHeader } from "@tiptap/extension-table-header"; import { TableCell } from "@tiptap/extension-table-cell"; +import TaskList from "@tiptap/extension-task-list"; +import TaskItem from "@tiptap/extension-task-item"; import { Node } from "@tiptap/core"; import { createLowlight, common } from "lowlight"; import css from "highlight.js/lib/languages/css"; @@ -216,6 +218,13 @@ export default function TextEditor(props: TextEditorProps) { }), Image, IframeEmbed, + TaskList, + TaskItem.configure({ + nested: true, + HTMLAttributes: { + class: "task-item" + } + }), Table.configure({ resizable: true, HTMLAttributes: { @@ -711,6 +720,20 @@ export default function TextEditor(props: TextEditorProps) { > Code + {/* Table controls in bubble menu */} @@ -962,6 +985,20 @@ export default function TextEditor(props: TextEditorProps) { > 1. List +