Skip to content

Commit 046a3d9

Browse files
authored
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
1 parent ad0854b commit 046a3d9

File tree

1 file changed

+78
-2
lines changed

1 file changed

+78
-2
lines changed

src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ import {
1010
UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
1111
} from '@umbraco-cms/backoffice/validation';
1212
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
13+
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
1314
import { UMB_CONTENT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content';
1415
import { UMB_PROPERTY_CONTEXT, UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
15-
import type { UmbBlockRteTypeModel } from '@umbraco-cms/backoffice/block-rte';
16+
import type { StyleInfo } from '@umbraco-cms/backoffice/external/lit';
17+
import type { UmbBlockDataModel } from '@umbraco-cms/backoffice/block';
18+
import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '@umbraco-cms/backoffice/block-rte';
1619
import type {
1720
UmbPropertyEditorUiElement,
1821
UmbPropertyEditorConfigCollection,
1922
} from '@umbraco-cms/backoffice/property-editor';
20-
import type { StyleInfo } from '@umbraco-cms/backoffice/external/lit';
2123

2224
export abstract class UmbPropertyEditorUiRteElementBase
2325
extends UmbFormControlMixin<UmbPropertyEditorRteValueType | undefined, typeof UmbLitElement, undefined>(UmbLitElement)
@@ -127,6 +129,10 @@ export abstract class UmbPropertyEditorUiRteElementBase
127129

128130
readonly #validationContext = new UmbValidationContext(this);
129131

132+
readonly #unusedLayoutLookup: Map<string, UmbBlockRteLayoutModel> = new Map();
133+
readonly #unusedContentLookup: Map<string, UmbBlockDataModel> = new Map();
134+
readonly #unusedSettingsLookup: Map<string, UmbBlockDataModel> = new Map();
135+
130136
constructor() {
131137
super();
132138

@@ -262,9 +268,72 @@ export abstract class UmbPropertyEditorUiRteElementBase
262268
);
263269
}
264270

271+
#setUnusedBlockLookups(unusedLayouts: Array<UmbBlockRteLayoutModel>) {
272+
if (unusedLayouts.length) {
273+
unusedLayouts.forEach((layout) => {
274+
if (layout.contentKey) {
275+
this.#unusedLayoutLookup.set(layout.contentKey, layout);
276+
277+
const contentBlock = this.#managerContext.getContentOf(layout.contentKey);
278+
if (contentBlock) {
279+
this.#unusedContentLookup.set(layout.contentKey, contentBlock);
280+
} else {
281+
console.warn(
282+
`Expected content block for '${layout.contentKey}' was not found. This may indicate a data consistency issue.`,
283+
);
284+
}
285+
286+
if (layout.settingsKey) {
287+
const settingsBlock = this.#managerContext.getSettingsOf(layout.settingsKey);
288+
if (settingsBlock) {
289+
this.#unusedSettingsLookup.set(layout.settingsKey, settingsBlock);
290+
} else {
291+
console.warn(
292+
`Expected settings block for '${layout.settingsKey}' was not found. This may indicate a data consistency issue.`,
293+
);
294+
}
295+
}
296+
}
297+
});
298+
}
299+
}
300+
301+
#restoreUnusedBlocks(usedContentKeys: Array<string | null>) {
302+
if (usedContentKeys.length) {
303+
usedContentKeys.forEach((contentKey) => {
304+
if (contentKey && this.#unusedLayoutLookup.has(contentKey)) {
305+
const layout = this.#unusedLayoutLookup.get(contentKey);
306+
if (layout) {
307+
this.#managerContext.setOneLayout(layout);
308+
this.#unusedLayoutLookup.delete(contentKey);
309+
310+
const contentBlock = this.#unusedContentLookup.get(contentKey);
311+
if (contentBlock) {
312+
this.#managerContext.setOneContent(contentBlock);
313+
this.#managerContext.setOneExpose(contentKey, UmbVariantId.CreateInvariant());
314+
this.#unusedContentLookup.delete(contentKey);
315+
}
316+
317+
if (layout.settingsKey && this.#unusedSettingsLookup.has(layout.settingsKey)) {
318+
const settingsBlock = this.#unusedSettingsLookup.get(layout.settingsKey);
319+
if (settingsBlock) {
320+
this.#managerContext.setOneSettings(settingsBlock);
321+
this.#unusedSettingsLookup.delete(layout.settingsKey);
322+
}
323+
}
324+
}
325+
}
326+
});
327+
}
328+
}
329+
265330
protected _filterUnusedBlocks(usedContentKeys: (string | null)[]) {
266331
const unusedLayouts = this.#managerContext.getLayouts().filter((x) => usedContentKeys.indexOf(x.contentKey) === -1);
267332

333+
// Temporarily set the unused layouts to the lookup, as they could be restored later, e.g. via an RTE undo action. [LK]
334+
this.#restoreUnusedBlocks(usedContentKeys);
335+
this.#setUnusedBlockLookups(unusedLayouts);
336+
268337
const unusedContentKeys = unusedLayouts.map((x) => x.contentKey);
269338

270339
const unusedSettingsKeys = unusedLayouts
@@ -279,4 +348,11 @@ export abstract class UmbPropertyEditorUiRteElementBase
279348
protected _fireChangeEvent() {
280349
this.dispatchEvent(new UmbChangeEvent());
281350
}
351+
352+
override destroy() {
353+
super.destroy();
354+
this.#unusedLayoutLookup.clear();
355+
this.#unusedContentLookup.clear();
356+
this.#unusedSettingsLookup.clear();
357+
}
282358
}

0 commit comments

Comments
 (0)