Skip to content

Commit d05add4

Browse files
leekelleherCopilot
authored andcommitted
Tiptap RTE: Allow removal of unregistered extensions (#20571)
* Tiptap toolbar config: enable removal of unregistered extensions * Tiptap statusbar config: enable removal of unregistered extensions * Tiptap toolbar config: Typescript tidy-up * Tiptap toolbar sorting amend Removed the need for the `tiptap-toolbar-alias` attribute, we can reuse the `data-mark`. * Tiptap extension config UI amend If the extension doesn't have a `description`, then add the `alias` to the title/tooltip, to give a DX hint. * Tiptap toolbar: adds `title` to placeholder skeleton * Added missing `forExtensions` for Style Select and Horizontal Rule toolbar extensions * Update src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/toolbar-configuration/property-editor-ui-tiptap-toolbar-configuration.element.ts Co-authored-by: Copilot <[email protected]> * Update src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/statusbar-configuration/property-editor-ui-tiptap-statusbar-configuration.element.ts Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 68d1b94 commit d05add4

File tree

10 files changed

+80
-70
lines changed

10 files changed

+80
-70
lines changed

src/Umbraco.Web.UI.Client/src/packages/tiptap/components/toolbar/tiptap-toolbar.element.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ export class UmbTiptapToolbarElement extends UmbLitElement {
9292
}
9393

9494
#renderActions(aliases: Array<string>) {
95-
return repeat(aliases, (alias) => this.#lookup?.get(alias) ?? this.#renderActionPlaceholder());
95+
return repeat(aliases, (alias) => this.#lookup?.get(alias) ?? this.#renderActionPlaceholder(alias));
9696
}
9797

98-
#renderActionPlaceholder() {
99-
return html`<span class="skeleton" role="none"></span>`;
98+
#renderActionPlaceholder(alias: string) {
99+
return html`<span class="skeleton" role="none" title="Loading '${alias}'"></span>`;
100100
}
101101

102102
static override readonly styles = css`

src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/horizontal-rule/manifests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const manifests: Array<UmbExtensionManifest> = [
1616
alias: 'Umb.Tiptap.Toolbar.HorizontalRule',
1717
name: 'Horizontal Rule Tiptap Toolbar Extension',
1818
api: () => import('./horizontal-rule.tiptap-toolbar-api.js'),
19+
forExtensions: ['Umb.Tiptap.HorizontalRule'],
1920
meta: {
2021
alias: 'horizontalRule',
2122
icon: 'icon-horizontal-rule',

src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/style-select/manifests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export const manifests: Array<UmbExtensionManifest> = [
44
kind: 'styleMenu',
55
alias: 'Umb.Tiptap.Toolbar.StyleSelect',
66
name: 'Style Select Tiptap Extension',
7+
forExtensions: ['Umb.Tiptap.Heading', 'Umb.Tiptap.Blockquote', 'Umb.Tiptap.CodeBlock'],
78
items: [
89
{
910
label: 'Headers',

src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/extensions-configuration/property-editor-ui-tiptap-extensions-configuration.element.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,4 @@
1-
import {
2-
css,
3-
customElement,
4-
html,
5-
ifDefined,
6-
nothing,
7-
property,
8-
state,
9-
repeat,
10-
when,
11-
} from '@umbraco-cms/backoffice/external/lit';
1+
import { css, customElement, html, nothing, property, state, repeat, when } from '@umbraco-cms/backoffice/external/lit';
122
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
133
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
144
import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
@@ -166,7 +156,7 @@ export class UmbPropertyEditorUiTiptapExtensionsConfigurationElement
166156
${repeat(
167157
group.extensions,
168158
(item) => html`
169-
<li title=${ifDefined(item.description)}>
159+
<li title=${item.description ?? item.alias}>
170160
<uui-checkbox
171161
label=${this.localize.string(item.label)}
172162
value=${item.alias}

src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/statusbar-configuration/property-editor-ui-tiptap-statusbar-configuration.element.ts

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export class UmbPropertyEditorUiTiptapStatusbarConfigurationElement
161161
<uui-button
162162
compact
163163
class=${forbidden ? 'forbidden' : ''}
164-
data-mark="tiptap-toolbar-item:${item.alias}"
164+
data-mark="tiptap-statusbar-item:${item.alias}"
165165
draggable="true"
166166
label=${label}
167167
look=${forbidden ? 'placeholder' : 'outline'}
@@ -212,30 +212,44 @@ export class UmbPropertyEditorUiTiptapStatusbarConfigurationElement
212212

213213
#renderItem(alias: string, areaIndex = 0, itemIndex = 0) {
214214
const item = this.#context?.getExtensionByAlias(alias);
215-
if (!item) return nothing;
216215

217216
const forbidden = !this.#context?.isExtensionEnabled(item.alias);
218-
const label = this.localize.string(item.label);
219-
220-
return html`
221-
<uui-button
222-
compact
223-
class=${forbidden ? 'forbidden' : ''}
224-
data-mark="tiptap-toolbar-item:${item.alias}"
225-
draggable="true"
226-
label=${label}
227-
look=${forbidden ? 'placeholder' : 'outline'}
228-
title=${label}
229-
?disabled=${forbidden}
230-
@click=${() => this.#context.removeStatusbarItem([areaIndex, itemIndex])}
231-
@dragend=${this.#onDragEnd}
232-
@dragstart=${(e: DragEvent) => this.#onDragStart(e, alias, [areaIndex, itemIndex])}>
233-
<div class="inner">
234-
${when(item.icon, (icon) => html`<umb-icon .name=${icon}></umb-icon>`)}
235-
<span>${label}</span>
236-
</div>
237-
</uui-button>
238-
`;
217+
const label = this.localize.string(item.label) || item.alias;
218+
219+
switch (item.kind) {
220+
case 'unknown':
221+
return html`
222+
<uui-button
223+
compact
224+
class="missing"
225+
data-mark="tiptap-statusbar-item:${item.alias}"
226+
color="danger"
227+
look="placeholder"
228+
label="Missing extension"
229+
title="Missing extension: ${item.alias}"
230+
@click=${() => this.#context.removeStatusbarItem([areaIndex, itemIndex])}></uui-button>
231+
`;
232+
233+
default:
234+
return html`
235+
<uui-button
236+
compact
237+
class=${forbidden ? 'forbidden' : ''}
238+
data-mark="tiptap-statusbar-item:${item.alias}"
239+
draggable="true"
240+
label=${label}
241+
look=${forbidden ? 'placeholder' : 'outline'}
242+
title=${label}
243+
@click=${() => this.#context.removeStatusbarItem([areaIndex, itemIndex])}
244+
@dragend=${this.#onDragEnd}
245+
@dragstart=${(e: DragEvent) => this.#onDragStart(e, alias, [areaIndex, itemIndex])}>
246+
<div class="inner">
247+
${when(item.icon, (icon) => html`<umb-icon .name=${icon}></umb-icon>`)}
248+
<span>${label}</span>
249+
</div>
250+
</uui-button>
251+
`;
252+
}
239253
}
240254

