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 feab96f6c5..a06fb569bd 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,155 +1,23 @@ -import type { UmbMediaDetailModel } from '../types.js'; -import { type UmbMediaTreeStore, UMB_MEDIA_TREE_STORE_CONTEXT } from '../tree/media-tree.store.js'; import type { UmbMediaCollectionContext } from './media-collection.context.js'; import { css, customElement, html } from '@umbraco-cms/backoffice/external/lit'; import { UMB_DEFAULT_COLLECTION_CONTEXT, UmbCollectionDefaultElement } from '@umbraco-cms/backoffice/collection'; import './media-collection-toolbar.element.js'; -import type { UUIFileDropzoneEvent } from '@umbraco-cms/backoffice/external/uui'; -import { UMB_MEDIA_ENTITY_TYPE, UmbMediaDetailRepository } from '@umbraco-cms/backoffice/media'; -import { UmbTemporaryFileManager } from '@umbraco-cms/backoffice/temporary-file'; -import { UmbId } from '@umbraco-cms/backoffice/id'; - -import { getMediaTypeByFileMimeType, UmbMediaTypeStructureRepository } from '@umbraco-cms/backoffice/media-type'; @customElement('umb-media-collection') export class UmbMediaCollectionElement extends UmbCollectionDefaultElement { - #fileManager = new UmbTemporaryFileManager(this); - #mediaTypeStructure = new UmbMediaTypeStructureRepository(this); - #mediaDetailRepository = new UmbMediaDetailRepository(this); - #mediaCollection?: UmbMediaCollectionContext; - #mediaTreeStore?: UmbMediaTreeStore; constructor() { super(); this.consumeContext(UMB_DEFAULT_COLLECTION_CONTEXT, (instance) => { this.#mediaCollection = instance as UmbMediaCollectionContext; }); - this.consumeContext(UMB_MEDIA_TREE_STORE_CONTEXT, (instance) => { - this.#mediaTreeStore = instance; - console.log('instance is here', instance); - }); - document.addEventListener('dragenter', this.#handleDragEnter.bind(this)); - document.addEventListener('dragleave', this.#handleDragLeave.bind(this)); - document.addEventListener('drop', this.#handleDrop.bind(this)); - } - - disconnectedCallback(): void { - super.disconnectedCallback(); - document.removeEventListener('dragenter', this.#handleDragEnter.bind(this)); - document.removeEventListener('dragleave', this.#handleDragLeave.bind(this)); - document.removeEventListener('drop', this.#handleDrop.bind(this)); - } - - #handleDragEnter() { - this.toggleAttribute('dragging', true); - } - - #handleDragLeave() { - this.toggleAttribute('dragging', false); - } - - #handleDrop(event: DragEvent) { - event.preventDefault(); - console.log('#handleDrop', event); - this.toggleAttribute('dragging', false); - } - - async #onFileUpload(event: UUIFileDropzoneEvent) { - /** TODO: Move dropzone and logic into its own component, so that we can reuse it in more places... */ - const files: Array = event.detail.files; - if (!files.length) return; - - const { data } = await this.#mediaTypeStructure.requestAllowedChildrenOf(null); - if (!data) return; - - for (const file of files) { - const mediaTypeDetailUnique = UmbId.new(); - - const mediaTypeName = getMediaTypeByFileMimeType(file.type); - const mediaType = data.items.find((type) => type.name === mediaTypeName)!; - - const mediaTempFileUnique = UmbId.new(); - - const uploaded = await this.#fileManager.uploadOne({ file, unique: mediaTempFileUnique }); - if (uploaded.find((item) => item.status === 'error')) return; - - const model: UmbMediaDetailModel = { - unique: mediaTypeDetailUnique, - mediaType: { - unique: mediaType.unique, - collection: null, - }, - entityType: UMB_MEDIA_ENTITY_TYPE, - isTrashed: false, - urls: [], - values: [ - { - alias: 'umbracoFile', - value: { src: mediaTempFileUnique }, - culture: null, - segment: null, - }, - ], - variants: [ - { - culture: null, - segment: null, - name: file.name, - createDate: '', - updateDate: '', - }, - ], - }; - - await this.#mediaDetailRepository.create(model, null); - - this.#mediaCollection?.requestCollection(); - } } protected renderToolbar() { return html` - `; + this.#mediaCollection?.requestCollection()}>`; } - - static styles = [ - css` - :host([dragging]) #dropzone { - opacity: 1; - pointer-events: all; - } - [dropzone] { - opacity: 0; - } - #dropzone { - opacity: 0; - pointer-events: none; - display: block; - position: absolute; - inset: 0px; - z-index: 100; - backdrop-filter: opacity(1); /* Removes the built in blur effect */ - border-radius: var(--uui-border-radius); - overflow: clip; - border: 1px solid var(--uui-color-focus); - } - #dropzone:after { - content: ''; - display: block; - position: absolute; - inset: 0; - border-radius: var(--uui-border-radius); - background-color: var(--uui-color-focus); - opacity: 0.2; - } - `, - ]; } export default UmbMediaCollectionElement; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/dropzone-media/dropzone-media.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/dropzone-media/dropzone-media.element.ts new file mode 100644 index 0000000000..3c252bed80 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/dropzone-media/dropzone-media.element.ts @@ -0,0 +1,172 @@ +import { UmbMediaDetailRepository } from '../../repository/index.js'; +import { UMB_MEDIA_ENTITY_TYPE } from '../../entity.js'; +import type { UmbMediaDetailModel } from '../../types.js'; +import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import type { UUIFileDropzoneEvent } from '@umbraco-cms/backoffice/external/uui'; +import { UmbId } from '@umbraco-cms/backoffice/id'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { + type UmbAllowedMediaTypeModel, + UmbMediaTypeStructureRepository, + getMediaTypeByFileMimeType, +} from '@umbraco-cms/backoffice/media-type'; +import { UmbTemporaryFileManager } from '@umbraco-cms/backoffice/temporary-file'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; + +@customElement('umb-dropzone-media') +export class UmbDropzoneMediaElement extends UmbLitElement { + #fileManager = new UmbTemporaryFileManager(this); + #mediaTypeStructure = new UmbMediaTypeStructureRepository(this); + #allowedMediaTypes: Array = []; + #mediaDetailRepository = new UmbMediaDetailRepository(this); + + @property() + collectionUnique: string | null = null; + + @property() + parentUnique: string | null = null; + + constructor() { + super(); + this.#getAllowedMediaTypes(); + document.addEventListener('dragenter', this.#handleDragEnter.bind(this)); + document.addEventListener('dragleave', this.#handleDragLeave.bind(this)); + document.addEventListener('drop', this.#handleDrop.bind(this)); + } + + disconnectedCallback(): void { + super.disconnectedCallback(); + document.removeEventListener('dragenter', this.#handleDragEnter.bind(this)); + document.removeEventListener('dragleave', this.#handleDragLeave.bind(this)); + document.removeEventListener('drop', this.#handleDrop.bind(this)); + } + + #handleDragEnter() { + this.toggleAttribute('dragging', true); + } + + #handleDragLeave() { + this.toggleAttribute('dragging', false); + } + + #handleDrop(event: DragEvent) { + event.preventDefault(); + this.toggleAttribute('dragging', false); + } + + async #getAllowedMediaTypes() { + const { data } = await this.#mediaTypeStructure.requestAllowedChildrenOf(null); + if (!data) return; + this.#allowedMediaTypes = data.items; + } + + #getMediaTypeFromMime(mimetype: string): UmbAllowedMediaTypeModel { + const mediaTypeName = getMediaTypeByFileMimeType(mimetype); + return this.#allowedMediaTypes.find((type) => type.name === mediaTypeName)!; + } + + async #uploadHandler(file: File) { + const unique = UmbId.new(); + const uploaded = await this.#fileManager.uploadOne({ file, unique }); + if (uploaded[0].status === 'error') { + throw new Error('Error uploading file'); + } + return uploaded[0]; + } + + async #onFileUpload(event: UUIFileDropzoneEvent) { + const files: Array = event.detail.files; + if (!files.length) return; + + for (const file of files) { + const mediaTypeDetailUnique = UmbId.new(); + + const mediaType = this.#getMediaTypeFromMime(file.type); + const uploaded = await this.#uploadHandler(file); + + const model: UmbMediaDetailModel = { + unique: mediaTypeDetailUnique, + mediaType: { + unique: mediaType.unique, + collection: this.collectionUnique ? { unique: this.collectionUnique } : null, + }, + entityType: UMB_MEDIA_ENTITY_TYPE, + isTrashed: false, + urls: [], + values: [ + { + alias: 'umbracoFile', + value: { src: uploaded.unique }, + culture: null, + segment: null, + }, + ], + variants: [ + { + culture: null, + segment: null, + name: file.name, + createDate: '', + updateDate: '', + }, + ], + }; + await this.#mediaDetailRepository.create(model, null); + this.dispatchEvent(new UmbChangeEvent()); + } + } + + render() { + return html``; + } + + static styles = [ + css` + :host([dragging]) #dropzone { + opacity: 1; + pointer-events: all; + } + + [dropzone] { + opacity: 0; + } + + #dropzone { + opacity: 0; + pointer-events: none; + display: flex; + align-items: center; + justify-content: center; + position: absolute; + inset: 0px; + z-index: 100; + backdrop-filter: opacity(1); /* Removes the built in blur effect */ + border-radius: var(--uui-border-radius); + overflow: clip; + border: 1px solid var(--uui-color-focus); + } + #dropzone:after { + content: ''; + display: block; + position: absolute; + inset: 0; + border-radius: var(--uui-border-radius); + background-color: var(--uui-color-focus); + opacity: 0.2; + } + `, + ]; +} + +export default UmbDropzoneMediaElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-dropzone-media': UmbDropzoneMediaElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/dropzone-media/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/dropzone-media/index.ts new file mode 100644 index 0000000000..a6c28ed09c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/dropzone-media/index.ts @@ -0,0 +1 @@ +export * from './dropzone-media.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/index.ts index 3c48dc1e29..d884d15b41 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/index.ts @@ -1,4 +1,5 @@ import './input-media/index.js'; +export * from './dropzone-media/index.js'; export * from './input-media/index.js'; export * from './input-image-cropper/index.js';