A better version of regex 101
| 1 | import { useState, useEffect } from "react"; |
| 2 | import parseBuilder, { readVars } 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 [hideVariables, setHideVariables] = useState(false); |
| 20 | const [hideBuilder, setHideBuilder] = useState(false); |
| 21 | const [varText, setVarText] = useState(""); |
| 22 | const [builderText, setBuilderText] = useState(""); |
| 23 | console.log("app is running"); |
| 24 | const regexInput = document.querySelector( |
| 25 | "#regex-app > div > div.QtZzw > div > div.AUc0W > div.rjodX > div.cO83v > div.h9z_E.T886D > div > div > div:nth-child(1) > textarea" |
| 26 | ); |
| 27 | const regexValue = document.querySelector( |
| 28 | "#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" |
| 29 | ); |
| 30 | const hideButton = (setHide, hide) => { |
| 31 | return ( |
| 32 | <div className="absolute flex bottom-3 w-full"> |
| 33 | <button |
| 34 | className="h-10 w-24 rounded-md mx-auto bg-blue-500 text-white" |
| 35 | onClick={(e) => { |
| 36 | e.preventDefault(); |
| 37 | setHide(!hide); |
| 38 | }} |
| 39 | > |
| 40 | {hide ? "Show" : "Hide"} |
| 41 | </button> |
| 42 | </div> |
| 43 | ); |
| 44 | }; |
| 45 | const variables = ( |
| 46 | <div className="relative"> |
| 47 | <textarea |
| 48 | rows={hideVariables ? 2 : 10} |
| 49 | className="w-full" |
| 50 | onChange={(e) => { |
| 51 | setVarText(e.target.value); |
| 52 | const parsedVars = readVars(e.target.value); |
| 53 | const parseBuild = parseBuilder(builderText, parsedVars); |
| 54 | if (!parseBuild) setValid(false); |
| 55 | else setValid(true); |
| 56 | setBuild(parseBuild); |
| 57 | }} |
| 58 | onKeyDown={(e) => { |
| 59 | if (e.ctrlKey && e.key == "s") { |
| 60 | saveBuild(); |
| 61 | } |
| 62 | }} |
| 63 | ></textarea> |
| 64 | {hideButton(setHideVariables, hideVariables)} |
| 65 | </div> |
| 66 | ); |
| 67 | const builder = ( |
| 68 | <div className="relative"> |
| 69 | <textarea |
| 70 | rows={hideBuilder ? 2 : 30} |
| 71 | className={ |
| 72 | "w-full p-2 rounded-sm font-re101-input text-input decoration-slate-500 underline" + |
| 73 | (valid ? "" : " text-red-500") |
| 74 | } |
| 75 | placeholder={placeholder} |
| 76 | spellCheck="off" |
| 77 | onKeyDown={(e) => { |
| 78 | switch (e.key) { |
| 79 | case "Enter": |
| 80 | break; |
| 81 | case "Tab": |
| 82 | e.preventDefault(); |
| 83 | const index = e.target.selectionStart; |
| 84 | e.target.value = |
| 85 | e.target.value.substring(0, index) + |
| 86 | "\t" + |
| 87 | e.target.value.substring(index); |
| 88 | e.target.selectionStart = e.target.selectionEnd = index + 1; |
| 89 | break; |
| 90 | } |
| 91 | if (e.ctrlKey && e.key == "s") { |
| 92 | saveBuild(); |
| 93 | } |
| 94 | }} |
| 95 | onChangeCapture={(e) => { |
| 96 | const parsedVars = readVars(varText); |
| 97 | setBuilderText(e.target.value); |
| 98 | const parseBuild = parseBuilder(e.target.value, parsedVars); |
| 99 | if (!parseBuild) setValid(false); |
| 100 | else setValid(true); |
| 101 | setBuild(parseBuild); |
| 102 | setTarget(e.target); |
| 103 | setTimeout( |
| 104 | () => |
| 105 | setEdited((old) => { |
| 106 | return old - 1; |
| 107 | }), |
| 108 | 1000 |
| 109 | ); |
| 110 | setEdited(edited + 1); |
| 111 | }} |
| 112 | ></textarea> |
| 113 | {hideButton(setHideBuilder, hideBuilder)} |
| 114 | </div> |
| 115 | ); |
| 116 | |
| 117 | const updateRegex = () => { |
| 118 | if (edited !== 0 || !save) return; |
| 119 | saveBuild(); |
| 120 | }; |
| 121 | |
| 122 | useEffect(updateRegex, [edited]); |
| 123 | |
| 124 | useEffect(() => { |
| 125 | if (save) saveBuild(); |
| 126 | }, [save]); |
| 127 | |
| 128 | return ( |
| 129 | <div> |
| 130 | <div className="w-full"> |
| 131 | <div className="w-full text-lg text-center relative"> |
| 132 | <div>Build Variables</div> |
| 133 | {variables} |
| 134 | <div>Build Regex</div> |
| 135 | {builder} |
| 136 | <div className="absolute right-8 top-8"> |
| 137 | {saveButton(setSave, save)} |
| 138 | </div> |
| 139 | </div> |
| 140 | </div> |
| 141 | </div> |
| 142 | ); |
| 143 | |
| 144 | function saveBuild() { |
| 145 | if (!regexValue) return; |
| 146 | const length = regexValue.textContent.length; |
| 147 | for (let i = 0; i < length; i++) |
| 148 | regexInput.dispatchEvent( |
| 149 | new KeyboardEvent("keydown", { |
| 150 | key: "Backspace", |
| 151 | code: "Backspace", |
| 152 | keyCode: 8, |
| 153 | }) |
| 154 | ); |
| 155 | regexInput.value = build; |
| 156 | setTimeout(() => target?.focus(), 100); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | export default App; |
| 161 | function saveButton(setSave, save) { |
| 162 | const active = "bg-blue-500 h-10 p-1 rounded-sm hover:bg-blue-100"; |
| 163 | const inactive = "bg-blue-100 h-10 p-1 rounded-sm hover:bg-blue-500"; |
| 164 | const activeSave = <AiFillSave size="100%" color="white" />; |
| 165 | const inactiveSave = <AiFillSave size="100%" color="white" />; |
| 166 | return ( |
| 167 | <button |
| 168 | className={save ? active : inactive} |
| 169 | onClick={(e) => { |
| 170 | e.preventDefault(); |
| 171 | setSave(!save); |
| 172 | }} |
| 173 | > |
| 174 | {save ? activeSave : inactiveSave} |
| 175 | </button> |
| 176 | ); |
| 177 | } |