Content/Document Picker: make not existing items appear as not found items (#20198)

make not existing items appear as not found items
This commit is contained in:
Niels Lyngsø
2025-09-19 19:45:46 +02:00
committed by GitHub
parent 12ae51d67e
commit 07f0b7c6ae
5 changed files with 61 additions and 22 deletions

View File

@@ -120,6 +120,12 @@ export class UmbEntityItemRefElement extends UmbLitElement {
}
}
@property({ type: Boolean })
error?: boolean;
@property({ type: String, attribute: 'error-message', reflect: false })
errorMessage?: string;
#pathAddendum = new UmbRoutePathAddendumContext(this);
#onSelected(event: UmbSelectedEvent) {
@@ -155,6 +161,7 @@ export class UmbEntityItemRefElement extends UmbLitElement {
this._component?.remove();
const component = extensionControllers[0]?.component || document.createElement('umb-default-item-ref');
// TODO: I would say this code can use feature of the UmbExtensionsElementInitializer, to set properties and get a fallback element. [NL]
// assign the properties to the component
component.item = this.#item;
component.readonly = this.readonly;
@@ -182,7 +189,25 @@ export class UmbEntityItemRefElement extends UmbLitElement {
}
override render() {
return html`${this._component}`;
if (this._component) {
return html`${this._component}`;
}
// Error:
if (this.error) {
return html`<uui-ref-node
style="color: var(--uui-color-danger);"
.name=${this.localize.string(this.errorMessage ?? '#general_notFound')}
.readonly=${this.readonly}
.standalone=${this.standalone}
.selectOnly=${this.selectOnly}
.selected=${this.selected}
.disabled=${this.disabled}>
<uui-icon slot="icon" name="icon-alert" style="color: var(--uui-color-danger);"></uui-icon>
<slot name="actions"></slot>
</uui-ref-node>`;
}
// Loading:
return html`<uui-loader-bar style="margin-top:10px;"></uui-loader-bar>`;
}
override destroy(): void {

View File

@@ -29,6 +29,7 @@ export class UmbPickerInputContext<
public readonly selection;
public readonly selectedItems;
public readonly statuses;
public readonly interactionMemory = new UmbInteractionMemoryManager(this);
/**
@@ -84,6 +85,7 @@ export class UmbPickerInputContext<
this.#itemManager = new UmbRepositoryItemsManager<PickedItemType>(this, repositoryAlias, getUniqueMethod);
this.selection = this.#itemManager.uniques;
this.statuses = this.#itemManager.statuses;
this.selectedItems = this.#itemManager.items;
}
@@ -116,12 +118,12 @@ export class UmbPickerInputContext<
async requestRemoveItem(unique: string) {
const item = this.#itemManager.getItems().find((item) => this.#getUnique(item) === unique);
if (!item) throw new Error('Could not find item with unique: ' + unique);
const name = item?.name ?? '#general_notFound';
await umbConfirmModal(this, {
color: 'danger',
headline: `#actions_remove ${item.name}?`,
content: `#defaultdialogs_confirmremove ${item.name}?`,
headline: `#actions_remove ${name}?`,
content: `#defaultdialogs_confirmremove ${name}?`,
confirmLabel: '#actions_remove',
});

View File

@@ -7,17 +7,10 @@ import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-ap
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UmbEntityUpdatedEvent } from '@umbraco-cms/backoffice/entity-action';
import type { UmbRepositoryItemsStatus } from './types.js';
const ObserveRepositoryAlias = Symbol();
interface UmbRepositoryItemsStatus {
state: {
type: 'success' | 'error' | 'loading';
error?: string;
};
unique: string;
}
export class UmbRepositoryItemsManager<ItemType extends { unique: string }> extends UmbControllerBase {
//
repository?: UmbItemRepository<ItemType>;

View File

@@ -11,6 +11,14 @@ export interface UmbRepositoryResponse<T> extends UmbDataSourceResponse<T> {}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface UmbRepositoryErrorResponse extends UmbDataSourceErrorResponse {}
export interface UmbRepositoryItemsStatus {
state: {
type: 'success' | 'error' | 'loading';
error?: string;
};
unique: string;
}
/**
* Interface for a repository that can return a paged model.
* @template T - The type of items in the paged model.

View File

@@ -11,6 +11,7 @@ import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
import { UMB_DOCUMENT_TYPE_ENTITY_TYPE } from '@umbraco-cms/backoffice/document-type';
import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree';
import type { UmbInteractionMemoryModel } from '@umbraco-cms/backoffice/interaction-memory';
import type { UmbRepositoryItemsStatus } from '@umbraco-cms/backoffice/repository';
@customElement('umb-input-document')
export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefined, typeof UmbLitElement>(
@@ -139,6 +140,9 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
@state()
private _items?: Array<UmbDocumentItemModel>;
@state()
private _statuses?: Array<UmbRepositoryItemsStatus>;
#pickerInputContext = new UmbDocumentPickerInputContext(this);
constructor() {
@@ -168,6 +172,8 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
'_observerItems',
);
this.observe(this.#pickerInputContext.statuses, (statuses) => (this._statuses = statuses), '_observerStatuses');
this.observe(
this.#pickerInputContext.interactionMemory.memories,
(memories) => {
@@ -199,8 +205,8 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
);
}
#onRemove(item: UmbDocumentItemModel) {
this.#pickerInputContext.requestRemoveItem(item.unique);
#onRemove(unique: string) {
this.#pickerInputContext.requestRemoveItem(unique);
}
override render() {
@@ -224,16 +230,20 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
}
#renderItems() {
if (!this._items) return;
if (!this._statuses) return;
return html`
<uui-ref-list>
${repeat(
this._items,
(item) => item.unique,
(item) =>
html`<umb-entity-item-ref
id=${item.unique}
this._statuses,
(status) => status.unique,
(status) => {
const unique = status.unique;
const item = this._items?.find((x) => x.unique === unique);
return html`<umb-entity-item-ref
id=${unique}
.item=${item}
?error=${status.state.type === 'error'}
.errorMessage=${status.state.error}
?readonly=${this.readonly}
?standalone=${this.max === 1}>
${when(
@@ -242,11 +252,12 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
<uui-action-bar slot="actions">
<uui-button
label=${this.localize.term('general_remove')}
@click=${() => this.#onRemove(item)}></uui-button>
@click=${() => this.#onRemove(unique)}></uui-button>
</uui-action-bar>
`,
)}
</umb-entity-item-ref>`,
</umb-entity-item-ref>`;
},
)}
</uui-ref-list>
`;