@eccenca/gui-elements 24.1.0-rc.4 → 24.1.0-rc.6

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 (74) hide show
  1. package/CHANGELOG.md +35 -2
  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/components/AutoSuggestion/AutoSuggestion.js +8 -0
  5. package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
  6. package/dist/cjs/components/Icon/canonicalIconNames.js +10 -0
  7. package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
  8. package/dist/cjs/components/OverviewItem/OverviewItem.js +5 -2
  9. package/dist/cjs/components/OverviewItem/OverviewItem.js.map +1 -1
  10. package/dist/cjs/components/OverviewItem/OverviewItemList.js +2 -2
  11. package/dist/cjs/components/OverviewItem/OverviewItemList.js.map +1 -1
  12. package/dist/cjs/components/TextField/SearchField.js +19 -2
  13. package/dist/cjs/components/TextField/SearchField.js.map +1 -1
  14. package/dist/cjs/components/Typography/OverflowText.js +1 -1
  15. package/dist/cjs/components/Typography/OverflowText.js.map +1 -1
  16. package/dist/cjs/extensions/codemirror/CodeMirror.js +37 -8
  17. package/dist/cjs/extensions/codemirror/CodeMirror.js.map +1 -1
  18. package/dist/cjs/extensions/codemirror/toolbars/commands/markdown.command.js +278 -0
  19. package/dist/cjs/extensions/codemirror/toolbars/commands/markdown.command.js.map +1 -0
  20. package/dist/cjs/extensions/codemirror/toolbars/markdown.toolbar.js +47 -0
  21. package/dist/cjs/extensions/codemirror/toolbars/markdown.toolbar.js.map +1 -0
  22. package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js +7 -2
  23. package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
  24. package/dist/esm/components/AutoSuggestion/AutoSuggestion.js +8 -0
  25. package/dist/esm/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
  26. package/dist/esm/components/Icon/canonicalIconNames.js +10 -0
  27. package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
  28. package/dist/esm/components/OverviewItem/OverviewItem.js +5 -2
  29. package/dist/esm/components/OverviewItem/OverviewItem.js.map +1 -1
  30. package/dist/esm/components/OverviewItem/OverviewItemList.js +2 -2
  31. package/dist/esm/components/OverviewItem/OverviewItemList.js.map +1 -1
  32. package/dist/esm/components/TextField/SearchField.js +35 -2
  33. package/dist/esm/components/TextField/SearchField.js.map +1 -1
  34. package/dist/esm/components/Typography/OverflowText.js +1 -1
  35. package/dist/esm/components/Typography/OverflowText.js.map +1 -1
  36. package/dist/esm/extensions/codemirror/CodeMirror.js +38 -9
  37. package/dist/esm/extensions/codemirror/CodeMirror.js.map +1 -1
  38. package/dist/esm/extensions/codemirror/toolbars/commands/markdown.command.js +283 -0
  39. package/dist/esm/extensions/codemirror/toolbars/commands/markdown.command.js.map +1 -0
  40. package/dist/esm/extensions/codemirror/toolbars/markdown.toolbar.js +41 -0
  41. package/dist/esm/extensions/codemirror/toolbars/markdown.toolbar.js.map +1 -0
  42. package/dist/types/cmem/ActivityControl/ActivityControlWidget.d.ts +1 -1
  43. package/dist/types/components/Icon/canonicalIconNames.d.ts +10 -0
  44. package/dist/types/components/OverviewItem/OverviewItem.d.ts +13 -1
  45. package/dist/types/components/OverviewItem/OverviewItemList.d.ts +3 -2
  46. package/dist/types/components/TextField/SearchField.d.ts +1 -1
  47. package/dist/types/components/Typography/OverflowText.d.ts +23 -2
  48. package/dist/types/extensions/codemirror/CodeMirror.d.ts +10 -1
  49. package/dist/types/extensions/codemirror/toolbars/commands/markdown.command.d.ts +55 -0
  50. package/dist/types/extensions/codemirror/toolbars/markdown.toolbar.d.ts +12 -0
  51. package/package.json +21 -19
  52. package/src/cmem/ActivityControl/ActivityControlWidget.tsx +5 -2
  53. package/src/components/AutoSuggestion/AutoSuggestion.tsx +9 -0
  54. package/src/components/CodeAutocompleteField/CodeAutocompleteField.stories.tsx +3 -2
  55. package/src/components/ContextOverlay/ContextOverlay.stories.tsx +15 -4
  56. package/src/components/Depiction/depiction.scss +7 -0
  57. package/src/components/Dialog/stories/AlertDialog.stories.tsx +5 -1
  58. package/src/components/Dialog/stories/Modal.stories.tsx +4 -2
  59. package/src/components/Dialog/stories/SimpleDialog.stories.tsx +5 -2
  60. package/src/components/Icon/canonicalIconNames.tsx +10 -0
  61. package/src/components/OverviewItem/OverviewItem.tsx +24 -1
  62. package/src/components/OverviewItem/OverviewItemList.tsx +3 -2
  63. package/src/components/OverviewItem/stories/OverviewItem.stories.tsx +6 -12
  64. package/src/components/Select/Select.stories.tsx +4 -1
  65. package/src/components/TextField/SearchField.tsx +37 -9
  66. package/src/components/TextField/stories/SearchField.stories.tsx +15 -1
  67. package/src/components/TextField/textfield.scss +17 -3
  68. package/src/components/Typography/OverflowText.tsx +24 -3
  69. package/src/components/Typography/stories/OverflowText.stories.tsx +33 -0
  70. package/src/extensions/codemirror/CodeMirror.stories.tsx +5 -17
  71. package/src/extensions/codemirror/CodeMirror.tsx +67 -8
  72. package/src/extensions/codemirror/_codemirror.scss +35 -2
  73. package/src/extensions/codemirror/toolbars/commands/markdown.command.ts +340 -0
  74. package/src/extensions/codemirror/toolbars/markdown.toolbar.tsx +117 -0
