KpiView.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2019, 2020
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. */
  7. define(['jquery', 'underscore', '../VisView', 'text!./Kpi.html', 'text!./kpiSparklineSpec.json', '../../../util/DashboardFormatter', './KpiEventTarget', './KpiViewFactory', '../VisEventHandler', '../../../DynamicFileLoader', '../../../widgets/livewidget/nls/StringResources', '../../../util/TextScrambler', '../../../features/content/dataQueryExecution/QueryResults'], function ($, _, VisView, KpiTemplate, KPISparklineSpec, Formatter, KpiEventTarget, KpiViewFactory, VisEventHandler, DynamicFileLoader, StringResources, TextScrambler, QueryResults) {
  8. 'use strict';
  9. var QUERY_RESULT_ROW_INDEX = 0;
  10. var BASE_DATA_ITEM_INDEX = 0;
  11. var TARGET_DATA_ITEM_INDEX = 1;
  12. var KpiView = VisView.extend({
  13. BASE_SLOT_ID: 'actual',
  14. TARGET_SLOT_ID: 'goal',
  15. SPARKLINE_TIME_SLOT_ID: 'sparkline.time',
  16. SPARKLINE_ACTUAL_SLOT_ID: 'sparkline.actual',
  17. className: 'dataview kpi-widget',
  18. templateString: KpiTemplate,
  19. init: function init(options) {
  20. KpiView.inherited('init', this, arguments);
  21. this._baseValueView = null;
  22. this._targetValueView = null;
  23. this._sparkLineView = null;
  24. this._content = options.content;
  25. var isNewFeature = this._isNewConditionalFormatFeature();
  26. var condFeatureName = isNewFeature ? 'ConditionalFormatting' : 'conditionalFormatting';
  27. this._conditionalFormattingFeature = this._content.getFeature(condFeatureName);
  28. this._colorsService = this.dashboardApi.getFeature('Colors');
  29. this._visualizationAPI = this._content.getFeature('Visualization.internal');
  30. this._kpiViewFactory = options.kpiViewFactory || new KpiViewFactory();
  31. // deregister here since the new component do not have access to the dashboardAPI to check whether the feature flag is turned on or off
  32. // Instead when the new feature gets loaded it always register itself with the 'Properties' and 'CustomColor' feature
  33. // Remove this code when the new feature get turn on by default
  34. if (!isNewFeature) {
  35. var newCondFeature = this._content.getFeature('ConditionalFormatting');
  36. if (newCondFeature) {
  37. this._content.getFeature('Properties').deregisterProvider(newCondFeature);
  38. this.dashboardApi.getFeature('CustomColor').deregisterProvider(newCondFeature);
  39. }
  40. }
  41. },
  42. summaryReveal: function summaryReveal() {
  43. this.visModel.renderCompleteBeforeAnimation();
  44. if (!this._readyToRender()) {
  45. return Promise.resolve();
  46. }
  47. var $label = this.$el.find('.kpi-widget-base-value .labeled-value-label');
  48. var $value = this.$el.find('.kpi-widget-base-value .labeled-value-value');
  49. if ($label.length) {
  50. this.labelScrambler = new TextScrambler({
  51. el: $label[0]
  52. });
  53. this.labelScrambler.scramble();
  54. }
  55. if ($value.length) {
  56. this.valueScrambler = new TextScrambler({
  57. ignoreSet: ['-', ',', '.'],
  58. el: $value[0]
  59. });
  60. this.valueScrambler.scramble();
  61. }
  62. return Promise.resolve();
  63. },
  64. remove: function remove() {
  65. if (this.eventHandler) {
  66. this.eventHandler.remove();
  67. this.eventHandler = null;
  68. }
  69. this._removeViews();
  70. KpiView.inherited('remove', this, arguments);
  71. },
  72. /**
  73. * Loads and creates the control. Returns a promise which is resolved when the control
  74. * is created and ready to render
  75. */
  76. whenVisControlReady: function whenVisControlReady() {
  77. var _this = this;
  78. if (this.visControl) {
  79. return Promise.resolve(this.visControl);
  80. } else {
  81. return DynamicFileLoader.load(['dashboard-analytics/visualizations/renderer/kpi/control/KpiControl']).then(function (modules) {
  82. var VisControl = modules[0];
  83. _this.visControl = new VisControl({
  84. domNode: _this.$el.find('.kpi-widget-content')[0]
  85. });
  86. _this.eventHandler = new VisEventHandler({
  87. target: new KpiEventTarget({
  88. $el: _this.$el,
  89. visControl: _this.visControl,
  90. visAPI: _this.visModel,
  91. view: _this,
  92. visualization: _this.visualization
  93. }),
  94. transaction: _this.transactionApi,
  95. ownerWidget: _this.ownerWidget,
  96. visAPI: _this.visModel,
  97. edgeSelection: true
  98. });
  99. return _this.visControl;
  100. });
  101. }
  102. },
  103. _removeViews: function _removeViews() {
  104. if (this._baseValueView) {
  105. this._baseValueView.remove();
  106. this._baseValueView = null;
  107. }
  108. if (this._targetValueView) {
  109. this._targetValueView.remove();
  110. this._targetValueView = null;
  111. }
  112. this._removeSparkLine();
  113. },
  114. _initializeConditionalFormatting: function _initializeConditionalFormatting() {
  115. // This is temporary until features have a proper way of initializing themselves
  116. if (this._conditionalFormattingFeature) {
  117. return this._conditionalFormattingFeature.initializeFeature();
  118. } else {
  119. return Promise.resolve();
  120. }
  121. },
  122. render: function render(renderInfo) {
  123. var _this2 = this,
  124. _arguments = arguments;
  125. // This is temporary until features have a proper way of initializing themselves
  126. return this._initializeConditionalFormatting().then(function () {
  127. _this2._baseValue = null;
  128. _this2._targetValue = null;
  129. var baseSlot = _this2._getBaseSlot();
  130. var targetSlot = _this2._getTargetSlot();
  131. if (!_this2._readyToRender() || !baseSlot) {
  132. _this2.renderIconView();
  133. return Promise.resolve();
  134. }
  135. _this2.removeIconView();
  136. $(_this2.contentNode).css('text-align', _this2._getTextAlignment() || 'center');
  137. if (!_this2._baseValueNode) {
  138. _this2._baseValueNode = _this2.$el.find('.kpi-widget-base-value');
  139. _this2._targetValueNode = _this2.$el.find('.kpi-widget-target-value');
  140. _this2._sparkLineNode = _this2._getSparklineContainer();
  141. }
  142. var queryResultData = renderInfo.data.getResult();
  143. _this2._baseValue = _this2._getQueryResultValue(queryResultData, QUERY_RESULT_ROW_INDEX, BASE_DATA_ITEM_INDEX);
  144. _this2._targetValue = _this2._getQueryResultValue(queryResultData, QUERY_RESULT_ROW_INDEX, TARGET_DATA_ITEM_INDEX);
  145. var baseProperties = _this2._getBaseProperties();
  146. var baseFormattedValue = _this2._formatValue(baseSlot, _this2._baseValue);
  147. var baseDataItem = _this2._getDataItem(baseSlot);
  148. var baseLabel = baseDataItem.getLabel();
  149. var targetProperties = _this2._getTargetProperties();
  150. _this2._targetValueNode.addClass('hidden');
  151. _this2._targetValueNode.empty();
  152. var renderPromises = [];
  153. var targetSlotIsMapped = _this2._isSlotMapped(targetSlot);
  154. if (targetSlotIsMapped || (targetProperties.manualValue || targetProperties.manualValue === 0) && !isNaN(targetProperties.manualValue)) {
  155. var formatSlot = void 0,
  156. targetLabel = void 0;
  157. if (targetSlotIsMapped) {
  158. formatSlot = targetSlot;
  159. var targetDataItem = _this2._getDataItem(targetSlot);
  160. targetLabel = targetDataItem.getLabel();
  161. } else {
  162. //Manual Value
  163. formatSlot = baseSlot;
  164. targetLabel = StringResources.get('kpiDefaultTargetLabel');
  165. _this2._targetValue = Number(targetProperties.manualValue);
  166. }
  167. if (targetProperties.showValue || targetProperties.showDistance) {
  168. _this2._targetValueNode.removeClass('hidden');
  169. var targetString = _this2._getTargetValueString(targetProperties, formatSlot);
  170. renderPromises.push(_this2._renderLabeledValue(targetString, targetLabel, targetProperties, 'targetValue'));
  171. }
  172. }
  173. var conditionalFormatting = _this2._conditionalFormattingFeature ? _this2._conditionalFormattingFeature.getFormatting(_this2._baseValue, _this2._targetValue, baseDataItem.getColumnId()) : null;
  174. renderPromises.push(_this2._renderLabeledValue(baseFormattedValue, baseLabel, baseProperties, 'baseValue', conditionalFormatting));
  175. var sparklineQueryResultData = renderInfo.data.getResult('sparkline');
  176. var showSparkline = _this2._shouldDisplaySparkline(sparklineQueryResultData);
  177. _this2._sparkLineNode.toggleClass('hidden', !showSparkline);
  178. if (showSparkline) {
  179. renderPromises.push(_this2._renderSparkLine(renderInfo, conditionalFormatting, baseProperties));
  180. } else {
  181. _this2._removeSparkLine();
  182. }
  183. return Promise.all(renderPromises);
  184. }).then(function () {
  185. var baseProperties = _this2._getBaseProperties();
  186. if (baseProperties.labelSize === 'auto') {
  187. _this2._scaleSections(_this2._getResizeDefinition());
  188. }
  189. return KpiView.inherited('render', _this2, _arguments);
  190. });
  191. },
  192. _getQueryResultValue: function _getQueryResultValue(queryResultData, rowIndex, dataItemIndex) {
  193. var value = null;
  194. var rowCount = queryResultData.getRowCount();
  195. if (rowIndex < rowCount) {
  196. var dataPointCount = queryResultData.getDataPointValueList()[rowIndex].pt.length;
  197. if (dataItemIndex < dataPointCount) {
  198. value = queryResultData.getValue(rowIndex, dataItemIndex).value;
  199. }
  200. }
  201. return value;
  202. },
  203. _getTargetValueString: function _getTargetValueString(targetProperties, formatSlot) {
  204. var targetFormattedValue = this._formatValue(formatSlot, this._targetValue);
  205. var targetString = '';
  206. if (targetProperties.showDistance) {
  207. var formattedTargetDistance = this._getFormattedTargetDistanceString(formatSlot, this._baseValue, this._targetValue);
  208. if (targetProperties.showValue) {
  209. targetString = StringResources.get('kpiTargetWithDistance', {
  210. value: targetFormattedValue,
  211. distance: formattedTargetDistance
  212. });
  213. } else {
  214. targetString = formattedTargetDistance;
  215. }
  216. } else {
  217. targetString = targetFormattedValue;
  218. }
  219. return targetString;
  220. },
  221. _renderLabeledValue: function _renderLabeledValue(value, label, properties, sectionName, conditionalFormatting) {
  222. var viewName = '_' + sectionName + 'View';
  223. var nodeName = '_' + sectionName + 'Node';
  224. var valueStyle = '';
  225. if (properties.labelSize && properties.labelSize !== 'auto') {
  226. valueStyle = 'font-size:' + properties.labelSize + 'px;';
  227. }
  228. if (conditionalFormatting && conditionalFormatting.color) {
  229. valueStyle += 'color:' + conditionalFormatting.color + ';';
  230. } else if (properties.valueColor) {
  231. var hexColor = this._colorsService.getHexColorFromDashboardColorSet(properties.valueColor);
  232. if (hexColor !== 'transparent') {
  233. valueStyle += 'color:' + hexColor + ';';
  234. }
  235. }
  236. var finalLabel = properties.label || label;
  237. var view = this[viewName];
  238. if (!view) {
  239. view = this[viewName] = this._kpiViewFactory.createLabeledValueView({
  240. el: this[nodeName][0],
  241. dashboardApi: this.dashboardApi,
  242. value: value,
  243. valueStyle: valueStyle,
  244. label: finalLabel,
  245. showLabel: properties.showLabel,
  246. conditionalFormatting: conditionalFormatting
  247. });
  248. } else {
  249. view.setValue(value);
  250. view.setValueStyle(valueStyle);
  251. view.setLabel(finalLabel);
  252. view.setShowLabel(properties.showLabel);
  253. view.setConditionalFormatting(conditionalFormatting);
  254. }
  255. return view.render();
  256. },
  257. _readyToRender: function _readyToRender() {
  258. return this.isMappingComplete() && !this.hasMissingFilters() && !this.hasUnavailableMetadataColumns();
  259. },
  260. _getResizeDefinition: function _getResizeDefinition() {
  261. var sizingDefinition = {
  262. $node: this.$el,
  263. reservedSpace: 16, //Remove any padding between and around elements from size calculations.
  264. children: []
  265. };
  266. if (this._baseValueView) {
  267. sizingDefinition.children.push({
  268. $node: this._baseValueView.getValueNode(),
  269. referenceHeight: 40, //Observed reference height in chrome given the rendering at the default size.
  270. minimumHeight: 22.8, //19 - Commented number is the desired minimum font size. Increased due to the line-height -> font-size reduction.
  271. text: true
  272. });
  273. if (this._baseValueView.hasShape()) {
  274. sizingDefinition.children[0].icon = {
  275. $node: this._baseValueView.getValueNode().find('svg'),
  276. paddingDivisor: 4
  277. };
  278. }
  279. if (this._baseValueView.getShowLabel()) {
  280. sizingDefinition.reservedSpace += 8;
  281. sizingDefinition.children.push({
  282. $node: this._baseValueView.getLabelNode(),
  283. referenceHeight: 14,
  284. minimumHeight: 9.6, //8
  285. maximumHeight: 29, //24 - Commented number is the desired maximum font size. Increased due to the line-height -> font-size reduction.
  286. text: true
  287. });
  288. }
  289. if (this._targetValueNode && !this._targetValueNode.hasClass('hidden') && this._targetValueView) {
  290. sizingDefinition.reservedSpace += 16;
  291. sizingDefinition.children.push({
  292. $node: this._targetValueView.getValueNode(),
  293. referenceHeight: 19,
  294. minimumHeight: 12, //10
  295. maximumHeight: 39, //32
  296. text: true
  297. });
  298. if (this._targetValueView.getShowLabel()) {
  299. sizingDefinition.reservedSpace += 8;
  300. sizingDefinition.children.push({
  301. $node: this._targetValueView.getLabelNode(),
  302. referenceHeight: 14,
  303. minimumHeight: 9.6, //8
  304. maximumHeight: 29, //24
  305. text: true
  306. });
  307. }
  308. }
  309. if (this._sparkLineNode && !this._sparkLineNode.hasClass('hidden') && this._sparkLineView) {
  310. sizingDefinition.reservedSpace += 16;
  311. sizingDefinition.children.push({
  312. $node: this._sparkLineNode,
  313. referenceHeight: 32,
  314. minimumHeight: 16,
  315. text: false
  316. });
  317. }
  318. }
  319. return sizingDefinition;
  320. },
  321. _scaleSections: function _scaleSections(sizingDefinition) {
  322. if (!sizingDefinition.children || !sizingDefinition.children.length) {
  323. return; //There are no child items, so there is nothing to size.
  324. }
  325. var LINE_HEIGHT_TO_FONT_SIZE = 1.2; //The font-size does not cover the full size needed to display that font. Reduce the font below the line-height to prevent font overflow.
  326. var padding = 0;
  327. if (sizingDefinition.reservedSpace) {
  328. padding += sizingDefinition.reservedSpace;
  329. }
  330. sizingDefinition.$node.css('overflow', 'hidden');
  331. var existingHeight = sizingDefinition.$node.height() - padding;
  332. var referenceHeight = 0;
  333. sizingDefinition.children.forEach(function (child) {
  334. referenceHeight += child.referenceHeight;
  335. });
  336. function setSizes(definition, size) {
  337. var sizeCapped = false;
  338. if (definition.minimumHeight) {
  339. size = Math.max(Math.floor(size), definition.minimumHeight);
  340. }
  341. if (definition.maximumHeight) {
  342. size = Math.min(size, definition.maximumHeight);
  343. sizeCapped = size === definition.maximumHeight;
  344. }
  345. var height = size;
  346. definition.$node.css('height', height + 'px');
  347. var fontSize = size / LINE_HEIGHT_TO_FONT_SIZE;
  348. if (definition.text) {
  349. definition.$node.css({ 'font-size': fontSize + 'px', 'line-height': size + 'px', 'flex-basis': size + 'px' });
  350. }
  351. if (definition.icon) {
  352. var iconSize = fontSize / 2;
  353. var iconCss = {
  354. width: iconSize + 'px',
  355. height: iconSize + 'px'
  356. };
  357. if (definition.icon.paddingDivisor) {
  358. iconCss['padding-left'] = iconSize / definition.icon.paddingDivisor + 'px';
  359. }
  360. definition.icon.$node.css(iconCss);
  361. }
  362. return sizeCapped;
  363. }
  364. var someItemsCapped = false;
  365. var adjustedDefinition = {
  366. $node: sizingDefinition.$node,
  367. reservedSpace: sizingDefinition.reservedSpace,
  368. children: []
  369. };
  370. sizingDefinition.children.forEach(function (child) {
  371. var percentOfFullHeight = child.referenceHeight / referenceHeight;
  372. var childSize = percentOfFullHeight * existingHeight;
  373. var childSizeCapped = setSizes(child, childSize);
  374. if (child.text) {
  375. var boundingWidth = child.$node.width();
  376. var actualWidth = child.$node[0].scrollWidth;
  377. if (actualWidth > Math.round(boundingWidth)) {
  378. actualWidth += 8; //scrollWidth is rounded, plus reserve a couple pixels around the text to prevent some infrequent scrollbars, some of which can be caused by the selection chrome
  379. if (childSizeCapped) {
  380. childSize = child.maximumHeight;
  381. }
  382. childSize = childSize * boundingWidth / actualWidth;
  383. childSizeCapped = setSizes(child, childSize);
  384. }
  385. }
  386. someItemsCapped = someItemsCapped || childSizeCapped;
  387. if (!childSizeCapped) {
  388. adjustedDefinition.children.push(child);
  389. } else {
  390. adjustedDefinition.reservedSpace += child.maximumHeight;
  391. }
  392. });
  393. sizingDefinition.$node.css('overflow', '');
  394. if (someItemsCapped) {
  395. //Some items were capped. Resize the other items.
  396. this._scaleSections(adjustedDefinition);
  397. }
  398. },
  399. /**
  400. * Return text formatted value for field
  401. *
  402. * @param slot, slotAPI
  403. * @param v The raw fact value.
  404. * @returns text formatted data value
  405. */
  406. _formatValue: function _formatValue(slot, v) {
  407. var formatSpec = this._getFormatForSlot(slot);
  408. v = Formatter.format(v, formatSpec);
  409. return v;
  410. },
  411. _getFormatForSlot: function _getFormatForSlot(slot) {
  412. var dataItem = this._getDataItem(slot);
  413. var format = dataItem && dataItem.getFormat();
  414. return format || {};
  415. },
  416. _getFormattedTargetDistanceString: function _getFormattedTargetDistanceString(slot, baseValue, targetValue) {
  417. if (targetValue === 0) {
  418. return StringResources.get('kpiDistanceUndefined');
  419. }
  420. var targetDistance = (baseValue - targetValue) / Math.abs(targetValue);
  421. var formatSpec = {
  422. convertPercent: true,
  423. maximumFractionDigits: 2,
  424. minimumFractionDigits: 0,
  425. type: 'percent',
  426. locale: slot.locale
  427. };
  428. var distanceString = Formatter.format(targetDistance, formatSpec);
  429. if (targetDistance > 0) {
  430. distanceString = '+' + distanceString;
  431. }
  432. return distanceString;
  433. },
  434. _getBaseProperties: function _getBaseProperties() {
  435. return {
  436. labelSize: this._content.getPropertyValue('baseValueSize'),
  437. showLabel: this._content.getPropertyValue('showItemLabel') === false ? false : true,
  438. label: this._content.getPropertyValue('baseValueLabel'),
  439. valueColor: this._content.getPropertyValue('elementColor')
  440. };
  441. },
  442. _getTargetProperties: function _getTargetProperties() {
  443. return {
  444. labelSize: this._content.getPropertyValue('targetValueSize'),
  445. showLabel: this._content.getPropertyValue('showTargetLabel') === false ? false : true,
  446. showValue: this._content.getPropertyValue('showTargetValue') === false ? false : true,
  447. showDistance: this._content.getPropertyValue('showTargetDistance') === false ? false : true,
  448. label: this._content.getPropertyValue('targetValueLabel'),
  449. manualValue: this._content.getPropertyValue('targetValue')
  450. };
  451. },
  452. _getTimeProperties: function _getTimeProperties() {
  453. return {
  454. showSparkline: this._content.getPropertyValue('showSparkline') === false ? false : true
  455. };
  456. },
  457. _getTextAlignment: function _getTextAlignment() {
  458. return this._content.getPropertyValue('textAlignment');
  459. },
  460. _getBaseSlot: function _getBaseSlot() {
  461. return this._visualizationAPI.getSlots().getSlot(this.BASE_SLOT_ID);
  462. },
  463. _getTargetSlot: function _getTargetSlot() {
  464. return this._visualizationAPI.getSlots().getSlot(this.TARGET_SLOT_ID);
  465. },
  466. _getSparklineTimeSlot: function _getSparklineTimeSlot() {
  467. return this._visualizationAPI.getSlots().getSlot(this.SPARKLINE_TIME_SLOT_ID);
  468. },
  469. _getSparklineActualSlot: function _getSparklineActualSlot() {
  470. return this._visualizationAPI.getSlots().getSlot(this.SPARKLINE_ACTUAL_SLOT_ID);
  471. },
  472. _getDataItem: function _getDataItem(slot) {
  473. var dataItemIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  474. return slot.getDataItemList()[dataItemIndex];
  475. },
  476. _isSlotMapped: function _isSlotMapped(slot) {
  477. var dataItems = slot && slot.getDataItemList() || [];
  478. return dataItems.length > 0;
  479. },
  480. animate: function animate() {
  481. if (this.resizing) {
  482. this.visModel.renderCompleteBeforeAnimation(); // Rendering is complete, no animation
  483. } else {
  484. KpiView.inherited('animate', this, arguments);
  485. }
  486. },
  487. resize: function resize() {
  488. // intentionally override VisView behavior.
  489. if (this.isMappingComplete()) {
  490. //TODO: Update which sections are shown based on size.
  491. var baseProperties = this._getBaseProperties();
  492. if (baseProperties.labelSize === 'auto') {
  493. this._scaleSections(this._getResizeDefinition());
  494. }
  495. } else {
  496. KpiView.inherited('resize', this, arguments);
  497. }
  498. },
  499. //[livewidget-cleanup] todo: this function is moved to KpiPropertyCallbacks
  500. onChangeValueSizeProperty: function onChangeValueSizeProperty(propertyName, propertyValue) {
  501. var props = {};
  502. if (propertyValue === 'auto') {
  503. props['baseValueSize'] = 'auto';
  504. props['targetValueSize'] = 'auto';
  505. } else {
  506. props['baseValueSize'] = this._content.getPropertyValue('baseValueSize');
  507. props['targetValueSize'] = this._content.getPropertyValue('targetValueSize');
  508. if (props['baseValueSize'] === 'auto') {
  509. props['baseValueSize'] = '32';
  510. props['targetValueSize'] = '16';
  511. }
  512. props[propertyName] = propertyValue;
  513. }
  514. var transactionId = _.uniqueId('kpi_font_size');
  515. for (var property in props) {
  516. this.ownerWidget.onPropertyUpdate({
  517. 'category': property,
  518. 'item': props[property],
  519. 'transactionId': transactionId
  520. });
  521. }
  522. },
  523. getDecoratorAPI: function getDecoratorAPI() {
  524. var indexedView = [this._baseValueView, this._targetValueView];
  525. return {
  526. decorateItem: function decorateItem(itemIndex) {
  527. if (indexedView[itemIndex]) {
  528. indexedView[itemIndex].setShowLabelTranslationIcon(true);
  529. }
  530. },
  531. decoratePoint: function decoratePoint() {
  532. return null;
  533. },
  534. decorate: function decorate() {
  535. return null;
  536. },
  537. clearItemDecoration: function clearItemDecoration() {
  538. indexedView.forEach(function (view) {
  539. if (view) {
  540. view.setShowLabelTranslationIcon(false);
  541. }
  542. });
  543. },
  544. updateDecorations: function updateDecorations() {
  545. indexedView.forEach(function (view) {
  546. if (view) {
  547. view.render();
  548. }
  549. });
  550. }
  551. };
  552. },
  553. getBaseValue: function getBaseValue() {
  554. return this._baseValue;
  555. },
  556. getTargetValue: function getTargetValue() {
  557. return this._targetValue;
  558. },
  559. _removeSparkLine: function _removeSparkLine() {
  560. if (this._sparkLineView) {
  561. this._sparkLineView.destroy();
  562. this._sparkLineView = null;
  563. }
  564. },
  565. _shouldDisplaySparkline: function _shouldDisplaySparkline(sparklineQueryResultData) {
  566. if (!this._isSlotMapped(this._getSparklineActualSlot())) {
  567. return false;
  568. }
  569. if (!this._isSlotMapped(this._getSparklineTimeSlot())) {
  570. return false;
  571. }
  572. if (!this._getTimeProperties().showSparkline) {
  573. return false;
  574. }
  575. if (!this._hasEnoughDataToRenderSparkline(sparklineQueryResultData)) {
  576. return false;
  577. }
  578. return true;
  579. },
  580. _hasEnoughDataToRenderSparkline: function _hasEnoughDataToRenderSparkline(sparklineQueryResultData) {
  581. if (!sparklineQueryResultData) {
  582. return false;
  583. }
  584. var dataPointValueList = sparklineQueryResultData.getDataPointValueList();
  585. var dataPointValueListLength = dataPointValueList.length;
  586. // Need at least 2 data point to draw the sparkline
  587. if (!dataPointValueList || dataPointValueListLength < 2) {
  588. this._addNotRenderingSparklineInfo();
  589. return false;
  590. }
  591. // Make sure we have at least 2 non NULL values
  592. var nonNullCount = 0;
  593. for (var i = 0; i < dataPointValueListLength; i++) {
  594. if (sparklineQueryResultData.getValue(i, 0).value !== null) {
  595. nonNullCount++;
  596. }
  597. if (nonNullCount > 1) {
  598. break;
  599. }
  600. }
  601. if (nonNullCount < 2) {
  602. this._addNotRenderingSparklineInfo();
  603. return false;
  604. }
  605. return true;
  606. },
  607. _addNotRenderingSparklineInfo: function _addNotRenderingSparklineInfo() {
  608. if (this.infoIndicator) {
  609. this.infoIndicator.addInfo([{
  610. id: 'data_notifications',
  611. label: StringResources.get('visualizationNotifications'),
  612. items: [{
  613. id: 'sparkLineInfoLabel',
  614. label: StringResources.get('kpiUnableDrawSparklineLabel'),
  615. isSubMessage: false
  616. }, {
  617. id: 'sparkLineInfo',
  618. label: StringResources.get('kpiUnableDrawSparklineInfo'),
  619. isSubMessage: true
  620. }]
  621. }]);
  622. }
  623. },
  624. _renderSparkLine: function _renderSparkLine(renderInfo, conditionalFormatting, baseProperties) {
  625. var sparkLineColor = '';
  626. if (conditionalFormatting && conditionalFormatting.color) {
  627. sparkLineColor = conditionalFormatting.color;
  628. } else if (baseProperties.valueColor) {
  629. var hexColor = this._colorsService.getHexColorFromDashboardColorSet(baseProperties.valueColor);
  630. if (hexColor !== 'transparent') {
  631. sparkLineColor = hexColor;
  632. }
  633. }
  634. if (this._sparkLineView) {
  635. if (renderInfo.refresh && renderInfo.refresh.data) {
  636. // Query data, or color has changed, need to recreate the sparkline
  637. this._removeSparkLine();
  638. } else if (this.resizing) {
  639. this._sparkLineView.resize();
  640. }
  641. }
  642. var sparklineColorChanged = sparkLineColor !== this._cachedSparkLineColor;
  643. this._updateCachedSparkLineColor(sparkLineColor);
  644. if (!this._sparkLineView) {
  645. var sparklineQueryResults = new QueryResults();
  646. sparklineQueryResults.addResult(renderInfo.data.getResult('sparkline'));
  647. this._sparkLineView = this._createSparkLine(sparklineQueryResults.getAPI());
  648. this._sparkLineView.render();
  649. } else if (sparklineColorChanged) {
  650. //If we already have a sparkline and there was a color change. Update the color property of the sparkline.
  651. this._sparkLineView.widget.updateVisProperties(this._getSparklineColorProperty());
  652. }
  653. return Promise.resolve();
  654. },
  655. _getSparklineContainer: function _getSparklineContainer() {
  656. return this.$el.find('.kpi-widget-sparkline');
  657. },
  658. _addSlotDef: function _addSlotDef(spec, slotName, slot) {
  659. var slotDef = {
  660. 'name': slotName,
  661. 'dataItems': []
  662. };
  663. slot.getDataItemList().forEach(function (dataItem) {
  664. var dataItemDef = {
  665. id: dataItem.getId(),
  666. itemId: dataItem.getColumnId()
  667. };
  668. if (dataItem.getSort()) {
  669. dataItemDef.selection = [{
  670. operation: 'order',
  671. sort: dataItem.getSort()
  672. }];
  673. }
  674. spec.data.dataViews[0].dataItems.push(dataItemDef);
  675. slotDef.dataItems.push(dataItem.getId());
  676. });
  677. spec.slotmapping.slots.push(slotDef);
  678. },
  679. _getSparklineColorProperty: function _getSparklineColorProperty() {
  680. return {
  681. id: 'colors.series',
  682. value: this._cachedSparkLineColor
  683. };
  684. },
  685. _updateCachedSparkLineColor: function _updateCachedSparkLineColor(color) {
  686. this._cachedSparkLineColor = color;
  687. var container = this._getSparklineContainer();
  688. if (this._cachedSparkLineColor) {
  689. container.removeClass('defaultColors');
  690. } else {
  691. container.addClass('defaultColors'); //When no explicit color is set, use CSS to override the line color to match the default value color.
  692. }
  693. },
  694. _buildSparklineSpec: function _buildSparklineSpec() {
  695. var spec = JSON.parse(KPISparklineSpec);
  696. if (this._cachedSparkLineColor) {
  697. spec.properties.push(this._getSparklineColorProperty());
  698. }
  699. spec.data = {
  700. 'dataViews': [{
  701. modelRef: this._visualizationAPI.getDataSource().getId(),
  702. dataItems: []
  703. }]
  704. };
  705. spec.slotmapping.slots = [];
  706. this._addSlotDef(spec, 'categories', this._getSparklineTimeSlot());
  707. this._addSlotDef(spec, 'values', this._getSparklineActualSlot());
  708. var localFilters = this._visualizationAPI.getLocalFilters();
  709. spec.localFilters = localFilters.getFilterList();
  710. return spec;
  711. },
  712. _createSparkLine: function _createSparkLine(queryResults) {
  713. var spec = this._buildSparklineSpec();
  714. var container = this._getSparklineContainer();
  715. var options = {
  716. enableHover: true,
  717. enableClick: false,
  718. enablePanAndZoom: false,
  719. tabNavigation: false,
  720. runtimeEnv: {
  721. dashboardApi: this.dashboardApi
  722. },
  723. node: container[0],
  724. spec: spec,
  725. queryResults: queryResults,
  726. managesOwnQueries: true
  727. };
  728. return this._kpiViewFactory.createSparklineView(options);
  729. },
  730. _isNewConditionalFormatFeature: function _isNewConditionalFormatFeature() {
  731. return !this.dashboardApi.getGlassCoreSvc('.FeatureChecker').checkValue('dashboard', 'condFormat', 'disabled');
  732. }
  733. });
  734. return KpiView;
  735. });
  736. //# sourceMappingURL=KpiView.js.map