foorm 0.0.2 → 0.1.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/dist/index.cjs +124 -100
- package/dist/index.d.ts +117 -85
- package/dist/index.mjs +122 -99
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var deserializeFn = require('@prostojs/deserialize-fn');
|
|
4
|
+
var serializeFn = require('@prostojs/serialize-fn');
|
|
4
5
|
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
function evalParameter(fn, scope, forField) {
|
|
7
|
+
if (typeof fn === 'function') {
|
|
8
|
+
if (fn.__deserialized) {
|
|
9
|
+
return fn(scope);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
const args = (forField
|
|
13
|
+
? [scope.v, scope.data, scope.context, scope.entry]
|
|
14
|
+
: [scope.data, scope.context, scope.entry]);
|
|
15
|
+
return fn(...args);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return fn;
|
|
16
19
|
}
|
|
17
20
|
|
|
21
|
+
/* eslint-disable no-inner-declarations */
|
|
22
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
23
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
18
24
|
class Foorm {
|
|
25
|
+
// private fns!: FNPool<string | boolean, TFoormFnScope>
|
|
19
26
|
constructor(opts) {
|
|
20
27
|
this.entries = (opts === null || opts === void 0 ? void 0 : opts.entries) || [];
|
|
21
28
|
this.submit = opts === null || opts === void 0 ? void 0 : opts.submit;
|
|
@@ -34,55 +41,23 @@ class Foorm {
|
|
|
34
41
|
setContext(context) {
|
|
35
42
|
this.context = context;
|
|
36
43
|
}
|
|
37
|
-
|
|
38
|
-
* Normalizes form metadata and removes all the functions
|
|
39
|
-
* from validators.
|
|
40
|
-
*
|
|
41
|
-
* @param replaceContext a context to be transported along with metadata
|
|
42
|
-
* @returns form metadata without functions
|
|
43
|
-
*/
|
|
44
|
-
transportable(replaceContext, replaceValues) {
|
|
45
|
-
var _a, _b;
|
|
44
|
+
getDefinition() {
|
|
46
45
|
return {
|
|
47
|
-
title:
|
|
48
|
-
submit:
|
|
49
|
-
context:
|
|
50
|
-
entries: this.entries
|
|
46
|
+
title: this.title,
|
|
47
|
+
submit: this.submit || { text: 'Submit' },
|
|
48
|
+
context: this.context,
|
|
49
|
+
entries: this.entries,
|
|
51
50
|
};
|
|
52
51
|
}
|
|
53
52
|
normalizeEntry(e) {
|
|
54
|
-
return Object.assign(Object.assign({}, e), { name: e.name || e.field, label: e.label || e.field, type: e.type || 'text' });
|
|
53
|
+
return Object.assign(Object.assign({}, e), { name: e.name || e.field, label: (e.label || e.field), type: e.type || 'text' });
|
|
55
54
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Evaluates all the ftrings into functions, makes it ready for execution
|
|
58
|
-
*
|
|
59
|
-
* @returns form metadata with functions
|
|
60
|
-
*/
|
|
61
55
|
executable() {
|
|
62
|
-
var _a, _b;
|
|
63
|
-
if (!this.fns) {
|
|
64
|
-
this.fns = new ftring$1.FtringsPool();
|
|
65
|
-
}
|
|
66
56
|
return {
|
|
67
|
-
title:
|
|
68
|
-
submit: {
|
|
69
|
-
text: transformFtrings(((_a = this.submit) === null || _a === void 0 ? void 0 : _a.text) || 'Submit', this.fns),
|
|
70
|
-
disabled: transformFtrings((_b = this.submit) === null || _b === void 0 ? void 0 : _b.disabled, this.fns),
|
|
71
|
-
},
|
|
57
|
+
title: this.title || '',
|
|
58
|
+
submit: this.submit || { text: 'Submit' },
|
|
72
59
|
context: this.context,
|
|
73
|
-
entries: this.entries
|
|
74
|
-
.map(e => this.normalizeEntry(e))
|
|
75
|
-
.map(e => (Object.assign(Object.assign({}, e), {
|
|
76
|
-
// strings
|
|
77
|
-
label: transformFtrings(e.label, this.fns), description: transformFtrings(e.description, this.fns), hint: transformFtrings(e.hint, this.fns), placeholder: transformFtrings(e.placeholder, this.fns),
|
|
78
|
-
// strings || objects
|
|
79
|
-
classes: transformFtringsInObj(e.classes, this.fns), styles: transformFtringsInObj(e.styles, this.fns),
|
|
80
|
-
// booleans
|
|
81
|
-
optional: transformFtrings(e.optional, this.fns), disabled: transformFtrings(e.disabled, this.fns), hidden: transformFtrings(e.hidden, this.fns), validators: this.prepareValidators(e.validators),
|
|
82
|
-
// options
|
|
83
|
-
options: transformFtrings(e.options, this.fns),
|
|
84
|
-
// attrs
|
|
85
|
-
attrs: transformFtringsInObj(e.attrs, this.fns) }))),
|
|
60
|
+
entries: this.entries.map(e => this.normalizeEntry(e)),
|
|
86
61
|
};
|
|
87
62
|
}
|
|
88
63
|
createFormData() {
|
|
@@ -95,27 +70,25 @@ class Foorm {
|
|
|
95
70
|
return data;
|
|
96
71
|
}
|
|
97
72
|
prepareValidators(_validators) {
|
|
98
|
-
const validators =
|
|
99
|
-
validators.unshift(
|
|
73
|
+
const validators = _validators || [];
|
|
74
|
+
validators.unshift((v, _d, _c, entry) => entry.optional || !!v || 'Required');
|
|
100
75
|
return validators;
|
|
101
76
|
}
|
|
102
77
|
supportsAltAction(altAction) {
|
|
103
78
|
return !!this.entries.some(e => e.altAction === altAction);
|
|
104
79
|
}
|
|
105
80
|
getFormValidator() {
|
|
106
|
-
if (!this.fns) {
|
|
107
|
-
this.fns = new ftring$1.FtringsPool();
|
|
108
|
-
}
|
|
109
81
|
const entries = this.executable().entries;
|
|
110
82
|
const fields = {};
|
|
111
83
|
for (const entry of entries) {
|
|
112
84
|
if (entry.field) {
|
|
85
|
+
const validators = this.prepareValidators(entry.validators);
|
|
113
86
|
fields[entry.field] = {
|
|
114
87
|
entry,
|
|
115
|
-
validators
|
|
88
|
+
validators,
|
|
116
89
|
};
|
|
90
|
+
fields[entry.field].validators = validators;
|
|
117
91
|
}
|
|
118
|
-
fields[entry.field].validators.unshift(this.fns.getFn('entry.optional || !!v || "Required"'));
|
|
119
92
|
}
|
|
120
93
|
return (data) => {
|
|
121
94
|
let passed = true;
|
|
@@ -132,27 +105,12 @@ class Foorm {
|
|
|
132
105
|
name: evalEntry.name,
|
|
133
106
|
length: evalEntry.length,
|
|
134
107
|
},
|
|
135
|
-
data,
|
|
108
|
+
data: data,
|
|
136
109
|
};
|
|
137
110
|
if (scope.entry) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
else {
|
|
142
|
-
scope.entry.disabled = evalEntry.disabled;
|
|
143
|
-
}
|
|
144
|
-
if (typeof evalEntry.optional === 'function') {
|
|
145
|
-
scope.entry.optional = evalEntry.optional = evalEntry.optional(scope);
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
scope.entry.optional = evalEntry.optional;
|
|
149
|
-
}
|
|
150
|
-
if (typeof evalEntry.hidden === 'function') {
|
|
151
|
-
scope.entry.hidden = evalEntry.hidden = evalEntry.hidden(scope);
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
scope.entry.hidden = evalEntry.hidden;
|
|
155
|
-
}
|
|
111
|
+
scope.entry.disabled = evalParameter(evalEntry.disabled, scope, true);
|
|
112
|
+
scope.entry.optional = evalParameter(evalEntry.optional, scope, true);
|
|
113
|
+
scope.entry.hidden = evalParameter(evalEntry.hidden, scope, true);
|
|
156
114
|
}
|
|
157
115
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
158
116
|
const result = validate({
|
|
@@ -160,7 +118,7 @@ class Foorm {
|
|
|
160
118
|
context: this.context,
|
|
161
119
|
validators: value.validators,
|
|
162
120
|
entry: scope.entry,
|
|
163
|
-
data,
|
|
121
|
+
data: data,
|
|
164
122
|
});
|
|
165
123
|
if (!result.passed) {
|
|
166
124
|
passed = false;
|
|
@@ -178,12 +136,12 @@ class Foorm {
|
|
|
178
136
|
}
|
|
179
137
|
function validate(opts) {
|
|
180
138
|
for (const validator of opts.validators || []) {
|
|
181
|
-
const result = validator
|
|
139
|
+
const result = evalParameter(validator, {
|
|
182
140
|
v: opts.v,
|
|
183
141
|
context: opts.context,
|
|
184
142
|
data: opts.data,
|
|
185
143
|
entry: opts.entry,
|
|
186
|
-
});
|
|
144
|
+
}, true);
|
|
187
145
|
if (result !== true) {
|
|
188
146
|
return {
|
|
189
147
|
passed: false,
|
|
@@ -193,30 +151,96 @@ function validate(opts) {
|
|
|
193
151
|
}
|
|
194
152
|
return { passed: true };
|
|
195
153
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return
|
|
154
|
+
|
|
155
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
156
|
+
const pool = new deserializeFn.FNPool();
|
|
157
|
+
function deserializeForm(form) {
|
|
158
|
+
return new Foorm({
|
|
159
|
+
title: deserializeComputedFn(form.title),
|
|
160
|
+
context: form.context,
|
|
161
|
+
submit: deserializeComputedFn(form.submit, true),
|
|
162
|
+
entries: form.entries.map(e => (Object.assign(Object.assign({}, e), {
|
|
163
|
+
// strings
|
|
164
|
+
label: deserializeComputedFn(e.label), description: deserializeComputedFn(e.description), hint: deserializeComputedFn(e.hint), placeholder: deserializeComputedFn(e.placeholder),
|
|
165
|
+
// strings || objects
|
|
166
|
+
classes: deserializeComputedFn(e.classes, true), styles: deserializeComputedFn(e.styles, true),
|
|
167
|
+
// booleans
|
|
168
|
+
optional: deserializeComputedFn(e.optional), disabled: deserializeComputedFn(e.disabled), hidden: deserializeComputedFn(e.hidden),
|
|
169
|
+
// options
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
|
171
|
+
options: deserializeComputedFn(e.options),
|
|
172
|
+
// attrs
|
|
173
|
+
attrs: deserializeComputedFn(e.attrs, true),
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
175
|
+
value: e.value, validators: (e.validators || []).map(fn => deserializeComputedFn(fn)) }))),
|
|
176
|
+
});
|
|
201
177
|
}
|
|
202
|
-
function
|
|
203
|
-
if (
|
|
204
|
-
|
|
178
|
+
function deserializeComputedFn(v, asObj) {
|
|
179
|
+
if (typeof v === 'object' && typeof v.__fn__ === 'string') {
|
|
180
|
+
const fn = pool.getFn(v.__fn__);
|
|
181
|
+
fn.__deserialized = true;
|
|
182
|
+
return fn;
|
|
183
|
+
}
|
|
184
|
+
if (typeof v === 'object' && asObj) {
|
|
185
|
+
const o = {};
|
|
186
|
+
for (const [key, val] of Object.entries(v)) {
|
|
187
|
+
o[key] = deserializeComputedFn(val);
|
|
188
|
+
}
|
|
189
|
+
return o;
|
|
205
190
|
}
|
|
206
|
-
|
|
207
|
-
|
|
191
|
+
return v;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
195
|
+
function serializeForm(form, opts) {
|
|
196
|
+
const def = form.getDefinition();
|
|
197
|
+
return {
|
|
198
|
+
title: serializeComputedFn(def.title),
|
|
199
|
+
submit: {
|
|
200
|
+
text: serializeComputedFn(def.submit.text || 'Submit'),
|
|
201
|
+
disabled: serializeComputedFn(def.submit.disabled),
|
|
202
|
+
},
|
|
203
|
+
context: (opts === null || opts === void 0 ? void 0 : opts.replaceContext) || def.context,
|
|
204
|
+
entries: def.entries.map(e => (Object.assign(Object.assign({}, e), {
|
|
205
|
+
// strings
|
|
206
|
+
label: serializeComputedFnWithVal(e.label), description: serializeComputedFnWithVal(e.description), hint: serializeComputedFnWithVal(e.hint), placeholder: serializeComputedFnWithVal(e.placeholder),
|
|
207
|
+
// strings || objects
|
|
208
|
+
classes: serializeComputedFnWithVal(e.classes, true), styles: serializeComputedFnWithVal(e.styles, true),
|
|
209
|
+
// booleans
|
|
210
|
+
optional: serializeComputedFnWithVal(e.optional), disabled: serializeComputedFnWithVal(e.disabled), hidden: serializeComputedFnWithVal(e.hidden),
|
|
211
|
+
// options
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
|
213
|
+
options: serializeComputedFnWithVal(e.options),
|
|
214
|
+
// attrs
|
|
215
|
+
attrs: serializeComputedFnWithVal(e.attrs, true),
|
|
216
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
217
|
+
value: (opts === null || opts === void 0 ? void 0 : opts.replaceValues) ? opts.replaceValues[e.field] : e.value, validators: (e.validators || [])
|
|
218
|
+
.filter(v => serializeFn.isCleanFn(v))
|
|
219
|
+
.map(fn => serializeComputedFnWithVal(fn)) }))),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function serializeComputedFn(fn) {
|
|
223
|
+
if (typeof fn === 'function') {
|
|
224
|
+
return { __fn__: serializeFn.serializeFn(fn, 'data', 'context') };
|
|
208
225
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
226
|
+
return fn;
|
|
227
|
+
}
|
|
228
|
+
function serializeComputedFnWithVal(fn, inObj) {
|
|
229
|
+
if (inObj && typeof fn === 'object') {
|
|
230
|
+
const o = {};
|
|
231
|
+
for (const [key, val] of Object.entries(fn)) {
|
|
232
|
+
o[key] = serializeComputedFnWithVal(val);
|
|
213
233
|
}
|
|
214
|
-
return
|
|
234
|
+
return o;
|
|
235
|
+
}
|
|
236
|
+
if (typeof fn === 'function') {
|
|
237
|
+
return { __fn__: serializeFn.serializeFn(fn, 'v', 'data', 'context', 'entry') };
|
|
215
238
|
}
|
|
216
|
-
return
|
|
239
|
+
return fn;
|
|
217
240
|
}
|
|
218
241
|
|
|
219
242
|
exports.Foorm = Foorm;
|
|
220
|
-
exports.
|
|
221
|
-
exports.
|
|
243
|
+
exports.deserializeForm = deserializeForm;
|
|
244
|
+
exports.evalParameter = evalParameter;
|
|
245
|
+
exports.serializeForm = serializeForm;
|
|
222
246
|
exports.validate = validate;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,117 +1,143 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
type TFoormFnTop<OF, D, C> = (data: D, ctx: C) => OF;
|
|
2
|
+
type TFoormFnField<OF, V, D, C> = (v: V, data: D, ctx: C, entry: TFoormEntryEvaluated) => OF;
|
|
3
|
+
interface TFoormFnSerializedTop<OF, D, C> {
|
|
4
|
+
(ctx: TFoormFnScope<undefined, D, C>): OF;
|
|
5
|
+
__deserialized?: boolean;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
interface TFoormFnSerializedField<OF, V, D, C> {
|
|
8
|
+
(ctx: TFoormFnScope<V, D, C>): OF;
|
|
9
|
+
__deserialized?: boolean;
|
|
10
|
+
}
|
|
11
|
+
type TComputed<OF, D, C> = OF | TFoormFnTop<OF, D, C> | TFoormFnSerializedTop<OF, D, C>;
|
|
12
|
+
type TComputedWithVal<OF, V, D, C> = OF | TFoormFnField<OF, V, D, C> | TFoormFnSerializedField<OF, V, D, C>;
|
|
13
|
+
interface TFoormFnScope<V = string, D = Record<string, unknown>, C = Record<string, unknown>> {
|
|
14
|
+
v?: V;
|
|
15
|
+
data: D;
|
|
16
|
+
context: C;
|
|
17
|
+
entry?: TFoormEntryEvaluated;
|
|
17
18
|
action?: string;
|
|
18
19
|
}
|
|
19
|
-
type TFoormValidatorFn<
|
|
20
|
-
type TFoormFn<T = string, R = string | boolean> = (ctx: TFoormFnScope<T>) => R;
|
|
21
|
-
type TRelevantFields = 'field' | 'type' | 'component' | 'name' | 'attrs' | 'length';
|
|
20
|
+
type TFoormValidatorFn<V, D, C> = Exclude<Exclude<TComputedWithVal<boolean | string, V, D, C>, boolean>, string>;
|
|
22
21
|
type TFoormEntryOptions = {
|
|
23
22
|
key: string;
|
|
24
23
|
label: string;
|
|
25
24
|
} | string;
|
|
26
|
-
interface TFoormEntry<
|
|
25
|
+
interface TFoormEntry<V, D, C, O extends TFoormEntryOptions> {
|
|
27
26
|
field: string;
|
|
28
27
|
altAction?: string;
|
|
29
|
-
label?: string
|
|
30
|
-
description?: string
|
|
31
|
-
hint?: string
|
|
32
|
-
placeholder?: string
|
|
33
|
-
classes?:
|
|
34
|
-
styles?:
|
|
28
|
+
label?: TComputedWithVal<string, V, D, C>;
|
|
29
|
+
description?: TComputedWithVal<string, V, D, C>;
|
|
30
|
+
hint?: TComputedWithVal<string, V, D, C>;
|
|
31
|
+
placeholder?: TComputedWithVal<string, V, D, C>;
|
|
32
|
+
classes?: TComputedWithVal<string, V, D, C> | Record<string, TComputedWithVal<boolean, V, D, C>>;
|
|
33
|
+
styles?: TComputedWithVal<string, V, D, C> | Record<string, TComputedWithVal<string, V, D, C>>;
|
|
35
34
|
type?: string;
|
|
36
35
|
component?: string;
|
|
37
36
|
autocomplete?: string;
|
|
38
37
|
name?: string;
|
|
39
|
-
value?:
|
|
40
|
-
options?: O[] |
|
|
41
|
-
attrs?: Record<string,
|
|
42
|
-
optional?: boolean
|
|
43
|
-
disabled?: boolean
|
|
44
|
-
hidden?: boolean
|
|
38
|
+
value?: V;
|
|
39
|
+
options?: O[] | TComputedWithVal<O[], V, D, C>;
|
|
40
|
+
attrs?: Record<string, TComputedWithVal<any, V, D, C>>;
|
|
41
|
+
optional?: TComputedWithVal<boolean, V, D, C>;
|
|
42
|
+
disabled?: TComputedWithVal<boolean, V, D, C>;
|
|
43
|
+
hidden?: TComputedWithVal<boolean, V, D, C>;
|
|
45
44
|
length?: number;
|
|
46
|
-
validators?: Array<
|
|
45
|
+
validators?: Array<TFoormValidatorFn<V, D, C>>;
|
|
47
46
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
label: string | TFoormFn<T, string>;
|
|
51
|
-
type: string;
|
|
52
|
-
};
|
|
53
|
-
interface TFoormMetaExecutable {
|
|
54
|
-
title: string | TFoormFn<undefined, string>;
|
|
47
|
+
interface TFoormMetaExecutable<D, C> {
|
|
48
|
+
title: TComputed<string, D, C>;
|
|
55
49
|
submit: {
|
|
56
|
-
text: string
|
|
57
|
-
disabled
|
|
50
|
+
text: TComputed<string, D, C>;
|
|
51
|
+
disabled?: TComputed<boolean, D, C>;
|
|
58
52
|
};
|
|
59
|
-
context:
|
|
60
|
-
entries:
|
|
53
|
+
context: C;
|
|
54
|
+
entries: Array<TFoormEntry<any, D, C, TFoormEntryOptions>>;
|
|
61
55
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
interface TSerializedFn {
|
|
57
|
+
__fn__: string;
|
|
58
|
+
}
|
|
59
|
+
interface TFoormSerialized<V = any, O = any> {
|
|
60
|
+
title?: string | TSerializedFn;
|
|
61
|
+
submit: {
|
|
62
|
+
text: string | TSerializedFn;
|
|
63
|
+
disabled: boolean | TSerializedFn;
|
|
64
|
+
};
|
|
65
|
+
context: any;
|
|
66
|
+
entries: Array<{
|
|
67
|
+
field: string;
|
|
68
|
+
altAction?: string;
|
|
69
|
+
label?: string | TSerializedFn;
|
|
70
|
+
description?: string | TSerializedFn;
|
|
71
|
+
hint?: string | TSerializedFn;
|
|
72
|
+
placeholder?: string | TSerializedFn;
|
|
73
|
+
classes?: string | TSerializedFn | Record<string, TSerializedFn>;
|
|
74
|
+
styles?: string | TSerializedFn | Record<string, string | TSerializedFn>;
|
|
75
|
+
type?: string;
|
|
76
|
+
component?: string;
|
|
77
|
+
autocomplete?: string;
|
|
78
|
+
name?: string;
|
|
79
|
+
value?: V;
|
|
80
|
+
options?: O[] | TSerializedFn;
|
|
81
|
+
attrs?: Record<string, string | TSerializedFn>;
|
|
82
|
+
optional?: TSerializedFn;
|
|
83
|
+
disabled?: TSerializedFn;
|
|
84
|
+
hidden?: TSerializedFn;
|
|
85
|
+
length?: number;
|
|
86
|
+
validators?: TSerializedFn[];
|
|
87
|
+
}>;
|
|
66
88
|
}
|
|
67
|
-
interface
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
89
|
+
interface TFoormEntryEvaluated {
|
|
90
|
+
field: string;
|
|
91
|
+
type: string;
|
|
92
|
+
component?: string;
|
|
93
|
+
name: string;
|
|
94
|
+
length?: number;
|
|
95
|
+
disabled?: boolean;
|
|
96
|
+
optional?: boolean;
|
|
97
|
+
hidden?: boolean;
|
|
72
98
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
|
|
100
|
+
interface TFoormOptions<D, C> {
|
|
101
|
+
title?: TComputed<string, D, C>;
|
|
102
|
+
entries: Array<TFoormEntry<any, D, C, TFoormEntryOptions>>;
|
|
103
|
+
submit?: TFoormMetaExecutable<D, C>['submit'];
|
|
104
|
+
context?: C;
|
|
105
|
+
}
|
|
106
|
+
declare class Foorm<D = any, C = any> {
|
|
107
|
+
protected entries: Array<TFoormEntry<any, D, C, TFoormEntryOptions>>;
|
|
108
|
+
protected submit?: TFoormMetaExecutable<D, C>['submit'];
|
|
109
|
+
protected title?: TComputed<string, D, C>;
|
|
110
|
+
protected context: C;
|
|
111
|
+
constructor(opts?: TFoormOptions<D, C>);
|
|
112
|
+
addEntry<V>(entry: TFoormEntry<V, D, C, TFoormEntryOptions>): void;
|
|
81
113
|
setTitle(title: string): void;
|
|
82
|
-
setSubmit(submit:
|
|
83
|
-
setContext
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
context?: Record<string, unknown>;
|
|
114
|
+
setSubmit(submit: TFoormMetaExecutable<D, C>['submit']): void;
|
|
115
|
+
setContext(context: C): void;
|
|
116
|
+
getDefinition(): {
|
|
117
|
+
title: TComputed<string, D, C> | undefined;
|
|
118
|
+
submit: {
|
|
119
|
+
text: TComputed<string, D, C>;
|
|
120
|
+
disabled?: TComputed<boolean, D, C> | undefined;
|
|
121
|
+
};
|
|
122
|
+
context: C;
|
|
123
|
+
entries: TFoormEntry<any, D, C, TFoormEntryOptions>[];
|
|
93
124
|
};
|
|
94
|
-
protected normalizeEntry<
|
|
125
|
+
protected normalizeEntry<V, O extends TFoormEntryOptions>(e: TFoormEntry<V, D, C, O>): TFoormEntry<V, D, C, O> & {
|
|
95
126
|
name: string;
|
|
96
|
-
label: string
|
|
127
|
+
label: TComputed<string, D, C>;
|
|
97
128
|
type: string;
|
|
98
129
|
};
|
|
99
|
-
|
|
100
|
-
* Evaluates all the ftrings into functions, makes it ready for execution
|
|
101
|
-
*
|
|
102
|
-
* @returns form metadata with functions
|
|
103
|
-
*/
|
|
104
|
-
executable(): TFoormMetaExecutable;
|
|
130
|
+
executable(): TFoormMetaExecutable<D, C>;
|
|
105
131
|
createFormData<T extends Record<string, unknown>>(): T;
|
|
106
|
-
prepareValidators(_validators: TFoormEntry['validators']): TFoormValidatorFn<
|
|
132
|
+
prepareValidators(_validators: TFoormEntry<any, D, C, TFoormEntryOptions>['validators']): TFoormValidatorFn<any, D, C>[];
|
|
107
133
|
supportsAltAction(altAction: string): boolean;
|
|
108
134
|
getFormValidator(): (inputs: Record<string, unknown>) => {
|
|
109
135
|
passed: boolean;
|
|
110
136
|
errors: Record<string, string>;
|
|
111
137
|
};
|
|
112
138
|
}
|
|
113
|
-
declare function validate<
|
|
114
|
-
validators:
|
|
139
|
+
declare function validate<V, D, C>(opts: TFoormFnScope<V, D, C> & {
|
|
140
|
+
validators: TFoormEntry<any, D, C, TFoormEntryOptions>['validators'];
|
|
115
141
|
}): {
|
|
116
142
|
passed: boolean;
|
|
117
143
|
error: string;
|
|
@@ -120,7 +146,13 @@ declare function validate<T = string>(opts: TFoormFnScope<T> & {
|
|
|
120
146
|
error?: undefined;
|
|
121
147
|
};
|
|
122
148
|
|
|
123
|
-
declare function
|
|
124
|
-
|
|
149
|
+
declare function deserializeForm<D = unknown, C = unknown>(form: TFoormSerialized): Foorm<D, C>;
|
|
150
|
+
|
|
151
|
+
declare function serializeForm<D, C>(form: Foorm<D, C>, opts?: {
|
|
152
|
+
replaceContext?: C;
|
|
153
|
+
replaceValues?: Record<string, unknown>;
|
|
154
|
+
}): TFoormSerialized;
|
|
155
|
+
|
|
156
|
+
declare function evalParameter<OF>(fn: TComputed<OF, any, any> | TComputedWithVal<OF, any, any, any>, scope: TFoormFnScope<any, any, any>, forField?: boolean): OF | undefined;
|
|
125
157
|
|
|
126
|
-
export { Foorm, type
|
|
158
|
+
export { Foorm, type TComputed, type TComputedWithVal, type TFoormEntry, type TFoormEntryEvaluated, type TFoormEntryOptions, type TFoormFnField, type TFoormFnScope, type TFoormFnSerializedField, type TFoormFnSerializedTop, type TFoormFnTop, type TFoormMetaExecutable, type TFoormOptions, type TFoormSerialized, type TFoormValidatorFn, type TSerializedFn, deserializeForm, evalParameter, serializeForm, validate };
|
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FNPool } from '@prostojs/deserialize-fn';
|
|
2
|
+
import { isCleanFn, serializeFn } from '@prostojs/serialize-fn';
|
|
2
3
|
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
function evalParameter(fn, scope, forField) {
|
|
5
|
+
if (typeof fn === 'function') {
|
|
6
|
+
if (fn.__deserialized) {
|
|
7
|
+
return fn(scope);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
const args = (forField
|
|
11
|
+
? [scope.v, scope.data, scope.context, scope.entry]
|
|
12
|
+
: [scope.data, scope.context, scope.entry]);
|
|
13
|
+
return fn(...args);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return fn;
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
/* eslint-disable no-inner-declarations */
|
|
20
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
21
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
16
22
|
class Foorm {
|
|
23
|
+
// private fns!: FNPool<string | boolean, TFoormFnScope>
|
|
17
24
|
constructor(opts) {
|
|
18
25
|
this.entries = (opts === null || opts === void 0 ? void 0 : opts.entries) || [];
|
|
19
26
|
this.submit = opts === null || opts === void 0 ? void 0 : opts.submit;
|
|
@@ -32,55 +39,23 @@ class Foorm {
|
|
|
32
39
|
setContext(context) {
|
|
33
40
|
this.context = context;
|
|
34
41
|
}
|
|
35
|
-
|
|
36
|
-
* Normalizes form metadata and removes all the functions
|
|
37
|
-
* from validators.
|
|
38
|
-
*
|
|
39
|
-
* @param replaceContext a context to be transported along with metadata
|
|
40
|
-
* @returns form metadata without functions
|
|
41
|
-
*/
|
|
42
|
-
transportable(replaceContext, replaceValues) {
|
|
43
|
-
var _a, _b;
|
|
42
|
+
getDefinition() {
|
|
44
43
|
return {
|
|
45
|
-
title:
|
|
46
|
-
submit:
|
|
47
|
-
context:
|
|
48
|
-
entries: this.entries
|
|
44
|
+
title: this.title,
|
|
45
|
+
submit: this.submit || { text: 'Submit' },
|
|
46
|
+
context: this.context,
|
|
47
|
+
entries: this.entries,
|
|
49
48
|
};
|
|
50
49
|
}
|
|
51
50
|
normalizeEntry(e) {
|
|
52
|
-
return Object.assign(Object.assign({}, e), { name: e.name || e.field, label: e.label || e.field, type: e.type || 'text' });
|
|
51
|
+
return Object.assign(Object.assign({}, e), { name: e.name || e.field, label: (e.label || e.field), type: e.type || 'text' });
|
|
53
52
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Evaluates all the ftrings into functions, makes it ready for execution
|
|
56
|
-
*
|
|
57
|
-
* @returns form metadata with functions
|
|
58
|
-
*/
|
|
59
53
|
executable() {
|
|
60
|
-
var _a, _b;
|
|
61
|
-
if (!this.fns) {
|
|
62
|
-
this.fns = new FtringsPool();
|
|
63
|
-
}
|
|
64
54
|
return {
|
|
65
|
-
title:
|
|
66
|
-
submit: {
|
|
67
|
-
text: transformFtrings(((_a = this.submit) === null || _a === void 0 ? void 0 : _a.text) || 'Submit', this.fns),
|
|
68
|
-
disabled: transformFtrings((_b = this.submit) === null || _b === void 0 ? void 0 : _b.disabled, this.fns),
|
|
69
|
-
},
|
|
55
|
+
title: this.title || '',
|
|
56
|
+
submit: this.submit || { text: 'Submit' },
|
|
70
57
|
context: this.context,
|
|
71
|
-
entries: this.entries
|
|
72
|
-
.map(e => this.normalizeEntry(e))
|
|
73
|
-
.map(e => (Object.assign(Object.assign({}, e), {
|
|
74
|
-
// strings
|
|
75
|
-
label: transformFtrings(e.label, this.fns), description: transformFtrings(e.description, this.fns), hint: transformFtrings(e.hint, this.fns), placeholder: transformFtrings(e.placeholder, this.fns),
|
|
76
|
-
// strings || objects
|
|
77
|
-
classes: transformFtringsInObj(e.classes, this.fns), styles: transformFtringsInObj(e.styles, this.fns),
|
|
78
|
-
// booleans
|
|
79
|
-
optional: transformFtrings(e.optional, this.fns), disabled: transformFtrings(e.disabled, this.fns), hidden: transformFtrings(e.hidden, this.fns), validators: this.prepareValidators(e.validators),
|
|
80
|
-
// options
|
|
81
|
-
options: transformFtrings(e.options, this.fns),
|
|
82
|
-
// attrs
|
|
83
|
-
attrs: transformFtringsInObj(e.attrs, this.fns) }))),
|
|
58
|
+
entries: this.entries.map(e => this.normalizeEntry(e)),
|
|
84
59
|
};
|
|
85
60
|
}
|
|
86
61
|
createFormData() {
|
|
@@ -93,27 +68,25 @@ class Foorm {
|
|
|
93
68
|
return data;
|
|
94
69
|
}
|
|
95
70
|
prepareValidators(_validators) {
|
|
96
|
-
const validators =
|
|
97
|
-
validators.unshift(
|
|
71
|
+
const validators = _validators || [];
|
|
72
|
+
validators.unshift((v, _d, _c, entry) => entry.optional || !!v || 'Required');
|
|
98
73
|
return validators;
|
|
99
74
|
}
|
|
100
75
|
supportsAltAction(altAction) {
|
|
101
76
|
return !!this.entries.some(e => e.altAction === altAction);
|
|
102
77
|
}
|
|
103
78
|
getFormValidator() {
|
|
104
|
-
if (!this.fns) {
|
|
105
|
-
this.fns = new FtringsPool();
|
|
106
|
-
}
|
|
107
79
|
const entries = this.executable().entries;
|
|
108
80
|
const fields = {};
|
|
109
81
|
for (const entry of entries) {
|
|
110
82
|
if (entry.field) {
|
|
83
|
+
const validators = this.prepareValidators(entry.validators);
|
|
111
84
|
fields[entry.field] = {
|
|
112
85
|
entry,
|
|
113
|
-
validators
|
|
86
|
+
validators,
|
|
114
87
|
};
|
|
88
|
+
fields[entry.field].validators = validators;
|
|
115
89
|
}
|
|
116
|
-
fields[entry.field].validators.unshift(this.fns.getFn('entry.optional || !!v || "Required"'));
|
|
117
90
|
}
|
|
118
91
|
return (data) => {
|
|
119
92
|
let passed = true;
|
|
@@ -130,27 +103,12 @@ class Foorm {
|
|
|
130
103
|
name: evalEntry.name,
|
|
131
104
|
length: evalEntry.length,
|
|
132
105
|
},
|
|
133
|
-
data,
|
|
106
|
+
data: data,
|
|
134
107
|
};
|
|
135
108
|
if (scope.entry) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
else {
|
|
140
|
-
scope.entry.disabled = evalEntry.disabled;
|
|
141
|
-
}
|
|
142
|
-
if (typeof evalEntry.optional === 'function') {
|
|
143
|
-
scope.entry.optional = evalEntry.optional = evalEntry.optional(scope);
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
scope.entry.optional = evalEntry.optional;
|
|
147
|
-
}
|
|
148
|
-
if (typeof evalEntry.hidden === 'function') {
|
|
149
|
-
scope.entry.hidden = evalEntry.hidden = evalEntry.hidden(scope);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
scope.entry.hidden = evalEntry.hidden;
|
|
153
|
-
}
|
|
109
|
+
scope.entry.disabled = evalParameter(evalEntry.disabled, scope, true);
|
|
110
|
+
scope.entry.optional = evalParameter(evalEntry.optional, scope, true);
|
|
111
|
+
scope.entry.hidden = evalParameter(evalEntry.hidden, scope, true);
|
|
154
112
|
}
|
|
155
113
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
114
|
const result = validate({
|
|
@@ -158,7 +116,7 @@ class Foorm {
|
|
|
158
116
|
context: this.context,
|
|
159
117
|
validators: value.validators,
|
|
160
118
|
entry: scope.entry,
|
|
161
|
-
data,
|
|
119
|
+
data: data,
|
|
162
120
|
});
|
|
163
121
|
if (!result.passed) {
|
|
164
122
|
passed = false;
|
|
@@ -176,12 +134,12 @@ class Foorm {
|
|
|
176
134
|
}
|
|
177
135
|
function validate(opts) {
|
|
178
136
|
for (const validator of opts.validators || []) {
|
|
179
|
-
const result = validator
|
|
137
|
+
const result = evalParameter(validator, {
|
|
180
138
|
v: opts.v,
|
|
181
139
|
context: opts.context,
|
|
182
140
|
data: opts.data,
|
|
183
141
|
entry: opts.entry,
|
|
184
|
-
});
|
|
142
|
+
}, true);
|
|
185
143
|
if (result !== true) {
|
|
186
144
|
return {
|
|
187
145
|
passed: false,
|
|
@@ -191,27 +149,92 @@ function validate(opts) {
|
|
|
191
149
|
}
|
|
192
150
|
return { passed: true };
|
|
193
151
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
return
|
|
152
|
+
|
|
153
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
154
|
+
const pool = new FNPool();
|
|
155
|
+
function deserializeForm(form) {
|
|
156
|
+
return new Foorm({
|
|
157
|
+
title: deserializeComputedFn(form.title),
|
|
158
|
+
context: form.context,
|
|
159
|
+
submit: deserializeComputedFn(form.submit, true),
|
|
160
|
+
entries: form.entries.map(e => (Object.assign(Object.assign({}, e), {
|
|
161
|
+
// strings
|
|
162
|
+
label: deserializeComputedFn(e.label), description: deserializeComputedFn(e.description), hint: deserializeComputedFn(e.hint), placeholder: deserializeComputedFn(e.placeholder),
|
|
163
|
+
// strings || objects
|
|
164
|
+
classes: deserializeComputedFn(e.classes, true), styles: deserializeComputedFn(e.styles, true),
|
|
165
|
+
// booleans
|
|
166
|
+
optional: deserializeComputedFn(e.optional), disabled: deserializeComputedFn(e.disabled), hidden: deserializeComputedFn(e.hidden),
|
|
167
|
+
// options
|
|
168
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
|
169
|
+
options: deserializeComputedFn(e.options),
|
|
170
|
+
// attrs
|
|
171
|
+
attrs: deserializeComputedFn(e.attrs, true),
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
173
|
+
value: e.value, validators: (e.validators || []).map(fn => deserializeComputedFn(fn)) }))),
|
|
174
|
+
});
|
|
199
175
|
}
|
|
200
|
-
function
|
|
201
|
-
if (
|
|
202
|
-
|
|
176
|
+
function deserializeComputedFn(v, asObj) {
|
|
177
|
+
if (typeof v === 'object' && typeof v.__fn__ === 'string') {
|
|
178
|
+
const fn = pool.getFn(v.__fn__);
|
|
179
|
+
fn.__deserialized = true;
|
|
180
|
+
return fn;
|
|
181
|
+
}
|
|
182
|
+
if (typeof v === 'object' && asObj) {
|
|
183
|
+
const o = {};
|
|
184
|
+
for (const [key, val] of Object.entries(v)) {
|
|
185
|
+
o[key] = deserializeComputedFn(val);
|
|
186
|
+
}
|
|
187
|
+
return o;
|
|
203
188
|
}
|
|
204
|
-
|
|
205
|
-
|
|
189
|
+
return v;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
193
|
+
function serializeForm(form, opts) {
|
|
194
|
+
const def = form.getDefinition();
|
|
195
|
+
return {
|
|
196
|
+
title: serializeComputedFn(def.title),
|
|
197
|
+
submit: {
|
|
198
|
+
text: serializeComputedFn(def.submit.text || 'Submit'),
|
|
199
|
+
disabled: serializeComputedFn(def.submit.disabled),
|
|
200
|
+
},
|
|
201
|
+
context: (opts === null || opts === void 0 ? void 0 : opts.replaceContext) || def.context,
|
|
202
|
+
entries: def.entries.map(e => (Object.assign(Object.assign({}, e), {
|
|
203
|
+
// strings
|
|
204
|
+
label: serializeComputedFnWithVal(e.label), description: serializeComputedFnWithVal(e.description), hint: serializeComputedFnWithVal(e.hint), placeholder: serializeComputedFnWithVal(e.placeholder),
|
|
205
|
+
// strings || objects
|
|
206
|
+
classes: serializeComputedFnWithVal(e.classes, true), styles: serializeComputedFnWithVal(e.styles, true),
|
|
207
|
+
// booleans
|
|
208
|
+
optional: serializeComputedFnWithVal(e.optional), disabled: serializeComputedFnWithVal(e.disabled), hidden: serializeComputedFnWithVal(e.hidden),
|
|
209
|
+
// options
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
|
211
|
+
options: serializeComputedFnWithVal(e.options),
|
|
212
|
+
// attrs
|
|
213
|
+
attrs: serializeComputedFnWithVal(e.attrs, true),
|
|
214
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
215
|
+
value: (opts === null || opts === void 0 ? void 0 : opts.replaceValues) ? opts.replaceValues[e.field] : e.value, validators: (e.validators || [])
|
|
216
|
+
.filter(v => isCleanFn(v))
|
|
217
|
+
.map(fn => serializeComputedFnWithVal(fn)) }))),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function serializeComputedFn(fn) {
|
|
221
|
+
if (typeof fn === 'function') {
|
|
222
|
+
return { __fn__: serializeFn(fn, 'data', 'context') };
|
|
206
223
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
224
|
+
return fn;
|
|
225
|
+
}
|
|
226
|
+
function serializeComputedFnWithVal(fn, inObj) {
|
|
227
|
+
if (inObj && typeof fn === 'object') {
|
|
228
|
+
const o = {};
|
|
229
|
+
for (const [key, val] of Object.entries(fn)) {
|
|
230
|
+
o[key] = serializeComputedFnWithVal(val);
|
|
211
231
|
}
|
|
212
|
-
return
|
|
232
|
+
return o;
|
|
233
|
+
}
|
|
234
|
+
if (typeof fn === 'function') {
|
|
235
|
+
return { __fn__: serializeFn(fn, 'v', 'data', 'context', 'entry') };
|
|
213
236
|
}
|
|
214
|
-
return
|
|
237
|
+
return fn;
|
|
215
238
|
}
|
|
216
239
|
|
|
217
|
-
export { Foorm,
|
|
240
|
+
export { Foorm, deserializeForm, evalParameter, serializeForm, validate };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foorm",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "foorm",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/foormjs/foormjs/tree/main/packages/foorm#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@prostojs/
|
|
38
|
+
"@prostojs/serialize-fn": "^0.0.5",
|
|
39
|
+
"@prostojs/deserialize-fn": "^0.0.5"
|
|
39
40
|
}
|
|
40
41
|
}
|