@eccenca/gui-elements 24.0.1 → 24.1.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -5
- package/dist/cjs/cmem/react-flow/StickyNoteModal/StickyNoteModal.js +3 -3
- package/dist/cjs/cmem/react-flow/StickyNoteModal/StickyNoteModal.js.map +1 -1
- package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js +3 -3
- package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -1
- package/dist/cjs/extensions/codemirror/CodeMirror.js +58 -6
- package/dist/cjs/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/cjs/extensions/codemirror/debouncedLinter.js +18 -0
- package/dist/cjs/extensions/codemirror/debouncedLinter.js.map +1 -0
- package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +9 -18
- package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
- package/dist/cjs/extensions/codemirror/linters/jsLinter.js +36 -0
- package/dist/cjs/extensions/codemirror/linters/jsLinter.js.map +1 -0
- package/dist/cjs/extensions/codemirror/linters/turtleLinter.js +81 -0
- package/dist/cjs/extensions/codemirror/linters/turtleLinter.js.map +1 -0
- package/dist/cjs/extensions/codemirror/types.js +3 -0
- package/dist/cjs/extensions/codemirror/types.js.map +1 -0
- package/dist/esm/cmem/react-flow/StickyNoteModal/StickyNoteModal.js +4 -4
- package/dist/esm/cmem/react-flow/StickyNoteModal/StickyNoteModal.js.map +1 -1
- package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js +14 -3
- package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -1
- package/dist/esm/extensions/codemirror/CodeMirror.js +58 -7
- package/dist/esm/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/esm/extensions/codemirror/debouncedLinter.js +15 -0
- package/dist/esm/extensions/codemirror/debouncedLinter.js.map +1 -0
- package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +6 -15
- package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
- package/dist/esm/extensions/codemirror/linters/jsLinter.js +32 -0
- package/dist/esm/extensions/codemirror/linters/jsLinter.js.map +1 -0
- package/dist/esm/extensions/codemirror/linters/turtleLinter.js +77 -0
- package/dist/esm/extensions/codemirror/linters/turtleLinter.js.map +1 -0
- package/dist/esm/extensions/codemirror/types.js +2 -0
- package/dist/esm/extensions/codemirror/types.js.map +1 -0
- package/dist/types/cmem/react-flow/StickyNoteModal/StickyNoteModal.d.ts +5 -1
- package/dist/types/components/AutoSuggestion/ExtendedCodeEditor.d.ts +11 -6
- package/dist/types/extensions/codemirror/CodeMirror.d.ts +23 -5
- package/dist/types/extensions/codemirror/debouncedLinter.d.ts +4 -0
- package/dist/types/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.d.ts +3 -5
- package/dist/types/extensions/codemirror/linters/jsLinter.d.ts +5 -0
- package/dist/types/extensions/codemirror/linters/turtleLinter.d.ts +5 -0
- package/dist/types/extensions/codemirror/types.d.ts +5 -0
- package/package.json +6 -5
- package/src/cmem/react-flow/StickyNoteModal/StickyNoteModal.tsx +16 -2
- package/src/components/AutoSuggestion/ExtendedCodeEditor.tsx +29 -6
- package/src/components/TextField/stories/TextField.stories.tsx +2 -1
- package/src/extensions/codemirror/CodeMirror.stories.tsx +30 -0
- package/src/extensions/codemirror/CodeMirror.tsx +86 -9
- package/src/extensions/codemirror/_codemirror.scss +96 -0
- package/src/extensions/codemirror/debouncedLinter.ts +26 -0
- package/src/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.ts +6 -16
- package/src/extensions/codemirror/linters/jsLinter.ts +38 -0
- package/src/extensions/codemirror/linters/turtleLinter.ts +102 -0
- package/src/extensions/codemirror/types.ts +7 -0
|
@@ -4,7 +4,16 @@ import getColorConfiguration from "../../../common/utils/getColorConfiguration";
|
|
|
4
4
|
import { CodeEditor } from "../../../extensions";
|
|
5
5
|
import { ReactFlowHotkeyContext } from "../extensions/ReactFlowHotkeyContext";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Button,
|
|
9
|
+
CodeEditorProps,
|
|
10
|
+
FieldItem,
|
|
11
|
+
Icon,
|
|
12
|
+
SimpleDialog,
|
|
13
|
+
SimpleDialogProps,
|
|
14
|
+
Tag,
|
|
15
|
+
TagList,
|
|
16
|
+
} from "./../../../index";
|
|
8
17
|
|
|
9
18
|
export type StickyNoteModalTranslationKeys = "modalTitle" | "noteLabel" | "colorLabel" | "saveButton" | "cancelButton";
|
|
10
19
|
|
|
@@ -32,10 +41,14 @@ export interface StickyNoteModalProps {
|
|
|
32
41
|
* Forward other properties to the `SimpleModal` element that is used for this dialog.
|
|
33
42
|
*/
|
|
34
43
|
simpleDialogProps?: Omit<SimpleDialogProps, "size" | "title" | "hasBorder" | "isOpen" | "onClose" | "actions">;
|
|
44
|
+
/**
|
|
45
|
+
* Code editor props
|
|
46
|
+
*/
|
|
47
|
+
codeEditorProps?: Omit<CodeEditorProps, "defaultValue" | "onChange" | "preventLinuNumbers" | "id" | "name">;
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
export const StickyNoteModal: React.FC<StickyNoteModalProps> = React.memo(
|
|
38
|
-
({ metaData, onClose, onSubmit, translate, simpleDialogProps }) => {
|
|
51
|
+
({ metaData, onClose, onSubmit, translate, simpleDialogProps, codeEditorProps }) => {
|
|
39
52
|
const refNote = React.useRef<string>(metaData?.note ?? "");
|
|
40
53
|
const [color, setSelectedColor] = React.useState<string>(metaData?.color ?? "");
|
|
41
54
|
const noteColors: [string, string][] = Object.entries(getColorConfiguration("stickynotes")).map(
|
|
@@ -123,6 +136,7 @@ export const StickyNoteModal: React.FC<StickyNoteModalProps> = React.memo(
|
|
|
123
136
|
refNote.current = value;
|
|
124
137
|
}}
|
|
125
138
|
defaultValue={refNote.current}
|
|
139
|
+
{...codeEditorProps}
|
|
126
140
|
/>
|
|
127
141
|
</FieldItem>
|
|
128
142
|
<FieldItem
|
|
@@ -4,7 +4,7 @@ import { EditorState } from "@codemirror/state";
|
|
|
4
4
|
import { EditorView, lineNumbers, Rect } from "@codemirror/view";
|
|
5
5
|
|
|
6
6
|
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
7
|
-
import { CodeEditor } from "../../extensions/codemirror/CodeMirror";
|
|
7
|
+
import { CodeEditor, CodeEditorProps } from "../../extensions/codemirror/CodeMirror";
|
|
8
8
|
//hooks
|
|
9
9
|
import { SupportedCodeEditorModes } from "../../extensions/codemirror/hooks/useCodemirrorModeExtension.hooks";
|
|
10
10
|
|
|
@@ -15,23 +15,23 @@ export interface IRange {
|
|
|
15
15
|
|
|
16
16
|
export interface ExtendedCodeEditorProps {
|
|
17
17
|
// Is called with the editor instance that allows access via the CodeMirror API
|
|
18
|
-
setCM: (editor: EditorView | undefined) =>
|
|
18
|
+
setCM: (editor: EditorView | undefined) => void;
|
|
19
19
|
// Called whenever the editor content changes
|
|
20
|
-
onChange: (value: string) =>
|
|
20
|
+
onChange: (value: string) => void;
|
|
21
21
|
// Called when the cursor position changes
|
|
22
|
-
onCursorChange: (pos: number, coords: Rect, scrollinfo: HTMLElement, cm: EditorView) =>
|
|
22
|
+
onCursorChange: (pos: number, coords: Rect, scrollinfo: HTMLElement, cm: EditorView) => void;
|
|
23
23
|
// The editor theme, e.g. "sparql"
|
|
24
24
|
mode?: SupportedCodeEditorModes;
|
|
25
25
|
// The initial value of the editor
|
|
26
26
|
initialValue: string;
|
|
27
27
|
// Called when the focus status changes
|
|
28
|
-
onFocusChange: (focused: boolean) =>
|
|
28
|
+
onFocusChange: (focused: boolean) => void;
|
|
29
29
|
// Called when the user presses a key
|
|
30
30
|
onKeyDown: (event: KeyboardEvent) => boolean;
|
|
31
31
|
// function invoked when any click occurs
|
|
32
32
|
onMouseDown?: (view: EditorView) => void;
|
|
33
33
|
// Called when the user selects text
|
|
34
|
-
onSelection: (ranges: IRange[]) =>
|
|
34
|
+
onSelection: (ranges: IRange[]) => void;
|
|
35
35
|
// If the <Tab> key is enabled as normal input, i.e. it won't have the behavior of changing to the next input element, expected in a web app.
|
|
36
36
|
enableTab?: boolean;
|
|
37
37
|
/** Placeholder to be shown when no text has been entered, yet. */
|
|
@@ -40,6 +40,27 @@ export interface ExtendedCodeEditorProps {
|
|
|
40
40
|
showScrollBar?: boolean;
|
|
41
41
|
/** allow multiline entries when new line characters are entered */
|
|
42
42
|
multiline?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Code editor props
|
|
45
|
+
*/
|
|
46
|
+
codeEditorProps?: Omit<
|
|
47
|
+
CodeEditorProps,
|
|
48
|
+
| "defaultValue"
|
|
49
|
+
| "setEditorView"
|
|
50
|
+
| "onChange"
|
|
51
|
+
| "onCursorChange"
|
|
52
|
+
| "onFocusChange"
|
|
53
|
+
| "onKeyDown"
|
|
54
|
+
| "onSelection"
|
|
55
|
+
| "onMouseDown"
|
|
56
|
+
| "shouldHaveMinimalSetup"
|
|
57
|
+
| "preventLineNumbers"
|
|
58
|
+
| "mode"
|
|
59
|
+
| "name"
|
|
60
|
+
| "enableTab"
|
|
61
|
+
| "additionalExtensions"
|
|
62
|
+
| "outerDivAttributes"
|
|
63
|
+
>;
|
|
43
64
|
}
|
|
44
65
|
|
|
45
66
|
export type IEditorProps = ExtendedCodeEditorProps;
|
|
@@ -58,6 +79,7 @@ export const ExtendedCodeEditor = ({
|
|
|
58
79
|
placeholder,
|
|
59
80
|
onCursorChange,
|
|
60
81
|
onSelection,
|
|
82
|
+
codeEditorProps,
|
|
61
83
|
}: ExtendedCodeEditorProps) => {
|
|
62
84
|
const initialContent = React.useRef(multiline ? initialValue : initialValue.replace(/[\r\n]/g, " "));
|
|
63
85
|
const multilineExtensions = multiline
|
|
@@ -88,6 +110,7 @@ export const ExtendedCodeEditor = ({
|
|
|
88
110
|
multiline ? "codeeditor" : `singlelinecodeeditor ${BlueprintClassNames.INPUT}`
|
|
89
111
|
}`,
|
|
90
112
|
}}
|
|
113
|
+
{...codeEditorProps}
|
|
91
114
|
/>
|
|
92
115
|
);
|
|
93
116
|
};
|
|
@@ -31,6 +31,7 @@ Default.args = {
|
|
|
31
31
|
fullWidth: false,
|
|
32
32
|
placeholder: "placeholder text",
|
|
33
33
|
readOnly: false,
|
|
34
|
+
disabled: false,
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
/** Text field with default value that contains a zero width/invisible character.
|
|
@@ -46,7 +47,7 @@ const invisibleCharacterWarningProps: TextFieldProps = {
|
|
|
46
47
|
const codePointsString = [...Array.from(codePoints)]
|
|
47
48
|
.map((n) => {
|
|
48
49
|
const info = characters.invisibleZeroWidthCharacters.codePointMap.get(n);
|
|
49
|
-
return info
|
|
50
|
+
return info?.fullLabel;
|
|
50
51
|
})
|
|
51
52
|
.join(", ");
|
|
52
53
|
alert("Invisible character detected in input string. Code points: " + codePointsString);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Meta, StoryFn } from "@storybook/react";
|
|
3
3
|
|
|
4
|
+
import { helpersArgTypes } from "../../../.storybook/helpers";
|
|
5
|
+
|
|
4
6
|
import { CodeEditor } from "./CodeMirror";
|
|
5
7
|
|
|
6
8
|
export default {
|
|
@@ -11,6 +13,9 @@ export default {
|
|
|
11
13
|
onChange: {
|
|
12
14
|
action: "value changed",
|
|
13
15
|
},
|
|
16
|
+
intent: {
|
|
17
|
+
...helpersArgTypes.exampleIntent,
|
|
18
|
+
},
|
|
14
19
|
},
|
|
15
20
|
} as Meta<typeof CodeEditor>;
|
|
16
21
|
|
|
@@ -22,3 +27,28 @@ BasicExample.args = {
|
|
|
22
27
|
mode: "markdown",
|
|
23
28
|
defaultValue: "**test me**",
|
|
24
29
|
};
|
|
30
|
+
|
|
31
|
+
export const LinterExample = TemplateFull.bind({});
|
|
32
|
+
LinterExample.args = {
|
|
33
|
+
name: "codeinput",
|
|
34
|
+
defaultValue: "**test me**",
|
|
35
|
+
mode: "javascript",
|
|
36
|
+
useLinting: true,
|
|
37
|
+
autoFocus: true,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const DisabledExample = TemplateFull.bind({});
|
|
41
|
+
DisabledExample.args = {
|
|
42
|
+
name: "codeinput",
|
|
43
|
+
defaultValue: "**test me**",
|
|
44
|
+
mode: "javascript",
|
|
45
|
+
disabled: true,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const IntentExample = TemplateFull.bind({});
|
|
49
|
+
IntentExample.args = {
|
|
50
|
+
name: "codeinput",
|
|
51
|
+
defaultValue: "**test me**",
|
|
52
|
+
mode: "javascript",
|
|
53
|
+
intent: "warning",
|
|
54
|
+
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import React, { useRef } from "react";
|
|
1
|
+
import React, { useMemo, useRef } from "react";
|
|
2
2
|
import { defaultKeymap, indentWithTab } from "@codemirror/commands";
|
|
3
3
|
import { foldKeymap } from "@codemirror/language";
|
|
4
|
+
import { lintGutter } from "@codemirror/lint";
|
|
4
5
|
import { EditorState, Extension } from "@codemirror/state";
|
|
5
6
|
import { DOMEventHandlers, EditorView, KeyBinding, keymap, Rect, ViewUpdate } from "@codemirror/view";
|
|
6
7
|
import { minimalSetup } from "codemirror";
|
|
7
8
|
|
|
9
|
+
import { IntentTypes } from "../../common/Intent";
|
|
8
10
|
import { markField } from "../../components/AutoSuggestion/extensions/markText";
|
|
11
|
+
import { TestableComponent } from "../../components/interfaces";
|
|
9
12
|
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
|
|
10
13
|
|
|
11
14
|
//hooks
|
|
@@ -14,6 +17,8 @@ import {
|
|
|
14
17
|
supportedCodeEditorModes,
|
|
15
18
|
useCodeMirrorModeExtension,
|
|
16
19
|
} from "./hooks/useCodemirrorModeExtension.hooks";
|
|
20
|
+
import { jsLinter } from "./linters/jsLinter";
|
|
21
|
+
import { turtleLinter } from "./linters/turtleLinter";
|
|
17
22
|
//adaptations
|
|
18
23
|
import {
|
|
19
24
|
adaptedCodeFolding,
|
|
@@ -25,10 +30,11 @@ import {
|
|
|
25
30
|
adaptedLineNumbers,
|
|
26
31
|
adaptedPlaceholder,
|
|
27
32
|
} from "./tests/codemirrorTestHelper";
|
|
33
|
+
import { ExtensionCreator } from "./types";
|
|
28
34
|
|
|
29
|
-
export interface CodeEditorProps {
|
|
35
|
+
export interface CodeEditorProps extends TestableComponent {
|
|
30
36
|
// Is called with the editor instance that allows access via the CodeMirror API
|
|
31
|
-
setEditorView?: (editor: EditorView | undefined) =>
|
|
37
|
+
setEditorView?: (editor: EditorView | undefined) => void;
|
|
32
38
|
/**
|
|
33
39
|
* `name` attribute of connected textarea element.
|
|
34
40
|
*/
|
|
@@ -50,7 +56,7 @@ export interface CodeEditorProps {
|
|
|
50
56
|
/**
|
|
51
57
|
* Called when the focus status changes
|
|
52
58
|
*/
|
|
53
|
-
onFocusChange?: (focused: boolean) =>
|
|
59
|
+
onFocusChange?: (focused: boolean) => void;
|
|
54
60
|
/**
|
|
55
61
|
* Called when the user presses a key
|
|
56
62
|
*/
|
|
@@ -62,7 +68,7 @@ export interface CodeEditorProps {
|
|
|
62
68
|
/**
|
|
63
69
|
* Called when the user selects text
|
|
64
70
|
*/
|
|
65
|
-
onSelection?: (ranges: { from: number; to: number }[]) =>
|
|
71
|
+
onSelection?: (ranges: { from: number; to: number }[]) => void;
|
|
66
72
|
/**
|
|
67
73
|
* Called when the cursor position changes
|
|
68
74
|
*/
|
|
@@ -133,6 +139,23 @@ export interface CodeEditorProps {
|
|
|
133
139
|
* If the <Tab> key is enabled as normal input, i.e. it won't have the behavior of changing to the next input element, expected in a web app.
|
|
134
140
|
*/
|
|
135
141
|
enableTab?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Enables linting feature in the editor ("turtle" and "javascript" modes can use linting currently).
|
|
144
|
+
*/
|
|
145
|
+
useLinting?: boolean;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Autofocus the editor when it is rendered
|
|
149
|
+
*/
|
|
150
|
+
autoFocus?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Intent state of the code editor.
|
|
153
|
+
*/
|
|
154
|
+
intent?: IntentTypes | "edited" | "removed";
|
|
155
|
+
/**
|
|
156
|
+
* Disables the editor.
|
|
157
|
+
*/
|
|
158
|
+
disabled?: boolean;
|
|
136
159
|
}
|
|
137
160
|
|
|
138
161
|
const addExtensionsFor = (flag: boolean, ...extensions: Extension[]) => (flag ? [...extensions] : []);
|
|
@@ -140,6 +163,11 @@ const addToKeyMapConfigFor = (flag: boolean, ...keys: any) => (flag ? [...keys]
|
|
|
140
163
|
const addHandlersFor = (flag: boolean, handlerName: string, handler: any) =>
|
|
141
164
|
flag ? ({ [handlerName]: handler } as DOMEventHandlers<any>) : {};
|
|
142
165
|
|
|
166
|
+
const ModeLinterMap: ReadonlyMap<SupportedCodeEditorModes, ReadonlyArray<ExtensionCreator>> = new Map([
|
|
167
|
+
["turtle", [turtleLinter]],
|
|
168
|
+
["javascript", [jsLinter]],
|
|
169
|
+
]);
|
|
170
|
+
|
|
143
171
|
/**
|
|
144
172
|
* Includes a code editor, currently we use CodeMirror library as base.
|
|
145
173
|
*/
|
|
@@ -170,9 +198,30 @@ export const CodeEditor = ({
|
|
|
170
198
|
tabForceSpaceForModes = ["python", "yaml"],
|
|
171
199
|
enableTab = false,
|
|
172
200
|
height,
|
|
201
|
+
useLinting = false,
|
|
202
|
+
"data-test-id": dataTestId,
|
|
203
|
+
autoFocus = false,
|
|
204
|
+
disabled = false,
|
|
205
|
+
intent,
|
|
206
|
+
...otherCodeEditorProps
|
|
173
207
|
}: CodeEditorProps) => {
|
|
174
208
|
const parent = useRef<any>(undefined);
|
|
175
209
|
|
|
210
|
+
const linters = useMemo(() => {
|
|
211
|
+
if (!mode) {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const values = [lintGutter()];
|
|
216
|
+
|
|
217
|
+
const linters = ModeLinterMap.get(mode);
|
|
218
|
+
if (linters) {
|
|
219
|
+
values.push(...linters.map((linter) => linter()));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return values;
|
|
223
|
+
}, [mode]);
|
|
224
|
+
|
|
176
225
|
const onKeyDownHandler = (event: KeyboardEvent, view: EditorView) => {
|
|
177
226
|
if (onKeyDown && !onKeyDown(event)) {
|
|
178
227
|
if (event.key === "Enter") {
|
|
@@ -219,13 +268,22 @@ export const CodeEditor = ({
|
|
|
219
268
|
keymap?.of(keyMapConfigs),
|
|
220
269
|
EditorState?.tabSize.of(tabIntentSize),
|
|
221
270
|
EditorState?.readOnly.of(readOnly),
|
|
271
|
+
EditorView?.editable.of(!disabled),
|
|
222
272
|
AdaptedEditorViewDomEventHandlers(domEventHandlers) as Extension,
|
|
223
273
|
EditorView?.updateListener.of((v: ViewUpdate) => {
|
|
224
|
-
|
|
274
|
+
if (disabled) return;
|
|
275
|
+
|
|
276
|
+
if (onChange) {
|
|
277
|
+
onChange(v.state.doc.toString());
|
|
278
|
+
}
|
|
225
279
|
|
|
226
280
|
if (onSelection)
|
|
227
281
|
onSelection(v.state.selection.ranges.filter((r) => !r.empty).map(({ from, to }) => ({ from, to })));
|
|
228
282
|
|
|
283
|
+
if (onFocusChange) {
|
|
284
|
+
v.view.dom.className += ` ${eccgui}-intent--${intent}`;
|
|
285
|
+
}
|
|
286
|
+
|
|
229
287
|
if (onCursorChange) {
|
|
230
288
|
const cursorPosition = v.state.selection.main.head ?? 0;
|
|
231
289
|
const editorRect = v.view.dom.getBoundingClientRect();
|
|
@@ -250,6 +308,7 @@ export const CodeEditor = ({
|
|
|
250
308
|
addExtensionsFor(shouldHighlightActiveLine, adaptedHighlightActiveLine()),
|
|
251
309
|
addExtensionsFor(wrapLines, EditorView?.lineWrapping),
|
|
252
310
|
addExtensionsFor(supportCodeFolding, adaptedFoldGutter(), adaptedCodeFolding()),
|
|
311
|
+
addExtensionsFor(useLinting, ...linters),
|
|
253
312
|
additionalExtensions,
|
|
254
313
|
];
|
|
255
314
|
|
|
@@ -265,11 +324,27 @@ export const CodeEditor = ({
|
|
|
265
324
|
view.dom.style.height = typeof height === "string" ? height : `${height}px`;
|
|
266
325
|
}
|
|
267
326
|
|
|
268
|
-
|
|
327
|
+
if (disabled) {
|
|
328
|
+
view.dom.className += ` ${eccgui}-disabled`;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (intent) {
|
|
332
|
+
view.dom.className += ` ${eccgui}-intent--${intent}`;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (autoFocus) {
|
|
336
|
+
view.focus();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (setEditorView) {
|
|
340
|
+
setEditorView(view);
|
|
341
|
+
}
|
|
269
342
|
|
|
270
343
|
return () => {
|
|
271
344
|
view.destroy();
|
|
272
|
-
|
|
345
|
+
if (setEditorView) {
|
|
346
|
+
setEditorView(undefined);
|
|
347
|
+
}
|
|
273
348
|
};
|
|
274
349
|
}, [parent.current, mode, preventLineNumbers]);
|
|
275
350
|
|
|
@@ -279,11 +354,13 @@ export const CodeEditor = ({
|
|
|
279
354
|
// overwrite/extend some attributes
|
|
280
355
|
id={id ? id : name ? `codemirror-${name}` : undefined}
|
|
281
356
|
ref={parent}
|
|
282
|
-
|
|
357
|
+
// @deprecated (v25) fallback with static test id will be removed
|
|
358
|
+
data-test-id={dataTestId ? dataTestId : "codemirror-wrapper"}
|
|
283
359
|
className={
|
|
284
360
|
`${eccgui}-codeeditor ${eccgui}-codeeditor--mode-${mode}` +
|
|
285
361
|
(outerDivAttributes?.className ? ` ${outerDivAttributes?.className}` : "")
|
|
286
362
|
}
|
|
363
|
+
{...otherCodeEditorProps}
|
|
287
364
|
/>
|
|
288
365
|
);
|
|
289
366
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@use "sass:color";
|
|
2
|
+
|
|
1
3
|
// own vars
|
|
2
4
|
$eccgui-color-codeeditor-background: $eccgui-color-textfield-background !default;
|
|
3
5
|
|
|
@@ -22,6 +24,63 @@ $eccgui-color-codeeditor-background: $eccgui-color-textfield-background !default
|
|
|
22
24
|
// get them a "border" like input boxes from blueprintjs
|
|
23
25
|
box-shadow: input-transition-shadow($input-shadow-color-focus), $pt-input-box-shadow;
|
|
24
26
|
|
|
27
|
+
&.#{eccgui}-disabled {
|
|
28
|
+
@extend .#{$ns}-input, .#{$ns}-disabled;
|
|
29
|
+
|
|
30
|
+
height: 290px;
|
|
31
|
+
padding: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&[class*="#{$eccgui}-intent--"] {
|
|
35
|
+
animation-duration: 1s;
|
|
36
|
+
animation-delay: 0.5s;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@each $each-intent, $each-bgcolor in $eccgui-map-intent-bgcolors {
|
|
40
|
+
&.#{eccgui}-intent--#{$each-intent} {
|
|
41
|
+
background-color: color.mix($each-bgcolor, $eccgui-color-textfield-background, 24%);
|
|
42
|
+
animation-name: intent-state-flash-#{$each-intent};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&.#{eccgui}-intent--warning {
|
|
47
|
+
@include pt-input-intent($eccgui-color-warning-text);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&.#{eccgui}-intent--success {
|
|
51
|
+
@include pt-input-intent($eccgui-color-success-text);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&.#{eccgui}-intent--danger {
|
|
55
|
+
@include pt-input-intent($eccgui-color-danger-text);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&.#{eccgui}-intent--primary {
|
|
59
|
+
@include pt-input-intent($eccgui-color-info-text);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&.#{eccgui}-intent--info {
|
|
63
|
+
@include pt-input-intent($eccgui-color-info-text);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&.#{eccgui}-intent--accent {
|
|
67
|
+
@include pt-input-intent($eccgui-color-primary);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&.#{eccgui}-intent--neutral {
|
|
71
|
+
@include pt-input-intent($eccgui-color-workspace-text);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&.#{eccgui}-intent--edited {
|
|
75
|
+
@include pt-input-intent($eccgui-color-info-text);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&.#{eccgui}-intent--removed {
|
|
79
|
+
@include pt-input-intent($eccgui-color-danger-text);
|
|
80
|
+
|
|
81
|
+
text-decoration: line-through $eccgui-color-danger-text 2px;
|
|
82
|
+
}
|
|
83
|
+
|
|
25
84
|
.cm-scroller {
|
|
26
85
|
width: calc(100% - 2px);
|
|
27
86
|
height: calc(100% - 2px);
|
|
@@ -34,6 +93,43 @@ $eccgui-color-codeeditor-background: $eccgui-color-textfield-background !default
|
|
|
34
93
|
&.cm-focused {
|
|
35
94
|
outline: none;
|
|
36
95
|
box-shadow: input-transition-shadow($input-shadow-color-focus, true), $input-box-shadow-focus;
|
|
96
|
+
|
|
97
|
+
&.#{eccgui}-intent--warning {
|
|
98
|
+
box-shadow: input-transition-shadow($eccgui-color-warning-text, true), $input-box-shadow-focus;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
&.#{eccgui}-intent--success {
|
|
102
|
+
box-shadow: input-transition-shadow($eccgui-color-success-text, true), $input-box-shadow-focus;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
&.#{eccgui}-intent--danger {
|
|
106
|
+
box-shadow: input-transition-shadow($eccgui-color-danger-text, true), $input-box-shadow-focus;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
&.#{eccgui}-intent--primary {
|
|
110
|
+
box-shadow: input-transition-shadow($eccgui-color-info-text, true), $input-box-shadow-focus;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&.#{eccgui}-intent--info {
|
|
114
|
+
box-shadow: input-transition-shadow($eccgui-color-info-text, true), $input-box-shadow-focus;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
&.#{eccgui}-intent--accent {
|
|
118
|
+
box-shadow: input-transition-shadow($eccgui-color-warning-text, true), $input-box-shadow-focus;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
&.#{eccgui}-intent--neutral {
|
|
122
|
+
box-shadow: input-transition-shadow($eccgui-color-workspace-text, true), $input-box-shadow-focus;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&.#{eccgui}-intent--edited {
|
|
126
|
+
box-shadow: input-transition-shadow($eccgui-color-info-text, true), $input-box-shadow-focus;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&.#{eccgui}-intent--removed {
|
|
130
|
+
text-decoration: line-through $eccgui-color-danger-text 2px;
|
|
131
|
+
box-shadow: input-transition-shadow($eccgui-color-danger-text, true), $input-box-shadow-focus;
|
|
132
|
+
}
|
|
37
133
|
}
|
|
38
134
|
|
|
39
135
|
.CodeMirror-hscrollbar {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Diagnostic } from "@codemirror/lint";
|
|
2
|
+
import { EditorView } from "@codemirror/view";
|
|
3
|
+
import { debounce } from "lodash";
|
|
4
|
+
|
|
5
|
+
import { Linter } from "./types";
|
|
6
|
+
|
|
7
|
+
const DEBOUNCE_TIME = 500;
|
|
8
|
+
|
|
9
|
+
export const debouncedLinter = (lintFunction: Linter, time = DEBOUNCE_TIME) => {
|
|
10
|
+
const debouncedFn = debounce(
|
|
11
|
+
(
|
|
12
|
+
view: EditorView,
|
|
13
|
+
resolve: (diagnostics: ReadonlyArray<Diagnostic> | Promise<ReadonlyArray<Diagnostic>>) => void
|
|
14
|
+
) => {
|
|
15
|
+
const diagnostics = lintFunction(view);
|
|
16
|
+
resolve(diagnostics);
|
|
17
|
+
},
|
|
18
|
+
time
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return (view: EditorView) => {
|
|
22
|
+
return new Promise<ReadonlyArray<Diagnostic>>((resolve) => {
|
|
23
|
+
debouncedFn(view, resolve);
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
};
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
//adapted v6 modes imports
|
|
2
|
-
import { javascript } from "@codemirror/lang-javascript";
|
|
3
1
|
import { json } from "@codemirror/lang-json";
|
|
2
|
+
//modes imports
|
|
4
3
|
import { markdown } from "@codemirror/lang-markdown";
|
|
5
|
-
import { sql } from "@codemirror/lang-sql";
|
|
6
4
|
import { xml } from "@codemirror/lang-xml";
|
|
7
|
-
import { yaml } from "@codemirror/lang-yaml";
|
|
8
5
|
import { defaultHighlightStyle, LanguageSupport, StreamLanguage, StreamParser } from "@codemirror/language";
|
|
9
|
-
|
|
6
|
+
import { javascript } from "@codemirror/legacy-modes/mode/javascript";
|
|
10
7
|
import { jinja2 } from "@codemirror/legacy-modes/mode/jinja2";
|
|
11
8
|
import { mathematica } from "@codemirror/legacy-modes/mode/mathematica";
|
|
12
9
|
import { ntriples } from "@codemirror/legacy-modes/mode/ntriples";
|
|
13
10
|
import { python } from "@codemirror/legacy-modes/mode/python";
|
|
14
11
|
import { sparql } from "@codemirror/legacy-modes/mode/sparql";
|
|
12
|
+
import { sql } from "@codemirror/legacy-modes/mode/sql";
|
|
15
13
|
import { turtle } from "@codemirror/legacy-modes/mode/turtle";
|
|
14
|
+
import { yaml } from "@codemirror/legacy-modes/mode/yaml";
|
|
16
15
|
|
|
17
16
|
//adaptations
|
|
18
17
|
import { adaptedSyntaxHighlighting } from "../tests/codemirrorTestHelper";
|
|
@@ -35,19 +34,10 @@ const supportedModes = {
|
|
|
35
34
|
export const supportedCodeEditorModes = Object.keys(supportedModes) as Array<keyof typeof supportedModes>;
|
|
36
35
|
export type SupportedCodeEditorModes = (typeof supportedCodeEditorModes)[number];
|
|
37
36
|
|
|
38
|
-
const v6AdaptedModes: ReadonlyMap<SupportedCodeEditorModes, boolean> = new Map([
|
|
39
|
-
["json", true],
|
|
40
|
-
["markdown", true],
|
|
41
|
-
["xml", true],
|
|
42
|
-
["sql", true],
|
|
43
|
-
["yaml", true],
|
|
44
|
-
["javascript", true],
|
|
45
|
-
]);
|
|
46
|
-
|
|
47
37
|
export const useCodeMirrorModeExtension = (mode?: SupportedCodeEditorModes) => {
|
|
48
38
|
return !mode
|
|
49
39
|
? adaptedSyntaxHighlighting(defaultHighlightStyle)
|
|
50
|
-
:
|
|
51
|
-
? ((typeof supportedModes[mode] === "function" ? supportedModes[mode] : () =>
|
|
40
|
+
: ["json", "markdown", "xml"].includes(mode)
|
|
41
|
+
? ((typeof supportedModes[mode] === "function" ? supportedModes[mode] : () => null) as () => LanguageSupport)()
|
|
52
42
|
: StreamLanguage?.define(supportedModes[mode] as StreamParser<unknown>);
|
|
53
43
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Diagnostic, linter } from "@codemirror/lint";
|
|
2
|
+
import { JSHINT as jshint } from "jshint";
|
|
3
|
+
|
|
4
|
+
import { ExtensionCreator } from "../types";
|
|
5
|
+
|
|
6
|
+
const lintOptions = {
|
|
7
|
+
esversion: 11,
|
|
8
|
+
browser: true,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sets up the javascript linter. Documentation: https://codemirror.net/examples/lint/
|
|
13
|
+
*/
|
|
14
|
+
export const jsLinter: ExtensionCreator = () => {
|
|
15
|
+
return linter((view) => {
|
|
16
|
+
const diagnostics: Array<Diagnostic> = [];
|
|
17
|
+
const codeText = view.state.doc.toJSON();
|
|
18
|
+
jshint(codeText, lintOptions);
|
|
19
|
+
const errors = jshint?.data()?.errors;
|
|
20
|
+
|
|
21
|
+
if (errors && errors.length > 0) {
|
|
22
|
+
errors.forEach((error) => {
|
|
23
|
+
const selectedLine = view.state.doc.line(error.line);
|
|
24
|
+
|
|
25
|
+
const diagnostic: Diagnostic = {
|
|
26
|
+
from: selectedLine.from,
|
|
27
|
+
to: selectedLine.to,
|
|
28
|
+
severity: "error",
|
|
29
|
+
message: error.reason,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
diagnostics.push(diagnostic);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return diagnostics;
|
|
37
|
+
});
|
|
38
|
+
};
|