@osimatic/helpers-js 1.1.78 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -179,6 +179,10 @@ class TelephoneNumber {
179
179
  });
180
180
  }
181
181
 
182
+ static getIntlTelInputInstance(input) {
183
+ return window.intlTelInput.getInstance(input[0]);
184
+ }
185
+
182
186
  static getEnteredNumberInInternationalFormat(intlTelInput) {
183
187
  return intlTelInput.getNumber(window.intlTelInput.utils.numberFormat.E164);
184
188
  }
@@ -0,0 +1,186 @@
1
+ <form action="#">
2
+ <div class="form-group field_emails">
3
+ <label>Liste des adresses email :</label>
4
+ <div class="hide list_empty"><em>aucune</em><br></div>
5
+ <table class="table table-sm">
6
+ <tbody>
7
+ <tr class="hide base">
8
+ <td><input type="hidden" name="emails[]"> <span class="value"></span></td>
9
+ <td class="text-end"><a href="#" title="Supprimer" class="remove btn btn-sm btn-danger"><i class="fas fa-times"></i></a></td>
10
+ </tr>
11
+ </tbody>
12
+ </table>
13
+ <div class="links text-center">
14
+ <a href="#" class="add_one btn btn-sm btn-success">Ajouter une adresse email</a>
15
+ </div>
16
+ <div class="item_add_one">
17
+ <div class="alert alert-danger pt-1 pb-1 errors hide"></div>
18
+ <div class="form-inline">
19
+ <input type="text" class="form-control" value="" placeholder="Adresse email" size="40" /> &nbsp;
20
+ <a href="#" title="Ajouter" class="add btn btn-success"><i class="fas fa-plus"></i></a>
21
+ &nbsp; <a href="#" class="cancel">Annuler</a>
22
+ </div>
23
+ </div>
24
+ <div class="item_add_mutli">
25
+ <div class="alert alert-danger pt-1 pb-1 errors hide"></div>
26
+ <div class="form-group">
27
+ <label>Liste des adresses email :</label>
28
+ <textarea name="phone_numbers" class="form-control" rows="10"></textarea>
29
+ <span class="form-text">Une adresse email par ligne.</span>
30
+ </div>
31
+ <div class="form-inline">
32
+ <a href="#" title="Ajouter" class="add btn btn-success"><i class="fas fa-plus"></i></a>
33
+ &nbsp; <a href="#" class="cancel">Annuler</a>
34
+ </div>
35
+ </div>
36
+ </div>
37
+
38
+ <div class="form-group field_phone_numbers">
39
+ <label>Liste des numéros de téléphone :</label>
40
+ <div class="hide list_empty"><em>aucun</em><br></div>
41
+ <table class="table table-sm">
42
+ <tbody>
43
+ <tr class="hide base">
44
+ <td><input type="hidden" name="phone_numbers[]"> <span class="value"></span></td>
45
+ <td class="text-end"><a href="#" title="Supprimer" class="remove btn btn-sm btn-danger"><i class="fas fa-times"></i></a></td>
46
+ </tr>
47
+ </tbody>
48
+ </table>
49
+ <div class="links text-center">
50
+ <a href="#" class="add_one btn btn-sm btn-success">Ajouter un numéro de téléphone</a>
51
+ </div>
52
+ <div class="item_add_one">
53
+ <div class="alert alert-danger pt-1 pb-1 errors hide"></div>
54
+ <div class="form-inline">
55
+ <input type="text" class="form-control" value="" placeholder="Numéro de téléphone" size="40" /> &nbsp;
56
+ <a href="#" title="Ajouter" class="add btn btn-success"><i class="fas fa-plus"></i></a>
57
+ &nbsp; <a href="#" class="cancel">Annuler</a>
58
+ </div>
59
+ </div>
60
+ <div class="item_add_mutli">
61
+ <div class="alert alert-danger pt-1 pb-1 errors hide"></div>
62
+ <div class="form-group">
63
+ <label>Pays des numéros de téléphone :</label>
64
+ <select name="country" class="form-control"></select>
65
+ </div>
66
+ <div class="form-group">
67
+ <label>Liste des numéros de téléphone :</label>
68
+ <textarea name="phone_numbers" class="form-control" rows="10"></textarea>
69
+ <span class="form-text">Un numéro de téléphone par ligne.</span>
70
+ </div>
71
+ <div class="form-inline">
72
+ <a href="#" title="Ajouter" class="add btn btn-success"><i class="fas fa-plus"></i></a>
73
+ &nbsp; <a href="#" class="cancel">Annuler</a>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </form>
78
+
79
+ <script>
80
+ const form = $('form');
81
+ ArrayField.init(form.find('.field_emails'), ['email@company.com'], {
82
+ entering_field_in_table: false,
83
+ item_name: 'Adresse email',
84
+ get_errors_callback: (email, itemsList) => {
85
+ if (Array.isArray(email) && email.length > 1) {
86
+ const emails = email;
87
+ if (emails.length === 0) {
88
+ return 'Veuillez saisir au moins une adresse email.';
89
+ }
90
+
91
+ let errors = [];
92
+ for (let i = 0; i < emails.length; i++) {
93
+ if (false === Email.validateEmail(emails[i])) {
94
+ errors.push(('L’adresse email n°{0} est incorrecte.').format(i + 1));
95
+ continue;
96
+ }
97
+
98
+ if (itemsList.indexOf(emails[i]) !== -1) {
99
+ errors.push(('L’adresse email n°{0} a déjà été ajoutée.').format(i + 1));
100
+ continue;
101
+ }
102
+ }
103
+
104
+ return errors.length ? errors : null;
105
+ }
106
+
107
+ email = Array.isArray(email) ? email[0] : email;
108
+
109
+ if (null == email || '' === email) {
110
+ return 'Veuillez saisir une adresse email.';
111
+ }
112
+ if (false === Email.validateEmail(email)) {
113
+ return 'Cette adresse email est incorrecte.';
114
+ }
115
+ if (itemsList.indexOf(email) !== -1) {
116
+ return 'Cette adresse email a déjà été ajoutée.';
117
+ }
118
+ return null;
119
+ },
120
+ });
121
+
122
+ ArrayField.init(form.find('field_phone_numbers'), ['+33601020304'], {
123
+ entering_field_in_table: false,
124
+ item_name: 'Numéro de téléphone',
125
+ init_callback: (div) => {
126
+ let itiPhoneNumberField = TelephoneNumber.setIntlTelInput(div.find('.item_add_one input[type="text"]'));
127
+ Location.fillCountrySelect(div.find('select[name="country"]'), 'FR');
128
+ },
129
+ format_entered_value_callback: (value, addDiv) => {
130
+ if (addDiv.hasClass('item_add_one')) {
131
+ const itiPhoneNumberField = TelephoneNumber.getIntlTelInputInstance(addDiv.find('input[type="text"]'));
132
+ return TelephoneNumber.getEnteredNumberInInternationalFormat(itiPhoneNumberField);
133
+ }
134
+
135
+ let country = addDiv.find('select[name="country"]').val();
136
+ if (false !== TelephoneNumber.check(value, country)) {
137
+ return TelephoneNumber.parse(value, country);
138
+ }
139
+ return value;
140
+ },
141
+ get_errors_callback: (phoneNumber, itemsList, addDiv) => {
142
+ if (Array.isArray(phoneNumber) && phoneNumber.length > 1) {
143
+ const phoneNumbers = phoneNumber;
144
+
145
+ if (phoneNumbers.length === 0) {
146
+ return 'Veuillez saisir au moins un numéro de téléphone.';
147
+ }
148
+
149
+ let country = addDiv.find('select[name="country"]').val();
150
+
151
+ let errors = [];
152
+ for (let i = 0; i < phoneNumbers.length; i++) {
153
+ if (false === TelephoneNumber.check(phoneNumbers[i], country)) {
154
+ errors.push(('Le numéro de téléphone n°{0} est incorrect.').format(i + 1));
155
+ continue;
156
+ }
157
+
158
+ let phoneNumber = TelephoneNumber.parse(phoneNumbers[i], country);
159
+ if (null === phoneNumber) {
160
+ continue;
161
+ }
162
+
163
+ if (itemsList.indexOf(phoneNumber) !== -1) {
164
+ errors.push(('Le numéro de téléphone n°{0} a déjà été ajouté.').format(i + 1));
165
+ continue;
166
+ }
167
+ }
168
+
169
+ return errors.length ? errors : null;
170
+ }
171
+
172
+ phoneNumber = Array.isArray(phoneNumber) ? phoneNumber[0] : phoneNumber;
173
+
174
+ const itiPhoneNumberField = TelephoneNumber.getIntlTelInputInstance(addDiv.find('input[type="text"]'));
175
+ if (null == phoneNumber || '' === phoneNumber) {
176
+ return 'Veuillez saisir un numéro de téléphone.';
177
+ }
178
+ if (!TelephoneNumber.check(phoneNumber, itiPhoneNumberField.defaultCountry)) {
179
+ return 'Ce numéro de téléphone est incorrect.';
180
+ }
181
+ if (itemsList.indexOf(phoneNumber) !== -1) {
182
+ return 'Ce numéro de téléphone a déjà été ajouté.';
183
+ }
184
+ },
185
+ });
186
+ </script>
package/form_helper.js CHANGED
@@ -363,6 +363,286 @@ class FormHelper {
363
363
 
364
364
  }