@@ -27,7 +27,7 @@ export default {
27
27
  },
28
28
  argTypes: {
29
29
  children: {
30
- control: "none",
30
+ control: false,
31
31
  description: "Elements used as depiction, text and interactive elements of an overview-item.",
32
32
  },
33
33
  },
@@ -47,6 +47,9 @@ ItemExample.args = {
47
47
  <OverviewItemActions children={ActionsExample.args.children[0]} hiddenInteractions key="hiddenactions" />,
48
48
  <OverviewItemActions children={ActionsExample.args.children[1]} key="actions" />,
49
49
  ],
50
+ densityHigh: false,
51
+ hasSpacing: false,
52
+ hasCardWrapper: false,
50
53
  };
51
54
 
52
55
  export const ItemWithDepictionElement = Template.bind({});
@@ -69,16 +72,7 @@ ItemWithDepictionElement.args = {
69
72
  <OverviewItemActions children={ActionsExample.args.children[0]} hiddenInteractions />,
70
73
  <OverviewItemActions children={ActionsExample.args.children[1]} />,
71
74
  ],
72
- };
73
-
74
- const TemplateCard: StoryFn<typeof OverviewItem> = (args) => (
75
- <Card isOnlyLayout>
76
- <OverviewItem {...args}></OverviewItem>
77
- </Card>
78
- );
79
-
80
- export const ItemInCard = TemplateCard.bind({});
81
- ItemInCard.args = {
82
- ...ItemExample.args,
75
+ densityHigh: false,
83
76
  hasSpacing: true,
77
+ hasCardWrapper: true,
84
78
  };
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { loremIpsum } from "react-lorem-ipsum";
3
3
  import { Meta, StoryFn } from "@storybook/react";
4
+ import { fn } from "@storybook/test";
4
5
 
5
6
  import { helpersArgTypes } from "../../../.storybook/helpers";
6
7
  import { Button, Depiction, MenuItem, Select } from "../../index";
@@ -30,7 +31,7 @@ Default.args = {
30
31
  return { label: item };
31
32
  }),
32
33
  itemRenderer: (item, props) => {
33
- return <MenuItem text={item.label} title={item.label} />;
34
+ return <MenuItem text={item.label} title={item.label} {...props} />;
34
35
  },
35
36
  fill: true,
36
37
  };
