Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 50 additions & 52 deletions jquery-editable-table.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
$.fn.editableTable = function (options) {
$.fn.editableTable = function(options) {
// Default options
let defaultOptions = {
cloneProperties: ['padding', 'padding-top', 'padding-bottom', 'padding-left', 'padding-right',
'text-align', 'font', 'font-size', 'font-family', 'font-weight',
'border', 'border-top', 'border-bottom', 'border-left', 'border-right', 'color', 'background-color', 'border-radius'],
'border', 'border-top', 'border-bottom', 'border-left', 'border-right', 'color', 'background-color', 'border-radius'
],
columns: []
}

Expand Down Expand Up @@ -33,21 +34,20 @@ $.fn.editableTable = function (options) {

// The table element
let element = $(this);

// Show all columns and then hide any hidden ones
element.find('th').show();
options.columns.forEach((col, i)=>{
if(col.isHidden !== undefined && col.isHidden) element.find('th').eq(i).hide();
options.columns.forEach((col, i) => {
if (col.isHidden !== undefined && col.isHidden) element.find('th').eq(i).hide();
});


// The textbox allowing user input. Only add if there's not already an editor input control around
let editor;
let existingEditor = element.parent().find(`input[${identifierAttribute}]`);
if (existingEditor.length) {
editor = existingEditor.first();
}
else {
} else {
editor = $('<input>');
}

Expand All @@ -66,14 +66,14 @@ $.fn.editableTable = function (options) {
activeCell = element.find('td:focus');
if (activeCell.length) {
// Prepare
editor.val(activeCell.text()) // Throw the value in
.removeClass(errorClass) // remove any error classes
.show() // show it
.offset(activeCell.offset()) // position it
.css(activeCell.css(options.cloneProperties)) // make it look similar by cloning properties
.width(activeCell.width()) // size it
.height(activeCell.height()) // size it
.focus(); // focu user input into it
editor.val(activeCell.text()) // Throw the value in
.removeClass(errorClass) // remove any error classes
.show() // show it
.offset(activeCell.offset()) // position it
.css(activeCell.css(options.cloneProperties)) // make it look similar by cloning properties
.width(activeCell.width()) // size it
.height(activeCell.height()) // size it
.focus(); // focus user input into it
if (select) {
editor.select();
}
Expand Down Expand Up @@ -109,25 +109,22 @@ $.fn.editableTable = function (options) {
};

// On the editor losing focus, hide the input
editor.blur(function () {
editor.blur(function() {
setActiveText();
editor.hide();
});

// Handle typing into the input
editor.keydown(function (e) {
editor.keydown(function(e) {
if (e.which === ENTER) {
setActiveText();
editor.hide();
activeCell.focus();
e.preventDefault();
e.stopPropagation();
} else if (e.which === ESC) {
editor.val(activeCell.text());
e.preventDefault();
e.stopPropagation();
editor.hide();
activeCell.focus();
} else if (e.which === TAB) {
activeCell.focus();
} else if (this.selectionEnd - this.selectionStart === this.value.length) {
Expand All @@ -141,7 +138,7 @@ $.fn.editableTable = function (options) {
});

// Validate cell input on typing or pasting
editor.on('input paste', function () {
editor.on('input paste', function() {
let evt = $.Event('validate');
activeCell.trigger(evt, editor.val());
if (evt.result !== undefined) {
Expand All @@ -156,7 +153,7 @@ $.fn.editableTable = function (options) {
// On table clicking, move around cells
element.on('click keypress dblclick', showEditor)
.css('cursor', 'pointer')
.keydown(function (e) {
.keydown(function(e) {
let prevent = true,
possibleMove = handleMovement($(e.target), e.which);
if (possibleMove.length > 0) {
Expand All @@ -175,9 +172,9 @@ $.fn.editableTable = function (options) {
}
});

element.find('td').prop('tabindex', 1);
element.find('td').not('.no-editor').prop('tabindex', 1);

$(window).on('resize', function () {
$(window).on('resize', function() {
if (editor.is(':visible')) {
editor.offset(activeCell.offset())
.width(activeCell.width())
Expand All @@ -190,15 +187,17 @@ $.fn.editableTable = function (options) {
}

// Validate based on options
$('table td').on('validate', function (evt, newValue) {
$('table td').on('validate', function(evt, newValue) {
let currentColIndex = $(evt.currentTarget).index();
let columnDef = options.columns[currentColIndex];
let currentData = _instance.getData({ convert: false }); // current data to allow user to validate based on existing data
let currentData = _instance.getData({
convert: false
}); // current data to allow user to validate based on existing data
let isValid = columnDef.isValid && columnDef.isValid(newValue, currentData);
return isValid;
});

$('table td').on('change', function (evt, newValue) {
$('table td').on('change', function(evt, newValue) {
let td = $(this);
let currentColIndex = $(evt.currentTarget).index();
let columnDef = options.columns[currentColIndex];
Expand All @@ -209,7 +208,8 @@ $.fn.editableTable = function (options) {

// Bind user-specified events if they exist
if (typeof columnDef.afterChange == 'function') {
columnDef.afterChange(newValue, td);
let updatedValue = columnDef.afterChange(newValue, td);
if (updatedValue) td.text(updatedValue);
}

return true;
Expand All @@ -218,7 +218,7 @@ $.fn.editableTable = function (options) {
// Set up the instance reference
_instance = {
// Get table back out as JSON
getData: function (opts) {
getData: function(opts) {
opts = $.extend({}, {
convert: true
}, opts);
Expand Down Expand Up @@ -253,7 +253,7 @@ $.fn.editableTable = function (options) {
},

// Add a new row with JSON
addRow: function (row) {
addRow: function(row) {
let newRow = $(`<tr></tr>`);

if (row !== undefined && row !== null) {
Expand All @@ -271,41 +271,40 @@ $.fn.editableTable = function (options) {
def: columnDef
});
}
})
});

columnsToAdd.sort((a, b) => a.order - b.order).forEach((colToAdd, index) => {
let newCell;
if (colToAdd.value !== null)
newCell = $(`<td>${colToAdd.value}</td>`);
else
newCell = $(`<td data-is-null></td>`);
// Apply any classes

// Apply any classes
if (colToAdd.def.classes !== undefined && colToAdd.def.classes.length) {
colToAdd.def.classes.forEach(classToAdd => newCell.addClass(classToAdd));
}

// Apply any style
// Apply any style
if (colToAdd.def.style !== undefined && colToAdd.def.style.length) {
newCell.attr("style", newCell.attr("style") + "; " + colToAdd.def.style);
}

// Hide if hidden
if (colToAdd.def.isHidden !== undefined && colToAdd.def.isHidden) {
newCell.hide();
}

// Add to the column
newRow.append(newCell);
}

// Trigger any events
let columnDef = options.columns.filter(col => col.name === colToAdd.prop)[0];
if (typeof columnDef.afterAdd == 'function') {
columnDef.afterAdd(colToAdd.value, newCell);
}
});

}
else {
// Add to the column
newRow.append(newCell);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@avp90 the only issue I can foresee with this is that I had run newRow.append(newCell); BEFORE afterAdd handling because the user's custom afterAdd callback might want to climb up the newCell td and interact with the tr/row itself. I had wanted to only run a column's afterAdd after it was physically added to the row. If it is run any earlier, then it does not exist in the row yet and would be slightly more limited.

Was there a reason you moved it to after the callback handler?

Copy link
Author

@avp90 avp90 Sep 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes sure it has a reason, but I don't know anymore why. 😄
Maybe to modify the newCell before add 🤔

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm if you're not sure of the reason, would you mind please putting the .append back before the afterAdd handler and check to see if your changes still work? I just already know of some personal usage that will break otherwise - unless it was required for your change

});
} else {
newRow = $(`<tr></tr>`);
activeOptions.columns.forEach(x => {
newRow.append(`<td></td>`);
Expand All @@ -314,29 +313,28 @@ $.fn.editableTable = function (options) {

// Add the new row
let lastRow = element.find('tbody tr:last');
if (lastRow.length > 0)
lastRow.after(newRow);
else
element.find('tbody').append(newRow);
if (lastRow.length > 0) lastRow.after(newRow);
else element.find('tbody').append(newRow);

refresh();
},

// Clear the table
clear: function () {
clear: function() {
element.find('tbody tr').remove();
},

// Set the table's data with JSON
setData: function (data) {
setData: function(data, afterLoad) {
if (data) {
this.clear();
data.forEach(datum => {
this.addRow(datum);
data.forEach(entry => {
this.addRow(entry);
});
if (typeof afterLoad == 'function') afterLoad();
}
}

};
return _instance;
};
};
2 changes: 1 addition & 1 deletion jquery-editable-table.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.