@eccenca/gui-elements 24.0.1 → 24.1.0-featureimprovepublishingprocesscmem6356.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 (208) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js +7 -2
  3. package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
  4. package/dist/cjs/cmem/react-flow/StickyNoteModal/StickyNoteModal.js +3 -3
  5. package/dist/cjs/cmem/react-flow/StickyNoteModal/StickyNoteModal.js.map +1 -1
  6. package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +13 -3
  7. package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
  8. package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js +3 -3
  9. package/dist/cjs/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -1
  10. package/dist/cjs/components/AutocompleteField/AutoCompleteField.js +4 -2
  11. package/dist/cjs/components/AutocompleteField/AutoCompleteField.js.map +1 -1
  12. package/dist/cjs/components/Card/CardActions.js +2 -1
  13. package/dist/cjs/components/Card/CardActions.js.map +1 -1
  14. package/dist/cjs/components/Card/CardContent.js +4 -6
  15. package/dist/cjs/components/Card/CardContent.js.map +1 -1
  16. package/dist/cjs/components/ContentGroup/ContentGroup.js +95 -0
  17. package/dist/cjs/components/ContentGroup/ContentGroup.js.map +1 -0
  18. package/dist/cjs/components/Dialog/SimpleDialog.js +3 -3
  19. package/dist/cjs/components/Dialog/SimpleDialog.js.map +1 -1
  20. package/dist/cjs/components/Icon/canonicalIconNames.js +12 -0
  21. package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
  22. package/dist/cjs/components/Label/Label.js +8 -3
  23. package/dist/cjs/components/Label/Label.js.map +1 -1
  24. package/dist/cjs/components/Menu/MenuItem.js +3 -2
  25. package/dist/cjs/components/Menu/MenuItem.js.map +1 -1
  26. package/dist/cjs/components/OverviewItem/OverviewItem.js +5 -2
  27. package/dist/cjs/components/OverviewItem/OverviewItem.js.map +1 -1
  28. package/dist/cjs/components/OverviewItem/OverviewItemList.js +2 -2
  29. package/dist/cjs/components/OverviewItem/OverviewItemList.js.map +1 -1
  30. package/dist/cjs/components/Switch/Switch.js +6 -4
  31. package/dist/cjs/components/Switch/Switch.js.map +1 -1
  32. package/dist/cjs/components/Tag/TagList.js +1 -1
  33. package/dist/cjs/components/Tag/TagList.js.map +1 -1
  34. package/dist/cjs/components/TextField/SearchField.js +19 -2
  35. package/dist/cjs/components/TextField/SearchField.js.map +1 -1
  36. package/dist/cjs/components/Typography/OverflowText.js +1 -1
  37. package/dist/cjs/components/Typography/OverflowText.js.map +1 -1
  38. package/dist/cjs/components/index.js +1 -0
  39. package/dist/cjs/components/index.js.map +1 -1
  40. package/dist/cjs/extensions/codemirror/CodeMirror.js +93 -11
  41. package/dist/cjs/extensions/codemirror/CodeMirror.js.map +1 -1
  42. package/dist/cjs/extensions/codemirror/debouncedLinter.js +18 -0
  43. package/dist/cjs/extensions/codemirror/debouncedLinter.js.map +1 -0
  44. package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +1 -1
  45. package/dist/cjs/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
  46. package/dist/cjs/extensions/codemirror/linters/jsLinter.js +36 -0
  47. package/dist/cjs/extensions/codemirror/linters/jsLinter.js.map +1 -0
  48. package/dist/cjs/extensions/codemirror/linters/turtleLinter.js +81 -0
  49. package/dist/cjs/extensions/codemirror/linters/turtleLinter.js.map +1 -0
  50. package/dist/cjs/extensions/codemirror/tests/codemirrorTestHelper.js +4 -1
  51. package/dist/cjs/extensions/codemirror/tests/codemirrorTestHelper.js.map +1 -1
  52. package/dist/cjs/extensions/codemirror/toolbars/commands/markdown.command.js +278 -0
  53. package/dist/cjs/extensions/codemirror/toolbars/commands/markdown.command.js.map +1 -0
  54. package/dist/cjs/extensions/codemirror/toolbars/markdown.toolbar.js +47 -0
  55. package/dist/cjs/extensions/codemirror/toolbars/markdown.toolbar.js.map +1 -0
  56. package/dist/cjs/extensions/codemirror/types.js +3 -0
  57. package/dist/cjs/extensions/codemirror/types.js.map +1 -0
  58. package/dist/cjs/extensions/react-flow/nodes/NodeContent.js +140 -41
  59. package/dist/cjs/extensions/react-flow/nodes/NodeContent.js.map +1 -1
  60. package/dist/cjs/extensions/react-flow/nodes/nodeUtils.js +5 -6
  61. package/dist/cjs/extensions/react-flow/nodes/nodeUtils.js.map +1 -1
  62. package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js +7 -2
  63. package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
  64. package/dist/esm/cmem/react-flow/StickyNoteModal/StickyNoteModal.js +4 -4
  65. package/dist/esm/cmem/react-flow/StickyNoteModal/StickyNoteModal.js.map +1 -1
  66. package/dist/esm/components/AutoSuggestion/AutoSuggestion.js +13 -3
  67. package/dist/esm/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
  68. package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js +14 -3
  69. package/dist/esm/components/AutoSuggestion/ExtendedCodeEditor.js.map +1 -1
  70. package/dist/esm/components/AutocompleteField/AutoCompleteField.js +4 -3
  71. package/dist/esm/components/AutocompleteField/AutoCompleteField.js.map +1 -1
  72. package/dist/esm/components/Card/CardActions.js +2 -1
  73. package/dist/esm/components/Card/CardActions.js.map +1 -1
  74. package/dist/esm/components/Card/CardContent.js +4 -5
  75. package/dist/esm/components/Card/CardContent.js.map +1 -1
  76. package/dist/esm/components/ContentGroup/ContentGroup.js +100 -0
  77. package/dist/esm/components/ContentGroup/ContentGroup.js.map +1 -0
  78. package/dist/esm/components/Dialog/SimpleDialog.js +4 -4
  79. package/dist/esm/components/Dialog/SimpleDialog.js.map +1 -1
  80. package/dist/esm/components/Icon/canonicalIconNames.js +12 -0
  81. package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
  82. package/dist/esm/components/Label/Label.js +8 -3
  83. package/dist/esm/components/Label/Label.js.map +1 -1
  84. package/dist/esm/components/Menu/MenuItem.js +3 -2
  85. package/dist/esm/components/Menu/MenuItem.js.map +1 -1
  86. package/dist/esm/components/OverviewItem/OverviewItem.js +5 -2
  87. package/dist/esm/components/OverviewItem/OverviewItem.js.map +1 -1
  88. package/dist/esm/components/OverviewItem/OverviewItemList.js +2 -2
  89. package/dist/esm/components/OverviewItem/OverviewItemList.js.map +1 -1
  90. package/dist/esm/components/Switch/Switch.js +7 -5
  91. package/dist/esm/components/Switch/Switch.js.map +1 -1
  92. package/dist/esm/components/Tag/TagList.js +1 -1
  93. package/dist/esm/components/Tag/TagList.js.map +1 -1
  94. package/dist/esm/components/TextField/SearchField.js +35 -2
  95. package/dist/esm/components/TextField/SearchField.js.map +1 -1
  96. package/dist/esm/components/Typography/OverflowText.js +1 -1
  97. package/dist/esm/components/Typography/OverflowText.js.map +1 -1
  98. package/dist/esm/components/index.js +1 -0
  99. package/dist/esm/components/index.js.map +1 -1
  100. package/dist/esm/extensions/codemirror/CodeMirror.js +94 -13
  101. package/dist/esm/extensions/codemirror/CodeMirror.js.map +1 -1
  102. package/dist/esm/extensions/codemirror/debouncedLinter.js +15 -0
  103. package/dist/esm/extensions/codemirror/debouncedLinter.js.map +1 -0
  104. package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js +1 -1
  105. package/dist/esm/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.js.map +1 -1
  106. package/dist/esm/extensions/codemirror/linters/jsLinter.js +32 -0
  107. package/dist/esm/extensions/codemirror/linters/jsLinter.js.map +1 -0
  108. package/dist/esm/extensions/codemirror/linters/turtleLinter.js +77 -0
  109. package/dist/esm/extensions/codemirror/linters/turtleLinter.js.map +1 -0
  110. package/dist/esm/extensions/codemirror/tests/codemirrorTestHelper.js +4 -0
  111. package/dist/esm/extensions/codemirror/tests/codemirrorTestHelper.js.map +1 -1
  112. package/dist/esm/extensions/codemirror/toolbars/commands/markdown.command.js +283 -0
  113. package/dist/esm/extensions/codemirror/toolbars/commands/markdown.command.js.map +1 -0
  114. package/dist/esm/extensions/codemirror/toolbars/markdown.toolbar.js +41 -0
  115. package/dist/esm/extensions/codemirror/toolbars/markdown.toolbar.js.map +1 -0
  116. package/dist/esm/extensions/codemirror/types.js +2 -0
  117. package/dist/esm/extensions/codemirror/types.js.map +1 -0
  118. package/dist/esm/extensions/react-flow/nodes/NodeContent.js +149 -48
  119. package/dist/esm/extensions/react-flow/nodes/NodeContent.js.map +1 -1
  120. package/dist/esm/extensions/react-flow/nodes/nodeUtils.js +5 -6
  121. package/dist/esm/extensions/react-flow/nodes/nodeUtils.js.map +1 -1
  122. package/dist/types/cmem/ActivityControl/ActivityControlWidget.d.ts +1 -1
  123. package/dist/types/cmem/react-flow/StickyNoteModal/StickyNoteModal.d.ts +5 -1
  124. package/dist/types/components/AutoSuggestion/ExtendedCodeEditor.d.ts +11 -6
  125. package/dist/types/components/Card/CardActions.d.ts +5 -1
  126. package/dist/types/components/Card/CardContent.d.ts +1 -2
  127. package/dist/types/components/ContentGroup/ContentGroup.d.ts +78 -0
  128. package/dist/types/components/Dialog/SimpleDialog.d.ts +4 -1
  129. package/dist/types/components/Icon/canonicalIconNames.d.ts +12 -0
  130. package/dist/types/components/Label/Label.d.ts +7 -1
  131. package/dist/types/components/Menu/MenuItem.d.ts +8 -1
  132. package/dist/types/components/OverviewItem/OverviewItem.d.ts +13 -1
  133. package/dist/types/components/OverviewItem/OverviewItemList.d.ts +3 -2
  134. package/dist/types/components/ProgressBar/ProgressBar.d.ts +2 -2
  135. package/dist/types/components/Structure/TitleSubsection.d.ts +7 -0
  136. package/dist/types/components/Switch/Switch.d.ts +3 -3
  137. package/dist/types/components/Tabs/Tab.d.ts +14 -0
  138. package/dist/types/components/TextField/SearchField.d.ts +1 -1
  139. package/dist/types/components/Typography/OverflowText.d.ts +23 -2
  140. package/dist/types/components/index.d.ts +1 -0
  141. package/dist/types/extensions/codemirror/CodeMirror.d.ts +32 -5
  142. package/dist/types/extensions/codemirror/debouncedLinter.d.ts +4 -0
  143. package/dist/types/extensions/codemirror/linters/jsLinter.d.ts +5 -0
  144. package/dist/types/extensions/codemirror/linters/turtleLinter.d.ts +5 -0
  145. package/dist/types/extensions/codemirror/tests/codemirrorTestHelper.d.ts +1 -0
  146. package/dist/types/extensions/codemirror/toolbars/commands/markdown.command.d.ts +55 -0
  147. package/dist/types/extensions/codemirror/toolbars/markdown.toolbar.d.ts +12 -0
  148. package/dist/types/extensions/codemirror/types.d.ts +5 -0
  149. package/dist/types/extensions/react-flow/nodes/NodeContent.d.ts +18 -4
  150. package/dist/types/extensions/react-flow/nodes/nodeUtils.d.ts +7 -6
  151. package/package.json +50 -47
  152. package/src/cmem/ActivityControl/ActivityControlWidget.tsx +5 -2
  153. package/src/cmem/react-flow/StickyNoteModal/StickyNoteModal.tsx +16 -2
  154. package/src/cmem/react-flow/configuration/_colors-graph.scss +4 -1
  155. package/src/cmem/react-flow/configuration/_colors-workflow.scss +3 -0
  156. package/src/components/AutoSuggestion/AutoSuggestion.tsx +14 -3
  157. package/src/components/AutoSuggestion/ExtendedCodeEditor.tsx +29 -6
  158. package/src/components/AutocompleteField/AutoCompleteField.tsx +5 -3
  159. package/src/components/Card/CardActions.tsx +6 -0
  160. package/src/components/Card/CardContent.tsx +8 -4
  161. package/src/components/Card/card.scss +15 -0
  162. package/src/components/CodeAutocompleteField/CodeAutocompleteField.stories.tsx +3 -2
  163. package/src/components/ContentGroup/ContentGroup.stories.tsx +47 -0
  164. package/src/components/ContentGroup/ContentGroup.tsx +256 -0
  165. package/src/components/ContentGroup/_contentgroup.scss +56 -0
  166. package/src/components/ContextOverlay/ContextOverlay.stories.tsx +15 -4
  167. package/src/components/Depiction/depiction.scss +7 -0
  168. package/src/components/Dialog/SimpleDialog.tsx +9 -2
  169. package/src/components/Dialog/stories/AlertDialog.stories.tsx +5 -1
  170. package/src/components/Dialog/stories/Modal.stories.tsx +4 -2
  171. package/src/components/Dialog/stories/SimpleDialog.stories.tsx +5 -2
  172. package/src/components/Icon/canonicalIconNames.tsx +12 -0
  173. package/src/components/Label/Label.stories.tsx +2 -1
  174. package/src/components/Label/Label.tsx +17 -1
  175. package/src/components/Label/label.scss +5 -1
  176. package/src/components/Menu/MenuItem.tsx +27 -1
  177. package/src/components/Menu/menu.scss +1 -0
  178. package/src/components/OverviewItem/OverviewItem.tsx +24 -1
  179. package/src/components/OverviewItem/OverviewItemList.tsx +3 -2
  180. package/src/components/OverviewItem/overviewitem.scss +4 -1
  181. package/src/components/OverviewItem/stories/OverviewItem.stories.tsx +6 -12
  182. package/src/components/Select/Select.stories.tsx +4 -1
  183. package/src/components/Switch/Switch.tsx +27 -8
  184. package/src/components/Tag/TagList.tsx +2 -2
  185. package/src/components/TextField/SearchField.tsx +37 -9
  186. package/src/components/TextField/stories/SearchField.stories.tsx +15 -1
  187. package/src/components/TextField/stories/TextField.stories.tsx +2 -1
  188. package/src/components/TextField/textfield.scss +17 -3
  189. package/src/components/Typography/OverflowText.tsx +24 -3
  190. package/src/components/Typography/stories/OverflowText.stories.tsx +33 -0
  191. package/src/components/index.scss +1 -0
  192. package/src/components/index.ts +1 -0
  193. package/src/extensions/codemirror/CodeMirror.stories.tsx +19 -1
  194. package/src/extensions/codemirror/CodeMirror.tsx +154 -16
  195. package/src/extensions/codemirror/_codemirror.scss +130 -1
  196. package/src/extensions/codemirror/debouncedLinter.ts +26 -0
  197. package/src/extensions/codemirror/hooks/useCodemirrorModeExtension.hooks.ts +1 -1
  198. package/src/extensions/codemirror/linters/jsLinter.ts +38 -0
  199. package/src/extensions/codemirror/linters/turtleLinter.ts +102 -0
  200. package/src/extensions/codemirror/tests/codemirrorTestHelper.ts +4 -0
  201. package/src/extensions/codemirror/toolbars/commands/markdown.command.ts +340 -0
  202. package/src/extensions/codemirror/toolbars/markdown.toolbar.tsx +117 -0
  203. package/src/extensions/codemirror/types.ts +7 -0
  204. package/src/extensions/react-flow/_config.scss +1 -0
  205. package/src/extensions/react-flow/nodes/NodeContent.tsx +170 -52
  206. package/src/extensions/react-flow/nodes/_nodes.scss +71 -35
  207. package/src/extensions/react-flow/nodes/nodeUtils.tsx +16 -14
  208. package/src/extensions/react-flow/nodes/stories/NodeContent.stories.tsx +51 -12