@@ -53,6 +54,7 @@ ControlledTarget.args = {
53
54
  ...Default.args,
54
55
  fill: false,
55
56
  children: <Button text="Controlled select target" intent="primary" />,
57
+ onActiveItemChange: fn(),
56
58
  };
57
59
 
58
60
  /**
@@ -70,4 +72,5 @@ ClearanceOption.args = {
70
72
  onClearanceHandler: () => {
71
73
  alert("Reset now.");
72
74
  },
75
+ onActiveItemChange: fn(),
73
76
  };
@@ -35,10 +35,38 @@ export const SearchField = ({
35
35
  className = "",
36
36
  emptySearchInputMessage = "Enter search term",
37
37
  onClearanceHandler,
38
- onClearanceText = "Clear input",
38
+ onClearanceText = "Clear current search term",
39
+ onChange,
39
40
  leftIcon = <Icon name="operation-search" />,
41
+ rightElement,
40
42
  ...otherProps
41
43
  }: SearchFieldProps) => {
44
+ const [value, setValue] = React.useState<string>("");
45
+
46
+ const clearanceButton =
47
+ onClearanceHandler && value ? (
48
+ <IconButton
49
+ data-test-id={otherProps["data-test-id"] && `${otherProps["data-test-id"]}-clear-btn`}
50
+ name="operation-clear"
51
+ text={onClearanceText}
52
+ onClick={() => {
53
+ setValue("");
54
+ onClearanceHandler();
55
+ }}
56
+ />
57
+ ) : undefined;
58
+
59
+ const changeHandlerProcess = (e: React.ChangeEvent<HTMLInputElement>) => {
60
+ setValue(e.target.value);
61
+ if (onChange) {
62
+ onChange(e);
63
+ }
64
+ };
65
+
66
+ React.useEffect(() => {
67
+ setValue(otherProps.value ?? otherProps.defaultValue ?? "");
68
+ }, [otherProps.value, otherProps.defaultValue]);
69
+
42
70
  return (
43
71
  <TextField
44
72
  className={
@@ -50,16 +78,16 @@ export const SearchField = ({
50
78
  placeholder={emptySearchInputMessage}
51
79
  aria-label={emptySearchInputMessage}
52
80
  rightElement={
53
- onClearanceHandler && otherProps.value ? (
54
- <IconButton
55
- data-test-id={otherProps["data-test-id"] && `${otherProps["data-test-id"]}-clear-btn`}
56
- name="operation-clear"
57
- text={onClearanceText ? onClearanceText : "Clear current search term"}
58
- onClick={onClearanceHandler}
59
- />
60
- ) : undefined
81
+ (clearanceButton || rightElement) && (
82
+ <>
83
+ {rightElement}
84
+ {clearanceButton}
85
+ </>
86
+ )
61
87
  }
88
+ onChange={changeHandlerProcess}
62
89
  {...otherProps}
90
+ value={value}
63
91
  type={"search"}
64
92
  leftIcon={leftIcon}
65
93
  round={true}
@@ -1,12 +1,20 @@
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 SearchField from "./../SearchField";
5
7
 
6
8
  export default {
7
9
  title: "Components/SearchField",
8
10
  component: SearchField,
9
11
  argTypes: {
12
+ leftIcon: {
13
+ ...helpersArgTypes.exampleIcon,
14
+ },
15
+ rightElement: {
16
+ ...helpersArgTypes.exampleIcon,
17
+ },
10
18
  hasStatePrimary: { table: { disable: true } },
11
19
  hasStateSuccess: { table: { disable: true } },
12
20
  hasStateWarning: { table: { disable: true } },
@@ -23,7 +31,7 @@ Default.args = {
23
31
  onClearanceText: "",
24
32
  };
25
33
 
26
- export const SearchFieldWithClearanceIcon: StoryFn<typeof SearchField> = (args) => {
34
+ const SearchFieldWithClearanceIconTemplate: StoryFn<typeof SearchField> = (args) => {
27
35
  const [query, setQuery] = React.useState<string>("");
28
36
  return (
29
37
  <SearchField
@@ -34,3 +42,9 @@ export const SearchFieldWithClearanceIcon: StoryFn<typeof SearchField> = (args)
34
42
  />
35
43
  );
36
44
  };
45
+
46
+ export const SearchFieldWithClearanceIcon = SearchFieldWithClearanceIconTemplate.bind({});
47
+ SearchFieldWithClearanceIcon.args = {
48
+ onClearanceHandler: null,
49
+ onClearanceText: "Clear field",
50
+ };
@@ -4,7 +4,8 @@
4
4
  // own vars
5
5
  $eccgui-size-textfield-height-small: $eccgui-size-block-whitespace * 2 !default;
6
6
  $eccgui-size-textfield-height-regular: $eccgui-size-textfield-height-small * $eccgui-size-type-levelratio !default;
7
- $eccgui-size-textfield-height-large: $eccgui-size-textfield-height-regular * $eccgui-size-type-levelratio * $eccgui-size-type-levelratio !default;
7
+ $eccgui-size-textfield-height-large: $eccgui-size-textfield-height-regular * $eccgui-size-type-levelratio *
8
+ $eccgui-size-type-levelratio !default;
8
9
  $eccgui-size-textfield-padding-horizontal-regular: $eccgui-size-inline-whitespace !default;
9
10
  $eccgui-size-textfield-padding-horizontal-small: $eccgui-size-inline-whitespace * 0.5 !default;
10
11
  $eccgui-typo-textfield-fontweight: $eccgui-font-weight-regular !default;
@@ -50,10 +51,23 @@ $input-button-height-small: math.div($eccgui-size-textfield-height-small, $eccgu
50
51
  height: 100%;
51
52
  max-height: $input-button-height-large;
52
53
 
53
- & > .#{eccgui}-icon {
54
+ & > * {
55
+ vertical-align: middle;
56
+ }
57
+
58
+ & > .#{eccgui}-icon,
59
+ & > .#{eccgui}-tooltip__wrapper {
54
60
  display: inline-flex;
61
+ align-items: center;
55
62
  height: 100%;
56
- margin: 0 0.5 * $eccgui-size-block-whitespace;
63
+
64
+ &:first-child {
65
+ margin-left: 0.5 * $eccgui-size-block-whitespace;
66
+ }
67
+
68
+ &:last-child {
69
+ margin-right: 0.5 * $eccgui-size-block-whitespace;
70
+ }
57
71
  }
58
72
  }
59
73
  }
@@ -1,13 +1,34 @@
1
1
  import React from "react";
2
2
 
3
3
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
4
+ import { TestableComponent } from "../../components/interfaces";
4
5
 
5
- export interface OverflowTextProps {
6
- ellipsis?: "reverse" | "none";
6
+ export interface OverflowTextProps extends React.HTMLAttributes<HTMLElement>, TestableComponent {
7
+ /**
8
+ * How is ellipsis used to cut text overflows.
9
+ * Use `reverse`to use the ellipis on text start and display the end of the text.
10
+ */
11
+ ellipsis?: "add" | "reverse" | "none";
12
+ /**
13
+ * Display component as inline element.
14
+ */
7
15
  inline?: boolean;
