@sealcode/sealgen 0.19.17 → 0.19.20

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 (38) hide show
  1. package/@types/add-crud.test.d.ts +1 -0
  2. package/@types/controllers/sealgen-table.stimulus.d.ts +13 -0
  3. package/@types/forms/controls/table.d.ts +2 -0
  4. package/@types/index.d.ts +1 -0
  5. package/lib/controllers/sealgen-table.stimulus.js +98 -0
  6. package/lib/forms/controls/table.js +49 -43
  7. package/lib/generate-routes.js +11 -2
  8. package/lib/mount.js +8 -10
  9. package/lib/templates/form.js +2 -2
  10. package/lib/templates/jdd-editor.js +2 -0
  11. package/lib/templates/long-running-process.js +2 -0
  12. package/lib/templates/multiform.js +2 -0
  13. package/lib/templates/page.js +2 -0
  14. package/lib/templates/post.js +2 -0
  15. package/lib/templates/redirect.js +2 -0
  16. package/lib/templates/shared/collection-list.js +2 -1
  17. package/lib/templates/shared/item-delete.js +2 -0
  18. package/lib/templates/stateful-page.js +2 -0
  19. package/package.json +1 -1
  20. package/src/add-crud.test.ts +44 -0
  21. package/src/add-crud.ts +1 -1
  22. package/src/controllers/sealgen-table.stimulus.ts +115 -0
  23. package/src/forms/controls/table.ts +94 -81
  24. package/src/generate-routes.ts +13 -2
  25. package/src/index.ts +2 -0
  26. package/src/mount.ts +8 -12
  27. package/src/templates/form.ts +2 -2
  28. package/src/templates/jdd-editor.ts +2 -0
  29. package/src/templates/long-running-process.ts +2 -0
  30. package/src/templates/multiform.ts +2 -0
  31. package/src/templates/page.ts +2 -0
  32. package/src/templates/post.ts +2 -0
  33. package/src/templates/redirect.ts +2 -0
  34. package/src/templates/shared/collection-list.ts +2 -1
  35. package/src/templates/shared/item-delete.ts +2 -0
  36. package/src/templates/stateful-page.ts +2 -0
  37. package/lib/controllers/table-add-button.stimulus.js +0 -35
  38. package/src/controllers/table-add-button.stimulus.ts +0 -35
