diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts index 9832192b15..d89e2cb23c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts @@ -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([], (x) => x.key); documentTypes = this.#documentTypes.asObservable(); + // Notice the DocumentTypePropertyTypeContainerModel is equivalent to PropertyTypeContainerViewModelBaseModel, making it easy to generalize. + #containers = new ArrayState([], (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) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/document-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/document-workspace-view-edit-tab.element.ts new file mode 100644 index 0000000000..68bc452b87 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/document-workspace-view-edit-tab.element.ts @@ -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 = new Map(); + + private _workspaceContext?: UmbDocumentWorkspaceContext; + + constructor() { + super(); + + // TODO: Figure out how to get the magic string for the workspace context. + this.consumeContext('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` + + ${repeat( + this._getPropertiesOfGroup(group), + (property) => property.alias, + (property) => + html` x.alias === property.alias)?.value}> ` + )} + + ` + ) + );*/ + } +} + +export default UmbDocumentWorkspaceViewEditTabElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-document-workspace-view-edit-tab': UmbDocumentWorkspaceViewEditTabElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/document-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/document-workspace-view-edit.element.ts index a07fae0556..6474d5eb74 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/document-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/document-workspace-view-edit.element.ts @@ -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('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) => { + (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` - + ${repeat( - this._propertyStructures, - (property) => property.alias, - (property) => - html` x.alias === property.alias)?.value}> ` + this._tabs, + (tab) => tab.key, + (tab) => { + const path = this._routerPath + '/tab/' + encodeURI(tab.name || ''); + return html`${tab.name}`; + } )} - + + + { + this._routerPath = event.target.absoluteRouterPath; + }} + @change=${(event: UmbRouterSlotChangeEvent) => { + this._activePath = event.target.localActiveViewPath || ''; + }}> + `; } }