UNPKG

@mochabug/adapt-web

Version:

Browser client library for Adapt automation platform

2 lines (1 loc) 20.8 kB
import{getIframeSrc}from"./iframe-url.js";import{PANEL_MANAGER_CSS}from"./css-panel.js";class IframePanelComponent{constructor(host){this.panelId="";this.resizeCapable=!1;this.host=host,this.element=document.createElement("div"),this.element.style.width="100%",this.element.style.height="100%",this.element.style.position="relative",this.iframe=this.createIframe(),this.element.appendChild(this.iframe)}get contentWindow(){return this.iframe.contentWindow}init(panelId,params){this.panelId=panelId,params.url&&(this.currentUrl=params.url,this.currentToken=params.token,this.setIframeSrc(params.url,params.token)),this.host.onComponentCreated(this.panelId,this)}update(params){if(params.url!==void 0){const urlChanged=params.url!==this.currentUrl,tokenChanged=params.token!==this.currentToken;this.currentUrl=params.url,params.token!==void 0&&(this.currentToken=params.token),(urlChanged||tokenChanged)&&(this.replaceIframe(),this.setIframeSrc(this.currentUrl,this.currentToken))}}dispose(){this.iframe.src="about:blank",this.host.onComponentDisposed(this.panelId)}postMessage(message,targetOrigin){try{this.iframe.contentWindow?.postMessage(message,targetOrigin)}catch{}}getOrigin(){if(!this.currentUrl)return null;try{return new URL(this.currentUrl).origin}catch{return null}}show(){this.iframe.classList.remove("mb-adapt__iframe--hidden"),this.iframe.offsetHeight,this.iframe.classList.add("mb-adapt__iframe--visible")}hide(){this.iframe.classList.remove("mb-adapt__iframe--visible"),this.iframe.classList.add("mb-adapt__iframe--hidden")}createIframe(){const iframe=document.createElement("iframe");return iframe.className=this.host.getClassNames()?.iframe||"mb-adapt__iframe",iframe.setAttribute("sandbox","allow-downloads allow-same-origin allow-scripts allow-popups allow-popups-to-escape-sandbox allow-forms allow-modals"),iframe.setAttribute("allow","autoplay; geolocation; camera; microphone; fullscreen; display-capture; web-share"),iframe.src="about:blank",iframe.addEventListener("load",()=>{if(iframe.src&&iframe.src!=="about:blank"){const origin=this.getOrigin();if(origin)try{iframe.contentWindow?.postMessage({type:"adapt-darkMode",darkMode:this.host.getDarkMode()},origin)}catch{}}}),iframe}setIframeSrc(url,token){const newSrc=getIframeSrc(url,token);this.iframe.src!==newSrc&&(this.resizeCapable=!1,this.iframe.style.height="",this.iframe.src=newSrc),this.show()}replaceIframe(){const fresh=this.createIframe();this.element.replaceChild(fresh,this.iframe),this.iframe=fresh,this.resizeCapable=!1}}const SVG_POPOUT='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 2h4.5v4.5"/><path d="M14 2L8.5 7.5"/><path d="M12 9v4.5a1.5 1.5 0 01-1.5 1.5h-8A1.5 1.5 0 011 13.5v-8A1.5 1.5 0 012.5 4H7"/></svg>',SVG_WINDOW='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="1.5" y="2.5" width="13" height="11" rx="1.5"/><line x1="1.5" y1="6" x2="14.5" y2="6"/><circle cx="4" cy="4.25" r="0.75" fill="currentColor" stroke="none"/><circle cx="6.5" cy="4.25" r="0.75" fill="currentColor" stroke="none"/></svg>',SVG_MINIMIZE='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="4" y1="8" x2="12" y2="8"/></svg>',SVG_MAXIMIZE='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2.5" y="2.5" width="11" height="11" rx="1.5"/></svg>',SVG_RESTORE='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="1.5" y="4.5" width="10" height="10" rx="1.5"/><path d="M4.5 4.5V3a1.5 1.5 0 011.5-1.5h7A1.5 1.5 0 0115 3v7a1.5 1.5 0 01-1.5 1.5H12"/></svg>',SVG_CLOSE='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="4" y1="4" x2="12" y2="12"/><line x1="12" y1="4" x2="4" y2="12"/></svg>',SVG_DOCK='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6.5 14H2V9.5"/><path d="M2 14l5.5-5.5"/><path d="M4 7V2.5A1.5 1.5 0 015.5 1h8A1.5 1.5 0 0115 2.5v8a1.5 1.5 0 01-1.5 1.5H9"/></svg>';class MinimizeManager{constructor(){this.tabsContainer=null;this.panelManager=null;this.entries=new Map;this._onBarVisibilityChanged=null;this._allowDocking=!0;this.dragState=null}setAllowDocking(allow){this._allowDocking=allow}onBarVisibilityChanged(cb){this._onBarVisibilityChanged=cb}init(tabsContainer,panelManager){this.tabsContainer=tabsContainer,this.panelManager=panelManager}minimize(groupId,overlay,group){if(!this.tabsContainer||this.entries.has(groupId))return;overlay.hide();const tab=this.createTab(groupId,group.panels.length,"floating");this.entries.set(groupId,{tab,overlay,group,mode:"floating"}),this.tabsContainer.appendChild(tab),this.showBar()}minimizeDocked(groupId,group,panelManager){if(!this.tabsContainer||this.entries.has(groupId))return;const rect=group.element.getBoundingClientRect();group.element.style.display="none",(this.tabsContainer.parentElement??document.body).appendChild(group.element);const posInfo=panelManager.minimizeDocked(group),dir=posInfo?.direction??"right",savedSize=dir==="top"||dir==="bottom"?rect.height>0?rect.height:void 0:rect.width>0?rect.width:void 0,tab=this.createTab(groupId,group.panels.length,"docked");this.entries.set(groupId,{tab,overlay:null,group,mode:"docked",savedPosition:{direction:dir,...posInfo?.siblingGroupId&&{siblingGroupId:posInfo.siblingGroupId},initialSize:savedSize}}),this.tabsContainer.appendChild(tab),this.showBar()}restore(groupId){const entry=this.entries.get(groupId);!entry||!this.panelManager||(entry.mode==="floating"&&entry.overlay?entry.overlay.show():entry.mode==="docked"&&(entry.group.element.style.display="",this.panelManager.restoreDocked(entry.group,entry.savedPosition?.direction??"right",entry.savedPosition?.initialSize,entry.savedPosition?.siblingGroupId),entry.group.location="grid"),entry.tab.remove(),this.entries.delete(groupId),this.hideBarIfEmpty())}closeGroup(groupId){const entry=this.entries.get(groupId);if(!entry||!this.panelManager)return;entry.mode==="floating"&&entry.overlay?entry.overlay.show():entry.mode==="docked"&&(entry.group.element.style.display=""),entry.tab.remove(),this.entries.delete(groupId);const panels=entry.group.panels.slice();for(const panel of panels)this.panelManager.removePanel(panel.id);this.hideBarIfEmpty()}isMinimized(groupId){return this.entries.has(groupId)}getMinimizedGroupIds(){return Array.from(this.entries.keys())}getEntry(groupId){return this.entries.get(groupId)}removeGroup(groupId){const entry=this.entries.get(groupId);entry&&(entry.tab.remove(),this.entries.delete(groupId),this.hideBarIfEmpty())}dispose(){for(const entry of this.entries.values())entry.overlay&&entry.overlay.show(),entry.mode==="docked"&&(entry.group.element.style.display="");this.entries.clear(),this.tabsContainer&&(this.tabsContainer.innerHTML="",this.tabsContainer.style.display="none")}showBar(){if(this.tabsContainer){const wasHidden=this.tabsContainer.style.display==="none";this.tabsContainer.style.display="flex",wasHidden&&this._onBarVisibilityChanged?.()}}hideBarIfEmpty(){if(this.entries.size===0&&this.tabsContainer){const wasVisible=this.tabsContainer.style.display!=="none";this.tabsContainer.style.display="none",wasVisible&&this._onBarVisibilityChanged?.()}}mergeMinimized(sourceGroupId,targetGroupId){const source=this.entries.get(sourceGroupId),target=this.entries.get(targetGroupId);if(!source||!target||!this.panelManager)return;source.mode==="floating"&&source.overlay?source.overlay.show():source.mode==="docked"&&(source.group.element.style.display=""),target.mode==="floating"&&target.overlay?target.overlay.show():target.mode==="docked"&&(target.group.element.style.display="",this.panelManager.restoreDocked(target.group,target.savedPosition?.direction??"right",target.savedPosition?.initialSize),target.group.location="grid");const panelIds=source.group.panels.map(p=>p.id);for(const pid of panelIds)this.panelManager.movePanel(pid,target.group.id);if(source.tab.remove(),this.entries.delete(sourceGroupId),target.tab.remove(),this.entries.delete(targetGroupId),target.mode==="floating"&&target.overlay){const newTab=this.createTab(targetGroupId,target.group.panels.length);this.entries.set(targetGroupId,{...target,tab:newTab}),this.tabsContainer.appendChild(newTab),target.overlay.hide()}this.hideBarIfEmpty()}dockMinimized(groupId){const entry=this.entries.get(groupId);!entry||!this.panelManager||(entry.mode==="floating"&&entry.overlay?(entry.overlay.show(),this.panelManager.dockGroup(groupId,"right")):entry.mode==="docked"&&(entry.group.element.style.display="",this.panelManager.restoreDocked(entry.group,entry.savedPosition?.direction??"right",entry.savedPosition?.initialSize,entry.savedPosition?.siblingGroupId),entry.group.location="grid"),entry.tab.remove(),this.entries.delete(groupId),this.hideBarIfEmpty())}createTab(groupId,panelCount,mode="floating"){const tab=document.createElement("div");tab.className="mb-adapt-minimized-tab",tab.dataset.groupId=groupId;const labelEl=document.createElement("span");if(labelEl.className="mb-adapt-minimized-tab-label",labelEl.innerHTML=Array(panelCount).fill(SVG_WINDOW).join(""),tab.appendChild(labelEl),mode==="floating"&&this._allowDocking){const dockBtn=document.createElement("button");dockBtn.className="mb-adapt-minimized-tab-action",dockBtn.title="Dock to main",dockBtn.innerHTML=SVG_DOCK,dockBtn.addEventListener("click",e=>{e.stopPropagation(),this.dockMinimized(groupId)}),tab.appendChild(dockBtn)}const restoreBtn=document.createElement("button");restoreBtn.className="mb-adapt-minimized-tab-action",restoreBtn.title="Restore",restoreBtn.innerHTML=SVG_MAXIMIZE,restoreBtn.addEventListener("click",e=>{e.stopPropagation(),this.restore(groupId)}),tab.appendChild(restoreBtn);const closeBtn=document.createElement("button");return closeBtn.className="mb-adapt-minimized-tab-action",closeBtn.title="Close",closeBtn.innerHTML=SVG_CLOSE,closeBtn.addEventListener("click",e=>{e.stopPropagation(),this.closeGroup(groupId)}),tab.appendChild(closeBtn),tab.addEventListener("pointerdown",e=>{e.target.closest("button")||e.button===0&&(e.preventDefault(),this.onTabDragStart(groupId,tab,e))}),tab}onTabDragStart(groupId,tab,e){if(this.dragState)return;this.dragState={groupId,tab,startX:e.clientX,startY:e.clientY,isDragging:!1,ghost:null,highlightedTab:null};const onMove=ev=>{if(!this.dragState)return;if(!this.dragState.isDragging){const dx=ev.clientX-this.dragState.startX,dy=ev.clientY-this.dragState.startY;if(dx*dx+dy*dy<25)return;this.dragState.isDragging=!0,this.dragState.ghost=this.createDragGhost(groupId),document.body.appendChild(this.dragState.ghost),this.dragState.tab.style.opacity="0.4"}this.dragState.ghost.style.left=`${ev.clientX+8}px`,this.dragState.ghost.style.top=`${ev.clientY+8}px`;const target=this.findDropTarget(ev.clientX,ev.clientY,groupId);this.dragState.highlightedTab!==target&&(this.dragState.highlightedTab?.classList.remove("mb-adapt-minimized-tab--drop-target"),target?.classList.add("mb-adapt-minimized-tab--drop-target"),this.dragState.highlightedTab=target)},onUp=ev=>{if(document.removeEventListener("pointermove",onMove),document.removeEventListener("pointerup",onUp),document.removeEventListener("pointercancel",onCancel),!!this.dragState){if(this.dragState.ghost?.remove(),this.dragState.tab.style.opacity="",this.dragState.highlightedTab?.classList.remove("mb-adapt-minimized-tab--drop-target"),this.dragState.isDragging){const target=this.findDropTarget(ev.clientX,ev.clientY,groupId);if(target){const targetGroupId=target.dataset.groupId;targetGroupId&&targetGroupId!==groupId&&this.mergeMinimized(groupId,targetGroupId)}}this.dragState=null}},onCancel=()=>{document.removeEventListener("pointermove",onMove),document.removeEventListener("pointerup",onUp),document.removeEventListener("pointercancel",onCancel),this.dragState&&(this.dragState.ghost?.remove(),this.dragState.tab.style.opacity="",this.dragState.highlightedTab?.classList.remove("mb-adapt-minimized-tab--drop-target")),this.dragState=null};document.addEventListener("pointermove",onMove),document.addEventListener("pointerup",onUp),document.addEventListener("pointercancel",onCancel)}createDragGhost(groupId){const entry=this.entries.get(groupId),ghost=document.createElement("div");return ghost.className="mb-drag-ghost",ghost.textContent=entry?.group.panels.map(p=>p.title||"Panel").join(" \xB7 ")??groupId,ghost}findDropTarget(x,y,excludeGroupId){if(!this.tabsContainer)return null;const tabs=this.tabsContainer.querySelectorAll(".mb-adapt-minimized-tab");for(const tab of tabs){const el=tab;if(el.dataset.groupId===excludeGroupId)continue;const rect=el.getBoundingClientRect();if(x>=rect.left&&x<=rect.right&&y>=rect.top&&y<=rect.bottom)return el}return null}}class FloatingToolbar{constructor(overlay,panelManager,minimizeManager,constraints){this.isMaximized=!1;this._beforeClose=null;this.overlay=overlay,this.panelManager=panelManager,this.minimizeManager=minimizeManager,this.element=document.createElement("div"),this.element.className="mb-adapt-fork-actions",this.minimizeBtn=this.createButton("Minimize",SVG_MINIMIZE),this.maxRestoreBtn=this.createButton("Maximize",SVG_MAXIMIZE),this.dockBtn=this.createButton("Dock to main",SVG_DOCK);const closeBtn=this.createButton("Close all",SVG_CLOSE);constraints?.allowDocking===!1&&(this.dockBtn.style.display="none"),constraints?.allowMinimize===!1&&(this.minimizeBtn.style.display="none"),constraints?.allowMaximize===!1&&(this.maxRestoreBtn.style.display="none"),this.element.appendChild(this.minimizeBtn),this.element.appendChild(this.maxRestoreBtn),this.element.appendChild(this.dockBtn),this.element.appendChild(closeBtn),this.minimizeBtn.addEventListener("click",()=>this.onMinimize()),this.maxRestoreBtn.addEventListener("click",()=>this.onMaxRestore()),this.dockBtn.addEventListener("click",()=>this.onDock()),closeBtn.addEventListener("click",()=>this.onClose())}setAllowDocking(allow){this.dockBtn.style.display=allow?"":"none"}setAllowMinimize(allow){this.minimizeBtn.style.display=allow?"":"none"}setAllowMaximize(allow){this.maxRestoreBtn.style.display=allow?"":"none"}setBeforeClose(cb){this._beforeClose=cb}syncMaximizedState(){this.overlay.isMaximized&&!this.isMaximized?(this.isMaximized=!0,this.maxRestoreBtn.innerHTML=SVG_RESTORE,this.maxRestoreBtn.title="Restore"):!this.overlay.isMaximized&&this.isMaximized&&(this.isMaximized=!1,this.maxRestoreBtn.innerHTML=SVG_MAXIMIZE,this.maxRestoreBtn.title="Maximize")}createButton(title,svgHtml){const btn=document.createElement("button");return btn.title=title,btn.innerHTML=svgHtml,btn}clearMaximize(){this.isMaximized&&(this.overlay.restore(),this.isMaximized=!1,this.maxRestoreBtn.innerHTML=SVG_MAXIMIZE,this.maxRestoreBtn.title="Maximize")}onMinimize(){this.clearMaximize();const group=this.overlay.group;this.minimizeManager.minimize(group.id,this.overlay,group)}onMaxRestore(){this.isMaximized?(this.overlay.restore(),this.isMaximized=!1,this.maxRestoreBtn.innerHTML=SVG_MAXIMIZE,this.maxRestoreBtn.title="Maximize"):(this.overlay.maximize(),this.isMaximized=!0,this.maxRestoreBtn.innerHTML=SVG_RESTORE,this.maxRestoreBtn.title="Restore")}onDock(){this.clearMaximize(),this.panelManager.dockOverlay(this.overlay.group.id)}onClose(){const doClose=()=>{this.clearMaximize();for(const group of this.overlay.getAllGroups())for(const panel of group.panels.slice())this.panelManager.removePanel(panel.id)};if(this._beforeClose){const result=this._beforeClose();result&&typeof result.then=="function"?result.then(ok=>{ok&&doClose()}):result!==!1&&doClose()}else doClose()}}class ForkGroupActions{constructor(group,minimizeManager,panelManager,onLocationChange,constraints){this._isMaximized=!1;this._autoMaximizedByResize=!1;this._beforeClose=null;this._onMaximizeChanged=null;this.group=group,this.minimizeManager=minimizeManager,this.panelManager=panelManager,this._allowFloating=constraints?.allowFloating??!0,this._allowMinimize=constraints?.allowMinimize??!0,this._allowMaximize=constraints?.allowMaximize??!0,this.element=document.createElement("div"),this.element.className="mb-adapt-fork-actions",this.floatDockBtn=this.createButton("Pop out",SVG_POPOUT),this.maximizeBtn=this.createButton("Maximize",SVG_MAXIMIZE),this.minimizeBtn=this.createButton("Minimize",SVG_MINIMIZE),this.closeBtn=this.createButton("Close all",SVG_CLOSE),this._allowFloating||(this.floatDockBtn.style.display="none",group.element.setAttribute("data-allow-floating","false")),this.element.appendChild(this.floatDockBtn),this.element.appendChild(this.maximizeBtn),this.element.appendChild(this.minimizeBtn),this.element.appendChild(this.closeBtn),this.floatDockBtn.addEventListener("click",()=>this.onFloatDock()),this.maximizeBtn.addEventListener("click",()=>this.onMaximize()),this.minimizeBtn.addEventListener("click",()=>this.onMinimize()),this.closeBtn.addEventListener("click",()=>this.onCloseGroup()),this.updateVisibility(),group.onLocationChanged(()=>{this.updateVisibility(),onLocationChange?.()}),group.onSplitStateChanged(()=>this.updateVisibility())}dispose(){this.restoreMaximized(),this.minimizeManager.removeGroup(this.group.id)}setAllowFloating(allow){this._allowFloating=allow,allow?this.group.element.removeAttribute("data-allow-floating"):this.group.element.setAttribute("data-allow-floating","false"),this.updateVisibility()}setAllowMinimize(allow){this._allowMinimize=allow,this.updateVisibility()}setAllowMaximize(allow){this._allowMaximize=allow,this.updateVisibility()}setAutoMaximizedByResize(auto){this._autoMaximizedByResize=auto,this.updateVisibility()}setBeforeClose(cb){this._beforeClose=cb}onMaximizeChanged(cb){this._onMaximizeChanged=cb}updateVisibility(){this.group.location==="floating"||this.group.isInFloatingSplit?(this.floatDockBtn.style.display="none",this.maximizeBtn.style.display="none",this.minimizeBtn.style.display="none",this.closeBtn.style.display="none"):(this.floatDockBtn.style.display=this._allowFloating?"":"none",this.floatDockBtn.innerHTML=SVG_POPOUT,this.floatDockBtn.title="Pop out",this.maximizeBtn.style.display=this._allowMaximize&&!this._autoMaximizedByResize?"":"none",this.minimizeBtn.style.display=this._allowMinimize?"":"none",this.closeBtn.style.display="")}createButton(title,svgHtml){const btn=document.createElement("button");return btn.title=title,btn.innerHTML=svgHtml,btn}onFloatDock(){this.restoreMaximized();const vw=window.innerWidth,vh=window.innerHeight,fw=Math.min(720,Math.max(200,vw-20)),fh=Math.min(600,Math.max(200,vh-20));this.panelManager.addFloatingGroup(this.group,{width:fw,height:fh,x:Math.max(0,(vw-fw)/2),y:Math.max(0,(vh-fh)/2)})}get isMaximized(){return this._isMaximized}clearMaximized(){this._isMaximized&&(this.panelManager.restoreMaximizedDockedGroup(this.group),this._isMaximized=!1,this.maximizeBtn.innerHTML=SVG_MAXIMIZE,this.maximizeBtn.title="Maximize",this._onMaximizeChanged?.(!1))}setMaximized(){this._isMaximized||(this.panelManager.maximizeDockedGroup(this.group),this._isMaximized=!0,this.maximizeBtn.innerHTML=SVG_RESTORE,this.maximizeBtn.title="Restore",this._onMaximizeChanged?.(!0))}onMaximize(){this._isMaximized?this.restoreMaximized():(this.panelManager.maximizeDockedGroup(this.group),this._isMaximized=!0,this.maximizeBtn.innerHTML=SVG_RESTORE,this.maximizeBtn.title="Restore",this._onMaximizeChanged?.(!0))}restoreMaximized(){this._isMaximized&&(this.panelManager.restoreMaximizedDockedGroup(this.group),this._isMaximized=!1,this.maximizeBtn.innerHTML=SVG_MAXIMIZE,this.maximizeBtn.title="Maximize",this._onMaximizeChanged?.(!1))}onMinimize(){this.restoreMaximized(),this.minimizeManager.minimizeDocked(this.group.id,this.group,this.panelManager)}onCloseGroup(){const doClose=()=>{this.restoreMaximized();for(const panel of this.group.panels.slice())this.panelManager.removePanel(panel.id)};if(this._beforeClose){const result=this._beforeClose();result&&typeof result.then=="function"?result.then(ok=>{ok&&doClose()}):result!==!1&&doClose()}else doClose()}}function injectPanelManagerStyles(){if(typeof document>"u"||getComputedStyle(document.documentElement).getPropertyValue("--mb-adapt-styles-loaded").trim()||document.getElementById("mb-adapt-panel-styles"))return;const style=document.createElement("style");style.id="mb-adapt-panel-styles",style.textContent=PANEL_MANAGER_CSS;const firstStyle=document.querySelector("style, link[rel=stylesheet]");firstStyle?firstStyle.parentNode.insertBefore(style,firstStyle):document.head.appendChild(style)}export{FloatingToolbar,ForkGroupActions,IframePanelComponent,MinimizeManager,SVG_POPOUT,SVG_WINDOW,injectPanelManagerStyles};