241255
static override readonly styles = [
@@ -303,8 +317,8 @@ export class UmbPropertyEditorUiTiptapStatusbarConfigurationElement
303317
--color-standalone: var(--uui-color-danger-standalone);
304318
--color-emphasis: var(--uui-color-danger-emphasis);
305319
--color-contrast: var(--uui-color-danger);
306-
--uui-button-contrast-disabled: var(--uui-color-danger);
307-
--uui-button-border-color-disabled: var(--uui-color-danger);
320+
--uui-button-contrast: var(--uui-color-danger);
321+
--uui-button-border-color: var(--uui-color-danger);
308322
}
309323
310324
div {

src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/statusbar-configuration/tiptap-statusbar-configuration.context.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export class UmbTiptapStatusbarConfigurationContext extends UmbContextBase {
3131
const _extensions = extensions
3232
.sort((a, b) => a.alias.localeCompare(b.alias))
3333
.map((ext) => ({
34+
kind: 'default',
3435
alias: ext.alias,
3536
label: ext.meta.label,
3637
icon: ext.meta.icon,
@@ -75,8 +76,8 @@ export class UmbTiptapStatusbarConfigurationContext extends UmbContextBase {
7576
.filter((ext) => ext.alias?.toLowerCase().includes(query) || ext.label?.toLowerCase().includes(query));
7677
}
7778

78-
public getExtensionByAlias(alias: string): UmbTiptapStatusbarExtension | undefined {
79-
return this.#lookup?.get(alias);
79+
public getExtensionByAlias(alias: string): UmbTiptapStatusbarExtension {
80+
return this.#lookup?.get(alias) ?? { label: '', alias, icon: '', kind: 'unknown' };
8081
}
8182

8283
public isExtensionEnabled(alias: string): boolean {

src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/toolbar-configuration/property-editor-ui-tiptap-toolbar-configuration.element.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,7 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement
255255
#renderGroup(group?: UmbTiptapToolbarGroupViewModel, rowIndex = 0, groupIndex = 0) {
256256
if (!group) return nothing;
257257
const showActionBar = this._toolbar[rowIndex].data.length > 1 && group.data.length === 0;
258-
const items: UmbTiptapToolbarExtension[] = group!.data
259-
.map((alias) => this.#context?.getExtensionByAlias(alias))
260-
.filter((item): item is UmbTiptapToolbarExtension => !!item);
258+
const items = group.data.map((alias) => this.#context?.getExtensionByAlias(alias));
261259
return html`
262260
<div
263261
class="group"

src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/toolbar-configuration/tiptap-toolbar-configuration.context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ export class UmbTiptapToolbarConfigurationContext extends UmbContextBase {
8080
.filter((ext) => ext.alias?.toLowerCase().includes(query) || ext.label?.toLowerCase().includes(query));
8181
}
8282

83-
public getExtensionByAlias(alias: string): UmbTiptapToolbarExtension | undefined {
84-
return this.#lookup?.get(alias);
83+
public getExtensionByAlias(alias: string): UmbTiptapToolbarExtension {
84+
return this.#lookup?.get(alias) ?? { label: '', alias, icon: '', kind: 'unknown' };
8585
}
8686

8787
public isExtensionEnabled(alias: string): boolean {

src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/toolbar-configuration/tiptap-toolbar-group-configuration.element.ts

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ export class UmbTiptapToolbarGroupConfigurationElement<
1010
TiptapToolbarItem extends UmbTiptapToolbarExtension = UmbTiptapToolbarExtension,
1111
> extends UmbLitElement {
1212
#sorter = new UmbSorterController<TiptapToolbarItem, HTMLElement>(this, {
13-
getUniqueOfElement: (element) => element.getAttribute('tiptap-toolbar-alias'),
14-
getUniqueOfModel: (modelEntry) => modelEntry.alias!,
15-
itemSelector: 'uui-button',
13+
getUniqueOfElement: (element) => element.dataset.mark,
14+
getUniqueOfModel: (modelEntry) => `tiptap-toolbar-item:${modelEntry.alias}`,
15+
itemSelector: '.draggable',
1616
identifier: 'umb-tiptap-toolbar-sorter',
1717
containerSelector: '.items',
1818
resolvePlacement: UmbSorterResolvePlacementAsGrid,
@@ -71,7 +71,7 @@ export class UmbTiptapToolbarGroupConfigurationElement<
7171
}
7272

7373
#renderItem(item: TiptapToolbarItem, index = 0) {
74-
const label = this.localize.string(item.label);
74+
const label = this.localize.string(item.label) || item.alias;
7575
const forbidden = !this.#context?.isExtensionEnabled(item.alias);
7676

7777
switch (item.kind) {
@@ -80,13 +80,11 @@ export class UmbTiptapToolbarGroupConfigurationElement<
8080
return html`
8181
<uui-button
8282
compact
83-
class=${forbidden ? 'forbidden' : ''}
83+
class="draggable ${forbidden ? 'forbidden' : ''}"
8484
data-mark="tiptap-toolbar-item:${item.alias}"
8585
look=${forbidden ? 'placeholder' : 'outline'}
8686
label=${label}
8787
title=${label}
88-
?disabled=${forbidden}
89-
tiptap-toolbar-alias=${item.alias}
9088
@click=${() => this.#onRequestRemove(item, index)}>
9189
<div class="inner">
9290
<span>${label}</span>
@@ -95,18 +93,29 @@ export class UmbTiptapToolbarGroupConfigurationElement<
9593
</uui-button>
9694
`;
9795

96+
case 'unknown':
97+
return html`
98+
<uui-button
99+
compact
100+
data-mark="tiptap-toolbar-item:${item.alias}"
101+
color="danger"
102+
look="placeholder"
103+
label="Missing extension"
104+
title="Missing extension: ${item.alias}"
105+
@click=${() => this.#onRequestRemove(item, index)}></uui-button>
106+
`;
107+
98108
case 'button':
109+
case 'colorPickerButton':
99110
default:
100111
return html`
101112
<uui-button
102113
compact
103-
class=${forbidden ? 'forbidden' : ''}
114+
class="draggable ${forbidden ? 'forbidden' : ''}"
104115
data-mark="tiptap-toolbar-item:${item.alias}"
105116
look=${forbidden ? 'placeholder' : 'outline'}
106117
label=${label}
107118
title=${label}
108-
?disabled=${forbidden}
109-
tiptap-toolbar-alias=${item.alias}
110119
@click=${() => this.#onRequestRemove(item, index)}>
111120
<div class="inner">
112121
${when(
@@ -131,23 +140,18 @@ export class UmbTiptapToolbarGroupConfigurationElement<
131140
uui-button {
132141
--uui-button-font-weight: normal;
133142
134-
&[draggable='true'],
135-
&[draggable='true'] > .inner {
143+
&.draggable,
144+
&.draggable > .inner {
136145
cursor: move;
137146
}
138147
139-
&[disabled],
140-
&[disabled] > .inner {
141-
cursor: not-allowed;
142-
}
143-
144148
&.forbidden {
145149
--color: var(--uui-color-danger);
146150
--color-standalone: var(--uui-color-danger-standalone);
147151
--color-emphasis: var(--uui-color-danger-emphasis);
148152
--color-contrast: var(--uui-color-danger);
149-
--uui-button-contrast-disabled: var(--uui-color-danger);
150-
--uui-button-border-color-disabled: var(--uui-color-danger);
153+
--uui-button-contrast: var(--uui-color-danger);
154+
--uui-button-border-color: var(--uui-color-danger);
151155
}
152156
153157
div {
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
export type UmbTiptapSortableViewModel<T> = { unique: string; data: T };
22

3-
export type UmbTiptapStatusbarExtension = {
3+
export type UmbTiptapExtensionBase = {
4+
kind?: string;
45
alias: string;
56
label: string;
67
icon: string;
78
dependencies?: Array<string>;
89
};
910

11+
export type UmbTiptapStatusbarExtension = UmbTiptapExtensionBase;
12+
1013
export type UmbTiptapStatusbarViewModel = UmbTiptapSortableViewModel<Array<string>>;
1114

12-
export type UmbTiptapToolbarExtension = UmbTiptapStatusbarExtension & {
13-
kind?: string;
14-
};
15+
export type UmbTiptapToolbarExtension = UmbTiptapExtensionBase;
1516

1617
export type UmbTiptapToolbarRowViewModel = UmbTiptapSortableViewModel<Array<UmbTiptapToolbarGroupViewModel>>;
1718
export type UmbTiptapToolbarGroupViewModel = UmbTiptapSortableViewModel<Array<string>>;

0 commit comments

Comments
 (0)