16
+ /**
17
+ * Using text overflow on all children elements.
18
+ */
8
19
  passDown?: boolean;
20
+ /**
21
+ * Additional CSS class name.
22
+ */
9
23
  className?: string;
24
+ /**
25
+ * HTML element that is used for the component.
26
+ */
10
27
  useHtmlElement?: "p" | "div" | "span";
28
+ /**
29
+ * Used for all other necessary properties.
30
+ * @deprecated (v25) we will allow only basic HTML element properties and testing IDs
31
+ */
11
32
  [key: string]: any;
12
33
  }
13
34
 
@@ -15,7 +36,7 @@ export interface OverflowTextProps {
15
36
  export const OverflowText = ({
16
37
  className = "",
17
38
  children,
18
- ellipsis,
39
+ ellipsis = "add",
19
40
  inline = false,
20
41
  passDown = false,
21
42
  useHtmlElement,
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import LoremIpsum, { loremIpsum } from "react-lorem-ipsum";
3
+ import { Meta, StoryFn } from "@storybook/react";
4
+
5
+ import { OverflowText } from "../../../index";
6
+
7
+ const config = {
8
+ title: "Components/Typography/OverflowText",
9
+ component: OverflowText,
10
+ argTypes: {
11
+ children: {
12
+ control: "select",
13
+ options: ["simple text", "2 paragraphs"],
14
+ mapping: {
15
+ "simple text": loremIpsum({
16
+ p: 1,
17
+ avgSentencesPerParagraph: 4,
18
+ random: false,
19
+ }),
20
+ "2 paragraphs": <LoremIpsum p={2} avgSentencesPerParagraph={4} random={false} />
21
+ },
22
+ description: "Content of the element.",
23
+ },
24
+ },
25
+ } as Meta<typeof OverflowText>;
26
+ export default config;
27
+
28
+ const Template: StoryFn<typeof OverflowText> = (args) => <OverflowText {...args} />;
29
+
30
+ export const Default = Template.bind({});
31
+ Default.args = {
32
+ children: config?.argTypes?.children?.mapping ? config.argTypes.children.mapping["simple text"] : "Overflow text",
33
+ };
@@ -19,13 +19,17 @@ export default {
19
19
  },
20
20
  } as Meta<typeof CodeEditor>;
21
21
 
22
- 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} />;
23
24
 
24
25
  export const BasicExample = TemplateFull.bind({});
25
26
  BasicExample.args = {
26
27
  name: "codeinput",
27
28
  mode: "markdown",
28
29
  defaultValue: "**test me**",
30
+ useToolbar: true,
31
+ disabled: false,
32
+ readOnly: true,
29
33
  };
30
34
 
31
35
  export const LinterExample = TemplateFull.bind({});
@@ -36,19 +40,3 @@ LinterExample.args = {
36
40
  useLinting: true,
37
41
  autoFocus: true,
38
42
  };
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
- };
@@ -8,6 +8,8 @@ import { minimalSetup } from "codemirror";
8
8
  import { IntentTypes } from "../../common/Intent";
