| 1 | import { useState, useEffect } from "react"; |
| 2 | import parseBuilder from "./builder"; |
| 3 | import { AiFillSave } from "react-icons/ai"; |
| 4 | |
| 5 | const placeholder = `Enter your variables here like below. |
| 6 | group1Delim:\\n |
| 7 | \tvar1:\\w |
| 8 | \tvar2:\\d |
| 9 | \tgroup2Delim:\\s |
| 10 | \t\tvar3:\\w |
| 11 | \t\tvar4:\\d`; |
| 12 | |
| 13 | function App() { |
| 14 | const [valid, setValid] = useState(false); |
| 15 | const [build, setBuild] = useState(""); |
| 16 | const [edited, setEdited] = useState(0); |
| 17 | const [target, setTarget] = useState(null); |
| 18 | const [save, setSave] = useState(true); |
| 19 | const regexInput = document.querySelector( |
| 20 | "#regex-app > div > div.QtZzw > div > div.AUc0W > div.rjodX > div.cO83v > div.h9z_E.T886D > div > div > div:nth-child(1) > textarea" |
| 21 | ); |
| 22 | const regexValue = document.querySelector( |
| 23 | "#regex-app > div > div.QtZzw > div > div.AUc0W > div.rjodX > div.cO83v > div.h9z_E.T886D > div > div > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code" |
| 24 | ); |
| 25 | const builder = ( |
| 26 | <textarea |
| 27 | rows="20" |
| 28 | className={ |
| 29 | "w-full p-2 rounded-sm font-re101-input text-input decoration-slate-500 underline" + |
| 30 | (valid ? "" : " text-red-500") |
| 31 | } |
| 32 | placeholder={placeholder} |
| 33 | spellCheck="off" |
| 34 | onKeyDown={(e) => { |
| 35 | switch (e.key) { |
| 36 | case "Enter": |
| 37 | break; |
| 38 | case "Tab": |
| 39 | e.preventDefault(); |
| 40 | const index = e.target.selectionStart; |
| 41 | e.target.value = |
| 42 | e.target.value.substring(0, index) + |
| 43 | "\t" + |
| 44 | e.target.value.substring(index); |
| 45 | e.target.selectionStart = e.target.selectionEnd = index + 1; |
| 46 | break; |
| 47 | } |
| 48 | if (e.ctrlKey && e.key == "s") { |
| 49 | saveBuild(); |
| 50 | } |
| 51 | }} |
| 52 | onChangeCapture={(e) => { |
| 53 | const parseBuild = parseBuilder(e.target.value); |
| 54 | if (!parseBuild) setValid(false); |
| 55 | else setValid(true); |
| 56 | setBuild(parseBuild); |
| 57 | setTarget(e.target); |
| 58 | setTimeout( |
| 59 | () => |
| 60 | setEdited((old) => { |
| 61 | return old - 1; |
| 62 | }), |
| 63 | 1000 |
| 64 | ); |
| 65 | setEdited(edited + 1); |
| 66 | }} |
| 67 | ></textarea> |
| 68 | ); |
| 69 | |
| 70 | const updateRegex = () => { |
| 71 | if (edited !== 0 || !save) return; |
| 72 | saveBuild(); |
| 73 | }; |
| 74 | |
| 75 | useEffect(updateRegex, [edited]); |
| 76 | |
| 77 | useEffect(() => { |
| 78 | if (save) saveBuild(); |
| 79 | }, [save]); |
| 80 | |
| 81 | return ( |
| 82 | <div> |
| 83 | <div className="w-full"> |
| 84 | <div className="w-full text-lg text-center relative"> |
| 85 | <div>Build Variables</div> |
| 86 | <div className="absolute right-2 top-8"> |
| 87 | {saveButton(setSave, save)} |
| 88 | </div> |
| 89 | </div> |
| 90 | {builder} |
| 91 | </div> |
| 92 | </div> |
| 93 | ); |
| 94 | |
| 95 | function saveBuild() { |
| 96 | const length = regexValue.textContent.length; |
| 97 | for (let i = 0; i < length; i++) |
| 98 | regexInput.dispatchEvent( |
| 99 | new KeyboardEvent("keydown", { |
| 100 | key: "Backspace", |
| 101 | code: "Backspace", |
| 102 | keyCode: 8, |
| 103 | }) |
| 104 | ); |
| 105 | regexInput.value = build; |
| 106 | setTimeout(() => target?.focus(), 100); |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | export default App; |
| 111 | function saveButton(setSave, save) { |
| 112 | const active = "bg-blue-500 h-10 p-1 rounded-sm hover:bg-blue-100"; |
| 113 | const inactive = "bg-blue-100 h-10 p-1 rounded-sm hover:bg-blue-500"; |
| 114 | const activeSave = <AiFillSave size="100%" color="white" />; |
| 115 | const inactiveSave = <AiFillSave size="100%" color="white" />; |
| 116 | return ( |
| 117 | <button |
| 118 | className={save ? active : inactive} |
| 119 | onClick={(e) => { |
| 120 | e.preventDefault(); |
| 121 | setSave(!save); |
| 122 | }} |
| 123 | > |
| 124 | {save ? activeSave : inactiveSave} |
| 125 | </button> |
| 126 | ); |
| 127 | } |