@@ -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,14 +13,30 @@ 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
 
17
- const TemplateFull: StoryFn<typeof CodeEditor> = (args) => <CodeEditor {...args} />;
22
+ let forcedUpdateKey = 0; // @see https://github.com/storybookjs/storybook/issues/13375#issuecomment-1291011856
23
+ const TemplateFull: StoryFn<typeof CodeEditor> = (args) => <CodeEditor {...args} key={++forcedUpdateKey} />;
18
24
 
19
25
  export const BasicExample = TemplateFull.bind({});
20
26
  BasicExample.args = {
21
27
  name: "codeinput",
22
28
  mode: "markdown",
23
29
  defaultValue: "**test me**",
30
+ useToolbar: true,
31
+ disabled: false,
32
+ readOnly: true,
33
+ };
34
+
35
+ export const LinterExample = TemplateFull.bind({});
36
+ LinterExample.args = {
37
+ name: "codeinput",
38
+ defaultValue: "**test me**",
39
+ mode: "javascript",
40
+ useLinting: true,
41
+ autoFocus: true,
24
42
  };
@@ -1,11 +1,15 @@
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
4
  import { EditorState, Extension } from "@codemirror/state";
5
5
  import { DOMEventHandlers, EditorView, KeyBinding, keymap, Rect, ViewUpdate } from "@codemirror/view";
