TabbedPane.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. export default class TabbedPane extends UI.VBox{constructor(){super(true);this.registerRequiredCSS('ui/tabbedPane.css');this.element.classList.add('tabbed-pane');this.contentElement.classList.add('tabbed-pane-shadow');this.contentElement.tabIndex=-1;this.setDefaultFocusedElement(this.contentElement);this._headerElement=this.contentElement.createChild('div','tabbed-pane-header');this._headerContentsElement=this._headerElement.createChild('div','tabbed-pane-header-contents');this._tabSlider=createElementWithClass('div','tabbed-pane-tab-slider');this._tabsElement=this._headerContentsElement.createChild('div','tabbed-pane-header-tabs');this._tabsElement.setAttribute('role','tablist');this._tabsElement.addEventListener('keydown',this._keyDown.bind(this),false);this._contentElement=this.contentElement.createChild('div','tabbed-pane-content');this._contentElement.createChild('slot');this._tabs=[];this._tabsHistory=[];this._tabsById=new Map();this._currentTabLocked=false;this._autoSelectFirstItemOnShow=true;this._triggerDropDownTimeout=null;this._dropDownButton=this._createDropDownButton();UI.zoomManager.addEventListener(UI.ZoomManager.Events.ZoomChanged,this._zoomChanged,this);this.makeTabSlider();}
  2. setAccessibleName(name){UI.ARIAUtils.setAccessibleName(this._tabsElement,name);}
  3. setCurrentTabLocked(locked){this._currentTabLocked=locked;this._headerElement.classList.toggle('locked',this._currentTabLocked);}
  4. setAutoSelectFirstItemOnShow(autoSelect){this._autoSelectFirstItemOnShow=autoSelect;}
  5. get visibleView(){return this._currentTab?this._currentTab.view:null;}
  6. tabIds(){return this._tabs.map(tab=>tab._id);}
  7. tabIndex(tabId){return this._tabs.findIndex(tab=>tab.id===tabId);}
  8. tabViews(){return this._tabs.map(tab=>tab.view);}
  9. tabView(tabId){return this._tabsById.has(tabId)?this._tabsById.get(tabId).view:null;}
  10. get selectedTabId(){return this._currentTab?this._currentTab.id:null;}
  11. setShrinkableTabs(shrinkableTabs){this._shrinkableTabs=shrinkableTabs;}
  12. makeVerticalTabLayout(){this._verticalTabLayout=true;this._setTabSlider(false);this.contentElement.classList.add('vertical-tab-layout');this.invalidateConstraints();}
  13. setCloseableTabs(closeableTabs){this._closeableTabs=closeableTabs;}
  14. focus(){if(this.visibleView){this.visibleView.focus();}else{this._defaultFocusedElement.focus();}}
  15. headerElement(){return this._headerElement;}
  16. isTabCloseable(id){const tab=this._tabsById.get(id);return tab?tab.isCloseable():false;}
  17. setTabDelegate(delegate){const tabs=this._tabs.slice();for(let i=0;i<tabs.length;++i){tabs[i].setDelegate(delegate);}
  18. this._delegate=delegate;}
  19. appendTab(id,tabTitle,view,tabTooltip,userGesture,isCloseable,index){isCloseable=typeof isCloseable==='boolean'?isCloseable:this._closeableTabs;const tab=new TabbedPaneTab(this,id,tabTitle,isCloseable,view,tabTooltip);tab.setDelegate(this._delegate);console.assert(!this._tabsById.has(id),`Tabbed pane already contains a tab with id '${id}'`);this._tabsById.set(id,tab);if(index!==undefined){this._tabs.splice(index,0,tab);}else{this._tabs.push(tab);}
  20. this._tabsHistory.push(tab);if(this._tabsHistory[0]===tab&&this.isShowing()){this.selectTab(tab.id,userGesture);}
  21. this._updateTabElements();}
  22. closeTab(id,userGesture){this.closeTabs([id],userGesture);}
  23. closeTabs(ids,userGesture){const focused=this.hasFocus();for(let i=0;i<ids.length;++i){this._innerCloseTab(ids[i],userGesture);}
  24. this._updateTabElements();if(this._tabsHistory.length){this.selectTab(this._tabsHistory[0].id,false);}
  25. if(focused){this.focus();}}
  26. _innerCloseTab(id,userGesture){if(!this._tabsById.has(id)){return;}
  27. if(userGesture&&!this._tabsById.get(id)._closeable){return;}
  28. if(this._currentTab&&this._currentTab.id===id){this._hideCurrentTab();}
  29. const tab=this._tabsById.get(id);this._tabsById.delete(id);this._tabsHistory.splice(this._tabsHistory.indexOf(tab),1);this._tabs.splice(this._tabs.indexOf(tab),1);if(tab._shown){this._hideTabElement(tab);}
  30. const eventData={tabId:id,view:tab.view,isUserGesture:userGesture};this.dispatchEventToListeners(Events.TabClosed,eventData);return true;}
  31. hasTab(tabId){return this._tabsById.has(tabId);}
  32. otherTabs(id){const result=[];for(let i=0;i<this._tabs.length;++i){if(this._tabs[i].id!==id){result.push(this._tabs[i].id);}}
  33. return result;}
  34. _tabsToTheRight(id){let index=-1;for(let i=0;i<this._tabs.length;++i){if(this._tabs[i].id===id){index=i;break;}}
  35. if(index===-1){return[];}
  36. return this._tabs.slice(index+1).map(function(tab){return tab.id;});}
  37. _viewHasFocus(){if(this.visibleView&&this.visibleView.hasFocus()){return true;}
  38. return this.contentElement===this.contentElement.getComponentRoot().activeElement;}
  39. selectTab(id,userGesture,forceFocus){if(this._currentTabLocked){return false;}
  40. const focused=this._viewHasFocus();const tab=this._tabsById.get(id);if(!tab){return false;}
  41. if(this._currentTab&&this._currentTab.id===id){return true;}
  42. this.suspendInvalidations();this._hideCurrentTab();this._showTab(tab);this.resumeInvalidations();this._currentTab=tab;this._tabsHistory.splice(this._tabsHistory.indexOf(tab),1);this._tabsHistory.splice(0,0,tab);this._updateTabElements();if(focused||forceFocus){this.focus();}
  43. const eventData={tabId:id,view:tab.view,isUserGesture:userGesture};this.dispatchEventToListeners(Events.TabSelected,eventData);return true;}
  44. selectNextTab(){const index=this._tabs.indexOf(this._currentTab);const nextIndex=mod(index+1,this._tabs.length);this.selectTab(this._tabs[nextIndex].id,true);}
  45. selectPrevTab(){const index=this._tabs.indexOf(this._currentTab);const nextIndex=mod(index-1,this._tabs.length);this.selectTab(this._tabs[nextIndex].id,true);}
  46. lastOpenedTabIds(tabsCount){function tabToTabId(tab){return tab.id;}
  47. return this._tabsHistory.slice(0,tabsCount).map(tabToTabId);}
  48. setTabIcon(id,icon){const tab=this._tabsById.get(id);tab._setIcon(icon);this._updateTabElements();}
  49. setTabEnabled(id,enabled){const tab=this._tabsById.get(id);tab.tabElement.classList.toggle('disabled',!enabled);}
  50. toggleTabClass(id,className,force){const tab=this._tabsById.get(id);if(tab._toggleClass(className,force)){this._updateTabElements();}}
  51. _zoomChanged(event){for(let i=0;i<this._tabs.length;++i){delete this._tabs[i]._measuredWidth;}
  52. if(this.isShowing()){this._updateTabElements();}}
  53. changeTabTitle(id,tabTitle,tabTooltip){const tab=this._tabsById.get(id);if(tabTooltip!==undefined){tab.tooltip=tabTooltip;}
  54. if(tab.title!==tabTitle){tab.title=tabTitle;UI.ARIAUtils.setAccessibleName(tab.tabElement,tabTitle);this._updateTabElements();}}
  55. changeTabView(id,view){const tab=this._tabsById.get(id);if(tab.view===view){return;}
  56. this.suspendInvalidations();const isSelected=this._currentTab&&this._currentTab.id===id;const shouldFocus=tab.view.hasFocus();if(isSelected){this._hideTab(tab);}
  57. tab.view=view;if(isSelected){this._showTab(tab);}
  58. if(shouldFocus){tab.view.focus();}
  59. this.resumeInvalidations();}
  60. onResize(){this._updateTabElements();}
  61. headerResized(){this._updateTabElements();}
  62. wasShown(){const effectiveTab=this._currentTab||this._tabsHistory[0];if(effectiveTab&&this._autoSelectFirstItemOnShow){this.selectTab(effectiveTab.id);}}
  63. makeTabSlider(){if(this._verticalTabLayout){return;}
  64. this._setTabSlider(true);}
  65. _setTabSlider(enable){this._sliderEnabled=enable;this._tabSlider.classList.toggle('enabled',enable);}
  66. calculateConstraints(){let constraints=super.calculateConstraints();const minContentConstraints=new UI.Constraints(new UI.Size(0,0),new UI.Size(50,50));constraints=constraints.widthToMax(minContentConstraints).heightToMax(minContentConstraints);if(this._verticalTabLayout){constraints=constraints.addWidth(new UI.Constraints(new UI.Size(120,0)));}else{constraints=constraints.addHeight(new UI.Constraints(new UI.Size(0,30)));}
  67. return constraints;}
  68. _updateTabElements(){UI.invokeOnceAfterBatchUpdate(this,this._innerUpdateTabElements);}
  69. setPlaceholderElement(element,focusedElement){this._placeholderElement=element;if(focusedElement){this._focusedPlaceholderElement=focusedElement;}
  70. if(this._placeholderContainerElement){this._placeholderContainerElement.removeChildren();this._placeholderContainerElement.appendChild(element);}}
  71. _innerUpdateTabElements(){if(!this.isShowing()){return;}
  72. if(!this._tabs.length){this._contentElement.classList.add('has-no-tabs');if(this._placeholderElement&&!this._placeholderContainerElement){this._placeholderContainerElement=this._contentElement.createChild('div','tabbed-pane-placeholder fill');this._placeholderContainerElement.appendChild(this._placeholderElement);if(this._focusedPlaceholderElement){this.setDefaultFocusedElement(this._focusedPlaceholderElement);this.focus();}}}else{this._contentElement.classList.remove('has-no-tabs');if(this._placeholderContainerElement){this._placeholderContainerElement.remove();this.setDefaultFocusedElement(this.contentElement);delete this._placeholderContainerElement;}}
  73. this._measureDropDownButton();this._updateWidths();this._updateTabsDropDown();this._updateTabSlider();}
  74. _showTabElement(index,tab){if(index>=this._tabsElement.children.length){this._tabsElement.appendChild(tab.tabElement);}else{this._tabsElement.insertBefore(tab.tabElement,this._tabsElement.children[index]);}
  75. tab._shown=true;}
  76. _hideTabElement(tab){this._tabsElement.removeChild(tab.tabElement);tab._shown=false;}
  77. _createDropDownButton(){const dropDownContainer=createElementWithClass('div','tabbed-pane-header-tabs-drop-down-container');const chevronIcon=UI.Icon.create('largeicon-chevron','chevron-icon');UI.ARIAUtils.markAsMenuButton(dropDownContainer);UI.ARIAUtils.setAccessibleName(dropDownContainer,ls`More tabs`);dropDownContainer.tabIndex=0;dropDownContainer.appendChild(chevronIcon);dropDownContainer.addEventListener('click',this._dropDownClicked.bind(this));dropDownContainer.addEventListener('keydown',this._dropDownKeydown.bind(this));dropDownContainer.addEventListener('mousedown',event=>{if(event.which!==1||this._triggerDropDownTimeout){return;}
  78. this._triggerDropDownTimeout=setTimeout(this._dropDownClicked.bind(this,event),200);});return dropDownContainer;}
  79. _dropDownClicked(event){if(event.which!==1){return;}
  80. if(this._triggerDropDownTimeout){clearTimeout(this._triggerDropDownTimeout);this._triggerDropDownTimeout=null;}
  81. const rect=this._dropDownButton.getBoundingClientRect();const menu=new UI.ContextMenu(event,false,rect.left,rect.bottom);for(const tab of this._tabs){if(tab._shown){continue;}
  82. if(this._numberOfTabsShown()===0&&this._tabsHistory[0]===tab){menu.defaultSection().appendCheckboxItem(tab.title,this._dropDownMenuItemSelected.bind(this,tab),true);}else{menu.defaultSection().appendItem(tab.title,this._dropDownMenuItemSelected.bind(this,tab));}}
  83. menu.show();}
  84. _dropDownKeydown(event){if(isEnterOrSpaceKey(event)){this._dropDownButton.click();event.consume(true);}}
  85. _dropDownMenuItemSelected(tab){this._lastSelectedOverflowTab=tab;this.selectTab(tab.id,true,true);}
  86. _totalWidth(){return this._headerContentsElement.getBoundingClientRect().width;}
  87. _numberOfTabsShown(){let numTabsShown=0;for(const tab of this._tabs){if(tab._shown){numTabsShown++;}}
  88. return numTabsShown;}
  89. disableOverflowMenu(){this._overflowDisabled=true;}
  90. _updateTabsDropDown(){const tabsToShowIndexes=this._tabsToShowIndexes(this._tabs,this._tabsHistory,this._totalWidth(),this._measuredDropDownButtonWidth||0);if(this._lastSelectedOverflowTab&&this._numberOfTabsShown()!==tabsToShowIndexes.length){delete this._lastSelectedOverflowTab;this._updateTabsDropDown();return;}
  91. for(let i=0;i<this._tabs.length;++i){if(this._tabs[i]._shown&&tabsToShowIndexes.indexOf(i)===-1){this._hideTabElement(this._tabs[i]);}}
  92. for(let i=0;i<tabsToShowIndexes.length;++i){const tab=this._tabs[tabsToShowIndexes[i]];if(!tab._shown){this._showTabElement(i,tab);}}
  93. if(!this._overflowDisabled){this._maybeShowDropDown(tabsToShowIndexes.length!==this._tabs.length);}}
  94. _maybeShowDropDown(hasMoreTabs){if(hasMoreTabs&&!this._dropDownButton.parentElement){this._headerContentsElement.appendChild(this._dropDownButton);}else if(!hasMoreTabs&&this._dropDownButton.parentElement){this._headerContentsElement.removeChild(this._dropDownButton);}}
  95. _measureDropDownButton(){if(this._overflowDisabled||this._measuredDropDownButtonWidth){return;}
  96. this._dropDownButton.classList.add('measuring');this._headerContentsElement.appendChild(this._dropDownButton);this._measuredDropDownButtonWidth=this._dropDownButton.getBoundingClientRect().width;this._headerContentsElement.removeChild(this._dropDownButton);this._dropDownButton.classList.remove('measuring');}
  97. _updateWidths(){const measuredWidths=this._measureWidths();const maxWidth=this._shrinkableTabs?this._calculateMaxWidth(measuredWidths.slice(),this._totalWidth()):Number.MAX_VALUE;let i=0;for(const tab of this._tabs){tab.setWidth(this._verticalTabLayout?-1:Math.min(maxWidth,measuredWidths[i++]));}}
  98. _measureWidths(){this._tabsElement.style.setProperty('width','2000px');const measuringTabElements=[];for(const tab of this._tabs){if(typeof tab._measuredWidth==='number'){continue;}
  99. const measuringTabElement=tab._createTabElement(true);measuringTabElement.__tab=tab;measuringTabElements.push(measuringTabElement);this._tabsElement.appendChild(measuringTabElement);}
  100. for(let i=0;i<measuringTabElements.length;++i){const width=measuringTabElements[i].getBoundingClientRect().width;measuringTabElements[i].__tab._measuredWidth=Math.ceil(width);}
  101. for(let i=0;i<measuringTabElements.length;++i){measuringTabElements[i].remove();}
  102. const measuredWidths=[];for(const tab of this._tabs){measuredWidths.push(tab._measuredWidth);}
  103. this._tabsElement.style.removeProperty('width');return measuredWidths;}
  104. _calculateMaxWidth(measuredWidths,totalWidth){if(!measuredWidths.length){return 0;}
  105. measuredWidths.sort(function(x,y){return x-y;});let totalMeasuredWidth=0;for(let i=0;i<measuredWidths.length;++i){totalMeasuredWidth+=measuredWidths[i];}
  106. if(totalWidth>=totalMeasuredWidth){return measuredWidths[measuredWidths.length-1];}
  107. let totalExtraWidth=0;for(let i=measuredWidths.length-1;i>0;--i){const extraWidth=measuredWidths[i]-measuredWidths[i-1];totalExtraWidth+=(measuredWidths.length-i)*extraWidth;if(totalWidth+totalExtraWidth>=totalMeasuredWidth){return measuredWidths[i-1]+
  108. (totalWidth+totalExtraWidth-totalMeasuredWidth)/(measuredWidths.length-i);}}
  109. return totalWidth/measuredWidths.length;}
  110. _tabsToShowIndexes(tabsOrdered,tabsHistory,totalWidth,measuredDropDownButtonWidth){const tabsToShowIndexes=[];let totalTabsWidth=0;const tabCount=tabsOrdered.length;const tabsToLookAt=tabsOrdered.slice(0);if(this._currentTab!==undefined){tabsToLookAt.unshift(tabsToLookAt.splice(tabsToLookAt.indexOf(this._currentTab),1)[0]);}
  111. if(this._lastSelectedOverflowTab!==undefined){tabsToLookAt.unshift(tabsToLookAt.splice(tabsToLookAt.indexOf(this._lastSelectedOverflowTab),1)[0]);}
  112. for(let i=0;i<tabCount;++i){const tab=this._automaticReorder?tabsHistory[i]:tabsToLookAt[i];totalTabsWidth+=tab.width();let minimalRequiredWidth=totalTabsWidth;if(i!==tabCount-1){minimalRequiredWidth+=measuredDropDownButtonWidth;}
  113. if(!this._verticalTabLayout&&minimalRequiredWidth>totalWidth){break;}
  114. tabsToShowIndexes.push(tabsOrdered.indexOf(tab));}
  115. tabsToShowIndexes.sort(function(x,y){return x-y;});return tabsToShowIndexes;}
  116. _hideCurrentTab(){if(!this._currentTab){return;}
  117. this._hideTab(this._currentTab);delete this._currentTab;}
  118. _showTab(tab){tab.tabElement.tabIndex=0;tab.tabElement.classList.add('selected');UI.ARIAUtils.setSelected(tab.tabElement,true);tab.view.show(this.element);this._updateTabSlider();}
  119. _updateTabSlider(){if(!this._sliderEnabled){return;}
  120. if(!this._currentTab){this._tabSlider.style.width=0;return;}
  121. let left=0;for(let i=0;i<this._tabs.length&&this._currentTab!==this._tabs[i];i++){if(this._tabs[i]._shown){left+=this._tabs[i]._measuredWidth;}}
  122. const sliderWidth=this._currentTab._shown?this._currentTab._measuredWidth:this._dropDownButton.offsetWidth;const scaleFactor=window.devicePixelRatio>=1.5?' scaleY(0.75)':'';this._tabSlider.style.transform='translateX('+left+'px)'+scaleFactor;this._tabSlider.style.width=sliderWidth+'px';if(this._tabSlider.parentElement!==this._headerContentsElement){this._headerContentsElement.appendChild(this._tabSlider);}}
  123. _hideTab(tab){tab.tabElement.removeAttribute('tabIndex');tab.tabElement.classList.remove('selected');tab.tabElement.setAttribute('aria-selected','false');tab.view.detach();}
  124. elementsToRestoreScrollPositionsFor(){return[this._contentElement];}
  125. _insertBefore(tab,index){this._tabsElement.insertBefore(tab.tabElement,this._tabsElement.childNodes[index]);const oldIndex=this._tabs.indexOf(tab);this._tabs.splice(oldIndex,1);if(oldIndex<index){--index;}
  126. this._tabs.splice(index,0,tab);this.dispatchEventToListeners(Events.TabOrderChanged,{tabId:tab.id});}
  127. leftToolbar(){if(!this._leftToolbar){this._leftToolbar=new UI.Toolbar('tabbed-pane-left-toolbar');this._headerElement.insertBefore(this._leftToolbar.element,this._headerElement.firstChild);}
  128. return this._leftToolbar;}
  129. rightToolbar(){if(!this._rightToolbar){this._rightToolbar=new UI.Toolbar('tabbed-pane-right-toolbar');this._headerElement.appendChild(this._rightToolbar.element);}
  130. return this._rightToolbar;}
  131. setAllowTabReorder(allow,automatic){this._allowTabReorder=allow;this._automaticReorder=automatic;}
  132. _keyDown(event){if(!this._currentTab){return;}
  133. let nextTabElement=null;switch(event.key){case'ArrowUp':case'ArrowLeft':nextTabElement=this._currentTab.tabElement.previousElementSibling;if(!nextTabElement&&!this._dropDownButton.parentElement){nextTabElement=this._currentTab.tabElement.parentElement.lastElementChild;}
  134. break;case'ArrowDown':case'ArrowRight':nextTabElement=this._currentTab.tabElement.nextElementSibling;if(!nextTabElement&&!this._dropDownButton.parentElement){nextTabElement=this._currentTab.tabElement.parentElement.firstElementChild;}
  135. break;case'Enter':case' ':this._currentTab.view.focus();return;default:return;}
  136. if(!nextTabElement){this._dropDownButton.click();return;}
  137. const tab=this._tabs.find(tab=>tab.tabElement===nextTabElement);this.selectTab(tab.id,true);nextTabElement.focus();}}
  138. export const Events={TabSelected:Symbol('TabSelected'),TabClosed:Symbol('TabClosed'),TabOrderChanged:Symbol('TabOrderChanged')};export class TabbedPaneTab{constructor(tabbedPane,id,title,closeable,view,tooltip){this._closeable=closeable;this._tabbedPane=tabbedPane;this._id=id;this._title=title;this._tooltip=tooltip;this._view=view;this._shown=false;this._measuredWidth;this._tabElement;this._iconContainer=null;}
  139. get id(){return this._id;}
  140. get title(){return this._title;}
  141. set title(title){if(title===this._title){return;}
  142. this._title=title;if(this._titleElement){this._titleElement.textContent=title;}
  143. delete this._measuredWidth;}
  144. isCloseable(){return this._closeable;}
  145. _setIcon(icon){this._icon=icon;if(this._tabElement){this._createIconElement(this._tabElement,this._titleElement,false);}
  146. delete this._measuredWidth;}
  147. _toggleClass(className,force){const element=this.tabElement;const hasClass=element.classList.contains(className);if(hasClass===force){return false;}
  148. element.classList.toggle(className,force);delete this._measuredWidth;return true;}
  149. get view(){return this._view;}
  150. set view(view){this._view=view;}
  151. get tooltip(){return this._tooltip;}
  152. set tooltip(tooltip){this._tooltip=tooltip;if(this._titleElement){this._titleElement.title=tooltip||'';}}
  153. get tabElement(){if(!this._tabElement){this._tabElement=this._createTabElement(false);}
  154. return this._tabElement;}
  155. width(){return this._width;}
  156. setWidth(width){this.tabElement.style.width=width===-1?'':(width+'px');this._width=width;}
  157. setDelegate(delegate){this._delegate=delegate;}
  158. _createIconElement(tabElement,titleElement,measuring){if(tabElement.__iconElement){tabElement.__iconElement.remove();tabElement.__iconElement=null;}
  159. if(!this._icon){return;}
  160. const iconContainer=createElementWithClass('span','tabbed-pane-header-tab-icon');const iconNode=measuring?this._icon.cloneNode(true):this._icon;iconContainer.appendChild(iconNode);tabElement.insertBefore(iconContainer,titleElement);tabElement.__iconElement=iconContainer;}
  161. _createTabElement(measuring){const tabElement=createElementWithClass('div','tabbed-pane-header-tab');tabElement.id='tab-'+this._id;UI.ARIAUtils.markAsTab(tabElement);UI.ARIAUtils.setSelected(tabElement,false);UI.ARIAUtils.setAccessibleName(tabElement,this.title);const titleElement=tabElement.createChild('span','tabbed-pane-header-tab-title');titleElement.textContent=this.title;titleElement.title=this.tooltip||'';this._createIconElement(tabElement,titleElement,measuring);if(!measuring){this._titleElement=titleElement;}
  162. if(this._closeable){const closeButton=tabElement.createChild('div','tabbed-pane-close-button','dt-close-button');closeButton.gray=true;closeButton.setAccessibleName(ls`Close ${this.title}`);tabElement.classList.add('closeable');}
  163. if(measuring){tabElement.classList.add('measuring');}else{tabElement.addEventListener('click',this._tabClicked.bind(this),false);tabElement.addEventListener('auxclick',this._tabClicked.bind(this),false);tabElement.addEventListener('mousedown',this._tabMouseDown.bind(this),false);tabElement.addEventListener('mouseup',this._tabMouseUp.bind(this),false);tabElement.addEventListener('contextmenu',this._tabContextMenu.bind(this),false);if(this._tabbedPane._allowTabReorder){UI.installDragHandle(tabElement,this._startTabDragging.bind(this),this._tabDragging.bind(this),this._endTabDragging.bind(this),'-webkit-grabbing','pointer',200);}}
  164. return tabElement;}
  165. _tabClicked(event){const middleButton=event.button===1;const shouldClose=this._closeable&&(middleButton||event.target.classList.contains('tabbed-pane-close-button'));if(!shouldClose){this._tabbedPane.focus();return;}
  166. this._closeTabs([this.id]);event.consume(true);}
  167. _tabMouseDown(event){if(event.target.classList.contains('tabbed-pane-close-button')||event.button===1){return;}
  168. this._tabbedPane.selectTab(this.id,true);}
  169. _tabMouseUp(event){if(event.button===1){event.consume(true);}}
  170. _closeTabs(ids){if(this._delegate){this._delegate.closeTabs(this._tabbedPane,ids);return;}
  171. this._tabbedPane.closeTabs(ids,true);}
  172. _tabContextMenu(event){function close(){this._closeTabs([this.id]);}
  173. function closeOthers(){this._closeTabs(this._tabbedPane.otherTabs(this.id));}
  174. function closeAll(){this._closeTabs(this._tabbedPane.tabIds());}
  175. function closeToTheRight(){this._closeTabs(this._tabbedPane._tabsToTheRight(this.id));}
  176. const contextMenu=new UI.ContextMenu(event);if(this._closeable){contextMenu.defaultSection().appendItem(Common.UIString('Close'),close.bind(this));contextMenu.defaultSection().appendItem(Common.UIString('Close others'),closeOthers.bind(this));contextMenu.defaultSection().appendItem(Common.UIString('Close tabs to the right'),closeToTheRight.bind(this));contextMenu.defaultSection().appendItem(Common.UIString('Close all'),closeAll.bind(this));}
  177. if(this._delegate){this._delegate.onContextMenu(this.id,contextMenu);}
  178. contextMenu.show();}
  179. _startTabDragging(event){if(event.target.classList.contains('tabbed-pane-close-button')){return false;}
  180. this._dragStartX=event.pageX;this._tabElement.classList.add('dragging');this._tabbedPane._tabSlider.remove();return true;}
  181. _tabDragging(event){const tabElements=this._tabbedPane._tabsElement.childNodes;for(let i=0;i<tabElements.length;++i){let tabElement=tabElements[i];if(tabElement===this._tabElement){continue;}
  182. const intersects=tabElement.offsetLeft+tabElement.clientWidth>this._tabElement.offsetLeft&&this._tabElement.offsetLeft+this._tabElement.clientWidth>tabElement.offsetLeft;if(!intersects){continue;}
  183. if(Math.abs(event.pageX-this._dragStartX)<tabElement.clientWidth/2+5){break;}
  184. if(event.pageX-this._dragStartX>0){tabElement=tabElement.nextSibling;++i;}
  185. const oldOffsetLeft=this._tabElement.offsetLeft;this._tabbedPane._insertBefore(this,i);this._dragStartX+=this._tabElement.offsetLeft-oldOffsetLeft;break;}
  186. if(!this._tabElement.previousSibling&&event.pageX-this._dragStartX<0){this._tabElement.style.setProperty('left','0px');return;}
  187. if(!this._tabElement.nextSibling&&event.pageX-this._dragStartX>0){this._tabElement.style.setProperty('left','0px');return;}
  188. this._tabElement.style.setProperty('left',(event.pageX-this._dragStartX)+'px');}
  189. _endTabDragging(event){this._tabElement.classList.remove('dragging');this._tabElement.style.removeProperty('left');delete this._dragStartX;this._tabbedPane._updateTabSlider();}}
  190. export class TabbedPaneTabDelegate{closeTabs(tabbedPane,ids){}
  191. onContextMenu(tabId,contextMenu){}}
  192. self.UI=self.UI||{};UI=UI||{};UI.TabbedPane=TabbedPane;UI.TabbedPane.Events=Events;UI.TabbedPaneTab=TabbedPaneTab;UI.TabbedPaneTabDelegate=TabbedPaneTabDelegate;