@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.
- package/CHANGELOG.md +35 -2
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js +7 -2
- package/dist/cjs/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js +8 -0
- package/dist/cjs/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/cjs/components/Icon/canonicalIconNames.js +10 -0
- package/dist/cjs/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/cjs/components/OverviewItem/OverviewItem.js +5 -2
- package/dist/cjs/components/OverviewItem/OverviewItem.js.map +1 -1
- package/dist/cjs/components/OverviewItem/OverviewItemList.js +2 -2
- package/dist/cjs/components/OverviewItem/OverviewItemList.js.map +1 -1
- package/dist/cjs/components/TextField/SearchField.js +19 -2
- package/dist/cjs/components/TextField/SearchField.js.map +1 -1
- package/dist/cjs/components/Typography/OverflowText.js +1 -1
- package/dist/cjs/components/Typography/OverflowText.js.map +1 -1
- package/dist/cjs/extensions/codemirror/CodeMirror.js +37 -8
- package/dist/cjs/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/cjs/extensions/codemirror/toolbars/commands/markdown.command.js +278 -0
- package/dist/cjs/extensions/codemirror/toolbars/commands/markdown.command.js.map +1 -0
- package/dist/cjs/extensions/codemirror/toolbars/markdown.toolbar.js +47 -0
- package/dist/cjs/extensions/codemirror/toolbars/markdown.toolbar.js.map +1 -0
- package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js +7 -2
- package/dist/esm/cmem/ActivityControl/ActivityControlWidget.js.map +1 -1
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js +8 -0
- package/dist/esm/components/AutoSuggestion/AutoSuggestion.js.map +1 -1
- package/dist/esm/components/Icon/canonicalIconNames.js +10 -0
- package/dist/esm/components/Icon/canonicalIconNames.js.map +1 -1
- package/dist/esm/components/OverviewItem/OverviewItem.js +5 -2
- package/dist/esm/components/OverviewItem/OverviewItem.js.map +1 -1
- package/dist/esm/components/OverviewItem/OverviewItemList.js +2 -2
- package/dist/esm/components/OverviewItem/OverviewItemList.js.map +1 -1
- package/dist/esm/components/TextField/SearchField.js +35 -2
- package/dist/esm/components/TextField/SearchField.js.map +1 -1
- package/dist/esm/components/Typography/OverflowText.js +1 -1
- package/dist/esm/components/Typography/OverflowText.js.map +1 -1
- package/dist/esm/extensions/codemirror/CodeMirror.js +38 -9
- package/dist/esm/extensions/codemirror/CodeMirror.js.map +1 -1
- package/dist/esm/extensions/codemirror/toolbars/commands/markdown.command.js +283 -0
- package/dist/esm/extensions/codemirror/toolbars/commands/markdown.command.js.map +1 -0
- package/dist/esm/extensions/codemirror/toolbars/markdown.toolbar.js +41 -0
- package/dist/esm/extensions/codemirror/toolbars/markdown.toolbar.js.map +1 -0
- package/dist/types/cmem/ActivityControl/ActivityControlWidget.d.ts +1 -1
- package/dist/types/components/Icon/canonicalIconNames.d.ts +10 -0
- package/dist/types/components/OverviewItem/OverviewItem.d.ts +13 -1
- package/dist/types/components/OverviewItem/OverviewItemList.d.ts +3 -2
- package/dist/types/components/TextField/SearchField.d.ts +1 -1
- package/dist/types/components/Typography/OverflowText.d.ts +23 -2
- package/dist/types/extensions/codemirror/CodeMirror.d.ts +10 -1
- package/dist/types/extensions/codemirror/toolbars/commands/markdown.command.d.ts +55 -0
- package/dist/types/extensions/codemirror/toolbars/markdown.toolbar.d.ts +12 -0
- package/package.json +21 -19
- package/src/cmem/ActivityControl/ActivityControlWidget.tsx +5 -2
- package/src/components/AutoSuggestion/AutoSuggestion.tsx +9 -0
- package/src/components/CodeAutocompleteField/CodeAutocompleteField.stories.tsx +3 -2
- package/src/components/ContextOverlay/ContextOverlay.stories.tsx +15 -4
- package/src/components/Depiction/depiction.scss +7 -0
- package/src/components/Dialog/stories/AlertDialog.stories.tsx +5 -1
- package/src/components/Dialog/stories/Modal.stories.tsx +4 -2
- package/src/components/Dialog/stories/SimpleDialog.stories.tsx +5 -2
- package/src/components/Icon/canonicalIconNames.tsx +10 -0
- package/src/components/OverviewItem/OverviewItem.tsx +24 -1
- package/src/components/OverviewItem/OverviewItemList.tsx +3 -2
- package/src/components/OverviewItem/stories/OverviewItem.stories.tsx +6 -12
- package/src/components/Select/Select.stories.tsx +4 -1
- package/src/components/TextField/SearchField.tsx +37 -9
- package/src/components/TextField/stories/SearchField.stories.tsx +15 -1
- package/src/components/TextField/textfield.scss +17 -3
- package/src/components/Typography/OverflowText.tsx +24 -3
- package/src/components/Typography/stories/OverflowText.stories.tsx +33 -0
- package/src/extensions/codemirror/CodeMirror.stories.tsx +5 -17
- package/src/extensions/codemirror/CodeMirror.tsx +67 -8
- package/src/extensions/codemirror/_codemirror.scss +35 -2
- package/src/extensions/codemirror/toolbars/commands/markdown.command.ts +340 -0
- 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:
|
|
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
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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 *
|
|
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
|
-
& >
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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:
|
|
63
|
+
height: $eccgui-size-codeeditor-height;
|
|
31
64
|
padding: 0;
|
|
32
65
|
}
|
|
33
66
|
|