aw-front-helpers 2.3.10 → 2.3.12

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.
Files changed (2) hide show
  1. package/classes/formControl.js +236 -139
  2. package/package.json +1 -1
@@ -1,17 +1,13 @@
1
- import { TweenMax, gsap } from 'gsap/all';
2
- import ScrollToPlugin from 'gsap/ScrollToPlugin';
3
- gsap.registerPlugin( ScrollToPlugin );
4
-
5
- import create from 'dom-create-element';
6
-
7
- import getClosest from '../utils/dom/getClosest';
8
- import regex_email from '../utils/regex/email';
1
+ import { gsap } from 'gsap/all';
2
+ import ScrollToPlugin from 'gsap/ScrollToPlugin';
3
+ gsap.registerPlugin(ScrollToPlugin);
4
+ import regex_email from '../utils/regex/email';
9
5
 
10
6
  const defaultOptions = {
11
7
  selectorGroup: '.form-group',
12
8
  classErrorElement: 'form-error',
13
9
  classIsError: 'is-error',
14
- animateToFirstError: true,
10
+ animateToFirstError: false,
15
11
  i18n: {
16
12
  required: Translator ? Translator.trans('form_errors.pattern') : 'Ce champs est obligatoire',
17
13
  same: Translator ? Translator.trans('form_errors.same') : 'Les champs ne correspondent pas.',
@@ -23,17 +19,39 @@ const defaultOptions = {
23
19
  }
24
20
  };
25
21
 
26
- class FormControl {
22
+ export default class FormControl {
27
23
  //________________________________________________________ constructor
28
24
  // -
29
- constructor(el, options = {}) {
30
- this._el = el;
25
+ /**
26
+ * Add controls on your formulaire
27
+ * @param {Element} form formulaire need add control
28
+ * @param {Object} options define options of controls
29
+ * @param {string} options.selectorGroup - The name of the employee.
30
+ * @param {string} options.classErrorElement - The employee's department.
31
+ * @param {string} options.classIsError - The employee's department.
32
+ * @param {string} options.animateToFirstError - The employee's department.
33
+ * @param {Object} options.i18n - The employee's department.
34
+ * @param {Object} options.i18n.required - The employee's department.
35
+ * @param {Object} options.i18n.same - The employee's department.
36
+ * @param {Object} options.i18n.email - The employee's department.
37
+ * @param {Object} options.i18n.empty - The employee's department.
38
+ * @param {Object} options.i18n.checkbox - The employee's department.
39
+ * @param {Object} options.i18n.radio - The employee's department.
40
+ * @param {Object} options.i18n.pattern - The employee's department.
41
+ **/
42
+ constructor(form, options = {}) {
43
+ /** @private */
44
+ this._form = form;
45
+ /** @private */
31
46
  this._dom = {};
32
- this._options = {...defaultOptions, ...options};
47
+ /** @private */
48
+ this._options = {
49
+ ...defaultOptions,
50
+ ...options
51
+ };
33
52
 
53
+ this._form.setAttribute('novalidate', 'novalidate');
34
54
  this._formCreateBound();
35
- this._el.setAttribute('novalidate', 'novalidate');
36
-
37
55
  this._formGrabDom();
38
56
  this._formAddEvents();
39
57
  }
@@ -45,168 +63,247 @@ class FormControl {
45
63
  };
46
64
  }
47
65
 
66
+ get form() {
67
+ return this._form;
68
+ }
69
+
48
70
  get options() {
49
71
  return this._options;
50
72
  }
51
73
 
74
+ /**
75
+ * @deprecated use isValid
76
+ */
52
77
  get isValidate() {
53
- return this._formValidate();
78
+ return this._isValid();
79
+ }
80
+
81
+ get isValid() {
82
+ return this._isValid();
54
83
  }
55
84
 
56
85
  //________________________________________________________ public
57
86
  // -
58
87
  destroy() {
59
- this._el.removeEventListener('submit', this._onFormSubmit);
60
- this._domForm.inputs.forEach( input => {
61
- input.removeEventListener('keyup', this._onFormKeyUp);
62
- if (input.getAttribute('type') === 'checkbox' ||
63
- input.getAttribute('type') === 'radio' ||
64
- input.tagName === 'SELECT') {
65
- input.removeEventListener('change', this._onFormKeyUp);
66
- }
67
- })
68
- }
69
-
70
- removeErrors() {
71
- const isErrors = [...this._el.querySelectorAll(`${this._options.classIsError}`)];
72
- const currentErrors = [...this._el.querySelectorAll(`.${this._options.classErrorElement}`)];
73
- currentErrors.forEach(error => {
74
- error.parentNode.removeChild(error);
88
+ this._form.removeEventListener('submit', this._onFormSubmit);
89
+ this._domForm.inputs.forEach(input => {
90
+ input.removeEventListener('keyup', this._onChange);
75
91
  });
76
- isErrors.forEach(error => {
77
- error.classList.remove(this._options.classIsError);
92
+ this._domForm.choices.forEach(input => {
93
+ input.removeEventListener('change', this._onChange);
78
94
  });
79
95
  }
80
96
 
97
+ removeErrors(el = null) {
98
+ this._removeError(el);
99
+ }
100
+
81
101
  setError(element, message = null) {
82
- const parent = getClosest(element, this._options.selectorGroup);
83
- if( parent) {
84
- let error = parent.querySelector(`.${this._options.classErrorElement}`);
85
- if(!error) {
86
- error = create({
87
- selector: 'ul',
88
- styles: this._options.classErrorElement
89
- });
90
- parent.appendChild(error);
91
- }
92
- parent.classList.add(this._options.classIsError);
93
- if(message) {
94
- const errorElement = document.createElement('li');
95
- errorElement.innerHTML = message;
96
- error.appendChild(errorElement);
97
- }
102
+ const parent = element.closest(this._options.selectorGroup);
103
+ if (parent === null) return;
104
+ let error = parent.querySelector(`.${this._options.classErrorElement}`);
105
+ if (!error) {
106
+ error = Object.assign(
107
+ document.createElement('ul'), {
108
+ className: this._options.classErrorElement
109
+ });
110
+ parent.appendChild(error);
111
+ }
112
+ parent.classList.add(this._options.classIsError);
113
+
114
+ const previousErrors = [...error.querySelectorAll('li')];
115
+ for (let i = 0; i < previousErrors.length; i++) {
116
+ if (previousErrors[i].innerHTML === message) return;
117
+ }
118
+ if (message) {
119
+ const errorElement = document.createElement('li');
120
+ errorElement.innerHTML = message;
121
+ error.appendChild(errorElement);
98
122
  }
99
123
  }
100
124
 
101
125
  //________________________________________________________ events
102
126
  // -
103
- _onFormSubmit(pEvt) {
104
- if (!this._formValidate()) {
127
+ /** @protected */
128
+ onFormSubmit(pEvt) {
129
+ if (!this._isValid()) {
105
130
  pEvt.preventDefault();
106
131
  }
107
132
  }
108
133
 
109
- _onFormKeyUp(pEvt) {
110
- var el = pEvt.target,
111
- parent = getClosest(el, this._options.selectorGroup);
112
- parent.classList.remove(this._options.classIsError);
113
- const currentErrors = [...parent.querySelectorAll(`.${this._options.classErrorElement}`)];
114
- currentErrors.forEach(error => {
115
- error.classList.remove(this._options.classIsError);
116
- error.parentNode.removeChild(error);
117
- });
134
+ /**
135
+ * @deprecated use onFormSubmit
136
+ */
137
+ _onFormSubmit(pEvt) {
138
+ this.onFormSubmit(pEvt);
139
+ }
140
+
141
+ /** @private */
142
+ _onChange(pEvt) {
143
+ const el = pEvt.target;
144
+ this._removeError(el);
118
145
  }
119
146
 
120
147
  //________________________________________________________ private
121
148
  // -
149
+ /** @private */
122
150
  _formCreateBound() {
123
- ['_onFormKeyUp', '_onFormSubmit']
151
+ ['_onChange', '_onFormSubmit', 'onFormSubmit']
124
152
  .forEach((fn) => this[fn] = this[fn].bind(this));
125
153
  }
126
154
 
127
- _formGrabDom() {
128
- this._domForm = {};
129
- let texts = [...this._el.querySelectorAll('input[type=text]')],
130
- hiddens = [...this._el.querySelectorAll('input[type=hidden]')],
131
- emails = [...this._el.querySelectorAll('input[type=email]')],
132
- tels = [...this._el.querySelectorAll('input[type=tel]')],
133
- passwords = [...this._el.querySelectorAll('input[type=password]')],
134
- textarea = [...this._el.querySelectorAll('textarea')],
135
- radios = [...this._el.querySelectorAll('input[type=radio]')],
136
- checkbox = [...this._el.querySelectorAll('input[type=checkbox]')],
137
- numbers = [...this._el.querySelectorAll('input[type=number]')],
138
- selects = [...this._el.querySelectorAll('select')];
139
- this._domForm.inputs = [...texts, ...hiddens, ...emails, ...passwords, ...tels, ...numbers, ...checkbox, ...radios, ...selects, ...textarea];
140
- }
141
-
142
- _formValidate() {
143
- let validate = true,
144
- errorEl = null;
145
- this.removeErrors();
146
- for (let i = 0; i < this._domForm.inputs.length; i++) {
147
- let el = this._domForm.inputs[i],
148
- parent = getClosest(el, this._options.selectorGroup);
149
- if (parent === null) continue;
150
- if (el.getAttribute('disabled')) continue;
151
- if (el.getAttribute('type') && el.getAttribute('type') === 'email' && (!regex_email(el.value) && el.value !== '')) {
152
- validate = false;
153
- this.setError(el, this._options.i18n.email);
154
- }
155
- if (el.getAttribute('data-formvalidate-same') && this._el.querySelector(`[data-formvalidate-same=${el.getAttribute('data-formvalidate-same')}]`).value !== el.value) {
156
- validate = false;
157
- this.setError(el, this._options.i18n.same);
158
- }
159
- if ( el.getAttribute('pattern') && el.value !== '') {
160
- const pattern = el.getAttribute('pattern');
161
- const rgx = new RegExp(pattern);
162
- if(rgx.test(el.value)){
163
- validate = false;
164
- this.setError(el, this._options.i18n.pattern);
165
- }
166
- }
167
- if (el.getAttribute('required') !== null) {
168
- if (el.getAttribute('type') === 'radio') {
169
- let isChecked = false;
170
- let els = [...this._el.querySelectorAll(`input[type="radio"][name="${el.getAttribute('name')}"]`)];
171
- for (let i = 0; i < els.length; i++) {
172
- if (els[i].checked) {
173
- isChecked = true;
174
- break;
175
- }
176
- }
177
- if (!isChecked) {
178
- validate = false;
179
- this.setError(el, this._options.i18n.radio);
180
- }
181
- }
182
- if (el.getAttribute('type') === 'checkbox' && !el.checked) {
183
- validate = false;
184
- this.setError(el, this._options.i18n.checkbox);
185
- } else if (el.value === '') {
186
- validate = false;
187
- const message = el.getAttribute('type') !== 'hidden' ? this._options.i18n.empty : null;
188
- this.setError(el, message);
189
- }
190
- }
191
- if (!validate && errorEl === null) errorEl = el;
155
+ /** @private */
156
+ _removeError(el = null) {
157
+ if (el) {
158
+ const group = el.closest(this._options.selectorGroup);
159
+ if (group === null) return;
160
+ group.classList.remove(this._options.classIsError);
161
+ const currentErrors = [...group.querySelectorAll(`.${this._options.classErrorElement}`)];
162
+ currentErrors.forEach(error => {
163
+ error.classList.remove(this._options.classIsError);
164
+ error.parentNode.removeChild(error);
165
+ });
166
+ } else {
167
+ const isErrors = [...this._form.querySelectorAll(`${this._options.classIsError}`)];
168
+ const currentErrors = [...this._form.querySelectorAll(`.${this._options.classErrorElement}`)];
169
+ currentErrors.forEach(error => {
170
+ error.parentNode.removeChild(error);
171
+ });
172
+ isErrors.forEach(error => {
173
+ error.classList.remove(this._options.classIsError);
174
+ });
192
175
  }
193
- if (!validate && errorEl && this._options.animateToFirstError) {
194
- TweenMax.to(window, .6, { scrollTo: (window.pageYOffset + errorEl.getBoundingClientRect().top - 150) })
176
+ }
177
+
178
+ /** @private */
179
+ _checkError(el) {
180
+ const group = el.closest(this._options.selectorGroup);
181
+ if (group === null) return false;
182
+ if (el.getAttribute('disabled') !== null) return false;
183
+
184
+ let invalid = false;
185
+ if (!testRequired(el, this._form)) {
186
+ invalid = true;
187
+ const type = el.getAttribute('type');
188
+ if (type === 'radio') this.setError(el, this._options.i18n.radio);
189
+ else if (type === 'checkbox') this.setError(el, this._options.i18n.checkbox);
190
+ else if (type !== 'hidden') this.setError(el, this._options.i18n.empty);
195
191
  }
196
- return validate;
192
+ if (!testEmail(el, this._form)) {
193
+ invalid = true;
194
+ this.setError(el, this._options.i18n.email);
195
+ }
196
+ if (!testPattern(el, this._form)) {
197
+ invalid = true;
198
+ this.setError(el, this._options.i18n.pattern);
199
+ }
200
+ if (!testSame(el, this._form)) {
201
+ invalid = true;
202
+ this.setError(el, this._options.i18n.same);
203
+ }
204
+
205
+ return invalid;
206
+ }
207
+
208
+ /** @private */
209
+ _isValid() {
210
+ let isValid = true,
211
+ firstErrorElement = null;
212
+ this._removeError();
213
+
214
+ for (let i = 0; i < this._domForm.els.length; i++) {
215
+ const el = this._domForm.els[i];
216
+ const error = this._checkError(el);
217
+ if (!error) continue;
218
+ else isValid = false;
219
+ if (!isValid && firstErrorElement === null) firstErrorElement = el;
220
+ }
221
+
222
+ if (!isValid && firstErrorElement && this._options.animateToFirstError) {
223
+ gsap.to(window, {
224
+ duration: .6,
225
+ scrollTo: (window.pageYOffset + firstErrorElement.getBoundingClientRect().top - 150)
226
+ })
227
+ }
228
+ return isValid;
197
229
  }
198
230
 
231
+ /** @private */
232
+ _formGrabDom() {
233
+ /** @private */
234
+ this._domForm = {};
235
+ this._domForm.inputs = [...this._form.querySelectorAll('input[type=text],input[type=number],input[type=password],input[type=hidden],input[type=search],input[type=email],input[type=email],input[type=tel],textarea')];
236
+ this._domForm.choices = [...this._form.querySelectorAll('select, input[type=radio],input[type=checkbox]')];
237
+ this._domForm.els = [...this._domForm.inputs, ...this._domForm.choices];
238
+ }
239
+
240
+ /** @private */
199
241
  _formAddEvents() {
200
- this._el.addEventListener('submit', this._onFormSubmit);
201
- this._domForm.inputs.forEach( input => {
202
- input.addEventListener('keyup', this._onFormKeyUp);
203
- if (input.getAttribute('type') === 'checkbox' ||
204
- input.getAttribute('type') === 'radio' ||
205
- input.tagName === 'SELECT') {
206
- input.addEventListener('change', this._onFormKeyUp);
207
- }
242
+ this._form.addEventListener('submit', this._onFormSubmit);
243
+ this._domForm.inputs.forEach(input => {
244
+ input.addEventListener('keyup', this._onChange);
245
+ });
246
+ this._domForm.choices.forEach(input => {
247
+ input.addEventListener('change', this._onChange);
208
248
  });
209
249
  }
210
250
  }
211
251
 
212
- export default FormControl;
252
+ /**
253
+ * Test required input/radio/checkbox is valid
254
+ * @param {Element} el Input to test
255
+ * @param {Element} form Formulaire
256
+ * @return {Boolean} Returns if element is valid or not
257
+ */
258
+ export function testRequired(el, form) {
259
+ const isRequired = el.getAttribute('required');
260
+ if (isRequired === null) return true;
261
+
262
+ const type = el.getAttribute('type');
263
+ if (type === 'radio') {
264
+ const radios = [...form.querySelectorAll(`input[type="radio"][name="${el.getAttribute('name')}"]`)];
265
+ for (let i = 0; i < radios.length; i++) {
266
+ if (radios[i].checked) return true;
267
+ }
268
+ return false;
269
+ } else if (type === 'checkbox') return el.checked;
270
+ return el.value !== '';
271
+ }
272
+
273
+ /**
274
+ * Test input of email type is valid
275
+ * @param {Element} el Input to test
276
+ * @param {Element} form Formulaire
277
+ * @return {Boolean} Returns if element is valid or not
278
+ */
279
+ export function testEmail(el, form) {
280
+ const type = el.getAttribute('type');
281
+ if (type !== 'email' || el.value === '') return true;
282
+ return regex_email(el.value);
283
+ }
284
+
285
+ /**
286
+ * Test element with attribute data-formvalidate-same and his twin are same
287
+ * @param {Element} el Input to test
288
+ * @param {Element} form Formulaire
289
+ * @return {Boolean} Returns if element is valid or not
290
+ */
291
+ export function testSame(el, form) {
292
+ const hasSame = el.getAttribute('data-formvalidate-same');
293
+ if (hasSame === null) return true;
294
+ const twinEl = form.querySelector(`[data-formvalidate-same=${el.getAttribute('data-formvalidate-same')}]`);
295
+ return twinEl.value === el.value;
296
+ }
297
+
298
+ /**
299
+ * Test element with pattern is valid
300
+ * @param {Element} el Input to test
301
+ * @param {Element} form Formulaire
302
+ * @return {Boolean} Returns if element is valid or not
303
+ */
304
+ export function testPattern(el, form) {
305
+ const pattern = el.getAttribute('pattern');
306
+ if (pattern === null || el.value === '') return true;
307
+ const rgx = new RegExp(pattern);
308
+ return rgx.test(el.value);
309
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aw-front-helpers",
3
- "version": "2.3.10",
3
+ "version": "2.3.12",
4
4
  "description": "Assets helpers for projects",
5
5
  "author": "Yoann Le Poupon",
6
6
  "license": "ISC",