@@ -12,14 +12,16 @@ import pick from 'ember-composable-helpers/helpers/pick';
1212import { modifier } from ' ember-modifier' ;
1313import set from ' ember-set-helper/helpers/set' ;
1414import style from ' ember-style-modifier/modifiers/style' ;
15- import { eq , not } from ' ember-truth-helpers' ;
15+ import { eq } from ' ember-truth-helpers' ;
1616import unified from ' unified' ;
1717
1818import { argOrDefaultDecorator } from ' ../helpers/arg-or-default' ;
1919import classNames from ' ../helpers/class-names' ;
2020import resizeObserver from ' ../modifiers/resize-observer' ;
2121import validatableControl from ' ../modifiers/validatable-control' ;
22- import MarkdownActions from ' ../utils/markdown/markdown-actions' ;
22+ import MarkdownActions , {
23+ insertText
24+ } from ' ../utils/markdown/markdown-actions' ;
2325import { MODE_EDITING , MODE_VIEWING } from ' ../utils/markdown/markdown-modes' ;
2426import {
2527 defaultParsingPlugins ,
@@ -30,6 +32,7 @@ import EuiMarkdownEditorDropZone from './eui-markdown-editor-drop-zone.gts';
3032import EuiMarkdownEditorTextArea from ' ./eui-markdown-editor-text-area.gts' ;
3133import EuiMarkdownEditorToolbar from ' ./eui-markdown-editor-toolbar.gts' ;
3234import EuiMarkdownFormat from ' ./eui-markdown-format.gts' ;
35+ import EuiModal from ' ./eui-modal.gts' ;
3336
3437import type {
3538 EuiMarkdownAstNode ,
@@ -127,7 +130,32 @@ export const getCursorNodeModifier = modifier(function getCursorNodeModifier(
127130 };
128131});
129132
133+ function isNewLine(char : string | undefined ): boolean {
134+ if (char == null ) return true ;
135+
136+ return !! char .match (/ [\r\n ] / );
137+ }
138+
139+ function padWithNewlinesIfNeeded(textarea : HTMLTextAreaElement , text : string ) {
140+ const selectionStart = textarea .selectionStart ;
141+ const selectionEnd = textarea .selectionEnd ;
142+
143+ // block parsing requires two leading new lines and none trailing, but we add an extra trailing line for readability
144+ const isPrevNewLine = isNewLine (textarea .value [selectionStart - 1 ]);
145+ const isPrevPrevNewLine = isNewLine (textarea .value [selectionStart - 2 ]);
146+ const isNextNewLine = isNewLine (textarea .value [selectionEnd ]);
147+
148+ // pad text with newlines as needed
149+ text = ` ${isPrevNewLine ? ' ' : ' \n ' }${isPrevPrevNewLine ? ' ' : ' \n ' }${text }${
150+ isNextNewLine ? ' ' : ' \n '
151+ } ` ;
152+
153+ return text ;
154+ }
155+
130156export default class EuiMarkdownEditorComponent extends Component <EuiMarkdownEditorSignature > {
157+ @tracked pluginEditorPlugin? : EuiMarkdownEditorUiPlugin ;
158+
131159 // Defaults
132160 @argOrDefaultDecorator (defaultParsingPlugins ) declare parsingPluginList: typeof defaultParsingPlugins ;
133161
@@ -157,7 +185,6 @@ export default class EuiMarkdownEditorComponent extends Component<EuiMarkdownEdi
157185 super (owner , args );
158186 this .markdownActions = new MarkdownActions (
159187 this .editorId ,
160- // @ts-expect-error
161188 this .toolbarPlugins
162189 );
163190 this .currentHeight = this .height ;
@@ -235,6 +262,8 @@ export default class EuiMarkdownEditorComponent extends Component<EuiMarkdownEdi
235262 }
236263 }
237264
265+ getCursorNode = () => {};
266+
238267 @action
239268 setTextAreaRef(ref : HTMLTextAreaElement ) {
240269 this .textareaRef = ref ;
@@ -317,6 +346,45 @@ export default class EuiMarkdownEditorComponent extends Component<EuiMarkdownEdi
317346 this .selectedNode = node ;
318347 }
319348
349+ openPluginEditor = (plugin : EuiMarkdownEditorUiPlugin ) => {
350+ this .pluginEditorPlugin = plugin ;
351+ };
352+
353+ onEditorPluginSave = (markdown : any , config : any ) => {
354+ let { selectedNode, textareaRef } = this ;
355+
356+ if (
357+ this .pluginEditorPlugin &&
358+ selectedNode &&
359+ // @ts-expect-error
360+ selectedNode .type === this .pluginEditorPlugin .name &&
361+ // @ts-expect-error
362+ selectedNode .position
363+ ) {
364+ // modifying an existing node
365+ textareaRef ! .setSelectionRange (
366+ // @ts-expect-error
367+ selectedNode .position .start .offset ,
368+ // @ts-expect-error
369+ selectedNode .position .end .offset
370+ );
371+ } else {
372+ // creating a new node
373+ if (config .block ) {
374+ // inject newlines if needed
375+ markdown = padWithNewlinesIfNeeded (textareaRef ! , markdown );
376+ }
377+ }
378+
379+ insertText (textareaRef ! , {
380+ text: markdown ,
381+ selectionStart: undefined ,
382+ selectionEnd: undefined
383+ });
384+
385+ this .pluginEditorPlugin = undefined ;
386+ };
387+
320388 <template >
321389 <div
322390 class ={{classNames
@@ -336,6 +404,7 @@ export default class EuiMarkdownEditorComponent extends Component<EuiMarkdownEdi
336404 @ selectedNode ={{this .selectedNode }}
337405 @ markdownActions ={{this .markdownActions }}
338406 @ onClickPreview ={{this .setViewMode }}
407+ @ openPluginEditor ={{this .openPluginEditor }}
339408 @ viewMode ={{this .viewMode }}
340409 @ uiPlugins ={{this .toolbarPlugins }}
341410 {{didInsert this . setEditorToolbarRef}}
@@ -380,6 +449,17 @@ export default class EuiMarkdownEditorComponent extends Component<EuiMarkdownEdi
380449 ...attributes
381450 />
382451 </EuiMarkdownEditorDropZone >
452+ {{#if this . pluginEditorPlugin.editor }}
453+ <EuiModal @ onClose ={{set this " pluginEditorPlugin" undefined }} >
454+ {{#let ( component this . pluginEditorPlugin.editor) as | Editor | }}
455+ <Editor
456+ @ node ={{this .selectedNode }}
457+ @ onCancel ={{set this " pluginEditorPlugin" undefined }}
458+ @ onSave ={{this .onEditorPluginSave }}
459+ />
460+ {{/let }}
461+ </EuiModal >
462+ {{/if }}
383463 </div >
384464 </div >
385465 </template >
0 commit comments