router for edit-tabs

This commit is contained in:
Niels Lyngsø
2023-02-17 15:52:37 +01:00
parent b895c0a1ca
commit 71869a239b
3 changed files with 259 additions and 64 deletions

View File

@@ -2,7 +2,11 @@ import { UmbWorkspaceContext } from '../../../shared/components/workspace/worksp
import { UmbDocumentRepository } from '../repository/document.repository';
import type { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface';
import { UmbDocumentTypeRepository } from '../../document-types/repository/document-type.repository';
import type { DocumentModel, DocumentTypeModel } from '@umbraco-cms/backend-api';
import type {
DocumentModel,
DocumentTypeModel,
DocumentTypePropertyTypeContainerModel,
} from '@umbraco-cms/backend-api';
import {
partialUpdateFrozenArray,
ObjectState,
@@ -31,6 +35,10 @@ export class UmbDocumentWorkspaceContext
#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);
//containers = this.#containers.asObservable();
constructor(host: UmbControllerHostInterface) {
super(host);
this.#host = host;
@@ -69,14 +77,31 @@ export class UmbDocumentWorkspaceContext
}
});
new UmbObserverController(this._host, await this.#documentTypeRepository.byKey(key), (data) => {
if (data) {
this.#documentTypes.appendOne(data);
this.loadDataTypeOfDocumentType(data);
new UmbObserverController(this._host, await this.#documentTypeRepository.byKey(key), (docType) => {
if (docType) {
this.#documentTypes.appendOne(docType);
this.initDocumentTypeContainers(docType);
this.loadDocumentTypeCompositions(docType);
}
});
}
async loadDocumentTypeCompositions(documentType: DocumentTypeModel) {
documentType.compositions?.forEach((composition) => {
this.loadDocumentType(composition.key);
});
}
async initDocumentTypeContainers(documentType: DocumentTypeModel) {
documentType.containers?.forEach((container) => {
console.log('add container', container);
this.#containers.appendOne(container);
});
}
/*
No need for this currently. The data types are loaded by the properties.
async loadDataTypeOfDocumentType(documentType?: DocumentTypeModel) {
if (!documentType) return;
@@ -93,12 +118,13 @@ export class UmbDocumentWorkspaceContext
//const { data } = await this.#dataTypeRepository.requestDetails(key);
/*new UmbObserverController(this._host, await this.#documentTypeRepository.byKey(key), (data) => {
if (data) {
this.#documentTypes.appendOne(data);
}
});*/
// new UmbObserverController(this._host, await this.#documentTypeRepository.byKey(key), (data) => {
// if (data) {
// this.#documentTypes.appendOne(data);
// }
//});
}
*/
getData() {
return this.#data.getValue();
@@ -150,15 +176,36 @@ export class UmbDocumentWorkspaceContext
}
*/
propertiesOf(culture: string | null, segment: string | null) {
propertyValuesOf(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);
propertyStructuresOf(containerKey: string | null) {
return this.#documentTypes.getObservablePart((data) =>
// TODO: some merging of properties across document types here:
data[0]?.properties?.filter((p) => containerKey === p.containerKey || null)
);
}
rootContainers(containerType: 'Group' | 'Tab') {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.parentKey === null && x.type === containerType);
});
}
containerByKey(key: DocumentTypePropertyTypeContainerModel['key']) {
return this.#containers.getObservablePart((data) => {
return data.filter((x) => x.key === key);
});
}
containersOf(parentKey: DocumentTypePropertyTypeContainerModel['parentKey'], containerType: 'Group' | 'Tab') {
return this.#containers.getObservablePart((data) => {
console.log(data, parentKey, containerType);
return data.filter((x) => x.parentKey === parentKey && x.type === containerType);
});
}
setPropertyValue(alias: string, value: unknown) {

View File

@@ -0,0 +1,136 @@
import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, property, 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, PropertyTypeContainerViewModelBaseModel } from '@umbraco-cms/backend-api';
@customElement('umb-document-workspace-view-edit-tab')
export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement {
static styles = [
UUITextStyles,
css`
:host {
display: block;
margin: var(--uui-size-layout-1);
}
`,
];
private _tabName?: string | undefined;
@property({ type: String })
public get tabName(): string | undefined {
return this._tabName;
}
public set tabName(value: string | undefined) {
if (this._tabName === value) return;
this._tabName = value;
this._observeContainers();
}
@state()
_propertyData: DocumentPropertyModel[] = [];
@state()
_groups: PropertyTypeContainerViewModelBaseModel[] = [];
_propertiesObservables: Map<string, unknown> = new Map();
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._observeContainers();
//this._observeContent();
});
}
private _observeProperties() {
if (!this._workspaceContext) return;
/*
Just get the properties for the current containers. (and eventually variants later)
*/
this.observe(
this._workspaceContext.propertyValuesOf(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'
);
}
private _observeContainers() {
if (!this._workspaceContext) return;
this.observe(
this._workspaceContext.containersOf(this.tabName, 'Group'),
(groups) => {
this._groups = groups || [];
},
'observeWorkspaceContextData'
);
}
private _getPropertiesOfGroup(group: PropertyTypeContainerViewModelBaseModel) {
if (!this._workspaceContext) return undefined;
this.observe(
this._workspaceContext.propertyValuesOf(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'
);
// cache observable
}
render() {
return 'hello worlds' + this._tabName;
/*repeat(
this._groups,
(group) => group.key,
(group) =>
html`
<uui-box>
${repeat(
this._getPropertiesOfGroup(group),
(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 UmbDocumentWorkspaceViewEditTabElement;
declare global {
interface HTMLElementTagNameMap {
'umb-document-workspace-view-edit-tab': UmbDocumentWorkspaceViewEditTabElement;
}
}

View File

@@ -2,13 +2,11 @@ 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 { IRoute } from 'router-slot';
import { UmbDocumentWorkspaceContext } from '../document-workspace.context';
import { UmbLitElement } from '@umbraco-cms/element';
import {
DocumentPropertyModel,
DocumentTypePropertyTypeModel,
PropertyTypeContainerViewModelBaseModel,
} from '@umbraco-cms/backend-api';
import { PropertyTypeContainerViewModelBaseModel } from '@umbraco-cms/backend-api';
import { UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/router';
@customElement('umb-document-workspace-view-edit')
export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement {
@@ -23,14 +21,17 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement {
];
@state()
_propertyData: DocumentPropertyModel[] = [];
@state()
_propertyStructures: DocumentTypePropertyTypeModel[] = [];
private _routes: IRoute[] = [];
@state()
_tabs: PropertyTypeContainerViewModelBaseModel[] = [];
@state()
private _routerPath?: string;
@state()
private _activePath = '';
private _workspaceContext?: UmbDocumentWorkspaceContext;
constructor() {
@@ -39,64 +40,75 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement {
// TODO: Figure out how to get the magic string for the workspace context.
this.consumeContext<UmbDocumentWorkspaceContext>('umbWorkspaceContext', (workspaceContext) => {
this._workspaceContext = workspaceContext;
this._observeContent();
this._observeTabs();
});
}
private _observeContent() {
private _observeTabs() {
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.propertyValuesOf(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'
);
*/
this.observe(
this._workspaceContext.containersOf(null, 'tab'),
this._workspaceContext.containersOf(null, 'Tab'),
(tabs) => {
// TODO: make tabs unique based on name.
this._tabs = tabs || [];
this._createRoutes();
},
'observeWorkspaceContextData'
);
}
private _createRoutes() {
const routes: any[] = [];
if (this._tabs.length > 0) {
this._tabs?.forEach((tab) => {
routes.push({
path: `tab/${encodeURI(tab.name || '').toString()}`,
component: () => import('./document-workspace-view-edit-tab.element'),
setup: (component: Promise<HTMLElement>) => {
(component as any).tabName = tab.name;
},
});
});
routes.push({
path: '',
redirectTo: routes[0]?.path,
});
routes.push({
path: '**',
redirectTo: routes[0]?.path,
});
}
this._routes = routes;
}
render() {
return html`
<uui-box>
<uui-tab-group>
${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> `
this._tabs,
(tab) => tab.key,
(tab) => {
const path = this._routerPath + '/tab/' + encodeURI(tab.name || '');
return html`<uui-tab label=${tab.name!} .active=${path === this._activePath} href=${path}
>${tab.name}</uui-tab
>`;
}
)}
</uui-box>
</uui-tab-group>
<umb-router-slot
.routes=${this._routes}
@init=${(event: UmbRouterSlotInitEvent) => {
this._routerPath = event.target.absoluteRouterPath;
}}
@change=${(event: UmbRouterSlotChangeEvent) => {
this._activePath = event.target.localActiveViewPath || '';
}}>
</umb-router-slot>
`;
}
}