diff --git a/Build/Sources/TypeScript/backend/module/router.ts b/Build/Sources/TypeScript/backend/module/router.ts index b38746a82a89dc06663cbab80adc599667c09fb2..bfcea526cc619c9bf322cace61eb8153f945c090 100644 --- a/Build/Sources/TypeScript/backend/module/router.ts +++ b/Build/Sources/TypeScript/backend/module/router.ts @@ -158,7 +158,14 @@ export class ModuleRouter extends LitElement { try { const module = await import(moduleName + '.js'); - // @todo: Check if .componentName exists + element = this.querySelector(`*[slot="${moduleName}"]`); + if (element !== null) { + // The element has been created parallelly during the asynchronous module load; use that instance + return element; + } + if (!('componentName' in module)) { + throw new Error(`module ${moduleName} is missing the "componentName" export`); + } element = document.createElement(module.componentName); } catch (e) { console.error({ msg: `Error importing ${moduleName} as backend module`, err: e }); diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/module/router.js b/typo3/sysext/backend/Resources/Public/JavaScript/module/router.js index 319e338430721e087c687a171e8c4e875337b333..023b9aeddec91745cc693ab4ff9b21a83a61a30a 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/module/router.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/module/router.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(t,e,o,r){var i,l=arguments.length,n=l<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(t,e,o,r);else for(var a=t.length-1;a>=0;a--)(i=t[a])&&(n=(l<3?i(n):l>3?i(e,o,n):i(e,o))||n);return l>3&&n&&Object.defineProperty(e,o,n),n};import{html,css,LitElement}from"lit";import{customElement,property,query}from"lit/decorators.js";import{ModuleUtility}from"@typo3/backend/module.js";const IFRAME_COMPONENT="@typo3/backend/module/iframe",alwaysUpdate=()=>!0;let ModuleRouter=class extends LitElement{constructor(){super(),this.module="",this.endpoint="",this.addEventListener("typo3-module-load",(({target:t,detail:e})=>{const o=t.getAttribute("slot");this.pushState({slotName:o,detail:e})})),this.addEventListener("typo3-module-loaded",(({detail:t})=>{this.updateBrowserState(t)})),this.addEventListener("typo3-iframe-load",(({detail:t})=>{let e={slotName:IFRAME_COMPONENT,detail:t};if(e.detail.url.includes(this.stateTrackerUrl+"?state=")){const t=e.detail.url.split("?state=");e=JSON.parse(decodeURIComponent(t[1]||"{}"))}this.slotElement.getAttribute("name")!==e.slotName&&this.slotElement.setAttribute("name",e.slotName),this.markActive(e.slotName,this.slotElement.getAttribute("name")===IFRAME_COMPONENT?null:e.detail.url,!1),this.updateBrowserState(e.detail),this.parentElement.dispatchEvent(new CustomEvent("typo3-module-load",{bubbles:!0,composed:!0,detail:e.detail}))})),this.addEventListener("typo3-iframe-loaded",(({detail:t})=>{this.updateBrowserState(t),this.parentElement.dispatchEvent(new CustomEvent("typo3-module-loaded",{bubbles:!0,composed:!0,detail:t}))}))}render(){const t=ModuleUtility.getFromName(this.module).component||IFRAME_COMPONENT;return html`<slot name="${t}"></slot>`}updated(){const t=ModuleUtility.getFromName(this.module).component||IFRAME_COMPONENT;this.markActive(t,this.endpoint)}async markActive(t,e,o=!0){const r=await this.getModuleElement(t);e&&(o||r.getAttribute("endpoint")!==e)&&r.setAttribute("endpoint",e),r.hasAttribute("active")||r.setAttribute("active","");for(let t=r.previousElementSibling;null!==t;t=t.previousElementSibling)t.removeAttribute("active");for(let t=r.nextElementSibling;null!==t;t=t.nextElementSibling)t.removeAttribute("active")}async getModuleElement(t){let e=this.querySelector(`*[slot="${t}"]`);if(null!==e)return e;try{const o=await import(t+".js");e=document.createElement(o.componentName)}catch(e){throw console.error({msg:`Error importing ${t} as backend module`,err:e}),e}return e.setAttribute("slot",t),this.appendChild(e),e}async pushState(t){const e=this.stateTrackerUrl+"?state="+encodeURIComponent(JSON.stringify(t));(await this.getModuleElement(IFRAME_COMPONENT)).setAttribute("endpoint",e)}updateBrowserState(t){const e=new URL(t.url||"",window.location.origin),o=new URLSearchParams(e.search),r="title"in t?t.title:"";if(null!==r){const t=[this.sitename];""!==r&&t.unshift(r),this.sitenameFirst&&t.reverse(),document.title=t.join(" · ")}if(!o.has("token")){if(!o.has("install[controller]"))return;{const t=o.get("install[controller]");o.delete("install[controller]"),o.delete("install[context]"),e.pathname=e.pathname.replace("/typo3/install.php","/typo3/module/tools/"+t)}}o.delete("token"),e.search=o.toString();const i=e.toString();window.history.replaceState(t,"",i)}};ModuleRouter.styles=css` +var __decorate=function(t,e,o,r){var i,l=arguments.length,n=l<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(t,e,o,r);else for(var a=t.length-1;a>=0;a--)(i=t[a])&&(n=(l<3?i(n):l>3?i(e,o,n):i(e,o))||n);return l>3&&n&&Object.defineProperty(e,o,n),n};import{html,css,LitElement}from"lit";import{customElement,property,query}from"lit/decorators.js";import{ModuleUtility}from"@typo3/backend/module.js";const IFRAME_COMPONENT="@typo3/backend/module/iframe",alwaysUpdate=()=>!0;let ModuleRouter=class extends LitElement{constructor(){super(),this.module="",this.endpoint="",this.addEventListener("typo3-module-load",(({target:t,detail:e})=>{const o=t.getAttribute("slot");this.pushState({slotName:o,detail:e})})),this.addEventListener("typo3-module-loaded",(({detail:t})=>{this.updateBrowserState(t)})),this.addEventListener("typo3-iframe-load",(({detail:t})=>{let e={slotName:IFRAME_COMPONENT,detail:t};if(e.detail.url.includes(this.stateTrackerUrl+"?state=")){const t=e.detail.url.split("?state=");e=JSON.parse(decodeURIComponent(t[1]||"{}"))}this.slotElement.getAttribute("name")!==e.slotName&&this.slotElement.setAttribute("name",e.slotName),this.markActive(e.slotName,this.slotElement.getAttribute("name")===IFRAME_COMPONENT?null:e.detail.url,!1),this.updateBrowserState(e.detail),this.parentElement.dispatchEvent(new CustomEvent("typo3-module-load",{bubbles:!0,composed:!0,detail:e.detail}))})),this.addEventListener("typo3-iframe-loaded",(({detail:t})=>{this.updateBrowserState(t),this.parentElement.dispatchEvent(new CustomEvent("typo3-module-loaded",{bubbles:!0,composed:!0,detail:t}))}))}render(){const t=ModuleUtility.getFromName(this.module).component||IFRAME_COMPONENT;return html`<slot name="${t}"></slot>`}updated(){const t=ModuleUtility.getFromName(this.module).component||IFRAME_COMPONENT;this.markActive(t,this.endpoint)}async markActive(t,e,o=!0){const r=await this.getModuleElement(t);e&&(o||r.getAttribute("endpoint")!==e)&&r.setAttribute("endpoint",e),r.hasAttribute("active")||r.setAttribute("active","");for(let t=r.previousElementSibling;null!==t;t=t.previousElementSibling)t.removeAttribute("active");for(let t=r.nextElementSibling;null!==t;t=t.nextElementSibling)t.removeAttribute("active")}async getModuleElement(t){let e=this.querySelector(`*[slot="${t}"]`);if(null!==e)return e;try{const o=await import(t+".js");if(e=this.querySelector(`*[slot="${t}"]`),null!==e)return e;if(!("componentName"in o))throw new Error(`module ${t} is missing the "componentName" export`);e=document.createElement(o.componentName)}catch(e){throw console.error({msg:`Error importing ${t} as backend module`,err:e}),e}return e.setAttribute("slot",t),this.appendChild(e),e}async pushState(t){const e=this.stateTrackerUrl+"?state="+encodeURIComponent(JSON.stringify(t));(await this.getModuleElement(IFRAME_COMPONENT)).setAttribute("endpoint",e)}updateBrowserState(t){const e=new URL(t.url||"",window.location.origin),o=new URLSearchParams(e.search),r="title"in t?t.title:"";if(null!==r){const t=[this.sitename];""!==r&&t.unshift(r),this.sitenameFirst&&t.reverse(),document.title=t.join(" · ")}if(!o.has("token")){if(!o.has("install[controller]"))return;{const t=o.get("install[controller]");o.delete("install[controller]"),o.delete("install[context]"),e.pathname=e.pathname.replace("/typo3/install.php","/typo3/module/tools/"+t)}}o.delete("token"),e.search=o.toString();const i=e.toString();window.history.replaceState(t,"",i)}};ModuleRouter.styles=css` :host { width: 100%; min-height: 100%;