bst-edittable.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * BSTable
  3. * @description Javascript (JQuery) library to make bootstrap tables editable. Inspired by Tito Hinostroza's library Bootstable. BSTable Copyright (C) 2020 Thomas Rokicki
  4. *
  5. * @version 1.0
  6. * @author Thomas Rokicki (CraftingGamerTom), Tito Hinostroza (t-edson)
  7. */
  8. "use strict";
  9. /** @class BSTable class that represents an editable bootstrap table. */
  10. class BSTable {
  11. /**
  12. * Creates an instance of BSTable.
  13. *
  14. * @constructor
  15. * @author: Thomas Rokicki (CraftingGamerTom)
  16. * @param {tableId} tableId The id of the table to make editable.
  17. * @param {options} options The desired options for the editable table.
  18. */
  19. constructor(tableId, options) {
  20. var defaults = {
  21. editableColumns: null, // Index to editable columns. If null all td will be editable. Ex.: "1,2,3,4,5"
  22. $addButton: null, // Jquery object of "Add" button
  23. onEdit: function() {}, // Called after editing (accept button clicked)
  24. onBeforeDelete: function() {}, // Called before deletion
  25. onDelete: function() {}, // Called after deletion
  26. onAdd: function() {}, // Called when added a new row
  27. advanced: { // Do not override advanced unless you know what youre doing
  28. columnLabel: 'Actions',
  29. buttonHTML: `<div class="btn-list">
  30. <button id="bEdit" type="button" class="btn btn-sm btn-primary">
  31. <span class="fe fe-edit" > </span>
  32. </button>
  33. <button id="bDel" type="button" class="btn btn-sm btn-danger">
  34. <span class="fe fe-trash-2" > </span>
  35. </button>
  36. <button id="bAcep" type="button" class="btn btn-sm btn-primary" style="display:none;">
  37. <span class="fe fe-check-circle" > </span>
  38. </button>
  39. <button id="bCanc" type="button" class="btn btn-sm btn-danger" style="display:none;">
  40. <span class="fe fe-x-circle" > </span>
  41. </button>
  42. </div>`
  43. }
  44. };
  45. this.table = $('#' + tableId);
  46. this.options = $.extend(true, defaults, options);
  47. /** @private */
  48. this.actionsColumnHTML = '<td name="bstable-actions">' + this.options.advanced.buttonHTML + '</td>';
  49. //Process "editableColumns" parameter. Sets the columns that will be editable
  50. if (this.options.editableColumns != null) {
  51. // console.log("[DEBUG] editable columns: ", this.options.editableColumns);
  52. //Extract felds
  53. this.options.editableColumns = this.options.editableColumns.split(',');
  54. }
  55. }
  56. // --------------------------------------------------
  57. // -- Public Functions
  58. // --------------------------------------------------
  59. /**
  60. * Initializes the editable table. Creates the actions column.
  61. * @since 1.0.0
  62. */
  63. init() {
  64. this.table.find('thead tr').append('<th name="bstable-actions">' + this.options.advanced.columnLabel + '</th>'); // Append column to header
  65. this.table.find('tbody tr').append(this.actionsColumnHTML);
  66. this._addOnClickEventsToActions(); // Add onclick events to each action button in all rows
  67. // Process "addButton" parameter
  68. if (this.options.$addButton != null) {
  69. let _this = this;
  70. // Add a managed onclick event to the button
  71. this.options.$addButton.click(function() {
  72. _this._actionAddRow();
  73. });
  74. }
  75. }
  76. /**
  77. * Destroys the editable table. Removes the actions column.
  78. * @since 1.0.0
  79. */
  80. destroy() {
  81. this.table.find('th[name="bstable-actions"]').remove(); //remove header
  82. this.table.find('td[name="bstable-actions"]').remove(); //remove body rows
  83. }
  84. /**
  85. * Refreshes the editable table.
  86. *
  87. * Literally just removes and initializes the editable table again, wrapped in one function.
  88. * @since 1.0.0
  89. */
  90. refresh() {
  91. this.destroy();
  92. this.init();
  93. }
  94. // --------------------------------------------------
  95. // -- 'Static' Functions
  96. // --------------------------------------------------
  97. /**
  98. * Returns whether the provided row is currently being edited.
  99. *
  100. * @param {Object} row
  101. * @return {boolean} true if row is currently being edited.
  102. * @since 1.0.0
  103. */
  104. currentlyEditingRow($currentRow) {
  105. // Check if the row is currently being edited
  106. if ($currentRow.attr('data-status') == 'editing') {
  107. return true;
  108. } else {
  109. return false;
  110. }
  111. }
  112. // --------------------------------------------------
  113. // -- Button Mode Functions
  114. // --------------------------------------------------
  115. _actionsModeNormal(button) {
  116. $(button).parent().find('#bAcep').hide();
  117. $(button).parent().find('#bCanc').hide();
  118. $(button).parent().find('#bEdit').show();
  119. $(button).parent().find('#bDel').show();
  120. let $currentRow = $(button).parents('tr'); // get the row
  121. $currentRow.attr('data-status', ''); // remove editing status
  122. }
  123. _actionsModeEdit(button) {
  124. $(button).parent().find('#bAcep').show();
  125. $(button).parent().find('#bCanc').show();
  126. $(button).parent().find('#bEdit').hide();
  127. $(button).parent().find('#bDel').hide();
  128. let $currentRow = $(button).parents('tr'); // get the row
  129. $currentRow.attr('data-status', 'editing'); // indicate the editing status
  130. }
  131. // --------------------------------------------------
  132. // -- Private Event Functions
  133. // --------------------------------------------------
  134. _rowEdit(button) {
  135. // Indicate user is editing the row
  136. let $currentRow = $(button).parents('tr'); // access the row
  137. console.log($currentRow);
  138. let $cols = $currentRow.find('td'); // read rows
  139. console.log($cols);
  140. if (this.currentlyEditingRow($currentRow)) return; // not currently editing, return
  141. //Pone en modo de edición
  142. this._modifyEachColumn(this.options.editableColumns, $cols, function($td) { // modify each column
  143. let content = $td.html(); // read content
  144. console.log(content);
  145. let div = '<div style="display: none;">' + content + '</div>'; // hide content (save for later use)
  146. let input = '<input class="form-control input-sm" data-original-value="' + content + '" value="' + content + '">';
  147. $td.html(div + input); // set content
  148. });
  149. this._actionsModeEdit(button);
  150. }
  151. _rowDelete(button) {
  152. // Remove the row
  153. let $currentRow = $(button).parents('tr'); // access the row
  154. this.options.onBeforeDelete($currentRow);
  155. $currentRow.remove();
  156. this.options.onDelete();
  157. }
  158. _rowAccept(button) {
  159. // Accept the changes to the row
  160. let $currentRow = $(button).parents('tr'); // access the row
  161. console.log($currentRow);
  162. let $cols = $currentRow.find('td'); // read fields
  163. if (!this.currentlyEditingRow($currentRow)) return; // not currently editing, return
  164. // Finish editing the row & save edits
  165. this._modifyEachColumn(this.options.editableColumns, $cols, function($td) { // modify each column
  166. let cont = $td.find('input').val(); // read through each input
  167. $td.html(cont); // set the content and remove the input fields
  168. });
  169. this._actionsModeNormal(button);
  170. this.options.onEdit($currentRow[0]);
  171. }
  172. _rowCancel(button) {
  173. // Reject the changes
  174. let $currentRow = $(button).parents('tr'); // access the row
  175. let $cols = $currentRow.find('td'); // read fields
  176. if (!this.currentlyEditingRow($currentRow)) return; // not currently editing, return
  177. // Finish editing the row & delete changes
  178. this._modifyEachColumn(this.options.editableColumns, $cols, function($td) { // modify each column
  179. let cont = $td.find('div').html(); // read div content
  180. $td.html(cont); // set the content and remove the input fields
  181. });
  182. this._actionsModeNormal(button);
  183. }
  184. _actionAddRow() {
  185. // Add row to this table
  186. let $allRows = this.table.find('tbody tr');
  187. if ($allRows.length == 0) { // there are no rows. we must create them
  188. let $currentRow = this.table.find('thead tr'); // find header
  189. let $cols = $currentRow.find('th'); // read each header field
  190. // create the new row
  191. let newColumnHTML = '';
  192. $cols.each(function() {
  193. let column = this; // Inner function this (column object)
  194. if ($(column).attr('name') == 'bstable-actions') {
  195. newColumnHTML = newColumnHTML + actionsColumnHTML; // add action buttons
  196. } else {
  197. newColumnHTML = newColumnHTML + '<td></td>';
  198. }
  199. });
  200. this.table.find('tbody').append('<tr>' + newColumnHTML + '</tr>');
  201. } else { // there are rows in the table. We will clone the last row
  202. let $lastRow = this.table.find('tr:last');
  203. $lastRow.clone().appendTo($lastRow.parent());
  204. $lastRow = this.table.find('tr:last');
  205. let $cols = $lastRow.find('td'); //lee campos
  206. $cols.each(function() {
  207. let column = this; // Inner function this (column object)
  208. if ($(column).attr('name') == 'bstable-actions') {
  209. // action buttons column. change nothing
  210. } else {
  211. $(column).html(''); // clear the text
  212. }
  213. });
  214. }
  215. this._addOnClickEventsToActions(); // Add onclick events to each action button in all rows
  216. this.options.onAdd();
  217. }
  218. // --------------------------------------------------
  219. // -- Helper Functions
  220. // --------------------------------------------------
  221. _modifyEachColumn($editableColumns, $cols, howToModify) {
  222. // Go through each editable field and perform the howToModifyFunction function
  223. let n = 0;
  224. $cols.each(function() {
  225. n++;
  226. if ($(this).attr('name') == 'bstable-actions') return; // exclude the actions column
  227. if (!isEditableColumn(n - 1)) return; // Check if the column is editable
  228. howToModify($(this)); // If editable, call the provided function
  229. });
  230. // console.log("Number of modified columns: " + n); // debug log
  231. function isEditableColumn(columnIndex) {
  232. // Indicates if the column is editable, based on configuration
  233. if ($editableColumns == null) { // option not defined
  234. return true; // all columns are editable
  235. } else { // option is defined
  236. //console.log('isEditableColumn: ' + columnIndex); // DEBUG
  237. for (let i = 0; i < $editableColumns.length; i++) {
  238. if (columnIndex == $editableColumns[i]) return true;
  239. }
  240. return false; // column not found
  241. }
  242. }
  243. }
  244. _addOnClickEventsToActions() {
  245. let _this = this;
  246. // Add onclick events to each action button
  247. this.table.find('tbody tr #bEdit').each(function() {
  248. let button = this;
  249. button.onclick = function() { _this._rowEdit(button) }
  250. });
  251. this.table.find('tbody tr #bDel').each(function() {
  252. let button = this;
  253. button.onclick = function() { _this._rowDelete(button) }
  254. });
  255. this.table.find('tbody tr #bAcep').each(function() {
  256. let button = this;
  257. button.onclick = function() { _this._rowAccept(button) }
  258. });
  259. this.table.find('tbody tr #bCanc').each(function() {
  260. let button = this;
  261. button.onclick = function() { _this._rowCancel(button) }
  262. });
  263. }
  264. // --------------------------------------------------
  265. // -- Conversion Functions
  266. // --------------------------------------------------
  267. convertTableToCSV(separator) {
  268. // Convert table to CSV
  269. let _this = this;
  270. let $currentRowValues = '';
  271. let tableValues = '';
  272. _this.table.find('tbody tr').each(function() {
  273. // force edits to complete if in progress
  274. if (_this.currentlyEditingRow($(this))) {
  275. $(this).find('#bAcep').click(); // Force Accept Edit
  276. }
  277. let $cols = $(this).find('td'); // read columns
  278. $currentRowValues = '';
  279. $cols.each(function() {
  280. if ($(this).attr('name') == 'bstable-actions') {
  281. // buttons column - do nothing
  282. } else {
  283. $currentRowValues = $currentRowValues + $(this).html() + separator;
  284. }
  285. });
  286. if ($currentRowValues != '') {
  287. $currentRowValues = $currentRowValues.substr(0, $currentRowValues.length - separator.length);
  288. }
  289. tableValues = tableValues + $currentRowValues + '\n';
  290. });
  291. return tableValues;
  292. }
  293. }