From 22f8aac99f33b0de55ddc15ce971ce9c742b4df7 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 14 Oct 2024 13:16:25 +0100 Subject: [PATCH] Tiptap extensions field observes the Block configuration, updates UI accordingly, to enable/disable the Block extension. --- .../src/assets/lang/en.ts | 1 + ...tiptap-extensions-configuration.element.ts | 133 +++++++++++++----- 2 files changed, 96 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts index 363c255c74..e5b2ca73c7 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts @@ -2648,6 +2648,7 @@ export default { extGroup_interactive: 'Interactive elements', extGroup_media: 'Embeds and media', extGroup_structure: 'Content structure', + extGroup_unknown: 'Uncategorized', toobar_availableItems: 'Available toolbar items', toobar_availableItemsEmpty: 'There are no toolbar extensions to show', toolbar_designer: 'Toolbar designer', diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-extensions-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-extensions-configuration.element.ts index 2a313a31af..3762eae369 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-extensions-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/components/property-editor-ui-tiptap-extensions-configuration.element.ts @@ -1,4 +1,14 @@ -import { customElement, css, html, property, state, repeat, when, nothing } from '@umbraco-cms/backoffice/external/lit'; +import { + customElement, + css, + html, + ifDefined, + nothing, + property, + state, + repeat, + when, +} from '@umbraco-cms/backoffice/external/lit'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; @@ -7,12 +17,14 @@ import type { UmbPropertyEditorConfigCollection, UmbPropertyEditorUiElement, } from '@umbraco-cms/backoffice/property-editor'; +import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property'; type UmbTiptapExtension = { alias: string; label: string; icon?: string; group?: string; + description?: string; }; type UmbTiptapExtensionGroupItem = UmbTiptapExtension & { @@ -24,6 +36,9 @@ type UmbTiptapExtensionGroup = { extensions: Array; }; +const TIPTAP_CORE_EXTENSION_ALIAS = 'Umb.Tiptap.RichTextEssentials'; +const TIPTAP_BLOCK_EXTENSION_ALIAS = 'Umb.Tiptap.Block'; + const elementName = 'umb-property-editor-ui-tiptap-extensions-configuration'; @customElement(elementName) @@ -31,8 +46,10 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement extends UmbLitElement implements UmbPropertyEditorUiElement { + #disabledExtensions = new Set([TIPTAP_CORE_EXTENSION_ALIAS]); + @property({ attribute: false }) - value?: Array = []; + value?: Array = [TIPTAP_CORE_EXTENSION_ALIAS]; @property({ attribute: false }) config?: UmbPropertyEditorConfigCollection; @@ -43,6 +60,41 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement @state() private _groups: Array = []; + constructor() { + super(); + this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, async (dataset) => { + this.observe( + await dataset.propertyValueByAlias>('blocks'), + (blocks) => { + const tmpValue = this.value ? [...this.value] : []; + + // When blocks are configured, the block extension can be enabled; + // otherwise, the block extension must be disabled. + if (blocks?.length) { + // Check if the block extension is already enabled, if not, add it. + if (!tmpValue.includes(TIPTAP_BLOCK_EXTENSION_ALIAS)) { + tmpValue.push(TIPTAP_BLOCK_EXTENSION_ALIAS); + } + this.#disabledExtensions.delete(TIPTAP_BLOCK_EXTENSION_ALIAS); + } else { + // Check if the block extension is enabled, if so, remove it. + const idx = tmpValue.indexOf(TIPTAP_BLOCK_EXTENSION_ALIAS) ?? -1; + if (idx >= 0) { + tmpValue.splice(idx, 1); + } + this.#disabledExtensions.add(TIPTAP_BLOCK_EXTENSION_ALIAS); + } + + if (!this.value || !this.#isArrayEqualTo(tmpValue, this.value)) { + this.#setValue(tmpValue); + this.#syncViewModel(); + } + }, + '_observeBlocks', + ); + }); + } + protected override async firstUpdated(_changedProperties: PropertyValueMap) { super.firstUpdated(_changedProperties); @@ -51,43 +103,60 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement .sort((a, b) => a.alias.localeCompare(b.alias)) .map((ext) => ({ alias: ext.alias, label: ext.meta.label, icon: ext.meta.icon, group: ext.meta.group })); + // Hardcoded core extension + this._extensions.unshift({ + alias: TIPTAP_CORE_EXTENSION_ALIAS, + label: 'Rich Text Essentials', + icon: 'icon-browser-window', + group: '#tiptap_extGroup_formatting', + description: 'This is a core extension, it must be enabled', + }); + if (!this.value) { // The default value is all extensions enabled - this.value = this._extensions.map((ext) => ext.alias); - this.dispatchEvent(new UmbPropertyValueChangeEvent()); + this.#setValue(this._extensions.map((ext) => ext.alias)); } - const items: Array = this._extensions.map((extension) => ({ - ...extension, - selected: this.value!.includes(extension.alias), - })); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - const grouped = Object.groupBy(items, (item: UmbTiptapExtensionGroupItem) => item.group || 'Uncategorized'); - - this._groups = Object.keys(grouped) - .sort((a, b) => a.localeCompare(b)) - .map((key) => ({ group: key, extensions: grouped[key] })); + this.#syncViewModel(); }); } + #isArrayEqualTo(a: Array, b: Array) { + return a.length === b.length && a.every((item) => b.includes(item)) && b.every((item) => a.includes(item)); + } + #onClick(item: UmbTiptapExtensionGroupItem) { item.selected = !item.selected; - if (!this.value) { - this.value = []; - } + const tmpValue = item.selected + ? [...(this.value ?? []), item.alias] + : (this.value ?? []).filter((alias) => alias !== item.alias); - if (item.selected) { - this.value = [...this.value, item.alias]; - } else { - this.value = this.value.filter((alias) => alias !== item.alias); - } + this.#setValue(tmpValue); + } + #setValue(value: Array) { + this.value = value; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } + #syncViewModel() { + const items: Array = this._extensions.map((extension) => ({ + ...extension, + selected: this.value!.includes(extension.alias) || extension.alias === TIPTAP_CORE_EXTENSION_ALIAS, + })); + + const uncategorizedLabel = this.localize.term('tiptap_extGroup_unknown'); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + const grouped = Object.groupBy(items, (item: UmbTiptapExtensionGroupItem) => item.group || uncategorizedLabel); + + this._groups = Object.keys(grouped) + .sort((a, b) => a.localeCompare(b)) + .map((key) => ({ group: key, extensions: grouped[key] })); + } + override render() { if (!this._groups.length) return nothing; return html` @@ -97,27 +166,15 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement
${this.localize.string(group.group)}
    - ${when( - group.group === '#tiptap_extGroup_formatting', - () => html` -
  • - -
    - - Rich Text Essentials -
    -
    -
  • - `, - )} ${repeat( group.extensions, (item) => html` -
  • +
  • this.#onClick(item)}>
    ${when(item.icon, () => html``)}