diff --git a/frappe/public/css/desk.css b/frappe/public/css/desk.css index 22757905db..837388cc1d 100644 --- a/frappe/public/css/desk.css +++ b/frappe/public/css/desk.css @@ -238,6 +238,7 @@ a.form-link { border-radius: 2px; padding: 3px; display: none; + z-index: 3; } .link-primary { color: #5E64FF; diff --git a/frappe/public/css/form_grid.css b/frappe/public/css/form_grid.css index 67a0bb925b..065f40fe6f 100644 --- a/frappe/public/css/form_grid.css +++ b/frappe/public/css/form_grid.css @@ -30,9 +30,6 @@ .grid-body { background-color: #fff; } -.form-grid .data-row { - padding: 10px 15px; -} .form-grid .data-row.highlight { background-color: #fffdf4; } @@ -47,15 +44,21 @@ padding: 10px 15px; color: #d1d8dd; } -.grid-static-col { +.grid-static-col, +.row-index { + height: 39px; + padding: 10px 15px; max-height: 200px; - overflow: hidden; + border-right: 1px solid #d1d8dd; + margin-bottom: -1px; +} +.grid-static-col input[type="checkbox"] { + margin-left: -16px !important; } .row-index { text-align: right; - width: 30px; - margin-left: -15px; - margin-top: 1px; + width: 40px; + margin-left: -7px; float: left; } .grid-row > .row { @@ -63,41 +66,33 @@ } .grid-row > .row .col { padding-left: 10px; - padding-right: 0px; + padding-right: 10px; } .grid-body .btn-open-row { - margin-top: -3px; -} -.grid-body .editable-row { - padding: 0px 10px; - margin-bottom: -1px; -} -.grid-body .editable-row .row-index { - margin-top: 7px; + margin: -10px; + padding: 10px; + padding-top: 15px; } -.grid-body .editable-row .btn-open-row { - margin: -10px -4px; - padding: 15px 6px; - margin-right: -20px; +.grid-body .editable-row .grid-static-col { + padding: 0px !important; } .grid-body .editable-row .checkbox { margin: 0px; - margin-right: -7px; text-align: center; } .grid-body .editable-row textarea { height: 38px !important; } -.grid-body .editable-row .field-area { - margin-right: -11px; - margin-top: -1px; -} .grid-body .editable-row .form-control { border-radius: 0px; - padding-top: 9px; + border: 0px; + padding-top: 8px; padding-bottom: 9px; height: 38px; } +.grid-body .editable-row .link-btn { + top: 8px; +} .grid-body .editable-row .form-control:focus { border-color: #8D99A6; z-index: 2; @@ -113,6 +108,18 @@ .grid-body .editable-row input[data-fieldtype="Currency"] { text-align: right; } +@media (max-width: 767px) { + .grid-body .btn-open-row { + margin-top: 0px; + padding: 0px; + } + .editable-row .frappe-control { + padding-top: 0px !important; + padding-bottom: 0px !important; + margin-left: -5px !important; + margin-right: -5px !important; + } +} .row-data > .row { margin-left: 15px; } diff --git a/frappe/public/js/frappe/form/control.js b/frappe/public/js/frappe/form/control.js index dd1636dfa8..90998a7d30 100644 --- a/frappe/public/js/frappe/form/control.js +++ b/frappe/public/js/frappe/form/control.js @@ -1109,7 +1109,7 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ \ \ \ - \ + \ \ ').prependTo(this.input_area); this.$input_area = $(this.input_area); @@ -1118,21 +1118,23 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ this.$link_open = this.$link.find('.btn-open'); this.set_input_attributes(); this.$input.on("focus", function() { - var value = me.get_value(); - if(value && me.get_options()) { - me.$link.toggle(true); - me.$link_open.attr('href', '#Form/' + me.get_options() + '/' + value); - - } - setTimeout(function() { + if(me.$input.val() && me.get_options()) { + me.$link.toggle(true); + me.$link_open.attr('href', '#Form/' + me.get_options() + '/' + me.$input.val()); + } + if(!me.$input.val()) { me.$input.autocomplete("search", ""); } }, 500); }); this.$input.on("blur", function() { - setTimeout(function() { me.$link.toggle(false); }, 500); + // if this disappears immediately, the user's click + // does not register, hence timeout + setTimeout(function() { + me.$link.toggle(false); + }, 500); }); this.input = this.$input.get(0); this.has_input = true; diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index 60c09873c0..9528a3ee2d 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -8,6 +8,11 @@ frappe.ui.form.get_open_grid_form = function() { frappe.ui.form.close_grid_form = function() { var open_form = frappe.ui.form.get_open_grid_form(); open_form && open_form.hide_form(); + + // hide editable row too + if(frappe.ui.form.editable_row) { + frappe.ui.form.editable_row.toggle_editable_row(false); + } } @@ -47,7 +52,7 @@ frappe.ui.form.Grid = Class.extend({ this.wrapper.find(".grid-add-row").click(function() { me.add_new_row(null, null, true); - me.set_focus_on_new_row(); + me.set_focus_on_row(); return false; }); @@ -299,12 +304,15 @@ frappe.ui.form.Grid = Class.extend({ } }, - set_focus_on_new_row: function() { + set_focus_on_row: function(idx) { var me = this; + if(!idx) { + idx = me.grid_rows.length - 1; + } setTimeout(function() { - me.grid_rows[me.grid_rows.length - 1].row + me.grid_rows[idx].row .find('input,textarea,select').filter(':visible:first').focus(); - }); + }, 100); }, setup_visible_columns: function() { @@ -527,6 +535,7 @@ frappe.ui.form.GridRow = Class.extend({ this.on_grid_fields_dict = {}; this.on_grid_fields = []; this.columns = {}; + this.columns_list = []; $.extend(this, opts); this.make(); }, @@ -548,6 +557,11 @@ frappe.ui.form.GridRow = Class.extend({ this.set_data(); } }, + set_data: function() { + this.wrapper.data({ + "doc": this.doc + }) + }, set_row_index: function() { if(this.doc) { this.wrapper @@ -607,26 +621,31 @@ frappe.ui.form.GridRow = Class.extend({ } this.setup_columns(); + this.add_open_form_button(); - 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; }); - - if(this.row.width() < 400) { - this.remove_row.css({'padding-right': '1px'}); - } - } - } + if(this.doc) { + $(this.frm.wrapper).trigger("grid-row-render", [this]); } + }, + make_editable: function() { + this.row.toggleClass('editable-row', this.grid.is_editable()); + }, + + add_open_form_button: function() { + var me = this; if(this.doc) { - $(this.frm.wrapper).trigger("grid-row-render", [this]); + // remove row + if(!this.open_form_button) { + this.open_form_button = $('\ + ') + .appendTo(this.row) + .on('click', function() { me.toggle_view(); return false; }); + + if(this.row.width() < 400) { + this.open_form_button.css({'padding-right': '1px'}); + } + } } }, @@ -653,48 +672,86 @@ frappe.ui.form.GridRow = Class.extend({ this.refresh_field(df.fieldname, 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_column: function(df, colsize, txt, ci) { - var 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"); - } + var me = this; + var add_class = ((["Text", "Small Text"].indexOf(df.fieldtype)!==-1) ? + " grid-overflow-no-ellipsis" : ""); add_class += (["Int", "Currency", "Float", "Percent"].indexOf(df.fieldtype)!==-1) ? " text-right": ""; + add_class += (["Check"].indexOf(df.fieldtype)!==-1) ? + " text-center": ""; - $col = $('
') + $col = $('
') .attr("data-fieldname", df.fieldname) .data("df", df) - .appendTo(this.row); + .appendTo(this.row) + .on('click', function() { + if(frappe.ui.form.editable_row===me) { + return; + } + out = me.toggle_editable_row(); + var col = this; + setTimeout(function() { + $(col).find(':input:first').focus(); + }, 500); + return out; + }); $col.field_area = $('
').appendTo($col).toggle(false); $col.static_area = $('
').appendTo($col).html(txt); + $col.df = df; + $col.column_index = ci; this.columns[df.fieldname] = $col; - - if(this.grid.allow_on_grid_editing() && this.doc) { - this.make_control($col.field_area, df, ci); - } + this.columns_list.push($col); return $col; }, - make_control: function(parent, df, ci) { + toggle_editable_row: function(show) { var me = this; + // show static for field based on + // whether grid is editable + if(this.grid.is_editable() && this.doc && show !== false) { + + // disable other editale row + if(frappe.ui.form.editable_row + && frappe.ui.form.editable_row !== this) { + frappe.ui.form.editable_row.toggle_editable_row(false); + }; + + this.row.toggleClass('editable-row', true); + + // setup controls + this.columns_list.forEach(function(column) { + me.make_control(column); + column.static_area.toggle(false); + column.field_area.toggle(true); + }); + frappe.ui.form.editable_row = this; + return false; + } else { + this.row.toggleClass('editable-row', false); + this.columns_list.forEach(function(column) { + column.static_area.toggle(true); + column.field_area && column.field_area.toggle(false); + }); + frappe.ui.form.editable_row = null; + } + }, + + + make_control: function(column) { + if(column.field) return; + + var me = this, + parent = column.field_area, + df = column.df; + var field = frappe.ui.form.make_control({ df: df, parent: parent, @@ -710,21 +767,53 @@ frappe.ui.form.GridRow = Class.extend({ field.refresh(); if(field.$input) { field.$input.addClass('input-sm'); - field.$input.attr('data-col-idx', ci).attr('placeholder', __(df.label)); + field.$input.attr('data-col-idx', column.column_index).attr('placeholder', __(df.label)); field.$input.on('keydown', function(e) { + var values = me.frm.doc[me.grid.df.fieldname]; + var fieldname = $(this).attr('data-fieldname'); // TAB - if(e.which==9) { - // last row, last column + if(e.which==TAB) { + // last column if(me.grid.wrapper.find('input:enabled:last').get(0)===this) { - setTimeout(function() { - me.grid.add_new_row(null, null, true); - me.grid.set_focus_on_new_row(); + if(me.doc.idx === values.length) { + // last row + me.grid.add_new_row(null, null, true); + me.grid.grid_rows[me.grid.grid_rows.length - 1].toggle_editable_row(); + me.grid.set_focus_on_row(); + } else { + me.grid.grid_rows[me.doc.idx].toggle_editable_row(); + me.grid.set_focus_on_row(me.doc.idx+1); + } }, 500); } + } else if(e.which==UP_ARROW) { + if(me.doc.idx > 1) { + var prev = me.grid.grid_rows[me.doc.idx-2]; + prev.toggle_editable_row(); + setTimeout(function() { + var input = prev.columns[fieldname].field.$input; + if(input) { + input.focus(); + } + }, 400) + } + } else if(e.which==DOWN_ARROW) { + if(me.doc.idx < values.length) { + var next = me.grid.grid_rows[me.doc.idx]; + next.toggle_editable_row(); + setTimeout(function() { + var input = next.columns[fieldname].field.$input; + if(input) { + input.focus(); + } + }, 400) + } } + }); } + column.field = field; this.on_grid_fields_dict[df.fieldname] = field; this.on_grid_fields.push(field); @@ -769,42 +858,24 @@ frappe.ui.form.GridRow = Class.extend({ return this; }, show_form: function() { - if(!this.form_panel) { - this.form_panel = $('
') - .appendTo(this.wrapper); + if(!this.grid_form) { + this.grid_form = new frappe.ui.form.GridRowForm({ + row: this + }); } - this.render_form(); + this.grid_form.render(); this.row.toggle(false); // this.form_panel.toggle(true); frappe.dom.freeze("", "dark"); cur_frm.cur_grid = this; this.wrapper.addClass("grid-row-open"); - if(!frappe.dom.is_element_in_viewport) { + if(!frappe.dom.is_element_in_viewport(this.wrapper)) { 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(); - }, - set_focus: function() { - // wait for animation and then focus on the first row - var me = this; - setTimeout(function() { - if(me.frm.doc.docstatus===0) { - var first = me.form_area.find(":input:first"); - if(first.length && !in_list(["Date", "Datetime", "Time"], first.attr("data-fieldtype"))) { - try { - first.get(0).focus(); - } catch(e) { - // - } - } - } - }, 500); }, hide_form: function() { - // if(this.form_panel) - // this.form_panel.toggle(false); frappe.dom.unfreeze(); this.row.toggle(true); this.render_row(); @@ -823,68 +894,6 @@ frappe.ui.form.GridRow = Class.extend({ this.grid.add_new_row(null, null, true); } }, - toggle_add_delete_button_display: function($parent) { - $parent.find(".grid-delete-row, .grid-insert-row, .grid-append-row") - .toggle(this.grid.is_editable()); - }, - render_form: function() { - var me = this; - this.make_form(); - this.form_area.empty(); - - this.layout = new frappe.ui.form.Layout({ - fields: this.docfields, - body: this.form_area, - no_submit_on_enter: true, - frm: this.frm, - }); - this.layout.make(); - - this.fields = this.layout.fields; - this.fields_dict = this.layout.fields_dict; - - this.layout.refresh(this.doc); - - // copy get_query to fields - for(var fieldname in (this.grid.fieldinfo || {})) { - var fi = this.grid.fieldinfo[fieldname]; - $.extend(me.fields_dict[fieldname], fi); - } - - this.toggle_add_delete_button_display(this.wrapper); - - this.grid.open_grid_row = this; - }, - make_form: function() { - if(!this.form_area) { - $(frappe.render_template("grid_form", {grid:this})).appendTo(this.form_panel); - this.form_area = this.wrapper.find(".form-area"); - this.set_row_index(); - this.set_form_events(); - } - }, - set_form_events: function() { - var me = this; - this.form_panel.find(".grid-delete-row") - .click(function() { me.remove(); return false; }) - this.form_panel.find(".grid-insert-row") - .click(function() { me.insert(true); return false; }) - this.form_panel.find(".grid-append-row") - .click(function() { - me.toggle_view(false); - me.grid.add_new_row(me.doc.idx+1, null, true); - return false; - }) - this.form_panel.find(".grid-form-heading, .grid-footer-toolbar").on("click", function() { - me.toggle_view(); - return false; - }); - }, - set_data: function() { - this.wrapper.data({ - "doc": this.doc - }) - }, refresh_field: function(fieldname, txt) { if(txt===undefined) { var txt = frappe.format(this.doc[fieldname], this.grid.get_docfield(fieldname), @@ -944,3 +953,87 @@ frappe.ui.form.GridRow = Class.extend({ this.set_field_property(fieldname, 'read_only', editable ? 0 : 1); }, }); + +frappe.ui.form.GridRowForm = Class.extend({ + init: function(opts) { + $.extend(this, opts); + this.wrapper = $('
') + .appendTo(this.row.wrapper); + + }, + render: function() { + var me = this; + this.make_form(); + this.form_area.empty(); + + this.layout = new frappe.ui.form.Layout({ + fields: this.row.docfields, + body: this.form_area, + no_submit_on_enter: true, + frm: this.row.frm, + }); + this.layout.make(); + + this.fields = this.layout.fields; + this.fields_dict = this.layout.fields_dict; + + this.layout.refresh(this.row.doc); + + // copy get_query to fields + for(var fieldname in (this.row.grid.fieldinfo || {})) { + var fi = this.row.grid.fieldinfo[fieldname]; + $.extend(me.fields_dict[fieldname], fi); + } + + this.toggle_add_delete_button_display(this.wrapper); + + this.row.grid.open_grid_row = this; + + this.set_focus(); + }, + make_form: function() { + if(!this.form_area) { + $(frappe.render_template("grid_form", {grid:this})).appendTo(this.wrapper); + this.form_area = this.wrapper.find(".form-area"); + this.row.set_row_index(); + this.set_form_events(); + } + }, + set_form_events: function() { + var me = this; + this.wrapper.find(".grid-delete-row") + .click(function() { me.row.remove(); return false; }) + this.wrapper.find(".grid-insert-row") + .click(function() { me.row.insert(true); return false; }) + this.wrapper.find(".grid-append-row") + .click(function() { + me.row.toggle_view(false); + me.row.grid.add_new_row(me.doc.idx+1, null, true); + return false; + }) + this.wrapper.find(".grid-form-heading, .grid-footer-toolbar").on("click", function() { + me.row.toggle_view(); + return false; + }); + }, + toggle_add_delete_button_display: function($parent) { + $parent.find(".grid-delete-row, .grid-insert-row, .grid-append-row") + .toggle(this.row.grid.is_editable()); + }, + set_focus: function() { + // wait for animation and then focus on the first row + var me = this; + setTimeout(function() { + if(me.row.frm.doc.docstatus===0) { + var first = me.form_area.find(":input:first"); + if(first.length && !in_list(["Date", "Datetime", "Time"], first.attr("data-fieldtype"))) { + try { + first.get(0).focus(); + } catch(e) { + // + } + } + } + }, 500); + }, +}); \ No newline at end of file diff --git a/frappe/public/js/legacy/clientscriptAPI.js b/frappe/public/js/legacy/clientscriptAPI.js index 6db9f90ad0..ffbc0f969f 100644 --- a/frappe/public/js/legacy/clientscriptAPI.js +++ b/frappe/public/js/legacy/clientscriptAPI.js @@ -378,10 +378,16 @@ _f.Frm.prototype.set_indicator_formatter = function(fieldname, get_color, get_te frappe.meta.get_docfield(doctype, fieldname, this.doc.name).formatter = function(value, df, options, doc) { - return repl('%(name)s', { - color: get_color(doc), - name: get_text ? get_text(doc) : value - }); + if(value) { + return repl('%(label)s', { + color: get_color(doc), + doctype: df.options, + name: value, + label: get_text ? get_text(doc) : value + }); + } else { + return ''; + } }; } \ No newline at end of file diff --git a/frappe/public/js/legacy/globals.js b/frappe/public/js/legacy/globals.js index a82aa363fc..9d339d6caa 100644 --- a/frappe/public/js/legacy/globals.js +++ b/frappe/public/js/legacy/globals.js @@ -19,6 +19,9 @@ frappe.settings.no_history = 1; // constants var NEWLINE = '\n'; +var TAB = 9; +var UP_ARROW = 38; +var DOWN_ARROW = 40; // user var user=null; diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index 7b4ea58d1a..a2debad4c6 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -19,6 +19,7 @@ a.form-link { border-radius: 2px; padding: 3px; display: none; + z-index: 3; } .link-primary& { diff --git a/frappe/public/less/form_grid.less b/frappe/public/less/form_grid.less index aa724a3047..6c56f33631 100644 --- a/frappe/public/less/form_grid.less +++ b/frappe/public/less/form_grid.less @@ -38,7 +38,7 @@ } .form-grid .data-row { - padding: 10px 15px; + // padding: 10px 15px; &.highlight { background-color: @extra-light-yellow; @@ -58,16 +58,24 @@ color: @border-color; } -.grid-static-col { +.grid-static-col, .row-index { + height: 39px; + padding: 10px 15px; max-height: 200px; - overflow: hidden; + border-right: 1px solid @border-color; + margin-bottom: -1px; +} + +.grid-static-col { + input[type="checkbox"] { + margin-left: -16px !important; + } } .row-index { text-align: right; - width: 30px; - margin-left: -15px; - margin-top: 1px; + width: 40px; + margin-left: -7px; float: left; } @@ -76,33 +84,24 @@ .col { padding-left: 10px; - padding-right: 0px; + padding-right: 10px; } } .grid-body { .btn-open-row { - margin-top: -3px; + margin: -10px; + padding: 10px; + padding-top: 15px; } .editable-row { - - padding: 0px 10px; - margin-bottom: -1px; - - .row-index { - margin-top: 7px; - } - - .btn-open-row { - margin: -10px -4px; - padding: 15px 6px; - margin-right: -20px; + .grid-static-col { + padding: 0px !important; } .checkbox { margin: 0px; - margin-right: -7px; text-align: center; } @@ -110,18 +109,18 @@ height: 38px !important; } - .field-area { - margin-right: -11px; - margin-top: -1px; - } - .form-control { border-radius: 0px; - padding-top: 9px; + border: 0px; + padding-top: 8px; padding-bottom: 9px; height: 38px; } + .link-btn { + top: 8px; + } + .form-control:focus { border-color: @text-muted; z-index: 2; @@ -141,6 +140,23 @@ } } +@media (max-width: 767px) { + .grid-body { + .btn-open-row { + margin-top: 0px; + padding: 0px; + } + } + + // lesser padding for controls + .editable-row .frappe-control { + padding-top: 0px !important; + padding-bottom: 0px !important; + margin-left: -5px !important; + margin-right: -5px !important; + } +} + .row-data > .row { margin-left: 15px; }