SearchAndSelectPromptView.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2019
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. */
  7. define(['../../../../lib/@waca/core-client/js/core-client/ui/AccessibleView', '../../../../widgets/livewidget/nls/StringResources', 'text!./templates/BasePromptView.html', 'text!./templates/SearchAndSelectContent.html', 'jquery', 'underscore', 'doT'], function (View, stringResources, BasePromptViewTemplate, SearchAndSelectContentTemplate, $, _, dot) {
  8. 'use strict';
  9. var SearchAndSelectPromptView = View.extend({
  10. events: {
  11. 'primaryaction .itemRow': '_selectItem',
  12. 'keydown .itemRow': '_moveFocus',
  13. 'primaryaction .searchWrapper .wfg_remove': '_clearSearch',
  14. 'keyup .search': 'searchTypeDelay'
  15. },
  16. _selectedPromptValues: null,
  17. _selectedTab: null,
  18. init: function init(options) {
  19. options.width = '400px';
  20. SearchAndSelectPromptView.inherited('init', this, arguments);
  21. _.extend(this, options);
  22. if (this.queryResult) {
  23. this._getQueryResultValues(this.queryResult);
  24. }
  25. // Default specs
  26. this._selectedPromptValues = {
  27. refId: options.columnId,
  28. columnId: options.columnId,
  29. values: this.defaultValues ? this.defaultValues : [],
  30. ui: 'picklist'
  31. };
  32. },
  33. render: function render() {
  34. var sBasePromptViewHtml = dot.template(BasePromptViewTemplate)({
  35. promptModuleName: this.promptModuleName
  36. });
  37. this.$el.addClass('promptDialogContainer promptViewDialog').width(this.width).append(sBasePromptViewHtml);
  38. this.$('.content').html(SearchAndSelectContentTemplate);
  39. this.$el.attr('role', 'region');
  40. this.$el.attr('aria-label', this.viewTitle);
  41. // Search Field
  42. this.$('.search').attr('placeholder', stringResources.get('search')).attr('aria-label', stringResources.get('search'));
  43. this._populateRows();
  44. var axisLabelList = this.$('.selectItems');
  45. axisLabelList.attr('aria-label', this.$('.title').text());
  46. return this;
  47. },
  48. _moveFocus: function _moveFocus(evt) {
  49. var $target = $(evt.currentTarget);
  50. if (evt.keyCode === 37 || evt.keyCode === 38) {
  51. //left/up
  52. this._moveToItem($target, $target.prev());
  53. } else if (evt.keyCode === 39 || evt.keyCode === 40) {
  54. //right/down
  55. this._moveToItem($target, $target.next());
  56. }
  57. },
  58. _moveToItem: function _moveToItem($current, $next) {
  59. $current.attr('tabindex', '-1');
  60. $next.focus();
  61. $next.attr('tabindex', '0');
  62. },
  63. setFocus: function setFocus() {
  64. this.$('.search').focus();
  65. },
  66. _clearSearch: function _clearSearch() {
  67. this.$('.search').val('');
  68. return this.searchData();
  69. },
  70. _serverResponse: function _serverResponse(resultData) {
  71. this._getQueryResultValues(resultData);
  72. this._populateRows();
  73. },
  74. _getQueryResultValues: function _getQueryResultValues(queryResult) {
  75. var resultItem = queryResult.getResultItemList()[0];
  76. var aResults = [];
  77. for (var rowIndex = 0; rowIndex < resultItem.getRowCount(); rowIndex++) {
  78. // Only one column and one tuple part for singleItemQuery result.
  79. aResults.push.apply(aResults, resultItem.getValue(rowIndex));
  80. }
  81. this.promptValues = aResults;
  82. },
  83. _getRowValue: function _getRowValue($el) {
  84. var rowValue = JSON.parse(_.unescape($el.attr('data')));
  85. return rowValue === undefined ? null : rowValue;
  86. },
  87. _highlightSelected: function _highlightSelected() {
  88. var selectRows = this.$('.itemRow');
  89. var aExistingSelectionKeys = _.pluck(this._selectedPromptValues.values, 'value');
  90. _.each(selectRows, function (item) {
  91. var $item = $(item);
  92. var data = this._getRowValue($item);
  93. var bInclude = true;
  94. if (_.contains(aExistingSelectionKeys, data.value)) {
  95. $item.toggleClass('exclude', !bInclude).toggleClass('include', bInclude);
  96. $item.find('.check').toggleClass('wfg_checkmark', bInclude);
  97. $item.find('.text').toggleClass('bold', bInclude);
  98. $item.attr('aria-selected', 'true');
  99. } else {
  100. $item.removeClass('include exclude');
  101. $item.find('.check').toggleClass('wfg_checkmark', !bInclude);
  102. $item.find('.text').toggleClass('bold', !bInclude);
  103. $item.attr('aria-selected', 'false');
  104. }
  105. }.bind(this));
  106. },
  107. _populateRows: function _populateRows() {
  108. var items = this.$('.selectItems').empty();
  109. if (this.promptValues.length > 0) {
  110. _.each(this.promptValues, function (dataItem) {
  111. var sRowLabel = dataItem === null ? stringResources.get('nullValueLabel') : dataItem.label;
  112. var item = $('<div></div>').addClass('itemRow').attr('data', _.escape(JSON.stringify(dataItem)));
  113. $('<div></div>').addClass('check wfg_checkmark').appendTo(item);
  114. $('<div></div>').addClass('text').text(sRowLabel).appendTo(item);
  115. item.appendTo(items);
  116. }.bind(this));
  117. setTimeout(function () {
  118. if (!items[0].style.height && items.height()) {
  119. // set height to prevent shrinking lists when search.
  120. items.height(items.height());
  121. }
  122. }, 500);
  123. } else {
  124. $('<div></div>').addClass('itemNotAvailable').text(stringResources.get('textFilterItemsNotFound')).appendTo(items);
  125. }
  126. this._highlightSelected();
  127. var listItem = this.$('.itemRow');
  128. listItem.attr('role', 'option');
  129. listItem.attr('aria-selected', 'false');
  130. listItem.attr('tabindex', '-1');
  131. listItem.first().attr('tabindex', '0');
  132. },
  133. _selectItem: function _selectItem(evt) {
  134. evt.stopPropagation();
  135. var target = this._getRowValue($(evt.currentTarget));
  136. var aExistingSelectionKeys = _.pluck(this._selectedPromptValues.values, 'value');
  137. if (_.contains(aExistingSelectionKeys, target.value)) {
  138. this._selectedPromptValues.values = _.filter(this._selectedPromptValues.values, function (promptValue) {
  139. return promptValue.value !== target.value;
  140. }.bind(this));
  141. } else if (this.singleSelect && this._selectedPromptValues.values.length > 0) {
  142. this._selectedPromptValues.values = [target];
  143. } else {
  144. this._selectedPromptValues.values.push(target);
  145. }
  146. this._highlightSelected();
  147. this._enableOk();
  148. },
  149. /**
  150. * @public
  151. * Get prompt values from dialog.
  152. *
  153. */
  154. getPromptValues: function getPromptValues() {
  155. return this._selectedPromptValues.values;
  156. },
  157. // TODO needs to support search filter.
  158. searchData: function searchData() {
  159. var searchTerm = this.$('.search')[0].value;
  160. var isArray = _.isArray(searchTerm);
  161. var searchValues = isArray ? searchTerm : [searchTerm];
  162. var result = void 0;
  163. if (this.getColumnValues) {
  164. result = this.getColumnValues(this.columnId, [], /** no default sort in older releases */null, searchValues).then(this._serverResponse.bind(this));
  165. } else {
  166. result = Promise.resolve();
  167. }
  168. var sRemoveBtnVisibility = searchTerm.length > 0 ? 'visible' : 'hidden';
  169. this.$('.searchWrapper .wfg_remove').css('visibility', sRemoveBtnVisibility);
  170. return result;
  171. },
  172. /**
  173. * Performs search request on a delay. If a new request arrives before the
  174. * search is performed then the previous search is cancelled and the new
  175. * request is set up on a delay.
  176. * @protected
  177. */
  178. searchTypeDelay: function searchTypeDelay() {
  179. // already have search pending so clear
  180. if (this.iTypeDelayTimer) {
  181. window.clearTimeout(this.iTypeDelayTimer);
  182. }
  183. // set up a search for 250ms after a type event stops.
  184. // hard to set a value here, needs to be slow enough to collect multiple key presses
  185. // but not too slow that the delay after typing is noticeable to the end user.
  186. // according to the internets the average person types at 240 characters/minute
  187. this.iTypeDelayTimer = window.setTimeout(this.searchData.bind(this), 250);
  188. },
  189. _enableOk: function _enableOk() {
  190. if (this._selectedPromptValues.values.length > 0) {
  191. this.enableOk(true);
  192. } else {
  193. this.enableOk(false);
  194. }
  195. }
  196. });
  197. return SearchAndSelectPromptView;
  198. });
  199. //# sourceMappingURL=SearchAndSelectPromptView.js.map