[MERGE] [ADD] web: form: added FieldCharDomain widget. This widget works on a char field and is used to manage a domain. It allows to select records in a list view and to store the domain in the field, without having to deal with the complexity of writing domains.

[ADD] web: form: added FieldBarChart widget. This widget works on char field and display a serialized list of values as a barchart. This is used in stat buttons, like percent pie or stat info, to display a summary of some data on form view.

[IMP] web: some fixes and improvements in the stat buttons.

[IMP] web: list view: buttons in list view can now be text button, not only icon-based buttons. Icons are so v6 and not swag.

bzr revid: tde@openerp.com-20140416114938-3qsv8b8dumw5x4pj
This commit is contained in:
Thibault Delavallée 2014-04-16 13:49:38 +02:00
commit 4f53557311
5 changed files with 183 additions and 21 deletions

View File

@ -1,4 +1,4 @@
@charset "UTF-8";
@charset "utf-8";
@font-face {
font-family: "mnmliconsRegular";
src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot");
@ -352,9 +352,17 @@
width: 37px;
text-align: center;
}
.openerp .oe_button_box .oe_stat_button .oe_form_field_percent_pie {
width: 42px;
}
.openerp .oe_button_box .oe_stat_button .oe_form_field_bar_chart {
width: 42px;
}
.openerp .oe_button_box .oe_stat_button svg {
width: 38px;
height: 38px;
display: inline;
vertical-align: middle;
}
.openerp .oe_avatar > img {
max-height: 90px;
@ -2768,7 +2776,7 @@
padding: 3px 6px;
white-space: pre-line;
}
.openerp .oe_list_content > tbody > tr > td > button, .openerp .oe_list_content > tbody > tr > th > button {
.openerp .oe_list_content > tbody > tr > td > button.btn_img, .openerp .oe_list_content > tbody > tr > th > button.btn_img {
border: none;
background: transparent;
padding: 0;

View File

@ -362,9 +362,15 @@ $sheet-padding: 16px
padding: 0px 3px
width: 37px
text-align: center
.oe_form_field_percent_pie
width: 42px
.oe_form_field_bar_chart
width: 42px
svg
width: 38px
height: 38px
display: inline
vertical-align: middle
.oe_avatar
> img
max-height: 90px
@ -2241,7 +2247,7 @@ $sheet-padding: 16px
padding: 3px 6px
white-space: pre-line
> td, > th
> button
> button.btn_img
border: none
background: transparent
padding: 0

View File

@ -2438,6 +2438,76 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
}
});
instance.web.form.FieldCharDomain = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
init: function(field_manager, node) {
this._super.apply(this, arguments);
},
start: function() {
var self = this;
this._super.apply(this, arguments);
this.on("change:effective_readonly", this, function () {
this.display_field();
this.render_value();
});
this.display_field();
return this._super();
},
render_value: function() {
this.$('button.select_records').css('visibility', this.get('effective_readonly') ? 'hidden': '');
},
set_value: function(value_) {
var self = this;
this.set('value', value_ || false);
this.display_field();
},
display_field: function() {
var self = this;
this.$el.html(instance.web.qweb.render("FieldCharDomain", {widget: this}));
if (this.get('value')) {
var domain = instance.web.pyeval.eval('domain', this.get('value'));
var relation = this.getParent().fields.mailing_model.get('value')[0];
var ds = new instance.web.DataSetStatic(self, relation, self.build_context());
ds.call('search_count', [domain]).then(function (results) {
$('.oe_domain_count', self.$el).text(results + ' records selected');
$('button span', self.$el).text(' Change selection');
});
} else {
$('.oe_domain_count', this.$el).text('0 record selected');
$('button span', this.$el).text(' Select records');
};
this.$('.select_records').on('click', self.on_click);
},
on_click: function(ev) {
var self = this;
var model = this.options.model || this.field_manager.get_field_value(this.options.model_field);
this.pop = new instance.web.form.SelectCreatePopup(this);
this.pop.select_element(
model, {title: 'Select records...'},
[], this.build_context());
this.pop.on("elements_selected", self, function(element_ids) {
if (this.pop.$('input.oe_list_record_selector').prop('checked')) {
var search_data = this.pop.searchview.build_search_data();
var domain_done = instance.web.pyeval.eval_domains_and_contexts({
domains: search_data.domains,
contexts: search_data.contexts,
group_by_seq: search_data.groupbys || []
}).then(function (results) {
return results.domain;
});
}
else {
var domain = ["id", "in", element_ids];
var domain_done = $.Deferred().resolve(domain);
}
$.when(domain_done).then(function (domain) {
var domain = self.pop.dataset.domain.concat(domain || []);
self.set_value(JSON.stringify(domain))
});
});
event.preventDefault();
},
});
instance.web.DateTimeWidget = instance.web.Widget.extend({
template: "web.datepicker",
jqueryui_object: 'datetimepicker',
@ -2833,10 +2903,10 @@ instance.web.form.FieldPercentPie = instance.web.form.AbstractField.extend({
svg.innerHTML = "";
nv.addGraph(function() {
var size=43;
var width = 42, height = 42;
var chart = nv.models.pieChart()
.width(size)
.height(size)
.width(width)
.height(height)
.margin({top: 0, right: 0, bottom: 0, left: 0})
.donut(true)
.showLegend(false)
@ -2849,11 +2919,11 @@ instance.web.form.FieldPercentPie = instance.web.form.AbstractField.extend({
.datum([{'x': 'value', 'y': value}, {'x': 'complement', 'y': 100 - value}])
.transition()
.call(chart)
.attr({width:size, height:size});
.attr('style', 'width: ' + width + 'px; height:' + height + 'px;');
d3.select(svg)
.append("text")
.attr({x: size/2, y: size/2 + 3, 'text-anchor': 'middle'})
.attr({x: width/2, y: height/2 + 3, 'text-anchor': 'middle'})
.style({"font-size": "10px", "font-weight": "bold"})
.text(formatted_value);
@ -2863,6 +2933,43 @@ instance.web.form.FieldPercentPie = instance.web.form.AbstractField.extend({
}
});
/**
The FieldBarChart expectsa list of values (indeed)
*/
instance.web.form.FieldBarChart = instance.web.form.AbstractField.extend({
template: 'FieldBarChart',
render_value: function() {
var value = JSON.parse(this.get('value'));
var svg = this.$('svg')[0];
svg.innerHTML = "";
nv.addGraph(function() {
var width = 34, height = 34;
var chart = nv.models.discreteBarChart()
.x(function (d) { return d.tooltip })
.y(function (d) { return d.value })
.width(width)
.height(height)
.margin({top: 0, right: 0, bottom: 0, left: 0})
.tooltips(false)
.showValues(false)
.transitionDuration(350)
.showXAxis(false)
.showYAxis(false);
d3.select(svg)
.datum([{key: 'values', values: value}])
.transition()
.call(chart)
.attr('style', 'width: ' + (width + 4) + 'px; height: ' + (height + 8) + 'px;');
nv.utils.windowResize(chart.update);
return chart;
});
}
});
instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
@ -3419,7 +3526,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
}
self.floating = false;
}
if (used && self.get("value") === false && ! self.no_ed) {
if (used && self.get("value") === false && ! self.no_ed && (self.options.no_create === false || self.options.no_create === undefined)) {
self.ed_def.reject();
self.uned_def.reject();
self.ed_def = $.Deferred();
@ -5919,20 +6026,30 @@ instance.web.form.X2ManyCounter = instance.web.form.AbstractField.extend(instanc
display a simple string "<value of field> <label of the field>"
*/
instance.web.form.StatInfo = instance.web.form.AbstractField.extend({
is_field_number: true,
init: function() {
this._super.apply(this, arguments);
this.set("value", 0);
this.internal_set_value(0);
},
set_value: function(value_) {
if (value_ === false || value_ === undefined) {
value_ = 0;
}
this._super.apply(this, [value_]);
},
render_value: function() {
var options = {
value: this.get("value") || 0,
text: this.string,
};
if (! this.node.attrs.nolabel) {
options.text = this.string
}
this.$el.html(QWeb.render("StatInfo", options));
},
});
/**
* Registry of form fields, called by :js:`instance.web.FormView`.
*
@ -5946,6 +6063,7 @@ instance.web.form.widgets = new instance.web.Registry({
'url' : 'instance.web.form.FieldUrl',
'text' : 'instance.web.form.FieldText',
'html' : 'instance.web.form.FieldTextHtml',
'char_domain': 'instance.web.form.FieldCharDomain',
'date' : 'instance.web.form.FieldDate',
'datetime' : 'instance.web.form.FieldDatetime',
'selection' : 'instance.web.form.FieldSelection',
@ -5961,6 +6079,7 @@ instance.web.form.widgets = new instance.web.Registry({
'boolean' : 'instance.web.form.FieldBoolean',
'float' : 'instance.web.form.FieldFloat',
'percentpie': 'instance.web.form.FieldPercentPie',
'barchart': 'instance.web.form.FieldBarChart',
'integer': 'instance.web.form.FieldFloat',
'float_time': 'instance.web.form.FieldFloat',
'progressbar': 'instance.web.form.FieldProgressBar',

View File

@ -2261,8 +2261,8 @@ instance.web.list.Button = instance.web.list.Column.extend({
attrs = this.modifiers_for(row_data);
}
if (attrs.invisible) { return ''; }
return QWeb.render('ListView.row.button', {
var template = this.icon && 'ListView.row.button' || 'ListView.row.text_button';
return QWeb.render(template, {
widget: this,
prefix: instance.session.prefix,
disabled: attrs.readonly

View File

@ -612,10 +612,16 @@
<div class="oe_sidebar">
<t t-foreach="widget.sections" t-as="section">
<div class="oe_form_dropdown_section">
<button class="oe_dropdown_toggle oe_dropdown_arrow">
<button class="oe_dropdown_toggle oe_dropdown_arrow" t-if="section.name != 'buttons'">
<t t-if="section.name == 'files'" t-raw="widget.items[section.name].length || ''"/>
<t t-esc="section.label"/>
</button>
<t t-if="section.name == 'buttons'" t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
<button t-att-title="item.title or ''" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url"
target="_blank" class="oe_sidebar_button oe_highlight">
<t t-raw="item.label"/>
</button>
</t>
<ul class="oe_dropdown_menu">
<li t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
<t t-if="section.name == 'files'">
@ -792,11 +798,16 @@
</span>
</t>
</t>
<button t-name="ListView.row.text_button" type="button"
t-att-title="widget.string" t-att-disabled="disabled || undefined"
t-att-class="disabled ? 'oe_list_button_disabled btn' : 'btn'">
<t t-esc="widget.string"/>
</button>
<button t-name="ListView.row.button" type="button"
t-att-title="widget.string" t-att-disabled="disabled || undefined"
t-att-class="disabled ? 'oe_list_button_disabled' : undefined"
><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png"
t-att-alt="widget.string"/></button>
t-att-class="disabled ? 'oe_list_button_disabled btn_img' : 'btn_img'"
><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png"
t-att-alt="widget.string"/></button>
<t t-extend="ListView.row">
<!-- adds back padding to row being rendered after edition, if necessary
(if not deletable add back padding), otherwise the row being added is
@ -1069,6 +1080,16 @@
</t>
</div>
</t>
<t t-name="FieldCharDomain">
<div class="oe_form_field">
<span class="oe_domain_count"/>
<button class="oe_button oe_link select_records" type="button"
t-att-style="widget.node.attrs.style"
t-att-accesskey="widget.node.attrs.accesskey">
<span class="fa fa-arrow-right"/>
</button>
</div>
</t>
<t t-name="web.datepicker">
<span>
<t t-set="placeholder" t-value="widget.getParent().node and widget.getParent().node.attrs.placeholder"/>
@ -1209,9 +1230,16 @@
</span>
</t>
<t t-name="FieldPercentPie">
<span class="oe_form_field oe_form_field_percent_pie" t-att-style="widget.node.attrs.style">
<div class="oe_form_field oe_form_field_percent_pie" t-att-style="widget.node.attrs.style">
<svg></svg>
</span>
<span t-if="widget.string"><t t-esc="widget.string"/></span>
</div>
</t>
<t t-name="FieldBarChart">
<div class="oe_form_field oe_form_field_bar_chart" t-att-style="widget.node.attrs.style">
<svg></svg>
<span t-if="widget.string"><t t-esc="widget.string"/></span>
</div>
</t>
<t t-name="FieldStatus">
<ul t-att-class="'oe_form_field_status ' + (widget.options.clickable ? 'oe_form_status_clickable' : 'oe_form_status')" t-att-style="widget.node.attrs.style"/>
@ -1382,7 +1410,7 @@
t-att-autofocus="widget.node.attrs.autofocus"
t-att-accesskey="widget.node.attrs.accesskey">
<img t-if="!widget.is_stat_button and widget.node.attrs.icon " t-att-src="_s + widget.node.attrs.icon" width="16" height="16"/>
<div t-if="widget.is_stat_button" class="stat_button_icon"><t t-if="widget.icon" t-raw="widget.icon"/></div>
<div t-if="widget.is_stat_button and widget.icon" class="stat_button_icon"><t t-raw="widget.icon"/></div>
<span t-if="widget.string and !widget.is_stat_button"><t t-esc="widget.string"/></span>
<div t-if="widget.string and widget.is_stat_button"><t t-esc="widget.string"/></div>
</button>
@ -1958,5 +1986,6 @@
<a href="javascript:void(0)"><t t-esc="text"/></a>
</t>
<t t-name="StatInfo">
<strong><t t-esc="value"/></strong><br/><t t-esc="text"/></t>
<strong><t t-esc="value"/></strong>
<t t-esc="text"/></t>
</templates>