Page MenuHomeSealhub

tickable.ts
No OneTemporary

tickable.ts

import { Context } from "koa";
import { FlatTemplatable } from "tempstream";
import { attribute } from "../../sanitize.js";
import { getRequiredClass, inputWrapper } from "../../utils/input-wrapper.js";
import { FormField } from "../fields/field.js";
import { FormDataValue } from "../form-types.js";
import { FormControlContext } from "./form-control.js";
import { FormFieldControl } from "./form-field-control.js";
export type TickableOptions = {
id?: string;
name?: string;
label?: string;
hide_errors?: boolean;
readonly?: boolean;
};
export abstract class Tickable<T> extends FormFieldControl {
constructor(
public field: FormField<boolean, T>,
public options: TickableOptions = {}
) {
super([field]);
}
public type: "checkbox" | "radio";
abstract getValueAttribute(
ctx: Context,
data: Record<string, FormDataValue>,
value: T
): string;
abstract isChecked(
ctx: Context,
data: Record<string, FormDataValue>,
value: T
): boolean;
makeInputID(
ctx: Context,
data: Record<string, FormDataValue>,
value: T
): string {
const value_attr = this.getValueAttribute(ctx, data, value);
return (
this.options.id ||
this.field.name + (value_attr ? "--" + value_attr : "")
);
}
getWrapperClasses(
ctx: Context,
data: Record<string, FormDataValue>,
value: T
): string[] {
return [
this.makeInputID(ctx, data, value),
this.type,
this.field.name.replaceAll(/\W/g, "-"),
getRequiredClass(this.field.required),
...(this.isChecked(ctx, data, value) ? ["checked"] : []),
];
}
async render(fctx: FormControlContext): Promise<FlatTemplatable> {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const { parsed, valid, message } = await this.field.getValue(
fctx.ctx,
fctx.data
);
const value = this.getValueAttribute(fctx.ctx, fctx.data, parsed);
const id = this.makeInputID(fctx.ctx, fctx.data, parsed);
const label =
this.options.label != undefined
? this.options.label
: this.field.name;
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const readonly = this.options.readonly || false;
const required = this.field.required;
return inputWrapper(
this.getWrapperClasses(fctx.ctx, fctx.data, parsed),
/* HTML */ `
<input
id="${id}"
type="${this.type}"
name="${fctx.field_name_prefix}${this.options.name ||
this.field.name}"
${value ? `value="${attribute(value)}"` : ""}
${fctx.form_id ? `form="${fctx.form_id}"` : ""}
${this.isChecked(fctx.ctx, fctx.data, parsed)
? "checked"
: ""}
${readonly ? "readonly" : ""}
${required ? "required" : ""}
autocomplete="off"
onchange="this.closest('.form-container').querySelectorAll('.form-input__wrapper--${this
.field
.name}').forEach(e=>e.setAttribute('data-checked','false'));
this.closest('.form-input__wrapper').setAttribute('data-checked',this.checked);"
/>
<label for="${id}">${label}</label>
<script>
(function () {
let wrapper = document.currentScript.closest(
".form-input__wrapper"
);
wrapper.setAttribute(
"data-checked",
wrapper.querySelector("input").checked.toString()
);
})();
</script>
${~valid && !this.options.hide_errors && fctx.validate
? `<div class="input__error">${message}</div>`
: ""}
`
);
}
}

File Metadata

Mime Type
text/html
Expires
Tue, Dec 24, 14:02 (1 d, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
557119
Default Alt Text
tickable.ts (3 KB)

Event Timeline