restructuring on UmbVariantId

This commit is contained in:
Niels Lyngsø
2023-02-21 14:39:08 +01:00
parent 7467f5f58e
commit 99f4d70051
14 changed files with 189 additions and 116 deletions

View File

@@ -1,8 +1,4 @@
{
"cssVariables.lookupFiles": [
"node_modules/@umbraco-ui/uui-css/dist/custom-properties.css"
],
"cSpell.words": [
"combobox"
]
"cssVariables.lookupFiles": ["node_modules/@umbraco-ui/uui-css/dist/custom-properties.css"],
"cSpell.words": ["combobox", "variantable"]
}

View File

@@ -0,0 +1,25 @@
import { BehaviorSubject } from 'rxjs';
interface ClassStateData {
equal(otherClass: ClassStateData): boolean;
}
/**
* @export
* @class DeepState
* @extends {BehaviorSubject<T>}
* @description - A RxJS BehaviorSubject which deepFreezes the data to ensure its not manipulated from any implementations.
* Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
*/
export class ClassState<T extends ClassStateData | undefined | null> extends BehaviorSubject<T> {
constructor(initialData: T) {
super(initialData);
}
next(newData: T): void {
const currentValue = this.getValue();
if (newData && currentValue ? currentValue.equal(newData) : true) {
super.next(newData);
}
}
}

View File

