Skip to content

Commit

Permalink
Feature: Markdown Editor toolbar styling (#2498)
Browse files Browse the repository at this point in the history
Stylizes the Markdown Editor toolbar

to align with the Tiptap RTE toolbar style.
  • Loading branch information
leekelleher authored Oct 29, 2024
1 parent 322a138 commit bef2d31
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
query,
state,
unsafeHTML,
when,
} from '@umbraco-cms/backoffice/external/lit';
import { createExtensionApi } from '@umbraco-cms/backoffice/extension-api';
import { marked } from '@umbraco-cms/backoffice/external/marked';
Expand All @@ -23,13 +24,15 @@ import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
import { sanitizeHTML } from '@umbraco-cms/backoffice/utils';

const elementName = 'umb-input-markdown';
interface UmbMarkdownEditorAction extends monaco.editor.IActionDescriptor {
icon?: string | null;
}

/**
* @element umb-input-markdown
* @fires change - when the value of the input changes
*/
@customElement(elementName)
@customElement('umb-input-markdown')
export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement, '') {
protected override getFormElement() {
return this._codeEditor;
Expand Down Expand Up @@ -65,7 +68,7 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
private _codeEditor?: UmbCodeEditorElement;

@state()
private _actionExtensions: Array<monaco.editor.IActionDescriptor> = [];
private _actionExtensions: Array<UmbMarkdownEditorAction> = [];

#mediaUrlRepository = new UmbMediaUrlRepository(this);

Expand All @@ -82,9 +85,10 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
this.observe(umbExtensionsRegistry.byType('monacoMarkdownEditorAction'), (manifests) => {
manifests.forEach(async (manifest) => {
const api = await createExtensionApi(this, manifest, [this]);
const action: monaco.editor.IActionDescriptor = {
id: api.getUnique(),
label: api.getLabel(),
const action: UmbMarkdownEditorAction = {
id: manifest.alias ?? api.getUnique(),
label: this.localize.string(manifest.meta?.label ?? api.getLabel()),
icon: manifest.meta?.icon,
keybindings: api.getKeybindings(),
run: async () => await api.execute({ editor: this.#editor, overlaySize: this.overlaySize }),
};
Expand Down Expand Up @@ -180,7 +184,7 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
run: () => this._insertBetweenSelection('```', '```', 'Code'),
});
this.#editor?.monacoEditor?.addAction({
label: 'Add Line',
label: 'Add Horizontal Line',
id: 'line',
run: () => this._insertLine(),
});
Expand All @@ -193,7 +197,7 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
});
}

#onActionClick(event: any, action: monaco.editor.IActionDescriptor) {
#onActionClick(event: Event, action: monaco.editor.IActionDescriptor) {
event.stopPropagation();
const hasAction = this.#editor?.monacoEditor?.getAction(action.id);
if (!hasAction) throw new Error(`Action ${action.id} not found in the editor.`);
Expand Down Expand Up @@ -429,6 +433,7 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
override render() {
return html`
${this.#renderToolbar()}
<umb-code-editor
language="markdown"
.code=${this.value as string}
Expand All @@ -439,6 +444,7 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
@keypress=${this.#onKeyPress}
@loaded=${this.#onCodeEditorLoaded}>
</umb-code-editor>
${this.#renderPreview()}
`;
}
Expand All @@ -447,112 +453,120 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
if (this.readonly) return nothing;
return html`
<div id="toolbar">
<uui-button-group>
<uui-button
compact
look="secondary"
label="Heading"
title="Heading, &lt;Ctrl+Shift+1&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('h1')?.run()}>
H
</uui-button>
<uui-button
compact
look="secondary"
label="Bold"
title="Bold, &lt;Ctrl+B&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('b')?.run()}>
B
</uui-button>
<uui-button
compact
look="secondary"
label="Italic"
title="Italic, &lt;Ctrl+I&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('i')?.run()}>
I
</uui-button>
</uui-button-group>
<uui-button-group>
<uui-button
compact
look="secondary"
label="Quote"
title="Quote, &lt;Ctrl+Shift+.&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('q')?.run()}>
<uui-icon name="icon-quote"></uui-icon>
</uui-button>
<uui-button
compact
look="secondary"
label="Ordered List"
title="Ordered List, &lt;Ctrl+Shift+7&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('ol')?.run()}>
<uui-icon name="icon-ordered-list"></uui-icon>
</uui-button>
<uui-button
compact
look="secondary"
label="Unordered List"
title="Unordered List, &lt;Ctrl+Shift+8&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('ul')?.run()}>
<uui-icon name="icon-bulleted-list"></uui-icon>
</uui-button>
</uui-button-group>
<uui-button-group>
<uui-button
compact
look="secondary"
label="Code"
title="Code, &lt;Ctrl+E&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('code')?.run()}>
<uui-icon name="icon-code"></uui-icon>
</uui-button>
<uui-button
compact
look="secondary"
label="Line"
title="Line"
@click=${() => this.#editor?.monacoEditor?.getAction('line')?.run()}>
<uui-icon name="icon-width"></uui-icon>
</uui-button>
<uui-button
compact
look="secondary"
label="Image"
title="Image"
@click=${() => this.#editor?.monacoEditor?.getAction('image')?.run()}>
<uui-icon name="icon-picture"></uui-icon>
</uui-button>
</uui-button-group>
<uui-button-group>
${this._actionExtensions.map(
(action) => html`
<uui-button
compact
look="secondary"
label=${action.label}
@click=${(event: any) => this.#onActionClick(event, action)}>
<uui-icon name="icon-link"></uui-icon>
</uui-button>
`,
)}
</uui-button-group>
<uui-button-group>
<uui-button
compact
label="Press F1 for all actions"
title="Press F1 for all actions"
@click=${() => {
this._focusEditor();
this.#editor?.monacoEditor?.trigger('', 'editor.action.quickCommand', '');
}}>
<uui-key>F1</uui-key>
</uui-button>
</uui-button-group>
<div id="buttons">
<uui-button-group>
<uui-button
compact
look="default"
label="Heading"
title="Heading, &lt;Ctrl+Shift+1&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('h1')?.run()}>
<umb-icon name="icon-heading-1"></umb-icon>
</uui-button>
<uui-button
compact
look="default"
label="Bold"
title="Bold, &lt;Ctrl+B&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('b')?.run()}>
<umb-icon name="icon-bold"></umb-icon>
</uui-button>
<uui-button
compact
look="default"
label="Italic"
title="Italic, &lt;Ctrl+I&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('i')?.run()}>
<umb-icon name="icon-italic"></umb-icon>
</uui-button>
</uui-button-group>
<uui-button-group>
<uui-button
compact
look="default"
label="Blockquote"
title="Blockquote, &lt;Ctrl+Shift+.&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('q')?.run()}>
<uui-icon name="icon-blockquote"></uui-icon>
</uui-button>
<uui-button
compact
look="default"
label="Ordered List"
title="Ordered List, &lt;Ctrl+Shift+7&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('ol')?.run()}>
<uui-icon name="icon-ordered-list"></uui-icon>
</uui-button>
<uui-button
compact
look="default"
label="Unordered List"
title="Unordered List, &lt;Ctrl+Shift+8&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('ul')?.run()}>
<uui-icon name="icon-bulleted-list"></uui-icon>
</uui-button>
</uui-button-group>
<uui-button-group>
<uui-button
compact
look="default"
label="Code"
title="Code, &lt;Ctrl+E&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('code')?.run()}>
<uui-icon name="icon-code"></uui-icon>
</uui-button>
<uui-button
compact
look="default"
label="Horizontal Rule"
title="Horizontal Rule"
@click=${() => this.#editor?.monacoEditor?.getAction('line')?.run()}>
<uui-icon name="icon-horizontal-rule"></uui-icon>
</uui-button>
<uui-button
compact
look="default"
label="Image"
title="Image"
@click=${() => this.#editor?.monacoEditor?.getAction('image')?.run()}>
<uui-icon name="icon-picture"></uui-icon>
</uui-button>
</uui-button-group>
<uui-button-group>
${this._actionExtensions.map(
(action) => html`
<uui-button
compact
look="default"
label=${this.localize.string(action.label)}
title=${this.localize.string(action.label)}
@click=${(event: Event) => this.#onActionClick(event, action)}>
${when(
action.icon,
() => html`<uui-icon name=${action.icon!}></uui-icon>`,
() => html`<span>${this.localize.string(action.label)}</span>`,
)}
</uui-button>
`,
)}
</uui-button-group>
</div>
<div id="actions">
<uui-button-group>
<uui-button
compact
label="Press F1 for all actions"
title="Press F1 for all actions"
@click=${() => {
this._focusEditor();
this.#editor?.monacoEditor?.trigger('', 'editor.action.quickCommand', '');
}}>
<uui-key>F1</uui-key>
</uui-button>
</uui-button-group>
</div>
</div>
`;
}
Expand All @@ -573,21 +587,58 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
}
#toolbar {
background-color: var(--uui-color-background-alt);
display: flex;
justify-content: space-between;
align-items: center;
border-radius: var(--uui-border-radius);
border: 1px solid var(--uui-color-border);
border-bottom: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
box-shadow:
0 2px 2px -2px rgba(34, 47, 62, 0.1),
0 8px 8px -4px rgba(34, 47, 62, 0.07);
background-color: var(--uui-color-surface-alt);
color: var(--color-text);
position: sticky;
top: -25px;
left: 0px;
right: 0px;
padding: var(--uui-size-3);
z-index: 9999999;
uui-key {
text-transform: uppercase;
}
}
#buttons {
flex: 1;
display: flex;
flex-wrap: wrap;
gap: var(--uui-size-2);
margin-bottom: var(--uui-size-2);
align-items: center;
uui-button-group:not(:last-child)::after {
content: '';
background-color: var(--uui-color-border);
width: 1px;
place-self: center;
height: 22px;
margin: 0 var(--uui-size-3);
}
}
umb-code-editor {
height: 200px;
border-radius: var(--uui-border-radius);
border: 1px solid var(--uui-color-divider-emphasis);
}
uui-button {
width: 50px;
border: 1px solid var(--uui-color-border);
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
padding-top: var(--uui-size-3);
}
#preview {
Expand Down Expand Up @@ -623,6 +674,6 @@ export default UmbInputMarkdownElement;

declare global {
interface HTMLElementTagNameMap {
[elementName]: UmbInputMarkdownElement;
'umb-input-markdown': UmbInputMarkdownElement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ export interface ManifestMonacoMarkdownEditorAction extends ManifestApi<any> {
meta?: MetaMonacoMarkdownEditorAction;
}

export type MetaMonacoMarkdownEditorAction = object;
export type MetaMonacoMarkdownEditorAction = {
icon?: string | null;
label?: string | null;
};

declare global {
interface UmbExtensionManifestMap {
Expand Down
Loading

0 comments on commit bef2d31

Please sign in to comment.