diff --git a/client/components/cards/cardCustomFields.jade b/client/components/cards/cardCustomFields.jade index aef24b696..eb8464e83 100644 --- a/client/components/cards/cardCustomFields.jade +++ b/client/components/cards/cardCustomFields.jade @@ -123,8 +123,9 @@ template(name="cardCustomField-dropdown") template(name="cardCustomField-stringtemplate") if canModifyCard +inlinedForm(classNames="js-card-customfield-stringtemplate") - +editor(autofocus=true) - = data.value + each item in stringtemplateItems.get + input.js-card-customfield-stringtemplate-item(type="text" value=item placeholder="") + input.js-card-customfield-stringtemplate-item.last(type="text" value="" placeholder="{{_ 'custom-field-dropdown-options-placeholder'}}") .edit-controls.clearfix button.primary(type="submit") {{_ 'save'}} a.fa.fa-times-thin.js-close-inlined-form diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index 30d0a9e36..7fe16463e 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -242,25 +242,68 @@ CardCustomField.register('cardCustomField'); this.stringtemplateFormat = this.data().definition.settings.stringtemplateFormat; this.stringtemplateSeparator = this.data().definition.settings.stringtemplateSeparator; + + this.stringtemplateItems = new ReactiveVar( + this.data().value ?? [], + ); } formattedValue() { - return this.data().value - .replace(/\r\n|\n\r|\n|\r/g, '\n') - .split('\n') - .filter(value => value.trim() !== '') + return this.stringtemplateItems.get() + // .replace(/\r\n|\n\r|\n|\r/g, '\n') + // .split('\n') + .filter(value => !!value.trim()) .map(value => this.stringtemplateFormat.replace(/%\{value\}/gi, value)) .join(this.stringtemplateSeparator ?? ''); } + getItems() { + return Array.from(this.findAll('input')) + .map(input => input.value) + .filter(value => !!value.trim()); + } + events() { return [ { 'submit .js-card-customfield-stringtemplate'(event) { event.preventDefault(); - const value = this.currentComponent().getValue(); - this.card.setCustomField(this.customFieldId, value); + const items = this.getItems(); + this.card.setCustomField(this.customFieldId, items); }, + + 'keydown .js-card-customfield-stringtemplate-item'(event) { + if (event.keyCode === 13) { + event.preventDefault(); + + if (event.target.value.trim()) { + if(event.target === this.find('input.last')) { + const items = this.getItems(); + this.stringtemplateItems.set(items); + this.find('input.last').value = ''; + } else { + const idx = Array.from(this.findAll('input')) + .indexOf(event.target); + let items = this.getItems(); + items.splice(idx + 1, 0, ''); + this.stringtemplateItems.set(items); + //event.target.nextSibling.focus(); + } + } + } + }, + + 'focusout .js-card-customfield-stringtemplate-item'(event) { + if (!event.target.value.trim() || event.target === this.find('input.last')) { + const items = this.getItems(); + this.stringtemplateItems.set(items); + this.find('input.last').value = ''; + } + }, + + 'click .js-close-inlined-form'(event) { + this.stringtemplateItems.set(this.data().value ?? []); + } }, ]; } diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index 10e2f4a84..1fd117d50 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -30,9 +30,9 @@ BlazeComponent.extendComponent({ customField && customField.trueValue ? customField.trueValue : ''; return customFieldTrueValue - .replace(/\r\n|\n\r|\n|\r/g, '\n') - .split('\n') - .filter(value => value.trim() !== '') + // .replace(/\r\n|\n\r|\n|\r/g, '\n') + // .split('\n') + .filter(value => !!value.trim()) .map(value => definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, value)) .join(definition.settings.stringtemplateSeparator ?? ''); }, diff --git a/models/cards.js b/models/cards.js index a0aed5c69..2a3593404 100644 --- a/models/cards.js +++ b/models/cards.js @@ -155,10 +155,14 @@ Cards.attachSchema( /** * value attached to the custom field */ - type: Match.OneOf(String, Number, Boolean, Date), + type: Match.OneOf(String, Number, Boolean, Date, [String]), optional: true, defaultValue: '', }, + 'value.$': { + type: String, + optional: true, + } }), }, dateLastActivity: {