diff --git a/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts b/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts index 9921108fdc..39e3004534 100644 --- a/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts +++ b/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts @@ -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'; @@ -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; @@ -65,7 +68,7 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement, private _codeEditor?: UmbCodeEditorElement; @state() - private _actionExtensions: Array = []; + private _actionExtensions: Array = []; #mediaUrlRepository = new UmbMediaUrlRepository(this); @@ -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 }), }; @@ -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(), }); @@ -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.`); @@ -429,6 +433,7 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement, override render() { return html` ${this.#renderToolbar()} + + ${this.#renderPreview()} `; } @@ -447,112 +453,120 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement, if (this.readonly) return nothing; return html`
- - this.#editor?.monacoEditor?.getAction('h1')?.run()}> - H - - this.#editor?.monacoEditor?.getAction('b')?.run()}> - B - - this.#editor?.monacoEditor?.getAction('i')?.run()}> - I - - - - - this.#editor?.monacoEditor?.getAction('q')?.run()}> - - - this.#editor?.monacoEditor?.getAction('ol')?.run()}> - - - this.#editor?.monacoEditor?.getAction('ul')?.run()}> - - - - - this.#editor?.monacoEditor?.getAction('code')?.run()}> - - - this.#editor?.monacoEditor?.getAction('line')?.run()}> - - - this.#editor?.monacoEditor?.getAction('image')?.run()}> - - - - - - ${this._actionExtensions.map( - (action) => html` - this.#onActionClick(event, action)}> - - - `, - )} - - - - { - this._focusEditor(); - this.#editor?.monacoEditor?.trigger('', 'editor.action.quickCommand', ''); - }}> - F1 - - +
+ + this.#editor?.monacoEditor?.getAction('h1')?.run()}> + + + this.#editor?.monacoEditor?.getAction('b')?.run()}> + + + this.#editor?.monacoEditor?.getAction('i')?.run()}> + + + + + + this.#editor?.monacoEditor?.getAction('q')?.run()}> + + + this.#editor?.monacoEditor?.getAction('ol')?.run()}> + + + this.#editor?.monacoEditor?.getAction('ul')?.run()}> + + + + + this.#editor?.monacoEditor?.getAction('code')?.run()}> + + + this.#editor?.monacoEditor?.getAction('line')?.run()}> + + + this.#editor?.monacoEditor?.getAction('image')?.run()}> + + + + + + ${this._actionExtensions.map( + (action) => html` + this.#onActionClick(event, action)}> + ${when( + action.icon, + () => html``, + () => html`${this.localize.string(action.label)}`, + )} + + `, + )} + +
+
+ + { + this._focusEditor(); + this.#editor?.monacoEditor?.trigger('', 'editor.action.quickCommand', ''); + }}> + F1 + + +
`; } @@ -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 { @@ -623,6 +674,6 @@ export default UmbInputMarkdownElement; declare global { interface HTMLElementTagNameMap { - [elementName]: UmbInputMarkdownElement; + 'umb-input-markdown': UmbInputMarkdownElement; } } diff --git a/src/packages/markdown-editor/monaco-markdown-editor-action.extension.ts b/src/packages/markdown-editor/monaco-markdown-editor-action.extension.ts index ebe216aa1b..95aedbe030 100644 --- a/src/packages/markdown-editor/monaco-markdown-editor-action.extension.ts +++ b/src/packages/markdown-editor/monaco-markdown-editor-action.extension.ts @@ -5,7 +5,10 @@ export interface ManifestMonacoMarkdownEditorAction extends ManifestApi { meta?: MetaMonacoMarkdownEditorAction; } -export type MetaMonacoMarkdownEditorAction = object; +export type MetaMonacoMarkdownEditorAction = { + icon?: string | null; + label?: string | null; +}; declare global { interface UmbExtensionManifestMap { diff --git a/src/packages/multi-url-picker/monaco-markdown-editor-action/manifests.ts b/src/packages/multi-url-picker/monaco-markdown-editor-action/manifests.ts index 78748548a1..d76b047df1 100644 --- a/src/packages/multi-url-picker/monaco-markdown-editor-action/manifests.ts +++ b/src/packages/multi-url-picker/monaco-markdown-editor-action/manifests.ts @@ -3,6 +3,10 @@ export const manifests = [ type: 'monacoMarkdownEditorAction', alias: 'Umb.MonacoMarkdownEditorAction.MultiUrlPicker', name: 'Multi Url Picker Monaco Markdown Editor Action', - js: () => import('./url-picker-monaco-markdown-editor-action.js'), + api: () => import('./url-picker-monaco-markdown-editor-action.js'), + meta: { + label: '#buttons_linkInsert', + icon: 'icon-link', + }, }, ];