@sealcode/sealgen 0.8.3 → 0.8.5

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 (107) hide show
  1. package/@types/forms/controls/autocomplete.d.ts +1 -1
  2. package/@types/forms/controls/checkbox-group.d.ts +3 -2
  3. package/@types/forms/controls/collapsible-checkbox-group.d.ts +1 -1
  4. package/@types/forms/controls/collapsible-field-group.d.ts +1 -1
  5. package/@types/forms/controls/controls.d.ts +3 -0
  6. package/@types/forms/controls/dropdown.d.ts +6 -5
  7. package/@types/forms/controls/field-group.d.ts +3 -3
  8. package/@types/forms/controls/file.d.ts +14 -0
  9. package/@types/forms/controls/form-control.d.ts +4 -1
  10. package/@types/forms/controls/form-field-control.d.ts +1 -1
  11. package/@types/forms/controls/multiple-files.d.ts +31 -0
  12. package/@types/forms/controls/number.d.ts +17 -0
  13. package/@types/forms/controls/simple-input.d.ts +20 -27
  14. package/@types/forms/controls/textarea.d.ts +9 -7
  15. package/@types/forms/fields/field.d.ts +4 -1
  16. package/@types/forms/fields/file.d.ts +16 -0
  17. package/@types/forms/fields/files-multiple.d.ts +12 -0
  18. package/@types/forms/fields/multiple-files.d.ts +14 -0
  19. package/@types/forms/fields/number.d.ts +10 -1
  20. package/@types/forms/form.d.ts +7 -4
  21. package/@types/forms/multiform.d.ts +3 -3
  22. package/@types/page/list.d.ts +1 -1
  23. package/@types/page/mountable-with-fields.d.ts +3 -2
  24. package/@types/page/page.d.ts +1 -1
  25. package/@types/sanitize.d.ts +1 -1
  26. package/lib/forms/controls/autocomplete.js.map +1 -1
  27. package/lib/forms/controls/checkbox-group.js +11 -5
  28. package/lib/forms/controls/checkbox-group.js.map +1 -1
  29. package/lib/forms/controls/collapsible-checkbox-group.js +2 -2
  30. package/lib/forms/controls/collapsible-checkbox-group.js.map +1 -1
  31. package/lib/forms/controls/collapsible-field-group.js.map +1 -1
  32. package/lib/forms/controls/controls.js +3 -0
  33. package/lib/forms/controls/controls.js.map +1 -1
  34. package/lib/forms/controls/derived.js +12 -11
  35. package/lib/forms/controls/derived.js.map +1 -1
  36. package/lib/forms/controls/dropdown.js +25 -18
  37. package/lib/forms/controls/dropdown.js.map +1 -1
  38. package/lib/forms/controls/field-group.js +3 -3
  39. package/lib/forms/controls/field-group.js.map +1 -1
  40. package/lib/forms/controls/file.js +38 -0
  41. package/lib/forms/controls/file.js.map +1 -0
  42. package/lib/forms/controls/form-control.js +1 -0
  43. package/lib/forms/controls/form-control.js.map +1 -1
  44. package/lib/forms/controls/form-field-control.js.map +1 -1
  45. package/lib/forms/controls/multiple-files.js +137 -0
  46. package/lib/forms/controls/multiple-files.js.map +1 -0
  47. package/lib/forms/controls/number.js +34 -0
  48. package/lib/forms/controls/number.js.map +1 -0
  49. package/lib/forms/controls/simple-input.js +20 -10
  50. package/lib/forms/controls/simple-input.js.map +1 -1
  51. package/lib/forms/controls/textarea.js +13 -22
  52. package/lib/forms/controls/textarea.js.map +1 -1
  53. package/lib/forms/fields/enum-multiple.js +4 -1
  54. package/lib/forms/fields/enum-multiple.js.map +1 -1
  55. package/lib/forms/fields/field.js +8 -1
  56. package/lib/forms/fields/field.js.map +1 -1
  57. package/lib/forms/fields/file.js +58 -0
  58. package/lib/forms/fields/file.js.map +1 -0
  59. package/lib/forms/fields/files-multiple.js +18 -0
  60. package/lib/forms/fields/files-multiple.js.map +1 -0
  61. package/lib/forms/fields/multiple-files.js +19 -0
  62. package/lib/forms/fields/multiple-files.js.map +1 -0
  63. package/lib/forms/fields/number.js +6 -2
  64. package/lib/forms/fields/number.js.map +1 -1
  65. package/lib/forms/form.js +38 -21
  66. package/lib/forms/form.js.map +1 -1
  67. package/lib/forms/multiform.js +8 -8
  68. package/lib/forms/multiform.js.map +1 -1
  69. package/lib/page/list.js +9 -6
  70. package/lib/page/list.js.map +1 -1
  71. package/lib/page/mountable-with-fields.js +16 -3
  72. package/lib/page/mountable-with-fields.js.map +1 -1
  73. package/lib/page/page.js +1 -1
  74. package/lib/page/page.js.map +1 -1
  75. package/lib/sanitize.js +3 -0
  76. package/lib/sanitize.js.map +1 -1
  77. package/lib/tests/manual/multiform.js +13 -13
  78. package/lib/tests/manual/multiform.js.map +1 -1
  79. package/package.json +2 -2
  80. package/src/forms/controls/autocomplete.ts +1 -5
  81. package/src/forms/controls/checkbox-group.ts +17 -6
  82. package/src/forms/controls/collapsible-checkbox-group.ts +10 -2
  83. package/src/forms/controls/collapsible-field-group.ts +2 -4
  84. package/src/forms/controls/controls.ts +3 -0
  85. package/src/forms/controls/derived.ts +12 -11
  86. package/src/forms/controls/dropdown.ts +51 -34
  87. package/src/forms/controls/field-group.ts +6 -4
  88. package/src/forms/controls/file.ts +50 -0
  89. package/src/forms/controls/form-control.ts +5 -1
  90. package/src/forms/controls/form-field-control.ts +2 -1
  91. package/src/forms/controls/multiple-files.ts +213 -0
  92. package/src/forms/controls/number.ts +52 -0
  93. package/src/forms/controls/simple-input.ts +67 -37
  94. package/src/forms/controls/textarea.ts +23 -24
  95. package/src/forms/fields/enum-multiple.ts +3 -1
  96. package/src/forms/fields/field.ts +12 -2
  97. package/src/forms/fields/file.ts +86 -0
  98. package/src/forms/fields/multiple-files.ts +20 -0
  99. package/src/forms/fields/number.ts +17 -2
  100. package/src/forms/form.ts +84 -42
  101. package/src/forms/forms.scss +4 -1
  102. package/src/forms/multiform.ts +26 -13
  103. package/src/page/list.ts +10 -7
  104. package/src/page/mountable-with-fields.ts +26 -4
  105. package/src/page/page.ts +3 -1
  106. package/src/sanitize.ts +4 -1
  107. package/src/tests/manual/multiform.ts +19 -13
