prevent context consumption of route-context from a modal that is not routable
This commit is contained in:
@@ -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_CONTENT_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_CONTENT_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;
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
|
||||
|
||||
/**
|
||||
* @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_CONTENT_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);
|
||||
@@ -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,13 @@ export class UmbModalElement extends UmbLitElement {
|
||||
},
|
||||
];
|
||||
this.#modalRouterElement.parent = this.#modalContext.router;
|
||||
} else {
|
||||
this.#modalRouterElement = document.createElement('div');
|
||||
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 +160,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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user