Skip to content

Commit 64b6167

Browse files
committed
add task item view
1 parent 0ecc296 commit 64b6167

4 files changed

Lines changed: 61 additions & 4 deletions

File tree

apps/desktop/src/editor/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import {
5656
setSearchState,
5757
} from "./plugins";
5858
import { schema } from "./schema";
59+
import { TaskItemView } from "./task-item-view";
5960

6061
export type { MentionConfig, FileHandlerConfig, PlaceholderFunction };
6162
export { schema };
@@ -103,6 +104,7 @@ interface EditorProps {
103104
const nodeViews = {
104105
image: ResizableImageView,
105106
"mention-@": MentionNodeView,
107+
taskItem: TaskItemView,
106108
};
107109

108110
function ViewCapture({
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {
2+
type NodeViewComponentProps,
3+
useEditorEventCallback,
4+
useEditorState,
5+
} from "@handlewithcare/react-prosemirror";
6+
import { forwardRef, type ReactNode } from "react";
7+
8+
export const TaskItemView = forwardRef<
9+
HTMLLIElement,
10+
NodeViewComponentProps & { children?: ReactNode }
11+
>(({ nodeProps, children, ...htmlAttrs }, ref) => {
12+
const { node, getPos } = nodeProps;
13+
const checked = node.attrs.checked;
14+
15+
const pos = getPos();
16+
const { selection } = useEditorState();
17+
const isSelected =
18+
pos >= selection.from && pos + node.nodeSize <= selection.to - 1;
19+
20+
const handleChange = useEditorEventCallback((view) => {
21+
if (!view) return;
22+
const pos = getPos();
23+
const tr = view.state.tr.setNodeMarkup(pos, undefined, {
24+
...node.attrs,
25+
checked: !checked,
26+
});
27+
view.dispatch(tr);
28+
});
29+
30+
return (
31+
<li
32+
ref={ref}
33+
{...htmlAttrs}
34+
data-type="taskItem"
35+
data-checked={checked ? "true" : "false"}
36+
>
37+
<label contentEditable={false} suppressContentEditableWarning>
38+
<input
39+
type="checkbox"
40+
checked={checked}
41+
onChange={handleChange}
42+
onMouseDown={(e) => e.preventDefault()}
43+
data-selected={isSelected ? "true" : undefined}
44+
/>
45+
</label>
46+
<div>{children}</div>
47+
</li>
48+
);
49+
});
50+
51+
TaskItemView.displayName = "TaskItemView";

packages/tiptap/src/styles/nodes/list.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,4 @@
4444
}
4545
}
4646
}
47-
48-
li p {
49-
display: inline;
50-
}
5147
}

packages/tiptap/src/styles/nodes/task-list.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
> label {
1818
flex: 0 0 auto;
1919
margin-right: 6px;
20+
-webkit-user-select: none;
21+
-moz-user-select: none;
2022
user-select: none;
2123
position: relative;
2224
display: inline-flex;
@@ -36,11 +38,17 @@
3638
width: 18px;
3739
height: 18px;
3840
border: 1.5px solid #000000;
41+
border-radius: 4px;
3942
margin: 0;
4043
transition: all 0.2s ease;
4144
position: relative;
4245
margin-top: 3px;
4346

47+
&[data-selected="true"] {
48+
border-color: #3b82f6;
49+
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
50+
}
51+
4452
&:checked {
4553
background-color: #3b82f6;
4654
border-color: #3b82f6;

0 commit comments

Comments
 (0)