@sealcode/sealgen 0.8.31 → 0.8.33
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/autocomplete.d.ts +2 -2
- package/@types/forms/controls/big-radio-group.d.ts +4 -5
- package/@types/forms/controls/checkbox-group.d.ts +3 -4
- package/@types/forms/controls/checkboxed-list-input.d.ts +2 -3
- package/@types/forms/controls/collapsible-checkbox-group.d.ts +2 -3
- package/@types/forms/controls/controls.d.ts +2 -1
- package/@types/forms/controls/derived-frame.d.ts +21 -0
- package/@types/forms/controls/derived.d.ts +6 -6
- package/@types/forms/controls/dropdown.d.ts +23 -14
- package/@types/forms/controls/editable-collection-subset.d.ts +2 -3
- package/@types/forms/controls/field-group.d.ts +2 -4
- package/@types/forms/controls/file.d.ts +3 -2
- package/@types/forms/controls/form-control.d.ts +10 -1
- package/@types/forms/controls/form-field-control.d.ts +3 -4
- package/@types/forms/controls/form-header.d.ts +4 -4
- package/@types/forms/controls/hidden-input.d.ts +2 -3
- package/@types/forms/controls/html.d.ts +3 -4
- package/@types/forms/controls/multiple-files.d.ts +3 -3
- package/@types/forms/controls/number.d.ts +2 -3
- package/@types/forms/controls/photo.d.ts +2 -2
- package/@types/forms/controls/simple-input.d.ts +5 -4
- package/@types/forms/controls/textarea.d.ts +2 -1
- package/@types/forms/controls/tickable.d.ts +2 -1
- package/@types/forms/fields/pick-from-list.d.ts +3 -6
- package/@types/page/mountable-with-fields.d.ts +3 -2
- package/lib/forms/controls/autocomplete.js +3 -3
- package/lib/forms/controls/autocomplete.js.map +1 -1
- package/lib/forms/controls/big-radio-group.js +4 -4
- package/lib/forms/controls/big-radio-group.js.map +1 -1
- package/lib/forms/controls/checkbox-group.js +5 -5
- package/lib/forms/controls/checkbox-group.js.map +1 -1
- package/lib/forms/controls/checkboxed-list-input.js +4 -4
- package/lib/forms/controls/checkboxed-list-input.js.map +1 -1
- package/lib/forms/controls/collapsible-checkbox-group.js +2 -2
- package/lib/forms/controls/collapsible-checkbox-group.js.map +1 -1
- package/lib/forms/controls/controls.js +3 -3
- package/lib/forms/controls/controls.js.map +1 -1
- package/lib/forms/controls/derived-frame.js +57 -0
- package/lib/forms/controls/derived-frame.js.map +1 -0
- package/lib/forms/controls/derived.js +15 -16
- package/lib/forms/controls/derived.js.map +1 -1
- package/lib/forms/controls/dropdown.js +23 -9
- package/lib/forms/controls/dropdown.js.map +1 -1
- package/lib/forms/controls/editable-collection-subset.js +6 -6
- package/lib/forms/controls/editable-collection-subset.js.map +1 -1
- package/lib/forms/controls/field-group.js +2 -2
- package/lib/forms/controls/field-group.js.map +1 -1
- package/lib/forms/controls/file.js +4 -4
- package/lib/forms/controls/file.js.map +1 -1
- package/lib/forms/controls/form-control.js +12 -1
- package/lib/forms/controls/form-control.js.map +1 -1
- package/lib/forms/controls/form-field-control.js.map +1 -1
- package/lib/forms/controls/form-header.js +2 -2
- package/lib/forms/controls/form-header.js.map +1 -1
- package/lib/forms/controls/hidden-input.js +3 -3
- package/lib/forms/controls/hidden-input.js.map +1 -1
- package/lib/forms/controls/html.js +2 -2
- package/lib/forms/controls/html.js.map +1 -1
- package/lib/forms/controls/multiple-files.js +4 -4
- package/lib/forms/controls/multiple-files.js.map +1 -1
- package/lib/forms/controls/number.js +2 -2
- package/lib/forms/controls/number.js.map +1 -1
- package/lib/forms/controls/photo.js +3 -3
- package/lib/forms/controls/photo.js.map +1 -1
- package/lib/forms/controls/simple-input.js +11 -12
- package/lib/forms/controls/simple-input.js.map +1 -1
- package/lib/forms/controls/textarea.js +2 -2
- package/lib/forms/controls/textarea.js.map +1 -1
- package/lib/forms/controls/tickable.js +11 -9
- package/lib/forms/controls/tickable.js.map +1 -1
- package/lib/forms/fields/field.js +1 -1
- package/lib/forms/fields/field.js.map +1 -1
- package/lib/forms/fields/pick-from-list.js +1 -1
- package/lib/forms/fields/pick-from-list.js.map +1 -1
- package/lib/forms/form.js +1 -1
- package/lib/forms/form.js.map +1 -1
- package/lib/page/list.js +1 -1
- package/lib/page/list.js.map +1 -1
- package/lib/page/mountable-with-fields.js +6 -2
- package/lib/page/mountable-with-fields.js.map +1 -1
- package/package.json +1 -1
- package/src/forms/controls/autocomplete.ts +5 -14
- package/src/forms/controls/big-radio-group.ts +9 -28
- package/src/forms/controls/checkbox-group.ts +10 -31
- package/src/forms/controls/checkboxed-list-input.ts +8 -4
- package/src/forms/controls/collapsible-checkbox-group.ts +4 -15
- package/src/forms/controls/controls.ts +2 -1
- package/src/forms/controls/derived-frame.ts +92 -0
- package/src/forms/controls/derived.ts +19 -18
- package/src/forms/controls/dropdown.ts +41 -29
- package/src/forms/controls/editable-collection-subset.ts +25 -25
- package/src/forms/controls/field-group.ts +3 -19
- package/src/forms/controls/file.ts +5 -12
- package/src/forms/controls/form-control.ts +12 -6
- package/src/forms/controls/form-field-control.ts +2 -7
- package/src/forms/controls/form-header.ts +6 -3
- package/src/forms/controls/hidden-input.ts +6 -6
- package/src/forms/controls/html.ts +4 -9
- package/src/forms/controls/multiple-files.ts +5 -10
- package/src/forms/controls/number.ts +3 -10
- package/src/forms/controls/photo.ts +7 -14
- package/src/forms/controls/simple-input.ts +21 -34
- package/src/forms/controls/textarea.ts +6 -14
- package/src/forms/controls/tickable.ts +15 -14
- package/src/forms/fields/field.ts +1 -1
- package/src/forms/fields/pick-from-list.ts +3 -4
- package/src/forms/form.ts +3 -1
- package/src/page/list.ts +7 -1
- package/src/page/mountable-with-fields.ts +23 -14
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import Router from "@koa/router";
|
|
2
|
+
import { hasShape, predicates } from "@sealcode/ts-predicates";
|
|
3
|
+
import { Context } from "koa";
|
|
4
|
+
import { ParsedUrlQuery } from "querystring";
|
|
5
|
+
import { FlatTemplatable, tempstream } from "tempstream";
|
|
6
|
+
import { makeSlug } from "../../utils/make-slug";
|
|
7
|
+
import { FormField } from "../fields/field";
|
|
8
|
+
import { Derived } from "./derived";
|
|
9
|
+
import { FormControlContext } from "./form-control";
|
|
10
|
+
|
|
11
|
+
export class DerivedFrame extends Derived<{
|
|
12
|
+
frame_id: string;
|
|
13
|
+
form_id: string;
|
|
14
|
+
field_name_prefix: string;
|
|
15
|
+
}> {
|
|
16
|
+
constructor(
|
|
17
|
+
public frame_name: string,
|
|
18
|
+
fields: FormField[],
|
|
19
|
+
public generateFrameContent: (
|
|
20
|
+
ctx: FormControlContext,
|
|
21
|
+
args: ParsedUrlQuery
|
|
22
|
+
) => Promise<FlatTemplatable>,
|
|
23
|
+
// "a" is to make it a valid DOM id
|
|
24
|
+
public frame_id = "a" + makeSlug(frame_name)
|
|
25
|
+
) {
|
|
26
|
+
super(
|
|
27
|
+
fields,
|
|
28
|
+
async (values, consts) => /* HTML */ `<turbo-frame
|
|
29
|
+
id="${consts.frame_id}"
|
|
30
|
+
src="./${consts.frame_id}?${Object.entries({
|
|
31
|
+
...consts,
|
|
32
|
+
...(values || {}),
|
|
33
|
+
})
|
|
34
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
35
|
+
.join("&")}"
|
|
36
|
+
></turbo-frame>`,
|
|
37
|
+
async (fctx) => ({
|
|
38
|
+
form_id: fctx.form_id,
|
|
39
|
+
field_name_prefix: fctx.field_name_prefix,
|
|
40
|
+
frame_id,
|
|
41
|
+
validate: fctx.validate,
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
renderBackendResponseFrame(_ctx: Context, content: FlatTemplatable) {
|
|
47
|
+
return tempstream/* HTML */ `<turbo-frame
|
|
48
|
+
${content ? "" : `src="${this.frame_id}"`}
|
|
49
|
+
id="${this.frame_id}"
|
|
50
|
+
target="_top"
|
|
51
|
+
>
|
|
52
|
+
${content || ""}
|
|
53
|
+
</turbo-frame>`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
mount(router: Router) {
|
|
57
|
+
router.get(this.frame_id, async (ctx) => {
|
|
58
|
+
const query = ctx.query;
|
|
59
|
+
if (
|
|
60
|
+
!hasShape(
|
|
61
|
+
{
|
|
62
|
+
field_name_prefix: predicates.string,
|
|
63
|
+
form_id: predicates.string,
|
|
64
|
+
validate: predicates.string,
|
|
65
|
+
},
|
|
66
|
+
query
|
|
67
|
+
)
|
|
68
|
+
) {
|
|
69
|
+
ctx.body = this.renderBackendResponseFrame(
|
|
70
|
+
ctx,
|
|
71
|
+
"Missing query params"
|
|
72
|
+
);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
ctx.body = this.renderBackendResponseFrame(
|
|
76
|
+
ctx,
|
|
77
|
+
await this.generateFrameContent(
|
|
78
|
+
new FormControlContext(
|
|
79
|
+
ctx,
|
|
80
|
+
ctx.query,
|
|
81
|
+
[],
|
|
82
|
+
query.field_name_prefix,
|
|
83
|
+
query.form_id,
|
|
84
|
+
query.validate === "true" ? true : false
|
|
85
|
+
),
|
|
86
|
+
ctx.query
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import { FlatTemplatable, tempstream } from "tempstream";
|
|
3
3
|
import { FormField } from "../fields/field";
|
|
4
|
-
import { FormControl } from "./form-control";
|
|
4
|
+
import { FormControl, FormControlContext } from "./form-control";
|
|
5
5
|
|
|
6
6
|
export class Derived<
|
|
7
|
-
Consts extends Record<string, unknown
|
|
7
|
+
Consts extends Record<string, unknown | undefined>
|
|
8
8
|
> extends FormControl {
|
|
9
9
|
role = <const>"decoration";
|
|
10
10
|
|
|
@@ -13,19 +13,24 @@ export class Derived<
|
|
|
13
13
|
public _render: (
|
|
14
14
|
values: Record<string, string>,
|
|
15
15
|
consts: Consts
|
|
16
|
-
) =>
|
|
17
|
-
public
|
|
16
|
+
) => Promise<FlatTemplatable>,
|
|
17
|
+
public getConsts: (fctx: FormControlContext) => Promise<Consts>
|
|
18
18
|
) {
|
|
19
19
|
super();
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
render(): FlatTemplatable {
|
|
23
|
-
|
|
22
|
+
async render(fctx: FormControlContext): Promise<FlatTemplatable> {
|
|
23
|
+
// the A makes it always a valid id, as it cannot start with a number
|
|
24
|
+
const id = "A" + randomUUID();
|
|
25
|
+
|
|
26
|
+
const consts = this.getConsts(fctx);
|
|
27
|
+
|
|
28
|
+
const frontend_side_refresh_code = `render(getValues(), consts).then(result=>document.querySelector("#${id}").innerHTML=result)`;
|
|
24
29
|
return tempstream/* HTML */ `<div>
|
|
25
30
|
<script>
|
|
26
31
|
(function () {
|
|
27
32
|
const consts = ${"{" +
|
|
28
|
-
Object.entries(
|
|
33
|
+
Object.entries((await consts) || {})
|
|
29
34
|
.map(([key, value]) => {
|
|
30
35
|
return `"${key}": ${
|
|
31
36
|
typeof value === "object"
|
|
@@ -61,24 +66,20 @@ export class Derived<
|
|
|
61
66
|
form_container
|
|
62
67
|
.querySelectorAll(\`input[name="\${field_name}"]\`)
|
|
63
68
|
.forEach((input) => {
|
|
64
|
-
input.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
};
|
|
69
|
+
input.addEventListener("change", () => {
|
|
70
|
+
${frontend_side_refresh_code};
|
|
71
|
+
});
|
|
68
72
|
});
|
|
69
73
|
}
|
|
70
74
|
${JSON.stringify(
|
|
71
75
|
this.fields.map((field) => field.name)
|
|
72
76
|
)}.forEach(setupWatcher);
|
|
73
|
-
setTimeout(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
render(getValues(), consts)),
|
|
77
|
-
0
|
|
78
|
-
);
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
${frontend_side_refresh_code};
|
|
79
|
+
}, 0);
|
|
79
80
|
})();
|
|
80
81
|
</script>
|
|
81
|
-
<div id="${id}">${this._render({},
|
|
82
|
+
<div id="${id}">${this._render({}, await consts)}</div>
|
|
82
83
|
</div>`;
|
|
83
84
|
}
|
|
84
85
|
}
|
|
@@ -1,38 +1,40 @@
|
|
|
1
1
|
import { Context } from "koa";
|
|
2
2
|
import { FlatTemplatable, tempstream } from "tempstream";
|
|
3
3
|
import { attribute } from "../../sanitize";
|
|
4
|
-
import { PickFromListField } from "../fields/field";
|
|
4
|
+
import { FormField, PickFromListField } from "../fields/field";
|
|
5
5
|
import { FormDataValue } from "../form";
|
|
6
|
+
import { FormControlContext } from "./form-control";
|
|
6
7
|
import { SimpleInput } from "./simple-input";
|
|
7
8
|
|
|
8
|
-
export
|
|
9
|
+
export type DropdownOptions = {
|
|
10
|
+
label: string;
|
|
11
|
+
autosubmit?: boolean;
|
|
12
|
+
autocomplete?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type DropdownOption = { value: string; label: string };
|
|
16
|
+
|
|
17
|
+
export class FreeformDropdown extends SimpleInput<string | null> {
|
|
9
18
|
constructor(
|
|
10
|
-
public field:
|
|
11
|
-
public
|
|
12
|
-
|
|
13
|
-
autosubmit?: boolean;
|
|
14
|
-
autocomplete?: boolean;
|
|
15
|
-
} = {
|
|
19
|
+
public field: FormField<boolean, string | null>,
|
|
20
|
+
public _generateOptions: (ctx: Context) => Promise<DropdownOption[]>,
|
|
21
|
+
public options: DropdownOptions = {
|
|
16
22
|
label: field.name,
|
|
17
23
|
autosubmit: false,
|
|
18
24
|
autocomplete: true,
|
|
19
25
|
}
|
|
20
26
|
) {
|
|
21
|
-
super(field);
|
|
27
|
+
super(field, options);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async generateOptions(ctx: Context): Promise<DropdownOption[]> {
|
|
31
|
+
return this._generateOptions(ctx);
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
async getInputAttributes(
|
|
25
|
-
|
|
26
|
-
data: Record<string, FormDataValue>,
|
|
27
|
-
field_prefix: string,
|
|
28
|
-
form_id: string
|
|
35
|
+
fctx: FormControlContext
|
|
29
36
|
): Promise<Record<string, string | boolean>> {
|
|
30
|
-
const original = await super.getInputAttributes(
|
|
31
|
-
ctx,
|
|
32
|
-
data,
|
|
33
|
-
field_prefix,
|
|
34
|
-
form_id
|
|
35
|
-
);
|
|
37
|
+
const original = await super.getInputAttributes(fctx);
|
|
36
38
|
return {
|
|
37
39
|
...original,
|
|
38
40
|
...Object.fromEntries([
|
|
@@ -58,18 +60,28 @@ export class Dropdown extends SimpleInput<string | null> {
|
|
|
58
60
|
return /* HTML */ tempstream`<select ${attributes_str}>
|
|
59
61
|
${
|
|
60
62
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
61
|
-
Promise.resolve(this.
|
|
62
|
-
(
|
|
63
|
-
|
|
64
|
-
(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
: ""
|
|
69
|
-
}>${text}</option>`
|
|
70
|
-
)
|
|
63
|
+
Promise.resolve(this.generateOptions(ctx)).then((options) =>
|
|
64
|
+
options.map(
|
|
65
|
+
({ value, label }) =>
|
|
66
|
+
`<option value="${attribute(value)}" ${
|
|
67
|
+
(value || "") == picked_value ? "selected" : ""
|
|
68
|
+
}>${label}</option>`
|
|
69
|
+
)
|
|
71
70
|
)
|
|
72
71
|
}
|
|
73
72
|
</select>`;
|
|
74
73
|
}
|
|
75
74
|
}
|
|
75
|
+
|
|
76
|
+
export class Dropdown extends FreeformDropdown {
|
|
77
|
+
constructor(
|
|
78
|
+
public field: PickFromListField<boolean>,
|
|
79
|
+
public options: DropdownOptions = {
|
|
80
|
+
label: field.name,
|
|
81
|
+
autosubmit: false,
|
|
82
|
+
autocomplete: true,
|
|
83
|
+
}
|
|
84
|
+
) {
|
|
85
|
+
super(field, (ctx) => field.generateOptions(ctx), options);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { Context } from "koa";
|
|
2
1
|
import { FlatTemplatable, tempstream } from "tempstream";
|
|
3
2
|
import { attribute } from "../../sanitize";
|
|
4
3
|
import { PickFromListField } from "../fields/field";
|
|
5
|
-
import { FormDataValue } from "../form";
|
|
6
4
|
import { FormFieldControl } from "./controls";
|
|
5
|
+
import { FormControlContext } from "./form-control";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* This control has own forms in it so if you want to use it you
|
|
@@ -22,15 +21,15 @@ export class EditableCollectionSubset extends FormFieldControl {
|
|
|
22
21
|
super([field]);
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
async render(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
async render(fctx: FormControlContext): Promise<FlatTemplatable> {
|
|
25
|
+
const { parsed: values } = await this.field.getValue(
|
|
26
|
+
fctx.ctx,
|
|
27
|
+
fctx.data
|
|
28
|
+
);
|
|
30
29
|
|
|
31
30
|
return tempstream/* HTML */ `<div>
|
|
32
31
|
<ul>
|
|
33
|
-
${Promise.resolve(this.field.generateOptions(ctx)).then(
|
|
32
|
+
${Promise.resolve(this.field.generateOptions(fctx.ctx)).then(
|
|
34
33
|
(options) =>
|
|
35
34
|
Object.entries(options)
|
|
36
35
|
.filter(([value]) =>
|
|
@@ -41,7 +40,7 @@ export class EditableCollectionSubset extends FormFieldControl {
|
|
|
41
40
|
<li>
|
|
42
41
|
<form
|
|
43
42
|
method="POST"
|
|
44
|
-
action="${ctx.path}"
|
|
43
|
+
action="${fctx.ctx.path}"
|
|
45
44
|
>
|
|
46
45
|
<span>${text}</span>
|
|
47
46
|
<input
|
|
@@ -66,23 +65,24 @@ export class EditableCollectionSubset extends FormFieldControl {
|
|
|
66
65
|
)
|
|
67
66
|
)}
|
|
68
67
|
</ul>
|
|
69
|
-
<form method="POST" action="${ctx.path}">
|
|
68
|
+
<form method="POST" action="${fctx.ctx.path}">
|
|
70
69
|
<select name="${this.field.name}">
|
|
71
|
-
${Promise.resolve(
|
|
72
|
-
(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
70
|
+
${Promise.resolve(
|
|
71
|
+
this.field.generateOptions(fctx.ctx)
|
|
72
|
+
).then((options) =>
|
|
73
|
+
Object.entries(options)
|
|
74
|
+
.filter(
|
|
75
|
+
([value]) =>
|
|
76
|
+
!(values || ([] as string[])).includes(
|
|
77
|
+
value
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
.map(
|
|
81
|
+
([value, text]) =>
|
|
82
|
+
`<option value="${attribute(
|
|
83
|
+
value
|
|
84
|
+
)}">${text}</option>`
|
|
85
|
+
)
|
|
86
86
|
)}
|
|
87
87
|
</select>
|
|
88
88
|
<input type="hidden" name="${this.actionname}" value="select" />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Context } from "koa";
|
|
2
2
|
import { FlatTemplatable, tempstream } from "tempstream";
|
|
3
3
|
import { FormDataValue, FormMessage } from "../form";
|
|
4
|
-
import { FormControl } from "./form-control";
|
|
4
|
+
import { FormControl, FormControlContext } from "./form-control";
|
|
5
5
|
import { FormFieldControl } from "./form-field-control";
|
|
6
6
|
|
|
7
7
|
export class FieldGroup extends FormFieldControl {
|
|
@@ -40,14 +40,7 @@ export class FieldGroup extends FormFieldControl {
|
|
|
40
40
|
return `class="field-group__label"`;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
async render(
|
|
44
|
-
ctx: Context,
|
|
45
|
-
data: Record<string, FormDataValue>,
|
|
46
|
-
messages: FormMessage[],
|
|
47
|
-
field_prefix: string,
|
|
48
|
-
form_id: string,
|
|
49
|
-
show_field_errors: boolean
|
|
50
|
-
): Promise<FlatTemplatable> {
|
|
43
|
+
async render(fctx: FormControlContext): Promise<FlatTemplatable> {
|
|
51
44
|
const { label, classes } = this.options;
|
|
52
45
|
|
|
53
46
|
return tempstream/* HTML */ `
|
|
@@ -67,16 +60,7 @@ export class FieldGroup extends FormFieldControl {
|
|
|
67
60
|
</div>
|
|
68
61
|
${this.preFields()}
|
|
69
62
|
<div class="field-group__fields">
|
|
70
|
-
${this.controls.map((control) =>
|
|
71
|
-
control.render(
|
|
72
|
-
ctx,
|
|
73
|
-
data,
|
|
74
|
-
messages,
|
|
75
|
-
field_prefix,
|
|
76
|
-
form_id,
|
|
77
|
-
show_field_errors
|
|
78
|
-
)
|
|
79
|
-
)}
|
|
63
|
+
${this.controls.map((control) => control.render(fctx))}
|
|
80
64
|
</div>
|
|
81
65
|
${this.postFields()}
|
|
82
66
|
</div>
|
|
@@ -2,6 +2,7 @@ import { Context } from "koa";
|
|
|
2
2
|
import { FlatTemplatable, tempstream } from "tempstream";
|
|
3
3
|
import { FileContainer } from "../fields/file";
|
|
4
4
|
import { FormDataValue } from "../form";
|
|
5
|
+
import { FormControlContext } from "./form-control";
|
|
5
6
|
import { SimpleInput } from "./simple-input";
|
|
6
7
|
|
|
7
8
|
export class File extends SimpleInput<FileContainer> {
|
|
@@ -10,7 +11,7 @@ export class File extends SimpleInput<FileContainer> {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
async renderFilePreview(
|
|
13
|
-
|
|
14
|
+
_ctx: Context,
|
|
14
15
|
file_data: { id?: string; filename?: string } | null
|
|
15
16
|
): Promise<FlatTemplatable> {
|
|
16
17
|
return file_data?.filename || "";
|
|
@@ -32,18 +33,10 @@ export class File extends SimpleInput<FileContainer> {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
async getInputAttributes(
|
|
35
|
-
|
|
36
|
-
data: Record<string, FormDataValue>,
|
|
37
|
-
field_prefix: string,
|
|
38
|
-
form_id: string
|
|
36
|
+
fctx: FormControlContext
|
|
39
37
|
): Promise<Record<string, string | boolean>> {
|
|
40
|
-
const original = await super.getInputAttributes(
|
|
41
|
-
|
|
42
|
-
data,
|
|
43
|
-
field_prefix,
|
|
44
|
-
form_id
|
|
45
|
-
);
|
|
46
|
-
const { parsed } = await this.field.getValue(ctx, data);
|
|
38
|
+
const original = await super.getInputAttributes(fctx);
|
|
39
|
+
const { parsed } = await this.field.getValue(fctx.ctx, fctx.data);
|
|
47
40
|
return {
|
|
48
41
|
...original,
|
|
49
42
|
name: this.field.name + "[new]",
|
|
@@ -3,14 +3,20 @@ import { Context } from "koa";
|
|
|
3
3
|
import { FlatTemplatable } from "tempstream";
|
|
4
4
|
import { FormDataValue, FormMessage } from "../form";
|
|
5
5
|
|
|
6
|
+
export class FormControlContext {
|
|
7
|
+
constructor(
|
|
8
|
+
public ctx: Context,
|
|
9
|
+
public data: Record<string, FormDataValue>,
|
|
10
|
+
public messages: FormMessage[],
|
|
11
|
+
public field_name_prefix: string,
|
|
12
|
+
public form_id: string,
|
|
13
|
+
public validate: boolean
|
|
14
|
+
) {}
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
export abstract class FormControl {
|
|
7
18
|
abstract render(
|
|
8
|
-
|
|
9
|
-
data: Record<string, FormDataValue>,
|
|
10
|
-
messages: FormMessage[],
|
|
11
|
-
field_name_prefix: string,
|
|
12
|
-
form_id: string,
|
|
13
|
-
validate: boolean
|
|
19
|
+
fctx: FormControlContext
|
|
14
20
|
): FlatTemplatable | Promise<FlatTemplatable>;
|
|
15
21
|
abstract role: "input" | "decoration" | "messages" | "submit";
|
|
16
22
|
|
|
@@ -2,7 +2,7 @@ import { Context } from "koa";
|
|
|
2
2
|
import { FlatTemplatable } from "tempstream";
|
|
3
3
|
import { FormField } from "../fields/field";
|
|
4
4
|
import { FieldValueType, FormDataValue } from "../form";
|
|
5
|
-
import { FormControl } from "./form-control";
|
|
5
|
+
import { FormControl, FormControlContext } from "./form-control";
|
|
6
6
|
|
|
7
7
|
export type FormFieldsToTypes<FormFields extends readonly FormField[]> = {
|
|
8
8
|
[Index in keyof FormFields]: FormFields[Index] extends FormField
|
|
@@ -17,11 +17,6 @@ export abstract class FormFieldControl extends FormControl {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
abstract render(
|
|
20
|
-
|
|
21
|
-
data: Record<string, FormDataValue>,
|
|
22
|
-
messages: [],
|
|
23
|
-
field_name_prefix: string,
|
|
24
|
-
form_id: string,
|
|
25
|
-
validate: boolean
|
|
20
|
+
fctx: FormControlContext
|
|
26
21
|
): FlatTemplatable | Promise<FlatTemplatable>;
|
|
27
22
|
}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { Context } from "koa";
|
|
2
2
|
import { FormControl } from "./controls";
|
|
3
|
+
import { FormControlContext } from "./form-control";
|
|
3
4
|
|
|
4
5
|
export class FormHeader extends FormControl {
|
|
5
6
|
role = <const>"decoration";
|
|
6
7
|
constructor(
|
|
7
8
|
public text: string,
|
|
8
|
-
public isVisible: (
|
|
9
|
+
public isVisible: (
|
|
10
|
+
fctx: FormControlContext
|
|
11
|
+
) => Promise<boolean> = async () => true
|
|
9
12
|
) {
|
|
10
13
|
super();
|
|
11
14
|
}
|
|
12
|
-
async render(
|
|
13
|
-
const isVsbl = await this.isVisible(
|
|
15
|
+
async render(fctx: FormControlContext) {
|
|
16
|
+
const isVsbl = await this.isVisible(fctx);
|
|
14
17
|
return isVsbl ? `<h2>${this.text}</h2>` : "";
|
|
15
18
|
}
|
|
16
19
|
}
|
|
@@ -3,6 +3,7 @@ import { FlatTemplatable, tempstream } from "tempstream";
|
|
|
3
3
|
import { attribute } from "../../sanitize";
|
|
4
4
|
import { FormField } from "../fields/field";
|
|
5
5
|
import { FormDataValue } from "../form";
|
|
6
|
+
import { FormControlContext } from "./form-control";
|
|
6
7
|
import { FormFieldControl } from "./form-field-control";
|
|
7
8
|
|
|
8
9
|
export class HiddenInput extends FormFieldControl {
|
|
@@ -10,13 +11,12 @@ export class HiddenInput extends FormFieldControl {
|
|
|
10
11
|
super([field]);
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
async render(
|
|
14
|
-
ctx
|
|
15
|
-
data: Record<string, FormDataValue>
|
|
16
|
-
): Promise<FlatTemplatable> {
|
|
17
|
-
const { raw } = await this.field.getValue(ctx, data);
|
|
14
|
+
async render(fctx: FormControlContext): Promise<FlatTemplatable> {
|
|
15
|
+
const { raw } = await this.field.getValue(fctx.ctx, fctx.data);
|
|
18
16
|
return tempstream`<input type="hidden" name="${
|
|
19
17
|
this.field.name
|
|
20
|
-
}" value="${attribute((raw || "").toString())}"
|
|
18
|
+
}" value="${attribute((raw || "").toString())}" form="${
|
|
19
|
+
fctx.form_id
|
|
20
|
+
}">`;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -2,9 +2,10 @@ import { Context } from "koa";
|
|
|
2
2
|
import { FlatTemplatable } from "tempstream";
|
|
3
3
|
import { FormDataValue, FormMessage } from "../form";
|
|
4
4
|
import { FormControl } from "./controls";
|
|
5
|
+
import { FormControlContext } from "./form-control";
|
|
5
6
|
|
|
6
7
|
type Renderer =
|
|
7
|
-
| ((
|
|
8
|
+
| ((fctx: FormControlContext) => FlatTemplatable)
|
|
8
9
|
| FlatTemplatable;
|
|
9
10
|
|
|
10
11
|
export class HTML extends FormControl {
|
|
@@ -26,18 +27,12 @@ export class HTML extends FormControl {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
async render(
|
|
30
|
-
ctx: Context,
|
|
31
|
-
_data: Record<string, FormDataValue>,
|
|
32
|
-
_messages: FormMessage[],
|
|
33
|
-
_field_name_prefix: string,
|
|
34
|
-
form_id: string
|
|
35
|
-
): Promise<FlatTemplatable> {
|
|
30
|
+
async render(fctx: FormControlContext): Promise<FlatTemplatable> {
|
|
36
31
|
if (typeof this.renderer === "string") {
|
|
37
32
|
return this.renderer;
|
|
38
33
|
}
|
|
39
34
|
return typeof this.renderer == "function"
|
|
40
|
-
? this.renderer(
|
|
35
|
+
? this.renderer(fctx)
|
|
41
36
|
: this.renderer;
|
|
42
37
|
}
|
|
43
38
|
}
|
|
@@ -9,10 +9,10 @@ import { Context } from "koa";
|
|
|
9
9
|
import { inputWrapper } from "../../utils/input-wrapper";
|
|
10
10
|
import { FlatTemplatable, tempstream } from "tempstream";
|
|
11
11
|
import { MultipleFiles as MultipleFilesField } from "../fields/multiple-files";
|
|
12
|
-
import { FormDataValue } from "../form";
|
|
13
12
|
import { FormFieldControl } from "./form-field-control";
|
|
14
13
|
import { is, predicates } from "@sealcode/ts-predicates";
|
|
15
14
|
import { renderAttributes } from "../../utils/render-attributes";
|
|
15
|
+
import { FormControlContext } from "./form-control";
|
|
16
16
|
|
|
17
17
|
export type MultipleFilesOptions = {
|
|
18
18
|
label?: string;
|
|
@@ -122,14 +122,9 @@ export class MultipleFiles extends FormFieldControl {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
render(
|
|
125
|
-
|
|
126
|
-
_data: Record<string, FormDataValue>,
|
|
127
|
-
_messages: [],
|
|
128
|
-
field_name_prefix: string,
|
|
129
|
-
form_id: string,
|
|
130
|
-
validate: boolean
|
|
125
|
+
fctx: FormControlContext
|
|
131
126
|
): FlatTemplatable | Promise<FlatTemplatable> {
|
|
132
|
-
return this.renderFrame(ctx, "");
|
|
127
|
+
return this.renderFrame(fctx.ctx, "");
|
|
133
128
|
}
|
|
134
129
|
|
|
135
130
|
getReferencingCollection() {
|
|
@@ -141,7 +136,7 @@ export class MultipleFiles extends FormFieldControl {
|
|
|
141
136
|
}
|
|
142
137
|
|
|
143
138
|
getFileField(): SealiousField | null {
|
|
144
|
-
for (const [
|
|
139
|
+
for (const [_, field] of Object.entries(
|
|
145
140
|
this.getReferencingCollection().fields
|
|
146
141
|
)) {
|
|
147
142
|
if (field.handles_large_data) {
|
|
@@ -165,7 +160,7 @@ export class MultipleFiles extends FormFieldControl {
|
|
|
165
160
|
}
|
|
166
161
|
|
|
167
162
|
async renderFileItemPreview(
|
|
168
|
-
|
|
163
|
+
_fileItem: CollectionItem,
|
|
169
164
|
file: SealiousFile
|
|
170
165
|
): Promise<FlatTemplatable> {
|
|
171
166
|
return file.filename || "no filename";
|
|
@@ -2,6 +2,7 @@ import { Context } from "koa";
|
|
|
2
2
|
import { FlatTemplatable } from "tempstream";
|
|
3
3
|
import { FormField, NumberField } from "../fields/field";
|
|
4
4
|
import { FormDataValue } from "../form";
|
|
5
|
+
import { FormControlContext } from "./form-control";
|
|
5
6
|
import { SimpleInput, SimpleInputOptions } from "./simple-input";
|
|
6
7
|
|
|
7
8
|
export type NumberOptions = SimpleInputOptions & Partial<{ step: number }>;
|
|
@@ -18,17 +19,9 @@ export class Number extends SimpleInput<number | null> {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
async getInputAttributes(
|
|
21
|
-
|
|
22
|
-
data: Record<string, FormDataValue>,
|
|
23
|
-
field_prefix: string,
|
|
24
|
-
form_id: string
|
|
22
|
+
fctx: FormControlContext
|
|
25
23
|
): Promise<Record<string, string | boolean>> {
|
|
26
|
-
const original = await super.getInputAttributes(
|
|
27
|
-
ctx,
|
|
28
|
-
data,
|
|
29
|
-
field_prefix,
|
|
30
|
-
form_id
|
|
31
|
-
);
|
|
24
|
+
const original = await super.getInputAttributes(fctx);
|
|
32
25
|
return {
|
|
33
26
|
...original,
|
|
34
27
|
...Object.fromEntries([
|
|
@@ -5,7 +5,7 @@ import { File as SealiousFile } from "sealious";
|
|
|
5
5
|
import { FileContainer, FormField } from "../fields/field";
|
|
6
6
|
import { SimpleInputOptions } from "./simple-input";
|
|
7
7
|
import KoaResponsiveImageRouter from "koa-responsive-image-router";
|
|
8
|
-
import {
|
|
8
|
+
import { FormControlContext } from "./form-control";
|
|
9
9
|
|
|
10
10
|
export class Photo extends File {
|
|
11
11
|
constructor(
|
|
@@ -44,19 +44,12 @@ export class Photo extends File {
|
|
|
44
44
|
</div>`;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
async getInputAttributes(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const original = (await super.getInputAttributes(
|
|
54
|
-
ctx,
|
|
55
|
-
data,
|
|
56
|
-
field_prefix,
|
|
57
|
-
form_id
|
|
58
|
-
)) as Record<string, string | boolean>;
|
|
59
|
-
const { parsed } = await this.field.getValue(ctx, data);
|
|
47
|
+
async getInputAttributes(fctx: FormControlContext) {
|
|
48
|
+
const original = (await super.getInputAttributes(fctx)) as Record<
|
|
49
|
+
string,
|
|
50
|
+
string | boolean
|
|
51
|
+
>;
|
|
52
|
+
const { parsed } = await this.field.getValue(fctx.ctx, fctx.data);
|
|
60
53
|
return {
|
|
61
54
|
...original,
|
|
62
55
|
accept: "image/*",
|