Feature/document using content api (#513)

* out comment temprorary for development

* initial prep

* comment

* change port number for dev generate api

* generated new apis

* document-type repository

* rename to item

* rename to document

* use ItemType

* not name detail for full models

* correct token

* imports

* correct imports

* use DocumentTypeTreeItem

* mega type adapt commit

* move DataType import

* rename document detail store

* add document

* new mock data

* partialUpdateFrozenArray

* imports

* document context work

* document and document type in context

* data-type stores + data-sources

* byKey document + data-type

* remove type

* comment

* data-type repository

* data-type context adjustments

* data-type data observable

* fix model import

* use ContentTypeCompositionType

* correct mock data

* .

* split treedata / data

* correct mock endpoints

* new models

* update model usage

* correct models

* imports

* correct models

* update model imports

* update models

* update type

* update docuemnt models

* use DocumentModel

* DocumentModel

* import lit/decorators.js

* lint fixes

* remove console.logs

* new up router slot

* set hasChildren to false

* fix hos argument + add todo to revisit this code

* add todo

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
This commit is contained in:
Niels Lyngsø
2023-02-13 13:33:31 +01:00
committed by GitHub
parent be979867ce
commit 818bcb35bb
105 changed files with 3347 additions and 1272 deletions

View File

@@ -11,8 +11,11 @@ import { html } from 'lit-html';
import { initialize, mswDecorator } from 'msw-storybook-addon';
import { setCustomElements } from '@storybook/web-components';
import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN, UmbDataTypeDetailStore } from '../src/backoffice/settings/data-types/data-type.detail.store';
import { UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN, UmbDocumentTypeDetailStore } from '../src/backoffice/documents/document-types/document-type.detail.store';
import { UmbDataTypeStore } from '../src/backoffice/settings/data-types/data-type.store';
import {
UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN,
UmbDocumentTypeStore,
} from '../src/backoffice/documents/document-types/document-type.detail.store';
import customElementManifests from '../custom-elements.json';
import { UmbIconStore } from '../libs/store/icon/icon.store';
@@ -55,12 +58,19 @@ customElements.define('umb-storybook', UmbStoryBookElement);
const storybookProvider = (story) => html` <umb-storybook>${story()}</umb-storybook> `;
// TODO: Stop using this context provider element. If we need to continue this path, then we should make a new element which just has a create method that can be used to spin up code. This is because our ContextAPIs provide them self. so no need for a provider element. just a element.
const dataTypeStoreProvider = (story) => html`
<umb-context-provider key=${UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString()} .create=${host => new UmbDataTypeDetailStore(host)}>${story()}</umb-context-provider>
<umb-context-provider
key=${UMB_DATA_TYPE_STORE_CONTEXT_TOKEN.toString()}
.create=${(host) => new UmbDataTypeStore(host)}
>${story()}</umb-context-provider
>
`;
const documentTypeStoreProvider = (story) => html`
<umb-context-provider key=${UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString()} .create=${host => new UmbDocumentTypeDetailStore(host)}
<umb-context-provider
key=${UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString()}
.create=${(host) => new UmbDocumentTypeStore(host)}
>${story()}</umb-context-provider
>
`;

View File

@@ -12,7 +12,15 @@ export type { AssemblyModel } from './models/AssemblyModel';
export { CallingConventionsModel } from './models/CallingConventionsModel';
export type { ConsentLevelModel } from './models/ConsentLevelModel';
export type { ConstructorInfoModel } from './models/ConstructorInfoModel';
export { ContentStateModel } from './models/ContentStateModel';
export type { ContentTreeItemModel } from './models/ContentTreeItemModel';
export type { ContentTypeCleanupModel } from './models/ContentTypeCleanupModel';
export type { ContentTypeCompositionModel } from './models/ContentTypeCompositionModel';
export { ContentTypeCompositionTypeModel } from './models/ContentTypeCompositionTypeModel';
export type { ContentTypeSortModel } from './models/ContentTypeSortModel';
export type { ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel } from './models/ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel';
export type { ContentUrlInfoModel } from './models/ContentUrlInfoModel';
export type { ContentViewModelBaseDocumentPropertyDocumentVariantModel } from './models/ContentViewModelBaseDocumentPropertyDocumentVariantModel';
export type { CreatedResultModel } from './models/CreatedResultModel';
export type { CultureModel } from './models/CultureModel';
export type { CustomAttributeDataModel } from './models/CustomAttributeDataModel';
@@ -20,9 +28,11 @@ export type { CustomAttributeNamedArgumentModel } from './models/CustomAttribute
export type { CustomAttributeTypedArgumentModel } from './models/CustomAttributeTypedArgumentModel';
export type { DatabaseInstallModel } from './models/DatabaseInstallModel';
export type { DatabaseSettingsModel } from './models/DatabaseSettingsModel';
export type { DataTypeCopyModel } from './models/DataTypeCopyModel';
export type { DataTypeCreateModel } from './models/DataTypeCreateModel';
export type { DataTypeModel } from './models/DataTypeModel';
export type { DataTypeModelBaseModel } from './models/DataTypeModelBaseModel';
export type { DataTypeMoveModel } from './models/DataTypeMoveModel';
export type { DataTypePropertyModel } from './models/DataTypePropertyModel';
export type { DataTypePropertyReferenceModel } from './models/DataTypePropertyReferenceModel';
export type { DataTypeReferenceModel } from './models/DataTypeReferenceModel';
@@ -36,12 +46,19 @@ export type { DictionaryItemModelBaseModel } from './models/DictionaryItemModelB
export type { DictionaryItemsImportModel } from './models/DictionaryItemsImportModel';
export type { DictionaryItemTranslationModel } from './models/DictionaryItemTranslationModel';
export type { DictionaryItemUpdateModel } from './models/DictionaryItemUpdateModel';
export type { DictionaryMoveModel } from './models/DictionaryMoveModel';
export type { DictionaryOverviewModel } from './models/DictionaryOverviewModel';
export type { DictionaryUploadModel } from './models/DictionaryUploadModel';
export { DirectionModel } from './models/DirectionModel';
export type { DocumentBlueprintTreeItemModel } from './models/DocumentBlueprintTreeItemModel';
export type { DocumentModel } from './models/DocumentModel';
export type { DocumentPropertyModel } from './models/DocumentPropertyModel';
export type { DocumentTreeItemModel } from './models/DocumentTreeItemModel';
export type { DocumentTypeModel } from './models/DocumentTypeModel';
export type { DocumentTypePropertyTypeContainerModel } from './models/DocumentTypePropertyTypeContainerModel';
export type { DocumentTypePropertyTypeModel } from './models/DocumentTypePropertyTypeModel';
export type { DocumentTypeTreeItemModel } from './models/DocumentTypeTreeItemModel';
export type { DocumentVariantModel } from './models/DocumentVariantModel';
export type { EncoderFallbackModel } from './models/EncoderFallbackModel';
export type { EncodingModel } from './models/EncodingModel';
export type { EntityTreeItemModel } from './models/EntityTreeItemModel';
@@ -138,6 +155,11 @@ export type { ProblemDetailsModel } from './models/ProblemDetailsModel';
export type { ProfilingStatusModel } from './models/ProfilingStatusModel';
export { PropertyAttributesModel } from './models/PropertyAttributesModel';
export type { PropertyInfoModel } from './models/PropertyInfoModel';
export type { PropertyTypeAppearanceModel } from './models/PropertyTypeAppearanceModel';
export type { PropertyTypeContainerViewModelBaseModel } from './models/PropertyTypeContainerViewModelBaseModel';
export type { PropertyTypeValidationModel } from './models/PropertyTypeValidationModel';
export type { PropertyTypeViewModelBaseModel } from './models/PropertyTypeViewModelBaseModel';
export type { PropertyViewModelBaseModel } from './models/PropertyViewModelBaseModel';
export type { ReadOnlySpanByteModel } from './models/ReadOnlySpanByteModel';
export type { RecycleBinItemModel } from './models/RecycleBinItemModel';
export { RedirectStatusModel } from './models/RedirectStatusModel';
@@ -182,6 +204,7 @@ export type { UmbracoJsonTypeInfoResolverModel } from './models/UmbracoJsonTypeI
export type { UpgradeSettingsModel } from './models/UpgradeSettingsModel';
export type { UserInstallModel } from './models/UserInstallModel';
export type { UserSettingsModel } from './models/UserSettingsModel';
export type { VariantViewModelBaseModel } from './models/VariantViewModelBaseModel';
export type { VersionModel } from './models/VersionModel';
export { CultureResource } from './services/CultureResource';

View File

@@ -0,0 +1,10 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export enum ContentStateModel {
NOT_CREATED = 'NotCreated',
DRAFT = 'Draft',
PUBLISHED = 'Published',
PUBLISHED_PENDING_CHANGES = 'PublishedPendingChanges',
}

View File

@@ -0,0 +1,10 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type ContentTypeCleanupModel = {
preventCleanup?: boolean;
keepAllVersionsNewerThanDays?: number | null;
keepLatestVersionPerDayForDays?: number | null;
};

View File

@@ -0,0 +1,11 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ContentTypeCompositionTypeModel } from './ContentTypeCompositionTypeModel';
export type ContentTypeCompositionModel = {
key?: string;
compositionType?: ContentTypeCompositionTypeModel;
};

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export enum ContentTypeCompositionTypeModel {
COMPOSITION = 'Composition',
INHERITANCE = 'Inheritance',
}

View File

@@ -0,0 +1,9 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type ContentTypeSortModel = {
key?: string;
sortOrder?: number;
};

View File

@@ -0,0 +1,27 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ContentTypeCleanupModel } from './ContentTypeCleanupModel';
import type { ContentTypeCompositionModel } from './ContentTypeCompositionModel';
import type { ContentTypeSortModel } from './ContentTypeSortModel';
import type { DocumentTypePropertyTypeContainerModel } from './DocumentTypePropertyTypeContainerModel';
import type { DocumentTypePropertyTypeModel } from './DocumentTypePropertyTypeModel';
export type ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel = {
key?: string;
alias?: string;
name?: string;
description?: string | null;
icon?: string;
allowedAsRoot?: boolean;
variesByCulture?: boolean;
variesBySegment?: boolean;
isElement?: boolean;
properties?: Array<DocumentTypePropertyTypeModel>;
containers?: Array<DocumentTypePropertyTypeContainerModel>;
allowedContentTypes?: Array<ContentTypeSortModel>;
compositions?: Array<ContentTypeCompositionModel>;
cleanup?: ContentTypeCleanupModel;
};

View File

@@ -0,0 +1,9 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type ContentUrlInfoModel = {
culture?: string | null;
url?: string;
};

View File

@@ -0,0 +1,14 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { DocumentPropertyModel } from './DocumentPropertyModel';
import type { DocumentVariantModel } from './DocumentVariantModel';
export type ContentViewModelBaseDocumentPropertyDocumentVariantModel = {
key?: string;
contentTypeKey?: string;
properties?: Array<DocumentPropertyModel>;
variants?: Array<DocumentVariantModel>;
};

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DataTypeCopyModel = {
targetKey?: string | null;
};

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DataTypeMoveModel = {
targetKey?: string | null;
};

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DictionaryMoveModel = {
targetKey?: string | null;
};

View File

@@ -0,0 +1,12 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ContentUrlInfoModel } from './ContentUrlInfoModel';
import type { ContentViewModelBaseDocumentPropertyDocumentVariantModel } from './ContentViewModelBaseDocumentPropertyDocumentVariantModel';
export type DocumentModel = (ContentViewModelBaseDocumentPropertyDocumentVariantModel & {
urls?: Array<ContentUrlInfoModel>;
templateKey?: string | null;
});

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PropertyViewModelBaseModel } from './PropertyViewModelBaseModel';
export type DocumentPropertyModel = PropertyViewModelBaseModel;

View File

@@ -0,0 +1,11 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel } from './ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel';
export type DocumentTypeModel = (ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel & {
allowedTemplateKeys?: Array<string>;
defaultTemplateKey?: string | null;
});

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeContainerViewModelBaseModel } from './PropertyTypeContainerViewModelBaseModel';
export type DocumentTypePropertyTypeContainerModel = PropertyTypeContainerViewModelBaseModel;

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeViewModelBaseModel } from './PropertyTypeViewModelBaseModel';
export type DocumentTypePropertyTypeModel = PropertyTypeViewModelBaseModel;

View File

@@ -0,0 +1,12 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ContentStateModel } from './ContentStateModel';
import type { VariantViewModelBaseModel } from './VariantViewModelBaseModel';
export type DocumentVariantModel = (VariantViewModelBaseModel & {
state?: ContentStateModel;
publishDate?: string | null;
});

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type PropertyTypeAppearanceModel = {
labelOnTop?: boolean;
};

View File

@@ -0,0 +1,12 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type PropertyTypeContainerViewModelBaseModel = {
key?: string;
parentKey?: string | null;
name?: string | null;
type?: string;
sortOrder?: number;
};

View File

@@ -0,0 +1,11 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type PropertyTypeValidationModel = {
mandatory?: boolean;
mandatoryMessage?: string | null;
regEx?: string | null;
regExMessage?: string | null;
};

View File

@@ -0,0 +1,18 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeAppearanceModel } from './PropertyTypeAppearanceModel';
import type { PropertyTypeValidationModel } from './PropertyTypeValidationModel';
export type PropertyTypeViewModelBaseModel = {
key?: string;
containerKey?: string | null;
alias?: string;
name?: string;
description?: string | null;
dataTypeKey?: string;
validation?: PropertyTypeValidationModel;
appearance?: PropertyTypeAppearanceModel;
};

View File

@@ -0,0 +1,11 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type PropertyViewModelBaseModel = {
culture?: string | null;
segment?: string | null;
alias?: string;
value?: any;
};

View File

@@ -0,0 +1,12 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type VariantViewModelBaseModel = {
culture?: string | null;
segment?: string | null;
name?: string;
createDate?: string;
updateDate?: string;
};

View File

