datavis-glide 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,6 +65,9 @@ var GridTablePlain = makeSubclass('GridTablePlain', GridTable, function (grid, d
65
65
  self.features.filter = false;
66
66
 
67
67
  self._focusEventId = gensym('grid-plain-');
68
+ self._cellMarqueeNs = '.wcdv_cell_marquee_' + self._focusEventId;
69
+ self._cellDragging = false;
70
+ self._cellSelectFields = [];
68
71
 
69
72
  // Pagination state.
70
73
  self._paginationPage = 0;
@@ -97,7 +100,7 @@ GridTablePlain.prototype.draw = function (root, opts, cont) {
97
100
  var self = this;
98
101
 
99
102
  GridTable.prototype.draw.call(self, root, opts, function () {
100
- if (self.features.activeRow || self.features.omnifilter) {
103
+ if (self.features.activeRow || self.features.omnifilter || self.features.rowSelect || self.features.cellSelect) {
101
104
  self._hasFocus = false;
102
105
  addFocusHandler(root, self._focusEventId, function (isFocused) {
103
106
  self._hasFocus = isFocused;
@@ -178,6 +181,32 @@ GridTablePlain.prototype.draw = function (root, opts, cont) {
178
181
  });
179
182
  }
180
183
 
184
+ if (self.features.rowSelect || self.features.cellSelect) {
185
+ jQuery(document).on('keydown.copy-selection-' + self._focusEventId, function (evt) {
186
+ var avoidElts = ['A', 'BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'];
187
+
188
+ if (avoidElts.indexOf(evt.target.tagName) >= 0) {
189
+ return;
190
+ }
191
+
192
+ if (!self._hasFocus) {
193
+ return;
194
+ }
195
+
196
+ if (evt.key.toLowerCase() !== 'c' || (!evt.ctrlKey && !evt.metaKey)) {
197
+ return;
198
+ }
199
+
200
+ if (self.getSelection().rows.length === 0 && self.getCellSelection().cells.length === 0) {
201
+ return;
202
+ }
203
+
204
+ evt.preventDefault();
205
+ evt.stopPropagation();
206
+ self.grid.copySelection();
207
+ });
208
+ }
209
+
181
210
  return typeof cont === 'function' ? cont() : null;
182
211
  });
183
212
  };
@@ -571,6 +600,8 @@ GridTablePlain.prototype.drawBody = function (data, typeInfo, columns, cont, opt
571
600
  self.addDataToCsv(data);
572
601
  }
573
602
 
603
+ self._cellSelectFields = columns.slice();
604
+
574
605
  // When pagination is enabled, wrap the continuation so that page visibility and pagination
575
606
  // controls are applied after all rows have been rendered. The originalCont must run first
576
607
  // because it appends the tbody to the table and runs the full draw chain (including
@@ -660,6 +691,8 @@ GridTablePlain.prototype.drawBody = function (data, typeInfo, columns, cont, opt
660
691
  operations: getProp(self.defn, 'operations', 'cell', field)
661
692
  });
662
693
 
694
+ td.setAttribute('data-wcdv-field', field);
695
+
663
696
  // Buttons within cells share a common 'onClick' handler, e.g. all "show full value" buttons
664
697
  // have the same callback. In that handler, we need to be able to figure out what field we
665
698
  // were called for. So if we're going to render buttons within the data cell, we need to
@@ -670,10 +703,6 @@ GridTablePlain.prototype.drawBody = function (data, typeInfo, columns, cont, opt
670
703
  // 1. When `maxHeight` is set on the field (the "show full value" button).
671
704
  // 2. When there are operations on the field.
672
705
 
673
- if (fcc.maxHeight != null || self.hasOperations('cell', field)) {
674
- td.setAttribute('data-wcdv-field', field);
675
- }
676
-
677
706
  self.setCss(jQuery(td), field);
678
707
  self.setAlignment(td, fcc, typeInfo.get(field));
679
708
 
@@ -1553,6 +1582,8 @@ GridTablePlain.prototype._addRowSelectHandler = function () {
1553
1582
  var self = this;
1554
1583
 
1555
1584
  self.ui.tbody.on('change', '.wcdv_group_col_spacer > input[type="checkbox"]', function () {
1585
+ self.clearCellSelection();
1586
+
1556
1587
  if (this.checked) {
1557
1588
  self.select(+(jQuery(this).attr('data-row-num')));
1558
1589
  }
@@ -1562,6 +1593,231 @@ GridTablePlain.prototype._addRowSelectHandler = function () {
1562
1593
  });
1563
1594
  };
1564
1595
 
1596
+ // #_getCellSelectRowNums {{{2
1597
+
1598
+ GridTablePlain.prototype._getCellSelectRowNums = function () {
1599
+ var self = this;
1600
+ var rowNums = [];
1601
+
1602
+ self.ui.tbody.children('tr[data-row-num]:visible').each(function () {
1603
+ rowNums.push(+(jQuery(this).attr('data-row-num')));
1604
+ });
1605
+
1606
+ return rowNums;
1607
+ };
1608
+
1609
+ // #_getCellSelectFields {{{2
1610
+
1611
+ GridTablePlain.prototype._getCellSelectFields = function () {
1612
+ var self = this;
1613
+
1614
+ return self._cellSelectFields.slice();
1615
+ };
1616
+
1617
+ // #_resolveCellSelectTarget {{{2
1618
+
1619
+ GridTablePlain.prototype._resolveCellSelectTarget = function (target) {
1620
+ var self = this;
1621
+ var td = jQuery(target).closest('td[data-wcdv-field]');
1622
+
1623
+ if (td.length !== 1) {
1624
+ return null;
1625
+ }
1626
+
1627
+ if (!td.closest(self.ui.tbody).length) {
1628
+ return null;
1629
+ }
1630
+
1631
+ var tr = td.closest('tr[data-row-num]');
1632
+
1633
+ if (tr.length !== 1) {
1634
+ return null;
1635
+ }
1636
+
1637
+ return {
1638
+ rowNum: +(tr.attr('data-row-num')),
1639
+ field: td.attr('data-wcdv-field')
1640
+ };
1641
+ };
1642
+
1643
+ // #_makeCellSelection {{{2
1644
+
1645
+ GridTablePlain.prototype._makeCellSelection = function (anchor, focus) {
1646
+ var self = this;
1647
+ var rowNums = self._getCellSelectRowNums();
1648
+ var fields = self._getCellSelectFields();
1649
+
1650
+ var anchorRow = rowNums.indexOf(anchor.rowNum);
1651
+ var focusRow = rowNums.indexOf(focus.rowNum);
1652
+ var anchorField = fields.indexOf(anchor.field);
1653
+ var focusField = fields.indexOf(focus.field);
1654
+
1655
+ if (anchorRow < 0 || focusRow < 0 || anchorField < 0 || focusField < 0) {
1656
+ return null;
1657
+ }
1658
+
1659
+ var firstRow = Math.min(anchorRow, focusRow);
1660
+ var lastRow = Math.max(anchorRow, focusRow);
1661
+ var firstField = Math.min(anchorField, focusField);
1662
+ var lastField = Math.max(anchorField, focusField);
1663
+
1664
+ return {
1665
+ anchor: {
1666
+ rowNum: anchor.rowNum,
1667
+ field: anchor.field
1668
+ },
1669
+ focus: {
1670
+ rowNum: focus.rowNum,
1671
+ field: focus.field
1672
+ },
1673
+ rowNums: rowNums.slice(firstRow, lastRow + 1),
1674
+ fields: fields.slice(firstField, lastField + 1)
1675
+ };
1676
+ };
1677
+
1678
+ // #_clearCellSelectionGui {{{2
1679
+
1680
+ GridTablePlain.prototype._clearCellSelectionGui = function () {
1681
+ var self = this;
1682
+
1683
+ if (self.ui == null || self.ui.tbody == null) {
1684
+ return;
1685
+ }
1686
+
1687
+ self.ui.tbody.find('td.wcdv_selected_cell').removeClass('wcdv_selected_cell');
1688
+ self.ui.tbody.find('td.wcdv_selected_cell_anchor').removeClass('wcdv_selected_cell_anchor');
1689
+ };
1690
+
1691
+ // #_updateCellSelectionGui {{{2
1692
+
1693
+ GridTablePlain.prototype._updateCellSelectionGui = function () {
1694
+ var self = this;
1695
+ var selection = self.getCellSelection();
1696
+
1697
+ self._clearCellSelectionGui();
1698
+
1699
+ if (selection.cells.length === 0) {
1700
+ return;
1701
+ }
1702
+
1703
+ _.each(selection.rowNums, function (rowNum) {
1704
+ var tr = self.ui.tbody.find('tr[data-row-num="' + rowNum + '"]');
1705
+
1706
+ _.each(selection.fields, function (field) {
1707
+ tr.find('td[data-wcdv-field]').filter(function () {
1708
+ return jQuery(this).attr('data-wcdv-field') === field;
1709
+ }).addClass('wcdv_selected_cell');
1710
+ });
1711
+ });
1712
+
1713
+ if (selection.anchor != null) {
1714
+ self.ui.tbody.find('tr[data-row-num="' + selection.anchor.rowNum + '"] td[data-wcdv-field]').filter(function () {
1715
+ return jQuery(this).attr('data-wcdv-field') === selection.anchor.field;
1716
+ }).addClass('wcdv_selected_cell_anchor');
1717
+ }
1718
+ };
1719
+
1720
+ // #_setCellSelection {{{2
1721
+
1722
+ GridTablePlain.prototype._setCellSelection = function (anchor, focus, fireEvent) {
1723
+ var self = this;
1724
+ var nextSelection = self._makeCellSelection(anchor, focus);
1725
+ var prevCellCount = self.getCellSelection().cells.length;
1726
+
1727
+ if (nextSelection == null) {
1728
+ self.cellSelection = null;
1729
+ self._clearCellSelectionGui();
1730
+
1731
+ if (fireEvent && prevCellCount > 0) {
1732
+ self.fire('cellSelectionChange', null, self.getCellSelection());
1733
+ }
1734
+
1735
+ return;
1736
+ }
1737
+
1738
+ self.cellSelection = nextSelection;
1739
+ self._updateCellSelectionGui();
1740
+
1741
+ if (fireEvent) {
1742
+ self.fire('cellSelectionChange', null, self.getCellSelection());
1743
+ }
1744
+ };
1745
+
1746
+ // #_addCellSelectHandler {{{2
1747
+
1748
+ GridTablePlain.prototype._addCellSelectHandler = function () {
1749
+ var self = this;
1750
+
1751
+ self.ui.tbody.off('mousedown' + self._cellMarqueeNs);
1752
+ self.ui.tbody.on('mousedown' + self._cellMarqueeNs, 'td[data-wcdv-field]', function (evt) {
1753
+ if (evt.which !== 1) {
1754
+ return;
1755
+ }
1756
+
1757
+ var anchor = self._resolveCellSelectTarget(evt.target);
1758
+
1759
+ if (anchor == null) {
1760
+ return;
1761
+ }
1762
+
1763
+ evt.preventDefault();
1764
+
1765
+ if (self.features.rowSelect) {
1766
+ self.unselect();
1767
+ }
1768
+ else {
1769
+ self.clearCellSelection();
1770
+ }
1771
+
1772
+ self._cellDragging = true;
1773
+ self._cellAnchor = anchor;
1774
+ self._cellFocus = anchor;
1775
+ self._setCellSelection(anchor, anchor, false);
1776
+
1777
+ jQuery(document).off('mousemove' + self._cellMarqueeNs);
1778
+ jQuery(document).off('mouseup' + self._cellMarqueeNs);
1779
+
1780
+ jQuery(document).on('mousemove' + self._cellMarqueeNs, function (moveEvt) {
1781
+ if (!self._cellDragging) {
1782
+ return;
1783
+ }
1784
+
1785
+ var target = document.elementFromPoint(moveEvt.clientX, moveEvt.clientY) || moveEvt.target;
1786
+ var focus = self._resolveCellSelectTarget(target);
1787
+
1788
+ if (focus == null) {
1789
+ return;
1790
+ }
1791
+
1792
+ if (self._cellFocus != null
1793
+ && self._cellFocus.rowNum === focus.rowNum
1794
+ && self._cellFocus.field === focus.field) {
1795
+ return;
1796
+ }
1797
+
1798
+ self._cellFocus = focus;
1799
+ self._setCellSelection(self._cellAnchor, self._cellFocus, false);
1800
+ });
1801
+
1802
+ jQuery(document).on('mouseup' + self._cellMarqueeNs, function (upEvt) {
1803
+ if (!self._cellDragging) {
1804
+ return;
1805
+ }
1806
+
1807
+ self._cellDragging = false;
1808
+
1809
+ var target = document.elementFromPoint(upEvt.clientX, upEvt.clientY) || upEvt.target;
1810
+ var focus = self._resolveCellSelectTarget(target) || self._cellFocus || self._cellAnchor;
1811
+
1812
+ self._cellFocus = focus;
1813
+ self._setCellSelection(self._cellAnchor, self._cellFocus, true);
1814
+
1815
+ jQuery(document).off('mousemove' + self._cellMarqueeNs);
1816
+ jQuery(document).off('mouseup' + self._cellMarqueeNs);
1817
+ });
1818
+ });
1819
+ };
1820
+
1565
1821
  GridTablePlain.prototype.clear = function () {
1566
1822
  var self = this;
1567
1823
 
@@ -1574,8 +1830,19 @@ GridTablePlain.prototype.clear = function () {
1574
1830
  self.ui.paginationControls = null;
1575
1831
  }
1576
1832
 
1833
+ self.cellSelection = null;
1834
+ self._clearCellSelectionGui();
1835
+
1836
+ if (self.ui != null && self.ui.tbody != null) {
1837
+ self.ui.tbody.off('mousedown' + self._cellMarqueeNs);
1838
+ }
1839
+
1840
+ jQuery(document).off('mousemove' + self._cellMarqueeNs);
1841
+ jQuery(document).off('mouseup' + self._cellMarqueeNs);
1842
+
1577
1843
  jQuery(document).off('keydown.active-row-' + self._focusEventId);
1578
1844
  jQuery(document).off('keydown.omnifilter-' + self._focusEventId);
1845
+ jQuery(document).off('keydown.copy-selection-' + self._focusEventId);
1579
1846
  removeFocusHandler(self._focusEventId);
1580
1847
 
1581
1848
  self.super['GridTable'].clear();