@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.
Files changed (98) hide show
  1. package/@types/forms/controls/checkboxed-list-input.d.ts +2 -2
  2. package/@types/forms/controls/controls.d.ts +1 -0
  3. package/@types/forms/controls/dropdown.d.ts +2 -2
  4. package/@types/forms/controls/editable-collection-subset.d.ts +2 -2
  5. package/@types/forms/controls/form-control.d.ts +2 -2
  6. package/@types/forms/controls/form-field-control.d.ts +2 -2
  7. package/@types/forms/controls/form-header.d.ts +4 -4
  8. package/@types/forms/controls/html.d.ts +9 -0
  9. package/@types/forms/controls/simple-input.d.ts +4 -2
  10. package/@types/forms/fields/checkboxed-list.d.ts +7 -7
  11. package/@types/forms/fields/collection-field.d.ts +3 -3
  12. package/@types/forms/fields/field.d.ts +6 -6
  13. package/@types/forms/fields/number.d.ts +3 -3
  14. package/@types/forms/fields/pick-from-list.d.ts +7 -7
  15. package/@types/forms/fields/simple-form-field.d.ts +2 -2
  16. package/@types/forms/form.d.ts +10 -10
  17. package/@types/index.d.ts +5 -3
  18. package/@types/make-hidden-inputs.d.ts +5 -4
  19. package/@types/page/access-control.d.ts +2 -2
  20. package/@types/page/list.d.ts +14 -14
  21. package/@types/page/mountable.d.ts +9 -9
  22. package/@types/page/page.d.ts +6 -6
  23. package/@types/page/props-parser.d.ts +4 -4
  24. package/@types/utils/translate.d.ts +3 -0
  25. package/@types/utils/url-with-new-params.d.ts +2 -2
  26. package/lib/build.js +4 -3
  27. package/lib/build.js.map +1 -1
  28. package/lib/forms/controls/checkboxed-list-input.js.map +1 -1
  29. package/lib/forms/controls/controls.js +3 -1
  30. package/lib/forms/controls/controls.js.map +1 -1
  31. package/lib/forms/controls/dropdown.js.map +1 -1
  32. package/lib/forms/controls/editable-collection-subset.js.map +1 -1
  33. package/lib/forms/controls/form-header.js.map +1 -1
  34. package/lib/forms/controls/html.js +19 -0
  35. package/lib/forms/controls/html.js.map +1 -0
  36. package/lib/forms/controls/simple-input.js +4 -2
  37. package/lib/forms/controls/simple-input.js.map +1 -1
  38. package/lib/forms/fields/checkboxed-list.js.map +1 -1
  39. package/lib/forms/fields/collection-field.js.map +1 -1
  40. package/lib/forms/fields/field.js +5 -1
  41. package/lib/forms/fields/field.js.map +1 -1
  42. package/lib/forms/fields/number.js.map +1 -1
  43. package/lib/forms/fields/pick-from-list.js.map +1 -1
  44. package/lib/forms/fields/simple-form-field.js.map +1 -1
  45. package/lib/forms/form.js.map +1 -1
  46. package/lib/generate-routes.js +22 -13
  47. package/lib/generate-routes.js.map +1 -1
  48. package/lib/index.js +16 -2
  49. package/lib/index.js.map +1 -1
  50. package/lib/make-hidden-inputs.js.map +1 -1
  51. package/lib/page/access-control.js.map +1 -1
  52. package/lib/page/list.js +2 -1
  53. package/lib/page/list.js.map +1 -1
  54. package/lib/page/mountable.js.map +1 -1
  55. package/lib/page/page.js.map +1 -1
  56. package/lib/page/props-parser.js.map +1 -1
  57. package/lib/templates/form.js +9 -16
  58. package/lib/templates/form.js.map +1 -1
  59. package/lib/templates/list.js +4 -4
  60. package/lib/templates/list.js.map +1 -1
  61. package/lib/templates/page.js +3 -3
  62. package/lib/templates/route-test.js +1 -1
  63. package/lib/templates/route-test.js.map +1 -1
  64. package/lib/utils/translate.js +10 -0
  65. package/lib/utils/translate.js.map +1 -0
  66. package/lib/utils/url-with-new-params.js.map +1 -1
  67. package/package.json +3 -3
  68. package/src/build.ts +4 -3
  69. package/src/forms/controls/checkboxed-list-input.ts +2 -2
  70. package/src/forms/controls/controls.ts +1 -0
  71. package/src/forms/controls/dropdown.ts +2 -2
  72. package/src/forms/controls/editable-collection-subset.ts +2 -2
  73. package/src/forms/controls/form-control.ts +2 -2
  74. package/src/forms/controls/form-field-control.ts +2 -2
  75. package/src/forms/controls/form-header.ts +3 -3
  76. package/src/forms/controls/html.ts +18 -0
  77. package/src/forms/controls/simple-input.ts +7 -6
  78. package/src/forms/fields/checkboxed-list.ts +5 -5
  79. package/src/forms/fields/collection-field.ts +3 -3
  80. package/src/forms/fields/field.ts +17 -7
  81. package/src/forms/fields/number.ts +3 -3
  82. package/src/forms/fields/pick-from-list.ts +5 -5
  83. package/src/forms/fields/simple-form-field.ts +2 -2
  84. package/src/forms/form.ts +10 -10
  85. package/src/generate-routes.ts +34 -13
  86. package/src/index.ts +5 -3
  87. package/src/make-hidden-inputs.ts +6 -5
  88. package/src/page/access-control.ts +2 -2
  89. package/src/page/list.ts +17 -24
  90. package/src/page/mountable.ts +11 -11
  91. package/src/page/page.ts +5 -5
  92. package/src/page/props-parser.ts +4 -7
  93. package/src/templates/form.ts +9 -16
  94. package/src/templates/list.ts +4 -4
  95. package/src/templates/page.ts +3 -3
  96. package/src/templates/route-test.ts +1 -1
  97. package/src/utils/translate.ts +13 -0
  98. package/src/utils/url-with-new-params.ts +2 -2
