From 657fbc91deb7cba1faa25ac9a9de58d7f8f56f51 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 18 May 2016 18:15:35 +0530 Subject: [PATCH] [cleaup] direct editing on grids --- frappe/public/css/form_grid.css | 15 +- frappe/public/js/frappe/dom.js | 17 ++ frappe/public/js/frappe/form/control.js | 39 ++- frappe/public/js/frappe/form/grid.js | 322 +++++++++++++++--------- frappe/public/js/frappe/model/meta.js | 12 +- frappe/public/less/form_grid.less | 24 +- 6 files changed, 277 insertions(+), 152 deletions(-) diff --git a/frappe/public/css/form_grid.css b/frappe/public/css/form_grid.css index 07985a78a7..583cbf8c9b 100644 --- a/frappe/public/css/form_grid.css +++ b/frappe/public/css/form_grid.css @@ -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; } diff --git a/frappe/public/js/frappe/dom.js b/frappe/public/js/frappe/dom.js index dcd50335fd..66f33e153e 100644 --- a/frappe/public/js/frappe/dom.js +++ b/frappe/public/js/frappe/dom.js @@ -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; diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js index 34e8bd5ddb..7dbeb2d29b 100644 --- a/frappe/public/js/frappe/form/control.js +++ b/frappe/public/js/frappe/form/control.js @@ -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(); diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index e8fffc9c94..51300772f5 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -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 = $('
').appendTo(this.parent).data("grid_row", this); this.row = $('
').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 = $('\ - ') - .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 = $('\ + ') + .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 = $('
') - .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 = $('
') + .attr("data-fieldname", df.fieldname) + .data("df", df) + .appendTo(this.row); + + $col.field_area = $('
').appendTo($col).toggle(false); + $col.static_area = $('
').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)); } diff --git a/frappe/public/js/frappe/model/meta.js b/frappe/public/js/frappe/model/meta.js index 3fda79d0d5..b768b85960 100644 --- a/frappe/public/js/frappe/model/meta.js +++ b/frappe/public/js/frappe/model/meta.js @@ -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) { diff --git a/frappe/public/less/form_grid.less b/frappe/public/less/form_grid.less index 01d99644c6..b49aeeb744 100644 --- a/frappe/public/less/form_grid.less +++ b/frappe/public/less/form_grid.less @@ -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; + } } }