diff --git a/src/Umbraco.Web.UI.Client/index.html b/src/Umbraco.Web.UI.Client/index.html index 94d127f213..94d6a2ed4d 100644 --- a/src/Umbraco.Web.UI.Client/index.html +++ b/src/Umbraco.Web.UI.Client/index.html @@ -1,4 +1,4 @@ - + @@ -7,6 +7,7 @@ Umbraco + diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts index 6df071c5e2..1a2c453d63 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts @@ -44,6 +44,7 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement { UmbTextStyles, css` #logo { + display: var(--umb-header-logo-display, inline); --uui-button-padding-top-factor: 1; --uui-button-padding-bottom-factor: 0.5; margin-right: var(--uui-size-space-2); diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header.element.ts index feb5ef7681..3270a1bb74 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header.element.ts @@ -20,7 +20,7 @@ export class UmbBackofficeHeaderElement extends UmbLitElement { } #appHeader { - background-color: var(--uui-color-header-surface); + background-color: var(--umb-header-background-color, var(--uui-color-header-surface)); display: flex; align-items: center; justify-content: space-between; diff --git a/src/Umbraco.Web.UI.Client/src/css/user-defined.css b/src/Umbraco.Web.UI.Client/src/css/user-defined.css new file mode 100644 index 0000000000..35c48a145b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/css/user-defined.css @@ -0,0 +1 @@ +/* This file can be overridden by placing a file with the same name in the /wwwroot/umbraco/backoffice/css folder of the website */ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/header-app/header-app-button.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/header-app/header-app-button.element.ts index c3b915fcc2..1ec7d1de7c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/header-app/header-app-button.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/header-app/header-app-button.element.ts @@ -1,11 +1,12 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; -import { css, html, LitElement, customElement, ifDefined } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import type { ManifestHeaderAppButtonKind, UmbBackofficeManifestKind, } from '@umbraco-cms/backoffice/extension-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; const manifest: UmbBackofficeManifestKind = { type: 'kind', @@ -21,7 +22,7 @@ const manifest: UmbBackofficeManifestKind = { umbExtensionsRegistry.register(manifest); @customElement('umb-header-app-button') -export class UmbHeaderAppButtonElement extends LitElement { +export class UmbHeaderAppButtonElement extends UmbLitElement { public manifest?: ManifestHeaderAppButtonKind; render() { @@ -41,7 +42,11 @@ export class UmbHeaderAppButtonElement extends LitElement { css` uui-button { font-size: 18px; - --uui-button-background-color: transparent; + --uui-button-background-color: var(--umb-header-app-button-background-color, transparent); + --uui-button-background-color-hover: var( + --umb-header-app-button-background-color-hover, + var(--uui-color-emphasis) + ); } `, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts index b1dbeea472..58e708c156 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts @@ -3,10 +3,11 @@ import { UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '.. import type { UmbDocumentVariantOptionModel } from '../types.js'; import { UMB_APP_LANGUAGE_CONTEXT, UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; -import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; +import { UmbEntityActionBase, UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -44,11 +45,18 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { }), ); + const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + const event = new UmbRequestReloadStructureForEntityEvent({ + unique: this.args.unique, + entityType: this.args.entityType, + }); + // If the document has only one variant, we can skip the modal and publish directly: if (options.length === 1) { const variantId = UmbVariantId.Create(documentData.variants[0]); const publishingRepository = new UmbDocumentPublishingRepository(this._host); await publishingRepository.publish(this.args.unique, [{ variantId }]); + actionEventContext.dispatchEvent(event); return; } @@ -84,6 +92,7 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { this.args.unique, variantIds.map((variantId) => ({ variantId })), ); + actionEventContext.dispatchEvent(event); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/unpublish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/unpublish.action.ts index e20d8c50fc..3de8ac6e96 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/unpublish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/unpublish.action.ts @@ -2,10 +2,15 @@ import { UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '.. import type { UmbDocumentVariantOptionModel } from '../types.js'; import { UMB_DOCUMENT_UNPUBLISH_MODAL } from '../modals/index.js'; import { UMB_APP_LANGUAGE_CONTEXT, UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; -import { type UmbEntityActionArgs, UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; +import { + type UmbEntityActionArgs, + UmbEntityActionBase, + UmbRequestReloadStructureForEntityEvent, +} from '@umbraco-cms/backoffice/entity-action'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -73,6 +78,14 @@ export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase if (variantIds.length) { const publishingRepository = new UmbDocumentPublishingRepository(this._host); await publishingRepository.unpublish(this.args.unique, variantIds); + + const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + const event = new UmbRequestReloadStructureForEntityEvent({ + unique: this.args.unique, + entityType: this.args.entityType, + }); + + actionEventContext.dispatchEvent(event); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/publish/publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/publish/publish.action.ts index b5fd5d1230..5a3ee3e431 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/publish/publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/publish/publish.action.ts @@ -8,9 +8,19 @@ import { UMB_APP_LANGUAGE_CONTEXT, UmbLanguageCollectionRepository } from '@umbr import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; +import { UMB_ENTITY_CONTEXT } from '@umbraco-cms/backoffice/entity'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { UmbRequestReloadChildrenOfEntityEvent } from '@umbraco-cms/backoffice/entity-action'; export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase { async execute() { + const entityContext = await this.getContext(UMB_ENTITY_CONTEXT); + const entityType = entityContext.getEntityType(); + const unique = entityContext.getUnique(); + + if (!entityType) throw new Error('Entity type not found'); + if (unique === undefined) throw new Error('Entity unique not found'); + // If there is only one selection, we can refer to the regular publish entity action: if (this.selection.length === 1) { const action = new UmbPublishDocumentEntityAction(this._host, { @@ -43,6 +53,12 @@ export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase< const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + const event = new UmbRequestReloadChildrenOfEntityEvent({ + entityType, + unique, + }); + // If there is only one language available, we can skip the modal and publish directly: if (options.length === 1) { const localizationController = new UmbLocalizationController(this._host); @@ -62,6 +78,7 @@ export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase< const variantId = new UmbVariantId(options[0].language.unique, null); const publishingRepository = new UmbDocumentPublishingRepository(this._host); await publishingRepository.unpublish(this.selection[0], [variantId]); + eventContext.dispatchEvent(event); } return; } @@ -98,6 +115,7 @@ export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase< unique, variantIds.map((variantId) => ({ variantId })), ); + eventContext.dispatchEvent(event); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/unpublish/unpublish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/unpublish/unpublish.action.ts index e85694ebed..5c62ca3d49 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/unpublish/unpublish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-bulk-actions/unpublish/unpublish.action.ts @@ -8,9 +8,19 @@ import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-act import { UMB_APP_LANGUAGE_CONTEXT, UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; +import { UMB_ENTITY_CONTEXT } from '@umbraco-cms/backoffice/entity'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { UmbRequestReloadChildrenOfEntityEvent } from '@umbraco-cms/backoffice/entity-action'; export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBase { async execute() { + const entityContext = await this.getContext(UMB_ENTITY_CONTEXT); + const entityType = entityContext.getEntityType(); + const unique = entityContext.getUnique(); + + if (!entityType) throw new Error('Entity type not found'); + if (unique === undefined) throw new Error('Entity unique not found'); + // If there is only one selection, we can refer to the regular unpublish entity action: if (this.selection.length === 1) { const action = new UmbUnpublishDocumentEntityAction(this._host, { @@ -43,6 +53,12 @@ export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBas const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + const event = new UmbRequestReloadChildrenOfEntityEvent({ + entityType, + unique, + }); + // If there is only one language available, we can skip the modal and unpublish directly: if (options.length === 1) { const localizationController = new UmbLocalizationController(this._host); @@ -62,6 +78,7 @@ export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBas const variantId = new UmbVariantId(options[0].language.unique, null); const publishingRepository = new UmbDocumentPublishingRepository(this._host); await publishingRepository.unpublish(this.selection[0], [variantId]); + eventContext.dispatchEvent(event); } return; } @@ -95,6 +112,7 @@ export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBas if (variantIds.length) { for (const unique of this.selection) { await repository.unpublish(unique, variantIds); + eventContext.dispatchEvent(event); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/umb-search-header-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/search/umb-search-header-app.element.ts index f1d7b604ad..4e17d9896a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/umb-search-header-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/umb-search-header-app.element.ts @@ -1,25 +1,13 @@ import { UMB_SEARCH_MODAL } from './search-modal/search-modal.token.js'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; -import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbModalManagerContext } from '@umbraco-cms/backoffice/modal'; +import { html, customElement } from '@umbraco-cms/backoffice/external/lit'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbHeaderAppButtonElement } from '@umbraco-cms/backoffice/components'; @customElement('umb-search-header-app') -export class UmbSearchHeaderAppElement extends UmbLitElement { - private _modalContext?: UmbModalManagerContext; - - constructor() { - super(); - - this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (_instance) => { - this._modalContext = _instance; - }); - } - - #onSearchClick() { - this._modalContext?.open(this, UMB_SEARCH_MODAL); +export class UmbSearchHeaderAppElement extends UmbHeaderAppButtonElement { + async #onSearchClick() { + const context = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + context.open(this, UMB_SEARCH_MODAL); } render() { @@ -30,15 +18,7 @@ export class UmbSearchHeaderAppElement extends UmbLitElement { `; } - static styles: CSSResultGroup = [ - UmbTextStyles, - css` - uui-button { - font-size: 18px; - --uui-button-background-color: transparent; - } - `, - ]; + static styles = UmbHeaderAppButtonElement.styles; } export default UmbSearchHeaderAppElement; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts index d0e5aaa917..5e8f763901 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts @@ -1,13 +1,12 @@ import { UMB_CURRENT_USER_MODAL } from './modals/current-user/current-user-modal.token.js'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; import { css, html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_CURRENT_USER_CONTEXT, type UmbCurrentUserModel } from '@umbraco-cms/backoffice/current-user'; +import { UmbHeaderAppButtonElement } from '@umbraco-cms/backoffice/components'; @customElement('umb-current-user-header-app') -export class UmbCurrentUserHeaderAppElement extends UmbLitElement { +export class UmbCurrentUserHeaderAppElement extends UmbHeaderAppButtonElement { @state() private _currentUser?: UmbCurrentUserModel; @@ -96,11 +95,10 @@ export class UmbCurrentUserHeaderAppElement extends UmbLitElement { } static styles: CSSResultGroup = [ - UmbTextStyles, + UmbHeaderAppButtonElement.styles, css` uui-button { font-size: 14px; - --uui-button-background-color: transparent; } `, ]; diff --git a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs index 23006d64e2..09b67e621e 100644 --- a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs +++ b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs @@ -50,6 +50,7 @@ export default { window.__UMBRACO_TEST_RUN_A11Y_TEST = ${(!devMode).toString()}; +