@sealcode/sealgen 0.2.1 → 0.2.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 (139) hide show
  1. package/.eslintrc.js +1 -0
  2. package/.log/ti-292609.log +508 -0
  3. package/.log/ti-304933.log +524 -0
  4. package/.log/ti-312312.log +508 -0
  5. package/.log/ti-314240.log +1617 -0
  6. package/.log/ti-62627.log +508 -0
  7. package/.log/ti-64785.log +508 -0
  8. package/.log/ti-65911.log +533 -0
  9. package/.log/ti-69885.log +548 -0
  10. package/.log/ti-72106.log +551 -0
  11. package/.log/ti-74858.log +508 -0
  12. package/.log/tsserver.log +1610 -1625
  13. package/@types/build.d.ts +1 -0
  14. package/@types/cli.d.ts +2 -0
  15. package/@types/forms/controls/checkboxed-list-input.d.ts +16 -0
  16. package/@types/forms/controls/controls.d.ts +9 -0
  17. package/@types/forms/controls/dropdown.d.ts +20 -0
  18. package/@types/forms/controls/editable-collection-subset.d.ts +21 -0
  19. package/@types/forms/controls/form-control.d.ts +8 -0
  20. package/@types/forms/controls/form-field-control.d.ts +13 -0
  21. package/@types/forms/controls/form-header.d.ts +9 -0
  22. package/@types/forms/controls/form-paragraph.d.ts +7 -0
  23. package/@types/forms/controls/frame.d.ts +17 -0
  24. package/@types/forms/controls/simple-input.d.ts +28 -0
  25. package/@types/forms/controls.d.ts +0 -0
  26. package/@types/forms/field.d.ts +42 -0
  27. package/@types/forms/form-fields-list.d.ts +4 -0
  28. package/@types/forms/form.d.ts +42 -0
  29. package/@types/forms/validator.d.ts +3 -0
  30. package/@types/index.d.ts +14 -2
  31. package/@types/page/access-control.d.ts +6 -0
  32. package/@types/page/list.d.ts +59 -0
  33. package/@types/page/page.d.ts +26 -0
  34. package/@types/page/props-parser.d.ts +20 -0
  35. package/@types/run.d.ts +0 -0
  36. package/@types/utils/natural-numbers.d.ts +1 -0
  37. package/@types/utils/sleep.d.ts +1 -0
  38. package/@types/utils/url-with-new-params.d.ts +2 -0
  39. package/@types/utils/utils.d.ts +9 -0
  40. package/@types/utils/walk.d.ts +1 -1
  41. package/lib/build.js +108 -0
  42. package/lib/build.js.map +1 -0
  43. package/lib/cli.js +30 -0
  44. package/lib/cli.js.map +1 -0
  45. package/lib/find-scss-includes.js +2 -1
  46. package/lib/find-scss-includes.js.map +1 -1
  47. package/lib/forms/controls/checkboxed-list-input.js +43 -0
  48. package/lib/forms/controls/checkboxed-list-input.js.map +1 -0
  49. package/lib/forms/controls/controls.js +22 -0
  50. package/lib/forms/controls/controls.js.map +1 -0
  51. package/lib/forms/controls/dropdown.js +40 -0
  52. package/lib/forms/controls/dropdown.js.map +1 -0
  53. package/lib/forms/controls/editable-collection-subset.js +73 -0
  54. package/lib/forms/controls/editable-collection-subset.js.map +1 -0
  55. package/lib/forms/controls/form-control.js +7 -0
  56. package/lib/forms/controls/form-control.js.map +1 -0
  57. package/lib/forms/controls/form-field-control.js +22 -0
  58. package/lib/forms/controls/form-field-control.js.map +1 -0
  59. package/lib/forms/controls/form-header.js +18 -0
  60. package/lib/forms/controls/form-header.js.map +1 -0
  61. package/lib/forms/controls/form-paragraph.js +16 -0
  62. package/lib/forms/controls/form-paragraph.js.map +1 -0
  63. package/lib/forms/controls/frame.js +29 -0
  64. package/lib/forms/controls/frame.js.map +1 -0
  65. package/lib/forms/controls/simple-input.js +50 -0
  66. package/lib/forms/controls/simple-input.js.map +1 -0
  67. package/lib/forms/controls.js +1 -0
  68. package/lib/forms/controls.js.map +1 -0
  69. package/lib/forms/field.js +80 -0
  70. package/lib/forms/field.js.map +1 -0
  71. package/lib/forms/form-fields-list.js +10 -0
  72. package/lib/forms/form-fields-list.js.map +1 -0
  73. package/lib/forms/form.js +162 -0
  74. package/lib/forms/form.js.map +1 -0
  75. package/lib/forms/validator.js +11 -0
  76. package/lib/forms/validator.js.map +1 -0
  77. package/lib/generate-routes.js +1 -1
  78. package/lib/generate-routes.js.map +1 -1
  79. package/lib/index.js +17 -25
  80. package/lib/index.js.map +1 -1
  81. package/lib/page/access-control.js +20 -0
  82. package/lib/page/access-control.js.map +1 -0
  83. package/lib/page/list.js +148 -0
  84. package/lib/page/list.js.map +1 -0
  85. package/lib/page/page.js +20 -0
  86. package/lib/page/page.js.map +1 -0
  87. package/lib/page/props-parser.js +74 -0
  88. package/lib/page/props-parser.js.map +1 -0
  89. package/lib/run.js +1 -0
  90. package/lib/run.js.map +1 -0
  91. package/lib/templates/list.js +1 -1
  92. package/lib/utils/list-collections.js +1 -1
  93. package/lib/utils/list-collections.js.map +1 -1
  94. package/lib/utils/natural-numbers.js +10 -0
  95. package/lib/utils/natural-numbers.js.map +1 -0
  96. package/lib/utils/sleep.js +8 -0
  97. package/lib/utils/sleep.js.map +1 -0
  98. package/lib/utils/url-with-new-params.js +12 -0
  99. package/lib/utils/url-with-new-params.js.map +1 -0
  100. package/lib/utils/utils.js +19 -0
  101. package/lib/utils/utils.js.map +1 -0
  102. package/lib/utils/walk.js +4 -4
  103. package/lib/utils/walk.js.map +1 -1
  104. package/package.json +17 -4
  105. package/src/build.ts +82 -0
  106. package/src/cli.ts +34 -0
  107. package/src/find-scss-includes.ts +3 -2
  108. package/src/forms/controls/checkboxed-list-input.ts +50 -0
  109. package/src/forms/controls/controls.ts +9 -0
  110. package/src/forms/controls/dropdown.ts +57 -0
  111. package/src/forms/controls/editable-collection-subset.ts +91 -0
  112. package/src/forms/controls/form-control.ts +13 -0
  113. package/src/forms/controls/form-field-control.ts +39 -0
  114. package/src/forms/controls/form-header.ts +16 -0
  115. package/src/forms/controls/form-paragraph.ts +11 -0
  116. package/src/forms/controls/frame.ts +26 -0
  117. package/src/forms/controls/simple-input.ts +73 -0
  118. package/src/forms/controls.ts +0 -0
  119. package/src/forms/field.ts +119 -0
  120. package/src/forms/form-fields-list.ts +7 -0
  121. package/src/forms/form.ts +242 -0
  122. package/src/forms/forms.scss +18 -0
  123. package/src/forms/validator.ts +10 -0
  124. package/src/generate-routes.ts +2 -2
  125. package/src/index.ts +14 -28
  126. package/src/page/access-control.ts +18 -0
  127. package/src/page/list.ts +214 -0
  128. package/src/page/page.ts +31 -0
  129. package/src/page/props-parser.ts +99 -0
  130. package/src/run.ts +0 -0
  131. package/src/templates/list.ts +1 -1
  132. package/src/utils/list-collections.ts +2 -2
  133. package/src/utils/natural-numbers.ts +5 -0
  134. package/src/utils/sleep.ts +3 -0
  135. package/src/utils/url-with-new-params.ts +9 -0
  136. package/src/utils/utils.ts +10 -0
  137. package/src/utils/walk.ts +2 -2
  138. package/tsconfig.json +5 -2
  139. package/src/tsconfig.json +0 -18
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.walkDir = exports.listCollections = exports.curryImportPath = exports.importPath = exports.exec = exports.UrlWithNewParams = exports.naturalNumbers = exports.sleep = void 0;
4
+ var sleep_1 = require("./sleep");
5
+ Object.defineProperty(exports, "sleep", { enumerable: true, get: function () { return sleep_1.sleep; } });
6
+ var natural_numbers_1 = require("./natural-numbers");
7
+ Object.defineProperty(exports, "naturalNumbers", { enumerable: true, get: function () { return natural_numbers_1.naturalNumbers; } });
8
+ var url_with_new_params_1 = require("./url-with-new-params");
9
+ Object.defineProperty(exports, "UrlWithNewParams", { enumerable: true, get: function () { return url_with_new_params_1.UrlWithNewParams; } });
10
+ var exec_1 = require("./exec");
11
+ Object.defineProperty(exports, "exec", { enumerable: true, get: function () { return exec_1.exec; } });
12
+ var import_path_1 = require("./import-path");
13
+ Object.defineProperty(exports, "importPath", { enumerable: true, get: function () { return import_path_1.importPath; } });
14
+ Object.defineProperty(exports, "curryImportPath", { enumerable: true, get: function () { return import_path_1.curryImportPath; } });
15
+ var list_collections_1 = require("./list-collections");
16
+ Object.defineProperty(exports, "listCollections", { enumerable: true, get: function () { return list_collections_1.listCollections; } });
17
+ var walk_1 = require("./walk");
18
+ Object.defineProperty(exports, "walkDir", { enumerable: true, get: function () { return walk_1.walkDir; } });
19
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils/utils.ts"],"names":[],"mappings":";;;AAGA,iCAAgC;AAAvB,8FAAA,KAAK,OAAA;AACd,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AACvB,6DAAyD;AAAhD,uHAAA,gBAAgB,OAAA;AACzB,+BAA8B;AAArB,4FAAA,IAAI,OAAA;AACb,6CAA4D;AAAnD,yGAAA,UAAU,OAAA;AAAE,8GAAA,eAAe,OAAA;AACpC,uDAAqD;AAA5C,mHAAA,eAAe,OAAA;AACxB,+BAAiC;AAAxB,+FAAA,OAAO,OAAA"}
package/lib/utils/walk.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.walk = void 0;
3
+ exports.walkDir = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const path_1 = require("path");
6
- async function walk(dir) {
6
+ async function walkDir(dir) {
7
7
  const entries = await fs_1.promises.readdir(dir);
8
8
  let ret = [];
9
9
  for (const entry of entries) {
@@ -12,7 +12,7 @@ async function walk(dir) {
12
12
  const info = await fs_1.promises.stat(fullpath);
13
13
  if (info.isDirectory()) {
14
14
  // eslint-disable-next-line no-await-in-loop
15
- ret = [...ret, ...(await walk(fullpath))];
15
+ ret = [...ret, ...(await walkDir(fullpath))];
16
16
  }
17
17
  else {
18
18
  ret = [...ret, fullpath];
@@ -20,5 +20,5 @@ async function walk(dir) {
20
20
  }
21
21
  return ret;
22
22
  }
23
- exports.walk = walk;
23
+ exports.walkDir = walkDir;
24
24
  //# sourceMappingURL=walk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"walk.js","sourceRoot":"","sources":["../../src/utils/walk.ts"],"names":[],"mappings":";;;AAAA,2BAAoC;AACpC,+BAA+B;AAExB,KAAK,UAAU,IAAI,CAAC,GAAW;IACrC,MAAM,OAAO,GAAG,MAAM,aAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,GAAG,GAAa,EAAE,CAAC;IACvB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE;QAC5B,MAAM,QAAQ,GAAG,cAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACrC,4CAA4C;QAC5C,MAAM,IAAI,GAAG,MAAM,aAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAErC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE;YACvB,4CAA4C;YAC5C,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;SAC1C;aAAM;YACN,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,QAAQ,CAAC,CAAC;SACzB;KACD;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAhBD,oBAgBC"}
1
+ {"version":3,"file":"walk.js","sourceRoot":"","sources":["../../src/utils/walk.ts"],"names":[],"mappings":";;;AAAA,2BAAoC;AACpC,+BAA+B;AAExB,KAAK,UAAU,OAAO,CAAC,GAAW;IACxC,MAAM,OAAO,GAAG,MAAM,aAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,GAAG,GAAa,EAAE,CAAC;IACvB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE;QAC5B,MAAM,QAAQ,GAAG,cAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACrC,4CAA4C;QAC5C,MAAM,IAAI,GAAG,MAAM,aAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAErC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE;YACvB,4CAA4C;YAC5C,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,GAAG,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;SAC7C;aAAM;YACN,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,QAAQ,CAAC,CAAC;SACzB;KACD;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAhBD,0BAgBC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sealcode/sealgen",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Module to automate adding routes and collections to a sealious application",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -10,11 +10,12 @@
10
10
  "test-reports": "npm run build && rm -fr .xunit coverage && npm run test -- --cover --test-report"
11
11
  },
12
12
  "bin": {
13
- "sealgen": "./lib/index.js"
13
+ "sealgen": "./lib/cli.js"
14
14
  },
15
15
  "author": "",
16
16
  "license": "ISC",
17
17
  "devDependencies": {
18
+ "@types/koa__router": "^8.0.4",
18
19
  "@types/mocha": "^9.0.0",
19
20
  "@types/prompts": "^2.0.14",
20
21
  "@typescript-eslint/eslint-plugin": "^4.29.3",
@@ -33,8 +34,20 @@
33
34
  },
34
35
  "types": "./@types/index.d.ts",
35
36
  "dependencies": {
36
- "@sealcode/ts-predicates": "^0.2.1",
37
+ "@koa/router": "^10.1.1",
38
+ "@sealcode/ts-predicates": "^0.4.0",
39
+ "esbuild": "^0.14.54",
40
+ "esbuild-sass-plugin": "^2.3.1",
41
+ "koa": "^2.13.0",
37
42
  "locreq": "^2.0.2",
38
- "prompts": "^2.4.2"
43
+ "merge": "^2.1.1",
44
+ "prompts": "^2.4.2",
45
+ "tempstream": "^0.0.19",
46
+ "tiny-glob": "^0.2.9"
47
+ },
48
+ "peerDependencies": {
49
+ "@koa/router": "^10.1.1",
50
+ "koa": "^2.13.0",
51
+ "sealious": "0.14.x"
39
52
  }
40
53
  }
package/src/build.ts ADDED
@@ -0,0 +1,82 @@
1
+ import { build, BuildResult } from "esbuild";
2
+ import { sassPlugin } from "esbuild-sass-plugin";
3
+ import * as chokidar from "chokidar";
4
+ import { default as glob } from "tiny-glob";
5
+ import { generateScssIncludes } from "./find-scss-includes";
6
+ import { sleep } from "./utils/sleep";
7
+
8
+ async function build_ts() {
9
+ const entryPoints = await glob("./src/back/**/*.ts");
10
+ await build({
11
+ entryPoints,
12
+ sourcemap: true,
13
+ outdir: "./dist/back",
14
+ logLevel: "info",
15
+ platform: "node",
16
+ target: "node16",
17
+ format: "cjs",
18
+ });
19
+ }
20
+
21
+ async function complex_build(watch: boolean): Promise<void> {
22
+ let scss_build: BuildResult | null = null;
23
+ if (watch) {
24
+ const scss_watcher = chokidar.watch("src", { ignoreInitial: true });
25
+ scss_watcher.on("all", async (_, path) => {
26
+ if (!scss_build) return;
27
+ if (path.endsWith(".scss") && !path.endsWith("/includes.scss")) {
28
+ // refresh the list of all scss files in includes.scss
29
+ await generateScssIncludes();
30
+ try {
31
+ await scss_build?.rebuild?.();
32
+ console.log(`Built main.scss [on ${path}]`);
33
+ } catch (e) {
34
+ console.error(e);
35
+ await sleep(200);
36
+ scss_build
37
+ ?.rebuild?.()
38
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
39
+ .catch((e) => console.error(e?.message));
40
+ }
41
+ }
42
+ if (path.endsWith(".ts")) {
43
+ await build_ts();
44
+ console.log(`Finished TS biuld [on ${path}]`);
45
+ }
46
+ });
47
+ }
48
+
49
+ try {
50
+ scss_build = await build({
51
+ entryPoints: ["./src/main.scss"],
52
+ sourcemap: true,
53
+ outfile: "./public/dist/style.css",
54
+ logLevel: "info",
55
+ incremental: watch,
56
+ plugins: [sassPlugin()],
57
+ });
58
+ await build_ts();
59
+ watch && scss_build?.rebuild?.();
60
+ } catch (e) {
61
+ console.error(e);
62
+ }
63
+ }
64
+
65
+ export async function buildProject(...args: string[]): Promise<void> {
66
+ const watch = args.includes("--watch");
67
+ await build({
68
+ entryPoints: ["./src/front/index.ts"],
69
+ sourcemap: true,
70
+ outfile: "./public/dist/bundle.js",
71
+ logLevel: "info",
72
+ bundle: true,
73
+ watch,
74
+ });
75
+
76
+ try {
77
+ await complex_build(watch);
78
+ } catch (e) {
79
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
80
+ console.error(e.message);
81
+ }
82
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import { addCollection } from "./add-collection";
3
+ import { addRoute } from "./add-route";
4
+ import { buildProject } from "./build";
5
+ import { generateScssIncludes } from "./find-scss-includes";
6
+ import { generateCollections } from "./generate-collections";
7
+ import { generateRoutes } from "./generate-routes";
8
+
9
+ const actions: Record<
10
+ string,
11
+ (...args: string[]) => Promise<void> | undefined
12
+ > = {
13
+ "add-collection": addCollection,
14
+ "add-route": addRoute,
15
+ "generate-collections": generateCollections,
16
+ "generate-routes": generateRoutes,
17
+ "generate-scss-includes": generateScssIncludes,
18
+ build: buildProject,
19
+ default: async function () {
20
+ console.log("Usage: `sealscript <action>`");
21
+ console.log(
22
+ `Available actions: ${Object.keys(actions)
23
+ .filter((e) => e != "default")
24
+ .join(", ")}`
25
+ );
26
+ },
27
+ };
28
+
29
+ void (async function () {
30
+ const action = process.argv.at(2);
31
+ actions;
32
+ const fn = actions[action || "default"] || actions.default;
33
+ await fn(...process.argv.slice(3));
34
+ })();
@@ -1,13 +1,13 @@
1
1
  import { promises as fs } from "fs";
2
2
  import _locreq from "locreq";
3
3
  import { relative } from "path";
4
- import { walk } from "./utils/walk";
4
+ import { walkDir } from "./utils/walk";
5
5
 
6
6
  const target_locreq = _locreq(process.cwd());
7
7
 
8
8
  export async function generateScssIncludes(): Promise<void> {
9
9
  const files = await Promise.all(
10
- (await walk(target_locreq.resolve("src/")))
10
+ (await walkDir(target_locreq.resolve("src/")))
11
11
  .filter(
12
12
  (f) =>
13
13
  f.endsWith(".scss") &&
@@ -23,6 +23,7 @@ export async function generateScssIncludes(): Promise<void> {
23
23
  target_locreq.resolve("src/includes.scss"),
24
24
  `// DO NOT EDIT! This file is generated automaticaly with npx sealgen generate-scss-includes
25
25
 
26
+ @import "../node_modules/@sealcode/sealgen/src/forms/forms.scss";
26
27
  ${files.map((path) => `@import "${path}";`).join("\n")}
27
28
  `
28
29
  );
@@ -0,0 +1,50 @@
1
+ import { is, predicates } from "@sealcode/ts-predicates";
2
+ import { BaseContext } from "koa";
3
+ import { tempstream } from "tempstream";
4
+ import { ChekboxedListField, FormField } from "../field";
5
+ import { Form, FormData } from "../form";
6
+ import { FormFieldsList } from "../form-fields-list";
7
+ import { FormFieldControl } from "./controls";
8
+
9
+ export class CheckboxedListInput extends FormFieldControl {
10
+ constructor(
11
+ public fieldname: string,
12
+ public options: { label: string } = { label: fieldname }
13
+ ) {
14
+ super([fieldname]);
15
+ }
16
+
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] || "";
29
+ if (!is(pickedValues, predicates.array(predicates.string))) {
30
+ throw new Error("picked values is not an array of strings");
31
+ }
32
+ const [options, isVisible] = await Promise.all([
33
+ field.generateOptions(ctx),
34
+ field.isVisible(ctx),
35
+ ]);
36
+ return tempstream/* HTML */ `${isVisible
37
+ ? Object.entries(options).map(
38
+ ([value, text]) => /* HTML */ `<div>
39
+ <input
40
+ type="checkbox"
41
+ id="${field.name}.${value}"
42
+ name="${field.name}.${value}"
43
+ ${pickedValues.includes(value) ? "checked" : ""}
44
+ />
45
+ <label for="${field.name}.${value}">${text}</label>
46
+ </div>`
47
+ )
48
+ : ""}`;
49
+ }
50
+ }
@@ -0,0 +1,9 @@
1
+ export { FormControl } from "./form-control";
2
+ export { FormFieldControl } from "./form-field-control";
3
+ export { EditableCollectionSubset } from "./editable-collection-subset";
4
+ export { Frame } from "./frame";
5
+ export { CheckboxedListInput } from "./checkboxed-list-input";
6
+ export { Dropdown } from "./dropdown";
7
+ export { SimpleInput } from "./simple-input";
8
+ export { FormHeader } from "./form-header";
9
+ export { FormParagraph } from "./form-paragraph";
@@ -0,0 +1,57 @@
1
+ import { BaseContext } from "koa";
2
+ import { tempstream } from "tempstream";
3
+ import { FormField, PickFromListField } from "../field";
4
+ import { FormData } from "../form";
5
+ import { FormFieldsList } from "../form-fields-list";
6
+ import { FormFieldControl } from "./controls";
7
+
8
+ export class Dropdown extends FormFieldControl {
9
+ constructor(
10
+ public fieldname: string,
11
+ public options: {
12
+ label: string;
13
+ autosubmit?: boolean;
14
+ autocomplete?: boolean;
15
+ } = {
16
+ label: fieldname,
17
+ autosubmit: false,
18
+ autocomplete: true,
19
+ }
20
+ ) {
21
+ super([fieldname]);
22
+ }
23
+
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
41
+ ><select
42
+ name="${this.fieldnames}"
43
+ id="${id}"
44
+ ${this.options.autosubmit ? `onchange='this.form.submit()'` : ""}
45
+ ${!this.options.autocomplete ? `autocomplete="off"` : ""}
46
+ >
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
+ )
54
+ )}
55
+ </select>`;
56
+ }
57
+ }
@@ -0,0 +1,91 @@
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";
6
+ import { FormFieldControl } from "./controls";
7
+
8
+ /**
9
+ * This control has own forms in it so if you want to use it you
10
+ * probably shouldn't use `await super.render(ctx, data, path)` in
11
+ * render method of you from implementation and you should write
12
+ * your own implementation of this method. See forms that uses
13
+ * this control for reference.
14
+ */
15
+ export class EditableCollectionSubset extends FormFieldControl {
16
+ constructor(
17
+ public fieldname: string,
18
+ public actionname: string,
19
+ public listLabel?: string,
20
+ public selectLabel?: string
21
+ ) {
22
+ super([fieldname]);
23
+ }
24
+
25
+ _render(
26
+ 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[];
38
+
39
+ return tempstream/* HTML */ `<div>
40
+ <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) =>
74
+ Object.entries(options)
75
+ .filter(([value]) => !values.includes(value))
76
+ .map(
77
+ ([value, text]) =>
78
+ `<option value="${value}">${text}</option>`
79
+ )
80
+ )}
81
+ </select>
82
+ <input type="hidden" name="${this.actionname}" value="select" />
83
+ <input
84
+ type="submit"
85
+ ${this.selectLabel ? `value="${this.selectLabel}"` : ""}
86
+ />
87
+ </form>
88
+ </div>`;
89
+ }
90
+ role = <const>"input";
91
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseContext } from "koa";
2
+ import { Templatable } from "tempstream";
3
+ import { FormField } from "../field";
4
+ import { FormData } from "../form";
5
+
6
+ export abstract class FormControl {
7
+ abstract render(
8
+ ctx: BaseContext,
9
+ formFields: FormField[],
10
+ data: FormData
11
+ ): Templatable | Promise<Templatable>;
12
+ abstract role: "input" | "decoration" | "messages" | "submit";
13
+ }
@@ -0,0 +1,39 @@
1
+ import { BaseContext } from "koa";
2
+ import { Templatable } from "tempstream";
3
+ import { FormField } from "../field";
4
+ import { FormData } from "../form";
5
+ import { FormControl } from "./form-control";
6
+
7
+ export abstract class FormFieldControl extends FormControl {
8
+ role = <const>"input";
9
+ constructor(public fieldnames: string[]) {
10
+ super();
11
+ }
12
+
13
+ areFieldNamesValid(fields: FormField[]) {
14
+ return this.fieldnames.every((fieldname) =>
15
+ fields.some((f) => f.name == fieldname)
16
+ );
17
+ }
18
+
19
+ abstract _render(
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
+ }
39
+ }
@@ -0,0 +1,16 @@
1
+ import { BaseContext } from "koa";
2
+ import { FormControl } from "./controls";
3
+
4
+ export class FormHeader extends FormControl {
5
+ role = <const>"decoration";
6
+ constructor(
7
+ public text: string,
8
+ public isVisible: (ctx: BaseContext) => Promise<boolean> = async () => true
9
+ ) {
10
+ super();
11
+ }
12
+ async render(ctx: BaseContext) {
13
+ const isVsbl = await this.isVisible(ctx);
14
+ return isVsbl ? `<h2>${this.text}</h2>` : "";
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ import { FormControl } from "./controls";
2
+
3
+ export class FormParagraph extends FormControl {
4
+ role = <const>"decoration";
5
+ constructor(public text: string) {
6
+ super();
7
+ }
8
+ render() {
9
+ return `<p>${this.text}</p>`;
10
+ }
11
+ }
@@ -0,0 +1,26 @@
1
+ import { Templatable } from "tempstream";
2
+ import { FormControl } from "./controls";
3
+
4
+ /**
5
+ * This class will render `turbo-frame` tag so that u can
6
+ * embed other route inside your form. This will require
7
+ * to add value to `data: FormData` (inside your master form
8
+ * render function) with key `Frame.FRAME_PATH_KEY`. Value
9
+ * needs to be url to route that you want to embed. If you
10
+ * this value wont be provided frame will redner empty string.
11
+ * See `src/back/routes/profile/[id].form.ts` for an example.
12
+ */
13
+ export class Frame extends FormControl {
14
+ constructor(public src: string) {
15
+ super();
16
+ }
17
+
18
+ render(): Templatable | Promise<Templatable> {
19
+ return /* HTML */ `<turbo-frame
20
+ id="contrahents"
21
+ loading="lazy"
22
+ src="${this.src}"
23
+ ></turbo-frame>`;
24
+ }
25
+ role = <const>"decoration";
26
+ }
@@ -0,0 +1,73 @@
1
+ import { BaseContext } from "koa";
2
+ import { FormField } from "../field";
3
+ import { FormData } from "../form";
4
+ import { FormFieldsList } from "../form-fields-list";
5
+ import { FormFieldControl } from "./controls";
6
+
7
+ export class SimpleInput extends FormFieldControl {
8
+ constructor(
9
+ public fieldname: string,
10
+ public options: {
11
+ id?: string;
12
+ label?: string;
13
+ autocomplete?: boolean;
14
+ type?:
15
+ | "color"
16
+ | "date"
17
+ | "email"
18
+ | "file"
19
+ | "month"
20
+ | "number"
21
+ | "password"
22
+ | "search"
23
+ | "tel"
24
+ | "text"
25
+ | "time"
26
+ | "url"
27
+ | "week";
28
+ value?: string;
29
+ placeholder?: string;
30
+ readonly?: boolean;
31
+ step?: number;
32
+ } = {}
33
+ ) {
34
+ super([fieldname]);
35
+ }
36
+
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;
44
+ const type = this.options.type || "text";
45
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
46
+ const value = data.values[field.name] as string;
47
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
48
+ const raw_value = data.raw_values[field.name] as string;
49
+ const placeholder = this.options.placeholder || type;
50
+ const readonly = this.options.readonly || false;
51
+ const required = field.required;
52
+ const error = data.errors[field.name];
53
+ return /* HTML */ `<div class="input">
54
+ <label for="${id}">${label}</label>
55
+ <input
56
+ id="${id}"
57
+ type="${type}"
58
+ name="${field.name}"
59
+ value="${value === undefined
60
+ ? raw_value == undefined
61
+ ? ""
62
+ : raw_value
63
+ : value}"
64
+ placeholder="${placeholder}"
65
+ ${readonly ? "readonly" : ""}
66
+ ${required ? "required" : ""}
67
+ ${!this.options.autocomplete ? `autocomplete="off"` : ""}
68
+ ${this.options.step ? `step="${this.options.step}"` : ""}
69
+ />
70
+ ${error ? `<div class="input__error">${error}</div>` : ""}
71
+ </div>`;
72
+ }
73
+ }
File without changes