@ember-eui/core 1.2.6 → 1.4.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.
- package/CHANGELOG.md +24 -0
- package/addon/components/common.ts +13 -0
- package/addon/components/eui-button/index.hbs +2 -0
- package/addon/components/eui-button-content/index.hbs +1 -0
- package/addon/components/eui-button-empty/index.hbs +2 -0
- package/addon/components/eui-button-icon/index.hbs +2 -0
- package/addon/components/eui-code/index.hbs +9 -0
- package/addon/components/eui-code-block/index.d.ts +2 -0
- package/addon/components/eui-code-block/index.hbs +10 -0
- package/addon/components/eui-code-block-impl/code-block-controls/index.hbs +33 -0
- package/addon/components/eui-code-block-impl/index.hbs +111 -0
- package/addon/components/eui-code-block-impl/index.ts +121 -0
- package/addon/components/eui-copy/index.hbs +8 -0
- package/addon/components/eui-copy/index.ts +37 -0
- package/addon/components/eui-icon/index.hbs +37 -32
- package/addon/components/eui-icon/index.ts +1 -1
- package/addon/components/eui-inner-text/index.hbs +1 -0
- package/addon/components/eui-inner-text/index.ts +61 -0
- package/addon/components/eui-markdown-editor/index.hbs +63 -0
- package/addon/components/eui-markdown-editor/index.ts +221 -0
- package/addon/components/eui-markdown-editor-drop-zone/index.hbs +21 -0
- package/addon/components/eui-markdown-editor-drop-zone/index.ts +5 -0
- package/addon/components/eui-markdown-editor-footer/index.hbs +108 -0
- package/addon/components/eui-markdown-editor-footer/index.ts +20 -0
- package/addon/components/eui-markdown-editor-text-area/index.hbs +8 -0
- package/addon/components/eui-markdown-editor-toolbar/index.hbs +86 -0
- package/addon/components/eui-markdown-editor-toolbar/index.ts +97 -0
- package/addon/components/eui-markdown-format/index.hbs +13 -0
- package/addon/components/eui-markdown-format/index.ts +55 -0
- package/addon/components/eui-markdown-format/markdown-checkbox/index.hbs +8 -0
- package/addon/components/eui-markdown-format/markdown-checkbox/index.ts +28 -0
- package/addon/components/eui-markdown-format/markdown-code/index.hbs +3 -0
- package/addon/components/eui-markdown-format/markdown-code-block/index.hbs +7 -0
- package/addon/components/eui-markdown-format/markdown-tooltip/index.hbs +8 -0
- package/addon/components/markdown-checkmark-icon/index.hbs +0 -0
- package/addon/modifiers/get-cursor-node.ts +54 -0
- package/addon/modifiers/resize-observer.ts +6 -2
- package/addon/utils/copy-to-clipboard.ts +70 -0
- package/addon/utils/css-mappings/eui-code-block-impl.ts +24 -0
- package/addon/utils/css-mappings/index.ts +2 -0
- package/addon/utils/markdown/markdown-actions.ts +616 -0
- package/addon/utils/markdown/markdown-modes.ts +23 -0
- package/addon/utils/markdown/markdown-types.ts +140 -0
- package/addon/utils/markdown/markdown-unified-plugins.d.ts +27 -0
- package/addon/utils/markdown/plugins/markdown-add-components.ts +63 -0
- package/addon/utils/markdown/plugins/markdown-checkbox.ts +80 -0
- package/addon/utils/markdown/plugins/markdown-default-plugins.ts +77 -0
- package/addon/utils/markdown/plugins/markdown-tooltip.ts +113 -0
- package/addon/utils/markdown/plugins/to-dom.ts +93 -0
- package/app/components/eui-code/index.js +1 -0
- package/app/components/eui-code-block/index.js +1 -0
- package/app/components/eui-code-block-impl/code-block-controls/index.js +1 -0
- package/app/components/eui-code-block-impl/index.js +1 -0
- package/app/components/eui-copy/index.js +1 -0
- package/app/components/eui-inner-text/index.js +1 -0
- package/app/components/eui-markdown-editor/index.js +1 -0
- package/app/components/eui-markdown-editor-drop-zone/index.js +1 -0
- package/app/components/eui-markdown-editor-footer/index.js +1 -0
- package/app/components/eui-markdown-editor-text-area/index.js +1 -0
- package/app/components/eui-markdown-editor-toolbar/index.js +1 -0
- package/app/components/eui-markdown-format/index.js +1 -0
- package/app/components/eui-markdown-format/markdown-checkbox/index.js +1 -0
- package/app/components/eui-markdown-format/markdown-code/index.js +1 -0
- package/app/components/eui-markdown-format/markdown-code-block/index.js +1 -0
- package/app/components/eui-markdown-format/markdown-tooltip/index.js +1 -0
- package/app/modifiers/get-cursor-node.js +1 -0
- package/app/styles/ember-eui.scss +13 -0
- package/app/utils/markdown/plugins/markdown-add-components.js +1 -0
- package/app/utils/markdown/plugins/markdown-default-plugins.js +1 -0
- package/docs/editors/code/code-block-demo/demo1.md +62 -0
- package/docs/editors/code/code-block.md +1 -0
- package/docs/editors/code/inline-demo/demo1.md +49 -0
- package/docs/editors/code/inline.md +1 -0
- package/docs/editors/markdown-editor/base-editor-demo/demo1.md +120 -0
- package/docs/editors/markdown-editor/base-editor.md +1 -0
- package/package.json +16 -3
- package/public/markdown-checkmark.svg +3 -0
- package/public/markdown-logo.svg +3 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
import { cached, tracked } from '@glimmer/tracking';
|
|
4
|
+
import { argOrDefaultDecorator as argOrDefault } from '../../helpers/arg-or-default';
|
|
5
|
+
import MarkdownActions from '../../utils/markdown/markdown-actions';
|
|
6
|
+
import {
|
|
7
|
+
MODE_EDITING,
|
|
8
|
+
MODE_VIEWING
|
|
9
|
+
} from '../../utils/markdown/markdown-modes';
|
|
10
|
+
import {
|
|
11
|
+
defaultParsingPlugins,
|
|
12
|
+
defaultProcessingPlugins
|
|
13
|
+
} from '../../utils/markdown/plugins/markdown-default-plugins';
|
|
14
|
+
import { uniqueId } from '../../helpers/unique-id';
|
|
15
|
+
import unified from 'unified';
|
|
16
|
+
import * as MarkdownTooltipPlugin from '../../utils/markdown/plugins/markdown-tooltip';
|
|
17
|
+
import {
|
|
18
|
+
EuiMarkdownEditorUiPlugin,
|
|
19
|
+
EuiMarkdownAstNodePosition,
|
|
20
|
+
EuiMarkdownAstNode
|
|
21
|
+
} from '../../utils/markdown/markdown-types';
|
|
22
|
+
import { Processor } from 'unified';
|
|
23
|
+
|
|
24
|
+
export interface EuiMarkdownEditorArgs {
|
|
25
|
+
initialViewMode?: string;
|
|
26
|
+
editorId?: string;
|
|
27
|
+
uiPlugins: EuiMarkdownEditorUiPlugin[];
|
|
28
|
+
value: string;
|
|
29
|
+
onChange: (str: string) => void;
|
|
30
|
+
onParse: (
|
|
31
|
+
parseError: string | null,
|
|
32
|
+
parsed: { messages: string[]; ast: EuiMarkdownAstNode }
|
|
33
|
+
) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// function isNewLine(char: string | undefined): boolean {
|
|
37
|
+
// if (char == null) return true;
|
|
38
|
+
// return !!char.match(/[\r\n]/);
|
|
39
|
+
// }
|
|
40
|
+
// function padWithNewlinesIfNeeded(textarea: HTMLTextAreaElement, text: string) {
|
|
41
|
+
// const selectionStart = textarea.selectionStart;
|
|
42
|
+
// const selectionEnd = textarea.selectionEnd;
|
|
43
|
+
|
|
44
|
+
// // block parsing requires two leading new lines and none trailing, but we add an extra trailing line for readability
|
|
45
|
+
// const isPrevNewLine = isNewLine(textarea.value[selectionStart - 1]);
|
|
46
|
+
// const isPrevPrevNewLine = isNewLine(textarea.value[selectionStart - 2]);
|
|
47
|
+
// const isNextNewLine = isNewLine(textarea.value[selectionEnd]);
|
|
48
|
+
|
|
49
|
+
// // pad text with newlines as needed
|
|
50
|
+
// text = `${isPrevNewLine ? '' : '\n'}${isPrevPrevNewLine ? '' : '\n'}${text}${
|
|
51
|
+
// isNextNewLine ? '' : '\n'
|
|
52
|
+
// }`;
|
|
53
|
+
// return text;
|
|
54
|
+
// }
|
|
55
|
+
|
|
56
|
+
export default class EuiMarkdownEditorComponent extends Component<EuiMarkdownEditorArgs> {
|
|
57
|
+
// Defaults
|
|
58
|
+
@argOrDefault(defaultParsingPlugins)
|
|
59
|
+
declare parsingPluginList: typeof defaultParsingPlugins;
|
|
60
|
+
|
|
61
|
+
@argOrDefault(250) declare height: number | string;
|
|
62
|
+
@argOrDefault(500) declare maxHeight: number | string;
|
|
63
|
+
@argOrDefault(true) declare autoExpandPreview: boolean;
|
|
64
|
+
|
|
65
|
+
@argOrDefault(defaultProcessingPlugins)
|
|
66
|
+
declare processingPluginList: typeof defaultProcessingPlugins;
|
|
67
|
+
|
|
68
|
+
@tracked selectedNode: Node | null = null;
|
|
69
|
+
@tracked editorId = this.args.editorId ?? uniqueId();
|
|
70
|
+
@tracked viewMode = this.args.initialViewMode || MODE_EDITING;
|
|
71
|
+
@tracked textareaRef: HTMLTextAreaElement | null = null;
|
|
72
|
+
@tracked previewRef: HTMLDivElement | null = null;
|
|
73
|
+
@tracked editorToolbarRef: HTMLDivElement | null = null;
|
|
74
|
+
@tracked currentHeight: number | string = 250;
|
|
75
|
+
@tracked editorFooterHeight: number = 0;
|
|
76
|
+
@tracked editorToolbarHeight: number = 0;
|
|
77
|
+
|
|
78
|
+
markdownActions: MarkdownActions | null = null;
|
|
79
|
+
|
|
80
|
+
get toolbarPlugins() {
|
|
81
|
+
return [MarkdownTooltipPlugin.plugin, ...(this.args.uiPlugins || [])];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
constructor(owner: unknown, args: EuiMarkdownEditorArgs) {
|
|
85
|
+
super(owner, args);
|
|
86
|
+
this.markdownActions = new MarkdownActions(
|
|
87
|
+
this.editorId,
|
|
88
|
+
this.toolbarPlugins
|
|
89
|
+
);
|
|
90
|
+
this.currentHeight = this.height;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get previewHeight() {
|
|
94
|
+
if (this.height === 'full') {
|
|
95
|
+
return `calc(100% - ${this.editorFooterHeight}px)`;
|
|
96
|
+
}
|
|
97
|
+
return `${this.currentHeight}px`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get textAreaHeight() {
|
|
101
|
+
return this.height === 'full'
|
|
102
|
+
? '100%'
|
|
103
|
+
: `calc(${(this.height as number) - this.editorFooterHeight}px)`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get textAreaMaxHeight() {
|
|
107
|
+
return this.height !== 'full'
|
|
108
|
+
? `${(this.maxHeight as number) - this.editorFooterHeight}px`
|
|
109
|
+
: '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get editorToggleContainerHeight() {
|
|
113
|
+
return `calc(100% - ${this.editorToolbarHeight}px)`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@action
|
|
117
|
+
onResize() {
|
|
118
|
+
if (this.textareaRef && this.isEditing && this.height !== 'full') {
|
|
119
|
+
const resizedTextareaHeight =
|
|
120
|
+
this.textareaRef.offsetHeight + this.editorFooterHeight;
|
|
121
|
+
|
|
122
|
+
this.currentHeight = resizedTextareaHeight;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@action
|
|
127
|
+
updateCurrentHeight() {
|
|
128
|
+
let { isPreviewing, autoExpandPreview, height, previewRef, currentHeight } =
|
|
129
|
+
this;
|
|
130
|
+
if (isPreviewing && autoExpandPreview && height !== 'full' && previewRef) {
|
|
131
|
+
if (previewRef.scrollHeight > currentHeight) {
|
|
132
|
+
// scrollHeight does not include the border or margin
|
|
133
|
+
// so we ask for the computed value for those,
|
|
134
|
+
// which is always in pixels because getComputedValue
|
|
135
|
+
// returns the resolved values
|
|
136
|
+
const elementComputedStyle = window.getComputedStyle(previewRef);
|
|
137
|
+
const borderWidth =
|
|
138
|
+
parseFloat(elementComputedStyle.borderTopWidth) +
|
|
139
|
+
parseFloat(elementComputedStyle.borderBottomWidth);
|
|
140
|
+
const marginWidth =
|
|
141
|
+
parseFloat(elementComputedStyle.marginTop) +
|
|
142
|
+
parseFloat(elementComputedStyle.marginBottom);
|
|
143
|
+
|
|
144
|
+
// then add an extra pixel for safety and because the scrollHeight value is rounded
|
|
145
|
+
const extraHeight = borderWidth + marginWidth + 1;
|
|
146
|
+
|
|
147
|
+
this.currentHeight = previewRef.scrollHeight + extraHeight;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@action
|
|
153
|
+
setTextAreaRef(ref: HTMLTextAreaElement) {
|
|
154
|
+
this.textareaRef = ref;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@action
|
|
158
|
+
setEditorToolbarRef(ref: HTMLDivElement) {
|
|
159
|
+
this.editorToolbarRef = ref;
|
|
160
|
+
this.editorToolbarHeight = ref.offsetHeight;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@action
|
|
164
|
+
replaceNode(position: EuiMarkdownAstNodePosition, next: string) {
|
|
165
|
+
let value = this.args.value;
|
|
166
|
+
const leading = value.substr(0, position.start.offset);
|
|
167
|
+
const trailing = value.substr(position.end.offset);
|
|
168
|
+
this.args.onChange?.(`${leading}${next}${trailing}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get isEditing() {
|
|
172
|
+
return this.viewMode === MODE_EDITING;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
get isPreviewing() {
|
|
176
|
+
return this.viewMode === MODE_VIEWING;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@action
|
|
180
|
+
setViewMode() {
|
|
181
|
+
this.viewMode = this.isPreviewing ? MODE_EDITING : MODE_VIEWING;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@cached
|
|
185
|
+
get parser() {
|
|
186
|
+
const Compiler = (tree: any) => {
|
|
187
|
+
return tree;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
function identityCompiler(this: Processor) {
|
|
191
|
+
this.Compiler = Compiler;
|
|
192
|
+
}
|
|
193
|
+
return unified().use(this.parsingPluginList).use(identityCompiler);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@cached
|
|
197
|
+
get parsed() {
|
|
198
|
+
try {
|
|
199
|
+
const parsed = this.parser.processSync(this.args.value);
|
|
200
|
+
return [parsed, null];
|
|
201
|
+
} catch (e) {
|
|
202
|
+
return [null, e];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@action
|
|
207
|
+
onParse() {
|
|
208
|
+
if (this.args.onParse) {
|
|
209
|
+
const [parsed, parseError] = this.parsed;
|
|
210
|
+
const onParse = this.args.onParse;
|
|
211
|
+
const messages = parsed ? parsed.messages : [];
|
|
212
|
+
const ast = parsed ? parsed.result ?? parsed.contents : null;
|
|
213
|
+
onParse(parseError, { messages, ast });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@action
|
|
218
|
+
setSelectedNode(node: Node) {
|
|
219
|
+
this.selectedNode = node;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<div
|
|
2
|
+
class={{class-names
|
|
3
|
+
"euiMarkdownEditorDropZone"
|
|
4
|
+
(if this.isDragging "euiMarkdownEditorDropZone--isDragging")
|
|
5
|
+
(if @hasUnacceptedItems "euiMarkdownEditorDropZone--hasError")
|
|
6
|
+
(if this.isDraggingError "euiMarkdownEditorDropZone--isDraggingError")
|
|
7
|
+
}}
|
|
8
|
+
>
|
|
9
|
+
{{yield}}
|
|
10
|
+
<EuiMarkdownEditorFooter
|
|
11
|
+
@uiPlugins={{@uiPlugins}}
|
|
12
|
+
{{!-- openFiles={() => {
|
|
13
|
+
setHasUnacceptedItems(false);
|
|
14
|
+
open();
|
|
15
|
+
}} --}}
|
|
16
|
+
@isUploadingFiles={{this.isUploadingFiles}}
|
|
17
|
+
@hasUnacceptedItems={{@hasUnacceptedItems}}
|
|
18
|
+
{{! dropHandlers={dropHandlers} }}
|
|
19
|
+
@errors={{@errors}}
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<div class="euiMarkdownEditorFooter">
|
|
2
|
+
<div class="euiMarkdownEditorFooter__actions">
|
|
3
|
+
|
|
4
|
+
{{#if @isUploadingFiles}}
|
|
5
|
+
{{! pending implementation}}
|
|
6
|
+
<EuiButtonIcon
|
|
7
|
+
@iconType={{component "eui-loading-spinner"}}
|
|
8
|
+
{{! aria-label={ariaLabels.uploadingFiles} }}
|
|
9
|
+
/>
|
|
10
|
+
{{/if}}
|
|
11
|
+
{{#if (and @errors @errors.length)}}
|
|
12
|
+
<EuiPopover
|
|
13
|
+
@isOpen={{this.isPopoverOpen}}
|
|
14
|
+
@closePopover={{set this "isPopoverOpen" false}}
|
|
15
|
+
>
|
|
16
|
+
<:button>
|
|
17
|
+
<EuiButtonEmpty
|
|
18
|
+
@iconType="crossInACircleFilled"
|
|
19
|
+
@size="s"
|
|
20
|
+
@color="danger"
|
|
21
|
+
{{! aria-label={ariaLabels.showSyntaxErrors} }}
|
|
22
|
+
{{on "click" (set this "isPopoverOpen" (not this.isPopoverOpen))}}
|
|
23
|
+
>
|
|
24
|
+
{{@errors.length}}
|
|
25
|
+
</EuiButtonEmpty>
|
|
26
|
+
</:button>
|
|
27
|
+
</EuiPopover>
|
|
28
|
+
{{/if}}
|
|
29
|
+
</div>
|
|
30
|
+
<EuiButtonIcon
|
|
31
|
+
class="euiMarkdownEditorFooter__help"
|
|
32
|
+
@iconType={{this.markdownLogo}}
|
|
33
|
+
@color="text"
|
|
34
|
+
@useSvg={{true}}
|
|
35
|
+
{{! aria-label={ariaLabels.showMarkdownHelp} }}
|
|
36
|
+
{{on "click" (set this "isShowingHelp" (not this.isShowingHelp))}}
|
|
37
|
+
/>
|
|
38
|
+
|
|
39
|
+
{{#if this.isShowingHelp}}
|
|
40
|
+
<EuiOverlayMask>
|
|
41
|
+
<EuiModal @onClose={{set this "isShowingHelp" false}}>
|
|
42
|
+
<EuiModalHeader>
|
|
43
|
+
<EuiTitle>
|
|
44
|
+
<h3>
|
|
45
|
+
Syntax Help
|
|
46
|
+
{{! <EuiI18n
|
|
47
|
+
token="euiMarkdownEditorFooter.syntaxTitle"
|
|
48
|
+
default="Syntax help"
|
|
49
|
+
/> }}
|
|
50
|
+
</h3>
|
|
51
|
+
</EuiTitle>
|
|
52
|
+
</EuiModalHeader>
|
|
53
|
+
<EuiModalBody>
|
|
54
|
+
|
|
55
|
+
<EuiText>
|
|
56
|
+
<p>The editor uses</p>
|
|
57
|
+
<a
|
|
58
|
+
href="https://github.github.com/gfm/"
|
|
59
|
+
target="_blank"
|
|
60
|
+
rel="noreferrer noopener"
|
|
61
|
+
>
|
|
62
|
+
Github flavored markdown
|
|
63
|
+
</a>
|
|
64
|
+
<p>You can also utilize these additional syntax plugins to add rich
|
|
65
|
+
content to yoru text.</p>
|
|
66
|
+
|
|
67
|
+
{{! <EuiI18n
|
|
68
|
+
tokens={[
|
|
69
|
+
'euiMarkdownEditorFooter.descriptionPrefix',
|
|
70
|
+
'euiMarkdownEditorFooter.descriptionSuffix',
|
|
71
|
+
]}
|
|
72
|
+
defaults={[
|
|
73
|
+
'This editor uses',
|
|
74
|
+
'You can also utilize these additional syntax plugins to add rich content to your text.',
|
|
75
|
+
]}>
|
|
76
|
+
{([descriptionPrefix, descriptionSuffix]: ReactChild[]) => (
|
|
77
|
+
<p>
|
|
78
|
+
{descriptionPrefix}{' '}
|
|
79
|
+
<a
|
|
80
|
+
href="https://github.github.com/gfm/"
|
|
81
|
+
target="_blank">
|
|
82
|
+
Github flavored markdown
|
|
83
|
+
</a>
|
|
84
|
+
. {descriptionSuffix}
|
|
85
|
+
</p>
|
|
86
|
+
)}
|
|
87
|
+
</EuiI18n> }}
|
|
88
|
+
</EuiText>
|
|
89
|
+
<EuiHorizontalRule />
|
|
90
|
+
{{#each @uiPlugins as |uiPlugin|}}
|
|
91
|
+
{{#if uiPlugin.helpText}}
|
|
92
|
+
<EuiTitle size="xxs">
|
|
93
|
+
<p>
|
|
94
|
+
<strong>{{uiPlugin.name}}</strong>
|
|
95
|
+
</p>
|
|
96
|
+
</EuiTitle>
|
|
97
|
+
<EuiSpacer size="s" />
|
|
98
|
+
<EuiMarkdownFormat @value={{uiPlugin.helpText}} />
|
|
99
|
+
<EuiSpacer size="l" />
|
|
100
|
+
|
|
101
|
+
{{/if}}
|
|
102
|
+
{{/each}}
|
|
103
|
+
</EuiModalBody>
|
|
104
|
+
</EuiModal>
|
|
105
|
+
</EuiOverlayMask>
|
|
106
|
+
|
|
107
|
+
{{/if}}
|
|
108
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { getOwner } from '@ember/application';
|
|
4
|
+
|
|
5
|
+
export interface EuiMarkdownEditorFooterArgs {}
|
|
6
|
+
|
|
7
|
+
export default class EuiMarkdownEditorFooterComponent extends Component<EuiMarkdownEditorFooterArgs> {
|
|
8
|
+
@tracked isPopoverOpen = false;
|
|
9
|
+
@tracked isShowingHelp = false;
|
|
10
|
+
|
|
11
|
+
get svgPath() {
|
|
12
|
+
const config = getOwner(this).resolveRegistration('config:environment');
|
|
13
|
+
const svgPath = config?.['@ember-eui/core']?.svgPath || 'svg/';
|
|
14
|
+
return svgPath;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get markdownLogo() {
|
|
18
|
+
return `${this.svgPath}markdown-logo`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<div class="euiMarkdownEditorToolbar">
|
|
2
|
+
<div class="euiMarkdownEditorToolbar__buttons">
|
|
3
|
+
{{#each this.boldItalicsButtons as |item|}}
|
|
4
|
+
<EuiToolTip @content={{item.label}} @delay="long">
|
|
5
|
+
<EuiButtonIcon
|
|
6
|
+
@color="text"
|
|
7
|
+
{{on "click" (fn this.handleMdButtonClick item.id)}}
|
|
8
|
+
@iconType={{item.iconType}}
|
|
9
|
+
aria-label={{item.label}}
|
|
10
|
+
@isDisabled={{this.isPreviewing}}
|
|
11
|
+
/>
|
|
12
|
+
</EuiToolTip>
|
|
13
|
+
|
|
14
|
+
{{/each}}
|
|
15
|
+
<span className="euiMarkdownEditorToolbar__divider"></span>
|
|
16
|
+
{{#each this.listButtons as |item|}}
|
|
17
|
+
<EuiToolTip @content={{item.label}} @delay="long">
|
|
18
|
+
<EuiButtonIcon
|
|
19
|
+
@color="text"
|
|
20
|
+
{{on "click" (fn this.handleMdButtonClick item.id)}}
|
|
21
|
+
@iconType={{item.iconType}}
|
|
22
|
+
aria-label={{item.label}}
|
|
23
|
+
@useSvg={{item.useSvg}}
|
|
24
|
+
@isDisabled={{this.isPreviewing}}
|
|
25
|
+
/>
|
|
26
|
+
</EuiToolTip>
|
|
27
|
+
|
|
28
|
+
{{/each}}
|
|
29
|
+
<span className="euiMarkdownEditorToolbar__divider"></span>
|
|
30
|
+
{{#each this.quoteCodeLinkButtons as |item|}}
|
|
31
|
+
<EuiToolTip @content={{item.label}} @delay="long">
|
|
32
|
+
<EuiButtonIcon
|
|
33
|
+
@color="text"
|
|
34
|
+
{{on "click" (fn this.handleMdButtonClick item.id)}}
|
|
35
|
+
@iconType={{item.iconType}}
|
|
36
|
+
aria-label={{item.label}}
|
|
37
|
+
@isDisabled={{this.isPreviewing}}
|
|
38
|
+
/>
|
|
39
|
+
</EuiToolTip>
|
|
40
|
+
|
|
41
|
+
{{/each}}
|
|
42
|
+
{{#if (gte this.uiPlugins.length 0)}}
|
|
43
|
+
<span className="euiMarkdownEditorToolbar__divider"></span>
|
|
44
|
+
{{#each this.uiPlugins as |plugin|}}
|
|
45
|
+
{{#let
|
|
46
|
+
(and @selectedNode (eq @selectedNode.type plugin.name))
|
|
47
|
+
as |isSelectedNodeType|
|
|
48
|
+
}}
|
|
49
|
+
<EuiToolTip @content={{plugin.button.label}} @delay="long">
|
|
50
|
+
<EuiButtonIcon
|
|
51
|
+
@color="text"
|
|
52
|
+
{{style
|
|
53
|
+
(if isSelectedNodeType (hash background="rgba(0, 0, 0, 0.15)"))
|
|
54
|
+
}}
|
|
55
|
+
{{on "click" (fn this.handleMdButtonClick plugin.name)}}
|
|
56
|
+
@iconType={{plugin.button.iconType}}
|
|
57
|
+
aria-label={{plugin.button.label}}
|
|
58
|
+
@isDisabled={{this.isPreviewing}}
|
|
59
|
+
/>
|
|
60
|
+
</EuiToolTip>
|
|
61
|
+
{{/let}}
|
|
62
|
+
{{/each}}
|
|
63
|
+
{{/if}}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{{#if this.isPreviewing}}
|
|
67
|
+
<EuiButtonEmpty
|
|
68
|
+
@iconType="editorCodeBlock"
|
|
69
|
+
@color="text"
|
|
70
|
+
@size="s"
|
|
71
|
+
{{on "click" @onClickPreview}}
|
|
72
|
+
>
|
|
73
|
+
Editor
|
|
74
|
+
</EuiButtonEmpty>
|
|
75
|
+
|
|
76
|
+
{{else}}
|
|
77
|
+
<EuiButtonEmpty
|
|
78
|
+
@iconType="eye"
|
|
79
|
+
@color="text"
|
|
80
|
+
@size="s"
|
|
81
|
+
{{on "click" @onClickPreview}}
|
|
82
|
+
>
|
|
83
|
+
Preview
|
|
84
|
+
</EuiButtonEmpty>
|
|
85
|
+
{{/if}}
|
|
86
|
+
</div>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
import type MarkdownActions from '../../utils/markdown/markdown-actions';
|
|
4
|
+
import { MODE_VIEWING } from '../../utils/markdown/markdown-modes';
|
|
5
|
+
import { cached } from '@glimmer/tracking';
|
|
6
|
+
import { getOwner } from '@ember/application';
|
|
7
|
+
import { Plugin } from 'unified';
|
|
8
|
+
|
|
9
|
+
export interface EuiMarkdownEditorToolbarArgs {
|
|
10
|
+
viewMode?: string;
|
|
11
|
+
markdownActions: MarkdownActions;
|
|
12
|
+
uiPlugins: Plugin[];
|
|
13
|
+
openPluginEditor?: (actionResult: ReturnType<MarkdownActions['do']>) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default class EuiMarkdownEditorToolbarComponent extends Component<EuiMarkdownEditorToolbarArgs> {
|
|
17
|
+
boldItalicButtons = [
|
|
18
|
+
{
|
|
19
|
+
id: 'mdBold',
|
|
20
|
+
label: 'Bold',
|
|
21
|
+
name: 'bold',
|
|
22
|
+
iconType: 'editorBold'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'mdItalic',
|
|
26
|
+
label: 'Italic',
|
|
27
|
+
name: 'italic',
|
|
28
|
+
iconType: 'editorItalic'
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
get svgPath() {
|
|
33
|
+
const config = getOwner(this).resolveRegistration('config:environment');
|
|
34
|
+
const svgPath = config?.['@ember-eui/core']?.svgPath || 'svg/';
|
|
35
|
+
return svgPath;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@cached
|
|
39
|
+
get listButtons() {
|
|
40
|
+
return [
|
|
41
|
+
{
|
|
42
|
+
id: 'mdUl',
|
|
43
|
+
label: 'Unordered list',
|
|
44
|
+
name: 'ul',
|
|
45
|
+
iconType: 'editorUnorderedList'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'mdOl',
|
|
49
|
+
label: 'Ordered list',
|
|
50
|
+
name: 'ol',
|
|
51
|
+
iconType: 'editorOrderedList'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'mdTl',
|
|
55
|
+
label: 'Task list',
|
|
56
|
+
name: 'tl',
|
|
57
|
+
useSvg: true,
|
|
58
|
+
iconType: `${this.svgPath}markdown-checkmark`
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
quoteCodeLinkButtons = [
|
|
64
|
+
{
|
|
65
|
+
id: 'mdQuote',
|
|
66
|
+
label: 'Quote',
|
|
67
|
+
name: 'quote',
|
|
68
|
+
iconType: 'quote'
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'mdCode',
|
|
72
|
+
label: 'Code',
|
|
73
|
+
name: 'code',
|
|
74
|
+
iconType: 'editorCodeBlock'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'mdLink',
|
|
78
|
+
label: 'Link',
|
|
79
|
+
name: 'link',
|
|
80
|
+
iconType: 'editorLink'
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
get uiPlugins() {
|
|
85
|
+
return this.args.uiPlugins || [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get isPreviewing() {
|
|
89
|
+
return this.args.viewMode === MODE_VIEWING;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@action
|
|
93
|
+
handleMdButtonClick(mdButtonId: string) {
|
|
94
|
+
const actionResult = this.args.markdownActions.do(mdButtonId);
|
|
95
|
+
if (actionResult !== true) this.args.openPluginEditor?.(actionResult);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<div class="euiMarkdownFormat">
|
|
2
|
+
{{this.result.element}}
|
|
3
|
+
{{#each this.result.components as |CompNode|}}
|
|
4
|
+
{{#in-element CompNode.element}}
|
|
5
|
+
{{#let (component CompNode.componentName) as |DynamicComponent|}}
|
|
6
|
+
<DynamicComponent
|
|
7
|
+
@replaceNode={{optional @replaceNode}}
|
|
8
|
+
@node={{CompNode}}
|
|
9
|
+
/>
|
|
10
|
+
{{/let}}
|
|
11
|
+
{{/in-element}}
|
|
12
|
+
{{/each}}
|
|
13
|
+
</div>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import type MarkdownActions from '../../utils/markdown/markdown-actions';
|
|
3
|
+
import {
|
|
4
|
+
defaultParsingPlugins,
|
|
5
|
+
defaultProcessingPlugins
|
|
6
|
+
} from '../../utils/markdown/plugins/markdown-default-plugins';
|
|
7
|
+
import { cached } from '@glimmer/tracking';
|
|
8
|
+
import unified, { Processor } from 'unified';
|
|
9
|
+
import { toDOM } from '../../utils/markdown/plugins/to-dom';
|
|
10
|
+
import type { RehypeNode } from '../../utils/markdown/markdown-types';
|
|
11
|
+
|
|
12
|
+
export interface EuiMarkdownEditorToolbarArgs {
|
|
13
|
+
parsingPluginList?: typeof defaultParsingPlugins;
|
|
14
|
+
processingPluginList?: typeof defaultProcessingPlugins;
|
|
15
|
+
viewMode?: string;
|
|
16
|
+
markdownActions: MarkdownActions;
|
|
17
|
+
value: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default class EuiMarkdownEditorToolbarComponent extends Component<EuiMarkdownEditorToolbarArgs> {
|
|
21
|
+
get parsingPluginList() {
|
|
22
|
+
return this.args.parsingPluginList || defaultParsingPlugins;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get processingPluginList() {
|
|
26
|
+
return this.args.processingPluginList || defaultProcessingPlugins;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@cached
|
|
30
|
+
get processor() {
|
|
31
|
+
const Compiler = (tree: any) => {
|
|
32
|
+
return tree;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function identityCompiler(this: Processor) {
|
|
36
|
+
this.Compiler = Compiler;
|
|
37
|
+
}
|
|
38
|
+
return unified()
|
|
39
|
+
.use(this.parsingPluginList)
|
|
40
|
+
.use(this.processingPluginList)
|
|
41
|
+
.use(identityCompiler);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@cached
|
|
45
|
+
//@ts-ignore
|
|
46
|
+
get result() {
|
|
47
|
+
try {
|
|
48
|
+
const processed = this.processor.processSync(this.args.value);
|
|
49
|
+
return toDOM(processed.result as RehypeNode);
|
|
50
|
+
//eslint-disable-next-line
|
|
51
|
+
} catch (e) {
|
|
52
|
+
return this.args.value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
|
|
4
|
+
interface Position {
|
|
5
|
+
start: Record<string, unknown>;
|
|
6
|
+
end: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export interface EuiMarkdownFormatMarkdownCheckboxArgs {
|
|
9
|
+
replaceNode(position: Position, str: string): void;
|
|
10
|
+
node: {
|
|
11
|
+
position: Position;
|
|
12
|
+
lead: string;
|
|
13
|
+
isChecked: boolean;
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default class EuiMarkdownFormatMarkdownCheckboxComponent extends Component<EuiMarkdownFormatMarkdownCheckboxArgs> {
|
|
19
|
+
@action
|
|
20
|
+
handleChange() {
|
|
21
|
+
const { position, lead, isChecked, label } = this.args.node;
|
|
22
|
+
|
|
23
|
+
this.args.replaceNode(
|
|
24
|
+
position,
|
|
25
|
+
`${lead}[${isChecked ? ' ' : 'x'}]${label}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|