2
0

cardCustomFields.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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. showWeekOfYear() {
  132. return ReactiveCache.getCurrentUser().isShowWeekOfYear();
  133. }
  134. showDate() {
  135. // this will start working once mquandalle:moment
  136. // is updated to at least moment.js 2.10.5
  137. // until then, the date is displayed in the "L" format
  138. return this.date.get().calendar(null, {
  139. sameElse: 'llll',
  140. });
  141. }
  142. showISODate() {
  143. return this.date.get().toISOString();
  144. }
  145. classes() {
  146. if (
  147. this.date.get().isBefore(this.now.get(), 'minute') &&
  148. this.now.get().isBefore(this.data().value)
  149. ) {
  150. return 'current';
  151. }
  152. return '';
  153. }
  154. showTitle() {
  155. return `${TAPi18n.__('card-start-on')} ${this.date.get().format('LLLL')}`;
  156. }
  157. events() {
  158. return [
  159. {
  160. 'click .js-edit-date': Popup.open('cardCustomField-date'),
  161. },
  162. ];
  163. }
  164. }.register('cardCustomField-date'));
  165. // cardCustomField-datePopup
  166. (class extends DatePicker {
  167. onCreated() {
  168. super.onCreated();
  169. const self = this;
  170. self.card = Utils.getCurrentCard();
  171. self.customFieldId = this.data()._id;
  172. this.data().value && this.date.set(moment(this.data().value));
  173. }
  174. _storeDate(date) {
  175. this.card.setCustomField(this.customFieldId, date);
  176. }
  177. _deleteDate() {
  178. this.card.setCustomField(this.customFieldId, '');
  179. }
  180. }.register('cardCustomField-datePopup'));
  181. // cardCustomField-dropdown
  182. (class extends CardCustomField {
  183. onCreated() {
  184. super.onCreated();
  185. this._items = this.data().definition.settings.dropdownItems;
  186. this.items = this._items.slice(0);
  187. this.items.unshift({
  188. _id: '',
  189. name: TAPi18n.__('custom-field-dropdown-none'),
  190. });
  191. }
  192. selectedItem() {
  193. const selected = this._items.find(item => {
  194. return item._id === this.data().value;
  195. });
  196. return selected
  197. ? selected.name
  198. : TAPi18n.__('custom-field-dropdown-unknown');
  199. }
  200. events() {
  201. return [
  202. {
  203. 'submit .js-card-customfield-dropdown'(event) {
  204. event.preventDefault();
  205. const value = this.find('select').value;
  206. this.card.setCustomField(this.customFieldId, value);
  207. },
  208. },
  209. ];
  210. }
  211. }.register('cardCustomField-dropdown'));
  212. // cardCustomField-stringtemplate
  213. class CardCustomFieldStringTemplate extends CardCustomField {
  214. onCreated() {
  215. super.onCreated();
  216. this.customField = new CustomFieldStringTemplate(this.data().definition);
  217. this.stringtemplateItems = new ReactiveVar(this.data().value ?? []);
  218. }
  219. formattedValue() {
  220. const ret = this.customField.getFormattedValue(this.data().value);
  221. return ret;
  222. }
  223. getItems() {
  224. return Array.from(this.findAll('input'))
  225. .map(input => input.value)
  226. .filter(value => !!value.trim());
  227. }
  228. events() {
  229. return [
  230. {
  231. 'submit .js-card-customfield-stringtemplate'(event) {
  232. event.preventDefault();
  233. const items = this.stringtemplateItems.get();
  234. this.card.setCustomField(this.customFieldId, items);
  235. },
  236. 'keydown .js-card-customfield-stringtemplate-item'(event) {
  237. if (event.keyCode === 13) {
  238. event.preventDefault();
  239. if (event.target.value.trim() || event.metaKey || event.ctrlKey) {
  240. const inputLast = this.find('input.last');
  241. let items = this.getItems();
  242. if (event.target === inputLast) {
  243. inputLast.value = '';
  244. } else if (event.target.nextSibling === inputLast) {
  245. inputLast.focus();
  246. } else {
  247. event.target.blur();
  248. const idx = Array.from(this.findAll('input')).indexOf(
  249. event.target,
  250. );
  251. items.splice(idx + 1, 0, '');
  252. Tracker.afterFlush(() => {
  253. const element = this.findAll('input')[idx + 1];
  254. element.focus();
  255. element.value = '';
  256. });
  257. }
  258. this.stringtemplateItems.set(items);
  259. }
  260. if (event.metaKey || event.ctrlKey) {
  261. this.find('button[type=submit]').click();
  262. }
  263. }
  264. },
  265. 'blur .js-card-customfield-stringtemplate-item'(event) {
  266. if (
  267. !event.target.value.trim() ||
  268. event.target === this.find('input.last')
  269. ) {
  270. const items = this.getItems();
  271. this.stringtemplateItems.set(items);
  272. this.find('input.last').value = '';
  273. }
  274. },
  275. 'click .js-close-inlined-form'(event) {
  276. this.stringtemplateItems.set(this.data().value ?? []);
  277. },
  278. },
  279. ];
  280. }
  281. }
  282. CardCustomFieldStringTemplate.register('cardCustomField-stringtemplate');