@@ -1,8 +1,10 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { DataTypeCopyModel } from '../models/DataTypeCopyModel';
import type { DataTypeCreateModel } from '../models/DataTypeCreateModel';
import type { DataTypeModel } from '../models/DataTypeModel';
import type { DataTypeMoveModel } from '../models/DataTypeMoveModel';
import type { DataTypeReferenceModel } from '../models/DataTypeReferenceModel';
import type { DataTypeUpdateModel } from '../models/DataTypeUpdateModel';
import type { DocumentTypeTreeItemModel } from '../models/DocumentTypeTreeItemModel';
@@ -108,6 +110,56 @@ export class DataTypeResource {
});
}
/**
* @returns any Created
* @throws ApiError
*/
public static postDataTypeByKeyCopy({
key,
requestBody,
}: {
key: string,
requestBody?: DataTypeCopyModel,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/umbraco/management/api/v1/data-type/{key}/copy',
path: {
'key': key,
},
body: requestBody,
mediaType: 'application/json',
errors: {
404: `Not Found`,
},
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static postDataTypeByKeyMove({
key,
requestBody,
}: {
key: string,
requestBody?: DataTypeMoveModel,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/umbraco/management/api/v1/data-type/{key}/move',
path: {
'key': key,
},
body: requestBody,
mediaType: 'application/json',
errors: {
404: `Not Found`,
},
});
}
/**
* @returns any Success
* @throws ApiError

View File

@@ -5,6 +5,7 @@ import type { DictionaryImportModel } from '../models/DictionaryImportModel';
import type { DictionaryItemCreateModel } from '../models/DictionaryItemCreateModel';
import type { DictionaryItemModel } from '../models/DictionaryItemModel';
import type { DictionaryItemUpdateModel } from '../models/DictionaryItemUpdateModel';
import type { DictionaryMoveModel } from '../models/DictionaryMoveModel';
import type { DictionaryUploadModel } from '../models/DictionaryUploadModel';
import type { DocumentTypeTreeItemModel } from '../models/DocumentTypeTreeItemModel';
import type { FolderTreeItemModel } from '../models/FolderTreeItemModel';
@@ -155,6 +156,32 @@ export class DictionaryResource {
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static postDictionaryByKeyMove({
key,
requestBody,
}: {
key: string,
requestBody?: DictionaryMoveModel,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/umbraco/management/api/v1/dictionary/{key}/move',
path: {
'key': key,
},
body: requestBody,
mediaType: 'application/json',
errors: {
400: `Bad Request`,
404: `Not Found`,
},
});
}
/**
* @returns any Created
* @throws ApiError

View File

@@ -1,6 +1,7 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { DocumentModel } from '../models/DocumentModel';
import type { DocumentTreeItemModel } from '../models/DocumentTreeItemModel';
import type { PagedDocumentTreeItemModel } from '../models/PagedDocumentTreeItemModel';
import type { PagedRecycleBinItemModel } from '../models/PagedRecycleBinItemModel';
@@ -11,6 +12,27 @@ import { request as __request } from '../core/request';
export class DocumentResource {
/**
* @returns any Success
* @throws ApiError
*/
public static getDocumentByKey({
key,
}: {
key: string,
}): CancelablePromise<DocumentModel> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/document/{key}',
path: {
'key': key,
},
errors: {
404: `Not Found`,
},
});
}
/**
* @returns PagedRecycleBinItemModel Success
* @throws ApiError

View File

@@ -1,6 +1,7 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { DocumentTypeModel } from '../models/DocumentTypeModel';
import type { DocumentTypeTreeItemModel } from '../models/DocumentTypeTreeItemModel';
import type { PagedDocumentTypeTreeItemModel } from '../models/PagedDocumentTypeTreeItemModel';
@@ -10,6 +11,27 @@ import { request as __request } from '../core/request';
export class DocumentTypeResource {
/**
* @returns any Success
* @throws ApiError
*/
public static getDocumentTypeByKey({
key,
}: {
key: string,
}): CancelablePromise<DocumentTypeModel> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/document-type/{key}',
path: {
'key': key,
},
errors: {
404: `Not Found`,
},
});
}
/**
* @returns PagedDocumentTypeTreeItemModel Success
* @throws ApiError

View File

@@ -1,14 +1,9 @@
import { Observable } from 'rxjs';
import {
ContentTreeItemModel,
DocumentTreeItemModel,
DocumentTypeTreeItemModel,
EntityTreeItemModel,
FolderTreeItemModel,
PagedEntityTreeItemModel,
ProblemDetailsModel,
} from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
// Extension Manifests
export * from '@umbraco-cms/extensions-registry';
@@ -67,25 +62,20 @@ export interface UserGroupDetails extends UserGroupEntity {
permissions: Array<string>;
}
/*
// Data Types
export interface DataTypeDetails extends FolderTreeItemModel {
key: string; // TODO: Remove this when the backend is fixed
propertyEditorModelAlias: string | null;
propertyEditorUIAlias: string | null;
data: Array<DataTypePropertyData>;
propertyEditorAlias: string | null;
propertyEditorUiAlias: string | null;
data: Array<DataTypeProperty>;
}
export interface DataTypePropertyData {
export interface DataTypeProperty {
alias: string;
value: any;
}
// Document Types
export interface DocumentTypeDetails extends DocumentTypeTreeItemModel {
key: string; // TODO: Remove this when the backend is fixed
alias: string;
properties: [];
}
*/
// TODO: Make sure Entity Type/interface.
export interface MemberTypeDetails extends EntityTreeItemModel {
@@ -107,16 +97,6 @@ export interface ContentPropertyData {
value: any;
}
// Documents
export interface DocumentDetails extends DocumentTreeItemModel {
key: string; // TODO: Remove this when the backend is fixed
isTrashed: boolean; // TODO: remove only temp part of refactor
properties: Array<ContentProperty>;
data: Array<ContentPropertyData>;
variants: Array<any>; // TODO: define variant data
//layout?: any; // TODO: define layout type - make it non-optional
}
// Media
export interface MediaDetails extends ContentTreeItemModel {
key: string; // TODO: Remove this when the backend is fixed

View File

@@ -5,5 +5,6 @@ export * from './string-state';
export * from './deep-state';
export * from './array-state';
export * from './object-state';
export * from './create-observable-part.method'
export * from './append-to-frozen-array.method'
export * from './create-observable-part.method';
export * from './append-to-frozen-array.method';
export * from './partial-update-frozen-array.method';

View File

@@ -0,0 +1,24 @@
/**
* @export
* @method partialUpdateFrozenArray
* @param {Observable<T>} source - RxJS Subject to use for this Observable.
* @param {(mappable: T) => R} mappingFunction - Method to return the part for this Observable to return.
* @param {(previousResult: R, currentResult: R) => boolean} [memoizationFunction] - Method to Compare if the data has changed. Should return true when data is different.
* @description - Creates a RxJS Observable from RxJS Subject.
* @example <caption>Example append new entry for a ArrayState or a part of DeepState/ObjectState it which is an array. Where the key is unique and the item will be updated if matched with existing.</caption>
* const partialEntry = {value: 'myValue'};
* const newDataSet = partialUpdateFrozenArray(mySubject.getValue(), partialEntry, x => x.key === 'myKey');
* mySubject.next(newDataSet);
*/
export function partialUpdateFrozenArray<T>(
data: T[],
partialEntry: Partial<T>,
findMethod: (entry: T) => boolean
): T[] {
const unFrozenDataSet = [...data];
const indexToReplace = unFrozenDataSet.findIndex((x) => findMethod(x));
if (indexToReplace !== -1) {
unFrozenDataSet[indexToReplace] = { ...unFrozenDataSet[indexToReplace], ...partialEntry };
}
return unFrozenDataSet;
}

View File

@@ -6,7 +6,7 @@ export interface UmbDetailRepository<DetailType> {
error?: ProblemDetailsModel;
}>;
requestDetails(key: string): Promise<{
requestByKey(key: string): Promise<{
data?: DetailType;
error?: ProblemDetailsModel;
}>;

View File

@@ -1,7 +1,6 @@
import 'router-slot';
import { IRoute, RouterSlot } from 'router-slot';
import { LitElement, PropertyValueMap } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { IRoute, RouterSlot } from 'router-slot';
import { UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/router';
/**
@@ -40,12 +39,11 @@ export class UmbRouterSlotElement extends LitElement {
constructor() {
super();
this.#router = document.createElement('router-slot');
this.#router = new RouterSlot();
// Note: I decided not to use the local changestate event, because it is not fired when the route is changed from any router-slot. And for now I wanted to keep it local.
//this.#router.addEventListener('changestate', this._onNavigationChanged);
}
connectedCallback() {
super.connectedCallback();
if (this.#listening === false) {
@@ -60,7 +58,6 @@ export class UmbRouterSlotElement extends LitElement {
this.#listening = false;
}
protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
super.firstUpdated(_changedProperties);
this._routerPath = this.#router.constructAbsolutePath('') || '';

View File

@@ -42,7 +42,7 @@
"format": "prettier 'src/**/*.ts'",
"format:fix": "npm run format -- --write",
"generate:api": "openapi --input https://raw.githubusercontent.com/umbraco/Umbraco-CMS/v13/dev/src/Umbraco.Cms.Api.Management/OpenApi.json --output libs/backend-api/src --postfix Resource --useOptions",
"generate:api-dev": "openapi --input http://localhost:9000/umbraco/swagger/v1/swagger.json --output libs/backend-api/src --postfix Resource --useOptions",
"generate:api-dev": "openapi --input http://localhost:11000/umbraco/swagger/v1/swagger.json --output libs/backend-api/src --postfix Resource --useOptions",
"storybook": "npm run wc-analyze && start-storybook -p 6006",
"storybook:build": "npm run wc-analyze && build-storybook",
"build-storybook": "npm run wc-analyze && build-storybook",

View File

@@ -15,11 +15,11 @@ import {
UmbBackofficeContext,
UMB_BACKOFFICE_CONTEXT_TOKEN,
} from './shared/components/backoffice-frame/backoffice.context';
import { UmbDocumentTypeDetailStore } from './documents/document-types/document-type.detail.store';
import { UmbDocumentTypeTreeStore } from './documents/document-types/document-type.tree.store';
import { UmbDocumentTypeStore } from './documents/document-types/repository/document-type.store';
import { UmbDocumentTypeTreeStore } from './documents/document-types/repository/document-type.tree.store';
import { UmbMediaTypeDetailStore } from './media/media-types/media-type.detail.store';
import { UmbMediaTypeTreeStore } from './media/media-types/media-type.tree.store';
import { UmbDocumentDetailStore } from './documents/documents/repository/document.detail.store';
import { UmbDocumentStore } from './documents/documents/repository/document.store';
import { UmbDocumentTreeStore } from './documents/documents/repository/document.tree.store';
import { UmbMediaDetailStore } from './media/media/repository/media.detail.store';
import { UmbMediaTreeStore } from './media/media/repository/media.tree.store';
@@ -33,7 +33,7 @@ import { UmbDictionaryDetailStore } from './translation/dictionary/dictionary.de
import { UmbDictionaryTreeStore } from './translation/dictionary/dictionary.tree.store';
import { UmbDocumentBlueprintDetailStore } from './documents/document-blueprints/document-blueprint.detail.store';
import { UmbDocumentBlueprintTreeStore } from './documents/document-blueprints/document-blueprint.tree.store';
import { UmbDataTypeDetailStore } from './settings/data-types/data-type.detail.store';
import { UmbDataTypeStore } from './settings/data-types/repository/data-type.store';
import { UmbDataTypeTreeStore } from './settings/data-types/tree/data-type.tree.store';
import { UmbTemplateTreeStore } from './templating/templates/tree/data/template.tree.store';
import { UmbTemplateDetailStore } from './templating/templates/workspace/data/template.detail.store';
@@ -82,16 +82,16 @@ export class UmbBackofficeElement extends UmbLitElement {
// TODO: find a way this is possible outside this element. It needs to be possible to register stores in extensions
this.provideContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, new UmbCurrentUserStore());
new UmbDocumentDetailStore(this);
new UmbDocumentStore(this);
new UmbDocumentTreeStore(this);
new UmbMediaDetailStore(this);
new UmbMediaTreeStore(this);
new UmbDataTypeDetailStore(this);
new UmbDataTypeStore(this);
new UmbDataTypeTreeStore(this);
new UmbUserStore(this);
new UmbMediaTypeDetailStore(this);
new UmbMediaTypeTreeStore(this);
new UmbDocumentTypeDetailStore(this);
new UmbDocumentTypeStore(this);
new UmbDocumentTypeTreeStore(this);
new UmbMemberTypeDetailStore(this);
new UmbMemberTypeTreeStore(this);

View File

@@ -1,108 +0,0 @@
import type { DocumentTypeDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentTypeDetailStore>(
'UmbDocumentTypeDetailStore'
);
/**
* @export
* @class UmbDocumentTypeDetailStore
* @extends {UmbStoreBase}
* @description - Details Data Store for Document Types
*/
export class UmbDocumentTypeDetailStore extends UmbStoreBase implements UmbEntityDetailStore<DocumentTypeDetails> {
#data = new ArrayState<DocumentTypeDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
super(host, UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString());
}
getScaffold(entityType: string, parentKey: string | null) {
return {
key: '',
name: '',
icon: '',
type: '',
hasChildren: false,
parentKey: '',
alias: '',
properties: [],
} as DocumentTypeDetails;
}
/**
* @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable.
* @param {string} key
* @return {*} {(Observable<DocumentTypeDetails | undefined>)}
* @memberof UmbDocumentTypesStore
*/
getByKey(key: string) {
// TODO: use backend cli when available.
fetch(`/umbraco/management/api/v1/document-type/details/${key}`)
.then((res) => res.json())
.then((data) => {
this.#data.append(data);
});
return this.#data.getObservablePart((documentTypes) =>
documentTypes.find((documentType) => documentType.key === key)
);
}
// TODO: make sure UI somehow can follow the status of this action.
/**
* @description - Save a Data Type.
* @param {Array<DocumentTypeDetails>} documentTypes
* @memberof UmbDocumentTypesStore
* @return {*} {Promise<void>}
*/
save(data: DocumentTypeDetails[]) {
// fetch from server and update store
// TODO: use Fetcher API.
let body: string;
try {
body = JSON.stringify(data);
} catch (error) {
console.error(error);
return Promise.reject();
}
// TODO: use backend cli when available.
return fetch('/umbraco/management/api/v1/document-type/save', {
method: 'POST',
body: body,
headers: {
'Content-Type': 'application/json',
},
})
.then((res) => res.json())
.then((data: Array<DocumentTypeDetails>) => {
this.#data.append(data);
});
}
// TODO: How can we avoid having this in both stores?
/**
* @description - Delete a Data Type.
* @param {string[]} keys
* @memberof UmbDocumentTypesStore
* @return {*} {Promise<void>}
*/
async delete(keys: string[]) {
// TODO: use backend cli when available.
await fetch('/umbraco/backoffice/document-type/delete', {
method: 'POST',
body: JSON.stringify(keys),
headers: {
'Content-Type': 'application/json',
},
});
this.#data.remove(keys);
}
}

View File

@@ -1,91 +0,0 @@
import { DocumentTypeResource, DocumentTreeItemModel } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import type { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentTypeTreeStore>(
'UmbDocumentTypeTreeStore'
);
/**
* @export
* @class UmbDocumentTypeTreeStore
* @extends {UmbStoreBase}
* @description - Tree Data Store for Data Types
*/
export class UmbDocumentTypeTreeStore extends UmbStoreBase {
#data = new ArrayState<DocumentTreeItemModel>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
super(host, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN.toString());
}
// TODO: How can we avoid having this in both stores?
/**
* @description - Delete a Data Type.
* @param {string[]} keys
* @memberof UmbDocumentTypesStore
* @return {*} {Promise<void>}
*/
async delete(keys: string[]) {
// TODO: use backend cli when available.
await fetch('/umbraco/backoffice/document-type/delete', {
method: 'POST',
body: JSON.stringify(keys),
headers: {
'Content-Type': 'application/json',
},
});
this.#data.remove(keys);
}
getTreeRoot() {
tryExecuteAndNotify(this._host, DocumentTypeResource.getTreeDocumentTypeRoot({})).then(({ data }) => {
if (data) {
// TODO: how do we handle if an item has been removed during this session(like in another tab or by another user)?
this.#data.append(data.items);
}
});
// TODO: remove ignore when we know how to handle trashed items.
return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === null));
}
getTreeItemChildren(key: string) {
tryExecuteAndNotify(
this._host,
DocumentTypeResource.getTreeDocumentTypeChildren({
parentKey: key,
})
).then(({ data }) => {
if (data) {
// TODO: how do we handle if an item has been removed during this session(like in another tab or by another user)?
this.#data.append(data.items);
}
});
// TODO: remove ignore when we know how to handle trashed items.
return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === key));
}
getTreeItems(keys: Array<string>) {
if (keys?.length > 0) {
tryExecuteAndNotify(
this._host,
DocumentTypeResource.getTreeDocumentTypeItem({
key: keys,
})
).then(({ data }) => {
if (data) {
// TODO: how do we handle if an item has been removed during this session(like in another tab or by another user)?
this.#data.append(data);
}
});
}
return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? '')));
}
}

View File

@@ -0,0 +1,222 @@
import type { RepositoryTreeDataSource } from '../../../../../libs/repository/repository-tree-data-source.interface';
import { DocumentTypeTreeServerDataSource } from './sources/document-type.tree.server.data';
import { UmbDocumentTypeServerDataSource } from './sources/document-type.server.data';
import { UmbDocumentTypeTreeStore, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN } from './document-type.tree.store';
import { UmbDocumentTypeStore, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from './document-type.store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UmbContextConsumerController } from '@umbraco-cms/context-api';
import { ProblemDetailsModel, DocumentTypeModel } from '@umbraco-cms/backend-api';
import type { UmbTreeRepository } from 'libs/repository/tree-repository.interface';
import { UmbDetailRepository } from '@umbraco-cms/repository';
import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification';
type ItemType = DocumentTypeModel;
// Move to documentation / JSdoc
/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
// element -> context -> repository -> (store) -> data source
// All methods should be async and return a promise. Some methods might return an observable as part of the promise response.
export class UmbDocumentTypeRepository implements UmbTreeRepository, UmbDetailRepository<ItemType> {
#init!: Promise<unknown>;
#host: UmbControllerHostInterface;
#treeSource: RepositoryTreeDataSource;
#treeStore?: UmbDocumentTypeTreeStore;
#detailDataSource: UmbDocumentTypeServerDataSource;
#detailStore?: UmbDocumentTypeStore;
#notificationService?: UmbNotificationService;
constructor(host: UmbControllerHostInterface) {
this.#host = host;
// TODO: figure out how spin up get the correct data source
this.#treeSource = new DocumentTypeTreeServerDataSource(this.#host);
this.#detailDataSource = new UmbDocumentTypeServerDataSource(this.#host);
this.#init = Promise.all([
new UmbContextConsumerController(this.#host, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => {
this.#treeStore = instance;
}),
new UmbContextConsumerController(this.#host, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN, (instance) => {
this.#detailStore = instance;
}),
new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (instance) => {
this.#notificationService = instance;
}),
]);
}
// TODO: Trash
// TODO: Move
async requestRootTreeItems() {
await this.#init;
const { data, error } = await this.#treeSource.getRootItems();
if (data) {
this.#treeStore?.appendItems(data.items);
}
return { data, error, asObservable: () => this.#treeStore!.rootItems };
}
async requestTreeItemsOf(parentKey: string | null) {
await this.#init;
if (!parentKey) {
const error: ProblemDetailsModel = { title: 'Parent key is missing' };
return { data: undefined, error };
}
const { data, error } = await this.#treeSource.getChildrenOf(parentKey);
if (data) {
this.#treeStore?.appendItems(data.items);
}
return { data, error, asObservable: () => this.#treeStore!.childrenOf(parentKey) };
}
async requestTreeItems(keys: Array<string>) {
await this.#init;
if (!keys) {
const error: ProblemDetailsModel = { title: 'Keys are missing' };
return { data: undefined, error };
}
const { data, error } = await this.#treeSource.getItems(keys);
return { data, error, asObservable: () => this.#treeStore!.items(keys) };
}
async rootTreeItems() {
await this.#init;
return this.#treeStore!.rootItems;
}
async treeItemsOf(parentKey: string | null) {
await this.#init;
return this.#treeStore!.childrenOf(parentKey);
}
async treeItems(keys: Array<string>) {
await this.#init;
return this.#treeStore!.items(keys);
}
// DETAILS:
async createDetailsScaffold(parentKey: string | null) {
await this.#init;
if (!parentKey) {
throw new Error('Parent key is missing');
}
return this.#detailDataSource.createScaffold(parentKey);
}
async requestByKey(key: string) {
await this.#init;
// TODO: should we show a notification if the key is missing?
// Investigate what is best for Acceptance testing, cause in that perspective a thrown error might be the best choice?
if (!key) {
const error: ProblemDetailsModel = { title: 'Key is missing' };
return { error };
}
const { data, error } = await this.#detailDataSource.get(key);
if (data) {
this.#detailStore?.append(data);
}
return { data, error };
}
async byKey(key: string) {
await this.#init;
return this.#detailStore!.byKey(key);
}
// Could potentially be general methods:
async createDetail(template: ItemType) {
await this.#init;
if (!template || !template.key) {
throw new Error('Template is missing');
}
const { error } = await this.#detailDataSource.insert(template);
if (!error) {
const notification = { data: { message: `Document created` } };
this.#notificationService?.peek('positive', notification);
}
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
this.#detailStore?.append(template);
// TODO: Update tree store with the new item? or ask tree to request the new item?
return { error };
}
async saveDetail(item: ItemType) {
await this.#init;
if (!item || !item.key) {
throw new Error('Document-Type is missing');
}
const { error } = await this.#detailDataSource.update(item);
if (!error) {
const notification = { data: { message: `Document saved` } };
this.#notificationService?.peek('positive', notification);
}
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
// Consider notify a workspace if a template is updated in the store while someone is editing it.
this.#detailStore?.append(item);
this.#treeStore?.updateItem(item.key, { name: item.name });
// TODO: would be nice to align the stores on methods/methodNames.
return { error };
}
// General:
async delete(key: string) {
await this.#init;
if (!key) {
throw new Error('Document key is missing');
}
const { error } = await this.#detailDataSource.delete(key);
if (!error) {
const notification = { data: { message: `Document deleted` } };
this.#notificationService?.peek('positive', notification);
}
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server.
// Consider notify a workspace if a template is deleted from the store while someone is editing it.
this.#detailStore?.remove([key]);
this.#treeStore?.removeItem(key);
// TODO: would be nice to align the stores on methods/methodNames.
return { error };
}
}

View File

@@ -0,0 +1,55 @@
import { DocumentTypeModel } from '@umbraco-cms/backend-api';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
/**
* @export
* @class UmbDocumentTypeStore
* @extends {UmbStoreBase}
* @description - Data Store for Document Types
*/
export class UmbDocumentTypeStore extends UmbStoreBase {
#data = new ArrayState<DocumentTypeModel>([], (x) => x.key);
/**
* Creates an instance of UmbDocumentTypeStore.
* @param {UmbControllerHostInterface} host
* @memberof UmbDocumentTypeStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UmbDocumentTypeStore.name);
}
/**
* Append a document-type to the store
* @param {DocumentTypeModel} document
* @memberof UmbDocumentTypeStore
*/
append(document: DocumentTypeModel) {
this.#data.append([document]);
}
/**
* Append a document-type to the store
* @param {DocumentTypeModel} document
* @memberof UmbDocumentTypeStore
*/
byKey(key: DocumentTypeModel['key']) {
return this.#data.getObservablePart((x) => x.find((y) => y.key === key));
}
/**
* Removes document-types in the store with the given uniques
* @param {string[]} uniques
* @memberof UmbDocumentTypeStore
*/
remove(uniques: Array<DocumentTypeModel['key']>) {
this.#data.remove(uniques);
}
}
export const UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentTypeStore>(
UmbDocumentTypeStore.name
);

View File

@@ -0,0 +1,93 @@
import { EntityTreeItemModel } from '@umbraco-cms/backend-api';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
/**
* @export
* @class UmbDocumentTypeTreeStore
* @extends {UmbStoreBase}
* @description - Tree Data Store for Document-Types
*/
// TODO: consider if tree store could be turned into a general EntityTreeStore class?
export class UmbDocumentTypeTreeStore extends UmbStoreBase {
#data = new ArrayState<EntityTreeItemModel>([], (x) => x.key);
/**
* Creates an instance of UmbDocumentTypeTreeStore.
* @param {UmbControllerHostInterface} host
* @memberof UmbDocumentTypeTreeStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN.toString());
}
/**
* Appends items to the store
* @param {Array<EntityTreeItemModel>} items
* @memberof UmbDocumentTypeTreeStore
*/
appendItems(items: Array<EntityTreeItemModel>) {
this.#data.append(items);
}
/**
* Updates an item in the store
* @param {string} key
* @param {Partial<EntityTreeItemModel>} data
* @memberof UmbDocumentTypeTreeStore
*/
updateItem(key: string, data: Partial<EntityTreeItemModel>) {
const entries = this.#data.getValue();
const entry = entries.find((entry) => entry.key === key);
if (entry) {
this.#data.appendOne({ ...entry, ...data });
}
}
/**
* Removes an item from the store
* @param {string} key
* @memberof UmbDocumentTypeTreeStore
*/
removeItem(key: string) {
const entries = this.#data.getValue();
const entry = entries.find((entry) => entry.key === key);
if (entry) {
this.#data.remove([key]);
}
}
/**
* An observable to observe the root items
* @memberof UmbDocumentTypeTreeStore
*/
rootItems = this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === null));
/**
* Returns an observable to observe the children of a given parent
* @param {(string | null)} parentKey
* @return {*}
* @memberof UmbDocumentTypeTreeStore
*/
childrenOf(parentKey: string | null) {
return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === parentKey));
}
/**
* Returns an observable to observe the items with the given keys
* @param {Array<string>} keys
* @return {*}
* @memberof UmbDocumentTypeTreeStore
*/
items(keys: Array<string>) {
return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? '')));
}
}
export const UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentTypeTreeStore>(
UmbDocumentTypeTreeStore.name
);

View File

@@ -0,0 +1,13 @@
import { UmbDocumentTypeRepository } from './document-type.repository';
import { ManifestRepository } from 'libs/extensions-registry/repository.models';
export const DOCUMENT_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DocumentTypes';
const repository: ManifestRepository = {
type: 'repository',
alias: DOCUMENT_TYPE_REPOSITORY_ALIAS,
name: 'Document Types Repository',
class: UmbDocumentTypeRepository,
};
export const manifests = [repository];

View File

