new-personal-site/components/lib/editors/AceEditor.tsx
2024-12-09 16:36:17 +01:00

137 lines
4.5 KiB
TypeScript
Executable File

import { AceEditorAcceptedModes } from "@/components/general/data/partials/EditDataListButton";
import React, { MutableRefObject } from "react";
import { twMerge } from "tailwind-merge";
export type AceEditorComponentType = {
editorRef?: MutableRefObject<AceAjax.Editor>;
readOnly?: boolean;
/** Function to call when Ctrl+Enter is pressed */
ctrlEnterFn?: (editor: AceAjax.Editor) => void;
content?: string;
placeholder?: string;
mode?: AceEditorAcceptedModes;
fontSize?: string;
previewMode?: boolean;
onChange?: (value: string) => void;
delay?: number;
};
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,
}: AceEditorComponentType) {
try {
const editorElementRef = React.useRef<HTMLDivElement>();
const editorRefInstance = React.useRef<AceAjax.Editor>();
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_bright"
: "ace/theme/ace_light",
value: content,
placeholder: placeholder ? placeholder : "",
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
readOnly: readOnly ? true : false,
fontSize: fontSize ? fontSize : null,
showLineNumbers: previewMode ? false : true,
wrap: true,
wrapMethod: "code",
// onchange: (e) => {
// console.log(e);
// },
});
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(() => {
onChange(editor.getValue());
console.log(editor.getValue());
}, delay);
}
});
editorRefInstance.current = editor;
if (editorRef) editorRef.current = editor;
}, [refresh, darkMode, ready]);
React.useEffect(() => {
const htmlClassName = document.documentElement.className;
if (htmlClassName.match(/dark/i)) setDarkMode(true);
setTimeout(() => {
setReady(true);
}, 200);
}, []);
return (
<React.Fragment>
<div
className={twMerge(
"w-full h-[400px] block rounded-md overflow-hidden",
"border border-slate-200 border-solid",
"dark:border-white/20"
)}
>
<div
ref={editorElementRef as any}
className="w-full h-full"
></div>
</div>
</React.Fragment>
);
} catch (error: any) {
return (
<React.Fragment>
<span className="text-sm m-0">
Editor Error:{" "}
<b className="text-red-600">{error.message}</b>
</span>
</React.Fragment>
);
}
}