@@ -1,7 +1,7 @@
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
import { UmbDocumentRepository } from '../repository/document.repository';
import { UmbDocumentTypeRepository } from '../../document-types/repository/document-type.repository';
import { UmbWorkspaceVariantableEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-variantable-entity-context.interface';
import { UmbWorkspaceVariableEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-variable-entity-context.interface';
import { UmbVariantId } from '../../../shared/variants/variant-id.class';
import type {
DocumentModel,
@@ -16,13 +16,14 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export type ActiveVariant = {
index: number;
variantId: UmbVariantId;
culture: string | null;
segment: string | null;
};
type EntityType = DocumentModel;
export class UmbDocumentWorkspaceContext
extends UmbWorkspaceContext
implements UmbWorkspaceVariantableEntityContextInterface<EntityType | undefined>
implements UmbWorkspaceVariableEntityContextInterface<EntityType | undefined>
{
#isNew = false;
#host: UmbControllerHostInterface;
@@ -138,12 +139,12 @@ export class UmbDocumentWorkspaceContext
return 'document';
}
setActiveVariant(index: number, variantId: UmbVariantId) {
setActiveVariant(index: number, culture: string | null, segment: string | null) {
const activeVariants = [...this.#activeVariantsInfo.getValue()];
if (index < activeVariants.length) {
activeVariants[index] = { index, variantId: variantId };
activeVariants[index] = { index, culture, segment };
} else {
activeVariants.push({ index, variantId: variantId });
activeVariants.push({ index, culture, segment });
}
this.#activeVariantsInfo.next(activeVariants);
}

View File

@@ -6,7 +6,8 @@ import type { UmbWorkspaceEntityElement } from '../../../shared/components/works
import { UmbVariantId } from '../../../shared/variants/variant-id.class';
import { ActiveVariant, UmbDocumentWorkspaceContext } from './document-workspace.context';
import { UmbLitElement } from '@umbraco-cms/element';
import '../../../shared/components/workspace/workspace-variant-content/workspace-variant-content.element';
import '../../../shared/components/workspace/workspace-variant/workspace-variant.element';
import { DocumentModel } from '@umbraco-cms/backend-api';
@customElement('umb-document-workspace')
export class UmbDocumentWorkspaceElement extends UmbLitElement implements UmbWorkspaceEntityElement {
@@ -46,10 +47,9 @@ export class UmbDocumentWorkspaceElement extends UmbLitElement implements UmbWor
this._gotDocumentData(data);
}
private _gotDocumentData(data: any) {
private _gotDocumentData(data: DocumentModel | undefined) {
if (data && data.variants && data.variants.length > 0) {
// TODO: consider making a DocumentVariant object for this VariantId:
this._workspaceContext.setActiveVariant(0, new UmbVariantId(data.variants[0]));
this._workspaceContext.setActiveVariant(0, data.variants[0].culture || null, data.variants[0].segment || null);
this._unique = data.key;
} else {
// Fail beautifully?
@@ -62,12 +62,12 @@ export class UmbDocumentWorkspaceElement extends UmbLitElement implements UmbWor
this._workspaceSplitViews,
(view) => view.index,
(view) => html`
<umb-workspace-variant-content alias="Umb.Workspace.Document" .splitViewIndex=${view.index}>
<umb-workspace-variant alias="Umb.Workspace.Document" .splitViewIndex=${view.index}>
<umb-workspace-action-menu
slot="action-menu"
entity-type="document"
unique="${this._unique!}"></umb-workspace-action-menu>
</umb-workspace-variant-content>
</umb-workspace-variant>
`
)
: nothing;

View File

@@ -9,6 +9,9 @@ import {
DocumentTypePropertyTypeModel,
PropertyTypeContainerViewModelBaseModel,
} from '@umbraco-cms/backend-api';
import UmbWorkspaceVariantContentElement from 'src/backoffice/shared/components/workspace/workspace-variant/workspace-variant.element';
import { UmbWorkspaceVariantContext } from 'src/backoffice/shared/components/workspace/workspace-variant/workspace-variant.context';
import { UmbVariantId } from 'src/backoffice/shared/variants/variant-id.class';
@customElement('umb-document-workspace-view-edit-properties')
export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement {

View File

@@ -3,7 +3,8 @@ import { css, html } from 'lit';
import { ifDefined } from 'lit-html/directives/if-defined.js';
import { customElement, property, state } from 'lit/decorators.js';
import { UmbDataTypeRepository } from '../../../settings/data-types/repository/data-type.repository';
import { UmbWorkspacePropertySetContext } from '../workspace/workspace-context/workspace-property-set.context';
import { UmbVariantId } from '../../variants/variant-id.class';
import { UmbDocumentWorkspaceContext } from '../../../documents/documents/workspace/document-workspace.context';
import type { DataTypeModel, DataTypePropertyModel, PropertyTypeViewModelBaseModel } from '@umbraco-cms/backend-api';
import '../workspace-property/workspace-property.element';
import { UmbLitElement } from '@umbraco-cms/element';
@@ -46,28 +47,35 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement {
@state()
private _value?: unknown;
private _propertySetContext?: UmbWorkspacePropertySetContext;
/**
* VariantId. A Variant Configuration to identify which the variant of its value.
* @public
* @type {UmbVariantId}
* @attr
* @default undefined
*/
@property({ type: Object, attribute: false })
private _variantId?: UmbVariantId;
private _workspaceContext?: UmbDocumentWorkspaceContext;
constructor() {
super();
this.consumeContext('umbWorkspacePropertySetContext', (propertySetContext: UmbWorkspacePropertySetContext) => {
this._propertySetContext = propertySetContext;
this.consumeContext('umbWorkspaceContext', (workspaceContext: UmbDocumentWorkspaceContext) => {
this._workspaceContext = workspaceContext;
this._observeProperty();
});
}
private _observeProperty() {
if (!this._propertySetContext || !this.property || !this._property?.alias) return;
console.log('_observeProperty');
if (!this._workspaceContext || !this.property || !this._property?.alias) return;
this.observe(
this._propertySetContext.propertyValueByAlias(this._property.alias),
this._workspaceContext.propertyValueByAlias(this._property.alias, this._variantId),
(value) => {
console.log('got value', value);
this._value = value;
},
'observeValue'
'_observePropertyValueByAlias'
);
}
@@ -94,6 +102,7 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement {
description=${ifDefined(this._property?.description || undefined)}
property-editor-ui-alias=${ifDefined(this._propertyEditorUiAlias)}
.value=${this._value}
.variantId=${this._variantId}
.config=${this._dataTypeData}></umb-workspace-property>`;
}
}

View File

@@ -3,7 +3,7 @@ import { css, html, nothing } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui';
import { repeat } from 'lit-html/directives/repeat.js';
import { UmbVariantContentContext } from '../workspace/workspace-variant-content/variant-content.context';
import { UmbWorkspaceVariantContext } from '../workspace/workspace-variant/workspace-variant.context';
import { UmbDocumentWorkspaceContext } from '../../../documents/documents/workspace/document-workspace.context';
import { UmbLitElement } from '@umbraco-cms/element';
import type { DocumentVariantModel } from '@umbraco-cms/backend-api';
@@ -46,7 +46,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
_variants: Array<DocumentVariantModel> = [];
private _workspaceContext?: UmbDocumentWorkspaceContext;
private _variantContext?: UmbVariantContentContext;
private _variantContext?: UmbWorkspaceVariantContext;
@state()
private _name?: string;
@@ -66,7 +66,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
this._observeVariants();
});
this.consumeContext<UmbVariantContentContext>('umbVariantContext', (instance) => {
this.consumeContext<UmbWorkspaceVariantContext>('umbWorkspaceVariantContext', (instance) => {
this._variantContext = instance;
this._observeVariantContext();
});

View File

@@ -0,0 +1,78 @@
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { UmbVariantId } from '../../variants/variant-id.class';
import type { PropertyTypeViewModelBaseModel } from '@umbraco-cms/backend-api';
import '../workspace-property/workspace-property.element';
import { UmbLitElement } from '@umbraco-cms/element';
import { UmbWorkspaceVariantContext } from '../workspace/workspace-variant/workspace-variant.context';
@customElement('umb-variantable-property')
export class UmbVariantablePropertyElement extends UmbLitElement {
static styles = [
UUITextStyles,
css`
:host {
display: block;
}
`,
];
private _property?: PropertyTypeViewModelBaseModel | undefined;
@property({ type: Object, attribute: false })
public get property(): PropertyTypeViewModelBaseModel | undefined {
return this._property;
}
public set property(value: PropertyTypeViewModelBaseModel | undefined) {
this._property = value;
this._updatePropertyVariantId();
}
private _variantContext?: UmbWorkspaceVariantContext;
@state()
private _workspaceVariantId?: UmbVariantId;
@state()
private _propertyVariantId?: UmbVariantId;
constructor() {
super();
this.consumeContext('umbWorkspaceVariantContext', (workspaceContext: UmbWorkspaceVariantContext) => {
this._variantContext = workspaceContext;
this._observeVariantContext();
});
}
private _observeVariantContext() {
if (!this._variantContext || !this.property) return;
this.observe(this._variantContext.variantId, (variantId) => {
this._workspaceVariantId = variantId;
this._updatePropertyVariantId();
});
}
private _updatePropertyVariantId() {
if (this._workspaceVariantId && this.property) {
const newVariantId = new UmbVariantId(
this.property.varyByCulture ? this._workspaceVariantId.culture : null,
this.property.varyBySegment ? this._workspaceVariantId.segment : null
);
if (!this._propertyVariantId || !newVariantId.equal(this._propertyVariantId)) {
this._propertyVariantId = newVariantId;
}
}
}
render() {
return html`<umb-property-type-based-property
.property=${this._property}
.variantId=${this._propertyVariantId}></umb-property-type-based-property>`;
}
}
declare global {
interface HTMLElementTagNameMap {
'umb-variantable-property': UmbVariantablePropertyElement;
}
}

View File

@@ -1,4 +1,5 @@
import { UmbWorkspacePropertySetContextInterface } from '../workspace/workspace-context/workspace-property-set-context.interface';
import { UmbVariantId } from '../../variants/variant-id.class';
import { UmbWorkspaceVariableEntityContextInterface } from '../workspace/workspace-context/workspace-variable-entity-context.interface';
import type { DataTypeModel } from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { ObjectState } from '@umbraco-cms/observable-api';
@@ -24,15 +25,17 @@ export class UmbWorkspacePropertyContext<ValueType = unknown> {
public readonly value = this._data.getObservablePart((data) => data.value);
public readonly config = this._data.getObservablePart((data) => data.config);
private _propertySetContext?: UmbWorkspacePropertySetContextInterface;
private _variantId?: UmbVariantId;
private _workspaceContext?: UmbWorkspaceVariableEntityContextInterface;
constructor(host: UmbControllerHostInterface) {
// TODO: Figure out how to get the magic string in a better way.
new UmbContextConsumerController<UmbWorkspacePropertySetContextInterface>(
new UmbContextConsumerController<UmbWorkspaceVariableEntityContextInterface>(
host,
'umbWorkspacePropertySetContext',
'umbWorkspaceContext',
(workspaceContext) => {
this._propertySetContext = workspaceContext;
this._workspaceContext = workspaceContext;
}
);
@@ -55,12 +58,15 @@ export class UmbWorkspacePropertyContext<ValueType = unknown> {
const alias = this._data.getValue().alias;
if (alias) {
this._propertySetContext?.setPropertyValue(alias, value);
this._workspaceContext?.setPropertyValue(alias, value, this._variantId);
}
}
public setConfig(config: WorkspacePropertyData<ValueType>['config']) {
this._data.update({ config });
}
public setVariantId(variantId: UmbVariantId | undefined) {
this._variantId = variantId;
}
public resetValue() {
this.setValue(null); // TODO: We should get the default value from Property Editor maybe even later the DocumentType, as that would hold the default value for the property.

View File

@@ -3,8 +3,7 @@ import { UmbVariantId } from '../../../variants/variant-id.class';
import type { UmbWorkspaceEntityContextInterface } from './workspace-entity-context.interface';
import type { PropertyViewModelBaseModel } from '@umbraco-cms/backend-api';
export interface UmbWorkspaceVariantableEntityContextInterface<T = unknown>
extends UmbWorkspaceEntityContextInterface<T> {
export interface UmbWorkspaceVariableEntityContextInterface<T = unknown> extends UmbWorkspaceEntityContextInterface<T> {
getName(variantId?: UmbVariantId): void;
setName(name: string, variantId?: UmbVariantId): void;

View File

@@ -1,37 +0,0 @@
import { UmbVariantId } from '../../../variants/variant-id.class';
import { UmbWorkspacePropertySetContextInterface } from './workspace-property-set-context.interface';
import { UmbWorkspaceVariantableEntityContextInterface } from './workspace-variantable-entity-context.interface';
import { UmbContextProviderController } from '@umbraco-cms/context-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export class UmbWorkspaceVariantPropertySetContext implements UmbWorkspacePropertySetContextInterface {
workspaceContext: UmbWorkspaceVariantableEntityContextInterface;
variantId: UmbVariantId;
constructor(
host: UmbControllerHostInterface,
workspaceContext: UmbWorkspaceVariantableEntityContextInterface,
variantId: UmbVariantId
) {
this.workspaceContext = workspaceContext;
this.variantId = variantId;
new UmbContextProviderController(host, 'umbWorkspacePropertySetContext', this);
}
setVariantId(variantId: UmbVariantId) {
this.variantId = variantId;
}
propertyDataByAlias(alias: string) {
return this.workspaceContext.propertyDataByAlias(alias, this.variantId);
}
propertyValueByAlias(alias: string) {
return this.workspaceContext.propertyValueByAlias(alias, this.variantId);
}
getPropertyValue(alias: string) {
return this.workspaceContext.getPropertyValue(alias, this.variantId);
}
setPropertyValue(alias: string, value: unknown) {
return this.workspaceContext.setPropertyValue(alias, value, this.variantId);
}
}

View File

@@ -2,18 +2,16 @@ import {
ActiveVariant,
UmbDocumentWorkspaceContext,
} from '../../../../documents/documents/workspace/document-workspace.context';
//import { DocumentModel } from '@umbraco-cms/backend-api';
import { UmbVariantId } from '../../../variants/variant-id.class';
import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/context-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { NumberState, ObjectState, UmbObserverController } from '@umbraco-cms/observable-api';
import { DocumentVariantModel } from '@umbraco-cms/backend-api';
import { UmbWorkspaceVariantableEntityContextInterface } from '../workspace-context/workspace-variantable-entity-context.interface';
import { UmbWorkspaceVariantPropertySetContext } from '../workspace-context/workspace-variant-property-set.context';
import { UmbVariantId } from 'src/backoffice/shared/variants/variant-id.class';
import { ClassState } from 'libs/observable-api/class-state';
//type EntityType = DocumentModel;
export class UmbVariantContentContext {
export class UmbWorkspaceVariantContext {
#host: UmbControllerHostInterface;
#workspaceContext?: UmbDocumentWorkspaceContext;
@@ -28,38 +26,32 @@ export class UmbVariantContentContext {
culture = this.#currentVariant.getObservablePart((x) => x?.culture);
segment = this.#currentVariant.getObservablePart((x) => x?.segment);
private _variantObserver?: UmbObserverController<ActiveVariant>;
#variantId = new ClassState<UmbVariantId | undefined>(undefined);
variantId = this.#variantId.asObservable();
private _variantId?: UmbVariantId;
#propertySetContext?: UmbWorkspaceVariantPropertySetContext;
private _currentVariantObserver?: UmbObserverController<ActiveVariant>;
constructor(host: UmbControllerHostInterface) {
this.#host = host;
new UmbContextProviderController(host, 'umbVariantContext', this);
new UmbContextProviderController(host, 'umbWorkspaceVariantContext', this);
// How do we ensure this connects to a document workspace context? and not just any other context? (We could start providing workspace contexts twice, under the general name and under a specific name)
// TODO: Figure out if this is the best way to consume the context or if it can be strongly typed with an UmbContextToken
new UmbContextConsumerController(host, 'umbWorkspaceContext', (context) => {
this.#workspaceContext = context as UmbDocumentWorkspaceContext;
this._providePropertySetContext();
this._observeVariant();
});
this.#index.subscribe(() => {
this.index.subscribe(() => {
this._observeVariant();
});
}
private _providePropertySetContext() {
if (this.#propertySetContext || !this.#workspaceContext || !this._variantId) return;
this.#propertySetContext = new UmbWorkspaceVariantPropertySetContext(
this.#host,
this.#workspaceContext,
this._variantId
);
private _setVariantId(culture: string | null, segment: string | null) {
const variantId = new UmbVariantId(culture, segment);
this.#variantId.next(variantId);
return variantId;
}
private _observeVariant() {
@@ -68,15 +60,15 @@ export class UmbVariantContentContext {
const index = this.#index.getValue();
if (index === undefined) return;
this._variantObserver?.destroy();
this._variantObserver = new UmbObserverController(
this._currentVariantObserver?.destroy();
this._currentVariantObserver = new UmbObserverController(
this.#host,
this.#workspaceContext.activeVariantInfoByIndex(index),
async (activeVariantInfo) => {
this._variantId = activeVariantInfo.variantId;
const currentVariant = await this.#workspaceContext?.getVariant(this._variantId);
if (!activeVariantInfo) return;
const variantId = this._setVariantId(activeVariantInfo.culture, activeVariantInfo.segment);
const currentVariant = await this.#workspaceContext?.getVariant(variantId);
this.#currentVariant.next(currentVariant);
this._providePropertySetContext();
},
'_observeVariant'
);
@@ -92,8 +84,9 @@ export class UmbVariantContentContext {
}
public setName(newName: string) {
if (!this.#workspaceContext || !this._variantId) return;
this.#workspaceContext.setName(newName, this._variantId);
const variantId = this.#variantId.getValue();
if (!this.#workspaceContext || !variantId) return;
this.#workspaceContext.setName(newName, variantId);
}
/**

View File

@@ -6,7 +6,7 @@ import '../workspace-layout/workspace-layout.element';
// Lazy load
// TODO: Make this dynamic, use load-extensions method to loop over extensions for this node.
import { UmbVariantContentContext } from './variant-content.context';
import { UmbWorkspaceVariantContext } from './workspace-variant.context';
import { UmbLitElement } from '@umbraco-cms/element';
/**
@@ -15,7 +15,7 @@ import { UmbLitElement } from '@umbraco-cms/element';
* As well breadcrumbs etc.
*
*/
@customElement('umb-workspace-variant-content')
@customElement('umb-workspace-variant')
export class UmbWorkspaceVariantContentElement extends UmbLitElement {
static styles = [
UUITextStyles,
@@ -47,7 +47,7 @@ export class UmbWorkspaceVariantContentElement extends UmbLitElement {
this.variantContext.setSplitViewIndex(index);
}
variantContext = new UmbVariantContentContext(this);
variantContext = new UmbWorkspaceVariantContext(this);
render() {
return html`
@@ -68,6 +68,6 @@ export default UmbWorkspaceVariantContentElement;
declare global {
interface HTMLElementTagNameMap {
'umb-workspace-variant-content': UmbWorkspaceVariantContentElement;
'umb-workspace-variant': UmbWorkspaceVariantContentElement;
}
}

View File

@@ -1,15 +1,15 @@
import { VariantViewModelBaseModel } from '@umbraco-cms/backend-api';
export class UmbVariantId {
culture: string | null;
segment: string | null;
constructor(variantData?: VariantViewModelBaseModel) {
this.culture = variantData?.culture || null;
this.segment = variantData?.segment || null;
}
// prettier-ignore
constructor(
public culture: string | null = null,
public segment: string | null = null
) {}
public compare(obj: { culture?: string | null; segment?: string | null }): boolean {
return this.culture === (obj.culture || null) && this.segment === (obj.segment || null);
}
public equal(variantId: UmbVariantId): boolean {
return this.culture === variantId.culture && this.segment === variantId.segment;
}
}