Skip to content

Commit f3658bf

Browse files
authored
Tiptap RTE: Style Menu extension kind (#18918)
* Adds 'styleMenu' Tiptap toolbar extension kind * Adds icons for `<h4>` and `<p>` tags * Adds commands to HTML Global Attributes extension for setting the `class` and `id` attributes. * Renamed "default-tiptap-toolbar-element.api.ts" file The "element" part was confusing. * Toolbar Menu: uses correct `item` value * Cascading Menu: adds localization for the label * Adds `label` attribute to UUI components for accessibility. * Toolbar Menu: uses correct `appearance` value * Removed unrequired `api` from Style Select * Destructs the `item.data` object
1 parent e4b3104 commit f3658bf

17 files changed

+205
-79
lines changed

src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-div.extension.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ export const Div = Node.create<DivOptions>({
2323
},
2424

2525
parseHTML() {
26-
return [{ tag: 'div' }];
26+
return [{ tag: this.name }];
2727
},
2828

2929
renderHTML({ HTMLAttributes }) {
30-
return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
30+
return [this.name, mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
3131
},
3232
});

src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-html-global-attributes.extension.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,48 @@ export const HtmlGlobalAttributes = Extension.create<HtmlGlobalAttributesOptions
5252
},
5353
];
5454
},
55+
56+
addCommands() {
57+
return {
58+
setClassName:
59+
(className, type) =>
60+
({ commands }) => {
61+
if (!className) return false;
62+
const types = type ? [type] : this.options.types;
63+
return types
64+
.map((type) => commands.updateAttributes(type, { class: className }))
65+
.every((response) => response);
66+
},
67+
unsetClassName:
68+
(type) =>
69+
({ commands }) => {
70+
const types = type ? [type] : this.options.types;
71+
return types.map((type) => commands.resetAttributes(type, 'class')).every((response) => response);
72+
},
73+
setId:
74+
(id, type) =>
75+
({ commands }) => {
76+
if (!id) return false;
77+
const types = type ? [type] : this.options.types;
78+
return types.map((type) => commands.updateAttributes(type, { id })).every((response) => response);
79+
},
80+
unsetId:
81+
(type) =>
82+
({ commands }) => {
83+
const types = type ? [type] : this.options.types;
84+
return types.map((type) => commands.resetAttributes(type, 'id')).every((response) => response);
85+
},
86+
};
87+
},
5588
});
89+
90+
declare module '@tiptap/core' {
91+
interface Commands<ReturnType> {
92+
htmlGlobalAttributes: {
93+
setClassName: (className?: string, type?: string) => ReturnType;
94+
unsetClassName: (type?: string) => ReturnType;
95+
setId: (id?: string, type?: string) => ReturnType;
96+
unsetId: (type?: string) => ReturnType;
97+
};
98+
}
99+
}

src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-span.extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const Span = Mark.create<SpanOptions>({
2828
return {
2929
setSpanStyle:
3030
(styles) =>
31-
({ commands, editor, chain }) => {
31+
({ commands, editor }) => {
3232
if (!styles) return false;
3333

3434
const existing = editor.getAttributes(this.name)?.style as string;

src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icon-dictionary.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,10 @@
986986
"name": "icon-heading-3",
987987
"file": "heading-3.svg"
988988
},
989+
{
990+
"name": "icon-heading-4",
991+
"file": "heading-4.svg"
992+
},
989993
{
990994
"name": "icon-headphones",
991995
"file": "headphones.svg"
@@ -1507,6 +1511,10 @@
15071511
"name": "icon-partly-cloudy",
15081512
"file": "cloud-sun.svg"
15091513
},
1514+
{
1515+
"name": "icon-paragraph",
1516+
"file": "pilcrow.svg"
1517+
},
15101518
{
15111519
"name": "icon-paste-in",
15121520
"file": "clipboard-paste.svg",

src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,9 @@ path: () => import("./icons/icon-heading-2.js"),
775775
name: "icon-heading-3",
776776
path: () => import("./icons/icon-heading-3.js"),
777777
},{
778+
name: "icon-heading-4",
779+
path: () => import("./icons/icon-heading-4.js"),
780+
},{
778781
name: "icon-headphones",
779782
path: () => import("./icons/icon-headphones.js"),
780783
},{
@@ -1217,6 +1220,9 @@ path: () => import("./icons/icon-paper-plane.js"),
12171220
name: "icon-partly-cloudy",
12181221
path: () => import("./icons/icon-partly-cloudy.js"),
12191222
},{
1223+
name: "icon-paragraph",
1224+
path: () => import("./icons/icon-paragraph.js"),
1225+
},{
12201226
name: "icon-paste-in",
12211227
legacy: true,
12221228
hidden: true,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default `<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.75" class="lucide lucide-heading-4" viewBox="0 0 24 24"><path d="M12 18V6M17 10v3a1 1 0 0 0 1 1h3M21 10v8M4 12h8M4 18V6"/></svg>`;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default `<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.75" class="lucide lucide-pilcrow" viewBox="0 0 24 24"><path d="M13 4v16M17 4v16M19 4H9.5a4.5 4.5 0 0 0 0 9H13"/></svg>`;

src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/cascading-menu-popover.element.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { css, customElement, html, ifDefined, property, repeat, when } from '@umbraco-cms/backoffice/external/lit';
2+
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
23
import { UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui';
34

45
export type UmbCascadingMenuItem = {
@@ -12,7 +13,7 @@ export type UmbCascadingMenuItem = {
1213
};
1314

1415
@customElement('umb-cascading-menu-popover')
15-
export class UmbCascadingMenuPopoverElement extends UUIPopoverContainerElement {
16+
export class UmbCascadingMenuPopoverElement extends UmbElementMixin(UUIPopoverContainerElement) {
1617
@property({ type: Array })
1718
items?: Array<UmbCascadingMenuItem>;
1819

@@ -70,6 +71,8 @@ export class UmbCascadingMenuPopoverElement extends UUIPopoverContainerElement {
7071
element.setAttribute('popovertarget', popoverId);
7172
}
7273

74+
const label = this.localize.string(item.label);
75+
7376
return html`
7477
<div
7578
@mouseenter=${() => this.#onMouseEnter(item, popoverId)}
@@ -80,11 +83,12 @@ export class UmbCascadingMenuPopoverElement extends UUIPopoverContainerElement {
8083
() => html`
8184
<uui-menu-item
8285
class=${item.separatorAfter ? 'separator' : ''}
86+
label=${label}
8387
popovertarget=${popoverId}
8488
@click-label=${() => this.#onClick(item, popoverId)}>
8589
${when(item.icon, (icon) => html`<uui-icon slot="icon" name=${icon}></uui-icon>`)}
8690
<div slot="label" class="menu-item">
87-
<span style=${ifDefined(item.style)}>${item.label}</span>
91+
<span style=${ifDefined(item.style)}>${label}</span>
8892
${when(item.items, () => html`<uui-symbol-expand></uui-symbol-expand>`)}
8993
</div>
9094
</uui-menu-item>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class UmbTiptapToolbarElement extends UmbLitElement {
6161
},
6262
undefined,
6363
undefined,
64-
() => import('../toolbar/default-tiptap-toolbar-element.api.js'),
64+
() => import('../toolbar/default-tiptap-toolbar-api.js'),
6565
);
6666

6767
this.#extensionsController.apiProperties = { configuration: this.configuration };

0 commit comments

Comments
 (0)