feat: impl list syntax auto complete to editor

This commit is contained in:
Steven 2024-04-08 20:42:53 +08:00
parent 436a6cb084
commit 6d10251cbd
4 changed files with 137 additions and 89 deletions

View file

@ -1,8 +1,10 @@
import classNames from "classnames"; import classNames from "classnames";
import { forwardRef, ReactNode, useCallback, useEffect, useImperativeHandle, useRef } from "react"; import { forwardRef, ReactNode, useCallback, useEffect, useImperativeHandle, useRef } from "react";
import { useAutoComplete } from "../hooks";
import TagSuggestions from "./TagSuggestions"; import TagSuggestions from "./TagSuggestions";
export interface EditorRefActions { export interface EditorRefActions {
getEditor: () => HTMLTextAreaElement | null;
focus: FunctionType; focus: FunctionType;
scrollToCursor: FunctionType; scrollToCursor: FunctionType;
insertText: (text: string, prefix?: string, suffix?: string) => void; insertText: (text: string, prefix?: string, suffix?: string) => void;
@ -43,16 +45,10 @@ const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef<
} }
}, [editorRef.current?.value]); }, [editorRef.current?.value]);
const updateEditorHeight = () => { const editorActions = {
if (editorRef.current) { getEditor: () => {
editorRef.current.style.height = "auto"; return editorRef.current;
editorRef.current.style.height = (editorRef.current.scrollHeight ?? 0) + "px"; },
}
};
useImperativeHandle(
ref,
() => ({
focus: () => { focus: () => {
editorRef.current?.focus(); editorRef.current?.focus();
}, },
@ -135,9 +131,18 @@ const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef<
updateEditorHeight(); updateEditorHeight();
} }
}, },
}), };
[],
); useAutoComplete(editorActions);
useImperativeHandle(ref, () => editorActions, []);
const updateEditorHeight = () => {
if (editorRef.current) {
editorRef.current.style.height = "auto";
editorRef.current.style.height = (editorRef.current.scrollHeight ?? 0) + "px";
}
};
const handleEditorInput = useCallback(() => { const handleEditorInput = useCallback(() => {
handleContentChangeCallback(editorRef.current?.value ?? ""); handleContentChangeCallback(editorRef.current?.value ?? "");

View file

@ -0,0 +1,3 @@
import useAutoComplete from "./useAutoComplete";
export { useAutoComplete };

View file

@ -0,0 +1,40 @@
import { last } from "lodash-es";
import { useEffect } from "react";
import { NodeType, OrderedListNode, TaskListNode, UnorderedListNode } from "@/types/node";
import { EditorRefActions } from "../Editor";
const useAutoComplete = (actions: EditorRefActions) => {
useEffect(() => {
const editor = actions.getEditor();
if (!editor) return;
editor.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
const cursorPosition = actions.getCursorPosition();
const prevContent = actions.getContent().substring(0, cursorPosition);
const lastNode = last(window.parse(prevContent));
if (!lastNode) {
return;
}
let insertText = "";
if (lastNode.type === NodeType.TASK_LIST) {
const { complete } = lastNode.value as TaskListNode;
insertText = complete ? "- [x] " : "- [ ] ";
} else if (lastNode.type === NodeType.UNORDERED_LIST) {
const { symbol } = lastNode.value as UnorderedListNode;
insertText = `${symbol} `;
} else if (lastNode.type === NodeType.ORDERED_LIST) {
const { number } = lastNode.value as OrderedListNode;
insertText = `${Number(number) + 1}. `;
}
if (insertText) {
actions.insertText(`\n${insertText}`);
event.preventDefault();
}
}
});
}, []);
};
export default useAutoComplete;