Tiptap RTE: Undo deleted blocks (#19851)

* RTE: Restore deleted blocks

Maintains a state of unused (deleted) blocks,
that could be restored later, e.g. with Tiptap RTE's undo action.

Fixes #19637

* Updated with @copilot suggestions

* Fixes restored block state on variant documents
This commit is contained in:
Lee Kelleher
2025-08-06 14:51:21 +01:00
committed by GitHub
parent ad0854be4d
commit 046a3d9aad

View File

@@ -10,14 +10,16 @@ import {
UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
} from '@umbraco-cms/backoffice/validation';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import { UMB_CONTENT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content';
import { UMB_PROPERTY_CONTEXT, UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
import type { UmbBlockRteTypeModel } from '@umbraco-cms/backoffice/block-rte';
import type { StyleInfo } from '@umbraco-cms/backoffice/external/lit';
import type { UmbBlockDataModel } from '@umbraco-cms/backoffice/block';
import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '@umbraco-cms/backoffice/block-rte';
import type {
UmbPropertyEditorUiElement,
UmbPropertyEditorConfigCollection,
} from '@umbraco-cms/backoffice/property-editor';
import type { StyleInfo } from '@umbraco-cms/backoffice/external/lit';
export abstract class UmbPropertyEditorUiRteElementBase
extends UmbFormControlMixin<UmbPropertyEditorRteValueType | undefined, typeof UmbLitElement, undefined>(UmbLitElement)
@@ -127,6 +129,10 @@ export abstract class UmbPropertyEditorUiRteElementBase
readonly #validationContext = new UmbValidationContext(this);
readonly #unusedLayoutLookup: Map<string, UmbBlockRteLayoutModel> = new Map();
readonly #unusedContentLookup: Map<string, UmbBlockDataModel> = new Map();
readonly #unusedSettingsLookup: Map<string, UmbBlockDataModel> = new Map();
constructor() {
super();
@@ -262,9 +268,72 @@ export abstract class UmbPropertyEditorUiRteElementBase
);
}
#setUnusedBlockLookups(unusedLayouts: Array<UmbBlockRteLayoutModel>) {
if (unusedLayouts.length) {
unusedLayouts.forEach((layout) => {
if (layout.contentKey) {
this.#unusedLayoutLookup.set(layout.contentKey, layout);
const contentBlock = this.#managerContext.getContentOf(layout.contentKey);
if (contentBlock) {
this.#unusedContentLookup.set(layout.contentKey, contentBlock);
} else {
console.warn(
`Expected content block for '${layout.contentKey}' was not found. This may indicate a data consistency issue.`,
);
}
if (layout.settingsKey) {
const settingsBlock = this.#managerContext.getSettingsOf(layout.settingsKey);
if (settingsBlock) {
this.#unusedSettingsLookup.set(layout.settingsKey, settingsBlock);
} else {
console.warn(
`Expected settings block for '${layout.settingsKey}' was not found. This may indicate a data consistency issue.`,
);
}
}
}
});
}
}
#restoreUnusedBlocks(usedContentKeys: Array<string | null>) {
if (usedContentKeys.length) {
usedContentKeys.forEach((contentKey) => {
if (contentKey && this.#unusedLayoutLookup.has(contentKey)) {
const layout = this.#unusedLayoutLookup.get(contentKey);
if (layout) {
this.#managerContext.setOneLayout(layout);
this.#unusedLayoutLookup.delete(contentKey);
const contentBlock = this.#unusedContentLookup.get(contentKey);
if (contentBlock) {
this.#managerContext.setOneContent(contentBlock);
this.#managerContext.setOneExpose(contentKey, UmbVariantId.CreateInvariant());
this.#unusedContentLookup.delete(contentKey);
}
if (layout.settingsKey && this.#unusedSettingsLookup.has(layout.settingsKey)) {
const settingsBlock = this.#unusedSettingsLookup.get(layout.settingsKey);
if (settingsBlock) {
this.#managerContext.setOneSettings(settingsBlock);
this.#unusedSettingsLookup.delete(layout.settingsKey);
}
}
}
}
});
}
}
protected _filterUnusedBlocks(usedContentKeys: (string | null)[]) {
const unusedLayouts = this.#managerContext.getLayouts().filter((x) => usedContentKeys.indexOf(x.contentKey) === -1);
// Temporarily set the unused layouts to the lookup, as they could be restored later, e.g. via an RTE undo action. [LK]
this.#restoreUnusedBlocks(usedContentKeys);
this.#setUnusedBlockLookups(unusedLayouts);
const unusedContentKeys = unusedLayouts.map((x) => x.contentKey);
const unusedSettingsKeys = unusedLayouts
@@ -279,4 +348,11 @@ export abstract class UmbPropertyEditorUiRteElementBase
protected _fireChangeEvent() {
this.dispatchEvent(new UmbChangeEvent());
}
override destroy() {
super.destroy();
this.#unusedLayoutLookup.clear();
this.#unusedContentLookup.clear();
this.#unusedSettingsLookup.clear();
}
}