Merge remote-tracking branch 'origin/main' into bugfix/media-picker-type
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
import type { UmbClassInterface } from './class.interface.js';
|
||||
import type { UmbControllerHost, UmbController } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export interface UmbClassMixinInterface extends UmbClassInterface, UmbController {
|
||||
_host: UmbControllerHost;
|
||||
}
|
||||
@@ -4,19 +4,63 @@ import type {
|
||||
UmbContextProviderController,
|
||||
UmbContextToken,
|
||||
} from '../context-api/index.js';
|
||||
import type { UmbControllerHost, UmbController } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbControllerAlias, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { ObserverCallback, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
|
||||
export interface UmbClassMixinInterface extends UmbControllerHost, UmbController {
|
||||
observe<T>(
|
||||
source: Observable<T> | { asObservable: () => Observable<T> },
|
||||
callback: (_value: T) => void,
|
||||
unique?: string,
|
||||
): UmbObserverController<T>;
|
||||
export interface UmbClassInterface extends UmbControllerHost {
|
||||
/**
|
||||
* @description Observe an Observable. An Observable is a declared source of data that can be observed. An observables is declared from a UmbState.
|
||||
* @param {Observable<T>} source An Observable to observe from.
|
||||
* @param {method} callback Callback method called when data is changed.
|
||||
* @return {UmbObserverController} Reference to the created Observer Controller instance.
|
||||
* @memberof UmbClassMixin
|
||||
*/
|
||||
observe<
|
||||
ObservableType extends Observable<T>,
|
||||
T,
|
||||
SpecificT = ObservableType extends Observable<infer U>
|
||||
? ObservableType extends undefined
|
||||
? U | undefined
|
||||
: U
|
||||
: undefined,
|
||||
R extends UmbObserverController<SpecificT> = UmbObserverController<SpecificT>,
|
||||
SpecificR extends R | undefined = ObservableType extends undefined ? R | undefined : R,
|
||||
>(
|
||||
// This type dance checks if the Observable given could be undefined, if it potentially could be undefined it means that this potentially could return undefined and then call the callback with undefined. [NL]
|
||||
source: ObservableType | undefined,
|
||||
callback: ObserverCallback<SpecificT>,
|
||||
controllerAlias?: UmbControllerAlias,
|
||||
): SpecificR;
|
||||
|
||||
/**
|
||||
* @description Provide a context API for this or child elements.
|
||||
* @param {string} contextAlias
|
||||
* @param {instance} instance The API instance to be exposed.
|
||||
* @return {UmbContextProviderController} Reference to the created Context Provider Controller instance
|
||||
* @memberof UmbClassMixin
|
||||
*/
|
||||
provideContext<R = unknown>(alias: string | UmbContextToken<R>, instance: R): UmbContextProviderController<R>;
|
||||
|
||||
/**
|
||||
* @description Setup a subscription for a context. The callback is called when the context is resolved.
|
||||
* @param {string} contextAlias
|
||||
* @param {method} callback Callback method called when context is resolved.
|
||||
* @return {UmbContextConsumerController} Reference to the created Context Consumer Controller instance
|
||||
* @memberof UmbClassMixin
|
||||
*/
|
||||
consumeContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
callback: UmbContextCallback<ResultType>,
|
||||
): UmbContextConsumerController<BaseType, ResultType>;
|
||||
|
||||
/**
|
||||
* @description Retrieve a context. Notice this is a one time retrieving of a context, meaning if you expect this to be up to date with reality you should instead use the consumeContext method.
|
||||
* @param {string} contextAlias
|
||||
* @return {Promise<ContextType>} A Promise with the reference to the Context Api Instance
|
||||
* @memberof UmbClassMixin
|
||||
*/
|
||||
getContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
): Promise<ResultType>;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { UmbClassMixinInterface } from './class.interface.js';
|
||||
import type { UmbClassMixinInterface } from './class-mixin.interface.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { ClassConstructor } from '@umbraco-cms/backoffice/extension-api';
|
||||
import {
|
||||
type UmbControllerHost,
|
||||
UmbControllerHostMixin,
|
||||
type UmbController,
|
||||
type UmbControllerAlias,
|
||||
} from '@umbraco-cms/backoffice/controller-api';
|
||||
import {
|
||||
@@ -13,89 +12,16 @@ import {
|
||||
UmbContextConsumerController,
|
||||
UmbContextProviderController,
|
||||
} from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { type ObserverCallback, UmbObserverController, simpleHashCode } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
type UmbClassMixinConstructor = new (
|
||||
host: UmbControllerHost,
|
||||
controllerAlias?: UmbControllerAlias,
|
||||
) => UmbClassMixinDeclaration;
|
||||
) => UmbClassMixinInterface;
|
||||
|
||||
// TODO: we need the interface from EventTarget to be part of the controller base. As a temp solution the UmbClassMixinDeclaration extends EventTarget.
|
||||
declare class UmbClassMixinDeclaration extends EventTarget implements UmbClassMixinInterface {
|
||||
_host: UmbControllerHost;
|
||||
|
||||
/**
|
||||
* @description Observe a RxJS source of choice.
|
||||
* @param {Observable<T>} source RxJS source
|
||||
* @param {method} callback Callback method called when data is changed.
|
||||
* @return {UmbObserverController} Reference to a Observer Controller instance
|
||||
* @memberof UmbClassMixin
|
||||
*/
|
||||
observe<T>(
|
||||
source: Observable<T>,
|
||||
callback: (_value: T) => void,
|
||||
controllerAlias?: UmbControllerAlias,
|
||||
): UmbObserverController<T>;
|
||||
|
||||
/**
|
||||
* @description Provide a context API for this or child elements.
|
||||
* @param {string} contextAlias
|
||||
* @param {instance} instance The API instance to be exposed.
|
||||
* @return {UmbContextProviderController} Reference to a Context Provider Controller instance
|
||||
* @memberof UmbClassMixin
|
||||
*/
|
||||
provideContext<
|
||||
BaseType = unknown,
|
||||
ResultType extends BaseType = BaseType,
|
||||
InstanceType extends ResultType = ResultType,
|
||||
>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
instance: InstanceType,
|
||||
): UmbContextProviderController<BaseType, ResultType, InstanceType>;
|
||||
|
||||
/**
|
||||
* @description Setup a subscription for a context. The callback is called when the context is resolved.
|
||||
* @param {string} contextAlias
|
||||
* @param {method} callback Callback method called when context is resolved.
|
||||
* @return {UmbContextConsumerController} Reference to a Context Consumer Controller instance
|
||||
* @memberof UmbClassMixin
|
||||
*/
|
||||
consumeContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
callback: UmbContextCallback<ResultType>,
|
||||
): UmbContextConsumerController<BaseType, ResultType>;
|
||||
|
||||
/**
|
||||
* @description Retrieve a context. Notice this is a one time retrieving of a context, meaning if you expect this to be up to date with reality you should instead use the consumeContext method.
|
||||
* @param {string} contextAlias
|
||||
* @return {Promise<ContextType>} A Promise with the reference to the Context Api Instance
|
||||
* @memberof UmbClassMixin
|
||||
*/
|
||||
getContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
): Promise<ResultType>;
|
||||
|
||||
hasController(controller: UmbController): boolean;
|
||||
getControllers(filterMethod: (ctrl: UmbController) => boolean): UmbController[];
|
||||
addController(controller: UmbController): void;
|
||||
removeControllerByAlias(controllerAlias: UmbControllerAlias): void;
|
||||
removeController(controller: UmbController): void;
|
||||
getHostElement(): Element;
|
||||
|
||||
get controllerAlias(): UmbControllerAlias;
|
||||
hostConnected(): void;
|
||||
hostDisconnected(): void;
|
||||
|
||||
/**
|
||||
* @description Destroys the controller and removes it from the host.
|
||||
* @memberof UmbClassMixin
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export const UmbClassMixin = <T extends ClassConstructor>(superClass: T) => {
|
||||
class UmbClassMixinClass extends UmbControllerHostMixin(superClass) implements UmbControllerHost {
|
||||
protected _host: UmbControllerHost;
|
||||
export const UmbClassMixin = <T extends ClassConstructor<EventTarget>>(superClass: T) => {
|
||||
class UmbClassMixinClass extends UmbControllerHostMixin(superClass) implements UmbClassMixinInterface {
|
||||
_host: UmbControllerHost;
|
||||
protected _controllerAlias: UmbControllerAlias;
|
||||
|
||||
constructor(host: UmbControllerHost, controllerAlias?: UmbControllerAlias) {
|
||||
@@ -113,12 +39,37 @@ export const UmbClassMixin = <T extends ClassConstructor>(superClass: T) => {
|
||||
return this._controllerAlias;
|
||||
}
|
||||
|
||||
observe<T>(
|
||||
source: Observable<T>,
|
||||
callback: (_value: T) => void,
|
||||
observe<
|
||||
ObservableType extends Observable<T>,
|
||||
T,
|
||||
SpecificT = ObservableType extends Observable<infer U>
|
||||
? ObservableType extends undefined
|
||||
? U | undefined
|
||||
: U
|
||||
: undefined,
|
||||
R extends UmbObserverController<SpecificT> = UmbObserverController<SpecificT>,
|
||||
SpecificR extends R | undefined = ObservableType extends undefined ? R | undefined : R,
|
||||
>(
|
||||
// This type dance checks if the Observable given could be undefined, if it potentially could be undefined it means that this potentially could return undefined and then call the callback with undefined. [NL]
|
||||
source: ObservableType | undefined,
|
||||
callback: ObserverCallback<SpecificT>,
|
||||
controllerAlias?: UmbControllerAlias,
|
||||
): UmbObserverController<T> {
|
||||
return new UmbObserverController<T>(this, source, callback, controllerAlias);
|
||||
): SpecificR {
|
||||
// Fallback to use a hash of the provided method, but only if the alias is undefined.
|
||||
controllerAlias ??= controllerAlias === undefined ? simpleHashCode(callback.toString()) : undefined;
|
||||
|
||||
if (source) {
|
||||
return new UmbObserverController<T>(
|
||||
this,
|
||||
source,
|
||||
callback as unknown as ObserverCallback<T>,
|
||||
controllerAlias,
|
||||
) as unknown as SpecificR;
|
||||
} else {
|
||||
callback(undefined as SpecificT);
|
||||
this.removeControllerByAlias(controllerAlias);
|
||||
return undefined as SpecificR;
|
||||
}
|
||||
}
|
||||
|
||||
provideContext<
|
||||
@@ -159,5 +110,5 @@ export const UmbClassMixin = <T extends ClassConstructor>(superClass: T) => {
|
||||
}
|
||||
}
|
||||
|
||||
return UmbClassMixinClass as unknown as UmbClassMixinConstructor & UmbClassMixinDeclaration;
|
||||
return UmbClassMixinClass as unknown as UmbClassMixinConstructor & T;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { UmbController } from '../controller-api/controller.interface.js';
|
||||
import { UmbClassMixin } from './class.mixin.js';
|
||||
import type { ClassConstructor } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
/**
|
||||
* This mixin enables a web-component to host controllers.
|
||||
* This enables controllers to be added to the life cycle of this element.
|
||||
*
|
||||
*/
|
||||
export abstract class UmbControllerBase extends UmbClassMixin(EventTarget) implements UmbController {}
|
||||
export abstract class UmbControllerBase
|
||||
extends UmbClassMixin<ClassConstructor<EventTarget>>(EventTarget)
|
||||
implements UmbController {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './class.interface.js';
|
||||
export * from './class-mixin.interface.js';
|
||||
export * from './class.mixin.js';
|
||||
export * from './context.interface.js';
|
||||
export * from './context-base.class.js';
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
import type { UmbControllerAlias } from './controller-alias.type.js';
|
||||
import { UmbControllerHostMixin } from './controller-host.mixin.js';
|
||||
import type { UmbControllerHostElement } from './controller-host-element.interface.js';
|
||||
import type { UmbController } from './controller.interface.js';
|
||||
import type { UmbControllerHost } from './controller-host.interface.js';
|
||||
import type { HTMLElementConstructor } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
export declare class UmbControllerHostImplementationElement extends HTMLElement implements UmbControllerHostElement {
|
||||
hasController(controller: UmbController): boolean;
|
||||
getControllers(filterMethod: (ctrl: UmbController) => boolean): UmbController[];
|
||||
addController(controller: UmbController): void;
|
||||
removeControllerByAlias(alias: UmbControllerAlias): void;
|
||||
removeController(controller: UmbController): void;
|
||||
getHostElement(): Element;
|
||||
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This mixin enables a web-component to host controllers.
|
||||
* This enables controllers to be added to the life cycle of this element.
|
||||
@@ -24,7 +10,10 @@ export declare class UmbControllerHostImplementationElement extends HTMLElement
|
||||
* @mixin
|
||||
*/
|
||||
export const UmbControllerHostElementMixin = <T extends HTMLElementConstructor>(superClass: T) => {
|
||||
class UmbControllerHostElementClass extends UmbControllerHostMixin(superClass) implements UmbControllerHost {
|
||||
class UmbControllerHostElementClass
|
||||
extends UmbControllerHostMixin<T>(superClass)
|
||||
implements UmbControllerHostElement
|
||||
{
|
||||
getHostElement(): Element {
|
||||
return this;
|
||||
}
|
||||
@@ -38,9 +27,11 @@ export const UmbControllerHostElementMixin = <T extends HTMLElementConstructor>(
|
||||
super.disconnectedCallback?.();
|
||||
this.hostDisconnected();
|
||||
}
|
||||
|
||||
destroy(): void {}
|
||||
}
|
||||
|
||||
return UmbControllerHostElementClass as unknown as HTMLElementConstructor<UmbControllerHostImplementationElement> & T;
|
||||
return UmbControllerHostElementClass as unknown as HTMLElementConstructor<UmbControllerHostElement> & T;
|
||||
};
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -2,13 +2,7 @@ import type { ClassConstructor } from '../extension-api/types/utils.js';
|
||||
import type { UmbControllerHost } from './controller-host.interface.js';
|
||||
import type { UmbController } from './controller.interface.js';
|
||||
|
||||
declare class UmbControllerHostBaseDeclaration implements Omit<UmbControllerHost, 'getHostElement'> {
|
||||
hasController(controller: UmbController): boolean;
|
||||
getControllers(filterMethod: (ctrl: UmbController) => boolean): UmbController[];
|
||||
addController(controller: UmbController): void;
|
||||
removeControllerByAlias(unique: UmbController['controllerAlias']): void;
|
||||
removeController(controller: UmbController): void;
|
||||
|
||||
interface UmbControllerHostBaseDeclaration extends Omit<UmbControllerHost, 'getHostElement'> {
|
||||
hostConnected(): void;
|
||||
hostDisconnected(): void;
|
||||
destroy(): void;
|
||||
@@ -22,11 +16,15 @@ declare class UmbControllerHostBaseDeclaration implements Omit<UmbControllerHost
|
||||
* @mixin
|
||||
*/
|
||||
export const UmbControllerHostMixin = <T extends ClassConstructor>(superClass: T) => {
|
||||
class UmbControllerHostBaseClass extends superClass {
|
||||
class UmbControllerHostBaseClass extends superClass implements UmbControllerHostBaseDeclaration {
|
||||
#controllers: UmbController[] = [];
|
||||
|
||||
#attached = false;
|
||||
|
||||
getHostElement() {
|
||||
return undefined as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a controller is assigned to this element.
|
||||
* @param {UmbController} ctrl
|
||||
@@ -123,7 +121,7 @@ export const UmbControllerHostMixin = <T extends ClassConstructor>(superClass: T
|
||||
throw new Error(
|
||||
`Controller with controller alias: '${ctrl.controllerAlias?.toString()}' and class name: '${
|
||||
(ctrl as any).constructor.name
|
||||
}', does not remove it self when destroyed. This can cause memory leaks. Please fix this issue.`,
|
||||
}', does not remove it self when destroyed. This can cause memory leaks. Please fix this issue.\r\nThis usually occurs when you have a destroy() method that doesn't call super.destroy().`,
|
||||
);
|
||||
}
|
||||
prev = ctrl;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { UmbControllerHostElement } from '../controller-api/controller-host-element.interface.js';
|
||||
import type { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';
|
||||
import type { UmbClassInterface } from '@umbraco-cms/backoffice/class-api';
|
||||
|
||||
export interface UmbElement extends UmbClassInterface, UmbControllerHostElement {
|
||||
/**
|
||||
* Use the UmbLocalizeController to localize your element.
|
||||
* @see UmbLocalizationController
|
||||
*/
|
||||
localize: UmbLocalizationController;
|
||||
}
|
||||
@@ -1,72 +1,50 @@
|
||||
import type { UmbElement } from './element.interface.js';
|
||||
import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { HTMLElementConstructor } from '@umbraco-cms/backoffice/extension-api';
|
||||
import {
|
||||
UmbControllerHostElementMixin,
|
||||
type UmbControllerHostImplementationElement,
|
||||
} from '@umbraco-cms/backoffice/controller-api';
|
||||
import { type UmbControllerAlias, UmbControllerHostElementMixin } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbContextToken, UmbContextCallback } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { ObserverCallback } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
export declare class UmbElement extends UmbControllerHostImplementationElement {
|
||||
/**
|
||||
* @description Observe a RxJS source of choice.
|
||||
* @param {Observable<T>} source RxJS source
|
||||
* @param {method} callback Callback method called when data is changed.
|
||||
* @return {UmbObserverController} Reference to a Observer Controller instance
|
||||
* @memberof UmbElementMixin
|
||||
*/
|
||||
observe<T>(
|
||||
source: Observable<T> | { asObservable: () => Observable<T> },
|
||||
callback: ObserverCallback<T>,
|
||||
unique?: string,
|
||||
): UmbObserverController<T>;
|
||||
provideContext<
|
||||
BaseType = unknown,
|
||||
ResultType extends BaseType = BaseType,
|
||||
InstanceType extends ResultType = ResultType,
|
||||
>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
instance: InstanceType,
|
||||
): UmbContextProviderController<BaseType, ResultType, InstanceType>;
|
||||
consumeContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
callback: UmbContextCallback<ResultType>,
|
||||
): UmbContextConsumerController<BaseType, ResultType>;
|
||||
getContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
): Promise<ResultType>;
|
||||
/**
|
||||
* Use the UmbLocalizeController to localize your element.
|
||||
* @see UmbLocalizationController
|
||||
*/
|
||||
localize: UmbLocalizationController;
|
||||
}
|
||||
import { UmbObserverController, simpleHashCode } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
export const UmbElementMixin = <T extends HTMLElementConstructor>(superClass: T) => {
|
||||
class UmbElementMixinClass extends UmbControllerHostElementMixin(superClass) implements UmbElement {
|
||||
localize: UmbLocalizationController = new UmbLocalizationController(this);
|
||||
|
||||
/**
|
||||
* @description Observe a RxJS source of choice.
|
||||
* @param {Observable<T>} source RxJS source
|
||||
* @param {method} callback Callback method called when data is changed.
|
||||
* @return {UmbObserverController} Reference to a Observer Controller instance
|
||||
* @memberof UmbElementMixin
|
||||
*/
|
||||
observe<T>(source: Observable<T>, callback: ObserverCallback<T>, unique?: string): UmbObserverController<T> {
|
||||
return new UmbObserverController<T>(this, source, callback, unique);
|
||||
observe<
|
||||
ObservableType extends Observable<T>,
|
||||
T,
|
||||
SpecificT = ObservableType extends Observable<infer U>
|
||||
? ObservableType extends undefined
|
||||
? U | undefined
|
||||
: U
|
||||
: undefined,
|
||||
R extends UmbObserverController<SpecificT> = UmbObserverController<SpecificT>,
|
||||
SpecificR extends R | undefined = ObservableType extends undefined ? R | undefined : R,
|
||||
>(
|
||||
// This type dance checks if the Observable given could be undefined, if it potentially could be undefined it means that this potentially could return undefined and then call the callback with undefined. [NL]
|
||||
source: ObservableType | undefined,
|
||||
callback: ObserverCallback<SpecificT>,
|
||||
controllerAlias?: UmbControllerAlias,
|
||||
): SpecificR {
|
||||
// Fallback to use a hash of the provided method, but only if the alias is undefined.
|
||||
controllerAlias ??= controllerAlias === undefined ? simpleHashCode(callback.toString()) : undefined;
|
||||
|
||||
if (source) {
|
||||
return new UmbObserverController<T>(
|
||||
this,
|
||||
source,
|
||||
callback as unknown as ObserverCallback<T>,
|
||||
controllerAlias,
|
||||
) as unknown as SpecificR;
|
||||
} else {
|
||||
callback(undefined as SpecificT);
|
||||
this.removeControllerByAlias(controllerAlias);
|
||||
return undefined as SpecificR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Provide a context API for this or child elements.
|
||||
* @param {string} alias
|
||||
* @param {instance} instance The API instance to be exposed.
|
||||
* @return {UmbContextProviderController} Reference to a Context Provider Controller instance
|
||||
* @memberof UmbElementMixin
|
||||
*/
|
||||
provideContext<
|
||||
BaseType = unknown,
|
||||
ResultType extends BaseType = BaseType,
|
||||
@@ -78,13 +56,6 @@ export const UmbElementMixin = <T extends HTMLElementConstructor>(superClass: T)
|
||||
return new UmbContextProviderController(this, alias, instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Setup a subscription for a context. The callback is called when the context is resolved.
|
||||
* @param {string} alias
|
||||
* @param {method} callback Callback method called when context is resolved.
|
||||
* @return {UmbContextConsumerController} Reference to a Context Consumer Controller instance
|
||||
* @memberof UmbElementMixin
|
||||
*/
|
||||
consumeContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
callback: UmbContextCallback<ResultType>,
|
||||
@@ -92,13 +63,6 @@ export const UmbElementMixin = <T extends HTMLElementConstructor>(superClass: T)
|
||||
return new UmbContextConsumerController(this, alias, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Setup a subscription for a context. The callback is called when the context is resolved.
|
||||
* @param {string} contextAlias
|
||||
* @param {method} callback Callback method called when context is resolved.
|
||||
* @return {UmbContextConsumerController} Reference to a Context Consumer Controller instance
|
||||
* @memberof UmbElementMixin
|
||||
*/
|
||||
async getContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
contextAlias: string | UmbContextToken<BaseType, ResultType>,
|
||||
): Promise<ResultType> {
|
||||
|
||||
203
src/Umbraco.Web.UI.Client/src/libs/element-api/element.test.ts
Normal file
203
src/Umbraco.Web.UI.Client/src/libs/element-api/element.test.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import { expect } from '@open-wc/testing';
|
||||
import { UmbElementMixin } from './element.mixin.js';
|
||||
import { customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbStringState } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
@customElement('test-my-umb-element')
|
||||
class UmbTestUmbElement extends UmbElementMixin(HTMLElement) {}
|
||||
|
||||
describe('UmbElement', () => {
|
||||
let hostElement: UmbTestUmbElement;
|
||||
|
||||
beforeEach(() => {
|
||||
hostElement = document.createElement('test-my-umb-element') as UmbTestUmbElement;
|
||||
});
|
||||
|
||||
describe('Element general controller API', () => {
|
||||
describe('methods', () => {
|
||||
it('has an hasController method', () => {
|
||||
expect(hostElement).to.have.property('hasController').that.is.a('function');
|
||||
});
|
||||
it('has an getControllers method', () => {
|
||||
expect(hostElement).to.have.property('getControllers').that.is.a('function');
|
||||
});
|
||||
it('has an addController method', () => {
|
||||
expect(hostElement).to.have.property('addController').that.is.a('function');
|
||||
});
|
||||
it('has an removeControllerByAlias method', () => {
|
||||
expect(hostElement).to.have.property('removeControllerByAlias').that.is.a('function');
|
||||
});
|
||||
it('has an removeController method', () => {
|
||||
expect(hostElement).to.have.property('removeController').that.is.a('function');
|
||||
});
|
||||
it('has an destroy method', () => {
|
||||
expect(hostElement).to.have.property('destroy').that.is.a('function');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Element helper methods API', () => {
|
||||
describe('methods', () => {
|
||||
it('has an hasController method', () => {
|
||||
expect(hostElement).to.have.property('getHostElement').that.is.a('function');
|
||||
});
|
||||
it('has an hasController should return it self', () => {
|
||||
expect(hostElement.getHostElement()).to.be.equal(hostElement);
|
||||
});
|
||||
it('has an observe method', () => {
|
||||
expect(hostElement).to.have.property('observe').that.is.a('function');
|
||||
});
|
||||
it('has an provideContext method', () => {
|
||||
expect(hostElement).to.have.property('provideContext').that.is.a('function');
|
||||
});
|
||||
it('has an consumeContext method', () => {
|
||||
expect(hostElement).to.have.property('consumeContext').that.is.a('function');
|
||||
});
|
||||
it('has an getContext method', () => {
|
||||
expect(hostElement).to.have.property('getContext').that.is.a('function');
|
||||
});
|
||||
it('has an localization class instance', () => {
|
||||
expect(hostElement).to.have.property('localize').that.is.a('object');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Controllers lifecycle', () => {
|
||||
it('observe is removed when destroyed', () => {
|
||||
const myState = new UmbStringState('hello');
|
||||
const myObservable = myState.asObservable();
|
||||
|
||||
const ctrl = hostElement.observe(myObservable, () => {}, 'observer');
|
||||
|
||||
// The controller is now added to the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.true;
|
||||
|
||||
ctrl.destroy();
|
||||
|
||||
// The controller is removed from the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.false;
|
||||
});
|
||||
|
||||
it('observe is destroyed then removed', () => {
|
||||
const myState = new UmbStringState('hello');
|
||||
const myObservable = myState.asObservable();
|
||||
|
||||
const ctrl = hostElement.observe(myObservable, () => {}, 'observer');
|
||||
|
||||
// The controller is now added to the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.true;
|
||||
|
||||
hostElement.removeController(ctrl);
|
||||
|
||||
// The controller is removed from the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.false;
|
||||
});
|
||||
|
||||
it('observe is destroyed then removed via alias', () => {
|
||||
const myState = new UmbStringState('hello');
|
||||
const myObservable = myState.asObservable();
|
||||
|
||||
const ctrl = hostElement.observe(myObservable, () => {}, 'observer');
|
||||
|
||||
// The controller is now added to the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.true;
|
||||
|
||||
hostElement.removeControllerByAlias('observer');
|
||||
|
||||
// The controller is removed from the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.false;
|
||||
});
|
||||
|
||||
it('observe is removed when replaced with alias', () => {
|
||||
const myState = new UmbStringState('hello');
|
||||
const myObservable = myState.asObservable();
|
||||
|
||||
const ctrl = hostElement.observe(myObservable, () => {}, 'observer');
|
||||
|
||||
// The controller is now added to the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.true;
|
||||
|
||||
const ctrl2 = hostElement.observe(myObservable, () => {}, 'observer');
|
||||
|
||||
// The controller is removed from the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.false;
|
||||
// The controller is new one is there instead:
|
||||
expect(hostElement.hasController(ctrl2)).to.be.true;
|
||||
});
|
||||
|
||||
it('observe is removed when replaced with alias made of hash of callback method', () => {
|
||||
const myState = new UmbStringState('hello');
|
||||
const myObservable = myState.asObservable();
|
||||
|
||||
const ctrl = hostElement.observe(myObservable, () => {});
|
||||
|
||||
// The controller is now added to the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.true;
|
||||
|
||||
const ctrl2 = hostElement.observe(myObservable, () => {});
|
||||
|
||||
// The controller is removed from the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.false;
|
||||
// The controller is new one is there instead:
|
||||
expect(hostElement.hasController(ctrl2)).to.be.true;
|
||||
});
|
||||
|
||||
it('observe is NOT removed when controller alias does not align', () => {
|
||||
const myState = new UmbStringState('hello');
|
||||
const myObservable = myState.asObservable();
|
||||
|
||||
const ctrl = hostElement.observe(myObservable, () => {});
|
||||
|
||||
// The controller is now added to the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.true;
|
||||
|
||||
const ctrl2 = hostElement.observe(myObservable, (value) => {
|
||||
const a = value + 'bla';
|
||||
});
|
||||
|
||||
// The controller is not removed from the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.true;
|
||||
expect(hostElement.hasController(ctrl2)).to.be.true;
|
||||
});
|
||||
|
||||
it('observe is removed when observer is undefined and using the same alias', () => {
|
||||
const myState = new UmbStringState('hello');
|
||||
const myObservable = myState.asObservable();
|
||||
|
||||
const ctrl = hostElement.observe(myObservable, () => {}, 'observer');
|
||||
|
||||
// The controller is now added to the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.true;
|
||||
|
||||
const ctrl2 = hostElement.observe(
|
||||
undefined,
|
||||
() => {
|
||||
const a = 1;
|
||||
},
|
||||
'observer',
|
||||
);
|
||||
|
||||
// The controller is removed from the host, and the new one was NOT added:
|
||||
expect(hostElement.hasController(ctrl)).to.be.false;
|
||||
expect(ctrl2).to.be.undefined;
|
||||
expect(hostElement.hasController(ctrl2)).to.be.false;
|
||||
});
|
||||
|
||||
it('observe is removed when observer is undefined and using the same callback method', () => {
|
||||
const myState = new UmbStringState('hello');
|
||||
const myObservable = myState.asObservable();
|
||||
|
||||
const ctrl = hostElement.observe(myObservable, () => {});
|
||||
|
||||
// The controller is now added to the host:
|
||||
expect(hostElement.hasController(ctrl)).to.be.true;
|
||||
|
||||
const ctrl2 = hostElement.observe(undefined, () => {});
|
||||
|
||||
// The controller is removed from the host, and the new one was NOT added:
|
||||
expect(hostElement.hasController(ctrl)).to.be.false;
|
||||
expect(ctrl2).to.be.undefined;
|
||||
expect(hostElement.hasController(ctrl2)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1 +1,2 @@
|
||||
export * from './element.interface.js';
|
||||
export * from './element.mixin.js';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { expect } from '@open-wc/testing';
|
||||
import { UmbObjectState } from './states/object-state.js';
|
||||
import { UmbObserverController } from './observer.controller.js';
|
||||
import { simpleHashCode } from './utils/simple-hash-code.function.js';
|
||||
import { customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbControllerHostElementMixin } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
@@ -42,27 +43,21 @@ describe('UmbObserverController', () => {
|
||||
expect(hostElement.hasController(secondCtrl)).to.be.true;
|
||||
});
|
||||
|
||||
it('controller is replacing another controller when using the same callback method and no controller-alias', () => {
|
||||
const state = new UmbObjectState(undefined);
|
||||
const observable = state.asObservable();
|
||||
|
||||
const callbackMethod = (state: unknown) => {};
|
||||
|
||||
const firstCtrl = new UmbObserverController(hostElement, observable, callbackMethod);
|
||||
const secondCtrl = new UmbObserverController(hostElement, observable, callbackMethod);
|
||||
|
||||
expect(hostElement.hasController(firstCtrl)).to.be.false;
|
||||
expect(hostElement.hasController(secondCtrl)).to.be.true;
|
||||
});
|
||||
|
||||
it('controller is NOT replacing another controller when using a null for controller-alias', () => {
|
||||
const state = new UmbObjectState(undefined);
|
||||
const observable = state.asObservable();
|
||||
|
||||
const callbackMethod = (state: unknown) => {};
|
||||
|
||||
const firstCtrl = new UmbObserverController(hostElement, observable, callbackMethod, null);
|
||||
const secondCtrl = new UmbObserverController(hostElement, observable, callbackMethod, null);
|
||||
// Imitates the behavior of the observe method in the UmbClassMixin
|
||||
let controllerAlias1 = null;
|
||||
controllerAlias1 ??= controllerAlias1 === undefined ? simpleHashCode(callbackMethod.toString()) : undefined;
|
||||
|
||||
const firstCtrl = new UmbObserverController(hostElement, observable, callbackMethod, controllerAlias1);
|
||||
|
||||
let controllerAlias2 = null;
|
||||
controllerAlias2 ??= controllerAlias2 === undefined ? simpleHashCode(callbackMethod.toString()) : undefined;
|
||||
const secondCtrl = new UmbObserverController(hostElement, observable, callbackMethod, controllerAlias2);
|
||||
|
||||
expect(hostElement.hasController(firstCtrl)).to.be.true;
|
||||
expect(hostElement.hasController(secondCtrl)).to.be.true;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { type ObserverCallback, UmbObserver } from './observer.js';
|
||||
import { simpleHashCode } from './utils/simple-hash-code.function.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbController, UmbControllerAlias, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
@@ -15,12 +14,11 @@ export class UmbObserverController<T = unknown> extends UmbObserver<T> implement
|
||||
host: UmbControllerHost,
|
||||
source: Observable<T>,
|
||||
callback: ObserverCallback<T>,
|
||||
alias?: UmbControllerAlias | null,
|
||||
alias: UmbControllerAlias,
|
||||
) {
|
||||
super(source, callback);
|
||||
this.#host = host;
|
||||
// Fallback to use a hash of the provided method, but only if the alias is undefined.
|
||||
this.#alias = alias ?? (alias === undefined ? simpleHashCode(callback.toString()) : undefined);
|
||||
this.#alias = alias;
|
||||
|
||||
// Lets check if controller is already here:
|
||||
// No we don't want this, as multiple different controllers might be looking at the same source.
|
||||
|
||||
@@ -6,7 +6,9 @@ export type ObserverCallbackStack<T> = {
|
||||
complete?: () => void;
|
||||
};
|
||||
|
||||
export type ObserverCallback<T> = ((_value: T) => void) | ObserverCallbackStack<T>;
|
||||
export type ObserverCallback<T> = (_value: T) => void;
|
||||
// We do not use the ObserverCallbackStack type, and it was making things more complicated than they need to be so I have taken it out..
|
||||
//export type ObserverCallback<T> = ((_value: T) => void) | ObserverCallbackStack<T>;
|
||||
|
||||
export class UmbObserver<T> {
|
||||
#source!: Observable<T>;
|
||||
|
||||
@@ -45,6 +45,7 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
|
||||
this._groupsWithBlockTypes = model;
|
||||
},
|
||||
onEnd: () => {
|
||||
// TODO: make one method for updating the blockGroupsDataSetValue:
|
||||
this.#datasetContext?.setPropertyValue(
|
||||
'blockGroups',
|
||||
this._groupsWithBlockTypes.map((group) => ({ key: group.key, name: group.name })),
|
||||
@@ -143,6 +144,8 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
|
||||
|
||||
// TODO: Implement confirm dialog
|
||||
#deleteGroup(groupKey: string) {
|
||||
// TODO: make one method for updating the blockGroupsDataSetValue:
|
||||
// This one that deletes might require the ability to parse what to send as an argument to the method, then a filtering can occur before.
|
||||
this.#datasetContext?.setPropertyValue(
|
||||
'blockGroups',
|
||||
this._blockGroups.filter((group) => group.key !== groupKey),
|
||||
@@ -154,6 +157,7 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
|
||||
|
||||
#changeGroupName(e: UUIInputEvent, groupKey: string) {
|
||||
const groupName = e.target.value as string;
|
||||
// TODO: make one method for updating the blockGroupsDataSetValue:
|
||||
this.#datasetContext?.setPropertyValue(
|
||||
'blockGroups',
|
||||
this._blockGroups.map((group) => (group.key === groupKey ? { ...group, name: groupName } : group)),
|
||||
|
||||
@@ -144,22 +144,23 @@ export class UmbContentTypePropertyStructureManager<T extends UmbContentTypeMode
|
||||
// Load inherited and composed types:
|
||||
//this._loadContentTypeCompositions(data);// Should not be necessary as this will be done when appended to the contentTypes state. [NL]
|
||||
|
||||
this.#contentTypeObservers.push(
|
||||
this.observe(
|
||||
// Then lets start observation of the content type:
|
||||
await this.#contentTypeRepository.byUnique(data.unique),
|
||||
(docType) => {
|
||||
if (docType) {
|
||||
// TODO: Handle if there was changes made to the owner document type in this context.
|
||||
/*
|
||||
possible easy solutions could be to notify user wether they want to update(Discard the changes to accept the new ones).
|
||||
*/
|
||||
this.#contentTypes.appendOne(docType);
|
||||
}
|
||||
},
|
||||
'observeContentType_' + data.unique,
|
||||
),
|
||||
const ctrl = this.observe(
|
||||
// Then lets start observation of the content type:
|
||||
await this.#contentTypeRepository.byUnique(data.unique),
|
||||
(docType) => {
|
||||
if (docType) {
|
||||
// TODO: Handle if there was changes made to the owner document type in this context. [NL]
|
||||
/*
|
||||
possible easy solutions could be to notify user wether they want to update(Discard the changes to accept the new ones). [NL]
|
||||
*/
|
||||
this.#contentTypes.appendOne(docType);
|
||||
}
|
||||
// TODO: Do we need to handle the undefined case? [NL]
|
||||
},
|
||||
'observeContentType_' + data.unique,
|
||||
);
|
||||
|
||||
this.#contentTypeObservers.push(ctrl);
|
||||
}
|
||||
|
||||
private async _loadContentTypeCompositions(contentType: T) {
|
||||
@@ -493,7 +494,7 @@ export class UmbContentTypePropertyStructureManager<T extends UmbContentTypeMode
|
||||
});
|
||||
}
|
||||
|
||||
// In future this might need to take parentName(parentId lookup) into account as well? otherwise containers that share same name and type will always be merged, but their position might be different and they should not be merged.
|
||||
// In future this might need to take parentName(parentId lookup) into account as well? otherwise containers that share same name and type will always be merged, but their position might be different and they should not be merged. [NL]
|
||||
containersByNameAndType(name: string, containerType: UmbPropertyContainerTypes) {
|
||||
return this.#containers.asObservablePart((data) => {
|
||||
return data.filter((x) => x.name === name && x.type === containerType);
|
||||
|
||||
@@ -5,7 +5,7 @@ export const manifest: ManifestPropertyEditorSchema = {
|
||||
name: 'Multi Node Tree Picker',
|
||||
alias: 'Umbraco.MultiNodeTreePicker',
|
||||
meta: {
|
||||
defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.MultiNodeTreePicker',
|
||||
defaultPropertyEditorUiAlias: 'Umb.PropertyEditorUi.TreePicker',
|
||||
settings: {
|
||||
properties: [
|
||||
{
|
||||
|
||||
@@ -29,6 +29,9 @@ export interface UmbPropertyDatasetContext extends UmbContext {
|
||||
|
||||
// Property methods:
|
||||
propertyVariantId?: (propertyAlias: string) => Promise<Observable<UmbVariantId | undefined>>;
|
||||
propertyValueByAlias<ReturnType = unknown>(propertyAlias: string): Promise<Observable<ReturnType | undefined>>;
|
||||
propertyValueByAlias<ReturnType = unknown>(
|
||||
propertyAlias: string,
|
||||
): Promise<Observable<ReturnType | undefined> | undefined>;
|
||||
// TODO: Append the andCulture method as well..
|
||||
setPropertyValue(propertyAlias: string, value: unknown): void;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT } from '../property-dataset/index.js';
|
||||
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import {
|
||||
UmbArrayState,
|
||||
UmbBasicState,
|
||||
@@ -10,14 +9,12 @@ import {
|
||||
UmbDeepState,
|
||||
UmbStringState,
|
||||
} from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbContextProviderController, UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbPropertyEditorConfigProperty } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export class UmbPropertyContext<ValueType = any> extends UmbControllerBase {
|
||||
private _providerController: UmbContextProviderController;
|
||||
|
||||
export class UmbPropertyContext<ValueType = any> extends UmbContextBase<UmbPropertyContext<ValueType>> {
|
||||
#alias = new UmbStringState(undefined);
|
||||
public readonly alias = this.#alias.asObservable();
|
||||
#label = new UmbStringState(undefined);
|
||||
@@ -29,8 +26,8 @@ export class UmbPropertyContext<ValueType = any> extends UmbControllerBase {
|
||||
#configValues = new UmbArrayState<UmbPropertyEditorConfigProperty>([], (x) => x.alias);
|
||||
public readonly configValues = this.#configValues.asObservable();
|
||||
|
||||
#configCollection = new UmbClassState<UmbPropertyEditorConfigCollection | undefined>(undefined);
|
||||
public readonly config = this.#configCollection.asObservable();
|
||||
#config = new UmbClassState<UmbPropertyEditorConfigCollection | undefined>(undefined);
|
||||
public readonly config = this.#config.asObservable();
|
||||
|
||||
private _editor = new UmbBasicState<UmbPropertyEditorUiElement | undefined>(undefined);
|
||||
public readonly editor = this._editor.asObservable();
|
||||
@@ -51,7 +48,7 @@ export class UmbPropertyContext<ValueType = any> extends UmbControllerBase {
|
||||
#datasetContext?: typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE;
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host);
|
||||
super(host, UMB_PROPERTY_CONTEXT);
|
||||
|
||||
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (variantContext) => {
|
||||
this.#datasetContext = variantContext;
|
||||
@@ -63,10 +60,8 @@ export class UmbPropertyContext<ValueType = any> extends UmbControllerBase {
|
||||
this._observeProperty();
|
||||
});
|
||||
|
||||
this._providerController = new UmbContextProviderController(host, UMB_PROPERTY_CONTEXT, this);
|
||||
|
||||
this.observe(this.configValues, (configValues) => {
|
||||
this.#configCollection.setValue(configValues ? new UmbPropertyEditorConfigCollection(configValues) : undefined);
|
||||
this.#config.setValue(configValues ? new UmbPropertyEditorConfigCollection(configValues) : undefined);
|
||||
});
|
||||
|
||||
this.observe(this.variantId, () => {
|
||||
@@ -74,29 +69,25 @@ export class UmbPropertyContext<ValueType = any> extends UmbControllerBase {
|
||||
});
|
||||
}
|
||||
|
||||
private _observePropertyVariant?: UmbObserverController<UmbVariantId | undefined>;
|
||||
private _observePropertyValue?: UmbObserverController<ValueType | undefined>;
|
||||
private async _observeProperty(): Promise<void> {
|
||||
const alias = this.#alias.getValue();
|
||||
if (!this.#datasetContext || !alias) return;
|
||||
|
||||
const variantIdSubject = (await this.#datasetContext.propertyVariantId?.(alias)) ?? undefined;
|
||||
this._observePropertyVariant?.destroy();
|
||||
if (variantIdSubject) {
|
||||
this._observePropertyVariant = this.observe(variantIdSubject, (variantId) => {
|
||||
this.observe(
|
||||
await this.#datasetContext.propertyVariantId?.(alias),
|
||||
(variantId) => {
|
||||
this.#variantId.setValue(variantId);
|
||||
});
|
||||
}
|
||||
},
|
||||
'observeVariantId',
|
||||
);
|
||||
|
||||
// TODO: Verify if we need to optimize runtime by parsing the propertyVariantID, cause this method retrieves it again:
|
||||
const subject = await this.#datasetContext.propertyValueByAlias<ValueType>(alias);
|
||||
|
||||
this._observePropertyValue?.destroy();
|
||||
if (subject) {
|
||||
this._observePropertyValue = this.observe(subject, (value) => {
|
||||
this.observe(
|
||||
await this.#datasetContext.propertyValueByAlias<ValueType>(alias),
|
||||
(value) => {
|
||||
this.#value.setValue(value);
|
||||
});
|
||||
}
|
||||
},
|
||||
'observeValue',
|
||||
);
|
||||
}
|
||||
|
||||
private _generateVariantDifferenceString() {
|
||||
@@ -169,8 +160,7 @@ export class UmbPropertyContext<ValueType = any> extends UmbControllerBase {
|
||||
this.#description.destroy();
|
||||
this.#configValues.destroy();
|
||||
this.#value.destroy();
|
||||
this.#configCollection.destroy();
|
||||
this._providerController.destroy(); // This would also be handled by the controller host, but if someone wanted to replace/remove this context without the host being destroyed. Then we have clean up out selfs here.
|
||||
this.#config.destroy();
|
||||
this.#datasetContext = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,8 +101,9 @@ export class UmbPropertyElement extends UmbLitElement {
|
||||
@state()
|
||||
private _element?: ManifestPropertyEditorUi['ELEMENT_TYPE'];
|
||||
|
||||
@state()
|
||||
private _value?: unknown;
|
||||
// Not begin used currently [NL]
|
||||
//@state()
|
||||
//private _value?: unknown;
|
||||
|
||||
@state()
|
||||
private _alias?: string;
|
||||
@@ -178,12 +179,12 @@ export class UmbPropertyElement extends UmbLitElement {
|
||||
this.#propertyContext.setEditor(this._element);
|
||||
|
||||
if (this._element) {
|
||||
// TODO: Could this be changed to change event? (or additionally support change?)
|
||||
// TODO: Could this be changed to change event? (or additionally support the change event? [NL])
|
||||
this._element.addEventListener('property-value-change', this._onPropertyEditorChange as any as EventListener);
|
||||
|
||||
// No need for a controller alias, as the clean is handled via the observer prop:
|
||||
this.#valueObserver = this.observe(this.#propertyContext.value, (value) => {
|
||||
this._value = value;
|
||||
//this._value = value;// This was not used currently [NL]
|
||||
if (this._element) {
|
||||
this._element.value = value;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface UmbVariantableWorkspaceContextInterface<VariantType extends Umb
|
||||
propertyValueByAlias<ReturnValue = unknown>(
|
||||
alias: string,
|
||||
variantId?: UmbVariantId,
|
||||
): Promise<Observable<ReturnValue | undefined>>;
|
||||
): Promise<Observable<ReturnValue | undefined> | undefined>;
|
||||
getPropertyValue<ReturnValue = unknown>(alias: string, variantId?: UmbVariantId): ReturnValue | undefined;
|
||||
setPropertyValue(alias: string, value: unknown, variantId?: UmbVariantId): Promise<void>;
|
||||
//propertyDataByAlias(alias: string, variantId?: UmbVariantId): Observable<ValueModelBaseModel | undefined>;
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { UmbNameablePropertyDatasetContext, UmbPropertyDatasetContext } fro
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { map } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { type Observable, map } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
@@ -83,21 +83,24 @@ export class UmbDocumentPropertyDataContext
|
||||
|
||||
/**
|
||||
* TODO: Write proper JSDocs here.
|
||||
* Ideally do not use these methods, its better to communicate directly with the workspace, but if you do not know the property variant id, then this will figure it out for you. So good for externals to set or get values of a property.
|
||||
* Ideally do not use this method, its better to communicate directly with the workspace, but if you do not know the property variant id, then this will figure it out for you. So good for externals to set or get values of a property.
|
||||
*/
|
||||
async propertyValueByAlias<ReturnType = unknown>(propertyAlias: string) {
|
||||
async propertyValueByAlias<ReturnType = unknown>(
|
||||
propertyAlias: string,
|
||||
): Promise<Observable<ReturnType | undefined> | undefined> {
|
||||
await this.#workspace.isLoaded();
|
||||
return (await this.#workspace.structure.propertyStructureByAlias(propertyAlias)).pipe(
|
||||
map((property) =>
|
||||
property?.alias
|
||||
? this.#workspace.getPropertyValue<ReturnType>(property.alias, this.#createPropertyVariantId(property))
|
||||
: undefined,
|
||||
),
|
||||
);
|
||||
const structure = await this.#workspace.structure.getPropertyStructureByAlias(propertyAlias);
|
||||
if (structure) {
|
||||
return this.#workspace.propertyValueByAlias<ReturnType>(propertyAlias, this.#createPropertyVariantId(structure));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Refactor: Not used currently, but should investigate if we can implement this, to spare some energy.
|
||||
async propertyValueByAliasAndCulture<ReturnType = unknown>(propertyAlias: string, propertyVariantId: UmbVariantId) {
|
||||
async propertyValueByAliasAndCulture<ReturnType = unknown>(
|
||||
propertyAlias: string,
|
||||
propertyVariantId: UmbVariantId,
|
||||
): Promise<Observable<ReturnType | undefined> | undefined> {
|
||||
return this.#workspace.propertyValueByAlias<ReturnType>(propertyAlias, propertyVariantId);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
} from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbLanguageCollectionRepository, type UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language';
|
||||
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { type Observable, firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
|
||||
import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
@@ -230,7 +230,10 @@ export class UmbDocumentWorkspaceContext
|
||||
return this.structure.propertyStructureById(propertyId);
|
||||
}
|
||||
|
||||
async propertyValueByAlias<PropertyValueType = unknown>(propertyAlias: string, variantId?: UmbVariantId) {
|
||||
async propertyValueByAlias<PropertyValueType = unknown>(
|
||||
propertyAlias: string,
|
||||
variantId?: UmbVariantId,
|
||||
): Promise<Observable<PropertyValueType | undefined> | undefined> {
|
||||
return this.#currentData.asObservablePart(
|
||||
(data) =>
|
||||
data?.values?.find((x) => x?.alias === propertyAlias && (variantId ? variantId.compare(x) : true))
|
||||
@@ -254,20 +257,16 @@ export class UmbDocumentWorkspaceContext
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
async setPropertyValue<UmbDocumentValueModel = unknown>(
|
||||
alias: string,
|
||||
value: UmbDocumentValueModel,
|
||||
variantId?: UmbVariantId,
|
||||
) {
|
||||
async setPropertyValue<ValueType = unknown>(alias: string, value: ValueType, variantId?: UmbVariantId) {
|
||||
variantId ??= UmbVariantId.CreateInvariant();
|
||||
|
||||
const entry = { ...variantId.toObject(), alias, value };
|
||||
const entry = { ...variantId.toObject(), alias, value } as UmbDocumentValueModel<ValueType>;
|
||||
const currentData = this.getData();
|
||||
if (currentData) {
|
||||
const values = appendToFrozenArray(
|
||||
currentData.values || [],
|
||||
currentData.values ?? [],
|
||||
entry,
|
||||
(x) => x.alias === alias && (variantId ? variantId.compare(x) : true),
|
||||
(x) => x.alias === alias && variantId!.compare(x),
|
||||
);
|
||||
this.#currentData.update({ values });
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Meta } from '@storybook/addon-docs';
|
||||
|
||||
<Meta title="Guides/Umbraco Controller" parameters={{ previewTabs: { canvas: { hidden: true } } }} />
|
||||
|
||||
# Umbraco Controller
|
||||
|
||||
This class can be used as the base of any class.
|
||||
This will enable Controllers to be hosted in this class. Additionally it provides few shortcut methods for initializing core Umbraco Controllers.
|
||||
|
||||
```ts
|
||||
observe<T>(source: Observable<T>, callback: (_value: T) => void, unique?: string): UmbObserverController<T>
|
||||
|
||||
provideContext<R = unknown>(alias: string | UmbContextToken<R>, instance: R): UmbContextProviderController<R>
|
||||
|
||||
consumeContext<R = unknown>(alias: string | UmbContextToken<R>, callback: UmbContextCallback<R>): UmbContextConsumerController<R>
|
||||
```
|
||||
|
||||
Read about the 'observe' method in the [Store-API](?path=/docs/guides-store--docs).
|
||||
|
||||
Read about the 'provideContext' and 'consumeContext' methods in the [Context-API](?path=/docs/guides-context-api--docs).
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Meta } from '@storybook/addon-docs';
|
||||
|
||||
<Meta title="Guides/Umbraco Element" parameters={{ previewTabs: { canvas: { hidden: true } } }} />
|
||||
|
||||
# Umbraco Element
|
||||
|
||||
This element can be used as the base of any element.
|
||||
This will enable Controllers to be hosted at this element. Additionally it provides few shortcut methods for initializing core Umbraco Controllers.
|
||||
|
||||
```ts
|
||||
observe<T>(source: Observable<T>, callback: (_value: T) => void, unique?: string): UmbObserverController<T>
|
||||
|
||||
provideContext<R = unknown>(alias: string | UmbContextToken<R>, instance: R): UmbContextProviderController<R>
|
||||
|
||||
consumeContext<R = unknown>(alias: string | UmbContextToken<R>, callback: UmbContextCallback<R>): UmbContextConsumerController<R>
|
||||
```
|
||||
|
||||
Use these for an smooth consumption, like this request for a Context API using a simple string context, where the callback value is of an unknown type:
|
||||
|
||||
```ts
|
||||
this.consumeContext('requestThisContextAlias', (context) => {
|
||||
// Notice this is a subscription, as context might change or a new one appears.
|
||||
console.log("I've got the context", context);
|
||||
});
|
||||
```
|
||||
|
||||
Or use the a Context Token to get a typed context:
|
||||
|
||||
```ts
|
||||
import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification';
|
||||
|
||||
this.consumeContext(UMB_NOTIFICATION_CONTEXT, (context) => {
|
||||
// Notice this is a subscription, as context might change or a new one appears, but the value is strongly typed
|
||||
console.log("I've got the context", context);
|
||||
});
|
||||
```
|
||||
Reference in New Issue
Block a user