Skip to content

Commit e79af57

Browse files
Copilotnielslyngsoe
andcommitted
Fix circular dependency by introducing UmbModalRouteHandler abstraction
Co-authored-by: nielslyngsoe <[email protected]>
1 parent d80932e commit e79af57

File tree

11 files changed

+62
-10
lines changed

11 files changed

+62
-10
lines changed

src/Umbraco.Web.UI.Client/src/packages/core/modal/component/modal.element.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import {
1515
type UUIModalSidebarElement,
1616
type UUIModalSidebarSize,
1717
} from '@umbraco-cms/backoffice/external/uui';
18-
import { UMB_ROUTE_CONTEXT, type UmbRouterSlotElement } from '@umbraco-cms/backoffice/router';
18+
import type { UmbRouterSlotElement } from '@umbraco-cms/backoffice/router';
1919
import { createExtensionElement, loadManifestElement } from '@umbraco-cms/backoffice/extension-api';
20-
import { UmbContextBoundary, UmbContextProvider } from '@umbraco-cms/backoffice/context-api';
20+
import { UmbContextProvider } from '@umbraco-cms/backoffice/context-api';
2121
import { UmbContextProxyController } from '@umbraco-cms/backoffice/context-proxy';
2222

2323
@customElement('umb-modal')
@@ -93,7 +93,8 @@ export class UmbModalElement extends UmbLitElement {
9393
this.#modalRouterElement = document.createElement('div');
9494
// 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.
9595
this.#modalRouterElement.style.display = 'contents';
96-
new UmbContextBoundary(this.#modalRouterElement, UMB_ROUTE_CONTEXT).hostConnected();
96+
// TODO: Consider if this context boundary is necessary for modals without router
97+
// new UmbContextBoundary(this.#modalRouterElement, UMB_ROUTE_CONTEXT).hostConnected();
9798
}
9899

99100
this.element.appendChild(this.#modalRouterElement);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export type { UmbModalContext, UmbModalRejectReason, UmbModalContextClassArgs } from './modal.context.js';
2+
export type { UmbModalRouteHandler } from './modal-route-handler.interface.js';
23
export { UMB_MODAL_CONTEXT } from './modal.context-token.js';
34
export * from './modal-manager.context.js';
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Interface for handling modal route operations.
3+
* This abstraction allows modal contexts to interact with routing
4+
* without directly depending on router implementations.
5+
*/
6+
export interface UmbModalRouteHandler {
7+
/**
8+
* Removes a modal path from the current route
9+
* @param path - The modal path to remove
10+
*/
11+
removeModalPath(path?: string): void;
12+
}

src/Umbraco.Web.UI.Client/src/packages/core/modal/context/modal.context.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { UmbModalToken } from '../token/modal-token.js';
22
import type { UmbModalConfig, UmbModalType } from '../types.js';
3+
import type { UmbModalRouteHandler } from './modal-route-handler.interface.js';
34
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
45
import type { UUIModalElement, UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
56
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
67
import { umbDeepMerge } from '@umbraco-cms/backoffice/utils';
78
import { UmbId } from '@umbraco-cms/backoffice/id';
89
import { UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
910
import { UmbViewController } from '@umbraco-cms/backoffice/view';
10-
import { UMB_ROUTE_CONTEXT } from '@umbraco-cms/backoffice/router';
1111
import type { ElementLoaderProperty } from '@umbraco-cms/backoffice/extension-api';
1212
import type { IRouterSlot } from '@umbraco-cms/backoffice/router';
1313
import type { UmbDeepPartialObject } from '@umbraco-cms/backoffice/utils';
@@ -26,6 +26,7 @@ export type UmbModalContextClassArgs<
2626
data?: ModalAliasTypeAsToken['DATA'];
2727
value?: ModalAliasTypeAsToken['VALUE'];
2828
modal?: UmbModalConfig;
29+
routeHandler?: UmbModalRouteHandler;
2930
};
3031

3132
// TODO: consider splitting this into two separate handlers
@@ -55,6 +56,7 @@ export class UmbModalContext<
5556
public readonly backdropBackground?: string;
5657
public readonly router: IRouterSlot | null = null;
5758
public readonly alias: string | UmbModalToken<ModalData, ModalValue>;
59+
public readonly routeHandler?: UmbModalRouteHandler;
5860

5961
#value;
6062
public readonly value;
@@ -73,6 +75,7 @@ export class UmbModalContext<
7375
this.key = args.modal?.key || UmbId.new();
7476
this.router = args.router ?? null;
7577
this.alias = modalAlias;
78+
this.routeHandler = args.routeHandler;
7679

7780
this.view = new UmbViewController(this, modalAlias.toString());
7881

@@ -127,8 +130,7 @@ export class UmbModalContext<
127130

128131
// eslint-disable-next-line @typescript-eslint/naming-convention
129132
async _internal_removeCurrentModal() {
130-
const routeContext = await this.getContext(UMB_ROUTE_CONTEXT);
131-
routeContext?._internal_removeModalPath(this.#activeModalPath);
133+
this.routeHandler?.removeModalPath(this.#activeModalPath);
132134
}
133135

134136
forceResolve() {

src/Umbraco.Web.UI.Client/src/packages/core/router/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ export * from './encode-folder-name.function.js';
44
export * from './modal-registration/modal-route-registration.controller.js';
55
export * from './path-pattern.class.js';
66
export * from './router-slot/index.js';
7+
export * from './route/route-modal-handler.class.js';
78
export type * from './modal-registration/modal-route-registration.interface.js';
89
export type * from './types.js';

src/Umbraco.Web.UI.Client/src/packages/core/router/modal-registration/modal-route-registration.controller.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
UmbModalContext,
99
UmbModalContextClassArgs,
1010
UmbModalManagerContext,
11+
UmbModalRouteHandler,
1112
UmbModalToken,
1213
} from '@umbraco-cms/backoffice/modal';
1314
import type { UmbControllerAlias, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
@@ -327,7 +328,12 @@ export class UmbModalRouteRegistrationController<
327328
this.#modalContext = undefined;
328329
};
329330

330-
async routeSetup(router: IRouterSlot, modalManagerContext: UmbModalManagerContext, params: Params) {
331+
async routeSetup(
332+
router: IRouterSlot,
333+
modalManagerContext: UmbModalManagerContext,
334+
params: Params,
335+
routeHandler: UmbModalRouteHandler,
336+
) {
331337
// If already open, don't do anything:
332338
if (this.active) return;
333339

@@ -337,6 +343,7 @@ export class UmbModalRouteRegistrationController<
337343
modal: {},
338344
...modalData,
339345
router,
346+
routeHandler,
340347
} as UmbModalContextClassArgs<UmbModalToken<UmbModalTokenData, UmbModalTokenValue>>;
341348
args.modal!.key = this.#key;
342349

src/Umbraco.Web.UI.Client/src/packages/core/router/modal-registration/modal-route-registration.interface.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import type { IRouterSlot, Params } from '../router-slot/index.js';
22
import type { UmbModalRouteBuilder } from './modal-route-registration.controller.js';
3-
import type { UmbModalContext, UmbModalManagerContext, UmbModalToken } from '@umbraco-cms/backoffice/modal';
3+
import type {
4+
UmbModalContext,
5+
UmbModalManagerContext,
6+
UmbModalToken,
7+
UmbModalRouteHandler,
8+
} from '@umbraco-cms/backoffice/modal';
49

510
export interface UmbModalRouteRegistration<
611
UmbModalTokenData extends { [key: string]: any } = { [key: string]: any },
@@ -18,6 +23,7 @@ export interface UmbModalRouteRegistration<
1823
router: IRouterSlot,
1924
modalManagerContext: UmbModalManagerContext,
2025
params: Params,
26+
routeHandler: UmbModalRouteHandler,
2127
): Promise<undefined | UmbModalContext<UmbModalTokenData, UmbModalTokenValue>>;
2228

2329
// eslint-disable-next-line @typescript-eslint/naming-convention
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { UmbModalRouteHandler } from '@umbraco-cms/backoffice/modal';
2+
3+
/**
4+
* Implementation of UmbModalRouteHandler that delegates to a route context
5+
*/
6+
export class UmbRouteModalHandler implements UmbModalRouteHandler {
7+
readonly #removeModalPath: (path?: string) => void;
8+
9+
constructor(removeModalPath: (path?: string) => void) {
10+
this.#removeModalPath = removeModalPath;
11+
}
12+
13+
removeModalPath(path?: string): void {
14+
this.#removeModalPath(path);
15+
}
16+
}

src/Umbraco.Web.UI.Client/src/packages/core/router/route/route.context.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { IRouterSlot } from '../router-slot/index.js';
33
import type { UmbModalRouteRegistration } from '../modal-registration/modal-route-registration.interface.js';
44
import { umbGenerateRoutePathBuilder } from '../generate-route-path-builder.function.js';
55
import type { UmbRoute } from './route.interface.js';
6+
import { UmbRouteModalHandler } from './route-modal-handler.class.js';
67
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
78
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
89
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
@@ -19,6 +20,7 @@ export class UmbRouteContext extends UmbContextBase {
1920
#modalContext?: typeof UMB_MODAL_MANAGER_CONTEXT.TYPE;
2021
#modalRoutes: UmbRoutePlusModalKey[] = [];
2122
#activeModalPath?: string;
23+
#routeHandler: UmbRouteModalHandler;
2224

2325
#basePath = new UmbStringState(undefined);
2426
public readonly basePath = this.#basePath.asObservable();
@@ -32,6 +34,7 @@ export class UmbRouteContext extends UmbContextBase {
3234
constructor(host: UmbControllerHost, mainRouter: IRouterSlot, modalRouter: IRouterSlot) {
3335
super(host, UMB_ROUTE_CONTEXT);
3436
this.#modalRouter = modalRouter;
37+
this.#routeHandler = new UmbRouteModalHandler((path) => this._internal_removeModalPath(path));
3538
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (context) => {
3639
this.#modalContext = context;
3740
this.#generateModalRoutes();
@@ -70,6 +73,7 @@ export class UmbRouteContext extends UmbContextBase {
7073
this.#modalRouter,
7174
this.#modalContext,
7275
info.match.params,
76+
this.#routeHandler,
7377
);
7478
if (modalContext) {
7579
modalContext._internal_setCurrentModalPath(info.match.fragments.consumed);

src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-element-base.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ export abstract class UmbTreeItemElementBase<
136136
.loading=${this._isLoading}
137137
.hasChildren=${this._hasChildren}
138138
.showChildren=${this._isOpen}
139-
.caretLabel=${this._isOpen ? this.localize.term('visuallyHiddenTexts_collapseChildItems') + ' ' + this._label: this.localize.term('visuallyHiddenTexts_expandChildItems') + ' ' + this._label}
139+
.caretLabel=${this._isOpen
140+
? this.localize.term('visuallyHiddenTexts_collapseChildItems') + ' ' + this._label
141+
: this.localize.term('visuallyHiddenTexts_expandChildItems') + ' ' + this._label}
140142
label=${this._label}
141143
href="${ifDefined(this._isSelectableContext ? undefined : this._href)}">
142144
${this.renderIconContainer()} ${this.renderLabel()} ${this.#renderActions()} ${this.#renderChildItems()}

0 commit comments

Comments
 (0)