@sealcode/sealgen 0.19.4 → 0.19.6
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/@types/forms/fields/field.d.ts +1 -0
- package/@types/forms/fields/fields.d.ts +2 -1
- package/@types/forms/fields/get-field-for-sealious.d.ts +3 -1
- package/@types/forms/fields/hybrid.d.ts +11 -0
- package/@types/forms/fields/item-draft.d.ts +20 -0
- package/@types/forms/form.d.ts +1 -0
- package/@types/forms/multiform.d.ts +2 -2
- package/@types/page/list.d.ts +5 -4
- package/@types/page/mountable-with-fields.d.ts +9 -2
- package/@types/page/mountable.d.ts +1 -1
- package/@types/page/page.d.ts +1 -1
- package/@types/templates/form.d.ts +2 -2
- package/lib/forms/controls/checkboxed-list-input.test.js +16 -12
- package/lib/forms/controls/file.test.js +2 -2
- package/lib/forms/controls/image.test.js +4 -4
- package/lib/forms/controls/single-reference.test.js +2 -2
- package/lib/forms/fields/field.js +1 -1
- package/lib/forms/fields/field.test.js +9 -6
- package/lib/forms/fields/fields.js +2 -1
- package/lib/forms/fields/file.test.js +2 -2
- package/lib/forms/fields/get-field-for-sealious.js +14 -0
- package/lib/forms/fields/hybrid.js +37 -0
- package/lib/forms/fields/item-draft.js +34 -0
- package/lib/forms/form.js +11 -5
- package/lib/forms/form.test.js +9 -9
- package/lib/forms/multiform.js +7 -4
- package/lib/forms/multiform.test.js +7 -7
- package/lib/mount.js +4 -1
- package/lib/page/list.js +9 -6
- package/lib/page/mountable-with-fields.js +16 -1
- package/lib/page/mountable-with-fields.test.js +2 -2
- package/lib/page/mountable.js +1 -1
- package/lib/page/page.js +1 -1
- package/lib/templates/form.js +9 -18
- package/lib/templates/shared/collection-create-form.js +8 -8
- package/lib/templates/shared/collection-edit-form.js +9 -9
- package/lib/templates/shared/collection-list.js +2 -1
- package/lib/templates/shared/shared-crud-form-fields.js +4 -4
- package/lib/utils/extract-fields-from-collection.js +3 -0
- package/package.json +1 -1
- package/src/forms/controls/checkboxed-list-input.test.ts +16 -12
- package/src/forms/controls/file.test.ts +2 -2
- package/src/forms/controls/image.test.ts +4 -4
- package/src/forms/controls/single-reference.test.ts +2 -2
- package/src/forms/fields/field.test.ts +9 -6
- package/src/forms/fields/field.ts +6 -7
- package/src/forms/fields/fields.ts +2 -1
- package/src/forms/fields/file.test.ts +2 -2
- package/src/forms/fields/get-field-for-sealious.ts +66 -100
- package/src/forms/fields/hybrid.ts +47 -0
- package/src/forms/fields/item-draft.ts +44 -0
- package/src/forms/form.test.ts +15 -15
- package/src/forms/form.ts +34 -26
- package/src/forms/multiform.test.ts +7 -7
- package/src/forms/multiform.ts +10 -7
- package/src/mount.ts +11 -7
- package/src/page/list.ts +33 -28
- package/src/page/mountable-with-fields.test.ts +2 -2
- package/src/page/mountable-with-fields.ts +26 -5
- package/src/page/mountable.ts +1 -1
- package/src/page/page.ts +1 -1
- package/src/templates/form.ts +9 -18
- package/src/templates/shared/collection-create-form.ts +8 -8
- package/src/templates/shared/collection-edit-form.ts +9 -9
- package/src/templates/shared/collection-list.ts +5 -4
- package/src/templates/shared/shared-crud-form-fields.ts +5 -5
- package/src/utils/extract-fields-from-collection.ts +3 -0
|
@@ -37,107 +37,61 @@ import extract_fields_from_collection, {
|
|
|
37
37
|
import { EnumMultipleSealious } from "./enum-multiple-sealious.js";
|
|
38
38
|
import { URLField } from "./url.js";
|
|
39
39
|
import { Image } from "./image.js";
|
|
40
|
+
import { PhoneNumberWithCountryCode } from "./phone-number-with-country-code.js";
|
|
41
|
+
import { ItemDraft } from "./item-draft.js";
|
|
40
42
|
|
|
41
|
-
export type SealiousToFormField<T> =
|
|
42
|
-
? BooleanField<false>
|
|
43
|
-
: T extends SealiousFieldTypes.Color
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
false,
|
|
94
|
-
SimpleInput
|
|
95
|
-
>
|
|
96
|
-
: T extends SealiousFieldTypes.SecretToken
|
|
97
|
-
? TextBasedSimpleField<
|
|
98
|
-
false,
|
|
99
|
-
SimpleInput
|
|
100
|
-
>
|
|
101
|
-
: T extends SealiousFieldTypes.SettableBy<
|
|
102
|
-
infer R
|
|
103
|
-
>
|
|
104
|
-
? SealiousToFormField<R>
|
|
105
|
-
: T extends SealiousFieldTypes.SingleReference
|
|
106
|
-
? SingleReference<
|
|
107
|
-
Collection,
|
|
108
|
-
boolean,
|
|
109
|
-
T
|
|
110
|
-
>
|
|
111
|
-
: T extends SealiousFieldTypes.Text
|
|
112
|
-
? Text<false>
|
|
113
|
-
: T extends SealiousFieldTypes.Username
|
|
114
|
-
? CollectionField<
|
|
115
|
-
SealiousFieldTypes.Username,
|
|
116
|
-
false
|
|
117
|
-
>
|
|
118
|
-
: T extends SealiousFieldTypes.ValueExistingInCollection
|
|
119
|
-
? SimpleFormField<false>
|
|
120
|
-
: T extends SealiousFieldTypes.ValueNotExistingInCollection
|
|
121
|
-
? SimpleFormField<false>
|
|
122
|
-
: T extends SealiousRequiredField<
|
|
123
|
-
any,
|
|
124
|
-
any,
|
|
125
|
-
any
|
|
126
|
-
>
|
|
127
|
-
? SimpleFormField<false>
|
|
128
|
-
: T extends SealiousFieldTypes.Url
|
|
129
|
-
? URLField<false>
|
|
130
|
-
: T extends SealiousFieldTypes.Money
|
|
131
|
-
? SimpleFormField<false>
|
|
132
|
-
: T extends SealiousFieldTypes.Markdown
|
|
133
|
-
? Markdown<false>
|
|
134
|
-
: T extends SealiousFieldTypes.Coordinates
|
|
135
|
-
? Coordinates<false>
|
|
136
|
-
: T extends SealiousFieldTypes.StructuredArray<
|
|
137
|
-
infer R
|
|
138
|
-
>
|
|
139
|
-
? StructuredArrayField<R>
|
|
140
|
-
: TextBasedSimpleField<any>;
|
|
43
|
+
export type SealiousToFormField<T> =
|
|
44
|
+
T extends SealiousFieldTypes.Boolean ? BooleanField<false>
|
|
45
|
+
: T extends SealiousFieldTypes.Color ? Color<false>
|
|
46
|
+
: T extends SealiousFieldTypes.ControlAccess<infer R> ?
|
|
47
|
+
SealiousToFormField<R>
|
|
48
|
+
: T extends SealiousFieldTypes.Date ? DateField<false>
|
|
49
|
+
: T extends SealiousFieldTypes.DateTime ? DateTime<false>
|
|
50
|
+
: T extends SealiousFieldTypes.DeepReverseSingleReference ?
|
|
51
|
+
DeepReverseSingleReferenceList | DeepReverseSingleReferenceTable
|
|
52
|
+
: T extends SealiousFieldTypes.DisallowUpdate<any, any, any, infer R> ?
|
|
53
|
+
SealiousToFormField<R>
|
|
54
|
+
: T extends SealiousFieldTypes.Email ? EmailField<false>
|
|
55
|
+
: T extends SealiousFieldTypes.Enum<any> ? PickFromListSealious<false>
|
|
56
|
+
: T extends SealiousFieldTypes.EnumMultiple<infer R> ?
|
|
57
|
+
R extends string[] ?
|
|
58
|
+
EnumMultipleSealious<R>
|
|
59
|
+
: TextBasedSimpleField<false>
|
|
60
|
+
: T extends SealiousFieldTypes.File ? File<false>
|
|
61
|
+
: T extends SealiousFieldTypes.Float ? Float<false>
|
|
62
|
+
: T extends SealiousFieldTypes.HTML ? HTML<false>
|
|
63
|
+
: T extends SealiousFieldTypes.Image ? Image<false>
|
|
64
|
+
: T extends SealiousFieldTypes.Int ? Int<false>
|
|
65
|
+
: T extends SealiousFieldTypes.ItemDraft<infer C extends Collection> ?
|
|
66
|
+
ItemDraft<C>
|
|
67
|
+
: T extends SealiousFieldTypes.JsonObject ? JSONField<false>
|
|
68
|
+
: T extends SealiousFieldTypes.JDD ?
|
|
69
|
+
TextBasedSimpleField<false, SimpleInput>
|
|
70
|
+
: T extends SealiousFieldTypes.Password ? Password<false>
|
|
71
|
+
: T extends SealiousFieldTypes.PhoneNumber ?
|
|
72
|
+
PhoneNumberWithCountryCode<false>
|
|
73
|
+
: T extends SealiousFieldTypes.ReverseSingleReference ?
|
|
74
|
+
TextBasedSimpleField<false, SimpleInput>
|
|
75
|
+
: T extends SealiousFieldTypes.SecretToken ?
|
|
76
|
+
TextBasedSimpleField<false, SimpleInput>
|
|
77
|
+
: T extends SealiousFieldTypes.SettableBy<infer R> ? SealiousToFormField<R>
|
|
78
|
+
: T extends SealiousFieldTypes.SingleReference ?
|
|
79
|
+
SingleReference<Collection, boolean, T>
|
|
80
|
+
: T extends SealiousFieldTypes.Text ? Text<false>
|
|
81
|
+
: T extends SealiousFieldTypes.Username ?
|
|
82
|
+
CollectionField<SealiousFieldTypes.Username, false>
|
|
83
|
+
: T extends SealiousFieldTypes.ValueExistingInCollection ?
|
|
84
|
+
SimpleFormField<false>
|
|
85
|
+
: T extends SealiousFieldTypes.ValueNotExistingInCollection ?
|
|
86
|
+
SimpleFormField<false>
|
|
87
|
+
: T extends SealiousRequiredField<any, any, any> ? SimpleFormField<false>
|
|
88
|
+
: T extends SealiousFieldTypes.Url ? URLField<false>
|
|
89
|
+
: T extends SealiousFieldTypes.Money ? SimpleFormField<false>
|
|
90
|
+
: T extends SealiousFieldTypes.Markdown ? Markdown<false>
|
|
91
|
+
: T extends SealiousFieldTypes.Coordinates ? Coordinates<false>
|
|
92
|
+
: T extends SealiousFieldTypes.StructuredArray<infer R> ?
|
|
93
|
+
StructuredArrayField<R>
|
|
94
|
+
: TextBasedSimpleField<any>;
|
|
141
95
|
|
|
142
96
|
export function sealiousToFormField<SF extends SealiousField<any>>(
|
|
143
97
|
sealious_field: SF
|
|
@@ -219,6 +173,9 @@ export function sealiousToFormField<SF extends SealiousField<any>>(
|
|
|
219
173
|
if (sealious_field instanceof SealiousFieldTypes.Int) {
|
|
220
174
|
return new Int(sealious_field.required) as SealiousToFormField<SF>;
|
|
221
175
|
}
|
|
176
|
+
if (sealious_field instanceof SealiousFieldTypes.ItemDraft) {
|
|
177
|
+
return new ItemDraft(sealious_field) as SealiousToFormField<SF>;
|
|
178
|
+
}
|
|
222
179
|
if (sealious_field instanceof SealiousFieldTypes.JsonObject) {
|
|
223
180
|
return new JSONField(
|
|
224
181
|
sealious_field.required
|
|
@@ -227,6 +184,11 @@ export function sealiousToFormField<SF extends SealiousField<any>>(
|
|
|
227
184
|
if (sealious_field instanceof SealiousFieldTypes.Password) {
|
|
228
185
|
return new Password(sealious_field.required) as SealiousToFormField<SF>;
|
|
229
186
|
}
|
|
187
|
+
if (sealious_field instanceof SealiousFieldTypes.PhoneNumber) {
|
|
188
|
+
return new PhoneNumberWithCountryCode(
|
|
189
|
+
sealious_field.required
|
|
190
|
+
) as SealiousToFormField<SF>;
|
|
191
|
+
}
|
|
230
192
|
if (sealious_field instanceof SealiousFieldTypes.SettableBy) {
|
|
231
193
|
return sealiousToFormField(
|
|
232
194
|
sealious_field.virtual_field
|
|
@@ -304,10 +266,14 @@ export async function sealiousFieldInfoToFormField(
|
|
|
304
266
|
return new Image(field_info.is_required);
|
|
305
267
|
case "int":
|
|
306
268
|
return new Int(field_info.is_required);
|
|
269
|
+
case "item-draft":
|
|
270
|
+
return new ItemDraft({ target_collection: { fields: {} } } as any);
|
|
307
271
|
case "json-object":
|
|
308
272
|
return new JSONField(field_info.is_required);
|
|
309
273
|
case "password":
|
|
310
274
|
return new Password(field_info.is_required);
|
|
275
|
+
case "phone-number":
|
|
276
|
+
return new PhoneNumberWithCountryCode(field_info.is_required);
|
|
311
277
|
case "single-reference":
|
|
312
278
|
return new SingleReference(false, {} as any);
|
|
313
279
|
case "text":
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* eslint @typescript-eslint/no-unsafe-argument: off, @typescript-eslint/no-explicit-any: off, @typescript-eslint/no-unsafe-member-access: off */
|
|
2
|
+
import { Context } from "koa";
|
|
3
|
+
import { Hybrid as HybridControl } from "../controls/hybrid.js";
|
|
4
|
+
import { FormDataValue } from "../form-types.js";
|
|
5
|
+
import { FieldParseResult, FormField } from "./field.js";
|
|
6
|
+
|
|
7
|
+
export class Hybrid<Fields extends Record<string, FormField>> extends FormField<
|
|
8
|
+
true,
|
|
9
|
+
Record<string, unknown>
|
|
10
|
+
> {
|
|
11
|
+
constructor(public subfields: Fields) {
|
|
12
|
+
super(true);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public getEmptyValue(): Record<string, unknown> {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async parse(
|
|
20
|
+
ctx: Context,
|
|
21
|
+
raw_value: FormDataValue
|
|
22
|
+
): Promise<FieldParseResult<Record<string, unknown>>> {
|
|
23
|
+
return {
|
|
24
|
+
parsable: true,
|
|
25
|
+
error: null,
|
|
26
|
+
parsed_value: Object.fromEntries(
|
|
27
|
+
await Promise.all(
|
|
28
|
+
Object.entries(this.subfields).map(
|
|
29
|
+
async ([key, subfield]) => [
|
|
30
|
+
key,
|
|
31
|
+
(
|
|
32
|
+
await subfield.getParsedValue(
|
|
33
|
+
ctx,
|
|
34
|
+
(raw_value as any)[key]
|
|
35
|
+
)
|
|
36
|
+
).parsed as FormDataValue,
|
|
37
|
+
]
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
) as Record<string, unknown>,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getControl(): HybridControl {
|
|
45
|
+
return new HybridControl(this, this.subfields);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Collection, FieldTypes as SealiousFieldTypes } from "sealious";
|
|
2
|
+
import { sealiousToFormField } from "./get-field-for-sealious.js";
|
|
3
|
+
import { Hybrid } from "./hybrid.js";
|
|
4
|
+
import { FormField } from "./field.js";
|
|
5
|
+
import { ExtractedFieldInfo } from "../../utils/extract-fields-from-collection.js";
|
|
6
|
+
import { toPascalCase } from "js-convert-case";
|
|
7
|
+
|
|
8
|
+
export class ItemDraft<C extends Collection> extends Hybrid<{
|
|
9
|
+
[name in keyof C["fields"]]: FormField;
|
|
10
|
+
}> {
|
|
11
|
+
constructor(public sealious_field: SealiousFieldTypes.ItemDraft<C>) {
|
|
12
|
+
super(
|
|
13
|
+
Object.fromEntries(
|
|
14
|
+
Object.entries(sealious_field.target_collection.fields).map(
|
|
15
|
+
([field_name, sealious_field]) => [
|
|
16
|
+
field_name,
|
|
17
|
+
sealiousToFormField(sealious_field),
|
|
18
|
+
]
|
|
19
|
+
)
|
|
20
|
+
) as unknown as {
|
|
21
|
+
[name in keyof C["fields"]]: FormField;
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
generateFieldDeclaration(
|
|
27
|
+
field_info: ExtractedFieldInfo,
|
|
28
|
+
vars: { form_field_types: string; sealious_field: string }
|
|
29
|
+
): string {
|
|
30
|
+
return `new ${vars.form_field_types}.${this.constructor.name}<typeof ${toPascalCase(field_info.target!)}>(${vars.sealious_field})`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
generateImportsForFieldList(
|
|
34
|
+
field_info: ExtractedFieldInfo
|
|
35
|
+
): { what: string; from: string; as?: string; type?: boolean }[] {
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
what: toPascalCase(field_info.target!),
|
|
39
|
+
from: "src/back/collections/collections.js",
|
|
40
|
+
type: true,
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/forms/form.test.ts
CHANGED
|
@@ -18,9 +18,9 @@ const fields = {
|
|
|
18
18
|
|
|
19
19
|
export function form_factory(canAccessFun?: Mountable["canAccess"]): Form<typeof fields, void> {
|
|
20
20
|
const result = new (class extends Form<typeof fields, void> {
|
|
21
|
-
|
|
21
|
+
getFields = () => fields;
|
|
22
22
|
submitButtonText = "Submit";
|
|
23
|
-
|
|
23
|
+
getControls = () => [
|
|
24
24
|
new Controls.SimpleInput(fields.text, {
|
|
25
25
|
label: "This is a test:",
|
|
26
26
|
type: "password",
|
|
@@ -112,7 +112,7 @@ describe("form test", () => {
|
|
|
112
112
|
router,
|
|
113
113
|
"/",
|
|
114
114
|
form_factory(
|
|
115
|
-
async (
|
|
115
|
+
async (_ctx: Koa.Context): Promise<{ canAccess: boolean; message: string }> => {
|
|
116
116
|
return { canAccess: true, message: "" };
|
|
117
117
|
}
|
|
118
118
|
),
|
|
@@ -184,9 +184,9 @@ describe("form test", () => {
|
|
|
184
184
|
"/",
|
|
185
185
|
form_factory(
|
|
186
186
|
async (ctx: Koa.Context): Promise<{ canAccess: boolean; message: string }> => {
|
|
187
|
-
return ctx.$context && ctx.$context.user_id
|
|
188
|
-
|
|
189
|
-
:
|
|
187
|
+
return ctx.$context && ctx.$context.user_id ?
|
|
188
|
+
{ canAccess: true, message: "" }
|
|
189
|
+
: { canAccess: false, message: "" };
|
|
190
190
|
}
|
|
191
191
|
),
|
|
192
192
|
true
|
|
@@ -214,9 +214,9 @@ describe("form test", () => {
|
|
|
214
214
|
"/",
|
|
215
215
|
form_factory(
|
|
216
216
|
async (ctx: Koa.Context): Promise<{ canAccess: boolean; message: string }> => {
|
|
217
|
-
return ctx.$context && ctx.$context.user_id
|
|
218
|
-
|
|
219
|
-
:
|
|
217
|
+
return ctx.$context && ctx.$context.user_id ?
|
|
218
|
+
{ canAccess: true, message: "" }
|
|
219
|
+
: { canAccess: false, message: "" };
|
|
220
220
|
}
|
|
221
221
|
),
|
|
222
222
|
true
|
|
@@ -242,9 +242,9 @@ describe("form test", () => {
|
|
|
242
242
|
router,
|
|
243
243
|
"/",
|
|
244
244
|
new (class extends Form<typeof fields, void> {
|
|
245
|
-
|
|
245
|
+
getFields = () => fields;
|
|
246
246
|
submitButtonText = "Submit";
|
|
247
|
-
|
|
247
|
+
getControls = () => [
|
|
248
248
|
new Controls.SimpleInput(fields.text, {
|
|
249
249
|
label: "This is a test:",
|
|
250
250
|
type: "password",
|
|
@@ -311,9 +311,9 @@ describe("form test", () => {
|
|
|
311
311
|
router,
|
|
312
312
|
"/",
|
|
313
313
|
new (class extends Form<typeof fields, void> {
|
|
314
|
-
|
|
314
|
+
getFields = () => fields;
|
|
315
315
|
submitButtonText = "Submit";
|
|
316
|
-
|
|
316
|
+
getControls = () => [
|
|
317
317
|
new Controls.SimpleInput(fields.text, {
|
|
318
318
|
label: "This is a test:",
|
|
319
319
|
type: "text",
|
|
@@ -375,8 +375,8 @@ describe("form test", () => {
|
|
|
375
375
|
router,
|
|
376
376
|
"/",
|
|
377
377
|
new (class extends Form<typeof fields, null> {
|
|
378
|
-
|
|
379
|
-
|
|
378
|
+
getFields = () => fields;
|
|
379
|
+
getControls = () => [new Controls.SimpleInput(fields.title)];
|
|
380
380
|
|
|
381
381
|
async getInitialValues() {
|
|
382
382
|
return { title: "Hello" };
|
package/src/forms/form.ts
CHANGED
|
@@ -66,15 +66,15 @@ export abstract class Form<
|
|
|
66
66
|
): Promise<JSX.Element | Readable> {
|
|
67
67
|
return tempstream /* HTML */ `<div class="form-container">
|
|
68
68
|
${this.makeOpenFormTag(ctx)}
|
|
69
|
-
${!this.controls.some((control) => control.role == "messages")
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
${!this.controls.some((control) => control.role == "messages") ?
|
|
70
|
+
this.renderMessages(ctx, data)
|
|
71
|
+
: ""}
|
|
72
72
|
${this.renderControls(
|
|
73
73
|
this.makeFormControlContext(ctx, data, show_field_errors)
|
|
74
74
|
)}
|
|
75
|
-
${this.controls.some((control) => control.role == "submit")
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
${this.controls.some((control) => control.role == "submit") ?
|
|
76
|
+
""
|
|
77
|
+
: this.makeSubmitButton()}
|
|
78
78
|
${this.makeCloseFormTag()}
|
|
79
79
|
</div>`;
|
|
80
80
|
}
|
|
@@ -102,9 +102,10 @@ export abstract class Form<
|
|
|
102
102
|
form_messages: FormMessage[],
|
|
103
103
|
field_errors: FieldMessages<keyof Fields>
|
|
104
104
|
): Promise<FormReaction> {
|
|
105
|
-
const messages =
|
|
106
|
-
? form_messages
|
|
107
|
-
|
|
105
|
+
const messages =
|
|
106
|
+
form_messages.length ? form_messages : (
|
|
107
|
+
[<const>{ type: "error", text: "Some fields are invalid" }]
|
|
108
|
+
);
|
|
108
109
|
return {
|
|
109
110
|
action: "stay",
|
|
110
111
|
content: await this.render(
|
|
@@ -217,12 +218,13 @@ export abstract class Form<
|
|
|
217
218
|
// eslint-disable-next-line no-console
|
|
218
219
|
console.dir(e, { depth: 5 });
|
|
219
220
|
const message =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
221
|
+
(
|
|
222
|
+
is(e, predicates.object) &&
|
|
223
|
+
hasFieldOfType(e, "message", predicates.string)
|
|
224
|
+
) ?
|
|
225
|
+
e?.message
|
|
226
|
+
: is(e, predicates.string) ? e
|
|
227
|
+
: "Wystąpił błąd";
|
|
226
228
|
|
|
227
229
|
return this.onError(
|
|
228
230
|
ctx,
|
|
@@ -241,6 +243,20 @@ export abstract class Form<
|
|
|
241
243
|
}
|
|
242
244
|
}
|
|
243
245
|
|
|
246
|
+
init(path: string, router: Router) {
|
|
247
|
+
super.init(path, router);
|
|
248
|
+
if (this.initialized) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// for use with other subroutes or middlewares that individual controls
|
|
252
|
+
// might register
|
|
253
|
+
const subrouter = new Router();
|
|
254
|
+
for (const control of this.controls) {
|
|
255
|
+
control.mount(subrouter, this);
|
|
256
|
+
}
|
|
257
|
+
router.use(path, subrouter.routes(), subrouter.allowedMethods());
|
|
258
|
+
}
|
|
259
|
+
|
|
244
260
|
public mount(router: Router, path: string): void {
|
|
245
261
|
router.use(path, async (ctx, next) => {
|
|
246
262
|
const result = await this.canAccess(ctx);
|
|
@@ -255,14 +271,6 @@ export abstract class Form<
|
|
|
255
271
|
await next();
|
|
256
272
|
});
|
|
257
273
|
|
|
258
|
-
// for use with other subroutes or middlewares that individual controls
|
|
259
|
-
// might register
|
|
260
|
-
const subrouter = new Router();
|
|
261
|
-
for (const control of this.controls) {
|
|
262
|
-
control.mount(subrouter, this);
|
|
263
|
-
}
|
|
264
|
-
router.use(path, subrouter.routes(), subrouter.allowedMethods());
|
|
265
|
-
|
|
266
274
|
router.get(path, async (ctx) => {
|
|
267
275
|
ctx.type = "html";
|
|
268
276
|
ctx.body = await this.render(
|
|
@@ -292,9 +300,9 @@ export abstract class Form<
|
|
|
292
300
|
async extractRawValues(
|
|
293
301
|
ctx: Context
|
|
294
302
|
): Promise<Record<string, FormDataValue>> {
|
|
295
|
-
return Object.keys(ctx.$body).length
|
|
296
|
-
|
|
297
|
-
:
|
|
303
|
+
return Object.keys(ctx.$body).length ?
|
|
304
|
+
ctx.$body
|
|
305
|
+
: this.getInitialValues(ctx);
|
|
298
306
|
}
|
|
299
307
|
|
|
300
308
|
static initFieldNames(fields: Record<string, FormField>): void {
|
|
@@ -27,8 +27,8 @@ const fields1 = {
|
|
|
27
27
|
|
|
28
28
|
const form1 = new (class extends Form<typeof fields1, void> {
|
|
29
29
|
defaultSuccessMessage = "Form1 done!";
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
getControls = () => [new FormHeader("form1"), new SimpleInput(fields1.email)];
|
|
31
|
+
getFields = () => fields1;
|
|
32
32
|
onSubmit() {
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
@@ -40,8 +40,8 @@ const fields2 = {
|
|
|
40
40
|
|
|
41
41
|
const form2 = new (class extends Form<Record<never, unknown>, void> {
|
|
42
42
|
defaultSuccessMessage = "Form2 done!";
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
getControls = () => [new FormHeader("form2"), new SimpleInput(fields2.name)];
|
|
44
|
+
getFields = () => fields2;
|
|
45
45
|
onSubmit() {
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
@@ -427,7 +427,7 @@ describe("multiform test", () => {
|
|
|
427
427
|
form1: new (class extends Form<typeof fields1, void> {
|
|
428
428
|
fields = fields1;
|
|
429
429
|
submitButtonText = "Submit";
|
|
430
|
-
|
|
430
|
+
getControls = () => [
|
|
431
431
|
new SimpleInput(fields1.email, {
|
|
432
432
|
label: "This is a test:",
|
|
433
433
|
type: "text",
|
|
@@ -500,9 +500,9 @@ describe("multiform test", () => {
|
|
|
500
500
|
name = "multiform_test";
|
|
501
501
|
forms = {
|
|
502
502
|
form1: new (class extends Form<typeof fields, void> {
|
|
503
|
-
|
|
503
|
+
getFields = () => fields;
|
|
504
504
|
submitButtonText = "Submit";
|
|
505
|
-
|
|
505
|
+
getControls = () => [
|
|
506
506
|
new SimpleInput(fields.text, {
|
|
507
507
|
label: "This is a test:",
|
|
508
508
|
type: "text",
|
package/src/forms/multiform.ts
CHANGED
|
@@ -11,12 +11,15 @@ import { Form } from "./form.js";
|
|
|
11
11
|
const FIELD_PREFIX_SEPARATOR = "___";
|
|
12
12
|
|
|
13
13
|
export class Multiform extends MountableWithFields {
|
|
14
|
-
|
|
14
|
+
getControls = () => [];
|
|
15
15
|
public name: string;
|
|
16
16
|
public forms: Record<string, Form<Fields, unknown>>;
|
|
17
17
|
|
|
18
|
-
init(): void {
|
|
19
|
-
|
|
18
|
+
init(path: string, router: Router): void {
|
|
19
|
+
if (this.initialized) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
super.init(path, router);
|
|
20
23
|
for (const [key, form] of Object.entries(this.forms)) {
|
|
21
24
|
if (key !== attribute(key)) {
|
|
22
25
|
throw new Error(
|
|
@@ -30,7 +33,7 @@ export class Multiform extends MountableWithFields {
|
|
|
30
33
|
form.field_names_prefix = key + FIELD_PREFIX_SEPARATOR;
|
|
31
34
|
form.form_id = this.name;
|
|
32
35
|
form.action = "./" + key;
|
|
33
|
-
form.init();
|
|
36
|
+
form.init(path, router);
|
|
34
37
|
form.extractRawValues = async (context) =>
|
|
35
38
|
this.getSubformRawValues(context, key);
|
|
36
39
|
const submit_button_id = this.name + "_" + key + "_submit";
|
|
@@ -82,9 +85,9 @@ export class Multiform extends MountableWithFields {
|
|
|
82
85
|
throw new Error("Wrong turbo-frame header value type");
|
|
83
86
|
}
|
|
84
87
|
const forms_to_render =
|
|
85
|
-
requested_frame && this.forms[requested_frame]
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
requested_frame && this.forms[requested_frame] ?
|
|
89
|
+
[<const>[requested_frame, this.forms[requested_frame]]]
|
|
90
|
+
: Object.entries(this.forms);
|
|
88
91
|
return forms_to_render;
|
|
89
92
|
}
|
|
90
93
|
|
package/src/mount.ts
CHANGED
|
@@ -19,9 +19,9 @@ export function mount(
|
|
|
19
19
|
url: string | { rawURL: string },
|
|
20
20
|
mountable: Mountable,
|
|
21
21
|
use_dummy_app = false,
|
|
22
|
-
file_manager: FileManager | undefined = use_dummy_app
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
file_manager: FileManager | undefined = use_dummy_app ?
|
|
23
|
+
new FileManager("/tmp", "/uploaded_files")
|
|
24
|
+
: undefined
|
|
25
25
|
): void {
|
|
26
26
|
const raw_url = typeof url === "string" ? url : url.rawURL;
|
|
27
27
|
|
|
@@ -35,8 +35,9 @@ export function mount(
|
|
|
35
35
|
|
|
36
36
|
const fileManager = new FileManager("/tmp", "/uploaded_files");
|
|
37
37
|
|
|
38
|
-
const args =
|
|
39
|
-
?
|
|
38
|
+
const args =
|
|
39
|
+
use_dummy_app ?
|
|
40
|
+
[
|
|
40
41
|
async (ctx: Context, next: any) => {
|
|
41
42
|
// this dummy is a temporary solution it's ilustrating the ways that
|
|
42
43
|
// sealgen is coupled with sealious. Definitely room for improvement in this
|
|
@@ -66,14 +67,17 @@ export function mount(
|
|
|
66
67
|
SealiousMiddlewares.parseBody(file_manager),
|
|
67
68
|
handleHtmlPromise,
|
|
68
69
|
]
|
|
69
|
-
:
|
|
70
|
+
: [
|
|
70
71
|
SealiousMiddlewares.extractContext(),
|
|
71
72
|
SealiousMiddlewares.parseBody(file_manager),
|
|
72
73
|
handleHtmlPromise,
|
|
73
74
|
];
|
|
74
75
|
|
|
75
76
|
router.use(raw_url, ...args);
|
|
76
|
-
|
|
77
|
+
router.use(raw_url, async (_ctx, next) => {
|
|
78
|
+
mountable.init(raw_url, router);
|
|
79
|
+
await next();
|
|
80
|
+
});
|
|
77
81
|
|
|
78
82
|
// to automatically add trailing slashes:
|
|
79
83
|
router.get(raw_url.slice(0, -1), async (ctx, next) => {
|