I'm testing modular forms with Qwik. I'm using the component from the playground Git repository and have successfully created a custom toPhoneNumber function. It formats a 10-digit number correctly when I type it in. However, I've noticed that I can still type in a string, which is not the default behavior for phone number inputs on the web. Do I need to implement separate logic to handle this, or is there a way to accomplish this through the form API?
import type { TransformOptions } from "@modular-forms/qwik";
import { toCustom$ } from "@modular-forms/qwik";
export function toPhoneNumber(options: TransformOptions) {
return toCustom$<string>((value) => {
if (value === undefined) return;
// Remove everything that is not a number
const numbers = value.replace(/\D/g, "");
// Continue if string is not empty
if (numbers) {
// Extract area, first 3 and last 4
const matchResult = numbers.match(/(\d{0,3})(\d{0,3})(\d{0,4})/);
if (matchResult !== null) {
const [, area, first3, last4] = matchResult;
// If length or first 3 is less than 1
if (first3.length < 1) {
return `(${area}`;
}
// If length or last 4 is less than 1
if (last4.length < 1) {
return `(${area}) ${first3}`;
}
// Otherwise return full US number
return `(${area}) ${first3}-${last4}`;
}
}
// Otherwise return an empty string
return "";
}, options);
}
TextInput.tsx
import { component$, type QRL, useSignal, useTask$ } from "@builder.io/qwik";
import clsx from "clsx";
import { InputError } from "./InputError";
import { InputLabel } from "./InputLabel";
type TextInputProps = {
ref: QRL<(element: HTMLInputElement) => void>;
type: "text" | "email" | "tel" | "password" | "url" | "number" | "date";
name: string;
value: string | number | undefined;
onInput$: (event: Event, element: HTMLInputElement) => void;
onChange$: (event: Event, element: HTMLInputElement) => void;
onBlur$: (event: Event, element: HTMLInputElement) => void;
placeholder?: string;
required?: boolean;
class?: string;
label?: string;
error?: string;
form?: string;
maxlength?: number;
};
/**
* Text input field that users can type into. Various decorations can be
* displayed in or around the field to communicate the entry requirements.
*/
export const TextInput = component$(
({ label, value, error, ...props }: TextInputProps) => {
const { name, required } = props;
const input = useSignal<string | number>();
useTask$(({ track }) => {
if (!Number.isNaN(track(() => value))) {
input.value = value;
}
});
return (
<div class={clsx("px-8 lg:px-10", props.class)}>
<InputLabel name={name} label={label} required={required} />
<input
{...props}
class={clsx(
"h-14 w-full rounded-2xl border-2 bg-white px-5 text-amber-800 outline-none placeholder:text-slate-500 dark:bg-gray-900 md:h-16 md:text-lg lg:h-[70px] lg:px-6 lg:text-xl",
error
? "border-red-600/50 dark:border-red-400/50"
: "border-slate-200 hover:border-slate-300 focus:border-sky-600/50 dark:border-slate-800 dark:hover:border-slate-700 dark:focus:border-sky-400/50",
)}
id={name}
value={input.value}
aria-invalid={!!error}
aria-errormessage={`${name}-error`}
/>
<InputError name={name} error={error} />
</div>
);
},
);
You can try to filter and remove letters in toPhoneNumber with value.replace(/[a-z]/gi, '') or value.replace(/\D/g, ''). But it seems like that's already in your code.
Hey Fabian,
I'm testing modular forms with Qwik. I'm using the component from the playground Git repository and have successfully created a custom toPhoneNumber function. It formats a 10-digit number correctly when I type it in. However, I've noticed that I can still type in a string, which is not the default behavior for phone number inputs on the web. Do I need to implement separate logic to handle this, or is there a way to accomplish this through the form API?
index.tsc
toPhoneNumber.tsx
TextInput.tsx