Page MenuHomeSealhub

derived.ts
No OneTemporary

derived.ts

import { randomUUID } from "crypto";
import { FlatTemplatable, tempstream } from "tempstream";
import { FormField } from "../fields/field.js";
import { FormDataValue } from "../form-types.js";
import { FormControl, FormControlContext } from "./form-control.js";
export class Derived<
Consts extends Record<string, unknown | undefined>
> extends FormControl {
role = <const>"decoration";
constructor(
public fields: FormField[],
public _render: (
values: Record<string, string>,
consts: Consts
) => Promise<FlatTemplatable>,
public getConsts: (
fctx: FormControlContext
) => Promise<Consts> = async () => ({} as Consts)
) {
super();
}
async getBackendValues(
fctx: FormControlContext
): Promise<Record<string, FormDataValue>> {
const fieldnames = this.fields.map((f) => f.name);
const result = Object.fromEntries(
Object.entries(fctx.data).filter(([key]) =>
fieldnames.includes(key)
)
);
return result;
}
async render(fctx: FormControlContext): Promise<FlatTemplatable> {
// the A makes it always a valid id, as it cannot start with a number
const id = "A" + randomUUID();
const consts = this.getConsts(fctx);
const frontend_side_refresh_code = (values_code = "getValues()") =>
`render(${values_code}, consts)
.then(result=>document.querySelector("#${id}").innerHTML=result)`;
return tempstream/* HTML */ `<div>
<script>
(function () {
const consts = ${"{" +
Object.entries((await consts) || {})
.map(([key, value]) => {
return `"${key}": ${
typeof value === "object"
? JSON.stringify(value)
: typeof value == "string"
? '"' + value.toString() + '"'
: (value as string).toString()
}`;
})
.join(",") +
"}"};
const form_container =
document.currentScript.closest(".form-container");
const render = ${this._render.toString()};
function getValues() {
return Object.fromEntries(
${JSON.stringify(
this.fields.map((field) => field.name)
)}.map((field_name) => {
let input = form_container.querySelector(
\`input[name="\${field_name}"]\`
);
if (!input) {
return [field_name, ""];
}
if (input.type === "radio") {
input = form_container.querySelector(
\`input[name="\${field_name}"]:checked\`
);
}
return [field_name, input.value];
})
);
}
function setupWatcher(field_name) {
form_container
.querySelectorAll(\`input[name="\${field_name}"]\`)
.forEach((input) => {
input.addEventListener("change", () => {
${frontend_side_refresh_code()};
});
});
}
${JSON.stringify(
this.fields.map((field) => field.name)
)}.forEach(setupWatcher);
setTimeout(() => {
${frontend_side_refresh_code(
JSON.stringify(await this.getBackendValues(fctx))
)};
}, 0);
})();
</script>
<div id="${id}">${this._render({}, await consts)}</div>
</div>`;
}
}

File Metadata

Mime Type
text/html
Expires
Fri, Jan 24, 15:15 (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
600313
Default Alt Text
derived.ts (3 KB)

Event Timeline