101
common/resources/client/ace-editor/ace-dialog.tsx
Executable file
101
common/resources/client/ace-editor/ace-dialog.tsx
Executable file
@@ -0,0 +1,101 @@
|
||||
import React, {MutableRefObject, ReactNode, Suspense, useState} from 'react';
|
||||
import {Dialog} from '../ui/overlays/dialog/dialog';
|
||||
import {DialogHeader} from '../ui/overlays/dialog/dialog-header';
|
||||
import {Trans} from '../i18n/trans';
|
||||
import {DialogBody} from '../ui/overlays/dialog/dialog-body';
|
||||
import {ProgressCircle} from '../ui/progress/progress-circle';
|
||||
import {useDialogContext} from '../ui/overlays/dialog/dialog-context';
|
||||
import {DialogFooter} from '../ui/overlays/dialog/dialog-footer';
|
||||
import {Button} from '../ui/buttons/button';
|
||||
import type ReactAce from 'react-ace';
|
||||
|
||||
const AceEditor = React.lazy(() => import('./ace-editor'));
|
||||
|
||||
interface TextEditorSourcecodeDialogProps {
|
||||
defaultValue: string;
|
||||
mode?: 'css' | 'html' | 'php_laravel_blade';
|
||||
title: ReactNode;
|
||||
onSave?: (value?: string) => void;
|
||||
isSaving?: boolean;
|
||||
footerStartAction?: ReactNode;
|
||||
beautify?: boolean;
|
||||
editorRef?: MutableRefObject<ReactAce | null>;
|
||||
}
|
||||
export function AceDialog({
|
||||
defaultValue,
|
||||
mode = 'html',
|
||||
title,
|
||||
onSave,
|
||||
isSaving,
|
||||
footerStartAction,
|
||||
beautify,
|
||||
editorRef,
|
||||
}: TextEditorSourcecodeDialogProps) {
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const [isValid, setIsValid] = useState<boolean>(true);
|
||||
|
||||
return (
|
||||
<Dialog size="fullscreen" className="h-full w-full">
|
||||
<DialogHeader>{title}</DialogHeader>
|
||||
<DialogBody className="relative flex-auto" padding="p-0">
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex h-400 w-full items-center justify-center">
|
||||
<ProgressCircle
|
||||
aria-label="Loading editor..."
|
||||
isIndeterminate
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<AceEditor
|
||||
beautify={beautify}
|
||||
mode={mode}
|
||||
onChange={newValue => setValue(newValue)}
|
||||
defaultValue={value || ''}
|
||||
onIsValidChange={setIsValid}
|
||||
editorRef={editorRef}
|
||||
/>
|
||||
</Suspense>
|
||||
</DialogBody>
|
||||
<Footer
|
||||
disabled={!isValid || isSaving}
|
||||
value={value}
|
||||
onSave={onSave}
|
||||
startAction={footerStartAction}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
interface FooterProps {
|
||||
disabled: boolean | undefined;
|
||||
value?: string;
|
||||
onSave?: (value?: string) => void;
|
||||
startAction?: ReactNode;
|
||||
}
|
||||
function Footer({disabled, value, onSave, startAction}: FooterProps) {
|
||||
const {close} = useDialogContext();
|
||||
return (
|
||||
<DialogFooter dividerTop startAction={startAction}>
|
||||
<Button onClick={() => close()}>
|
||||
<Trans message="Cancel" />
|
||||
</Button>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
variant="flat"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
if (onSave) {
|
||||
onSave(value);
|
||||
} else {
|
||||
close(value);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trans message="Save" />
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
);
|
||||
}
|
||||
73
common/resources/client/ace-editor/ace-editor.tsx
Executable file
73
common/resources/client/ace-editor/ace-editor.tsx
Executable file
@@ -0,0 +1,73 @@
|
||||
import ace from 'ace-builds/src-noconflict/ace';
|
||||
import cssWorkerUrl from 'ace-builds/src-noconflict/worker-css?url';
|
||||
import htmlWorkerUrl from 'ace-builds/src-noconflict/worker-html?url';
|
||||
import phpWorkerUrl from 'ace-builds/src-noconflict/worker-php?url';
|
||||
import javascriptWorkerUrl from 'ace-builds/src-noconflict/worker-javascript?url';
|
||||
import React, {MutableRefObject, useEffect, useRef} from 'react';
|
||||
import AceEditorRender from 'react-ace';
|
||||
import ReactAce from 'react-ace';
|
||||
import 'ace-builds/src-noconflict/mode-css';
|
||||
import 'ace-builds/src-noconflict/mode-html';
|
||||
import 'ace-builds/src-noconflict/mode-javascript';
|
||||
import 'ace-builds/src-noconflict/mode-php_laravel_blade';
|
||||
import 'ace-builds/src-noconflict/theme-chrome';
|
||||
import 'ace-builds/src-noconflict/theme-tomorrow_night';
|
||||
import 'ace-builds/src-noconflict/ext-language_tools';
|
||||
import Beautify from 'ace-builds/src-noconflict/ext-beautify';
|
||||
import {useIsDarkMode} from '../ui/themes/use-is-dark-mode';
|
||||
|
||||
ace.config.setModuleUrl('ace/mode/css_worker', cssWorkerUrl);
|
||||
ace.config.setModuleUrl('ace/mode/html_worker', htmlWorkerUrl);
|
||||
ace.config.setModuleUrl('ace/mode/php_worker', phpWorkerUrl);
|
||||
ace.config.setModuleUrl('ace/mode/javascript_worker', javascriptWorkerUrl);
|
||||
|
||||
interface Props {
|
||||
mode: 'css' | 'html' | 'javascript' | 'php_laravel_blade';
|
||||
onChange: (value: string) => void;
|
||||
onIsValidChange: (isValid: boolean) => void;
|
||||
defaultValue: string;
|
||||
beautify?: boolean;
|
||||
editorRef?: MutableRefObject<ReactAce | null>;
|
||||
}
|
||||
export default function AceEditor({
|
||||
mode,
|
||||
onChange,
|
||||
onIsValidChange,
|
||||
defaultValue,
|
||||
beautify = true,
|
||||
editorRef: propsEditorRef,
|
||||
}: Props) {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const defaultRef = useRef<ReactAce | null>(null);
|
||||
const editorRef = propsEditorRef || defaultRef;
|
||||
|
||||
useEffect(() => {
|
||||
if (beautify && editorRef.current) {
|
||||
Beautify.beautify(editorRef.current.editor.session);
|
||||
}
|
||||
}, [beautify, editorRef]);
|
||||
|
||||
return (
|
||||
<AceEditorRender
|
||||
ref={editorRef}
|
||||
width="auto"
|
||||
height="auto"
|
||||
wrapEnabled
|
||||
className="absolute inset-0"
|
||||
focus
|
||||
mode={mode}
|
||||
theme={isDarkMode ? 'tomorrow_night' : 'chrome'}
|
||||
enableBasicAutocompletion
|
||||
enableLiveAutocompletion
|
||||
defaultValue={defaultValue}
|
||||
onChange={onChange}
|
||||
editorProps={{$blockScrolling: true}}
|
||||
commands={Beautify.commands}
|
||||
onValidate={annotations => {
|
||||
const isValid =
|
||||
annotations.filter(a => a.type === 'error').length === 0;
|
||||
onIsValidChange(isValid);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user