Move buildCssRule to utils, focus and edit cell functionality

This commit is contained in:
Faris Ansari 2017-10-01 12:32:02 +05:30
parent f2177f96ed
commit fa6a582a45
6 changed files with 286 additions and 193 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,11 +3,10 @@ import {
getBodyHTML, getBodyHTML,
getRowHTML, getRowHTML,
getColumnHTML, getColumnHTML,
getEditCellHTML,
prepareRowHeader, prepareRowHeader,
buildCSSRule,
prepareRows, prepareRows,
getDefault, getDefault
escapeRegExp
} from './utils.js'; } from './utils.js';
import $ from 'jQuery'; import $ from 'jQuery';
import Clusterize from 'clusterize.js'; import Clusterize from 'clusterize.js';
@ -220,15 +219,7 @@ export default class ReGrid {
} }
bindEvents() { bindEvents() {
const self = this; this.bindFocusCell();
this.bodyScrollable.on('click', '.data-table-col', function () {
const $col = $(this);
self.bodyScrollable.find('.data-table-col').removeClass('selected');
$col.addClass('selected');
});
this.bindEditCell(); this.bindEditCell();
this.bindResizeColumn(); this.bindResizeColumn();
this.bindSortColumn(); this.bindSortColumn();
@ -269,40 +260,36 @@ export default class ReGrid {
this.bodyScrollable.find('.table').css('margin', 0); this.bodyScrollable.find('.table').css('margin', 0);
} }
bindFocusCell() {
const self = this;
this.$focusedCell = null;
this.bodyScrollable.on('click', '.data-table-col', function () {
const $cell = $(this);
self.$focusedCell = $cell;
self.bodyScrollable.find('.data-table-col').removeClass('selected');
$cell.addClass('selected');
});
}
bindEditCell() { bindEditCell() {
const self = this; const self = this;
const $editPopup = this.wrapper.find('.edit-popup'); const $editPopup = this.wrapper.find('.edit-popup');
$editPopup.hide(); $editPopup.hide();
this.$editingCell = null;
// if (!self.events.onCellEdit) return; // if (!self.events.onCellEdit) return;
this.bodyScrollable.on('dblclick', '.data-table-col', function () { this.bodyScrollable.on('dblclick', '.data-table-col', function () {
const $cell = $(this); self.activateEditing($(this));
const rowIndex = $cell.attr('data-row-index'); });
const colIndex = $cell.attr('data-col-index');
const $editCell = $cell.find('.edit-cell');
const cell = self.getCell(rowIndex, colIndex); $(document.body).on('keypress', (e) => {
// enter keypress on focused cell
$editCell.find('input').val(cell.content); if (e.which === 13 && this.$focusedCell) {
// $editPopup.html(getEditCellHTML(cellValue.content)); self.activateEditing(this.$focusedCell);
}
const width = $cell.find('.content').width() + 1;
const height = $cell.find('.content').height() + 1;
const selector = `.data-table .body-scrollable [data-row-index="${rowIndex}"][data-row-index="${rowIndex}"] .edit-cell`;
self.setStyle(selector, { width, height });
$editCell.show();
$editCell.find('input').select();
// showing the popup is the responsibility of event handler
// self.events.onCellEdit(
// $cell.get(0),
// $editPopup,
// rowIndex,
// colIndex
// );
}); });
$(document.body).on('click', e => { $(document.body).on('click', e => {
@ -311,6 +298,26 @@ export default class ReGrid {
}); });
} }
activateEditing($cell) {
const rowIndex = $cell.attr('data-row-index');
const colIndex = $cell.attr('data-col-index');
const $editCell = $cell.find('.edit-cell');
const cell = this.getCell(rowIndex, colIndex);
this.$editingCell = $cell;
$editCell.find('input').val(cell.content);
$editCell.show();
$editCell.find('input').select();
// showing the popup is the responsibility of event handler
// self.events.onCellEdit(
// $cell.get(0),
// $editPopup,
// rowIndex,
// colIndex
// );
}
bindResizeColumn() { bindResizeColumn() {
const self = this; const self = this;
let isDragging = false; let isDragging = false;
@ -416,9 +423,14 @@ export default class ReGrid {
} }
setColumnWidth(colIndex, width) { setColumnWidth(colIndex, width) {
// set width for content
this.setStyle(`[data-col-index="${colIndex}"] .content`, { this.setStyle(`[data-col-index="${colIndex}"] .content`, {
width: width + 'px' width: width + 'px'
}); });
// set width for edit cell
this.setStyle(`[data-col-index="${colIndex}"] .edit-cell`, {
width: width + 'px'
});
} }
setRowHeight(rowIndex, height) { setRowHeight(rowIndex, height) {
@ -475,58 +487,12 @@ export default class ReGrid {
setStyle(rule, styleMap) { setStyle(rule, styleMap) {
this.getStyleEl(); this.getStyleEl();
const self = this;
let styles = this.$style.text(); let styles = this.$style.text();
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
const rulePattern = new RegExp(rulePatternStr, 'g');
if (styles.match(rulePattern)) {
// rules exists, append/replace properties
for (const property in styleMap) {
const value = styleMap[property];
const propPattern = new RegExp(`${escapeRegExp(property)}[^;]*;`);
styles = styles.replace(rulePattern, function (match, propertyStr) {
if (propertyStr.match(propPattern)) {
// property exists, replace with empty string
propertyStr = propertyStr.replace(propPattern, '');
}
propertyStr = propertyStr.trim();
const _styleMap = {};
_styleMap[property] = value;
const replacer =
`${rule} {${propertyStr}${self.getCSSString(_styleMap)}}`;
return replacer;
});
}
} else {
// rules doesn't exists, add rule with properties
styles += `${rule} {${styleMap}}`;
}
styles = buildCSSRule(rule, styleMap, styles);
this.$style.html(styles); this.$style.html(styles);
} }
getCSSString(styleMap) {
let style = '';
for (const prop in styleMap) {
if (styleMap.hasOwnProperty(prop)) {
style += `${prop}: ${styleMap[prop]};`;
}
}
console.log(style);
return style;
}
getStyleEl() { getStyleEl() {
if (!this.$style) { if (!this.$style) {
this.$style = $('<style data-id="regrid"></style>') this.$style = $('<style data-id="regrid"></style>')
@ -565,6 +531,10 @@ export default class ReGrid {
return this.minWidthMap && this.minWidthMap[colIndex]; return this.minWidthMap && this.minWidthMap[colIndex];
} }
getCellAttr($cell) {
return $cell.data();
}
log() { log() {
if (this.enableLogs) { if (this.enableLogs) {
console.log.apply(console, arguments); console.log.apply(console, arguments);

View File

@ -61,28 +61,11 @@
} }
} }
.edit-popup {
position: absolute;
background: white;
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
input {
outline: none;
padding: 8px;
font-size: inherit;
font-family: inherit;
width: inherit;
height: inherit;
border: 2px solid theme-color('primary');
}
}
.edit-cell { .edit-cell {
position: absolute; position: absolute;
top: -1px; top: -1px;
left: -1px; left: -1px;
background: white; background: white;
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
z-index: 1; z-index: 1;
input { input {

View File

@ -127,6 +127,61 @@ function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
} }
function getCSSString(styleMap) {
let style = '';
for (const prop in styleMap) {
if (styleMap.hasOwnProperty(prop)) {
style += `${prop}: ${styleMap[prop]}; `;
}
}
return style.trim();
}
function getCSSRuleBlock(rule, styleMap) {
return `${rule} { ${getCSSString(styleMap)} }`;
}
function namespaceSelector(selector) {
return '.data-table ' + selector;
}
function buildCSSRule(rule, styleMap, cssRulesString = '') {
// build css rules efficiently,
// append new rule if doesnt exist,
// update existing ones
const rulePatternStr = `${escapeRegExp(rule)} {([^}]*)}`;
const rulePattern = new RegExp(rulePatternStr, 'g');
if (cssRulesString && cssRulesString.match(rulePattern)) {
for (const property in styleMap) {
const value = styleMap[property];
const propPattern = new RegExp(`${escapeRegExp(property)}:([^;]*);`);
cssRulesString = cssRulesString.replace(rulePattern, function (match, propertyStr) {
if (propertyStr.match(propPattern)) {
// property exists, replace value with new value
propertyStr = propertyStr.replace(propPattern, (match, valueStr) => {
return `${property}: ${value};`;
});
}
propertyStr = propertyStr.trim();
const replacer =
`${rule} { ${propertyStr} }`;
return replacer;
});
}
return cssRulesString;
}
// no match, append new rule block
return `${cssRulesString}${getCSSRuleBlock(rule, styleMap)}`;
}
export default { export default {
getHeaderHTML, getHeaderHTML,
getBodyHTML, getBodyHTML,
@ -135,6 +190,9 @@ export default {
getEditCellHTML, getEditCellHTML,
prepareRowHeader, prepareRowHeader,
prepareRows, prepareRows,
namespaceSelector,
getCSSString,
buildCSSRule,
makeDataAttributeString, makeDataAttributeString,
getDefault, getDefault,
escapeRegExp escapeRegExp

View File

@ -2,7 +2,9 @@
import chai from 'chai'; import chai from 'chai';
import { import {
makeDataAttributeString makeDataAttributeString,
getCSSString,
buildCSSRule,
} from '../src/utils.js'; } from '../src/utils.js';
chai.expect(); chai.expect();
@ -21,4 +23,48 @@ describe('#utils', () => {
.to.be.equal('data-is-header="1" data-col-index="0" data-row-index="4"'); .to.be.equal('data-is-header="1" data-col-index="0" data-row-index="4"');
}); });
}); });
describe('getCSSString', () => {
it('should return CSS key value pairs', () => {
const style = {
width: '2px',
height: '4px',
'margin-top': '3px'
};
expect(getCSSString(style))
.to.be.equal('width: 2px; height: 4px; margin-top: 3px;');
});
});
describe('buildCSSRule', () => {
it('should return CSS rule string with updated properties', () => {
const rule = '.test';
const style = {
width: '2px',
height: '4px',
'margin-top': '3px'
};
const ruleString = buildCSSRule(rule, style);
expect(ruleString)
.to.be.equal('.test { width: 2px; height: 4px; margin-top: 3px; }');
const updatedRuleString = buildCSSRule(rule, { width: '5px' }, ruleString);
expect(updatedRuleString)
.to.be.equal('.test { width: 5px; height: 4px; margin-top: 3px; }');
const updatedRuleString2 = buildCSSRule(rule, { height: '19px' }, updatedRuleString);
expect(updatedRuleString2)
.to.be.equal('.test { width: 5px; height: 19px; margin-top: 3px; }');
const updatedRuleString3 = buildCSSRule('.test2', { height: '45px' }, updatedRuleString2);
expect(updatedRuleString3)
.to.be.equal('.test { width: 5px; height: 19px; margin-top: 3px; }.test2 { height: 45px; }');
});
});
}); });