checkboxRenderer.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. 'use strict';
  2. exports.__esModule = true;
  3. var _element = require('./../helpers/dom/element');
  4. var _string = require('./../helpers/string');
  5. var _eventManager = require('./../eventManager');
  6. var _eventManager2 = _interopRequireDefault(_eventManager);
  7. var _unicode = require('./../helpers/unicode');
  8. var _function = require('./../helpers/function');
  9. var _event = require('./../helpers/dom/event');
  10. var _index = require('./index');
  11. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  12. var isListeningKeyDownEvent = new WeakMap();
  13. var isCheckboxListenerAdded = new WeakMap();
  14. var BAD_VALUE_CLASS = 'htBadValue';
  15. /**
  16. * Checkbox renderer
  17. *
  18. * @private
  19. * @param {Object} instance Handsontable instance
  20. * @param {Element} TD Table cell where to render
  21. * @param {Number} row
  22. * @param {Number} col
  23. * @param {String|Number} prop Row object property name
  24. * @param value Value to render (remember to escape unsafe HTML before inserting to DOM!)
  25. * @param {Object} cellProperties Cell properties (shared by cell renderer and editor)
  26. */
  27. function checkboxRenderer(instance, TD, row, col, prop, value, cellProperties) {
  28. (0, _index.getRenderer)('base').apply(this, arguments);
  29. var eventManager = registerEvents(instance);
  30. var input = createInput();
  31. var labelOptions = cellProperties.label;
  32. var badValue = false;
  33. if (typeof cellProperties.checkedTemplate === 'undefined') {
  34. cellProperties.checkedTemplate = true;
  35. }
  36. if (typeof cellProperties.uncheckedTemplate === 'undefined') {
  37. cellProperties.uncheckedTemplate = false;
  38. }
  39. (0, _element.empty)(TD); // TODO identify under what circumstances this line can be removed
  40. if (value === cellProperties.checkedTemplate || (0, _string.equalsIgnoreCase)(value, cellProperties.checkedTemplate)) {
  41. input.checked = true;
  42. } else if (value === cellProperties.uncheckedTemplate || (0, _string.equalsIgnoreCase)(value, cellProperties.uncheckedTemplate)) {
  43. input.checked = false;
  44. } else if (value === null) {
  45. // default value
  46. (0, _element.addClass)(input, 'noValue');
  47. } else {
  48. input.style.display = 'none';
  49. (0, _element.addClass)(input, BAD_VALUE_CLASS);
  50. badValue = true;
  51. }
  52. input.setAttribute('data-row', row);
  53. input.setAttribute('data-col', col);
  54. if (!badValue && labelOptions) {
  55. var labelText = '';
  56. if (labelOptions.value) {
  57. labelText = typeof labelOptions.value === 'function' ? labelOptions.value.call(this, row, col, prop, value) : labelOptions.value;
  58. } else if (labelOptions.property) {
  59. labelText = instance.getDataAtRowProp(row, labelOptions.property);
  60. }
  61. var label = createLabel(labelText);
  62. if (labelOptions.position === 'before') {
  63. label.appendChild(input);
  64. } else {
  65. label.insertBefore(input, label.firstChild);
  66. }
  67. input = label;
  68. }
  69. TD.appendChild(input);
  70. if (badValue) {
  71. TD.appendChild(document.createTextNode('#bad-value#'));
  72. }
  73. if (!isListeningKeyDownEvent.has(instance)) {
  74. isListeningKeyDownEvent.set(instance, true);
  75. instance.addHook('beforeKeyDown', onBeforeKeyDown);
  76. }
  77. /**
  78. * On before key down DOM listener.
  79. *
  80. * @private
  81. * @param {Event} event
  82. */
  83. function onBeforeKeyDown(event) {
  84. var toggleKeys = 'SPACE|ENTER';
  85. var switchOffKeys = 'DELETE|BACKSPACE';
  86. var isKeyCode = (0, _function.partial)(_unicode.isKey, event.keyCode);
  87. if (isKeyCode(toggleKeys + '|' + switchOffKeys) && !(0, _event.isImmediatePropagationStopped)(event)) {
  88. eachSelectedCheckboxCell(function () {
  89. (0, _event.stopImmediatePropagation)(event);
  90. event.preventDefault();
  91. });
  92. }
  93. if (isKeyCode(toggleKeys)) {
  94. changeSelectedCheckboxesState();
  95. }
  96. if (isKeyCode(switchOffKeys)) {
  97. changeSelectedCheckboxesState(true);
  98. }
  99. }
  100. /**
  101. * Change checkbox checked property
  102. *
  103. * @private
  104. * @param {Boolean} [uncheckCheckbox=false]
  105. */
  106. function changeSelectedCheckboxesState() {
  107. var uncheckCheckbox = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  108. var selRange = instance.getSelectedRange();
  109. if (!selRange) {
  110. return;
  111. }
  112. var topLeft = selRange.getTopLeftCorner();
  113. var bottomRight = selRange.getBottomRightCorner();
  114. var changes = [];
  115. for (var _row = topLeft.row; _row <= bottomRight.row; _row += 1) {
  116. for (var _col = topLeft.col; _col <= bottomRight.col; _col += 1) {
  117. var _cellProperties = instance.getCellMeta(_row, _col);
  118. if (_cellProperties.type !== 'checkbox') {
  119. return;
  120. }
  121. /* eslint-disable no-continue */
  122. if (_cellProperties.readOnly === true) {
  123. continue;
  124. }
  125. if (typeof _cellProperties.checkedTemplate === 'undefined') {
  126. _cellProperties.checkedTemplate = true;
  127. }
  128. if (typeof _cellProperties.uncheckedTemplate === 'undefined') {
  129. _cellProperties.uncheckedTemplate = false;
  130. }
  131. var dataAtCell = instance.getDataAtCell(_row, _col);
  132. if (uncheckCheckbox === false) {
  133. if (dataAtCell === _cellProperties.checkedTemplate) {
  134. changes.push([_row, _col, _cellProperties.uncheckedTemplate]);
  135. } else if ([_cellProperties.uncheckedTemplate, null, void 0].indexOf(dataAtCell) !== -1) {
  136. changes.push([_row, _col, _cellProperties.checkedTemplate]);
  137. }
  138. } else {
  139. changes.push([_row, _col, _cellProperties.uncheckedTemplate]);
  140. }
  141. }
  142. }
  143. if (changes.length > 0) {
  144. instance.setDataAtCell(changes);
  145. }
  146. }
  147. /**
  148. * Call callback for each found selected cell with checkbox type.
  149. *
  150. * @private
  151. * @param {Function} callback
  152. */
  153. function eachSelectedCheckboxCell(callback) {
  154. var selRange = instance.getSelectedRange();
  155. if (!selRange) {
  156. return;
  157. }
  158. var topLeft = selRange.getTopLeftCorner();
  159. var bottomRight = selRange.getBottomRightCorner();
  160. for (var _row2 = topLeft.row; _row2 <= bottomRight.row; _row2++) {
  161. for (var _col2 = topLeft.col; _col2 <= bottomRight.col; _col2++) {
  162. var _cellProperties2 = instance.getCellMeta(_row2, _col2);
  163. if (_cellProperties2.type !== 'checkbox') {
  164. return;
  165. }
  166. var cell = instance.getCell(_row2, _col2);
  167. if (cell == null) {
  168. callback(_row2, _col2, _cellProperties2);
  169. } else {
  170. var checkboxes = cell.querySelectorAll('input[type=checkbox]');
  171. if (checkboxes.length > 0 && !_cellProperties2.readOnly) {
  172. callback(checkboxes);
  173. }
  174. }
  175. }
  176. }
  177. }
  178. }
  179. /**
  180. * Register checkbox listeners.
  181. *
  182. * @param {Handsontable} instance Handsontable instance.
  183. * @returns {EventManager}
  184. */
  185. function registerEvents(instance) {
  186. var eventManager = isCheckboxListenerAdded.get(instance);
  187. if (!eventManager) {
  188. eventManager = new _eventManager2.default(instance);
  189. eventManager.addEventListener(instance.rootElement, 'click', function (event) {
  190. return onClick(event, instance);
  191. });
  192. eventManager.addEventListener(instance.rootElement, 'mouseup', function (event) {
  193. return onMouseUp(event, instance);
  194. });
  195. eventManager.addEventListener(instance.rootElement, 'change', function (event) {
  196. return onChange(event, instance);
  197. });
  198. isCheckboxListenerAdded.set(instance, eventManager);
  199. }
  200. return eventManager;
  201. }
  202. /**
  203. * Create input element.
  204. *
  205. * @returns {Node}
  206. */
  207. function createInput() {
  208. var input = document.createElement('input');
  209. input.className = 'htCheckboxRendererInput';
  210. input.type = 'checkbox';
  211. input.setAttribute('autocomplete', 'off');
  212. input.setAttribute('tabindex', '-1');
  213. return input.cloneNode(false);
  214. }
  215. /**
  216. * Create label element.
  217. *
  218. * @returns {Node}
  219. */
  220. function createLabel(text) {
  221. var label = document.createElement('label');
  222. label.className = 'htCheckboxRendererLabel';
  223. label.appendChild(document.createTextNode(text));
  224. return label.cloneNode(true);
  225. }
  226. /**
  227. * `mouseup` callback.
  228. *
  229. * @private
  230. * @param {Event} event `mouseup` event.
  231. * @param {Object} instance Handsontable instance.
  232. */
  233. function onMouseUp(event, instance) {
  234. if (!isCheckboxInput(event.target)) {
  235. return;
  236. }
  237. setTimeout(instance.listen, 10);
  238. }
  239. /**
  240. * `click` callback.
  241. *
  242. * @private
  243. * @param {Event} event `click` event.
  244. * @param {Object} instance Handsontable instance.
  245. */
  246. function onClick(event, instance) {
  247. if (!isCheckboxInput(event.target)) {
  248. return false;
  249. }
  250. var row = parseInt(event.target.getAttribute('data-row'), 10);
  251. var col = parseInt(event.target.getAttribute('data-col'), 10);
  252. var cellProperties = instance.getCellMeta(row, col);
  253. if (cellProperties.readOnly) {
  254. event.preventDefault();
  255. }
  256. }
  257. /**
  258. * `change` callback.
  259. *
  260. * @param {Event} event `change` event.
  261. * @param {Object} instance Handsontable instance.
  262. * @param {Object} cellProperties Reference to cell properties.
  263. * @returns {Boolean}
  264. */
  265. function onChange(event, instance) {
  266. if (!isCheckboxInput(event.target)) {
  267. return false;
  268. }
  269. var row = parseInt(event.target.getAttribute('data-row'), 10);
  270. var col = parseInt(event.target.getAttribute('data-col'), 10);
  271. var cellProperties = instance.getCellMeta(row, col);
  272. if (!cellProperties.readOnly) {
  273. var newCheckboxValue = null;
  274. if (event.target.checked) {
  275. newCheckboxValue = cellProperties.uncheckedTemplate === void 0 ? true : cellProperties.checkedTemplate;
  276. } else {
  277. newCheckboxValue = cellProperties.uncheckedTemplate === void 0 ? false : cellProperties.uncheckedTemplate;
  278. }
  279. instance.setDataAtCell(row, col, newCheckboxValue);
  280. }
  281. }
  282. /**
  283. * Check if the provided element is the checkbox input.
  284. *
  285. * @private
  286. * @param {HTMLElement} element The element in question.
  287. * @returns {Boolean}
  288. */
  289. function isCheckboxInput(element) {
  290. return element.tagName === 'INPUT' && element.getAttribute('type') === 'checkbox';
  291. }
  292. exports.default = checkboxRenderer;