diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/cascading-menu-popover.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/cascading-menu-popover.element.ts index 047b6c05891c..8709139f6425 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/cascading-menu-popover.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/cascading-menu-popover.element.ts @@ -9,6 +9,7 @@ export type UmbCascadingMenuItem = { element?: HTMLElement; separatorAfter?: boolean; style?: string; + isActive?: () => boolean | undefined; execute?: () => void; }; @@ -21,8 +22,12 @@ export class UmbCascadingMenuPopoverElement extends UmbElementMixin(UUIPopoverCo return this.shadowRoot?.querySelector(`#${popoverId}`) as UUIPopoverContainerElement; } - #onMouseEnter(item: UmbCascadingMenuItem, popoverId: string) { - if (!item.items?.length) return; + #isMenuActive(items?: UmbCascadingMenuItem[]): boolean { + return !!items?.some((item) => item.isActive?.() || this.#isMenuActive(item.items)); + } + + #onMouseEnter(item: UmbCascadingMenuItem, popoverId?: string) { + if (!item.items?.length || !popoverId) return; const popover = this.#getPopoverById(popoverId); if (!popover) return; @@ -33,7 +38,9 @@ export class UmbCascadingMenuPopoverElement extends UmbElementMixin(UUIPopoverCo popover.showPopover(); } - #onMouseLeave(item: UmbCascadingMenuItem, popoverId: string) { + #onMouseLeave(item: UmbCascadingMenuItem, popoverId?: string) { + if (!popoverId) return; + const popover = this.#getPopoverById(popoverId); if (!popover) return; @@ -43,12 +50,16 @@ export class UmbCascadingMenuPopoverElement extends UmbElementMixin(UUIPopoverCo popover.hidePopover(); } - #onClick(item: UmbCascadingMenuItem, popoverId: string) { + #onClick(item: UmbCascadingMenuItem, popoverId?: string) { item.execute?.(); - setTimeout(() => { - this.#onMouseLeave(item, popoverId); - }, 100); + if (!popoverId) { + setTimeout(() => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this.hidePopover(); + }, 100); + } } override render() { @@ -64,14 +75,15 @@ export class UmbCascadingMenuPopoverElement extends UmbElementMixin(UUIPopoverCo } #renderItem(item: UmbCascadingMenuItem, index: number) { - const popoverId = `item-${index}`; + const popoverId = item.items ? `menu-${index}` : undefined; const element = item.element; - if (element) { + if (element && popoverId) { element.setAttribute('popovertarget', popoverId); } const label = this.localize.string(item.label); + const isActive = item.isActive?.() || this.#isMenuActive(item.items) || false; return html`
this.#onClick(item, popoverId)}> ${when(item.icon, (icon) => html``)} `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/toolbar/style-menu.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/toolbar/style-menu.tiptap-toolbar-api.ts index dda2e85f260c..381607675acd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/toolbar/style-menu.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/toolbar/style-menu.tiptap-toolbar-api.ts @@ -2,14 +2,28 @@ import { UmbTiptapToolbarElementApiBase } from '../../extensions/base.js'; import type { MetaTiptapToolbarStyleMenuItem } from '../../extensions/types.js'; import type { ChainedCommands, Editor } from '@umbraco-cms/backoffice/external/tiptap'; +type UmbTiptapToolbarStyleMenuCommandType = { + type: string; + command: (chain: ChainedCommands) => ChainedCommands; + isActive?: (editor?: Editor) => boolean | undefined; +}; + export default class UmbTiptapToolbarStyleMenuApi extends UmbTiptapToolbarElementApiBase { - #commands: Record ChainedCommands }> = { - h1: { type: 'heading', command: (chain) => chain.toggleHeading({ level: 1 }) }, - h2: { type: 'heading', command: (chain) => chain.toggleHeading({ level: 2 }) }, - h3: { type: 'heading', command: (chain) => chain.toggleHeading({ level: 3 }) }, - h4: { type: 'heading', command: (chain) => chain.toggleHeading({ level: 4 }) }, - h5: { type: 'heading', command: (chain) => chain.toggleHeading({ level: 5 }) }, - h6: { type: 'heading', command: (chain) => chain.toggleHeading({ level: 6 }) }, + #headingCommand(level: 1 | 2 | 3 | 4 | 5 | 6): UmbTiptapToolbarStyleMenuCommandType { + return { + type: 'heading', + command: (chain) => chain.toggleHeading({ level }), + isActive: (editor) => editor?.isActive('heading', { level }), + }; + } + + #commands: Record = { + h1: this.#headingCommand(1), + h2: this.#headingCommand(2), + h3: this.#headingCommand(3), + h4: this.#headingCommand(4), + h5: this.#headingCommand(5), + h6: this.#headingCommand(6), p: { type: 'paragraph', command: (chain) => chain.setParagraph() }, blockquote: { type: 'blockquote', command: (chain) => chain.toggleBlockquote() }, code: { type: 'code', command: (chain) => chain.toggleCode() }, @@ -24,6 +38,20 @@ export default class UmbTiptapToolbarStyleMenuApi extends UmbTiptapToolbarElemen ul: { type: 'bulletList', command: (chain) => chain.toggleBulletList() }, }; + override isActive(editor?: Editor, item?: MetaTiptapToolbarStyleMenuItem) { + if (!editor || !item?.data) return false; + + const { tag, id, class: className } = item.data; + const ext = tag ? this.#commands[tag] : null; + const attrs = editor?.getAttributes(ext?.type ?? 'paragraph'); + + const tagMatch = !tag ? true : ext ? (ext.isActive?.(editor) ?? editor?.isActive(ext.type) ?? false) : false; + const idMatch = !id ? true : attrs.id === id; + const classMatch = !className ? true : attrs.class?.includes(className) === true; + + return tagMatch && idMatch && classMatch; + } + override execute(editor?: Editor, item?: MetaTiptapToolbarStyleMenuItem) { if (!editor || !item?.data) return; const { tag, id, class: className } = item.data; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/toolbar/tiptap-toolbar-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/toolbar/tiptap-toolbar-menu.element.ts index 7f7726eb1040..4ffa6538d43d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/toolbar/tiptap-toolbar-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/toolbar/tiptap-toolbar-menu.element.ts @@ -99,13 +99,18 @@ export class UmbTiptapToolbarMenuElement extends UmbLitElement { style: item.appearance?.style ?? item.style, separatorAfter: item.separatorAfter, element, + isActive: () => this.api?.isActive(this.editor, item), execute: () => this.api?.execute(this.editor, item), }; } + #isMenuActive(items?: UmbCascadingMenuItem[]): boolean { + return !!items?.some((item) => item.isActive?.() || this.#isMenuActive(item.items)); + } + readonly #onEditorUpdate = () => { if (this.api && this.editor && this.manifest) { - this.isActive = this.api.isActive(this.editor); + this.isActive = this.api.isActive(this.editor) || this.#isMenuActive(this.#menu) || false; } }; @@ -117,8 +122,8 @@ export class UmbTiptapToolbarMenuElement extends UmbLitElement { () => html` ${when( @@ -130,7 +135,11 @@ export class UmbTiptapToolbarMenuElement extends UmbLitElement { `, () => html` - + ${label} diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-toolbar-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-toolbar-menu.element.ts index d57dc1340158..c59e0322659b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-toolbar-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-toolbar-menu.element.ts @@ -32,7 +32,7 @@ export class UmbTiptapTableToolbarMenuElement extends UmbTiptapToolbarMenuElemen `, )} ${this.renderMenu()} - + `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-toolbar-api.ts index 6fd0e5bba418..569b913fe667 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-toolbar-api.ts @@ -29,6 +29,11 @@ export class UmbTiptapToolbarTableExtensionApi extends UmbTiptapToolbarElementAp tableProperties: (editor) => this.#tableProperties(editor), }; + override isActive(editor?: Editor, item?: unknown) { + if (!item) return super.isActive(editor); + return false; + } + async #tableProperties(editor?: Editor) { if (!editor || !editor.isActive('table')) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/font-family.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/font-family.tiptap-toolbar-api.ts index 41f698d3f973..4b4c92e03f81 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/font-family.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/font-family.tiptap-toolbar-api.ts @@ -3,6 +3,11 @@ import type { MetaTiptapToolbarMenuItem } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapToolbarFontFamilyExtensionApi extends UmbTiptapToolbarElementApiBase { + override isActive(editor?: Editor, item?: MetaTiptapToolbarMenuItem) { + const styles = editor?.getAttributes('span')?.style; + return styles?.includes(`font-family: ${item?.data};`) === true; + } + override execute(editor?: Editor, item?: MetaTiptapToolbarMenuItem) { if (!item?.data) return; editor?.chain().focus().toggleSpanStyle(`font-family: ${item.data};`).run(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/font-size.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/font-size.tiptap-toolbar-api.ts index 3b7f4c5333f7..608807715968 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/font-size.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/font-size.tiptap-toolbar-api.ts @@ -3,6 +3,11 @@ import type { MetaTiptapToolbarMenuItem } from '../types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapToolbarFontFamilyExtensionApi extends UmbTiptapToolbarElementApiBase { + override isActive(editor?: Editor, item?: MetaTiptapToolbarMenuItem) { + const styles = editor?.getAttributes('span')?.style; + return styles?.includes(`font-size: ${item?.data};`) === true; + } + override execute(editor?: Editor, item?: MetaTiptapToolbarMenuItem) { if (!item?.data) return; editor?.chain().focus().toggleSpanStyle(`font-size: ${item.data};`).run(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/types.ts index 7b2f40033cb5..68d57116931a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/types.ts @@ -54,7 +54,7 @@ export interface UmbTiptapToolbarElementApi extends UmbApi, UmbTiptapExtensionAr /** * Checks if the toolbar element is active. */ - isActive(editor?: Editor): boolean; + isActive(editor?: Editor, ...args: Array): boolean; /** * Checks if the toolbar element is disabled.