@@ -29,7 +29,7 @@ export class Multiform extends MountableWithFields {
29
29
  form.form_id = this.name;
30
30
  form.action = "./" + key;
31
31
  form.init();
32
- form.extractRawValues = (context) =>
32
+ form.extractRawValues = async (context) =>
33
33
  this.getSubformRawValues(context, key);
34
34
  const submit_button_id = this.name + "_" + key + "_submit";
35
35
 
@@ -52,7 +52,9 @@ export class Multiform extends MountableWithFields {
52
52
  }
53
53
  }
54
54
 
55
- extractRawValues(ctx: Context): Record<string, FormDataValue> {
55
+ async extractRawValues(
56
+ ctx: Context
57
+ ): Promise<Record<string, FormDataValue>> {
56
58
  return ctx.$body || {};
57
59
  }
58
60
 
@@ -86,17 +88,22 @@ export class Multiform extends MountableWithFields {
86
88
 
87
89
  async renderSubform(
88
90
  ctx: Context,
89
- sub_form_name: string
91
+ sub_form_name: string,
92
+ show_field_errors: boolean
90
93
  ): Promise<FlatTemplatable> {
91
94
  const form = this.forms[sub_form_name];
92
95
  const result = await form.canAccess(ctx);
93
96
  if (!result.canAccess) {
94
97
  return result.message;
95
98
  }
96
- return form.render(ctx, {
97
- raw_values: this.getSubformRawValues(ctx, sub_form_name),
98
- messages: [],
99
- });
99
+ return form.render(
100
+ ctx,
101
+ {
102
+ raw_values: this.getSubformRawValues(ctx, sub_form_name),
103
+ messages: [],
104
+ },
105
+ show_field_errors
106
+ );
100
107
  }
101
108
 
102
109
  getSubformRawValues(
@@ -119,7 +126,8 @@ export class Multiform extends MountableWithFields {
119
126
  async render(
120
127
  ctx: Context,
121
128
  messages: FormMessage[],
122
- prerenderedForms: Record<string, FlatTemplatable | undefined> = {}
129
+ prerenderedForms: Record<string, FlatTemplatable | undefined> = {},
130
+ show_field_errors: boolean
123
131
  ): Promise<FlatTemplatable> {
124
132
  return tempstream/* HTML */ `${this.renderMessages(ctx, {
125
133
  raw_values: {},
@@ -143,7 +151,7 @@ export class Multiform extends MountableWithFields {
143
151
  subform-id="${frame_form_id}"
144
152
  >
145
153
  ${prerenderedForms[form_name] ||
146
- this.renderSubform(ctx, form_name)}
154
+ this.renderSubform(ctx, form_name, show_field_errors)}
147
155
  <form id="${frame_form_id}" method="POST"></form>
148
156
  </turbo-frame>`;
149
157
  })}
@@ -188,7 +196,7 @@ export class Multiform extends MountableWithFields {
188
196
  mount(router: Router, path: string): void {
189
197
  router.get(path, async (ctx) => {
190
198
  ctx.type = "html";
191
- ctx.body = await this.render(ctx, [], {});
199
+ ctx.body = await this.render(ctx, [], {}, false);
192
200
  });
193
201
 
194
202
  for (const [key, form] of Object.entries(this.forms)) {
@@ -199,9 +207,14 @@ export class Multiform extends MountableWithFields {
199
207
  if (reaction.action == "stay") {
200
208
  ctx.status = 422;
201
209
  const form_content = reaction.content;
202
- ctx.body = this.render(ctx, [], {
203
- [key]: form_content,
204
- });
210
+ ctx.body = this.render(
211
+ ctx,
212
+ [],
213
+ {
214
+ [key]: form_content,
215
+ },
216
+ true
217
+ );
205
218
  } else if (reaction.action == "redirect") {
206
219
  ctx.status = 303;
207
220
  ctx.redirect(reaction.url);
package/src/page/list.ts CHANGED
@@ -5,7 +5,7 @@ import { Templatable, tempstream, FlatTemplatable } from "tempstream";
5
5
  import { peopleWhoCan } from "./access-control";
6
6
  import { Page } from "./page";
7
7
  import { ShapeToType } from "@sealcode/ts-predicates";
8
- import { FormControl, FormFieldControl } from "../forms/controls/controls";
8
+ import { FormControl } from "../forms/controls/controls";
9
9
  import { FormField, NumberField } from "../forms/fields/field";
10
10
  import { naturalNumbers, UrlWithNewParams } from "../utils/utils";
11
11
  import { FormDataValue } from "..";
@@ -54,9 +54,12 @@ export abstract class ListPage<
54
54
 
55
55
  async renderItems(
56
56
  ctx: Context,
57
- values: Record<string, FormDataValue> = this.extractRawValues(ctx),
57
+ values?: Record<string, FormDataValue>,
58
58
  items?: ItemType[]
59
59
  ): Promise<FlatTemplatable> {
60
+ if (!values) {
61
+ values = await this.extractRawValues(ctx);
62
+ }
60
63
  const { parsed: page } = await this.fields.page.getValue(ctx, values);
61
64
  const { parsed: itemsPerPage } =
62
65
  await this.fields.itemsPerPage.getValue(ctx, values);
@@ -68,7 +71,7 @@ export abstract class ListPage<
68
71
  }
69
72
 
70
73
  async render(ctx: Context): Promise<FlatTemplatable> {
71
- const values = this.extractRawValues(ctx);
74
+ const values = await this.extractRawValues(ctx);
72
75
 
73
76
  return tempstream`${this.renderPagination(ctx, values)}
74
77
  ${this.renderFilters(ctx)}
@@ -130,7 +133,7 @@ ${this.renderListContainer(ctx, this.renderItems(ctx, values))}`;
130
133
 
131
134
  async getFilterValues(ctx: Context): Promise<Record<string, unknown>> {
132
135
  const filter = {} as Record<string, unknown>;
133
- const raw_values = this.extractRawValues(ctx);
136
+ const raw_values = await this.extractRawValues(ctx);
134
137
  for (const [fieldname, field] of Object.entries(this.filterFields)) {
135
138
  // eslint-disable-next-line no-await-in-loop
136
139
  const { parsed } = await field.getValue(ctx, raw_values);
@@ -139,8 +142,8 @@ ${this.renderListContainer(ctx, this.renderItems(ctx, values))}`;
139
142
  return filter;
140
143
  }
141
144
 
142
- renderFilters(ctx: Context): FlatTemplatable {
143
- const values = this.extractRawValues(ctx);
145
+ async renderFilters(ctx: Context): Promise<FlatTemplatable> {
146
+ const values = await this.extractRawValues(ctx);
144
147
  return tempstream/* HTML */ `<form method="GET">
145
148
  ${makeHiddenInputs(ctx, this.fields, values, [
146
149
  "page",
@@ -148,7 +151,7 @@ ${this.renderListContainer(ctx, this.renderItems(ctx, values))}`;
148
151
  ])}
149
152
  ${this.filterControls.map((control) => {
150
153
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
151
- return control.render(ctx, values, [], "", "");
154
+ return control.render(ctx, values, [], "", "", false);
152
155
  })}
153
156
  </form>`;
154
157
  }
@@ -77,15 +77,18 @@ export abstract class MountableWithFields<
77
77
 
78
78
  public renderControls(
79
79
  ctx: Context,
80
- data: FormData<keyof Fields>
80
+ data: FormData<keyof Fields>,
81
+ validate: boolean
81
82
  ): FlatTemplatable {
83
+ console.log("RENDER CONTROLS", data.raw_values);
82
84
  return tempstream/* HTML */ `${this.controls.map((control) =>
83
85
  control.render(
84
86
  ctx,
85
87
  data.raw_values,
86
88
  data.messages,
87
89
  this.field_names_prefix,
88
- this.form_id
90
+ this.form_id,
91
+ validate
89
92
  )
90
93
  )}`;
91
94
  }
@@ -109,12 +112,14 @@ export abstract class MountableWithFields<
109
112
  </div>`;
110
113
  }
111
114
 
112
- abstract extractRawValues(ctx: Context): Record<string, FormDataValue>;
115
+ abstract extractRawValues(
116
+ ctx: Context
117
+ ): Promise<Record<string, FormDataValue>>;
113
118
 
114
119
  async getParsedValues(
115
120
  ctx: Context
116
121
  ): Promise<ShapeToType<FieldsToShape<F>>> {
117
- const raw_values = this.extractRawValues(ctx);
122
+ const raw_values = await this.extractRawValues(ctx);
118
123
  const result: Record<string, unknown> = {};
119
124
  const promises = Object.entries(this.fields).map(
120
125
  async ([key, field]) => {
@@ -125,4 +130,21 @@ export abstract class MountableWithFields<
125
130
  await Promise.all(promises);
126
131
  return result as ShapeToType<FieldsToShape<F>>;
127
132
  }
133
+
134
+ async getDatabaseValues(
135
+ ctx: Context
136
+ ): Promise<ShapeToType<FieldsToShape<F>>> {
137
+ const raw_values = await this.extractRawValues(ctx);
138
+ const result: Record<string, unknown> = {};
139
+ const promises = Object.entries(this.fields).map(
140
+ async ([key, field]) => {
141
+ const db_value = await field.getDatabaseValue(ctx, raw_values);
142
+ if (db_value !== undefined) {
143
+ result[key] = db_value;
144
+ }
145
+ }
146
+ );
147
+ await Promise.all(promises);
148
+ return result as ShapeToType<FieldsToShape<F>>;
149
+ }
128
150
  }
package/src/page/page.ts CHANGED
@@ -38,7 +38,9 @@ export abstract class Page<
38
38
 
39
39
  public abstract render(ctx: Context): Promise<FlatTemplatable>;
40
40
 
41
- extractRawValues(ctx: Context): Record<string, FormDataValue> {
41
+ async extractRawValues(
42
+ ctx: Context
43
+ ): Promise<Record<string, FormDataValue>> {
42
44
  return this.propsParser.decode(ctx);
43
45
  }
44
46
  }
package/src/sanitize.ts CHANGED
@@ -1,4 +1,7 @@
1
- export function attribute(raw: string): string {
1
+ export function attribute(raw: string | number): string {
2
+ if (typeof raw !== "string") {
3
+ raw = raw.toString();
4
+ }
2
5
  return (raw || "")
3
6
  .replaceAll("<", "&lt;")
4
7
  .replace('"', "&quot;")
@@ -49,20 +49,26 @@ const multiform = new (class extends Multiform {
49
49
  async render(
50
50
  ctx: Koa.Context,
51
51
  messages: FormMessage[],
52
- prerenderedForms?: Record<string, FlatTemplatable | undefined>
52
+ prerenderedForms?: Record<string, FlatTemplatable | undefined>,
53
+ show_field_errors: boolean = true
53
54
  ): Promise<FlatTemplatable> {
54
- return tempstream`<!DOCTYPE html>
55
- <html>
56
- <head>
57
- <title>Multiform Test - Sealgen</title>
58
- <script type="module">
59
- import hotwiredTurbo from "https://cdn.skypack.dev/pin/@hotwired/turbo@v7.0.0-9XNv1By6uiqRlBzQLO2d/mode=imports,min/optimized/@hotwired/turbo.js";
60
- </script>
61
- </head>
62
- <body>
63
- ${super.render(ctx, messages, prerenderedForms)}
64
- </body>
65
- </html>`;
55
+ return tempstream/* HTML */ `<!DOCTYPE html>
56
+ <html>
57
+ <head>
58
+ <title>Multiform Test - Sealgen</title>
59
+ <script type="module">
60
+ import hotwiredTurbo from "https://cdn.skypack.dev/pin/@hotwired/turbo@v7.0.0-9XNv1By6uiqRlBzQLO2d/mode=imports,min/optimized/@hotwired/turbo.js";
61
+ </script>
62
+ </head>
63
+ <body>
64
+ ${super.render(
65
+ ctx,
66
+ messages,
67
+ prerenderedForms,
68
+ show_field_errors
69
+ )}
70
+ </body>
71
+ </html>`;
66
72
  }
67
73
  })();
68
74