Browse Source

Merge branch 'tod31-customfield-stringtemplate'

Lauri Ojansivu 4 năm trước cách đây
mục cha
commit
2fcf85fd61

+ 21 - 0
client/components/cards/cardCustomFields.jade

@@ -119,3 +119,24 @@ template(name="cardCustomField-dropdown")
         if value
             +viewer
                 = selectedItem
+
+template(name="cardCustomField-stringtemplate")
+    if canModifyCard
+        +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-stringtemplate-item-placeholder'}}")
+            .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

+ 83 - 0
client/components/cards/cardCustomFields.js

@@ -234,3 +234,86 @@ CardCustomField.register('cardCustomField');
     ];
   }
 }.register('cardCustomField-dropdown'));
+
+// cardCustomField-stringtemplate
+(class extends CardCustomField {
+  onCreated() {
+    super.onCreated();
+
+    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 ?? [])
+      .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 items = this.getItems();
+          this.card.setCustomField(this.customFieldId, items);
+        },
+
+        'keydown .js-card-customfield-stringtemplate-item'(event) {
+          if (event.keyCode === 13) {
+            event.preventDefault();
+
+            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();
+
+              if (event.target === inputLast) {
+                inputLast.value = '';
+              } else if (event.target.nextSibling === inputLast) {
+                inputLast.focus();
+              } else {
+                event.target.blur();
+
+                const idx = Array.from(this.findAll('input'))
+                  .indexOf(event.target);
+                items.splice(idx + 1, 0, '');
+
+                Tracker.afterFlush(() => {
+                  const element = this.findAll('input')[idx + 1];
+                  element.focus();
+                  element.value = '';
+                });
+              }
+
+              this.stringtemplateItems.set(items);
+            }
+          }
+        },
+
+        '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);
+            this.find('input.last').value = '';
+          }
+        },
+
+        'click .js-close-inlined-form'(event) {
+          this.stringtemplateItems.set(this.data().value ?? []);
+        },
+      },
+    ];
+  }
+}.register('cardCustomField-stringtemplate'));

+ 3 - 0
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

+ 14 - 0
client/components/cards/minicard.js

@@ -21,6 +21,20 @@ BlazeComponent.extendComponent({
     }).format(customFieldTrueValue);
   },
 
+  formattedStringtemplateCustomFieldValue(definition) {
+    const customField = this.data()
+      .customFieldsWD()
+      .find(f => f._id === definition._id);
+
+    const customFieldTrueValue =
+      customField && customField.trueValue ? customField.trueValue : [];
+
+    return customFieldTrueValue
+      .filter(value => !!value.trim())
+      .map(value => definition.settings.stringtemplateFormat.replace(/%\{value\}/gi, value))
+      .join(definition.settings.stringtemplateSeparator ?? '');
+  },
+
   events() {
     return [
       {

+ 9 - 0
client/components/sidebar/sidebarCustomFields.jade

@@ -50,6 +50,15 @@ 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-stringtemplate(class="{{#if isTypeNotSelected 'stringtemplate'}}hide{{/if}}")
+            label
+                | {{_ 'custom-field-stringtemplate-format'}}
+            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}}")
 

+ 45 - 1
client/components/sidebar/sidebarCustomFields.js

@@ -16,7 +16,15 @@ 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 +85,18 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
         ? this.data().settings.dropdownItems
         : [],
     );
+
+    this.stringtemplateFormat = new ReactiveVar(
+      this.data().settings && this.data().settings.stringtemplateFormat
+        ? this.data().settings.stringtemplateFormat
+        : '',
+    );
+
+    this.stringtemplateSeparator = new ReactiveVar(
+      this.data().settings && this.data().settings.stringtemplateSeparator
+        ? this.data().settings.stringtemplateSeparator
+        : '',
+    );
   },
 
   types() {
@@ -121,6 +141,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
     return items;
   },
 
+  getStringtemplateFormat() {
+    return this.stringtemplateFormat.get();
+  },
+
+  getStringtemplateSeparator() {
+    return this.stringtemplateSeparator.get();
+  },
+
   getSettings() {
     const settings = {};
     switch (this.type.get()) {
@@ -136,6 +164,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
         settings.dropdownItems = dropdownItems;
         break;
       }
+      case 'stringtemplate': {
+        const stringtemplateFormat = this.stringtemplateFormat.get();
+        settings.stringtemplateFormat = stringtemplateFormat;
+
+        const stringtemplateSeparator = this.stringtemplateSeparator.get();
+        settings.stringtemplateSeparator = stringtemplateSeparator;
+        break;
+      }
     }
     return settings;
   },
@@ -158,6 +194,14 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
             evt.target.value = '';
           }
         },
+        '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')) {

+ 5 - 1
i18n/en.i18n.json

@@ -988,5 +988,9 @@
   "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 (use %{value} as placeholder)",
+  "custom-field-stringtemplate-separator": "Separator (use   or   for a space)",
+  "custom-field-stringtemplate-item-placeholder": "Press enter to add more items"
 }

+ 5 - 1
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: {

+ 9 - 0
models/customFields.js

@@ -29,6 +29,7 @@ CustomFields.attachSchema(
         'dropdown',
         'checkbox',
         'currency',
+        'stringtemplate',
       ],
     },
     settings: {
@@ -64,6 +65,14 @@ CustomFields.attachSchema(
         },
       }),
     },
+    'settings.stringtemplateFormat': {
+      type: String,
+      optional: true,
+    },
+    'settings.stringtemplateSeparator': {
+      type: String,
+      optional: true,
+    },
     showOnCard: {
       /**
        * should we show on the cards this custom field