Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
david_stocker
Advisor
Advisor
0 Kudos

This is part of a tutorial series on creating extension components for Design Studio.

Last time, we brought our dynamic text callouts into the sandbox html file.  In this installment, we're going to bring them to the gauge component.  We'll spare ourselves the need to expose any properties to the script API, as - like with animations - callouts are likely something that the designer will strictly want to control.  (though an argument could also be made for user control and this could be a feature added later)  This leaves us with integrating the new code from kast time into component.js and adding the required properties.

New Properties

To review, when we added the callout JavaScript code to our  sandbox html file, we started off by declaring four variables that we later used for controlling the callouts:


var guidePositioning = "end";  //"end" and "midpoint"


var measureTextPositionType = "upperCentral";


var drawGuideText = true;


var drawMeasureText = true;



We'll add these four as properties.  The two draw… properties will be booleans, while the other two will be enumerated strings.  In addition, the callouts were hardcoded in the sandbox version.  In the component, we'll either want to use the relevant measure values, or (much less likely) the angle values.  In practice, this means that:

  • The measure callout is tied to measureVal or endAngleDeg
  • The start guide callout is tied to either measureMin or startAngelDeg
  • The end guide callout is tied to either measureMax or endAngleDegMax

None of these will require their own binding.  Instead, we'll refer back to the useMeasures property, that we introduced all the way back in Part 6a.  If the designer has decided to use measure values to define the gauge angles, then it makes no sense to display the angle values; and vice versa.

So our part 11 property declarations look like the following:


<!-- Part 11 -->


<property id="drawMeasureText"


  title="Enable Measure Text"


  type="boolean"


  tooltip="Enable in-component measure value display?"


  group="SCNGaugeTextCallouts"/>


<property id="measureTextPositionType"


  title="Measure Text Position Type"


  type="String"


  tooltip="Position of the measure text value"


  group="SCNGaugeTextCallouts">


  <possibleValue>endpoint</possibleValue>


  <possibleValue>upperCentral</possibleValue>


  <possibleValue>upperIdeographic</possibleValue>


  <possibleValue>lowerIdeographic</possibleValue>


  <possibleValue>lowerCentral</possibleValue>


  <possibleValue>bottom</possibleValue>


</property>


<property id="drawGuideText"


  title="Enable Guide Text"


  type="boolean"


  tooltip="Enable in-component guide line value display (guide lines don't need to be enabled to show the text)?"


  group="SCNGaugeTextCallouts"/>


<property id="guidePositioning"


  title="Guide Text Position Type"


  type="String"


  tooltip="Position of the guide text value, along the guide axis (middle or end)"


  group="SCNGaugeTextCallouts">


  <possibleValue>end</possibleValue>


  <possibleValue>middle</possibleValue>


</property>



In keeping with the good parctice of using sensible defaults, we'll set the following default values in the Initialization element (we do not default to displaying callouts):


<initialization>


  ...


  <defaultValue property="drawMeasureText">false</defaultValue>


  <defaultValue property="measureTextPositionType">upperCentral</defaultValue>


  <defaultValue property="drawGuideText">false</defaultValue>


  <defaultValue property="guidePositioning">end</defaultValue>


</initialization>



Component.js

We'll start with the textPositioning() function, introduced last time.  It will require only minimal changes when being copied over as a new function within component.js  the offsetXXX variables will need to move into the me._ namespace.


//New with Part 11


// Helper function to determine the vertical alignment (called 'dominant-baseline') and horizontal alignment (called ' text-anchor')


// In essence, this function tries to find a readable position for the text, so that it lies ourside the main arc, no matter the current


//  x and y are the absolute positions of the callout, within the component


//  isMiddleCallout determines whether this is anchored on the middle of a guide line


//  isStart determines whether or not this the start callout.  this function will try to position the callouts anchoren on the middle


// guide line outside of the gauge arc.


// text-anchor: https://developer.mozilla.org/en/docs/Web/SVG/Attribute/text-anchor


