From b8dc7ff18ecea2afe7268cdcbb698c2c6ffea00b Mon Sep 17 00:00:00 2001 From: tod31 Date: Wed, 7 Apr 2021 16:11:19 +0200 Subject: [PATCH 01/15] add custom field "stringtemplate" --- client/components/cards/cardCustomFields.jade | 20 +++++++++++++ client/components/cards/cardCustomFields.js | 30 +++++++++++++++++++ client/components/cards/minicard.jade | 3 ++ client/components/cards/minicard.js | 17 +++++++++++ .../sidebar/sidebarCustomFields.jade | 6 ++++ .../components/sidebar/sidebarCustomFields.js | 21 ++++++++++++- i18n/en.i18n.json | 4 ++- models/customFields.js | 5 ++++ 8 files changed, 104 insertions(+), 2 deletions(-) diff --git a/client/components/cards/cardCustomFields.jade b/client/components/cards/cardCustomFields.jade index 11f1bc2f6..aef24b696 100644 --- a/client/components/cards/cardCustomFields.jade +++ b/client/components/cards/cardCustomFields.jade @@ -119,3 +119,23 @@ template(name="cardCustomField-dropdown") if value +viewer = selectedItem + +template(name="cardCustomField-stringtemplate") + if canModifyCard + +inlinedForm(classNames="js-card-customfield-stringtemplate") + +editor(autofocus=true) + = data.value + .edit-controls.clearfix + button.primary(type="submit") {{_ 'save'}} + a.fa.fa-times-thin.js-close-inlined-form + else + a.js-open-inlined-form + if value + +viewer + = formattedValue + else + | {{_ 'edit'}} + else + if value + +viewer + = formattedValue diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index 4469e221e..effcc3e91 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -234,3 +234,33 @@ CardCustomField.register('cardCustomField'); ]; } }.register('cardCustomField-dropdown')); + +// cardCustomField-stringtemplate +(class extends CardCustomField { + onCreated() { + super.onCreated(); + + this.stringtemplateFormat = this.data().definition.settings.stringtemplateFormat; + } + + formattedValue() { + lines = this.data().value.replace(/\r\n|\n\r|\n|\r/g, '\n').split('\n'); + lines = lines.map(line => + this.stringtemplateFormat.replace(/%\{value\}/gi, line) + ); + + return lines.join(' '); + } + + events() { + return [ + { + 'submit .js-card-customfield-stringtemplate'(event) { + event.preventDefault(); + const value = this.currentComponent().getValue(); + this.card.setCustomField(this.customFieldId, value); + }, + }, + ]; + } +}.register('cardCustomField-stringtemplate')); diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index 3931bffbc..cc37cd383 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -82,6 +82,9 @@ template(name="minicard") +minicardCustomFieldDate else if $eq definition.type "checkbox" .materialCheckBox(class="{{#if value }}is-checked{{/if}}") + else if $eq definition.type "stringtemplate" + +viewer + = formattedStringtemplateCustomFieldValue(definition) else +viewer = trueValue diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index 88348fc48..60996684f 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -21,6 +21,23 @@ BlazeComponent.extendComponent({ }).format(customFieldTrueValue); }, + formattedStringtemplateCustomFieldValue(definition) { + const customField = this.data() + .customFieldsWD() + .find(f => f._id === definition._id); + + if(customField && customField.trueValue) { + lines = customField.trueValue.replace(/\r\n|\n\r|\n|\r/g, '\n').split('\n'); + lines = lines.map(line => + definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, line) + ); + + return lines.join(' '); + } else { + return ''; + } + }, + events() { return [ { diff --git a/client/components/sidebar/sidebarCustomFields.jade b/client/components/sidebar/sidebarCustomFields.jade index 3f7a3e2ee..58f095f12 100644 --- a/client/components/sidebar/sidebarCustomFields.jade +++ b/client/components/sidebar/sidebarCustomFields.jade @@ -50,6 +50,12 @@ template(name="createCustomFieldPopup") each dropdownItems.get input.js-dropdown-item(type="text" value=name placeholder="") input.js-dropdown-item.last(type="text" value="" placeholder="{{_ 'custom-field-dropdown-options-placeholder'}}") + + div.js-field-settings.js-field-settings-paramlink(class="{{#if isTypeNotSelected 'stringtemplate'}}hide{{/if}}") + label + | {{_ 'custom-field-stringtemplate-format'}} + input.js-field-stringtemplate(type="text" value=getStringtemplateFormat) + a.flex.js-field-show-on-card(class="{{#if showOnCard}}is-checked{{/if}}") .materialCheckBox(class="{{#if showOnCard}}is-checked{{/if}}") diff --git a/client/components/sidebar/sidebarCustomFields.js b/client/components/sidebar/sidebarCustomFields.js index 24385f72a..5cc1adf10 100644 --- a/client/components/sidebar/sidebarCustomFields.js +++ b/client/components/sidebar/sidebarCustomFields.js @@ -16,7 +16,7 @@ BlazeComponent.extendComponent({ }).register('customFieldsSidebar'); const CreateCustomFieldPopup = BlazeComponent.extendComponent({ - _types: ['text', 'number', 'date', 'dropdown', 'currency', 'checkbox'], + _types: ['text', 'number', 'date', 'dropdown', 'currency', 'checkbox', 'stringtemplate'], _currencyList: [ { @@ -77,6 +77,12 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ ? this.data().settings.dropdownItems : [], ); + + this.stringtemplateFormat = new ReactiveVar( + this.data().settings && this.data().settings.stringtemplateFormat + ? this.data().settings.stringtemplateFormat + : "", + ); }, types() { @@ -121,6 +127,10 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ return items; }, + getStringtemplateFormat() { + return this.stringtemplateFormat.get(); + }, + getSettings() { const settings = {}; switch (this.type.get()) { @@ -136,6 +146,11 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ settings.dropdownItems = dropdownItems; break; } + case 'stringtemplate': { + const stringtemplateFormat = this.stringtemplateFormat.get(); + settings.stringtemplateFormat = stringtemplateFormat; + break; + } } return settings; }, @@ -158,6 +173,10 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ evt.target.value = ''; } }, + 'input .js-field-stringtemplate'(evt) { + const value = evt.target.value; + this.stringtemplateFormat.set(value); + }, 'click .js-field-show-on-card'(evt) { let $target = $(evt.target); if (!$target.hasClass('js-field-show-on-card')) { diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index e20645c51..c5cb1f8ba 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -988,5 +988,7 @@ "hide-system-messages-of-all-users": "Hide system messages of all users", "now-system-messages-of-all-users-are-hidden": "Now system messages of all users are hidden", "move-swimlane": "Move Swimlane", - "moveSwimlanePopup-title": "Move Swimlane" + "moveSwimlanePopup-title": "Move Swimlane", + "custom-field-stringtemplate": "String Template", + "custom-field-stringtemplate-format": "Format" } diff --git a/models/customFields.js b/models/customFields.js index 5f3150174..4024e234e 100644 --- a/models/customFields.js +++ b/models/customFields.js @@ -29,6 +29,7 @@ CustomFields.attachSchema( 'dropdown', 'checkbox', 'currency', + 'stringtemplate', ], }, settings: { @@ -64,6 +65,10 @@ CustomFields.attachSchema( }, }), }, + 'settings.stringtemplateFormat': { + type: String, + optional: true, + }, showOnCard: { /** * should we show on the cards this custom field From af676c8f269d86775953ada9ca5dd578724f39c8 Mon Sep 17 00:00:00 2001 From: tod31 Date: Thu, 8 Apr 2021 08:34:40 +0200 Subject: [PATCH 02/15] fix custom field type name --- client/components/sidebar/sidebarCustomFields.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/sidebar/sidebarCustomFields.jade b/client/components/sidebar/sidebarCustomFields.jade index 58f095f12..f71108a6c 100644 --- a/client/components/sidebar/sidebarCustomFields.jade +++ b/client/components/sidebar/sidebarCustomFields.jade @@ -51,7 +51,7 @@ template(name="createCustomFieldPopup") input.js-dropdown-item(type="text" value=name placeholder="") input.js-dropdown-item.last(type="text" value="" placeholder="{{_ 'custom-field-dropdown-options-placeholder'}}") - div.js-field-settings.js-field-settings-paramlink(class="{{#if isTypeNotSelected 'stringtemplate'}}hide{{/if}}") + div.js-field-settings.js-field-settings-stringtemplate(class="{{#if isTypeNotSelected 'stringtemplate'}}hide{{/if}}") label | {{_ 'custom-field-stringtemplate-format'}} input.js-field-stringtemplate(type="text" value=getStringtemplateFormat) From 8164af281d51054142e1f2b3fe8e56479e05c4dc Mon Sep 17 00:00:00 2001 From: tod31 Date: Thu, 8 Apr 2021 09:29:24 +0200 Subject: [PATCH 03/15] add info text for placeholder --- i18n/en.i18n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index c5cb1f8ba..ca246338d 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -990,5 +990,5 @@ "move-swimlane": "Move Swimlane", "moveSwimlanePopup-title": "Move Swimlane", "custom-field-stringtemplate": "String Template", - "custom-field-stringtemplate-format": "Format" + "custom-field-stringtemplate-format": "Format (use %{value} as placeholder)" } From b1c26e6f6413dd408b25feb1310e5aafb3f77570 Mon Sep 17 00:00:00 2001 From: tod31 Date: Thu, 8 Apr 2021 10:54:28 +0200 Subject: [PATCH 04/15] remove empty lines --- client/components/cards/cardCustomFields.js | 12 ++++++------ client/components/cards/minicard.js | 17 ++++++++--------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index effcc3e91..016c22983 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -244,12 +244,12 @@ CardCustomField.register('cardCustomField'); } formattedValue() { - lines = this.data().value.replace(/\r\n|\n\r|\n|\r/g, '\n').split('\n'); - lines = lines.map(line => - this.stringtemplateFormat.replace(/%\{value\}/gi, line) - ); - - return lines.join(' '); + return this.data().value + .replace(/\r\n|\n\r|\n|\r/g, '\n') + .split('\n') + .filter(value => value.trim() != '') + .map(value => this.stringtemplateFormat.replace(/%\{value\}/gi, value)) + .join(' '); } events() { diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index 60996684f..ca3e241c8 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -26,16 +26,15 @@ BlazeComponent.extendComponent({ .customFieldsWD() .find(f => f._id === definition._id); - if(customField && customField.trueValue) { - lines = customField.trueValue.replace(/\r\n|\n\r|\n|\r/g, '\n').split('\n'); - lines = lines.map(line => - definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, line) - ); + const customFieldTrueValue = + customField && customField.trueValue ? customField.trueValue : ''; - return lines.join(' '); - } else { - return ''; - } + return customFieldTrueValue + .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(' '); }, events() { From 37607f72b29191ef43bf0e8be0aa6be555fdf09a Mon Sep 17 00:00:00 2001 From: tod31 Date: Thu, 8 Apr 2021 11:01:32 +0200 Subject: [PATCH 05/15] code cosmetics --- client/components/sidebar/sidebarCustomFields.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/client/components/sidebar/sidebarCustomFields.js b/client/components/sidebar/sidebarCustomFields.js index 5cc1adf10..977427e84 100644 --- a/client/components/sidebar/sidebarCustomFields.js +++ b/client/components/sidebar/sidebarCustomFields.js @@ -16,7 +16,15 @@ BlazeComponent.extendComponent({ }).register('customFieldsSidebar'); const CreateCustomFieldPopup = BlazeComponent.extendComponent({ - _types: ['text', 'number', 'date', 'dropdown', 'currency', 'checkbox', 'stringtemplate'], + _types: [ + 'text', + 'number', + 'date', + 'dropdown', + 'currency', + 'checkbox', + 'stringtemplate', + ], _currencyList: [ { @@ -81,7 +89,7 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ this.stringtemplateFormat = new ReactiveVar( this.data().settings && this.data().settings.stringtemplateFormat ? this.data().settings.stringtemplateFormat - : "", + : '', ); }, From 880d94e220acb4d65e23156fe5ad3c3775e1a53c Mon Sep 17 00:00:00 2001 From: tod31 Date: Thu, 8 Apr 2021 12:54:59 +0200 Subject: [PATCH 06/15] use nonidentity instead of inequality --- client/components/cards/cardCustomFields.js | 2 +- client/components/cards/minicard.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index 016c22983..37493578b 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -247,7 +247,7 @@ CardCustomField.register('cardCustomField'); return this.data().value .replace(/\r\n|\n\r|\n|\r/g, '\n') .split('\n') - .filter(value => value.trim() != '') + .filter(value => value.trim() !== '') .map(value => this.stringtemplateFormat.replace(/%\{value\}/gi, value)) .join(' '); } diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index ca3e241c8..68fcad261 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -32,7 +32,7 @@ BlazeComponent.extendComponent({ return customFieldTrueValue .replace(/\r\n|\n\r|\n|\r/g, '\n') .split('\n') - .filter(value => value.trim() != '') + .filter(value => value.trim() !== '') .map(value => definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, value)) .join(' '); }, From 3778a9ebcf17f9b9b81740b98280881e46aeee31 Mon Sep 17 00:00:00 2001 From: tod31 Date: Fri, 9 Apr 2021 11:28:32 +0200 Subject: [PATCH 07/15] add configuration field for separator --- client/components/cards/cardCustomFields.js | 3 ++- client/components/cards/minicard.js | 2 +- .../sidebar/sidebarCustomFields.jade | 5 ++++- .../components/sidebar/sidebarCustomFields.js | 19 ++++++++++++++++++- i18n/en.i18n.json | 3 ++- models/customFields.js | 4 ++++ 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index 37493578b..30d0a9e36 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -241,6 +241,7 @@ CardCustomField.register('cardCustomField'); super.onCreated(); this.stringtemplateFormat = this.data().definition.settings.stringtemplateFormat; + this.stringtemplateSeparator = this.data().definition.settings.stringtemplateSeparator; } formattedValue() { @@ -249,7 +250,7 @@ CardCustomField.register('cardCustomField'); .split('\n') .filter(value => value.trim() !== '') .map(value => this.stringtemplateFormat.replace(/%\{value\}/gi, value)) - .join(' '); + .join(this.stringtemplateSeparator ?? ''); } events() { diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index 68fcad261..10e2f4a84 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -34,7 +34,7 @@ BlazeComponent.extendComponent({ .split('\n') .filter(value => value.trim() !== '') .map(value => definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, value)) - .join(' '); + .join(definition.settings.stringtemplateSeparator ?? ''); }, events() { diff --git a/client/components/sidebar/sidebarCustomFields.jade b/client/components/sidebar/sidebarCustomFields.jade index f71108a6c..d1970ef1a 100644 --- a/client/components/sidebar/sidebarCustomFields.jade +++ b/client/components/sidebar/sidebarCustomFields.jade @@ -54,7 +54,10 @@ template(name="createCustomFieldPopup") div.js-field-settings.js-field-settings-stringtemplate(class="{{#if isTypeNotSelected 'stringtemplate'}}hide{{/if}}") label | {{_ 'custom-field-stringtemplate-format'}} - input.js-field-stringtemplate(type="text" value=getStringtemplateFormat) + input.js-field-stringtemplate-format(type="text" value=getStringtemplateFormat) + label + | {{_ 'custom-field-stringtemplate-separator'}} + input.js-field-stringtemplate-separator(type="text" value=getStringtemplateSeparator) a.flex.js-field-show-on-card(class="{{#if showOnCard}}is-checked{{/if}}") .materialCheckBox(class="{{#if showOnCard}}is-checked{{/if}}") diff --git a/client/components/sidebar/sidebarCustomFields.js b/client/components/sidebar/sidebarCustomFields.js index 977427e84..2f1fa7dd6 100644 --- a/client/components/sidebar/sidebarCustomFields.js +++ b/client/components/sidebar/sidebarCustomFields.js @@ -91,6 +91,12 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ ? this.data().settings.stringtemplateFormat : '', ); + + this.stringtemplateSeparator = new ReactiveVar( + this.data().settings && this.data().settings.stringtemplateSeparator + ? this.data().settings.stringtemplateSeparator + : '', + ); }, types() { @@ -139,6 +145,10 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ return this.stringtemplateFormat.get(); }, + getStringtemplateSeparator() { + return this.stringtemplateSeparator.get(); + }, + getSettings() { const settings = {}; switch (this.type.get()) { @@ -157,6 +167,9 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ case 'stringtemplate': { const stringtemplateFormat = this.stringtemplateFormat.get(); settings.stringtemplateFormat = stringtemplateFormat; + + const stringtemplateSeparator = this.stringtemplateSeparator.get(); + settings.stringtemplateSeparator = stringtemplateSeparator; break; } } @@ -181,10 +194,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({ evt.target.value = ''; } }, - 'input .js-field-stringtemplate'(evt) { + 'input .js-field-stringtemplate-format'(evt) { const value = evt.target.value; this.stringtemplateFormat.set(value); }, + 'input .js-field-stringtemplate-separator'(evt) { + const value = evt.target.value; + this.stringtemplateSeparator.set(value); + }, 'click .js-field-show-on-card'(evt) { let $target = $(evt.target); if (!$target.hasClass('js-field-show-on-card')) { diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index ca246338d..24711ba85 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -990,5 +990,6 @@ "move-swimlane": "Move Swimlane", "moveSwimlanePopup-title": "Move Swimlane", "custom-field-stringtemplate": "String Template", - "custom-field-stringtemplate-format": "Format (use %{value} as placeholder)" + "custom-field-stringtemplate-format": "Format (use %{value} as placeholder)", + "custom-field-stringtemplate-separator": "Separator (use   or   for a space)" } diff --git a/models/customFields.js b/models/customFields.js index 4024e234e..9f5db0667 100644 --- a/models/customFields.js +++ b/models/customFields.js @@ -69,6 +69,10 @@ CustomFields.attachSchema( type: String, optional: true, }, + 'settings.stringtemplateSeparator': { + type: String, + optional: true, + }, showOnCard: { /** * should we show on the cards this custom field From d4c69c3932b3a8723c6e1f4301400fd79fa6461d Mon Sep 17 00:00:00 2001 From: tod31 Date: Fri, 9 Apr 2021 12:46:12 +0200 Subject: [PATCH 08/15] fix custom-field-stringtemplate-separator string --- i18n/en.i18n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 24711ba85..2a99b886e 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -991,5 +991,5 @@ "moveSwimlanePopup-title": "Move Swimlane", "custom-field-stringtemplate": "String Template", "custom-field-stringtemplate-format": "Format (use %{value} as placeholder)", - "custom-field-stringtemplate-separator": "Separator (use   or   for a space)" + "custom-field-stringtemplate-separator": "Separator (use or   for a space)" } From 5cfa03ec071d353786aac50f5f020cd23839a8c3 Mon Sep 17 00:00:00 2001 From: tod31 Date: Sun, 11 Apr 2021 14:15:57 +0200 Subject: [PATCH 09/15] use string array to store items in database --- client/components/cards/cardCustomFields.jade | 5 +- client/components/cards/cardCustomFields.js | 55 +++++++++++++++++-- client/components/cards/minicard.js | 6 +- models/cards.js | 6 +- 4 files changed, 60 insertions(+), 12 deletions(-) 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: { From a8a3c9ad19eebaa2ab6a346d2c1ec474993b8a20 Mon Sep 17 00:00:00 2001 From: tod31 Date: Sun, 11 Apr 2021 14:18:12 +0200 Subject: [PATCH 10/15] fix default value --- client/components/cards/minicard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index 1fd117d50..90792375c 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -27,7 +27,7 @@ BlazeComponent.extendComponent({ .find(f => f._id === definition._id); const customFieldTrueValue = - customField && customField.trueValue ? customField.trueValue : ''; + customField && customField.trueValue ? customField.trueValue : []; return customFieldTrueValue // .replace(/\r\n|\n\r|\n|\r/g, '\n') From 31eb9b26b481ebb1802e5908db17b048ea022f19 Mon Sep 17 00:00:00 2001 From: tod31 Date: Sun, 11 Apr 2021 20:07:31 +0200 Subject: [PATCH 11/15] use tracker afterFlush to set focus --- client/components/cards/cardCustomFields.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index 7fe16463e..0e82e512d 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -287,7 +287,12 @@ CardCustomField.register('cardCustomField'); let items = this.getItems(); items.splice(idx + 1, 0, ''); this.stringtemplateItems.set(items); - //event.target.nextSibling.focus(); + + Tracker.afterFlush(() => { + const element = this.findAll('input')[idx + 1]; + element.focus(); + element.value = ''; + }); } } } From 4b904247733acfc9cad43a2d5d26810444daf95c Mon Sep 17 00:00:00 2001 From: tod31 Date: Mon, 12 Apr 2021 10:49:14 +0200 Subject: [PATCH 12/15] event handling/focussing --- client/components/cards/cardCustomFields.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index 0e82e512d..5e36bc6f2 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -276,12 +276,23 @@ CardCustomField.register('cardCustomField'); if (event.keyCode === 13) { event.preventDefault(); - if (event.target.value.trim()) { - if(event.target === this.find('input.last')) { + if (!!event.target.value.trim()) { + const inputLast = this.find('input.last'); + + if(event.target === inputLast) { + console.log("keydown[enter] - last"); const items = this.getItems(); this.stringtemplateItems.set(items); - this.find('input.last').value = ''; + inputLast.value = ''; + } else if(event.target.nextSibling === inputLast) { + console.log("keydown[enter] - last-1"); + const items = this.getItems(); + this.stringtemplateItems.set(items); + inputLast.focus(); } else { + console.log("keydown[enter]"); + event.target.blur(); + const idx = Array.from(this.findAll('input')) .indexOf(event.target); let items = this.getItems(); From ceec27bf90b8555710df7899cbcd568993835644 Mon Sep 17 00:00:00 2001 From: tod31 Date: Mon, 12 Apr 2021 11:01:19 +0200 Subject: [PATCH 13/15] cleanup and cosmetics --- client/components/cards/cardCustomFields.js | 31 ++++++++------------- client/components/cards/minicard.js | 2 -- models/cards.js | 2 +- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index 5e36bc6f2..b05076960 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -243,15 +243,11 @@ CardCustomField.register('cardCustomField'); this.stringtemplateFormat = this.data().definition.settings.stringtemplateFormat; this.stringtemplateSeparator = this.data().definition.settings.stringtemplateSeparator; - this.stringtemplateItems = new ReactiveVar( - this.data().value ?? [], - ); + this.stringtemplateItems = new ReactiveVar(this.data().value ?? []); } formattedValue() { - return this.stringtemplateItems.get() - // .replace(/\r\n|\n\r|\n|\r/g, '\n') - // .split('\n') + return (this.data().value ?? []) .filter(value => !!value.trim()) .map(value => this.stringtemplateFormat.replace(/%\{value\}/gi, value)) .join(this.stringtemplateSeparator ?? ''); @@ -276,28 +272,21 @@ CardCustomField.register('cardCustomField'); if (event.keyCode === 13) { event.preventDefault(); - if (!!event.target.value.trim()) { + if (event.target.value.trim()) { const inputLast = this.find('input.last'); - if(event.target === inputLast) { - console.log("keydown[enter] - last"); - const items = this.getItems(); - this.stringtemplateItems.set(items); + let items = this.getItems(); + + if (event.target === inputLast) { inputLast.value = ''; - } else if(event.target.nextSibling === inputLast) { - console.log("keydown[enter] - last-1"); - const items = this.getItems(); - this.stringtemplateItems.set(items); + } else if (event.target.nextSibling === inputLast) { inputLast.focus(); } else { - console.log("keydown[enter]"); event.target.blur(); const idx = Array.from(this.findAll('input')) .indexOf(event.target); - let items = this.getItems(); items.splice(idx + 1, 0, ''); - this.stringtemplateItems.set(items); Tracker.afterFlush(() => { const element = this.findAll('input')[idx + 1]; @@ -305,11 +294,13 @@ CardCustomField.register('cardCustomField'); element.value = ''; }); } + + this.stringtemplateItems.set(items); } } }, - 'focusout .js-card-customfield-stringtemplate-item'(event) { + 'blur .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); @@ -319,7 +310,7 @@ CardCustomField.register('cardCustomField'); '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 90792375c..ff2fa640c 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -30,8 +30,6 @@ BlazeComponent.extendComponent({ customField && customField.trueValue ? customField.trueValue : []; return customFieldTrueValue - // .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 2a3593404..f0e33b22f 100644 --- a/models/cards.js +++ b/models/cards.js @@ -162,7 +162,7 @@ Cards.attachSchema( 'value.$': { type: String, optional: true, - } + }, }), }, dateLastActivity: { From c845d25ae5bbbc98c9f7f1908fa0ab9a87dabfc4 Mon Sep 17 00:00:00 2001 From: tod31 Date: Mon, 12 Apr 2021 11:14:25 +0200 Subject: [PATCH 14/15] add localization string --- client/components/cards/cardCustomFields.jade | 2 +- i18n/en.i18n.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/components/cards/cardCustomFields.jade b/client/components/cards/cardCustomFields.jade index eb8464e83..6fa1ac90e 100644 --- a/client/components/cards/cardCustomFields.jade +++ b/client/components/cards/cardCustomFields.jade @@ -125,7 +125,7 @@ template(name="cardCustomField-stringtemplate") +inlinedForm(classNames="js-card-customfield-stringtemplate") 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'}}") + input.js-card-customfield-stringtemplate-item.last(type="text" value="" placeholder="{{_ 'custom-field-stringtemplate-item-placeholder'}}") .edit-controls.clearfix button.primary(type="submit") {{_ 'save'}} a.fa.fa-times-thin.js-close-inlined-form diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 2a99b886e..847352fc4 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -991,5 +991,6 @@ "moveSwimlanePopup-title": "Move Swimlane", "custom-field-stringtemplate": "String Template", "custom-field-stringtemplate-format": "Format (use %{value} as placeholder)", - "custom-field-stringtemplate-separator": "Separator (use or   for a space)" + "custom-field-stringtemplate-separator": "Separator (use or   for a space)", + "custom-field-stringtemplate-item-placeholder": "Press enter to add more items" } From 1590701fc93c5bacdc715be44b1c2465063a5634 Mon Sep 17 00:00:00 2001 From: Martin Filser Date: Mon, 12 Apr 2021 17:08:25 +0200 Subject: [PATCH 15/15] Custom Fields StringTemplate, Ctrl-Enter the same as the button "Save" --- client/components/cards/cardCustomFields.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/components/cards/cardCustomFields.js b/client/components/cards/cardCustomFields.js index b05076960..832379432 100644 --- a/client/components/cards/cardCustomFields.js +++ b/client/components/cards/cardCustomFields.js @@ -272,7 +272,9 @@ CardCustomField.register('cardCustomField'); if (event.keyCode === 13) { event.preventDefault(); - if (event.target.value.trim()) { + if (event.metaKey || event.ctrlKey) { + this.find('button[type=submit]').click(); + } else if (event.target.value.trim()) { const inputLast = this.find('input.last'); let items = this.getItems();