MetricsSidebarPane.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. export default class MetricsSidebarPane extends Elements.ElementsSidebarPane{constructor(){super();this.registerRequiredCSS('elements/metricsSidebarPane.css');this._inlineStyle=null;}
  2. doUpdate(){if(this._isEditingMetrics){return Promise.resolve();}
  3. const node=this.node();const cssModel=this.cssModel();if(!node||node.nodeType()!==Node.ELEMENT_NODE||!cssModel){this.contentElement.removeChildren();return Promise.resolve();}
  4. function callback(style){if(!style||this.node()!==node){return;}
  5. this._updateMetrics(style);}
  6. function inlineStyleCallback(inlineStyleResult){if(inlineStyleResult&&this.node()===node){this._inlineStyle=inlineStyleResult.inlineStyle;}}
  7. const promises=[cssModel.computedStylePromise(node.id).then(callback.bind(this)),cssModel.inlineStylesPromise(node.id).then(inlineStyleCallback.bind(this))];return Promise.all(promises);}
  8. onCSSModelChanged(){this.update();}
  9. _getPropertyValueAsPx(style,propertyName){return Number(style.get(propertyName).replace(/px$/,'')||0);}
  10. _getBox(computedStyle,componentName){const suffix=componentName==='border'?'-width':'';const left=this._getPropertyValueAsPx(computedStyle,componentName+'-left'+suffix);const top=this._getPropertyValueAsPx(computedStyle,componentName+'-top'+suffix);const right=this._getPropertyValueAsPx(computedStyle,componentName+'-right'+suffix);const bottom=this._getPropertyValueAsPx(computedStyle,componentName+'-bottom'+suffix);return{left:left,top:top,right:right,bottom:bottom};}
  11. _highlightDOMNode(showHighlight,mode,event){event.consume();if(showHighlight&&this.node()){if(this._highlightMode===mode){return;}
  12. this._highlightMode=mode;this.node().highlight(mode);}else{delete this._highlightMode;SDK.OverlayModel.hideDOMNodeHighlight();}
  13. for(let i=0;this._boxElements&&i<this._boxElements.length;++i){const element=this._boxElements[i];if(!this.node()||mode==='all'||element._name===mode){element.style.backgroundColor=element._backgroundColor;}else{element.style.backgroundColor='';}}}
  14. _updateMetrics(style){const metricsElement=createElement('div');metricsElement.className='metrics';const self=this;function createBoxPartElement(style,name,side,suffix){const propertyName=(name!=='position'?name+'-':'')+side+suffix;let value=style.get(propertyName);if(value===''||(name!=='position'&&value==='0px')){value='\u2012';}else if(name==='position'&&value==='auto'){value='\u2012';}
  15. value=value.replace(/px$/,'');value=Number.toFixedIfFloating(value);const element=createElement('div');element.className=side;element.textContent=value;element.addEventListener('dblclick',this.startEditing.bind(this,element,name,propertyName,style),false);return element;}
  16. function getContentAreaWidthPx(style){let width=style.get('width').replace(/px$/,'');if(!isNaN(width)&&style.get('box-sizing')==='border-box'){const borderBox=self._getBox(style,'border');const paddingBox=self._getBox(style,'padding');width=width-borderBox.left-borderBox.right-paddingBox.left-paddingBox.right;}
  17. return Number.toFixedIfFloating(width.toString());}
  18. function getContentAreaHeightPx(style){let height=style.get('height').replace(/px$/,'');if(!isNaN(height)&&style.get('box-sizing')==='border-box'){const borderBox=self._getBox(style,'border');const paddingBox=self._getBox(style,'padding');height=height-borderBox.top-borderBox.bottom-paddingBox.top-paddingBox.bottom;}
  19. return Number.toFixedIfFloating(height.toString());}
  20. const noMarginDisplayType={'table-cell':true,'table-column':true,'table-column-group':true,'table-footer-group':true,'table-header-group':true,'table-row':true,'table-row-group':true};const noPaddingDisplayType={'table-column':true,'table-column-group':true,'table-footer-group':true,'table-header-group':true,'table-row':true,'table-row-group':true};const noPositionType={'static':true};const boxes=['content','padding','border','margin','position'];const boxColors=[Common.Color.PageHighlight.Content,Common.Color.PageHighlight.Padding,Common.Color.PageHighlight.Border,Common.Color.PageHighlight.Margin,Common.Color.fromRGBA([0,0,0,0])];const boxLabels=[Common.UIString('content'),Common.UIString('padding'),Common.UIString('border'),Common.UIString('margin'),Common.UIString('position')];let previousBox=null;this._boxElements=[];for(let i=0;i<boxes.length;++i){const name=boxes[i];if(name==='margin'&&noMarginDisplayType[style.get('display')]){continue;}
  21. if(name==='padding'&&noPaddingDisplayType[style.get('display')]){continue;}
  22. if(name==='position'&&noPositionType[style.get('position')]){continue;}
  23. const boxElement=createElement('div');boxElement.className=name;boxElement._backgroundColor=boxColors[i].asString(Common.Color.Format.RGBA);boxElement._name=name;boxElement.style.backgroundColor=boxElement._backgroundColor;boxElement.addEventListener('mouseover',this._highlightDOMNode.bind(this,true,name==='position'?'all':name),false);this._boxElements.push(boxElement);if(name==='content'){const widthElement=createElement('span');widthElement.textContent=getContentAreaWidthPx(style);widthElement.addEventListener('dblclick',this.startEditing.bind(this,widthElement,'width','width',style),false);const heightElement=createElement('span');heightElement.textContent=getContentAreaHeightPx(style);heightElement.addEventListener('dblclick',this.startEditing.bind(this,heightElement,'height','height',style),false);boxElement.appendChild(widthElement);boxElement.createTextChild(' \u00D7 ');boxElement.appendChild(heightElement);}else{const suffix=(name==='border'?'-width':'');const labelElement=createElement('div');labelElement.className='label';labelElement.textContent=boxLabels[i];boxElement.appendChild(labelElement);boxElement.appendChild(createBoxPartElement.call(this,style,name,'top',suffix));boxElement.appendChild(createElement('br'));boxElement.appendChild(createBoxPartElement.call(this,style,name,'left',suffix));if(previousBox){boxElement.appendChild(previousBox);}
  24. boxElement.appendChild(createBoxPartElement.call(this,style,name,'right',suffix));boxElement.appendChild(createElement('br'));boxElement.appendChild(createBoxPartElement.call(this,style,name,'bottom',suffix));}
  25. previousBox=boxElement;}
  26. metricsElement.appendChild(previousBox);metricsElement.addEventListener('mouseover',this._highlightDOMNode.bind(this,false,'all'),false);this.contentElement.removeChildren();this.contentElement.appendChild(metricsElement);Host.userMetrics.panelLoaded('elements','DevTools.Launch.Elements');}
  27. startEditing(targetElement,box,styleProperty,computedStyle){if(UI.isBeingEdited(targetElement)){return;}
  28. const context={box:box,styleProperty:styleProperty,computedStyle:computedStyle};const boundKeyDown=this._handleKeyDown.bind(this,context,styleProperty);context.keyDownHandler=boundKeyDown;targetElement.addEventListener('keydown',boundKeyDown,false);this._isEditingMetrics=true;const config=new UI.InplaceEditor.Config(this._editingCommitted.bind(this),this.editingCancelled.bind(this),context);UI.InplaceEditor.startEditing(targetElement,config);targetElement.getComponentSelection().selectAllChildren(targetElement);}
  29. _handleKeyDown(context,styleProperty,event){const element=event.currentTarget;function finishHandler(originalValue,replacementString){this._applyUserInput(element,replacementString,originalValue,context,false);}
  30. function customNumberHandler(prefix,number,suffix){if(styleProperty!=='margin'&&number<0){number=0;}
  31. return prefix+number+suffix;}
  32. UI.handleElementValueModifications(event,element,finishHandler.bind(this),undefined,customNumberHandler);}
  33. editingEnded(element,context){delete this.originalPropertyData;delete this.previousPropertyDataCandidate;element.removeEventListener('keydown',context.keyDownHandler,false);delete this._isEditingMetrics;}
  34. editingCancelled(element,context){if('originalPropertyData'in this&&this._inlineStyle){if(!this.originalPropertyData){const pastLastSourcePropertyIndex=this._inlineStyle.pastLastSourcePropertyIndex();if(pastLastSourcePropertyIndex){this._inlineStyle.allProperties()[pastLastSourcePropertyIndex-1].setText('',false);}}else{this._inlineStyle.allProperties()[this.originalPropertyData.index].setText(this.originalPropertyData.propertyText,false);}}
  35. this.editingEnded(element,context);this.update();}
  36. _applyUserInput(element,userInput,previousContent,context,commitEditor){if(!this._inlineStyle){return this.editingCancelled(element,context);}
  37. if(commitEditor&&userInput===previousContent){return this.editingCancelled(element,context);}
  38. if(context.box!=='position'&&(!userInput||userInput==='\u2012')){userInput='0px';}else if(context.box==='position'&&(!userInput||userInput==='\u2012')){userInput='auto';}
  39. userInput=userInput.toLowerCase();if(/^\d+$/.test(userInput)){userInput+='px';}
  40. const styleProperty=context.styleProperty;const computedStyle=context.computedStyle;if(computedStyle.get('box-sizing')==='border-box'&&(styleProperty==='width'||styleProperty==='height')){if(!userInput.match(/px$/)){Common.console.error('For elements with box-sizing: border-box, only absolute content area dimensions can be applied');return;}
  41. const borderBox=this._getBox(computedStyle,'border');const paddingBox=this._getBox(computedStyle,'padding');let userValuePx=Number(userInput.replace(/px$/,''));if(isNaN(userValuePx)){return;}
  42. if(styleProperty==='width'){userValuePx+=borderBox.left+borderBox.right+paddingBox.left+paddingBox.right;}else{userValuePx+=borderBox.top+borderBox.bottom+paddingBox.top+paddingBox.bottom;}
  43. userInput=userValuePx+'px';}
  44. this.previousPropertyDataCandidate=null;const allProperties=this._inlineStyle.allProperties();for(let i=0;i<allProperties.length;++i){const property=allProperties[i];if(property.name!==context.styleProperty||!property.activeInStyle()){continue;}
  45. this.previousPropertyDataCandidate=property;property.setValue(userInput,commitEditor,true,callback.bind(this));return;}
  46. this._inlineStyle.appendProperty(context.styleProperty,userInput,callback.bind(this));function callback(success){if(!success){return;}
  47. if(!('originalPropertyData'in this)){this.originalPropertyData=this.previousPropertyDataCandidate;}
  48. if(typeof this._highlightMode!=='undefined'){this.node().highlight(this._highlightMode);}
  49. if(commitEditor){this.update();}}}
  50. _editingCommitted(element,userInput,previousContent,context){this.editingEnded(element,context);this._applyUserInput(element,userInput,previousContent,context,true);}}
  51. self.Elements=self.Elements||{};Elements=Elements||{};Elements.MetricsSidebarPane=MetricsSidebarPane;