@sealcode/sealgen 0.3.2 → 0.4.2

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