@@ -0,0 +1,115 @@
1
+ /// <reference lib="dom" />
2
+ import { Controller } from "stimulus";
3
+
4
+ export default class TableController extends Controller {
5
+ static targets = ["table", "template"];
6
+ declare tableTarget: HTMLTableElement;
7
+ declare templateTarget: HTMLTemplateElement;
8
+
9
+ getTbody() {
10
+ const tbody = this.element
11
+ .closest("turbo-frame")
12
+ ?.querySelector("tbody");
13
+ if (!tbody) {
14
+ throw new Error("tbody not found");
15
+ }
16
+ return tbody;
17
+ }
18
+
19
+ getRowIndex(tr: HTMLTableRowElement) {
20
+ let result = 0;
21
+ for (const child of Array.from(this.getTbody().children)) {
22
+ if (child == tr) {
23
+ return result;
24
+ }
25
+ result++;
26
+ }
27
+ return -1;
28
+ }
29
+
30
+ addRow() {
31
+ const tbody = this.getTbody();
32
+ const orig_template = this.templateTarget.innerHTML;
33
+ const placeholder = this.element.getAttribute("data-table-placeholder");
34
+ if (!placeholder) {
35
+ throw new Error("Could not find placeholder attribute");
36
+ }
37
+ this.templateTarget.innerHTML = orig_template.replaceAll(
38
+ placeholder,
39
+ String(tbody.querySelectorAll("tr").length)
40
+ );
41
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
42
+ tbody.appendChild((this.templateTarget.cloneNode(true) as any).content);
43
+ this.templateTarget.innerHTML = orig_template;
44
+ this.reindex();
45
+ }
46
+
47
+ swapRows(index_a: number, index_b: number) {
48
+ const tbody = this.getTbody();
49
+ const [a, b] = [index_a, index_b].sort().reverse(); // we want them in descending order
50
+ tbody.insertBefore(tbody.children[a], tbody.children[b]);
51
+ this.reindex();
52
+ }
53
+
54
+ moveUp(e: MouseEvent) {
55
+ const target = e.target as HTMLButtonElement;
56
+ const tr = target.closest("tr");
57
+ if (!tr) {
58
+ console.error("Not within a tr");
59
+ return;
60
+ }
61
+ const index_a = this.getRowIndex(tr);
62
+ if (index_a < 0) {
63
+ return;
64
+ }
65
+ const index_b = index_a - 1;
66
+ this.swapRows(index_a, index_b);
67
+ }
68
+
69
+ moveDown(e: MouseEvent) {
70
+ const target = e.target as HTMLButtonElement;
71
+ const tr = target.closest("tr");
72
+ if (!tr) {
73
+ console.error("Not within a tr");
74
+ return;
75
+ }
76
+ const index_a = this.getRowIndex(tr);
77
+ if (index_a < 0) {
78
+ return;
79
+ }
80
+ const index_b = index_a + 1;
81
+ this.swapRows(index_a, index_b);
82
+ }
83
+
84
+ reindex() {
85
+ let result = 0;
86
+
87
+ // assign proper number to each input within a tr by changing input names
88
+ for (const tr of Array.from(this.getTbody().children)) {
89
+ tr.querySelectorAll("input").forEach((input) => {
90
+ input.setAttribute(
91
+ "name",
92
+ (input.getAttribute("name") || "").replace(
93
+ /[(\d+)](?!.*\d)/, // last number in a regex
94
+ result.toString()
95
+ )
96
+ );
97
+ });
98
+ result++;
99
+ }
100
+
101
+ // disable up/down buttons for first/last items
102
+ // first reset all states to enabled
103
+ this.element
104
+ .querySelectorAll(
105
+ `[data-action="sealgen-table#moveUp"], [data-action="sealgen-table#moveDown"]`
106
+ )
107
+ .forEach((e: HTMLButtonElement) => (e.disabled = false));
108
+ // then disable the ones at the top and bottom
109
+ this.element
110
+ .querySelectorAll(
111
+ 'tr:first-child [data-action="sealgen-table#moveUp"], tr:last-child [data-action="sealgen-table#moveDown"]'
112
+ )
113
+ .forEach((e: HTMLButtonElement) => (e.disabled = true));
114
+ }
115
+ }
@@ -21,6 +21,7 @@ export type TableControlOptions<F extends Record<string, FormField>> = {
21
21
  [field_name in keyof F]: FormControl;
22
22
  };
23
23
  allow_removing?: boolean;
24
+ allow_reordering?: boolean;
24
25
  label_add?: string;
25
26
  label: string;
26
27
  label_remove?: string;
@@ -62,8 +63,8 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
62
63
  | null,
63
64
  row_index: number | string