6
6
  import { minimalSetup } from "codemirror";
7
7
 
8
+ import { IntentTypes } from "../../common/Intent";
8
9
  import { markField } from "../../components/AutoSuggestion/extensions/markText";
10
+ import { TestableComponent } from "../../components/interfaces";
11
+ import { MarkdownToolbar } from "./toolbars/markdown.toolbar";
12
+ import { Markdown } from "../../cmem/markdown/Markdown";
9
13
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
10
14
 
11
15
  //hooks
@@ -14,6 +18,8 @@ import {
14
18
  supportedCodeEditorModes,
15
19
  useCodeMirrorModeExtension,
16
20
  } from "./hooks/useCodemirrorModeExtension.hooks";
21
+ import { jsLinter } from "./linters/jsLinter";
22
+ import { turtleLinter } from "./linters/turtleLinter";
17
23
  //adaptations
18
24
  import {
19
25
  adaptedCodeFolding,
@@ -23,12 +29,14 @@ import {
23
29
  adaptedHighlightActiveLine,
24
30
  adaptedHighlightSpecialChars,
25
31
  adaptedLineNumbers,
32
+ adaptedLintGutter,
26
33
  adaptedPlaceholder,
27
34
  } from "./tests/codemirrorTestHelper";
35
+ import { ExtensionCreator } from "./types";
28
36
 
29
- export interface CodeEditorProps {
37
+ export interface CodeEditorProps extends TestableComponent {
30
38
  // Is called with the editor instance that allows access via the CodeMirror API
31
- setEditorView?: (editor: EditorView | undefined) => any;
39
+ setEditorView?: (editor: EditorView | undefined) => void;
32
40
  /**
33
41
  * `name` attribute of connected textarea element.
34
42
  */
@@ -50,7 +58,7 @@ export interface CodeEditorProps {
50
58
  /**
51
59
  * Called when the focus status changes
52
60
  */
53
- onFocusChange?: (focused: boolean) => any;
61
+ onFocusChange?: (focused: boolean) => void;
54
62
  /**
55
63
  * Called when the user presses a key
56
64
  */
@@ -62,7 +70,7 @@ export interface CodeEditorProps {
62
70
  /**
63
71
  * Called when the user selects text
64
72
  */
65
- onSelection?: (ranges: { from: number; to: number }[]) => any;
73
+ onSelection?: (ranges: { from: number; to: number }[]) => void;
66
74
  /**
67
75
  * Called when the cursor position changes
68
76
  */
@@ -71,7 +79,6 @@ export interface CodeEditorProps {
71
79
  /**
72
80
  * Syntax mode of the code editor.
73
81
  */
74
-
75
82
  mode?: SupportedCodeEditorModes;
76
83
  /**
77
84
  * Default value used first when the editor is instanciated.
@@ -133,6 +140,32 @@ export interface CodeEditorProps {
133
140
  * 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
141
  */
135
142
  enableTab?: boolean;
143
+ /**
144
+ * Enables linting feature in the editor ("turtle" and "javascript" modes can use linting currently).
145
+ */
146
+ useLinting?: boolean;
147
+
148
+ /**
149
+ * Autofocus the editor when it is rendered
150
+ */
151
+ autoFocus?: boolean;
152
+ /**
153
+ * Intent state of the code editor.
154
+ */
155
+ intent?: IntentTypes | "edited" | "removed";
156
+ /**
157
+ * Disables the editor.
158
+ */
159
+ disabled?: boolean;
160
+ /**
161
+ * Add toolbar for mode.
162
+ * Currently only `markdown` is supported.
163
+ */
164
+ useToolbar?: boolean;
165
+ /**
166
+ * Get the translation for a specific key
167
+ */
168
+ translate?: (key: string) => string | false;
136
169
  }
137
170
 
138
171
  const addExtensionsFor = (flag: boolean, ...extensions: Extension[]) => (flag ? [...extensions] : []);
@@ -140,6 +173,13 @@ const addToKeyMapConfigFor = (flag: boolean, ...keys: any) => (flag ? [...keys]
140
173
  const addHandlersFor = (flag: boolean, handlerName: string, handler: any) =>
141
174
  flag ? ({ [handlerName]: handler } as DOMEventHandlers<any>) : {};
142
175
 
176
+ const ModeLinterMap: ReadonlyMap<SupportedCodeEditorModes, ReadonlyArray<ExtensionCreator>> = new Map([
177
+ ["turtle", [turtleLinter]],
178
+ ["javascript", [jsLinter]],
179
+ ]);
180
+
181
+ const ModeToolbarSupport: ReadonlyArray<SupportedCodeEditorModes> = ["markdown"];
182
+
143
183
  /**
144
184
  * Includes a code editor, currently we use CodeMirror library as base.
145
185
  */
@@ -170,8 +210,33 @@ export const CodeEditor = ({
170
210
  tabForceSpaceForModes = ["python", "yaml"],
171
211
  enableTab = false,
172
212
  height,
213
+ useLinting = false,
214
+ "data-test-id": dataTestId,
215
+ autoFocus = false,
216
+ disabled = false,
217
+ intent,
218
+ useToolbar,
219
+ translate,
220
+ ...otherCodeEditorProps
173
221
  }: CodeEditorProps) => {
174
222
  const parent = useRef<any>(undefined);
223
+ const [view, setView] = React.useState<EditorView | undefined>();
224
+ const [showPreview, setShowPreview] = React.useState<boolean>(false);
225
+
226
+ const linters = useMemo(() => {
227
+ if (!mode) {
228
+ return [];
229
+ }
230
+
231
+ const values = [adaptedLintGutter()];
232
+
233
+ const linters = ModeLinterMap.get(mode);
234
+ if (linters) {
235
+ values.push(...linters.map((linter) => linter()));
236
+ }
237
+
238
+ return values;
239
+ }, [mode]);
175
240
 
176
241
  const onKeyDownHandler = (event: KeyboardEvent, view: EditorView) => {
177
242
  if (onKeyDown && !onKeyDown(event)) {
@@ -192,6 +257,14 @@ export const CodeEditor = ({
192
257
  }
193
258
  };
194
259
 
260
+ const getTranslation = (key: string): string | false => {
261
+ if (translate && typeof translate === "function") {
262
+ return translate(key);
263
+ }
264
+
265
+ return false;
266
+ };
267
+
195
268
  React.useEffect(() => {
196
269
  const tabIndent =
197
270
  !!(tabIntentStyle === "tab" && mode && !(tabForceSpaceForModes ?? []).includes(mode)) || enableTab;
@@ -219,13 +292,23 @@ export const CodeEditor = ({
219
292
  keymap?.of(keyMapConfigs),
220
293
  EditorState?.tabSize.of(tabIntentSize),
221
294
  EditorState?.readOnly.of(readOnly),
295
+ EditorView?.editable.of(!disabled),
222
296
  AdaptedEditorViewDomEventHandlers(domEventHandlers) as Extension,
223
297
  EditorView?.updateListener.of((v: ViewUpdate) => {
224
- onChange && onChange(v.state.doc.toString());
298
+ if (disabled) return;
299
+
300
+ if (onChange && v.docChanged) {
301
+ // Only fire if the text has actually been changed
302
+ onChange(v.state.doc.toString());
303
+ }
225
304
 
226
305
  if (onSelection)
227
306
  onSelection(v.state.selection.ranges.filter((r) => !r.empty).map(({ from, to }) => ({ from, to })));
228
307
 
308
+ if (onFocusChange && intent && !v.view.dom.classList?.contains(`${eccgui}-intent--${intent}`)) {
309
+ v.view.dom.classList.add(`${eccgui}-intent--${intent}`);
310
+ }
311
+
229
312
  if (onCursorChange) {
230
313
  const cursorPosition = v.state.selection.main.head ?? 0;
231
314
  const editorRect = v.view.dom.getBoundingClientRect();
@@ -250,6 +333,7 @@ export const CodeEditor = ({
250
333
  addExtensionsFor(shouldHighlightActiveLine, adaptedHighlightActiveLine()),
251
334
  addExtensionsFor(wrapLines, EditorView?.lineWrapping),
252
335
  addExtensionsFor(supportCodeFolding, adaptedFoldGutter(), adaptedCodeFolding()),
336
+ addExtensionsFor(useLinting, ...linters),
253
337
  additionalExtensions,
254
338
  ];
255
339
 
@@ -260,18 +344,67 @@ export const CodeEditor = ({
260
344
  }),
261
345
  parent: parent.current,
262
346
  });
347
+ setView(view);
263
348
 
264
- if (height) {
265
- view.dom.style.height = typeof height === "string" ? height : `${height}px`;
266
- }
349
+ if (view?.dom) {
350
+ if (height) {
351
+ view.dom.style.height = typeof height === "string" ? height : `${height}px`;
352
+ }
353
+
354
+ if (disabled) {
355
+ view.dom.className += ` ${eccgui}-disabled`;
356
+ }
267
357
 
268
- setEditorView && setEditorView(view);
358
+ if (intent) {
359
+ view.dom.className += ` ${eccgui}-intent--${intent}`;
360
+ }
361
+
362
+ if (autoFocus) {
363
+ view.focus();
364
+ }
365
+
366
+ if (setEditorView) {
367
+ setEditorView(view);
368
+ }
369
+ }
269
370
 
270
371
  return () => {
271
372
  view.destroy();
272
- setEditorView && setEditorView(undefined);
373
+ if (setEditorView) {
374
+ setEditorView(undefined);
375
+ setView(undefined);
376
+ }
273
377
  };
274
- }, [parent.current, mode, preventLineNumbers]);
378
+ }, [parent.current, mode, preventLineNumbers, wrapLines]);
379
+
380
+ const hasToolbarSupport = mode && ModeToolbarSupport.indexOf(mode) > -1 && useToolbar;
381
+
382
+ const editorToolbar = (mode?: SupportedCodeEditorModes): JSX.Element => {
383
+ switch (mode) {
384
+ case "markdown":
385
+ return (
386
+ <div>
387
+ <div className={`${eccgui}-codeeditor__toolbar`}>
388
+ <MarkdownToolbar
389
+ view={view}
390
+ togglePreviewStatus={() => setShowPreview((p) => !p)}
391
+ showPreview={showPreview}
392
+ translate={getTranslation}
393
+ disabled={disabled}
394
+ readonly={readOnly}
395
+ />
396
+ </div>
397
+ {showPreview && (
398
+ <div className={`${eccgui}-codeeditor__preview`}>
399
+ <Markdown>{view?.state.doc.toString() ?? ""}</Markdown>
400
+ </div>
401
+ )}
402
+ </div>
403
+ );
404
+ default:
405
+ return <></>;
406
+ }
407
+ };
275
408
 
276
409
  return (
277
410
  <div
@@ -279,12 +412,17 @@ export const CodeEditor = ({
279
412
  // overwrite/extend some attributes
280
413
  id={id ? id : name ? `codemirror-${name}` : undefined}
281
414
  ref={parent}
282
- data-test-id="codemirror-wrapper"
415
+ // @deprecated (v25) fallback with static test id will be removed
416
+ data-test-id={dataTestId ? dataTestId : "codemirror-wrapper"}
283
417
  className={
284
418
  `${eccgui}-codeeditor ${eccgui}-codeeditor--mode-${mode}` +
285
- (outerDivAttributes?.className ? ` ${outerDivAttributes?.className}` : "")
419
+ (outerDivAttributes?.className ? ` ${outerDivAttributes?.className}` : "") +
420
+ (hasToolbarSupport ? ` ${eccgui}-codeeditor--has-toolbar` : "")
286
421
  }
287
- />
422
+ {...otherCodeEditorProps}
423
+ >
424
+ {hasToolbarSupport && editorToolbar(mode)}
425
+ </div>
288
426
  );
289
427
  };
290
428
 
@@ -1,5 +1,10 @@
1
+ @use "sass:color";
2
+
1
3
  // own vars
2
4
  $eccgui-color-codeeditor-background: $eccgui-color-textfield-background !default;
5
+ $eccgui-color-codeeditor-separation: $eccgui-color-separation-divider !default;
6
+ $eccgui-size-codeeditor-height: 20rem !default;
7
+ $eccgui-size-codeeditor-toolbar-height: $button-height !default;
3
8
 
4
9
  // adjustments
5
10
  // stylelint-disable selector-class-pattern
@@ -12,9 +17,39 @@ $eccgui-color-codeeditor-background: $eccgui-color-textfield-background !default
12
17
  width: 100%;
13
18
  }
14
19
 
20
+ &__toolbar {
21
+ position: absolute;
22
+ z-index: 3;
23
+ left: 1px;
24
+ right: 1px;
25
+ top: 1px;
26
+ border-radius: $pt-border-radius $pt-border-radius 0 0;
27
+ border-bottom: solid 1px $eccgui-color-codeeditor-separation;
28
+ background-color: $eccgui-color-codeeditor-background;
29
+ }
30
+
31
+ &--has-toolbar {
32
+ .cm-scroller {
33
+ margin-top: $eccgui-size-codeeditor-toolbar-height !important;
34
+ }
35
+ }
36
+
37
+ &__preview {
38
+ position: absolute;
39
+ top: calc(#{$eccgui-size-codeeditor-toolbar-height} + 1px) !important;
40
+ left: 1px;
41
+ right: 1px;
42
+ bottom: 1px;
43
+ z-index: 2;
44
+ padding: $button-padding;
45
+ overflow-y: auto;
46
+ background-color: $eccgui-color-codeeditor-background;
47
+ border-radius: 0 0 $pt-border-radius $pt-border-radius;
48
+ }
49
+
15
50
  .cm-editor {
16
51
  width: 100%;
17
- height: 290px;
52
+ height: $eccgui-size-codeeditor-height;
18
53
  clip-path: unset !important; // we may check later why they set inset(0) now
19
54
  background-color: $eccgui-color-codeeditor-background;
20
55
  border-radius: $pt-border-radius;
@@ -22,6 +57,63 @@ $eccgui-color-codeeditor-background: $eccgui-color-textfield-background !default
22
57
  // get them a "border" like input boxes from blueprintjs
23
58
  box-shadow: input-transition-shadow($input-shadow-color-focus), $pt-input-box-shadow;
24
59
 
60
+ &.#{eccgui}-disabled {
61
+ @extend .#{$ns}-input, .#{$ns}-disabled;
62
+
63
+ height: $eccgui-size-codeeditor-height;
64
+ padding: 0;
65
+ }
66
+
67
+ &[class*="#{$eccgui}-intent--"] {
68
+ animation-duration: 1s;
69
+ animation-delay: 0.5s;
70
+ }
71
+
72
+ @each $each-intent, $each-bgcolor in $eccgui-map-intent-bgcolors {
73
+ &.#{eccgui}-intent--#{$each-intent} {
74
+ background-color: color.mix($each-bgcolor, $eccgui-color-textfield-background, 24%);
75
+ animation-name: intent-state-flash-#{$each-intent};
76
+ }
77
+ }
78
+
79
+ &.#{eccgui}-intent--warning {
80
+ @include pt-input-intent($eccgui-color-warning-text);
81
+ }
82
+
83
+ &.#{eccgui}-intent--success {
84
+ @include pt-input-intent($eccgui-color-success-text);
85
+ }
86
+
87
+ &.#{eccgui}-intent--danger {
88
+ @include pt-input-intent($eccgui-color-danger-text);
89
+ }
90
+
91
+ &.#{eccgui}-intent--primary {
92
+ @include pt-input-intent($eccgui-color-info-text);
93
+ }
94
+
95
+ &.#{eccgui}-intent--info {
96
+ @include pt-input-intent($eccgui-color-info-text);
97
+ }
98
+
99
+ &.#{eccgui}-intent--accent {
100
+ @include pt-input-intent($eccgui-color-primary);
101
+ }
102
+
103
+ &.#{eccgui}-intent--neutral {
104
+ @include pt-input-intent($eccgui-color-workspace-text);
105
+ }
106
+
107
+ &.#{eccgui}-intent--edited {
108
+ @include pt-input-intent($eccgui-color-info-text);
109
+ }
110
+
111
+ &.#{eccgui}-intent--removed {
112
+ @include pt-input-intent($eccgui-color-danger-text);
113
+
114
+ text-decoration: line-through $eccgui-color-danger-text 2px;
115
+ }
116
+
25
117
  .cm-scroller {
26
118
  width: calc(100% - 2px);
27
119
  height: calc(100% - 2px);
@@ -34,6 +126,43 @@ $eccgui-color-codeeditor-background: $eccgui-color-textfield-background !default
34
126
  &.cm-focused {
35
127
  outline: none;
36
128
  box-shadow: input-transition-shadow($input-shadow-color-focus, true), $input-box-shadow-focus;
129
+
130
+ &.#{eccgui}-intent--warning {
131
+ box-shadow: input-transition-shadow($eccgui-color-warning-text, true), $input-box-shadow-focus;
132
+ }
133
+
134
+ &.#{eccgui}-intent--success {
135
+ box-shadow: input-transition-shadow($eccgui-color-success-text, true), $input-box-shadow-focus;
136
+ }
137
+
138
+ &.#{eccgui}-intent--danger {
139
+ box-shadow: input-transition-shadow($eccgui-color-danger-text, true), $input-box-shadow-focus;
140
+ }
141
+
142
+ &.#{eccgui}-intent--primary {
143
+ box-shadow: input-transition-shadow($eccgui-color-info-text, true), $input-box-shadow-focus;
144
+ }
145
+
146
+ &.#{eccgui}-intent--info {
147
+ box-shadow: input-transition-shadow($eccgui-color-info-text, true), $input-box-shadow-focus;
148
+ }
149
+
150
+ &.#{eccgui}-intent--accent {
151
+ box-shadow: input-transition-shadow($eccgui-color-warning-text, true), $input-box-shadow-focus;
152
+ }
153
+
154
+ &.#{eccgui}-intent--neutral {
155
+ box-shadow: input-transition-shadow($eccgui-color-workspace-text, true), $input-box-shadow-focus;
156
+ }
157
+
158
+ &.#{eccgui}-intent--edited {
159
+ box-shadow: input-transition-shadow($eccgui-color-info-text, true), $input-box-shadow-focus;
160
+ }
161
+
162
+ &.#{eccgui}-intent--removed {
163
+ text-decoration: line-through $eccgui-color-danger-text 2px;
164
+ box-shadow: input-transition-shadow($eccgui-color-danger-text, true), $input-box-shadow-focus;
165
+ }
37
166
  }
