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 { forwardRef, ReactNode, useCallback, useEffect, useImperativeHandle, useRef } from "react";
import { useAutoComplete } from "../hooks";
import TagSuggestions from "./TagSuggestions";
export interface EditorRefActions {
getEditor: () => HTMLTextAreaElement | null;
focus: FunctionType;
scrollToCursor: FunctionType;
insertText: (text: string, prefix?: string, suffix?: string) => void;
@ -43,6 +45,98 @@ const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef<
}
}, [editorRef.current?.value]);
const editorActions = {
getEditor: () => {
return editorRef.current;
},
focus: () => {
editorRef.current?.focus();
},
scrollToCursor: () => {
if (editorRef.current) {
editorRef.current.scrollTop = editorRef.current.scrollHeight;
}
},
insertText: (content = "", prefix = "", suffix = "") => {
if (!editorRef.current) {
return;
}
const cursorPosition = editorRef.current.selectionStart;
const endPosition = editorRef.current.selectionEnd;
const prevValue = editorRef.current.value;
const value =
prevValue.slice(0, cursorPosition) +
prefix +
(content || prevValue.slice(cursorPosition, endPosition)) +
suffix +
prevValue.slice(endPosition);
editorRef.current.value = value;
editorRef.current.focus();
editorRef.current.selectionEnd = endPosition + prefix.length + content.length;
handleContentChangeCallback(editorRef.current.value);
updateEditorHeight();
},
removeText: (start: number, length: number) => {
if (!editorRef.current) {
return;
}
const prevValue = editorRef.current.value;
const value = prevValue.slice(0, start) + prevValue.slice(start + length);
editorRef.current.value = value;
editorRef.current.focus();
editorRef.current.selectionEnd = start;
handleContentChangeCallback(editorRef.current.value);
updateEditorHeight();
},
setContent: (text: string) => {
if (editorRef.current) {
editorRef.current.value = text;
handleContentChangeCallback(editorRef.current.value);
updateEditorHeight();
}
},
getContent: (): string => {
return editorRef.current?.value ?? "";
},
getCursorPosition: (): number => {
return editorRef.current?.selectionStart ?? 0;
},
getSelectedContent: () => {
const start = editorRef.current?.selectionStart;
const end = editorRef.current?.selectionEnd;
return editorRef.current?.value.slice(start, end) ?? "";
},
setCursorPosition: (startPos: number, endPos?: number) => {
const _endPos = isNaN(endPos as number) ? startPos : (endPos as number);
editorRef.current?.setSelectionRange(startPos, _endPos);
},
getCursorLineNumber: () => {
const cursorPosition = editorRef.current?.selectionStart ?? 0;
const lines = editorRef.current?.value.slice(0, cursorPosition).split("\n") ?? [];
return lines.length - 1;
},
getLine: (lineNumber: number) => {
return editorRef.current?.value.split("\n")[lineNumber] ?? "";
},
setLine: (lineNumber: number, text: string) => {
const lines = editorRef.current?.value.split("\n") ?? [];
lines[lineNumber] = text;
if (editorRef.current) {
editorRef.current.value = lines.join("\n");
editorRef.current.focus();
handleContentChangeCallback(editorRef.current.value);
updateEditorHeight();
}
},
};
useAutoComplete(editorActions);
useImperativeHandle(ref, () => editorActions, []);
const updateEditorHeight = () => {
if (editorRef.current) {
editorRef.current.style.height = "auto";
@ -50,95 +144,6 @@ const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef<
}
};
useImperativeHandle(
ref,
() => ({
focus: () => {
editorRef.current?.focus();
},
scrollToCursor: () => {
if (editorRef.current) {
editorRef.current.scrollTop = editorRef.current.scrollHeight;
}
},
insertText: (content = "", prefix = "", suffix = "") => {
if (!editorRef.current) {
return;
}
const cursorPosition = editorRef.current.selectionStart;
const endPosition = editorRef.current.selectionEnd;
const prevValue = editorRef.current.value;
const value =
prevValue.slice(0, cursorPosition) +
prefix +
(content || prevValue.slice(cursorPosition, endPosition)) +
suffix +
prevValue.slice(endPosition);
editorRef.current.value = value;
editorRef.current.focus();
editorRef.current.selectionEnd = endPosition + prefix.length + content.length;
handleContentChangeCallback(editorRef.current.value);
updateEditorHeight();
},
removeText: (start: number, length: number) => {
if (!editorRef.current) {
return;
}
const prevValue = editorRef.current.value;
const value = prevValue.slice(0, start) + prevValue.slice(start + length);
editorRef.current.value = value;
editorRef.current.focus();
editorRef.current.selectionEnd = start;
handleContentChangeCallback(editorRef.current.value);
updateEditorHeight();
},
setContent: (text: string) => {
if (editorRef.current) {
editorRef.current.value = text;
handleContentChangeCallback(editorRef.current.value);
updateEditorHeight();
}
},
getContent: (): string => {
return editorRef.current?.value ?? "";
},
getCursorPosition: (): number => {
return editorRef.current?.selectionStart ?? 0;
},
getSelectedContent: () => {
const start = editorRef.current?.selectionStart;
const end = editorRef.current?.selectionEnd;
return editorRef.current?.value.slice(start, end) ?? "";
},
setCursorPosition: (startPos: number, endPos?: number) => {
const _endPos = isNaN(endPos as number) ? startPos : (endPos as number);
editorRef.current?.setSelectionRange(startPos, _endPos);
},
getCursorLineNumber: () => {
const cursorPosition = editorRef.current?.selectionStart ?? 0;
const lines = editorRef.current?.value.slice(0, cursorPosition).split("\n") ?? [];
return lines.length - 1;
},
getLine: (lineNumber: number) => {
return editorRef.current?.value.split("\n")[lineNumber] ?? "";
},
setLine: (lineNumber: number, text: string) => {
const lines = editorRef.current?.value.split("\n") ?? [];
lines[lineNumber] = text;
if (editorRef.current) {
editorRef.current.value = lines.join("\n");
editorRef.current.focus();
handleContentChangeCallback(editorRef.current.value);
updateEditorHeight();
}
},
}),
[],
);
const handleEditorInput = useCallback(() => {
handleContentChangeCallback(editorRef.current?.value ?? "");
updateEditorHeight();