From 9ddd64c4291b317df12a9eb005d36384364c701b Mon Sep 17 00:00:00 2001 From: Lone Iversen <108085781+loivsen@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:09:42 +0200 Subject: [PATCH 1/2] Feature: Media Collection Placeholders (#2439) * mediacollection placeholder logic * media collection placeholder showing * make observables available * complete option for temp badge * placeholder entity * placeholder logic * remove consolelog * update todo and jdocs --- .../default/collection-default.context.ts | 76 +++++++++---------- .../temporary-file-badge.element.ts | 17 ++++- .../collection/media-collection.context.ts | 74 +++++++++++++++++- .../collection/media-collection.element.ts | 35 ++++++++- .../media-collection.server.data-source.ts | 3 +- .../packages/media/media/collection/types.ts | 12 +-- .../media-grid-collection-view.element.ts | 27 ++++++- .../media-table-column-name.element.ts | 4 +- .../media-table-collection-view.element.ts | 2 +- .../media/dropzone/dropzone-manager.class.ts | 10 ++- .../media/media/dropzone/dropzone.element.ts | 54 +++++++------ .../src/packages/media/media/entity.ts | 4 + 12 files changed, 234 insertions(+), 84 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts index 86b68f4411..be002996d7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts @@ -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(false); - public readonly loading = this.#loading.asObservable(); + protected _loading = new UmbObjectState(false); + public readonly loading = this._loading.asObservable(); - #items = new UmbArrayState([], (x) => x.unique); - public readonly items = this.#items.asObservable(); + protected _items = new UmbArrayState([], (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({}); - public readonly filter = this.#filter.asObservable(); + protected _filter = new UmbObjectState({}); + public readonly filter = this._filter.asObservable(); #userDefinedProperties = new UmbArrayState([], (x) => x.alias); public readonly userDefinedProperties = this.#userDefinedProperties.asObservable(); @@ -69,7 +69,7 @@ export class UmbDefaultCollectionContext< #initResolver?: () => void; #initialized = false; - #init = new Promise((resolve) => { + protected _init = new Promise((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) { - 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; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/components/temporary-file-badge.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/components/temporary-file-badge.element.ts index f42910cb0f..9577279569 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/components/temporary-file-badge.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/temporary-file/components/temporary-file-badge.element.ts @@ -19,11 +19,16 @@ export class UmbTemporaryFileBadgeElement extends UmbLitElement { return this._progress; } + @property({ type: Boolean, reflect: true }) + public complete = false; + override render() { return html`
- - + + ${this.complete + ? html`` + : html``}
`; } @@ -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); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts index 4785fc9b8c..da9cf9d437 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts @@ -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([], (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 = 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) { + 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 }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.element.ts index b6a7c9d677..38f89d775d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.element.ts @@ -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; + 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 { ${when(this._progress >= 0, () => html``)} `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/repository/media-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/repository/media-collection.server.data-source.ts index 19c75cb63a..7d127c6c39 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/repository/media-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/repository/media-collection.server.data-source.ts @@ -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, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/types.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/types.ts index d12a162972..1a18e4ba71 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/types.ts @@ -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 { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts index 3989e0c3f2..f6fff936ba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts @@ -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 {
${repeat( this._items, - (item) => item.unique, + (item) => item.unique + item.status, (item) => this.#renderItem(item), )}
@@ -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` 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"> - + `; } + #renderPlaceholder(item: UmbMediaCollectionItemModel) { + const complete = item.status === UmbFileDropzoneItemStatus.COMPLETE; + return html` + + `; + } + 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)); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/table/column-layouts/media-table-column-name.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/table/column-layouts/media-table-column-name.element.ts index 206ff2df61..cc60175031 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/table/column-layouts/media-table-column-name.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/table/column-layouts/media-table-column-name.element.ts @@ -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 `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/table/media-table-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/table/media-table-collection-view.element.ts index 0b187d3f7f..0d28d08609 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/table/media-table-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/table/media-table-collection-view.element.ts @@ -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 ?? ''; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone-manager.class.ts index 19a7896c1c..cce306a0bf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone-manager.class.ts @@ -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>} - 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. */ diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone.element.ts index 247528d08a..2805701413 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone.element.ts @@ -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 = []; - 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) => { + 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) => { - 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 })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity.ts index c7a5df2f81..9a83de535c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity.ts @@ -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; From a689b1c961b28954e897e53883a17187c60b46ac Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 11 Oct 2024 12:55:53 +0200 Subject: [PATCH 2/2] adds formatting to source blocks and excldues decorators --- src/Umbraco.Web.UI.Client/.storybook/preview.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/.storybook/preview.js b/src/Umbraco.Web.UI.Client/.storybook/preview.js index b601e2b586..262cfe4e44 100644 --- a/src/Umbraco.Web.UI.Client/.storybook/preview.js +++ b/src/Umbraco.Web.UI.Client/.storybook/preview.js @@ -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',