Browse Source

[cleaup] direct editing on grids

version-14
Rushabh Mehta 9 years ago
parent
commit
657fbc91de
6 changed files with 277 additions and 152 deletions
  1. +10
    -5
      frappe/public/css/form_grid.css
  2. +17
    -0
      frappe/public/js/frappe/dom.js
  3. +27
    -12
      frappe/public/js/frappe/form/control.js
  4. +198
    -124
      frappe/public/js/frappe/form/grid.js
  5. +8
    -4
      frappe/public/js/frappe/model/meta.js
  6. +17
    -7
      frappe/public/less/form_grid.less

+ 10
- 5
frappe/public/css/form_grid.css View File

@@ -52,21 +52,26 @@
text-align: right;
width: 30px;
margin-left: -15px;
margin-top: 1px;
float: left;
}
.editable-row {
margin-right: 15px !important;
}
.editable-row .row-index {
.editable-row .col {
padding: 0px 7.5px;
}
.grid-body .btn-open-row {
margin-top: -3px;
}
.grid-body .editable-row .row-index {
margin-top: 7px;
}
.editable-row .close {
.grid-body .editable-row .btn-open-row {
margin-top: 0px;
padding: 3px 7px;
margin-right: -20px;
}
.editable-row .col {
padding: 0px 7.5px;
}
.row-data > .row {
margin-left: 15px;
}


+ 17
- 0
frappe/public/js/frappe/dom.js View File

@@ -36,6 +36,23 @@ frappe.dom = {
});
return div.innerHTML;
},
is_element_in_viewport: function (el) {

//special bonus for those using jQuery
if (typeof jQuery === "function" && el instanceof jQuery) {
el = el[0];
}

var rect = el.getBoundingClientRect();

return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
);
},

