dataSource.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import { getProperty } from './helpers/object';
  2. import { arrayEach } from './helpers/array';
  3. import { rangeEach } from './helpers/number';
  4. /**
  5. * @class DataSource
  6. * @private
  7. */
  8. class DataSource {
  9. constructor(hotInstance, dataSource = []) {
  10. /**
  11. * Instance of Handsontable.
  12. *
  13. * @type {Handsontable}
  14. */
  15. this.hot = hotInstance;
  16. /**
  17. * Data source
  18. *
  19. * @type {Array}
  20. */
  21. this.data = dataSource;
  22. /**
  23. * Type of data source.
  24. *
  25. * @type {String}
  26. * @default 'array'
  27. */
  28. this.dataType = 'array';
  29. this.colToProp = () => {};
  30. this.propToCol = () => {};
  31. }
  32. /**
  33. * Get all data.
  34. *
  35. * @param {Boolean} [toArray=false] If `true` return source data as an array of arrays even when source data was provided
  36. * in another format.
  37. * @returns {Array}
  38. */
  39. getData(toArray = false) {
  40. let result = this.data;
  41. if (toArray) {
  42. result = this.getByRange(
  43. { row: 0, col: 0 },
  44. { row: Math.max(this.countRows() - 1, 0), col: Math.max(this.countColumns() - 1, 0) },
  45. true
  46. );
  47. }
  48. return result;
  49. }
  50. /**
  51. * Set new data source.
  52. *
  53. * @param data {Array}
  54. */
  55. setData(data) {
  56. this.data = data;
  57. }
  58. /**
  59. * Returns array of column values from the data source. `column` is the index of the row in the data source.
  60. *
  61. * @param {Number} column Visual column index.
  62. * @returns {Array}
  63. */
  64. getAtColumn(column) {
  65. const result = [];
  66. arrayEach(this.data, (row) => {
  67. const property = this.colToProp(column);
  68. let value;
  69. if (typeof property === 'string') {
  70. value = getProperty(row, property);
  71. } else if (typeof property === 'function') {
  72. value = property(row);
  73. } else {
  74. value = row[property];
  75. }
  76. result.push(value);
  77. });
  78. return result;
  79. }
  80. /**
  81. * Returns a single row of the data (array or object, depending on what you have). `row` is the index of the row in the data source.
  82. *
  83. * @param {Number} row Physical row index.
  84. * @returns {Array|Object}
  85. */
  86. getAtRow(row) {
  87. return this.data[row];
  88. }
  89. /**
  90. * Returns a single value from the data.
  91. *
  92. * @param {Number} row Physical row index.
  93. * @param {Number} column Visual column index.
  94. * @returns {*}
  95. */
  96. getAtCell(row, column) {
  97. let result = null;
  98. const modifyRowData = this.hot.runHooks('modifyRowData', row);
  99. const dataRow = isNaN(modifyRowData) ? modifyRowData : this.data[row];
  100. if (dataRow) {
  101. const prop = this.colToProp(column);
  102. if (typeof prop === 'string') {
  103. result = getProperty(dataRow, prop);
  104. } else if (typeof prop === 'function') {
  105. result = prop(this.data.slice(row, row + 1)[0]);
  106. } else {
  107. result = dataRow[prop];
  108. }
  109. }
  110. return result;
  111. }
  112. /**
  113. * Returns source data by passed range.
  114. *
  115. * @param {Object} start Object with physical `row` and `col` keys (or visual column index, if data type is an array of objects).
  116. * @param {Object} end Object with physical `row` and `col` keys (or visual column index, if data type is an array of objects).
  117. * @param {Boolean} [toArray=false] If `true` return source data as an array of arrays even when source data was provided
  118. * in another format.
  119. * @returns {Array}
  120. */
  121. getByRange(start, end, toArray = false) {
  122. const startRow = Math.min(start.row, end.row);
  123. const startCol = Math.min(start.col, end.col);
  124. const endRow = Math.max(start.row, end.row);
  125. const endCol = Math.max(start.col, end.col);
  126. const result = [];
  127. rangeEach(startRow, endRow, (currentRow) => {
  128. const row = this.getAtRow(currentRow);
  129. let newRow;
  130. if (this.dataType === 'array') {
  131. newRow = row.slice(startCol, endCol + 1);
  132. } else if (this.dataType === 'object') {
  133. newRow = toArray ? [] : {};
  134. rangeEach(startCol, endCol, (column) => {
  135. const prop = this.colToProp(column);
  136. if (toArray) {
  137. newRow.push(row[prop]);
  138. } else {
  139. newRow[prop] = row[prop];
  140. }
  141. });
  142. }
  143. result.push(newRow);
  144. });
  145. return result;
  146. }
  147. /**
  148. * Count number of rows.
  149. *
  150. * @returns {Number}
  151. */
  152. countRows() {
  153. return Array.isArray(this.data) ? this.data.length : 0;
  154. }
  155. /**
  156. * Count number of columns.
  157. *
  158. * @returns {Number}
  159. */
  160. countColumns() {
  161. let result = 0;
  162. if (Array.isArray(this.data)) {
  163. if (this.dataType === 'array') {
  164. result = this.data[0].length;
  165. } else if (this.dataType === 'object') {
  166. result = Object.keys(this.data[0]).length;
  167. }
  168. }
  169. return result;
  170. }
  171. /**
  172. * Destroy instance.
  173. */
  174. destroy() {
  175. this.data = null;
  176. this.hot = null;
  177. }
  178. }
  179. export default DataSource;