minsky-webform-formkit 1.0.0
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/.pnp.cjs +11555 -0
- package/.pnp.loader.mjs +2126 -0
- package/LICENSE +21 -0
- package/README.md +324 -0
- package/dist/index.css +1 -0
- package/dist/index.js +1 -0
- package/package.json +55 -0
- package/src/components/FormKit/FormKitPhoneEnhanced/FormKitPhoneEnhanced.js +100 -0
- package/src/components/FormKit/FormKitPhoneEnhanced/FormKitPhoneEnhanced.vue +30 -0
- package/src/components/FormKit/FormKitPhoneEnhanced/countryCodes.js +1214 -0
- package/src/components/FormKit/FormKitRecaptcha/FormKitRecaptcha.vue +46 -0
- package/src/components/Icon.vue +38 -0
- package/src/components/InfoTooltip.vue +16 -0
- package/src/components/MinksyWebformFormKit/MinskyWebformFormKit.vue +357 -0
- package/src/components/MinksyWebformFormKit/_MinskyWebformFormKit.js +282 -0
- package/src/composables/useExample.js +0 -0
- package/src/formkit.config.js +72 -0
- package/src/index.mjs +20 -0
- package/src/plugins/customLabelPlugin.js +42 -0
- package/src/plugins/htmlHelpPlugin.js +23 -0
- package/src/rules/iban.js +9 -0
- package/src/rules/insz.js +21 -0
- package/src/rules/phone.js +29 -0
- package/src/rules/time.js +79 -0
- package/src/rules/vat.js +42 -0
- package/src/styles/_functions.scss +96 -0
- package/src/styles/_mixins.scss +29 -0
- package/src/styles/_variables.scss +7 -0
- package/src/styles/main.scss +11 -0
- package/src/styles/webform-formkit-multistep.scss +98 -0
- package/src/styles/webform-formkit.scss +215 -0
- package/src/styles/webform.scss +400 -0
- package/src/utils/functions.js +78 -0
- package/src/utils/lodash.js +208 -0
- package/src/utils/messages.js +37 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import _keys from 'lodash/keys';
|
|
2
|
+
import _values from 'lodash/values';
|
|
3
|
+
import gsap from 'gsap';
|
|
4
|
+
import ScrollToPlugin from 'gsap/ScrollToPlugin';
|
|
5
|
+
import defaultMessages from '../../utils/messages';
|
|
6
|
+
|
|
7
|
+
gsap.registerPlugin(ScrollToPlugin);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
props: {
|
|
12
|
+
id: {
|
|
13
|
+
type: String,
|
|
14
|
+
required: true,
|
|
15
|
+
},
|
|
16
|
+
api: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: window.location.origin,
|
|
19
|
+
},
|
|
20
|
+
defaults: {
|
|
21
|
+
type: Object,
|
|
22
|
+
},
|
|
23
|
+
schemeProcessor: {
|
|
24
|
+
type: Function,
|
|
25
|
+
},
|
|
26
|
+
formDataAddons: {
|
|
27
|
+
type: Object,
|
|
28
|
+
},
|
|
29
|
+
cmpLibrary: {
|
|
30
|
+
type: Object,
|
|
31
|
+
default: () => { },
|
|
32
|
+
},
|
|
33
|
+
forceRedirect: {
|
|
34
|
+
type: Object,
|
|
35
|
+
default: {
|
|
36
|
+
bool: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
messages: {
|
|
40
|
+
type: Object,
|
|
41
|
+
default: () => { }
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
components: {
|
|
46
|
+
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
mounted() {
|
|
50
|
+
//we merge messages provided by the user with default messages so there will alway be feedback when the form is submitted or missing
|
|
51
|
+
this.msgs = Object.assign(defaultMessages, this.messages);
|
|
52
|
+
console.log('BOEM');
|
|
53
|
+
|
|
54
|
+
this.formData.submit = async (data) => {
|
|
55
|
+
//we remove vue values so they don't f*ck up the submit in drupal
|
|
56
|
+
delete data.form_id;
|
|
57
|
+
delete data.vApp;
|
|
58
|
+
delete data.webformId;
|
|
59
|
+
//we flatten all the data to a one level object so drupal can handle the data
|
|
60
|
+
data = flattenObject(data);
|
|
61
|
+
|
|
62
|
+
this.submitForm(data);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// fetch the form
|
|
66
|
+
this.fetchForm();
|
|
67
|
+
|
|
68
|
+
if (this.formDataAddons) {
|
|
69
|
+
this.formData = Object.assign(this.formData, this.formDataAddons);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
data() {
|
|
74
|
+
return {
|
|
75
|
+
otherSchema: [],
|
|
76
|
+
schema: null,
|
|
77
|
+
schemaDefaults: {},
|
|
78
|
+
settings: null,
|
|
79
|
+
loading: true,
|
|
80
|
+
success: false,
|
|
81
|
+
submissionLoading: false,
|
|
82
|
+
loadingMessage: null,
|
|
83
|
+
successMessage: null,
|
|
84
|
+
successTitle: null,
|
|
85
|
+
errorMessages: null,
|
|
86
|
+
showErrorMessage: false,
|
|
87
|
+
multiStep: null,
|
|
88
|
+
msgs: {},
|
|
89
|
+
formData: {
|
|
90
|
+
values: {},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
computed: {
|
|
96
|
+
formKeys: {
|
|
97
|
+
get() {
|
|
98
|
+
return _keys(this.formData.values);
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
methods: {
|
|
104
|
+
async fetchForm() {
|
|
105
|
+
// add page params
|
|
106
|
+
const params = new URLSearchParams(window.location.search);
|
|
107
|
+
params.append('schema', 'form_kit');
|
|
108
|
+
|
|
109
|
+
fetch(`${this.api}/webform/${this.id}/json/schema?${params.toString()}`)
|
|
110
|
+
.then((r) => r.json())
|
|
111
|
+
.then((result) => {
|
|
112
|
+
if (result.schema) {
|
|
113
|
+
this.schema = this.schemeProcessor ? this.schemeProcessor(result.schema) : result.schema;
|
|
114
|
+
this.schemaDefaults = result.values;
|
|
115
|
+
} else {
|
|
116
|
+
this.errorMessage = this.msgs.missing;
|
|
117
|
+
this.showErrorMessage = true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.loading = false;
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
setDefaults() {
|
|
125
|
+
_keys(this.defaults).forEach((key) => {
|
|
126
|
+
if (this.$formkit.get(`${this.id}--${key}`)) {
|
|
127
|
+
this.$formkit.get(`${this.id}--${key}`).context.node.input(this.defaults[key]);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
_keys(this.schemaDefaults).forEach((key) => {
|
|
132
|
+
if (this.$formkit.get(`${this.id}--${key}`)) {
|
|
133
|
+
if (this.schemaDefaults[key].fids) return;
|
|
134
|
+
this.$formkit.get(`${this.id}--${key}`).context.node.input(this.schemaDefaults[key]);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
async submitForm(data) {
|
|
140
|
+
/* eslint-disable */
|
|
141
|
+
this.submissionLoading = true;
|
|
142
|
+
|
|
143
|
+
// disable submit button
|
|
144
|
+
const submitButton = document.querySelector('.formkit-input[type=submit]');
|
|
145
|
+
if (submitButton) {
|
|
146
|
+
submitButton.disabled = true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// add page params
|
|
150
|
+
const params = new URLSearchParams(window.location.search);
|
|
151
|
+
params.append('schema', 'form_kit');
|
|
152
|
+
|
|
153
|
+
// convert data to formdata
|
|
154
|
+
const formData = new FormData();
|
|
155
|
+
for (const key in data) {
|
|
156
|
+
// only add to formdata if has value
|
|
157
|
+
if (hasValue(data[key])) {
|
|
158
|
+
if (Array.isArray(data[key])) {
|
|
159
|
+
data[key].forEach((val) => {
|
|
160
|
+
if (this.$formkit.get(`${this.id}--${key}`).context.type === 'imageUpload') {
|
|
161
|
+
if (typeof val === 'object') {
|
|
162
|
+
formData.append(`${key}[]`, _values(val)[0]);
|
|
163
|
+
} else {
|
|
164
|
+
formData.append(`${key}[]`, val);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
} else if (typeof data[key] === 'string') {
|
|
169
|
+
if (data[key].indexOf('json_value') === 2) {
|
|
170
|
+
const values = JSON.parse(data[key]).json_value;
|
|
171
|
+
_keys(values).forEach(subkey => {
|
|
172
|
+
formData.append(`${key}[${subkey}]`, values[subkey]);
|
|
173
|
+
})
|
|
174
|
+
} else {
|
|
175
|
+
formData.append(key, data[key]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
formData.append(key, data[key]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/* eslint-enable */
|
|
184
|
+
fetch(`${this.api}/webform/${this.id}/json/submission?${params.toString()}`, {
|
|
185
|
+
method: 'POST',
|
|
186
|
+
body: formData,
|
|
187
|
+
query: window.location.search,
|
|
188
|
+
})
|
|
189
|
+
.then((r) => {
|
|
190
|
+
if (r.status === 200) {
|
|
191
|
+
r.json().then((response) => {
|
|
192
|
+
this.handleSuccess(response);
|
|
193
|
+
});
|
|
194
|
+
} else {
|
|
195
|
+
r.json().then((response) => {
|
|
196
|
+
this.showErrorMessage = true;
|
|
197
|
+
this.submissionLoading = false;
|
|
198
|
+
this.errorMessages = response.errors;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
.catch(() => {
|
|
203
|
+
this.showErrorMessage = true;
|
|
204
|
+
this.submissionLoading = false;
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
handleSuccess(response) {
|
|
209
|
+
if (!this.forceRedirect.bool) {
|
|
210
|
+
switch (response.confirmation.type) {
|
|
211
|
+
case 'inline':
|
|
212
|
+
this.successTitle = response.confirmation.title;
|
|
213
|
+
this.successMessage = response.confirmation.message;
|
|
214
|
+
this.success = true;
|
|
215
|
+
// scroll to success
|
|
216
|
+
gsap.to(window, {
|
|
217
|
+
duration: 0.5,
|
|
218
|
+
scrollTo: { y: this.$refs.form, offsetY: 200 },
|
|
219
|
+
ease: 'power2',
|
|
220
|
+
});
|
|
221
|
+
// remove toggle
|
|
222
|
+
this.submissionLoading = false;
|
|
223
|
+
break;
|
|
224
|
+
case 'url':
|
|
225
|
+
window.location = response.confirmation.url;
|
|
226
|
+
break;
|
|
227
|
+
default:
|
|
228
|
+
this.successTitle = this.msgs.success.title;
|
|
229
|
+
this.successMessage = this.msgs.success.description;
|
|
230
|
+
this.success = true;
|
|
231
|
+
// remove toggle
|
|
232
|
+
this.submissionLoading = false;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
window.location = this.forceRedirect.url;
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
watch: {
|
|
242
|
+
formKeys() {
|
|
243
|
+
this.setDefaults();
|
|
244
|
+
|
|
245
|
+
const $form = this.$refs.form.querySelector('form');
|
|
246
|
+
if ($form) $form.setAttribute('novalidate', true); // remove native js form validation because of time input error AMNESTY-785
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
function flattenObject(obj) {
|
|
252
|
+
const flattened = {};
|
|
253
|
+
|
|
254
|
+
function flatten(_obj, parentKey = '') {
|
|
255
|
+
for (const key in _obj) {
|
|
256
|
+
if (Array.isArray(_obj[key])) {
|
|
257
|
+
flattened[key] = _obj[key];
|
|
258
|
+
} else if (typeof _obj[key] === 'object' && _obj[key] !== null) {
|
|
259
|
+
flatten(_obj[key], `${parentKey + key}.`);
|
|
260
|
+
} else {
|
|
261
|
+
flattened[key] = _obj[key];
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
flatten(obj);
|
|
267
|
+
|
|
268
|
+
return flattened;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function hasValue(val) {
|
|
272
|
+
let _hasValue = false;
|
|
273
|
+
if (val || val === 0) {
|
|
274
|
+
_hasValue = true;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (Array.isArray(val)) {
|
|
278
|
+
_hasValue = Boolean(val.length);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return _hasValue;
|
|
282
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { nl, en, fr } from '@formkit/i18n';
|
|
2
|
+
import { createMultiStepPlugin } from '@formkit/addons';
|
|
3
|
+
import { createInput } from '@formkit/vue';
|
|
4
|
+
|
|
5
|
+
// import plugins
|
|
6
|
+
import customLabelPlugin from './plugins/customLabelPlugin';
|
|
7
|
+
import htmlHelpPlugin from './plugins/htmlHelpPlugin';
|
|
8
|
+
|
|
9
|
+
// import custom components
|
|
10
|
+
import FormKitPhoneEnhanced from './components/FormKit/FormKitPhoneEnhanced/FormKitPhoneEnhanced.vue';
|
|
11
|
+
import FormKitRecaptcha from './components/FormKit/FormKitRecaptcha/FormKitRecaptcha.vue';
|
|
12
|
+
|
|
13
|
+
// import rules
|
|
14
|
+
import { internationalPhone, phone } from './rules/phone';
|
|
15
|
+
import iban from './rules/iban';
|
|
16
|
+
import vat from './rules/vat';
|
|
17
|
+
import insz from './rules/insz';
|
|
18
|
+
import { timeRule, timeMessage } from './rules/time';
|
|
19
|
+
|
|
20
|
+
// config
|
|
21
|
+
const config = {
|
|
22
|
+
plugins: [
|
|
23
|
+
customLabelPlugin,
|
|
24
|
+
htmlHelpPlugin,
|
|
25
|
+
createMultiStepPlugin({ allowIncomplete: false }),
|
|
26
|
+
],
|
|
27
|
+
rules: {
|
|
28
|
+
internationalPhone,
|
|
29
|
+
phone,
|
|
30
|
+
iban,
|
|
31
|
+
vat,
|
|
32
|
+
insz,
|
|
33
|
+
timeRule,
|
|
34
|
+
},
|
|
35
|
+
messages: {
|
|
36
|
+
nl: {
|
|
37
|
+
validation: {
|
|
38
|
+
phone: 'Dit is geen geldig telefoonnummer.',
|
|
39
|
+
required: 'Dit veld is verplicht.',
|
|
40
|
+
vat: 'Dit is geen geldig Btw-nummer.',
|
|
41
|
+
is: 'Dit veld is verplicht.',
|
|
42
|
+
timeRule({ node }) {
|
|
43
|
+
return timeMessage(node);
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
en: {
|
|
48
|
+
validation: {
|
|
49
|
+
phone: 'This is not a valid phone number.',
|
|
50
|
+
required: 'This field is required.',
|
|
51
|
+
is: 'This field is required.',
|
|
52
|
+
vat: 'This is not a valid VAT number.',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
fr: {
|
|
56
|
+
validation: {
|
|
57
|
+
phone: "Ce numΓ©ro de tΓ©lΓ©phone n'est pas valide.",
|
|
58
|
+
required: 'Ce champ est obligatoire.',
|
|
59
|
+
is: 'Ce champ est obligatoire.',
|
|
60
|
+
vat: "Ce numΓ©ro de TVA n'est pas valide.",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
locales: { nl, en, fr },
|
|
65
|
+
locale: document.documentElement.lang,
|
|
66
|
+
inputs: {
|
|
67
|
+
phoneEnhanced: createInput(FormKitPhoneEnhanced),
|
|
68
|
+
recaptcha: createInput(FormKitRecaptcha),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default config;
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import MinskyWebformFormKit from './components/MinksyWebformFormKit/MinskyWebformFormKit.vue';
|
|
2
|
+
import { plugin, defaultConfig, FormKitMessages } from '@formkit/vue';
|
|
3
|
+
import Icon from './components/Icon.vue';
|
|
4
|
+
import InfoTooltip from './components/InfoTooltip.vue';
|
|
5
|
+
import formkitConfig from './formkit.config.js';
|
|
6
|
+
import { _merge } from './utils/lodash.js';
|
|
7
|
+
import './styles/main.scss';
|
|
8
|
+
|
|
9
|
+
export { MinskyWebformFormKit };
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
install(app, userConfig) {
|
|
13
|
+
const mergedConfig = _merge({}, formkitConfig, userConfig);
|
|
14
|
+
app.component('MinskyWebformFormKit', MinskyWebformFormKit);
|
|
15
|
+
app.use(plugin, defaultConfig(mergedConfig));
|
|
16
|
+
app.component('Icon', Icon);
|
|
17
|
+
app.component('InfoTooltip', InfoTooltip);
|
|
18
|
+
app.component('FormKitMessages', FormKitMessages);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const legends = ['checkbox_multi', 'radio_multi', 'repeater', 'transferlist'];
|
|
2
|
+
|
|
3
|
+
function customLabelPlugin(node) {
|
|
4
|
+
if (['button', 'submit', 'hidden', 'group', 'list', 'meta'].includes(node.props.type)) return;
|
|
5
|
+
|
|
6
|
+
node.on('created', () => {
|
|
7
|
+
const legendOrLabel = legends.includes(`${node.props.type}${node.props.options ? '_multi' : ''}`) ? 'legend' : 'label';
|
|
8
|
+
|
|
9
|
+
if (node.props.definition.schemaMemoKey) {
|
|
10
|
+
node.props.definition.schemaMemoKey += `${node.props.options ? '_multi' : ''}_add_asterisk`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const schemaFn = node.props.definition.schema;
|
|
14
|
+
|
|
15
|
+
node.props.definition.schema = (sectionsSchema = {}) => {
|
|
16
|
+
sectionsSchema[legendOrLabel] = {
|
|
17
|
+
children: [
|
|
18
|
+
'$label',
|
|
19
|
+
{
|
|
20
|
+
$cmp: 'InfoTooltip',
|
|
21
|
+
if: '$attrs.tooltip',
|
|
22
|
+
props: {
|
|
23
|
+
content: '$attrs.tooltip',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
$el: 'span',
|
|
28
|
+
if: '$state.required',
|
|
29
|
+
attrs: {
|
|
30
|
+
class: 'formkit-asterisk',
|
|
31
|
+
},
|
|
32
|
+
children: ['*'],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return schemaFn(sectionsSchema);
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default customLabelPlugin;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function htmlHelpPlugin(node) {
|
|
2
|
+
if (['button', 'submit', 'hidden', 'group', 'list', 'meta'].includes(node.props.type)) return;
|
|
3
|
+
|
|
4
|
+
node.on('created', () => {
|
|
5
|
+
const schemaFn = node.props.definition.schema;
|
|
6
|
+
|
|
7
|
+
node.props.definition.schema = (sectionsSchema = {}) => {
|
|
8
|
+
sectionsSchema.help = {
|
|
9
|
+
children: [
|
|
10
|
+
{
|
|
11
|
+
$el: 'div',
|
|
12
|
+
attrs: {
|
|
13
|
+
innerHTML: '$help',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return schemaFn(sectionsSchema);
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export default htmlHelpPlugin;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const insz = function IsRRNoValid(node) {
|
|
2
|
+
// keep only digits
|
|
3
|
+
const n = node.value.replace(/[^0-9]/g, '');
|
|
4
|
+
|
|
5
|
+
// RR numbers need to be 11 chars long
|
|
6
|
+
if (n.length != 11) return false;
|
|
7
|
+
|
|
8
|
+
const checkDigit = n.substr(n.length - 2, 2);
|
|
9
|
+
const modFunction = function(nr) { return 97 - (nr % 97); };
|
|
10
|
+
let nrToCheck = parseInt(n.substr(0, 9));
|
|
11
|
+
|
|
12
|
+
// first check without 2
|
|
13
|
+
if (modFunction(nrToCheck) == checkDigit) return true;
|
|
14
|
+
|
|
15
|
+
// then check with 2 appended for y2k+ births
|
|
16
|
+
nrToCheck = parseInt('2' + n.substr(0, 9));
|
|
17
|
+
|
|
18
|
+
return (modFunction(nrToCheck) == checkDigit);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default insz;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { parsePhoneNumber } from 'libphonenumber-js';
|
|
2
|
+
/**
|
|
3
|
+
* A contrived validation rule that ensures the inputβs value is a valid phone number.
|
|
4
|
+
*/
|
|
5
|
+
export const internationalPhone = function (node) {
|
|
6
|
+
if (node.value !== '') {
|
|
7
|
+
try {
|
|
8
|
+
return parsePhoneNumber(node.value).isValid();
|
|
9
|
+
} catch (error) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
} else {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const phone = function (node) {
|
|
18
|
+
if (node.value !== '') {
|
|
19
|
+
try {
|
|
20
|
+
return parsePhoneNumber(node.value, 'BE').isValid();
|
|
21
|
+
} catch (error) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default { internationalPhone, phone };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const timeRule = function (node) {
|
|
2
|
+
const value = node.value;
|
|
3
|
+
const { step, min, max } = node.props.attrs;
|
|
4
|
+
|
|
5
|
+
if (!value) return true;
|
|
6
|
+
|
|
7
|
+
const total = parseTimeToSeconds(value)
|
|
8
|
+
const minSeconds = parseTimeToSeconds(min)
|
|
9
|
+
const maxSeconds = parseTimeToSeconds(max)
|
|
10
|
+
const stepSize = Number(step)
|
|
11
|
+
|
|
12
|
+
// β° Before min
|
|
13
|
+
if (total < minSeconds) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// π After max
|
|
18
|
+
if (total > maxSeconds) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// π’ Between steps
|
|
23
|
+
const delta = total - minSeconds
|
|
24
|
+
const remainder = delta % stepSize
|
|
25
|
+
if (remainder === 0) return true;
|
|
26
|
+
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const timeMessage = function (node) {
|
|
31
|
+
const value = node.value;
|
|
32
|
+
const { step, min, max } = node.props.attrs;
|
|
33
|
+
|
|
34
|
+
if (!value) return ''
|
|
35
|
+
|
|
36
|
+
const total = parseTimeToSeconds(value)
|
|
37
|
+
const minSeconds = parseTimeToSeconds(min)
|
|
38
|
+
const maxSeconds = parseTimeToSeconds(max)
|
|
39
|
+
const stepSize = Number(step)
|
|
40
|
+
|
|
41
|
+
// β° Before min
|
|
42
|
+
if (total < minSeconds) {
|
|
43
|
+
const minStr = formatSecondsToTime(minSeconds)
|
|
44
|
+
return `De waarde moet ${minStr} uur of later zijn.`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// π After max
|
|
48
|
+
if (total > maxSeconds) {
|
|
49
|
+
const maxStr = formatSecondsToTime(maxSeconds)
|
|
50
|
+
return `De waarde moet ${maxStr} uur of eerder zijn.`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// π’ Between steps
|
|
54
|
+
const delta = total - minSeconds
|
|
55
|
+
const remainder = delta % stepSize
|
|
56
|
+
if (remainder === 0) return ''
|
|
57
|
+
|
|
58
|
+
const lower = total - remainder
|
|
59
|
+
const upper = lower + stepSize
|
|
60
|
+
|
|
61
|
+
const lowerStr = formatSecondsToTime(Math.max(lower, minSeconds))
|
|
62
|
+
const upperStr = formatSecondsToTime(Math.min(upper, maxSeconds))
|
|
63
|
+
|
|
64
|
+
return `Voer een geldige tijd in. De twee dichtstbijzijnde geldige waarden zijn ${lowerStr} en ${upperStr}.`
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseTimeToSeconds(timeStr) {
|
|
68
|
+
if (!timeStr) return 0
|
|
69
|
+
const [h, m = 0, s = 0] = timeStr.split(':').map(Number)
|
|
70
|
+
return h * 3600 + m * 60 + s
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function formatSecondsToTime(seconds) {
|
|
74
|
+
const hh = Math.floor(seconds / 3600)
|
|
75
|
+
const mm = Math.floor((seconds % 3600) / 60)
|
|
76
|
+
return `${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { timeRule, timeMessage };
|
package/src/rules/vat.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { checkVAT, countries } from '@accountable/jsvat';
|
|
2
|
+
|
|
3
|
+
const isVatValid = (vat) => {
|
|
4
|
+
// BE0xxxxxxx β mod 97 on first 8 digits
|
|
5
|
+
if (vat[0] === '0') {
|
|
6
|
+
const base = Number(vat.slice(0, 8));
|
|
7
|
+
const checkDigits = Number(vat.slice(8, 10));
|
|
8
|
+
return 97 - (base % 97) === checkDigits;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// BE1xxxxxxx β check if is 10 digits
|
|
12
|
+
if (vat[0] === '1') {
|
|
13
|
+
return vat.length === 10;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return false; // other leading digits not allowed
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const newBelgium = {
|
|
20
|
+
name: 'newBelgium',
|
|
21
|
+
codes: ['BE', 'BEL', '056'],
|
|
22
|
+
calcWithFormatFn: (vat) => {
|
|
23
|
+
if (vat.length === 10) {
|
|
24
|
+
return { vat: `BE${vat}`, isValid: isVatValid(vat) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Invalid length
|
|
28
|
+
return { vat: `BE${vat}`, isValid: false };
|
|
29
|
+
},
|
|
30
|
+
rules: {
|
|
31
|
+
multipliers: {},
|
|
32
|
+
regex: [/^(BE)(\d{10})$/],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const newCountries = countries.map((obj) => (obj.name === 'Belgium' ? newBelgium : obj));
|
|
37
|
+
|
|
38
|
+
const vat = function (node) {
|
|
39
|
+
return checkVAT(node.value, newCountries).isValid;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default vat;
|