Merge branch 'main' into bugfix/composition-interfaces

This commit is contained in:
Mads Rasmussen
2024-05-01 11:44:12 +02:00
committed by GitHub
7 changed files with 156 additions and 109 deletions

View File

@@ -31,6 +31,9 @@ export class UmbDefaultCollectionContext<
#manifest?: ManifestCollection;
#repository?: UmbCollectionRepository;
#loading = new UmbObjectState<boolean>(false);
public readonly loading = this.#loading.asObservable();
#items = new UmbArrayState<CollectionItemType>([], (x) => x);
public readonly items = this.#items.asObservable();
@@ -176,6 +179,8 @@ export class UmbDefaultCollectionContext<
if (!this.#repository) throw new Error(`Missing repository for ${this.#manifest}`);
this.#loading.setValue(true);
const filter = this.#filter.getValue();
const { data } = await this.#repository.requestCollection(filter);
@@ -184,6 +189,8 @@ export class UmbDefaultCollectionContext<
this.#totalItems.setValue(data.total);
this.pagination.setTotalItems(data.total);
}
this.#loading.setValue(false);
}
/**

View File

@@ -57,7 +57,7 @@ export class UmbCollectionDefaultElement extends UmbLitElement {
return html`
<umb-body-layout header-transparent>
${this.renderToolbar()}
<umb-router-slot id="router-slot" .routes="${this._routes}"></umb-router-slot>
<umb-router-slot id="router-slot" .routes=${this._routes}></umb-router-slot>
${this.renderPagination()} ${this.renderSelectionActions()}
</umb-body-layout>
`;

View File

@@ -6,7 +6,7 @@ export interface UmbDocumentCollectionFilterModel extends UmbCollectionFilterMod
orderBy?: string;
orderCulture?: string;
orderDirection?: 'asc' | 'desc';
userDefinedProperties: Array<{alias: string, header: string, isSystem: boolean}>;
userDefinedProperties: Array<{ alias: string; header: string; isSystem: boolean }>;
}
export interface UmbDocumentCollectionItemModel {
@@ -23,3 +23,8 @@ export interface UmbDocumentCollectionItemModel {
updater?: string | null;
values: Array<{ alias: string; value: string }>;
}
export interface UmbEditableDocumentCollectionItemModel {
item: UmbDocumentCollectionItemModel;
editPath: string;
}

View File

@@ -1,15 +1,21 @@
import { getPropertyValueByAlias } from '../index.js';
import { UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN } from '../../../paths.js';
import type { UmbCollectionColumnConfiguration } from '../../../../../core/collection/types.js';
import type { UmbDocumentCollectionFilterModel, UmbDocumentCollectionItemModel } from '../../types.js';
import { css, html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit';
import { css, customElement, html, nothing, repeat, state, when } from '@umbraco-cms/backoffice/external/lit';
import { fromCamelCase } from '@umbraco-cms/backoffice/utils';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UMB_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/collection';
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
import type { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection';
import type { UUIInterfaceColor } from '@umbraco-cms/backoffice/external/uui';
@customElement('umb-document-grid-collection-view')
export class UmbDocumentGridCollectionViewElement extends UmbLitElement {
@state()
private _editDocumentPath = '';
@state()
private _items: Array<UmbDocumentCollectionItemModel> = [];
@@ -19,9 +25,6 @@ export class UmbDocumentGridCollectionViewElement extends UmbLitElement {
@state()
private _selection: Array<string | null> = [];
@state()
private _skip: number = 0;
@state()
private _userDefinedProperties?: Array<UmbCollectionColumnConfiguration>;
@@ -34,40 +37,51 @@ export class UmbDocumentGridCollectionViewElement extends UmbLitElement {
this.#collectionContext = collectionContext;
this.#observeCollectionContext();
});
new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
.addAdditionalPath('document')
.onSetup(() => {
return { data: { entityType: 'document', preset: {} } };
})
.onReject(() => {
this.#collectionContext?.requestCollection();
})
.onSubmit(() => {
this.#collectionContext?.requestCollection();
})
.observeRouteBuilder((routeBuilder) => {
this._editDocumentPath = routeBuilder({});
});
}
#observeCollectionContext() {
if (!this.#collectionContext) return;
this.observe(this.#collectionContext.loading, (loading) => (this._loading = loading), '_observeLoading');
this.observe(
this.#collectionContext.userDefinedProperties,
(userDefinedProperties) => {
this._userDefinedProperties = userDefinedProperties;
},
'umbCollectionUserDefinedPropertiesObserver',
'_observeUserDefinedProperties',
);
this.observe(this.#collectionContext.items, (items) => (this._items = items), 'umbCollectionItemsObserver');
this.observe(this.#collectionContext.items, (items) => (this._items = items), '_observeItems');
this.observe(
this.#collectionContext.selection.selection,
(selection) => (this._selection = selection),
'umbCollectionSelectionObserver',
);
this.observe(
this.#collectionContext.pagination.skip,
(skip) => {
this._skip = skip;
},
'umbCollectionSkipObserver',
'_observeSelection',
);
}
// TODO: How should we handle url stuff? [?]
#onOpen(id: string) {
// TODO: this will not be needed when cards works as links with href [?]
history.pushState(null, '', 'section/content/workspace/document/edit/' + id);
#onOpen(event: Event, unique: string) {
event.preventDefault();
event.stopPropagation();
const url = this._editDocumentPath + UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN.generateLocal({ unique });
window.history.pushState(null, '', url);
}
#onSelect(item: UmbDocumentCollectionItemModel) {
@@ -83,33 +97,44 @@ export class UmbDocumentGridCollectionViewElement extends UmbLitElement {
}
render() {
if (this._loading) {
return html`<div class="container"><uui-loader></uui-loader></div>`;
}
if (this._items.length === 0) {
return html`<div class="container"><p>${this.localize.term('content_listViewNoItems')}</p></div>`;
}
return this._items.length === 0 ? this.#renderEmpty() : this.#renderItems();
}
#renderEmpty() {
if (this._items.length > 0) return nothing;
return html`
<div id="document-grid">
${repeat(
this._items,
(item) => item.unique,
(item) => this.#renderCard(item),
<div class="container">
${when(
this._loading,
() => html`<uui-loader></uui-loader>`,
() => html`<p>${this.localize.term('content_listViewNoItems')}</p>`,
)}
</div>
`;
}
#renderCard(item: UmbDocumentCollectionItemModel) {
#renderItems() {
if (this._items.length === 0) return nothing;
return html`
<div id="document-grid">
${repeat(
this._items,
(item) => item.unique,
(item) => this.#renderItem(item),
)}
</div>
${when(this._loading, () => html`<uui-loader-bar></uui-loader-bar>`)}
`;
}
#renderItem(item: UmbDocumentCollectionItemModel) {
return html`
<uui-card-content-node
.name=${item.name ?? 'Unnamed Document'}
selectable
?select-only=${this._selection.length > 0}
?selected=${this.#isSelected(item)}
@open=${() => this.#onOpen(item.unique ?? '')}
@open=${(event: Event) => this.#onOpen(event, item.unique)}
@selected=${() => this.#onSelect(item)}
@deselected=${() => this.#onDeselect(item)}>
<umb-icon slot="icon" name=${item.icon}></umb-icon>
@@ -118,29 +143,26 @@ export class UmbDocumentGridCollectionViewElement extends UmbLitElement {
`;
}
#renderState(item: UmbDocumentCollectionItemModel) {
switch (item.state) {
#getStateTagConfig(state: string): { color: UUIInterfaceColor; label: string } {
switch (state) {
case 'Published':
return html`<uui-tag slot="tag" color="positive" look="secondary"
>${this.localize.term('content_published')}</uui-tag
>`;
return { color: 'positive', label: this.localize.term('content_published') };
case 'PublishedPendingChanges':
return html`<uui-tag slot="tag" color="warning" look="secondary"
>${this.localize.term('content_publishedPendingChanges')}</uui-tag
>`;
return { color: 'warning', label: this.localize.term('content_publishedPendingChanges') };
case 'Draft':
return html`<uui-tag slot="tag" color="default" look="secondary"
>${this.localize.term('content_unpublished')}</uui-tag
>`;
return { color: 'default', label: this.localize.term('content_unpublished') };
case 'NotCreated':
return html`<uui-tag slot="tag" color="danger" look="secondary"
>${this.localize.term('content_notCreated')}</uui-tag
>`;
return { color: 'danger', label: this.localize.term('content_notCreated') };
default:
return html`<uui-tag slot="tag" color="danger" look="secondary">${fromCamelCase(item.state)}</uui-tag>`;
return { color: 'danger', label: fromCamelCase(state) };
}
}
#renderState(item: UmbDocumentCollectionItemModel) {
const tagConfig = this.#getStateTagConfig(item.state);
return html`<uui-tag slot="tag" color=${tagConfig.color} look="secondary">${tagConfig.label}</uui-tag>`;
}
#renderProperties(item: UmbDocumentCollectionItemModel) {
if (!this._userDefinedProperties) return;
return html`

View File

@@ -1,49 +1,30 @@
import type { UmbDocumentCollectionItemModel } from '../../../types.js';
import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
import type { UmbEditableDocumentCollectionItemModel } from '../../../types.js';
import { css, customElement, html, nothing, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
import type { UmbTableColumn, UmbTableColumnLayoutElement, UmbTableItem } from '@umbraco-cms/backoffice/components';
import type { UUIButtonElement } from '@umbraco-cms/backoffice/external/uui';
@customElement('umb-document-table-column-name')
export class UmbDocumentTableColumnNameElement extends UmbLitElement implements UmbTableColumnLayoutElement {
@state()
private _editDocumentPath = '';
@property({ type: Object, attribute: false })
column!: UmbTableColumn;
@property({ type: Object, attribute: false })
item!: UmbTableItem;
@property({ attribute: false })
value!: UmbDocumentCollectionItemModel;
constructor() {
super();
new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
.addAdditionalPath('document')
.onSetup(() => {
return { data: { entityType: 'document', preset: {} } };
})
.observeRouteBuilder((routeBuilder) => {
this._editDocumentPath = routeBuilder({});
});
}
value!: UmbEditableDocumentCollectionItemModel;
#onClick(event: Event & { target: UUIButtonElement }) {
event.preventDefault();
event.stopPropagation();
window.history.pushState({}, '', event.target.href);
window.history.pushState(null, '', event.target.href);
}
render() {
if (!this.value) return nothing;
return html`
<uui-button
compact
href="${this._editDocumentPath}edit/${this.value.unique}"
label=${this.value.name}
href=${this.value.editPath}
label=${this.value.item.name}
@click=${this.#onClick}></uui-button>
`;
}

View File

@@ -1,4 +1,4 @@
import type { UmbDocumentCollectionItemModel } from '../../../types.js';
import type { UmbEditableDocumentCollectionItemModel } from '../../../types.js';
import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
import { fromCamelCase } from '@umbraco-cms/backoffice/utils';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
@@ -6,17 +6,14 @@ import type { UmbTableColumn, UmbTableColumnLayoutElement, UmbTableItem } from '
@customElement('umb-document-table-column-state')
export class UmbDocumentTableColumnStateElement extends UmbLitElement implements UmbTableColumnLayoutElement {
@property({ type: Object, attribute: false })
column!: UmbTableColumn;
@property({ type: Object, attribute: false })
item!: UmbTableItem;
@property({ attribute: false })
value!: UmbDocumentCollectionItemModel;
value!: UmbEditableDocumentCollectionItemModel;
render() {
switch (this.value.state) {
switch (this.value.item.state) {
case 'Published':
return html`<uui-tag color="positive" look="secondary">${this.localize.term('content_published')}</uui-tag>`;
case 'PublishedPendingChanges':
@@ -26,7 +23,7 @@ export class UmbDocumentTableColumnStateElement extends UmbLitElement implements
case 'NotCreated':
return html`<uui-tag color="danger" look="secondary">${this.localize.term('content_notCreated')}</uui-tag>`;
default:
return html`<uui-tag color="danger" look="secondary">${fromCamelCase(this.value.state)}</uui-tag>`;
return html`<uui-tag color="danger" look="secondary">${fromCamelCase(this.value.item.state)}</uui-tag>`;
}
}
}

View File

@@ -1,11 +1,14 @@
import { getPropertyValueByAlias } from '../index.js';
import { UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN } from '../../../paths.js';
import type { UmbCollectionColumnConfiguration } from '../../../../../core/collection/types.js';
import type { UmbDocumentCollectionItemModel } from '../../types.js';
import type { UmbDocumentCollectionContext } from '../../document-collection.context.js';
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
import { css, customElement, html, nothing, state, when } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UMB_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/collection';
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/modal';
import type {
UmbTableColumn,
UmbTableConfig,
@@ -59,29 +62,52 @@ export class UmbDocumentTableCollectionViewElement extends UmbLitElement {
@state()
private _selection: Array<string> = [];
@state()
private _skip: number = 0;
#collectionContext?: UmbDocumentCollectionContext;
#routeBuilder?: UmbModalRouteBuilder;
constructor() {
super();
this.consumeContext(UMB_COLLECTION_CONTEXT, (collectionContext) => {
this.#collectionContext = collectionContext;
this.#observeCollectionContext();
});
this.#registerModalRoute();
}
#registerModalRoute() {
new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
.addAdditionalPath(':entityType')
.onSetup((params) => {
return { data: { entityType: params.entityType, preset: {} } };
})
.onReject(() => {
this.#collectionContext?.requestCollection();
})
.onSubmit(() => {
this.#collectionContext?.requestCollection();
})
.observeRouteBuilder((routeBuilder) => {
this.#routeBuilder = routeBuilder;
// NOTE: Configuring the observations AFTER the route builder is ready,
// otherwise there is a race condition and `#collectionContext.items` tends to win. [LK]
this.#observeCollectionContext();
});
}
#observeCollectionContext() {
if (!this.#collectionContext) return;
this.observe(this.#collectionContext.loading, (loading) => (this._loading = loading), '_observeLoading');
this.observe(
this.#collectionContext.userDefinedProperties,
(userDefinedProperties) => {
this._userDefinedProperties = userDefinedProperties;
this.#createTableHeadings();
},
'umbCollectionUserDefinedPropertiesObserver',
'_observeUserDefinedProperties',
);
this.observe(
@@ -90,7 +116,7 @@ export class UmbDocumentTableCollectionViewElement extends UmbLitElement {
this._items = items;
this.#createTableItems(this._items);
},
'umbCollectionItemsObserver',
'_observeItems',
);
this.observe(
@@ -98,15 +124,7 @@ export class UmbDocumentTableCollectionViewElement extends UmbLitElement {
(selection) => {
this._selection = selection as string[];
},
'umbCollectionSelectionObserver',
);
this.observe(
this.#collectionContext.pagination.skip,
(skip) => {
this._skip = skip;
},
'umbCollectionSkipObserver',
'_observeSelection',
);
}
@@ -131,15 +149,21 @@ export class UmbDocumentTableCollectionViewElement extends UmbLitElement {
const data =
this._tableColumns?.map((column) => {
const editPath = this.#routeBuilder
? this.#routeBuilder({ entityType: item.entityType }) +
UMB_EDIT_DOCUMENT_WORKSPACE_PATH_PATTERN.generateLocal({ unique: item.unique })
: '';
return {
columnAlias: column.alias,
value: column.elementName ? item : getPropertyValueByAlias(item, column.alias),
value: column.elementName ? { item, editPath } : getPropertyValueByAlias(item, column.alias),
};
}) ?? [];
return {
id: item.unique,
icon: item.icon,
entityType: 'document',
data: data,
};
});
@@ -170,23 +194,34 @@ export class UmbDocumentTableCollectionViewElement extends UmbLitElement {
}
render() {
if (this._loading) {
return html`<div class="container"><uui-loader></uui-loader></div>`;
}
return this._tableItems.length === 0 ? this.#renderEmpty() : this.#renderItems();
}
if (this._tableItems.length === 0) {
return html`<div class="container"><p>${this.localize.term('content_listViewNoItems')}</p></div>`;
}
#renderEmpty() {
if (this._tableItems.length > 0) return nothing;
return html`
<div class="container">
${when(
this._loading,
() => html`<uui-loader></uui-loader>`,
() => html`<p>${this.localize.term('content_listViewNoItems')}</p>`,
)}
</div>
`;
}
#renderItems() {
if (this._tableItems.length === 0) return nothing;
return html`
<umb-table
.config=${this._tableConfig}
.columns=${this._tableColumns}
.items=${this._tableItems}
.selection=${this._selection}
@selected="${this.#handleSelect}"
@deselected="${this.#handleDeselect}"
@ordered="${this.#handleOrdering}"></umb-table>
@selected=${this.#handleSelect}
@deselected=${this.#handleDeselect}
@ordered=${this.#handleOrdering}></umb-table>
${when(this._loading, () => html`<uui-loader-bar></uui-loader-bar>`)}
`;
}