Slick keyboard navigation and selection

This commit is contained in:
Faris Ansari 2017-11-02 08:33:17 +05:30
parent 7a0e96926e
commit 9bd4b9fd89
5 changed files with 321 additions and 84 deletions

View File

@ -117,8 +117,9 @@ function getColumnHTML(column) {
});
var editCellHTML = isHeader ? '' : getEditCellHTML();
var sortIndicator = isHeader ? '<span class="sort-indicator"></span>' : '';
return '\n <td class="data-table-col noselect" ' + dataAttr + '>\n <div class="content ellipsis">\n ' + (column.format ? column.format(column.content) : column.content) + '\n <span class="sort-indicator"></span>\n </div>\n ' + editCellHTML + '\n </td>\n ';
return '\n <td class="data-table-col noselect" ' + dataAttr + '>\n <div class="content ellipsis">\n ' + (column.format ? column.format(column.content) : column.content) + '\n ' + sortIndicator + '\n </div>\n ' + editCellHTML + '\n </td>\n ';
}
function getRowHTML(columns, props) {
@ -1230,6 +1231,10 @@ Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _utils = __webpack_require__(0);
@ -1266,6 +1271,7 @@ var CellManager = function () {
this.bindFocusCell();
this.bindEditCell();
this.bindKeyboardNav();
this.bindKeyboardSelection();
}
}, {
key: 'bindFocusCell',
@ -1284,6 +1290,7 @@ var CellManager = function () {
value: function focusCell($cell) {
if (!$cell.length) return;
this.deactivateEditing();
this.clearSelection();
var _getCellAttr = this.getCellAttr($cell),
colIndex = _getCellAttr.colIndex;
@ -1359,58 +1366,147 @@ var CellManager = function () {
value: function bindKeyboardNav() {
var _this3 = this;
_keyboard2.default.on('left', function () {
var focusCell = function focusCell(direction) {
if (!_this3.$focusedCell) return;
var $cell = _this3.$focusedCell;
_this3.focusCell(_this3.$focusedCell.prev());
});
if (direction === 'left') {
$cell = _this3.getLeftCell$($cell);
} else if (direction === 'right') {
$cell = _this3.getRightCell$($cell);
} else if (direction === 'up') {
$cell = _this3.getAboveCell$($cell);
} else if (direction === 'down') {
$cell = _this3.getBelowCell$($cell);
}
_keyboard2.default.on('right', function () {
if (!_this3.$focusedCell) return;
_this3.focusCell($cell);
};
_this3.focusCell(_this3.$focusedCell.next());
});
_keyboard2.default.on('up', function () {
if (!_this3.$focusedCell) return;
var _getCellAttr3 = _this3.getCellAttr(_this3.$focusedCell),
colIndex = _getCellAttr3.colIndex;
var $upRow = _this3.$focusedCell.parent().prev();
var $upCell = $upRow.find('[data-col-index="' + colIndex + '"]');
_this3.focusCell($upCell);
});
_keyboard2.default.on('down', function () {
if (!_this3.$focusedCell) return;
var _getCellAttr4 = _this3.getCellAttr(_this3.$focusedCell),
colIndex = _getCellAttr4.colIndex;
var $downRow = _this3.$focusedCell.parent().next();
var $downCell = $downRow.find('[data-col-index="' + colIndex + '"]');
_this3.focusCell($downCell);
});
_keyboard2.default.on('shift+left', function () {
if (!_this3.$focusedCell) return;
// this.focusCell($downCell);
['left', 'right', 'up', 'down'].map(function (direction) {
return _keyboard2.default.on(direction, function () {
return focusCell(direction);
});
});
_keyboard2.default.on('esc', function () {
_this3.deactivateEditing();
});
}
}, {
key: 'bindKeyboardSelection',
value: function bindKeyboardSelection() {
var _this4 = this;
var getNextSelectionCursor = function getNextSelectionCursor(direction) {
var $selectionCursor = _this4.getSelectionCursor();
if (direction === 'left') {
$selectionCursor = _this4.getLeftCell$($selectionCursor);
} else if (direction === 'right') {
$selectionCursor = _this4.getRightCell$($selectionCursor);
} else if (direction === 'up') {
$selectionCursor = _this4.getAboveCell$($selectionCursor);
} else if (direction === 'down') {
$selectionCursor = _this4.getBelowCell$($selectionCursor);
}
return $selectionCursor;
};
var selectArea = function selectArea($selectionCursor) {
if (!_this4.$focusedCell) return;
if (_this4.selectArea(_this4.$focusedCell, $selectionCursor)) {
// valid selection
_this4.$selectionCursor = $selectionCursor;
}
};
['left', 'right', 'up', 'down'].map(function (direction) {
return _keyboard2.default.on('shift+' + direction, function () {
return selectArea(getNextSelectionCursor(direction));
});
});
}
}, {
key: 'selectArea',
value: function selectArea(colIndex1, rowIndex1, colIndex2, rowIndex2) {
var _this5 = this;
if ((typeof colIndex1 === 'undefined' ? 'undefined' : _typeof(colIndex1)) === 'object') {
if (!(colIndex1.length && rowIndex1.length)) return false;
var cell1 = this.getCellAttr(colIndex1);
var cell2 = this.getCellAttr(rowIndex1);
colIndex1 = cell1.colIndex;
rowIndex1 = cell1.rowIndex;
colIndex2 = cell2.colIndex;
rowIndex2 = cell2.rowIndex;
}
if (rowIndex1 > rowIndex2) {
var _ref = [rowIndex2, rowIndex1];
rowIndex1 = _ref[0];
rowIndex2 = _ref[1];
}
if (colIndex1 > colIndex2) {
var _ref2 = [colIndex2, colIndex1];
colIndex1 = _ref2[0];
colIndex2 = _ref2[1];
}
this.clearSelection();
var cells = [];
var colIndex = colIndex1;
var rowIndex = rowIndex1;
var rowIndices = [];
while (rowIndex <= rowIndex2) {
rowIndices.push(rowIndex);
rowIndex++;
}
rowIndices.map(function (rowIndex) {
while (colIndex <= colIndex2) {
cells.push([colIndex, rowIndex]);
colIndex++;
}
colIndex = colIndex1;
});
var $cells = cells.map(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
c = _ref4[0],
r = _ref4[1];
return _this5.getCell$(r, c)[0];
});
$($cells).addClass('highlight');
return true;
}
}, {
key: 'clearSelection',
value: function clearSelection() {
this.bodyScrollable.find('.data-table-col').removeClass('highlight');
this.$selectionCursor = null;
}
}, {
key: 'getSelectionCursor',
value: function getSelectionCursor() {
return this.$selectionCursor || this.$focusedCell;
}
}, {
key: 'activateEditing',
value: function activateEditing($cell) {
var _getCellAttr5 = this.getCellAttr($cell),
rowIndex = _getCellAttr5.rowIndex,
colIndex = _getCellAttr5.colIndex;
var _getCellAttr3 = this.getCellAttr($cell),
rowIndex = _getCellAttr3.rowIndex,
colIndex = _getCellAttr3.colIndex;
var col = this.instance.getColumn(colIndex);
@ -1419,9 +1515,9 @@ var CellManager = function () {
}
if (this.$editingCell) {
var _getCellAttr6 = this.getCellAttr(this.$editingCell),
_rowIndex = _getCellAttr6._rowIndex,
_colIndex = _getCellAttr6._colIndex;
var _getCellAttr4 = this.getCellAttr(this.$editingCell),
_rowIndex = _getCellAttr4._rowIndex,
_colIndex = _getCellAttr4._colIndex;
if (rowIndex === _rowIndex && colIndex === _colIndex) {
// editing the same cell
@ -1477,11 +1573,11 @@ var CellManager = function () {
}, {
key: 'submitEditing',
value: function submitEditing($cell) {
var _this4 = this;
var _this6 = this;
var _getCellAttr7 = this.getCellAttr($cell),
rowIndex = _getCellAttr7.rowIndex,
colIndex = _getCellAttr7.colIndex;
var _getCellAttr5 = this.getCellAttr($cell),
rowIndex = _getCellAttr5.rowIndex,
colIndex = _getCellAttr5.colIndex;
if ($cell) {
var editing = this.currentCellEditing;
@ -1493,7 +1589,7 @@ var CellManager = function () {
if (done && done.then) {
// wait for promise then update internal state
done.then(function () {
return _this4.updateCell(rowIndex, colIndex, value);
return _this6.updateCell(rowIndex, colIndex, value);
});
} else {
this.updateCell(rowIndex, colIndex, value);
@ -1521,6 +1617,41 @@ var CellManager = function () {
$cell.replaceWith($newCell);
}
}, {
key: 'getCell$',
value: function getCell$(rowIndex, colIndex) {
return this.bodyScrollable.find('.data-table-col[data-row-index="' + rowIndex + '"][data-col-index="' + colIndex + '"]');
}
}, {
key: 'getAboveCell$',
value: function getAboveCell$($cell) {
var _getCellAttr6 = this.getCellAttr($cell),
colIndex = _getCellAttr6.colIndex;
var $aboveRow = $cell.parent().prev();
return $aboveRow.find('[data-col-index="' + colIndex + '"]');
}
}, {
key: 'getBelowCell$',
value: function getBelowCell$($cell) {
var _getCellAttr7 = this.getCellAttr($cell),
colIndex = _getCellAttr7.colIndex;
var $belowRow = $cell.parent().next();
return $belowRow.find('[data-col-index="' + colIndex + '"]');
}
}, {
key: 'getLeftCell$',
value: function getLeftCell$($cell) {
return $cell.prev();
}
}, {
key: 'getRightCell$',
value: function getRightCell$($cell) {
return $cell.next();
}
}, {
key: 'getCell',
value: function getCell(rowIndex, colIndex) {
@ -1649,7 +1780,7 @@ exports = module.exports = __webpack_require__(8)(undefined);
// module
exports.push([module.i, "/* variables */\n:root {\n --border-color: #d1d8dd;\n --primary-color: #5292f7;\n --light-bg: #f5f7fa; }\n\n/* resets */\n*, *::after, *::before {\n box-sizing: border-box; }\n\nbutton, input {\n overflow: visible;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n margin: 0; }\n\n/* styling */\n.data-table * {\n outline: none; }\n\n.data-table {\n width: 100%;\n position: relative;\n overflow: auto; }\n .data-table table {\n border-collapse: collapse; }\n .data-table table td {\n padding: 0;\n border: 1px solid var(--border-color); }\n .data-table thead td {\n border-bottom-width: 2px; }\n\n.body-scrollable {\n max-height: 500px;\n overflow: auto;\n border-bottom: 1px solid var(--border-color); }\n\n.data-table-header {\n position: absolute;\n top: 0;\n left: 0;\n background-color: white;\n font-weight: bold;\n cursor: col-resize; }\n .data-table-header .content span {\n cursor: pointer; }\n .data-table-header .sort-indicator {\n position: absolute;\n right: 8px;\n top: 9px; }\n\n.data-table-col {\n position: relative; }\n .data-table-col .content {\n padding: 8px;\n border: 2px solid transparent; }\n .data-table-col .content.ellipsis {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden; }\n .data-table-col .edit-cell {\n display: none;\n padding: 8px;\n background: #fff;\n z-index: 1;\n height: 100%; }\n .data-table-col .edit-cell input {\n outline: none;\n width: 100%;\n border: none;\n height: 1em; }\n .data-table-col.selected .content {\n border: 2px solid var(--primary-color); }\n .data-table-col.editing .content {\n display: none; }\n .data-table-col.editing .edit-cell {\n border: 2px solid var(--primary-color);\n display: block; }\n\n.data-table-row.row-highlight {\n background-color: var(--light-bg); }\n\n.noselect {\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n", ""]);
exports.push([module.i, "/* variables */\n:root {\n --border-color: #d1d8dd;\n --primary-color: #5292f7;\n --light-bg: #f5f7fa; }\n\n/* resets */\n*, *::after, *::before {\n box-sizing: border-box; }\n\nbutton, input {\n overflow: visible;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n margin: 0; }\n\n/* styling */\n.data-table * {\n outline: none; }\n\n.data-table {\n width: 100%;\n position: relative;\n overflow: auto; }\n .data-table table {\n border-collapse: collapse; }\n .data-table table td {\n padding: 0;\n border: 1px solid var(--border-color); }\n .data-table thead td {\n border-bottom-width: 2px; }\n\n.body-scrollable {\n max-height: 500px;\n overflow: auto;\n border-bottom: 1px solid var(--border-color); }\n\n.data-table-header {\n position: absolute;\n top: 0;\n left: 0;\n background-color: white;\n font-weight: bold;\n cursor: col-resize; }\n .data-table-header .content span {\n cursor: pointer; }\n .data-table-header .sort-indicator {\n position: absolute;\n right: 8px;\n top: 9px; }\n\n.data-table-col {\n position: relative; }\n .data-table-col .content {\n padding: 8px;\n border: 2px solid transparent; }\n .data-table-col .content.ellipsis {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden; }\n .data-table-col .edit-cell {\n display: none;\n padding: 8px;\n background: #fff;\n z-index: 1;\n height: 100%; }\n .data-table-col .edit-cell input {\n outline: none;\n width: 100%;\n border: none;\n height: 1em; }\n .data-table-col.selected .content {\n border: 2px solid var(--primary-color); }\n .data-table-col.editing .content {\n display: none; }\n .data-table-col.editing .edit-cell {\n border: 2px solid var(--primary-color);\n display: block; }\n .data-table-col.highlight {\n background-color: var(--light-bg); }\n\n.data-table-row.row-highlight {\n background-color: var(--light-bg); }\n\n.noselect {\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none; }\n", ""]);
// exports

File diff suppressed because one or more lines are too long

View File

@ -20,6 +20,7 @@ export default class CellManager {
this.bindFocusCell();
this.bindEditCell();
this.bindKeyboardNav();
this.bindKeyboardSelection();
}
bindFocusCell() {
@ -34,6 +35,7 @@ export default class CellManager {
focusCell($cell) {
if (!$cell.length) return;
this.deactivateEditing();
this.clearSelection();
const { colIndex } = this.getCellAttr($cell);
@ -98,49 +100,122 @@ export default class CellManager {
}
bindKeyboardNav() {
keyboard.on('left', () => {
const focusCell = (direction) => {
if (!this.$focusedCell) return;
let $cell = this.$focusedCell;
this.focusCell(this.$focusedCell.prev());
});
if (direction === 'left') {
$cell = this.getLeftCell$($cell);
} else if (direction === 'right') {
$cell = this.getRightCell$($cell);
} else if (direction === 'up') {
$cell = this.getAboveCell$($cell);
} else if (direction === 'down') {
$cell = this.getBelowCell$($cell);
}
keyboard.on('right', () => {
if (!this.$focusedCell) return;
this.focusCell($cell);
};
this.focusCell(this.$focusedCell.next());
});
keyboard.on('up', () => {
if (!this.$focusedCell) return;
const { colIndex } = this.getCellAttr(this.$focusedCell);
const $upRow = this.$focusedCell.parent().prev();
const $upCell = $upRow.find(`[data-col-index="${colIndex}"]`);
this.focusCell($upCell);
});
keyboard.on('down', () => {
if (!this.$focusedCell) return;
const { colIndex } = this.getCellAttr(this.$focusedCell);
const $downRow = this.$focusedCell.parent().next();
const $downCell = $downRow.find(`[data-col-index="${colIndex}"]`);
this.focusCell($downCell);
});
keyboard.on('shift+left', () => {
if (!this.$focusedCell) return;
// this.focusCell($downCell);
});
['left', 'right', 'up', 'down'].map(
direction => keyboard.on(direction, () => focusCell(direction))
);
keyboard.on('esc', () => {
this.deactivateEditing();
});
}
bindKeyboardSelection() {
const getNextSelectionCursor = (direction) => {
let $selectionCursor = this.getSelectionCursor();
if (direction === 'left') {
$selectionCursor = this.getLeftCell$($selectionCursor);
} else if (direction === 'right') {
$selectionCursor = this.getRightCell$($selectionCursor);
} else if (direction === 'up') {
$selectionCursor = this.getAboveCell$($selectionCursor);
} else if (direction === 'down') {
$selectionCursor = this.getBelowCell$($selectionCursor);
}
return $selectionCursor;
};
const selectArea = ($selectionCursor) => {
if (!this.$focusedCell) return;
if (this.selectArea(this.$focusedCell, $selectionCursor)) {
// valid selection
this.$selectionCursor = $selectionCursor;
}
};
['left', 'right', 'up', 'down'].map(
direction => keyboard.on('shift+' + direction,
() => selectArea(getNextSelectionCursor(direction)))
);
}
selectArea(colIndex1, rowIndex1, colIndex2, rowIndex2) {
if (typeof colIndex1 === 'object') {
if (!(colIndex1.length && rowIndex1.length)) return false;
const cell1 = this.getCellAttr(colIndex1);
const cell2 = this.getCellAttr(rowIndex1);
colIndex1 = cell1.colIndex;
rowIndex1 = cell1.rowIndex;
colIndex2 = cell2.colIndex;
rowIndex2 = cell2.rowIndex;
}
if (rowIndex1 > rowIndex2) {
[rowIndex1, rowIndex2] = [rowIndex2, rowIndex1];
}
if (colIndex1 > colIndex2) {
[colIndex1, colIndex2] = [colIndex2, colIndex1];
}
this.clearSelection();
let cells = [];
let colIndex = colIndex1;
let rowIndex = rowIndex1;
let rowIndices = [];
while (rowIndex <= rowIndex2) {
rowIndices.push(rowIndex);
rowIndex++;
}
rowIndices.map(rowIndex => {
while (colIndex <= colIndex2) {
cells.push([colIndex, rowIndex]);
colIndex++;
}
colIndex = colIndex1;
});
const $cells = cells.map(([c, r]) => this.getCell$(r, c)[0]);
$($cells).addClass('highlight');
return true;
}
clearSelection() {
this.bodyScrollable.find('.data-table-col').removeClass('highlight');
this.$selectionCursor = null;
}
getSelectionCursor() {
return this.$selectionCursor || this.$focusedCell;
}
activateEditing($cell) {
const { rowIndex, colIndex } = this.getCellAttr($cell);
const col = this.instance.getColumn(colIndex);
@ -242,6 +317,32 @@ export default class CellManager {
$cell.replaceWith($newCell);
}
getCell$(rowIndex, colIndex) {
return this.bodyScrollable.find(`.data-table-col[data-row-index="${rowIndex}"][data-col-index="${colIndex}"]`);
}
getAboveCell$($cell) {
const { colIndex } = this.getCellAttr($cell);
const $aboveRow = $cell.parent().prev();
return $aboveRow.find(`[data-col-index="${colIndex}"]`);
}
getBelowCell$($cell) {
const { colIndex } = this.getCellAttr($cell);
const $belowRow = $cell.parent().next();
return $belowRow.find(`[data-col-index="${colIndex}"]`);
}
getLeftCell$($cell) {
return $cell.prev();
}
getRightCell$($cell) {
return $cell.next();
}
getCell(rowIndex, colIndex) {
return this.instance.datamanager.getCell(rowIndex, colIndex);
}

View File

@ -113,6 +113,10 @@ button, input {
display: block;
}
}
&.highlight {
background-color: var(--light-bg);
}
}
.data-table-row {

View File

@ -32,12 +32,13 @@ function getColumnHTML(column) {
});
const editCellHTML = isHeader ? '' : getEditCellHTML();
const sortIndicator = isHeader ? '<span class="sort-indicator"></span>' : '';
return `
<td class="data-table-col noselect" ${dataAttr}>
<div class="content ellipsis">
${column.format ? column.format(column.content) : column.content}
<span class="sort-indicator"></span>
${sortIndicator}
</div>
${editCellHTML}
</td>