@ember-eui/core 1.2.4 → 1.3.4
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 +28 -1
- package/README.md +1 -1
- 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-combo-box/index.hbs +3 -2
- package/addon/components/eui-combo-box/trigger/index.hbs +118 -112
- 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-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 +45 -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 +100 -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/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 +86 -0
- package/docs/editors/markdown-editor/base-editor.md +1 -0
- package/package.json +17 -4
- package/public/markdown-checkmark.svg +3 -0
- package/public/markdown-logo.svg +3 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
import { tracked } from '@glimmer/tracking';
|
|
4
|
+
import { copyToClipboard } from '../../utils/copy-to-clipboard';
|
|
5
|
+
|
|
6
|
+
type EuiCopyArgs = {
|
|
7
|
+
/**
|
|
8
|
+
* Text that will be copied to clipboard when copy function is executed.
|
|
9
|
+
*/
|
|
10
|
+
textToCopy: string;
|
|
11
|
+
/**
|
|
12
|
+
* Tooltip message displayed before copy function is called.
|
|
13
|
+
*/
|
|
14
|
+
beforeMessage?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Tooltip message displayed after copy function is called that lets the user know that
|
|
17
|
+
* 'textToCopy' has been copied to the clipboard.
|
|
18
|
+
*/
|
|
19
|
+
afterMessage?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default class EuiCopyComponent extends Component<EuiCopyArgs> {
|
|
23
|
+
@tracked tooltipText = this.args.beforeMessage;
|
|
24
|
+
|
|
25
|
+
@action
|
|
26
|
+
copy(): void {
|
|
27
|
+
const isCopied = copyToClipboard(this.args.textToCopy);
|
|
28
|
+
if (isCopied) {
|
|
29
|
+
this.tooltipText = this.args.afterMessage;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@action
|
|
34
|
+
resetTooltipText(): void {
|
|
35
|
+
this.tooltipText = this.args.beforeMessage;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,34 +1,39 @@
|
|
|
1
|
-
{{#if this.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class={{class-names
|
|
5
|
-
this.optionalColorClass
|
|
6
|
-
(if this.isAppIcon "euiIcon--app")
|
|
7
|
-
componentName="EuiIcon"
|
|
8
|
-
size=this.size
|
|
9
|
-
}}
|
|
10
|
-
alt={{if @title @title}}
|
|
11
|
-
tabIndex={{@tabIndex}}
|
|
12
|
-
...attributes
|
|
13
|
-
/>
|
|
1
|
+
{{#if this.icon.inner}}
|
|
2
|
+
{{! has the shape of a curried component }}
|
|
3
|
+
{{component this.icon}}
|
|
14
4
|
{{else}}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
5
|
+
{{#if this.useImage}}
|
|
6
|
+
<img
|
|
7
|
+
src={{this.icon}}
|
|
8
|
+
class={{class-names
|
|
9
|
+
this.optionalColorClass
|
|
10
|
+
(if this.isAppIcon "euiIcon--app")
|
|
11
|
+
componentName="EuiIcon"
|
|
12
|
+
size=this.size
|
|
13
|
+
}}
|
|
14
|
+
alt={{if @title @title}}
|
|
15
|
+
tabIndex={{@tabIndex}}
|
|
16
|
+
...attributes
|
|
17
|
+
/>
|
|
18
|
+
{{else}}
|
|
19
|
+
<Svg
|
|
20
|
+
@name={{this.icon}}
|
|
21
|
+
@loadingSvg={{this.emptyIcon}}
|
|
22
|
+
class={{class-names
|
|
23
|
+
this.optionalColorClass
|
|
24
|
+
(if this.isAppIcon "euiIcon--app")
|
|
25
|
+
componentName="EuiIcon"
|
|
26
|
+
size=this.size
|
|
27
|
+
}}
|
|
28
|
+
style={{this.optionalCustomStyles}}
|
|
29
|
+
tabIndex={{this.tabIndex}}
|
|
30
|
+
role="image"
|
|
31
|
+
aria-hidden={{if this.isAriaHidden true}}
|
|
32
|
+
aria-label={{if @aria-label @aria-label this.titleId}}
|
|
33
|
+
aria-labelledby={{if @aria-labelledby @aria-labelledby this.titleId}}
|
|
34
|
+
@title={{@title}}
|
|
35
|
+
@onIconLoad={{@onIconLoad}}
|
|
36
|
+
...attributes
|
|
37
|
+
/>
|
|
38
|
+
{{/if}}
|
|
34
39
|
{{/if}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{yield this.setRef this.innerText}}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import GlimmerComponent from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { action } from '@ember/object';
|
|
4
|
+
|
|
5
|
+
type InnerTextArgs = {
|
|
6
|
+
fallback?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default class EuiImage extends GlimmerComponent<InnerTextArgs> {
|
|
10
|
+
@tracked ref: HTMLElement | null = null;
|
|
11
|
+
@tracked innerText = '';
|
|
12
|
+
observer: MutationObserver | null = null;
|
|
13
|
+
|
|
14
|
+
get innerTextFallback() {
|
|
15
|
+
return this.args.fallback ?? '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setupObserver() {
|
|
19
|
+
this.observer?.disconnect();
|
|
20
|
+
this.observer = new MutationObserver((mutationsList) => {
|
|
21
|
+
if (mutationsList.length) this.updateInnerText(this.ref);
|
|
22
|
+
});
|
|
23
|
+
if (this.ref) {
|
|
24
|
+
this.updateInnerText(this.ref);
|
|
25
|
+
this.observer.observe(this.ref, {
|
|
26
|
+
characterData: true,
|
|
27
|
+
subtree: true,
|
|
28
|
+
childList: true
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
updateInnerText(node: HTMLElement | null) {
|
|
34
|
+
if (!node) return;
|
|
35
|
+
this.setInnerText(
|
|
36
|
+
// Check for `innerText` implementation rather than a simple OR check
|
|
37
|
+
// because in real cases the result of `innerText` could correctly be `null`
|
|
38
|
+
// while the result of `textContent` could correctly be non-`null` due to
|
|
39
|
+
// differing reliance on browser layout calculations.
|
|
40
|
+
// We prefer the result of `innerText`, if available.
|
|
41
|
+
'innerText' in node
|
|
42
|
+
? node.innerText
|
|
43
|
+
: (node as HTMLElement).textContent || this.innerTextFallback
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setInnerText(text: string) {
|
|
48
|
+
this.innerText = text;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@action
|
|
52
|
+
setRef(ref: HTMLElement): void {
|
|
53
|
+
this.ref = ref;
|
|
54
|
+
this.setupObserver();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
willDestroy(): void {
|
|
58
|
+
super.willDestroy();
|
|
59
|
+
this.observer?.disconnect();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<div
|
|
2
|
+
class={{class-names
|
|
3
|
+
"euiMarkdownEditor"
|
|
4
|
+
(if (eq this.height "full") "euiMarkdownEditor--fullHeight")
|
|
5
|
+
(if this.isPreviewing "euiMarkdownEditor--isPreviewing")
|
|
6
|
+
}}
|
|
7
|
+
{{did-update
|
|
8
|
+
this.updateCurrentHeight
|
|
9
|
+
this.currentHeight
|
|
10
|
+
this.isPreviewing
|
|
11
|
+
this.height
|
|
12
|
+
this.autoExpandPreview
|
|
13
|
+
}}
|
|
14
|
+
>
|
|
15
|
+
<EuiMarkdownEditorToolbar
|
|
16
|
+
@selectedNode={{this.selectedNode}}
|
|
17
|
+
@markdownActions={{this.markdownActions}}
|
|
18
|
+
@onClickPreview={{this.setViewMode}}
|
|
19
|
+
@viewMode={{this.viewMode}}
|
|
20
|
+
@uiPlugins={{this.toolbarPlugins}}
|
|
21
|
+
{{did-insert this.setEditorToolbarRef}}
|
|
22
|
+
/>
|
|
23
|
+
{{#if this.isPreviewing}}
|
|
24
|
+
<div
|
|
25
|
+
class="euiMarkdownEditorPreview"
|
|
26
|
+
{{did-insert (set this "previewRef")}}
|
|
27
|
+
{{style (hash height=this.previewHeight)}}
|
|
28
|
+
>
|
|
29
|
+
<EuiMarkdownFormat
|
|
30
|
+
@parsingPluginList={{this.parsingPluginList}}
|
|
31
|
+
@processingPluginList={{this.processingPluginList}}
|
|
32
|
+
@value={{@value}}
|
|
33
|
+
@replaceNode={{this.replaceNode}}
|
|
34
|
+
/>
|
|
35
|
+
</div>
|
|
36
|
+
{{/if}}
|
|
37
|
+
|
|
38
|
+
<div
|
|
39
|
+
class="euiMarkdownEditor__toggleContainer"
|
|
40
|
+
{{style height=this.editorToggleContainerHeight}}
|
|
41
|
+
>
|
|
42
|
+
<EuiMarkdownEditorDropZone
|
|
43
|
+
@uiPlugins={{this.toolbarPlugins}}
|
|
44
|
+
{{resize-observer onResize=this.onResize}}
|
|
45
|
+
>
|
|
46
|
+
<EuiMarkdownEditorTextArea
|
|
47
|
+
{{get-cursor-node (get this.parsed "0") this.setSelectedNode}}
|
|
48
|
+
{{did-update this.onParse this.parsed}}
|
|
49
|
+
{{did-insert this.setTextAreaRef}}
|
|
50
|
+
disabled={{@disabled}}
|
|
51
|
+
id={{this.editorId}}
|
|
52
|
+
@height={{this.textAreaHeight}}
|
|
53
|
+
@maxHeight={{this.textAreaMaxHeight}}
|
|
54
|
+
value={{@value}}
|
|
55
|
+
aria-label={{@ariaLabel}}
|
|
56
|
+
aria-labelledby={{@ariaLabelledBy}}
|
|
57
|
+
aria-describedby={{@ariaDescribedBy}}
|
|
58
|
+
{{on "input" (pick "target.value" @onChange)}}
|
|
59
|
+
...attributes
|
|
60
|
+
/>
|
|
61
|
+
</EuiMarkdownEditorDropZone>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
@@ -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
|
+
}
|