@yuebai008/cli
Version:
Command line interface for rapid qg-minigame development
1 lines • 11.7 kB
JavaScript
import*as Platform from"../../core/platform/platform.js";import*as ARIAUtils from"./ARIAUtils.js";import{Events as ListModelEvents}from"./ListModel.js";import{measurePreferredSize}from"./UIUtils.js";export var ListMode;!function(e){e.NonViewport="UI.ListMode.NonViewport",e.EqualHeightItems="UI.ListMode.EqualHeightItems",e.VariousHeightItems="UI.ListMode.VariousHeightItems"}(ListMode||(ListMode={}));export class ListControl{element;topElement;bottomElement;firstIndex;lastIndex;renderedHeight;topHeight;bottomHeight;model;itemToElement;selectedIndexInternal;selectedItemInternal;delegate;mode;fixedHeight;variableOffsets;constructor(e,t,i){this.element=document.createElement("div"),this.element.style.overflowY="auto",this.topElement=this.element.createChild("div"),this.bottomElement=this.element.createChild("div"),this.firstIndex=0,this.lastIndex=0,this.renderedHeight=0,this.topHeight=0,this.bottomHeight=0,this.model=e,this.model.addEventListener(ListModelEvents.ItemsReplaced,this.replacedItemsInRange,this),this.itemToElement=new Map,this.selectedIndexInternal=-1,this.selectedItemInternal=null,this.element.tabIndex=-1,this.element.addEventListener("click",this.onClick.bind(this),!1),this.element.addEventListener("keydown",this.onKeyDown.bind(this),!1),ARIAUtils.markAsListBox(this.element),this.delegate=t,this.mode=i||ListMode.EqualHeightItems,this.fixedHeight=0,this.variableOffsets=new Int32Array(0),this.clearContents(),this.mode!==ListMode.NonViewport&&this.element.addEventListener("scroll",(()=>{this.updateViewport(this.element.scrollTop,this.element.offsetHeight)}),!1)}setModel(e){this.itemToElement.clear();const t=this.model.length;this.model.removeEventListener(ListModelEvents.ItemsReplaced,this.replacedItemsInRange,this),this.model=e,this.model.addEventListener(ListModelEvents.ItemsReplaced,this.replacedItemsInRange,this),this.invalidateRange(0,t)}replacedItemsInRange(e){const t=e.data,i=t.index,s=i+t.removed.length,n=t.keepSelectedIndex,l=this.selectedItemInternal,o=l&&this.itemToElement.get(l)||null;for(let e=0;e<t.removed.length;e++)this.itemToElement.delete(t.removed[e]);if(this.invalidate(i,s,t.inserted),this.selectedIndexInternal>=s)this.selectedIndexInternal+=t.inserted-(s-i),this.selectedItemInternal=this.model.at(this.selectedIndexInternal);else if(this.selectedIndexInternal>=i){const e=n?i:i+t.inserted;let s=this.findFirstSelectable(e,1,!1);if(-1===s){const e=n?i:i-1;s=this.findFirstSelectable(e,-1,!1)}this.select(s,l,o)}}refreshItem(e){const t=this.model.indexOf(e);-1!==t?this.refreshItemByIndex(t):console.error("Item to refresh is not present")}refreshItemByIndex(e){const t=this.model.at(e);this.itemToElement.delete(t),this.invalidateRange(e,e+1),-1!==this.selectedIndexInternal&&this.select(this.selectedIndexInternal,null,null)}refreshAllItems(){this.itemToElement.clear(),this.invalidateRange(0,this.model.length),-1!==this.selectedIndexInternal&&this.select(this.selectedIndexInternal,null,null)}invalidateRange(e,t){this.invalidate(e,t,t-e)}viewportResized(){if(this.mode===ListMode.NonViewport)return;const e=this.element.scrollTop,t=this.element.offsetHeight;this.clearViewport(),this.updateViewport(Platform.NumberUtilities.clamp(e,0,this.totalHeight()-t),t)}invalidateItemHeight(){this.mode===ListMode.EqualHeightItems?(this.fixedHeight=0,this.model.length&&(this.itemToElement.clear(),this.invalidate(0,this.model.length,this.model.length))):console.error("Only supported in equal height items mode")}itemForNode(e){for(;e&&e.parentNodeOrShadowHost()!==this.element;)e=e.parentNodeOrShadowHost();if(!e)return null;const t=e,i=this.model.findIndex((e=>this.itemToElement.get(e)===t));return-1!==i?this.model.at(i):null}scrollItemIntoView(e,t){const i=this.model.indexOf(e);-1!==i?this.scrollIntoView(i,t):console.error("Attempt to scroll onto missing item")}selectedItem(){return this.selectedItemInternal}selectedIndex(){return this.selectedIndexInternal}selectItem(e,t,i){let s=-1;if(null!==e){if(s=this.model.indexOf(e),-1===s)return void console.error("Attempt to select missing item");if(!this.delegate.isItemSelectable(e))return void console.error("Attempt to select non-selectable item")}-1===s||i||this.scrollIntoView(s,t),this.selectedIndexInternal!==s&&this.select(s)}selectPreviousItem(e,t){if(-1===this.selectedIndexInternal&&!e)return!1;let i=-1===this.selectedIndexInternal?this.model.length-1:this.selectedIndexInternal-1;return i=this.findFirstSelectable(i,-1,Boolean(e)),-1!==i&&(this.scrollIntoView(i,t),this.select(i),!0)}selectNextItem(e,t){if(-1===this.selectedIndexInternal&&!e)return!1;let i=-1===this.selectedIndexInternal?0:this.selectedIndexInternal+1;return i=this.findFirstSelectable(i,1,Boolean(e)),-1!==i&&(this.scrollIntoView(i,t),this.select(i),!0)}selectItemPreviousPage(e){if(this.mode===ListMode.NonViewport)return!1;let t=-1===this.selectedIndexInternal?this.model.length-1:this.selectedIndexInternal;return t=this.findPageSelectable(t,-1),-1!==t&&(this.scrollIntoView(t,e),this.select(t),!0)}selectItemNextPage(e){if(this.mode===ListMode.NonViewport)return!1;let t=-1===this.selectedIndexInternal?0:this.selectedIndexInternal;return t=this.findPageSelectable(t,1),-1!==t&&(this.scrollIntoView(t,e),this.select(t),!0)}scrollIntoView(e,t){if(this.mode===ListMode.NonViewport)return void this.elementAtIndex(e).scrollIntoViewIfNeeded(Boolean(t));const i=this.offsetAtIndex(e),s=this.offsetAtIndex(e+1),n=this.element.offsetHeight;if(t){const e=(i+s)/2-n/2;return void this.updateViewport(Platform.NumberUtilities.clamp(e,0,this.totalHeight()-n),n)}const l=this.element.scrollTop;i<l?this.updateViewport(i,n):s>l+n&&this.updateViewport(s-n,n)}onClick(e){const t=this.itemForNode(e.target);t&&this.delegate.isItemSelectable(t)&&this.selectItem(t)}onKeyDown(e){const t=e;let i=!1;switch(t.key){case"ArrowUp":i=this.selectPreviousItem(!0,!1);break;case"ArrowDown":i=this.selectNextItem(!0,!1);break;case"PageUp":i=this.selectItemPreviousPage(!1);break;case"PageDown":i=this.selectItemNextPage(!1)}i&&t.consume(!0)}totalHeight(){return this.offsetAtIndex(this.model.length)}indexAtOffset(e){if(this.mode===ListMode.NonViewport)throw"There should be no offset conversions in non-viewport mode";return!this.model.length||e<0?0:this.mode===ListMode.VariousHeightItems?Math.min(this.model.length-1,Platform.ArrayUtilities.lowerBound(this.variableOffsets,e,Platform.ArrayUtilities.DEFAULT_COMPARATOR,0,this.model.length)):(this.fixedHeight||this.measureHeight(),Math.min(this.model.length-1,Math.floor(e/this.fixedHeight)))}elementAtIndex(e){const t=this.model.at(e);let i=this.itemToElement.get(t);return i||(i=this.delegate.createElementForItem(t),this.itemToElement.set(t,i),this.updateElementARIA(i,e)),i}refreshARIA(){for(let e=this.firstIndex;e<=this.lastIndex;e++){const t=this.model.at(e),i=this.itemToElement.get(t);i&&this.updateElementARIA(i,e)}}updateElementARIA(e,t){ARIAUtils.hasRole(e)||ARIAUtils.markAsOption(e),ARIAUtils.setSetSize(e,this.model.length),ARIAUtils.setPositionInSet(e,t+1)}offsetAtIndex(e){if(this.mode===ListMode.NonViewport)throw new Error("There should be no offset conversions in non-viewport mode");return this.model.length?this.mode===ListMode.VariousHeightItems?this.variableOffsets[e]:(this.fixedHeight||this.measureHeight(),e*this.fixedHeight):0}measureHeight(){this.fixedHeight=this.delegate.heightForItem(this.model.at(0)),this.fixedHeight||(this.fixedHeight=measurePreferredSize(this.elementAtIndex(0),this.element).height)}select(e,t,i){void 0===t&&(t=this.selectedItemInternal),void 0===i&&(i=this.itemToElement.get(t)||null),this.selectedIndexInternal=e,this.selectedItemInternal=-1===e?null:this.model.at(e);const s=this.selectedItemInternal,n=-1!==this.selectedIndexInternal?this.elementAtIndex(e):null;if(this.delegate.selectedItemChanged(t,s,i,n),!this.delegate.updateSelectedItemARIA(i,n)){if(i&&ARIAUtils.setSelected(i,!1),n){ARIAUtils.setSelected(n,!0);const e=n.textContent;e&&ARIAUtils.alert(e)}ARIAUtils.setActiveDescendant(this.element,n)}}findFirstSelectable(e,t,i){const s=this.model.length;if(!s)return-1;for(let n=0;n<=s;n++){if(e<0||e>=s){if(!i)return-1;e=(e+s)%s}if(this.delegate.isItemSelectable(this.model.at(e)))return e;e+=t}return-1}findPageSelectable(e,t){let i=-1;const s=this.offsetAtIndex(e),n=this.element.offsetHeight-1;for(;e>=0&&e<this.model.length;){if(this.delegate.isItemSelectable(this.model.at(e))){if(Math.abs(this.offsetAtIndex(e)-s)>=n)return e;i=e}e+=t}return i}reallocateVariableOffsets(e,t){if(this.variableOffsets.length<e){const i=new Int32Array(Math.max(e,2*this.variableOffsets.length));i.set(this.variableOffsets.slice(0,t),0),this.variableOffsets=i}else if(this.variableOffsets.length>=2*e){const i=new Int32Array(e);i.set(this.variableOffsets.slice(0,t),0),this.variableOffsets=i}}invalidate(e,t,i){if(this.mode===ListMode.NonViewport)return void this.invalidateNonViewportMode(e,t-e,i);if(this.mode===ListMode.VariousHeightItems){this.reallocateVariableOffsets(this.model.length+1,e+1);for(let t=e+1;t<=this.model.length;t++)this.variableOffsets[t]=this.variableOffsets[t-1]+this.delegate.heightForItem(this.model.at(t-1))}const s=this.element.offsetHeight,n=this.totalHeight(),l=this.element.scrollTop;if(this.renderedHeight<s||n<s)return this.clearViewport(),void this.updateViewport(Platform.NumberUtilities.clamp(l,0,n-s),s);const o=n-this.renderedHeight;if(t<=this.firstIndex){const s=this.topHeight+o;this.topElement.style.height=s+"px",this.element.scrollTop=l+o,this.topHeight=s,this.renderedHeight=n;const h=i-(t-e);return this.firstIndex+=h,void(this.lastIndex+=h)}if(e>=this.lastIndex){const e=this.bottomHeight+o;return this.bottomElement.style.height=e+"px",this.bottomHeight=e,void(this.renderedHeight=n)}this.clearViewport(),this.updateViewport(Platform.NumberUtilities.clamp(l,0,n-s),s),this.refreshARIA()}invalidateNonViewportMode(e,t,i){let s=this.topElement;for(let t=0;t<e;t++)s=s.nextElementSibling;for(;t--;)s.nextElementSibling.remove();for(;i--;)this.element.insertBefore(this.elementAtIndex(e+i),s.nextElementSibling)}clearViewport(){this.mode!==ListMode.NonViewport?(this.firstIndex=0,this.lastIndex=0,this.renderedHeight=0,this.topHeight=0,this.bottomHeight=0,this.clearContents()):console.error("There should be no viewport updates in non-viewport mode")}clearContents(){this.topElement.style.height="0",this.bottomElement.style.height="0",this.element.removeChildren(),this.element.appendChild(this.topElement),this.element.appendChild(this.bottomElement)}updateViewport(e,t){if(this.mode===ListMode.NonViewport)return void console.error("There should be no viewport updates in non-viewport mode");const i=this.totalHeight();if(!i)return this.firstIndex=0,this.lastIndex=0,this.topHeight=0,this.bottomHeight=0,this.renderedHeight=0,this.topElement.style.height="0",void(this.bottomElement.style.height="0");const s=this.indexAtOffset(e-t),n=this.indexAtOffset(e+2*t)+1;for(;this.firstIndex<Math.min(s,this.lastIndex);)this.elementAtIndex(this.firstIndex).remove(),this.firstIndex++;for(;this.lastIndex>Math.max(n,this.firstIndex);)this.elementAtIndex(this.lastIndex-1).remove(),this.lastIndex--;this.firstIndex=Math.min(this.firstIndex,n),this.lastIndex=Math.max(this.lastIndex,s);for(let e=this.firstIndex-1;e>=s;e--){const t=this.elementAtIndex(e);this.element.insertBefore(t,this.topElement.nextSibling)}for(let e=this.lastIndex;e<n;e++){const t=this.elementAtIndex(e);this.element.insertBefore(t,this.bottomElement)}this.firstIndex=s,this.lastIndex=n,this.topHeight=this.offsetAtIndex(s),this.topElement.style.height=this.topHeight+"px",this.bottomHeight=i-this.offsetAtIndex(n),this.bottomElement.style.height=this.bottomHeight+"px",this.renderedHeight=i,this.element.scrollTop=e}}