@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.
Files changed (53) hide show
  1. package/CHANGELOG.md +7 -5
  2. package/dist/cjs/cmem/react-flow/StickyNoteModal/StickyNoteModal.js +3 -3
  3. package/dist/cjs/cmem/react-flow/StickyNoteModal/StickyNoteModal.js.map +1 -1
  4. package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js +3 -3
  5. package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -1
  6. package/dist/cjs/extensions/codemirror/CodeMirror.js +58 -6
  7. package/dist/cjs/extensions/codemirror/CodeMirror.js.map +1 -1
  8. package/dist/cjs/extensions/codemirror/debouncedLinter.js +18 -0
  9. package/dist/cjs/extensions/codemirror/debouncedLinter.js.map +1 -0
  10. package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +9 -18
  11. package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
  12. package/dist/cjs/extensions/codemirror/linters/jsLinter.js +36 -0
  13. package/dist/cjs/extensions/codemirror/linters/jsLinter.js.map +1 -0
  14. package/dist/cjs/extensions/codemirror/linters/turtleLinter.js +81 -0
  15. package/dist/cjs/extensions/codemirror/linters/turtleLinter.js.map +1 -0
  16. package/dist/cjs/extensions/codemirror/types.js +3 -0
  17. package/dist/cjs/extensions/codemirror/types.js.map +1 -0
  18. package/dist/esm/cmem/react-flow/StickyNoteModal/StickyNoteModal.js +4 -4
  19. package/dist/esm/cmem/react-flow/StickyNoteModal/StickyNoteModal.js.map +1 -1
  20. package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js +14 -3
  21. package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -1
  22. package/dist/esm/extensions/codemirror/CodeMirror.js +58 -7
  23. package/dist/esm/extensions/codemirror/CodeMirror.js.map +1 -1
  24. package/dist/esm/extensions/codemirror/debouncedLinter.js +15 -0
  25. package/dist/esm/extensions/codemirror/debouncedLinter.js.map +1 -0
  26. package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +6 -15
  27. package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
  28. package/dist/esm/extensions/codemirror/linters/jsLinter.js +32 -0
  29. package/dist/esm/extensions/codemirror/linters/jsLinter.js.map +1 -0
  30. package/dist/esm/extensions/codemirror/linters/turtleLinter.js +77 -0
  31. package/dist/esm/extensions/codemirror/linters/turtleLinter.js.map +1 -0
  32. package/dist/esm/extensions/codemirror/types.js +2 -0
  33. package/dist/esm/extensions/codemirror/types.js.map +1 -0
  34. package/dist/types/cmem/react-flow/StickyNoteModal/StickyNoteModal.d.ts +5 -1
  35. package/dist/types/components/AutoSuggestion/ExtendedCodeEditor.d.ts +11 -6
  36. package/dist/types/extensions/codemirror/CodeMirror.d.ts +23 -5
  37. package/dist/types/extensions/codemirror/debouncedLinter.d.ts +4 -0
  38. package/dist/types/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.d.ts +3 -5
  39. package/dist/types/extensions/codemirror/linters/jsLinter.d.ts +5 -0
  40. package/dist/types/extensions/codemirror/linters/turtleLinter.d.ts +5 -0
  41. package/dist/types/extensions/codemirror/types.d.ts +5 -0
  42. package/package.json +6 -5
  43. package/src/cmem/react-flow/StickyNoteModal/StickyNoteModal.tsx +16 -2
  44. package/src/components/AutoSuggestion/ExtendedCodeEditor.tsx +29 -6
  45. package/src/components/TextField/stories/TextField.stories.tsx +2 -1
  46. package/src/extensions/codemirror/CodeMirror.stories.tsx +30 -0
  47. package/src/extensions/codemirror/CodeMirror.tsx +86 -9
  48. package/src/extensions/codemirror/_codemirror.scss +96 -0
  49. package/src/extensions/codemirror/debouncedLinter.ts +26 -0
  50. package/src/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.ts +6 -16
  51. package/src/extensions/codemirror/linters/jsLinter.ts +38 -0
  52. package/src/extensions/codemirror/linters/turtleLinter.ts +102 -0
  53. 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 { Button, FieldItem, Icon, SimpleDialog, SimpleDialogProps, Tag, TagList } from "./../../../index";
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) => any;
18
+ setCM: (editor: EditorView | undefined) => void;
19
19
  // Called whenever the editor content changes
20
- onChange: (value: string) => any;
20
+ onChange: (value: string) => void;
21
21
  // Called when the cursor position changes
22
- onCursorChange: (pos: number, coords: Rect, scrollinfo: HTMLElement, cm: EditorView) => any;
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) => any;
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[]) => any;
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.fullLabel;
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) => any;
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) => any;
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 }[]) => any;
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
- onChange && onChange(v.state.doc.toString());
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
- setEditorView && setEditorView(view);
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
- setEditorView && setEditorView(undefined);
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
- data-test-id="codemirror-wrapper"
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
- //legacy mode imports
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
- : v6AdaptedModes.has(mode)
51
- ? ((typeof supportedModes[mode] === "function" ? supportedModes[mode] : () => {}) as () => LanguageSupport)()
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
+ };