9
9
  import { markField } from "../../components/AutoSuggestion/extensions/markText";
10
10
  import { TestableComponent } from "../../components/interfaces";
11
+ import { MarkdownToolbar } from "./toolbars/markdown.toolbar";
12
+ import { Markdown } from "../../cmem/markdown/Markdown";
11
13
  import { CLASSPREFIX as eccgui } from "../../configuration/constants";
12
14
 
13
15
  //hooks
@@ -27,8 +29,8 @@ import {
27
29
  adaptedHighlightActiveLine,
28
30
  adaptedHighlightSpecialChars,
29
31
  adaptedLineNumbers,
30
- adaptedPlaceholder,
31
32
  adaptedLintGutter,
33
+ adaptedPlaceholder,
32
34
  } from "./tests/codemirrorTestHelper";
33
35
  import { ExtensionCreator } from "./types";
34
36
 
@@ -77,7 +79,6 @@ export interface CodeEditorProps extends TestableComponent {
77
79
  /**
78
80
  * Syntax mode of the code editor.
79
81
  */
80
-
81
82
  mode?: SupportedCodeEditorModes;
82
83
  /**
83
84
  * Default value used first when the editor is instanciated.
@@ -156,6 +157,15 @@ export interface CodeEditorProps extends TestableComponent {
156
157
  * Disables the editor.
157
158
  */
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;
159
169
  }
160
170
 
161
171
  const addExtensionsFor = (flag: boolean, ...extensions: Extension[]) => (flag ? [...extensions] : []);
@@ -168,6 +178,8 @@ const ModeLinterMap: ReadonlyMap<SupportedCodeEditorModes, ReadonlyArray<Extensi
168
178
  ["javascript", [jsLinter]],
169
179
  ]);
170
180
 
181
+ const ModeToolbarSupport: ReadonlyArray<SupportedCodeEditorModes> = ["markdown"];
182
+
171
183
  /**
172
184
  * Includes a code editor, currently we use CodeMirror library as base.
173
185
  */
