@yuebai008/cli
Version:
Command line interface for rapid qg-minigame development
1 lines • 14.8 kB
JavaScript
import*as Common from"../../core/common/common.js";import*as Host from"../../core/host/host.js";import*as i18n from"../../core/i18n/i18n.js";import*as ARIAUtils from"./ARIAUtils.js";import{Icon}from"./Icon.js";import{Events as TabbedPaneEvents,TabbedPane}from"./TabbedPane.js";import{Toolbar,ToolbarMenuButton}from"./Toolbar.js";import{createTextChild}from"./UIUtils.js";import{getRegisteredLocationResolvers,getRegisteredViewExtensions,getLocalizedViewLocationCategory,maybeRemoveViewExtension,registerLocationResolver,registerViewExtension,ViewLocationCategory,resetViewRegistration}from"./ViewRegistration.js";import{VBox}from"./Widget.js";import viewContainersStyles from"./viewContainers.css.legacy.js";const UIStrings={sPanel:"{PH1} panel"},str_=i18n.i18n.registerUIStrings("ui/legacy/ViewManager.ts",UIStrings),i18nString=i18n.i18n.getLocalizedString.bind(void 0,str_);export const defaultOptionsForTabs={security:!0};export class PreRegisteredView{viewRegistration;widgetRequested;constructor(e){this.viewRegistration=e,this.widgetRequested=!1}title(){return this.viewRegistration.title()}commandPrompt(){return this.viewRegistration.commandPrompt()}isCloseable(){return"closeable"===this.viewRegistration.persistence}isPreviewFeature(){return Boolean(this.viewRegistration.isPreviewFeature)}isTransient(){return"transient"===this.viewRegistration.persistence}viewId(){return this.viewRegistration.id}location(){return this.viewRegistration.location}order(){return this.viewRegistration.order}settings(){return this.viewRegistration.settings}tags(){if(this.viewRegistration.tags)return this.viewRegistration.tags.map((e=>e())).join("\0")}persistence(){return this.viewRegistration.persistence}async toolbarItems(){return this.viewRegistration.hasToolbar?this.widget().then((e=>e.toolbarItems())):[]}async widget(){return this.widgetRequested=!0,this.viewRegistration.loadView()}async disposeView(){if(!this.widgetRequested)return;const e=await this.widget();await e.ownerViewDisposed()}experiment(){return this.viewRegistration.experiment}condition(){return this.viewRegistration.condition}}let viewManagerInstance;export class ViewManager{views;locationNameByViewId;locationOverrideSetting;constructor(){this.views=new Map,this.locationNameByViewId=new Map,this.locationOverrideSetting=Common.Settings.Settings.instance().createSetting("viewsLocationOverride",{});const e=this.locationOverrideSetting.get(),t=new Map;for(const e of getRegisteredViewExtensions()){const i=e.location()||"none",s=t.get(i)||[];s.push(e),t.set(i,s)}let i=[];for(const e of t.values())e.sort(((e,t)=>{const i=e.order(),s=t.order();return void 0!==i&&void 0!==s?i-s:0})),i=i.concat(e);for(const t of i){const i=t.viewId(),s=t.location();if(this.views.has(i))throw new Error(`Duplicate view id '${i}'`);this.views.set(i,t);const n=e[i]||s;this.locationNameByViewId.set(i,n)}}static instance(e={forceNew:null}){const{forceNew:t}=e;return viewManagerInstance&&!t||(viewManagerInstance=new ViewManager),viewManagerInstance}static removeInstance(){viewManagerInstance=void 0}static createToolbar(e){if(!e.length)return null;const t=new Toolbar("");for(const i of e)t.appendToolbarItem(i);return t.element}locationNameForViewId(e){const t=this.locationNameByViewId.get(e);if(!t)throw new Error(`No location name for view with id ${e}`);return t}moveView(e,t,i){const{shouldSelectTab:s,overrideSaving:n}=i||{shouldSelectTab:!0,overrideSaving:!1};if(!e||!t)return;const a=this.view(e);if(a){if(!n){this.locationNameByViewId.set(e,t);const i=this.locationOverrideSetting.get();i[e]=t,this.locationOverrideSetting.set(i)}this.resolveLocation(t).then((t=>{if(!t)throw new Error("Move view: Could not resolve location for view: "+e);return t.reveal(),t.showView(a,void 0,!0,!1,s)}))}}revealView(e){const t=locationForView.get(e);return t?(t.reveal(),t.showView(e)):Promise.resolve()}showViewInLocation(e,t,i=!0){this.moveView(e,t,{shouldSelectTab:i,overrideSaving:!0})}view(e){const t=this.views.get(e);if(!t)throw new Error(`No view with id ${e} found!`);return t}materializedWidget(e){const t=this.view(e);return t&&widgetForView.get(t)||null}showView(e,t,i){const s=this.views.get(e);if(!s)return console.error("Could not find view for id: '"+e+"' "+(new Error).stack),Promise.resolve();const n=this.locationNameByViewId.get(e),a=locationForView.get(s);return a?(a.reveal(),a.showView(s,void 0,t,i)):this.resolveLocation(n).then((n=>{if(!n)throw new Error("Could not resolve location for view: "+e);return n.reveal(),n.showView(s,void 0,t,i)}))}async resolveLocation(e){if(!e)return Promise.resolve(null);const t=getRegisteredLocationResolvers().filter((t=>t.name===e));if(t.length>1)throw new Error("Duplicate resolver for location: "+e);if(t.length){return(await t[0].loadResolver()).resolveLocation(e)}throw new Error("Unresolved location: "+e)}createTabbedLocation(e,t,i,s,n){return new TabbedLocation(this,e,t,i,s,n)}createStackLocation(e,t){return new StackLocation(this,e,t)}hasViewsForLocation(e){return Boolean(this.viewsForLocation(e).length)}viewsForLocation(e){const t=[];for(const[i,s]of this.views.entries())this.locationNameByViewId.get(i)===e&&t.push(s);return t}}const widgetForView=new WeakMap;export class ContainerWidget extends VBox{view;materializePromise;constructor(e){super(),this.element.classList.add("flex-auto","view-container","overflow-auto"),this.view=e,this.element.tabIndex=-1,ARIAUtils.markAsTabpanel(this.element),ARIAUtils.setLabel(this.element,i18nString(UIStrings.sPanel,{PH1:e.title()})),this.setDefaultFocusedElement(this.element)}materialize(){if(this.materializePromise)return this.materializePromise;const e=[];return e.push(this.view.toolbarItems().then((e=>{const t=ViewManager.createToolbar(e);t&&this.element.insertBefore(t,this.element.firstChild)}))),e.push(this.view.widget().then((e=>{const t=this.element.hasFocus();this.setDefaultFocusedElement(null),widgetForView.set(this.view,e),e.show(this.element),t&&e.focus()}))),this.materializePromise=Promise.all(e).then((()=>{})),this.materializePromise}wasShown(){this.materialize().then((()=>{const e=widgetForView.get(this.view);e&&(e.show(this.element),this.wasShownForTest())}))}wasShownForTest(){}}class ExpandableContainerWidget extends VBox{titleElement;titleExpandIcon;view;widget;materializePromise;constructor(e){super(!0),this.element.classList.add("flex-none"),this.registerRequiredCSS(viewContainersStyles),this.titleElement=document.createElement("div"),this.titleElement.classList.add("expandable-view-title"),ARIAUtils.markAsTreeitem(this.titleElement),this.titleExpandIcon=Icon.create("triangle-right","title-expand-icon"),this.titleElement.appendChild(this.titleExpandIcon);const t=e.title();createTextChild(this.titleElement,t),ARIAUtils.setLabel(this.titleElement,t),ARIAUtils.setExpanded(this.titleElement,!1),this.titleElement.tabIndex=0,self.onInvokeElement(this.titleElement,this.toggleExpanded.bind(this)),this.titleElement.addEventListener("keydown",this.onTitleKeyDown.bind(this),!1),this.contentElement.insertBefore(this.titleElement,this.contentElement.firstChild),ARIAUtils.setControls(this.titleElement,this.contentElement.createChild("slot")),this.view=e,expandableContainerForView.set(e,this)}wasShown(){this.widget&&this.materializePromise&&this.materializePromise.then((()=>{this.titleElement.classList.contains("expanded")&&this.widget&&this.widget.show(this.element)}))}materialize(){if(this.materializePromise)return this.materializePromise;const e=[];return e.push(this.view.toolbarItems().then((e=>{const t=ViewManager.createToolbar(e);t&&this.titleElement.appendChild(t)}))),e.push(this.view.widget().then((e=>{this.widget=e,widgetForView.set(this.view,e),e.show(this.element)}))),this.materializePromise=Promise.all(e).then((()=>{})),this.materializePromise}expand(){return this.titleElement.classList.contains("expanded")?this.materialize():(this.titleElement.classList.add("expanded"),ARIAUtils.setExpanded(this.titleElement,!0),this.titleExpandIcon.setIconType("triangle-down"),this.materialize().then((()=>{this.widget&&this.widget.show(this.element)})))}collapse(){this.titleElement.classList.contains("expanded")&&(this.titleElement.classList.remove("expanded"),ARIAUtils.setExpanded(this.titleElement,!1),this.titleExpandIcon.setIconType("triangle-right"),this.materialize().then((()=>{this.widget&&this.widget.detach()})))}toggleExpanded(e){"keydown"===e.type&&e.target!==this.titleElement||(this.titleElement.classList.contains("expanded")?this.collapse():this.expand())}onTitleKeyDown(e){if(e.target!==this.titleElement)return;const t=e;"ArrowLeft"===t.key?this.collapse():"ArrowRight"===t.key&&(this.titleElement.classList.contains("expanded")?this.widget&&this.widget.focus():this.expand())}}const expandableContainerForView=new WeakMap;class Location{manager;revealCallback;widgetInternal;constructor(e,t,i){this.manager=e,this.revealCallback=i,this.widgetInternal=t}widget(){return this.widgetInternal}reveal(){this.revealCallback&&this.revealCallback()}showView(e,t,i,s,n){throw new Error("not implemented")}removeView(e){throw new Error("not implemented")}}const locationForView=new WeakMap;class TabbedLocation extends Location{tabbedPaneInternal;allowReorder;closeableTabSetting;tabOrderSetting;lastSelectedTabSetting;defaultTab;views;constructor(e,t,i,s,n,a){const o=new TabbedPane;n&&o.setAllowTabReorder(!0),super(e,o,t),this.tabbedPaneInternal=o,this.allowReorder=n,this.tabbedPaneInternal.addEventListener(TabbedPaneEvents.TabSelected,this.tabSelected,this),this.tabbedPaneInternal.addEventListener(TabbedPaneEvents.TabClosed,this.tabClosed,this),this.closeableTabSetting=Common.Settings.Settings.instance().createSetting("closeableTabs",{}),this.setOrUpdateCloseableTabsSetting(),this.tabOrderSetting=Common.Settings.Settings.instance().createSetting(i+"-tabOrder",{}),this.tabbedPaneInternal.addEventListener(TabbedPaneEvents.TabOrderChanged,this.persistTabOrder,this),s&&(this.lastSelectedTabSetting=Common.Settings.Settings.instance().createSetting(i+"-selectedTab","")),this.defaultTab=a,this.views=new Map,i&&this.appendApplicableItems(i)}setOrUpdateCloseableTabsSetting(){const e={...defaultOptionsForTabs,...this.closeableTabSetting.get()};this.closeableTabSetting.set(e)}widget(){return this.tabbedPaneInternal}tabbedPane(){return this.tabbedPaneInternal}enableMoreTabsButton(){const e=new ToolbarMenuButton(this.appendTabsToMenu.bind(this));return this.tabbedPaneInternal.leftToolbar().appendToolbarItem(e),this.tabbedPaneInternal.disableOverflowMenu(),e}appendApplicableItems(e){const t=this.manager.viewsForLocation(e);if(this.allowReorder){let e=0;const i=this.tabOrderSetting.get(),s=new Map;for(const n of t)s.set(n.viewId(),i[n.viewId()]||++e*TabbedLocation.orderStep);t.sort(((e,t)=>s.get(e.viewId())-s.get(t.viewId())))}for(const e of t){const t=e.viewId();this.views.set(t,e),locationForView.set(e,this),e.isTransient()||(e.isCloseable()?this.closeableTabSetting.get()[t]&&this.appendTab(e):this.appendTab(e))}if(this.defaultTab)if(this.tabbedPaneInternal.hasTab(this.defaultTab))this.tabbedPaneInternal.selectTab(this.defaultTab);else{const e=Array.from(this.views.values()).find((e=>e.viewId()===this.defaultTab));e&&this.showView(e)}else this.lastSelectedTabSetting&&this.tabbedPaneInternal.hasTab(this.lastSelectedTabSetting.get())&&this.tabbedPaneInternal.selectTab(this.lastSelectedTabSetting.get())}appendTabsToMenu(e){const t=Array.from(this.views.values());t.sort(((e,t)=>e.title().localeCompare(t.title())));for(const i of t){const t=i.title();"issues-pane"!==i.viewId()?e.defaultSection().appendItem(t,this.showView.bind(this,i,void 0,!0)):e.defaultSection().appendItem(t,(()=>{Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.HamburgerMenu),this.showView(i,void 0,!0)}))}}appendTab(e,t){this.tabbedPaneInternal.appendTab(e.viewId(),e.title(),new ContainerWidget(e),void 0,!1,e.isCloseable()||e.isTransient(),e.isPreviewFeature(),t)}appendView(e,t){if(this.tabbedPaneInternal.hasTab(e.viewId()))return;const i=locationForView.get(e);let s;i&&i!==this&&i.removeView(e),locationForView.set(e,this),this.manager.views.set(e.viewId(),e),this.views.set(e.viewId(),e);const n=this.tabbedPaneInternal.tabIds();if(this.allowReorder){const t=this.tabOrderSetting.get(),i=t[e.viewId()];for(let e=0;i&&e<n.length;++e)if(t[n[e]]&&t[n[e]]>i){s=e;break}}else if(t)for(let e=0;e<n.length;++e)if(n[e]===t.viewId()){s=e;break}if(this.appendTab(e,s),e.isCloseable()){const t=this.closeableTabSetting.get(),i=e.viewId();t[i]||(t[i]=!0,this.closeableTabSetting.set(t))}this.persistTabOrder()}async showView(e,t,i,s,n=!0){this.appendView(e,t),n&&this.tabbedPaneInternal.selectTab(e.viewId(),i),s||this.tabbedPaneInternal.focus();const a=this.tabbedPaneInternal.tabView(e.viewId());await a.materialize()}removeView(e){this.tabbedPaneInternal.hasTab(e.viewId())&&(locationForView.delete(e),this.manager.views.delete(e.viewId()),this.tabbedPaneInternal.closeTab(e.viewId()),this.views.delete(e.viewId()))}tabSelected(e){const{tabId:t}=e.data;this.lastSelectedTabSetting&&e.data.isUserGesture&&this.lastSelectedTabSetting.set(t)}tabClosed(e){const{tabId:t}=e.data,i=this.closeableTabSetting.get();i[t]&&(i[t]=!1,this.closeableTabSetting.set(i));const s=this.views.get(t);s&&s.disposeView()}persistTabOrder(){const e=this.tabbedPaneInternal.tabIds(),t={};for(let i=0;i<e.length;i++)t[e[i]]=(i+1)*TabbedLocation.orderStep;const i=this.tabOrderSetting.get(),s=Object.keys(i);s.sort(((e,t)=>i[e]-i[t]));let n=0;for(const e of s)e in t?n=t[e]:t[e]=++n;this.tabOrderSetting.set(t)}getCloseableTabSetting(){return this.closeableTabSetting.get()}static orderStep=10}class StackLocation extends Location{vbox;expandableContainers;constructor(e,t,i){const s=new VBox;super(e,s,t),this.vbox=s,ARIAUtils.markAsTree(s.element),this.expandableContainers=new Map,i&&this.appendApplicableItems(i)}appendView(e,t){const i=locationForView.get(e);i&&i!==this&&i.removeView(e);let s=this.expandableContainers.get(e.viewId());if(!s){locationForView.set(e,this),this.manager.views.set(e.viewId(),e),s=new ExpandableContainerWidget(e);let i=null;if(t){const e=expandableContainerForView.get(t);i=e?e.element:null}s.show(this.vbox.contentElement,i),this.expandableContainers.set(e.viewId(),s)}}async showView(e,t){this.appendView(e,t);const i=this.expandableContainers.get(e.viewId());i&&await i.expand()}removeView(e){const t=this.expandableContainers.get(e.viewId());t&&(t.detach(),this.expandableContainers.delete(e.viewId()),locationForView.delete(e),this.manager.views.delete(e.viewId()))}appendApplicableItems(e){for(const t of this.manager.viewsForLocation(e))this.appendView(t)}}export{getRegisteredViewExtensions,maybeRemoveViewExtension,registerViewExtension,getRegisteredLocationResolvers,registerLocationResolver,ViewLocationCategory,getLocalizedViewLocationCategory,resetViewRegistration};