more property structure into its own class/manager

This commit is contained in:
Niels Lyngsø
2023-02-22 16:18:52 +01:00
parent 48b8963f06
commit ce8e59cd34
6 changed files with 147 additions and 116 deletions

View File

@@ -3,12 +3,8 @@ import { UmbDocumentRepository } from '../repository/document.repository';
import { UmbDocumentTypeRepository } from '../../document-types/repository/document-type.repository';
import { UmbWorkspaceVariableEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-variable-entity-context.interface';
import { UmbVariantId } from '../../../shared/variants/variant-id.class';
import type {
DocumentModel,
DocumentTypeModel,
DocumentTypePropertyTypeContainerModel,
DocumentTypePropertyTypeModel,
} from '@umbraco-cms/backend-api';
import { UmbWorkspacePropertyStructureManager } from '../../../shared/components/workspace/workspace-context/workspace-property-structure-manager.class';
import type { DocumentModel } from '@umbraco-cms/backend-api';
import { partialUpdateFrozenArray, ObjectState, ArrayState, UmbObserverController } from '@umbraco-cms/observable-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -28,7 +24,6 @@ export class UmbDocumentWorkspaceContext
{
#host: UmbControllerHostInterface;
#documentRepository: UmbDocumentRepository;
#documentTypeRepository: UmbDocumentTypeRepository;
/**
* The document is the current stored version of the document.
@@ -50,19 +45,17 @@ export class UmbDocumentWorkspaceContext
#activeVariantsInfo = new ArrayState<ActiveVariant>([], (x) => x.index);
activeVariantsInfo = this.#activeVariantsInfo.asObservable();
#documentTypes = new ArrayState<DocumentTypeModel>([], (x) => x.key);
documentTypes = this.#documentTypes.asObservable();
// Notice the DocumentTypePropertyTypeContainerModel is equivalent to PropertyTypeContainerViewModelBaseModel, making it easy to generalize.
#containers = new ArrayState<DocumentTypePropertyTypeContainerModel>([], (x) => x.key);
readonly structure;
constructor(host: UmbControllerHostInterface) {
super(host);
this.#host = host;
this.#documentRepository = new UmbDocumentRepository(this.#host);
this.#documentTypeRepository = new UmbDocumentTypeRepository(this.#host);
new UmbObserverController(this._host, this.documentTypeKey, (key) => this._loadDocumentType(key));
this.#documentRepository = new UmbDocumentRepository(this.#host);
this.structure = new UmbWorkspacePropertyStructureManager(this.#host, new UmbDocumentTypeRepository(this.#host));
new UmbObserverController(this._host, this.documentTypeKey, (key) => this.structure.loadType(key));
}
async load(entityKey: string) {
@@ -85,40 +78,6 @@ export class UmbDocumentWorkspaceContext
return data || undefined;
}
private 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), (docType) => {
if (docType) {
this.#documentTypes.appendOne(docType);
this._initDocumentTypeContainers(docType);
this._loadDocumentTypeCompositions(docType);
}
});
}
private async _loadDocumentTypeCompositions(documentType: DocumentTypeModel) {
documentType.compositions?.forEach((composition) => {
this._loadDocumentType(composition.key);
});
}
private async _initDocumentTypeContainers(documentType: DocumentTypeModel) {
documentType.containers?.forEach((container) => {
this.#containers.appendOne(container);
});
}
getData() {
return this.#draft.getValue() || {};
}
@@ -193,61 +152,6 @@ export class UmbDocumentWorkspaceContext
);
}
// TODO: Structure methods:
hasPropertyStructuresOf(containerKey: string | null) {
return this.#documentTypes.getObservablePart((docTypes) => {
return (
docTypes.find((docType) => {
return docType.properties?.find((property) => property.containerKey === containerKey);
}) !== undefined
);
});
}
rootPropertyStructures() {
return this.propertyStructuresOf(null);
}
propertyStructuresOf(containerKey: string | null) {
return this.#documentTypes.getObservablePart((docTypes) => {
const props: DocumentTypePropertyTypeModel[] = [];
docTypes.forEach((docType) => {
docType.properties?.forEach((property) => {
if (property.containerKey === containerKey) {
props.push(property);
}
});
});
return props;
});
}
rootContainers(containerType: 'Group' | 'Tab') {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.parentKey === null && x.type === containerType);
});
}
hasRootContainers(containerType: 'Group' | 'Tab') {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.parentKey === null && x.type === containerType).length > 0;
});
}
containersOfParentKey(
parentKey: DocumentTypePropertyTypeContainerModel['parentKey'],
containerType: 'Group' | 'Tab'
) {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.parentKey === parentKey && x.type === containerType);
});
}
containersByNameAndType(name: string, containerType: 'Group' | 'Tab') {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.name === name && x.type === containerType);
});
}
getPropertyValue(alias: string, variantId?: UmbVariantId): void {
const currentData = this.#draft.value;
if (currentData) {
@@ -259,7 +163,6 @@ export class UmbDocumentWorkspaceContext
}
setPropertyValue(alias: string, value: unknown, variantId?: UmbVariantId) {
const partialEntry = { value };
console.log('€€€€€setPropertyValue', alias, value, variantId?.toString());
const currentData = this.#draft.value;
if (currentData) {
const values = partialUpdateFrozenArray(

View File

@@ -67,7 +67,7 @@ export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement
// TODO: Should be no need to update this observable if its already there.
this.observe(
this._workspaceContext!.containersByNameAndType(this._containerName, this._containerType),
this._workspaceContext!.structure.containersByNameAndType(this._containerName, this._containerType),
(groupContainers) => {
this._groupContainers = groupContainers || [];
groupContainers.forEach((group) => {
@@ -86,7 +86,7 @@ export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement
// TODO: Should be no need to update this observable if its already there.
this.observe(
this._workspaceContext.propertyStructuresOf(group.key),
this._workspaceContext.structure.propertyStructuresOf(group.key),
(properties) => {
// If this need to be able to remove properties, we need to clean out the ones of this group.key before inserting them:
this._propertyStructure = this._propertyStructure.filter((x) => x.containerKey !== group.key);

View File

@@ -80,7 +80,7 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement {
this._tabContainers.forEach((container) => {
this.observe(
this._workspaceContext!.hasPropertyStructuresOf(container.key!),
this._workspaceContext!.structure.hasPropertyStructuresOf(container.key!),
(hasTabProperties) => {
this._hasTabProperties = hasTabProperties;
},
@@ -95,7 +95,7 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement {
if (this._tabName) {
this._groups = [];
this.observe(
this._workspaceContext.containersByNameAndType(this._tabName, 'Tab'),
this._workspaceContext.structure.containersByNameAndType(this._tabName, 'Tab'),
(tabContainers) => {
this._tabContainers = tabContainers || [];
if (this._tabContainers.length > 0) {
@@ -116,7 +116,7 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement {
this._tabContainers.forEach((container) => {
this.observe(
this._workspaceContext!.containersOfParentKey(container.key, 'Group'),
this._workspaceContext!.structure.containersOfParentKey(container.key, 'Group'),
this._insertGroupContainers,
'_observeGroupsOf_' + container.key
);
@@ -127,7 +127,11 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement {
if (!this._workspaceContext || !this._noTabName) return;
// This is where we potentially could observe root properties as well.
this.observe(this._workspaceContext!.rootContainers('Group'), this._insertGroupContainers, '_observeRootGroups');
this.observe(
this._workspaceContext!.structure.rootContainers('Group'),
this._insertGroupContainers,
'_observeRootGroups'
);
}
private _insertGroupContainers = (groupContainers: PropertyTypeContainerViewModelBaseModel[]) => {

View File

@@ -19,8 +19,6 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement {
`,
];
// TODO: get variant information via variant-content?
//private _hasRootProperties = false;
private _hasRootGroups = false;
@@ -52,7 +50,7 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement {
if (!this._workspaceContext) return;
this.observe(
this._workspaceContext.rootContainers('Tab'),
this._workspaceContext.structure.rootContainers('Tab'),
(tabs) => {
tabs.forEach((tab) => {
// Only add each tab name once, as our containers merge on name:
@@ -79,7 +77,7 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement {
*/
this.observe(
this._workspaceContext.hasRootContainers('Group'),
this._workspaceContext.structure.hasRootContainers('Group'),
(hasRootGroups) => {
this._hasRootGroups = hasRootGroups;
this._createRoutes();

View File

@@ -0,0 +1,126 @@
import { UmbDocumentTypeRepository } from '../../../../documents/document-types/repository/document-type.repository';
import {
DocumentTypeModel,
DocumentTypePropertyTypeModel,
PropertyTypeContainerViewModelBaseModel,
} from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { ArrayState, UmbObserverController } from '@umbraco-cms/observable-api';
export type PropertyContainerTypes = 'Group' | 'Tab';
// TODO: get this type from the repository, or use some generic type.
type T = DocumentTypeModel;
// TODO: make general interface for NodeTypeRepository, to replace UmbDocumentTypeRepository:
export class UmbWorkspacePropertyStructureManager<R extends UmbDocumentTypeRepository = UmbDocumentTypeRepository> {
#host: UmbControllerHostInterface;
#documentTypeRepository: R;
#documentTypes = new ArrayState<T>([], (x) => x.key);
#containers = new ArrayState<PropertyTypeContainerViewModelBaseModel>([], (x) => x.key);
constructor(host: UmbControllerHostInterface, typeRepository: R) {
this.#host = host;
this.#documentTypeRepository = typeRepository;
}
/**
* loadType will load the node type and all inherited and composed types.
* This will give us all the structure for properties and containers.
*/
public async loadType(key?: string) {
// TODO: I guess it would make sense to clean up, in this case we most likely don't need any of the old document types:
//this.#documentTypes.next([]);
await this._loadType(key);
}
private async _loadType(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.loadType(composition.key);
}
});
new UmbObserverController(this.#host, await this.#documentTypeRepository.byKey(key), (docType) => {
if (docType) {
this.#documentTypes.appendOne(docType);
this._initDocumentTypeContainers(docType);
this._loadDocumentTypeCompositions(docType);
}
});
}
private async _loadDocumentTypeCompositions(documentType: T) {
documentType.compositions?.forEach((composition) => {
this._loadType(composition.key);
});
}
private async _initDocumentTypeContainers(documentType: T) {
documentType.containers?.forEach((container) => {
this.#containers.appendOne(container);
});
}
hasPropertyStructuresOf(containerKey: string | null) {
return this.#documentTypes.getObservablePart((docTypes) => {
return (
docTypes.find((docType) => {
return docType.properties?.find((property) => property.containerKey === containerKey);
}) !== undefined
);
});
}
rootPropertyStructures() {
return this.propertyStructuresOf(null);
}
propertyStructuresOf(containerKey: string | null) {
return this.#documentTypes.getObservablePart((docTypes) => {
const props: DocumentTypePropertyTypeModel[] = [];
docTypes.forEach((docType) => {
docType.properties?.forEach((property) => {
if (property.containerKey === containerKey) {
props.push(property);
}
});
});
return props;
});
}
rootContainers(containerType: PropertyContainerTypes) {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.parentKey === null && x.type === containerType);
});
}
hasRootContainers(containerType: PropertyContainerTypes) {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.parentKey === null && x.type === containerType).length > 0;
});
}
containersOfParentKey(
parentKey: PropertyTypeContainerViewModelBaseModel['parentKey'],
containerType: PropertyContainerTypes
) {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.parentKey === parentKey && x.type === containerType);
});
}
containersByNameAndType(name: string, containerType: PropertyContainerTypes) {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.name === name && x.type === containerType);
});
}
}

View File

@@ -4,8 +4,8 @@ import { customElement, property, query } from 'lit/decorators.js';
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
import { UUIInputEvent } from '@umbraco-ui/uui-input';
import { UUIInputElement } from '@umbraco-ui/uui';
import { UmbChangeEvent, UmbInputEvent, UmbDeleteEvent } from '@umbraco-cms/events';
import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '../../../../../../core/modal';
import { UmbChangeEvent, UmbInputEvent, UmbDeleteEvent } from '@umbraco-cms/events';
import { UmbLitElement } from '@umbraco-cms/element';
/**