Merge pull request #1249 from umbraco/feature/media-collection-repository

Feature: Media Collection Repository
This commit is contained in:
Lee Kelleher
2024-02-21 08:10:26 +00:00
committed by GitHub
10 changed files with 236 additions and 9 deletions

View File

@@ -1 +1 @@
export { UMB_MEDIA_COLLECTION_ALIAS } from './manifests.js';
export const UMB_MEDIA_COLLECTION_ALIAS = 'Umb.Collection.Media';

View File

@@ -1,11 +1,21 @@
export const UMB_MEDIA_COLLECTION_ALIAS = 'Umb.Collection.Media';
import { UMB_MEDIA_COLLECTION_REPOSITORY_ALIAS } from './repository/index.js';
import { manifests as collectionRepositoryManifests } from './repository/manifests.js';
import { UmbMediaCollectionContext } from './media-collection.context.js';
import { UMB_MEDIA_COLLECTION_ALIAS } from './index.js';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
const collectionManifest: ManifestTypes = {
type: 'collection',
alias: UMB_MEDIA_COLLECTION_ALIAS,
name: 'Media Collection',
api: UmbMediaCollectionContext,
element: () => import('./media-collection.element.js'),
meta: {
repositoryAlias: UMB_MEDIA_COLLECTION_REPOSITORY_ALIAS,
},
};
export const manifests = [
// TODO: temp registration, missing collection repository
{
type: 'collection',
kind: 'default',
alias: UMB_MEDIA_COLLECTION_ALIAS,
name: 'Media Collection',
},
collectionManifest,
...collectionRepositoryManifests,
];

View File

@@ -0,0 +1,14 @@
import type { UmbMediaCollectionFilterModel, UmbMediaCollectionItemModel } from './types.js';
import { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
export class UmbMediaCollectionContext extends UmbDefaultCollectionContext<
UmbMediaCollectionItemModel,
UmbMediaCollectionFilterModel
> {
constructor(host: UmbControllerHost) {
super(host, 'Umb.CollectionView.MediaGrid');
this.selection.setSelectable(true);
}
}

View File

@@ -0,0 +1,91 @@
import { css, customElement, html } from '@umbraco-cms/backoffice/external/lit';
import { UmbCollectionDefaultElement } from '@umbraco-cms/backoffice/collection';
@customElement('umb-media-collection')
export class UmbMediaCollectionElement extends UmbCollectionDefaultElement {
constructor() {
super();
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);
}
#onFileChange(event: Event) {
console.log('#onFileChange', event);
}
protected renderToolbar() {
return html`
<umb-collection-toolbar slot="header"></umb-collection-toolbar>
<!-- TODO: Add the Media Upload dropzone component in here. [LK] -->
<uui-file-dropzone
id="dropzone"
multiple
@file-change=${this.#onFileChange}
label="${this.localize.term('media_dragAndDropYourFilesIntoTheArea')}"
accept=""></uui-file-dropzone>
`;
}
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;
declare global {
interface HTMLElementTagNameMap {
'umb-media-collection': UmbMediaCollectionElement;
}
}

View File

@@ -0,0 +1,3 @@
export { UmbMediaCollectionRepository } from './media-collection.repository.js';
export const UMB_MEDIA_COLLECTION_REPOSITORY_ALIAS = 'Umb.Repository.MediaCollection';

View File

@@ -0,0 +1,12 @@
import { UmbMediaCollectionRepository } from './media-collection.repository.js';
import { UMB_MEDIA_COLLECTION_REPOSITORY_ALIAS } from './index.js';
import type { ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
const collectionRepositoryManifest: ManifestRepository = {
type: 'repository',
alias: UMB_MEDIA_COLLECTION_REPOSITORY_ALIAS,
name: 'Media Collection Repository',
api: UmbMediaCollectionRepository,
};
export const manifests = [collectionRepositoryManifest];

View File

@@ -0,0 +1,20 @@
import type { UmbMediaCollectionFilterModel } from '../types.js';
import { UmbMediaCollectionServerDataSource } from './media-collection.server.data-source.js';
import type { UmbCollectionRepository } from '@umbraco-cms/backoffice/repository';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
export class UmbMediaCollectionRepository implements UmbCollectionRepository {
#collectionSource: UmbMediaCollectionServerDataSource;
constructor(host: UmbControllerHost) {
this.#collectionSource = new UmbMediaCollectionServerDataSource(host);
}
async requestCollection(query: UmbMediaCollectionFilterModel) {
return this.#collectionSource.getCollection(query);
}
destroy(): void {}
}
export default UmbMediaCollectionRepository;

View File

@@ -0,0 +1,56 @@
import type { UmbMediaCollectionFilterModel, UmbMediaCollectionItemModel } from '../types.js';
import { DirectionModel, MediaResource } from '@umbraco-cms/backoffice/external/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import type { MediaCollectionResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/repository';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
export class UmbMediaCollectionServerDataSource implements UmbCollectionDataSource<UmbMediaCollectionItemModel> {
#host: UmbControllerHost;
constructor(host: UmbControllerHost) {
this.#host = host;
}
async getCollection(query: UmbMediaCollectionFilterModel) {
// if (!query.dataTypeId) {
// throw new Error('Data type ID is required to fetch a collection.');
// }
const params = {
id: query.unique ?? '',
dataTypeId: query.dataTypeId,
orderBy: query.orderBy ?? 'updateDate',
orderDirection: query.orderDirection === 'asc' ? DirectionModel.ASCENDING : DirectionModel.DESCENDING,
filter: query.filter,
skip: query.skip ?? 0,
take: query.take ?? 100,
};
const { data, error } = await tryExecuteAndNotify(this.#host, MediaResource.getCollectionMedia(params));
if (data) {
const items = data.items.map((item: MediaCollectionResponseModel) => {
// TODO: [LK] Temp solution, review how to get the name from the corresponding variant.
const variant = item.variants[0];
const model: UmbMediaCollectionItemModel = {
unique: item.id,
createDate: new Date(variant.createDate),
creator: item.creator,
icon: item.mediaType.icon,
name: variant.name,
updateDate: new Date(variant.updateDate),
values: item.values.map((item) => {
return { alias: item.alias, value: item.value };
}),
};
return model;
});
return { data: { items, total: data.total } };
}
return { error };
}
}

View File

@@ -0,0 +1,19 @@
import type { UmbCollectionFilterModel } from '@umbraco-cms/backoffice/collection';
export interface UmbMediaCollectionFilterModel extends UmbCollectionFilterModel {
unique?: string;
dataTypeId?: string;
orderBy?: string;
orderDirection?: 'asc' | 'desc';
userDefinedProperties: Array<{alias: string, header: string, isSystem: boolean}>;
}
export interface UmbMediaCollectionItemModel {
unique: string;
createDate: Date;
creator?: string | null;
icon: string;
name: string;
updateDate: Date;
values: Array<{ alias: string; value: string }>;
}

View File

@@ -1,4 +1,5 @@
import { manifests as collectionViewManifests } from './collection-view/manifests.js';
import { manifests as collectionManifests } from './collection/manifests.js';
import { manifests as entityActionsManifests } from './entity-actions/manifests.js';
import { manifests as entityBulkActionsManifests } from './entity-bulk-actions/manifests.js';
import { manifests as menuItemManifests } from './menu-item/manifests.js';
@@ -10,6 +11,7 @@ import { manifests as workspaceManifests } from './workspace/manifests.js';
export const manifests = [
...collectionViewManifests,
...collectionManifests,
...entityActionsManifests,
...entityBulkActionsManifests,
...menuItemManifests,