diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts index 4d8764fec6..6079200551 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts @@ -10,6 +10,8 @@ import '../inline-list-block/index.js'; import { stringOrStringArrayContains } from '@umbraco-cms/backoffice/utils'; import { UmbBlockListEntryContext } from '../../context/block-list-entry.context.js'; import { UMB_BLOCK_LIST, type UmbBlockListLayoutModel } from '../../types.js'; +import { UmbObserveValidationStateController } from '@umbraco-cms/backoffice/validation'; +import { UmbDataPathBlockElementDataFilter } from '@umbraco-cms/backoffice/block'; /** * @element umb-block-list-entry @@ -33,6 +35,16 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper if (!value) return; this._contentUdi = value; this.#context.setContentUdi(value); + + new UmbObserveValidationStateController( + this, + `$.contentData[${UmbDataPathBlockElementDataFilter({ udi: value })}]`, + (hasMessages) => { + this._contentInvalid = hasMessages; + this._blockViewProps.contentInvalid = hasMessages; + }, + 'observeMessagesForContent', + ); } private _contentUdi?: string | undefined; @@ -61,6 +73,14 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper @state() _inlineEditingMode?: boolean; + // 'content-invalid' attribute is used for styling purpose. + @property({ type: Boolean, attribute: 'content-invalid', reflect: true }) + _contentInvalid?: boolean; + + // 'settings-invalid' attribute is used for styling purpose. + @property({ type: Boolean, attribute: 'settings-invalid', reflect: true }) + _settingsInvalid?: boolean; + @state() _blockViewProps: UmbBlockEditorCustomViewProperties = { contentUdi: undefined!, @@ -141,6 +161,20 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper this.#context.settings, (settings) => { this.#updateBlockViewProps({ settings }); + + this.removeUmbControllerByAlias('observeMessagesForSettings'); + if (settings) { + // Observe settings validation state: + new UmbObserveValidationStateController( + this, + `$.settingsData[${UmbDataPathBlockElementDataFilter(settings)}]`, + (hasMessages) => { + this._settingsInvalid = hasMessages; + this._blockViewProps.settingsInvalid = hasMessages; + }, + 'observeMessagesForSettings', + ); + } }, null, ); @@ -218,16 +252,26 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper > ${this._showContentEdit && this._workspaceEditContentPath - ? html` + ? html` + ${this._contentInvalid + ? html`!` + : ''} ` : ''} ${this._hasSettings && this._workspaceEditSettingsPath - ? html` + ? html` + ${this._settingsInvalid + ? html`!` + : ''} ` : ''} - this.#context.requestDelete()}> + this.#context.requestDelete()}> @@ -248,11 +292,33 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper position: absolute; top: var(--uui-size-2); right: var(--uui-size-2); + opacity: 0; + transition: opacity 120ms; + } + :host(:hover) uui-action-bar, + uui-action-bar:focus, + :host(:focus-within) uui-action-bar { + opacity: 1; } :host([drag-placeholder]) { opacity: 0.2; } + + :host([settings-invalid])::before, + :host([content-invalid])::before { + content: ''; + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; + border: 1px solid var(--uui-color-danger); + border-radius: var(--uui-border-radius); + } + + uui-badge { + z-index: 2; + } `, ]; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index def96de8f3..61a09cea95 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -51,6 +51,7 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement #validationContext = new UmbValidationContext(this).provide(); #contentDataPathTranslator?: UmbBlockElementDataValidationPathTranslator; + #settingsDataPathTranslator?: UmbBlockElementDataValidationPathTranslator; //#catalogueModal: UmbModalRouteRegistrationController; @@ -133,9 +134,7 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement this.observe( context.dataPath, (dataPath) => { - // - // TODO: Make translator for settings. - + // Translate paths for content elements: this.#contentDataPathTranslator?.destroy(); if (dataPath) { // Set the data path for the local validation context: @@ -143,6 +142,15 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement this.#contentDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'contentData'); } + + // Translate paths for settings elements: + this.#settingsDataPathTranslator?.destroy(); + if (dataPath) { + // Set the data path for the local validation context: + this.#validationContext.setDataPath(dataPath); + + this.#settingsDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'settingsData'); + } }, 'observeDataPath', ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts index d9b7a73de0..35ed551b97 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/interfaces/block-editor-custom-view-element.interface.ts @@ -46,6 +46,8 @@ export interface UmbBlockEditorCustomViewProperties< layout?: LayoutType; content?: UmbBlockDataType; settings?: UmbBlockDataType; + contentInvalid?: boolean; + settingsInvalid?: boolean; } export interface UmbBlockEditorCustomViewElement< diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts index cbf4266b63..73d6fb95d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -276,6 +276,7 @@ export class UmbValidationContext extends UmbControllerBase implements UmbValida } override destroy(): void { + this.#providerCtrl = undefined; if (this.#parent) { this.#parent.removeValidator(this); }