Update input components

This commit is contained in:
2026-02-14 16:24:04 -05:00
parent f40845723d
commit aaa15b1cfc
6 changed files with 161 additions and 32 deletions

View File

@@ -1,26 +1,47 @@
import type { DateInputProps } from "$/types/InputTypes"; import type { DateInputProps } from "$/types/InputTypes";
import clsx from "clsx"; import clsx from "clsx";
import moment from "moment"; import { type ChangeEvent } from "react";
export default function DateInput({ export default function DateInput({
id,
className, className,
defaultValue,
value, value,
onChange onChange,
}: DateInputProps){ ...inputProps
}: Readonly<DateInputProps>){
//Used to translate the string from the input to a date for onChange
const changeDate = (e: ChangeEvent<HTMLInputElement>) => {
const [ year, month, day ] = e.target.value.split("-").map(Number);
const newDate = new Date(year, month - 1, day);
onChange(Number.isNaN(newDate.getTime()) ? undefined : newDate);
};
return ( return (
<input <input
type="date" type="date"
id={id}
className={clsx( className={clsx(
"border rounded-lg px-2 py-1", "border rounded-lg px-2 py-1",
className className
)} )}
defaultValue={defaultValue ? moment(defaultValue).format("YYYY-MM-DD") : undefined} value={formatDate(value)}
value={value ? moment(value).format("YYYY-MM-DD") : undefined} onChange={changeDate}
onChange={(e) => onChange?.(new Date(moment(e.target.value, "YYYY-MM-DD").toDate()))} {...inputProps}
/> />
); );
} }
//Used to translate the date to a string for the input
function formatDate(date: Date | undefined): string{
if(!date || Number.isNaN(date.getTime())){
return "";
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}

View File

@@ -1,15 +1,38 @@
import type { DateInputProps } from "$/types/InputTypes"; import type { DateInputProps } from "$/types/InputTypes";
import clsx from "clsx"; import clsx from "clsx";
import moment from "moment"; import type { ChangeEvent } from "react";
export default function DateTimeInput({ export default function DateTimeInput({
id, id,
className, className,
defaultValue, step = 60,
value, value,
onChange onChange,
}: DateInputProps){ ...inputProps
}: Readonly<DateInputProps>){
const changeDateTime = (e: ChangeEvent<HTMLInputElement>) => {
const match = new RegExp(/^(\d+)-(\d+)-(\d+)T(\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?$/).exec(e.target.value);
if(!match){
onChange(undefined);
return;
}
const [ ,
y, mo, d,
h, mi, s = "0", ms = "0"
] = match;
const date = new Date(
Number(y), Number(mo) - 1, Number(d),
Number(h), Number(mi), Number(s), Number(ms)
);
onChange(Number.isNaN(date.getTime()) ? undefined : date);
}
return ( return (
<input <input
type="datetime-local" type="datetime-local"
@@ -18,9 +41,28 @@ export default function DateTimeInput({
"border rounded-lg px-2 py-1 outline-none", "border rounded-lg px-2 py-1 outline-none",
className className
)} )}
defaultValue={defaultValue ? moment(defaultValue).format("YYYY-MM-DDTHH:mm") : undefined} value={formatDateTime(value)}
value={value ? moment(value).format("YYYY-MM-DDTHH:mm") : undefined} onChange={changeDateTime}
onChange={(e) => onChange?.(new Date(moment(e.target.value, "YYYY-MM-DDTHH:mm").toDate()))} step={step}
{...inputProps}
/> />
); );
} }
function formatDateTime(date: Date | undefined){
if(!date || Number.isNaN(date.getTime())){
return "";
}
const y = date.getFullYear();
const mo = String(date.getMonth() + 1).padStart(2, "0");
const d = String(date.getDate()).padStart(2, "0");
const h = String(date.getHours()).padStart(2, "0");
const mi = String(date.getMinutes()).padStart(2, "0");
const s = String(date.getSeconds()).padStart(2, "0");
const ms = String(date.getMilliseconds()).padStart(2, "0");
return `${y}-${mo}-${d}T${h}:${mi}:${s}.${ms}`;
}

View File

