aw-front-helpers 2.3.11 → 2.3.13
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.
- package/classes/formControl.js +236 -139
- package/package.json +1 -1
package/classes/formControl.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import ScrollToPlugin
|
|
3
|
-
gsap.registerPlugin(
|
|
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:
|
|
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
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
60
|
-
this._domForm.inputs.forEach(
|
|
61
|
-
input.removeEventListener('keyup', this.
|
|
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
|
-
|
|
77
|
-
|
|
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 =
|
|
83
|
-
if(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
104
|
-
|
|
127
|
+
/** @protected */
|
|
128
|
+
onFormSubmit(pEvt) {
|
|
129
|
+
if (!this._isValid()) {
|
|
105
130
|
pEvt.preventDefault();
|
|
106
131
|
}
|
|
107
132
|
}
|
|
108
133
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
['
|
|
151
|
+
['_onChange', '_onFormSubmit', 'onFormSubmit']
|
|
124
152
|
.forEach((fn) => this[fn] = this[fn].bind(this));
|
|
125
153
|
}
|
|
126
154
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (!
|
|
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
|
+
});
|
|
175
|
+
}
|
|
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);
|
|
191
|
+
}
|
|
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
|
+
this._domForm.els = [...this._domForm.inputs, ...this._domForm.choices];
|
|
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;
|
|
192
220
|
}
|
|
193
|
-
|
|
194
|
-
|
|
221
|
+
|
|
222
|
+
if (!isValid && firstErrorElement && this._options.animateToFirstError) {
|
|
223
|
+
gsap.to(window, {
|
|
224
|
+
duration: .6,
|
|
225
|
+
scrollTo: (window.pageYOffset + firstErrorElement.getBoundingClientRect().top - 150)
|
|
226
|
+
})
|
|
195
227
|
}
|
|
196
|
-
return
|
|
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.
|
|
201
|
-
this._domForm.inputs.forEach(
|
|
202
|
-
input.addEventListener('keyup', this.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
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
|
+
}
|