import React, { MutableRefObject } from "react"; import { twMerge } from "tailwind-merge"; import AceEditorModes from "./ace-editor-modes"; import { AceEditorOptions } from "@moduletrace/datasquirel/dist/package-shared/types"; export type AceEditorComponentType = { editorRef?: MutableRefObject; readOnly?: boolean; /** Function to call when Ctrl+Enter is pressed */ ctrlEnterFn?: (editor: AceAjax.Editor) => void; content?: string; placeholder?: string; title?: string; mode?: (typeof AceEditorModes)[number]; fontSize?: string; previewMode?: boolean; onChange?: (value: string) => void; delay?: number; wrapperProps?: React.DetailedHTMLProps< React.HTMLAttributes, HTMLDivElement >; refreshDepArr?: any[]; editorOptions?: AceEditorOptions; showLabel?: boolean; }; let timeout: any; /** * # Powerful Ace Editor * @note **NOTE** head scripts required * @script `https://cdnjs.cloudflare.com/ajax/libs/ace/1.22.0/ace.min.js` * @script `https://cdnjs.cloudflare.com/ajax/libs/ace/1.22.0/ext-language_tools.min.js` */ export default function AceEditor({ editorRef, readOnly, ctrlEnterFn, content = "", placeholder, mode, fontSize, previewMode, onChange, delay = 500, refreshDepArr, wrapperProps, editorOptions, showLabel, title, }: AceEditorComponentType) { try { const editorElementRef = React.useRef(undefined); const editorRefInstance = React.useRef(undefined); const [refresh, setRefresh] = React.useState(0); const [darkMode, setDarkMode] = React.useState(false); const [ready, setReady] = React.useState(false); React.useEffect(() => { if (!ready) return; if (!ace?.edit || !editorElementRef.current) { setTimeout(() => { setRefresh((prev) => prev + 1); }, 1000); return; } const editor = ace.edit(editorElementRef.current); editor.setOptions({ mode: `ace/mode/${mode ? mode : "javascript"}`, theme: darkMode ? "ace/theme/tomorrow_night_eighties" : "ace/theme/ace_light", value: (() => { try { return JSON.stringify(JSON.parse(content), null, 4); } catch (error) { return content; } })(), placeholder: placeholder ? placeholder : "", enableBasicAutocompletion: true, enableLiveAutocompletion: true, readOnly: readOnly ? true : false, fontSize: fontSize ? fontSize : null, showLineNumbers: previewMode ? false : true, wrap: true, wrapMethod: "code", ...editorOptions, }); editor.commands.addCommand({ name: "myCommand", bindKey: { win: "Ctrl-Enter", mac: "Command-Enter" }, exec: function (editor) { if (ctrlEnterFn) ctrlEnterFn(editor); }, readOnly: true, }); editor.getSession().on("change", function (e) { if (onChange) { clearTimeout(timeout); setTimeout(() => { try { onChange(editor.getValue()); } catch (error) {} }, delay); } }); editorRefInstance.current = editor; if (editorRef) editorRef.current = editor; return function () { editor.destroy(); }; }, [refresh, darkMode, ready, mode, ...(refreshDepArr || [])]); React.useEffect(() => { const htmlClassName = document.documentElement.className; if (htmlClassName.match(/dark/i)) setDarkMode(true); setTimeout(() => { setReady(true); }, 200); }, []); return (
{showLabel && title ? ( ) : null}
); } catch (error: any) { return ( Editor Error:{" "} {error.message} ); } }