@@ -1,15 +1,33 @@
import type { DateInputProps } from "$/types/InputTypes"; import type { DateInputProps } from "$/types/InputTypes";
import clsx from "clsx"; import clsx from "clsx";
import moment from "moment"; import type { ChangeEvent } from "react";
export default function TimeInput({ export default function TimeInput({
id, id,
className, className,
defaultValue, step = 60,
value, value,
onChange onChange,
}: DateInputProps){ ...inputProps
}: Readonly<DateInputProps>){
const changeTime = (e: ChangeEvent<HTMLInputElement>) => {
const match = new RegExp(/^(\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?$/).exec(e.target.value);
if(!match){
onChange(undefined);
return;
}
const [ , h, m, s = "0", ms = "0"] = match;
const newDate = new Date();
newDate.setHours(Number(h), Number(m), Number(s), Number(ms));
onChange(Number.isNaN(newDate) ? undefined : newDate);
}
return ( return (
<input <input
type="time" type="time"
@@ -18,9 +36,32 @@ export default function TimeInput({
"border rounded-lg px-2 py-1 outline-none", "border rounded-lg px-2 py-1 outline-none",
className className
)} )}
defaultValue={defaultValue ? moment(defaultValue).format("YYYY-MM-DDTHH:mm") : undefined} value={formatTime(value, step)}
value={value ? moment(value).format("YYYY-MM-DDTHH:mm") : undefined} onChange={changeTime}
onChange={(e) => onChange?.(new Date(moment(e.target.value, "YYYY-MM-DDTHH:mm").toDate()))} step={step}
{...inputProps}
/> />
); );
} }
function formatTime(date: Date | undefined, step: number){
if(!date || Number.isNaN(date.getTime())){
return "";
}
const h = String(date.getHours()).padStart(2, "0");
const m = String(date.getMinutes()).padStart(2, "0");
const s = String(date.getSeconds()).padStart(2, "0");
const ms = String(date.getMilliseconds()).padStart(3, "0");
let time = `${h}:${m}`;
if(step < 60){
time += `:${s}`;
}
if(step < 1){
time += `.${ms}`;
}
return time;
}

View File

@@ -1,5 +1,5 @@
import type React from "react"; import type React from "react";
import type { ChangeEventHandler } from "react"; import type { ChangeEventHandler, InputHTMLAttributes } from "react";
export interface TextInputProps { export interface TextInputProps {
@@ -164,10 +164,8 @@ export interface RadioListProps {
} }
export interface DateInputProps { export interface DateInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange" | "step">{
id?: string; step?: number;
className?: string; value: Date | undefined;
defaultValue?: Date; onChange: (newValue: Date | undefined) => void;
value?: Date;
onChange?: (newValue: Date) => void;
} }

View File

@@ -2,6 +2,7 @@ import { MattrixwvTabGroup } from "$/component/tab";
import type { TabGroupContent } from "$/types/TabTypes"; import type { TabGroupContent } from "$/types/TabTypes";
import { CheckboxContent, DateContent, FileContent, RadioContent, SwitchContent, TextContent } from "@/util/InputUtils"; import { CheckboxContent, DateContent, FileContent, RadioContent, SwitchContent, TextContent } from "@/util/InputUtils";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { useState } from "react";
export const Route = createFileRoute('/input/')({ export const Route = createFileRoute('/input/')({
@@ -10,15 +11,21 @@ export const Route = createFileRoute('/input/')({
function InputPage(){ function InputPage(){
const [ date, setDate ] = useState<Date>();
const [ dateTime, setDateTime ] = useState<Date>();
const [ time, setTime ] = useState<Date>();
const tabs: TabGroupContent[] = [ const tabs: TabGroupContent[] = [
{ tab: "Checkbox", content: <CheckboxContent/>}, { tab: "Checkbox", content: <CheckboxContent/>},
{ tab: "Radio", content: <RadioContent/> }, { tab: "Radio", content: <RadioContent/> },
{ tab: "Date", content: <DateContent/> }, { tab: "Date", content: <DateContent date={date} setDate={setDate} dateTime={dateTime} setDateTime={setDateTime} time={time} setTime={setTime}/> },
{ tab: "File", content: <FileContent/> }, { tab: "File", content: <FileContent/> },
{ tab: "Switch", content: <SwitchContent/> }, { tab: "Switch", content: <SwitchContent/> },
{ tab: "Text", content: <TextContent/> } { tab: "Text", content: <TextContent/> }
]; ];
return ( return (
<div <div
className="flex flex-col items-center justify-center" className="flex flex-col items-center justify-center"

View File

@@ -822,16 +822,36 @@ function RadioDisplay({
); );
} }
export function DateContent(){ export function DateContent({
date,
setDate,
dateTime,
setDateTime,
time,
setTime
}:Readonly<{
date: Date | undefined;
setDate: (newDate: Date | undefined) => void;
dateTime: Date | undefined;
setDateTime: (newDateTime: Date | undefined) => void;
time: Date | undefined;
setTime: (newTime: Date | undefined) => void;
}>){
return ( return (
<div <div
className="flex flex-col items-center justify-center my-8 gap-y-8" className="flex flex-col items-center justify-center my-8 gap-y-8"
> >
<DateInput <DateInput
onChange={setDate}
value={date}
/> />
<DateTimeInput <DateTimeInput
onChange={setDateTime}
value={dateTime}
/> />
<TimeInput <TimeInput
onChange={setTime}
value={time}
/> />
</div> </div>
); );