Feature: Imaging Endpoint
This commit is contained in:
@@ -41,6 +41,7 @@
|
||||
"./extension-registry": "./dist-cms/packages/core/extension-registry/index.js",
|
||||
"./icon": "./dist-cms/packages/core/icon-registry/index.js",
|
||||
"./id": "./dist-cms/packages/core/id/index.js",
|
||||
"./imaging": "./dist-cms/packages/core/imaging/index.js",
|
||||
"./language": "./dist-cms/packages/language/index.js",
|
||||
"./lit-element": "./dist-cms/packages/core/lit-element/index.js",
|
||||
"./localization": "./dist-cms/packages/core/localization/index.js",
|
||||
|
||||
@@ -991,6 +991,15 @@ url?: string | null
|
||||
type?: string | null
|
||||
};
|
||||
|
||||
export enum ImageCropModeModel {
|
||||
CROP = 'Crop',
|
||||
MAX = 'Max',
|
||||
STRETCH = 'Stretch',
|
||||
PAD = 'Pad',
|
||||
BOX_PAD = 'BoxPad',
|
||||
MIN = 'Min'
|
||||
}
|
||||
|
||||
export type ImportDictionaryRequestModel = {
|
||||
temporaryFile: ReferenceByIdModel
|
||||
parent?: ReferenceByIdModel | null
|
||||
@@ -2574,6 +2583,12 @@ value: string
|
||||
key: string
|
||||
};
|
||||
|
||||
export type UserExternalLoginProviderModel = {
|
||||
providerSchemeName: string
|
||||
isLinkedOnUser: boolean
|
||||
hasManualLinkingEnabled: boolean
|
||||
};
|
||||
|
||||
export type UserGroupItemResponseModel = {
|
||||
id: string
|
||||
name: string
|
||||
@@ -3510,6 +3525,26 @@ tree?: string
|
||||
|
||||
}
|
||||
|
||||
export type ImagingData = {
|
||||
|
||||
payloads: {
|
||||
GetImagingResizeUrls: {
|
||||
height?: number
|
||||
id?: Array<string>
|
||||
mode?: ImageCropModeModel
|
||||
width?: number
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
responses: {
|
||||
GetImagingResizeUrls: Array<MediaUrlInfoResponseModel>
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export type IndexerData = {
|
||||
|
||||
payloads: {
|
||||
@@ -5205,6 +5240,7 @@ PostUserUnlock: {
|
||||
,PostUserCurrentAvatar: string
|
||||
,PostUserCurrentChangePassword: string
|
||||
,GetUserCurrentConfiguration: CurrenUserConfigurationResponseModel
|
||||
,GetUserCurrentLoginProviders: Array<UserExternalLoginProviderModel>
|
||||
,GetUserCurrentLogins: LinkedLoginsRequestModel
|
||||
,GetUserCurrentPermissions: UserPermissionsResponseModel
|
||||
,GetUserCurrentPermissionsDocument: Array<UserPermissionsResponseModel>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CancelablePromise } from './core/CancelablePromise';
|
||||
import { OpenAPI } from './core/OpenAPI';
|
||||
import { request as __request } from './core/request';
|
||||
import type { CultureData, DataTypeData, DictionaryData, DocumentBlueprintData, DocumentTypeData, DocumentVersionData, DocumentData, DynamicRootData, HealthCheckData, HelpData, IndexerData, InstallData, LanguageData, LogViewerData, ManifestData, MediaTypeData, MediaData, MemberGroupData, MemberTypeData, MemberData, ModelsBuilderData, ObjectTypesData, OembedData, PackageData, PartialViewData, PreviewData, ProfilingData, PropertyTypeData, PublishedCacheData, RedirectManagementData, RelationTypeData, RelationData, ScriptData, SearcherData, SecurityData, SegmentData, ServerData, StaticFileData, StylesheetData, TagData, TelemetryData, TemplateData, TemporaryFileData, UpgradeData, UserDataData, UserGroupData, UserData, WebhookData } from './models';
|
||||
import type { CultureData, DataTypeData, DictionaryData, DocumentBlueprintData, DocumentTypeData, DocumentVersionData, DocumentData, DynamicRootData, HealthCheckData, HelpData, ImagingData, IndexerData, InstallData, LanguageData, LogViewerData, ManifestData, MediaTypeData, MediaData, MemberGroupData, MemberTypeData, MemberData, ModelsBuilderData, ObjectTypesData, OembedData, PackageData, PartialViewData, PreviewData, ProfilingData, PropertyTypeData, PublishedCacheData, RedirectManagementData, RelationTypeData, RelationData, ScriptData, SearcherData, SecurityData, SegmentData, ServerData, StaticFileData, StylesheetData, TagData, TelemetryData, TemplateData, TemporaryFileData, UpgradeData, UserDataData, UserGroupData, UserData, WebhookData } from './models';
|
||||
|
||||
export class CultureService {
|
||||
|
||||
@@ -2917,6 +2917,35 @@ baseUrl
|
||||
|
||||
}
|
||||
|
||||
export class ImagingService {
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getImagingResizeUrls(data: ImagingData['payloads']['GetImagingResizeUrls'] = {}): CancelablePromise<ImagingData['responses']['GetImagingResizeUrls']> {
|
||||
const {
|
||||
|
||||
id,
|
||||
height,
|
||||
width,
|
||||
mode
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/imaging/resize/urls',
|
||||
query: {
|
||||
id, height, width, mode
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class IndexerService {
|
||||
|
||||
/**
|
||||
@@ -8657,6 +8686,21 @@ requestBody
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getUserCurrentLoginProviders(): CancelablePromise<UserData['responses']['GetUserCurrentLoginProviders']> {
|
||||
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/user/current/login-providers',
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { UmbImagingModel } from './types.js';
|
||||
import { UmbImagingServerDataSource } from './imaging.server.data.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
export class UmbImagingRepository extends UmbControllerBase implements UmbApi {
|
||||
//protected _init: Promise<unknown>;
|
||||
//protected _itemStore?: UmbImagingStore;
|
||||
#itemSource: UmbImagingServerDataSource;
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host);
|
||||
this.#itemSource = new UmbImagingServerDataSource(host);
|
||||
|
||||
/*this._init = this.consumeContext(UMB_IMAGING_STORE_CONTEXT, (instance) => {
|
||||
this._itemStore = instance as UmbImagingStore;
|
||||
}).asPromise();*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the items for the given uniques
|
||||
* @param {Array<string>} uniques
|
||||
* @return {*}
|
||||
* @memberof UmbImagingRepository
|
||||
*/
|
||||
async requestResizedItems({ uniques, height, width, mode }: UmbImagingModel) {
|
||||
if (!uniques.length) throw new Error('Uniques are missing');
|
||||
//await this._init;
|
||||
|
||||
const { data, error: _error } = await this.#itemSource.getItems({ uniques, height, width, mode });
|
||||
const error: any = _error;
|
||||
/*if (data) {
|
||||
this._itemStore!.appendItems(data);
|
||||
}
|
||||
return { data, error, asObservable: () => this._itemStore!.items(uniques) };*/
|
||||
|
||||
return { data, error };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { UmbImagingModel } from './types.js';
|
||||
import { ImagingService, type MediaUrlInfoResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import type { UmbMediaUrlModel } from '@umbraco-cms/backoffice/media';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
/**
|
||||
* A data source for the Imaging Service that resizes a media item from the server
|
||||
* @export
|
||||
* @class UmbImagingServerDataSource
|
||||
* @implements {RepositoryDetailDataSource}
|
||||
*/
|
||||
export class UmbImagingServerDataSource {
|
||||
#host: UmbControllerHost;
|
||||
|
||||
/**
|
||||
* Creates an instance of UmbImagingServerDataSource.
|
||||
* @param {UmbControllerHost} host
|
||||
* @memberof UmbImagingServerDataSource
|
||||
*/
|
||||
constructor(host: UmbControllerHost) {
|
||||
this.#host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the URL for the given media items as resized images
|
||||
* @param {string} unique
|
||||
* @memberof UmbImagingServerDataSource
|
||||
*/
|
||||
async getItems({ uniques, width, height, mode }: UmbImagingModel) {
|
||||
if (!uniques.length) throw new Error('Uniques are missing');
|
||||
|
||||
const { data, error } = await tryExecuteAndNotify(
|
||||
this.#host,
|
||||
ImagingService.getImagingResizeUrls({ id: uniques, width, height, mode }),
|
||||
);
|
||||
|
||||
if (data) {
|
||||
const items = data.map((item) => this.#mapper(item));
|
||||
return { data: items };
|
||||
}
|
||||
|
||||
return { error };
|
||||
}
|
||||
|
||||
#mapper(item: MediaUrlInfoResponseModel): UmbMediaUrlModel {
|
||||
const url = item.urlInfos[0]?.url;
|
||||
return {
|
||||
unique: item.id,
|
||||
url: url,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbItemStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import type { UmbMediaUrlModel } from '@umbraco-cms/backoffice/media';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbImagingStore
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Data Store for Imaging Items
|
||||
*/
|
||||
|
||||
export class UmbImagingStore extends UmbItemStoreBase<UmbMediaUrlModel> {
|
||||
/**
|
||||
* Creates an instance of UmbImagingStore.
|
||||
* @param {UmbControllerHost} host
|
||||
* @memberof UmbImagingStore
|
||||
*/
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_IMAGING_STORE_CONTEXT.toString());
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbImagingStore;
|
||||
|
||||
export const UMB_IMAGING_STORE_CONTEXT = new UmbContextToken<UmbImagingStore>('UmbImagingStore');
|
||||
@@ -0,0 +1,2 @@
|
||||
export { UmbImagingRepository } from './imaging.repository.js';
|
||||
export { UMB_IMAGING_REPOSITORY_ALIAS } from './manifests.js';
|
||||
@@ -0,0 +1,13 @@
|
||||
import { UmbImagingRepository } from './imaging.repository.js';
|
||||
import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const UMB_IMAGING_REPOSITORY_ALIAS = 'Umb.Repository.Imaging';
|
||||
|
||||
const repository: ManifestRepository = {
|
||||
type: 'repository',
|
||||
alias: UMB_IMAGING_REPOSITORY_ALIAS,
|
||||
name: 'Imaging Repository',
|
||||
api: UmbImagingRepository,
|
||||
};
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [repository];
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
|
||||
export interface UmbImagingModel {
|
||||
uniques: Array<string>;
|
||||
height?: number;
|
||||
width?: number;
|
||||
mode?: ImageCropModeModel;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { manifests as debugManifests } from './debug/manifests.js';
|
||||
import { manifests as entityActionManifests } from './entity-action/manifests.js';
|
||||
import { manifests as extensionManifests } from './extension-registry/manifests.js';
|
||||
import { manifests as iconRegistryManifests } from './icon-registry/manifests.js';
|
||||
import { manifests as imagingManifests } from './imaging/manifests.js';
|
||||
import { manifests as localizationManifests } from './localization/manifests.js';
|
||||
import { manifests as modalManifests } from './modal/common/manifests.js';
|
||||
import { manifests as propertyActionManifests } from './property-action/manifests.js';
|
||||
@@ -24,6 +25,7 @@ export const manifests: Array<ManifestTypes | UmbBackofficeManifestKind> = [
|
||||
...authManifests,
|
||||
...extensionManifests,
|
||||
...iconRegistryManifests,
|
||||
...imagingManifests,
|
||||
...cultureManifests,
|
||||
...localizationManifests,
|
||||
...themeManifests,
|
||||
|
||||
@@ -1,13 +1,39 @@
|
||||
import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging';
|
||||
import type { UmbMediaCollectionFilterModel, UmbMediaCollectionItemModel } from './types.js';
|
||||
import { UMB_MEDIA_GRID_COLLECTION_VIEW_ALIAS } from './views/index.js';
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
|
||||
export class UmbMediaCollectionContext extends UmbDefaultCollectionContext<
|
||||
UmbMediaCollectionItemModel,
|
||||
UmbMediaCollectionFilterModel
|
||||
> {
|
||||
#imagingRepository: UmbImagingRepository;
|
||||
|
||||
#thumbnailItems = new UmbArrayState<UmbMediaCollectionItemModel>([], (x) => x);
|
||||
public readonly thumbnailItems = this.#thumbnailItems.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_MEDIA_GRID_COLLECTION_VIEW_ALIAS);
|
||||
this.#imagingRepository = new UmbImagingRepository(host);
|
||||
|
||||
this.observe(this.items, async (items) => {
|
||||
if (!items?.length) return;
|
||||
|
||||
const { data } = await this.#imagingRepository.requestResizedItems({
|
||||
uniques: items.map((m) => m.unique),
|
||||
width: 250,
|
||||
mode: ImageCropModeModel.MIN,
|
||||
});
|
||||
|
||||
this.#thumbnailItems.setValue(
|
||||
items.map((item) => {
|
||||
const thumbnail = data?.find((m) => m.unique === item.unique)?.url;
|
||||
return { ...item, url: thumbnail };
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ export interface UmbMediaCollectionItemModel {
|
||||
updateDate: Date;
|
||||
updater?: string | null;
|
||||
values: Array<{ alias: string; value: string }>;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface UmbEditableMediaCollectionItemModel {
|
||||
item: UmbMediaCollectionItemModel;
|
||||
editPath: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext(UMB_COLLECTION_CONTEXT, (collectionContext) => {
|
||||
this.#collectionContext = collectionContext;
|
||||
this.#collectionContext = collectionContext as UmbMediaCollectionContext;
|
||||
this.#observeCollectionContext();
|
||||
});
|
||||
|
||||
@@ -51,7 +51,7 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement {
|
||||
|
||||
this.observe(this.#collectionContext.loading, (loading) => (this._loading = loading), '_observeLoading');
|
||||
|
||||
this.observe(this.#collectionContext.items, (items) => (this._items = items), '_observeItems');
|
||||
this.observe(this.#collectionContext.thumbnailItems, (items) => (this._items = items), '_observeItems');
|
||||
|
||||
this.observe(
|
||||
this.#collectionContext.selection.selection,
|
||||
@@ -128,6 +128,7 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement {
|
||||
@deselected=${() => this.#onDeselect(item)}
|
||||
class="media-item"
|
||||
file-ext="${item.icon}">
|
||||
${item.url ? html`<img src=${item.url} alt=${item.name} />` : html`<umb-icon name=${item.icon}></umb-icon>`}
|
||||
<!-- TODO: [LK] I'd like to indicate a busy state when bulk actions are triggered. -->
|
||||
<!-- <div class="container"><uui-loader></uui-loader></div> -->
|
||||
</uui-card-media>
|
||||
@@ -151,9 +152,12 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement {
|
||||
#media-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
grid-template-rows: repeat(auto-fill, 200px);
|
||||
grid-auto-rows: 200px;
|
||||
gap: var(--uui-size-space-5);
|
||||
}
|
||||
umb-icon {
|
||||
font-size: var(--uui-size-24);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -253,7 +253,6 @@ export class UmbMediaTableCollectionViewElement extends UmbLitElement {
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
padding: var(--uui-size-space-3) var(--uui-size-space-6);
|
||||
}
|
||||
|
||||
.container {
|
||||
|
||||
@@ -2,24 +2,23 @@ import type { UmbMediaPathModel } from '../types.js';
|
||||
import type { UmbMediaDetailModel } from '../../../types.js';
|
||||
import { UmbMediaDetailRepository } from '../../../repository/index.js';
|
||||
import { UmbMediaTreeRepository } from '../../../tree/index.js';
|
||||
import { UMB_MEDIA_ROOT_ENTITY_TYPE } from '../../../entity.js';
|
||||
import { UMB_MEDIA_ENTITY_TYPE, UMB_MEDIA_ROOT_ENTITY_TYPE } from '../../../entity.js';
|
||||
import { css, html, customElement, state, repeat, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UUIInputElement, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import { getUmbracoFolderUnique } from '@umbraco-cms/backoffice/media-type';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
|
||||
// TODO: get root from tree repository
|
||||
const root = { name: 'Media', unique: null, entityType: UMB_MEDIA_ROOT_ENTITY_TYPE };
|
||||
const root: UmbMediaPathModel = { name: 'Media', unique: null, entityType: UMB_MEDIA_ROOT_ENTITY_TYPE };
|
||||
|
||||
@customElement('umb-media-picker-folder-path')
|
||||
export class UmbMediaPickerFolderPathElement extends UmbLitElement {
|
||||
#mediaTreeRepository = new UmbMediaTreeRepository(this); // used to get file structure
|
||||
#mediaDetailRepository = new UmbMediaDetailRepository(this); // used to create folders
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
public set currentMedia(value: UmbEntityModel | undefined) {
|
||||
@property({ attribute: false })
|
||||
public set currentMedia(value: UmbMediaPathModel) {
|
||||
if (value !== this._currentMedia) {
|
||||
this._currentMedia = value;
|
||||
this.#loadPath();
|
||||
@@ -31,7 +30,7 @@ export class UmbMediaPickerFolderPathElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
@state()
|
||||
private _currentMedia: UmbEntityModel | undefined;
|
||||
private _currentMedia: UmbMediaPathModel = root;
|
||||
|
||||
@state()
|
||||
private _paths: Array<UmbMediaPathModel> = [root];
|
||||
@@ -45,32 +44,30 @@ export class UmbMediaPickerFolderPathElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
async #loadPath() {
|
||||
const unique = this._currentMedia?.unique;
|
||||
const entityType = this._currentMedia?.entityType;
|
||||
const unique = this._currentMedia.unique;
|
||||
|
||||
if (unique && entityType) {
|
||||
const { data } = await this.#mediaTreeRepository.requestTreeItemAncestors({
|
||||
treeItem: {
|
||||
unique,
|
||||
entityType,
|
||||
},
|
||||
});
|
||||
const items = unique
|
||||
? (
|
||||
await this.#mediaTreeRepository.requestTreeItemAncestors({
|
||||
treeItem: { unique, entityType: UMB_MEDIA_ENTITY_TYPE },
|
||||
})
|
||||
).data
|
||||
: undefined;
|
||||
|
||||
if (data) {
|
||||
this._paths = [
|
||||
root,
|
||||
...data.map((item) => ({ name: item.name, unique: item.unique, entityType: item.entityType })),
|
||||
];
|
||||
return;
|
||||
}
|
||||
if (items) {
|
||||
this._paths = [
|
||||
root,
|
||||
...items.map((item) => ({ name: item.name, unique: item.unique, entityType: item.entityType })),
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
this._paths = [root];
|
||||
}
|
||||
|
||||
#goToFolder(entity: UmbEntityModel) {
|
||||
#goToFolder(entity: UmbMediaPathModel) {
|
||||
this._paths = [...this._paths].slice(0, this._paths.findIndex((path) => path.unique === entity.unique) + 1);
|
||||
this.currentMedia = entity;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
#focusFolderInput() {
|
||||
@@ -84,6 +81,7 @@ export class UmbMediaPickerFolderPathElement extends UmbLitElement {
|
||||
|
||||
async #addFolder(e: UUIInputEvent) {
|
||||
e.stopPropagation();
|
||||
|
||||
const newName = e.target.value as string;
|
||||
this._typingNewFolder = false;
|
||||
if (!newName) return;
|
||||
@@ -118,7 +116,8 @@ export class UmbMediaPickerFolderPathElement extends UmbLitElement {
|
||||
const entityType = data.entityType;
|
||||
|
||||
this._paths = [...this._paths, { name, unique, entityType }];
|
||||
this.currentMedia = { unique, entityType };
|
||||
this.currentMedia = { name, unique, entityType };
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -130,8 +129,8 @@ export class UmbMediaPickerFolderPathElement extends UmbLitElement {
|
||||
html`<uui-button
|
||||
compact
|
||||
.label=${path.name}
|
||||
?disabled=${this.currentMedia?.unique === path.unique}
|
||||
@click=${() => this.#goToFolder({ unique: path.unique, entityType: path.entityType })}></uui-button
|
||||
?disabled=${this.currentMedia.unique === path.unique}
|
||||
@click=${() => this.#goToFolder(path)}></uui-button
|
||||
>/`,
|
||||
)}${this._typingNewFolder
|
||||
? html`<uui-input
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging';
|
||||
import { type UmbMediaItemModel, UmbMediaItemRepository, UmbMediaUrlRepository } from '../../repository/index.js';
|
||||
import { UmbMediaTreeRepository } from '../../tree/media-tree.repository.js';
|
||||
import { UMB_MEDIA_ROOT_ENTITY_TYPE } from '../../entity.js';
|
||||
import type { UmbMediaCardItemModel } from './types.js';
|
||||
import type { UmbMediaCardItemModel, UmbMediaPathModel } from './types.js';
|
||||
import type { UmbMediaPickerFolderPathElement } from './components/media-picker-folder-path.element.js';
|
||||
import type { UmbMediaPickerModalData, UmbMediaPickerModalValue } from './media-picker-modal.token.js';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { css, html, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
|
||||
const root: UmbMediaPathModel = { name: 'Media', unique: null, entityType: UMB_MEDIA_ROOT_ENTITY_TYPE };
|
||||
|
||||
@customElement('umb-media-picker-modal')
|
||||
export class UmbMediaPickerModalElement extends UmbModalBaseElement<UmbMediaPickerModalData, UmbMediaPickerModalValue> {
|
||||
#mediaTreeRepository = new UmbMediaTreeRepository(this); // used to get file structure
|
||||
#mediaUrlRepository = new UmbMediaUrlRepository(this); // used to get urls
|
||||
#mediaItemRepository = new UmbMediaItemRepository(this); // used to search & get media type of current path
|
||||
#mediaItemRepository = new UmbMediaItemRepository(this); // used to search
|
||||
#imagingRepository = new UmbImagingRepository(this); // used to get image renditions
|
||||
|
||||
#mediaItemsCurrentFolder: Array<UmbMediaCardItemModel> = [];
|
||||
|
||||
@@ -27,7 +31,8 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement<UmbMediaPick
|
||||
private _searchQuery = '';
|
||||
|
||||
@state()
|
||||
private _currentMediaEntity: UmbEntityModel = { unique: null, entityType: UMB_MEDIA_ROOT_ENTITY_TYPE };
|
||||
private _currentMediaEntity: UmbMediaPathModel = root;
|
||||
|
||||
async connectedCallback(): Promise<void> {
|
||||
super.connectedCallback();
|
||||
|
||||
@@ -35,7 +40,7 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement<UmbMediaPick
|
||||
const { data } = await this.#mediaItemRepository.requestItems([this.data.startNode]);
|
||||
|
||||
if (data?.length) {
|
||||
this._currentMediaEntity = { unique: data[0].unique, entityType: data[0].entityType };
|
||||
this._currentMediaEntity = { name: data[0].name, unique: data[0].unique, entityType: data[0].entityType };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,18 +64,22 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement<UmbMediaPick
|
||||
async #mapMediaUrls(items: Array<UmbMediaItemModel>): Promise<Array<UmbMediaCardItemModel>> {
|
||||
if (!items.length) return [];
|
||||
|
||||
const { data } = await this.#mediaUrlRepository.requestItems(items.map((item) => item.unique));
|
||||
const { data } = await this.#imagingRepository.requestResizedItems({
|
||||
uniques: items.map((item) => item.unique),
|
||||
width: 250,
|
||||
mode: ImageCropModeModel.MIN,
|
||||
});
|
||||
|
||||
return items.map((item): UmbMediaCardItemModel => {
|
||||
const url = data?.find((media) => media.unique === item.unique)?.url;
|
||||
const extension = url?.split('.').pop();
|
||||
//TODO Eventually we will get a renderable img from the server. Use this for the url. [LI]
|
||||
return { name: item.name, unique: item.unique, url, extension, entityType: item.entityType };
|
||||
|
||||
return { name: item.name, unique: item.unique, url, icon: item.mediaType.icon, entityType: item.entityType };
|
||||
});
|
||||
}
|
||||
|
||||
#onOpen(item: UmbMediaCardItemModel) {
|
||||
this._currentMediaEntity = {
|
||||
name: item.name,
|
||||
unique: item.unique,
|
||||
entityType: UMB_MEDIA_ROOT_ENTITY_TYPE,
|
||||
};
|
||||
@@ -189,9 +198,10 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement<UmbMediaPick
|
||||
@selected=${() => this.#onSelected(item)}
|
||||
@deselected=${() => this.#onDeselected(item)}
|
||||
?selected=${this.value?.selection?.find((value) => value === item.unique)}
|
||||
selectable
|
||||
file-ext=${ifDefined(item.extension)}>
|
||||
${item.url ? html`<img src=${item.url} alt=${ifDefined(item.name)} />` : ''}
|
||||
selectable>
|
||||
${item.url
|
||||
? html`<img src=${item.url} alt=${ifDefined(item.name)} />`
|
||||
: html`<umb-icon .name=${item.icon}></umb-icon>`}
|
||||
</uui-card-media>
|
||||
`;
|
||||
}
|
||||
@@ -199,7 +209,7 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement<UmbMediaPick
|
||||
#renderPath() {
|
||||
return html`<umb-media-picker-folder-path
|
||||
slot="footer-info"
|
||||
.currentPath=${this._currentMediaEntity.unique}
|
||||
.currentMedia=${this._currentMediaEntity}
|
||||
@change=${this.#onPathChange}></umb-media-picker-folder-path>`;
|
||||
}
|
||||
|
||||
@@ -227,10 +237,23 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement<UmbMediaPick
|
||||
#media-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
grid-template-rows: repeat(auto-fill, 200px);
|
||||
grid-auto-rows: 200px;
|
||||
gap: var(--uui-size-space-5);
|
||||
padding-bottom: 5px; /** The modal is a bit jumpy due to the img card focus/hover border. This fixes the issue. */
|
||||
}
|
||||
umb-icon {
|
||||
font-size: var(--uui-size-24);
|
||||
}
|
||||
|
||||
img {
|
||||
background-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill-opacity=".1"><path d="M50 0h50v50H50zM0 50h50v50H0z"/></svg>');
|
||||
background-size: 10px 10px;
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
#actions {
|
||||
max-width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface UmbMediaCardItemModel {
|
||||
unique: string;
|
||||
entityType: UmbMediaEntityType;
|
||||
url?: string;
|
||||
extension?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface UmbMediaPathModel extends UmbEntityModel {
|
||||
|
||||
@@ -2,4 +2,5 @@ export { UmbMediaDetailRepository, UMB_MEDIA_DETAIL_REPOSITORY_ALIAS } from './d
|
||||
export { UmbMediaItemRepository, UMB_MEDIA_ITEM_REPOSITORY_ALIAS } from './item/index.js';
|
||||
export { UmbMediaUrlRepository, UMB_MEDIA_URL_REPOSITORY_ALIAS } from './url/index.js';
|
||||
|
||||
export type { UmbMediaUrlModel } from './url/types.js';
|
||||
export type { UmbMediaItemModel } from './item/types.js';
|
||||
|
||||
@@ -66,6 +66,7 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js
|
||||
"@umbraco-cms/backoffice/extension-registry": ["./src/packages/core/extension-registry/index.ts"],
|
||||
"@umbraco-cms/backoffice/icon": ["./src/packages/core/icon-registry/index.ts"],
|
||||
"@umbraco-cms/backoffice/id": ["./src/packages/core/id/index.ts"],
|
||||
"@umbraco-cms/backoffice/imaging": ["./src/packages/core/imaging/index.ts"],
|
||||
"@umbraco-cms/backoffice/language": ["./src/packages/language/index.ts"],
|
||||
"@umbraco-cms/backoffice/lit-element": ["./src/packages/core/lit-element/index.ts"],
|
||||
"@umbraco-cms/backoffice/localization": ["./src/packages/core/localization/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user