64
65
  ) => {
65
- return typeof row_index == "number"
66
- ? {
66
+ return typeof row_index == "number" ?
67
+ {
67
68
  data: {
68
69
  raw_values:
69
70
  (Object.fromEntries(
@@ -78,7 +79,7 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
78
79
  field_messages: {},
79
80
  },
80
81
  }
81
- : {};
82
+ : {};
82
83
  };
83
84
 
84
85
  const make_row = async (
@@ -89,7 +90,7 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
89
90
  >;
90
91
  }
91
92
  | null,
92
- row_index: number | string
93
+ row_index: number | string // when string, it's a placeholder, it will not parse as a number
93
94
  ) => {
94
95
  return /* HTML */ tempstream`<tr>
95
96
  ${Promise.all(
@@ -108,33 +109,42 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
108
109
  )
109
110
  )}
110
111
  ${
111
- this.options.allow_removing
112
- ? /* HTML */ `<td>
113
- <button
114
- onclick="this.closest('tr').remove()"
115
- >
116
- remove
117
- </button>
118
- <noscript>
119
- <input
120
- type="submit"
121
- data-turbo-frame="${this.getFrameID()}"
122
- value="${this.options
123
- .label_remove || "remove"}"
124
- form="${fctx.form_id}"
125
- formnovalidate
126
- formaction="${this.getActionURL(
127
- this.table_field.name,
128
- {
129
- remove: {
130
- index: row_index,
131
- },
132
- }
133
- )}"
134
- />
135
- </noscript>
136
- </td>`
137
- : ""
112
+ this.options.allow_removing ?
113
+ /* HTML */ `<td>
114
+ <button onclick="this.closest('tr').remove()">
115
+ remove
116
+ </button>
117
+ <noscript>
118
+ <input
119
+ type="submit"
120
+ data-turbo-frame="${this.getFrameID()}"
121
+ value="${this.options.label_remove ||
122
+ "remove"}"
123
+ form="${fctx.form_id}"
124
+ formnovalidate
125
+ formaction="${this.getActionURL(
126
+ this.table_field.name,
127
+ {
128
+ remove: {
129
+ index: row_index,
130
+ },
131
+ }
132
+ )}"
133
+ />
134
+ </noscript>
135
+ </td>`
136
+ : ""
137
+ }${
138
+ this.options.allow_reordering ?
139
+ /* HTML */ `<td>
140
+ <button data-action="sealgen-table#moveUp">
141
+ ↑
142
+ </button>
143
+ <button data-action="sealgen-table#moveDown">
144
+ ↓
145
+ </button>
146
+ </td>`
147
+ : ""
138
148
  }
139
149
  </tr>`;
140
150
  };
@@ -145,60 +155,62 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
145
155
  "form-input__wrapper",
146
156
  "form-input__wrapper--type--table",
147
157
  "form-input__wrapper--options-count--" + rows.length.toString(),
148
- ...(rows.length >= 5
149
- ? ["form-input__wrapper--options-count--5-or-more"]
150
- : []),
151
- ...(rows.length >= 10
152
- ? ["form-input__wrapper--options-count--10-or-more"]
153
- : []),
154
- ...(rows.length >= 15
155
- ? ["form-input__wrapper--options-count--15-or-more"]
156
- : []),
157
- ...(rows.length >= 20
158
- ? ["form-input__wrapper--options-count--20-or-more"]
159
- : []),
158
+ ...(rows.length >= 5 ?
159
+ ["form-input__wrapper--options-count--5-or-more"]
160
+ : []),
161
+ ...(rows.length >= 10 ?
162
+ ["form-input__wrapper--options-count--10-or-more"]
163
+ : []),
164
+ ...(rows.length >= 15 ?
165
+ ["form-input__wrapper--options-count--15-or-more"]
166
+ : []),
167
+ ...(rows.length >= 20 ?
168
+ ["form-input__wrapper--options-count--20-or-more"]
169
+ : []),
160
170
  ].join(" ")}"
161
171
  >
162
172
  <label>${this.options.label || this.table_field.label}</label>
163
- <div class="table__wrapper">
164
- <table>
173
+ <div
174
+ class="table__wrapper"
175
+ data-controller="sealgen-table"
176
+ data-table-placeholder="${TABLE_COLUMN_FIELD_INDEX_PLACEHOLDER}"
177
+ >
178
+ <table data-sealgen-table-target="table">
165
179
  <tbody>
166
180
  ${rows?.map((row, index) => make_row(row, index))}
167
181
  </tbody>
168
182
  </table>
169
- <template>
183
+ <template data-sealgen-table-target="template">
170
184
  ${make_row(null, TABLE_COLUMN_FIELD_INDEX_PLACEHOLDER)}
171
185
  </template>
172
186
 
