-
Notifications
You must be signed in to change notification settings - Fork 80
Expand file tree
/
Copy pathCodeEditor.tsx
More file actions
93 lines (86 loc) · 2.89 KB
/
Copy pathCodeEditor.tsx
File metadata and controls
93 lines (86 loc) · 2.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import hljs from "highlight.js/lib/core";
import json from "highlight.js/lib/languages/json";
import { ClipboardEvent, ReactElement, useRef } from "react";
import Editor from "react-simple-code-editor";
import "highlight.js/styles/atom-one-light.css";
hljs.registerLanguage("json", json);
export interface CodeEditorProps {
value: string;
onChange?: (value: string) => void;
readOnly?: boolean;
height?: string;
}
function highlight(code: string): string {
try {
return hljs.highlight(code, { language: "json", ignoreIllegals: true }).value;
} catch (error) {
console.warn("Syntax highlighting error:", error);
return code;
}
}
function jsonError(code: string): string | null {
if (code.trim() === "") {
return null;
}
try {
JSON.parse(code);
return null;
} catch (error) {
return error instanceof Error ? error.message : "Invalid JSON";
}
}
export function CodeEditor(props: CodeEditorProps): ReactElement {
const error = jsonError(props.value);
const containerRef = useRef<HTMLDivElement>(null);
const onPaste = (event: ClipboardEvent<HTMLDivElement>): void => {
const textarea = event.target;
if (!(textarea instanceof HTMLTextAreaElement)) {
return;
}
const replacesWholeValue = textarea.selectionStart === 0 && textarea.selectionEnd === textarea.value.length;
if (!replacesWholeValue) {
return;
}
// Pasting over the entire value lands the caret (and scroll position) at the end of
// the pasted text, same as any textarea. For a config the user is about to read
// top-to-bottom, jump back to the start instead.
requestAnimationFrame(() => {
textarea.setSelectionRange(0, 0);
textarea.scrollTop = 0;
const wrapper = containerRef.current;
if (wrapper) {
wrapper.scrollTop = 0;
}
});
};
return (
<div
ref={containerRef}
className="widget-charts-playground-code-editor"
style={{ height: props.height ?? "200px" }}
onPaste={onPaste}
>
{error && (
<div className="widget-charts-playground-code-editor-error" role="alert">
{error}
</div>
)}
<Editor
value={props.value}
onValueChange={value => props.onChange?.(value)}
highlight={highlight}
disabled={props.readOnly}
padding={8}
tabSize={2}
insertSpaces
ignoreTabKey={false}
spellCheck={false}
style={{
width: "100%",
minHeight: "100%",
fontFamily: "monospace"
}}
/>
</div>
);
}