@sealcode/sealgen 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@types/forms/controls/checkboxed-list-input.d.ts +2 -2
- 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/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 +2 -1
- package/lib/build.js.map +1 -1
- package/lib/forms/controls/checkboxed-list-input.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/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 +6 -3
- package/lib/forms/form.js.map +1 -1
- package/lib/generate-routes.js +18 -2
- 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/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 -2
- package/src/build.ts +2 -1
- package/src/forms/controls/checkboxed-list-input.ts +2 -2
- 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/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 +15 -12
- package/src/generate-routes.ts +18 -2
- 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/utils/translate.ts +13 -0
- package/src/utils/url-with-new-params.ts +2 -2
|
@@ -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,13 +96,13 @@ 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
|
-
raw_values:
|
|
105
|
+
raw_values: data.raw_values,
|
|
106
106
|
messages: [{ type: "success", text: this.defaultSuccessMessage }],
|
|
107
107
|
});
|
|
108
108
|
ctx.status = 422;
|
|
@@ -139,7 +139,10 @@ export abstract class Form<F extends Fields> extends Mountable<F> {
|
|
|
139
139
|
raw_values: ctx.$body,
|
|
140
140
|
messages: [],
|
|
141
141
|
});
|
|
142
|
-
await this.onSuccess(ctx
|
|
142
|
+
await this.onSuccess(ctx, {
|
|
143
|
+
raw_values: ctx.$body,
|
|
144
|
+
messages: [],
|
|
145
|
+
});
|
|
143
146
|
} catch (e: unknown) {
|
|
144
147
|
// eslint-disable-next-line no-console
|
|
145
148
|
console.dir(e, { depth: 5 });
|
|
@@ -168,7 +171,7 @@ export abstract class Form<F extends Fields> extends Mountable<F> {
|
|
|
168
171
|
});
|
|
169
172
|
}
|
|
170
173
|
|
|
171
|
-
extractRawValues(ctx:
|
|
174
|
+
extractRawValues(ctx: Context): Record<string, FormDataValue> {
|
|
172
175
|
return ctx.$body;
|
|
173
176
|
}
|
|
174
177
|
}
|
package/src/generate-routes.ts
CHANGED
|
@@ -50,6 +50,7 @@ export async function generateRoutes(): Promise<void> {
|
|
|
50
50
|
target_locreq.resolve("src/back/routes/routes.ts"),
|
|
51
51
|
`// DO NOT EDIT! This file is generated automaticaly with npm run generate-routes
|
|
52
52
|
|
|
53
|
+
import Koa, { Context } from "koa";
|
|
53
54
|
import Router from "@koa/router";
|
|
54
55
|
import { Middlewares } from "sealious";
|
|
55
56
|
import { Mountable } from "@sealcode/sealgen";
|
|
@@ -68,12 +69,27 @@ ${files
|
|
|
68
69
|
.map(({ actionName, url }) => `export const ${actionName}URL = "${url}";`)
|
|
69
70
|
.join("\n")}
|
|
70
71
|
|
|
71
|
-
function
|
|
72
|
-
|
|
72
|
+
async function handleHtmlPromise(ctx: Context, next: Koa.Next) {
|
|
73
|
+
await next();
|
|
74
|
+
if (ctx.body instanceof Promise) {
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
76
|
+
ctx.body = await ctx.body;
|
|
77
|
+
}
|
|
78
|
+
ctx.set("content-type", "text/html;charset=utf-8");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function mount(router: Router, URL: string, mountable: Mountable) {
|
|
82
|
+
router.use(
|
|
83
|
+
URL,
|
|
84
|
+
Middlewares.extractContext(),
|
|
85
|
+
Middlewares.parseBody(),
|
|
86
|
+
handleHtmlPromise
|
|
87
|
+
);
|
|
73
88
|
mountable.init();
|
|
74
89
|
mountable.mount(router, URL);
|
|
75
90
|
// to automatically add trailing slashes:
|
|
76
91
|
router.get(URL.slice(0, -1), (ctx) => ctx.redirect(URL));
|
|
92
|
+
router.use(URL, (ctx) => ctx.set("content-type", "text/html;charset=utf-8"));
|
|
77
93
|
}
|
|
78
94
|
|
|
79
95
|
export default function mountAutoRoutes(router: Router) {
|
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>`;
|
package/src/page/mountable.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Router from "@koa/router";
|
|
2
2
|
import { FormControl } from "../forms/controls/form-control";
|
|
3
|
-
import {
|
|
4
|
-
import { FlatTemplatable,
|
|
3
|
+
import { Context } from "koa";
|
|
4
|
+
import { FlatTemplatable, tempstream } from "tempstream";
|
|
5
5
|
import { FormField } from "../forms/fields/field";
|
|
6
6
|
import { FormData, FormDataValue, FormMessage } from "../forms/form";
|
|
7
7
|
|
|
@@ -20,19 +20,19 @@ export abstract class Mountable<F extends Fields = Fields> {
|
|
|
20
20
|
abstract mount(router: Router, path: string): void;
|
|
21
21
|
|
|
22
22
|
abstract canAccess(
|
|
23
|
-
ctx:
|
|
23
|
+
ctx: Context
|
|
24
24
|
): Promise<{ canAccess: boolean; message: string }>;
|
|
25
25
|
|
|
26
26
|
abstract renderError(
|
|
27
|
-
ctx:
|
|
27
|
+
ctx: Context,
|
|
28
28
|
error: PageErrorMessage
|
|
29
|
-
): Promise<
|
|
29
|
+
): Promise<FlatTemplatable>;
|
|
30
30
|
|
|
31
|
-
abstract extractRawValues(ctx:
|
|
31
|
+
abstract extractRawValues(ctx: Context): Record<string, FormDataValue>;
|
|
32
32
|
|
|
33
33
|
// this one is meant to be overwritten
|
|
34
34
|
async validateValues(
|
|
35
|
-
ctx:
|
|
35
|
+
ctx: Context,
|
|
36
36
|
data: Record<string, FormDataValue>
|
|
37
37
|
): Promise<{ valid: boolean; error: string }> {
|
|
38
38
|
return {
|
|
@@ -42,7 +42,7 @@ export abstract class Mountable<F extends Fields = Fields> {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
protected async validate(
|
|
45
|
-
ctx:
|
|
45
|
+
ctx: Context,
|
|
46
46
|
values: Record<string, FormDataValue>
|
|
47
47
|
): Promise<{
|
|
48
48
|
valid: boolean;
|
|
@@ -83,7 +83,7 @@ export abstract class Mountable<F extends Fields = Fields> {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
public renderControls(
|
|
86
|
-
ctx:
|
|
86
|
+
ctx: Context,
|
|
87
87
|
data: FormData<keyof Fields>
|
|
88
88
|
): FlatTemplatable {
|
|
89
89
|
return tempstream/* HTML */ `${this.controls.map((control) =>
|
|
@@ -92,9 +92,9 @@ export abstract class Mountable<F extends Fields = Fields> {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
public renderMessages(
|
|
95
|
-
_:
|
|
95
|
+
_: Context,
|
|
96
96
|
data: FormData<keyof Fields>
|
|
97
|
-
):
|
|
97
|
+
): FlatTemplatable {
|
|
98
98
|
return tempstream/* HTML */ `<div class="form-messages">
|
|
99
99
|
${data.messages.map(
|
|
100
100
|
(message) =>
|
package/src/page/page.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Router from "@koa/router";
|
|
2
|
-
import {
|
|
2
|
+
import { Context } from "koa";
|
|
3
3
|
|
|
4
4
|
import { FlatTemplatable, Templatable, tempstream } from "tempstream";
|
|
5
5
|
import { AllQueryParams } from "..";
|
|
@@ -21,20 +21,20 @@ export abstract class Page<
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
24
|
-
async canAccess(_:
|
|
24
|
+
async canAccess(_: Context) {
|
|
25
25
|
return { canAccess: true, message: "" };
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
async renderError(
|
|
29
|
-
_:
|
|
29
|
+
_: Context,
|
|
30
30
|
error: PageErrorMessage
|
|
31
31
|
): Promise<FlatTemplatable> {
|
|
32
32
|
return tempstream/* HTML */ `<div>${error.message}</div>`;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
public abstract render(ctx:
|
|
35
|
+
public abstract render(ctx: Context): Promise<FlatTemplatable>;
|
|
36
36
|
|
|
37
|
-
extractRawValues(ctx:
|
|
37
|
+
extractRawValues(ctx: Context): Record<string, FormDataValue> {
|
|
38
38
|
return this.propsParser.decode(ctx);
|
|
39
39
|
}
|
|
40
40
|
}
|
package/src/page/props-parser.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context } from "koa";
|
|
2
2
|
import merge from "merge";
|
|
3
3
|
import { FormData, FormDataValue } from "..";
|
|
4
4
|
import { BasePageProps } from "./list";
|
|
@@ -10,14 +10,11 @@ export type EncodedProps = Record<string, unknown>;
|
|
|
10
10
|
// or any other exotic types is done by fields logic.
|
|
11
11
|
|
|
12
12
|
export abstract class PagePropsParser<PropsType> {
|
|
13
|
-
abstract decode(ctx:
|
|
13
|
+
abstract decode(ctx: Context): Record<string, FormDataValue>;
|
|
14
14
|
abstract encode(props: Record<string, FormData>): EncodedProps;
|
|
15
15
|
abstract getHTMLInputName(prop_name: string): string;
|
|
16
16
|
|
|
17
|
-
overwriteProp(
|
|
18
|
-
ctx: BaseContext,
|
|
19
|
-
new_props: Partial<PropsType>
|
|
20
|
-
): EncodedProps {
|
|
17
|
+
overwriteProp(ctx: Context, new_props: Partial<PropsType>): EncodedProps {
|
|
21
18
|
const result = {};
|
|
22
19
|
merge.recursive(result, this.decode(ctx), new_props);
|
|
23
20
|
return result;
|
|
@@ -27,7 +24,7 @@ export abstract class PagePropsParser<PropsType> {
|
|
|
27
24
|
export class AllQueryParams<
|
|
28
25
|
PropsType extends BasePageProps
|
|
29
26
|
> extends PagePropsParser<PropsType> {
|
|
30
|
-
decode(ctx:
|
|
27
|
+
decode(ctx: Context): Record<string, FormDataValue> {
|
|
31
28
|
return ctx.query;
|
|
32
29
|
}
|
|
33
30
|
|