diff --git a/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts b/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts index bf46856f13..01f370afcd 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/modal-handler.ts @@ -13,7 +13,7 @@ import { ManifestModal } from '@umbraco-cms/extensions-registry'; /** * Type which omits the real submit method, and replaces it with a submit method which accepts an optional argument depending on the generic type. */ -export type UmbModalHandler = Omit< +export type UmbModalHandler = Omit< UmbModalHandlerClass, 'submit' > & @@ -35,7 +35,7 @@ type OptionalSubmitArgumentIfUndefined = T extends undefined }; //TODO consider splitting this into two separate handlers -export class UmbModalHandlerClass { +export class UmbModalHandlerClass { private _submitPromise: Promise; private _submitResolver?: (value: ModalResult) => void; private _submitRejecter?: () => void; diff --git a/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts b/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts index aacb0f1646..3bf9eda86a 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/modal.context.ts @@ -76,7 +76,7 @@ export class UmbModalContext { * @return {*} {UmbModalHandler} * @memberof UmbModalContext */ - public open( + public open( modalAlias: string | UmbModalToken, data?: ModalData, config?: UmbModalConfig diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts index ab466d34d7..6dc7240081 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/modal-token.ts @@ -1,6 +1,6 @@ import { UmbModalConfig } from '../modal.context'; -export class UmbModalToken { +export class UmbModalToken { /** * Get the data type of the token's data. * diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-multi-url-picker/input-multi-url-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-multi-url-picker/input-multi-url-picker.element.ts index dc71b0b20b..7ba2791d09 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-multi-url-picker/input-multi-url-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-multi-url-picker/input-multi-url-picker.element.ts @@ -3,10 +3,9 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property } from 'lit/decorators.js'; import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; import { UUIModalSidebarSize } from '@umbraco-ui/uui-modal-sidebar'; -import { UmbRouteContext, UMB_ROUTE_CONTEXT_TOKEN } from '@umbraco-cms/router'; +import { UmbModalRouteBuilder, UmbRouteContext, UMB_ROUTE_CONTEXT_TOKEN } from '@umbraco-cms/router'; import { UmbLinkPickerLink, UMB_LINK_PICKER_MODAL_TOKEN } from '../../modals/link-picker'; import { UmbLitElement } from '@umbraco-cms/element'; -import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/modal'; /** * @element umb-input-multi-url-picker @@ -88,7 +87,7 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen @property({ attribute: false }) set urls(data: Array) { if (!data) return; - this._urls = data; + this._urls = [...data]; // Unfreeze data coming from State, so we can manipulate it. super.value = this._urls.map((x) => x.url).join(','); } @@ -97,9 +96,11 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen } private _urls: Array = []; - private _modalContext?: UmbModalContext; + //private _modalContext?: UmbModalContext; private _routeContext?: UmbRouteContext; + private _linkPickerURL?: UmbModalRouteBuilder; + constructor() { super(); this.addValidator( @@ -113,9 +114,11 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen () => !!this.max && this.urls.length > this.max ); + /* this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => { this._modalContext = instance; }); + */ this.consumeContext(UMB_ROUTE_CONTEXT_TOKEN, (instance) => { this._routeContext = instance; @@ -123,16 +126,29 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen // Registre the routes of this UI: // TODO: To avoid retriving the property alias, we might make use of the property context? // Or maybe its not the property-alias, but something unique? as this might not be in a property?. - this._routeContext.registerModal(UMB_LINK_PICKER_MODAL_TOKEN, { - //path: `${'to-do-myPropertyAlias'}/:index`, - path: `modal`, - onSetup: (routeInfo) => { + this._linkPickerURL = this._routeContext.registerModal(UMB_LINK_PICKER_MODAL_TOKEN, { + path: `${'to-do-myPropertyAlias'}/:index`, + onSetup: (routingInfo) => { // Get index from routeInfo: - const index = 0; + const indexParam = routingInfo.match.params.index; + if (!indexParam) return false; + let index: number | null = parseInt(routingInfo.match.params.index); + if (Number.isNaN(index)) return false; + // Use the index to find data: - console.log('onSetup modal', routeInfo); - /* - modaldata = { + console.log('onSetup modal index:', index); + + let data: UmbLinkPickerLink | null = null; + if (index >= 0 && index < this.urls.length) { + data = this._getItemByIndex(index); + } else { + index = null; + } + + console.log('onSetup modal got data:', data); + + const modalData = { + index: index, link: { name: data?.name, published: data?.published, @@ -148,14 +164,12 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen overlaySize: this.overlaySize || 'small', }, }; - return modaldata; - */ + return modalData; }, - onSubmit: (newUrl) => { - if (!newUrl) return; - - const index = 0; // TODO: get the index in some way?. - this._setSelection(newUrl, index); + onSubmit: (submitData) => { + console.log('On submit in property editor input'); + if (!submitData) return; + this._setSelection(submitData.link, submitData.index); }, onReject: () => { console.log('User cancelled dialog.'); @@ -169,9 +183,16 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen this._dispatchChangeEvent(); } - private _setSelection(selection: UmbLinkPickerLink, index?: number) { - if (index !== undefined && index >= 0) this.urls[index] = selection; - else this.urls.push(selection); + private _getItemByIndex(index: number) { + return this.urls[index]; + } + + private _setSelection(selection: UmbLinkPickerLink, index: number | null) { + if (index !== null && index >= 0) { + this.urls[index] = selection; + } else { + this.urls.push(selection); + } this._dispatchChangeEvent(); } @@ -182,6 +203,8 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen } private _openPicker(data?: UmbLinkPickerLink, index?: number) { + console.log('JS open picker, should fail for now,'); + /* const modalHandler = this._modalContext?.open(UMB_LINK_PICKER_MODAL_TOKEN, { link: { name: data?.name, @@ -203,11 +226,13 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen this._setSelection(newUrl, index); }); + */ } render() { return html`${this.urls?.map((link, index) => this._renderItem(link, index))} - Add`; + Add`; + // "modal/Umb.Modal.LinkPicker/${'to-do-myPropertyAlias'}/-1" } private _renderItem(link: UmbLinkPickerLink, index: number) { @@ -217,10 +242,11 @@ export class UmbInputMultiUrlPickerElement extends FormControlMixin(UmbLitElemen @open="${() => this._openPicker(link, index)}"> - Edit + Edit Remove `; + // "modal/Umb.Modal.LinkPicker/${'to-do-myPropertyAlias'}/${index}" } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/index.ts index ecbd202e43..da95dca1d0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/index.ts @@ -2,11 +2,12 @@ import { UUIModalSidebarSize } from '@umbraco-ui/uui-modal-sidebar'; import { UmbModalToken } from '@umbraco-cms/modal'; export interface UmbLinkPickerModalData { + index: number | null; link: UmbLinkPickerLink; config: UmbLinkPickerConfig; } -export type UmbLinkPickerModalResult = UmbLinkPickerLink; +export type UmbLinkPickerModalResult = { index: number | null; link: UmbLinkPickerLink }; export interface UmbLinkPickerLink { icon?: string | null; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts index 73b31ccc82..dcd9b9181f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts @@ -46,6 +46,9 @@ export class UmbLinkPickerModalElement extends UmbModalBaseElement = [ alias: 'multiUrlPicker', culture: null, segment: null, - value: null, + value: [ + { + name: undefined, + published: undefined, + queryString: undefined, + target: undefined, + trashed: undefined, + udi: 'umb://document/c05da24d7740447b9cdcbd8ce2172e38', + url: 'umb://document/c05da24d7740447b9cdcbd8ce2172e38', + }, + ], }, { alias: 'multiNodeTreePicker', diff --git a/src/Umbraco.Web.UI.Client/src/core/router/route.context.ts b/src/Umbraco.Web.UI.Client/src/core/router/route.context.ts index 6b9fff800a..3fbc8458cb 100644 --- a/src/Umbraco.Web.UI.Client/src/core/router/route.context.ts +++ b/src/Umbraco.Web.UI.Client/src/core/router/route.context.ts @@ -1,22 +1,23 @@ -import { IRoute } from 'router-slot'; +import { IRoute, IRoutingInfo, PARAM_IDENTIFIER, stripSlash } from 'router-slot'; import { UmbContextConsumerController, UmbContextProviderController, UmbContextToken } from '@umbraco-cms/context-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { UmbModalToken } from '@umbraco-cms/modal'; - -// Get the second generic type of UmbModalToken: -type GetResultType = T extends UmbModalToken ? Result : unknown; +import { UmbModalToken, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/modal'; const EmptyDiv = document.createElement('div'); -export type UmbModalRoute = { +// TODO: Consider accepting the Token as a generic: +export type UmbModalRoute = { path: string; - onSetup: (routeInfo: any) => void; - onSubmit: (data: UmbModalTokenResult) => void; + onSetup: (routingInfo: IRoutingInfo) => UmbModalTokenData | false; + onSubmit: (data: UmbModalTokenResult) => void | PromiseLike; onReject: () => void; }; +export type UmbModalRouteBuilder = (params: { [key: string]: string | number }) => string; + export class UmbRouteContext { #host: UmbControllerHostInterface; + #modalContext?: typeof UMB_MODAL_CONTEXT_TOKEN.TYPE; #contextRoutes: IRoute[] = []; constructor(host: UmbControllerHostInterface, private _onGotModals: (contextRoutes: any) => void) { @@ -25,28 +26,54 @@ export class UmbRouteContext { /*new UmbContextConsumerController(host, UMB_ROUTE_CONTEXT_TOKEN, (context) => { console.log('got a parent', this === context, this, context); });*/ + new UmbContextConsumerController(host, UMB_MODAL_CONTEXT_TOKEN, (context) => { + this.#modalContext = context; + }); // Consider using this event, to stay up to date with current full-URL. which is necessary for Modal opening. // window.addEventListener('navigationsuccess', this._onNavigationChanged); } - public registerModal>( - modalAlias: T, - options: UmbModalRoute - ) { - console.log('registerModalRoute', modalAlias.toString(), options); - + public registerModal( + modalAlias: UmbModalToken | string, + options: UmbModalRoute + ): UmbModalRouteBuilder { + const localPath = `modal/${modalAlias.toString()}/${options.path}`; this.#contextRoutes.push({ - path: options.path, + path: localPath, + pathMatch: 'suffix', component: EmptyDiv, setup: (component, info) => { - console.log('modal open?', info); - options.onSetup(info); + const modalData = options.onSetup(info); + if (modalData && this.#modalContext) { + const modalHandler = this.#modalContext.open(modalAlias, modalData); + modalHandler.onSubmit().then( + () => this._removeModalPath(info), + () => this._removeModalPath(info) + ); + modalHandler.onSubmit().then(options.onSubmit, options.onReject); + } }, }); //TODO: move to a method: this._onGotModals(this.#contextRoutes); + + return (params: { [key: string]: string | number }) => { + const localRoutePath = stripSlash( + localPath.replace(PARAM_IDENTIFIER, (substring: string, ...args: string[]) => { + return params[args[0]]; + //return `([^\/]+)`; + }) + ); + const baseRoutePath = window.location.href; + return (baseRoutePath.endsWith('/') ? baseRoutePath : baseRoutePath + '/') + localRoutePath; + }; + } + + private _removeModalPath(info: IRoutingInfo) { + window.history.pushState({}, '', window.location.href.split(info.match.fragments.consumed)[0]); + console.log('ask to remove path', info.match.fragments.consumed); } } diff --git a/src/Umbraco.Web.UI.Client/src/core/router/router-slot.element.ts b/src/Umbraco.Web.UI.Client/src/core/router/router-slot.element.ts index f404efc65f..fde2927bc8 100644 --- a/src/Umbraco.Web.UI.Client/src/core/router/router-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/core/router/router-slot.element.ts @@ -44,7 +44,6 @@ export class UmbRouterSlotElement extends UmbLitElement { #routeContext = new UmbRouteContext(this, (contextRoutes) => { (this.#modalRouter as any).routes = contextRoutes; - console.log(this.absoluteRouterPath, this.#router, this.#modalRouter, 'Router got context routes', contextRoutes); // Force a render? this.#modalRouter.render(); });