@@ -0,0 +1,179 @@
import { RepositoryDetailDataSource } from '@umbraco-cms/repository';
import { DocumentTypeResource, ProblemDetailsModel, DocumentTypeModel } from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
/**
* A data source for the Document Type that fetches data from the server
* @export
* @class UmbDocumentTypeServerDataSource
* @implements {RepositoryDetailDataSource}
*/
export class UmbDocumentTypeServerDataSource implements RepositoryDetailDataSource<DocumentTypeModel> {
#host: UmbControllerHostInterface;
/**
* Creates an instance of UmbDocumentServerDataSource.
* @param {UmbControllerHostInterface} host
* @memberof UmbDocumentServerDataSource
*/
constructor(host: UmbControllerHostInterface) {
this.#host = host;
}
/**
* Fetches a Document with the given key from the server
* @param {string} key
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async get(key: string) {
if (!key) {
const error: ProblemDetailsModel = { title: 'Key is missing' };
return { error };
}
return tryExecuteAndNotify(
this.#host,
DocumentTypeResource.getDocumentTypeByKey({
key,
})
);
}
/**
* Creates a new Document scaffold
* @param {(string | null)} parentKey
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async createScaffold(parentKey: string | null) {
const data: DocumentTypeModel = {
properties: [],
};
return { data };
}
/**
* Inserts a new Document on the server
* @param {Document} document
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async insert(document: DocumentTypeModel) {
if (!document.key) {
//const error: ProblemDetails = { title: 'Document key is missing' };
return Promise.reject();
}
//const payload = { key: document.key, requestBody: document };
let body: string;
try {
body = JSON.stringify(document);
} catch (error) {
console.error(error);
return Promise.reject();
}
//return tryExecuteAndNotify(this.#host, DocumentTypeResource.postDocument(payload));
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DocumentTypeModel>(
this.#host,
fetch('/umbraco/management/api/v1/document/save', {
method: 'POST',
body: body,
headers: {
'Content-Type': 'application/json',
},
}) as any
);
}
/**
* Updates a Document on the server
* @param {Document} Document
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
// TODO: Error mistake in this:
async update(document: DocumentTypeModel) {
if (!document.key) {
const error: ProblemDetailsModel = { title: 'Document key is missing' };
return { error };
}
//const payload = { key: document.key, requestBody: document };
let body: string;
try {
body = JSON.stringify(document);
} catch (error) {
const myError: ProblemDetailsModel = { title: 'JSON could not parse' };
return { error: myError };
}
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DocumentTypeModel>(
this.#host,
fetch('/umbraco/management/api/v1/document-type/save', {
method: 'POST',
body: body,
headers: {
'Content-Type': 'application/json',
},
}) as any
);
}
/**
* Trash a Document on the server
* @param {Document} Document
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async trash(key: string) {
if (!key) {
const error: ProblemDetailsModel = { title: 'Key is missing' };
return { error };
}
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DocumentTypeModel>(
this.#host,
fetch('/umbraco/management/api/v1/document-type/trash', {
method: 'POST',
body: JSON.stringify([key]),
headers: {
'Content-Type': 'application/json',
},
}) as any
);
}
/**
* Deletes a Template on the server
* @param {string} key
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
// TODO: Error mistake in this:
async delete(key: string) {
if (!key) {
const error: ProblemDetailsModel = { title: 'Key is missing' };
return { error };
}
// TODO: use resources when end point is ready:
return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document-type/trash', {
method: 'POST',
body: JSON.stringify([key]),
headers: {
'Content-Type': 'application/json',
},
})
);
}
}

View File

@@ -0,0 +1,101 @@
import type { RepositoryTreeDataSource } from '../../../../../../libs/repository/repository-tree-data-source.interface';
import { ProblemDetailsModel, DocumentTypeResource } from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
/**
* A data source for the Document tree that fetches data from the server
* @export
* @class DocumentTreeServerDataSource
* @implements {DocumentTreeDataSource}
*/
export class DocumentTypeTreeServerDataSource implements RepositoryTreeDataSource {
#host: UmbControllerHostInterface;
// TODO: how do we handle trashed items?
async trashItems(keys: Array<string>) {
// TODO: use backend cli when available.
return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document-type/trash', {
method: 'POST',
body: JSON.stringify(keys),
headers: {
'Content-Type': 'application/json',
},
})
);
}
async moveItems(keys: Array<string>, destination: string) {
// TODO: use backend cli when available.
return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document-type/move', {
method: 'POST',
body: JSON.stringify({ keys, destination }),
headers: {
'Content-Type': 'application/json',
},
})
);
}
/**
* Creates an instance of DocumentTreeServerDataSource.
* @param {UmbControllerHostInterface} host
* @memberof DocumentTreeServerDataSource
*/
constructor(host: UmbControllerHostInterface) {
this.#host = host;
}
/**
* Fetches the root items for the tree from the server
* @return {*}
* @memberof DocumentTreeServerDataSource
*/
async getRootItems() {
return tryExecuteAndNotify(this.#host, DocumentTypeResource.getTreeDocumentTypeRoot({}));
}
/**
* Fetches the children of a given parent key from the server
* @param {(string | null)} parentKey
* @return {*}
* @memberof DocumentTreeServerDataSource
*/
async getChildrenOf(parentKey: string | null) {
if (!parentKey) {
const error: ProblemDetailsModel = { title: 'Parent key is missing' };
return { error };
}
return tryExecuteAndNotify(
this.#host,
DocumentTypeResource.getTreeDocumentTypeChildren({
parentKey,
})
);
}
/**
* Fetches the items for the given keys from the server
* @param {Array<string>} keys
* @return {*}
* @memberof DocumentTreeServerDataSource
*/
async getItems(keys: Array<string>) {
if (keys) {
const error: ProblemDetailsModel = { title: 'Keys are missing' };
return { error };
}
return tryExecuteAndNotify(
this.#host,
DocumentTypeResource.getTreeDocumentTypeItem({
key: keys,
})
);
}
}

View File

