Skip to content

Commit

Permalink
input validation and correct casting, readonly jsx props
Browse files Browse the repository at this point in the history
  • Loading branch information
benStre committed Sep 24, 2024
1 parent 5fbf09a commit 7a057e6
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 8 deletions.
50 changes: 46 additions & 4 deletions datex-bindings/dom-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export namespace DOMUtils {
}
}

type InputValidation = {
message: Datex.RefOrValue<string>,
enabled: boolean
}

export class DOMUtils {

static readonly EVENT_LISTENERS: unique symbol = Symbol.for("DOMUtils.EVENT_LISTENERS");
Expand All @@ -49,6 +54,23 @@ export class DOMUtils {
get document() {return this.context.document}


/**
* Global settings for input validation.
* Per default, input values bound to number references are validated.
* Input validation for specific types can be disabled by setting the 'enabled' property to false,
* and custom validation messages can be set.
*/
public defaultInputValidation: Record<'number'|'bigint', InputValidation> = {
'number': {
message: "Invalid number",
enabled: true,
},
'bigint': {
message: "Invalid integer",
enabled: true,
}
}

escapeHtml(str:string) {
if (typeof str != "string") return "";
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replaceAll('"', '&quot;').replaceAll("'", '&#039;').replace('\u0009', '&#9;')
Expand Down Expand Up @@ -349,7 +371,27 @@ export class DOMUtils {

const event = isSelectElement ? 'change' : 'input';

const handleSetVal = async (val:any) => {
const handleSetVal = async (val:any, type?: "number"|"bigint"|"boolean") => {
if (this.defaultInputValidation.number?.enabled && type == "number") {
if (isNaN(Number(val))) {
element.setCustomValidity(Datex.ReactiveValue.collapseValue(this.defaultInputValidation.number.message, true, true) as string)
element.reportValidity()
return;
}
}

if (this.defaultInputValidation.bigint?.enabled && type == "bigint") {
if (!val.match(/^-?\d+$/)) {
element.setCustomValidity(Datex.ReactiveValue.collapseValue(this.defaultInputValidation.bigint.message, true, true) as string)
element.reportValidity()
return;
}
}

if (type == "boolean") val = Boolean(val);
else if (type == "bigint") val = BigInt(val);
else if (type == "number") val = Number(val);

try {
await (value as Datex.ReactiveValue).setVal(val)
element.setCustomValidity("")
Expand All @@ -365,9 +407,9 @@ export class DOMUtils {
// out
if (attr == "value" || attr == "value:out") {
if (type.matchesType(Datex.Type.std.text)) element.addEventListener(event, () => handleSetVal(element.value))
else if (type.matchesType(Datex.Type.std.decimal)) element.addEventListener(event, () => handleSetVal(Number(element.value)))
else if (type.matchesType(Datex.Type.std.integer)) element.addEventListener(event, () => handleSetVal(BigInt(element.value)))
else if (type.matchesType(Datex.Type.std.boolean)) element.addEventListener(event, () => handleSetVal(Boolean(element.value)))
else if (type.matchesType(Datex.Type.std.decimal)) element.addEventListener(event, () => handleSetVal(element.value, "number"))
else if (type.matchesType(Datex.Type.std.integer)) element.addEventListener(event, () => handleSetVal(element.value, "bigint"))
else if (type.matchesType(Datex.Type.std.boolean)) element.addEventListener(event, () => handleSetVal(element.value, "boolean"))
else if (type.matchesType(Datex.Type.std.void) || type.matchesType(Datex.Type.std.null)) {console.warn("setting value attribute to " + type, element)}
else if (type.matchesType(Datex.Type.std.time)) element.addEventListener(event, () => {
handleSetVal(new Time((element as unknown as HTMLInputElement).valueAsDate ?? new Date((element as unknown as HTMLInputElement).value+"Z")))
Expand Down
9 changes: 5 additions & 4 deletions jsx/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export function getParseJSX(context: DOMContext, domUtils: DOMUtils) {
delete props._debug;
console.log(type,children,props,params)
}

let set_default_children = true;
let set_default_attributes = true;
let allow_invalid_attributes = true;
Expand All @@ -128,27 +128,28 @@ export function getParseJSX(context: DOMContext, domUtils: DOMUtils) {
shadow_root = props['shadow-root']==true?'open':props['shadow-root'];
delete props['shadow-root'];
}


// replace ShadowRoot with shadow-root
if (type === context.ShadowRoot) type = "shadow-root";

if (typeof type === 'function') {

// class component
if (isComponent) {
set_default_children = (type as any)[SET_DEFAULT_CHILDREN] ?? true;
set_default_attributes = (type as any)[SET_DEFAULT_ATTRIBUTES] ?? true;
if (set_default_children) delete params.children;

element = new type(set_default_children ? props : {...props, children}) // uix component
element = new type(set_default_children ? Object.freeze(props) : Object.freeze({...props, children})) // uix component
}
// function component
else {
set_default_children = (type as any)[SET_DEFAULT_CHILDREN];
set_default_attributes = (type as any)[SET_DEFAULT_ATTRIBUTES];
if (set_default_children) delete params.children;

element = type(params)
element = type(Object.freeze(params))

// async component, use uix-placeholder
if (element instanceof Promise) {
Expand Down

0 comments on commit 7a057e6

Please sign in to comment.