@sealcode/sealgen 0.3.2 → 0.4.2
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/controls/checkboxed-list-input.d.ts +5 -6
- package/@types/forms/controls/dropdown.d.ts +5 -6
- package/@types/forms/controls/editable-collection-subset.d.ts +6 -6
- package/@types/forms/controls/form-control.d.ts +3 -4
- package/@types/forms/controls/form-field-control.d.ts +9 -8
- package/@types/forms/controls/frame.d.ts +2 -2
- package/@types/forms/controls/simple-input.d.ts +5 -5
- package/@types/forms/field.d.ts +47 -16
- package/@types/forms/fields/checkboxed-list.d.ts +16 -0
- package/@types/forms/fields/collection-field.d.ts +11 -0
- package/@types/forms/fields/field.d.ts +38 -0
- package/@types/forms/fields/number.d.ts +13 -0
- package/@types/forms/fields/pick-from-list.d.ts +16 -0
- package/@types/forms/fields/simple-form-field.d.ts +7 -0
- package/@types/forms/form-fields-list.d.ts +1 -1
- package/@types/forms/form.d.ts +24 -29
- package/@types/forms/simple-form-field.d.ts +6 -0
- package/@types/forms/validator.d.ts +0 -3
- package/@types/index.d.ts +8 -5
- package/@types/make-hidden-inputs.d.ts +4 -0
- package/@types/page/list.d.ts +23 -42
- package/@types/page/mountable.d.ts +36 -0
- package/@types/page/page.d.ts +10 -17
- package/@types/page/props-parser.d.ts +5 -10
- package/@types/utils/translate.d.ts +3 -0
- package/lib/build.js +2 -1
- package/lib/build.js.map +1 -1
- package/lib/forms/controls/checkboxed-list-input.js +10 -17
- package/lib/forms/controls/checkboxed-list-input.js.map +1 -1
- package/lib/forms/controls/dropdown.js +16 -20
- package/lib/forms/controls/dropdown.js.map +1 -1
- package/lib/forms/controls/editable-collection-subset.js +33 -35
- package/lib/forms/controls/editable-collection-subset.js.map +1 -1
- package/lib/forms/controls/form-control.js.map +1 -1
- package/lib/forms/controls/form-field-control.js +2 -11
- package/lib/forms/controls/form-field-control.js.map +1 -1
- package/lib/forms/controls/simple-input.js +17 -23
- package/lib/forms/controls/simple-input.js.map +1 -1
- package/lib/forms/field.js +96 -20
- package/lib/forms/field.js.map +1 -1
- package/lib/forms/fields/checkboxed-list.js +43 -0
- package/lib/forms/fields/checkboxed-list.js.map +1 -0
- package/lib/forms/fields/collection-field.js +28 -0
- package/lib/forms/fields/collection-field.js.map +1 -0
- package/lib/forms/fields/field.js +70 -0
- package/lib/forms/fields/field.js.map +1 -0
- package/lib/forms/fields/number.js +45 -0
- package/lib/forms/fields/number.js.map +1 -0
- package/lib/forms/fields/pick-from-list.js +34 -0
- package/lib/forms/fields/pick-from-list.js.map +1 -0
- package/lib/forms/fields/simple-form-field.js +18 -0
- package/lib/forms/fields/simple-form-field.js.map +1 -0
- package/lib/forms/form.js +50 -96
- package/lib/forms/form.js.map +1 -1
- package/lib/forms/simple-form-field.js +18 -0
- package/lib/forms/simple-form-field.js.map +1 -0
- package/lib/forms/validator.js +0 -10
- package/lib/forms/validator.js.map +1 -1
- package/lib/generate-routes.js +19 -2
- package/lib/generate-routes.js.map +1 -1
- package/lib/index.js +4 -3
- package/lib/index.js.map +1 -1
- package/lib/make-hidden-inputs.js +17 -0
- package/lib/make-hidden-inputs.js.map +1 -0
- package/lib/page/list.js +65 -79
- package/lib/page/list.js.map +1 -1
- package/lib/page/mountable.js +56 -0
- package/lib/page/mountable.js.map +1 -0
- package/lib/page/page.js +11 -1
- package/lib/page/page.js.map +1 -1
- package/lib/page/props-parser.js +4 -47
- package/lib/page/props-parser.js.map +1 -1
- package/lib/templates/form.js +32 -17
- package/lib/templates/form.js.map +1 -1
- package/lib/templates/list.js +30 -20
- package/lib/templates/list.js.map +1 -1
- package/lib/utils/translate.js +10 -0
- package/lib/utils/translate.js.map +1 -0
- package/package.json +4 -3
- package/src/build.ts +2 -1
- package/src/forms/controls/checkboxed-list-input.ts +12 -23
- package/src/forms/controls/dropdown.ts +27 -32
- package/src/forms/controls/editable-collection-subset.ts +57 -54
- package/src/forms/controls/form-control.ts +5 -6
- package/src/forms/controls/form-field-control.ts +13 -29
- package/src/forms/controls/frame.ts +2 -2
- package/src/forms/controls/simple-input.ts +27 -24
- package/src/forms/fields/checkboxed-list.ts +61 -0
- package/src/forms/fields/collection-field.ts +54 -0
- package/src/forms/fields/field.ts +116 -0
- package/src/forms/fields/number.ts +53 -0
- package/src/forms/fields/pick-from-list.ts +49 -0
- package/src/forms/fields/simple-form-field.ts +20 -0
- package/src/forms/form.ts +98 -163
- package/src/generate-routes.ts +19 -2
- package/src/index.ts +8 -5
- package/src/make-hidden-inputs.ts +22 -0
- package/src/page/list.ts +115 -126
- package/src/page/mountable.ts +105 -0
- package/src/page/page.ts +21 -12
- package/src/page/props-parser.ts +9 -67
- package/src/templates/form.ts +32 -17
- package/src/templates/list.ts +30 -20
- package/src/utils/translate.ts +13 -0
- package/src/forms/controls.ts +0 -0
- package/src/forms/field.ts +0 -119
- package/src/forms/form-fields-list.ts +0 -7
- package/src/forms/validator.ts +0 -10
package/src/build.ts
CHANGED
|
@@ -37,7 +37,7 @@ async function complex_build(watch: boolean): Promise<void> {
|
|
|
37
37
|
await scss_build?.rebuild?.();
|
|
38
38
|
console.log(`Built main.scss [on ${path}]`);
|
|
39
39
|
} catch (e) {
|
|
40
|
-
console.error(e);
|
|
40
|
+
console.error(e.message);
|
|
41
41
|
await sleep(200);
|
|
42
42
|
scss_build
|
|
43
43
|
?.rebuild?.()
|
|
@@ -88,5 +88,6 @@ export async function buildProject(...args: string[]): Promise<void> {
|
|
|
88
88
|
} catch (e) {
|
|
89
89
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
90
90
|
console.error(e.message);
|
|
91
|
+
await buildProject(...args);
|
|
91
92
|
}
|
|
92
93
|
}
|
|
@@ -1,48 +1,37 @@
|
|
|
1
1
|
import { is, predicates } from "@sealcode/ts-predicates";
|
|
2
2
|
import { BaseContext } from "koa";
|
|
3
3
|
import { tempstream } from "tempstream";
|
|
4
|
-
import { ChekboxedListField
|
|
5
|
-
import {
|
|
6
|
-
import { FormFieldsList } from "../form-fields-list";
|
|
4
|
+
import { ChekboxedListField } from "../fields/field";
|
|
5
|
+
import { FormDataValue } from "../form";
|
|
7
6
|
import { FormFieldControl } from "./controls";
|
|
8
7
|
|
|
9
8
|
export class CheckboxedListInput extends FormFieldControl {
|
|
10
9
|
constructor(
|
|
11
|
-
public
|
|
12
|
-
public options: { label: string } = { label:
|
|
10
|
+
public field: ChekboxedListField,
|
|
11
|
+
public options: { label: string } = { label: field.name }
|
|
13
12
|
) {
|
|
14
|
-
super([
|
|
13
|
+
super([field]);
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async _render(ctx: BaseContext, fields: FormField[], data: FormData) {
|
|
22
|
-
// safe to disable this as isValidFieldName takes care of checking if the field is of this type
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
24
|
-
const field = FormFieldsList.getField(
|
|
25
|
-
fields,
|
|
26
|
-
this.fieldname
|
|
27
|
-
) as ChekboxedListField;
|
|
28
|
-
const pickedValues = data.values[field.name] || "";
|
|
16
|
+
async render(ctx: BaseContext, data: Record<string, FormDataValue>) {
|
|
17
|
+
const { parsed: pickedValues } = await this.field.getValue(ctx, data);
|
|
29
18
|
if (!is(pickedValues, predicates.array(predicates.string))) {
|
|
30
19
|
throw new Error("picked values is not an array of strings");
|
|
31
20
|
}
|
|
32
21
|
const [options, isVisible] = await Promise.all([
|
|
33
|
-
field.generateOptions(ctx),
|
|
34
|
-
field.isVisible(ctx),
|
|
22
|
+
this.field.generateOptions(ctx),
|
|
23
|
+
this.field.isVisible(ctx),
|
|
35
24
|
]);
|
|
36
25
|
return tempstream/* HTML */ `${isVisible
|
|
37
26
|
? Object.entries(options).map(
|
|
38
27
|
([value, text]) => /* HTML */ `<div>
|
|
39
28
|
<input
|
|
40
29
|
type="checkbox"
|
|
41
|
-
id="${field.name}.${value}"
|
|
42
|
-
name="${field.name}.${value}"
|
|
30
|
+
id="${this.field.name}.${value}"
|
|
31
|
+
name="${this.field.name}.${value}"
|
|
43
32
|
${pickedValues.includes(value) ? "checked" : ""}
|
|
44
33
|
/>
|
|
45
|
-
<label for="${field.name}.${value}">${text}</label>
|
|
34
|
+
<label for="${this.field.name}.${value}">${text}</label>
|
|
46
35
|
</div>`
|
|
47
36
|
)
|
|
48
37
|
: ""}`;
|
|
@@ -1,56 +1,51 @@
|
|
|
1
1
|
import { BaseContext } from "koa";
|
|
2
2
|
import { tempstream } from "tempstream";
|
|
3
|
-
import { FormField, PickFromListField } from "../field";
|
|
4
|
-
import {
|
|
5
|
-
import { FormFieldsList } from "../form-fields-list";
|
|
3
|
+
import { FormField, PickFromListField } from "../fields/field";
|
|
4
|
+
import { FormDataValue } from "../form";
|
|
6
5
|
import { FormFieldControl } from "./controls";
|
|
7
6
|
|
|
8
7
|
export class Dropdown extends FormFieldControl {
|
|
9
8
|
constructor(
|
|
10
|
-
public
|
|
9
|
+
public field: PickFromListField,
|
|
11
10
|
public options: {
|
|
12
11
|
label: string;
|
|
13
12
|
autosubmit?: boolean;
|
|
14
13
|
autocomplete?: boolean;
|
|
15
14
|
} = {
|
|
16
|
-
label:
|
|
15
|
+
label: field.name,
|
|
17
16
|
autosubmit: false,
|
|
18
17
|
autocomplete: true,
|
|
19
18
|
}
|
|
20
19
|
) {
|
|
21
|
-
super([
|
|
20
|
+
super([field]);
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// safe to disable this as isValidFieldName takes care of checking if the field is of this type
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
34
|
-
const field = FormFieldsList.getField(
|
|
35
|
-
fields,
|
|
36
|
-
this.fieldname
|
|
37
|
-
) as PickFromListField;
|
|
38
|
-
const picked_value = data.values[field.name] || "";
|
|
39
|
-
const id = field.name;
|
|
40
|
-
return tempstream/* HTML */ `<label for="${id}">${this.options.label}</label
|
|
23
|
+
async render<Fields extends Record<string, FormField>>(
|
|
24
|
+
ctx: BaseContext,
|
|
25
|
+
data: Record<string, FormDataValue>
|
|
26
|
+
) {
|
|
27
|
+
const { parsed: picked_value } = await this.field.getValue(ctx, data);
|
|
28
|
+
const id = this.field.name;
|
|
29
|
+
return tempstream/* HTML */ `<label for="${id}"
|
|
30
|
+
>${this.options.label}</label
|
|
41
31
|
><select
|
|
42
|
-
name="${this.
|
|
32
|
+
name="${this.field.name}"
|
|
43
33
|
id="${id}"
|
|
44
|
-
${this.options.autosubmit
|
|
34
|
+
${this.options.autosubmit
|
|
35
|
+
? `onchange='this.form.submit()'`
|
|
36
|
+
: ""}
|
|
45
37
|
${!this.options.autocomplete ? `autocomplete="off"` : ""}
|
|
46
38
|
>
|
|
47
|
-
${Promise.resolve(field.generateOptions(ctx)).then(
|
|
48
|
-
|
|
49
|
-
(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
${Promise.resolve(this.field.generateOptions(ctx)).then(
|
|
40
|
+
(options) =>
|
|
41
|
+
Object.entries(options).map(
|
|
42
|
+
([value, text]) =>
|
|
43
|
+
`<option value="${value}" ${
|
|
44
|
+
(value || "") == picked_value
|
|
45
|
+
? "selected"
|
|
46
|
+
: ""
|
|
47
|
+
}>${text}</option>`
|
|
48
|
+
)
|
|
54
49
|
)}
|
|
55
50
|
</select>`;
|
|
56
51
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { BaseContext } from "koa";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { FormFieldsList } from "../form-fields-list";
|
|
2
|
+
import { FlatTemplatable, tempstream } from "tempstream";
|
|
3
|
+
import { PickFromListField } from "../fields/field";
|
|
4
|
+
import { FormDataValue } from "../form";
|
|
6
5
|
import { FormFieldControl } from "./controls";
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -14,69 +13,73 @@ import { FormFieldControl } from "./controls";
|
|
|
14
13
|
*/
|
|
15
14
|
export class EditableCollectionSubset extends FormFieldControl {
|
|
16
15
|
constructor(
|
|
17
|
-
public
|
|
16
|
+
public field: PickFromListField,
|
|
18
17
|
public actionname: string,
|
|
19
18
|
public listLabel?: string,
|
|
20
19
|
public selectLabel?: string
|
|
21
20
|
) {
|
|
22
|
-
super([
|
|
21
|
+
super([field]);
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
async render(
|
|
26
25
|
ctx: BaseContext,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// safe to disable this as isValidFieldName takes care of checking if the field is of this type
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
32
|
-
const field = FormFieldsList.getField(
|
|
33
|
-
fields,
|
|
34
|
-
this.fieldname
|
|
35
|
-
) as PickFromListField;
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
37
|
-
const values = data.values[this.fieldname] as string[];
|
|
26
|
+
data: Record<string, FormDataValue>
|
|
27
|
+
): Promise<FlatTemplatable> {
|
|
28
|
+
const { parsed: values } = await this.field.getValue(ctx, data);
|
|
38
29
|
|
|
39
30
|
return tempstream/* HTML */ `<div>
|
|
40
31
|
<ul>
|
|
41
|
-
${Promise.resolve(field.generateOptions(ctx)).then(
|
|
42
|
-
|
|
43
|
-
.filter(([value]) => values.includes(value))
|
|
44
|
-
.map(
|
|
45
|
-
([value, text]) => /* HTML */ `
|
|
46
|
-
<li>
|
|
47
|
-
<form method="POST" action="${ctx.path}">
|
|
48
|
-
<span>${text}</span>
|
|
49
|
-
<input
|
|
50
|
-
type="hidden"
|
|
51
|
-
name="${this.fieldname}"
|
|
52
|
-
value="${value}"
|
|
53
|
-
/>
|
|
54
|
-
<input
|
|
55
|
-
type="hidden"
|
|
56
|
-
name="${this.actionname}"
|
|
57
|
-
value="list"
|
|
58
|
-
/>
|
|
59
|
-
<input
|
|
60
|
-
type="submit"
|
|
61
|
-
${this.listLabel
|
|
62
|
-
? `value="${this.listLabel}"`
|
|
63
|
-
: ""}
|
|
64
|
-
/>
|
|
65
|
-
</form>
|
|
66
|
-
</li>
|
|
67
|
-
`
|
|
68
|
-
)
|
|
69
|
-
)}
|
|
70
|
-
</ul>
|
|
71
|
-
<form method="POST" action="${ctx.path}">
|
|
72
|
-
<select name="${this.fieldname}">
|
|
73
|
-
${Promise.resolve(field.generateOptions(ctx)).then((options) =>
|
|
32
|
+
${Promise.resolve(this.field.generateOptions(ctx)).then(
|
|
33
|
+
(options) =>
|
|
74
34
|
Object.entries(options)
|
|
75
|
-
.filter(([value]) =>
|
|
35
|
+
.filter(([value]) =>
|
|
36
|
+
(values || ([] as string[])).includes(value)
|
|
37
|
+
)
|
|
76
38
|
.map(
|
|
77
|
-
([value, text]) =>
|
|
78
|
-
|
|
39
|
+
([value, text]) => /* HTML */ `
|
|
40
|
+
<li>
|
|
41
|
+
<form
|
|
42
|
+
method="POST"
|
|
43
|
+
action="${ctx.path}"
|
|
44
|
+
>
|
|
45
|
+
<span>${text}</span>
|
|
46
|
+
<input
|
|
47
|
+
type="hidden"
|
|
48
|
+
name="${this.field.name}"
|
|
49
|
+
value="${value}"
|
|
50
|
+
/>
|
|
51
|
+
<input
|
|
52
|
+
type="hidden"
|
|
53
|
+
name="${this.actionname}"
|
|
54
|
+
value="list"
|
|
55
|
+
/>
|
|
56
|
+
<input
|
|
57
|
+
type="submit"
|
|
58
|
+
${this.listLabel
|
|
59
|
+
? `value="${this.listLabel}"`
|
|
60
|
+
: ""}
|
|
61
|
+
/>
|
|
62
|
+
</form>
|
|
63
|
+
</li>
|
|
64
|
+
`
|
|
79
65
|
)
|
|
66
|
+
)}
|
|
67
|
+
</ul>
|
|
68
|
+
<form method="POST" action="${ctx.path}">
|
|
69
|
+
<select name="${this.field.name}">
|
|
70
|
+
${Promise.resolve(this.field.generateOptions(ctx)).then(
|
|
71
|
+
(options) =>
|
|
72
|
+
Object.entries(options)
|
|
73
|
+
.filter(
|
|
74
|
+
([value]) =>
|
|
75
|
+
!(values || ([] as string[])).includes(
|
|
76
|
+
value
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
.map(
|
|
80
|
+
([value, text]) =>
|
|
81
|
+
`<option value="${value}">${text}</option>`
|
|
82
|
+
)
|
|
80
83
|
)}
|
|
81
84
|
</select>
|
|
82
85
|
<input type="hidden" name="${this.actionname}" value="select" />
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { BaseContext } from "koa";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { FormData } from "../form";
|
|
2
|
+
import { FlatTemplatable } from "tempstream";
|
|
3
|
+
import { FormDataValue, FormMessage } from "../form";
|
|
5
4
|
|
|
6
5
|
export abstract class FormControl {
|
|
7
6
|
abstract render(
|
|
8
7
|
ctx: BaseContext,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
):
|
|
8
|
+
data: Record<string, FormDataValue>,
|
|
9
|
+
messages: FormMessage[]
|
|
10
|
+
): FlatTemplatable | Promise<FlatTemplatable>;
|
|
12
11
|
abstract role: "input" | "decoration" | "messages" | "submit";
|
|
13
12
|
}
|
|
@@ -1,39 +1,23 @@
|
|
|
1
1
|
import { BaseContext } from "koa";
|
|
2
|
-
import {
|
|
3
|
-
import { FormField } from "../field";
|
|
4
|
-
import {
|
|
2
|
+
import { FlatTemplatable } from "tempstream";
|
|
3
|
+
import { FormField } from "../fields/field";
|
|
4
|
+
import { FieldValueType, FormDataValue } from "../form";
|
|
5
5
|
import { FormControl } from "./form-control";
|
|
6
6
|
|
|
7
|
+
export type FormFieldsToTypes<FormFields extends readonly FormField[]> = {
|
|
8
|
+
[Index in keyof FormFields]: FormFields[Index] extends FormField
|
|
9
|
+
? FieldValueType<FormFields[Index]>
|
|
10
|
+
: never;
|
|
11
|
+
};
|
|
12
|
+
|
|
7
13
|
export abstract class FormFieldControl extends FormControl {
|
|
8
14
|
role = <const>"input";
|
|
9
|
-
constructor(public
|
|
15
|
+
constructor(public fields: FormField[]) {
|
|
10
16
|
super();
|
|
11
17
|
}
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
return this.fieldnames.every((fieldname) =>
|
|
15
|
-
fields.some((f) => f.name == fieldname)
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
abstract _render(
|
|
19
|
+
abstract render(
|
|
20
20
|
ctx: BaseContext,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
): Templatable | Promise<Templatable>;
|
|
24
|
-
|
|
25
|
-
render(
|
|
26
|
-
ctx: BaseContext,
|
|
27
|
-
fields: FormField[],
|
|
28
|
-
data: FormData
|
|
29
|
-
): Templatable | Promise<Templatable> {
|
|
30
|
-
if (!this.areFieldNamesValid(fields)) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
`Invalid field names given to form control: "${this.fieldnames.join(
|
|
33
|
-
", "
|
|
34
|
-
)}". Allowed fields are: ${fields.map((f) => f.name).join(", ")}`
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
return this._render(ctx, fields, data);
|
|
38
|
-
}
|
|
21
|
+
data: Record<string, FormDataValue>
|
|
22
|
+
): FlatTemplatable | Promise<FlatTemplatable>;
|
|
39
23
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Templatable } from "tempstream";
|
|
1
|
+
import { FlatTemplatable, Templatable } from "tempstream";
|
|
2
2
|
import { FormControl } from "./controls";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -15,7 +15,7 @@ export class Frame extends FormControl {
|
|
|
15
15
|
super();
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
render():
|
|
18
|
+
render(): FlatTemplatable | Promise<FlatTemplatable> {
|
|
19
19
|
return /* HTML */ `<turbo-frame
|
|
20
20
|
id="contrahents"
|
|
21
21
|
loading="lazy"
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { BaseContext } from "koa";
|
|
2
|
-
import { FormField } from "../field";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { FormField } from "../fields/field";
|
|
3
|
+
import { NumberField } from "../fields/number";
|
|
4
|
+
import { SimpleFormField } from "../fields/simple-form-field";
|
|
5
|
+
import { FormDataValue } from "../form";
|
|
5
6
|
import { FormFieldControl } from "./controls";
|
|
6
7
|
|
|
7
8
|
export class SimpleInput extends FormFieldControl {
|
|
8
9
|
constructor(
|
|
9
|
-
public
|
|
10
|
+
public field: FormField,
|
|
10
11
|
public options: {
|
|
11
12
|
id?: string;
|
|
12
13
|
label?: string;
|
|
@@ -31,43 +32,45 @@ export class SimpleInput extends FormFieldControl {
|
|
|
31
32
|
step?: number;
|
|
32
33
|
} = {}
|
|
33
34
|
) {
|
|
34
|
-
super([
|
|
35
|
+
super([field]);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
const label = this.options.label || field.name;
|
|
38
|
+
async render<Fields extends Record<string, FormField>>(
|
|
39
|
+
ctx: BaseContext,
|
|
40
|
+
data: Record<string, FormDataValue>
|
|
41
|
+
) {
|
|
42
|
+
const id = this.options.id || this.field.name;
|
|
43
|
+
const label = this.options.label || this.field.name;
|
|
44
44
|
const type = this.options.type || "text";
|
|
45
45
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
46
|
-
const
|
|
46
|
+
const { parsed, raw, valid, message } = await this.field.getValue(
|
|
47
|
+
ctx,
|
|
48
|
+
data
|
|
49
|
+
);
|
|
47
50
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
48
|
-
const raw_value = data.raw_values[field.name] as string;
|
|
49
51
|
const placeholder = this.options.placeholder || type;
|
|
50
52
|
const readonly = this.options.readonly || false;
|
|
51
|
-
const required = field.required;
|
|
52
|
-
|
|
53
|
-
return /* HTML */ `<div class="input">
|
|
53
|
+
const required = this.field.required;
|
|
54
|
+
|
|
55
|
+
return /* HTML */ `<div class="form-input form-input__wrapper">
|
|
54
56
|
<label for="${id}">${label}</label>
|
|
55
57
|
<input
|
|
56
58
|
id="${id}"
|
|
57
59
|
type="${type}"
|
|
58
|
-
name="${field.name}"
|
|
59
|
-
value="${
|
|
60
|
-
?
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
name="${this.field.name}"
|
|
61
|
+
value="${!valid && raw
|
|
62
|
+
? raw.toString()
|
|
63
|
+
: parsed !== undefined && parsed !== null
|
|
64
|
+
? // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
65
|
+
((parsed as any)?.toString() as string) || ""
|
|
66
|
+
: ""}"
|
|
64
67
|
placeholder="${placeholder}"
|
|
65
68
|
${readonly ? "readonly" : ""}
|
|
66
69
|
${required ? "required" : ""}
|
|
67
70
|
${!this.options.autocomplete ? `autocomplete="off"` : ""}
|
|
68
71
|
${this.options.step ? `step="${this.options.step}"` : ""}
|
|
69
72
|
/>
|
|
70
|
-
${
|
|
73
|
+
${~valid ? `<div class="input__error">${message}</div>` : ""}
|
|
71
74
|
</div>`;
|
|
72
75
|
}
|
|
73
76
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { is, predicates } from "@sealcode/ts-predicates";
|
|
2
|
+
import { BaseContext } from "koa";
|
|
3
|
+
import { FormDataValue } from "../form";
|
|
4
|
+
import {
|
|
5
|
+
FieldParseResult,
|
|
6
|
+
FormField,
|
|
7
|
+
FormFieldValidationResponse,
|
|
8
|
+
} from "./field";
|
|
9
|
+
|
|
10
|
+
export class ChekboxedListField extends FormField<Record<string, "on">> {
|
|
11
|
+
constructor(
|
|
12
|
+
public required: boolean = false,
|
|
13
|
+
public generateOptions: (
|
|
14
|
+
ctx: BaseContext
|
|
15
|
+
) => Promise<Record<string, string> | { [i: string]: string }>,
|
|
16
|
+
public isVisible: (ctx: BaseContext) => Promise<boolean> = () =>
|
|
17
|
+
Promise.resolve(true)
|
|
18
|
+
) {
|
|
19
|
+
super(required);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async isValueValid(
|
|
23
|
+
_: BaseContext,
|
|
24
|
+
value: unknown
|
|
25
|
+
): Promise<FormFieldValidationResponse> {
|
|
26
|
+
if (is(value, predicates.string)) {
|
|
27
|
+
return { valid: false, message: "you need an array" };
|
|
28
|
+
}
|
|
29
|
+
if (is(value, predicates.null)) {
|
|
30
|
+
return { valid: false, message: "you need an array" };
|
|
31
|
+
}
|
|
32
|
+
return { valid: true, message: "" };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async parse(
|
|
36
|
+
_: BaseContext,
|
|
37
|
+
raw_value: FormDataValue
|
|
38
|
+
): Promise<FieldParseResult<Record<string, "on">>> {
|
|
39
|
+
if (is(raw_value, predicates.object)) {
|
|
40
|
+
return {
|
|
41
|
+
parsable: true,
|
|
42
|
+
parsed_value: Object.fromEntries(
|
|
43
|
+
Object.entries(raw_value).filter(
|
|
44
|
+
([, value]) => value === "on"
|
|
45
|
+
)
|
|
46
|
+
) as Record<string, "on">,
|
|
47
|
+
error: null,
|
|
48
|
+
};
|
|
49
|
+
} else {
|
|
50
|
+
return {
|
|
51
|
+
parsed_value: null,
|
|
52
|
+
parsable: false,
|
|
53
|
+
error: "Expected an object",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public getEmptyValue(): Record<string, "on"> {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BaseContext } from "koa";
|
|
2
|
+
import { Depromisify, Field, FieldOutput } from "sealious";
|
|
3
|
+
import { FormDataValue } from "../form";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
FieldParseResult,
|
|
7
|
+
FormField,
|
|
8
|
+
FormFieldValidationResponse,
|
|
9
|
+
} from "./field";
|
|
10
|
+
|
|
11
|
+
export class CollectionField<F extends Field> extends FormField<
|
|
12
|
+
FieldOutput<F>
|
|
13
|
+
> {
|
|
14
|
+
constructor(required: boolean, public sealiousField: F) {
|
|
15
|
+
super(required);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async parse(
|
|
19
|
+
ctx: BaseContext,
|
|
20
|
+
raw_value: FormDataValue
|
|
21
|
+
): Promise<FieldParseResult<FieldOutput<F>>> {
|
|
22
|
+
const encoded_value = (await this.sealiousField.encode(
|
|
23
|
+
ctx.$context,
|
|
24
|
+
raw_value
|
|
25
|
+
)) as Depromisify<ReturnType<F["encode"]>>;
|
|
26
|
+
const parsed_value = (await this.sealiousField.decode(
|
|
27
|
+
ctx.$context,
|
|
28
|
+
encoded_value,
|
|
29
|
+
null,
|
|
30
|
+
{}
|
|
31
|
+
)) as Depromisify<ReturnType<F["encode"]>>;
|
|
32
|
+
return {
|
|
33
|
+
parsable: true,
|
|
34
|
+
error: null,
|
|
35
|
+
parsed_value,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async isValueValid(
|
|
40
|
+
ctx: BaseContext,
|
|
41
|
+
value: FieldOutput<F>
|
|
42
|
+
): Promise<FormFieldValidationResponse> {
|
|
43
|
+
const { valid, reason } = await this.sealiousField.checkValue(
|
|
44
|
+
ctx.$context,
|
|
45
|
+
value,
|
|
46
|
+
undefined
|
|
47
|
+
);
|
|
48
|
+
return { valid, message: reason || (!valid ? "Wrong value" : "") };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public getEmptyValue(): Depromisify<ReturnType<F["decode"]>> {
|
|
52
|
+
return "" as unknown as Depromisify<ReturnType<F["decode"]>>;
|
|
53
|
+
}
|
|
54
|
+
}
|