set_style: function(txt, id) {
if(!txt) return;



+ 27
- 12
frappe/public/js/frappe/form/control.js View File

@@ -248,6 +248,24 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
// mandatory style on refresh
setup_update_on_refresh: function() {
var me = this;

var make_input = function() {
if(!me.has_input) {
me.make_input();
if(me.df.on_make) {
me.df.on_make(me);
}
}
}

var update_input = function() {
if(me.doctype && me.docname) {
me.set_input(me.value);
} else {
me.set_input(me.value || null);
}
}

this.$wrapper.on("refresh", function() {
if(me.disp_status != "None") {
// refresh value
@@ -259,19 +277,16 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
me.disp_area && $(me.disp_area).toggle(false);
$(me.input_area).toggle(true);
$(me.input_area).find("input").prop("disabled", false);
if(!me.has_input) {
me.make_input();
if(me.df.on_make) {
me.df.on_make(me);
}
};
if(me.doctype && me.docname) {
me.set_input(me.value);
} else {
me.set_input(me.value || null);
}
make_input();
update_input();
} else {
$(me.input_area).toggle(false);
if(me.only_input) {
// show disabled input if only_input is true
// since there is no disp_area
make_input();
update_input();
}
$(me.input_area).toggle(me.only_input ? true : false);
$(me.input_area).find("input").prop("disabled", true);
if (me.disp_area) {
me.set_disp_area();


+ 198
- 124
frappe/public/js/frappe/form/grid.js View File

@@ -13,10 +13,12 @@ frappe.ui.form.close_grid_form = function() {

frappe.ui.form.Grid = Class.extend({
init: function(opts) {
var me = this;
$.extend(this, opts);
this.fieldinfo = {};
this.doctype = this.df.options;
this.meta = frappe.get_meta(this.doctype);
this.fields_map = {};
this.template = null;
this.multiple_set = false;
if(this.frm.meta.__form_grid_templates
@@ -28,10 +30,21 @@ frappe.ui.form.Grid = Class.extend({
if(this.meta.fields.length < 4 && !has_common(['Text', 'Small Text', 'Image',
'Text Editor', 'HTML', 'Section Break', 'Column Break'],
$.map(this.meta.fields, function(f) { return f.fieldtype }))) {
this.editable_rows = true;
this.on_grid_editing = true;
}

this.is_grid = true;
},

allow_on_grid_editing: function() {
if(frappe.utils.is_xs()) {
return false;
} else if(this.editable_fields) {
return true;
} else {
return this.on_grid_editing;
}
},
make: function() {
var me = this;

@@ -68,10 +81,6 @@ frappe.ui.form.Grid = Class.extend({
$rows = $(me.parent).find(".rows"),
data = this.get_data();

if(frappe.utils.is_xs()) {
this.editable_rows = false;
}

if (this.frm && this.frm.docname) {
// use doc specific docfield object
this.df = frappe.meta.get_docfield(this.frm.doctype, this.df.fieldname, this.frm.docname);
@@ -81,6 +90,11 @@ frappe.ui.form.Grid = Class.extend({
}

this.docfields = frappe.meta.get_docfields(this.doctype, this.frm.docname);

this.docfields.forEach(function(df) {
me.fields_map[df.fieldname] = df;
});

this.display_status = frappe.perm.get_field_display_status(this.df, this.frm.doc,
this.perm);

@@ -251,7 +265,7 @@ frappe.ui.form.Grid = Class.extend({
this.frm.script_manager.trigger(this.df.fieldname + "_add", d.doctype, d.name);
this.refresh();

if(show && !this.editable_rows) {
if(show && !this.allow_on_grid_editing()) {
if(idx) {
this.wrapper.find("[data-idx='"+idx+"']").data("grid_row")
.toggle_view(true, callback);
@@ -263,6 +277,81 @@ frappe.ui.form.Grid = Class.extend({
return d;
}
},

make_static_display_template: function() {
if(this.static_display_template) return;

var total_colsize = 1,
fields = this.editable_fields || this.docfields;

this.static_display_template = [];

for(var ci in fields) {
var _df = fields[ci];

// get docfield if from fieldname
df = this.fields_map[_df.fieldname];

if(!df) {
throw 'field not found: ' + _df.fieldname;
}

// map columns
if(_df.columns) {
df.colsize = _df.columns;
}

if(!df.hidden
&& (this.editable_fields || df.in_list_view)
&& this.frm.get_perm(df.permlevel, "read")
&& !in_list(frappe.model.layout_fields, df.fieldtype)) {
if(!df.colsize) {
var colsize = 2;
switch(df.fieldtype) {
case "Text":
case "Small Text":
colsize = 3;
break;
case "Check":
colsize = 1;
break;
}
df.colsize = colsize;
}

total_colsize += df.colsize
if(total_colsize > 12)
return false;
this.static_display_template.push([df, df.colsize]);
}
}

// redistribute if total-col size is less than 12
var passes = 0;
while(total_colsize < 12 && passes < 12) {
for(var i in this.static_display_template) {
var df = this.static_display_template[i][0];
var colsize = this.static_display_template[i][1];
if(colsize > 1 && colsize < 12
&& !in_list(frappe.model.std_fields_list, df.fieldname)) {

if (passes < 3 && ["Int", "Currency", "Float", "Check", "Percent"].indexOf(df.fieldtype)!==-1) {
// don't increase col size of these fields in first 3 passes
continue;
}

this.static_display_template[i][1] += 1;
total_colsize++;
}

if(total_colsize >= 12)
break;
}
passes++;
}
},


is_editable: function() {
return this.display_status=="Write" && !this.static_rows
},
@@ -416,7 +505,9 @@ frappe.ui.form.GridRow = Class.extend({
this.wrapper = $('<div class="grid-row"></div>').appendTo(this.parent).data("grid_row", this);
this.row = $('<div class="data-row row sortable-handle"></div>').appendTo(this.wrapper)
.on("click", function() {
if(!me.grid.editable_rows) {
if(me.grid.allow_on_grid_editing() && me.grid.is_editable()) {
// pass
} else {
me.toggle_view();
return false;
}
@@ -463,7 +554,7 @@ frappe.ui.form.GridRow = Class.extend({
this.doc = locals[this.doc.doctype][this.doc.name];
}
// re write columns
this.grid.static_display_template = null;
this.static_display_template = null;
this.make_static_display(true);

// refersh form fields
@@ -495,28 +586,32 @@ frappe.ui.form.GridRow = Class.extend({
this.add_visible_columns();
}

if(this.doc && this.grid.editable_rows) {
// remove row
if(!this.remove_row) {
this.row.addClass('editable-row');
this.remove_row = $('<a class="close pull-right">\
<span class="octicon octicon-chevron-down"</a>')
.appendTo(this.row)
.on('click', function() { me.toggle_view(); return false; });
if(this.grid.allow_on_grid_editing()) {
this.row.toggleClass('editable-row', this.grid.is_editable());
if(this.doc) {
// remove row
if(!this.remove_row) {
this.remove_row = $('<a class="close pull-right btn-open-row">\
<span class="octicon octicon-chevron-down"</a>')
.appendTo(this.row)
.on('click', function() { me.toggle_view(); return false; });
}
}
}


$(this.frm.wrapper).trigger("grid-row-render", [this]);
},

add_visible_columns: function() {
var me = this,
focus_set = false;
this.make_static_display_template();
for(var ci in this.static_display_template) {
var df = this.static_display_template[ci][0],
colsize = this.static_display_template[ci][1],
add_class = '',
var me = this;
this.focus_set = false;
this.grid.make_static_display_template();

for(var ci in this.grid.static_display_template) {

var df = this.grid.static_display_template[ci][0],
colsize = this.grid.static_display_template[ci][1],
txt = this.doc ?
frappe.format(this.doc[df.fieldname], df, null, this.doc) :
__(df.label);
@@ -526,128 +621,105 @@ frappe.ui.form.GridRow = Class.extend({
}

if(!this.columns[df.fieldname]) {
if(!this.grid.editable_rows || !this.doc) {
add_class = ' grid-static-col ' +
((["Text", "Small Text"].indexOf(df.fieldtype)===-1) ?
" grid-overflow-ellipsis" : " grid-overflow-no-ellipsis");
}
add_class += (["Int", "Currency", "Float", "Percent"].indexOf(df.fieldtype)!==-1) ?
" text-right": "";

$col = $('<div class="col col-xs-'+colsize+add_class+'"></div>')
.html(txt)
.attr("data-fieldname", df.fieldname)
.data("df", df)
.appendTo(this.row)

this.columns[df.fieldname] = $col;

if(this.grid.editable_rows && this.doc) {
$col.empty();
var field = frappe.ui.form.make_control({
df: df,
parent: $col,
only_input: true,
doctype: this.doc.doctype,
docname: this.doc.name,
frm: this.grid.frm
});

// sync get_query
field.get_query = this.grid.get_field(df.fieldname).get_query;
field.refresh();
if(field.$input) {
field.$input.addClass('input-sm');
field.$input.attr('data-col-idx', ci);
field.$input.on('keydown', function(e) {
// TAB
if(e.which==9) {
// last row, last column
if(cint($(this).attr('data-col-idx')) === me.static_display_template.length-1 &&
me.doc.idx===me.frm.doc[me.grid.df.fieldname].length) {

setTimeout(function() {
me.grid.add_new_row(null, null, true);
}, 500);

}
}
});
if(!focus_set) {
field.$input.focus();
focus_set = true;
}
}
this.on_grid_fields_dict[df.fieldname] = field;
}
var column = this.make_column(df, colsize, txt, ci);
} else {
var column = this.columns[df.fieldname]
// reset static value
column.static_area.html(txt);

// reset field value
if(this.on_grid_fields_dict[df.fieldname]) {
// reset
this.on_grid_fields_dict[df.fieldname].docname = this.doc.name;
this.on_grid_fields_dict[df.fieldname].refresh();
} else {
// reset value
this.columns[df.fieldname].html(txt);
}
}

// show static for field based on
// whether grid is editable
if(this.grid.is_editable() && this.on_grid_fields_dict[df.fieldname] && this.doc) {
column.static_area.toggle(false);
column.field_area.toggle(true);
} else {
column.static_area.toggle(true);
column.field_area && column.field_area.toggle(false);
}
}

},

make_static_display_template: function() {
if(this.static_display_template) return;
make_column: function(df, colsize, txt, ci) {
var me = this,
add_class = '';
if(!this.grid.allow_on_grid_editing() || !this.doc) {
add_class = ' grid-static-col ' +
((["Text", "Small Text"].indexOf(df.fieldtype)===-1) ?
" grid-overflow-ellipsis" : " grid-overflow-no-ellipsis");
}
add_class += (["Int", "Currency", "Float", "Percent"].indexOf(df.fieldtype)!==-1) ?
" text-right": "";

var total_colsize = 1;
this.static_display_template = [];
for(var ci in this.docfields) {
var df = this.docfields[ci];
if(!df.hidden && df.in_list_view && this.grid.frm.get_perm(df.permlevel, "read")
&& !in_list(frappe.model.layout_fields, df.fieldtype)) {
var colsize = 2;
switch(df.fieldtype) {
case "Text":
case "Small Text":
colsize = 3;
break;
case "Check":
colsize = 1;
break;
}
total_colsize += colsize
if(total_colsize > 12)
return false;
this.static_display_template.push([df, colsize]);
}
$col = $('<div class="col col-xs-'+colsize+add_class+'"></div>')
.attr("data-fieldname", df.fieldname)
.data("df", df)
.appendTo(this.row);

$col.field_area = $('<div class="field-area"></div>').appendTo($col).toggle(false);
$col.static_area = $('<div class="static-area"></div>').appendTo($col).html(txt);

this.columns[df.fieldname] = $col;

if(this.grid.allow_on_grid_editing() && this.doc) {
me.make_control($col.field_area, df, ci);
}

// redistribute if total-col size is less than 12
var passes = 0;
while(total_colsize < 12 && passes < 12) {
for(var i in this.static_display_template) {
var df = this.static_display_template[i][0];
var colsize = this.static_display_template[i][1];
if(colsize > 1 && colsize < 12
&& !in_list(frappe.model.std_fields_list, df.fieldname)) {
return $col;
},

if (passes < 3 && ["Int", "Currency", "Float", "Check", "Percent"].indexOf(df.fieldtype)!==-1) {
// don't increase col size of these fields in first 3 passes
continue;
}
make_control: function(parent, df, ci) {
var me = this;
var field = frappe.ui.form.make_control({
df: df,
parent: parent,
only_input: true,
doctype: this.doc.doctype,
docname: this.doc.name,
frm: this.grid.frm
});

this.static_display_template[i][1] += 1;
total_colsize++;
// sync get_query
field.get_query = this.grid.get_field(df.fieldname).get_query;
field.refresh();
if(field.$input) {
field.$input.addClass('input-sm');
field.$input.attr('data-col-idx', ci).attr('placeholder', __(df.label));
field.$input.on('keydown', function(e) {
// TAB
if(e.which==9) {
// last row, last column
if(cint($(this).attr('data-col-idx')) === me.grid.static_display_template.length-1 &&
me.doc.idx===me.frm.doc[me.grid.df.fieldname].length) {

setTimeout(function() {
me.grid.add_new_row(null, null, true);
}, 500);
}
}

if(total_colsize >= 12)
break;
});
if(!this.focus_set) {
setTimeout(function() {
field.$input.focus();
}, 500);
this.focus_set = true;
}
passes++;
}
this.on_grid_fields_dict[df.fieldname] = field;

},

get_open_form: function() {
return frappe.ui.form.get_open_grid_form();
},

toggle_view: function(show, callback) {
if(!this.doc) {
return this;
@@ -693,7 +765,9 @@ frappe.ui.form.GridRow = Class.extend({
frappe.dom.freeze("", "dark");
cur_frm.cur_grid = this;
this.wrapper.addClass("grid-row-open");
frappe.utils.scroll_to(this.wrapper, true, 15);
if(!frappe.dom.is_element_in_viewport) {
frappe.utils.scroll_to(this.wrapper, true, 15);
}
this.frm.script_manager.trigger(this.doc.parentfield + "_on_form_rendered");
this.frm.script_manager.trigger("form_render", this.doc.doctype, this.doc.name);
this.set_focus();
@@ -799,7 +873,7 @@ frappe.ui.form.GridRow = Class.extend({
},
refresh_field: function(fieldname) {
var $col = this.row.find("[data-fieldname='"+fieldname+"']");
if($col.length && !this.grid.editable_rows) {
if($col.length && !this.grid.allow_on_grid_editing()) {
$col.html(frappe.format(this.doc[fieldname], this.grid.get_docfield(fieldname),
null, this.frm.doc));
}


+ 8
- 4
frappe/public/js/frappe/model/meta.js View File

@@ -52,12 +52,16 @@ $.extend(frappe.meta, {
}
},

get_field: function(dt, fn, dn) {
return frappe.meta.get_docfield(dt, fn, dn);
get_field: function(doctype, fieldname, name) {
return frappe.meta.get_docfield(doctype, fieldname, name);
},

get_docfield: function(dt, fn, dn) {
return frappe.meta.get_docfield_copy(dt, dn)[fn];
get_docfield: function(doctype, fieldname, name) {
return frappe.meta.get_docfield_copy(doctype, name)[fieldname];
},

set_formatter: function(doctype, fieldname, name, formatter) {
frappe.meta.get_docfield(doctype, fieldname, name).formatter = formatter;
},

get_docfields: function(doctype, name, filters) {


+ 17
- 7
frappe/public/less/form_grid.less View File

@@ -63,23 +63,33 @@
text-align: right;
width: 30px;
margin-left: -15px;
margin-top: 1px;
float: left;
}

.editable-row {
margin-right: 15px !important;

.row-index {
margin-top: 7px;
.col {
padding: 0px 7.5px;
}
}

.close {
padding: 3px 7px;
margin-right: -20px;
.grid-body {
.btn-open-row {
margin-top: -3px;
}

.col {
padding: 0px 7.5px;
.editable-row {
.row-index {
margin-top: 7px;
}

.btn-open-row {
margin-top: 0px;
padding: 3px 7px;
margin-right: -20px;
}
}
}



Loading…
Cancel
Save