cardCustomFields.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import moment from 'moment/min/moment-with-locales';
  2. import { TAPi18n } from '/imports/i18n';
  3. import { DatePicker } from '/client/lib/datepicker';
  4. import Cards from '/models/cards';
  5. import { CustomFieldStringTemplate } from '/client/lib/customFields'
  6. Template.cardCustomFieldsPopup.helpers({
  7. hasCustomField() {
  8. const card = Utils.getCurrentCard();
  9. const customFieldId = this._id;
  10. return card.customFieldIndex(customFieldId) > -1;
  11. },
  12. });
  13. Template.cardCustomFieldsPopup.events({
  14. 'click .js-select-field'(event) {
  15. const card = Utils.getCurrentCard();
  16. const customFieldId = this._id;
  17. card.toggleCustomField(customFieldId);
  18. event.preventDefault();
  19. },
  20. 'click .js-settings'(event) {
  21. EscapeActions.executeUpTo('detailsPane');
  22. Sidebar.setView('customFields');
  23. event.preventDefault();
  24. },
  25. });
  26. // cardCustomField
  27. const CardCustomField = BlazeComponent.extendComponent({
  28. getTemplate() {
  29. return `cardCustomField-${this.data().definition.type}`;
  30. },
  31. onCreated() {
  32. const self = this;
  33. self.card = Utils.getCurrentCard();
  34. self.customFieldId = this.data()._id;
  35. },
  36. });
  37. CardCustomField.register('cardCustomField');
  38. // cardCustomField-text
  39. (class extends CardCustomField {
  40. onCreated() {
  41. super.onCreated();
  42. }
  43. events() {
  44. return [
  45. {
  46. 'submit .js-card-customfield-text'(event) {
  47. event.preventDefault();
  48. const value = this.currentComponent().getValue();
  49. this.card.setCustomField(this.customFieldId, value);
  50. },
  51. },
  52. ];
  53. }
  54. }.register('cardCustomField-text'));
  55. // cardCustomField-number
  56. (class extends CardCustomField {
  57. onCreated() {
  58. super.onCreated();
  59. }
  60. events() {
  61. return [
  62. {
  63. 'submit .js-card-customfield-number'(event) {
  64. event.preventDefault();
  65. const value = parseInt(this.find('input').value, 10);
  66. this.card.setCustomField(this.customFieldId, value);
  67. },
  68. },
  69. ];
  70. }
  71. }.register('cardCustomField-number'));
  72. // cardCustomField-checkbox
  73. (class extends CardCustomField {
  74. onCreated() {
  75. super.onCreated();
  76. }
  77. toggleItem() {
  78. this.card.setCustomField(this.customFieldId, !this.data().value);
  79. }
  80. events() {
  81. return [
  82. {
  83. 'click .js-checklist-item .check-box-container': this.toggleItem,
  84. },
  85. ];
  86. }
  87. }.register('cardCustomField-checkbox'));
  88. // cardCustomField-currency
  89. (class extends CardCustomField {
  90. onCreated() {
  91. super.onCreated();
  92. this.currencyCode = this.data().definition.settings.currencyCode;
  93. }
  94. formattedValue() {
  95. const locale = TAPi18n.getLanguage();
  96. return new Intl.NumberFormat(locale, {
  97. style: 'currency',
  98. currency: this.currencyCode,
  99. }).format(this.data().value);
  100. }
  101. events() {
  102. return [
  103. {
  104. 'submit .js-card-customfield-currency'(event) {
  105. event.preventDefault();
  106. // To allow input separated by comma, the comma is replaced by a period.
  107. const value = Number(this.find('input').value.replace(/,/i, '.'), 10);
  108. this.card.setCustomField(this.customFieldId, value);
  109. },
  110. },
  111. ];
  112. }
  113. }.register('cardCustomField-currency'));
  114. // cardCustomField-date
  115. (class extends CardCustomField {
  116. onCreated() {
  117. super.onCreated();
  118. const self = this;
  119. self.date = ReactiveVar();
  120. self.now = ReactiveVar(moment());
  121. window.setInterval(() => {
  122. self.now.set(moment());
  123. }, 60000);
  124. self.autorun(() => {
  125. self.date.set(moment(self.data().value));
  126. });
  127. }
  128. showWeek() {
  129. return this.date.get().week().toString();
  130. }
  131. showDate() {
  132. // this will start working once mquandalle:moment
  133. // is updated to at least moment.js 2.10.5
  134. // until then, the date is displayed in the "L" format
  135. return this.date.get().calendar(null, {
  136. sameElse: 'llll',
  137. });
  138. }
  139. showISODate() {
  140. return this.date.get().toISOString();
  141. }
  142. classes() {
  143. if (
  144. this.date.get().isBefore(this.now.get(), 'minute') &&
  145. this.now.get().isBefore(this.data().value)
  146. ) {
  147. return 'current';
  148. }
  149. return '';
  150. }
  151. showTitle() {
  152. return `${TAPi18n.__('card-start-on')} ${this.date.get().format('LLLL')}`;
  153. }
  154. events() {
  155. return [
  156. {
  157. 'click .js-edit-date': Popup.open('cardCustomField-date'),
  158. },
  159. ];
  160. }
  161. }.register('cardCustomField-date'));
  162. // cardCustomField-datePopup
  163. (class extends DatePicker {
  164. onCreated() {
  165. super.onCreated();
  166. const self = this;
  167. self.card = Utils.getCurrentCard();
  168. self.customFieldId = this.data()._id;
  169. this.data().value && this.date.set(moment(this.data().value));
  170. }
  171. _storeDate(date) {
  172. this.card.setCustomField(this.customFieldId, date);
  173. }
  174. _deleteDate() {
  175. this.card.setCustomField(this.customFieldId, '');
  176. }
  177. }.register('cardCustomField-datePopup'));
  178. // cardCustomField-dropdown
  179. (class extends CardCustomField {
  180. onCreated() {
  181. super.onCreated();
  182. this._items = this.data().definition.settings.dropdownItems;
  183. this.items = this._items.slice(0);
  184. this.items.unshift({
  185. _id: '',
  186. name: TAPi18n.__('custom-field-dropdown-none'),
  187. });
  188. }
  189. selectedItem() {
  190. const selected = this._items.find(item => {
  191. return item._id === this.data().value;
  192. });
  193. return selected
  194. ? selected.name
  195. : TAPi18n.__('custom-field-dropdown-unknown');
  196. }
  197. events() {
  198. return [
  199. {
  200. 'submit .js-card-customfield-dropdown'(event) {
  201. event.preventDefault();
  202. const value = this.find('select').value;
  203. this.card.setCustomField(this.customFieldId, value);
  204. },
  205. },
  206. ];
  207. }
  208. }.register('cardCustomField-dropdown'));
  209. // cardCustomField-stringtemplate
  210. class CardCustomFieldStringTemplate extends CardCustomField {
  211. onCreated() {
  212. super.onCreated();
  213. this.customField = new CustomFieldStringTemplate(this.data().definition);
  214. this.stringtemplateItems = new ReactiveVar(this.data().value ?? []);
  215. }
  216. formattedValue() {
  217. const ret = this.customField.getFormattedValue(this.data().value);
  218. return ret;
  219. }
  220. getItems() {
  221. return Array.from(this.findAll('input'))
  222. .map(input => input.value)
  223. .filter(value => !!value.trim());
  224. }
  225. events() {
  226. return [
  227. {
  228. 'submit .js-card-customfield-stringtemplate'(event) {
  229. event.preventDefault();
  230. const items = this.stringtemplateItems.get();
  231. this.card.setCustomField(this.customFieldId, items);
  232. },
  233. 'keydown .js-card-customfield-stringtemplate-item'(event) {
  234. if (event.keyCode === 13) {
  235. event.preventDefault();
  236. if (event.target.value.trim() || event.metaKey || event.ctrlKey) {
  237. const inputLast = this.find('input.last');
  238. let items = this.getItems();
  239. if (event.target === inputLast) {
  240. inputLast.value = '';
  241. } else if (event.target.nextSibling === inputLast) {
  242. inputLast.focus();
  243. } else {
  244. event.target.blur();
  245. const idx = Array.from(this.findAll('input')).indexOf(
  246. event.target,
  247. );
  248. items.splice(idx + 1, 0, '');
  249. Tracker.afterFlush(() => {
  250. const element = this.findAll('input')[idx + 1];
  251. element.focus();
  252. element.value = '';
  253. });
  254. }
  255. this.stringtemplateItems.set(items);
  256. }
  257. if (event.metaKey || event.ctrlKey) {
  258. this.find('button[type=submit]').click();
  259. }
  260. }
  261. },
  262. 'blur .js-card-customfield-stringtemplate-item'(event) {
  263. if (
  264. !event.target.value.trim() ||
  265. event.target === this.find('input.last')
  266. ) {
  267. const items = this.getItems();
  268. this.stringtemplateItems.set(items);
  269. this.find('input.last').value = '';
  270. }
  271. },
  272. 'click .js-close-inlined-form'(event) {
  273. this.stringtemplateItems.set(this.data().value ?? []);
  274. },
  275. },
  276. ];
  277. }
  278. }
  279. CardCustomFieldStringTemplate.register('cardCustomField-stringtemplate');