173
187
  ${
174
188
  /* because of https://github.com/ljharb/qs/issues/252 we have to
175
189
  put the indexes in the field names */
176
- this.options.allow_adding
177
- ? /* HTML */ `<button
178
- data-controller="table-add-button"
179
- data-action="click->table-add-button#addRow"
180
- data-table-placeholder="${TABLE_COLUMN_FIELD_INDEX_PLACEHOLDER}"
181
- >
182
- ${this.options.label_add || "add"}
183
- </button>
184
- <noscript>
185
- <input
186
- type="submit"
187
- value="add"
188
- form="${fctx.form_id}"
189
- formnovalidate
190
- formaction="${this.getActionURL(
191
- this.table_field.name,
192
- {
193
- insert: {
194
- index: rows.length,
195
- value: {},
196
- },
197
- }
198
- )}"
199
- />
200
- </noscript>`
201
- : ""
190
+ this.options.allow_adding ?
191
+ /* HTML */ `<button
192
+ data-action="click->sealgen-table#addRow"
193
+ >
194
+ ${this.options.label_add || "add"}
195
+ </button>
196
+ <noscript>
197
+ <input
198
+ type="submit"
199
+ value="add"
200
+ form="${fctx.form_id}"
201
+ formnovalidate
202
+ formaction="${this.getActionURL(
203
+ this.table_field.name,
204
+ {
205
+ insert: {
206
+ index: rows.length,
207
+ value: {},
208
+ },
209
+ }
210
+ )}"
211
+ />
212
+ </noscript>`
213
+ : ""
202
214
  }
203
215
  </div>
