memos/web/src/components/MemoContent/TaskListItem.tsx

83 lines
2.6 KiB
TypeScript

import { useContext, useRef } from "react";
import { Checkbox } from "@/components/ui/checkbox";
import { memoStore } from "@/store";
import { toggleTaskAtIndex } from "@/utils/markdown-manipulation";
import { MemoContentContext } from "./MemoContentContext";
/**
* Custom checkbox component for react-markdown task lists
*
* Handles interactive task checkbox clicks and updates memo content.
* This component is used via react-markdown's components prop.
*
* Note: This component should only be used for task list checkboxes.
* Regular inputs are handled by the default input element.
*/
interface TaskListItemProps extends React.InputHTMLAttributes<HTMLInputElement> {
node?: any; // AST node from react-markdown
checked?: boolean;
}
export const TaskListItem: React.FC<TaskListItemProps> = ({ checked, ...props }) => {
const context = useContext(MemoContentContext);
const checkboxRef = useRef<HTMLButtonElement>(null);
const handleChange = async (newChecked: boolean) => {
// Don't update if readonly or no memo context
if (context.readonly || !context.memoName) {
return;
}
// Find the task index by walking up the DOM
const listItem = checkboxRef.current?.closest("li.task-list-item");
if (!listItem) {
return;
}
// Get task index from data attribute, or calculate by counting
const taskIndexStr = listItem.getAttribute("data-task-index");
let taskIndex = 0;
if (taskIndexStr !== null) {
taskIndex = parseInt(taskIndexStr);
} else {
// Fallback: Calculate index by counting ALL task list items in the memo
// Use the container ref from context for proper scoping
const container = context.containerRef?.current;
if (!container) {
return;
}
const allTaskItems = container.querySelectorAll("li.task-list-item");
for (let i = 0; i < allTaskItems.length; i++) {
if (allTaskItems[i] === listItem) {
taskIndex = i;
break;
}
}
}
// Update memo content using the string manipulation utility
const memo = memoStore.getMemoByName(context.memoName);
if (!memo) {
return;
}
const newContent = toggleTaskAtIndex(memo.content, taskIndex, newChecked);
await memoStore.updateMemo(
{
name: memo.name,
content: newContent,
},
["content"],
);
};
// Override the disabled prop from remark-gfm (which defaults to true)
// We want interactive checkboxes, only disabled when readonly
return (
<Checkbox ref={checkboxRef} checked={checked} disabled={context.readonly} onCheckedChange={handleChange} className={props.className} />
);
};