editablemultiselect.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. /**
  6. * @deprecated since version 2.2.0
  7. */
  8. /* global EditableMultiselect */
  9. /* eslint-disable strict */
  10. define([
  11. 'jquery',
  12. 'Magento_Ui/js/modal/alert',
  13. 'Magento_Ui/js/modal/confirm',
  14. 'jquery/editableMultiselect/js/jquery.editable',
  15. 'jquery/editableMultiselect/js/jquery.multiselect'
  16. ], function ($, alert, confirm) {
  17. /**
  18. * Editable multiselect wrapper for multiselects
  19. * This class is defined in global scope ('var' is not needed).
  20. *
  21. * @param {Object} settings - settings object.
  22. * @param {String} settings.add_button_caption - caption of the 'Add New Value' button
  23. * @param {String} settings.new_url - URL to which new request has to be submitted
  24. * @param {String} settings.save_url - URL to which save request has to be submitted
  25. * @param {String} settings.delete_url - URL to which delete request has to be submitted
  26. * @param {String} settings.delete_confirm_message - confirmation message that is shown to user during
  27. * delete operation
  28. * @param {String} settings.target_select_id - HTML ID of target select element
  29. * @param {Hash} settings.submit_data - extra parameters to send with new/edit/delete requests
  30. * @param {String} settings.entity_value_name - name of the request parameter that represents select option text
  31. * @param {String} settings.entity_id_name - name of the request parameter that represents select option value
  32. * @param {Boolean} settings.is_entry_editable - flag that shows if user can add/edit/remove data
  33. *
  34. * @constructor
  35. */
  36. window.EditableMultiselect = function (settings) {
  37. this.settings = settings || {};
  38. this.addButtonCaption = this.settings['add_button_caption'] || 'Add new value';
  39. this.newUrl = this.settings['new_url'];
  40. this.saveUrl = this.settings['save_url'];
  41. this.deleteUrl = this.settings['delete_url'];
  42. this.deleteConfirmMessage = this.settings['delete_confirm_message'];
  43. this.targetSelectId = this.settings['target_select_id'];
  44. this.submitData = this.settings['submit_data'] || {};
  45. this.entityIdName = this.settings['entity_id_name'] || 'entity_id';
  46. this.entityValueName = this.settings['entity_value_name'] || 'entity_value';
  47. this.isEntityEditable = this.settings['is_entity_editable'] || false;
  48. /**
  49. * Initialize editable multiselect (make it visible in UI)
  50. */
  51. EditableMultiselect.prototype.init = function () {
  52. var self = this,
  53. mselectOptions = {
  54. addText: this.addButtonCaption,
  55. /**
  56. * @param {*} value
  57. * @param {*} options
  58. */
  59. mselectInputSubmitCallback: function (value, options) {
  60. self.createEntity(value, options);
  61. }
  62. },
  63. mselectList;
  64. if (!this.isEntityEditable) {
  65. // Override default layout of editable multiselect
  66. mselectOptions.layout = '<section class="block %mselectListClass%">' +
  67. '<div class="block-content"><div class="%mselectItemsWrapperClass%">' +
  68. '%items%' +
  69. '</div></div>' +
  70. '<div class="%mselectInputContainerClass%">' +
  71. '<input type="text" class="%mselectInputClass%" title="%inputTitle%"/>' +
  72. '<span class="%mselectButtonCancelClass%" title="%cancelText%"></span>' +
  73. '<span class="%mselectButtonSaveClass%" title="Add"></span>' +
  74. '</div>' +
  75. '</section>';
  76. }
  77. $('#' + this.targetSelectId).multiselect(mselectOptions);
  78. // Make multiselect editable if needed
  79. if (this.isEntityEditable) {
  80. this.makeMultiselectEditable();
  81. // Root element of HTML markup that represents select element in UI
  82. mselectList = $('#' + this.targetSelectId).next();
  83. this.attachEventsToControls(mselectList);
  84. }
  85. };
  86. /**
  87. * Attach required event handlers to control elements of editable multiselect
  88. *
  89. * @param {Object} mselectList
  90. */
  91. EditableMultiselect.prototype.attachEventsToControls = function (mselectList) {
  92. mselectList.on('click.mselect-delete', '.mselect-delete', {
  93. container: this
  94. }, function (event) {
  95. // Pass the clicked button to container
  96. event.data.container.deleteEntity({
  97. 'delete_button': this
  98. });
  99. });
  100. mselectList.on('click.mselect-checked', '.mselect-list-item input', {
  101. container: this
  102. }, function (event) {
  103. var el = $(this),
  104. checkedClassName = 'mselect-checked';
  105. el[el.is(':checked') ? 'addClass' : 'removeClass'](checkedClassName);
  106. event.data.container.makeMultiselectEditable();
  107. });
  108. mselectList.on('click.mselect-edit', '.mselect-edit', {
  109. container: this
  110. }, function (event) {
  111. event.data.container.makeMultiselectEditable();
  112. $(this).parent().find('label span').trigger('dblclick');
  113. });
  114. };
  115. /**
  116. * Make multiselect editable
  117. */
  118. EditableMultiselect.prototype.makeMultiselectEditable = function () {
  119. var entityIdName = this.entityIdName,
  120. entityValueName = this.entityValueName,
  121. selectList = $('#' + this.targetSelectId).next();
  122. selectList.find('.mselect-list-item:not(.mselect-list-item-not-editable) label span').editable(this.saveUrl,
  123. {
  124. type: 'text',
  125. submit: '<button class="mselect-save" title="Save" type="submit" />',
  126. cancel: '<span class="mselect-cancel" title="Cancel"></span>',
  127. event: 'dblclick',
  128. placeholder: '',
  129. /**
  130. * Is checked.
  131. */
  132. isChecked: function () {
  133. var that = $(this),
  134. checked;
  135. if (!that.closest('.mselect-list-item').hasClass('mselect-disabled')) {
  136. checked = that.parent().find('[type=checkbox]').prop('disabled');
  137. that.parent().find('[type=checkbox]').prop({
  138. disabled: !checked
  139. });
  140. }
  141. },
  142. /**
  143. * @param {*} value
  144. * @param {Object} sett
  145. * @return {*}
  146. */
  147. data: function (value, sett) {
  148. var retval;
  149. sett.isChecked.apply(this, [sett]);
  150. if (typeof value === 'string') {
  151. retval = value.unescapeHTML();
  152. return retval;
  153. }
  154. return value;
  155. },
  156. submitdata: this.submitData,
  157. onblur: 'cancel',
  158. name: entityValueName,
  159. ajaxoptions: {
  160. dataType: 'json'
  161. },
  162. /**
  163. * @param {Object} sett
  164. * @param {*} original
  165. */
  166. onsubmit: function (sett, original) {
  167. var select = $(original).closest('.mselect-list').prev(),
  168. current = $(original).closest('.mselect-list-item').index(),
  169. entityId = select.find('option').eq(current).val(),
  170. entityInfo = {};
  171. entityInfo[entityIdName] = entityId;
  172. sett.submitdata = $.extend(sett.submitdata || {}, entityInfo);
  173. },
  174. /**
  175. * @param {Object} result
  176. * @param {Object} sett
  177. */
  178. callback: function (result, sett) {
  179. var select, current;
  180. sett.isChecked.apply(this, [sett]);
  181. select = $(this).closest('.mselect-list').prev();
  182. current = $(this).closest('.mselect-list-item').index();
  183. if (result.success) {
  184. if (typeof result[entityValueName] === 'string') {
  185. select.find('option').eq(current).val(result[entityIdName]).text(result[entityValueName]);
  186. $(this).html(result[entityValueName].escapeHTML());
  187. }
  188. } else {
  189. alert({
  190. content: result['error_message']
  191. });
  192. }
  193. }
  194. });
  195. };
  196. /**
  197. * Callback function that is called when admin adds new value to select
  198. *
  199. * @param {*} value
  200. * @param {Object} options - list of settings of multiselect
  201. */
  202. EditableMultiselect.prototype.createEntity = function (value, options) {
  203. var select, entityIdName, entityValueName, entityInfo, postData, ajaxOptions;
  204. if (!value) {
  205. return;
  206. }
  207. select = $('#' + this.targetSelectId),
  208. entityIdName = this.entityIdName,
  209. entityValueName = this.entityValueName,
  210. entityInfo = {};
  211. entityInfo[entityIdName] = null;
  212. entityInfo[entityValueName] = value;
  213. postData = $.extend(entityInfo, this.submitData);
  214. ajaxOptions = {
  215. type: 'POST',
  216. data: postData,
  217. dataType: 'json',
  218. url: this.newUrl,
  219. /**
  220. * @param {Object} result
  221. */
  222. success: function (result) {
  223. var resultEntityValueName, mselectItemHtml, sectionBlock, itemsWrapper, inputSelector;
  224. if (result.success) {
  225. resultEntityValueName = '';
  226. if (typeof result[entityValueName] === 'string') {
  227. resultEntityValueName = result[entityValueName].escapeHTML();
  228. } else {
  229. resultEntityValueName = result[entityValueName];
  230. }
  231. // Add item to initial select element
  232. select.append('<option value="' + result[entityIdName] + '" selected="selected">' +
  233. resultEntityValueName + '</option>');
  234. // Add editable multiselect item
  235. mselectItemHtml = $(options.item.replace(/%value%|%label%/gi, resultEntityValueName)
  236. .replace(/%mselectDisabledClass%|%iseditable%|%isremovable%/gi, '')
  237. .replace(/%mselectListItemClass%/gi, options.mselectListItemClass))
  238. .find('[type=checkbox]')
  239. .attr('checked', true)
  240. .addClass(options.mselectCheckedClass)
  241. .end();
  242. sectionBlock = select.nextAll('section.block:first');
  243. itemsWrapper = sectionBlock.find('.' + options.mselectItemsWrapperClass + '');
  244. if (itemsWrapper.children('.' + options.mselectListItemClass + '').length) {
  245. itemsWrapper.children('.' + options.mselectListItemClass + ':last').after(mselectItemHtml);
  246. } else {
  247. itemsWrapper.prepend(mselectItemHtml);
  248. }
  249. // Trigger blur event on input field, that is used to add new value, to hide it
  250. inputSelector = '.' + options.mselectInputContainerClass + ' [type=text].' +
  251. options.mselectInputClass + '';
  252. sectionBlock.find(inputSelector).trigger('blur');
  253. } else {
  254. alert({
  255. content: result['error_message']
  256. });
  257. }
  258. }
  259. };
  260. $.ajax(ajaxOptions);
  261. };
  262. /**
  263. * Callback function that is called when user tries to delete value from select
  264. *
  265. * @param {Object} options
  266. */
  267. EditableMultiselect.prototype.deleteEntity = function (options) {
  268. var self = this;
  269. if (options['delete_button']) {
  270. confirm({
  271. content: this.deleteConfirmMessage,
  272. actions: {
  273. /**
  274. * Confirm.
  275. */
  276. confirm: function () {
  277. // Button that has been clicked
  278. var deleteButton = $(options['delete_button']),
  279. index = deleteButton.parent().index(),
  280. select = deleteButton.closest('.mselect-list').prev(),
  281. entityId = select.find('option').eq(index).val(),
  282. entityInfo = {},
  283. postData, ajaxOptions;
  284. entityInfo[self.entityIdName] = entityId;
  285. postData = $.extend(entityInfo, self.submitData);
  286. ajaxOptions = {
  287. type: 'POST',
  288. data: postData,
  289. dataType: 'json',
  290. url: self.deleteUrl,
  291. /**
  292. * @param {Object} result
  293. */
  294. success: function (result) {
  295. if (result.success) {
  296. deleteButton.parent().remove();
  297. select.find('option').eq(index).remove();
  298. } else {
  299. alert({
  300. content: result['error_message']
  301. });
  302. }
  303. }
  304. };
  305. $.ajax(ajaxOptions);
  306. }
  307. }
  308. });
  309. }
  310. };
  311. };
  312. });