@@ -242,19 +257,22 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement
#renderGroup(group?: UmbTiptapToolbarGroupViewModel, rowIndex = 0, groupIndex = 0) {
if (!group) return nothing;
const showActionBar = this._toolbar[rowIndex].data.length > 1 && group.data.length === 0;
+ const items: UmbTiptapToolbarExtension[] = group!.data
+ .map((alias) => this.#context?.getExtensionByAlias(alias))
+ .filter((item): item is UmbTiptapToolbarExtension => !!item);
return html`
this.#onDrop(e, [rowIndex, groupIndex, group.data.length - 1])}>
-
- ${when(
- group?.data.length === 0,
- () => html`Empty`,
- () => html`${group!.data.map((alias, idx) => this.#renderItem(alias, rowIndex, groupIndex, idx))}`,
- )}
-
+
+
${when(
showActionBar,
() => html`
@@ -276,62 +294,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement
`;
}
- #renderItem(alias: string, rowIndex = 0, groupIndex = 0, itemIndex = 0) {
- const item = this.#context?.getExtensionByAlias(alias);
- if (!item) return nothing;
-
- const forbidden = !this.#context?.isExtensionEnabled(item.alias);
- const label = this.localize.string(item.label);
-
- switch (item.kind) {
- case 'styleMenu':
- case 'menu':
- return html`
-
this.#context.removeToolbarItem([rowIndex, groupIndex, itemIndex])}
- @dragend=${this.#onDragEnd}
- @dragstart=${(e: DragEvent) => this.#onDragStart(e, alias, [rowIndex, groupIndex, itemIndex])}>
-
- ${label}
-
-
-
- `;
-
- case 'button':
- default:
- return html`
-
this.#context.removeToolbarItem([rowIndex, groupIndex, itemIndex])}
- @dragend=${this.#onDragEnd}
- @dragstart=${(e: DragEvent) => this.#onDragStart(e, alias, [rowIndex, groupIndex, itemIndex])}>
-
- ${when(
- item.icon,
- () => html``,
- () => html`${label}`,
- )}
-
-
- `;
- }
- }
-
static override readonly styles = [
css`
:host {
@@ -468,45 +430,6 @@ export class UmbPropertyEditorUiTiptapToolbarConfigurationElement
opacity: 1;
}
}
-
- .items {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- gap: var(--uui-size-1);
-
- uui-button {
- --uui-button-font-weight: normal;
-
- &[draggable='true'],
- &[draggable='true'] > .inner {
- cursor: move;
- }
-
- &[disabled],
- &[disabled] > .inner {
- cursor: not-allowed;
- }
-
- &.forbidden {
- --color: var(--uui-color-danger);
- --color-standalone: var(--uui-color-danger-standalone);
- --color-emphasis: var(--uui-color-danger-emphasis);
- --color-contrast: var(--uui-color-danger);
- --uui-button-contrast-disabled: var(--uui-color-danger);
- --uui-button-border-color-disabled: var(--uui-color-danger);
- }
-
- div {
- display: flex;
- gap: var(--uui-size-1);
- }
-
- uui-symbol-expand {
- margin-left: var(--uui-size-space-2);
- }
- }
- }
}
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/components/tiptap-toolbar-group-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/components/tiptap-toolbar-group-configuration.element.ts
new file mode 100644
index 0000000000..0ec24d6fd6
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/components/tiptap-toolbar-group-configuration.element.ts
@@ -0,0 +1,177 @@
+import { UmbTiptapToolbarConfigurationContext } from '../contexts/tiptap-toolbar-configuration.context.js';
+import type { UmbTiptapToolbarExtension } from '../types.js';
+import { css, customElement, html, property, repeat, when } from '@umbraco-cms/backoffice/external/lit';
+import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
+import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
+import { UmbSorterController, UmbSorterResolvePlacementAsGrid } from '@umbraco-cms/backoffice/sorter';
+
+@customElement('umb-tiptap-toolbar-group-configuration')
+export class UmbTiptapToolbarGroupConfigurationElement<
+ TiptapToolbarItem extends UmbTiptapToolbarExtension = UmbTiptapToolbarExtension,
+> extends UmbLitElement {
+ #sorter = new UmbSorterController
(this, {
+ getUniqueOfElement: (element) => element.getAttribute('tiptap-toolbar-alias'),
+ getUniqueOfModel: (modelEntry) => modelEntry.alias!,
+ itemSelector: 'uui-button',
+ identifier: 'umb-tiptap-toolbar-sorter',
+ containerSelector: '.items',
+ resolvePlacement: UmbSorterResolvePlacementAsGrid,
+ onContainerChange: ({ item, model }) => {
+ this.dispatchEvent(new CustomEvent('container-change', { detail: { item, model } }));
+ },
+ onChange: ({ model }) => {
+ this.#items = model;
+ this.requestUpdate();
+ this.dispatchEvent(new UmbChangeEvent());
+ },
+ });
+
+ #context = new UmbTiptapToolbarConfigurationContext(this);
+
+ @property({ type: Array, attribute: false })
+ public set items(value: Array | undefined) {
+ this.#items = (value ?? []).filter((item, index, self) => self.findIndex((x) => x.alias === item.alias) === index);
+ this.#sorter.setModel(this.#items);
+ }
+ public get items(): Array {
+ return this.#items;
+ }
+ #items: Array = [];
+
+ @property({ type: Number })
+ rowIndex = 0;
+
+ @property({ type: Number })
+ groupIndex = 0;
+
+ #onRequestRemove(item: TiptapToolbarItem, index = 0) {
+ this.items = this.items.filter((x) => x.alias !== item.alias);
+ const rowIndex = this.rowIndex;
+ const groupIndex = this.groupIndex;
+ this.dispatchEvent(
+ new CustomEvent('remove', { detail: { rowIndex, groupIndex, index }, bubbles: true, composed: true }),
+ );
+ }
+
+ override render() {
+ return html`
+
+ ${when(
+ this.items?.length === 0,
+ () => html`Empty`,
+ () =>
+ repeat(
+ this.items,
+ (item) => item.alias,
+ (item, index) => this.#renderItem(item, index),
+ ),
+ )}
+
+ `;
+ }
+
+ #renderItem(item: TiptapToolbarItem, index = 0) {
+ const label = this.localize.string(item.label);
+ const forbidden = !this.#context?.isExtensionEnabled(item.alias);
+
+ switch (item.kind) {
+ case 'styleMenu':
+ case 'menu':
+ return html`
+ this.#onRequestRemove(item, index)}>
+
+ ${label}
+
+
+
+ `;
+
+ case 'button':
+ default:
+ return html`
+ this.#onRequestRemove(item, index)}>
+
+ ${when(
+ item.icon,
+ () => html``,
+ () => html`${label}`,
+ )}
+
+
+ `;
+ }
+ }
+
+ static override styles = [
+ css`
+ .items {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: var(--uui-size-1);
+
+ uui-button {
+ --uui-button-font-weight: normal;
+
+ &[draggable='true'],
+ &[draggable='true'] > .inner {
+ cursor: move;
+ }
+
+ &[disabled],
+ &[disabled] > .inner {
+ cursor: not-allowed;
+ }
+
+ &.forbidden {
+ --color: var(--uui-color-danger);
+ --color-standalone: var(--uui-color-danger-standalone);
+ --color-emphasis: var(--uui-color-danger-emphasis);
+ --color-contrast: var(--uui-color-danger);
+ --uui-button-contrast-disabled: var(--uui-color-danger);
+ --uui-button-border-color-disabled: var(--uui-color-danger);
+ }
+
+ div {
+ display: flex;
+ gap: var(--uui-size-1);
+ }
+
+ uui-symbol-expand {
+ margin-left: var(--uui-size-space-2);
+ }
+ }
+ }
+
+ uui-button[look='outline'] {
+ --uui-button-background-color-hover: var(--uui-color-surface);
+ }
+ `,
+ ];
+}
+
+export default UmbTiptapToolbarGroupConfigurationElement;
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'umb-tiptap-toolbar-group-configuration': UmbTiptapToolbarGroupConfigurationElement;
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/contexts/tiptap-toolbar-configuration.context.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/contexts/tiptap-toolbar-configuration.context.ts
index 646c6c32d9..c758839d3d 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/contexts/tiptap-toolbar-configuration.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/property-editors/tiptap/contexts/tiptap-toolbar-configuration.context.ts
@@ -240,6 +240,27 @@ export class UmbTiptapToolbarConfigurationContext extends UmbContextBase {
this.#toolbar.setValue(toolbar);
}
+ public updateToolbarItem(aliases: Array, to: [number, number]) {
+ const toolbar = [...this.#toolbar.getValue()];
+ const [rowIndex, groupIndex] = to;
+
+ const newToolbar = toolbar.map((row, rIdx) => {
+ if (rIdx !== rowIndex) return row;
+ return {
+ ...row,
+ data: row.data.map((group, gIdx) => {
+ if (gIdx !== groupIndex) return group;
+ return {
+ ...group,
+ data: [...aliases],
+ };
+ }),
+ };
+ });
+
+ this.#toolbar.setValue(newToolbar);
+ }
+
public updateToolbarRow(rowIndex: number, groups: Array) {
const toolbar = [...this.#toolbar.getValue()];