@sealcode/sealgen 0.4.1 → 0.5.1
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 +2 -2
- package/@types/forms/controls/controls.d.ts +1 -0
- package/@types/forms/controls/dropdown.d.ts +2 -2
- package/@types/forms/controls/editable-collection-subset.d.ts +2 -2
- package/@types/forms/controls/form-control.d.ts +2 -2
- package/@types/forms/controls/form-field-control.d.ts +2 -2
- package/@types/forms/controls/form-header.d.ts +4 -4
- package/@types/forms/controls/html.d.ts +9 -0
- package/@types/forms/controls/simple-input.d.ts +4 -2
- package/@types/forms/fields/checkboxed-list.d.ts +7 -7
- package/@types/forms/fields/collection-field.d.ts +3 -3
- package/@types/forms/fields/field.d.ts +6 -6
- package/@types/forms/fields/number.d.ts +3 -3
- package/@types/forms/fields/pick-from-list.d.ts +7 -7
- package/@types/forms/fields/simple-form-field.d.ts +2 -2
- package/@types/forms/form.d.ts +10 -10
- package/@types/index.d.ts +5 -3
- package/@types/make-hidden-inputs.d.ts +5 -4
- package/@types/page/access-control.d.ts +2 -2
- package/@types/page/list.d.ts +14 -14
- package/@types/page/mountable.d.ts +9 -9
- package/@types/page/page.d.ts +6 -6
- package/@types/page/props-parser.d.ts +4 -4
- package/@types/utils/translate.d.ts +3 -0
- package/@types/utils/url-with-new-params.d.ts +2 -2
- package/lib/build.js +4 -3
- package/lib/build.js.map +1 -1
- package/lib/forms/controls/checkboxed-list-input.js.map +1 -1
- package/lib/forms/controls/controls.js +3 -1
- package/lib/forms/controls/controls.js.map +1 -1
- package/lib/forms/controls/dropdown.js.map +1 -1
- package/lib/forms/controls/editable-collection-subset.js.map +1 -1
- package/lib/forms/controls/form-header.js.map +1 -1
- package/lib/forms/controls/html.js +19 -0
- package/lib/forms/controls/html.js.map +1 -0
- package/lib/forms/controls/simple-input.js +4 -2
- package/lib/forms/controls/simple-input.js.map +1 -1
- package/lib/forms/fields/checkboxed-list.js.map +1 -1
- package/lib/forms/fields/collection-field.js.map +1 -1
- package/lib/forms/fields/field.js +5 -1
- package/lib/forms/fields/field.js.map +1 -1
- package/lib/forms/fields/number.js.map +1 -1
- package/lib/forms/fields/pick-from-list.js.map +1 -1
- package/lib/forms/fields/simple-form-field.js.map +1 -1
- package/lib/forms/form.js.map +1 -1
- package/lib/generate-routes.js +22 -13
- package/lib/generate-routes.js.map +1 -1
- package/lib/index.js +16 -2
- package/lib/index.js.map +1 -1
- package/lib/make-hidden-inputs.js.map +1 -1
- package/lib/page/access-control.js.map +1 -1
- package/lib/page/list.js +2 -1
- package/lib/page/list.js.map +1 -1
- package/lib/page/mountable.js.map +1 -1
- package/lib/page/page.js.map +1 -1
- package/lib/page/props-parser.js.map +1 -1
- package/lib/templates/form.js +9 -16
- package/lib/templates/form.js.map +1 -1
- package/lib/templates/list.js +4 -4
- package/lib/templates/list.js.map +1 -1
- package/lib/templates/page.js +3 -3
- package/lib/templates/route-test.js +1 -1
- package/lib/templates/route-test.js.map +1 -1
- package/lib/utils/translate.js +10 -0
- package/lib/utils/translate.js.map +1 -0
- package/lib/utils/url-with-new-params.js.map +1 -1
- package/package.json +3 -3
- package/src/build.ts +4 -3
- package/src/forms/controls/checkboxed-list-input.ts +2 -2
- package/src/forms/controls/controls.ts +1 -0
- package/src/forms/controls/dropdown.ts +2 -2
- package/src/forms/controls/editable-collection-subset.ts +2 -2
- package/src/forms/controls/form-control.ts +2 -2
- package/src/forms/controls/form-field-control.ts +2 -2
- package/src/forms/controls/form-header.ts +3 -3
- package/src/forms/controls/html.ts +18 -0
- package/src/forms/controls/simple-input.ts +7 -6
- package/src/forms/fields/checkboxed-list.ts +5 -5
- package/src/forms/fields/collection-field.ts +3 -3
- package/src/forms/fields/field.ts +17 -7
- package/src/forms/fields/number.ts +3 -3
- package/src/forms/fields/pick-from-list.ts +5 -5
- package/src/forms/fields/simple-form-field.ts +2 -2
- package/src/forms/form.ts +10 -10
- package/src/generate-routes.ts +34 -13
- package/src/index.ts +5 -3
- package/src/make-hidden-inputs.ts +6 -5
- package/src/page/access-control.ts +2 -2
- package/src/page/list.ts +17 -24
- package/src/page/mountable.ts +11 -11
- package/src/page/page.ts +5 -5
- package/src/page/props-parser.ts +4 -7
- package/src/templates/form.ts +9 -16
- package/src/templates/list.ts +4 -4
- package/src/templates/page.ts +3 -3
- package/src/templates/route-test.ts +1 -1
- package/src/utils/translate.ts +13 -0
- package/src/utils/url-with-new-params.ts +2 -2
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import { FlatTemplatable } from "tempstream";
|
|
3
3
|
import { FormDataValue, FormMessage } from "../form";
|
|
4
4
|
|
|
5
5
|
export abstract class FormControl {
|
|
6
6
|
abstract render(
|
|
7
|
-
ctx:
|
|
7
|
+
ctx: Context,
|
|
8
8
|
data: Record<string, FormDataValue>,
|
|
9
9
|
messages: FormMessage[]
|
|
10
10
|
): FlatTemplatable | Promise<FlatTemplatable>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import { FlatTemplatable } from "tempstream";
|
|
3
3
|
import { FormField } from "../fields/field";
|
|
4
4
|
import { FieldValueType, FormDataValue } from "../form";
|
|
@@ -17,7 +17,7 @@ export abstract class FormFieldControl extends FormControl {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
abstract render(
|
|
20
|
-
ctx:
|
|
20
|
+
ctx: Context,
|
|
21
21
|
data: Record<string, FormDataValue>
|
|
22
22
|
): FlatTemplatable | Promise<FlatTemplatable>;
|
|
23
23
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import { FormControl } from "./controls";
|
|
3
3
|
|
|
4
4
|
export class FormHeader extends FormControl {
|
|
5
5
|
role = <const>"decoration";
|
|
6
6
|
constructor(
|
|
7
7
|
public text: string,
|
|
8
|
-
public isVisible: (ctx:
|
|
8
|
+
public isVisible: (ctx: Context) => Promise<boolean> = async () => true
|
|
9
9
|
) {
|
|
10
10
|
super();
|
|
11
11
|
}
|
|
12
|
-
async render(ctx:
|
|
12
|
+
async render(ctx: Context) {
|
|
13
13
|
const isVsbl = await this.isVisible(ctx);
|
|
14
14
|
return isVsbl ? `<h2>${this.text}</h2>` : "";
|
|
15
15
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Context } from "koa";
|
|
2
|
+
import { FlatTemplatable } from "tempstream";
|
|
3
|
+
import { FormControl } from "./controls";
|
|
4
|
+
|
|
5
|
+
export class HTML extends FormControl {
|
|
6
|
+
role = <const>"decoration";
|
|
7
|
+
|
|
8
|
+
constructor(public renderer: ((ctx: Context) => FlatTemplatable) | string) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async render(ctx: Context): Promise<FlatTemplatable> {
|
|
13
|
+
if (typeof this.renderer === "string") {
|
|
14
|
+
return this.renderer;
|
|
15
|
+
}
|
|
16
|
+
return this.renderer(ctx);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import { FormField } from "../fields/field";
|
|
3
|
-
import { NumberField } from "../fields/number";
|
|
4
|
-
import { SimpleFormField } from "../fields/simple-form-field";
|
|
5
3
|
import { FormDataValue } from "../form";
|
|
6
4
|
import { FormFieldControl } from "./controls";
|
|
7
5
|
|
|
@@ -12,6 +10,7 @@ export class SimpleInput extends FormFieldControl {
|
|
|
12
10
|
id?: string;
|
|
13
11
|
label?: string;
|
|
14
12
|
autocomplete?: boolean;
|
|
13
|
+
hide_errors?: boolean;
|
|
15
14
|
type?:
|
|
16
15
|
| "color"
|
|
17
16
|
| "date"
|
|
@@ -36,7 +35,7 @@ export class SimpleInput extends FormFieldControl {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
async render<Fields extends Record<string, FormField>>(
|
|
39
|
-
ctx:
|
|
38
|
+
ctx: Context,
|
|
40
39
|
data: Record<string, FormDataValue>
|
|
41
40
|
) {
|
|
42
41
|
const id = this.options.id || this.field.name;
|
|
@@ -52,7 +51,7 @@ export class SimpleInput extends FormFieldControl {
|
|
|
52
51
|
const readonly = this.options.readonly || false;
|
|
53
52
|
const required = this.field.required;
|
|
54
53
|
|
|
55
|
-
return /* HTML */ `<div class="input">
|
|
54
|
+
return /* HTML */ `<div class="form-input form-input__wrapper">
|
|
56
55
|
<label for="${id}">${label}</label>
|
|
57
56
|
<input
|
|
58
57
|
id="${id}"
|
|
@@ -70,7 +69,9 @@ export class SimpleInput extends FormFieldControl {
|
|
|
70
69
|
${!this.options.autocomplete ? `autocomplete="off"` : ""}
|
|
71
70
|
${this.options.step ? `step="${this.options.step}"` : ""}
|
|
72
71
|
/>
|
|
73
|
-
${~valid
|
|
72
|
+
${~valid && !this.options.hide_errors
|
|
73
|
+
? `<div class="input__error">${message}</div>`
|
|
74
|
+
: ""}
|
|
74
75
|
</div>`;
|
|
75
76
|
}
|
|
76
77
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { is, predicates } from "@sealcode/ts-predicates";
|
|
2
|
-
import {
|
|
2
|
+
import { Context } from "koa";
|
|
3
3
|
import { FormDataValue } from "../form";
|
|
4
4
|
import {
|
|
5
5
|
FieldParseResult,
|
|
@@ -11,16 +11,16 @@ export class ChekboxedListField extends FormField<Record<string, "on">> {
|
|
|
11
11
|
constructor(
|
|
12
12
|
public required: boolean = false,
|
|
13
13
|
public generateOptions: (
|
|
14
|
-
ctx:
|
|
14
|
+
ctx: Context
|
|
15
15
|
) => Promise<Record<string, string> | { [i: string]: string }>,
|
|
16
|
-
public isVisible: (ctx:
|
|
16
|
+
public isVisible: (ctx: Context) => Promise<boolean> = () =>
|
|
17
17
|
Promise.resolve(true)
|
|
18
18
|
) {
|
|
19
19
|
super(required);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
public async isValueValid(
|
|
23
|
-
_:
|
|
23
|
+
_: Context,
|
|
24
24
|
value: unknown
|
|
25
25
|
): Promise<FormFieldValidationResponse> {
|
|
26
26
|
if (is(value, predicates.string)) {
|
|
@@ -33,7 +33,7 @@ export class ChekboxedListField extends FormField<Record<string, "on">> {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
async parse(
|
|
36
|
-
_:
|
|
36
|
+
_: Context,
|
|
37
37
|
raw_value: FormDataValue
|
|
38
38
|
): Promise<FieldParseResult<Record<string, "on">>> {
|
|
39
39
|
if (is(raw_value, predicates.object)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import { Depromisify, Field, FieldOutput } from "sealious";
|
|
3
3
|
import { FormDataValue } from "../form";
|
|
4
4
|
|
|
@@ -16,7 +16,7 @@ export class CollectionField<F extends Field> extends FormField<
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async parse(
|
|
19
|
-
ctx:
|
|
19
|
+
ctx: Context,
|
|
20
20
|
raw_value: FormDataValue
|
|
21
21
|
): Promise<FieldParseResult<FieldOutput<F>>> {
|
|
22
22
|
const encoded_value = (await this.sealiousField.encode(
|
|
@@ -37,7 +37,7 @@ export class CollectionField<F extends Field> extends FormField<
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
public async isValueValid(
|
|
40
|
-
ctx:
|
|
40
|
+
ctx: Context,
|
|
41
41
|
value: FieldOutput<F>
|
|
42
42
|
): Promise<FormFieldValidationResponse> {
|
|
43
43
|
const { valid, reason } = await this.sealiousField.checkValue(
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
|
+
import { t } from "../../utils/translate";
|
|
2
3
|
import { FormDataValue } from "../form";
|
|
3
4
|
|
|
4
5
|
export type FormFieldValidationResponse = { valid: boolean; message: string };
|
|
5
6
|
|
|
6
7
|
export type FormFieldValidationFn<ValueType> = (
|
|
7
|
-
ctx:
|
|
8
|
+
ctx: Context,
|
|
8
9
|
value: ValueType,
|
|
9
10
|
field: FormField<ValueType>
|
|
10
11
|
) => Promise<FormFieldValidationResponse>;
|
|
@@ -28,14 +29,23 @@ export abstract class FormField<ParsedValue extends unknown = unknown> {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
public async _validate(
|
|
31
|
-
ctx:
|
|
32
|
+
ctx: Context,
|
|
32
33
|
value: ParsedValue
|
|
33
34
|
): Promise<FormFieldValidationResponse> {
|
|
34
35
|
if (
|
|
35
36
|
this.required &&
|
|
36
37
|
(value === "" || value === null || value === undefined)
|
|
37
38
|
) {
|
|
38
|
-
return {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
|
|
42
|
+
message: t(
|
|
43
|
+
ctx,
|
|
44
|
+
"field_is_required",
|
|
45
|
+
[],
|
|
46
|
+
"This field is required"
|
|
47
|
+
),
|
|
48
|
+
};
|
|
39
49
|
}
|
|
40
50
|
return this.isValueValid(ctx, value);
|
|
41
51
|
}
|
|
@@ -43,19 +53,19 @@ export abstract class FormField<ParsedValue extends unknown = unknown> {
|
|
|
43
53
|
public abstract getEmptyValue(): ParsedValue;
|
|
44
54
|
|
|
45
55
|
public async isValueValid(
|
|
46
|
-
ctx:
|
|
56
|
+
ctx: Context,
|
|
47
57
|
value: ParsedValue
|
|
48
58
|
): Promise<FormFieldValidationResponse> {
|
|
49
59
|
return { valid: true, message: "" };
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
abstract parse(
|
|
53
|
-
ctx:
|
|
63
|
+
ctx: Context,
|
|
54
64
|
raw_value: FormDataValue
|
|
55
65
|
): Promise<FieldParseResult<ParsedValue>>;
|
|
56
66
|
|
|
57
67
|
async getValue(
|
|
58
|
-
ctx:
|
|
68
|
+
ctx: Context,
|
|
59
69
|
all_form_values: Record<string, FormDataValue>
|
|
60
70
|
): Promise<{
|
|
61
71
|
valid: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { is, predicates } from "@sealcode/ts-predicates";
|
|
2
|
-
import {
|
|
2
|
+
import { Context } from "koa";
|
|
3
3
|
import { FormDataValue } from "../form";
|
|
4
4
|
import { FieldParseResult, FormField } from "./field";
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@ export class NumberField<
|
|
|
10
10
|
super(required);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
public async isValueValid(_:
|
|
13
|
+
public async isValueValid(_: Context, value: unknown) {
|
|
14
14
|
if (
|
|
15
15
|
(is(value, predicates.string) &&
|
|
16
16
|
!isNaN(parseFloat(value)) &&
|
|
@@ -24,7 +24,7 @@ export class NumberField<
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async parse(
|
|
27
|
-
_:
|
|
27
|
+
_: Context,
|
|
28
28
|
value: FormDataValue
|
|
29
29
|
): Promise<FieldParseResult<number>> {
|
|
30
30
|
if (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { is, predicates } from "@sealcode/ts-predicates";
|
|
2
|
-
import {
|
|
2
|
+
import { Context } from "koa";
|
|
3
3
|
import { FormFieldValidationResponse } from "./field";
|
|
4
4
|
import { SimpleFormField } from "./simple-form-field";
|
|
5
5
|
|
|
@@ -7,10 +7,10 @@ export class PickFromListField extends SimpleFormField {
|
|
|
7
7
|
constructor(
|
|
8
8
|
public required: boolean = false,
|
|
9
9
|
public generateOptions: (
|
|
10
|
-
ctx:
|
|
10
|
+
ctx: Context
|
|
11
11
|
) => Promise<Record<string, string> | { [i: string]: string }>,
|
|
12
12
|
public customValidation: (
|
|
13
|
-
ctx:
|
|
13
|
+
ctx: Context,
|
|
14
14
|
value: unknown,
|
|
15
15
|
instance: PickFromListField
|
|
16
16
|
) => Promise<FormFieldValidationResponse> = (ctx, value, instance) =>
|
|
@@ -20,14 +20,14 @@ export class PickFromListField extends SimpleFormField {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
public isValueValid(
|
|
23
|
-
ctx:
|
|
23
|
+
ctx: Context,
|
|
24
24
|
value: string
|
|
25
25
|
): Promise<FormFieldValidationResponse> {
|
|
26
26
|
return this.customValidation(ctx, value, this);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async valueInList(
|
|
30
|
-
ctx:
|
|
30
|
+
ctx: Context,
|
|
31
31
|
value: unknown
|
|
32
32
|
): Promise<FormFieldValidationResponse> {
|
|
33
33
|
const options = await this.generateOptions(ctx);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { FieldParseResult, FormField } from "./field";
|
|
2
2
|
import { FormDataValue } from "../form";
|
|
3
|
-
import {
|
|
3
|
+
import { Context } from "koa";
|
|
4
4
|
|
|
5
5
|
export class SimpleFormField extends FormField<string | null> {
|
|
6
6
|
async parse(
|
|
7
|
-
_:
|
|
7
|
+
_: Context,
|
|
8
8
|
raw_value: FormDataValue
|
|
9
9
|
): Promise<FieldParseResult<string>> {
|
|
10
10
|
return {
|
package/src/forms/form.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import Router from "@koa/router";
|
|
3
3
|
import { FlatTemplatable, Templatable, tempstream } from "tempstream";
|
|
4
4
|
import {
|
|
@@ -36,19 +36,19 @@ export abstract class Form<F extends Fields> extends Mountable<F> {
|
|
|
36
36
|
|
|
37
37
|
async canAccess(
|
|
38
38
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
39
|
-
_:
|
|
39
|
+
_: Context
|
|
40
40
|
): Promise<{ canAccess: boolean; message: string }> {
|
|
41
41
|
return { canAccess: true, message: "" };
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
async renderError(
|
|
45
|
-
_:
|
|
45
|
+
_: Context,
|
|
46
46
|
error: PageErrorMessage
|
|
47
|
-
): Promise<
|
|
47
|
+
): Promise<FlatTemplatable> {
|
|
48
48
|
return tempstream/* HTML */ `<div>${error.message}</div>`;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
async render(ctx:
|
|
51
|
+
async render(ctx: Context, data: FormData): Promise<FlatTemplatable> {
|
|
52
52
|
return tempstream/* HTML */ `${this.makeFormTag()} ${
|
|
53
53
|
!this.controls.some((control) => control.role == "messages")
|
|
54
54
|
? this.renderMessages(ctx, data)
|
|
@@ -63,7 +63,7 @@ export abstract class Form<F extends Fields> extends Mountable<F> {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
public async onValuesInvalid(
|
|
66
|
-
ctx:
|
|
66
|
+
ctx: Context,
|
|
67
67
|
form_messages: FormMessage[]
|
|
68
68
|
): Promise<void> {
|
|
69
69
|
ctx.status = 422;
|
|
@@ -77,7 +77,7 @@ export abstract class Form<F extends Fields> extends Mountable<F> {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
public async onError(
|
|
80
|
-
ctx:
|
|
80
|
+
ctx: Context,
|
|
81
81
|
data: FormData,
|
|
82
82
|
error: unknown
|
|
83
83
|
): Promise<void> {
|
|
@@ -96,11 +96,11 @@ export abstract class Form<F extends Fields> extends Mountable<F> {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
public abstract onSubmit(
|
|
99
|
-
ctx:
|
|
99
|
+
ctx: Context,
|
|
100
100
|
data: FormData
|
|
101
101
|
): void | Promise<void>;
|
|
102
102
|
|
|
103
|
-
public async onSuccess(ctx:
|
|
103
|
+
public async onSuccess(ctx: Context, data: FormData): Promise<void> {
|
|
104
104
|
ctx.body = await this.render(ctx, {
|
|
105
105
|
raw_values: data.raw_values,
|
|
106
106
|
messages: [{ type: "success", text: this.defaultSuccessMessage }],
|
|
@@ -171,7 +171,7 @@ export abstract class Form<F extends Fields> extends Mountable<F> {
|
|
|
171
171
|
});
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
extractRawValues(ctx:
|
|
174
|
+
extractRawValues(ctx: Context): Record<string, FormDataValue> {
|
|
175
175
|
return ctx.$body;
|
|
176
176
|
}
|
|
177
177
|
}
|
package/src/generate-routes.ts
CHANGED
|
@@ -46,13 +46,25 @@ export async function generateRoutes(): Promise<void> {
|
|
|
46
46
|
}))
|
|
47
47
|
);
|
|
48
48
|
|
|
49
|
+
await fs.writeFile(
|
|
50
|
+
target_locreq.resolve("src/back/routes/urls.ts"),
|
|
51
|
+
`${files
|
|
52
|
+
.map(
|
|
53
|
+
({ actionName, url }) =>
|
|
54
|
+
`export const ${actionName}URL = "${url}";`
|
|
55
|
+
)
|
|
56
|
+
.join("\n")}`
|
|
57
|
+
);
|
|
58
|
+
|
|
49
59
|
await fs.writeFile(
|
|
50
60
|
target_locreq.resolve("src/back/routes/routes.ts"),
|
|
51
61
|
`// DO NOT EDIT! This file is generated automaticaly with npm run generate-routes
|
|
52
62
|
|
|
63
|
+
import Koa, { Context } from "koa";
|
|
53
64
|
import Router from "@koa/router";
|
|
54
65
|
import { Middlewares } from "sealious";
|
|
55
66
|
import { Mountable } from "@sealcode/sealgen";
|
|
67
|
+
import * as URLs from "./urls"
|
|
56
68
|
|
|
57
69
|
${files
|
|
58
70
|
.map(
|
|
@@ -64,32 +76,41 @@ ${files
|
|
|
64
76
|
)
|
|
65
77
|
.join("\n")}
|
|
66
78
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
.
|
|
79
|
+
async function handleHtmlPromise(ctx: Context, next: Koa.Next) {
|
|
80
|
+
await next();
|
|
81
|
+
if (ctx.body instanceof Promise) {
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
83
|
+
ctx.body = await ctx.body;
|
|
84
|
+
}
|
|
85
|
+
ctx.set("content-type", "text/html;charset=utf-8");
|
|
86
|
+
}
|
|
70
87
|
|
|
71
88
|
function mount(router: Router, URL: string, mountable: Mountable) {
|
|
72
|
-
router.use(
|
|
89
|
+
router.use(
|
|
90
|
+
URL,
|
|
91
|
+
Middlewares.extractContext(),
|
|
92
|
+
Middlewares.parseBody(),
|
|
93
|
+
handleHtmlPromise
|
|
94
|
+
);
|
|
73
95
|
mountable.init();
|
|
74
96
|
mountable.mount(router, URL);
|
|
75
97
|
// to automatically add trailing slashes:
|
|
76
98
|
router.get(URL.slice(0, -1), (ctx) => ctx.redirect(URL));
|
|
99
|
+
router.use(URL, (ctx) => ctx.set("content-type", "text/html;charset=utf-8"));
|
|
77
100
|
}
|
|
78
101
|
|
|
79
102
|
export default function mountAutoRoutes(router: Router) {
|
|
80
|
-
router.use(async (ctx, next) => {
|
|
81
|
-
await next();
|
|
82
|
-
if (ctx.body instanceof Promise) {
|
|
83
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
84
|
-
ctx.body = await ctx.body;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
103
|
${files
|
|
88
|
-
.map(
|
|
104
|
+
.map(
|
|
105
|
+
({ actionName }) =>
|
|
106
|
+
` mount(router, URLs.${actionName}URL, ${actionName});`
|
|
107
|
+
)
|
|
89
108
|
.join("\n")}
|
|
90
109
|
}
|
|
91
110
|
`
|
|
92
111
|
);
|
|
93
112
|
// eslint-disable-next-line no-console
|
|
94
|
-
console.log(
|
|
113
|
+
console.log(
|
|
114
|
+
"Successfuly generated new src/back/routes/routes.ts and src/back/routes/urls.ts"
|
|
115
|
+
);
|
|
95
116
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import Sealious from "sealious";
|
|
1
|
+
import Sealious, { App } from "sealious";
|
|
2
2
|
import { FormDataValue } from "./forms/form";
|
|
3
3
|
|
|
4
4
|
declare module "koa" {
|
|
5
5
|
interface BaseContext {
|
|
6
6
|
$context: Sealious.Context;
|
|
7
|
+
$app: App;
|
|
7
8
|
$body: Record<string, FormDataValue>;
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
export * from "./forms/controls/controls";
|
|
12
|
-
export * from "./forms/fields/field";
|
|
12
|
+
export * as Controls from "./forms/controls/controls";
|
|
13
|
+
export * as Fields from "./forms/fields/field";
|
|
13
14
|
export * from "./page/mountable";
|
|
14
15
|
export * from "./page/page";
|
|
15
16
|
export * from "./forms/form";
|
|
16
17
|
export * from "./page/props-parser";
|
|
17
18
|
export * from "./utils/utils";
|
|
18
19
|
export * from "./page/list";
|
|
20
|
+
export * from "./utils/translate";
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { FormDataValue
|
|
1
|
+
import { Context } from "koa";
|
|
2
|
+
import { FlatTemplatable, tempstream } from "tempstream";
|
|
3
|
+
import { FormDataValue } from ".";
|
|
4
|
+
import { FormField } from "./forms/fields/field";
|
|
4
5
|
|
|
5
6
|
export function makeHiddenInputs(
|
|
6
|
-
ctx:
|
|
7
|
+
ctx: Context,
|
|
7
8
|
fields: Record<string, FormField>,
|
|
8
9
|
values: Record<string, FormDataValue>,
|
|
9
10
|
fields_to_skip: string[]
|
|
10
|
-
):
|
|
11
|
+
): FlatTemplatable {
|
|
11
12
|
return tempstream`${Object.entries(fields)
|
|
12
13
|
.filter(([key]) => !fields_to_skip.includes(key))
|
|
13
14
|
.map(
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import { ActionName, Collection } from "sealious";
|
|
3
3
|
|
|
4
4
|
export function peopleWhoCan(action: ActionName, collection: Collection) {
|
|
5
|
-
return async function (ctx:
|
|
5
|
+
return async function (ctx: Context) {
|
|
6
6
|
const policy = collection.getPolicy(action);
|
|
7
7
|
const result = await policy.check(ctx.$context);
|
|
8
8
|
if (!result) {
|
package/src/page/list.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import { Collection, CollectionItem, ItemFields, ItemList } from "sealious";
|
|
3
|
+
|
|
3
4
|
import { Templatable, tempstream, FlatTemplatable } from "tempstream";
|
|
4
5
|
import { peopleWhoCan } from "./access-control";
|
|
5
6
|
import { Page } from "./page";
|
|
@@ -23,22 +24,19 @@ export abstract class ListPage<
|
|
|
23
24
|
F extends typeof BaseListPageFields
|
|
24
25
|
> extends Page<F> {
|
|
25
26
|
abstract getItems(
|
|
26
|
-
ctx:
|
|
27
|
+
ctx: Context,
|
|
27
28
|
page: number,
|
|
28
29
|
itemsPerPage: number,
|
|
29
30
|
values: Record<string, FormDataValue>
|
|
30
31
|
): Promise<ItemType[]>;
|
|
31
32
|
|
|
32
33
|
abstract getTotalPages(
|
|
33
|
-
ctx:
|
|
34
|
+
ctx: Context,
|
|
34
35
|
itemsPerPage: number,
|
|
35
36
|
values: Record<string, FormDataValue>
|
|
36
37
|
): Promise<number>;
|
|
37
38
|
|
|
38
|
-
abstract renderItem(
|
|
39
|
-
ctx: BaseContext,
|
|
40
|
-
item: ItemType
|
|
41
|
-
): Promise<FlatTemplatable>;
|
|
39
|
+
abstract renderItem(ctx: Context, item: ItemType): Promise<FlatTemplatable>;
|
|
42
40
|
|
|
43
41
|
filterFields: Record<string, FormField> = {};
|
|
44
42
|
filterControls: FormFieldControl[] = [];
|
|
@@ -50,19 +48,20 @@ export abstract class ListPage<
|
|
|
50
48
|
}
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
renderListContainer(_:
|
|
51
|
+
renderListContainer(_: Context, content: Templatable): FlatTemplatable {
|
|
54
52
|
return tempstream`<div>${content}</div>`;
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
async render(ctx:
|
|
55
|
+
async render(ctx: Context): Promise<FlatTemplatable> {
|
|
58
56
|
const values = this.extractRawValues(ctx);
|
|
59
57
|
const { parsed: page } = await this.fields.page.getValue(ctx, values);
|
|
60
58
|
const { parsed: itemsPerPage } =
|
|
61
59
|
await this.fields.itemsPerPage.getValue(ctx, values);
|
|
62
60
|
|
|
61
|
+
const items_promise = this.getItems(ctx, page, itemsPerPage, values);
|
|
63
62
|
return tempstream`${this.renderPagination(ctx, values)}
|
|
64
63
|
${this.renderFilters(ctx)}
|
|
65
|
-
${
|
|
64
|
+
${items_promise.then((items) =>
|
|
66
65
|
this.renderListContainer(
|
|
67
66
|
ctx,
|
|
68
67
|
items.map((item) => this.renderItem(ctx, item))
|
|
@@ -71,7 +70,7 @@ export abstract class ListPage<
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
async renderPagination(
|
|
74
|
-
ctx:
|
|
73
|
+
ctx: Context,
|
|
75
74
|
values: Record<string, FormDataValue>
|
|
76
75
|
): Promise<FlatTemplatable> {
|
|
77
76
|
const { parsed: page } = await this.fields.page.getValue(ctx, values);
|
|
@@ -110,7 +109,7 @@ export abstract class ListPage<
|
|
|
110
109
|
</center>`;
|
|
111
110
|
}
|
|
112
111
|
|
|
113
|
-
private renderPageButton(ctx:
|
|
112
|
+
private renderPageButton(ctx: Context, page: number, text: string) {
|
|
114
113
|
return /* HTML */ `<a
|
|
115
114
|
href="${UrlWithNewParams(
|
|
116
115
|
ctx,
|
|
@@ -123,7 +122,7 @@ export abstract class ListPage<
|
|
|
123
122
|
>`;
|
|
124
123
|
}
|
|
125
124
|
|
|
126
|
-
async getFilterValues(ctx:
|
|
125
|
+
async getFilterValues(ctx: Context): Promise<Record<string, unknown>> {
|
|
127
126
|
const filter = {} as Record<string, unknown>;
|
|
128
127
|
const raw_values = this.extractRawValues(ctx);
|
|
129
128
|
for (const [fieldname, field] of Object.entries(this.filterFields)) {
|
|
@@ -134,7 +133,7 @@ export abstract class ListPage<
|
|
|
134
133
|
return filter;
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
renderFilters(ctx:
|
|
136
|
+
renderFilters(ctx: Context): FlatTemplatable {
|
|
138
137
|
const values = this.extractRawValues(ctx);
|
|
139
138
|
return tempstream/* HTML */ `<form method="GET">
|
|
140
139
|
${makeHiddenInputs(ctx, this.fields, values, [
|
|
@@ -157,20 +156,14 @@ export abstract class SealiousItemListPage<
|
|
|
157
156
|
super();
|
|
158
157
|
}
|
|
159
158
|
|
|
160
|
-
async getTotalPages(
|
|
161
|
-
ctx: BaseContext,
|
|
162
|
-
itemsPerPage: number
|
|
163
|
-
): Promise<number> {
|
|
159
|
+
async getTotalPages(ctx: Context, itemsPerPage: number): Promise<number> {
|
|
164
160
|
const query = this.collection.list(ctx.$context);
|
|
165
161
|
await this.addFilter(ctx, query);
|
|
166
162
|
const { items } = await query.fetch();
|
|
167
163
|
return Math.ceil(items.length / itemsPerPage);
|
|
168
164
|
}
|
|
169
165
|
|
|
170
|
-
protected async addFilter(
|
|
171
|
-
ctx: BaseContext,
|
|
172
|
-
list: ItemList<C>
|
|
173
|
-
): Promise<void> {
|
|
166
|
+
protected async addFilter(ctx: Context, list: ItemList<C>): Promise<void> {
|
|
174
167
|
const filters = await this.getFilterValues(ctx);
|
|
175
168
|
if (Object.keys(filters).length) {
|
|
176
169
|
list.filter(filters as Partial<ItemFields<C>>);
|
|
@@ -178,7 +171,7 @@ export abstract class SealiousItemListPage<
|
|
|
178
171
|
}
|
|
179
172
|
|
|
180
173
|
async getItems(
|
|
181
|
-
ctx:
|
|
174
|
+
ctx: Context,
|
|
182
175
|
page: number,
|
|
183
176
|
itemsPerPage: number
|
|
184
177
|
): Promise<CollectionItem<C>[]> {
|
|
@@ -191,7 +184,7 @@ export abstract class SealiousItemListPage<
|
|
|
191
184
|
}
|
|
192
185
|
|
|
193
186
|
async renderItem(
|
|
194
|
-
_:
|
|
187
|
+
_: Context,
|
|
195
188
|
item: CollectionItem<C>
|
|
196
189
|
): Promise<FlatTemplatable> {
|
|
197
190
|
return `<div>${item.id}</div>`;
|