Merge branch 'main' into v15/feature/select-all-option-in-publishing-dialogs
This commit is contained in:
@@ -88,6 +88,12 @@ const documentTreeStoreProvider = (story) => html`
|
||||
export const decorators = [documentStoreProvider, documentTreeStoreProvider, dataTypeStoreProvider, storybookProvider];
|
||||
|
||||
export const parameters = {
|
||||
docs: {
|
||||
source: {
|
||||
excludeDecorators: true,
|
||||
format: 'html', // see storybook docs for more info on this format https://storybook.js.org/docs/api/doc-blocks/doc-block-source#format
|
||||
},
|
||||
},
|
||||
options: {
|
||||
storySort: {
|
||||
method: 'alphabetical',
|
||||
|
||||
@@ -37,21 +37,21 @@ export class UmbDefaultCollectionContext<
|
||||
implements UmbCollectionContext, UmbApi
|
||||
{
|
||||
#config?: UmbCollectionConfiguration = { pageSize: 50 };
|
||||
#manifest?: ManifestCollection;
|
||||
#repository?: UmbCollectionRepository;
|
||||
protected _manifest?: ManifestCollection;
|
||||
protected _repository?: UmbCollectionRepository;
|
||||
|
||||
// TODO: replace with a state manager
|
||||
#loading = new UmbObjectState<boolean>(false);
|
||||
public readonly loading = this.#loading.asObservable();
|
||||
protected _loading = new UmbObjectState<boolean>(false);
|
||||
public readonly loading = this._loading.asObservable();
|
||||
|
||||
#items = new UmbArrayState<CollectionItemType>([], (x) => x.unique);
|
||||
public readonly items = this.#items.asObservable();
|
||||
protected _items = new UmbArrayState<CollectionItemType>([], (x) => x.unique);
|
||||
public readonly items = this._items.asObservable();
|
||||
|
||||
#totalItems = new UmbNumberState(0);
|
||||
public readonly totalItems = this.#totalItems.asObservable();
|
||||
protected _totalItems = new UmbNumberState(0);
|
||||
public readonly totalItems = this._totalItems.asObservable();
|
||||
|
||||
#filter = new UmbObjectState<FilterModelType | object>({});
|
||||
public readonly filter = this.#filter.asObservable();
|
||||
protected _filter = new UmbObjectState<FilterModelType | object>({});
|
||||
public readonly filter = this._filter.asObservable();
|
||||
|
||||
#userDefinedProperties = new UmbArrayState<UmbCollectionColumnConfiguration>([], (x) => x.alias);
|
||||
public readonly userDefinedProperties = this.#userDefinedProperties.asObservable();
|
||||
@@ -69,7 +69,7 @@ export class UmbDefaultCollectionContext<
|
||||
#initResolver?: () => void;
|
||||
#initialized = false;
|
||||
|
||||
#init = new Promise<void>((resolve) => {
|
||||
protected _init = new Promise<void>((resolve) => {
|
||||
if (this.#initialized) {
|
||||
resolve();
|
||||
} else {
|
||||
@@ -115,9 +115,9 @@ export class UmbDefaultCollectionContext<
|
||||
});
|
||||
}
|
||||
|
||||
#configured = false;
|
||||
protected _configured = false;
|
||||
|
||||
#configure() {
|
||||
protected _configure() {
|
||||
if (!this.#config) return;
|
||||
|
||||
this.selection.setMultiple(true);
|
||||
@@ -126,9 +126,9 @@ export class UmbDefaultCollectionContext<
|
||||
this.pagination.setPageSize(this.#config.pageSize);
|
||||
}
|
||||
|
||||
const filterValue = this.#filter.getValue() as FilterModelType;
|
||||
const filterValue = this._filter.getValue() as FilterModelType;
|
||||
|
||||
this.#filter.setValue({
|
||||
this._filter.setValue({
|
||||
...this.#defaultFilter,
|
||||
...this.#config,
|
||||
...filterValue,
|
||||
@@ -148,11 +148,11 @@ export class UmbDefaultCollectionContext<
|
||||
|
||||
this.view.setConfig(viewManagerConfig);
|
||||
|
||||
this.#configured = true;
|
||||
this._configured = true;
|
||||
}
|
||||
|
||||
#checkIfInitialized() {
|
||||
if (this.#repository) {
|
||||
if (this._repository) {
|
||||
this.#initialized = true;
|
||||
this.#initResolver?.();
|
||||
}
|
||||
@@ -167,7 +167,7 @@ export class UmbDefaultCollectionContext<
|
||||
repositoryAlias,
|
||||
[this._host],
|
||||
(permitted, ctrl) => {
|
||||
this.#repository = permitted ? ctrl.api : undefined;
|
||||
this._repository = permitted ? ctrl.api : undefined;
|
||||
this.#checkIfInitialized();
|
||||
},
|
||||
);
|
||||
@@ -193,12 +193,12 @@ export class UmbDefaultCollectionContext<
|
||||
}
|
||||
|
||||
public set manifest(manifest: ManifestCollection | undefined) {
|
||||
if (this.#manifest === manifest) return;
|
||||
this.#manifest = manifest;
|
||||
this.#observeRepository(this.#manifest?.meta.repositoryAlias);
|
||||
if (this._manifest === manifest) return;
|
||||
this._manifest = manifest;
|
||||
this.#observeRepository(this._manifest?.meta.repositoryAlias);
|
||||
}
|
||||
public get manifest() {
|
||||
return this.#manifest;
|
||||
return this._manifest;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,24 +207,24 @@ export class UmbDefaultCollectionContext<
|
||||
* @memberof UmbCollectionContext
|
||||
*/
|
||||
public async requestCollection() {
|
||||
await this.#init;
|
||||
await this._init;
|
||||
|
||||
if (!this.#configured) this.#configure();
|
||||
if (!this._configured) this._configure();
|
||||
|
||||
if (!this.#repository) throw new Error(`Missing repository for ${this.#manifest}`);
|
||||
if (!this._repository) throw new Error(`Missing repository for ${this._manifest}`);
|
||||
|
||||
this.#loading.setValue(true);
|
||||
this._loading.setValue(true);
|
||||
|
||||
const filter = this.#filter.getValue();
|
||||
const { data } = await this.#repository.requestCollection(filter);
|
||||
const filter = this._filter.getValue();
|
||||
const { data } = await this._repository.requestCollection(filter);
|
||||
|
||||
if (data) {
|
||||
this.#items.setValue(data.items);
|
||||
this.#totalItems.setValue(data.total);
|
||||
this._items.setValue(data.items);
|
||||
this._totalItems.setValue(data.total);
|
||||
this.pagination.setTotalItems(data.total);
|
||||
}
|
||||
|
||||
this.#loading.setValue(false);
|
||||
this._loading.setValue(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,7 +233,7 @@ export class UmbDefaultCollectionContext<
|
||||
* @memberof UmbCollectionContext
|
||||
*/
|
||||
public setFilter(filter: Partial<FilterModelType>) {
|
||||
this.#filter.setValue({ ...this.#filter.getValue(), ...filter });
|
||||
this._filter.setValue({ ...this._filter.getValue(), ...filter });
|
||||
this.requestCollection();
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ export class UmbDefaultCollectionContext<
|
||||
}
|
||||
|
||||
#onReloadStructureRequest = (event: UmbRequestReloadStructureForEntityEvent) => {
|
||||
const items = this.#items.getValue();
|
||||
const items = this._items.getValue();
|
||||
const hasItem = items.some((item) => item.unique === event.getUnique());
|
||||
if (hasItem) {
|
||||
this.requestCollection();
|
||||
@@ -297,11 +297,11 @@ export class UmbDefaultCollectionContext<
|
||||
* @deprecated Use set the `.manifest` property instead.
|
||||
*/
|
||||
public setManifest(manifest: ManifestCollection | undefined) {
|
||||
if (this.#manifest === manifest) return;
|
||||
this.#manifest = manifest;
|
||||
if (this._manifest === manifest) return;
|
||||
this._manifest = manifest;
|
||||
|
||||
if (!this.#manifest) return;
|
||||
this.#observeRepository(this.#manifest.meta.repositoryAlias);
|
||||
if (!this._manifest) return;
|
||||
this.#observeRepository(this._manifest.meta.repositoryAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -311,6 +311,6 @@ export class UmbDefaultCollectionContext<
|
||||
* @deprecated Use get the `.manifest` property instead.
|
||||
*/
|
||||
public getManifest() {
|
||||
return this.#manifest;
|
||||
return this._manifest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,16 @@ export class UmbTemporaryFileBadgeElement extends UmbLitElement {
|
||||
return this._progress;
|
||||
}
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public complete = false;
|
||||
|
||||
override render() {
|
||||
return html`<uui-badge>
|
||||
<div id="wrapper">
|
||||
<uui-loader-circle progress=${this.progress}></uui-loader-circle>
|
||||
<uui-icon name="icon-arrow-up"></uui-icon>
|
||||
<uui-loader-circle .progress=${this.complete ? 100 : this.progress}></uui-loader-circle>
|
||||
${this.complete
|
||||
? html`<uui-icon name="icon-check"></uui-icon>`
|
||||
: html`<uui-icon name="icon-arrow-up"></uui-icon>`}
|
||||
</div>
|
||||
</uui-badge>`;
|
||||
}
|
||||
@@ -42,8 +47,16 @@ export class UmbTemporaryFileBadgeElement extends UmbLitElement {
|
||||
font-size: var(--uui-size-6);
|
||||
}
|
||||
|
||||
:host([complete]) #wrapper {
|
||||
background-color: var(--uui-color-positive);
|
||||
}
|
||||
:host([complete]) uui-loader-circle {
|
||||
color: var(--uui-color-positive);
|
||||
}
|
||||
|
||||
uui-loader-circle {
|
||||
display: absolute;
|
||||
z-index: 2;
|
||||
inset: 0;
|
||||
color: var(--uui-color-focus);
|
||||
font-size: var(--uui-size-12);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import type { UmbMediaCollectionFilterModel, UmbMediaCollectionItemModel } from './types.js';
|
||||
import { UMB_MEDIA_PLACEHOLDER_ENTITY_TYPE } from '../entity.js';
|
||||
import type { UmbFileDropzoneItemStatus } from '../dropzone/types.js';
|
||||
import { UMB_MEDIA_GRID_COLLECTION_VIEW_ALIAS } from './views/index.js';
|
||||
import type { UmbMediaCollectionFilterModel, UmbMediaCollectionItemModel } from './types.js';
|
||||
import { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
export class UmbMediaCollectionContext extends UmbDefaultCollectionContext<
|
||||
UmbMediaCollectionItemModel,
|
||||
UmbMediaCollectionFilterModel
|
||||
@@ -13,9 +15,77 @@ export class UmbMediaCollectionContext extends UmbDefaultCollectionContext<
|
||||
*/
|
||||
public readonly thumbnailItems = this.items;
|
||||
|
||||
#placeholders = new UmbArrayState<UmbMediaCollectionItemModel>([], (x) => x.unique);
|
||||
public readonly placeholders = this.#placeholders.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_MEDIA_GRID_COLLECTION_VIEW_ALIAS);
|
||||
}
|
||||
|
||||
setPlaceholders(partial: Array<{ unique: string; status: UmbFileDropzoneItemStatus; name?: string }>) {
|
||||
const items = this._items.getValue();
|
||||
|
||||
// We do not want to set a placeholder which unique already exists in the collection.
|
||||
const date = new Date();
|
||||
const placeholders: Array<UmbMediaCollectionItemModel> = partial
|
||||
.filter((placeholder) => !items.find((item) => item.unique === placeholder.unique))
|
||||
.map((placeholder) => ({
|
||||
updateDate: date,
|
||||
createDate: date,
|
||||
entityType: UMB_MEDIA_PLACEHOLDER_ENTITY_TYPE,
|
||||
...placeholder,
|
||||
}))
|
||||
.reverse();
|
||||
this.#placeholders.setValue(placeholders);
|
||||
|
||||
this._items.setValue([...placeholders, ...items]);
|
||||
this._totalItems.setValue(placeholders.length + items.length);
|
||||
this.pagination.setTotalItems(placeholders.length + items.length);
|
||||
}
|
||||
|
||||
updatePlaceholderStatus(unique: string, status?: UmbFileDropzoneItemStatus) {
|
||||
this._items.updateOne(unique, { status });
|
||||
this.#placeholders.updateOne(unique, { status });
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the collection from the repository.
|
||||
* @returns {*}
|
||||
* @memberof UmbCollectionContext
|
||||
*/
|
||||
public override async requestCollection() {
|
||||
await this._init;
|
||||
|
||||
if (!this._configured) this._configure();
|
||||
|
||||
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);
|
||||
|
||||
if (data) {
|
||||
this.#cleanupPlaceholdersFromCollection(data.items);
|
||||
const placeholders = this.#placeholders.getValue();
|
||||
|
||||
this._items.setValue([...placeholders, ...data.items]);
|
||||
this._totalItems.setValue(placeholders.length + data.total);
|
||||
this.pagination.setTotalItems(placeholders.length + data.total);
|
||||
}
|
||||
|
||||
this._loading.setValue(false);
|
||||
}
|
||||
|
||||
#cleanupPlaceholdersFromCollection(collection: Array<UmbMediaCollectionItemModel>) {
|
||||
const placeholderItems = this.#placeholders.getValue();
|
||||
|
||||
const dataSet = new Set(collection.map((item) => item.unique));
|
||||
const completedPlaceholders = placeholderItems.filter((item) => dataSet.has(item.unique));
|
||||
completedPlaceholders.forEach((placeholder) => {
|
||||
this.#placeholders.removeOne(placeholder.unique);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { UmbMediaCollectionContext as api };
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { UMB_MEDIA_ENTITY_TYPE, UMB_MEDIA_ROOT_ENTITY_TYPE } from '../entity.js';
|
||||
import { UMB_MEDIA_WORKSPACE_CONTEXT } from '../workspace/media-workspace.context-token.js';
|
||||
import { UmbFileDropzoneItemStatus, type UmbUploadableItem } from '../dropzone/types.js';
|
||||
import type { UmbDropzoneElement } from '../dropzone/dropzone.element.js';
|
||||
import type { UmbMediaCollectionContext } from './media-collection.context.js';
|
||||
import { UMB_MEDIA_COLLECTION_CONTEXT } from './media-collection.context-token.js';
|
||||
import { customElement, html, state, when } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { customElement, html, query, state, when } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbCollectionDefaultElement } from '@umbraco-cms/backoffice/collection';
|
||||
import './media-collection-toolbar.element.js';
|
||||
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
|
||||
@@ -18,6 +20,9 @@ export class UmbMediaCollectionElement extends UmbCollectionDefaultElement {
|
||||
@state()
|
||||
private _unique: string | null = null;
|
||||
|
||||
@query('#dropzone')
|
||||
private _dropzone!: UmbDropzoneElement;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext(UMB_MEDIA_COLLECTION_CONTEXT, (instance) => {
|
||||
@@ -30,6 +35,32 @@ export class UmbMediaCollectionElement extends UmbCollectionDefaultElement {
|
||||
});
|
||||
}
|
||||
|
||||
#observeProgressItems() {
|
||||
this.observe(
|
||||
this._dropzone.progressItems(),
|
||||
(progressItems) => {
|
||||
progressItems.forEach((item) => {
|
||||
if (item.status === UmbFileDropzoneItemStatus.COMPLETE && !item.folder?.name) {
|
||||
// We do not update folders as it may have children still being uploaded.
|
||||
this.#mediaCollection?.updatePlaceholderStatus(item.unique, UmbFileDropzoneItemStatus.COMPLETE);
|
||||
}
|
||||
});
|
||||
},
|
||||
'_observeProgressItems',
|
||||
);
|
||||
}
|
||||
|
||||
async #setupPlaceholders(event: CustomEvent) {
|
||||
event.preventDefault();
|
||||
const uploadable = event.detail as Array<UmbUploadableItem>;
|
||||
const placeholders = uploadable
|
||||
.filter((p) => p.parentUnique === this._unique)
|
||||
.map((p) => ({ unique: p.unique, status: p.status, name: p.temporaryFile?.file.name ?? p.folder?.name }));
|
||||
|
||||
this.#mediaCollection?.setPlaceholders(placeholders);
|
||||
this.#observeProgressItems();
|
||||
}
|
||||
|
||||
async #onComplete() {
|
||||
this._progress = -1;
|
||||
this.#mediaCollection?.requestCollection();
|
||||
@@ -54,8 +85,10 @@ export class UmbMediaCollectionElement extends UmbCollectionDefaultElement {
|
||||
<umb-media-collection-toolbar slot="header"></umb-media-collection-toolbar>
|
||||
${when(this._progress >= 0, () => html`<uui-loader-bar progress=${this._progress}></uui-loader-bar>`)}
|
||||
<umb-dropzone
|
||||
id="dropzone"
|
||||
multiple
|
||||
.parentUnique=${this._unique}
|
||||
@submitted=${this.#setupPlaceholders}
|
||||
@complete=${this.#onComplete}
|
||||
@progress=${this.#onProgress}></umb-dropzone>
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { UmbMediaCollectionFilterModel, UmbMediaCollectionItemModel } from '../types.js';
|
||||
import { UMB_MEDIA_ENTITY_TYPE } from '../../entity.js';
|
||||
import { DirectionModel, MediaService } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
import type { MediaCollectionResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
@@ -32,7 +33,7 @@ export class UmbMediaCollectionServerDataSource implements UmbCollectionDataSour
|
||||
|
||||
const model: UmbMediaCollectionItemModel = {
|
||||
unique: item.id,
|
||||
entityType: 'media',
|
||||
entityType: UMB_MEDIA_ENTITY_TYPE,
|
||||
contentTypeAlias: item.mediaType.alias,
|
||||
createDate: new Date(variant.createDate),
|
||||
creator: item.creator,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { UmbFileDropzoneItemStatus } from '../dropzone/types.js';
|
||||
import type { UmbCollectionFilterModel } from '@umbraco-cms/backoffice/collection';
|
||||
|
||||
export interface UmbMediaCollectionFilterModel extends UmbCollectionFilterModel {
|
||||
@@ -11,16 +12,17 @@ export interface UmbMediaCollectionFilterModel extends UmbCollectionFilterModel
|
||||
export interface UmbMediaCollectionItemModel {
|
||||
unique: string;
|
||||
entityType: string;
|
||||
contentTypeAlias: string;
|
||||
contentTypeAlias?: string;
|
||||
createDate: Date;
|
||||
creator?: string | null;
|
||||
icon: string;
|
||||
name: string;
|
||||
sortOrder: number;
|
||||
icon?: string;
|
||||
name?: string;
|
||||
sortOrder?: number;
|
||||
updateDate: Date;
|
||||
updater?: string | null;
|
||||
values: Array<{ alias: string; value: string }>;
|
||||
values?: Array<{ alias: string; value: string }>;
|
||||
url?: string;
|
||||
status?: UmbFileDropzoneItemStatus;
|
||||
}
|
||||
|
||||
export interface UmbEditableMediaCollectionItemModel {
|
||||
|
||||
@@ -2,7 +2,9 @@ import { UMB_EDIT_MEDIA_WORKSPACE_PATH_PATTERN } from '../../../paths.js';
|
||||
import type { UmbMediaCollectionItemModel } from '../../types.js';
|
||||
import type { UmbMediaCollectionContext } from '../../media-collection.context.js';
|
||||
import { UMB_MEDIA_COLLECTION_CONTEXT } from '../../media-collection.context-token.js';
|
||||
import { css, customElement, html, repeat, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbFileDropzoneItemStatus } from '../../../dropzone/types.js';
|
||||
import { UMB_MEDIA_PLACEHOLDER_ENTITY_TYPE } from '../../../entity.js';
|
||||
import { css, customElement, html, ifDefined, repeat, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/workspace';
|
||||
@@ -87,7 +89,7 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement {
|
||||
<div id="media-grid">
|
||||
${repeat(
|
||||
this._items,
|
||||
(item) => item.unique,
|
||||
(item) => item.unique + item.status,
|
||||
(item) => this.#renderItem(item),
|
||||
)}
|
||||
</div>
|
||||
@@ -95,9 +97,12 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#renderItem(item: UmbMediaCollectionItemModel) {
|
||||
if (item.entityType === UMB_MEDIA_PLACEHOLDER_ENTITY_TYPE) {
|
||||
return this.#renderPlaceholder(item);
|
||||
}
|
||||
return html`
|
||||
<uui-card-media
|
||||
.name=${item.name}
|
||||
name=${ifDefined(item.name)}
|
||||
selectable
|
||||
?select-only=${this._selection && this._selection.length > 0}
|
||||
?selected=${this.#isSelected(item)}
|
||||
@@ -105,11 +110,21 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement {
|
||||
@selected=${() => this.#onSelect(item)}
|
||||
@deselected=${() => this.#onDeselect(item)}
|
||||
class="media-item">
|
||||
<umb-imaging-thumbnail unique=${item.unique} alt=${item.name} icon=${item.icon}></umb-imaging-thumbnail>
|
||||
<umb-imaging-thumbnail
|
||||
unique=${item.unique}
|
||||
alt=${ifDefined(item.name)}
|
||||
icon=${ifDefined(item.icon)}></umb-imaging-thumbnail>
|
||||
</uui-card-media>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderPlaceholder(item: UmbMediaCollectionItemModel) {
|
||||
const complete = item.status === UmbFileDropzoneItemStatus.COMPLETE;
|
||||
return html`<uui-card-media disabled class="media-placeholder-item" name=${ifDefined(item.name)}>
|
||||
<umb-temporary-file-badge ?complete=${complete}></umb-temporary-file-badge>
|
||||
</uui-card-media>`;
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
@@ -124,6 +139,10 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.media-placeholder-item {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#media-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbEditableMediaCollectionItemModel } from '../../../types.js';
|
||||
import { css, customElement, html, nothing, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, customElement, html, ifDefined, nothing, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbTableColumn, UmbTableColumnLayoutElement, UmbTableItem } from '@umbraco-cms/backoffice/components';
|
||||
import type { UUIButtonElement } from '@umbraco-cms/backoffice/external/uui';
|
||||
@@ -24,7 +24,7 @@ export class UmbMediaTableColumnNameElement extends UmbLitElement implements Umb
|
||||
<uui-button
|
||||
compact
|
||||
href=${this.value.editPath}
|
||||
label=${this.value.item.name}
|
||||
label=${ifDefined(this.value.item.name)}
|
||||
@click=${this.#onClick}></uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ export class UmbMediaTableCollectionViewElement extends UmbLitElement {
|
||||
case 'updater':
|
||||
return item.updater;
|
||||
default:
|
||||
return item.values.find((value) => value.alias === alias)?.value ?? '';
|
||||
return item.values?.find((value) => value.alias === alias)?.value ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,18 +68,20 @@ export class UmbDropzoneManager extends UmbControllerBase {
|
||||
/**
|
||||
* Uploads files and folders to the server and creates the media items with corresponding media type.\
|
||||
* Allows the user to pick a media type option if multiple types are allowed.
|
||||
* @param {UmbFileDropzoneDroppedItems} items - The files and folders to upload
|
||||
* @param {string | null} parentUnique - Where the items should be uploaded
|
||||
* @param {UmbFileDropzoneDroppedItems} items - The files and folders to upload.
|
||||
* @param {string | null} parentUnique - Where the items should be uploaded.
|
||||
* @returns {Promise<Array<UmbUploadableItem>>} - The items about to be uploaded.
|
||||
*/
|
||||
public async createMediaItems(items: UmbFileDropzoneDroppedItems, parentUnique: string | null = null) {
|
||||
const uploadableItems = await this.#setupProgress(items, parentUnique);
|
||||
if (uploadableItems.length === 1) {
|
||||
// When there is only one item being uploaded, allow the user to pick the media type, if more than one is allowed.
|
||||
await this.#createOneMediaItem(uploadableItems[0]);
|
||||
this.#createOneMediaItem(uploadableItems[0]);
|
||||
} else {
|
||||
// When there are multiple items being uploaded, automatically pick the media types for each item. We probably want to allow the user to pick the media type in the future.
|
||||
await this.#createMediaItems(uploadableItems);
|
||||
this.#createMediaItems(uploadableItems);
|
||||
}
|
||||
return uploadableItems;
|
||||
}
|
||||
|
||||
/** @deprecated Please use `createTemporaryFiles()` instead; this method will be removed in Umbraco 17. */
|
||||
|
||||
@@ -26,14 +26,14 @@ export class UmbDropzoneElement extends UmbLitElement {
|
||||
return this._disableFolderUpload;
|
||||
}
|
||||
public set disableFolderUpload(isAllowed: boolean) {
|
||||
this.dropzoneManager.setIsFoldersAllowed(!isAllowed);
|
||||
this.#dropzoneManager.setIsFoldersAllowed(!isAllowed);
|
||||
}
|
||||
private readonly _disableFolderUpload = false;
|
||||
|
||||
@state()
|
||||
private _progressItems: Array<UmbUploadableItem> = [];
|
||||
|
||||
public dropzoneManager: UmbDropzoneManager;
|
||||
#dropzoneManager: UmbDropzoneManager;
|
||||
|
||||
/**
|
||||
* @deprecated Please use `getItems()` instead; this method will be removed in Umbraco 17.
|
||||
@@ -47,6 +47,9 @@ export class UmbDropzoneElement extends UmbLitElement {
|
||||
return this._progressItems;
|
||||
}
|
||||
|
||||
public progressItems = () => this.#dropzoneManager.progressItems;
|
||||
public progress = () => this.#dropzoneManager.progress;
|
||||
|
||||
public browse() {
|
||||
if (this.disabled) return;
|
||||
const element = this.shadowRoot?.querySelector('#dropzone') as UUIFileDropzoneElement;
|
||||
@@ -55,15 +58,34 @@ export class UmbDropzoneElement extends UmbLitElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.dropzoneManager = new UmbDropzoneManager(this);
|
||||
this.#dropzoneManager = new UmbDropzoneManager(this);
|
||||
document.addEventListener('dragenter', this.#handleDragEnter.bind(this));
|
||||
document.addEventListener('dragleave', this.#handleDragLeave.bind(this));
|
||||
document.addEventListener('drop', this.#handleDrop.bind(this));
|
||||
|
||||
this.observe(
|
||||
this.#dropzoneManager.progress,
|
||||
(progress) =>
|
||||
this.dispatchEvent(new ProgressEvent('progress', { loaded: progress.completed, total: progress.total })),
|
||||
'_observeProgress',
|
||||
);
|
||||
|
||||
this.observe(
|
||||
this.#dropzoneManager.progressItems,
|
||||
(progressItems: Array<UmbUploadableItem>) => {
|
||||
this._progressItems = progressItems;
|
||||
const waiting = progressItems.find((item) => item.status === UmbFileDropzoneItemStatus.WAITING);
|
||||
if (progressItems.length && !waiting) {
|
||||
this.dispatchEvent(new CustomEvent('complete', { detail: progressItems }));
|
||||
}
|
||||
},
|
||||
'_observeProgressItems',
|
||||
);
|
||||
}
|
||||
|
||||
override disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this.dropzoneManager.destroy();
|
||||
this.#dropzoneManager.destroy();
|
||||
document.removeEventListener('dragenter', this.#handleDragEnter.bind(this));
|
||||
document.removeEventListener('dragleave', this.#handleDragLeave.bind(this));
|
||||
document.removeEventListener('drop', this.#handleDrop.bind(this));
|
||||
@@ -93,28 +115,12 @@ export class UmbDropzoneElement extends UmbLitElement {
|
||||
if (this.disabled) return;
|
||||
if (!event.detail.files.length && !event.detail.folders.length) return;
|
||||
|
||||
// TODO Create some placeholder items while files are being uploaded? Could update them as they get completed.
|
||||
// We can observe progressItems and check for any files that did not succeed, then show some kind of dialog to the user with the information.
|
||||
|
||||
this.observe(
|
||||
this.dropzoneManager.progress,
|
||||
(progress) =>
|
||||
this.dispatchEvent(new ProgressEvent('progress', { loaded: progress.completed, total: progress.total })),
|
||||
'_observeProgress',
|
||||
);
|
||||
|
||||
this.observe(this.dropzoneManager.progressItems, (progressItems: Array<UmbUploadableItem>) => {
|
||||
this._progressItems = progressItems;
|
||||
const waiting = progressItems.find((item) => item.status === UmbFileDropzoneItemStatus.WAITING);
|
||||
if (progressItems.length && !waiting) {
|
||||
this.dispatchEvent(new CustomEvent('complete', { detail: progressItems }));
|
||||
}
|
||||
});
|
||||
|
||||
if (this.createAsTemporary) {
|
||||
this.dropzoneManager.createTemporaryFiles(event.detail.files);
|
||||
const uploadable = this.#dropzoneManager.createTemporaryFiles(event.detail.files);
|
||||
this.dispatchEvent(new CustomEvent('submitted', { detail: await uploadable }));
|
||||
} else {
|
||||
this.dropzoneManager.createMediaItems(event.detail, this.parentUnique);
|
||||
const uploadable = this.#dropzoneManager.createMediaItems(event.detail, this.parentUnique);
|
||||
this.dispatchEvent(new CustomEvent('submitted', { detail: await uploadable }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
export const UMB_MEDIA_ENTITY_TYPE = 'media';
|
||||
export const UMB_MEDIA_ROOT_ENTITY_TYPE = 'media-root';
|
||||
|
||||
export const UMB_MEDIA_PLACEHOLDER_ENTITY_TYPE = 'umb-media-placeholder';
|
||||
|
||||
export type UmbMediaEntityType = typeof UMB_MEDIA_ENTITY_TYPE;
|
||||
export type UmbMediaRootEntityType = typeof UMB_MEDIA_ROOT_ENTITY_TYPE;
|
||||
|
||||
export type UmbMediaPlaceholderEntityType = typeof UMB_MEDIA_PLACEHOLDER_ENTITY_TYPE;
|
||||
|
||||
export type UmbMediaEntityTypeUnion = UmbMediaEntityType | UmbMediaRootEntityType;
|
||||
|
||||
Reference in New Issue
Block a user