function textPositioning (x, y, isStart){


  var relativeOffsetX = x - me._offsetLeft;


  var relativeOffsetY = y - me._offsetDown;




  if (isStart == undefined){


  isStart = false;


  }




  var dominantBaseline = null;


  var textAnchor = null;


  if ((relativeOffsetX >= 0) && (relativeOffsetY >= 0)){


  //Lower Right Quadrant


  // Both middle and enf have a negative dominant baseline


  if (isStart == true){


  textAnchor = "start";


  dominantBaseline = "0em";


  } else {


  textAnchor = "end";


  dominantBaseline = ".8em";


  }



  } else if ((relativeOffsetX >= 0) && (relativeOffsetY < 0)){


  //Upper Right Quadrant


  if (isStart == true){


  textAnchor = "end";


  dominantBaseline = "0em";


  } else {


  textAnchor = "start";


  dominantBaseline = ".8em";


  }


  }


  else if ((relativeOffsetX < 0) && (relativeOffsetY < 0)){


  //Upper Left Quadrant


  if (isStart == true){


  textAnchor = "end";


  dominantBaseline = ".8em";


  } else {


  textAnchor = "start";


  dominantBaseline = "0em";


  }


  } else {


  //Lower Left Quadrant


  if (isStart == true){


  textAnchor = "start";


  dominantBaseline = ".8em";


  } else {


  textAnchor = "end";


  dominantBaseline = "0em";


  }


  }



  return [textAnchor, dominantBaseline]


}



The properties are also straightforward to bring into copmponent.js:


//Part 11 - Callouts


me._drawMeasureText = false;


me._measureTextPositionType = "upperCentral";


me._drawGuideText = false;


me._guidePositioning = "end";



And let's not forget our getter/setters:


//Part 11 Properties


me.drawMeasureText = function(value) {


  if (value === undefined) {


  return me._drawMeasureText;


  } else {


  me._drawMeasureText = value;


  return me;


  }


};


me.measureTextPositionType = function(value) {


  if (value === undefined) {


  return me._measureTextPositionType;


  } else {


  me._measureTextPositionType = value;


  return me;


  }


};


me.drawGuideText = function(value) {


  if (value === undefined) {


  return me._drawGuideText;


  } else {


  me._drawGuideText = value;


  return me;


  }


};


me.guidePositioning = function(value) {


  if (value === undefined) {


  return me._guidePositioning;


  } else {


  me._guidePositioning = value;


  return me;


  }


};


//End Part 11 Properties



And the 33 lines "Guide Positioning " code go to the end of the me.redraw() function.  Don't forget to refactor it.  The outerRad and xxxAnglexxx variables need a me._ prefix:


//Guide Positioning


if (me._drawGuideText == true){


  var guidePositionStart = {};


  var guidePositionEnd = {};


  var isMiddleCO = false;


  if (me._guidePositioning == "end"){


  guidePositionStart = endPoints (me._outerRad, me._startAngleDeg);


  guidePositionEnd = endPoints (me._outerRad, me._endAngleDegMax);


  }


  else {


  guidePositionStart = endPoints (me._outerRad/2, me._startAngleDeg);


  guidePositionEnd = endPoints (me._outerRad/2, me._endAngleDegMax);


  }


  var guideTextPositionStart = textPositioning (guidePositionStart.x, guidePositionStart.y, true);


  var guideTextPositionEnd= textPositioning (guidePositionEnd.x, guidePositionEnd.y);




  //Start Text


  vis.append("text")


  .attr("transform", "translate(" + guidePositionStart.x + "," + guidePositionStart.y + ")")


  .text(calloutTextStart)


  .attr("text-anchor", guideTextPositionStart[0])


  //.attr("dominant-baseline", guideTextPositionStart[1]);


  .attr("dy", guideTextPositionStart[1]);




  //End Text


  vis.append("text")


  .attr("transform", "translate(" + guidePositionEnd.x + "," + guidePositionEnd.y + ")")


  .text(calloutTextEnd)


  //.attr("text-anchor", "start")


  .attr("text-anchor", guideTextPositionEnd[0])


  //.attr("dominant-baseline", guideTextPositionEnd[1]);


  .attr("dy", guideTextPositionEnd[1]);


}



Here is a short demonstration video of it all coming together:

As usual, the current state of the project and the test app are in a public repository on GithubNext time, we'll have a look at styling the fonts in the callouts via SCC.