Tiptap extensions field

observes the Block configuration,
updates UI accordingly, to enable/disable the Block extension.
This commit is contained in:
leekelleher
2024-10-14 13:16:25 +01:00
committed by Jacob Overgaard
parent bd5b898fce
commit 22f8aac99f
2 changed files with 96 additions and 38 deletions

View File

@@ -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',

View File

@@ -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<UmbTiptapExtensionGroupItem>;
};
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<string>([TIPTAP_CORE_EXTENSION_ALIAS]);
@property({ attribute: false })
value?: Array<string> = [];
value?: Array<string> = [TIPTAP_CORE_EXTENSION_ALIAS];
@property({ attribute: false })
config?: UmbPropertyEditorConfigCollection;
@@ -43,6 +60,41 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement
@state()
private _groups: Array<UmbTiptapExtensionGroup> = [];
constructor() {
super();
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, async (dataset) => {
this.observe(
await dataset.propertyValueByAlias<Array<unknown>>('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<unknown>) {
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<UmbTiptapExtensionGroupItem> = 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<string>, b: Array<string>) {
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<string>) {
this.value = value;
this.dispatchEvent(new UmbPropertyValueChangeEvent());
}
#syncViewModel() {
const items: Array<UmbTiptapExtensionGroupItem> = 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
<div class="group">
<uui-label>${this.localize.string(group.group)}</uui-label>
<ul>
${when(
group.group === '#tiptap_extGroup_formatting',
() => html`
<li title="This is a core extension, it must be enabled">
<uui-checkbox checked disabled label="Rich Text Essentials">
<div class="inner">
<umb-icon name="icon-browser-window"></umb-icon>
<span>Rich Text Essentials</span>
</div>
</uui-checkbox>
</li>
`,
)}
${repeat(
group.extensions,
(item) => html`
<li>
<li title=${ifDefined(item.description)}>
<uui-checkbox
label=${this.localize.string(item.label)}
value=${item.alias}
?checked=${item.selected}
?disabled=${this.#disabledExtensions.has(item.alias)}
@change=${() => this.#onClick(item)}>
<div class="inner">
${when(item.icon, () => html`<umb-icon .name=${item.icon}></umb-icon>`)}