204
216
  </turbo-frame>`;
@@ -221,11 +233,13 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
221
233
  const field_name = ctx.$body.field_name;
222
234
  if (
223
235
  !is(action, predicates.object) ||
224
- !is(field_name, predicates.string)
236
+ !is(field_name, predicates.string) ||
237
+ field_name !== this.table_field.name
225
238
  ) {
226
239
  await next();
227
240
  return;
228
241
  }
242
+
229
243
  if (
230
244
  this.options.allow_adding &&
231
245
  hasShape(
@@ -235,16 +249,13 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
235
249
  action
236
250
  )
237
251
  ) {
238
- if (field_name !== this.table_field.name) {
239
- await next();
240
- return;
241
- }
242
252
  if (!ctx.$body[this.table_field.name]) {
243
253
  ctx.$body[this.table_field.name] = {};
244
254
  }
245
255
  (ctx.$body[this.table_field.name] as any)[action.insert.index] =
246
256
  await this.options.make_new_row(ctx);
247
257
  }
258
+
248
259
  if (
249
260
  hasShape(
250
261
  {
@@ -253,10 +264,6 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
253
264
  action
254
265
  )
255
266
  ) {
256
- if (field_name !== this.table_field.name) {
257
- await next();
258
- return;
259
- }
260
267
  if (!ctx.$body[this.table_field.name]) {
261
268
  ctx.$body[this.table_field.name] = {};
262
269
  }
@@ -270,6 +277,7 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
270
277
  )
271
278
  );
272
279
  }
280
+
273
281
  ctx.override_reaction = {
274
282
  action: "stay",
275
283
  content: await form.render(
@@ -310,4 +318,9 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
310
318
  this.options.label_add = value;
311
319
  return this;
312
320
  }
321
+
322
+ setAllowReordering(value: boolean) {
323
+ this.options.allow_reordering = value;
324
+ return this;
325
+ }
313
326
  }
@@ -5,8 +5,8 @@ import { walkDir } from "./utils/walk.js";
5
5
  import { importPath } from "./utils/import-path.js";
6
6
  import { assertType, predicates } from "@sealcode/ts-predicates";
7
7
  import { unescape_url_params } from "./utils/escape-url-params.js";
8
- import { formatWithPrettier } from "./utils/prettier.js";
9
8
  import { Templates } from "./templates/templates.js";
9
+ import { formatWithPrettier } from "./utils/prettier.js";
10
10
 
11
11
  const target_locreq = _locreq(process.cwd());
12
12
 
@@ -67,6 +67,13 @@ export function sortRoutes<T extends { url: string }>(urls: T[]): T[] {
67
67
  });
68
68
  }
69
69
 
70
+ const getPathToBuiltFile = (filePath: string) => {
71
+ const rootPath = target_locreq.resolve("");
72
+ const relativePath = relative(rootPath, filePath);
73
+ const distPath = relativePath.replace(/^src[\\/]/, "dist/");
74
+ return distPath.replace(/\.(ts|tsx)$/, ".js");
75
+ };
76
+
70
77
  export async function generateRoutes(): Promise<void> {
71
78
  const files = await Promise.all(
72
79
  (await walkDir(target_locreq.resolve("src/back/routes")))
@@ -79,6 +86,7 @@ export async function generateRoutes(): Promise<void> {
79
86
  .map(async (fullpath) => ({
80
87
  fullpath,
81
88
  actionName: await extractActionName(fullpath),
89
+ module_path: getPathToBuiltFile(fullpath),
82
90
  // trailing slash is important here, as it enables to use the entire path while building relative URLs. For example, while visiting /users/123, the path ./add-photo leads to /users/add-photo. While visiting /users/123/ (note the trailing slash), the path ./add-photo leads to /users/123/add-photo
83
91
  url: unescape_url_params(
84
92
  "/" +
@@ -98,11 +106,12 @@ export async function generateRoutes(): Promise<void> {
98
106
 
99
107
  type URLTree = {
100
108
  actionName?: string;
109
+ module_path?: string;
101
110
  children: { [key: string]: URLTree };
102
111
  };
103
112
 
104
113
  const url_tree: URLTree = { children: {} };
105
- sortRoutes(files).forEach(({ actionName, url }) => {
114
+ sortRoutes(files).forEach(({ actionName, url, module_path }) => {
106
115
  const elements = url.split("/").filter((e) => e != "");
107
116
  let pointer: URLTree = url_tree;
108
117
  for (const [index, element] of elements.entries()) {
@@ -113,6 +122,7 @@ export async function generateRoutes(): Promise<void> {
113
122
  if (index == elements.length - 1) {
114
123
  (pointer as unknown as { actionName: string }).actionName =
115
124
  actionName;
125
+ pointer.module_path = module_path;
116
126
  }
117
127
  }
118
128
  });
@@ -121,6 +131,7 @@ export async function generateRoutes(): Promise<void> {
121
131
 
122
132
  export type URLTree = {
123
133
  actionName?: string;
134
+ module_path?: string;
124
135
  children: { [key: string]: URLTree };
125
136
  };
126
137
  export const url_tree = ${JSON.stringify(url_tree)};
package/src/index.ts CHANGED
@@ -52,3 +52,5 @@ export interface HTMLArgs {
52
52
  }
53
53
 
54
54
  export type HTMLFunc = (args: HTMLArgs) => Readable | Promise<Readable>;
55
+
56
+ export type BreadcrumbLabel = string | ((ctx: Context) => Promise<string>);
package/src/mount.ts CHANGED
@@ -25,16 +25,6 @@ export function mount(
25
25
  ): void {
26
26
  const raw_url = typeof url === "string" ? url : url.rawURL;
27
27
 
28
- const imageRouter = new KoaResponsiveImageRouter({
29
- staticPath: "/tmp",
30
- thumbnailSize: 10,
31
- cacheManagerResolutionThreshold: 10,
32
- imageStoragePath: "/tmp",
33
- smartCropStoragePath: "/tmp",
34
- });
35
-
36
- const fileManager = new FileManager("/tmp", "/uploaded_files");
37
-
38
28
  const args =
39
29
  use_dummy_app ?
40
30
  [
@@ -44,8 +34,14 @@ export function mount(
44
34
  // regard
45
35
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
46
36
  ctx.$app = {
47
- fileManager,
48
- imageRouter,
37
+ fileManager: new FileManager("/tmp", "/uploaded_files"),
38
+ imageRouter: new KoaResponsiveImageRouter({
39
+ staticPath: "/tmp",
40
+ thumbnailSize: 10,
41
+ cacheManagerResolutionThreshold: 10,
42
+ imageStoragePath: "/tmp",
43
+ smartCropStoragePath: "/tmp",
44
+ }),
49
45
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
50
46
  Logger: new Proxy(
51
47
  {},
@@ -46,13 +46,13 @@ export async function formTemplate(
46
46
  ): Promise<string> {
47
47
  const params = { ...defaults, ..._params };
48
48
  const content = `import type { Context } from "koa";