@@ -1,10 +1,10 @@
1
- import { BaseContext } from "koa";
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: BaseContext,
7
+ ctx: Context,
8
8
  data: Record<string, FormDataValue>,
9
9
  messages: FormMessage[]
10
10
  ): FlatTemplatable | Promise<FlatTemplatable>;
@@ -1,4 +1,4 @@
1
- import { BaseContext } from "koa";
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: BaseContext,
20
+ ctx: Context,
21
21
  data: Record<string, FormDataValue>
22
22
  ): FlatTemplatable | Promise<FlatTemplatable>;
23
23
  }
@@ -1,15 +1,15 @@
1
- import { BaseContext } from "koa";
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: BaseContext) => Promise<boolean> = async () => true
8
+ public isVisible: (ctx: Context) => Promise<boolean> = async () => true
9
9
  ) {
10
10
  super();
11
11
  }
12
- async render(ctx: BaseContext) {
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 { BaseContext } from "koa";
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: BaseContext,
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 ? `<div class="input__error">${message}</div>` : ""}
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 { BaseContext } from "koa";
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: BaseContext
14
+ ctx: Context
15
15
  ) => Promise<Record<string, string> | { [i: string]: string }>,
16
- public isVisible: (ctx: BaseContext) => Promise<boolean> = () =>
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
- _: BaseContext,
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
- _: BaseContext,
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 { BaseContext } from "koa";
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: BaseContext,
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: BaseContext,
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 { BaseContext } from "koa";
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: BaseContext,
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: BaseContext,
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 { valid: false, message: "This field is required" };
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: BaseContext,
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: BaseContext,
63
+ ctx: Context,
54
64
  raw_value: FormDataValue
55
65
  ): Promise<FieldParseResult<ParsedValue>>;
56
66
 
57
67
  async getValue(
58
- ctx: BaseContext,
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 { BaseContext } from "koa";
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(_: BaseContext, value: unknown) {
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
- _: BaseContext,
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 { BaseContext } from "koa";
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: BaseContext
10
+ ctx: Context
11
11
  ) => Promise<Record<string, string> | { [i: string]: string }>,
12
12
  public customValidation: (
13
- ctx: BaseContext,
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: BaseContext,
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: BaseContext,
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 { BaseContext } from "koa";
3
+ import { Context } from "koa";
4
4
 
5
5
  export class SimpleFormField extends FormField<string | null> {
6
6
  async parse(
7
- _: BaseContext,
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 { BaseContext } from "koa";
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
- _: BaseContext
39
+ _: Context
40
40
  ): Promise<{ canAccess: boolean; message: string }> {
41
41
  return { canAccess: true, message: "" };
42
42
  }
43
43
 
44
44
  async renderError(
45
- _: BaseContext,
45
+ _: Context,
46
46
  error: PageErrorMessage
47
- ): Promise<Templatable> {
47
+ ): Promise<FlatTemplatable> {
48
48
  return tempstream/* HTML */ `<div>${error.message}</div>`;
49
49
  }
50
50
 
51
- async render(ctx: BaseContext, data: FormData): Promise<Templatable> {
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: BaseContext,
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: BaseContext,
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: BaseContext,
99
+ ctx: Context,
100
100
  data: FormData
101
101
  ): void | Promise<void>;
102
102
 
103
- public async onSuccess(ctx: BaseContext, data: FormData): Promise<void> {
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: BaseContext): Record<string, FormDataValue> {
174
+ extractRawValues(ctx: Context): Record<string, FormDataValue> {
175
175
  return ctx.$body;
176
176
  }
177
177
  }
@@ -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
- ${files
68
- .map(({ actionName, url }) => `export const ${actionName}URL = "${url}";`)
69
- .join("\n")}
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(URL, Middlewares.extractContext(), Middlewares.parseBody());
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(({ actionName }) => ` mount(router, ${actionName}URL, ${actionName});`)
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("Successfuly generated new src/back/routes/routes.ts file");
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 { BaseContext } from "koa";
2
- import { Templatable, tempstream } from "tempstream";
3
- import { FormDataValue, FormField } from ".";
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: BaseContext,
7
+ ctx: Context,
7
8
  fields: Record<string, FormField>,
8
9
  values: Record<string, FormDataValue>,
9
10
  fields_to_skip: string[]
10
- ): Templatable {
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 { BaseContext } from "koa";
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: BaseContext) {
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 { BaseContext } from "koa";
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: BaseContext,
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: BaseContext,
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(_: BaseContext, content: Templatable): FlatTemplatable {
51
+ renderListContainer(_: Context, content: Templatable): FlatTemplatable {
54
52
  return tempstream`<div>${content}</div>`;
55
53
  }
56
54
 
57
- async render(ctx: BaseContext): Promise<FlatTemplatable> {
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
- ${this.getItems(ctx, page, itemsPerPage, values).then((items) =>
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: BaseContext,
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: BaseContext, page: number, text: string) {
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: BaseContext): Promise<Record<string, unknown>> {
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: BaseContext): FlatTemplatable {
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: BaseContext,
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
- _: BaseContext,
187
+ _: Context,
195
188
  item: CollectionItem<C>
196
189
  ): Promise<FlatTemplatable> {
197
190
  return `<div>${item.id}</div>`;