@steveesamson/microform 0.0.10 → 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/README.md +307 -140
- package/dist/form-action.d.ts +3 -2
- package/dist/form-action.js +41 -8
- package/dist/form-validators.d.ts +23 -2
- package/dist/form-validators.js +135 -162
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -64
- package/dist/index.svelte.js +79 -0
- package/dist/internal.d.ts +3 -2
- package/dist/types.d.ts +25 -10
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +21 -0
- package/package.json +6 -7
package/dist/form-validators.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { get } from "svelte/store";
|
|
2
|
-
import { makeName } from "./utils.js";
|
|
2
|
+
import { makeName, isValidFileSize } from "./utils.js";
|
|
3
3
|
const regexes = {
|
|
4
4
|
number: /^[-+]?[0-9]+(\.[0-9]+)?$/g,
|
|
5
5
|
alpha: /^[A-Z\s]+$/gi,
|
|
@@ -9,175 +9,148 @@ const regexes = {
|
|
|
9
9
|
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
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,
|
|
11
11
|
};
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
12
|
+
export const IS_REQUIRED = "required";
|
|
13
|
+
export const IS_EMAIL = "email";
|
|
14
|
+
export const IS_URL = "url";
|
|
15
|
+
export const IS_IP = "ip";
|
|
16
|
+
export const IS_INTEGER = "integer";
|
|
17
|
+
export const IS_NUMBER = "number";
|
|
18
|
+
export const IS_ALPHA = "alpha";
|
|
19
|
+
export const IS_ALPHANUM = "alphanum";
|
|
20
|
+
export const IS_MIN_LEN = "minlen";
|
|
21
|
+
export const IS_MAX_LEN = "maxlen";
|
|
22
|
+
export const IS_LEN = "len";
|
|
23
|
+
export const IS_MIN = "min";
|
|
24
|
+
export const IS_MAX = "max";
|
|
25
|
+
export const IT_MATCHES = "match";
|
|
26
|
+
export const IS_FILE_SIZE_MB = 'file-size-mb';
|
|
27
|
+
const getDefaultValidators = () => {
|
|
28
|
+
const validators = {
|
|
29
|
+
[IS_REQUIRED]: ({ label, value }) => {
|
|
30
|
+
if (!value || (Array.isArray(value) && !value.length)) {
|
|
31
|
+
return `${label} is mandatory.`;
|
|
32
|
+
}
|
|
33
|
+
return "";
|
|
34
|
+
},
|
|
35
|
+
[IS_EMAIL]: ({ value, label }) => {
|
|
36
|
+
return (!!value && !value.match(regexes['email'])) ? `${label} should be a valid email.` : '';
|
|
37
|
+
},
|
|
38
|
+
[IS_URL]: ({ value, label }) => {
|
|
39
|
+
return !!value && !value.match(regexes['url']) ? `${label} should be a valid URL.` : "";
|
|
40
|
+
},
|
|
41
|
+
[IS_IP]: ({ value, label }) => {
|
|
42
|
+
return !!value && !value.match(regexes['ip']) ? `${label} should be a valid IP.` : "";
|
|
43
|
+
},
|
|
44
|
+
[IS_INTEGER]: ({ value, label }) => {
|
|
45
|
+
return !!value && !value.match(regexes['integer']) ? `${label} must be an integer number.` : "";
|
|
46
|
+
},
|
|
47
|
+
[IS_NUMBER]: ({ value, label }) => {
|
|
48
|
+
return !!value && !value.match(regexes['number']) ? `${label} must be a number.` : "";
|
|
49
|
+
},
|
|
50
|
+
[IS_ALPHA]: ({ value, label }) => {
|
|
51
|
+
return !!value && !value.match(regexes['alpha']) ? `${label} should be a string of alphabets.` : "";
|
|
52
|
+
},
|
|
53
|
+
[IS_ALPHANUM]: ({ value, label }) => {
|
|
54
|
+
return !!value && !value.match(regexes['alphanum']) ? `${label} should be a string of alphanumerics starting with alphabets.` : "";
|
|
55
|
+
},
|
|
56
|
+
[IS_MIN_LEN]: ({ value, label, parts }) => {
|
|
57
|
+
if (!!value) {
|
|
58
|
+
if (!parts || parts.length < 2) {
|
|
59
|
+
return `${label}: min-length validation requires minimum length.`;
|
|
60
|
+
}
|
|
61
|
+
const extra = parts[1].trim();
|
|
62
|
+
return value.length >= parseInt(parts[1], 10) ? "" : `${label} must be at least ${extra} characters long.`;
|
|
63
|
+
}
|
|
64
|
+
return "";
|
|
65
|
+
},
|
|
66
|
+
[IS_MAX_LEN]: ({ value, label, parts }) => {
|
|
67
|
+
if (!!value) {
|
|
68
|
+
if (!parts || parts.length < 2) {
|
|
69
|
+
return `${label}: max-length validation requires maximum length.`;
|
|
70
|
+
}
|
|
71
|
+
const extra = parts[1].trim();
|
|
72
|
+
return value.length <= parseInt(parts[1], 10) ? "" : `${label} must be at most ${extra} characters long.`;
|
|
73
|
+
}
|
|
74
|
+
return "";
|
|
75
|
+
},
|
|
76
|
+
[IS_LEN]: ({ value, label, parts }) => {
|
|
77
|
+
if (!!value) {
|
|
78
|
+
if (!parts || parts.length < 2) {
|
|
79
|
+
return `${label}: length validation requires length.`;
|
|
80
|
+
}
|
|
81
|
+
const extra = parts[1].trim();
|
|
82
|
+
return value.length === parseInt(parts[1], 10) ? "" : `${label} must exactly be ${extra} characters long.`;
|
|
83
|
+
}
|
|
84
|
+
return "";
|
|
85
|
+
},
|
|
86
|
+
[IS_MAX]: ({ value, label, parts }) => {
|
|
87
|
+
if (!!value) {
|
|
88
|
+
if (!parts || parts.length < 2) {
|
|
89
|
+
return `${label}: max validation requires the maximum value.`;
|
|
90
|
+
}
|
|
91
|
+
const extra = parts[1].trim();
|
|
92
|
+
return parseInt(value, 10) <= parseInt(parts[1], 10) ? "" : `${label} must not be greater than ${extra}.`;
|
|
93
|
+
}
|
|
94
|
+
return "";
|
|
95
|
+
},
|
|
96
|
+
[IS_MIN]: ({ value, label, parts }) => {
|
|
97
|
+
if (!!value) {
|
|
98
|
+
if (!parts || parts.length < 2) {
|
|
99
|
+
return `${label}: min validation requires the minimum value.`;
|
|
100
|
+
}
|
|
101
|
+
const extra = parts[1].trim();
|
|
102
|
+
return parseInt(value, 10) >= parseInt(parts[1], 10) ? "" : `${label} must not be less than ${extra}.`;
|
|
103
|
+
}
|
|
104
|
+
return "";
|
|
105
|
+
},
|
|
106
|
+
[IT_MATCHES]: ({ values, value, label, parts }) => {
|
|
107
|
+
if (value) {
|
|
108
|
+
if (!parts || parts.length < 2) {
|
|
109
|
+
return `${label}: match validation requires id of field to match`;
|
|
110
|
+
}
|
|
111
|
+
const partyName = parts[1].trim();
|
|
112
|
+
const partyValue = values[partyName];
|
|
113
|
+
return value === partyValue ? "" : `${label} does not match ${makeName(partyName)}.`;
|
|
114
|
+
}
|
|
115
|
+
return "";
|
|
116
|
+
},
|
|
117
|
+
[IS_FILE_SIZE_MB]: ({ value, node, label, parts }) => {
|
|
118
|
+
if (!!value) {
|
|
119
|
+
if (!parts || parts.length < 2) {
|
|
120
|
+
return `${label}: max file size in MB validation requires maximum file size of mb value.`;
|
|
121
|
+
;
|
|
122
|
+
}
|
|
123
|
+
const extra = parts[1].trim();
|
|
124
|
+
return isValidFileSize(node, parseInt(extra, 10));
|
|
125
|
+
}
|
|
126
|
+
return "";
|
|
61
127
|
}
|
|
62
|
-
}
|
|
63
|
-
return
|
|
128
|
+
};
|
|
129
|
+
return validators;
|
|
64
130
|
};
|
|
65
|
-
export const useValidator = (errors, values) => {
|
|
131
|
+
export const useValidator = (errors, values, validators = getDefaultValidators()) => {
|
|
66
132
|
const setError = (name, error) => {
|
|
67
133
|
errors.update((prev) => {
|
|
68
134
|
return { ...prev, [name]: error };
|
|
69
135
|
});
|
|
70
136
|
};
|
|
71
|
-
return
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
case 'match':
|
|
84
|
-
error = typeDetails.length < 2 ? inputName + ': match validation requires party target' : '';
|
|
85
|
-
setError(name, error);
|
|
86
|
-
if (error)
|
|
87
|
-
break;
|
|
88
|
-
partyName = typeDetails[1].trim();
|
|
89
|
-
partyValue = get(values)[partyName] || '';
|
|
90
|
-
error = !value || value !== partyValue ? getErrorText(inputName, 'match', typeDetails[1]) : '';
|
|
91
|
-
setError(name, error);
|
|
92
|
-
break;
|
|
93
|
-
case 'email':
|
|
94
|
-
error = !value || !value.match(regexes['email']) ? getErrorText(inputName, 'email') : '';
|
|
95
|
-
setError(name, error);
|
|
96
|
-
break;
|
|
97
|
-
case 'url':
|
|
98
|
-
error = !value || !value.match(regexes['url']) ? getErrorText(inputName, 'url') : '';
|
|
99
|
-
setError(name, error);
|
|
137
|
+
return {
|
|
138
|
+
validate: async ({ name, value, validations = [], node = undefined }) => {
|
|
139
|
+
if (validations.length) {
|
|
140
|
+
const _validations = validations.includes('required') ? ['required', ...validations.filter((val) => val !== 'required')] : [...validations];
|
|
141
|
+
for (let i = 0; i < _validations.length; ++i) {
|
|
142
|
+
const validation = _validations[i], parts = validation.split(':'), type = parts[0].trim();
|
|
143
|
+
const validator = validators[type];
|
|
144
|
+
if (!validator)
|
|
145
|
+
return;
|
|
146
|
+
const error = validator({ name, label: makeName(name), value, values: get(values), node, parts });
|
|
147
|
+
setError(name, error ? error : '');
|
|
148
|
+
if (error) {
|
|
100
149
|
break;
|
|
101
|
-
|
|
102
|
-
error = !value || !value.match(regexes['ip']) ? getErrorText(inputName, 'ip') : '';
|
|
103
|
-
setError(name, error);
|
|
104
|
-
break;
|
|
105
|
-
case 'integer':
|
|
106
|
-
error = !value || !value.match(regexes['integer']) ? getErrorText(inputName, 'integer') : '';
|
|
107
|
-
setError(name, error);
|
|
108
|
-
break;
|
|
109
|
-
case 'number':
|
|
110
|
-
error = !value || !value.match(regexes['number']) ? getErrorText(inputName, 'number') : '';
|
|
111
|
-
setError(name, error);
|
|
112
|
-
break;
|
|
113
|
-
case 'alpha':
|
|
114
|
-
error = !value || !value.match(regexes['alpha']) ? getErrorText(inputName, 'alpha') : '';
|
|
115
|
-
setError(name, error);
|
|
116
|
-
break;
|
|
117
|
-
case 'alphanum':
|
|
118
|
-
error =
|
|
119
|
-
!value || !value.match(regexes['alphanum']) ? getErrorText(inputName, 'alphanum') : '';
|
|
120
|
-
setError(name, error);
|
|
121
|
-
break;
|
|
122
|
-
case 'min-length':
|
|
123
|
-
error = typeDetails.length < 2 ? inputName + ': min-length validation requires width' : '';
|
|
124
|
-
setError(name, error);
|
|
125
|
-
if (error)
|
|
126
|
-
break;
|
|
127
|
-
error =
|
|
128
|
-
!value || value.length < parseInt(typeDetails[1], 10)
|
|
129
|
-
? getErrorText(inputName, 'min-length', typeDetails[1])
|
|
130
|
-
: '';
|
|
131
|
-
setError(name, error);
|
|
132
|
-
break;
|
|
133
|
-
case 'max-length':
|
|
134
|
-
error = typeDetails.length < 2 ? inputName + ': max-length validation requires width' : '';
|
|
135
|
-
setError(name, error);
|
|
136
|
-
if (error)
|
|
137
|
-
break;
|
|
138
|
-
error =
|
|
139
|
-
!value || value.length > parseInt(typeDetails[1], 10)
|
|
140
|
-
? getErrorText(inputName, 'max-length', typeDetails[1])
|
|
141
|
-
: '';
|
|
142
|
-
setError(name, error);
|
|
143
|
-
break;
|
|
144
|
-
case 'length':
|
|
145
|
-
error = typeDetails.length < 2 ? inputName + ': length validation requires width' : '';
|
|
146
|
-
setError(name, error);
|
|
147
|
-
if (error)
|
|
148
|
-
break;
|
|
149
|
-
error =
|
|
150
|
-
!value || value.length !== parseInt(typeDetails[1], 10)
|
|
151
|
-
? getErrorText(inputName, 'length', typeDetails[1])
|
|
152
|
-
: '';
|
|
153
|
-
setError(name, error);
|
|
154
|
-
break;
|
|
155
|
-
case 'max':
|
|
156
|
-
error = typeDetails.length < 2 ? inputName + ': max validation requires max value' : '';
|
|
157
|
-
setError(name, error);
|
|
158
|
-
if (error)
|
|
159
|
-
break;
|
|
160
|
-
error =
|
|
161
|
-
!value || parseInt(value, 10) !== parseInt(typeDetails[1], 10)
|
|
162
|
-
? getErrorText(inputName, 'max', typeDetails[1])
|
|
163
|
-
: '';
|
|
164
|
-
setError(name, error);
|
|
165
|
-
break;
|
|
166
|
-
case 'max-file-size-mb':
|
|
167
|
-
error = typeDetails.length < 2 ? inputName + ': max file size in MB validation requires max-file-size-mb value' : '';
|
|
168
|
-
setError(name, error);
|
|
169
|
-
if (error)
|
|
170
|
-
break;
|
|
171
|
-
error = checkFileSize(node, parseInt(typeDetails[1], 10));
|
|
172
|
-
setError(name, error);
|
|
173
|
-
break;
|
|
174
|
-
default:
|
|
175
|
-
}
|
|
176
|
-
if (error) {
|
|
177
|
-
setError(name, error);
|
|
178
|
-
break;
|
|
150
|
+
}
|
|
179
151
|
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
validators
|
|
182
155
|
};
|
|
183
156
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
declare const microForm: (props?: MicroFormProps) => MicroFormReturn;
|
|
1
|
+
import { microForm } from "./index.svelte.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";
|
|
4
3
|
export default microForm;
|
package/dist/index.js
CHANGED
|
@@ -1,65 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { formAction } from './form-action.js';
|
|
4
|
-
const microForm = (props) => {
|
|
5
|
-
// form default values
|
|
6
|
-
const data = props?.data || {};
|
|
7
|
-
// form values
|
|
8
|
-
const values = writable(data);
|
|
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
|
-
});
|
|
23
|
-
const validationMap = {};
|
|
24
|
-
const { options = {
|
|
25
|
-
validateEvent: 'blur'
|
|
26
|
-
} } = props || {};
|
|
27
|
-
const form = formAction(values, errors, unfits, isdirty, options, validationMap);
|
|
28
|
-
const handleSubmit = (e, handler) => {
|
|
29
|
-
e.preventDefault();
|
|
30
|
-
if (!get(valid))
|
|
31
|
-
return;
|
|
32
|
-
handler({ ...get(values) });
|
|
33
|
-
};
|
|
34
|
-
const onsubmit = (handler) => {
|
|
35
|
-
const onSubmit = async (e) => {
|
|
36
|
-
handleSubmit(e, handler);
|
|
37
|
-
};
|
|
38
|
-
return onSubmit;
|
|
39
|
-
};
|
|
40
|
-
const submit = (formNode, handler) => {
|
|
41
|
-
formNode.addEventListener('submit', (e) => {
|
|
42
|
-
handleSubmit(e, handler);
|
|
43
|
-
});
|
|
44
|
-
};
|
|
45
|
-
const reset = () => {
|
|
46
|
-
errors.set({});
|
|
47
|
-
unfits.set({});
|
|
48
|
-
values.set({ ...data });
|
|
49
|
-
for (const [name, { nodeRef, html }] of Object.entries(validationMap).filter(([, { nodeRef }]) => !!nodeRef)) {
|
|
50
|
-
if (nodeRef) {
|
|
51
|
-
nodeRef[html ? "innerHTML" : 'textContent'] = data[name] || '';
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
return {
|
|
56
|
-
values,
|
|
57
|
-
errors,
|
|
58
|
-
valid,
|
|
59
|
-
form,
|
|
60
|
-
submit,
|
|
61
|
-
onsubmit,
|
|
62
|
-
reset,
|
|
63
|
-
};
|
|
64
|
-
};
|
|
1
|
+
import { microForm } from "./index.svelte.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";
|
|
65
3
|
export default microForm;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { writable, derived, get } from 'svelte/store';
|
|
2
|
+
import { formAction } from './form-action.js';
|
|
3
|
+
import { bindStateToStore } from "./utils.js";
|
|
4
|
+
export const microForm = (props) => {
|
|
5
|
+
// form default values
|
|
6
|
+
const data = props?.data || {};
|
|
7
|
+
// form values
|
|
8
|
+
const _values = writable({ ...data });
|
|
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
|
+
});
|
|
23
|
+
const validationMap = {};
|
|
24
|
+
const { options = {
|
|
25
|
+
validateEvent: 'blur',
|
|
26
|
+
validators: {}
|
|
27
|
+
} } = props || {};
|
|
28
|
+
const form = formAction(_values, _errors, unfits, isdirty, options, validationMap);
|
|
29
|
+
const handleSubmit = (e, handler) => {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
if (!get(_valid))
|
|
32
|
+
return;
|
|
33
|
+
handler({ ...get(_values) });
|
|
34
|
+
};
|
|
35
|
+
const onsubmit = (handler) => {
|
|
36
|
+
const onSubmit = async (e) => {
|
|
37
|
+
handleSubmit(e, handler);
|
|
38
|
+
};
|
|
39
|
+
return onSubmit;
|
|
40
|
+
};
|
|
41
|
+
const submit = (formNode, handler) => {
|
|
42
|
+
formNode.addEventListener('submit', (e) => {
|
|
43
|
+
handleSubmit(e, handler);
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
const reset = () => {
|
|
47
|
+
_errors.set({});
|
|
48
|
+
unfits.set({});
|
|
49
|
+
_values.set({ ...data });
|
|
50
|
+
isdirty.set(false);
|
|
51
|
+
for (const [name, { nodeRef, html }] of Object.entries(validationMap).filter(([, { nodeRef }]) => !!nodeRef)) {
|
|
52
|
+
if (nodeRef) {
|
|
53
|
+
if (nodeRef.isContentEditable) {
|
|
54
|
+
nodeRef[html ? "innerHTML" : 'textContent'] = data[name] || '';
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
nodeRef["value"] = data[name] || '';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
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
|
+
return {
|
|
71
|
+
values,
|
|
72
|
+
errors,
|
|
73
|
+
sanity,
|
|
74
|
+
form,
|
|
75
|
+
submit,
|
|
76
|
+
onsubmit,
|
|
77
|
+
reset,
|
|
78
|
+
};
|
|
79
|
+
};
|
package/dist/internal.d.ts
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -1,28 +1,40 @@
|
|
|
1
1
|
/// <reference types="svelte" />
|
|
2
|
-
import { type Writable
|
|
2
|
+
import { type Writable } from "svelte/store";
|
|
3
3
|
import type { Params } from "./internal.js";
|
|
4
|
-
export type {
|
|
5
|
-
export type
|
|
4
|
+
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';
|
|
5
|
+
export type ValidatorKey = 'required' | 'email' | 'integer' | 'number' | 'alpha' | 'alphanum' | 'url' | 'ip' | `max` | `min` | `len` | `minlen` | `maxlen` | `file-size-mb` | `match`;
|
|
6
|
+
export type FieldProps = {
|
|
7
|
+
name: string;
|
|
8
|
+
value: string;
|
|
9
|
+
label: string;
|
|
10
|
+
node?: HTMLElement;
|
|
11
|
+
values: Params;
|
|
12
|
+
parts?: string[];
|
|
13
|
+
};
|
|
14
|
+
export type ValidatorType = (props: FieldProps) => string;
|
|
15
|
+
export type ValidatorMap<T> = {
|
|
16
|
+
[VAL in ValidatorKey]: T;
|
|
17
|
+
};
|
|
6
18
|
export type InputTypes = 'text' | 'number' | 'color' | 'time' | 'date' | 'range' | 'email' | 'hidden' | 'password' | 'tel' | 'url';
|
|
7
19
|
export type FieldType = HTMLSelectElement | HTMLInputElement | HTMLTextAreaElement;
|
|
8
20
|
export type InputType = HTMLSelectElement | HTMLInputElement | HTMLTextAreaElement;
|
|
9
21
|
export interface ValidateArgs {
|
|
10
22
|
name: string;
|
|
11
23
|
value: string;
|
|
12
|
-
validations
|
|
24
|
+
validations?: Validator[];
|
|
13
25
|
node?: HTMLElement;
|
|
14
26
|
}
|
|
15
27
|
export type FormReturn = {
|
|
16
28
|
destroy: () => void;
|
|
17
29
|
};
|
|
18
30
|
export type ValidateEvent = 'input' | 'change' | 'keyup' | 'blur';
|
|
19
|
-
export type FormValues =
|
|
20
|
-
export type FormErrors =
|
|
31
|
+
export type FormValues = Params;
|
|
32
|
+
export type FormErrors = Params;
|
|
21
33
|
export type Dirty = Writable<boolean>;
|
|
22
34
|
export type ActionOptions = {
|
|
23
35
|
validateEvent?: ValidateEvent;
|
|
24
36
|
name?: string;
|
|
25
|
-
validations?:
|
|
37
|
+
validations?: Validator[];
|
|
26
38
|
node?: HTMLElement;
|
|
27
39
|
html?: boolean;
|
|
28
40
|
};
|
|
@@ -32,18 +44,21 @@ export type FormSubmitEvent = SubmitEvent & {
|
|
|
32
44
|
};
|
|
33
45
|
export type FormSubmit = (_data: Params) => void;
|
|
34
46
|
export type FormOptions = {
|
|
35
|
-
validateEvent
|
|
47
|
+
validateEvent?: ValidateEvent;
|
|
48
|
+
validators?: Partial<ValidatorMap<ValidatorType>>;
|
|
36
49
|
};
|
|
37
50
|
export type MicroFormProps = {
|
|
38
51
|
data?: Params;
|
|
39
52
|
options?: FormOptions;
|
|
40
53
|
};
|
|
41
|
-
export type FormSanity =
|
|
54
|
+
export type FormSanity = {
|
|
55
|
+
ok: boolean;
|
|
56
|
+
};
|
|
42
57
|
export type MicroFormReturn = {
|
|
43
58
|
values: FormValues;
|
|
44
59
|
errors: FormErrors;
|
|
60
|
+
sanity: FormSanity;
|
|
45
61
|
form: (node: HTMLElement, eventProps?: ActionOptions) => FormReturn;
|
|
46
|
-
valid: FormSanity;
|
|
47
62
|
submit: (formNode: HTMLFormElement, handler: FormSubmit) => void;
|
|
48
63
|
onsubmit: (handler: FormSubmit) => (e: Event) => Promise<void>;
|
|
49
64
|
reset: () => void;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/// <reference types="svelte" />
|
|
2
|
+
import type { Writable } from "svelte/store";
|
|
3
|
+
import type { Params } from "./internal.js";
|
|
1
4
|
type TEvent = {
|
|
2
5
|
target: HTMLElement;
|
|
3
6
|
};
|
|
@@ -6,4 +9,6 @@ export declare const getEditableContent: (e: TEvent, isHtml: boolean) => {
|
|
|
6
9
|
value: string;
|
|
7
10
|
};
|
|
8
11
|
export declare const makeName: (str: string) => string;
|
|
12
|
+
export declare const isValidFileSize: (node: HTMLInputElement | undefined, maxFileSizeInMB: number) => string;
|
|
13
|
+
export declare const bindStateToStore: (state: Params, store: Writable<Params>) => void;
|
|
9
14
|
export {};
|
package/dist/utils.js
CHANGED
|
@@ -30,3 +30,24 @@ export const makeName = function (str) {
|
|
|
30
30
|
});
|
|
31
31
|
return new_name;
|
|
32
32
|
};
|
|
33
|
+
export const isValidFileSize = (node, maxFileSizeInMB) => {
|
|
34
|
+
if (!node)
|
|
35
|
+
return '';
|
|
36
|
+
const { files } = node;
|
|
37
|
+
if (!files)
|
|
38
|
+
return `${node.name} is required`;
|
|
39
|
+
const max = maxFileSizeInMB * 1024 * 1024;
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
if (file.size > max) {
|
|
42
|
+
return `File '${file.name}' is larger the ${maxFileSizeInMB}MB.`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return '';
|
|
46
|
+
};
|
|
47
|
+
export const bindStateToStore = (state, store) => {
|
|
48
|
+
store.subscribe((changes) => {
|
|
49
|
+
for (const [k, v] of Object.entries(changes)) {
|
|
50
|
+
state[k] = v;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steveesamson/microform",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"postbuild": "touch ./docs/.nojekyll",
|
|
@@ -20,9 +20,8 @@
|
|
|
20
20
|
},
|
|
21
21
|
"exports": {
|
|
22
22
|
".": {
|
|
23
|
-
"types": "./dist/
|
|
24
|
-
"svelte": "./dist/index.js"
|
|
25
|
-
"default": "./dist/index.js"
|
|
23
|
+
"types": "./dist/types.d.ts",
|
|
24
|
+
"svelte": "./dist/index.js"
|
|
26
25
|
}
|
|
27
26
|
},
|
|
28
27
|
"author": "Steve S. Samson <stevee.samson@gmail.com> (http://github.com/steveesamson)",
|
|
@@ -33,7 +32,7 @@
|
|
|
33
32
|
"!dist/**/*.spec.*"
|
|
34
33
|
],
|
|
35
34
|
"peerDependencies": {
|
|
36
|
-
"svelte": "^
|
|
35
|
+
"svelte": "^5.0.0-next.135"
|
|
37
36
|
},
|
|
38
37
|
"devDependencies": {
|
|
39
38
|
"@sveltejs/adapter-auto": "^3.0.0",
|
|
@@ -52,14 +51,14 @@
|
|
|
52
51
|
"prettier-plugin-svelte": "^3.1.2",
|
|
53
52
|
"publint": "^0.1.9",
|
|
54
53
|
"shiki": "^0.14.7",
|
|
55
|
-
"svelte": "^
|
|
54
|
+
"svelte": "^5.0.0-next.135",
|
|
56
55
|
"svelte-check": "^3.6.0",
|
|
57
56
|
"tslib": "^2.4.1",
|
|
58
57
|
"typescript": "^5.0.0",
|
|
59
58
|
"vite": "^5.0.3"
|
|
60
59
|
},
|
|
61
60
|
"default": "./dist/index.js",
|
|
62
|
-
"types": "./dist/
|
|
61
|
+
"types": "./dist/types.d.ts",
|
|
63
62
|
"type": "module",
|
|
64
63
|
"keywords": [
|
|
65
64
|
"microform",
|