49
- import type { FormData } from "@sealcode/sealgen";
49
+ import type { FormData, BreadcrumbLabel } from "@sealcode/sealgen";
50
50
  import { Form, Controls } from "@sealcode/sealgen";
51
51
  import html from "src/back/html.js";
52
-
53
52
  ${params.postimport}
54
53
 
55
54
  export const actionName = "${action_name}";
55
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
56
56
 
57
57
  const getFields = ${params.get_fields};
58
58
 
@@ -58,12 +58,14 @@ import type { FieldNames } from "sealious";
58
58
  import { TempstreamJSX } from "tempstream";
59
59
  import type ${collectionClassName} from "src/back/collections/${collection_name}.js";
60
60
  import { EditJDDField } from "@sealcode/jdd-editor";
61
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
61
62
  import html from "src/back/html.js";
62
63
  import { registry } from "src/back/jdd-components/registry.js";
63
64
  import { makeJDDContext } from "src/back/jdd-context.js";
64
65
  import { defaultHead } from "src/back/defaultHead.js";
65
66
 
66
67
  export const actionName = "${actionName}";
68
+ export const breadcrumbLabel: BreadcrumbLabel = "${actionName}";
67
69
 
68
70
  export default new (class JDDCreatePreviewPage extends EditJDDField<${collectionClassName}> {
69
71
  getCollection(ctx: Context) {
@@ -9,10 +9,12 @@ export async function LongRunningProcessTemplate(
9
9
  return `import { Context } from "koa";
10
10
  import { TempstreamJSX } from "tempstream";
11
11
  import { Page } from "@sealcode/sealgen";
12
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
12
13
  import html from "${rel("src/back/html.js")}";
13
14
  import { LongRunningProcess } from "sealious";
14
15
 
15
16
  export const actionName = "${action_name}";
17
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
16
18
 
17
19
  export default new (class PatroniteAutoImportStatusPage extends Page {
18
20
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -8,9 +8,11 @@ export async function multiformTemplate(
8
8
  return `import { Context } from "koa";
9
9
  import html from "${rel("src/back/html.js")}";
10
10
  import { Controls, Fields, Form, FormMessage, Multiform } from "@sealcode/sealgen";
11
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
11
12
  import { FlatTemplatable } from "tempstream";
12
13
 
13
14
  export const actionName = "${action_name}";
15
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
14
16
 
15
17
  const fields1 = {
16
18
  email: new Fields.TextBasedSimpleField(true),
@@ -2,9 +2,11 @@ export async function pageTemplate(action_name: string): Promise<string> {
2
2
  return `import type { Context } from "koa";
3
3
  import { TempstreamJSX } from "tempstream";
4
4
  import { Page } from "@sealcode/sealgen";
5
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
5
6
  import html from "src/back/html.js";
6
7
 
7
8
  export const actionName = "${action_name}";
9
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
8
10
 
9
11
  export default new (class ${action_name}Page extends Page {
10
12
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1,9 +1,11 @@
1
1
  export async function postTemplate(action_name: string): Promise<string> {
2
2
  return `import type { Context } from "koa";
3
3
  import { Mountable } from "@sealcode/sealgen";
4
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
4
5
  import type Router from "@koa/router";
5
6
 
6
7
  export const actionName = "${action_name}";
8
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
7
9
 
8
10
  export default new (class ${action_name}Redirect extends Mountable {
9
11
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1,9 +1,11 @@
1
1
  export async function redirectTemplate(action_name: string): Promise<string> {
2
2
  return `import type { Context } from "koa";
3
3
  import { Mountable } from "@sealcode/sealgen";
4
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
4
5
  import type Router from "@koa/router";
5
6
 
6
7
  export const actionName = "${action_name}";
8
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
7
9
 
8
10
  export default new (class ${action_name}Redirect extends Mountable {
9
11
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -118,7 +118,7 @@ import type { FlatTemplatable, Templatable } from "tempstream";
118
118
  import { TempstreamJSX, tempstream } from "tempstream";
119
119
  import { ${uppercase_collection} } from "src/back/collections/collections.js";
120
120
  import html from "src/back/html.js";
121
- import type { ListFilterRender, FormDisplayInfo } from "@sealcode/sealgen";
121
+ import type { ListFilterRender, FormDisplayInfo, BreadcrumbLabel } from "@sealcode/sealgen";
122
122
  import {
123
123
  SealiousItemListPage,
124
124
  BaseListPageFields,
@@ -130,6 +130,7 @@ ${hooks.post_import_js}
130
130
  ${field_import_string}
131
131
 
132
132
  export const actionName = "${action_name}";
133
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
133
134
 
134
135
  const filterFields:{
135
136
  field: keyof (typeof ${uppercase_collection})["fields"];
@@ -10,6 +10,7 @@ export function itemDeleteTemplate(
10
10
  const result = `import type { Context } from "koa";
11
11
  import { Mountable } from "@sealcode/sealgen";
12
12
  import type Router from "@koa/router";
13
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
13
14
 
14
15
  import { ${toPascalCase(collection_name)} } from "${importPath(
15
16
  "src/back/collections/collections.js"
@@ -20,6 +21,7 @@ import { ${list_action_name}URL } from "${importPath(
20
21
  )}";
21
22
 
22
23
  export const actionName = "${delete_action_name}";
24
+ export const breadcrumbLabel: BreadcrumbLabel = "${delete_action_name}";
23
25
 
24
26
  export default new (class ${delete_action_name}Redirect extends Mountable {
25
27
  canAccess = async (ctx: Context) => {
@@ -9,9 +9,11 @@ export async function statefulPageTemplate(
9
9
  return formatWithPrettier(`import { TempstreamJSX, Templatable } from "tempstream";
10
10
  import { Context } from "koa";
11
11
  import { StatefulPage, StatefulPageActionArgument } from "@sealcode/sealgen";
12
+ import type { BreadcrumbLabel } from "@sealcode/sealgen";
12
13
  import html from "${rel("src/back/html.js")}";
13
14
 
14
15
  export const actionName = "${action_name}";
16
+ export const breadcrumbLabel: BreadcrumbLabel = "${action_name}";
15
17
 
16
18
  const actions = {
17
19
  add: ({ state, inputs }: StatefulPageActionArgument<State>) => {
@@ -1,35 +0,0 @@
1
- import { Controller } from "stimulus";
2
- class table_add_button_stimulus_default extends Controller {
3
- constructor() {
4
- super(...arguments);
5
- this.defaultRowsNumber = 20;
6
- }
7
- static {
8
- this.targets = ["textarea"];
9
- }
10
- addRow() {
11
- const template = this.element.closest("turbo-frame")?.querySelector("template");
12
- if (!template) {
13
- throw new Error("Couldn't find template");
14
- }
15
- const tbody = this.element.closest("turbo-frame")?.querySelector("tbody");
16
- if (!tbody) {
17
- throw new Error("tbody not found");
18
- }
19
- const orig_template = template.innerHTML;
20
- const placeholder = this.element.getAttribute("data-table-placeholder");
21
- if (!placeholder) {
22
- throw new Error("Could find placeholder attribute");
23
- }
24
- template.innerHTML = orig_template.replaceAll(
25
- placeholder,
26
- String(tbody.querySelectorAll("tr").length)
27
- );
28
- tbody.appendChild(template.cloneNode(true).content);
29
- template.innerHTML = orig_template;
30
- }
31
- }
32
- export {
33
- table_add_button_stimulus_default as default
34
- };
35
- //# sourceMappingURL=table-add-button.stimulus.js.map