Merge branch 'main' of https://github.com/umbraco/Umbraco.CMS.Backoffice
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",
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/** Example of how a grid layout stylehseet could be done with Flex box: */
|
||||
|
||||
.umb-block-grid__layout-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--umb-block-grid--row-gap, 0) var(--umb-block-grid--column-gap, 0);
|
||||
}
|
||||
.umb-block-grid__layout-item {
|
||||
position: relative;
|
||||
--umb-block-grid__layout-item-calc: calc(var(--umb-block-grid--item-column-span) / var(--umb-block-grid--grid-columns));
|
||||
width: calc(var(--umb-block-grid__layout-item-calc) * 100% - (1 - var(--umb-block-grid__layout-item-calc)) * var(--umb-block-grid--column-gap, 0px));
|
||||
}
|
||||
|
||||
|
||||
.umb-block-grid__area-container, .umb-block-grid__block--view::part(area-container) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
gap: var(--umb-block-grid--areas-row-gap, 0) var(--umb-block-grid--areas-column-gap, 0);
|
||||
}
|
||||
.umb-block-grid__area {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
--umb-block-grid__area-calc: calc(var(--umb-block-grid--area-column-span) / var(--umb-block-grid--area-grid-columns, 1));
|
||||
width: calc(var(--umb-block-grid__area-calc) * 100% - (1 - var(--umb-block-grid__area-calc)) * var(--umb-block-grid--areas-column-gap, 0px));
|
||||
}
|
||||
|
||||
|
||||
.umb-block-grid__actions {
|
||||
clear: both;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
.umb-block-grid__layout-container {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--umb-block-grid--grid-columns, 1), minmax(0, 1fr));
|
||||
grid-auto-flow: row;
|
||||
grid-auto-rows: minmax(50px, min-content);
|
||||
|
||||
column-gap: var(--umb-block-grid--column-gap, 0);
|
||||
row-gap: var(--umb-block-grid--row-gap, 0);
|
||||
}
|
||||
.umb-block-grid__layout-item {
|
||||
position: relative;
|
||||
/* For small devices we scale columnSpan by three, to make everything bigger than 1/3 take full width: */
|
||||
grid-column-end: span min(calc(var(--umb-block-grid--item-column-span, 1) * 3), var(--umb-block-grid--grid-columns));
|
||||
grid-row: span var(--umb-block-grid--item-row-span, 1);
|
||||
}
|
||||
|
||||
|
||||
.umb-block-grid__area-container, .umb-block-grid__block--view::part(area-container) {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--umb-block-grid--area-grid-columns, var(--umb-block-grid--grid-columns, 1)), minmax(0, 1fr));
|
||||
grid-auto-flow: row;
|
||||
grid-auto-rows: minmax(50px, min-content);
|
||||
|
||||
column-gap: var(--umb-block-grid--areas-column-gap, 0);
|
||||
row-gap: var(--umb-block-grid--areas-row-gap, 0);
|
||||
}
|
||||
.umb-block-grid__area {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* For small devices we scale columnSpan by three, to make everything bigger than 1/3 take full width: */
|
||||
grid-column-end: span min(calc(var(--umb-block-grid--area-column-span, 1) * 3), var(--umb-block-grid--area-grid-columns));
|
||||
grid-row: span var(--umb-block-grid--area-row-span, 1);
|
||||
}
|
||||
|
||||
@media (min-width:1024px) {
|
||||
.umb-block-grid__layout-item {
|
||||
grid-column-end: span min(var(--umb-block-grid--item-column-span, 1), var(--umb-block-grid--grid-columns));
|
||||
}
|
||||
.umb-block-grid__area {
|
||||
grid-column-end: span min(var(--umb-block-grid--area-column-span, 1), var(--umb-block-grid--area-grid-columns));
|
||||
}
|
||||
}
|
||||
@@ -2583,6 +2583,12 @@ value: string
|
||||
key: string
|
||||
};
|
||||
|
||||
export type UserExternalLoginProviderModel = {
|
||||
providerSchemeName: string
|
||||
isLinkedOnUser: boolean
|
||||
hasManualLinkingEnabled: boolean
|
||||
};
|
||||
|
||||
export type UserGroupItemResponseModel = {
|
||||
id: string
|
||||
name: string
|
||||
@@ -5234,6 +5240,7 @@ PostUserUnlock: {
|
||||
,PostUserCurrentAvatar: string
|
||||
,PostUserCurrentChangePassword: string
|
||||
,GetUserCurrentConfiguration: CurrenUserConfigurationResponseModel
|
||||
,GetUserCurrentLoginProviders: Array<UserExternalLoginProviderModel>
|
||||
,GetUserCurrentLogins: LinkedLoginsRequestModel
|
||||
,GetUserCurrentPermissions: UserPermissionsResponseModel
|
||||
,GetUserCurrentPermissionsDocument: Array<UserPermissionsResponseModel>
|
||||
|
||||
@@ -8686,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
|
||||
|
||||
@@ -19,6 +19,7 @@ export const manifest: ManifestPropertyEditorSchema = {
|
||||
label: 'Amount',
|
||||
propertyEditorUiAlias: 'Umb.PropertyEditorUi.NumberRange',
|
||||
config: [{ alias: 'validationRange', value: { min: 0, max: Infinity } }],
|
||||
weight: 100,
|
||||
},
|
||||
],
|
||||
defaultData: [
|
||||
|
||||
@@ -20,6 +20,7 @@ export const manifests: Array<ManifestTypes> = [
|
||||
alias: 'blockGroups',
|
||||
label: '',
|
||||
propertyEditorUiAlias: 'Umb.PropertyEditorUi.BlockTypeGroupConfiguration',
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
alias: 'useLiveEditing',
|
||||
@@ -42,9 +43,12 @@ export const manifests: Array<ManifestTypes> = [
|
||||
{
|
||||
alias: 'gridColumns',
|
||||
label: 'Grid Columns',
|
||||
description: 'Set the number of columns for the layout. (defaults to 12)',
|
||||
description: 'Set the number of columns for the layout.',
|
||||
propertyEditorUiAlias: 'Umb.PropertyEditorUi.Integer',
|
||||
config: [{ alias: 'min', value: 0 }],
|
||||
config: [
|
||||
{ alias: 'min', value: 0 },
|
||||
{ alias: 'placeholder', value: '12' },
|
||||
],
|
||||
},
|
||||
{
|
||||
alias: 'layoutStylesheet',
|
||||
|
||||
@@ -44,7 +44,7 @@ export class UmbPropertyEditorUIBlockGridLayoutStylesheetElement
|
||||
.min=${0}
|
||||
.max=${1}></umb-input-static-file>
|
||||
<br />
|
||||
<a href="#Missinhg_link_to_default_layout_stylesheet">Link to default layout stylesheet</a>
|
||||
<a href="/umbraco/backoffice/assets/css/umbraco-blockgridlayout.css">Link to default layout stylesheet</a>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, customElement, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbPropertyEditorConfig } from '@umbraco-cms/backoffice/property-editor';
|
||||
@@ -8,6 +8,9 @@ import { UMB_DATA_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/data-ty
|
||||
@customElement('umb-block-grid-type-workspace-view-areas')
|
||||
export class UmbBlockGridTypeWorkspaceViewAreasElement extends UmbLitElement implements UmbWorkspaceViewElement {
|
||||
//
|
||||
@state()
|
||||
_areaColumnsConfigurationObject?: UmbPropertyEditorConfig;
|
||||
|
||||
@state()
|
||||
_areaConfigConfigurationObject?: UmbPropertyEditorConfig;
|
||||
|
||||
@@ -18,7 +21,8 @@ export class UmbBlockGridTypeWorkspaceViewAreasElement extends UmbLitElement imp
|
||||
this.observe(
|
||||
await context.propertyValueByAlias<undefined | string>('gridColumns'),
|
||||
(value) => {
|
||||
const dataTypeGridColumns = value ? parseInt(value, 10) : undefined;
|
||||
const dataTypeGridColumns = value ? parseInt(value, 10) : 12;
|
||||
this._areaColumnsConfigurationObject = [{ alias: 'placeholder', value: dataTypeGridColumns }];
|
||||
this._areaConfigConfigurationObject = [{ alias: 'defaultAreaGridColumns', value: dataTypeGridColumns }];
|
||||
},
|
||||
'observeGridColumns',
|
||||
@@ -27,21 +31,24 @@ export class UmbBlockGridTypeWorkspaceViewAreasElement extends UmbLitElement imp
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-box headline="Areas">
|
||||
<umb-property
|
||||
label=${this.localize.term('blockEditor_areasLayoutColumns')}
|
||||
alias="areaGridColumns"
|
||||
property-editor-ui-alias="Umb.PropertyEditorUi.Integer"></umb-property>
|
||||
<umb-property
|
||||
label=${this.localize.term('blockEditor_areasConfigurations')}
|
||||
alias="areas"
|
||||
property-editor-ui-alias="Umb.PropertyEditorUi.BlockGridAreasConfig"
|
||||
.config=${this._areaConfigConfigurationObject}
|
||||
>></umb-property
|
||||
>
|
||||
</uui-box>
|
||||
`;
|
||||
return this._areaConfigConfigurationObject
|
||||
? html`
|
||||
<uui-box headline="Areas">
|
||||
<umb-property
|
||||
label=${this.localize.term('blockEditor_areasLayoutColumns')}
|
||||
alias="areaGridColumns"
|
||||
property-editor-ui-alias="Umb.PropertyEditorUi.Integer"
|
||||
.config=${this._areaColumnsConfigurationObject}></umb-property>
|
||||
<umb-property
|
||||
label=${this.localize.term('blockEditor_areasConfigurations')}
|
||||
alias="areas"
|
||||
property-editor-ui-alias="Umb.PropertyEditorUi.BlockGridAreasConfig"
|
||||
.config=${this._areaConfigConfigurationObject}
|
||||
>></umb-property
|
||||
>
|
||||
</uui-box>
|
||||
`
|
||||
: nothing;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
|
||||
@@ -82,20 +82,23 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement
|
||||
const blocks = config.getValueByAlias<Array<UmbBlockTypeBaseModel>>('blocks') ?? [];
|
||||
this.#managerContext.setBlockTypes(blocks);
|
||||
|
||||
const useInlineEditingAsDefault = config.getValueByAlias<boolean>('useInlineEditingAsDefault');
|
||||
this.#managerContext.setInlineEditingMode(useInlineEditingAsDefault);
|
||||
this.style.maxWidth = config.getValueByAlias<string>('maxPropertyWidth') ?? '';
|
||||
// TODO:
|
||||
//config.useSingleBlockMode, not done jet
|
||||
|
||||
this.#managerContext.setEditorConfiguration(config);
|
||||
|
||||
const customCreateButtonLabel = config.getValueByAlias<string>('createLabel');
|
||||
if (customCreateButtonLabel) {
|
||||
this._createButtonLabel = customCreateButtonLabel;
|
||||
} else if (blocks.length === 1) {
|
||||
this._createButtonLabel = `${this.localize.term('general_add')} ${blocks[0].label}`;
|
||||
this.#managerContext.contentTypesLoaded.then(() => {
|
||||
const firstContentTypeName = this.#managerContext.getContentTypeNameOf(blocks[0].contentElementTypeKey);
|
||||
this._createButtonLabel = `${this.localize.term('general_add')} ${firstContentTypeName}`;
|
||||
});
|
||||
}
|
||||
|
||||
const useInlineEditingAsDefault = config.getValueByAlias<boolean>('useInlineEditingAsDefault');
|
||||
this.#managerContext.setInlineEditingMode(useInlineEditingAsDefault);
|
||||
// TODO:
|
||||
//config.useSingleBlockMode, not done jet
|
||||
this.style.maxWidth = config.getValueByAlias<string>('maxPropertyWidth') ?? '';
|
||||
|
||||
this.#managerContext.setEditorConfiguration(config);
|
||||
}
|
||||
|
||||
@state()
|
||||
|
||||
@@ -18,6 +18,9 @@ export class UmbBlockTypeCardElement extends UmbLitElement {
|
||||
@property({ type: String, attribute: false })
|
||||
href?: string;
|
||||
|
||||
@property({ type: String, attribute: false })
|
||||
iconFile?: string;
|
||||
|
||||
@property({ type: String, attribute: false })
|
||||
iconColor?: string;
|
||||
|
||||
@@ -41,7 +44,7 @@ export class UmbBlockTypeCardElement extends UmbLitElement {
|
||||
private _elementTypeKey?: string | undefined;
|
||||
|
||||
@state()
|
||||
_fallbackName?: string;
|
||||
_name?: string;
|
||||
|
||||
@state()
|
||||
_fallbackIcon?: string | null;
|
||||
@@ -53,7 +56,7 @@ export class UmbBlockTypeCardElement extends UmbLitElement {
|
||||
const item = items[0];
|
||||
if (item) {
|
||||
this._fallbackIcon = item.icon;
|
||||
this._fallbackName = item.name;
|
||||
this._name = item.name;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -63,9 +66,11 @@ export class UmbBlockTypeCardElement extends UmbLitElement {
|
||||
return html`
|
||||
<uui-card-block-type
|
||||
href=${ifDefined(this.href)}
|
||||
.name=${this._fallbackName ?? 'Unknown'}
|
||||
.name=${this._name ?? 'Unknown'}
|
||||
.background=${this.backgroundColor}>
|
||||
<umb-icon name=${this._fallbackIcon ?? ''} style="color:${this.iconColor}"></umb-icon>
|
||||
${this.iconFile
|
||||
? html`<img src=${this.iconFile} alt="" />`
|
||||
: html`<umb-icon name=${this._fallbackIcon ?? ''} style="color:${this.iconColor}"></umb-icon>`}
|
||||
<slot name="actions" slot="actions"> </slot>
|
||||
</uui-card-block-type>
|
||||
`;
|
||||
|
||||
@@ -7,7 +7,10 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import { UmbDeleteEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UMB_DOCUMENT_TYPE_PICKER_MODAL } from '@umbraco-cms/backoffice/document-type';
|
||||
import {
|
||||
UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT,
|
||||
UMB_DOCUMENT_TYPE_PICKER_MODAL,
|
||||
} from '@umbraco-cms/backoffice/document-type';
|
||||
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
|
||||
/** TODO: Look into sending a "change" event when there is a change, rather than create, delete, and change event. Make sure it doesn't break move for RTE/List/Grid. [LI] */
|
||||
@@ -123,10 +126,13 @@ export class UmbInputBlockTypeElement<
|
||||
}
|
||||
|
||||
async #onRequestDelete(item: BlockType) {
|
||||
const store = await this.getContext(UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT);
|
||||
const contentType = store.getItems([item.contentElementTypeKey]);
|
||||
await umbConfirmModal(this, {
|
||||
color: 'danger',
|
||||
headline: `Remove [TODO: Get name]?`,
|
||||
content: 'Are you sure you want to remove this block type?',
|
||||
headline: `Remove ${contentType[0]?.name}?`,
|
||||
// TODO: Translations: [NL]
|
||||
content: 'Are you sure you want to remove this Block Type Configuration?',
|
||||
confirmLabel: 'Remove',
|
||||
});
|
||||
this.deleteItem(item.contentElementTypeKey);
|
||||
@@ -143,6 +149,7 @@ export class UmbInputBlockTypeElement<
|
||||
<umb-block-type-card
|
||||
.data-umb-content-element-key=${block.contentElementTypeKey}
|
||||
.name=${block.label}
|
||||
.iconFile=${block.thumbnail}
|
||||
.iconColor=${block.iconColor}
|
||||
.backgroundColor=${block.backgroundColor}
|
||||
.href="${this.workspacePath}edit/${block.contentElementTypeKey}"
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface UmbBlockTypeBaseModel {
|
||||
label?: string;
|
||||
//view?: string; // TODO: remove/replace with custom element manifest type for block list.
|
||||
//stylesheet?: string; // TODO: remove/replace with custom element manifest type for block list.
|
||||
thumbnail?: string;
|
||||
iconColor?: string;
|
||||
backgroundColor?: string;
|
||||
editorSize?: UUIModalSidebarSize;
|
||||
|
||||
@@ -26,6 +26,10 @@ export abstract class UmbBlockManagerContext<
|
||||
BlockLayoutType extends UmbBlockLayoutBaseModel = UmbBlockLayoutBaseModel,
|
||||
> extends UmbContextBase<UmbBlockManagerContext> {
|
||||
//
|
||||
get contentTypesLoaded() {
|
||||
return Promise.all(this.#contentTypeRequests);
|
||||
}
|
||||
#contentTypeRequests: Array<Promise<unknown>> = [];
|
||||
#contentTypeRepository = new UmbDocumentTypeDetailRepository(this);
|
||||
|
||||
#propertyAlias = new UmbStringState(undefined);
|
||||
@@ -115,7 +119,9 @@ export abstract class UmbBlockManagerContext<
|
||||
async #ensureContentType(unique: string) {
|
||||
if (this.#contentTypes.getValue().find((x) => x.unique === unique)) return;
|
||||
|
||||
const { data } = await this.#contentTypeRepository.requestByUnique(unique);
|
||||
const contentTypeRequest = this.#contentTypeRepository.requestByUnique(unique);
|
||||
this.#contentTypeRequests.push(contentTypeRequest);
|
||||
const { data } = await contentTypeRequest;
|
||||
if (!data) {
|
||||
this.#contentTypes.removeOne(unique);
|
||||
return;
|
||||
@@ -132,6 +138,9 @@ export abstract class UmbBlockManagerContext<
|
||||
contentTypeNameOf(contentTypeKey: string) {
|
||||
return this.#contentTypes.asObservablePart((source) => source.find((x) => x.unique === contentTypeKey)?.name);
|
||||
}
|
||||
getContentTypeNameOf(contentTypeKey: string) {
|
||||
return this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.name;
|
||||
}
|
||||
blockTypeOf(contentTypeKey: string) {
|
||||
return this.#blockTypes.asObservablePart((source) =>
|
||||
source.find((x) => x.contentElementTypeKey === contentTypeKey),
|
||||
|
||||
@@ -51,6 +51,7 @@ export interface PropertyEditorSettingsProperty {
|
||||
alias: string;
|
||||
propertyEditorUiAlias: string;
|
||||
config?: UmbPropertyEditorConfig;
|
||||
weight?: number;
|
||||
}
|
||||
|
||||
export interface PropertyEditorSettingsDefaultData {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
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 {
|
||||
#itemSource: UmbImagingServerDataSource;
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host);
|
||||
this.#itemSource = new UmbImagingServerDataSource(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the items for the given uniques
|
||||
* @param {Array<string>} uniques
|
||||
* @return {*}
|
||||
* @memberof UmbImagingRepository
|
||||
*/
|
||||
async requestResizedItems(uniques: Array<string>, imagingModel?: UmbImagingModel) {
|
||||
if (!uniques.length) throw new Error('Uniques are missing');
|
||||
|
||||
const { data, error: _error } = await this.#itemSource.getItems(uniques, imagingModel);
|
||||
const error: any = _error;
|
||||
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: Array<string>, imagingModel?: UmbImagingModel) {
|
||||
if (!uniques.length) throw new Error('Uniques are missing');
|
||||
|
||||
const { data, error } = await tryExecuteAndNotify(
|
||||
this.#host,
|
||||
ImagingService.getImagingResizeUrls({ id: uniques, ...imagingModel }),
|
||||
);
|
||||
|
||||
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,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,7 @@
|
||||
import type { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
|
||||
export interface UmbImagingModel {
|
||||
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,
|
||||
|
||||
@@ -74,7 +74,7 @@ export class UmbStoreBase<StoreItemType = any> extends UmbContextBase<any> imple
|
||||
* @returns {Array<StoreItemType>}
|
||||
* @memberof UmbStoreBase
|
||||
*/
|
||||
getItems(uniques: Array<string>) {
|
||||
getItems(uniques: Array<string>): Array<StoreItemType> {
|
||||
return this._data.getValue().filter((item) => uniques.includes(this._data.getUniqueMethod(item) as string));
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,9 @@ export class UmbDataTypeWorkspaceContext
|
||||
readonly propertyEditorUiAlias = this.#currentData.asObservablePart((data) => data?.editorUiAlias);
|
||||
readonly propertyEditorSchemaAlias = this.#currentData.asObservablePart((data) => data?.editorAlias);
|
||||
|
||||
#properties = new UmbArrayState<PropertyEditorSettingsProperty>([], (x) => x.alias);
|
||||
#properties = new UmbArrayState<PropertyEditorSettingsProperty>([], (x) => x.alias).sortBy(
|
||||
(a, b) => (a.weight || 0) - (b.weight || 0),
|
||||
);
|
||||
readonly properties = this.#properties.asObservable();
|
||||
|
||||
#defaults = new UmbArrayState<PropertyEditorSettingsDefaultData>([], (entry) => entry.alias);
|
||||
@@ -164,7 +166,11 @@ export class UmbDataTypeWorkspaceContext
|
||||
return this.observe(
|
||||
umbExtensionsRegistry.byTypeAndAlias('propertyEditorSchema', propertyEditorSchemaAlias),
|
||||
(manifest) => {
|
||||
this.#propertyEditorSchemaSettingsProperties = manifest?.meta.settings?.properties || [];
|
||||
// Maps properties to have a weight, so they can be sorted
|
||||
this.#propertyEditorSchemaSettingsProperties = (manifest?.meta.settings?.properties ?? []).map((x, i) => ({
|
||||
...x,
|
||||
weight: x.weight ?? i,
|
||||
}));
|
||||
this.#propertyEditorSchemaSettingsDefaultData = manifest?.meta.settings?.defaultData || [];
|
||||
this.#propertyEditorSchemaConfigDefaultUIAlias = manifest?.meta.defaultPropertyEditorUiAlias || null;
|
||||
},
|
||||
@@ -180,7 +186,11 @@ export class UmbDataTypeWorkspaceContext
|
||||
this.#propertyEditorUiName.setValue(manifest?.name || null);
|
||||
|
||||
this.#propertyEditorUISettingsSchemaAlias = manifest?.meta.propertyEditorSchemaAlias;
|
||||
this.#propertyEditorUISettingsProperties = manifest?.meta.settings?.properties || [];
|
||||
// Maps properties to have a weight, so they can be sorted, notice UI properties have a +1000 weight compared to schema properties.
|
||||
this.#propertyEditorUISettingsProperties = (manifest?.meta.settings?.properties ?? []).map((x, i) => ({
|
||||
...x,
|
||||
weight: x.weight ?? 1000 + i,
|
||||
}));
|
||||
this.#propertyEditorUISettingsDefaultData = manifest?.meta.settings?.defaultData || [];
|
||||
},
|
||||
'editorUi',
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { UmbDocumentTypeItemStore } from './document-type-item.store.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
export const UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT = new UmbContextToken<UmbDocumentTypeItemStore>(
|
||||
'UmbDocumentTypeItemStore',
|
||||
);
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { UmbDocumentTypeItemModel } from './types.js';
|
||||
import { UmbDocumentTypeItemServerDataSource } from './document-type-item.server.data-source.js';
|
||||
import { UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT } from './document-type-item.store.js';
|
||||
import { UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT } from './document-type-item-store.context-token.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbItemRepositoryBase } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbDocumentTypeItemModel } from './types.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT } from './document-type-item-store.context-token.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbItemStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
|
||||
@@ -20,7 +20,3 @@ export class UmbDocumentTypeItemStore extends UmbItemStoreBase<UmbDocumentTypeIt
|
||||
super(host, UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT.toString());
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT = new UmbContextToken<UmbDocumentTypeItemStore>(
|
||||
'UmbDocumentTypeItemStore',
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { UmbDocumentTypeItemRepository } from './document-type-item.repository.js';
|
||||
export { UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS, UMB_DOCUMENT_TYPE_ITEM_STORE_ALIAS } from './manifests.js';
|
||||
export { UmbDocumentTypeItemRepository } from './document-type-item.repository.js';
|
||||
export * from './document-type-item-store.context-token.js';
|
||||
export * from './types.js';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
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';
|
||||
|
||||
@@ -7,7 +9,26 @@ 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(items.map((m) => m.unique));
|
||||
|
||||
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,22 @@
|
||||
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';
|
||||
|
||||
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 +30,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 +39,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 +63,17 @@ 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(items.map((item) => item.unique));
|
||||
|
||||
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 +192,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 +203,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 +231,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';
|
||||
|
||||
@@ -14,7 +14,14 @@ export const manifests: Array<ManifestTypes> = [
|
||||
icon: 'icon-autofill',
|
||||
group: 'common',
|
||||
settings: {
|
||||
properties: [],
|
||||
properties: [
|
||||
{
|
||||
alias: 'placeholder',
|
||||
label: 'Placeholder text',
|
||||
description: 'Enter the text to be displayed when the value is empty',
|
||||
propertyEditorUiAlias: 'Umb.PropertyEditorUi.TextBox',
|
||||
},
|
||||
],
|
||||
defaultData: [
|
||||
{
|
||||
alias: 'step',
|
||||
|
||||
@@ -18,11 +18,15 @@ export class UmbPropertyEditorUINumberElement extends UmbLitElement implements U
|
||||
@state()
|
||||
private _step?: number;
|
||||
|
||||
@state()
|
||||
private _placeholder?: string;
|
||||
|
||||
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
|
||||
if (!config) return;
|
||||
this._min = this.#parseInt(config.getValueByAlias('min'));
|
||||
this._max = this.#parseInt(config.getValueByAlias('max'));
|
||||
this._step = this.#parseInt(config.getValueByAlias('step'));
|
||||
this._placeholder = config.getValueByAlias('placeholder');
|
||||
}
|
||||
|
||||
#parseInt(input: unknown): number | undefined {
|
||||
@@ -42,7 +46,8 @@ export class UmbPropertyEditorUINumberElement extends UmbLitElement implements U
|
||||
min=${ifDefined(this._min)}
|
||||
max=${ifDefined(this._max)}
|
||||
step=${ifDefined(this._step)}
|
||||
.value=${this.value ?? 0}
|
||||
placeholder=${ifDefined(this._placeholder)}
|
||||
.value=${this.value ?? (this._placeholder ? undefined : 0)}
|
||||
@input=${this.#onInput}>
|
||||
</uui-input>
|
||||
`;
|
||||
|
||||
@@ -40,12 +40,15 @@ export class UmbUserWorkspaceAccessElement extends UmbLitElement {
|
||||
#renderDocumentStartNodes() {
|
||||
return html` <b><umb-localize key="sections_content">Content</umb-localize></b>
|
||||
<umb-user-document-start-node
|
||||
.uniques=${this._user?.documentStartNodeUniques || []}></umb-user-document-start-node>`;
|
||||
.uniques=${this._user?.documentStartNodeUniques.map((reference) => reference.unique) ||
|
||||
[]}></umb-user-document-start-node>`;
|
||||
}
|
||||
|
||||
#renderMediaStartNodes() {
|
||||
return html` <b><umb-localize key="sections_media">Media</umb-localize></b>
|
||||
<umb-user-media-start-node .uniques=${this._user?.mediaStartNodeUniques || []}></umb-user-media-start-node>`;
|
||||
<umb-user-media-start-node
|
||||
.uniques=${this._user?.mediaStartNodeUniques.map((reference) => reference.unique) ||
|
||||
[]}></umb-user-media-start-node>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
|
||||
@@ -130,7 +130,7 @@ export class UmbUserWorkspaceAssignAccessElement extends UmbLitElement {
|
||||
description="${this.localize.term('user_groupsHelp')}">
|
||||
<umb-user-group-input
|
||||
slot="editor"
|
||||
.selection=${this._userGroupUniques}
|
||||
.selection=${this._userGroupUniques.map((reference) => reference.unique)}
|
||||
@change=${this.#onUserGroupsChange}></umb-user-group-input>
|
||||
</umb-property-layout>`;
|
||||
}
|
||||
@@ -152,7 +152,7 @@ export class UmbUserWorkspaceAssignAccessElement extends UmbLitElement {
|
||||
? html`
|
||||
<umb-input-document
|
||||
slot="editor"
|
||||
.selection=${this._documentStartNodeUniques}
|
||||
.selection=${this._documentStartNodeUniques.map((reference) => reference.unique)}
|
||||
@change=${this.#onDocumentStartNodeChange}></umb-input-document>
|
||||
`
|
||||
: nothing}
|
||||
@@ -177,7 +177,7 @@ export class UmbUserWorkspaceAssignAccessElement extends UmbLitElement {
|
||||
? html`
|
||||
<umb-input-media
|
||||
slot="editor"
|
||||
.selection=${this._mediaStartNodeUniques}
|
||||
.selection=${this._mediaStartNodeUniques.map((reference) => reference.unique)}
|
||||
@change=${this.#onMediaStartNodeChange}></umb-input-media>
|
||||
`
|
||||
: nothing}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
{
|
||||
"navigationFallback": {
|
||||
"rewrite": "/index.html",
|
||||
"exclude": [
|
||||
"*.{jpg,jpeg,gif,png,svg}",
|
||||
"/assets/*"
|
||||
]
|
||||
},
|
||||
"trailingSlash": "never"
|
||||
}
|
||||
"navigationFallback": {
|
||||
"rewrite": "/index.html",
|
||||
"exclude": ["*.{jpg,jpeg,gif,png,svg,css}", "/assets/*"]
|
||||
},
|
||||
"trailingSlash": "never"
|
||||
}
|
||||
|
||||
@@ -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