@sealcode/sealgen 0.8.48 → 0.8.49
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/.log/ti-107027.log +576 -0
- package/.log/ti-129414.log +1355 -0
- package/.log/ti-135707.log +1049 -0
- package/.log/ti-136226.log +1049 -0
- package/.log/ti-147652.log +533 -0
- package/.log/ti-157088.log +2938 -0
- package/.log/ti-250667.log +548 -0
- package/.log/ti-29438.log +165 -0
- package/.log/ti-29894.log +149 -0
- package/.log/ti-33785.log +149 -0
- package/.log/ti-34262.log +149 -0
- package/.log/ti-35719.log +149 -0
- package/.log/ti-41629.log +518 -0
- package/.log/ti-441248.log +517 -0
- package/.log/ti-5352.log +517 -0
- package/.log/ti-76335.log +639 -0
- package/.log/ti-811639.log +517 -0
- package/.log/ti-81805.log +564 -0
- package/.log/ti-86459.log +517 -0
- package/.log/tsserver.log +6881 -0
- package/.log/tsserver.sync-conflict-20221010-222251-JUX7UR2.log +9412 -0
- package/package.json +1 -1
- package/src/add-collection.ts +31 -0
- package/src/add-route.test.ts +58 -0
- package/src/add-route.ts +89 -0
- package/src/build.ts +94 -0
- package/src/cli.ts +36 -0
- package/src/components/components.ts +2 -0
- package/src/components/tabs.ts +95 -0
- package/src/components/toggle-modal.ts +52 -0
- package/src/example.test.ts +3 -0
- package/src/find-scss-includes.ts +32 -0
- package/src/forms/controls/array.ts +226 -0
- package/src/forms/controls/autocomplete.ts +33 -0
- package/src/forms/controls/big-radio-group.ts +48 -0
- package/src/forms/controls/checkbox-group.ts +71 -0
- package/src/forms/controls/checkbox.ts +34 -0
- package/src/forms/controls/checkboxed-list-input.ts +50 -0
- package/src/forms/controls/collapsible-checkbox-group.ts +28 -0
- package/src/forms/controls/collapsible-field-group.ts +94 -0
- package/src/forms/controls/controls.ts +30 -0
- package/src/forms/controls/derived-frame.ts +91 -0
- package/src/forms/controls/derived.ts +108 -0
- package/src/forms/controls/dropdown.ts +87 -0
- package/src/forms/controls/editable-collection-subset.ts +97 -0
- package/src/forms/controls/field-group.ts +69 -0
- package/src/forms/controls/file.ts +47 -0
- package/src/forms/controls/form-control.ts +24 -0
- package/src/forms/controls/form-field-control.ts +22 -0
- package/src/forms/controls/form-header.ts +19 -0
- package/src/forms/controls/form-paragraph.ts +11 -0
- package/src/forms/controls/frame.ts +26 -0
- package/src/forms/controls/hidden-input.ts +22 -0
- package/src/forms/controls/html.ts +38 -0
- package/src/forms/controls/multiple-files.ts +256 -0
- package/src/forms/controls/multiple-photos.ts +44 -0
- package/src/forms/controls/number.ts +38 -0
- package/src/forms/controls/photo.ts +74 -0
- package/src/forms/controls/radio.ts +43 -0
- package/src/forms/controls/reverse-single-reference.ts +50 -0
- package/src/forms/controls/simple-input.ts +148 -0
- package/src/forms/controls/textarea.ts +60 -0
- package/src/forms/controls/tickable.ts +127 -0
- package/src/forms/fields/boolean.test.ts +36 -0
- package/src/forms/fields/boolean.ts +63 -0
- package/src/forms/fields/checkboxed-list.ts +66 -0
- package/src/forms/fields/collection-field.ts +62 -0
- package/src/forms/fields/email.test.ts +24 -0
- package/src/forms/fields/email.ts +20 -0
- package/src/forms/fields/enum-multiple.test.ts +31 -0
- package/src/forms/fields/enum-multiple.ts +62 -0
- package/src/forms/fields/enum.test.ts +25 -0
- package/src/forms/fields/enum.ts +56 -0
- package/src/forms/fields/existing-sealious-value.ts +22 -0
- package/src/forms/fields/field.ts +172 -0
- package/src/forms/fields/file.ts +89 -0
- package/src/forms/fields/multiple-files.ts +20 -0
- package/src/forms/fields/number.test.ts +34 -0
- package/src/forms/fields/number.ts +70 -0
- package/src/forms/fields/pick-from-list.test.ts +19 -0
- package/src/forms/fields/pick-from-list.ts +52 -0
- package/src/forms/fields/proxy-field.ts +24 -0
- package/src/forms/fields/regexp.test.ts +25 -0
- package/src/forms/fields/regexp.ts +53 -0
- package/src/forms/fields/reverse-single-reference.ts +20 -0
- package/src/forms/fields/simple-form-field.ts +30 -0
- package/src/forms/fields/structured-array.ts +27 -0
- package/src/forms/form.test.ts +351 -0
- package/src/forms/form.ts +294 -0
- package/src/forms/forms.scss +85 -0
- package/src/forms/multiform.test.ts +540 -0
- package/src/forms/multiform.ts +236 -0
- package/src/generate-collections.ts +77 -0
- package/src/generate-routes.ts +121 -0
- package/src/index.ts +25 -0
- package/src/make-env.ts +15 -0
- package/src/make-hidden-inputs.ts +24 -0
- package/src/mount.ts +85 -0
- package/src/page/access-control.ts +18 -0
- package/src/page/list.ts +240 -0
- package/src/page/mountable-with-fields.ts +158 -0
- package/src/page/mountable.ts +13 -0
- package/src/page/page.ts +46 -0
- package/src/page/props-parser.ts +38 -0
- package/src/page/stateful-page.ts +202 -0
- package/src/run.ts +0 -0
- package/src/sanitize.ts +9 -0
- package/src/templates/collection.ts +11 -0
- package/src/templates/form.ts +60 -0
- package/src/templates/list.ts +75 -0
- package/src/templates/multiform.ts +58 -0
- package/src/templates/page.ts +25 -0
- package/src/templates/post.ts +21 -0
- package/src/templates/redirect.ts +22 -0
- package/src/templates/route-test.ts +27 -0
- package/src/templates/stateful-page.ts +65 -0
- package/src/test_utils/locator_is_visible.ts +17 -0
- package/src/utils/assert-throws.ts +18 -0
- package/src/utils/base64.ts +7 -0
- package/src/utils/browser-creator.js +10 -0
- package/src/utils/escape-url-params.ts +14 -0
- package/src/utils/exec.ts +30 -0
- package/src/utils/format-external-url.ts +13 -0
- package/src/utils/import-path.ts +25 -0
- package/src/utils/input-wrapper.ts +21 -0
- package/src/utils/list-collections.ts +16 -0
- package/src/utils/make-slug.ts +5 -0
- package/src/utils/natural-numbers.ts +5 -0
- package/src/utils/prettier.ts +13 -0
- package/src/utils/question.ts +23 -0
- package/src/utils/render-attributes.ts +17 -0
- package/src/utils/sleep.ts +3 -0
- package/src/utils/translate.ts +13 -0
- package/src/utils/url-with-new-params.ts +9 -0
- package/src/utils/utils.ts +23 -0
- package/src/utils/walk.ts +20 -0
package/package.json
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { resolve } from "path";
|
|
2
|
+
import _locreq from "locreq";
|
|
3
|
+
import { promises as fs, existsSync } from "fs";
|
|
4
|
+
import { question } from "./utils/question";
|
|
5
|
+
import { collectionTemplate } from "./templates/collection";
|
|
6
|
+
import { generateCollections } from "./generate-collections";
|
|
7
|
+
const target_locreq = _locreq(process.cwd());
|
|
8
|
+
|
|
9
|
+
export async function addCollection(): Promise<void> {
|
|
10
|
+
const regex_pattern = /^([A-Z_$][a-zA-Z0-9_$]*)$/;
|
|
11
|
+
const collection_name = await question(
|
|
12
|
+
'What\'s the name of the collection class (e.g. "SecretDocuments", must start with a capital letter): ',
|
|
13
|
+
(s: string) => s.length > 3 && regex_pattern.test(s)
|
|
14
|
+
);
|
|
15
|
+
const url = await question(
|
|
16
|
+
"Enter the url-friendly name for the collection (e.g. secret-documents): ",
|
|
17
|
+
(s: string) => !s.includes("/") && !s.includes(" ")
|
|
18
|
+
);
|
|
19
|
+
const file_path = target_locreq.resolve(`src/back/collections/${url}.ts`);
|
|
20
|
+
|
|
21
|
+
if (existsSync(file_path)) {
|
|
22
|
+
// eslint-disable-next-line no-console
|
|
23
|
+
console.error(`ERROR: File ${file_path} already exists.`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
await fs.mkdir(resolve(file_path, "../"), { recursive: true });
|
|
27
|
+
await fs.writeFile(file_path, collectionTemplate(collection_name));
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log(`${file_path} created`);
|
|
30
|
+
await generateCollections();
|
|
31
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import { exec, spawn } from "child_process";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
import axios from "axios";
|
|
5
|
+
import waitPort from "wait-port";
|
|
6
|
+
import getPort from "get-port";
|
|
7
|
+
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
|
|
10
|
+
describe("add route test", () => {
|
|
11
|
+
let node_pid = 0;
|
|
12
|
+
const app_path = "/tmp/sealious-minimal";
|
|
13
|
+
const container_name = "sealgen-test-mongo";
|
|
14
|
+
afterEach(async function () {
|
|
15
|
+
if (node_pid != 0) await execAsync(`kill -9 ${node_pid}`);
|
|
16
|
+
await Promise.all([
|
|
17
|
+
fs.rm(app_path, { recursive: true, force: true }),
|
|
18
|
+
execAsync(`docker rm -f ${container_name}`),
|
|
19
|
+
]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("creates minimal sealious app", async function () {
|
|
23
|
+
this.timeout(250000);
|
|
24
|
+
console.log("\tcloning sealious-minimal...");
|
|
25
|
+
await execAsync(
|
|
26
|
+
`git clone https://hub.sealcode.org/source/sealious-minimal.git ${app_path} && cd ${app_path} && git checkout f48991a0a8b426e0d2f4a4b448fdd41d643b0dae`
|
|
27
|
+
);
|
|
28
|
+
console.log("\tinstalling dependencies and sealgen...");
|
|
29
|
+
await execAsync(
|
|
30
|
+
`cd ${app_path} && npm ci && rm -rf node_modules/@sealcode/sealgen && ln -s $OLDPWD node_modules/@sealcode/sealgen`
|
|
31
|
+
);
|
|
32
|
+
console.log("\trunning add-route and building app...");
|
|
33
|
+
await execAsync(
|
|
34
|
+
`cd ${app_path} && npx sealgen add-route --url=/sample --action=Sample --mode=page && npm run build`
|
|
35
|
+
);
|
|
36
|
+
console.log(
|
|
37
|
+
"\tstarting the database...(if timeouts here, run 'docker pull mongo:4.4-bionic' to solve)"
|
|
38
|
+
);
|
|
39
|
+
const port = await getPort();
|
|
40
|
+
await execAsync(`docker run -p ${port}:27017 --name ${container_name} -d mongo:4.4-bionic`);
|
|
41
|
+
console.log("\trunning app and making request to the generated url...");
|
|
42
|
+
const env = { ...process.env, SEALIOUS_MONGO_PORT: port.toString() };
|
|
43
|
+
let app_start = spawn(`node`, [`${app_path}/dist/back/index.js`], {
|
|
44
|
+
env,
|
|
45
|
+
});
|
|
46
|
+
if (app_start.pid == undefined) throw new Error("node failed to start");
|
|
47
|
+
node_pid = app_start.pid;
|
|
48
|
+
await waitPort({
|
|
49
|
+
host: "localhost",
|
|
50
|
+
port: 8080,
|
|
51
|
+
output: "silent",
|
|
52
|
+
timeout: 6000,
|
|
53
|
+
});
|
|
54
|
+
const res = await axios.get("http://localhost:8080/sample");
|
|
55
|
+
if (res.status != 200)
|
|
56
|
+
throw new Error("url generated by add-route exists but doesnt return 200");
|
|
57
|
+
});
|
|
58
|
+
});
|
package/src/add-route.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { existsSync, promises as fs } from "fs";
|
|
2
|
+
import _locreq from "locreq";
|
|
3
|
+
import { resolve } from "path";
|
|
4
|
+
import { generateRoutes } from "./generate-routes";
|
|
5
|
+
import { formTemplate } from "./templates/form";
|
|
6
|
+
import { listTemplate } from "./templates/list";
|
|
7
|
+
import { multiformTemplate } from "./templates/multiform";
|
|
8
|
+
import { pageTemplate } from "./templates/page";
|
|
9
|
+
import { redirectTemplate } from "./templates/redirect";
|
|
10
|
+
import { testTemplate } from "./templates/route-test";
|
|
11
|
+
import { escape_url_params } from "./utils/escape-url-params";
|
|
12
|
+
import prompts = require("prompts");
|
|
13
|
+
import { postTemplate } from "./templates/post";
|
|
14
|
+
import { statefulPageTemplate } from "./templates/stateful-page";
|
|
15
|
+
|
|
16
|
+
export async function addRoute(
|
|
17
|
+
params: Partial<{ [key in "action" | "url" | "mode"]: string }>,
|
|
18
|
+
app_directory: string = process.cwd()
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
const target_locreq = _locreq(app_directory);
|
|
21
|
+
prompts.override(params);
|
|
22
|
+
const response = await prompts([
|
|
23
|
+
{
|
|
24
|
+
type: "text",
|
|
25
|
+
name: "action",
|
|
26
|
+
message: 'What\'s the name of the action (e.g. "AddUser"): ',
|
|
27
|
+
validate: (s: string) =>
|
|
28
|
+
s.length > 3 ? true : "Should be at least 3 characters long",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: "text",
|
|
32
|
+
name: "url",
|
|
33
|
+
message:
|
|
34
|
+
"Enter a full absolute path for the new route (e.g. /admin/users/add): ",
|
|
35
|
+
validate: (s: string) =>
|
|
36
|
+
s.trim()[0] == "/" ? true : "Should start with a '/'",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: "select",
|
|
40
|
+
name: "mode",
|
|
41
|
+
message: "What kind of route is it?",
|
|
42
|
+
choices: [
|
|
43
|
+
{ title: "page", value: "page" },
|
|
44
|
+
{ title: "stateful page", value: "sreact" },
|
|
45
|
+
{ title: "form", value: "form" },
|
|
46
|
+
{ title: "list", value: "list" },
|
|
47
|
+
{ title: "multiform", value: "multiform" },
|
|
48
|
+
{ title: "redirect", value: "redirect" },
|
|
49
|
+
{ title: "raw POST", value: "post" },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const { action, url, mode } = response as Record<string, string>;
|
|
55
|
+
|
|
56
|
+
const file_path = target_locreq.resolve(
|
|
57
|
+
`src/back/routes/${escape_url_params(url)}.${mode}.ts`
|
|
58
|
+
);
|
|
59
|
+
const test_file_path = target_locreq.resolve(
|
|
60
|
+
`src/back/routes/${escape_url_params(url)}.test.ts`
|
|
61
|
+
);
|
|
62
|
+
const template = {
|
|
63
|
+
page: pageTemplate,
|
|
64
|
+
form: formTemplate,
|
|
65
|
+
list: listTemplate,
|
|
66
|
+
multiform: multiformTemplate,
|
|
67
|
+
redirect: redirectTemplate,
|
|
68
|
+
post: postTemplate,
|
|
69
|
+
sreact: statefulPageTemplate,
|
|
70
|
+
}[mode];
|
|
71
|
+
|
|
72
|
+
if (!template) {
|
|
73
|
+
throw new Error(`Could not get template ${mode}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (existsSync(file_path)) {
|
|
77
|
+
// eslint-disable-next-line no-console
|
|
78
|
+
console.error(`ERROR: File ${file_path} already exists.`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await fs.mkdir(resolve(file_path, "../"), { recursive: true });
|
|
82
|
+
await fs.writeFile(file_path, await template(action, file_path));
|
|
83
|
+
await fs.writeFile(test_file_path, testTemplate(action, file_path));
|
|
84
|
+
// eslint-disable-next-line no-console
|
|
85
|
+
console.log(`${file_path} created`);
|
|
86
|
+
// eslint-disable-next-line no-console
|
|
87
|
+
console.log(`${test_file_path} created`);
|
|
88
|
+
await generateRoutes();
|
|
89
|
+
}
|
package/src/build.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
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
|
+
import { generateCollections } from "./generate-collections";
|
|
8
|
+
import { generateRoutes } from "./generate-routes";
|
|
9
|
+
|
|
10
|
+
async function build_ts() {
|
|
11
|
+
await Promise.all([generateCollections(), generateRoutes()]);
|
|
12
|
+
const entryPoints = await glob("./src/back/**/*.ts");
|
|
13
|
+
try {
|
|
14
|
+
await build({
|
|
15
|
+
entryPoints,
|
|
16
|
+
sourcemap: true,
|
|
17
|
+
outdir: "./dist/back",
|
|
18
|
+
logLevel: "info",
|
|
19
|
+
platform: "node",
|
|
20
|
+
target: "es2022",
|
|
21
|
+
format: "cjs",
|
|
22
|
+
});
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.error(e.message);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function complex_build(watch: boolean): Promise<void> {
|
|
29
|
+
let scss_build: BuildResult | null = null;
|
|
30
|
+
if (watch) {
|
|
31
|
+
const watcher = chokidar.watch("src", { ignoreInitial: true });
|
|
32
|
+
watcher.on("all", async (_, path) => {
|
|
33
|
+
if (!scss_build) return;
|
|
34
|
+
if (path.includes(".#")) return;
|
|
35
|
+
if (path.endsWith(".scss") && !path.endsWith("/includes.scss")) {
|
|
36
|
+
// refresh the list of all scss files in includes.scss
|
|
37
|
+
await generateScssIncludes();
|
|
38
|
+
try {
|
|
39
|
+
await scss_build?.rebuild?.();
|
|
40
|
+
console.log(`Built main.scss [on ${path}]`);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.error(e.message);
|
|
43
|
+
await sleep(200);
|
|
44
|
+
scss_build
|
|
45
|
+
?.rebuild?.()
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
47
|
+
.catch((e) => console.error(e?.message));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
path.endsWith(".ts") &&
|
|
52
|
+
!path.endsWith("src/back/collections/collections.ts") &&
|
|
53
|
+
!path.endsWith("src/back/routes/routes.ts") &&
|
|
54
|
+
!path.endsWith("src/back/routes/urls.ts")
|
|
55
|
+
) {
|
|
56
|
+
await build_ts();
|
|
57
|
+
console.log(`Finished TS build [on ${path}]`);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
await generateScssIncludes();
|
|
62
|
+
scss_build = await build({
|
|
63
|
+
entryPoints: ["./src/main.scss"],
|
|
64
|
+
sourcemap: true,
|
|
65
|
+
outfile: "./public/dist/style.css",
|
|
66
|
+
logLevel: "info",
|
|
67
|
+
incremental: watch,
|
|
68
|
+
plugins: [sassPlugin()],
|
|
69
|
+
});
|
|
70
|
+
await build_ts();
|
|
71
|
+
watch && scss_build?.rebuild?.();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function buildProject({
|
|
75
|
+
watch,
|
|
76
|
+
}: {
|
|
77
|
+
watch: boolean;
|
|
78
|
+
}): Promise<void> {
|
|
79
|
+
try {
|
|
80
|
+
await build({
|
|
81
|
+
entryPoints: ["./src/front/index.ts"],
|
|
82
|
+
sourcemap: true,
|
|
83
|
+
outfile: "./public/dist/bundle.js",
|
|
84
|
+
logLevel: "info",
|
|
85
|
+
bundle: true,
|
|
86
|
+
watch,
|
|
87
|
+
});
|
|
88
|
+
await complex_build(watch);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.log("CAUGHT!");
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
92
|
+
console.error(e.message);
|
|
93
|
+
}
|
|
94
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
import { makeEnv } from "./make-env";
|
|
9
|
+
|
|
10
|
+
const actions: Record<
|
|
11
|
+
string,
|
|
12
|
+
(args: Record<string, string | boolean>) => Promise<void> | undefined
|
|
13
|
+
> = {
|
|
14
|
+
"add-collection": addCollection,
|
|
15
|
+
"add-route": addRoute,
|
|
16
|
+
"generate-collections": generateCollections,
|
|
17
|
+
"generate-routes": generateRoutes,
|
|
18
|
+
"generate-scss-includes": generateScssIncludes,
|
|
19
|
+
build: buildProject,
|
|
20
|
+
default: async function () {
|
|
21
|
+
console.log("Usage: `sealscript <action>`");
|
|
22
|
+
console.log(
|
|
23
|
+
`Available actions: ${Object.keys(actions)
|
|
24
|
+
.filter((e) => e != "default")
|
|
25
|
+
.join(", ")}`
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
"make-env": makeEnv,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
void (async function () {
|
|
32
|
+
const action = process.argv.at(2);
|
|
33
|
+
actions;
|
|
34
|
+
const fn = actions[action || "default"] || actions.default;
|
|
35
|
+
await fn(require("yargs").argv);
|
|
36
|
+
})();
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { FlatTemplatable, tempstream } from "tempstream";
|
|
2
|
+
|
|
3
|
+
function name_to_id(str: string) {
|
|
4
|
+
return str.replace(/\W/g, "-");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const style = (hide_tabs_width: number) => /* HTML */ `<style>
|
|
8
|
+
.tab-label {
|
|
9
|
+
height: 45px;
|
|
10
|
+
line-height: 45px;
|
|
11
|
+
padding: 0 45px;
|
|
12
|
+
display: block;
|
|
13
|
+
float: left;
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
border-bottom: 3px solid transparent;
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
font-size: 18px;
|
|
18
|
+
-webkit-user-select: none;
|
|
19
|
+
user-select: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@media screen and (max-width: ${hide_tabs_width}px) {
|
|
23
|
+
.tab-label {
|
|
24
|
+
display: none;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.tab-label--mobile {
|
|
29
|
+
line-height: unset;
|
|
30
|
+
padding-top: 30px;
|
|
31
|
+
padding-left: 0;
|
|
32
|
+
pointer-events: none;
|
|
33
|
+
padding-bottom: 30px;
|
|
34
|
+
color: $font-graphite;
|
|
35
|
+
border-bottom: 2px solid $font-primary;
|
|
36
|
+
display: none;
|
|
37
|
+
@media screen and (max-width: ${hide_tabs_width}px) {
|
|
38
|
+
display: block;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.tab-content {
|
|
43
|
+
padding-top: 30px;
|
|
44
|
+
clear: both;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@media screen and (max-width: ${hide_tabs_width}px) {
|
|
48
|
+
.tab-content {
|
|
49
|
+
display: block !important;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
</style>`;
|
|
53
|
+
|
|
54
|
+
export function tabs(
|
|
55
|
+
tabs: Record<string, FlatTemplatable>,
|
|
56
|
+
selected_tab_style = "font-weight: bold;",
|
|
57
|
+
selected_index = 0,
|
|
58
|
+
hide_tabs_width = 0
|
|
59
|
+
): FlatTemplatable {
|
|
60
|
+
return /* HTML */ tempstream`${style(hide_tabs_width)}
|
|
61
|
+
${Object.keys(tabs)
|
|
62
|
+
.map((tab_title, index) => {
|
|
63
|
+
const id = name_to_id(tab_title);
|
|
64
|
+
return /* HTML */ `<input
|
|
65
|
+
type="radio"
|
|
66
|
+
name="tabs"
|
|
67
|
+
id="${id}"
|
|
68
|
+
class="tab-radio--${id}"
|
|
69
|
+
style="display: none"
|
|
70
|
+
${selected_index == index ? "checked" : ""}
|
|
71
|
+
/>
|
|
72
|
+
<label class="tab-label tab-label--${id}" for="${id}">
|
|
73
|
+
${tab_title}
|
|
74
|
+
</label>`;
|
|
75
|
+
})
|
|
76
|
+
.join("\n")}
|
|
77
|
+
${Object.entries(tabs).map(([title, content]) => {
|
|
78
|
+
const id = name_to_id(title);
|
|
79
|
+
return /* HTML */ tempstream`<div class="tab-label tab-label--mobile">
|
|
80
|
+
${title}
|
|
81
|
+
</div>
|
|
82
|
+
<div class="tab-content tab-content--${id}">${content}</div>
|
|
83
|
+
<style>
|
|
84
|
+
.tab-radio--${id} ~ .tab-content--${id} {
|
|
85
|
+
display:none;
|
|
86
|
+
}
|
|
87
|
+
.tab-radio--${id}:checked ~ .tab-content--${id} {
|
|
88
|
+
display:block;
|
|
89
|
+
}
|
|
90
|
+
.tab-radio--${id}:checked ~ label.tab-label--${id} {
|
|
91
|
+
${selected_tab_style}
|
|
92
|
+
}
|
|
93
|
+
</style>`;
|
|
94
|
+
})}`;
|
|
95
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { FlatTemplatable, tempstream } from "tempstream";
|
|
2
|
+
import { makeSlug } from "../utils/utils";
|
|
3
|
+
|
|
4
|
+
export function toggleModal(options: {
|
|
5
|
+
name: string;
|
|
6
|
+
contentWrapped: (id: string) => FlatTemplatable | Promise<FlatTemplatable>;
|
|
7
|
+
contentUnwrapped: (
|
|
8
|
+
id: string
|
|
9
|
+
) => FlatTemplatable | Promise<FlatTemplatable>;
|
|
10
|
+
}) {
|
|
11
|
+
const { contentWrapped, contentUnwrapped, name } = options;
|
|
12
|
+
const id = makeSlug(name);
|
|
13
|
+
return tempstream/* HTML */ `<style>
|
|
14
|
+
.toggle-modal__wrapped {
|
|
15
|
+
display: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@media (max-width: 960px) {
|
|
19
|
+
.toggle-modal__wrapped {
|
|
20
|
+
display: block;
|
|
21
|
+
}
|
|
22
|
+
.toggle-modal__unwrapped {
|
|
23
|
+
display: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#${id} {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#${id}:checked ~ .toggle-modal__wrapped {
|
|
31
|
+
display: none;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#${id}:checked ~ .toggle-modal__unwrapped {
|
|
35
|
+
display: block;
|
|
36
|
+
position: fixed;
|
|
37
|
+
height: 100vh;
|
|
38
|
+
width: 100vw;
|
|
39
|
+
top: 0;
|
|
40
|
+
left: 0;
|
|
41
|
+
background-color: white;
|
|
42
|
+
z-index: 99;
|
|
43
|
+
overflow-y: scroll;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
</style>
|
|
47
|
+
<div class="toggle-modal toggle-modal--${id}">
|
|
48
|
+
<input id="${id}" name="${id}" type="checkbox" autocomplete="off" />
|
|
49
|
+
<div class="toggle-modal__wrapped">${contentWrapped(id)}</div>
|
|
50
|
+
<div class="toggle-modal__unwrapped">${contentUnwrapped(id)}</div>
|
|
51
|
+
</div>`;
|
|
52
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import _locreq from "locreq";
|
|
3
|
+
import { relative } from "path";
|
|
4
|
+
import { walkDir } from "./utils/walk";
|
|
5
|
+
|
|
6
|
+
const target_locreq = _locreq(process.cwd());
|
|
7
|
+
|
|
8
|
+
export async function generateScssIncludes(): Promise<void> {
|
|
9
|
+
const files = await Promise.all(
|
|
10
|
+
(await walkDir(target_locreq.resolve("src/")))
|
|
11
|
+
.filter(
|
|
12
|
+
(f) =>
|
|
13
|
+
f.endsWith(".scss") &&
|
|
14
|
+
!f.endsWith("/main.scss") &&
|
|
15
|
+
!f.endsWith("/includes.scss")
|
|
16
|
+
)
|
|
17
|
+
.map((fullpath) =>
|
|
18
|
+
relative(target_locreq.resolve("src/"), fullpath)
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
await fs.writeFile(
|
|
23
|
+
target_locreq.resolve("src/includes.scss"),
|
|
24
|
+
`// DO NOT EDIT! This file is generated automaticaly with npx sealgen generate-scss-includes
|
|
25
|
+
|
|
26
|
+
@import "../node_modules/@sealcode/sealgen/src/forms/forms.scss";
|
|
27
|
+
${files.map((path) => `@import "${path}";`).join("\n")}
|
|
28
|
+
`
|
|
29
|
+
);
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
console.log("Successfuly generated new src/includes.scss file");
|
|
32
|
+
}
|