[REF] web_editor, *: make form list a UserValueWidget
* = website_form The form we-list is now a generic UserValueWidget. Part of: https://github.com/odoo/odoo/pull/47949 task-2011583 Co-authored-by: qsm-odoo <qsm@odoo.com>
This commit is contained in:
committed by
qsm-odoo
co-authored by
qsm-odoo
parent
eb80d33471
commit
9304f8fa7b
@@ -1695,6 +1695,229 @@ const DatetimePickerUserValueWidget = InputUserValueWidget.extend({
|
||||
},
|
||||
});
|
||||
|
||||
const ListUserValueWidget = UserValueWidget.extend({
|
||||
tagName: 'we-list',
|
||||
events: {
|
||||
'click we-button.o_we_select_remove_option': '_onRemoveItemClick',
|
||||
'click we-button.o_we_list_add_optional': '_onAddCustomItemClick',
|
||||
'click we-button.o_we_list_add_existing': '_onAddExistingItemClick',
|
||||
'click we-select.o_we_user_value_widget': '_onAddItemSelectClick',
|
||||
'input table input': '_onListItemInput',
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start() {
|
||||
this.addItemTitle = this.el.dataset.addItemTitle;
|
||||
if (this.el.dataset.availableRecords) {
|
||||
this.records = JSON.parse(this.el.dataset.availableRecords);
|
||||
} else {
|
||||
this.isCustom = true;
|
||||
}
|
||||
this.listTable = document.createElement('table');
|
||||
const tableWrapper = document.createElement('div');
|
||||
tableWrapper.classList.add('o_we_table_wrapper');
|
||||
tableWrapper.appendChild(this.listTable);
|
||||
this.containerEl.appendChild(tableWrapper);
|
||||
this.el.classList.add('o_we_fw');
|
||||
this._makeListItemsSortable();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
getMethodsParams() {
|
||||
return _.extend(this._super(...arguments), {
|
||||
records: this.records,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
setValue() {
|
||||
this._super(...arguments);
|
||||
const currentValues = JSON.parse(this._value);
|
||||
this.listTable.innerHTML = '';
|
||||
if (this.addItemButton) {
|
||||
this.addItemButton.remove();
|
||||
}
|
||||
if (this.isCustom) {
|
||||
this.addItemButton = document.createElement('we-button');
|
||||
this.addItemButton.textContent = this.addItemTitle;
|
||||
this.addItemButton.classList.add('o_we_list_add_optional');
|
||||
currentValues.forEach(el => this._addItemToTable(el, el));
|
||||
} else {
|
||||
// TODO use a real select widget ?
|
||||
this.addItemButton = document.createElement('we-select');
|
||||
this.addItemButton.classList.add('o_we_user_value_widget');
|
||||
const divEl = document.createElement('div');
|
||||
this.addItemButton.appendChild(divEl);
|
||||
const togglerEl = document.createElement('we-toggler');
|
||||
togglerEl.textContent = this.addItemTitle;
|
||||
divEl.appendChild(togglerEl);
|
||||
this.selectMenuEl = document.createElement('we-selection-items');
|
||||
divEl.appendChild(this.selectMenuEl);
|
||||
currentValues.forEach(val => {
|
||||
const record = this.records.find(rec => rec.id === val);
|
||||
this._addItemToTable(record.id, record.display_name);
|
||||
});
|
||||
this._reloadSelectDropdown(currentValues);
|
||||
}
|
||||
this.containerEl.appendChild(this.addItemButton);
|
||||
this._makeListItemsSortable();
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Private
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string || integer} id
|
||||
* @param {string} text
|
||||
*/
|
||||
_addItemToTable(id, text) {
|
||||
if (text === undefined) {
|
||||
text = _t("Item");
|
||||
}
|
||||
const draggableEl = document.createElement('we-button');
|
||||
draggableEl.classList.add('o_we_drag_handle', 'o_we_link', 'fa', 'fa-fw', 'fa-arrows');
|
||||
draggableEl.dataset.noPreview = 'true';
|
||||
const inputEl = document.createElement('input');
|
||||
inputEl.type = 'text';
|
||||
if (text) {
|
||||
inputEl.value = text;
|
||||
}
|
||||
if (!this.isCustom && id) {
|
||||
inputEl.name = id;
|
||||
}
|
||||
inputEl.disabled = !this.isCustom;
|
||||
const trEl = document.createElement('tr');
|
||||
const buttonEl = document.createElement('we-button');
|
||||
buttonEl.classList.add('o_we_select_remove_option', 'o_we_link', 'o_we_text_danger', 'fa', 'fa-fw', 'fa-minus');
|
||||
buttonEl.dataset.removeOption = id;
|
||||
const draggableTdEl = document.createElement('td');
|
||||
const inputTdEl = document.createElement('td');
|
||||
const buttonTdEl = document.createElement('td');
|
||||
draggableTdEl.appendChild(draggableEl);
|
||||
trEl.appendChild(draggableTdEl);
|
||||
inputTdEl.appendChild(inputEl);
|
||||
trEl.appendChild(inputTdEl);
|
||||
buttonTdEl.appendChild(buttonEl);
|
||||
trEl.appendChild(buttonTdEl);
|
||||
this.listTable.appendChild(trEl);
|
||||
if (this.isCustom) {
|
||||
inputEl.focus();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_makeListItemsSortable() {
|
||||
$(this.listTable).sortable({
|
||||
axis: 'y',
|
||||
handle: '.o_we_drag_handle',
|
||||
items: 'tr',
|
||||
cursor: 'move',
|
||||
opacity: 0.6,
|
||||
stop: (event, ui) => {
|
||||
this._notifyCurrentState();
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_notifyCurrentState() {
|
||||
const values = [...this.listTable.querySelectorAll('input')].map(el => {
|
||||
const id = this.isCustom ? el.value : el.name;
|
||||
const idInt = parseInt(id);
|
||||
return isNaN(idInt) ? id : idInt;
|
||||
});
|
||||
if (this.isCustom) {
|
||||
this.records = values.map(v => ({id: v, display_name: v}));
|
||||
}
|
||||
this._value = JSON.stringify(values);
|
||||
this.notifyValueChange(true);
|
||||
if (!this.isCustom) {
|
||||
this._reloadSelectDropdown(values);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Array} currentValues
|
||||
*/
|
||||
_reloadSelectDropdown(currentValues) {
|
||||
this.selectMenuEl.innerHTML = '';
|
||||
this.records.forEach(el => {
|
||||
if (!currentValues.includes(el.id)) {
|
||||
const option = document.createElement('we-button');
|
||||
option.classList.add('o_we_list_add_existing');
|
||||
option.dataset.addOption = el.id;
|
||||
option.dataset.noPreview = 'true';
|
||||
const divEl = document.createElement('div');
|
||||
divEl.textContent = el.display_name;
|
||||
option.appendChild(divEl);
|
||||
this.selectMenuEl.appendChild(option);
|
||||
}
|
||||
});
|
||||
if (!this.selectMenuEl.children.length) {
|
||||
const title = document.createElement('we-title');
|
||||
title.textContent = _("No more records");
|
||||
this.selectMenuEl.appendChild(title);
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onAddCustomItemClick() {
|
||||
this._addItemToTable();
|
||||
this._notifyCurrentState();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onAddExistingItemClick(ev) {
|
||||
const value = ev.currentTarget.dataset.addOption;
|
||||
this._addItemToTable(value, ev.currentTarget.textContent);
|
||||
this._notifyCurrentState();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onAddItemSelectClick(ev) {
|
||||
ev.currentTarget.querySelector('we-toggler').classList.toggle('active');
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onListItemInput() {
|
||||
this._notifyCurrentState();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onRemoveItemClick(ev) {
|
||||
if (ev.target.closest('table').querySelectorAll('tr').length > 1) {
|
||||
ev.target.closest('tr').remove();
|
||||
this._notifyCurrentState();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const RangeUserValueWidget = UnitUserValueWidget.extend({
|
||||
tagName: 'we-range',
|
||||
events: {
|
||||
@@ -2075,6 +2298,7 @@ const userValueWidgetsRegistry = {
|
||||
'we-multi': MultiUserValueWidget,
|
||||
'we-colorpicker': ColorpickerUserValueWidget,
|
||||
'we-datetimepicker': DatetimePickerUserValueWidget,
|
||||
'we-list': ListUserValueWidget,
|
||||
'we-imagepicker': ImagepickerUserValueWidget,
|
||||
'we-videopicker': VideopickerUserValueWidget,
|
||||
'we-range': RangeUserValueWidget,
|
||||
|
||||
@@ -1174,6 +1174,47 @@ body.editor_enable.editor_has_snippets {
|
||||
}
|
||||
}
|
||||
|
||||
// Lists
|
||||
we-list {
|
||||
> div {
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
.o_we_table_wrapper {
|
||||
width: 100%;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
margin-bottom: $o-we-sidebar-content-field-spacing / 2;
|
||||
|
||||
table {
|
||||
table-layout: auto;
|
||||
width: 100%;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
border: $o-we-sidebar-content-field-border-width solid $o-we-sidebar-content-field-border-color;
|
||||
border-radius: $o-we-sidebar-content-field-border-radius;
|
||||
padding: 0 $o-we-sidebar-content-field-clickable-spacing;
|
||||
background-color: $o-we-sidebar-content-field-input-bg;
|
||||
color: inherit;
|
||||
font-family: $o-we-sidebar-content-field-input-font-family;
|
||||
}
|
||||
tr {
|
||||
border: none;
|
||||
}
|
||||
td {
|
||||
padding-bottom: $o-we-sidebar-content-field-spacing / 2;
|
||||
|
||||
&:first-child, &:last-child {
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.o_we_user_value_widget {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Multi widgets
|
||||
we-multi.o_we_user_value_widget {
|
||||
margin-top: 0;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#oe_snippets > .o_we_customize_panel we-customizeblock-option {
|
||||
we-list {
|
||||
we-title, we-button {
|
||||
margin-top: $o-we-sidebar-content-field-spacing;
|
||||
}
|
||||
.oe_we_table_wraper {
|
||||
margin-top: $o-we-sidebar-content-field-spacing;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
border: $o-we-sidebar-content-field-border-width solid $o-we-sidebar-content-field-border-color;
|
||||
border-radius: $o-we-sidebar-content-field-border-radius;
|
||||
padding: 0 $o-we-sidebar-content-field-clickable-spacing;
|
||||
background-color: $o-we-sidebar-content-field-input-bg;
|
||||
color: inherit;
|
||||
font-family: $o-we-sidebar-content-field-input-font-family;
|
||||
}
|
||||
tr {
|
||||
border: 1px solid rgba(white, 0.1);
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
td {
|
||||
&:first-child, &:last-child {
|
||||
width: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,9 +146,10 @@ const FieldEditor = FormEditor.extend({
|
||||
* Returns the target as a field Object
|
||||
*
|
||||
* @private
|
||||
* @param {boolean} noRecords
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getActiveField: function () {
|
||||
_getActiveField: function (noRecords) {
|
||||
let field;
|
||||
const labelText = this.$target.find('.s_website_form_label_content').text();
|
||||
if (this._isFieldCustom()) {
|
||||
@@ -157,7 +158,9 @@ const FieldEditor = FormEditor.extend({
|
||||
field = Object.assign({}, this.fields[this._getFieldName()]);
|
||||
field.string = labelText;
|
||||
}
|
||||
field.records = this._getListItems();
|
||||
if (!noRecords) {
|
||||
field.records = this._getListItems();
|
||||
}
|
||||
this._setActiveProperties(field);
|
||||
return field;
|
||||
},
|
||||
@@ -689,14 +692,6 @@ options.registry.WebsiteFormEditor = FormEditor.extend({
|
||||
const authorizedFieldsCache = {};
|
||||
|
||||
options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
events: _.extend({}, FieldEditor.prototype.events, {
|
||||
'click we-button.o_we_select_remove_option': '_onRemoveItemClick',
|
||||
'click we-button.o_we_list_add_optional': '_onAddCustomItemClick',
|
||||
'click we-button.o_we_list_add_existing': '_onAddExistingItemClick',
|
||||
'click we-list we-select': '_onAddItemSelectClick',
|
||||
'input we-list input': '_onListItemInput',
|
||||
}),
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
@@ -739,22 +734,27 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
});
|
||||
return _super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: async function () {
|
||||
const _super = this._super.bind(this);
|
||||
// Build the custom select
|
||||
const select = this._getSelect();
|
||||
if (select) {
|
||||
const field = this._getActiveField();
|
||||
await this._replaceField(field);
|
||||
}
|
||||
return _super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
cleanForSave: function () {
|
||||
this.$target[0].querySelectorAll('#editable_select').forEach(el => el.remove());
|
||||
const select = this._getSelect();
|
||||
if (select && this.listTable) {
|
||||
if (select) {
|
||||
select.style.display = '';
|
||||
$(select).empty();
|
||||
// Rebuild the select from the we-list
|
||||
this.listTable.querySelectorAll('input').forEach(el => {
|
||||
const option = document.createElement('option');
|
||||
option.textContent = el.value;
|
||||
option.value = this._isFieldCustom() ? el.value : el.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -763,16 +763,8 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
updateUI: async function () {
|
||||
// See Form updateUI
|
||||
if (this.rerender) {
|
||||
const select = this._getSelect();
|
||||
if (select && !this.$target[0].querySelector('#editable_select')) {
|
||||
select.style.display = 'none';
|
||||
const editableSelect = document.createElement('div');
|
||||
editableSelect.id = 'editable_select';
|
||||
editableSelect.classList = 'form-control s_website_form_input';
|
||||
select.parentElement.appendChild(editableSelect);
|
||||
}
|
||||
this.rerender = false;
|
||||
await this._rerenderXML().then(() => this._renderList());
|
||||
await this._rerenderXML();
|
||||
return;
|
||||
}
|
||||
await this._super.apply(this, arguments);
|
||||
@@ -838,7 +830,6 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
const field = this._getActiveField();
|
||||
field.formatInfo.labelPosition = value;
|
||||
await this._replaceField(field);
|
||||
this.rerender = true;
|
||||
},
|
||||
selectType: async function (previewMode, value, params) {
|
||||
const field = this._getActiveField();
|
||||
@@ -871,6 +862,15 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
name: 'field_mark',
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Apply the we-list on the target and rebuild the input(s)
|
||||
*/
|
||||
renderListItems: async function (previewMode, value, params) {
|
||||
const valueList = JSON.parse(value);
|
||||
const field = this._getActiveField(true);
|
||||
field.records = valueList.map(val => params.records.find(rec => rec.id === val));
|
||||
await this._replaceField(field);
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Private
|
||||
@@ -897,6 +897,10 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
}
|
||||
case 'toggleRequired':
|
||||
return this.$target[0].classList.contains(params.activeValue) ? params.activeValue : 'false';
|
||||
case 'renderListItems': {
|
||||
const values = this._getListItems().map(el => el.id);
|
||||
return JSON.stringify(values);
|
||||
}
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
@@ -919,7 +923,7 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_renderCustomXML: function (uiFragment) {
|
||||
_renderCustomXML: async function (uiFragment) {
|
||||
const selectEl = uiFragment.querySelector('we-select[data-name="type_opt"]');
|
||||
const currentFieldName = this._getFieldName();
|
||||
const fieldsInForm = Array.from(this.formEl.querySelectorAll('.s_website_form_field:not(.s_website_form_custom) .s_website_form_input')).map(el => el.name).filter(el => el !== currentFieldName);
|
||||
@@ -930,6 +934,27 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
availableFields.unshift(title);
|
||||
availableFields.forEach(option => selectEl.append(option.cloneNode(true)));
|
||||
}
|
||||
|
||||
const select = this._getSelect();
|
||||
const multipleInputs = this._getMultipleInputs();
|
||||
if (!select && !multipleInputs) {
|
||||
return;
|
||||
}
|
||||
|
||||
const field = Object.assign({}, this.fields[this._getFieldName()]);
|
||||
const type = this._getFieldType();
|
||||
|
||||
const list = document.createElement('we-list');
|
||||
const optionText = select ? 'Option' : type === 'selection' ? 'Radio' : 'Checkbox';
|
||||
list.setAttribute('string', `${optionText} List`);
|
||||
list.dataset.addItemTitle = _.str.sprintf(_t("Add new %s"), optionText);
|
||||
list.dataset.renderListItems = '';
|
||||
|
||||
if (!this._isFieldCustom()) {
|
||||
await this._fetchFieldRecords(field);
|
||||
list.dataset.availableRecords = JSON.stringify(field.records);
|
||||
}
|
||||
uiFragment.appendChild(list);
|
||||
},
|
||||
/**
|
||||
* Replace the target content with the field provided
|
||||
@@ -946,211 +971,25 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
[...htmlField.attributes].forEach(el => this.$target[0].removeAttribute(el.nodeName));
|
||||
[...htmlField.attributes].forEach(el => this.$target[0].setAttribute(el.nodeName, el.nodeValue));
|
||||
},
|
||||
|
||||
/**
|
||||
* To do after rerenderXML to add the list to the options
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_renderList: function () {
|
||||
let addItemButton, addItemTitle, listTitle;
|
||||
const select = this._getSelect();
|
||||
const multipleInputs = this._getMultipleInputs();
|
||||
this.listTable = document.createElement('table');
|
||||
const isCustomField = this._isFieldCustom();
|
||||
|
||||
if (select) {
|
||||
listTitle = 'Options List';
|
||||
addItemTitle = 'Add new Option';
|
||||
select.querySelectorAll('option').forEach(opt => {
|
||||
this._addItemToTable(opt.value, opt.textContent.trim());
|
||||
});
|
||||
this._renderListItems();
|
||||
} else if (multipleInputs) {
|
||||
listTitle = multipleInputs.querySelector('.radio') ? 'Radio List' : 'Checkbox List';
|
||||
addItemTitle = 'Add new Checkbox';
|
||||
multipleInputs.querySelectorAll('.checkbox, .radio').forEach(opt => {
|
||||
this._addItemToTable(opt.querySelector('input').value, opt.querySelector('.s_website_form_check_label').textContent.trim());
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCustomField) {
|
||||
addItemButton = document.createElement('we-button');
|
||||
addItemButton.textContent = addItemTitle;
|
||||
addItemButton.classList.add('o_we_list_add_optional');
|
||||
addItemButton.dataset.noPreview = 'true';
|
||||
} else {
|
||||
addItemButton = document.createElement('we-select');
|
||||
addItemButton.classList.add('o_we_user_value_widget'); // Todo dont use user value widget class
|
||||
const togglerEl = document.createElement('we-toggler');
|
||||
togglerEl.textContent = addItemTitle;
|
||||
addItemButton.appendChild(togglerEl);
|
||||
const selectMenuEl = document.createElement('we-selection-items');
|
||||
addItemButton.appendChild(selectMenuEl);
|
||||
this._loadListDropdown(selectMenuEl);
|
||||
}
|
||||
const selectInputEl = document.createElement('we-list');
|
||||
const title = document.createElement('we-title');
|
||||
title.textContent = listTitle;
|
||||
selectInputEl.appendChild(title);
|
||||
const tableWrapper = document.createElement('div');
|
||||
tableWrapper.classList.add('oe_we_table_wraper');
|
||||
tableWrapper.appendChild(this.listTable);
|
||||
selectInputEl.appendChild(tableWrapper);
|
||||
selectInputEl.appendChild(addItemButton);
|
||||
this.el.insertBefore(selectInputEl, this.el.querySelector('[data-set-placeholder]'));
|
||||
this._makeListItemsSortable();
|
||||
},
|
||||
/**
|
||||
* Load the dropdown of the list with the records missing from the list.
|
||||
*
|
||||
* @private
|
||||
* @param {HTMLElement} selectMenu
|
||||
*/
|
||||
_loadListDropdown: function (selectMenu) {
|
||||
selectMenu = selectMenu || this.el.querySelector('we-list we-selection-items');
|
||||
if (selectMenu) {
|
||||
$(selectMenu).empty();
|
||||
const field = Object.assign({}, this.fields[this._getFieldName()]);
|
||||
this._fetchFieldRecords(field).then(() => {
|
||||
let buttonItems;
|
||||
const optionIds = Array.from(this.listTable.querySelectorAll('input')).map(opt => {
|
||||
return field.type === 'selection' ? opt.name : parseInt(opt.name);
|
||||
});
|
||||
const availableRecords = (field.records || []).filter(el => !optionIds.includes(el.id));
|
||||
if (availableRecords.length) {
|
||||
buttonItems = availableRecords.map(el => {
|
||||
const option = document.createElement('we-button');
|
||||
option.classList.add('o_we_list_add_existing');
|
||||
option.dataset.addOption = el.id;
|
||||
option.dataset.noPreview = 'true';
|
||||
option.textContent = el.display_name;
|
||||
return option;
|
||||
});
|
||||
} else {
|
||||
const title = document.createElement('we-title');
|
||||
title.textContent = 'No more records';
|
||||
buttonItems = [title];
|
||||
}
|
||||
buttonItems.forEach(button => selectMenu.appendChild(button));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_makeListItemsSortable: function () {
|
||||
$(this.listTable).sortable({
|
||||
axis: 'y',
|
||||
handle: '.o_we_drag_handle',
|
||||
items: 'tr',
|
||||
cursor: 'move',
|
||||
opacity: 0.6,
|
||||
stop: (event, ui) => {
|
||||
this._renderListItems();
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {string} id
|
||||
* @param {string} text
|
||||
*/
|
||||
_addItemToTable: function (id, text) {
|
||||
const isCustomField = this._isFieldCustom();
|
||||
const draggableEl = document.createElement('we-button');
|
||||
draggableEl.classList.add('o_we_drag_handle', 'o_we_link', 'fa', 'fa-fw', 'fa-arrows');
|
||||
draggableEl.dataset.noPreview = 'true';
|
||||
const inputEl = document.createElement('input');
|
||||
inputEl.type = 'text';
|
||||
if (text) {
|
||||
inputEl.value = text;
|
||||
}
|
||||
if (!isCustomField && id) {
|
||||
inputEl.name = id;
|
||||
}
|
||||
inputEl.disabled = !isCustomField;
|
||||
const trEl = document.createElement('tr');
|
||||
const buttonEl = document.createElement('we-button');
|
||||
buttonEl.classList.add('o_we_select_remove_option', 'o_we_link', 'o_we_text_danger', 'fa', 'fa-fw', 'fa-minus');
|
||||
buttonEl.dataset.removeOption = id;
|
||||
buttonEl.dataset.noPreview = 'true';
|
||||
const draggableTdEl = document.createElement('td');
|
||||
const inputTdEl = document.createElement('td');
|
||||
const buttonTdEl = document.createElement('td');
|
||||
draggableTdEl.appendChild(draggableEl);
|
||||
trEl.appendChild(draggableTdEl);
|
||||
inputTdEl.appendChild(inputEl);
|
||||
trEl.appendChild(inputTdEl);
|
||||
buttonTdEl.appendChild(buttonEl);
|
||||
trEl.appendChild(buttonTdEl);
|
||||
this.listTable.appendChild(trEl);
|
||||
if (isCustomField) {
|
||||
inputEl.focus();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Apply the we-list on the target and rebuild the input(s)
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_renderListItems: function () {
|
||||
const multiInputsWrap = this._getMultipleInputs();
|
||||
const selectWrap = this.$target[0].querySelector('#editable_select');
|
||||
const isRequiredField = this._isFieldRequired();
|
||||
const name = this._getFieldName();
|
||||
if (multiInputsWrap) {
|
||||
const type = multiInputsWrap.querySelector('.radio') ? 'radio' : 'checkbox';
|
||||
$(multiInputsWrap).empty();
|
||||
const params = {
|
||||
field: {
|
||||
name: name,
|
||||
id: Math.random().toString(36).substring(2, 15), // Big unique ID
|
||||
required: isRequiredField,
|
||||
formatInfo: {
|
||||
multiPosition: multiInputsWrap.dataset.display,
|
||||
}
|
||||
}
|
||||
};
|
||||
this._getListItems().forEach((record, idx) => {
|
||||
params.record_index = idx;
|
||||
params.record = record;
|
||||
const template = document.createElement('template');
|
||||
$(template).html(qweb.render(`website_form.${type}`, params));
|
||||
multiInputsWrap.appendChild(template.content.firstElementChild);
|
||||
});
|
||||
} else if (selectWrap) {
|
||||
$(selectWrap).empty();
|
||||
this.listTable.querySelectorAll('input').forEach(el => {
|
||||
const option = document.createElement('div');
|
||||
option.id = (el.name || el.value);
|
||||
option.classList.add('s_website_form_select_item');
|
||||
option.textContent = el.value;
|
||||
selectWrap.appendChild(option);
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Returns an array based on the we-list containing the field's records
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
_getListItems: function () {
|
||||
if (!this.listTable) {
|
||||
return null;
|
||||
const select = this._getSelect();
|
||||
const multipleInputs = this._getMultipleInputs();
|
||||
let options = [];
|
||||
if (select) {
|
||||
options = [...select.querySelectorAll('option')];
|
||||
} else if (multipleInputs) {
|
||||
options = [...multipleInputs.querySelectorAll('.checkbox input, .radio input')];
|
||||
}
|
||||
const isCustomField = this._isFieldCustom();
|
||||
const records = [];
|
||||
this.listTable.querySelectorAll('input').forEach(el => {
|
||||
const id = isCustomField ? el.value : el.name;
|
||||
records.push({
|
||||
id: id,
|
||||
display_name: el.value,
|
||||
});
|
||||
return options.map(opt => {
|
||||
const id = parseInt(opt.value);
|
||||
return {
|
||||
id: isNaN(id) ? opt.value : id,
|
||||
display_name: select ? opt.textContent : opt.value
|
||||
};
|
||||
});
|
||||
return records;
|
||||
},
|
||||
/**
|
||||
* Returns the select element if it exist else null
|
||||
@@ -1161,53 +1000,6 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
|
||||
_getSelect: function () {
|
||||
return this.$target[0].querySelector('select');
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onRemoveItemClick: function (ev) {
|
||||
ev.target.closest('tr').remove();
|
||||
this._loadListDropdown();
|
||||
this._renderListItems();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onAddCustomItemClick: function (ev) {
|
||||
this._addItemToTable();
|
||||
this._makeListItemsSortable();
|
||||
this._renderListItems();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onAddExistingItemClick: function (ev) {
|
||||
const value = ev.currentTarget.dataset.addOption;
|
||||
this._addItemToTable(value, ev.currentTarget.textContent);
|
||||
this._makeListItemsSortable();
|
||||
this._loadListDropdown();
|
||||
this._renderListItems();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onAddItemSelectClick: function (ev) {
|
||||
ev.currentTarget.querySelector('we-toggler').classList.toggle('active');
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onListItemInput: function () {
|
||||
this._renderListItems();
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.AddFieldForm = FormEditor.extend({
|
||||
|
||||
@@ -275,13 +275,18 @@
|
||||
<!-- Generic one2many -->
|
||||
<t t-if="field.relation != 'ir.attachment'">
|
||||
<t t-call="website_form.field">
|
||||
<select class="form-control s_website_form_input" t-att-name="field.name" t-att-required="field.required || field.modelRequired || None" t-att-id="field.id">
|
||||
<select class="form-control s_website_form_input" t-att-name="field.name" t-att-required="field.required || field.modelRequired || None" t-att-id="field.id" style="display: none">
|
||||
<t t-foreach="field.records" t-as="record">
|
||||
<option t-att-value="record.id" t-att-selected="record.selected">
|
||||
<t t-esc="record.display_name"/>
|
||||
</option>
|
||||
</t>
|
||||
</select>
|
||||
<div id="editable_select" class="form-control s_website_form_input">
|
||||
<t t-foreach="field.records" t-as="record">
|
||||
<div t-esc="record.display_name" t-att-id="record.id" class="s_website_form_select_item"/>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<data>
|
||||
<template id="assets_editor" name="Website Form Editor Assets Editor" inherit_id="website.assets_editor">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/website_form/static/src/scss/wysiwyg_snippets.scss"/>
|
||||
<script type="text/javascript" src="/website_form/static/src/snippets/s_website_form/options.js"/>
|
||||
<script type="text/javascript" src="/website_form/static/src/js/website_form_editor_registry.js"/>
|
||||
</xpath>
|
||||
|
||||
Reference in New Issue
Block a user