@eccenca/gui-elements 24.0.1 → 24.1.0-featureimprovepublishingprocesscmem6356.1

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
@@ -0,0 +1,340 @@
1
+ import { type ChangeSpec, EditorSelection } from "@codemirror/state";
2
+ import { EditorView } from "codemirror";
3
+
4
+ import { ValidIconName } from "../../../../components/Icon/canonicalIconNames";
5
+
6
+ enum Commands {
7
+ header1 = "Heading 1",
8
+ header2 = "Heading 2",
9
+ header3 = "Heading 3",
10
+ header4 = "Heading 4",
11
+ header5 = "Heading 5",
12
+ header6 = "Heading 6",
13
+ codeBlock = "Code block",
14
+ quote = "Block quote",
15
+ bold = "Bold",
16
+ italic = "Italic",
17
+ strike = "StrikeThrough",
18
+ inlineCode = "Inline code",
19
+ unorderedList = "Unordered list",
20
+ orderedList = "Ordered list",
21
+ todoList = "Todo list",
22
+ link = "Link",
23
+ image = "Image",
24
+ }
25
+
26
+ type formatConfig = { start: number; startDelimiter: string; stop?: number; endDelimiter?: string };
27
+ type headerLevels = 1 | 2 | 3 | 4 | 5 | 6;
28
+ type ListType = "ul" | "ol" | "todo";
29
+
30
+ //contains all utilities for markdown toolbar
31
+ export default class MarkdownCommand {
32
+ private view: EditorView | null = null;
33
+
34
+ //list of supported commands as well as the valid icon names.
35
+ public static commands = {
36
+ paragraphs: [
37
+ Commands.header1,
38
+ Commands.header2,
39
+ Commands.header3,
40
+ Commands.header4,
41
+ Commands.header5,
42
+ Commands.header6,
43
+ Commands.quote,
44
+ Commands.codeBlock,
45
+ ],
46
+ basic: [
47
+ { title: Commands.bold, icon: "operation-format-text-bold" },
48
+ { title: Commands.italic, icon: "operation-format-text-italic" },
49
+ { title: Commands.strike, icon: "operation-format-text-strikethrough" },
50
+ { title: Commands.inlineCode, icon: "operation-format-text-code" },
51
+ ] as { title: Commands; icon: ValidIconName }[],
52
+ lists: [
53
+ { title: Commands.unorderedList, icon: "operation-format-list-bullet", moniker: "ul" },
54
+ { title: Commands.orderedList, icon: "operation-format-list-numbered", moniker: "ol" },
55
+ { title: Commands.todoList, icon: "operation-format-list-checked", moniker: "todo" },
56
+ ] as { title: Commands; icon: ValidIconName; moniker: string }[],
57
+ attachments: [
58
+ { title: Commands.link, icon: "operation-link" },
59
+ { title: Commands.image, icon: "item-image" },
60
+ ] as { title: Commands; icon: ValidIconName }[],
61
+ } as const;
62
+
63
+ constructor(view: EditorView) {
64
+ this.view = view;
65
+ }
66
+
67
+ /**
68
+ * Supported list types are ol, ul, todo.
69
+ * utility helps to determine which at the start of the line
70
+ */
71
+ private getListTypeOfLine = (text: string): [ListType, number?] | undefined => {
72
+ if (!text) return;
73
+ text = text?.trimStart();
74
+
75
+ if (text.startsWith("- ")) {
76
+ if (text.startsWith("- [ ] ") || text.startsWith("- [x] ")) return ["todo"];
77
+ return ["ul"];
78
+ }
79
+
80
+ const v = text.match(/^(\d+)\. /);
81
+
82
+ return v ? ["ol", Number.parseInt(v[1], 10)] : undefined;
83
+ };
84
+
85
+ //inserts the list delimiters of "-", "- [ ]" and "{number}."
86
+ private createListDelimiter(text: string, type: string, orderedList: { currentIndex: number }) {
87
+ return text.replace(/^(( *)(-( \[[x ]])?|\d+\.) )?/, (...args) => {
88
+ const { space = "" } = args[args.length - 1];
89
+
90
+ let newFlag = "- ";
91
+
92
+ if (type === "ol") {
93
+ newFlag = `${orderedList.currentIndex}. `;
94
+ orderedList.currentIndex++;
95
+ } else if (type === "todo") {
96
+ newFlag = "- [ ] ";
97
+ }
98
+
99
+ return space + newFlag;
100
+ });
101
+ }
102
+
103
+ //factory for different list types.
104
+ private createList = (type: ListType) => {
105
+ if (!this.view) return;
106
+ const view = this.view;
107
+ const doc = view.state.doc;
108
+
109
+ const orderedList = { currentIndex: 1 };
110
+
111
+ view.dispatch(
112
+ view.state.changeByRange((range) => {
113
+ const text = doc.slice(range.from, range.to);
114
+ const changes: ChangeSpec[] = [];
115
+
116
+ let selectionStart: number = range.from;
117
+ let selectionLength: number = range.to - range.from;
118
+
119
+ Array.from({ length: text.lines }).forEach((_, index) => {
120
+ const line = doc.line(doc.lineAt(range.from).number + index);
121
+
122
+ const currentListType = this.getListTypeOfLine(line.text);
123
+
124
+ if (currentListType && currentListType[0] === type) {
125
+ if (currentListType[0] === "ol" && currentListType[1]) {
126
+ orderedList.currentIndex = currentListType[1];
127
+ }
128
+
129
+ return;
130
+ }
131
+ const content = this.createListDelimiter(line.text, type, orderedList);
132
+
133
+ const diffLength = content.length - line.length;
134
+
135
+ changes.push({
136
+ from: line.from,
137
+ to: line.to,
138
+ insert: content,
139
+ });
140
+
141
+ if (index === 0) {
142
+ selectionStart = selectionStart + diffLength;
143
+ } else {
144
+ selectionLength = selectionLength + diffLength;
145
+ }
146
+ });
147
+
148
+ return {
149
+ changes,
150
+ range: EditorSelection.range(selectionStart, selectionStart + selectionLength),
151
+ };
152
+ })
153
+ );
154
+
155
+ view.focus();
156
+ };
157
+
158
+ private enforceCursorFocus = (cursorPosition: number) => {
159
+ if (!this.view) return;
160
+ const view = this.view;
161
+ setTimeout(() => {
162
+ view.dispatch({
163
+ selection: EditorSelection.cursor(cursorPosition),
164
+ });
165
+ view.focus();
166
+ }, 50);
167
+ };
168
+
169
+ //supported headers from h1-h6, h6 being the smallest
170
+ private createHeading = (level: headerLevels) => {
171
+ if (!this.view) return;
172
+ const view = this.view;
173
+ const state = view.state;
174
+
175
+ const flags = "#".repeat(level) + " ";
176
+
177
+ let lastCursorPosition = 0;
178
+
179
+ view.dispatch(
180
+ state.changeByRange((range) => {
181
+ const line = state.doc.lineAt(range.from);
182
+
183
+ const content = line.text.replace(/^((#+) )?/, flags);
184
+
185
+ const diffLength = content.length - line.length;
186
+ lastCursorPosition = line.to + diffLength;
187
+ return {
188
+ changes: {
189
+ from: line.from,
190
+ to: line.to,
191
+ insert: content,
192
+ },
193
+ range: EditorSelection.range(range.anchor + diffLength, range.head + diffLength),
194
+ };
195
+ })
196
+ );
197
+
198
+ this.enforceCursorFocus(lastCursorPosition);
199
+ };
200
+
201
+ private applyFormatting = ({
202
+ start,
203
+ startDelimiter,
204
+ endDelimiter = startDelimiter,
205
+ stop = start,
206
+ }: formatConfig) => {
207
+ if (!this.view) return;
208
+ const view = this.view;
209
+ const { from, to } = view.state.selection.main;
210
+ const text = view.state.sliceDoc(from, to);
211
+ view.dispatch(
212
+ view.state.changeByRange((range) => {
213
+ return {
214
+ changes: [{ from: range.from, to: range.to, insert: `${startDelimiter}${text}${endDelimiter}` }],
215
+ range: EditorSelection.range(range.from + start, range.to + stop),
216
+ };
217
+ })
218
+ );
219
+ view.focus();
220
+ };
221
+
222
+ private applyAttachment = (type: Commands.link | Commands.image) => {
223
+ if (!this.view) return;
224
+ const view = this.view;
225
+ const { state } = view;
226
+ const isImageAttachmentType = type === Commands.image;
227
+
228
+ const { doc } = state;
229
+
230
+ view.dispatch(
231
+ state.changeByRange((range) => {
232
+ const { from, to } = range;
233
+
234
+ const text = doc.sliceString(from, to);
235
+
236
+ const link = `${isImageAttachmentType ? `!` : ""}[${text}]()`;
237
+
238
+ const cursor = from + (text.length ? 3 + text.length : 1 + Number(isImageAttachmentType));
239
+
240
+ return {
241
+ changes: [
242
+ {
243
+ from,
244
+ to,
245
+ insert: link,
246
+ },
247
+ ],
248
+ range: EditorSelection.range(cursor, cursor),
249
+ };
250
+ })
251
+ );
252
+
253
+ view.focus();
254
+ };
255
+
256
+ private applyQuoteFormatting = () => {
257
+ if (!this.view) return;
258
+ const view = this.view;
259
+ const { state } = view;
260
+ const { doc } = state;
261
+
262
+ let lastCursorPosition = 0;
263
+
264
+ view.dispatch(
265
+ view.state.changeByRange((range) => {
266
+ const startLine = doc.lineAt(range.from);
267
+
268
+ const text = doc.slice(range.from, range.to);
269
+
270
+ const lineCount = text.lines;
271
+
272
+ const changes: ChangeSpec[] = [];
273
+
274
+ let selectionStart: number = range.from;
275
+ let selectionLength: number = range.to - range.from;
276
+
277
+ new Array(lineCount).fill(0).forEach((_, index) => {
278
+ const line = doc.line(startLine.number + index);
279
+
280
+ if (line.text.startsWith("> ")) {
281
+ return;
282
+ }
283
+ changes.push({
284
+ from: line.from,
285
+ insert: "> ",
286
+ });
287
+
288
+ if (index === 0) {
289
+ selectionStart = selectionStart + 2;
290
+ } else {
291
+ selectionLength += 2;
292
+ }
293
+ });
294
+
295
+ lastCursorPosition = selectionStart + selectionLength;
296
+
297
+ return {
298
+ changes,
299
+ range: EditorSelection.range(selectionStart, selectionStart + selectionLength),
300
+ };
301
+ })
302
+ );
303
+ this.enforceCursorFocus(lastCursorPosition);
304
+ };
305
+
306
+ executeCommand = (command: Commands): true | void => {
307
+ switch (command) {
308
+ case Commands.bold:
309
+ return this.applyFormatting({ start: 2, startDelimiter: "**" });
310
+ case Commands.italic:
311
+ return this.applyFormatting({ start: 1, startDelimiter: "*" });
312
+ case Commands.codeBlock:
313
+ return this.applyFormatting({ start: 3, startDelimiter: "```\n", endDelimiter: "\n```" });
314
+ case Commands.strike:
315
+ return this.applyFormatting({ start: 2, startDelimiter: "~~" });
316
+ case Commands.inlineCode:
317
+ return this.applyFormatting({ start: 1, startDelimiter: "`" });
318
+ case Commands.header1:
319
+ case Commands.header2:
320
+ case Commands.header3:
321
+ case Commands.header4:
322
+ case Commands.header5:
323
+ case Commands.header6:
324
+ return this.createHeading(Number(command.slice(-1)) as headerLevels);
325
+ case Commands.unorderedList:
326
+ case Commands.orderedList:
327
+ case Commands.todoList:
328
+ return this.createList(
329
+ MarkdownCommand.commands.lists.find((l) => l.title === command)?.moniker as ListType
330
+ );
331
+ case Commands.image:
332
+ case Commands.link:
333
+ return this.applyAttachment(command);
334
+ case Commands.quote:
335
+ return this.applyQuoteFormatting();
336
+ default:
337
+ return; //do nothing;
338
+ }
339
+ };
340
+ }
@@ -0,0 +1,117 @@
1
+ import React from "react";
2
+ import { EditorView } from "codemirror";
3
+
4
+ import { Button } from "../../../components/Button/Button";
5
+ import { ContextMenu } from "../../../components/ContextOverlay";
6
+ import { Icon, IconButton } from "../../../components/Icon";
7
+ import { MenuItem } from "../../../components/Menu";
8
+ import { Spacing } from "../../../components/Separation/Spacing";
9
+ import { Toolbar, ToolbarSection } from "../../../components/Toolbar";
10
+
11
+ import MarkdownCommand from "./commands/markdown.command";
12
+
13
+ interface MarkdownToolbarProps {
14
+ view?: EditorView;
15
+ togglePreviewStatus: () => void;
16
+ showPreview: boolean;
17
+ translate: (key: string) => string | false;
18
+ disabled?: boolean;
19
+ readonly?: boolean;
20
+ }
21
+
22
+ export const MarkdownToolbar: React.FC<MarkdownToolbarProps> = ({
23
+ view,
24
+ togglePreviewStatus,
25
+ showPreview,
26
+ disabled,
27
+ readonly,
28
+ translate
29
+ }) => {
30
+ const commandRef = React.useRef<MarkdownCommand | null>(null);
31
+
32
+ React.useEffect(() => {
33
+ if (view) {
34
+ commandRef.current = new MarkdownCommand(view);
35
+ }
36
+ }, [view]);
37
+
38
+ const getTranslation = (fallback: string) : string => {
39
+ const key = fallback.toLowerCase().replace(" ", "-");
40
+ return translate(key) || fallback;
41
+ }
42
+
43
+ const { basic, lists, attachments } = MarkdownCommand.commands;
44
+ return (
45
+ <Toolbar noWrap>
46
+ <ToolbarSection canShrink hideOverflow>
47
+ <ContextMenu
48
+ togglerElement={
49
+ <Button
50
+ rightIcon={<Icon name="toggler-showmore" />}
51
+ text={getTranslation("Paragraphs")}
52
+ minimal
53
+ fill
54
+ ellipsizeText
55
+ disabled={showPreview || disabled || readonly}
56
+ />
57
+ }
58
+ >
59
+ {MarkdownCommand.commands.paragraphs.map((p, i) => (
60
+ <MenuItem
61
+ key={p}
62
+ text={
63
+ <>
64
+ <span style={p.startsWith("Head") ? { fontSize: 22 - (i * (22 - 12)) / 5 } : {}}>
65
+ {getTranslation(p)}
66
+ </span>
67
+ </>
68
+ }
69
+ onClick={() => commandRef.current?.executeCommand(p)}
70
+ />
71
+ ))}
72
+ </ContextMenu>
73
+ </ToolbarSection>
74
+ <ToolbarSection canShrink>
75
+ <Spacing vertical hasDivider size="tiny" />
76
+ </ToolbarSection>
77
+
78
+ {[basic, lists, attachments].map((section, i) => {
79
+ return (
80
+ <React.Fragment key={i}>
81
+ <ToolbarSection>
82
+ {section.map((command) => {
83
+ return (
84
+ <IconButton
85
+ key={command.title}
86
+ name={command.icon}
87
+ onClick={() => commandRef.current?.executeCommand(command.title)}
88
+ text={getTranslation(command.title)}
89
+ disabled={showPreview || disabled || readonly}
90
+ />
91
+ );
92
+ })}
93
+ </ToolbarSection>
94
+ {i < 2 && (
95
+ <ToolbarSection canShrink>
96
+ <Spacing vertical hasDivider size="tiny" />
97
+ </ToolbarSection>
98
+ )}
99
+ </React.Fragment>
100
+ );
101
+ })}
102
+ <ToolbarSection canGrow canShrink>
103
+ <Spacing vertical size="small" />
104
+ </ToolbarSection>
105
+ <ToolbarSection canShrink hideOverflow>
106
+ <Button
107
+ minimal
108
+ ellipsizeText
109
+ onClick={togglePreviewStatus}
110
+ text={showPreview ? getTranslation("Continue editing") : getTranslation("Preview")}
111
+ icon={showPreview ? "item-edit" : "item-viewdetails"}
112
+ disabled={disabled}
113
+ />
114
+ </ToolbarSection>
115
+ </Toolbar>
116
+ );
117
+ };
@@ -0,0 +1,7 @@
1
+ import { Diagnostic } from "@codemirror/lint";
2
+ import { Extension } from "@codemirror/state";
3
+ import { EditorView } from "@codemirror/view";
4
+
5
+ export type Linter = (view: EditorView) => ReadonlyArray<Diagnostic> | Promise<ReadonlyArray<Diagnostic>>;
6
+
7
+ export type ExtensionCreator<T = unknown> = (options?: T) => Extension;
@@ -15,3 +15,4 @@ $reactflow-edge-stroke-color-selected: $eccgui-color-accent !default;
15
15
  $reactflow-transition-time: 0.25s !default;
16
16
  $reactflow-transition-function: "" !default;
17
17
  $reactflow-transition-anglestart: -90deg;
18
+ $reactflow-cursor-delimiter-offset: 0.9 * $eccgui-size-block-whitespace;