diff --git a/application/Espo/Resources/i18n/en_US/Global.json b/application/Espo/Resources/i18n/en_US/Global.json index b797401c3d..b88ccb281a 100644 --- a/application/Espo/Resources/i18n/en_US/Global.json +++ b/application/Espo/Resources/i18n/en_US/Global.json @@ -271,6 +271,7 @@ "No": "No", "Select All Results": "Select All Results", "Value": "Value", + "Edit Item": "Edit Item", "Current version": "Current version", "List View": "List View", "Tree View": "Tree View", diff --git a/client/src/views/fields/array.js b/client/src/views/fields/array.js index 7815144159..aca106aff1 100644 --- a/client/src/views/fields/array.js +++ b/client/src/views/fields/array.js @@ -31,6 +31,10 @@ import BaseFieldView from 'views/fields/base'; import RegExpPattern from 'helpers/reg-exp-pattern'; import MultiSelect from 'ui/multi-select'; +import ModalView from 'views/modal'; +import Model from 'model'; +import EditForModalRecordView from 'views/record/edit-for-modal'; +import VarcharFieldView from 'views/fields/varchar'; /** * An array field. @@ -67,6 +71,7 @@ class ArrayFieldView extends BaseFieldView { * @property {string} [pattern] A regular expression pattern. * @property {boolean} [keepItems] Disable the ability to add or remove items. Reordering is allowed. * @property {number} [maxItemLength] Max item length. If not specified, 100 is used. + * @property {boolean} [itemsEditable] Items are editable. As of v9.2. */ /** @@ -128,6 +133,12 @@ class ArrayFieldView extends BaseFieldView { */ noDragHandle = false + /** + * @protected + * @type {string[]} + */ + selected + // noinspection JSCheckFunctionSignatures /** @inheritDoc */ @@ -171,12 +182,18 @@ class ArrayFieldView extends BaseFieldView { setup() { super.setup(); + this.addActionHandler('editItem', (e, target) => { + this.actionEditItem(target.dataset.value); + }); + this.noEmptyString = this.params.noEmptyString; if (this.params.maxItemLength != null) { this.maxItemLength = this.params.maxItemLength; } + this.maxItemLength = this.maxItemLength || this.MAX_ITEM_LENGTH; + this.listenTo(this.model, 'change:' + this.name, () => { this.selected = Espo.Utils.clone(this.model.get(this.name)) || []; }); @@ -704,6 +721,39 @@ class ArrayFieldView extends BaseFieldView { return span; })(), + ); + + if (this.params.itemsEditable && this.allowCustomOptions) { + div.append( + (() => { + const span = document.createElement('span'); + span.className = 'item-button' + span.append( + (() => { + const a = document.createElement('a'); + a.role = 'button'; + a.tabIndex = 0; + a.dataset.value = value; + a.dataset.action = 'editItem'; + a.append( + (() => { + const span = document.createElement('span'); + span.className = 'fas fa-pencil-alt fa-sm'; + + return span; + })(), + ); + + return a; + })(), + ) + + return span; + })(), + ); + } + + div.append( (() => { const span = document.createElement('span'); span.classList.add('text'); @@ -953,6 +1003,36 @@ class ArrayFieldView extends BaseFieldView { }); } + /** + * @protected + * @param value + */ + async actionEditItem(value) { + const view = new EditItemModalView({ + value: value, + required: this.noEmptyString, + maxLength: this.maxItemLength, + onApply: async data => { + const index = this.selected.findIndex(it => it === value); + + if (index < 0) { + return; + } + + this.selected[index] = data.value; + + this.selected = this.selected.filter((it, i) => this.selected.indexOf(it) === i); + + await this.reRender(); + this.trigger('change'); + }, + }); + + await this.assignView('dialog', view); + await view.render(); + + } + // noinspection JSUnusedGlobalSymbols /** * @protected @@ -982,3 +1062,94 @@ class ArrayFieldView extends BaseFieldView { } export default ArrayFieldView; + +class EditItemModalView extends ModalView { + + // language=Handlebars + templateContent = ` +