@sealcode/sealgen 0.15.33 → 0.15.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@types/src/forms/controls/controls.d.ts +1 -0
- package/@types/src/forms/controls/table.d.ts +15 -0
- package/@types/src/forms/fields/fields.d.ts +1 -0
- package/@types/src/forms/fields/table.d.ts +12 -0
- package/@types/src/templates/shared/collection-create-form.d.ts +2 -0
- package/@types/src/templates/shared/collection-edit-form.d.ts +1 -0
- package/@types/src/templates/shared/collection-list.d.ts +1 -0
- package/@types/src/templates/shared/confirm-item-delete.d.ts +1 -0
- package/@types/src/templates/shared/item-delete.d.ts +1 -0
- package/@types/src/templates/shared/shared-crud-form-fields.d.ts +44 -0
- package/@types/src/utils/utils.d.ts +1 -0
- package/@types/src/utils/with-fallback.d.ts +2 -0
- package/lib/add-crud.js +131 -0
- package/lib/forms/controls/controls.js +1 -0
- package/lib/forms/controls/table.js +62 -0
- package/lib/forms/fields/fields.js +1 -0
- package/lib/forms/fields/table.js +58 -0
- package/lib/templates/shared/collection-create-form.js +146 -0
- package/lib/templates/shared/collection-edit-form.js +200 -0
- package/lib/templates/shared/collection-list.js +211 -0
- package/lib/templates/shared/confirm-item-delete.js +72 -0
- package/lib/templates/shared/item-delete.js +41 -0
- package/lib/templates/shared/shared-crud-form-fields.js +118 -0
- package/lib/utils/utils.js +1 -0
- package/lib/utils/with-fallback.js +21 -0
- package/lib/utils/write-file.js +11 -0
- package/package.json +1 -1
- package/src/forms/controls/controls.ts +1 -0
- package/src/forms/controls/table.ts +78 -0
- package/src/forms/fields/fields.ts +1 -0
- package/src/forms/fields/table.ts +72 -0
- package/src/utils/utils.ts +1 -0
- package/src/utils/with-fallback.ts +47 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { FlatTemplatable } from "tempstream";
|
|
2
|
+
import { FormField } from "../fields/field.js";
|
|
3
|
+
import { Table as TableField } from "../fields/table.js";
|
|
4
|
+
import { FormControl, FormControlContext } from "./form-control.js";
|
|
5
|
+
|
|
6
|
+
export class Table<F extends Record<string, FormField>> extends FormControl {
|
|
7
|
+
role = <const>"input";
|
|
8
|
+
constructor(
|
|
9
|
+
public table_field: TableField<F>,
|
|
10
|
+
public render_fields: {
|
|
11
|
+
[field_name in keyof F]: (
|
|
12
|
+
fctx: FormControlContext,
|
|
13
|
+
name: string,
|
|
14
|
+
value: string
|
|
15
|
+
) => string;
|
|
16
|
+
}
|
|
17
|
+
) {
|
|
18
|
+
super();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async render(fctx: FormControlContext): Promise<FlatTemplatable> {
|
|
22
|
+
const { parsed: rows } = await this.table_field.getValue(
|
|
23
|
+
fctx.ctx,
|
|
24
|
+
fctx.data
|
|
25
|
+
);
|
|
26
|
+
if (!rows) {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
return /* HTML */ `<div
|
|
30
|
+
class="${[
|
|
31
|
+
"form-input__wrapper",
|
|
32
|
+
"form-input__wrapper--type--table",
|
|
33
|
+
"form-input__wrapper--options-count--" + rows.length.toString(),
|
|
34
|
+
...(rows.length >= 5
|
|
35
|
+
? ["form-input__wrapper--options-count--5-or-more"]
|
|
36
|
+
: []),
|
|
37
|
+
...(rows.length >= 10
|
|
38
|
+
? ["form-input__wrapper--options-count--10-or-more"]
|
|
39
|
+
: []),
|
|
40
|
+
...(rows.length >= 15
|
|
41
|
+
? ["form-input__wrapper--options-count--15-or-more"]
|
|
42
|
+
: []),
|
|
43
|
+
...(rows.length >= 20
|
|
44
|
+
? ["form-input__wrapper--options-count--20-or-more"]
|
|
45
|
+
: []),
|
|
46
|
+
].join(" ")}"
|
|
47
|
+
>
|
|
48
|
+
<div class="table__wrapper">
|
|
49
|
+
<table>
|
|
50
|
+
<tbody>
|
|
51
|
+
<tr>
|
|
52
|
+
${rows
|
|
53
|
+
?.map((row, row_index) => {
|
|
54
|
+
return /* HTML */ `<tr>
|
|
55
|
+
${Object.entries(
|
|
56
|
+
this.table_field.columns
|
|
57
|
+
)
|
|
58
|
+
.map(([key]) => {
|
|
59
|
+
return `<td>${this.render_fields[
|
|
60
|
+
key
|
|
61
|
+
](
|
|
62
|
+
fctx,
|
|
63
|
+
`${this.table_field.name}[${row_index}][${key}]`,
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
65
|
+
row[key] as string
|
|
66
|
+
)}</td>`;
|
|
67
|
+
})
|
|
68
|
+
.join("")}
|
|
69
|
+
</tr>`;
|
|
70
|
+
})
|
|
71
|
+
.join("")}
|
|
72
|
+
</tr>
|
|
73
|
+
</tbody>
|
|
74
|
+
</table>
|
|
75
|
+
</div>
|
|
76
|
+
</div>`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { is, predicates } from "@sealcode/ts-predicates";
|
|
2
|
+
import { Context } from "koa";
|
|
3
|
+
import { FormDataValue } from "../form-types.js";
|
|
4
|
+
import { FieldParseResult, FormField } from "./field.js";
|
|
5
|
+
|
|
6
|
+
export type TableFieldParsed<F extends Record<string, FormField>> = {
|
|
7
|
+
[field_name in keyof F]: unknown;
|
|
8
|
+
}[];
|
|
9
|
+
|
|
10
|
+
export class Table<F extends Record<string, FormField>> extends FormField<
|
|
11
|
+
true,
|
|
12
|
+
TableFieldParsed<F>
|
|
13
|
+
> {
|
|
14
|
+
constructor(public columns: F) {
|
|
15
|
+
super(true);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getEmptyValue() {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async parse(
|
|
23
|
+
ctx: Context,
|
|
24
|
+
raw_value: FormDataValue
|
|
25
|
+
): Promise<FieldParseResult<TableFieldParsed<F>>> {
|
|
26
|
+
// we expect an array-like object: {"0": {...}, ...etc}
|
|
27
|
+
if (!is(raw_value, predicates.object)) {
|
|
28
|
+
return {
|
|
29
|
+
parsable: false,
|
|
30
|
+
error: "Expected an object",
|
|
31
|
+
parsed_value: null,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const result: { [field_name in keyof F]: unknown }[] = [];
|
|
35
|
+
for (const row_number in raw_value) {
|
|
36
|
+
const row_data = raw_value[row_number];
|
|
37
|
+
const parsed_row: Record<string, unknown> = {};
|
|
38
|
+
if (!is(row_data, predicates.object)) {
|
|
39
|
+
throw new Error(`Expected object, got ${String(row_data)}`);
|
|
40
|
+
}
|
|
41
|
+
for (const key in row_data) {
|
|
42
|
+
const field = this.columns[key];
|
|
43
|
+
if (field == undefined) {
|
|
44
|
+
return {
|
|
45
|
+
parsable: false,
|
|
46
|
+
error: `Unknown column: ${key}`,
|
|
47
|
+
parsed_value: null,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// eslint-disable-next-line no-await-in-loop, @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
51
|
+
const parse_result = await field.parse(
|
|
52
|
+
ctx,
|
|
53
|
+
(row_data as any)[key]
|
|
54
|
+
);
|
|
55
|
+
if (!parse_result.parsable) {
|
|
56
|
+
console.debug("table.ts:51");
|
|
57
|
+
return {
|
|
58
|
+
parsable: false,
|
|
59
|
+
error: `Invalid value for row ${row_number}.${key}: ${parse_result.error}`,
|
|
60
|
+
parsed_value: null,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
|
|
64
|
+
parsed_row[key] = parse_result.parsed_value as any;
|
|
65
|
+
}
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
67
|
+
result.push(parsed_row as { [field_name in keyof F]: unknown });
|
|
68
|
+
}
|
|
69
|
+
const ret = { parsed_value: result, parsable: true, error: null };
|
|
70
|
+
return ret as FieldParseResult<TableFieldParsed<F>>;
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/utils/utils.ts
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// use this to more concisely define values with one or more fallbacks to use when they are null.
|
|
2
|
+
|
|
3
|
+
// If called with two args, the second one has to be non-null
|
|
4
|
+
// if called with three args, the third one has to be non-null
|
|
5
|
+
//
|
|
6
|
+
// in two-arg mode, if the first argument is not null, then it's returned and fallback is not used.
|
|
7
|
+
// if the first argument is null, then the second arg is returned
|
|
8
|
+
//
|
|
9
|
+
// in three-arg mode, the first value is never returned. If the first arg is not-null, then the second arg is returned (if it's not null). Otherwise the third arg is returned
|
|
10
|
+
|
|
11
|
+
export function withFallback<T1, T2 extends NonNullable<unknown>>(
|
|
12
|
+
value1: T1,
|
|
13
|
+
value2: T2
|
|
14
|
+
): NonNullable<T1> | T2;
|
|
15
|
+
export function withFallback<T1, T2, T3 extends NonNullable<unknown>>(
|
|
16
|
+
value1: T1,
|
|
17
|
+
value2: T2,
|
|
18
|
+
value3?: T3
|
|
19
|
+
): NonNullable<T1> | NonNullable<T2> | T3;
|
|
20
|
+
export function withFallback<T1, T2, T3 extends NonNullable<unknown>>(
|
|
21
|
+
value1: T1,
|
|
22
|
+
value2: T2,
|
|
23
|
+
value3?: T3
|
|
24
|
+
): NonNullable<T1> | NonNullable<T2> | T3 {
|
|
25
|
+
if (value3 !== undefined) {
|
|
26
|
+
// three arg mode
|
|
27
|
+
if (
|
|
28
|
+
value1 !== null &&
|
|
29
|
+
value1 !== undefined &&
|
|
30
|
+
value2 !== null &&
|
|
31
|
+
value2 !== undefined
|
|
32
|
+
) {
|
|
33
|
+
return value2;
|
|
34
|
+
} else {
|
|
35
|
+
return value3;
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
// two arg mode
|
|
39
|
+
if (value1 !== null && value1 !== undefined) {
|
|
40
|
+
return value1;
|
|
41
|
+
} else if (value2 !== undefined && value2 !== null) {
|
|
42
|
+
return value2;
|
|
43
|
+
} else {
|
|
44
|
+
throw new Error("No suitable fallback: ${arguments}");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|