diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts index bdc95016b6..c76175b5e7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts @@ -21,6 +21,7 @@ import './input-language-picker/input-language-picker.element'; import './input-media-picker/input-media-picker.element'; import './input-multi-url-picker/input-multi-url-picker.element'; import './input-slider/input-slider.element'; +import './input-tiny-mce/input-tiny-mce.element'; import './input-toggle/input-toggle-element'; import './property-type-based-property/property-type-based-property.element'; import './ref-property-editor-ui/ref-property-editor-ui.element'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-tiny-mce/input-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-tiny-mce/input-tiny-mce.element.ts new file mode 100644 index 0000000000..d0614ea495 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-tiny-mce/input-tiny-mce.element.ts @@ -0,0 +1,158 @@ +import { html } from 'lit'; +import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; +import { customElement, property } from 'lit/decorators.js'; +import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; +import { AcePlugin } from '../../property-editors/uis/tiny-mce/plugins/ace.plugin'; +import { LinkPickerPlugin } from '../../property-editors/uis/tiny-mce/plugins/linkpicker.plugin'; +import { UmbLitElement } from '@umbraco-cms/element'; +import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/modal'; + +/// TINY MCE +import '@tinymce/tinymce-webcomponent'; +// import 'tinymce'; +// /* Default icons are required. After that, import custom icons if applicable */ +// import 'tinymce/icons/default'; + +// /* Required TinyMCE components */ +// import 'tinymce/themes/silver'; +// import 'tinymce/models/dom'; + +// /* Import a skin (can be a custom skin instead of the default) */ +// import 'tinymce/skins/ui/oxide/skin.css'; + +// /* Import plugins */ +// import 'tinymce/plugins/advlist'; +// import 'tinymce/plugins/code'; +// import 'tinymce/plugins/emoticons'; +// import 'tinymce/plugins/emoticons/js/emojis'; +// import 'tinymce/plugins/link'; +// import 'tinymce/plugins/lists'; +// import 'tinymce/plugins/table'; + +declare global { + interface Window { + tinyConfig: any; + tinymce: any; + } +} + +@customElement('umb-input-tiny-mce') +export class UmbInputTinyMceElement extends FormControlMixin(UmbLitElement) { + static styles = [UUITextStyles]; + + @property() + configuration: Array = []; + + @property() + private _dimensions?: { [key: string]: number }; + + @property() + private _styleFormats = [ + { + title: 'Headers', + items: [ + { title: 'Page header', block: 'h2' }, + { title: 'Section header', block: 'h3' }, + { title: 'Paragraph header', block: 'h4' }, + ], + }, + { + title: 'Blocks', + items: [{ title: 'Normal', block: 'p' }], + }, + { + title: 'Containers', + items: [ + { title: 'Quote', block: 'blockquote' }, + { title: 'Code', block: 'code' }, + ], + }, + ]; + + @property({ type: Array }) + private _toolbar: Array = []; + + @property({ type: Array }) + private _plugins: Array = []; + + @property({ type: Array }) + private _stylesheets: Array = []; + + modalService?: UmbModalService; + + constructor() { + super(); + + this.consumeContext(UMB_MODAL_SERVICE_CONTEXT_TOKEN, (instance) => { + this.modalService = instance; + this.#setTinyConfig(); + }); + } + + connectedCallback() { + super.connectedCallback(); + + console.log(this.value); + + this._dimensions = this.configuration.find((x) => x.alias === 'dimensions')?.value as { [key: string]: number }; + this._toolbar = this.configuration.find((x) => x.alias === 'toolbar')?.value; + this._plugins = this.configuration + .find((x) => x.alias === 'plugins') + ?.value.map((x: { [key: string]: string }) => x.name); + this._stylesheets = this.configuration.find((x) => x.alias === 'stylesheets')?.value; + } + + // TODO => setup runs before rendering, here we can add any custom plugins + // TODO => fix TinyMCE type definitions + #setTinyConfig() { + window.tinyConfig = { + statusbar: false, + menubar: false, + contextMenu: false, + resize: false, + style_formats: this._styleFormats, + convert_urls: false, + setup: (editor: any) => { + new AcePlugin(editor, this.modalService); + new LinkPickerPlugin(editor, this.modalService, this.configuration); + + // TODO => the editor frame catches Ctrl+S and handles it with the system save dialog + // - we want to handle it in the content controller, so we'll emit an event instead + editor.addShortcut('Ctrl+S', '', () => this.dispatchEvent(new CustomEvent('rte.shortcut.save'))); + editor.addShortcut('Ctrl+P', '', () => this.dispatchEvent(new CustomEvent('rte.shortcut.saveAndPublish'))); + + editor.on('Change', () => this.#onChange(editor.getContent())); + }, + }; + } + + protected getFormElement() { + return undefined; + } + + #onChange(value: string) { + super.value = value; + this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); + } + + render() { + return html` ${this.value}`; + } +} + +export default UmbInputTinyMceElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-input-tiny-mce': UmbInputTinyMceElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/tiny-mce/property-editor-ui-tiny-mce.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/tiny-mce/property-editor-ui-tiny-mce.element.ts index 0b3a67cb76..c0b6b4d2eb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/tiny-mce/property-editor-ui-tiny-mce.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/tiny-mce/property-editor-ui-tiny-mce.element.ts @@ -1,42 +1,10 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property } from 'lit/decorators.js'; -import { LinkPickerPlugin } from './plugins/linkpicker.plugin'; -import { AcePlugin } from './plugins/ace.plugin'; +import UmbInputTinyMceElement from 'src/backoffice/shared/components/input-tiny-mce/input-tiny-mce.element'; import { UmbLitElement } from '@umbraco-cms/element'; import { DataTypePropertyModel } from '@umbraco-cms/backend-api'; -/// TINY MCE -import '@tinymce/tinymce-webcomponent'; -// import 'tinymce'; -// /* Default icons are required. After that, import custom icons if applicable */ -// import 'tinymce/icons/default'; - -// /* Required TinyMCE components */ -// import 'tinymce/themes/silver'; -// import 'tinymce/models/dom'; - -// /* Import a skin (can be a custom skin instead of the default) */ -// import 'tinymce/skins/ui/oxide/skin.css'; - -// /* Import plugins */ -// import 'tinymce/plugins/advlist'; -// import 'tinymce/plugins/code'; -// import 'tinymce/plugins/emoticons'; -// import 'tinymce/plugins/emoticons/js/emojis'; -// import 'tinymce/plugins/link'; -// import 'tinymce/plugins/lists'; -// import 'tinymce/plugins/table'; - -import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/modal'; - -declare global { - interface Window { - tinyConfig: any; - tinymce: any; - } -} - /** * @element umb-property-editor-ui-tiny-mce */ @@ -44,101 +12,32 @@ declare global { export class UmbPropertyEditorUITinyMceElement extends UmbLitElement { static styles = [UUITextStyles, css``]; - @property() - value?: string; + #value = ''; + @property({ type: Array }) + public get value(): string { + return this.#value; + } + public set value(value: string) { + this.#value = value || ''; + } - @property() - private _dimensions?: { [key: string]: number }; - - @property() - private _styleFormats = [ - { - title: 'Headers', - items: [ - { title: 'Page header', block: 'h2' }, - { title: 'Section header', block: 'h3' }, - { title: 'Paragraph header', block: 'h4' }, - ], - }, - { - title: 'Blocks', - items: [{ title: 'Normal', block: 'p' }], - }, - { - title: 'Containers', - items: [ - { title: 'Quote', block: 'blockquote' }, - { title: 'Code', block: 'code' }, - ], - }, - ]; - - @property({ type: Array }) - private _toolbar: Array = []; - - @property({ type: Array }) - private _plugins: Array = []; - - @property({ type: Array }) - private _stylesheets: Array = []; - - configuration?: Array = []; + configuration: Array = []; @property({ type: Array, attribute: false }) public set config(config: Array) { this.configuration = config; - this._dimensions = config.find((x) => x.alias === 'dimensions')?.value as { [key: string]: number }; - this._toolbar = config.find((x) => x.alias === 'toolbar')?.value; - this._plugins = config.find((x) => x.alias === 'plugins')?.value.map((x: { [key: string]: string }) => x.name); - this._stylesheets = config.find((x) => x.alias === 'stylesheets')?.value; } - // TODO => setup runs before rendering, here we can add any custom plugins - // TODO => fix TinyMCE type definitions - #setTinyConfig() { - window.tinyConfig = { - statusbar: false, - menubar: false, - contextMenu: false, - resize: false, - style_formats: this._styleFormats, - convert_urls: false, - setup: (editor: any) => { - new AcePlugin(editor, this.modalService); - new LinkPickerPlugin(editor, this.modalService, this.configuration); - - // TODO => the editor frame catches Ctrl+S and handles it with the system save dialog - // - we want to handle it in the content controller, so we'll emit an event instead - editor.addShortcut('Ctrl+S', '', () => window.dispatchEvent(new CustomEvent('rte.shortcut.save'))); - editor.addShortcut('Ctrl+P', '', () => window.dispatchEvent(new CustomEvent('rte.shortcut.saveAndPublish'))); - }, - }; - } - - modalService?: UmbModalService; - - constructor() { - super(); - - this.consumeContext(UMB_MODAL_SERVICE_CONTEXT_TOKEN, (instance) => { - this.modalService = instance; - this.#setTinyConfig(); - }); + #onChange(event: CustomEvent) { + this.value = (event.target as UmbInputTinyMceElement).value as string; + this.dispatchEvent(new CustomEvent('property-value-change')); } render() { - return html`
- ${this.value} -
`; + return html``; } }