@steveesamson/microform 1.0.8 → 1.0.10
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/dist/form-action.svelte.d.ts +3 -0
- package/dist/{form-action.js → form-action.svelte.js} +31 -40
- package/dist/form-validators.d.ts +2 -4
- package/dist/form-validators.js +71 -50
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/index.svelte.js +28 -40
- package/dist/internal.svelte.d.ts +12 -0
- package/dist/internal.svelte.js +15 -0
- package/dist/types.d.ts +5 -6
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +0 -3
- package/dist/utils.js +17 -18
- package/package.json +1 -1
- package/dist/form-action.d.ts +0 -4
- package/dist/internal.d.ts +0 -4
- package/dist/internal.js +0 -1
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FormOptions, FormAction, FormValues, FormErrors } from './types.js';
|
|
2
|
+
import type { Params } from './internal.svelte.js';
|
|
3
|
+
export declare const formAction: (values: FormValues, errors: FormErrors, unfits: FormErrors, reportDirty: (clean: boolean) => void, options: FormOptions, validationMap: Params) => FormAction;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { getEditableContent } from "./utils.js";
|
|
1
|
+
import { useValidator } from './form-validators.js';
|
|
2
|
+
import { getEditableContent } from './utils.js';
|
|
4
3
|
const isField = (node) => {
|
|
5
|
-
return node instanceof HTMLSelectElement ||
|
|
4
|
+
return (node instanceof HTMLSelectElement ||
|
|
6
5
|
node instanceof HTMLInputElement ||
|
|
7
|
-
node instanceof HTMLTextAreaElement;
|
|
6
|
+
node instanceof HTMLTextAreaElement);
|
|
8
7
|
};
|
|
9
8
|
const isExcluded = (node) => {
|
|
10
|
-
return node instanceof HTMLInputElement && ['radio', 'checkbox'].includes(node.type.toLowerCase());
|
|
9
|
+
return (node instanceof HTMLInputElement && ['radio', 'checkbox'].includes(node.type.toLowerCase()));
|
|
11
10
|
};
|
|
12
11
|
const isCheckbox = (node) => {
|
|
13
12
|
return node instanceof HTMLInputElement && ['checkbox'].includes(node.type.toLowerCase());
|
|
@@ -16,12 +15,11 @@ const isRadio = (node) => {
|
|
|
16
15
|
return node instanceof HTMLInputElement && ['radio'].includes(node.type.toLowerCase());
|
|
17
16
|
};
|
|
18
17
|
const checkFormFitness = (values, validationMap, validate) => {
|
|
19
|
-
const _values = get(values);
|
|
20
18
|
for (const [name, { validations }] of Object.entries(validationMap)) {
|
|
21
|
-
validate({ name, value:
|
|
19
|
+
validate({ name, value: values[name], validations });
|
|
22
20
|
}
|
|
23
21
|
};
|
|
24
|
-
export const formAction = (values, errors, unfits,
|
|
22
|
+
export const formAction = (values, errors, unfits, reportDirty, options, validationMap) => {
|
|
25
23
|
const { validators: customValidators } = options;
|
|
26
24
|
const { validate, validators } = useValidator(errors, values);
|
|
27
25
|
// override
|
|
@@ -30,12 +28,13 @@ export const formAction = (values, errors, unfits, isdirty, options, validationM
|
|
|
30
28
|
validators[key] = val;
|
|
31
29
|
}
|
|
32
30
|
}
|
|
31
|
+
const hasError = (next) => !!next;
|
|
33
32
|
return (node, eventProps) => {
|
|
34
33
|
const nodeName = isField(node) ? node.name : '';
|
|
35
34
|
const { name: dsname = nodeName } = node.dataset || {};
|
|
36
|
-
const { name = dsname, validations = [], validateEvent = options.validateEvent = 'blur', html = false } = eventProps || {};
|
|
35
|
+
const { name = dsname, validations = [], validateEvent = (options.validateEvent = 'blur'), html = false } = eventProps || {};
|
|
37
36
|
validationMap[name] = { validations, html, nodeRef: node };
|
|
38
|
-
const storedValue =
|
|
37
|
+
const storedValue = values[name] || '';
|
|
39
38
|
let defValue = storedValue;
|
|
40
39
|
if (isField(node) && !isExcluded(node)) {
|
|
41
40
|
defValue = node.value || storedValue;
|
|
@@ -45,58 +44,50 @@ export const formAction = (values, errors, unfits, isdirty, options, validationM
|
|
|
45
44
|
defValue = node.innerHTML || storedValue;
|
|
46
45
|
node.innerHTML = defValue;
|
|
47
46
|
}
|
|
48
|
-
values
|
|
49
|
-
|
|
50
|
-
});
|
|
51
|
-
let unsubscribe;
|
|
47
|
+
values[name] = defValue;
|
|
48
|
+
let eventBound = $state(false);
|
|
52
49
|
const updateNode = (e) => {
|
|
53
|
-
if (!
|
|
54
|
-
|
|
55
|
-
validate({ name, value: data[name], validations, node });
|
|
56
|
-
});
|
|
50
|
+
if (!eventBound) {
|
|
51
|
+
eventBound = true;
|
|
57
52
|
}
|
|
58
53
|
if (isField(node) && !isExcluded(node)) {
|
|
59
54
|
const value = e.target.value || '';
|
|
60
|
-
values
|
|
61
|
-
return { ...data, [name]: value };
|
|
62
|
-
});
|
|
55
|
+
values[name] = value;
|
|
63
56
|
}
|
|
64
57
|
else if (node.isContentEditable) {
|
|
65
58
|
const { value: htm, text } = getEditableContent({ target: node }, html);
|
|
66
|
-
values
|
|
67
|
-
|
|
68
|
-
});
|
|
59
|
+
values[name] = htm;
|
|
60
|
+
values[`${name}-text`] = text;
|
|
69
61
|
}
|
|
70
62
|
else if (isCheckbox(node)) {
|
|
71
63
|
const { checked, value: val } = node;
|
|
72
|
-
const
|
|
73
|
-
let current = fieldValue
|
|
64
|
+
const fieldValue = String(values[name] ?? '');
|
|
65
|
+
let current = fieldValue?.split(',');
|
|
74
66
|
if (checked) {
|
|
75
67
|
current.push(val);
|
|
76
68
|
}
|
|
77
69
|
else {
|
|
78
70
|
current = current.filter((next) => next !== val);
|
|
79
71
|
}
|
|
80
|
-
values.
|
|
81
|
-
return { ...data, [name]: [...new Set(current)].join(',') };
|
|
82
|
-
});
|
|
72
|
+
values[name] = [...new Set(current)].join(',');
|
|
83
73
|
}
|
|
84
74
|
else if (isRadio(node)) {
|
|
85
75
|
const { value: fvalue } = node;
|
|
86
|
-
values
|
|
87
|
-
return { ...data, [name]: fvalue };
|
|
88
|
-
});
|
|
76
|
+
values[name] = fvalue;
|
|
89
77
|
}
|
|
90
78
|
const { validate: validateUnfit } = useValidator(unfits, values, validators);
|
|
91
79
|
checkFormFitness(values, validationMap, validateUnfit);
|
|
92
|
-
|
|
80
|
+
validate({ name, value: values[name], validations, node });
|
|
81
|
+
const withErrors = Object.values(errors).some(hasError);
|
|
82
|
+
const withUnfits = Object.values(unfits).some(hasError);
|
|
83
|
+
const clean = !withErrors && !withUnfits;
|
|
84
|
+
reportDirty(clean);
|
|
93
85
|
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
unsubscribe?.();
|
|
86
|
+
$effect(() => {
|
|
87
|
+
node.addEventListener(validateEvent, updateNode);
|
|
88
|
+
return () => {
|
|
98
89
|
node.removeEventListener(validateEvent, updateNode);
|
|
99
|
-
}
|
|
100
|
-
};
|
|
90
|
+
};
|
|
91
|
+
});
|
|
101
92
|
};
|
|
102
93
|
};
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { ValidateArgs, ValidatorType, ValidatorMap } from "./types.js";
|
|
3
|
-
import type { Params } from "./internal.js";
|
|
1
|
+
import type { ValidateArgs, ValidatorType, ValidatorMap, FormErrors, FormValues } from './types.js';
|
|
4
2
|
export declare const IS_REQUIRED = "required";
|
|
5
3
|
export declare const IS_EMAIL = "email";
|
|
6
4
|
export declare const IS_URL = "url";
|
|
@@ -16,7 +14,7 @@ export declare const IS_MIN = "min";
|
|
|
16
14
|
export declare const IS_MAX = "max";
|
|
17
15
|
export declare const IT_MATCHES = "match";
|
|
18
16
|
export declare const IS_FILE_SIZE_MB = "file-size-mb";
|
|
19
|
-
export declare const useValidator: (errors:
|
|
17
|
+
export declare const useValidator: (errors: FormErrors, values: FormValues, validators?: ValidatorMap<ValidatorType>) => {
|
|
20
18
|
validate: ({ name, value, validations, node }: ValidateArgs) => Promise<void>;
|
|
21
19
|
validators: ValidatorMap<ValidatorType>;
|
|
22
20
|
};
|
package/dist/form-validators.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { makeName, isValidFileSize } from "./utils.js";
|
|
1
|
+
import { makeName, isValidFileSize } from './utils.js';
|
|
3
2
|
const regexes = {
|
|
4
3
|
number: /^[-+]?[0-9]+(\.[0-9]+)?$/g,
|
|
5
4
|
alpha: /^[A-Z\s]+$/gi,
|
|
@@ -7,22 +6,22 @@ const regexes = {
|
|
|
7
6
|
integer: /^[-+]?\d+$/g,
|
|
8
7
|
email: /^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$/gi,
|
|
9
8
|
url: /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i,
|
|
10
|
-
ip: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/g
|
|
9
|
+
ip: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/g
|
|
11
10
|
};
|
|
12
|
-
export const IS_REQUIRED =
|
|
13
|
-
export const IS_EMAIL =
|
|
14
|
-
export const IS_URL =
|
|
15
|
-
export const IS_IP =
|
|
16
|
-
export const IS_INTEGER =
|
|
17
|
-
export const IS_NUMBER =
|
|
18
|
-
export const IS_ALPHA =
|
|
19
|
-
export const IS_ALPHANUM =
|
|
20
|
-
export const IS_MIN_LEN =
|
|
21
|
-
export const IS_MAX_LEN =
|
|
22
|
-
export const IS_LEN =
|
|
23
|
-
export const IS_MIN =
|
|
24
|
-
export const IS_MAX =
|
|
25
|
-
export const IT_MATCHES =
|
|
11
|
+
export const IS_REQUIRED = 'required';
|
|
12
|
+
export const IS_EMAIL = 'email';
|
|
13
|
+
export const IS_URL = 'url';
|
|
14
|
+
export const IS_IP = 'ip';
|
|
15
|
+
export const IS_INTEGER = 'integer';
|
|
16
|
+
export const IS_NUMBER = 'number';
|
|
17
|
+
export const IS_ALPHA = 'alpha';
|
|
18
|
+
export const IS_ALPHANUM = 'alphanum';
|
|
19
|
+
export const IS_MIN_LEN = 'minlen';
|
|
20
|
+
export const IS_MAX_LEN = 'maxlen';
|
|
21
|
+
export const IS_LEN = 'len';
|
|
22
|
+
export const IS_MIN = 'min';
|
|
23
|
+
export const IS_MAX = 'max';
|
|
24
|
+
export const IT_MATCHES = 'match';
|
|
26
25
|
export const IS_FILE_SIZE_MB = 'file-size-mb';
|
|
27
26
|
const getDefaultValidators = () => {
|
|
28
27
|
const validators = {
|
|
@@ -30,78 +29,94 @@ const getDefaultValidators = () => {
|
|
|
30
29
|
if (!value || (Array.isArray(value) && !value.length)) {
|
|
31
30
|
return `${label} is mandatory.`;
|
|
32
31
|
}
|
|
33
|
-
return
|
|
32
|
+
return '';
|
|
34
33
|
},
|
|
35
34
|
[IS_EMAIL]: ({ value, label }) => {
|
|
36
|
-
return
|
|
35
|
+
return !!value && !value.match(regexes['email']) ? `${label} should be a valid email.` : '';
|
|
37
36
|
},
|
|
38
37
|
[IS_URL]: ({ value, label }) => {
|
|
39
|
-
return !!value && !value.match(regexes['url']) ? `${label} should be a valid URL.` :
|
|
38
|
+
return !!value && !value.match(regexes['url']) ? `${label} should be a valid URL.` : '';
|
|
40
39
|
},
|
|
41
40
|
[IS_IP]: ({ value, label }) => {
|
|
42
|
-
return !!value && !value.match(regexes['ip']) ? `${label} should be a valid IP.` :
|
|
41
|
+
return !!value && !value.match(regexes['ip']) ? `${label} should be a valid IP.` : '';
|
|
43
42
|
},
|
|
44
43
|
[IS_INTEGER]: ({ value, label }) => {
|
|
45
|
-
return !!value && !value.match(regexes['integer'])
|
|
44
|
+
return !!value && !value.match(regexes['integer'])
|
|
45
|
+
? `${label} must be an integer number.`
|
|
46
|
+
: '';
|
|
46
47
|
},
|
|
47
48
|
[IS_NUMBER]: ({ value, label }) => {
|
|
48
|
-
return !!value && !value.match(regexes['number']) ? `${label} must be a number.` :
|
|
49
|
+
return !!value && !value.match(regexes['number']) ? `${label} must be a number.` : '';
|
|
49
50
|
},
|
|
50
51
|
[IS_ALPHA]: ({ value, label }) => {
|
|
51
|
-
return !!value && !value.match(regexes['alpha'])
|
|
52
|
+
return !!value && !value.match(regexes['alpha'])
|
|
53
|
+
? `${label} should be a string of alphabets.`
|
|
54
|
+
: '';
|
|
52
55
|
},
|
|
53
56
|
[IS_ALPHANUM]: ({ value, label }) => {
|
|
54
|
-
return !!value && !value.match(regexes['alphanum'])
|
|
57
|
+
return !!value && !value.match(regexes['alphanum'])
|
|
58
|
+
? `${label} should be a string of alphanumerics starting with alphabets.`
|
|
59
|
+
: '';
|
|
55
60
|
},
|
|
56
61
|
[IS_MIN_LEN]: ({ value, label, parts }) => {
|
|
57
|
-
if (
|
|
62
|
+
if (value) {
|
|
58
63
|
if (!parts || parts.length < 2) {
|
|
59
64
|
return `${label}: min-length validation requires minimum length.`;
|
|
60
65
|
}
|
|
61
66
|
const extra = parts[1].trim();
|
|
62
|
-
return value.length >= parseInt(parts[1], 10)
|
|
67
|
+
return value.length >= parseInt(parts[1], 10)
|
|
68
|
+
? ''
|
|
69
|
+
: `${label} must be at least ${extra} characters long.`;
|
|
63
70
|
}
|
|
64
|
-
return
|
|
71
|
+
return '';
|
|
65
72
|
},
|
|
66
73
|
[IS_MAX_LEN]: ({ value, label, parts }) => {
|
|
67
|
-
if (
|
|
74
|
+
if (value) {
|
|
68
75
|
if (!parts || parts.length < 2) {
|
|
69
76
|
return `${label}: max-length validation requires maximum length.`;
|
|
70
77
|
}
|
|
71
78
|
const extra = parts[1].trim();
|
|
72
|
-
return value.length <= parseInt(parts[1], 10)
|
|
79
|
+
return value.length <= parseInt(parts[1], 10)
|
|
80
|
+
? ''
|
|
81
|
+
: `${label} must be at most ${extra} characters long.`;
|
|
73
82
|
}
|
|
74
|
-
return
|
|
83
|
+
return '';
|
|
75
84
|
},
|
|
76
85
|
[IS_LEN]: ({ value, label, parts }) => {
|
|
77
|
-
if (
|
|
86
|
+
if (value) {
|
|
78
87
|
if (!parts || parts.length < 2) {
|
|
79
88
|
return `${label}: length validation requires length.`;
|
|
80
89
|
}
|
|
81
90
|
const extra = parts[1].trim();
|
|
82
|
-
return value.length === parseInt(parts[1], 10)
|
|
91
|
+
return value.length === parseInt(parts[1], 10)
|
|
92
|
+
? ''
|
|
93
|
+
: `${label} must exactly be ${extra} characters long.`;
|
|
83
94
|
}
|
|
84
|
-
return
|
|
95
|
+
return '';
|
|
85
96
|
},
|
|
86
97
|
[IS_MAX]: ({ value, label, parts }) => {
|
|
87
|
-
if (
|
|
98
|
+
if (value) {
|
|
88
99
|
if (!parts || parts.length < 2) {
|
|
89
100
|
return `${label}: max validation requires the maximum value.`;
|
|
90
101
|
}
|
|
91
102
|
const extra = parts[1].trim();
|
|
92
|
-
return parseInt(value, 10) <= parseInt(parts[1], 10)
|
|
103
|
+
return parseInt(value, 10) <= parseInt(parts[1], 10)
|
|
104
|
+
? ''
|
|
105
|
+
: `${label} must not be greater than ${extra}.`;
|
|
93
106
|
}
|
|
94
|
-
return
|
|
107
|
+
return '';
|
|
95
108
|
},
|
|
96
109
|
[IS_MIN]: ({ value, label, parts }) => {
|
|
97
|
-
if (
|
|
110
|
+
if (value) {
|
|
98
111
|
if (!parts || parts.length < 2) {
|
|
99
112
|
return `${label}: min validation requires the minimum value.`;
|
|
100
113
|
}
|
|
101
114
|
const extra = parts[1].trim();
|
|
102
|
-
return parseInt(value, 10) >= parseInt(parts[1], 10)
|
|
115
|
+
return parseInt(value, 10) >= parseInt(parts[1], 10)
|
|
116
|
+
? ''
|
|
117
|
+
: `${label} must not be less than ${extra}.`;
|
|
103
118
|
}
|
|
104
|
-
return
|
|
119
|
+
return '';
|
|
105
120
|
},
|
|
106
121
|
[IT_MATCHES]: ({ values, value, label, parts }) => {
|
|
107
122
|
if (value) {
|
|
@@ -110,40 +125,46 @@ const getDefaultValidators = () => {
|
|
|
110
125
|
}
|
|
111
126
|
const partyName = parts[1].trim();
|
|
112
127
|
const partyValue = values[partyName];
|
|
113
|
-
return value === partyValue ?
|
|
128
|
+
return value === partyValue ? '' : `${label} does not match ${makeName(partyName)}.`;
|
|
114
129
|
}
|
|
115
|
-
return
|
|
130
|
+
return '';
|
|
116
131
|
},
|
|
117
132
|
[IS_FILE_SIZE_MB]: ({ value, node, label, parts }) => {
|
|
118
|
-
if (
|
|
133
|
+
if (value) {
|
|
119
134
|
if (!parts || parts.length < 2) {
|
|
120
135
|
return `${label}: max file size in MB validation requires maximum file size of mb value.`;
|
|
121
|
-
;
|
|
122
136
|
}
|
|
123
137
|
const extra = parts[1].trim();
|
|
124
138
|
return isValidFileSize(node, parseInt(extra, 10));
|
|
125
139
|
}
|
|
126
|
-
return
|
|
140
|
+
return '';
|
|
127
141
|
}
|
|
128
142
|
};
|
|
129
143
|
return validators;
|
|
130
144
|
};
|
|
131
145
|
export const useValidator = (errors, values, validators = getDefaultValidators()) => {
|
|
132
146
|
const setError = (name, error) => {
|
|
133
|
-
errors
|
|
134
|
-
return { ...prev, [name]: error };
|
|
135
|
-
});
|
|
147
|
+
errors[name] = error ?? '';
|
|
136
148
|
};
|
|
137
149
|
return {
|
|
138
150
|
validate: async ({ name, value, validations = [], node = undefined }) => {
|
|
139
151
|
if (validations.length) {
|
|
140
|
-
const _validations = validations.includes('required')
|
|
152
|
+
const _validations = validations.includes('required')
|
|
153
|
+
? ['required', ...validations.filter((val) => val !== 'required')]
|
|
154
|
+
: [...validations];
|
|
141
155
|
for (let i = 0; i < _validations.length; ++i) {
|
|
142
156
|
const validation = _validations[i], parts = validation.split(':'), type = parts[0].trim();
|
|
143
157
|
const validator = validators[type];
|
|
144
158
|
if (!validator)
|
|
145
159
|
return;
|
|
146
|
-
const error = validator({
|
|
160
|
+
const error = validator({
|
|
161
|
+
name,
|
|
162
|
+
label: makeName(name),
|
|
163
|
+
value,
|
|
164
|
+
values,
|
|
165
|
+
node,
|
|
166
|
+
parts
|
|
167
|
+
});
|
|
147
168
|
setError(name, error ? error : '');
|
|
148
169
|
if (error) {
|
|
149
170
|
break;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export { IS_EMAIL, IS_ALPHA, IS_ALPHANUM, IS_INTEGER, IS_IP, IS_LEN, IT_MATCHES as IS_MATCH_FOR, IS_MAX, IS_FILE_SIZE_MB as IS_MAX_FILE_SIZE, IS_MAX_LEN, IS_MIN, IS_MIN_LEN, IS_NUMBER, IS_REQUIRED, IS_URL } from
|
|
3
|
-
import microform from
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export { IS_EMAIL, IS_ALPHA, IS_ALPHANUM, IS_INTEGER, IS_IP, IS_LEN, IT_MATCHES as IS_MATCH_FOR, IS_MAX, IS_FILE_SIZE_MB as IS_MAX_FILE_SIZE, IS_MAX_LEN, IS_MIN, IS_MIN_LEN, IS_NUMBER, IS_REQUIRED, IS_URL } from './form-validators.js';
|
|
3
|
+
import microform from './index.svelte.js';
|
|
4
4
|
export default microform;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export { IS_EMAIL, IS_ALPHA, IS_ALPHANUM, IS_INTEGER, IS_IP, IS_LEN, IT_MATCHES as IS_MATCH_FOR, IS_MAX, IS_FILE_SIZE_MB as IS_MAX_FILE_SIZE, IS_MAX_LEN, IS_MIN, IS_MIN_LEN, IS_NUMBER, IS_REQUIRED, IS_URL } from
|
|
3
|
-
import microform from
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export { IS_EMAIL, IS_ALPHA, IS_ALPHANUM, IS_INTEGER, IS_IP, IS_LEN, IT_MATCHES as IS_MATCH_FOR, IS_MAX, IS_FILE_SIZE_MB as IS_MAX_FILE_SIZE, IS_MAX_LEN, IS_MIN, IS_MIN_LEN, IS_NUMBER, IS_REQUIRED, IS_URL } from './form-validators.js';
|
|
3
|
+
import microform from './index.svelte.js';
|
|
4
4
|
export default microform;
|
package/dist/index.svelte.js
CHANGED
|
@@ -1,36 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { bindStateToStore } from "./utils.js";
|
|
1
|
+
import { formAction } from './form-action.svelte.js';
|
|
2
|
+
import { formState } from './internal.svelte.js';
|
|
4
3
|
const microform = (props) => {
|
|
5
4
|
// form default values
|
|
6
5
|
const data = props?.data || {};
|
|
7
|
-
// form
|
|
8
|
-
const
|
|
9
|
-
// internal checks
|
|
10
|
-
const unfits = writable({});
|
|
11
|
-
// external form errors
|
|
12
|
-
const _errors = writable({});
|
|
13
|
-
const isdirty = writable(false);
|
|
14
|
-
const isclean = derived(([_errors, unfits]), ([$errors, $unfits]) => {
|
|
15
|
-
const errVals = Object.values($errors);
|
|
16
|
-
const unfitVals = Object.values($unfits);
|
|
17
|
-
return (errVals.length === 0 || errVals.reduce((comm, next) => comm && !next, true))
|
|
18
|
-
&& (unfitVals.length === 0 || unfitVals.reduce((comm, next) => comm && !next, true));
|
|
19
|
-
});
|
|
20
|
-
const _valid = derived(([isclean, isdirty]), ([$isclean, $isdirty]) => {
|
|
21
|
-
return $isclean && $isdirty;
|
|
22
|
-
});
|
|
6
|
+
// form state
|
|
7
|
+
const state = formState(data);
|
|
23
8
|
const validationMap = {};
|
|
24
9
|
const { options = {
|
|
25
10
|
validateEvent: 'blur',
|
|
26
11
|
validators: {}
|
|
27
12
|
} } = props || {};
|
|
28
|
-
const
|
|
13
|
+
const updateSanity = (isOk) => {
|
|
14
|
+
state.sanity.ok = isOk;
|
|
15
|
+
};
|
|
16
|
+
const form = formAction(state.values, state.errors, state.unfits, updateSanity, options, validationMap);
|
|
29
17
|
const handleSubmit = (e, handler) => {
|
|
30
18
|
e.preventDefault();
|
|
31
|
-
if (!
|
|
19
|
+
if (!state.sanity.ok)
|
|
32
20
|
return;
|
|
33
|
-
handler({ ...
|
|
21
|
+
handler({ ...state.values });
|
|
34
22
|
};
|
|
35
23
|
const onsubmit = (handler) => {
|
|
36
24
|
const onSubmit = async (e) => {
|
|
@@ -44,37 +32,37 @@ const microform = (props) => {
|
|
|
44
32
|
});
|
|
45
33
|
};
|
|
46
34
|
const reset = () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
35
|
+
const defaultKeys = Object.keys({ ...data });
|
|
36
|
+
for (const [key,] of Object.entries(state.values)) {
|
|
37
|
+
if (defaultKeys.includes(key)) {
|
|
38
|
+
state.values[key] = data[key];
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
delete state.values[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
state.errors = {};
|
|
45
|
+
state.unfits = {};
|
|
46
|
+
state.sanity.ok = false;
|
|
51
47
|
for (const [name, { nodeRef, html }] of Object.entries(validationMap).filter(([, { nodeRef }]) => !!nodeRef)) {
|
|
52
48
|
if (nodeRef) {
|
|
53
49
|
if (nodeRef.isContentEditable) {
|
|
54
|
-
nodeRef[html ?
|
|
50
|
+
nodeRef[html ? 'innerHTML' : 'textContent'] = data[name] || '';
|
|
55
51
|
}
|
|
56
52
|
else {
|
|
57
|
-
nodeRef[
|
|
53
|
+
nodeRef['value'] = data[name] || '';
|
|
58
54
|
}
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
};
|
|
62
|
-
const values = $state({ ...data });
|
|
63
|
-
const errors = $state({});
|
|
64
|
-
const sanity = $state({ ok: get(_valid) });
|
|
65
|
-
bindStateToStore(values, _values);
|
|
66
|
-
bindStateToStore(errors, _errors);
|
|
67
|
-
_valid.subscribe((changes) => {
|
|
68
|
-
sanity.ok = changes;
|
|
69
|
-
});
|
|
70
58
|
return {
|
|
71
|
-
values,
|
|
72
|
-
errors,
|
|
73
|
-
sanity,
|
|
59
|
+
values: state.values,
|
|
60
|
+
errors: state.errors,
|
|
61
|
+
sanity: state.sanity,
|
|
74
62
|
form,
|
|
75
63
|
submit,
|
|
76
64
|
onsubmit,
|
|
77
|
-
reset
|
|
65
|
+
reset
|
|
78
66
|
};
|
|
79
67
|
};
|
|
80
68
|
export default microform;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FormValues, FormErrors, FormSanity } from "./types.js";
|
|
2
|
+
export type Params<T = any> = {
|
|
3
|
+
[key: string | number | symbol]: T;
|
|
4
|
+
};
|
|
5
|
+
export type Primitive = string | number | boolean | string[] | number[] | boolean[];
|
|
6
|
+
export type FormState = {
|
|
7
|
+
values: FormValues;
|
|
8
|
+
errors: FormErrors;
|
|
9
|
+
sanity: FormSanity;
|
|
10
|
+
unfits: FormErrors;
|
|
11
|
+
};
|
|
12
|
+
export declare const formState: (data: Params) => FormState;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const formState = (data) => {
|
|
2
|
+
// form values
|
|
3
|
+
const values = $state({ ...data });
|
|
4
|
+
// external form errors
|
|
5
|
+
const errors = $state({});
|
|
6
|
+
const sanity = $state({ ok: false });
|
|
7
|
+
// internal checks
|
|
8
|
+
const unfits = $state({});
|
|
9
|
+
return {
|
|
10
|
+
values,
|
|
11
|
+
errors,
|
|
12
|
+
sanity,
|
|
13
|
+
unfits,
|
|
14
|
+
};
|
|
15
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Params } from "./internal.js";
|
|
1
|
+
import type { Params } from './internal.svelte.js';
|
|
3
2
|
export type Validator = `max:${number}` | `min:${number}` | `len:${number}` | `minlen:${number}` | `maxlen:${number}` | `file-size-mb:${number}` | `match:${string}` | 'required' | 'email' | 'integer' | 'number' | 'alpha' | 'alphanum' | 'url' | 'ip';
|
|
4
3
|
export type ValidatorKey = 'required' | 'email' | 'integer' | 'number' | 'alpha' | 'alphanum' | 'url' | 'ip' | `max` | `min` | `len` | `minlen` | `maxlen` | `file-size-mb` | `match`;
|
|
5
4
|
export type FieldProps = {
|
|
@@ -26,10 +25,10 @@ export interface ValidateArgs {
|
|
|
26
25
|
export type FormReturn = {
|
|
27
26
|
destroy: () => void;
|
|
28
27
|
};
|
|
29
|
-
export type ValidateEvent = 'input' | 'change' | 'keyup' | 'blur';
|
|
28
|
+
export type ValidateEvent = 'input' | 'change' | 'keyup' | 'blur' | 'keydown';
|
|
30
29
|
export type FormValues = Params;
|
|
31
30
|
export type FormErrors = Params;
|
|
32
|
-
export type Dirty =
|
|
31
|
+
export type Dirty = boolean;
|
|
33
32
|
export type ActionOptions = {
|
|
34
33
|
validateEvent?: ValidateEvent;
|
|
35
34
|
name?: string;
|
|
@@ -37,7 +36,7 @@ export type ActionOptions = {
|
|
|
37
36
|
node?: HTMLElement;
|
|
38
37
|
html?: boolean;
|
|
39
38
|
};
|
|
40
|
-
export type FormAction = (node: HTMLElement, eventProps?: ActionOptions) =>
|
|
39
|
+
export type FormAction = (node: HTMLElement, eventProps?: ActionOptions) => void;
|
|
41
40
|
export type FormSubmitEvent = SubmitEvent & {
|
|
42
41
|
currentTarget: EventTarget & HTMLFormElement;
|
|
43
42
|
};
|
|
@@ -57,7 +56,7 @@ export type MicroFormReturn = {
|
|
|
57
56
|
values: FormValues;
|
|
58
57
|
errors: FormErrors;
|
|
59
58
|
sanity: FormSanity;
|
|
60
|
-
form: (node: HTMLElement, eventProps?: ActionOptions) =>
|
|
59
|
+
form: (node: HTMLElement, eventProps?: ActionOptions) => void;
|
|
61
60
|
submit: (formNode: HTMLFormElement, handler: FormSubmit) => void;
|
|
62
61
|
onsubmit: (handler: FormSubmit) => (e: Event) => Promise<void>;
|
|
63
62
|
reset: () => void;
|
package/dist/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { Writable } from "svelte/store";
|
|
2
|
-
import type { Params } from "./internal.js";
|
|
3
1
|
type TEvent = {
|
|
4
2
|
target: HTMLElement;
|
|
5
3
|
};
|
|
@@ -9,5 +7,4 @@ export declare const getEditableContent: (e: TEvent, isHtml: boolean) => {
|
|
|
9
7
|
};
|
|
10
8
|
export declare const makeName: (str: string) => string;
|
|
11
9
|
export declare const isValidFileSize: (node: HTMLInputElement | undefined, maxFileSizeInMB: number) => string;
|
|
12
|
-
export declare const bindStateToStore: (state: Params, store: Writable<Params>) => void;
|
|
13
10
|
export {};
|
package/dist/utils.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
export const getEditableContent = (e, isHtml) => {
|
|
2
2
|
const el = e.target;
|
|
3
|
-
const text = el.textContent?.trim() ||
|
|
3
|
+
const text = el.textContent?.trim() || '';
|
|
4
4
|
if (!isHtml) {
|
|
5
5
|
return { text, value: text };
|
|
6
6
|
}
|
|
7
|
+
if (!text) {
|
|
8
|
+
return { value: '', text };
|
|
9
|
+
}
|
|
7
10
|
let htm = el.innerHTML;
|
|
8
11
|
htm = htm.trim();
|
|
9
|
-
htm = htm.replace(/<br>/g,
|
|
10
|
-
htm = htm.replace(/<div><\/div>/g,
|
|
11
|
-
htm = htm.replace(/<p><\/p>/g,
|
|
12
|
-
htm = htm.replace(/<div>/g,
|
|
13
|
-
htm = htm.replace(/<\/div>/g,
|
|
14
|
-
htm = htm.trim()
|
|
15
|
-
? htm.indexOf("<p>") === -1
|
|
16
|
-
? `<p>${htm}</p>`
|
|
17
|
-
: htm
|
|
18
|
-
: htm;
|
|
12
|
+
htm = htm.replace(/<br>/g, '');
|
|
13
|
+
htm = htm.replace(/<div><\/div>/g, '');
|
|
14
|
+
htm = htm.replace(/<p><\/p>/g, '');
|
|
15
|
+
htm = htm.replace(/<div>/g, '<p>');
|
|
16
|
+
htm = htm.replace(/<\/div>/g, '</p>');
|
|
17
|
+
htm = htm.trim() ? (htm.indexOf('<p>') === -1 ? `<p>${htm}</p>` : htm) : htm;
|
|
19
18
|
return { value: htm.trim(), text };
|
|
20
19
|
};
|
|
21
20
|
export const makeName = function (str) {
|
|
@@ -44,10 +43,10 @@ export const isValidFileSize = (node, maxFileSizeInMB) => {
|
|
|
44
43
|
}
|
|
45
44
|
return '';
|
|
46
45
|
};
|
|
47
|
-
export const bindStateToStore = (state, store) => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
};
|
|
46
|
+
// export const bindStateToStore = (state: Params, store: Writable<Params>): void => {
|
|
47
|
+
// store.subscribe((changes: Params) => {
|
|
48
|
+
// for (const [k, v] of Object.entries(changes)) {
|
|
49
|
+
// state[k] = v;
|
|
50
|
+
// }
|
|
51
|
+
// });
|
|
52
|
+
// };
|
package/package.json
CHANGED
package/dist/form-action.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { type Writable } from "svelte/store";
|
|
2
|
-
import type { FormOptions, FormAction } from "./types.js";
|
|
3
|
-
import type { Params } from "./internal.js";
|
|
4
|
-
export declare const formAction: (values: Writable<Params>, errors: Writable<Params>, unfits: Writable<Params>, isdirty: Writable<boolean>, options: FormOptions, validationMap: Params) => FormAction;
|
package/dist/internal.d.ts
DELETED
package/dist/internal.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|