summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/example-composite-editor-item-details.html233
-rw-r--r--examples/examples.css2
-rw-r--r--examples/slick.compositeeditor.js213
-rw-r--r--slick.grid.js18
4 files changed, 458 insertions, 8 deletions
diff --git a/examples/example-composite-editor-item-details.html b/examples/example-composite-editor-item-details.html
new file mode 100644
index 0000000..978067f
--- /dev/null
+++ b/examples/example-composite-editor-item-details.html
@@ -0,0 +1,233 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>SlickGrid example: CompositeEditor</title>
+ <link rel="stylesheet" href="../slick.grid.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="../css/smoothness/jquery-ui-1.8.5.custom.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="examples.css" type="text/css" media="screen" charset="utf-8" />
+ <style>
+ .cell-title {
+ font-weight: bold;
+ }
+
+ .cell-effort-driven {
+ text-align: center;
+ }
+
+ .item-details-form {
+ display:inline-block;
+ border:1px solid black;
+ margin:8px;
+ padding:10px;
+ background: #efefef;
+ -moz-box-shadow: 0px 0px 15px black;
+ -webkit-box-shadow: 0px 0px 15px black;
+ box-shadow: 0px 0px 15px black;
+
+ position:absolute;
+ top: 10px;
+ left: 150px;
+ }
+
+ .item-details-form-buttons {
+ float: right;
+ }
+
+ .item-details-row {
+
+ }
+
+ .item-details-label {
+ margin-left:10px;
+ margin-top:20px;
+ display:block;
+ font-weight:bold;
+ }
+
+ .item-details-editor-container {
+ width:200px;
+ height:20px;
+ border:1px solid silver;
+ background:white;
+ display:block;
+ margin:10px;
+ margin-top:4px;
+ padding:0;
+ padding-left:4px;
+ padding-right:4px;
+ }
+ </style>
+ </head>
+ <body>
+ <div style="width:600px;float:left;">
+ <div id="myGrid" style="width:100%;height:500px;"></div>
+ </div>
+
+ <div class="options-panel" style="width:320px;margin-left:650px;">
+ <h2>Demonstrates:</h2>
+ <ul>
+ <li>using a CompositeEditor to implement detached item edit form</li>
+ </ul>
+
+ <h2>Options:</h2>
+ <button onclick="openDetails()">Open Item Edit for active row</button>
+
+
+ </div>
+
+
+ <script id="itemDetailsTemplate" type="text/x-jquery-tmpl">
+ <div class='item-details-form'>
+ {{each columns}}
+ <div class='item-details-label'>
+ ${name}
+ </div>
+ <div class='item-details-editor-container' data-editorid='${id}'></div>
+ {{/each}}
+
+ <hr/>
+ <div class='item-details-form-buttons'>
+ <button data-action='save'>Save</button>
+ <button data-action='cancel'>Cancel</button>
+ </div>
+ </div>
+ </script>
+
+
+
+ <script src="../lib/firebugx.js"></script>
+
+ <script src="../lib/jquery-1.4.3.min.js"></script>
+ <script src="../lib/jquery-ui-1.8.5.custom.min.js"></script>
+ <script src="../lib/jquery.event.drag-2.0.min.js"></script>
+ <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
+
+ <script src="../slick.core.js"></script>
+ <script src="../plugins/slick.cellrangeselector.js"></script>
+ <script src="../plugins/slick.cellselectionmodel.js"></script>
+ <script src="../slick.editors.js"></script>
+ <script src="../slick.grid.js"></script>
+ <script src="slick.compositeeditor.js"></script>
+
+ <script>
+ function requiredFieldValidator(value) {
+ if (value == null || value == undefined || !value.length)
+ return {valid:false, msg:"This is a required field"};
+ else
+ return {valid:true, msg:null};
+ }
+
+ var grid;
+ var data = [];
+ var columns = [
+ {id:"title", name:"Title", field:"title", width:120, cssClass:"cell-title", editor:TextCellEditor, validator:requiredFieldValidator},
+ {id:"desc", name:"Description", field:"description", width:100, editor:TextCellEditor},
+ {id:"duration", name:"Duration", field:"duration", editor:TextCellEditor},
+ {id:"%", name:"% Complete", field:"percentComplete", width:80, resizable:false, formatter:GraphicalPercentCompleteCellFormatter, editor:PercentCompleteCellEditor},
+ {id:"start", name:"Start", field:"start", minWidth:60, editor:DateCellEditor},
+ {id:"finish", name:"Finish", field:"finish", minWidth:60, editor:DateCellEditor},
+ {id:"effort-driven", name:"Effort Driven", width:80, minWidth:20, maxWidth:80, cssClass:"cell-effort-driven", field:"effortDriven", formatter:BoolCellFormatter, editor:YesNoCheckboxCellEditor}
+ ];
+ var options = {
+ editable: true,
+ enableAddRow: true,
+ enableCellNavigation: true,
+ asyncEditorLoading: false,
+ autoEdit: false
+ };
+
+
+
+ function openDetails() {
+ if (grid.getEditorLock().isActive() && !grid.getEditorLock().commitCurrentEdit()) {
+ return;
+ }
+
+ var $modal = $("<div class='item-details-form'></div>");
+
+ $modal = $("#itemDetailsTemplate")
+ .tmpl({
+ context: grid.getDataItem(grid.getActiveCell().row),
+ columns: columns
+ })
+ .appendTo("body");
+
+ $modal.keydown(function(e) {
+ if (e.which == $.ui.keyCode.ENTER) {
+ grid.getEditController().commitCurrentEdit();
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ else if (e.which == $.ui.keyCode.ESCAPE) {
+ grid.getEditController().cancelCurrentEdit();
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ });
+
+ $modal.find("[data-action=save]").click(function() {
+ grid.getEditController().commitCurrentEdit();
+ });
+
+ $modal.find("[data-action=cancel]").click(function() {
+ grid.getEditController().cancelCurrentEdit();
+ });
+
+
+ var containers = $.map(columns, function(c) { return $modal.find("[data-editorid=" + c.id + "]"); });
+
+ var compositeEditor = new Slick.CompositeEditor(
+ columns,
+ containers,
+ {
+ destroy: function () { $modal.remove(); }
+ }
+ );
+
+ grid.editActiveCell(compositeEditor);
+ }
+
+ $(function()
+ {
+ for (var i=0; i<500; i++) {
+ var d = (data[i] = {});
+
+ d["title"] = "Task " + i;
+ d["description"] = "This is a sample task description.\n It can be multiline";
+ d["duration"] = "5 days";
+ d["percentComplete"] = Math.round(Math.random() * 100);
+ d["start"] = "01/01/2009";
+ d["finish"] = "01/05/2009";
+ d["effortDriven"] = (i % 5 == 0);
+ }
+
+ grid = new Slick.Grid("#myGrid", data, columns, options);
+
+ grid.onAddNewRow.subscribe(function(e, args) {
+ var item = args.item;
+ var column = args.column;
+ grid.invalidateRow(data.length);
+ data.push(item);
+ grid.updateRowCount();
+ grid.render();
+ });
+
+
+ grid.onValidationError.subscribe(function(e, args) {
+ // handle validation errors originating from the CompositeEditor
+ if (args.editor && (args.editor instanceof Slick.CompositeEditor)) {
+ var err;
+ var idx = args.validationResults.errors.length;
+ while (idx--) {
+ err = args.validationResults.errors[idx];
+ $(err.container).stop(true,true).effect("highlight", {color:"red"});
+ }
+ }
+ });
+
+ grid.setActiveCell(0, 0);
+ })
+ </script>
+ </body>
+</html>
diff --git a/examples/examples.css b/examples/examples.css
index 9f7abdc..2f413eb 100644
--- a/examples/examples.css
+++ b/examples/examples.css
@@ -137,7 +137,7 @@ input.editor-text {
}
-.slick-cell .ui-datepicker-trigger {
+.ui-datepicker-trigger {
margin-top: 2px;
padding: 0;
vertical-align: top;
diff --git a/examples/slick.compositeeditor.js b/examples/slick.compositeeditor.js
new file mode 100644
index 0000000..9dbbc41
--- /dev/null
+++ b/examples/slick.compositeeditor.js
@@ -0,0 +1,213 @@
+;(function($) {
+
+ $.extend(true, window, {
+ Slick: {
+ CompositeEditor: CompositeEditor
+ }
+ });
+
+
+ /***
+ * A composite SlickGrid editor factory.
+ * Generates an editor that is composed of multiple editors for given columns.
+ * Individual editors are provided given containers instead of the original cell.
+ * Validation will be performed on all editors individually and the results will be aggregated into one
+ * validation result.
+ *
+ *
+ * The returned editor will have its prototype set to CompositeEditor, so you can use the "instanceof" check.
+ *
+ * NOTE: This doesn't work for detached editors since they will be created and positioned relative to the
+ * active cell and not the provided container.
+ *
+ * @namespace Slick
+ * @class CompositeEditor
+ * @constructor
+ * @param columns {Array} Column definitions from which editors will be pulled.
+ * @param containers {Array} Container HTMLElements in which editors will be placed.
+ * @param options {Object} Options hash:
+ * validationFailedMsg - A generic failed validation message set on the aggregated validation resuls.
+ * hide - A function to be called when the grid asks the editor to hide itself.
+ * show - A function to be called when the grid asks the editor to show itself.
+ * position - A function to be called when the grid asks the editor to reposition itself.
+ * destroy - A function to be called when the editor is destroyed.
+ */
+ function CompositeEditor(columns, containers, options) {
+ var defaultOptions = {
+ validationFailedMsg: "Some of the fields have failed validation",
+ show: null,
+ hide: null,
+ position: null,
+ destroy: null
+ };
+
+ var noop = function () {};
+
+ var firstInvalidEditor;
+
+ options = $.extend({}, defaultOptions, options);
+
+
+ function getContainerBox(i) {
+ var c = containers[i];
+ var offset = $(c).offset();
+ var w = $(c).width();
+ var h = $(c).height();
+
+ return {
+ top: offset.top,
+ left: offset.left,
+ bottom: offset.top + h,
+ right: offset.left + w,
+ width: w,
+ height: h,
+ visible: true
+ };
+ }
+
+
+ function editor(args) {
+ var editors = [];
+
+
+ function init() {
+ var newArgs = {};
+ var idx = columns.length;
+ while (idx--) {
+ if (columns[idx].editor) {
+ newArgs = $.extend({}, args);
+ newArgs.container = containers[idx];
+ newArgs.column = columns[idx];
+ newArgs.position = getContainerBox(idx);
+ newArgs.commitChanges = noop;
+ newArgs.cancelChanges = noop;
+
+ editors[idx] = new (columns[idx].editor)(newArgs);
+ }
+ }
+ }
+
+
+ this.destroy = function () {
+ var idx = editors.length;
+ while (idx--) {
+ editors[idx].destroy();
+ }
+
+ options.destroy && options.destroy();
+ };
+
+
+ this.focus = function () {
+ // if validation has failed, set the focus to the first invalid editor
+ (firstInvalidEditor || editors[0]).focus();
+ };
+
+
+ this.isValueChanged = function () {
+ var idx = editors.length;
+ while (idx--) {
+ if (editors[idx].isValueChanged()) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+
+ this.serializeValue = function () {
+ var serializedValue = [];
+ var idx = editors.length;
+ while (idx--) {
+ serializedValue[idx] = editors[idx].serializeValue();
+ }
+ return serializedValue;
+ };
+
+
+ this.applyValue = function (item, state) {
+ var idx = editors.length;
+ while (idx--) {
+ editors[idx].applyValue(item, state[idx]);
+ }
+ };
+
+
+ this.loadValue = function (item) {
+ var idx = editors.length;
+ while (idx--) {
+ editors[idx].loadValue(item);
+ }
+ };
+
+
+ this.validate = function () {
+ var validationResults;
+ var errors = [];
+
+ firstInvalidEditor = null;
+
+ var idx = editors.length;
+ while (idx--) {
+ validationResults = editors[idx].validate();
+ if (!validationResults.valid) {
+ firstInvalidEditor = editors[idx];
+ errors.push({
+ index: idx,
+ editor: editors[idx],
+ container: containers[idx],
+ msg: validationResults.msg
+ });
+ }
+ }
+
+ if (errors.length) {
+ return {
+ valid: false,
+ msg: options.validationFailedMsg,
+ errors: errors
+ };
+ }
+ else {
+ return {
+ valid: true,
+ msg: ""
+ };
+ }
+ };
+
+
+ this.hide = function () {
+ var idx = editors.length;
+ while (idx--) {
+ editors[idx].hide && editors[idx].hide();
+ }
+
+ options.hide && options.hide();
+ };
+
+
+ this.show = function () {
+ var idx = editors.length;
+ while (idx--) {
+ editors[idx].show && editors[idx].show();
+ }
+
+ options.show && options.show();
+ };
+
+
+ this.position = function (box) {
+ options.position && options.position(box);
+ };
+
+
+ init();
+ }
+
+ // so we can do "editor instanceof Slick.CompositeEditor
+ editor.prototype = this;
+
+ return editor;
+ }
+})(jQuery); \ No newline at end of file
diff --git a/slick.grid.js b/slick.grid.js
index 6b7c815..1afeaee 100644
--- a/slick.grid.js
+++ b/slick.grid.js
@@ -1843,7 +1843,7 @@ if (typeof Slick === "undefined") {
getEditorLock().deactivate(editController);
}
- function makeActiveCellEditable() {
+ function makeActiveCellEditable(editor) {
if (!activeCellNode) { return; }
if (!options.editable) {
throw "Grid : makeActiveCellEditable : should never get called when options.editable is false";
@@ -1856,7 +1856,10 @@ if (typeof Slick === "undefined") {
return;
}
- if (trigger(self.onBeforeEditCell, {row:activeRow, cell:activeCell,item:getDataItem(activeRow)}) === false) {
+ var columnDef = columns[activeCell];
+ var item = getDataItem(activeRow);
+
+ if (trigger(self.onBeforeEditCell, {row:activeRow, cell:activeCell, item:item, column:columnDef}) === false) {
setFocus();
return;
}
@@ -1864,12 +1867,12 @@ if (typeof Slick === "undefined") {
getEditorLock().activate(editController);
$(activeCellNode).addClass("editable");
- activeCellNode.innerHTML = "";
-
- var columnDef = columns[activeCell];
- var item = getDataItem(activeRow);
+ // don't clear the cell if a custom editor is passed through
+ if (!editor) {
+ activeCellNode.innerHTML = "";
+ }
- currentEditor = new (getEditor(columnDef))({
+ currentEditor = new (editor || getEditor(columnDef))({
grid: self,
gridPosition: absBox($container[0]),
position: absBox(activeCellNode),
@@ -2192,6 +2195,7 @@ if (typeof Slick === "undefined") {
$(activeCellNode).stop(true,true).effect("highlight", {color:"red"}, 300);
trigger(self.onValidationError, {
+ editor: currentEditor,
cellNode: activeCellNode,
validationResults: validationResults,
row: activeRow,