[MERGE] website_form: add some usability imp to the form builder
Note: the form builder will be refactored in a deeper way in a next update. See sub-commits for details. task-1874350 Thanks to @dbh-odoo @pna-odoo and @dja-odoo for the work on this task. closes odoo/odoo#30595 Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
@@ -32,6 +32,10 @@ odoo.define('website_form_editor', function (require) {
|
||||
close: true
|
||||
}],
|
||||
}, options));
|
||||
|
||||
this.opened(() => {
|
||||
this.$modal.find('.o_website_form_input, .modal-footer .btn').first().focus();
|
||||
});
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -309,6 +313,12 @@ odoo.define('website_form_editor', function (require) {
|
||||
|
||||
append_field: function (field) {
|
||||
var self = this;
|
||||
// Copy formatting classes of last row (field)
|
||||
var $lastField = this.$target.find('.form-field.row:last');
|
||||
field.formatInfo = {
|
||||
labelClass: $lastField.find('> div:first').attr('class'),
|
||||
contentClass: $lastField.find('> div:last').attr('class'),
|
||||
};
|
||||
this.render_field(field).then(function (field){
|
||||
self.$target.find(".form-group:has('.o_website_form_send')").before(field);
|
||||
});
|
||||
@@ -359,32 +369,56 @@ odoo.define('website_form_editor', function (require) {
|
||||
init_form: function () {
|
||||
var self = this;
|
||||
var modelName = this.activeForm.model;
|
||||
var currentModel = this.$target.attr('data-model_name');
|
||||
var formKey = this.activeForm.website_form_key;
|
||||
if (modelName !== this.$target.attr('data-model_name')) {
|
||||
this.$target.attr('data-model_name', modelName);
|
||||
this.$target.find(".form-field:not(:has('.o_website_form_send')), .o_form_heading").remove();
|
||||
if (!currentModel) {
|
||||
// Directly change the parameters if model is being set for the first time
|
||||
this.changeFormParameters(modelName, formKey);
|
||||
} else if (modelName !== currentModel) {
|
||||
var warningMessage = _t("Are you sure you want to change the parameters of your form? All the current fields will be discarded.");
|
||||
new Dialog(this, {
|
||||
title: _t("Warning"),
|
||||
size: 'medium',
|
||||
$content: $('<div>', {text: warningMessage}),
|
||||
buttons: [{
|
||||
text: _t("Ok"),
|
||||
classes: 'btn-primary',
|
||||
click: function () {
|
||||
self.changeFormParameters(modelName, formKey);
|
||||
},
|
||||
close: true,
|
||||
}, {
|
||||
text: _t("Discard"),
|
||||
close: true,
|
||||
}],
|
||||
}).open({shouldFocusButtons: true});
|
||||
}
|
||||
},
|
||||
|
||||
if (formKey) {
|
||||
var formInfo = FormEditorRegistry.get(formKey);
|
||||
ajax.loadXML(formInfo.defaultTemplatePath, qweb).then(function () {
|
||||
// Append form title
|
||||
$('<h1>', {
|
||||
class: 'o_form_heading',
|
||||
text: self.activeForm.website_form_label,
|
||||
}).prependTo(self.$target.find('.container'));
|
||||
self.$target.find('.form-group:has(".o_website_form_send")').before($(qweb.render(formInfo.defaultTemplateName)));
|
||||
changeFormParameters: function (modelName, formKey) {
|
||||
var self = this;
|
||||
this.$target.attr('data-model_name', modelName);
|
||||
this.$target.find(".form-field:not(:has('.o_website_form_send')), .o_form_heading").remove();
|
||||
if (formKey) {
|
||||
var formInfo = FormEditorRegistry.get(formKey);
|
||||
ajax.loadXML(formInfo.defaultTemplatePath, qweb).then(function () {
|
||||
// Append form title
|
||||
$('<h1>', {
|
||||
class: 'o_form_heading',
|
||||
text: self.activeForm.website_form_label,
|
||||
}).prependTo(self.$target.find('.container'));
|
||||
self.$target.find('.form-group:has(".o_website_form_send")').before($(qweb.render(formInfo.defaultTemplateName)));
|
||||
});
|
||||
} else {
|
||||
// Force fetch the fields of the new model
|
||||
// and render all model required fields
|
||||
this.fetch_model_fields().then(function (fields) {
|
||||
_.each(fields, function (field, field_name) {
|
||||
if (field.required) {
|
||||
self.append_field(field);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Force fetch the fields of the new model
|
||||
// and render all model required fields
|
||||
this.fetch_model_fields().then(function (fields) {
|
||||
_.each(fields, function (field, field_name){
|
||||
if (field.required) {
|
||||
self.append_field(field);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -434,6 +468,34 @@ odoo.define('website_form_editor', function (require) {
|
||||
}
|
||||
});
|
||||
|
||||
// radio / checkboxes field option to display them horizontally or vertically
|
||||
options.registry['website_form_editor_choice_field_display'] = options.Class.extend({
|
||||
xmlDependencies: ['/website_form/static/src/xml/website_form_editor.xml'],
|
||||
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
website_form_choice_field_display: function (previewMode, value, $opt) {
|
||||
this.$target.toggleClass('o_website_form_flex_fw', value === 'vertical');
|
||||
this.$target[0].dataset.display = value;
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Private
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_setActive: function () {
|
||||
this._super(...arguments);
|
||||
this.$el.find('[data-website_form_choice_field_display]')
|
||||
.removeClass('active')
|
||||
.filter('[data-website_form_choice_field_display=' + this.$target.attr('data-display') + ']')
|
||||
.addClass('active');
|
||||
},
|
||||
});
|
||||
|
||||
// Generic custom field options
|
||||
options.registry['website_form_editor_field'] = options.Class.extend({
|
||||
xmlDependencies: ['/website_form/static/src/xml/website_form_editor.xml'],
|
||||
@@ -475,6 +537,10 @@ odoo.define('website_form_editor', function (require) {
|
||||
string: this.$target.find('.col-form-label').text().trim(),
|
||||
required: self.$target.hasClass('o_website_form_required'),
|
||||
custom: self.$target.hasClass('o_website_form_custom'),
|
||||
formatInfo: {
|
||||
labelClass: this.$target.find('> div:first').attr('class'),
|
||||
contentClass: this.$target.find('> div:last').attr('class'),
|
||||
},
|
||||
};
|
||||
|
||||
// Build the new records list from the editable select field
|
||||
@@ -497,10 +563,6 @@ odoo.define('website_form_editor', function (require) {
|
||||
if (this.$target.hasClass('o_website_form_field_hidden')) {
|
||||
$new_select.addClass('o_website_form_field_hidden');
|
||||
}
|
||||
var labelClasses = this.$target.find('> div:first').attr('class');
|
||||
var inputClasses = this.$target.find('> div:last').attr('class');
|
||||
$new_select.find('> div:first').attr('class', labelClasses);
|
||||
$new_select.find('> div:last').attr('class', inputClasses);
|
||||
this.$target.replaceWith($new_select);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,24 @@
|
||||
.o_website_form_flex {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.o_website_form_flex_item {
|
||||
// col-lg-4
|
||||
flex-basis: 33%;
|
||||
|
||||
// col-md-6
|
||||
@include media-breakpoint-down(md) {
|
||||
flex-basis: 50%;
|
||||
}
|
||||
|
||||
// col-12
|
||||
@include media-breakpoint-down(sm) {
|
||||
&.o_website_form_flex_fw > .o_website_form_flex_item {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
&:not(.o_website_form_flex_fw) > .o_website_form_flex_item {
|
||||
// col-lg-4
|
||||
flex-basis: 33%;
|
||||
|
||||
// col-md-6
|
||||
@include media-breakpoint-down(md) {
|
||||
flex-basis: 50%;
|
||||
}
|
||||
|
||||
// col-12
|
||||
@include media-breakpoint-down(sm) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hidden field is only partially hidden in editor
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
<!-- Generic Field Layout -->
|
||||
<t t-name="website_form.field">
|
||||
<div t-att-class="'row form-group form-field' + (field.custom ? ' o_website_form_custom' : '') + (field.required ? ' o_website_form_required' : '')">
|
||||
<div class="col-lg-3 col-md-4">
|
||||
<div t-att-class="field.formatInfo and field.formatInfo.labelClass || 'col-lg-3 col-md-4'">
|
||||
<label class="col-form-label" t-att-for="field.name">
|
||||
<t t-esc="field.string"/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-lg-7 col-md-8">
|
||||
<div t-att-class="field.formatInfo and field.formatInfo.contentClass || 'col-lg-7 col-md-8'">
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -39,6 +39,18 @@
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!-- Email Field -->
|
||||
<t t-name="website_form.field_email">
|
||||
<t t-call="website_form.field">
|
||||
<input
|
||||
type="email"
|
||||
class="form-control o_website_form_input"
|
||||
t-att-name="field.name"
|
||||
t-att-required="field.required"
|
||||
/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!-- HTML Field -->
|
||||
<t t-name="website_form.field_html">
|
||||
<!--
|
||||
@@ -115,23 +127,25 @@
|
||||
<!-- Boolean Field -->
|
||||
<t t-name="website_form.field_boolean">
|
||||
<t t-call="website_form.field">
|
||||
<input
|
||||
type="checkbox"
|
||||
value="Yes"
|
||||
class="o_website_form_input"
|
||||
t-att-name="field.name"
|
||||
t-att-required="field.required"
|
||||
/>
|
||||
<div class="col-form-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
value="Yes"
|
||||
class="o_website_form_input"
|
||||
t-att-name="field.name"
|
||||
t-att-required="field.required"
|
||||
/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!-- Selection Field -->
|
||||
<t t-name="website_form.field_selection">
|
||||
<t t-call="website_form.field">
|
||||
<div class="o_website_form_flex">
|
||||
<div class="o_website_form_flex" data-display="horizontal">
|
||||
<t t-foreach="field.selection" t-as="option">
|
||||
<div class="radio o_website_form_flex_item">
|
||||
<label>
|
||||
<label class="col-form-label">
|
||||
<input
|
||||
type="radio"
|
||||
class="o_website_form_input"
|
||||
@@ -180,7 +194,7 @@
|
||||
<!-- Generic one2many -->
|
||||
<t t-if="field.relation != 'ir.attachment'">
|
||||
<t t-call="website_form.field">
|
||||
<div class="o_website_form_flex">
|
||||
<div class="o_website_form_flex" data-display="horizontal">
|
||||
<t t-if="!field.records">
|
||||
<input
|
||||
class="o_website_form_input"
|
||||
@@ -192,7 +206,7 @@
|
||||
</t>
|
||||
<t t-foreach="field.records" t-as="record">
|
||||
<div class="checkbox o_website_form_flex_item">
|
||||
<label>
|
||||
<label class="col-form-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="o_website_form_input"
|
||||
|
||||
@@ -240,10 +240,19 @@ odoo.define('website_form_editor.tour', function(require) {
|
||||
content: "Click on Required",
|
||||
trigger: "[data-website_form_field_require]"
|
||||
},
|
||||
{
|
||||
content: "Click on first Check Box",
|
||||
trigger: "label:contains('Iphone') span",
|
||||
},
|
||||
{
|
||||
content: "Click on 'Vertical' in menu 'Display'",
|
||||
trigger: "we-button:contains('Vertical')",
|
||||
},
|
||||
{
|
||||
content: "Check the resulting field",
|
||||
trigger: ".form-field.o_website_form_custom.o_website_form_required_custom" +
|
||||
":has(.col-form-label:contains('Products'))" +
|
||||
":has(.o_website_form_flex[data-display='vertical'])" +
|
||||
":has(.checkbox label:contains('Iphone'):has(input[type='checkbox'][required]))" +
|
||||
":has(.checkbox label:contains('Galaxy S'):has(input[type='checkbox'][required]))" +
|
||||
":has(.checkbox label:contains('Xperia'):has(input[type='checkbox'][required]))" +
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<we-collapse data-no-preview="true">
|
||||
<we-button data-website_form_custom_field="char">Text</we-button>
|
||||
<we-button data-website_form_custom_field="text">Long Text</we-button>
|
||||
<we-button data-website_form_custom_field="email">Email</we-button>
|
||||
<we-button data-website_form_custom_field="integer">Number</we-button>
|
||||
<we-button data-website_form_custom_field="float">Decimal Number</we-button>
|
||||
<we-button data-website_form_custom_field="boolean">Checkbox</we-button>
|
||||
@@ -55,6 +56,17 @@
|
||||
<we-button data-toggle-class="o_website_form_field_hidden" data-no-preview="true">Hidden</we-button>
|
||||
</div>
|
||||
|
||||
<!-- Add an option to display radio buttons / checkboxes horizontally or vertically -->
|
||||
<div data-js='website_form_editor_choice_field_display' data-selector=".o_website_form_flex">
|
||||
<we-collapse-area>
|
||||
<we-toggler>Display</we-toggler>
|
||||
<we-collapse data-no-preview="true">
|
||||
<we-button data-website_form_choice_field_display="horizontal">Horizontal</we-button>
|
||||
<we-button data-website_form_choice_field_display="vertical">Vertical</we-button>
|
||||
</we-collapse>
|
||||
</we-collapse-area>
|
||||
</div>
|
||||
|
||||
<!-- Add move, duplicate and remove controllers to checkboxes and radio buttons -->
|
||||
<div data-selector=".o_website_form_flex_item" data-drop-near=".o_website_form_flex_item"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user