@@ -1,4 +1,4 @@
import { UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../document-type.detail.store';
import { UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from '../repository/document-type.store';
import type { ManifestTree, ManifestTreeItemAction } from '@umbraco-cms/models';
const tree: ManifestTree = {
@@ -6,7 +6,7 @@ const tree: ManifestTree = {
alias: 'Umb.Tree.DocumentTypes',
name: 'Document Types Tree',
meta: {
storeAlias: UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString(),
storeAlias: UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN.toString(),
},
};

View File

@@ -1,22 +1,23 @@
import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller';
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface';
import { UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../document-type.detail.store';
import type { DocumentTypeDetails } from '@umbraco-cms/models';
import { UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from '../repository/document-type.store';
import type { DocumentTypeModel } from '@umbraco-cms/backend-api';
export class UmbWorkspaceDocumentTypeContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface<DocumentTypeDetails | undefined> {
#manager = new UmbEntityWorkspaceManager(this._host, 'document-type', UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN);
export class UmbWorkspaceDocumentTypeContext
extends UmbWorkspaceContext
implements UmbWorkspaceEntityContextInterface<DocumentTypeModel | undefined>
{
#manager = new UmbEntityWorkspaceManager(this._host, 'document-type', UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN);
public readonly data = this.#manager.state.asObservable();
public readonly name = this.#manager.state.getObservablePart((state) => state?.name);
setName(name: string) {
this.#manager.state.update({name: name})
this.#manager.state.update({ name: name });
}
setIcon(icon: string) {
this.#manager.state.update({icon: icon})
this.#manager.state.update({ icon: icon });
}
getEntityType = this.#manager.getEntityType;
getUnique = this.#manager.getEntityKey;

View File

@@ -1,13 +1,12 @@
import { UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { distinctUntilChanged } from 'rxjs';
import { customElement, state } from 'lit/decorators.js';
import type { UmbWorkspaceEntityElement } from '../../../shared/components/workspace/workspace-entity-element.interface';
import { UmbWorkspaceDocumentTypeContext } from './document-type-workspace.context';
import type { DocumentTypeDetails } from '@umbraco-cms/models';
import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from 'src/core/modal';
import type { DocumentTypeModel } from '@umbraco-cms/backend-api';
import { UmbLitElement } from '@umbraco-cms/element';
import type { UmbWorkspaceEntityElement } from 'src/backoffice/shared/components/workspace/workspace-entity-element.interface';
import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/modal';
@customElement('umb-document-type-workspace')
export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements UmbWorkspaceEntityElement {
@@ -46,11 +45,10 @@ export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements Um
name: 'umb:document-dashed-line',
};
private _workspaceContext: UmbWorkspaceDocumentTypeContext = new UmbWorkspaceDocumentTypeContext(this);
@state()
private _documentType?: DocumentTypeDetails;
private _documentType?: DocumentTypeModel;
private _modalService?: UmbModalService;
@@ -61,9 +59,9 @@ export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements Um
this._modalService = instance;
});
this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (data) => {
// TODO: make method to identify if data is of type DocumentTypeDetails
this._documentType = data as DocumentTypeDetails;
this.observe(this._workspaceContext.data, (data) => {
// TODO: make method to identify if data is of type DocumentType
this._documentType = data as DocumentType;
});
}

View File

@@ -2,7 +2,7 @@ import './document-type-workspace.element';
import { Meta, Story } from '@storybook/web-components';
import { html } from 'lit-html';
import type { UmbDocumentTypeWorkspaceElement } from './document-type-workspace.element';
import { data } from 'src/core/mocks/data/document-type.data';
import { treeData } from 'src/core/mocks/data/document-type.data';
export default {
title: 'Workspaces/Document Type',
@@ -11,5 +11,5 @@ export default {
} as Meta;
export const AAAOverview: Story<UmbDocumentTypeWorkspaceElement> = () =>
html` <umb-document-type-workspace id="${data[0].key}"></umb-document-type-workspace>`;
html` <umb-document-type-workspace id="${treeData[0].key}"></umb-document-type-workspace>`;
AAAOverview.storyName = 'Overview';

View File

@@ -1,17 +1,16 @@
import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, state } from 'lit/decorators.js';
import { distinctUntilChanged } from 'rxjs';
import { UmbWorkspaceDocumentTypeContext } from '../../document-type-workspace.context';
import { UmbLitElement } from '@umbraco-cms/element';
import type { DocumentTypeDetails } from '@umbraco-cms/models';
import type { DocumentTypeModel } from '@umbraco-cms/backend-api';
@customElement('umb-workspace-view-document-type-design')
export class UmbWorkspaceViewDocumentTypeDesignElement extends UmbLitElement {
static styles = [UUITextStyles, css``];
@state()
_documentType?: DocumentTypeDetails | null;
_documentType?: DocumentTypeModel;
private _workspaceContext?: UmbWorkspaceDocumentTypeContext;
@@ -28,7 +27,7 @@ export class UmbWorkspaceViewDocumentTypeDesignElement extends UmbLitElement {
private _observeDocumentType() {
if (!this._workspaceContext) return;
this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (documentType) => {
this.observe(this._workspaceContext.data, (documentType) => {
this._documentType = documentType;
});
}

View File

@@ -1,8 +1,8 @@
import { css, html, LitElement, nothing } from 'lit';
import { ifDefined } from 'lit-html/directives/if-defined.js';
import { customElement, property, state } from 'lit/decorators.js';
import { UmbExecutedEvent } from 'src/core/events';
import type { UmbTableColumn, UmbTableItem } from '../../../../../../shared/components/table';
import { UmbExecutedEvent } from 'src/core/events';
// TODO: this could be done more generic, but for now we just need it for the document table
@customElement('umb-document-table-actions-column-layout')

View File

@@ -14,13 +14,12 @@ import {
UmbTableOrderedEvent,
UmbTableSelectedEvent,
} from '../../../../../shared/components/table';
import type { DocumentDetails } from '@umbraco-cms/models';
import { UmbLitElement } from '@umbraco-cms/element';
import { EntityTreeItemModel } from '@umbraco-cms/backend-api';
import { DocumentTreeItemModel, EntityTreeItemModel } from '@umbraco-cms/backend-api';
import './column-layouts/document-table-actions-column-layout.element';
type EntityType = DocumentDetails;
type EntityType = DocumentTreeItemModel;
@customElement('umb-document-table-collection-view')
export class UmbDocumentTableCollectionViewElement extends UmbLitElement {

View File

@@ -1,23 +1,22 @@
import type { RepositoryTreeDataSource } from '../../../../../libs/repository/repository-tree-data-source.interface';
import { DocumentTreeServerDataSource } from './sources/document.tree.server.data';
import { UmbDocumentTreeStore, UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from './document.tree.store';
import { UmbDocumentDetailStore, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from './document.detail.store';
import { UmbDocumentDetailServerDataSource } from './sources/document.detail.server.data';
import { UmbDocumentStore, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from './document.store';
import { UmbDocumentServerDataSource } from './sources/document.server.data';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UmbContextConsumerController } from '@umbraco-cms/context-api';
import { ProblemDetailsModel } from '@umbraco-cms/backend-api';
import { ProblemDetailsModel, DocumentModel } from '@umbraco-cms/backend-api';
import type { UmbTreeRepository } from 'libs/repository/tree-repository.interface';
import { UmbDetailRepository } from '@umbraco-cms/repository';
import type { DocumentDetails } from '@umbraco-cms/models';
import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification';
type ItemDetailType = DocumentDetails;
type ItemType = DocumentModel;
// Move to documentation / JSdoc
/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
// element -> context -> repository -> (store) -> data source
// All methods should be async and return a promise. Some methods might return an observable as part of the promise response.
export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailRepository<ItemDetailType> {
export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailRepository<ItemType> {
#init!: Promise<unknown>;
#host: UmbControllerHostInterface;
@@ -25,8 +24,8 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
#treeSource: RepositoryTreeDataSource;
#treeStore?: UmbDocumentTreeStore;
#detailDataSource: UmbDocumentDetailServerDataSource;
#detailStore?: UmbDocumentDetailStore;
#detailDataSource: UmbDocumentServerDataSource;
#detailStore?: UmbDocumentStore;
#notificationService?: UmbNotificationService;
@@ -35,7 +34,7 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// TODO: figure out how spin up get the correct data source
this.#treeSource = new DocumentTreeServerDataSource(this.#host);
this.#detailDataSource = new UmbDocumentDetailServerDataSource(this.#host);
this.#detailDataSource = new UmbDocumentServerDataSource(this.#host);
this.#init = Promise.all([
new UmbContextConsumerController(this.#host, UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN, (instance) => {
@@ -124,7 +123,7 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
return this.#detailDataSource.createScaffold(parentKey);
}
async requestDetails(key: string) {
async requestByKey(key: string) {
await this.#init;
// TODO: should we show a notification if the key is missing?
@@ -144,14 +143,14 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// Could potentially be general methods:
async createDetail(template: ItemDetailType) {
async createDetail(item: ItemType) {
await this.#init;
if (!template || !template.key) {
throw new Error('Template is missing');
if (!item || !item.key) {
throw new Error('Document is missing');
}
const { error } = await this.#detailDataSource.insert(template);
const { error } = await this.#detailDataSource.insert(item);
if (!error) {
const notification = { data: { message: `Document created` } };
@@ -160,20 +159,20 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
this.#detailStore?.append(template);
this.#detailStore?.append(item);
// TODO: Update tree store with the new item? or ask tree to request the new item?
return { error };
}
async saveDetail(document: ItemDetailType) {
async saveDetail(item: ItemType) {
await this.#init;
if (!document || !document.key) {
throw new Error('Template is missing');
if (!item || !item.key) {
throw new Error('Document is missing');
}
const { error } = await this.#detailDataSource.update(document);
const { error } = await this.#detailDataSource.update(item);
if (!error) {
const notification = { data: { message: `Document saved` } };
@@ -182,10 +181,9 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
// Consider notify a workspace if a template is updated in the store while someone is editing it.
this.#detailStore?.append(document);
this.#treeStore?.updateItem(document.key, { name: document.name });
// Consider notify a workspace if a document is updated in the store while someone is editing it.
this.#detailStore?.append(item);
//this.#treeStore?.updateItem(item.key, { name: item.name });// Port data to tree store.
// TODO: would be nice to align the stores on methods/methodNames.
return { error };
@@ -209,7 +207,7 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server.
// Consider notify a workspace if a template is deleted from the store while someone is editing it.
// Consider notify a workspace if a document is deleted from the store while someone is editing it.
this.#detailStore?.remove([key]);
this.#treeStore?.removeItem(key);
// TODO: would be nice to align the stores on methods/methodNames.

View File

@@ -1,4 +1,4 @@
import type { DocumentDetails } from '@umbraco-cms/models';
import { DocumentModel } from '@umbraco-cms/backend-api';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
@@ -10,9 +10,8 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller';
* @extends {UmbStoreBase}
* @description - Data Store for Template Details
*/
export class UmbDocumentDetailStore extends UmbStoreBase {
#data = new ArrayState<DocumentDetails>([], (x) => x.key);
export class UmbDocumentStore extends UmbStoreBase {
#data = new ArrayState<DocumentModel>([], (x) => x.key);
/**
* Creates an instance of UmbDocumentDetailStore.
@@ -20,7 +19,7 @@ export class UmbDocumentDetailStore extends UmbStoreBase {
* @memberof UmbDocumentDetailStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UmbDocumentDetailStore.name);
super(host, UmbDocumentStore.name);
}
/**
@@ -28,20 +27,27 @@ export class UmbDocumentDetailStore extends UmbStoreBase {
* @param {DocumentDetails} document
* @memberof UmbDocumentDetailStore
*/
append(document: DocumentDetails) {
append(document: DocumentModel) {
this.#data.append([document]);
}
/**
* Append a document to the store
* @param {DocumentModel} document
* @memberof UmbDocumentStore
*/
byKey(key: DocumentModel['key']) {
return this.#data.getObservablePart((x) => x.find((y) => y.key === key));
}
/**
* Removes documents in the store with the given uniques
* @param {string[]} uniques
* @memberof UmbDocumentDetailStore
*/
remove(uniques: string[]) {
remove(uniques: Array<DocumentModel['key']>) {
this.#data.remove(uniques);
}
}
export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentDetailStore>(
UmbDocumentDetailStore.name
);
export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentStore>(UmbDocumentStore.name);

View File

@@ -1,22 +1,21 @@
import { RepositoryDetailDataSource } from '@umbraco-cms/repository';
import { ProblemDetailsModel } from '@umbraco-cms/backend-api';
import { DocumentResource, ProblemDetailsModel, DocumentModel } from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import type { DataSourceResponse, DocumentDetails } from '@umbraco-cms/models';
/**
* A data source for the Template detail that fetches data from the server
* A data source for the Document that fetches data from the server
* @export
* @class UmbTemplateDetailServerDataSource
* @implements {TemplateDetailDataSource}
* @class UmbDocumentServerDataSource
* @implements {RepositoryDetailDataSource}
*/
export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSource<DocumentDetails> {
export class UmbDocumentServerDataSource implements RepositoryDetailDataSource<DocumentModel> {
#host: UmbControllerHostInterface;
/**
* Creates an instance of UmbDocumentDetailServerDataSource.
* Creates an instance of UmbDocumentServerDataSource.
* @param {UmbControllerHostInterface} host
* @memberof UmbDocumentDetailServerDataSource
* @memberof UmbDocumentServerDataSource
*/
constructor(host: UmbControllerHostInterface) {
this.#host = host;
@@ -26,7 +25,7 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Fetches a Document with the given key from the server
* @param {string} key
* @return {*}
* @memberof UmbDocumentDetailServerDataSource
* @memberof UmbDocumentServerDataSource
*/
async get(key: string) {
if (!key) {
@@ -36,10 +35,9 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return tryExecuteAndNotify(
this.#host,
// TODO: use backend cli when available.
fetch(`/umbraco/management/api/v1/document/details/${key}`)
.then((res) => res.json())
.then((res) => res[0] || undefined)
DocumentResource.getDocumentByKey({
key,
})
);
}
@@ -47,37 +45,13 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Creates a new Document scaffold
* @param {(string | null)} parentKey
* @return {*}
* @memberof UmbDocumentDetailServerDataSource
* @memberof UmbDocumentServerDataSource
*/
async createScaffold(parentKey: string | null) {
const data: DocumentDetails = {
key: '',
name: '',
icon: '',
type: '',
hasChildren: false,
parentKey: parentKey ?? '',
isTrashed: false,
properties: [
{
alias: '',
label: '',
description: '',
dataTypeKey: '',
},
],
data: [
{
alias: '',
value: '',
},
],
variants: [
{
name: '',
},
],
} as DocumentDetails;
const data: DocumentModel = {
properties: [],
variants: [],
};
return { data };
}
@@ -86,9 +60,9 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Inserts a new Document on the server
* @param {Document} document
* @return {*}
* @memberof UmbDocumentDetailServerDataSource
* @memberof UmbDocumentServerDataSource
*/
async insert(document: DocumentDetails) {
async insert(document: DocumentModel) {
if (!document.key) {
//const error: ProblemDetails = { title: 'Document key is missing' };
return Promise.reject();
@@ -104,7 +78,8 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return Promise.reject();
}
//return tryExecuteAndNotify(this.#host, DocumentResource.postDocument(payload));
return tryExecuteAndNotify<DocumentDetails>(
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DocumentModel>(
this.#host,
fetch('/umbraco/management/api/v1/document/save', {
method: 'POST',
@@ -120,10 +95,10 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Updates a Document on the server
* @param {Document} Document
* @return {*}
* @memberof UmbDocumentDetailServerDataSource
* @memberof UmbDocumentServerDataSource
*/
// TODO: Error mistake in this:
async update(document: DocumentDetails) {
async update(document: DocumentModel) {
if (!document.key) {
const error: ProblemDetailsModel = { title: 'Document key is missing' };
return { error };
@@ -139,7 +114,8 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return { error: myError };
}
return tryExecuteAndNotify<DocumentDetails>(
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DocumentModel>(
this.#host,
fetch('/umbraco/management/api/v1/document/save', {
method: 'POST',
@@ -155,7 +131,7 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Trash a Document on the server
* @param {Document} Document
* @return {*}
* @memberof UmbDocumentDetailServerDataSource
* @memberof UmbDocumentServerDataSource
*/
async trash(key: string) {
if (!key) {
@@ -163,7 +139,8 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return { error };
}
return tryExecuteAndNotify<DocumentDetails>(
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DocumentModel>(
this.#host,
fetch('/umbraco/management/api/v1/document/trash', {
method: 'POST',
@@ -176,10 +153,10 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
}
/**
* Deletes a Template on the server
* Deletes a Document on the server
* @param {string} key
* @return {*}
* @memberof UmbTemplateDetailServerDataSource
* @memberof UmbDocumentServerDataSource
*/
// TODO: Error mistake in this:
async delete(key: string) {
@@ -188,6 +165,7 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return { error };
}
// TODO: use resources when end point is ready:
return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document/trash', {

View File

@@ -1,29 +1,103 @@
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
import { UmbDocumentRepository } from '../repository/document.repository';
import type { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface';
import type { DocumentDetails } from '@umbraco-cms/models';
import { appendToFrozenArray, ObjectState } from '@umbraco-cms/observable-api';
import { UmbDocumentTypeRepository } from '../../document-types/repository/document-type.repository';
import type { DocumentModel, DocumentTypeModel } from '@umbraco-cms/backend-api';
import {
partialUpdateFrozenArray,
ObjectState,
ArrayState,
UmbObserverController,
appendToFrozenArray,
} from '@umbraco-cms/observable-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
// TODO: should this contex be called DocumentDraft instead of workspace? or should the draft be part of this?
// TODO: should this context be called DocumentDraft instead of workspace? or should the draft be part of this?
type EntityType = DocumentDetails;
type EntityType = DocumentModel;
export class UmbDocumentWorkspaceContext
extends UmbWorkspaceContext
implements UmbWorkspaceEntityContextInterface<EntityType | undefined>
{
#isNew = false;
#host: UmbControllerHostInterface;
#templateDetailRepo: UmbDocumentRepository;
#documentRepository: UmbDocumentRepository;
#documentTypeRepository: UmbDocumentTypeRepository;
//#dataTypeRepository: UmbDataTypeRepository;
#data = new ObjectState<EntityType | undefined>(undefined);
data = this.#data.asObservable();
name = this.#data.getObservablePart((data) => data?.name);
documentTypeKey = this.#data.getObservablePart((data) => data?.contentTypeKey);
#documentTypes = new ArrayState<DocumentTypeModel>([], (x) => x.key);
documentTypes = this.#documentTypes.asObservable();
constructor(host: UmbControllerHostInterface) {
super(host);
this.#host = host;
this.#templateDetailRepo = new UmbDocumentRepository(this.#host);
this.#documentRepository = new UmbDocumentRepository(this.#host);
this.#documentTypeRepository = new UmbDocumentTypeRepository(this.#host);
//this.#dataTypeRepository = new UmbDataTypeRepository(this.#host);
new UmbObserverController(this._host, this.documentTypeKey, (key) => this.loadDocumentType(key));
}
async load(entityKey: string) {
const { data } = await this.#documentRepository.requestByKey(entityKey);
if (data) {
this.#isNew = false;
this.#data.next(data);
}
}
async createScaffold(parentKey: string | null) {
const { data } = await this.#documentRepository.createDetailsScaffold(parentKey);
if (!data) return;
this.#isNew = true;
this.#data.next(data);
}
async loadDocumentType(key?: string) {
if (!key) return;
const { data } = await this.#documentTypeRepository.requestByKey(key);
if (!data) return;
// Load inherited and composed types:
await data?.compositions?.forEach(async (composition) => {
if (composition.key) {
this.loadDocumentType(composition.key);
}
});
new UmbObserverController(this._host, await this.#documentTypeRepository.byKey(key), (data) => {
if (data) {
this.#documentTypes.appendOne(data);
this.loadDataTypeOfDocumentType(data);
}
});
}
async loadDataTypeOfDocumentType(documentType?: DocumentTypeModel) {
if (!documentType) return;
// Load inherited and composed types:
await documentType?.properties?.forEach(async (property) => {
if (property.dataTypeKey) {
this.loadDataType(property.dataTypeKey);
}
});
}
async loadDataType(key?: string) {
if (!key) return;
//const { data } = await this.#dataTypeRepository.requestDetails(key);
/*new UmbObserverController(this._host, await this.#documentTypeRepository.byKey(key), (data) => {
if (data) {
this.#documentTypes.appendOne(data);
}
});*/
}
getData() {
@@ -44,8 +118,14 @@ export class UmbDocumentWorkspaceContext
return 'document';
}
setName(name: string) {
this.#data.update({ name });
setName(name: string, culture?: string | null, segment?: string | null) {
const variants = this.#data.getValue()?.variants || [];
const newVariants = partialUpdateFrozenArray(
variants,
{ name },
(v) => v.culture == culture && v.segment == segment
);
this.#data.update({ variants: newVariants });
}
/*
getEntityType = this.#manager.getEntityType;
@@ -68,48 +148,43 @@ export class UmbDocumentWorkspaceContext
this.#draft.next(data)
})
}
*/
*/
propertiesOf(culture: string | null, segment: string | null) {
return this.#data.getObservablePart((data) =>
data?.properties?.filter((p) => (culture === p.culture || null) && (segment === p.segment || null))
);
}
propertyStructure() {
// TODO: handle composition of document types.
return this.#documentTypes.getObservablePart((data) => data[0]?.properties);
}
setPropertyValue(alias: string, value: unknown) {
const entry = { alias: alias, value: value };
const currentData = this.#data.value;
if (currentData) {
const newDataSet = appendToFrozenArray(currentData.data, entry, (x) => x.alias);
this.#data.update({ data: newDataSet });
// TODO: make a partial update method for array of data, (idea/concept, use if this case is getting common)
const newDataSet = appendToFrozenArray(currentData.properties || [], entry, (x) => x.alias);
this.#data.update({ properties: newDataSet });
}
}
async load(entityKey: string) {
const { data } = await this.#templateDetailRepo.requestDetails(entityKey);
if (data) {
this.#isNew = false;
this.#data.next(data);
}
}
async createScaffold(parentKey: string | null) {
const { data } = await this.#templateDetailRepo.createDetailsScaffold(parentKey);
if (!data) return;
this.#isNew = true;
this.#data.next(data);
}
async save() {
if (!this.#data.value) return;
if (this.#isNew) {
await this.#templateDetailRepo.createDetail(this.#data.value);
await this.#documentRepository.createDetail(this.#data.value);
} else {
await this.#templateDetailRepo.saveDetail(this.#data.value);
await this.#documentRepository.saveDetail(this.#data.value);
}
// If it went well, then its not new anymore?.
this.#isNew = false;
}
async delete(key: string) {
await this.#templateDetailRepo.delete(key);
await this.#documentRepository.delete(key);
}
/*

View File

@@ -1,8 +1,8 @@
import './document-workspace.element';
import { Meta, Story } from '@storybook/web-components';
import { html } from 'lit-html';
import { data as documentNodes } from '../../../../core/mocks/data/document.data';
import type { UmbDocumentWorkspaceElement } from './document-workspace.element';
import { data as documentNodes } from 'src/core/mocks/data/document.data';
export default {
title: 'Workspaces/Document',

View File

@@ -25,8 +25,7 @@ const workspaceViews: Array<ManifestWorkspaceView> = [
type: 'workspaceView',
alias: 'Umb.WorkspaceView.Document.Edit',
name: 'Document Workspace Edit View',
loader: () =>
import('../../../shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element'),
loader: () => import('./views/workspace-view-document-edit.element'),
weight: 200,
meta: {
workspaces: ['Umb.Workspace.Document'],
@@ -52,6 +51,8 @@ const workspaceViews: Array<ManifestWorkspaceView> = [
];
const workspaceViewCollections: Array<ManifestWorkspaceViewCollection> = [
/*
// TODO: Reenable this:
{
type: 'workspaceViewCollection',
alias: 'Umb.WorkspaceView.Document.Collection',
@@ -66,6 +67,7 @@ const workspaceViewCollections: Array<ManifestWorkspaceViewCollection> = [
repositoryAlias: DOCUMENT_REPOSITORY_ALIAS,
},
},
*/
];
const workspaceActions: Array<ManifestWorkspaceAction> = [

View File

@@ -0,0 +1,93 @@
import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { UmbDocumentWorkspaceContext } from '../document-workspace.context';
import { UmbLitElement } from '@umbraco-cms/element';
import { DocumentPropertyModel, DocumentTypePropertyTypeModel } from '@umbraco-cms/backend-api';
@customElement('umb-workspace-view-document-edit')
export class UmbWorkspaceViewDocumentEditElement extends UmbLitElement {
static styles = [
UUITextStyles,
css`
:host {
display: block;
margin: var(--uui-size-layout-1);
}
`,
];
@state()
_propertyData: DocumentPropertyModel[] = [];
@state()
_propertyStructures: DocumentTypePropertyTypeModel[] = [];
private _workspaceContext?: UmbDocumentWorkspaceContext;
constructor() {
super();
// TODO: Figure out how to get the magic string for the workspace context.
this.consumeContext<UmbDocumentWorkspaceContext>('umbWorkspaceContext', (workspaceContext) => {
this._workspaceContext = workspaceContext;
this._observeContent();
});
}
private _observeContent() {
if (!this._workspaceContext) return;
/*
TODO: Property-Context: This observer gets all changes, We need to fix this. it should be simpler.
An idea to optimize this would be for this to only care about layout, meaning to property data should be watched here.
As the properties could handle their own data on their own?
Should use a Observable for example: this._workspaceContext.properties
*/
this.observe(
this._workspaceContext.propertiesOf(null, null),
(properties) => {
this._propertyData = properties || [];
//this._data = content?.data || [];
/*
Maybe we should not give the value(Data), but the umb-content-property should get the context and observe its own data.
This would become a more specific Observer therefor better performance?.. Note to self: Debate with Mads how he sees this perspective.
*/
},
'observeWorkspaceContextData'
);
this.observe(
this._workspaceContext.propertyStructure(),
(propertyStructure) => {
this._propertyStructures = propertyStructure || [];
},
'observeWorkspaceContextData'
);
}
render() {
return html`
<uui-box>
${repeat(
this._propertyStructures,
(property) => property.alias,
(property) =>
html`<umb-content-property
.property=${property}
.value=${this._propertyData.find((x) => x.alias === property.alias)?.value}></umb-content-property> `
)}
</uui-box>
`;
}
}
export default UmbWorkspaceViewDocumentEditElement;
declare global {
interface HTMLElementTagNameMap {
'umb-workspace-view-document-edit': UmbWorkspaceViewDocumentEditElement;
}
}

View File

@@ -1,12 +1,12 @@
import type { DataTypeDetails, MediaTypeDetails } from '@umbraco-cms/models';
import type { MediaTypeDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTypeDetailStore>('UmbMediaTypeDetailStore');
export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTypeDetailStore>(
'UmbMediaTypeDetailStore'
);
/**
* @export
@@ -15,25 +15,20 @@ export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<Umb
* @description - Details Data Store for Media Types
*/
export class UmbMediaTypeDetailStore extends UmbStoreBase implements UmbEntityDetailStore<MediaTypeDetails> {
private _data = new ArrayState<MediaTypeDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
super(host, UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString());
}
getScaffold(entityType: string, parentKey: string | null) {
return {
} as MediaTypeDetails;
return {} as MediaTypeDetails;
}
/**
* @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable.
* @param {string} key
* @return {*} {(Observable<DataTypeDetails | undefined>)}
* @return {*} {(Observable<DataTypeModel | undefined>)}
* @memberof UmbMediaTypesStore
*/
getByKey(key: string) {

View File

@@ -121,7 +121,7 @@ export class UmbMediaRepository implements UmbTreeRepository, UmbDetailRepositor
return this.#detailDataSource.createScaffold(parentKey);
}
async requestDetails(key: string) {
async requestByKey(key: string) {
await this.#init;
// TODO: should we show a notification if the key is missing?

View File

@@ -52,7 +52,7 @@ export class UmbMediaWorkspaceContext
}
async load(entityKey: string) {
const { data } = await this.#detailRepository.requestDetails(entityKey);
const { data } = await this.#detailRepository.requestByKey(entityKey);
if (data) {
this.#isNew = false;
this.#data.next(data);

View File

@@ -1,114 +0,0 @@
import type { DataTypeDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeDetailStore>('UmbDataTypeDetailStore');
/**
* @export
* @class UmbDataTypeDetailStore
* @extends {UmbStoreBase}
* @description - Details Data Store for Data Types
*/
export class UmbDataTypeDetailStore extends UmbStoreBase implements UmbEntityDetailStore<DataTypeDetails> {
#data = new ArrayState<DataTypeDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
super(host, UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString());
}
getScaffold(entityType: string, parentKey: string | null) {
return {
key: '',
name: '',
icon: '',
type: 'data-type',
hasChildren: false,
parentKey: '',
propertyEditorModelAlias: '',
propertyEditorUIAlias: '',
data: [],
} as DataTypeDetails;
}
/**
* @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable.
* @param {string} key
* @return {*} {(Observable<DataTypeDetails | undefined>)}
* @memberof UmbDataTypesStore
*/
getByKey(key: string) {
// TODO: use backend cli when available.
fetch(`/umbraco/backoffice/data-type/details/${key}`)
.then((res) => res.json())
.then((data) => {
this.#data.append(data);
});
return this.#data.getObservablePart((documents) =>
documents.find((document) => document.key === key)
);
}
// TODO: make sure UI somehow can follow the status of this action.
/**
* @description - Save a Data Type.
* @param {Array<DataTypeDetails>} dataTypes
* @memberof UmbDataTypesStore
* @return {*} {Promise<void>}
*/
save(data: DataTypeDetails[]) {
// fetch from server and update store
// TODO: use Fetcher API.
let body: string;
try {
body = JSON.stringify(data);
} catch (error) {
console.error(error);
return Promise.reject();
}
// TODO: use backend cli when available.
return fetch('/umbraco/management/api/v1/data-type/save', {
method: 'POST',
body: body,
headers: {
'Content-Type': 'application/json',
},
})
.then((res) => res.json())
.then((data: Array<DataTypeDetails>) => {
this.#data.append(data);
});
}
// TODO: How can we avoid having this in both stores?
/**
* @description - Delete a Data Type.
* @param {string[]} keys
* @memberof UmbDataTypesStore
* @return {*} {Promise<void>}
*/
async delete(keys: string[]) {
// TODO: use backend cli when available.
await fetch('/umbraco/backoffice/data-type/delete', {
method: 'POST',
body: JSON.stringify(keys),
headers: {
'Content-Type': 'application/json',
},
});
this.#data.remove(keys);
}
}

View File

@@ -0,0 +1,222 @@
import type { RepositoryTreeDataSource } from '../../../../../libs/repository/repository-tree-data-source.interface';
import { UmbDataTypeTreeStore, UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN } from './data-type.tree.store';
import { UmbDataTypeServerDataSource } from './sources/data-type.server.data';
import { UmbDataTypeStore, UMB_DATA_TYPE_STORE_CONTEXT_TOKEN } from './data-type.store';
import { DataTypeTreeServerDataSource } from './sources/data-type.tree.server.data';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UmbContextConsumerController } from '@umbraco-cms/context-api';
import { ProblemDetailsModel, DataTypeModel } from '@umbraco-cms/backend-api';
import type { UmbTreeRepository } from 'libs/repository/tree-repository.interface';
import { UmbDetailRepository } from '@umbraco-cms/repository';
import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification';
type ItemType = DataTypeModel;
// Move to documentation / JSdoc
/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
// element -> context -> repository -> (store) -> data source
// All methods should be async and return a promise. Some methods might return an observable as part of the promise response.
export class UmbDataTypeRepository implements UmbTreeRepository, UmbDetailRepository<ItemType> {
#init!: Promise<unknown>;
#host: UmbControllerHostInterface;
#treeSource: RepositoryTreeDataSource;
#treeStore?: UmbDataTypeTreeStore;
#detailDataSource: UmbDataTypeServerDataSource;
#detailStore?: UmbDataTypeStore;
#notificationService?: UmbNotificationService;
constructor(host: UmbControllerHostInterface) {
this.#host = host;
// TODO: figure out how spin up get the correct data source
this.#treeSource = new DataTypeTreeServerDataSource(this.#host);
this.#detailDataSource = new UmbDataTypeServerDataSource(this.#host);
this.#init = Promise.all([
new UmbContextConsumerController(this.#host, UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => {
this.#treeStore = instance;
}),
new UmbContextConsumerController(this.#host, UMB_DATA_TYPE_STORE_CONTEXT_TOKEN, (instance) => {
this.#detailStore = instance;
}),
new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (instance) => {
this.#notificationService = instance;
}),
]);
}
// TODO: Trash
// TODO: Move
async requestRootTreeItems() {
await this.#init;
const { data, error } = await this.#treeSource.getRootItems();
if (data) {
this.#treeStore?.appendItems(data.items);
}
return { data, error, asObservable: () => this.#treeStore!.rootItems };
}
async requestTreeItemsOf(parentKey: string | null) {
await this.#init;
if (!parentKey) {
const error: ProblemDetailsModel = { title: 'Parent key is missing' };
return { data: undefined, error };
}
const { data, error } = await this.#treeSource.getChildrenOf(parentKey);
if (data) {
this.#treeStore?.appendItems(data.items);
}
return { data, error, asObservable: () => this.#treeStore!.childrenOf(parentKey) };
}
async requestTreeItems(keys: Array<string>) {
await this.#init;
if (!keys) {
const error: ProblemDetailsModel = { title: 'Keys are missing' };
return { data: undefined, error };
}
const { data, error } = await this.#treeSource.getItems(keys);
return { data, error, asObservable: () => this.#treeStore!.items(keys) };
}
async rootTreeItems() {
await this.#init;
return this.#treeStore!.rootItems;
}
async treeItemsOf(parentKey: string | null) {
await this.#init;
return this.#treeStore!.childrenOf(parentKey);
}
async treeItems(keys: Array<string>) {
await this.#init;
return this.#treeStore!.items(keys);
}
// DETAILS:
async createDetailsScaffold(parentKey: string | null) {
await this.#init;
if (!parentKey) {
throw new Error('Parent key is missing');
}
return this.#detailDataSource.createScaffold(parentKey);
}
async requestByKey(key: string) {
await this.#init;
// TODO: should we show a notification if the key is missing?
// Investigate what is best for Acceptance testing, cause in that perspective a thrown error might be the best choice?
if (!key) {
const error: ProblemDetailsModel = { title: 'Key is missing' };
return { error };
}
const { data, error } = await this.#detailDataSource.get(key);
if (data) {
this.#detailStore?.append(data);
}
return { data, error };
}
async byKey(key: string) {
await this.#init;
return this.#detailStore!.byKey(key);
}
// Could potentially be general methods:
async createDetail(template: ItemType) {
await this.#init;
if (!template || !template.key) {
throw new Error('Template is missing');
}
const { error } = await this.#detailDataSource.insert(template);
if (!error) {
const notification = { data: { message: `Document created` } };
this.#notificationService?.peek('positive', notification);
}
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
this.#detailStore?.append(template);
// TODO: Update tree store with the new item? or ask tree to request the new item?
return { error };
}
async saveDetail(item: ItemType) {
await this.#init;
if (!item || !item.key) {
throw new Error('Document-Type is missing');
}
const { error } = await this.#detailDataSource.update(item);
if (!error) {
const notification = { data: { message: `Document saved` } };
this.#notificationService?.peek('positive', notification);
}
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
// Consider notify a workspace if a template is updated in the store while someone is editing it.
this.#detailStore?.append(item);
this.#treeStore?.updateItem(item.key, { name: item.name });
// TODO: would be nice to align the stores on methods/methodNames.
return { error };
}
// General:
async delete(key: string) {
await this.#init;
if (!key) {
throw new Error('Document key is missing');
}
const { error } = await this.#detailDataSource.delete(key);
if (!error) {
const notification = { data: { message: `Document deleted` } };
this.#notificationService?.peek('positive', notification);
}
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server.
// Consider notify a workspace if a template is deleted from the store while someone is editing it.
this.#detailStore?.remove([key]);
this.#treeStore?.removeItem(key);
// TODO: would be nice to align the stores on methods/methodNames.
return { error };
}
}

View File

@@ -0,0 +1,53 @@
import type { DataTypeModel } from '@umbraco-cms/backend-api';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
/**
* @export
* @class UmbDataTypeStore
* @extends {UmbStoreBase}
* @description - Data Store for Template Details
*/
export class UmbDataTypeStore extends UmbStoreBase {
#data = new ArrayState<DataTypeModel>([], (x) => x.key);
/**
* Creates an instance of UmbDataTypeStore.
* @param {UmbControllerHostInterface} host
* @memberof UmbDataTypeStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UmbDataTypeStore.name);
}
/**
* Append a data-type to the store
* @param {DataTypeModel} dataType
* @memberof UmbDataTypeStore
*/
append(dataType: DataTypeModel) {
this.#data.append([dataType]);
}
/**
* Append a data-type to the store
* @param {key} DataTypeModel key.
* @memberof UmbDataTypeStore
*/
byKey(key: DataTypeModel['key']) {
return this.#data.getObservablePart((x) => x.find((y) => y.key === key));
}
/**
* Removes data-types in the store with the given uniques
* @param {string[]} uniques
* @memberof UmbDataTypeStore
*/
remove(uniques: Array<DataTypeModel['key']>) {
this.#data.remove(uniques);
}
}
export const UMB_DATA_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeStore>(UmbDataTypeStore.name);

View File

@@ -0,0 +1,93 @@
import { EntityTreeItemModel } from '@umbraco-cms/backend-api';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
/**
* @export
* @class UmbDataTypeTreeStore
* @extends {UmbStoreBase}
* @description - Tree Data Store for Data-Types
*/
// TODO: consider if tree store could be turned into a general EntityTreeStore class?
export class UmbDataTypeTreeStore extends UmbStoreBase {
#data = new ArrayState<EntityTreeItemModel>([], (x) => x.key);
/**
* Creates an instance of UmbDataTypeTreeStore.
* @param {UmbControllerHostInterface} host
* @memberof UmbDataTypeTreeStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN.toString());
}
/**
* Appends items to the store
* @param {Array<EntityTreeItemModel>} items
* @memberof UmbDataTypeTreeStore
*/
appendItems(items: Array<EntityTreeItemModel>) {
this.#data.append(items);
}
/**
* Updates an item in the store
* @param {string} key
* @param {Partial<EntityTreeItemModel>} data
* @memberof UmbDataTypeTreeStore
*/
updateItem(key: string, data: Partial<EntityTreeItemModel>) {
const entries = this.#data.getValue();
const entry = entries.find((entry) => entry.key === key);
if (entry) {
this.#data.appendOne({ ...entry, ...data });
}
}
/**
* Removes an item from the store
* @param {string} key
* @memberof UmbDataTypeTreeStore
*/
removeItem(key: string) {
const entries = this.#data.getValue();
const entry = entries.find((entry) => entry.key === key);
if (entry) {
this.#data.remove([key]);
}
}
/**
* An observable to observe the root items
* @memberof UmbDataTypeTreeStore
*/
rootItems = this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === null));
/**
* Returns an observable to observe the children of a given parent
* @param {(string | null)} parentKey
* @return {*}
* @memberof UmbDataTypeTreeStore
*/
childrenOf(parentKey: string | null) {
return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === parentKey));
}
/**
* Returns an observable to observe the items with the given keys
* @param {Array<string>} keys
* @return {*}
* @memberof UmbDataTypeTreeStore
*/
items(keys: Array<string>) {
return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? '')));
}
}
export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeTreeStore>(
UmbDataTypeTreeStore.name
);

View File

@@ -0,0 +1,152 @@
import { RepositoryDetailDataSource } from '@umbraco-cms/repository';
import {
ProblemDetailsModel,
DataTypeResource,
DataTypeModel,
DataTypeCreateModel,
DataTypeUpdateModel,
} from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
/**
* A data source for the Data Type that fetches data from the server
* @export
* @class UmbDataTypeServerDataSource
* @implements {RepositoryDetailDataSource}
*/
export class UmbDataTypeServerDataSource implements RepositoryDetailDataSource<DataTypeModel> {
#host: UmbControllerHostInterface;
/**
* Creates an instance of UmbDataTypeServerDataSource.
* @param {UmbControllerHostInterface} host
* @memberof UmbDataTypeServerDataSource
*/
constructor(host: UmbControllerHostInterface) {
this.#host = host;
}
/**
* Fetches a Data Type with the given key from the server
* @param {string} key
* @return {*}
* @memberof UmbDataTypeServerDataSource
*/
async get(key: string) {
if (!key) {
const error: ProblemDetailsModel = { title: 'Key is missing' };
return { error };
}
return tryExecuteAndNotify(
this.#host,
DataTypeResource.getDataTypeByKey({
key,
})
);
}
/**
* Creates a new Data Type scaffold
* @param {(string | null)} parentKey
* @return {*}
* @memberof UmbDataTypeServerDataSource
*/
async createScaffold(parentKey: string | null) {
const data: DataTypeModel = {
parentKey: parentKey,
};
return { data };
}
/**
* Inserts a new Data Type on the server
* @param {Document} dataType
* @return {*}
* @memberof UmbDataTypeServerDataSource
*/
async insert(dataType: DataTypeModel) {
if (!dataType.key) {
const error: ProblemDetailsModel = { title: 'DataType key is missing' };
return { error };
}
const requestBody: DataTypeCreateModel = { ...dataType };
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DataTypeModel>(
this.#host,
DataTypeResource.postDataType({
requestBody,
})
);
}
/**
* Updates a DataType on the server
* @param {DataTypeModel} DataType
* @return {*}
* @memberof UmbDataTypeServerDataSource
*/
// TODO: Error mistake in this:
async update(dataType: DataTypeModel) {
if (!dataType.key) {
const error: ProblemDetailsModel = { title: 'DataType key is missing' };
return { error };
}
const requestBody: DataTypeUpdateModel = { ...dataType };
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DataTypeModel>(
this.#host,
DataTypeResource.putDataTypeByKey({
key: dataType.key,
requestBody,
})
);
}
/**
* Trash a Document on the server
* @param {Document} Document
* @return {*}
* @memberof UmbDataTypeServerDataSource
*/
async trash(key: string) {
if (!key) {
const error: ProblemDetailsModel = { title: 'DataType key is missing' };
return { error };
}
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DataTypeModel>(
this.#host,
DataTypeResource.deleteDataTypeByKey({
key,
})
);
}
/**
* Deletes a Data Type on the server
* @param {string} key
* @return {*}
* @memberof UmbDataTypeServerDataSource
*/
async delete(key: string) {
if (!key) {
const error: ProblemDetailsModel = { title: 'DataType key is missing' };
return { error };
}
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DataTypeModel>(
this.#host,
DataTypeResource.deleteDataTypeByKey({
key,
})
);
}
}

View File

@@ -0,0 +1,105 @@
import type { RepositoryTreeDataSource } from '../../../../../../libs/repository/repository-tree-data-source.interface';
import { ProblemDetailsModel, DataTypeResource } from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
/**
* A data source for the Document tree that fetches data from the server
* @export
* @class DocumentTreeServerDataSource
* @implements {DocumentTreeDataSource}
*/
export class DataTypeTreeServerDataSource implements RepositoryTreeDataSource {
#host: UmbControllerHostInterface;
// TODO: how do we handle trashed items?
async trashItems(keys: Array<string>) {
if (!keys) {
const error: ProblemDetailsModel = { title: 'DataType keys is missing' };
return { error };
}
// TODO: use resources when end point is ready:
/*
return tryExecuteAndNotify<DataType>(
this.#host,
DataTypeResource.deleteDataTypeByKey({
key: keys,
})
);
*/
return Promise.resolve({ error: null, data: null });
}
async moveItems(keys: Array<string>, destination: string) {
// TODO: use backend cli when available.
return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/data-type/move', {
method: 'POST',
body: JSON.stringify({ keys, destination }),
headers: {
'Content-Type': 'application/json',
},
})
);
}
/**
* Creates an instance of DocumentTreeServerDataSource.
* @param {UmbControllerHostInterface} host
* @memberof DocumentTreeServerDataSource
*/
constructor(host: UmbControllerHostInterface) {
this.#host = host;
}
/**
* Fetches the root items for the tree from the server
* @return {*}
* @memberof DocumentTreeServerDataSource
*/
async getRootItems() {
return tryExecuteAndNotify(this.#host, DataTypeResource.getTreeDataTypeRoot({}));
}
/**
* Fetches the children of a given parent key from the server
* @param {(string | null)} parentKey
* @return {*}
* @memberof DocumentTreeServerDataSource
*/
async getChildrenOf(parentKey: string | null) {
if (!parentKey) {
const error: ProblemDetailsModel = { title: 'Parent key is missing' };
return { error };
}
return tryExecuteAndNotify(
this.#host,
DataTypeResource.getTreeDataTypeChildren({
parentKey,
})
);
}
/**
* Fetches the items for the given keys from the server
* @param {Array<string>} keys
* @return {*}
* @memberof DocumentTreeServerDataSource
*/
async getItems(keys: Array<string>) {
if (keys) {
const error: ProblemDetailsModel = { title: 'Keys are missing' };
return { error };
}
return tryExecuteAndNotify(
this.#host,
DataTypeResource.getTreeDataTypeItem({
key: keys,
})
);
}
}

View File

@@ -2,8 +2,6 @@ import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '../../../../../../core/modal';
import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../../../data-type.detail.store';
import type { UmbDataTypeDetailStore } from '../../../data-type.detail.store';
import UmbTreeItemActionElement from '../../../../../shared/components/tree/action/tree-item-action.element';
@customElement('umb-tree-action-data-type-delete')
@@ -11,7 +9,7 @@ export default class UmbTreeActionDataTypeDeleteElement extends UmbTreeItemActio
static styles = [UUITextStyles, css``];
private _modalService?: UmbModalService;
private _dataTypeStore?: UmbDataTypeDetailStore;
//private _dataTypeStore?: UmbDataTypeStore;
connectedCallback(): void {
super.connectedCallback();
@@ -19,10 +17,6 @@ export default class UmbTreeActionDataTypeDeleteElement extends UmbTreeItemActio
this.consumeContext(UMB_MODAL_SERVICE_CONTEXT_TOKEN, (modalService) => {
this._modalService = modalService;
});
this.consumeContext(UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN, (dataTypeStore) => {
this._dataTypeStore = dataTypeStore;
});
}
private _handleLabelClick() {
@@ -34,10 +28,13 @@ export default class UmbTreeActionDataTypeDeleteElement extends UmbTreeItemActio
});
modalHandler?.onClose().then(({ confirmed }: any) => {
//TODO: Generally no one should talk to stores directly.
/*
if (confirmed && this._treeContextMenuService && this._dataTypeStore && this._activeTreeItem) {
this._dataTypeStore?.delete([this._activeTreeItem.key]);
this._treeContextMenuService.close();
}
*/
});
}

View File

@@ -1,51 +1,95 @@
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface';
import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller';
import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../data-type.detail.store';
import type { DataTypeDetails } from '@umbraco-cms/models';
import { appendToFrozenArray } from '@umbraco-cms/observable-api';
import { UmbDataTypeRepository } from '../repository/data-type.repository';
import type { DataTypeModel } from '@umbraco-cms/backend-api';
import { appendToFrozenArray, ObjectState } from '@umbraco-cms/observable-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export class UmbWorkspaceDataTypeContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface<DataTypeDetails | undefined> {
type EntityType = DataTypeModel;
#manager = new UmbEntityWorkspaceManager(this._host, 'data-type', UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN);
export class UmbWorkspaceDataTypeContext
extends UmbWorkspaceContext
implements UmbWorkspaceEntityContextInterface<EntityType | undefined>
{
#isNew = false;
#host: UmbControllerHostInterface;
#dataTypeRepository: UmbDataTypeRepository;
#data = new ObjectState<EntityType | undefined>(undefined);
data = this.#data.asObservable();
name = this.#data.getObservablePart((data) => data?.name);
key = this.#data.getObservablePart((data) => data?.key);
public readonly data = this.#manager.state.asObservable();
public readonly name = this.#manager.state.getObservablePart((state) => state?.name);
setName(name: string) {
this.#manager.state.update({name: name});
constructor(host: UmbControllerHostInterface) {
super(host);
this.#host = host;
this.#dataTypeRepository = new UmbDataTypeRepository(this.#host);
}
setPropertyEditorModelAlias(alias?: string) {
this.#manager.state.update({propertyEditorModelAlias: alias});
}
setPropertyEditorUIAlias(alias?: string) {
this.#manager.state.update({propertyEditorUIAlias: alias});
}
getEntityType = this.#manager.getEntityType;
getUnique = this.#manager.getEntityKey;
getEntityKey = this.#manager.getEntityKey;
getStore = this.#manager.getStore;
getData = this.#manager.getData;
load = this.#manager.load;
create = this.#manager.create;
save = this.#manager.save;
destroy = this.#manager.destroy;
// This could eventually be moved out as well?
setPropertyValue(alias: string, value: unknown) {
const entry = {alias: alias, value: value};
const currentData = this.#manager.getData();
if (currentData) {
const newDataSet = appendToFrozenArray(currentData.data, entry, x => x.alias);
this.#manager.state.update({data: newDataSet});
async load(entityKey: string) {
const { data } = await this.#dataTypeRepository.requestByKey(entityKey);
if (data) {
this.#isNew = false;
this.#data.next(data);
}
}
async createScaffold(parentKey: string | null) {
const { data } = await this.#dataTypeRepository.createDetailsScaffold(parentKey);
if (!data) return;
this.#isNew = true;
this.#data.next(data);
}
getData() {
return this.#data.getValue();
}
getEntityKey() {
return this.getData()?.key || '';
}
getEntityType() {
return 'data-type';
}
setName(name: string) {
this.#data.update({ name });
}
setPropertyEditorAlias(alias?: string) {
this.#data.update({ propertyEditorAlias: alias });
}
setPropertyEditorUiAlias(alias?: string) {
this.#data.update({ propertyEditorUiAlias: alias });
}
// TODO: its not called a property in the model, but we do consider this way in our front-end
setPropertyValue(alias: string, value: unknown) {
const entry = { alias: alias, value: value };
const currentData = this.#data.value;
if (currentData) {
// TODO: make a partial update method for array of data, (idea/concept, use if this case is getting common)
const newDataSet = appendToFrozenArray(currentData.data || [], entry, (x) => x.alias);
this.#data.update({ data: newDataSet });
}
}
async save() {
if (!this.#data.value) return;
if (this.#isNew) {
await this.#dataTypeRepository.createDetail(this.#data.value);
} else {
await this.#dataTypeRepository.saveDetail(this.#data.value);
}
// If it went well, then its not new anymore?.
this.#isNew = false;
}
async delete(key: string) {
await this.#dataTypeRepository.delete(key);
}
public destroy(): void {
this.#data.complete();
}
}

View File

@@ -33,10 +33,11 @@ export class UmbDataTypeWorkspaceElement extends UmbLitElement {
public load(value: string) {
this._workspaceContext?.load(value);
//this._unique = entityKey;
}
public create(parentKey: string | null) {
this._workspaceContext?.create(parentKey);
this._workspaceContext.createScaffold(parentKey);
}
@state()
@@ -45,9 +46,9 @@ export class UmbDataTypeWorkspaceElement extends UmbLitElement {
constructor() {
super();
this.provideContext('umbWorkspaceContext', this._workspaceContext);
this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (dataType) => {
if (dataType && dataType.name !== this._dataTypeName) {
this._dataTypeName = dataType.name ?? '';
this.observe(this._workspaceContext.name, (dataTypeName) => {
if (dataTypeName !== this._dataTypeName) {
this._dataTypeName = dataTypeName ?? '';
}
});
}

View File

@@ -4,7 +4,7 @@ import { customElement, state } from 'lit/decorators.js';
import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '../../../../../../core/modal';
import { UmbWorkspaceDataTypeContext } from '../../data-type-workspace.context';
import { UmbLitElement } from '@umbraco-cms/element';
import type { DataTypeDetails } from '@umbraco-cms/models';
import type { DataTypeModel } from '@umbraco-cms/backend-api';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
import '../../../../../shared/property-editors/shared/property-editor-config/property-editor-config.element';
@@ -23,7 +23,7 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
];
@state()
_dataType?: DataTypeDetails;
_dataType?: DataTypeModel;
@state()
private _propertyEditorUIIcon = '';
@@ -32,10 +32,10 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
private _propertyEditorUIName = '';
@state()
private _propertyEditorUIAlias = '';
private _propertyEditorUiAlias = '';
@state()
private _propertyEditorModelAlias = '';
private _propertyEditorAlias = '';
@state()
private _data: Array<any> = [];
@@ -66,33 +66,33 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
if (!dataType) return;
// TODO: handle if model is not of the type wanted.
this._dataType = dataType as DataTypeDetails;
this._dataType = dataType;
if (this._dataType.propertyEditorUIAlias !== this._propertyEditorUIAlias) {
this._observePropertyEditorUI(this._dataType.propertyEditorUIAlias || undefined);
if (this._dataType.propertyEditorUiAlias !== this._propertyEditorUiAlias) {
this._observePropertyEditorUI(this._dataType.propertyEditorUiAlias || undefined);
}
if (this._dataType.data !== this._data) {
if (this._dataType.data && this._dataType.data !== this._data) {
this._data = this._dataType.data;
}
});
}
private _observePropertyEditorUI(propertyEditorUIAlias?: string) {
if (!propertyEditorUIAlias) return;
private _observePropertyEditorUI(propertyEditorUiAlias?: string) {
if (!propertyEditorUiAlias) return;
this.observe(
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', propertyEditorUIAlias),
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', propertyEditorUiAlias),
(propertyEditorUI) => {
// TODO: show error. We have stored a PropertyEditorUIAlias and can't find the PropertyEditorUI in the registry.
if (!propertyEditorUI) return;
this._propertyEditorUIName = propertyEditorUI?.meta.label ?? propertyEditorUI?.name ?? '';
this._propertyEditorUIAlias = propertyEditorUI?.alias ?? '';
this._propertyEditorUiAlias = propertyEditorUI?.alias ?? '';
this._propertyEditorUIIcon = propertyEditorUI?.meta.icon ?? '';
this._propertyEditorModelAlias = propertyEditorUI?.meta.propertyEditorModel ?? '';
this._propertyEditorAlias = propertyEditorUI?.meta.propertyEditorModel ?? '';
this._workspaceContext?.setPropertyEditorModelAlias(this._propertyEditorModelAlias);
this._workspaceContext?.setPropertyEditorAlias(this._propertyEditorAlias);
}
);
}
@@ -101,7 +101,7 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
if (!this._dataType) return;
const modalHandler = this._modalService?.propertyEditorUIPicker({
selection: this._propertyEditorUIAlias ? [this._propertyEditorUIAlias] : [],
selection: this._propertyEditorUiAlias ? [this._propertyEditorUiAlias] : [],
});
modalHandler?.onClose().then(({ selection } = {}) => {
@@ -110,10 +110,10 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
});
}
private _selectPropertyEditorUI(propertyEditorUIAlias: string | undefined) {
if (!this._dataType || this._dataType.propertyEditorUIAlias === propertyEditorUIAlias) return;
this._workspaceContext?.setPropertyEditorUIAlias(propertyEditorUIAlias);
this._observePropertyEditorUI(propertyEditorUIAlias);
private _selectPropertyEditorUI(propertyEditorUiAlias: string | undefined) {
if (!this._dataType || this._dataType.propertyEditorUiAlias === propertyEditorUiAlias) return;
this._workspaceContext?.setPropertyEditorUiAlias(propertyEditorUiAlias);
this._observePropertyEditorUI(propertyEditorUiAlias);
}
render() {
@@ -126,14 +126,14 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
private _renderPropertyEditorUI() {
return html`
<umb-workspace-property-layout label="Property Editor" description="Select a property editor">
${this._propertyEditorUIAlias
${this._propertyEditorUiAlias
? html`
<!-- TODO: border is a bit weird attribute name. Maybe single or standalone would be better? -->
<umb-ref-property-editor-ui
slot="editor"
name=${this._propertyEditorUIName}
alias=${this._propertyEditorUIAlias}
property-editor-model-alias=${this._propertyEditorModelAlias}
alias=${this._propertyEditorUiAlias}
property-editor-model-alias=${this._propertyEditorAlias}
border>
<uui-icon name="${this._propertyEditorUIIcon}" slot="icon"></uui-icon>
<uui-action-bar slot="actions">
@@ -155,11 +155,11 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
private _renderConfig() {
return html`
${this._propertyEditorModelAlias && this._propertyEditorUIAlias
${this._propertyEditorAlias && this._propertyEditorUiAlias
? html`
<uui-box headline="Config">
<umb-property-editor-config
property-editor-ui-alias="${this._propertyEditorUIAlias}"
property-editor-ui-alias="${this._propertyEditorUiAlias}"
.data="${this._data}"></umb-property-editor-config>
</uui-box>
`

View File

@@ -1,17 +1,17 @@
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { distinctUntilChanged } from 'rxjs';
import { UmbWorkspaceDataTypeContext } from '../../data-type-workspace.context';
import type { DataTypeDetails } from '@umbraco-cms/models';
import { UmbLitElement } from '@umbraco-cms/element';
import { DataTypeModel } from '@umbraco-cms/backend-api';
@customElement('umb-workspace-view-data-type-info')
export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement {
static styles = [UUITextStyles, css``];
@state()
_dataType?: DataTypeDetails;
_dataType?: DataTypeModel;
private _workspaceContext?: UmbWorkspaceDataTypeContext;
@@ -28,12 +28,9 @@ export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement {
private _observeDataType() {
if (!this._workspaceContext) return;
this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (dataType) => {
this.observe(this._workspaceContext.data, (dataType) => {
if (!dataType) return;
// TODO: handle if model is not of the type wanted.
// TODO: Make method to identify wether data is of type DataTypeDetails
this._dataType = dataType as DataTypeDetails;
this._dataType = dataType;
});
}
@@ -48,11 +45,11 @@ export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement {
<div slot="editor">${this._dataType?.key}</div>
</umb-workspace-property-layout>
<umb-workspace-property-layout label="Property Editor Alias">
<div slot="editor">${this._dataType?.propertyEditorModelAlias}</div>
<div slot="editor">${this._dataType?.propertyEditorAlias}</div>
</umb-workspace-property-layout>
<umb-workspace-property-layout label="Property Editor UI Alias">
<div slot="editor">${this._dataType?.propertyEditorUIAlias}</div>
<div slot="editor">${this._dataType?.propertyEditorUiAlias}</div>
</umb-workspace-property-layout>
</uui-box>
`;

View File

@@ -73,10 +73,11 @@ export class UmbLanguageStore extends UmbStoreBase {
}
async delete(isoCodes: Array<string>) {
// TODO: revisit this. It looks a bit weird with the nested tryExecuteAndNotify
const queue = isoCodes.map((isoCode) =>
tryExecuteAndNotify(
this._host,
LanguageResource.deleteLanguageByIsoCode({ isoCode }).then(() => isoCode)
tryExecuteAndNotify(this._host, LanguageResource.deleteLanguageByIsoCode({ isoCode })).then(() => isoCode)
)
);
const results = await Promise.all(queue);

View File

@@ -2,11 +2,8 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html } from 'lit';
import { ifDefined } from 'lit-html/directives/if-defined.js';
import { customElement, property, state } from 'lit/decorators.js';
import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../../../settings/data-types/data-type.detail.store';
import type { UmbDataTypeDetailStore } from '../../../settings/data-types/data-type.detail.store';
import type { ContentProperty, DataTypeDetails, DataTypePropertyData } from '@umbraco-cms/models';
import { UmbDataTypeRepository } from '../../../settings/data-types/repository/data-type.repository';
import type { DataTypeModel, DataTypePropertyModel, DocumentTypePropertyTypeModel } from '@umbraco-cms/backend-api';
import '../workspace-property/workspace-property.element';
import { UmbLitElement } from '@umbraco-cms/element';
import { UmbObserverController } from '@umbraco-cms/observable-api';
@@ -23,12 +20,13 @@ export class UmbContentPropertyElement extends UmbLitElement {
];
// TODO: Consider if we just need to get the DataType Key?..
private _property?: ContentProperty;
// TODO: consider if we should make a base type of the DocumentTypePropertyType, which could become the ContentProperty. A shared common type for all properties.
private _property?: DocumentTypePropertyTypeModel;
@property({ type: Object, attribute: false })
public get property(): ContentProperty | undefined {
public get property(): DocumentTypePropertyTypeModel | undefined {
return this._property;
}
public set property(value: ContentProperty | undefined) {
public set property(value: DocumentTypePropertyTypeModel | undefined) {
const oldProperty = this._property;
this._property = value;
if (this._property?.dataTypeKey !== oldProperty?.dataTypeKey) {
@@ -40,31 +38,22 @@ export class UmbContentPropertyElement extends UmbLitElement {
value?: object | string;
@state()
private _propertyEditorUIAlias?: string;
private _propertyEditorUiAlias?: string;
@state()
private _dataTypeData: DataTypePropertyData[] = [];
private _dataTypeData: DataTypePropertyModel[] = [];
private _dataTypeStore?: UmbDataTypeDetailStore;
private _dataTypeObserver?: UmbObserverController<DataTypeDetails | null>;
constructor() {
super();
this.consumeContext(UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN, (instance) => {
this._dataTypeStore = instance;
this._observeDataType(this._property?.dataTypeKey);
});
}
private _observeDataType(dataTypeKey?: string) {
if (!this._dataTypeStore) return;
private _dataTypeRepository: UmbDataTypeRepository = new UmbDataTypeRepository(this);
private _dataTypeObserver?: UmbObserverController<DataTypeModel | null>;
private async _observeDataType(dataTypeKey?: string) {
this._dataTypeObserver?.destroy();
if (dataTypeKey) {
this._dataTypeObserver = this.observe(this._dataTypeStore.getByKey(dataTypeKey), (dataType) => {
// We do not need to have await here, this is only to ensure that the data is loaded before we try to observe it, and thereby update the DOM with it.
await this._dataTypeRepository.requestByKey(dataTypeKey);
this._dataTypeObserver = this.observe(await this._dataTypeRepository.byKey(dataTypeKey), (dataType) => {
this._dataTypeData = dataType?.data || [];
this._propertyEditorUIAlias = dataType?.propertyEditorUIAlias || undefined;
this._propertyEditorUiAlias = dataType?.propertyEditorUiAlias || undefined;
});
}
}
@@ -72,9 +61,9 @@ export class UmbContentPropertyElement extends UmbLitElement {
render() {
return html`<umb-workspace-property
alias=${ifDefined(this._property?.alias)}
label=${ifDefined(this._property?.label)}
description=${ifDefined(this._property?.description)}
property-editor-ui-alias="${ifDefined(this._propertyEditorUIAlias)}"
label=${ifDefined(this._property?.name)}
description=${ifDefined(this._property?.description || undefined)}
property-editor-ui-alias=${ifDefined(this._propertyEditorUiAlias)}
.value=${this.value}
.config=${this._dataTypeData}></umb-workspace-property>`;
}

View File

@@ -30,7 +30,7 @@ export class UmbRefPropertyEditorUIElement extends UUIRefNodeElement {
* @default ''
*/
@property({ type: String, attribute: 'property-editor-model-alias' })
propertyEditorModelAlias = '';
propertyEditorAlias = '';
protected renderDetail() {
const details: string[] = [];
@@ -39,8 +39,8 @@ export class UmbRefPropertyEditorUIElement extends UUIRefNodeElement {
details.push(this.alias);
}
if (this.propertyEditorModelAlias !== '') {
details.push(this.propertyEditorModelAlias);
if (this.propertyEditorAlias !== '') {
details.push(this.propertyEditorAlias);
} else {
details.push('Property Editor Missing');
}

View File

@@ -50,23 +50,23 @@ export class UmbVariantSelectorElement extends UmbLitElement {
super();
// TODO: Figure out how to get the magic string for the workspace context.
this.consumeContext<UmbWorkspaceEntityContextInterface<ContentTreeItemModel>>(
'umbWorkspaceContext',
(instance) => {
this._workspaceContext = instance;
this._observeWorkspace();
}
);
this.consumeContext<UmbWorkspaceEntityContextInterface<ContentTreeItemModel>>('umbWorkspaceContext', (instance) => {
this._workspaceContext = instance;
this._observeWorkspace();
});
}
private async _observeWorkspace() {
if (!this._workspaceContext) return;
/*
// TODO: update this with nre repository and document types.
this.observe(this._workspaceContext.data, (data) => {
if(data) {
this._content = data;
}
});
*/
}
// TODO. find a way where we don't have to do this for all workspaces.

View File

@@ -1,5 +1,5 @@
import { UmbWorkspaceEntityContextInterface } from '../workspace/workspace-context/workspace-entity-context.interface';
import type { DataTypeDetails } from '@umbraco-cms/models';
import type { DataTypeModel } from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { ObjectState } from '@umbraco-cms/observable-api';
import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/context-api';
@@ -10,7 +10,7 @@ export type WorkspacePropertyData<ValueType> = {
label?: string;
description?: string;
value?: ValueType | null;
config?: DataTypeDetails['data']; // This could potentially then come from hardcoded JS object and not the DataType store.
config?: DataTypeModel['data']; // This could potentially then come from hardcoded JS object and not the DataType store.
};
export class UmbWorkspacePropertyContext<ValueType = unknown> {
@@ -28,9 +28,13 @@ export class UmbWorkspacePropertyContext<ValueType = unknown> {
constructor(host: UmbControllerHostInterface) {
// TODO: Figure out how to get the magic string in a better way.
new UmbContextConsumerController<UmbWorkspaceEntityContextInterface>(host, 'umbWorkspaceContext', (workspaceContext) => {
this._workspaceContext = workspaceContext;
});
new UmbContextConsumerController<UmbWorkspaceEntityContextInterface>(
host,
'umbWorkspaceContext',
(workspaceContext) => {
this._workspaceContext = workspaceContext;
}
);
this._providerController = new UmbContextProviderController(host, 'umbPropertyContext', this);
}

View File

@@ -3,13 +3,14 @@ import { css, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { ifDefined } from 'lit-html/directives/if-defined.js';
import { UmbWorkspacePropertyContext } from './workspace-property.context';
import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
import type { DataTypePropertyData, ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models';
import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
import type { ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models';
import '../../property-actions/shared/property-action-menu/property-action-menu.element';
import '../../../../backoffice/shared/components/workspace/workspace-property-layout/workspace-property-layout.element';
import { UmbObserverController } from '@umbraco-cms/observable-api';
import { UmbLitElement } from '@umbraco-cms/element';
import { DataTypePropertyModel } from '@umbraco-cms/backend-api';
/**
* @element umb-workspace-property
@@ -94,11 +95,11 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
* @attr
* @default ''
*/
private _propertyEditorUIAlias = '';
private _propertyEditorUiAlias = '';
@property({ type: String, attribute: 'property-editor-ui-alias' })
public set propertyEditorUIAlias(value: string) {
if (this._propertyEditorUIAlias === value) return;
this._propertyEditorUIAlias = value;
public set propertyEditorUiAlias(value: string) {
if (this._propertyEditorUiAlias === value) return;
this._propertyEditorUiAlias = value;
this._observePropertyEditorUI();
}
@@ -122,7 +123,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
* @default ''
*/
@property({ type: Object, attribute: false })
public set config(value: DataTypePropertyData[]) {
public set config(value: DataTypePropertyModel[]) {
this._propertyContext.setConfig(value);
}
@@ -158,7 +159,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
private _observePropertyEditorUI() {
this.propertyEditorUIObserver?.destroy();
this.propertyEditorUIObserver = this.observe(
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this._propertyEditorUIAlias),
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this._propertyEditorUiAlias),
(manifest) => {
this._gotEditorUI(manifest);
}
@@ -167,7 +168,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
private _gotEditorUI(manifest?: ManifestPropertyEditorUI | null) {
if (!manifest) {
// TODO: if propertyEditorUIAlias didn't exist in store, we should do some nice fail UI.
// TODO: if propertyEditorUiAlias didn't exist in store, we should do some nice fail UI.
return;
}
@@ -217,11 +218,11 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
}
private _renderPropertyActionMenu() {
return html`${this._propertyEditorUIAlias
return html`${this._propertyEditorUiAlias
? html`<umb-property-action-menu
slot="property-action-menu"
id="property-action-menu"
.propertyEditorUIAlias="${this._propertyEditorUIAlias}"
.propertyEditorUiAlias="${this._propertyEditorUiAlias}"
.value="${this.value}"></umb-property-action-menu>`
: ''}`;
}

View File

@@ -7,7 +7,6 @@ import {
UMB_COLLECTION_CONTEXT_TOKEN,
} from '../../../../../../shared/collection/collection.context';
import '../../../../../../shared/components/content-property/content-property.element';
import '../../../../../../shared/collection/dashboards/dashboard-collection.element';
import type { UmbWorkspaceEntityContextInterface } from '../../../workspace-context/workspace-entity-context.interface';
import { UmbLitElement } from '@umbraco-cms/element';

View File

@@ -3,9 +3,8 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import type { UmbWorkspaceEntityContextInterface } from '../../../workspace-context/workspace-entity-context.interface';
import type { ContentProperty, ContentPropertyData, DocumentDetails, MediaDetails } from '@umbraco-cms/models';
import type { ContentProperty, ContentPropertyData, MediaTypeDetails } from '@umbraco-cms/models';
import '../../../../content-property/content-property.element';
import { UmbLitElement } from '@umbraco-cms/element';
@customElement('umb-workspace-view-content-edit')
@@ -26,17 +25,16 @@ export class UmbWorkspaceViewContentEditElement extends UmbLitElement {
@state()
_data: ContentPropertyData[] = [];
private _workspaceContext?: UmbWorkspaceEntityContextInterface<DocumentDetails | MediaDetails>;
private _workspaceContext?: UmbWorkspaceEntityContextInterface<MediaTypeDetails>;
constructor() {
super();
// TODO: Figure out how to get the magic string for the workspace context.
this.consumeContext<UmbWorkspaceEntityContextInterface<DocumentDetails | MediaDetails>>(
this.consumeContext<UmbWorkspaceEntityContextInterface<MediaTypeDetails>>(
'umbWorkspaceContext',
(workspaceContext) => {
this._workspaceContext = workspaceContext;
console.log("workspaceContext", workspaceContext)
this._observeContent();
}
);
@@ -52,17 +50,22 @@ export class UmbWorkspaceViewContentEditElement extends UmbLitElement {
Should use a Observable for example: this._workspaceContext.properties
*/
this.observe(this._workspaceContext.data, (content) => {
this._properties = content?.properties || [];
this._data = content?.data || [];
console.log("content", content)
/*
// TODO: broken for now, as we need to transfer into the repository way:
this.observe(
this._workspaceContext.data,
(content) => {
// TODO: Should be adapted to new models, maybe a shared 'Content' solution is not the right thing here.
this._properties = content?.properties || [];
console.log('content', content);
/*
Maybe we should not give the value(Data), but the umb-content-property should get the context and observe its own data.
This would become a more specific Observer therefor better performance?.. Note to self: Debate with Mads how he sees this perspective.
*/
}, 'observeWorkspaceContextData');
//Maybe we should not give the value(Data), but the umb-content-property should get the context and observe its own data.
//This would become a more specific Observer therefor better performance?.. Note to self: Debate with Mads how he sees this perspective.
},
'observeWorkspaceContextData'
);
*/
}
render() {
@@ -72,9 +75,7 @@ export class UmbWorkspaceViewContentEditElement extends UmbLitElement {
this._properties,
(property) => property.alias,
(property) =>
html`<umb-content-property
.property=${property}
.value=${this._data.find((data) => data.alias === property.alias)?.value}></umb-content-property> `
html`<umb-content-property .property=${property} .value=${property.label}></umb-content-property> `
)}
</uui-box>
`;

View File

@@ -2,7 +2,7 @@ import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, state } from 'lit/decorators.js';
import { UmbWorkspaceEntityContextInterface } from '../../../workspace-context/workspace-entity-context.interface';
import type { DocumentDetails, MediaDetails } from '@umbraco-cms/models';
import type { DocumentModel } from '@umbraco-cms/backend-api';
import { UmbLitElement } from '@umbraco-cms/element';
@customElement('umb-workspace-view-content-info')
@@ -20,27 +20,27 @@ export class UmbWorkspaceViewContentInfoElement extends UmbLitElement {
@state()
private _nodeName = '';
private _workspaceContext?: UmbWorkspaceEntityContextInterface<DocumentDetails | MediaDetails>;
private _workspaceContext?: UmbWorkspaceEntityContextInterface<DocumentModel>;
constructor() {
super();
// TODO: Figure out how to get the magic string for the workspace context.
this.consumeContext<UmbWorkspaceEntityContextInterface<DocumentDetails | MediaDetails>>(
'umbWorkspaceContext',
(nodeContext) => {
this._workspaceContext = nodeContext;
this._observeContent();
}
);
this.consumeContext<UmbWorkspaceEntityContextInterface<DocumentModel>>('umbWorkspaceContext', (nodeContext) => {
this._workspaceContext = nodeContext;
this._observeContent();
});
}
private _observeContent() {
if (!this._workspaceContext) return;
this._nodeName = 'TBD, with variants this is not as simple.';
/*
this.observe(this._workspaceContext.name, (name) => {
this._nodeName = name || '';
});
*/
}
render() {

View File

@@ -1,10 +1,5 @@
import type { Observable } from "rxjs";
export interface UmbWorkspaceContextInterface<T = unknown> {
readonly data: Observable<T>;
//readonly data: Observable<T>;
//getUnique(): string | undefined;
getEntityType(): string;

View File

@@ -1,8 +1,7 @@
import type { Observable } from 'rxjs';
import { UmbWorkspaceContextInterface } from './workspace-context.interface';
export interface UmbWorkspaceEntityContextInterface<T = unknown> extends UmbWorkspaceContextInterface<T> {
readonly name: Observable<string | undefined>;
//readonly name: Observable<string | undefined>;
getEntityKey(): string | undefined; // COnsider if this should go away now that we have getUnique()
getEntityType(): string;

View File

@@ -46,7 +46,7 @@ export class UmbPropertyActionMenuElement extends UmbLitElement {
public value?: string;
@property()
set propertyEditorUIAlias(alias: string) {
set propertyEditorUiAlias(alias: string) {
this._observeActions(alias);
}

View File

@@ -22,15 +22,15 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement {
* @attr
* @default ''
*/
private _propertyEditorUIAlias = '';
private _propertyEditorUiAlias = '';
@property({ type: String, attribute: 'property-editor-ui-alias' })
public get propertyEditorUIAlias(): string {
return this._propertyEditorUIAlias;
public get propertyEditorUiAlias(): string {
return this._propertyEditorUiAlias;
}
public set propertyEditorUIAlias(value: string) {
const oldVal = this._propertyEditorUIAlias;
this._propertyEditorUIAlias = value;
this.requestUpdate('propertyEditorUIAlias', oldVal);
public set propertyEditorUiAlias(value: string) {
const oldVal = this._propertyEditorUiAlias;
this._propertyEditorUiAlias = value;
this.requestUpdate('propertyEditorUiAlias', oldVal);
this._observePropertyEditorUIConfig();
}
@@ -56,10 +56,10 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement {
private _propertyEditorUIConfigProperties: Array<PropertyEditorConfigProperty> = [];
private _observePropertyEditorUIConfig() {
if (!this._propertyEditorUIAlias) return;
if (!this._propertyEditorUiAlias) return;
this.observe(
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this.propertyEditorUIAlias),
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this.propertyEditorUiAlias),
(manifest) => {
this._observePropertyEditorModelConfig(manifest?.meta.propertyEditorModel);
this._propertyEditorUIConfigProperties = manifest?.meta.config?.properties || [];
@@ -70,18 +70,15 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement {
);
}
private _observePropertyEditorModelConfig(propertyEditorModelAlias?: string) {
if (!propertyEditorModelAlias) return;
private _observePropertyEditorModelConfig(propertyEditorAlias?: string) {
if (!propertyEditorAlias) return;
this.observe(
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorModel', propertyEditorModelAlias),
(manifest) => {
this._propertyEditorModelConfigProperties = manifest?.meta.config?.properties || [];
this._propertyEditorModelConfigDefaultData = manifest?.meta.config?.defaultData || [];
this._mergeConfigProperties();
this._mergeConfigDefaultData();
}
);
this.observe(umbExtensionsRegistry.getByTypeAndAlias('propertyEditorModel', propertyEditorAlias), (manifest) => {
this._propertyEditorModelConfigProperties = manifest?.meta.config?.properties || [];
this._propertyEditorModelConfigDefaultData = manifest?.meta.config?.defaultData || [];
this._mergeConfigProperties();
this._mergeConfigDefaultData();
});
}
private _mergeConfigProperties() {

View File

@@ -2,9 +2,9 @@ import { html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import '../../../components/input-checkbox-list/input-checkbox-list.element';
import { UmbInputCheckboxListElement } from '../../../components/input-checkbox-list/input-checkbox-list.element';
import type { UmbInputCheckboxListElement } from '../../../components/input-checkbox-list/input-checkbox-list.element';
import { UmbLitElement } from '@umbraco-cms/element';
import type { DataTypePropertyData } from '@umbraco-cms/models';
import type { DataTypePropertyModel } from '@umbraco-cms/backend-api';
/**
* @element umb-property-editor-ui-checkbox-list
@@ -23,7 +23,7 @@ export class UmbPropertyEditorUICheckboxListElement extends UmbLitElement {
}
@property({ type: Array, attribute: false })
public set config(config: Array<DataTypePropertyData>) {
public set config(config: Array<DataTypePropertyModel>) {
const listData = config.find((x) => x.alias === 'itemList');
if (!listData) return;

View File

@@ -1,11 +1,10 @@
import { html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { ifDefined } from 'lit/directives/if-defined';
import { UUIColorSwatchesEvent } from '@umbraco-ui/uui';
import '../../../../shared/components/color-picker/color-picker.element';
import { UmbLitElement } from '@umbraco-cms/element';
import type { DataTypePropertyData } from '@umbraco-cms/models';
import type { UmbColorPickerElement } from 'src/backoffice/shared/components/color-picker/color-picker.element';
import type { DataTypePropertyModel } from '@umbraco-cms/backend-api';
/**
* @element umb-property-editor-ui-color-picker
@@ -24,7 +23,7 @@ export class UmbPropertyEditorUIColorPickerElement extends UmbLitElement {
private _colorSwatches: string[] = [];
@property({ type: Array, attribute: false })
public set config(config: Array<DataTypePropertyData>) {
public set config(config: Array<DataTypePropertyModel>) {
const includeLabels = config.find((x) => x.alias === 'includeLabels');
if (includeLabels) this._includeLabels = includeLabels.value;

View File

@@ -1,8 +1,9 @@
import { html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { UmbInputDocumentPickerElement } from '../../../components/input-document-picker/input-document-picker.element';
import type { UmbInputDocumentPickerElement } from '../../../components/input-document-picker/input-document-picker.element';
import { UmbLitElement } from '@umbraco-cms/element';
import type { DataTypePropertyData } from '@umbraco-cms/models';
import '../../../components/input-document-picker/input-document-picker.element';
import type { DataTypePropertyModel } from '@umbraco-cms/backend-api';
@customElement('umb-property-editor-ui-document-picker')
export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement {
@@ -17,7 +18,7 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement {
}
@property({ type: Array, attribute: false })
public set config(config: Array<DataTypePropertyData>) {
public set config(config: Array<DataTypePropertyModel>) {
const validationLimit = config.find((x) => x.alias === 'validationLimit');
this._limitMin = (validationLimit?.value as any).min;

View File

@@ -4,7 +4,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { UUIColorPickerChangeEvent } from '@umbraco-ui/uui';
import { UmbLitElement } from '@umbraco-cms/element';
import '../../../components/eye-dropper/eye-dropper.element';
import type { DataTypePropertyData } from '@umbraco-cms/models';
import type { DataTypePropertyModel } from '@umbraco-cms/backend-api';
/**
* @element umb-property-editor-ui-eye-dropper
@@ -23,7 +23,7 @@ export class UmbPropertyEditorUIEyeDropperElement extends UmbLitElement {
private _swatches: string[] = [];
@property({ type: Array, attribute: false })
public set config(config: Array<DataTypePropertyData>) {
public set config(config: Array<DataTypePropertyModel>) {
const showAlpha = config.find((x) => x.alias === 'showAlpha');
if (showAlpha) this._opacity = showAlpha.value;

View File

@@ -2,7 +2,7 @@ import { html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { UmbInputMediaPickerElement } from '../../../../../backoffice/shared/components/input-media-picker/input-media-picker.element';
import { UmbLitElement } from '@umbraco-cms/element';
import type { DataTypePropertyData } from '@umbraco-cms/models';
import type { DataTypePropertyModel } from '@umbraco-cms/backend-api';
/**
* @element umb-property-editor-ui-media-picker
@@ -20,7 +20,7 @@ export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement {
}
@property({ type: Array, attribute: false })
public set config(config: Array<DataTypePropertyData>) {
public set config(config: Array<DataTypePropertyModel>) {
const validationLimit = config.find((x) => x.alias === 'validationLimit');
if (!validationLimit) return;

View File

@@ -7,7 +7,6 @@ import UmbInputMultipleTextStringItemElement from '../input-multiple-text-string
import { UmbInputEvent, UmbChangeEvent, UmbDeleteEvent } from '../../../../../../core/events';
import { UmbLitElement } from '@umbraco-cms/element';
import '../input-multiple-text-string-item/input-multiple-text-string-item.element';
export type MultipleTextStringValue = Array<MultipleTextStringValueItem>;

View File

@@ -8,7 +8,6 @@ import UmbInputMultipleTextStringElement, {
import { UmbChangeEvent } from 'src/core/events/change.event';
import { UmbLitElement } from '@umbraco-cms/element';
import './input-multiple-text-string/input-multiple-text-string.element';
export type MultipleTextStringConfigData = Array<{
alias: 'minNumber' | 'maxNumber';

View File

@@ -122,7 +122,7 @@ export class UmbTemplateRepository implements UmbTreeRepository, UmbDetailReposi
return this.#detailDataSource.createScaffold();
}
async requestDetails(key: string) {
async requestByKey(key: string) {
await this.#init;
// TODO: should we show a notification if the key is missing?

View File

@@ -39,7 +39,7 @@ export class UmbTemplateDetailServerDataSource implements TemplateDetailDataSour
* @memberof UmbTemplateDetailServerDataSource
*/
async createScaffold() {
let error = undefined;
const error = undefined;
const data: TemplateModel = {
key: uuid(),
name: '',
@@ -47,13 +47,17 @@ export class UmbTemplateDetailServerDataSource implements TemplateDetailDataSour
content: '',
};
// TODO: update when backend is updated so we don't have to do two calls
/*
// TODO: Revisit template models, masterTemplateAlias is not here anymore?
const { data: scaffoldData, error: scaffoldError } = await tryExecuteAndNotify(
this.#host,
TemplateResource.getTemplateScaffold()
);
*/
error = scaffoldError;
data.content = scaffoldData?.content || '';
//error = scaffoldError;
//data.content = scaffoldData?.content || '';
return { data, error };
}

View File

@@ -204,7 +204,7 @@ export class UmbUserGroupWorkspaceElement extends UmbLitElement implements UmbWo
this._observeUsers();
});
this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (userGroup) => {
this.observe(this._workspaceContext.data, (userGroup) => {
this._userGroup = userGroup;
});
}

View File

@@ -15,22 +15,18 @@ export const UMB_USER_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbUserStore>('U
* @description - Data Store for Users
*/
export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore<UserDetails> {
#users = new ArrayState<UserDetails>([], x => x.key);
#users = new ArrayState<UserDetails>([], (x) => x.key);
public users = this.#users.asObservable();
#totalUsers = new NumberState(0);
public readonly totalUsers = this.#totalUsers.asObservable();
constructor(host: UmbControllerHostInterface) {
super(host, UMB_USER_STORE_CONTEXT_TOKEN.toString());
}
getScaffold(entityType: string, parentKey: string | null) {
return {
return {
key: '',
name: '',
icon: '',
@@ -49,7 +45,6 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore<U
} as UserDetails;
}
getAll() {
// TODO: use Fetcher API.
// TODO: only fetch if the data type is not in the store?
@@ -66,7 +61,7 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore<U
/**
* @description - Request a User by key. The User is added to the store and is returned as an Observable.
* @param {string} key
* @return {*} {(Observable<DataTypeDetails | null>)}
* @return {*} {(Observable<DataTypeModel | null>)}
* @memberof UmbDataTypeStore
*/
getByKey(key: string) {
@@ -78,10 +73,11 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore<U
this.#users.appendOne(data);
});
return this.#users.getObservablePart((users: Array<UmbUserStoreItemType>) => users.find((user: UmbUserStoreItemType) => user.key === key));
return this.#users.getObservablePart((users: Array<UmbUserStoreItemType>) =>
users.find((user: UmbUserStoreItemType) => user.key === key)
);
}
/**
* @description - Request Users by keys.
* @param {string} key
@@ -96,7 +92,9 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore<U
this.#users.append(data);
});
return this.#users.getObservablePart((users: Array<UmbUserStoreItemType>) => users.filter((user: UmbUserStoreItemType) => keys.includes(user.key)));
return this.#users.getObservablePart((users: Array<UmbUserStoreItemType>) =>
users.filter((user: UmbUserStoreItemType) => keys.includes(user.key))
);
}
getByName(name: string) {
@@ -110,7 +108,9 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore<U
this.#users.append(data);
});
return this.#users.getObservablePart((users: Array<UmbUserStoreItemType>) => users.filter((user: UmbUserStoreItemType) => user.name.toLocaleLowerCase().includes(name)));
return this.#users.getObservablePart((users: Array<UmbUserStoreItemType>) =>
users.filter((user: UmbUserStoreItemType) => user.name.toLocaleLowerCase().includes(name))
);
}
async enableUsers(userKeys: Array<string>) {
@@ -243,12 +243,7 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore<U
}
}
async invite(
name: string,
email: string,
message: string,
userGroups: Array<string>
) {
async invite(name: string, email: string, message: string, userGroups: Array<string>) {
// TODO: use Fetcher API.
try {
const res = await fetch('/umbraco/backoffice/users/invite', {

View File

@@ -88,7 +88,6 @@ export class UmbUserWorkspaceElement extends UmbLitElement implements UmbWorkspa
private _languages = []; //TODO Add languages
private _workspaceContext: UmbWorkspaceUserContext = new UmbWorkspaceUserContext(this);
@state()
@@ -105,7 +104,7 @@ export class UmbUserWorkspaceElement extends UmbLitElement implements UmbWorkspa
this._observeCurrentUser();
});
this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (user) => {
this.observe(this._workspaceContext.data, (user) => {
this._user = user;
if (user && user.name !== this._userName) {
this._userName = user.name;

View File

@@ -1,20 +1,22 @@
import { UmbEntityData } from './entity.data';
import { createFolderTreeItem } from './utils';
import type { FolderTreeItemModel } from '@umbraco-cms/backend-api';
import type { DataTypeDetails } from '@umbraco-cms/models';
import type { FolderTreeItemModel, DataTypeModel } from '@umbraco-cms/backend-api';
export const data: Array<DataTypeDetails> = [
export const data: Array<DataTypeModel> = [
{
key: '0cc0eba1-9960-42c9-bf9b-60e150b429ae',
parentKey: null,
name: 'Textstring',
propertyEditorAlias: 'Umbraco.TextBox',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.TextBox',
data: [],
},
{
name: 'Text',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-textBox',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.TextBox',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.TextBox',
propertyEditorAlias: 'Umbraco.TextBox',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.TextBox',
data: [
{
alias: 'maxChars',
@@ -24,41 +26,26 @@ export const data: Array<DataTypeDetails> = [
},
{
name: 'Text Area',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-textArea',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.TextArea',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.TextArea',
propertyEditorAlias: 'Umbraco.TextArea',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.TextArea',
data: [],
},
{
name: 'My JS Property Editor',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-custom',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.JSON',
propertyEditorUIAlias: 'My.PropertyEditorUI.Custom',
propertyEditorAlias: 'Umbraco.JSON',
propertyEditorUiAlias: 'My.PropertyEditorUI.Custom',
data: [],
},
{
name: 'Color Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-colorPicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.ColorPicker',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.ColorPicker',
propertyEditorAlias: 'Umbraco.ColorPicker',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.ColorPicker',
data: [
{
alias: 'includeLabels',
@@ -72,15 +59,10 @@ export const data: Array<DataTypeDetails> = [
},
{
name: 'Content Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-contentPicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.ContentPicker',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.DocumentPicker',
propertyEditorAlias: 'Umbraco.ContentPicker',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.DocumentPicker',
data: [
{
alias: 'validationLimit',
@@ -90,15 +72,10 @@ export const data: Array<DataTypeDetails> = [
},
{
name: 'Eye Dropper',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-eyeDropper',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.ColorPicker.EyeDropper',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.EyeDropper',
propertyEditorAlias: 'Umbraco.ColorPicker.EyeDropper',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.EyeDropper',
data: [
{
alias: 'palette',
@@ -129,67 +106,42 @@ export const data: Array<DataTypeDetails> = [
},
{
name: 'Multi URL Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-multiUrlPicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.MultiUrlPicker',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.MultiUrlPicker',
propertyEditorAlias: 'Umbraco.MultiUrlPicker',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.MultiUrlPicker',
data: [],
},
{
name: 'Multi Node Tree Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-multiNodeTreePicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.MultiNodeTreePicker',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.TreePicker',
propertyEditorAlias: 'Umbraco.MultiNodeTreePicker',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.TreePicker',
data: [],
},
{
name: 'Date Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-datePicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.DateTime',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.DatePicker',
propertyEditorAlias: 'Umbraco.DateTime',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.DatePicker',
data: [],
},
{
name: 'Email',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-email',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.EmailAddress',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Email',
propertyEditorAlias: 'Umbraco.EmailAddress',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.Email',
data: [],
},
{
name: 'Multiple Text String',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-multipleTextString',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.MultipleTextString',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.MultipleTextString',
propertyEditorAlias: 'Umbraco.MultipleTextString',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.MultipleTextString',
data: [
{
alias: 'minNumber',
@@ -203,93 +155,58 @@ export const data: Array<DataTypeDetails> = [
},
{
name: 'Dropdown',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-dropdown',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.DropDown.Flexible',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Dropdown',
propertyEditorAlias: 'Umbraco.DropDown.Flexible',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.Dropdown',
data: [],
},
{
name: 'Slider',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-slider',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.Slider',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Slider',
propertyEditorAlias: 'Umbraco.Slider',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.Slider',
data: [],
},
{
name: 'Toggle',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-toggle',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.TrueFalse',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Toggle',
propertyEditorAlias: 'Umbraco.TrueFalse',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.Toggle',
data: [],
},
{
name: 'Tags',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-tags',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.Tags',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Tags',
propertyEditorAlias: 'Umbraco.Tags',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.Tags',
data: [],
},
{
name: 'Markdown Editor',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-markdownEditor',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.MarkdownEditor',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.MarkdownEditor',
propertyEditorAlias: 'Umbraco.MarkdownEditor',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.MarkdownEditor',
data: [],
},
{
name: 'Radio Button List',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-radioButtonList',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.RadioButtonList',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.RadioButtonList',
propertyEditorAlias: 'Umbraco.RadioButtonList',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.RadioButtonList',
data: [],
},
{
name: 'Checkbox List',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-checkboxList',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.CheckboxList',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.CheckboxList',
propertyEditorAlias: 'Umbraco.CheckboxList',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.CheckboxList',
data: [
{
alias: 'itemList',
@@ -302,223 +219,138 @@ export const data: Array<DataTypeDetails> = [
},
{
name: 'Block List',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-blockList',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.BlockList',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.BlockList',
propertyEditorAlias: 'Umbraco.BlockList',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.BlockList',
data: [],
},
{
name: 'Media Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-mediaPicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.MediaPicker3',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.MediaPicker',
propertyEditorAlias: 'Umbraco.MediaPicker3',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.MediaPicker',
data: [],
},
{
name: 'Image Cropper',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-imageCropper',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.ImageCropper',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.ImageCropper',
propertyEditorAlias: 'Umbraco.ImageCropper',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.ImageCropper',
data: [],
},
{
name: 'Upload Field',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-uploadField',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.UploadField',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.UploadField',
propertyEditorAlias: 'Umbraco.UploadField',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.UploadField',
data: [],
},
{
name: 'Block Grid',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-blockGrid',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.BlockGrid',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.BlockGrid',
propertyEditorAlias: 'Umbraco.BlockGrid',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.BlockGrid',
data: [],
},
{
name: 'Collection View',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-collectionView',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.ListView',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.CollectionView',
propertyEditorAlias: 'Umbraco.ListView',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.CollectionView',
data: [],
},
{
name: 'Icon Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-iconPicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.IconPicker',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.IconPicker',
propertyEditorAlias: 'Umbraco.IconPicker',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.IconPicker',
data: [],
},
{
name: 'Number Range',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-numberRange',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.JSON',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.NumberRange',
propertyEditorAlias: 'Umbraco.JSON',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.NumberRange',
data: [],
},
{
name: 'Order Direction',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-orderDirection',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.JSON',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.OrderDirection',
propertyEditorAlias: 'Umbraco.JSON',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.OrderDirection',
data: [],
},
{
name: 'Overlay Size',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-overlaySize',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.JSON',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.OverlaySize',
propertyEditorAlias: 'Umbraco.JSON',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.OverlaySize',
data: [],
},
{
name: 'Rich Text Editor',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-richTextEditor',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.TinyMCE',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.TinyMCE',
propertyEditorAlias: 'Umbraco.TinyMCE',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.TinyMCE',
data: [],
},
{
name: 'Label',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-label',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.Label',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Label',
propertyEditorAlias: 'Umbraco.Label',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.Label',
data: [],
},
{
name: 'Integer',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-integer',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.Integer',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Integer',
propertyEditorAlias: 'Umbraco.Integer',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.Integer',
data: [],
},
{
name: 'Decimal',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-decimal',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.Decimal',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Decimal',
propertyEditorAlias: 'Umbraco.Decimal',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.Decimal',
data: [],
},
{
name: 'User Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-userPicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.UserPicker',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.UserPicker',
propertyEditorAlias: 'Umbraco.UserPicker',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.UserPicker',
data: [],
},
{
name: 'Member Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-memberPicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.MemberPicker',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.MemberPicker',
propertyEditorAlias: 'Umbraco.MemberPicker',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.MemberPicker',
data: [],
},
{
name: 'Member Group Picker',
type: 'data-type',
icon: 'umb:autofill',
hasChildren: false,
key: 'dt-memberGroupPicker',
isContainer: false,
parentKey: null,
isFolder: false,
propertyEditorModelAlias: 'Umbraco.MemberGroupPicker',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.MemberGroupPicker',
propertyEditorAlias: 'Umbraco.MemberGroupPicker',
propertyEditorUiAlias: 'Umb.PropertyEditorUI.MemberGroupPicker',
data: [],
},
];
@@ -527,7 +359,7 @@ export const data: Array<DataTypeDetails> = [
// TODO: all properties are optional in the server schema. I don't think this is correct.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
class UmbDataTypeData extends UmbEntityData<DataTypeDetails> {
class UmbDataTypeData extends UmbEntityData<DataTypeModel> {
constructor() {
super(data);
}

View File

@@ -1,9 +1,708 @@
import { UmbEntityData } from './entity.data';
import { createDocumentTypeTreeItem } from './utils';
import type { DocumentTypeTreeItemModel } from '@umbraco-cms/backend-api';
import type { DocumentTypeDetails } from '@umbraco-cms/models';
import {
DocumentTypeTreeItemModel,
DocumentTypeModel,
ContentTypeCompositionTypeModel,
} from '@umbraco-cms/backend-api';
export const data: Array<DocumentTypeDetails> = [
export const data: Array<DocumentTypeModel> = [
{
allowedTemplateKeys: [],
defaultTemplateKey: null,
key: 'all-property-editors-document-type-key',
alias: 'blogPost',
name: 'Blog Post',
description: null,
icon: 'icon-item-arrangement',
allowedAsRoot: true,
variesByCulture: true,
variesBySegment: false,
isElement: false,
properties: [
{
key: '2',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'colorPicker',
name: 'Color Picker',
description: '',
dataTypeKey: 'dt-colorPicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '3',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'contentPicker',
name: 'Content Picker',
description: '',
dataTypeKey: 'dt-contentPicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '4',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'eyeDropper',
name: 'Eye Dropper',
description: '',
dataTypeKey: 'dt-eyeDropper',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '5',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'multiUrlPicker',
name: 'Multi URL Picker',
description: '',
dataTypeKey: 'dt-multiUrlPicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '6',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'multiNodeTreePicker',
name: 'Multi Node Tree Picker',
description: '',
dataTypeKey: 'dt-multiNodeTreePicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '7',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'datePicker',
name: 'Date Picker',
description: '',
dataTypeKey: 'dt-datePicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '8',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'email',
name: 'Email',
description: '',
dataTypeKey: 'dt-email',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '9',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'textBox',
name: 'Text Box',
description: '',
dataTypeKey: 'dt-textBox',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '19',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'dropdown',
name: 'Dropdown',
description: '',
dataTypeKey: 'dt-dropdown',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '11',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'textArea',
name: 'Text Area',
description: '',
dataTypeKey: 'dt-textArea',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '12',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'slider',
name: 'Slider',
description: '',
dataTypeKey: 'dt-slider',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '13',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'toggle',
name: 'Toggle',
description: '',
dataTypeKey: 'dt-toggle',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '14',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'tags',
name: 'Tags',
description: '',
dataTypeKey: 'dt-tags',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '15',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'markdownEditor',
name: 'MarkdownEditor',
description: '',
dataTypeKey: 'dt-markdownEditor',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '16',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'radioButtonList',
name: 'Radio Button List',
description: '',
dataTypeKey: 'dt-radioButtonList',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '17',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'checkboxList',
name: 'Checkbox List',
description: '',
dataTypeKey: 'dt-checkboxList',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '18',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'blockList',
name: 'Block List',
description: '',
dataTypeKey: 'dt-blockList',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '19',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'mediaPicker',
name: 'Media Picker',
description: '',
dataTypeKey: 'dt-mediaPicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '20',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'imageCropper',
name: 'Image Cropper',
description: '',
dataTypeKey: 'dt-imageCropper',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '21',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'uploadField',
name: 'Upload Field',
description: '',
dataTypeKey: 'dt-uploadField',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '22',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'blockGrid',
name: 'Block Grid',
description: '',
dataTypeKey: 'dt-blockGrid',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '23',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'blockGrid',
name: 'Icon Picker',
description: '',
dataTypeKey: 'dt-iconPicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '24',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'numberRange',
name: 'Number Range',
description: '',
dataTypeKey: 'dt-numberRange',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '25',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'orderDirection',
name: 'Order Direction',
description: '',
dataTypeKey: 'dt-orderDirection',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '26',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'overlaySize',
name: 'Overlay Size',
description: '',
dataTypeKey: 'dt-overlaySize',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '27',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'label',
name: 'Label',
description: '',
dataTypeKey: 'dt-label',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '28',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'integer',
name: 'Integer',
description: '',
dataTypeKey: 'dt-integer',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '29',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'decimal',
name: 'Decimal',
description: '',
dataTypeKey: 'dt-decimal',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '30',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'memberPicker',
name: 'Member Picker',
description: '',
dataTypeKey: 'dt-memberPicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '31',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'memberGroupPicker',
name: 'Member Group Picker',
description: '',
dataTypeKey: 'dt-memberGroupPicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '32',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'userPicker',
name: 'User Picker',
description: '',
dataTypeKey: 'dt-userPicker',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
],
containers: [
{
key: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
parentKey: null,
name: 'Content',
type: 'Group',
sortOrder: 0,
},
],
allowedContentTypes: [],
compositions: [],
cleanup: {
preventCleanup: false,
keepAllVersionsNewerThanDays: null,
keepLatestVersionPerDayForDays: null,
},
},
{
allowedTemplateKeys: [],
defaultTemplateKey: null,
key: '29643452-cff9-47f2-98cd-7de4b6807681',
alias: 'blogPost',
name: 'Blog Post',
description: null,
icon: 'icon-item-arrangement',
allowedAsRoot: true,
variesByCulture: true,
variesBySegment: false,
isElement: false,
properties: [
{
key: '5b4ca208-134e-4865-b423-06e5e97adf3c',
containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
alias: 'blogPostText',
name: 'Blog Post Text',
description: null,
dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: 'ef7096b6-7c9e-49ba-8d49-395111e65ea2',
containerKey: '227d6ed2-e118-4494-b8f2-deb69854a56a',
alias: 'blogTextStringUnderMasterTab',
name: 'Blog text string under master tab',
description: null,
dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae',
validation: {
mandatory: false,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: 'e010c429-b298-499a-9bfe-79687af8972a',
containerKey: '22177c49-ecba-4f2e-b7fa-3f2c04d02cfb',
alias: 'blogTextStringUnderGroupUnderMasterTab',
name: 'Blog text string under group under master tab',
description: null,
dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae',
validation: {
mandatory: false,
mandatoryMessage: null,
regEx: null,
regExMessage: null,
},
appearance: {
labelOnTop: false,
},
},
{
key: '1a22749a-c7d2-44bb-b36b-c977c2ced6ef',
containerKey: '2c943997-b685-432d-a6c5-601d8e7a298a',
alias: 'localBlogTabString',
name: 'Local Blog Tab String',
description: null,
dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae',
validation: {
mandatory: true,
mandatoryMessage: null,
regEx: '^[0-9]*$',
regExMessage: null,
},
appearance: {
labelOnTop: true,
},
},
],
containers: [
{
key: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75',
parentKey: null,
name: 'Content',
type: 'Group',
sortOrder: 0,
},
{
key: '227d6ed2-e118-4494-b8f2-deb69854a56a',
parentKey: null,
name: 'Master Tab',
type: 'Tab',
sortOrder: 0,
},
{
key: '22177c49-ecba-4f2e-b7fa-3f2c04d02cfb',
parentKey: '227d6ed2-e118-4494-b8f2-deb69854a56a',
name: 'Blog Group under master tab',
type: 'Group',
sortOrder: 0,
},
{
key: '2c943997-b685-432d-a6c5-601d8e7a298a',
parentKey: null,
name: 'Local blog tab',
type: 'Tab',
sortOrder: 1,
},
],
allowedContentTypes: [
{
key: '29643452-cff9-47f2-98cd-7de4b6807681',
sortOrder: 0,
},
],
compositions: [
{
key: '5035d7d9-0a63-415c-9e75-ee2cf931db92',
compositionType: ContentTypeCompositionTypeModel.INHERITANCE,
},
{
key: '8f68ba66-6fb2-4778-83b8-6ab4ca3a7c5d',
compositionType: ContentTypeCompositionTypeModel.COMPOSITION,
},
],
cleanup: {
preventCleanup: false,
keepAllVersionsNewerThanDays: null,
keepLatestVersionPerDayForDays: null,
},
},
];
export const treeData: Array<DocumentTypeTreeItemModel> = [
{
name: 'Document Type 1',
type: 'document-type',
@@ -11,11 +710,7 @@ export const data: Array<DocumentTypeDetails> = [
key: 'd81c7957-153c-4b5a-aa6f-b434a4964624',
isContainer: false,
parentKey: null,
isFolder: false,
isElement: false,
icon: '',
alias: 'documentType1',
properties: [],
},
{
name: 'Document Type 2',
@@ -24,11 +719,7 @@ export const data: Array<DocumentTypeDetails> = [
key: 'a99e4018-3ffc-486b-aa76-eecea9593d17',
isContainer: false,
parentKey: null,
isFolder: false,
isElement: false,
icon: '',
alias: 'documentType2',
properties: [],
},
];
@@ -36,23 +727,25 @@ export const data: Array<DocumentTypeDetails> = [
// TODO: all properties are optional in the server schema. I don't think this is correct.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
class UmbDocumentTypeData extends UmbEntityData<DocumentTypeDetails> {
class UmbDocumentTypeData extends UmbEntityData<DocumentTypeTreeItemModel> {
private treeData = treeData;
constructor() {
super(data);
}
getTreeRoot(): Array<DocumentTypeTreeItemModel> {
const rootItems = this.data.filter((item) => item.parentKey === null);
const rootItems = this.treeData.filter((item) => item.parentKey === null);
return rootItems.map((item) => createDocumentTypeTreeItem(item));
}
getTreeItemChildren(key: string): Array<DocumentTypeTreeItemModel> {
const childItems = this.data.filter((item) => item.parentKey === key);
const childItems = this.treeData.filter((item) => item.parentKey === key);
return childItems.map((item) => createDocumentTypeTreeItem(item));
}
getTreeItem(keys: Array<string>): Array<DocumentTypeTreeItemModel> {
const items = this.data.filter((item) => keys.includes(item.key ?? ''));
const items = this.treeData.filter((item) => keys.includes(item.key ?? ''));
return items.map((item) => createDocumentTypeTreeItem(item));
}
}

View File

@@ -1,49 +1,15 @@
import { UmbEntityData } from './entity.data';
import { createDocumentTreeItem } from './utils';
import { DocumentTreeItemModel, PagedDocumentTreeItemModel } from '@umbraco-cms/backend-api';
import type { DocumentDetails } from '@umbraco-cms/models';
import {
ContentStateModel,
DocumentModel,
DocumentTreeItemModel,
PagedDocumentTreeItemModel,
} from '@umbraco-cms/backend-api';
export const data: Array<DocumentDetails> = [
{
name: 'Multiple Text String',
type: 'document',
icon: 'favorite',
hasChildren: false,
key: '6f31e382-458c-4f96-95ea-cc26c51009d4',
isContainer: false,
parentKey: null,
noAccess: false,
isProtected: false,
isPublished: false,
isEdited: false,
isTrashed: false,
properties: [
{
alias: 'multipleTextString',
label: 'Multiple Text String',
description: '',
dataTypeKey: 'dt-multipleTextString',
},
],
data: [
{
alias: 'multipleTextString',
value: [
{
value: 'Value 1',
},
{
value: 'Value 2',
},
{
value: 'Value 3',
},
],
},
],
variants: [],
},
{
/*
{
name: 'All Property Editors',
type: 'document',
icon: 'favorite',
@@ -247,179 +213,201 @@ export const data: Array<DocumentDetails> = [
data: [],
variants: [],
},
*/
export const data: Array<DocumentModel> = [
{
name: 'Document 1',
type: 'document',
icon: 'document',
hasChildren: false,
key: '74e4008a-ea4f-4793-b924-15e02fd380d1',
isContainer: false,
parentKey: null,
noAccess: false,
isProtected: false,
isPublished: false,
isEdited: false,
isTrashed: false,
urls: [
{
culture: 'en-US',
url: '/',
},
],
templateKey: null,
key: 'all-property-editors-document-key',
contentTypeKey: 'all-property-editors-document-type-key',
properties: [
// TODO: is 'properties' the correct name for this? The property comes from the doc type, and this only holds the values.
{
alias: 'myHeadline',
label: 'Headline',
description: 'Text string property',
dataTypeKey: 'dt-textBox',
},
{
alias: 'myDescription',
label: 'Description',
description: 'Textarea property',
dataTypeKey: 'dt-textArea',
culture: null,
segment: null,
alias: 'email',
value: 'mail@umbraco.com',
},
],
data: [
variants: [
{
alias: 'myHeadline',
value: 'The daily life at Umbraco HQ',
},
{
alias: 'myDescription',
value: 'Every day, a rabbit in a military costume greets me at the front door',
state: ContentStateModel.PUBLISHED,
publishDate: '2023-02-06T15:31:51.354764',
culture: 'en-us',
segment: null,
name: 'Blog post A',
createDate: '2023-02-06T15:31:46.876902',
updateDate: '2023-02-06T15:31:51.354764',
},
],
variants: [{ name: 'fake data' }],
/*
// Concept for node layout, separation of design from config and data.
layout: [
{
type: 'group',
children: [
{
type: 'property',
alias: 'myHeadline'
},
{
type: 'property',
alias: 'myDescription'
}
]
}
],
*/
},
{
name: 'Document 2',
type: 'document',
icon: 'favorite',
hasChildren: false,
key: '74e4008a-ea4f-4793-b924-15e02fd380d2',
isContainer: false,
parentKey: null,
noAccess: false,
isProtected: false,
isPublished: false,
isEdited: false,
isTrashed: false,
urls: [
{
culture: 'en-US',
url: '/',
},
],
templateKey: null,
key: 'c05da24d-7740-447b-9cdc-bd8ce2172e38',
contentTypeKey: '29643452-cff9-47f2-98cd-7de4b6807681',
properties: [
{
alias: 'myHeadline',
label: 'Text string label',
description: 'this is a text string property',
dataTypeKey: 'dt-textBox',
culture: null,
segment: null,
alias: 'masterText',
value: 'i have a master text',
},
{
alias: 'myDescription',
label: 'Textarea label',
description: 'This is the a textarea property',
dataTypeKey: 'dt-textArea',
culture: null,
segment: null,
alias: 'pageTitle',
value: 'with a page title',
},
{
alias: 'myExternalEditor',
label: 'My JS Property Editor',
description: 'This is the a external property',
dataTypeKey: 'dt-custom',
culture: null,
segment: null,
alias: 'blogPostText',
value: 'My first blog post',
},
{
alias: 'myContentPicker',
label: 'Content Picker',
description: 'This is a content picker',
dataTypeKey: 'dt-contentPicker',
culture: 'en-us',
segment: null,
alias: 'blogTextStringUnderMasterTab',
value: 'in the master tab',
},
{
culture: 'en-us',
segment: null,
alias: 'blogTextStringUnderGroupUnderMasterTab',
value: 'which is under another group in the tab',
},
{
culture: null,
segment: null,
alias: 'localBlogTabString',
value: '1234567',
},
],
data: [
variants: [
{
alias: 'myHeadline',
value: 'Is it all just fun and curling and scary rabbits?',
},
{
alias: 'myDescription',
value:
"So no, there's not confetti every day. And no, there's not champagne every week or a crazy rabbit running around 🐰",
},
{
alias: 'myExternalEditor',
value: 'Tex lkasdfkljdfsa 1',
},
{
alias: 'myContextExampleEditor',
value: '',
},
{
alias: 'myContentPicker',
value: '',
state: ContentStateModel.PUBLISHED,
publishDate: '2023-02-06T15:31:51.354764',
culture: 'en-us',
segment: null,
name: 'Blog post A',
createDate: '2023-02-06T15:31:46.876902',
updateDate: '2023-02-06T15:31:51.354764',
},
],
variants: [{ name: 'Variant 1' }],
},
{
name: 'Document 3',
urls: [],
templateKey: null,
key: 'fd56a0b5-01a0-4da2-b428-52773bfa9cc4',
contentTypeKey: '29643452-cff9-47f2-98cd-7de4b6807681',
properties: [
{
culture: null,
segment: null,
alias: 'masterText',
value: 'i have a master text B',
},
{
culture: null,
segment: null,
alias: 'pageTitle',
value: 'with a page title B',
},
{
culture: null,
segment: null,
alias: 'blogPostText',
value: 'My first blog post B',
},
{
culture: 'en-us',
segment: null,
alias: 'blogTextStringUnderMasterTab',
value: 'in the master tab B',
},
{
culture: 'en-us',
segment: null,
alias: 'blogTextStringUnderGroupUnderMasterTab',
value: 'which is under another group in the tab B',
},
{
culture: null,
segment: null,
alias: 'localBlogTabString',
value: '1234567890',
},
],
variants: [
{
state: ContentStateModel.DRAFT,
publishDate: '2023-02-06T15:32:24.957009',
culture: 'en-us',
segment: null,
name: 'Blog post B',
createDate: '2023-02-06T15:32:05.350038',
updateDate: '2023-02-06T15:32:24.957009',
},
],
},
];
// TODO: make tree data:
export const treeData: Array<DocumentTreeItemModel> = [
{
isProtected: false,
isPublished: true,
isEdited: false,
noAccess: false,
isTrashed: false,
key: 'all-property-editors-document-key',
isContainer: false,
parentKey: null,
name: 'All property editors',
type: 'document',
icon: 'document',
icon: 'icon-item-arrangement',
hasChildren: false,
},
{
isProtected: false,
isPublished: true,
isEdited: false,
noAccess: false,
isTrashed: false,
key: 'c05da24d-7740-447b-9cdc-bd8ce2172e38',
isContainer: false,
parentKey: null,
name: 'Blog post A',
type: 'document',
icon: 'icon-item-arrangement',
hasChildren: true,
key: 'cdd30288-2d1c-41b4-89a9-61647b4a10d5',
isContainer: false,
parentKey: null,
noAccess: false,
isProtected: false,
isPublished: false,
isEdited: false,
isTrashed: false,
properties: [
{
alias: 'myDescription',
label: 'Description',
description: 'Textarea property',
dataTypeKey: 'dt-textArea',
},
],
data: [
{
alias: 'myDescription',
value: 'Every day, a rabbit in a military costume greets me at the front door',
},
],
variants: [],
},
{
name: 'Document 4',
type: 'document',
icon: 'document',
hasChildren: false,
key: 'f6f7a5b2-e7c0-463a-97bc-6cb5b9bcf447',
isContainer: false,
parentKey: 'cdd30288-2d1c-41b4-89a9-61647b4a10d5',
noAccess: false,
isProtected: false,
isPublished: false,
isEdited: false,
noAccess: false,
isTrashed: false,
properties: [
{
alias: 'myDescription',
label: 'Description',
description: 'Textarea property',
dataTypeKey: 'dt-textArea',
},
],
data: [],
variants: [],
key: 'fd56a0b5-01a0-4da2-b428-52773bfa9cc4',
isContainer: false,
parentKey: 'c05da24d-7740-447b-9cdc-bd8ce2172e38',
name: 'Blog post B',
type: 'document',
icon: 'icon-item-arrangement',
hasChildren: false,
},
{
name: 'Document 5',
@@ -434,9 +422,6 @@ export const data: Array<DocumentDetails> = [
isPublished: false,
isEdited: false,
isTrashed: false,
properties: [],
data: [],
variants: [],
},
];
@@ -444,27 +429,29 @@ export const data: Array<DocumentDetails> = [
// TODO: all properties are optional in the server schema. I don't think this is correct.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
class UmbDocumentData extends UmbEntityData<DocumentDetails> {
class UmbDocumentData extends UmbEntityData<DocumentModel> {
private treeData = treeData;
constructor() {
super(data);
}
getTreeRoot(): PagedDocumentTreeItemModel {
const items = this.data.filter((item) => item.parentKey === null);
const items = this.treeData.filter((item) => item.parentKey === null);
const treeItems = items.map((item) => createDocumentTreeItem(item));
const total = items.length;
return { items: treeItems, total };
}
getTreeItemChildren(key: string): PagedDocumentTreeItemModel {
const items = this.data.filter((item) => item.parentKey === key);
const items = this.treeData.filter((item) => item.parentKey === key);
const treeItems = items.map((item) => createDocumentTreeItem(item));
const total = items.length;
return { items: treeItems, total };
}
getTreeItem(keys: Array<string>): Array<DocumentTreeItemModel> {
const items = this.data.filter((item) => keys.includes(item.key ?? ''));
const items = this.treeData.filter((item) => keys.includes(item.key ?? ''));
return items.map((item) => createDocumentTreeItem(item));
}
}

Some files were not shown because too many files have changed in this diff Show More