365
365
 
366
+ class ArrayField {
367
+ static init(formGroupDiv, defaultValues=[], options = {
368
+ entering_field_in_table: true,
369
+ item_name: null,
370
+ nb_min_lines: 5,
371
+ nb_max_lines: null,
372
+ list_empty_text: null,
373
+ add_one_button_enabled: true,
374
+ add_one_button_label: null,
375
+ add_multi_button_enabled: false,
376
+ add_multi_button_label: null,
377
+ input_name: null,
378
+ init_callback: null,
379
+ update_list_callback: null,
380
+ get_errors_callback: null,
381
+
382
+ }) {
383
+ function isOptionDefined(optionName) {
384
+ return typeof options[optionName] != 'undefined' && null !== options[optionName];
385
+ }
386
+
387
+ let itemsList = defaultValues;
388
+
389
+ if (!formGroupDiv.find('table').length) {
390
+ formGroupDiv.append($('<table class="table table-sm"><tbody></tbody></table>'));
391
+ }
392
+ if (!formGroupDiv.find('.list_empty').length) {
393
+ formGroupDiv.append($('<div class="list_empty">'+(isOptionDefined('list_empty_text') ? options['list_empty_text'] : '<em>aucun</em>')+'</div>'));
394
+ }
395
+ if (!options['entering_field_in_table'] && !formGroupDiv.find('.add_one, .add_multi').length) {
396
+ let divLinks = formGroupDiv.find('.links');
397
+ if (!divLinks.length) {
398
+ divLinks = $('<div class="links text-center"></div>');
399
+ formGroupDiv.append(divLinks);
400
+ }
401
+
402
+ if (options['add_one_button_enabled']) {
403
+ divLinks.append($('<a href="#" class="add_one btn btn-sm btn-success">'+(isOptionDefined('add_one_button_label') ? options['add_one_button_label'] : 'Ajouter')+'</a>'));
404
+ }
405
+ if (options['add_multi_button_enabled']) {
406
+ divLinks.append($('<a href="#" class="add_multi btn btn-sm btn-success">'+(isOptionDefined('add_multi_button_label') ? options['add_multi_button_label'] : 'Ajout multiple')+'</a>'));
407
+ }
408
+ formGroupDiv.append(divLinks);
409
+ }
410
+
411
+ function addLine(item) {
412
+ const table = formGroupDiv.find('table').removeClass('hide');
413
+ let tr;
414
+ if (table.find('tbody tr.base').length) {
415
+ tr = table.find('tbody tr.base').clone().removeClass('hide').removeClass('base');
416
+ }
417
+ else {
418
+ let links = '';
419
+ if (options['entering_field_in_table']) {
420
+ links += '<a href="#" title="Ajouter" class="add btn btn-sm btn-success ms-1"><i class="fas fa-plus"></i></a>';
421
+ }
422
+ links += '<a href="#" title="Supprimer" class="remove btn btn-sm btn-danger ms-1"><i class="fas fa-times"></i></a>';
423
+
424
+ tr = $(
425
+ '<tr>' +
426
+ '<td class="table-input" style="vertical-align: middle">' +
427
+ (options['entering_field_in_table'] ? '<input type="text" name="'+options['input_name']+'" class="form-control pt-1 pb-1">' : '<input type="hidden" name="'+options['input_name']+'"> <span class="value"></span>') +
428
+ '</td>' +
429
+ '<td class="table-links text-end" style="vertical-align: middle">'+links+'</td>' +
430
+ '</tr>'
431
+ );
432
+ }
433
+
434
+ tr.find('a.add').click(function () {
435
+ const tr = $(this).closest('tr');
436
+ const tableBody = tr.closest('tbody');
437
+
438
+ if (isOptionDefined('nb_max_lines') && tableBody.find('tr').length >= options['nb_max_lines']) {
439
+ return false;
440
+ }
441
+ addLine();
442
+ onUpdateList();
443
+ return false;
444
+ });
445
+ tr.find('a.remove').click(function () {
446
+ const tr = $(this).closest('tr');
447
+ const tableBody = tr.closest('tbody');
448
+ if (options['entering_field_in_table'] && tableBody.find('tr').length <= 1) {
449
+ return false;
450
+ }
451
+
452
+ if (!options['entering_field_in_table'] || '' !== tr.data('item')) {
453
+ itemsList.unsetVal(tr.data('item'));
454
+ }
455
+
456
+ tr.remove();
457
+ onUpdateList();
458
+ return false;
459
+ });
460
+
461
+ if (typeof item != 'undefined' && null !== item) {
462
+ tr.data('item', item);
463
+ tr.find('input').val(item);
464
+ tr.find('span.value').text(item);
465
+ }
466
+
467
+ table.find('tbody').append(tr);
468
+ return tr;
469
+ }
470
+
471
+ function onUpdateList() {
472
+ formGroupDiv.find('.list_empty, table').addClass('hide');
473
+
474
+ // Maj tableau
475
+ let table = formGroupDiv.find('table');
476
+ const tableLines = table.find('tbody tr:not(.base)');
477
+ if ((options['entering_field_in_table'] && tableLines.length ) || (!options['entering_field_in_table'] && itemsList.length)) {
478
+ table.removeClass('hide');
479
+ }
480
+ else {
481
+ formGroupDiv.find('.list_empty').removeClass('hide');
482
+ }
483
+
484
+ tableLines.find('a').prop('disabled', false).removeClass('disabled');
485
+ if (isOptionDefined('nb_max_lines') && tableLines.length >= options['nb_max_lines']) {
486
+ tableLines.find('a.add').prop('disabled', true).addClass('disabled');
487
+ }
488
+ if (options['entering_field_in_table'] && tableLines.length <= 1) {
489
+ tableLines.find('a.remove').prop('disabled', true).addClass('disabled');
490
+ }
491
+
492
+ if (typeof options['update_list_callback'] == 'function') {
493
+ options['update_list_callback'](itemsList);
494
+ }
495
+
496
+ cancelAdd();
497
+ }
498
+
499
+ function startAdd() {
500
+ formGroupDiv.find('a.add_one, a.add_multi').addClass('hide').closest('.links').addClass('hide');
501
+ formGroupDiv.find('.item_add_one, .item_add_multi').addClass('hide');
502
+ formGroupDiv.find('.item_add_one, .item_add_multi').find('input[type="text"], textarea').val('');
503
+ formGroupDiv.find('.item_add_one, .item_add_multi').find('.errors').addClass('hide');
504
+ }
505
+ function cancelAdd() {
506
+ formGroupDiv.find('a.add_one, a.add_multi').removeClass('hide').closest('.links').removeClass('hide');
507
+ formGroupDiv.find('.item_add_one, .item_add_multi').addClass('hide');
508
+ }
509
+
510
+ function addItemsInList(items) {
511
+ if (!Array.isArray(items)) {
512
+ items = [items];
513
+ }
514
+
515
+ for (let i = 0; i < items.length; i++) {
516
+ if (itemsList.indexOf(items[i]) === -1) {
517
+ itemsList.push(items[i]);
518
+ addLine(items[i]);
519
+ }
520
+ }
521
+
522
+ onUpdateList();
523
+ }
524
+
525
+ function submitAddNewItem(item, divAdd) {
526
+ let items = Array.isArray(item) ? item : [item];
527
+
528
+ if (typeof options['format_entered_value_callback'] == 'function') {
529
+ items = items.map(item => options['format_entered_value_callback'](item, divAdd));
530
+ }
531
+
532
+ if (typeof options['get_errors_callback'] == 'function') {
533
+ const errors = options['get_errors_callback'](items, itemsList, divAdd);
534
+ if (null !== errors && errors.length) {
535
+ displayErrors(divAdd, errors);
536
+ return;
537
+ }
538
+ }
539
+
540
+ addItemsInList(items);
541
+ }
542
+
543
+ function displayErrors(divAdd, errors) {
544
+ if (!Array.isArray(errors)) {
545
+ errors = [errors];
546
+ }
547
+ divAdd.find('.errors').text(errors.join('<br/>')).removeClass('hide');
548
+ }
549
+
550
+ function initLinkAddOne() {
551
+ if (!formGroupDiv.find('a.add_one').length || !formGroupDiv.find('a.add_one:not([disabled])').length) {
552
+ return;
553
+ }
554
+
555
+ let divAdd = formGroupDiv.find('.item_add_one');
556
+ if (!divAdd.length) {
557
+ divAdd = $(
558
+ '<div class="item_add_one">' +
559
+ '<div class="alert alert-danger pt-1 pb-1 errors hide"></div>' +
560
+ '<div class="form-inline">' +
561
+ '<input type="text" class="form-control" placeholder="'+options['item_name']+'" value="" /> &nbsp;' +
562
+ '<a href="#" title="Ajouter" class="add btn btn-success"><i class="fas fa-plus"></i></a> &nbsp;' +
563
+ '<a href="#" class="cancel">Annuler</a>' +
564
+ '</div>' +
565
+ (isOptionDefined('form_desc')?'<br><span class="form-text">'+options['form_desc']+'</span>':'') +
566
+ '</div>'
567
+ );
568
+ formGroupDiv.append(divAdd);
569
+ }
570
+
571
+ divAdd.find('a.cancel').off('click').click(function () {
572
+ cancelAdd();
573
+ return false;
574
+ });
575
+ divAdd.find('a.add').off('click').click(function () {
576
+ submitAddNewItem(divAdd.find('input[type="text"]').val(), divAdd);
577
+ return false;
578
+ });
579
+
580
+ formGroupDiv.find('a.add_one').off('click').click(function () {
581
+ startAdd();
582
+ formGroupDiv.find('.item_add_one').removeClass('hide');
583
+ return false;
584
+ });
585
+ }
586
+
587
+ function initLinkAddMulti() {
588
+ if (!formGroupDiv.find('a.add_multi').length || !formGroupDiv.find('a.add_multi:not([disabled])').length) {
589
+ return;
590
+ }
591
+
592
+ let divAdd = formGroupDiv.find('.item_add_multi');
593
+ if (!divAdd.length) {
594
+ divAdd = $(
595
+ '<div class="item_add_multi">' +
596
+ '<div class="alert alert-danger pt-1 pb-1 errors hide"></div>' +
597
+ '<div class="form-group">' +
598
+ '<label>Liste à ajouter :</label>' +
599
+ '<textarea name="emails" class="form-control" rows="10"></textarea>' +
600
+ '<span class="form-text">Un élément par ligne.</span>' +
601
+ '</div>' +
602
+ '<div class="form-inline">' +
603
+ '<a href="#" title="Ajouter" class="add btn btn-success"><i class="fas fa-plus"></i></a> &nbsp;' +
604
+ '<a href="#" class="cancel">Annuler</a>' +
605
+ '</div>' +
606
+ '</div>'
607
+ );
608
+ formGroupDiv.append(divAdd);
609
+ }
610
+
611
+ divAdd.find('a.cancel').off('click').click(function () {
612
+ cancelAdd();
613
+ return false;
614
+ });
615
+ divAdd.find('a.add').off('click').click(function () {
616
+ const items = divAdd.find('textarea').val().normalizeBreaks("\n").split(/\n/ms).filter(value => value.length > 0);
617
+ submitAddNewItem(items, divAdd);
618
+ return false;
619
+ });
620
+
621
+ formGroupDiv.find('a.add_multi').off('click').click(function () {
622
+ startAdd();
623
+ formGroupDiv.find('.item_add_multi').removeClass('hide');
624
+ return false;
625
+ });
626
+ }
627
+
628
+ initLinkAddOne();
629
+ initLinkAddMulti();
630
+
631
+ itemsList.forEach(item => addLine(item));
632
+ if (options['entering_field_in_table']) {
633
+ for (let i=itemsList.length; i <= options['nb_min_lines']; i++) {
634
+ addLine();
635
+ }
636
+ }
637
+
638
+ onUpdateList();
639
+
640
+ if (typeof options['init_callback'] == 'function') {
641
+ options['init_callback'](formGroupDiv, onUpdateList, addItemsInList);
642
+ }
643
+ }
644
+ }
645
+
366
646
  class EditValue {
367
647
  static init(valueDiv, onSubmitCallback, getInputCallback) {
368
648
  let link = $('<a href="#" class="text-warning"><i class="fas fa-pencil-alt"></i></a>');
@@ -408,4 +688,4 @@ class EditValue {
408
688
  }
409
689
  }
410
690
 
411
- module.exports = { FormHelper, EditValue };
691
+ module.exports = { FormHelper, ArrayField, EditValue };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osimatic/helpers-js",
3
- "version": "1.1.78",
3
+ "version": "1.2.1",
4
4
  "main": "main.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"