| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- import { useRef, useState } from "react";
- import "./style.css";
- import TermTextAreaMenu from "./TermTextAreaMenu";
- interface IWidget {
- value?: string;
- menuOptions?: string[];
- placeholder?: string;
- onSave?: Function;
- onClose?: Function;
- onChange?: Function;
- }
- const TermTextAreaWidget = ({
- value,
- menuOptions,
- placeholder,
- onSave,
- onClose,
- onChange,
- }: IWidget) => {
- const [shadowHeight, setShadowHeight] = useState<number>();
- const [menuFocusIndex, setMenuFocusIndex] = useState(0);
- const [menuDisplay, setMenuDisplay] = useState("none");
- const [menuTop, setMenuTop] = useState(0);
- const [menuLeft, setMenuLeft] = useState(0);
- const [menuSelected, setMenuSelected] = useState<string>();
- const [textAreaValue, setTextAreaValue] = useState(value);
- const [textAreaHeight, setTextAreaHeight] = useState(100);
- const [termSearch, setTermSearch] = useState<string>();
- const _term_max_menu = 10;
- const refTextArea = useRef<HTMLTextAreaElement>(null);
- const refShadow = useRef<HTMLDivElement>(null);
- console.log("render");
- function term_at_menu_hide() {
- setMenuDisplay("none");
- setTermSearch("");
- }
- function termInsert(strTerm: string) {
- if (refTextArea.current === null) {
- return;
- }
- const value = refTextArea.current.value;
- const selectionStart = refTextArea.current.selectionStart;
- let str1 = value.slice(0, selectionStart);
- const str2 = value.slice(selectionStart);
- const pos1 = str1.lastIndexOf("[[");
- const pos2 = str1.lastIndexOf("]]");
- if (pos1 !== -1) {
- //光标前有[[
- if (pos2 === -1 || pos2 < pos1) {
- //光标在[[之间]]
- str1 = str1.slice(0, str1.lastIndexOf("[[") + 2);
- }
- }
- //TODO 光标会跑到最下面
- const newValue = str1 + strTerm + "]]" + str2;
- refTextArea.current.value = newValue;
- setTextAreaValue(newValue);
- if (typeof onChange !== "undefined") {
- onChange(newValue);
- }
- term_at_menu_hide();
- refTextArea.current.focus();
- }
- return (
- <div className="text_input">
- <div
- className="menu"
- style={{ display: menuDisplay, top: menuTop, left: menuLeft }}
- >
- <TermTextAreaMenu
- currIndex={menuFocusIndex}
- items={menuOptions}
- visible={menuDisplay === "block"}
- searchKey={termSearch}
- onSelect={(value: string) => {
- termInsert(value);
- }}
- onChange={(value: string) => {
- setMenuSelected(value);
- }}
- />
- </div>
- <div
- ref={refShadow}
- className="textarea text_shadow"
- style={{ height: shadowHeight }}
- ></div>
- <textarea
- className="textarea tran_sent_textarea"
- ref={refTextArea}
- style={{ height: textAreaHeight }}
- placeholder={placeholder}
- value={textAreaValue}
- onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => {
- setTextAreaValue(event.target.value);
- if (typeof onChange !== "undefined") {
- onChange(event.target.value);
- }
- }}
- onResize={(_event: unknown) => {
- setShadowHeight(refTextArea.current?.clientHeight);
- }}
- onKeyDown={(event: React.KeyboardEvent<HTMLTextAreaElement>) => {
- switch (event.key) {
- case "ArrowDown":
- if (menuDisplay === "block") {
- if (menuFocusIndex < _term_max_menu) {
- setMenuFocusIndex((value) => ++value);
- }
- event.preventDefault();
- }
- break;
- case "ArrowUp":
- if (menuDisplay === "block") {
- if (menuFocusIndex > 0) {
- setMenuFocusIndex((value) => --value);
- }
- event.preventDefault();
- }
- break;
- case "Enter":
- if (menuDisplay === "block") {
- console.log("enter", menuSelected);
- if (menuSelected) {
- termInsert(menuSelected);
- }
- setMenuDisplay("none");
- event.preventDefault();
- }
- if (event.ctrlKey || event.metaKey) {
- //回车存盘
- console.log("save", textAreaValue);
- if (typeof onSave !== "undefined") {
- onSave(textAreaValue);
- }
- }
- break;
- case "Escape":
- if (menuDisplay === "block") {
- setMenuDisplay("none");
- } else {
- if (typeof onClose !== "undefined") {
- onClose();
- }
- }
- break;
- default:
- break;
- }
- }}
- onKeyUp={(_event) => {
- if (
- refShadow.current === null ||
- refTextArea.current === null ||
- refTextArea.current.parentElement === null
- ) {
- return;
- }
- let textHeight = refShadow.current.scrollHeight;
- const textHeight2 = refTextArea.current.clientHeight;
- if (textHeight2 > textHeight) {
- textHeight = textHeight2;
- }
- setTextAreaHeight(textHeight);
- const value = refTextArea.current.value;
- const selectionStart = refTextArea.current.selectionStart;
- const str1 = value.slice(0, selectionStart);
- const str2 = value.slice(selectionStart);
- const textNode1 = document.createTextNode(str1);
- const textNode2 = document.createTextNode(str2);
- const cursor = document.createElement("span");
- cursor.innerHTML = " ";
- cursor.setAttribute("class", "cursor");
- const mirror =
- refTextArea.current.parentElement.querySelector(".text_shadow");
- if (mirror === null) {
- return;
- }
- mirror.innerHTML = "";
- mirror.appendChild(textNode1);
- mirror.appendChild(cursor);
- mirror.appendChild(textNode2);
- if (str1.slice(-2) === "[[") {
- if (menuDisplay !== "block") {
- setMenuFocusIndex(0);
- setMenuDisplay("block");
- setMenuTop(cursor.offsetTop + 20);
- setMenuLeft(cursor.offsetLeft);
- //menu.innerHTML = TermAtRenderMenu({ focus: 0 });
- //term_at_menu_show(cursor);
- }
- } else {
- if (menuDisplay === "block") {
- const pos1 = str1.lastIndexOf("[[");
- const pos2 = str1.lastIndexOf("]]");
- if (pos1 === -1 || (pos1 !== -1 && pos2 > pos1)) {
- //光标前没有[[ 或 光标在[[]] 之后
- setMenuDisplay("none");
- setTermSearch("");
- }
- }
- }
- if (menuDisplay === "block") {
- const value = refTextArea.current.value;
- const selectionStart = refTextArea.current.selectionStart;
- const str1 = value.slice(0, selectionStart);
- const pos1 = str1.lastIndexOf("[[");
- const pos2 = str1.lastIndexOf("]]");
- if (pos1 !== -1) {
- if (pos2 === -1 || pos2 < pos1) {
- //光标
- const term_input = str1.slice(str1.lastIndexOf("[[") + 2);
- setTermSearch(term_input);
- }
- }
- }
- }}
- />
- </div>
- );
- };
- export default TermTextAreaWidget;
|