@@ -203,9 +215,13 @@ export const CodeEditor = ({
203
215
  autoFocus = false,
204
216
  disabled = false,
205
217
  intent,
218
+ useToolbar,
219
+ translate,
206
220
  ...otherCodeEditorProps
207
221
  }: CodeEditorProps) => {
208
222
  const parent = useRef<any>(undefined);
223
+ const [view, setView] = React.useState<EditorView | undefined>();
224
+ const [showPreview, setShowPreview] = React.useState<boolean>(false);
209
225
 
210
226
  const linters = useMemo(() => {
211
227
  if (!mode) {
@@ -241,6 +257,14 @@ export const CodeEditor = ({
241
257
  }
242
258
  };
243
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
+
244
268
  React.useEffect(() => {
245
269
  const tabIndent =
246
270
  !!(tabIntentStyle === "tab" && mode && !(tabForceSpaceForModes ?? []).includes(mode)) || enableTab;
@@ -273,15 +297,16 @@ export const CodeEditor = ({
273
297
  EditorView?.updateListener.of((v: ViewUpdate) => {
274
298
  if (disabled) return;
275
299
 
276
- if (onChange) {
300
+ if (onChange && v.docChanged) {
301
+ // Only fire if the text has actually been changed
277
302
  onChange(v.state.doc.toString());
278
303
  }
279
304
 
280
305
  if (onSelection)
281
306
  onSelection(v.state.selection.ranges.filter((r) => !r.empty).map(({ from, to }) => ({ from, to })));
282
307
 
283
- if (onFocusChange) {
284
- v.view.dom.className += ` ${eccgui}-intent--${intent}`;
308
+ if (onFocusChange && intent && !v.view.dom.classList?.contains(`${eccgui}-intent--${intent}`)) {
309
+ v.view.dom.classList.add(`${eccgui}-intent--${intent}`);
285
310
  }
286
311
 
287
312
  if (onCursorChange) {
@@ -319,6 +344,7 @@ export const CodeEditor = ({
319
344
  }),
320
345
  parent: parent.current,
321
346
  });
347
+ setView(view);
322
348
 
323
349
  if (view?.dom) {
324
350
  if (height) {
@@ -346,9 +372,39 @@ export const CodeEditor = ({
346
372
  view.destroy();
347
373
  if (setEditorView) {
348
374
  setEditorView(undefined);
375
+ setView(undefined);
349
376
  }
350
377
  };
351
- }, [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
+ };
352
408
 
353
409
  return (
354
410
  <div
@@ -360,10 +416,13 @@ export const CodeEditor = ({
360
416
  data-test-id={dataTestId ? dataTestId : "codemirror-wrapper"}
361
417
  className={
362
418
  `${eccgui}-codeeditor ${eccgui}-codeeditor--mode-${mode}` +
363
- (outerDivAttributes?.className ? ` ${outerDivAttributes?.className}` : "")
419
+ (outerDivAttributes?.className ? ` ${outerDivAttributes?.className}` : "") +
420
+ (hasToolbarSupport ? ` ${eccgui}-codeeditor--has-toolbar` : "")
364
421
  }
365
422
  {...otherCodeEditorProps}
366
- />
423
+ >
424
+ {hasToolbarSupport && editorToolbar(mode)}
425
+ </div>
367
426
  );
368
427
  };
369
428
 
@@ -2,6 +2,9 @@
2
2
 
3
3
  // own vars
4
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;
5
8
 
6
9
  // adjustments
7
10
  // stylelint-disable selector-class-pattern
@@ -14,9 +17,39 @@ $eccgui-color-codeeditor-background: $eccgui-color-textfield-background !default
14
17
  width: 100%;
15
18
  }
16
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
+
17
50
  .cm-editor {
18
51
  width: 100%;
19
- height: 290px;
52
+ height: $eccgui-size-codeeditor-height;
20
53
  clip-path: unset !important; // we may check later why they set inset(0) now
21
54
  background-color: $eccgui-color-codeeditor-background;
22
55
  border-radius: $pt-border-radius;
@@ -27,7 +60,7 @@ $eccgui-color-codeeditor-background: $eccgui-color-textfield-background !default
27
60
  &.#{eccgui}-disabled {
28
61
  @extend .#{$ns}-input, .#{$ns}-disabled;
29
62
 
30
- height: 290px;
63
+ height: $eccgui-size-codeeditor-height;
31
64
  padding: 0;
32
65
  }
33
66