38
167
 
39
168
  .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
+ };
@@ -48,6 +48,6 @@ export const useCodeMirrorModeExtension = (mode?: SupportedCodeEditorModes) => {
48
48
  return !mode
49
49
  ? adaptedSyntaxHighlighting(defaultHighlightStyle)
50
50
  : v6AdaptedModes.has(mode)
51
- ? ((typeof supportedModes[mode] === "function" ? supportedModes[mode] : () => {}) as () => LanguageSupport)()
51
+ ? ((typeof supportedModes[mode] === "function" ? supportedModes[mode] : () => null) as () => LanguageSupport)()
52
52
  : StreamLanguage?.define(supportedModes[mode] as StreamParser<unknown>);
53
53
  };
@@ -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
+ };
@@ -0,0 +1,102 @@
1
+ import { Diagnostic, linter } from "@codemirror/lint";
2
+ import { EditorView } from "@codemirror/view";
3
+ import { Parser } from "n3";
4
+
5
+ import { debouncedLinter } from "../debouncedLinter";
6
+ import { ExtensionCreator, Linter } from "../types";
7
+
8
+ const parser = new Parser();
9
+
10
+ const EMPTY_RESOURCE = "<>";
11
+
12
+ const getError = (message: string, view: EditorView) => {
13
+ const lineMatch = message.match(/(?<=line )\d{1,}/);
14
+ const valueMatch = message.match(/"([^"]*)"/);
15
+
16
+ const lineNumber = lineMatch ? Number(lineMatch[0]) : 1;
17
+ // the [1] index is used to get the caputre group
18
+ const errorContent = valueMatch && valueMatch[1];
19
+
20
+ const line = view.state.doc.line(lineNumber);
21
+ const position = line.text.search(errorContent ?? /\S/);
22
+
23
+ const from = line.from + position;
24
+ const errorLength = errorContent?.length;
25
+
26
+ return { from, to: errorLength ? from + errorLength : line.to };
27
+ };
28
+
29
+ const getQuadError = (view: EditorView) => {
30
+ const lines = view.state.doc.toJSON();
31
+
32
+ for (let i = 0; i < lines.length; i += 1) {
33
+ const input = lines[i].trim();
34
+
35
+ if (!input) {
36
+ continue;
37
+ }
38
+
39
+ if (input.includes(EMPTY_RESOURCE)) {
40
+ // i + 1 is used here because the codemirror uses 1-indexes
41
+ const line = view.state.doc.line(i + 1);
42
+ const position = line.text.search(EMPTY_RESOURCE);
43
+
44
+ const from = line.from + position;
45
+
46
+ return {
47
+ from,
48
+ to: from + EMPTY_RESOURCE.length,
49
+ };
50
+ }
51
+ }
52
+
53
+ return { from: 0, to: view.state.doc.length };
54
+ };
55
+
56
+ const n3Linter: Linter = (view) => {
57
+ const diagnostics: Array<Diagnostic> = [];
58
+ const value = view.state.doc.toString();
59
+
60
+ try {
61
+ const quads = parser.parse(value);
62
+
63
+ quads.forEach((quad) => {
64
+ if (!quad.subject || !quad.predicate || !quad.object) {
65
+ const { from, to } = getQuadError(view);
66
+
67
+ view.dispatch({
68
+ scrollIntoView: true,
69
+ });
70
+
71
+ diagnostics.push({
72
+ from,
73
+ to,
74
+ severity: "error",
75
+ message: `Invalid RDF quad:\n\nsubject: ${quad.subject}\npredicate: ${quad.predicate}\nobject: ${quad.object}`,
76
+ });
77
+ }
78
+ });
79
+ } catch (error) {
80
+ const { message } = error as Error;
81
+
82
+ const { from, to } = getError(message, view);
83
+
84
+ view.dispatch({
85
+ scrollIntoView: true,
86
+ });
87
+
88
+ diagnostics.push({
89
+ from,
90
+ to,
91
+ severity: "error",
92
+ message: (error as Error).message,
93
+ });
94
+ }
95
+
96
+ return diagnostics;
97
+ };
98
+
99
+ /**
100
+ * Sets up the turtle linter. Documentation: https://codemirror.net/examples/lint/
101
+ */
102
+ export const turtleLinter: ExtensionCreator = () => linter(debouncedLinter(n3Linter));
@@ -10,6 +10,7 @@
10
10
  import { EditorView, placeholder, highlightSpecialChars, lineNumbers, highlightActiveLine } from "@codemirror/view";
11
11
  import { syntaxHighlighting, foldGutter, codeFolding } from "@codemirror/language";
12
12
  import { Extension } from "@codemirror/state";
13
+ import { lintGutter } from "@codemirror/lint";
13
14
 
14
15
  /** placeholder extension, current error '_view.placeholder is not a function' */
15
16
  export const adaptedPlaceholder = (text?: string) =>
@@ -55,3 +56,6 @@ export const adaptedFoldGutter = (props?: any) =>
55
56
 
56
57
  export const adaptedCodeFolding = (props?: any) =>
57
58
  typeof codeFolding === "function" ? codeFolding(props) : emptyExtension;
59
+
60
+ export const adaptedLintGutter = (props?: any) =>
61
+ typeof lintGutter === "function" ? lintGutter(props) : emptyExtension;