Merge branch 'main' into v15/feature/content-workspace-base
This commit is contained in:
@@ -2,7 +2,7 @@ import { UmbContextProvider } from '../provide/context-provider.js';
|
||||
import { UmbContextToken } from '../token/context-token.js';
|
||||
import { UmbContextConsumer } from './context-consumer.js';
|
||||
import type { UmbContextRequestEventImplementation } from './context-request.event.js';
|
||||
import { UMB_CONTENT_REQUEST_EVENT_TYPE } from './context-request.event.js';
|
||||
import { UMB_CONTEXT_REQUEST_EVENT_TYPE } from './context-request.event.js';
|
||||
import { expect, oneEvent } from '@open-wc/testing';
|
||||
|
||||
const testContextAlias = 'my-test-context';
|
||||
@@ -38,13 +38,13 @@ describe('UmbContextConsumer', () => {
|
||||
|
||||
describe('events', () => {
|
||||
it('dispatches context request event when constructed', async () => {
|
||||
const listener = oneEvent(window, UMB_CONTENT_REQUEST_EVENT_TYPE);
|
||||
const listener = oneEvent(window, UMB_CONTEXT_REQUEST_EVENT_TYPE);
|
||||
|
||||
consumer.hostConnected();
|
||||
|
||||
const event = (await listener) as unknown as UmbContextRequestEventImplementation;
|
||||
expect(event).to.exist;
|
||||
expect(event.type).to.eq(UMB_CONTENT_REQUEST_EVENT_TYPE);
|
||||
expect(event.type).to.eq(UMB_CONTEXT_REQUEST_EVENT_TYPE);
|
||||
expect(event.contextAlias).to.eq(testContextAlias);
|
||||
consumer.hostDisconnected();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
export const UMB_CONTENT_REQUEST_EVENT_TYPE = 'umb:context-request';
|
||||
export const UMB_CONTEXT_REQUEST_EVENT_TYPE = 'umb:context-request';
|
||||
/**
|
||||
* @deprecated use UMB_CONTEXT_REQUEST_EVENT_TYPE
|
||||
* This will be removed in Umbraco 17
|
||||
*/
|
||||
export const UMB_CONTENT_REQUEST_EVENT_TYPE = UMB_CONTEXT_REQUEST_EVENT_TYPE;
|
||||
export const UMB_DEBUG_CONTEXT_EVENT_TYPE = 'umb:debug-contexts';
|
||||
|
||||
export type UmbContextCallback<T> = (instance: T) => void;
|
||||
@@ -29,7 +34,7 @@ export class UmbContextRequestEventImplementation<ResultType = unknown>
|
||||
public readonly callback: (context: ResultType) => boolean,
|
||||
public readonly stopAtContextMatch: boolean = true,
|
||||
) {
|
||||
super(UMB_CONTENT_REQUEST_EVENT_TYPE, { bubbles: true, composed: true, cancelable: true });
|
||||
super(UMB_CONTEXT_REQUEST_EVENT_TYPE, { bubbles: true, composed: true, cancelable: true });
|
||||
}
|
||||
|
||||
clone() {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { UmbContextToken } from '../token/index.js';
|
||||
import { UmbContextBoundary } from './context-boundary.js';
|
||||
import type { UmbControllerHost, UmbController } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbContextBoundaryController extends UmbContextBoundary implements UmbController {
|
||||
#host: UmbControllerHost;
|
||||
#controllerAlias: string;
|
||||
|
||||
public get controllerAlias(): string {
|
||||
return this.#controllerAlias;
|
||||
}
|
||||
|
||||
constructor(host: UmbControllerHost, contextAlias: string | UmbContextToken<any>) {
|
||||
super(host.getHostElement(), contextAlias);
|
||||
this.#host = host;
|
||||
// Makes the controllerAlias unique for this instance, this enables multiple Contexts to be provided under the same name. (This only makes sense cause of Context Token Discriminators)
|
||||
// This does mean that if someone provides a context with the same name, but with a different instance, it will not override the previous instance. But its good since it enables extensions to provide contexts at the same scope of other contexts.
|
||||
this.#controllerAlias = 'umbContextBoundary_' + contextAlias.toString();
|
||||
|
||||
// If this API is already provided with this alias? Then we do not want to register this controller:
|
||||
const existingControllers = host.getUmbControllers((x) => x.controllerAlias === this.controllerAlias);
|
||||
if (existingControllers.length > 0) {
|
||||
// This just an additional awareness feature to make devs Aware, the alternative would be adding it anyway, but that would destroy existing controller of this alias.
|
||||
// Back out, this instance is already provided, by another controller.
|
||||
throw new Error(
|
||||
`Context API: The context boundary of '${this.controllerAlias}' is already provided by another Context Provider Controller.`,
|
||||
);
|
||||
} else {
|
||||
host.addUmbController(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override destroy(): void {
|
||||
if (this.#host) {
|
||||
this.#host.removeUmbController(this);
|
||||
(this.#host as unknown) = undefined;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { UmbContextRequestEvent } from '../consume/context-request.event.js';
|
||||
import type { UmbContextToken } from '../token/index.js';
|
||||
import { UMB_CONTEXT_REQUEST_EVENT_TYPE } from '../consume/context-request.event.js';
|
||||
import { UmbContextProvideEventImplementation } from './context-provide.event.js';
|
||||
|
||||
/**
|
||||
* @class UmbContextBoundary
|
||||
*/
|
||||
export class UmbContextBoundary {
|
||||
#eventTarget: EventTarget;
|
||||
|
||||
#contextAlias: string;
|
||||
|
||||
/**
|
||||
* Creates an instance of UmbContextBoundary.
|
||||
* @param {EventTarget} eventTarget - the host element for this context provider
|
||||
* @param {string | UmbContextToken} contextIdentifier - a string or token to identify the context
|
||||
* @param {*} instance - the instance to provide
|
||||
* @memberof UmbContextBoundary
|
||||
*/
|
||||
constructor(eventTarget: EventTarget, contextIdentifier: string | UmbContextToken<any>) {
|
||||
this.#eventTarget = eventTarget;
|
||||
|
||||
const idSplit = contextIdentifier.toString().split('#');
|
||||
this.#contextAlias = idSplit[0];
|
||||
|
||||
this.#eventTarget.addEventListener(UMB_CONTEXT_REQUEST_EVENT_TYPE, this.#handleContextRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {UmbContextRequestEvent} event - the event to handle
|
||||
* @memberof UmbContextBoundary
|
||||
*/
|
||||
#handleContextRequest = ((event: UmbContextRequestEvent): void => {
|
||||
if (event.contextAlias !== this.#contextAlias) return;
|
||||
|
||||
if (event.stopAtContextMatch) {
|
||||
// Since the alias matches, we will stop it from bubbling further up. But we still allow it to ask the other Contexts of the element. Hence not calling `event.stopImmediatePropagation();`
|
||||
event.stopPropagation();
|
||||
}
|
||||
}) as EventListener;
|
||||
|
||||
/**
|
||||
* @memberof UmbContextBoundary
|
||||
*/
|
||||
public hostConnected(): void {
|
||||
//this.hostElement.addEventListener(UMB_CONTENT_REQUEST_EVENT_TYPE, this.#handleContextRequest);
|
||||
this.#eventTarget.dispatchEvent(new UmbContextProvideEventImplementation(this.#contextAlias));
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof UmbContextBoundary
|
||||
*/
|
||||
public hostDisconnected(): void {}
|
||||
|
||||
destroy(): void {
|
||||
(this.#eventTarget as unknown) = undefined;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { UmbContextRequestEvent } from '../consume/context-request.event.js';
|
||||
import type { UmbContextToken } from '../token/index.js';
|
||||
import { UMB_CONTENT_REQUEST_EVENT_TYPE, UMB_DEBUG_CONTEXT_EVENT_TYPE } from '../consume/context-request.event.js';
|
||||
import { UMB_CONTEXT_REQUEST_EVENT_TYPE, UMB_DEBUG_CONTEXT_EVENT_TYPE } from '../consume/context-request.event.js';
|
||||
import { UmbContextProvideEventImplementation } from './context-provide.event.js';
|
||||
|
||||
/**
|
||||
@@ -41,12 +41,12 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
|
||||
this.#apiAlias = idSplit[1] ?? 'default';
|
||||
this.#instance = instance;
|
||||
|
||||
this.#eventTarget.addEventListener(UMB_CONTENT_REQUEST_EVENT_TYPE, this.#handleContextRequest);
|
||||
this.#eventTarget.addEventListener(UMB_CONTEXT_REQUEST_EVENT_TYPE, this.#handleContextRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {UmbContextRequestEvent} event
|
||||
* @param {UmbContextRequestEvent} event - the event to handle
|
||||
* @memberof UmbContextProvider
|
||||
*/
|
||||
#handleContextRequest = ((event: UmbContextRequestEvent): void => {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from './context-boundary.js';
|
||||
export * from './context-boundary.controller.js';
|
||||
export * from './context-provide.event.js';
|
||||
export * from './context-provider.controller.js';
|
||||
export * from './context-provider.js';
|
||||
export * from './context-provide.event.js';
|
||||
|
||||
@@ -13,10 +13,14 @@ import {
|
||||
type UUIModalDialogElement,
|
||||
type UUIModalSidebarElement,
|
||||
} from '@umbraco-cms/backoffice/external/uui';
|
||||
import type { UmbRouterSlotElement } from '@umbraco-cms/backoffice/router';
|
||||
import { UMB_ROUTE_CONTEXT, type UmbRouterSlotElement } from '@umbraco-cms/backoffice/router';
|
||||
import { createExtensionElement } from '@umbraco-cms/backoffice/extension-api';
|
||||
import type { UmbContextRequestEvent } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UMB_CONTENT_REQUEST_EVENT_TYPE, UmbContextProvider } from '@umbraco-cms/backoffice/context-api';
|
||||
import {
|
||||
UMB_CONTEXT_REQUEST_EVENT_TYPE,
|
||||
UmbContextBoundary,
|
||||
UmbContextProvider,
|
||||
} from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
@customElement('umb-modal')
|
||||
export class UmbModalElement extends UmbLitElement {
|
||||
@@ -41,7 +45,7 @@ export class UmbModalElement extends UmbLitElement {
|
||||
#innerElement = new UmbBasicState<HTMLElement | undefined>(undefined);
|
||||
|
||||
#modalExtensionObserver?: UmbObserverController<ManifestModal | undefined>;
|
||||
#modalRouterElement: UmbRouterSlotElement = document.createElement('umb-router-slot');
|
||||
#modalRouterElement?: HTMLDivElement | UmbRouterSlotElement;
|
||||
|
||||
#onClose = () => {
|
||||
this.element?.removeEventListener(UUIModalCloseEvent, this.#onClose);
|
||||
@@ -59,7 +63,7 @@ export class UmbModalElement extends UmbLitElement {
|
||||
|
||||
// The following code is the context api proxy.
|
||||
// It re-dispatches the context api request event to the origin target of this modal, in other words the element that initiated the modal. [NL]
|
||||
this.element.addEventListener(UMB_CONTENT_REQUEST_EVENT_TYPE, ((event: UmbContextRequestEvent) => {
|
||||
this.element.addEventListener(UMB_CONTEXT_REQUEST_EVENT_TYPE, ((event: UmbContextRequestEvent) => {
|
||||
if (!this.#modalContext) return;
|
||||
// Note for this hack (The if-sentence): [NL]
|
||||
// We do not currently have a good enough control to ensure that the proxy is last, meaning if another context is provided at this element, it might respond after the proxy event has been dispatched.
|
||||
@@ -85,6 +89,7 @@ export class UmbModalElement extends UmbLitElement {
|
||||
*
|
||||
*/
|
||||
if (this.#modalContext.router) {
|
||||
this.#modalRouterElement = document.createElement('umb-router-slot');
|
||||
this.#modalRouterElement.routes = [
|
||||
{
|
||||
path: '',
|
||||
@@ -92,9 +97,16 @@ export class UmbModalElement extends UmbLitElement {
|
||||
},
|
||||
];
|
||||
this.#modalRouterElement.parent = this.#modalContext.router;
|
||||
} else {
|
||||
this.#modalRouterElement = document.createElement('div');
|
||||
// Notice inline styling here is used cause the element is not appended into this elements shadowDom but outside and there by gets into the element via a slot.
|
||||
this.#modalRouterElement.style.position = 'relative';
|
||||
this.#modalRouterElement.style.height = '100%';
|
||||
new UmbContextBoundary(this.#modalRouterElement, UMB_ROUTE_CONTEXT).hostConnected();
|
||||
}
|
||||
|
||||
this.element.appendChild(this.#modalRouterElement);
|
||||
|
||||
this.#observeModal(this.#modalContext.alias.toString());
|
||||
|
||||
const provider = new UmbContextProvider(this.element, UMB_MODAL_CONTEXT, this.#modalContext);
|
||||
@@ -151,14 +163,14 @@ export class UmbModalElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#appendInnerElement(element: HTMLElement) {
|
||||
this.#modalRouterElement.appendChild(element);
|
||||
this.#modalRouterElement!.appendChild(element);
|
||||
this.#innerElement.setValue(element);
|
||||
}
|
||||
|
||||
#removeInnerElement() {
|
||||
const innerElement = this.#innerElement.getValue();
|
||||
if (innerElement) {
|
||||
this.#modalRouterElement.removeChild(innerElement);
|
||||
this.#modalRouterElement!.removeChild(innerElement);
|
||||
this.#innerElement.setValue(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,9 +259,9 @@ export class UmbModalRouteRegistrationController<
|
||||
}
|
||||
|
||||
public open(params: { [key: string]: string | number }, prepend?: string) {
|
||||
if (this.active) return;
|
||||
if (this.active || !this.#routeBuilder) return;
|
||||
|
||||
window.history.pushState({}, '', this.#routeBuilder?.(params) + (prepend ? `${prepend}` : ''));
|
||||
window.history.pushState({}, '', this.#routeBuilder(params) + (prepend ? `${prepend}` : ''));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,6 +277,7 @@ export class UmbModalRouteRegistrationController<
|
||||
return this;
|
||||
}
|
||||
public _internal_setRouteBuilder(urlBuilder: UmbModalRouteBuilder) {
|
||||
if (!this.#routeContext) return;
|
||||
this.#routeBuilder = urlBuilder;
|
||||
this.#urlBuilderCallback?.(urlBuilder);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UMB_CONTENT_REQUEST_EVENT_TYPE, type UmbContextRequestEvent } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UMB_CONTEXT_REQUEST_EVENT_TYPE, type UmbContextRequestEvent } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { RawEditorOptions } from '@umbraco-cms/backoffice/external/tinymce';
|
||||
import { UUIIconRequestEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
|
||||
@@ -34,7 +34,7 @@ export const defaultFallbackConfig: RawEditorOptions = {
|
||||
init_instance_callback: function (editor) {
|
||||
// The following code is the context api proxy. [NL]
|
||||
// It re-dispatches the context api request event to the origin target of this modal, in other words the element that initiated the modal. [NL]
|
||||
editor.dom.doc.addEventListener(UMB_CONTENT_REQUEST_EVENT_TYPE, ((event: UmbContextRequestEvent) => {
|
||||
editor.dom.doc.addEventListener(UMB_CONTEXT_REQUEST_EVENT_TYPE, ((event: UmbContextRequestEvent) => {
|
||||
if (!editor.iframeElement) return;
|
||||
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
Reference in New Issue
Block a user