Many inputs added

This commit is contained in:
2025-07-20 23:33:21 -04:00
parent f84f0a0ebc
commit cb8c2c23be
13 changed files with 335 additions and 16 deletions

View File

@@ -1,10 +1,18 @@
import OptionInput from "./input/OptionInput";
import SelectInput from "./input/SelectInput";
import TextInput from "./input/TextInput";
import DragAndDropFileInput from "./input/file/DragAndDropFileInput";
import FileInput from "./input/file/FileInput";
import NumberInput from "./input/number/NumberInput";
import OptionInput from "./input/text/OptionInput";
import SelectInput from "./input/text/SelectInput";
import TextInput from "./input/text/TextInput";
export {
DragAndDropFileInput,
FileInput,
NumberInput,
//NumberSlider,
OptionInput,
SelectInput,
TextInput
};

View File

@@ -0,0 +1,20 @@
import { useRef } from "react";
export default function DragAndDropFileInput(){
const inputRef = useRef<HTMLInputElement>(null);
return (
<div
className="relative border-2 rounded-lg w-full h-full cursor-pointer"
>
<input
ref={inputRef}
type="file"
className="sr-only"
/>
Drag And Drop File Input
</div>
);
}

View File

@@ -0,0 +1,85 @@
import { SecondaryButton } from "$/component/button";
import type { FileInputProps } from "$/types/Input";
import { humanReadableBytes } from "$/util/FileUtil";
import clsx from "clsx";
import { useRef, useState } from "react";
export default function FileInput({
id,
className,
name,
minSize,
maxSize,
showFileName,
showSize,
onChange,
disabled,
children
}: FileInputProps){
const inputRef = useRef<HTMLInputElement>(null);
const [ file, setFile ] = useState<File>();
return (
<div
className="flex flex-row items-center justify-between w-full border-2 rounded-lg"
>
<input
ref={inputRef}
id={id}
type="file"
className={clsx(
"sr-only",
className
)}
name={name}
onChange={(e) => { setFile(e.target.files?.[0]); onChange?.(e.target.files?.[0]); }}
disabled={disabled}
/>
<div
className="flex flex-row items-center justify-between grow w-full px-2"
>
{
children && !showFileName &&
<div>{children}</div>
}
{
showFileName &&
<div>{file?.name}</div>
}
{
!children && !showFileName &&
<>&nbsp;</>
}
{
showSize &&
<div
className={clsx(
{
"text-red-600": minSize && file?.size && file?.size < minSize,
"text-red-600 ": maxSize && file?.size && file?.size > maxSize,
"text-green-600": minSize && !maxSize && file?.size && file?.size > minSize,
"text-green-600 ": !minSize && maxSize && file?.size && file?.size < maxSize,
" text-green-600": minSize && maxSize && file?.size && file?.size > minSize && file?.size < maxSize
}
)}
>
{humanReadableBytes(file?.size ?? 0)}
</div>
}
</div>
<div
className="border-l-2"
>
<SecondaryButton
className="text-nowrap rounded-r-lg"
rounding="none"
onClick={() => { inputRef.current?.click(); }}
>
Click Me
</SecondaryButton>
</div>
</div>
);
}

View File

@@ -0,0 +1,58 @@
import type { NumberInputProps } from "$/types/Input";
import clsx from "clsx";
export default function NumberInput({
id,
className,
inputClassName,
labelClassName,
name,
min,
max,
defaultValue,
value,
onChange,
disabled,
children
}: NumberInputProps){
return (
<div
className={clsx(
"flex flex-row items-center justify-center rounded-lg border-2 w-full",
className
)}
>
<div
className="relative flex flex-row items-center justify-center px-2 py-1 w-full"
>
<input
type="number"
id={id}
className={clsx(
"peer bg-transparent outline-none placeholder-transparent w-full",
"[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
inputClassName
)}
name={name}
min={min}
max={max}
defaultValue={defaultValue}
value={value}
onChange={(e) => onChange?.(e.target.valueAsNumber)}
disabled={disabled}
/>
<label
className={clsx(
"absolute ml-2 -top-3 left-0 text-sm rounded-md px-1 select-none cursor-default",
labelClassName
)}
style={{ transitionProperty: "top, left, font-size, line-height", transitionTimingFunction: "cubic-bezier(0.4 0, 0.2, 1)", transitionDuration: "250ms" }}
htmlFor={id}
>
{children}
</label>
</div>
</div>
);
}

View File

@@ -0,0 +1,36 @@
import type { NumberSliderProps } from "$/types/Input";
import clsx from "clsx";
export default function NumberSlider({
id,
className,
name,
min,
max,
step,
defaultValue,
value,
onChange,
disabled
}: NumberSliderProps){
return (
<input
type="range"
id={id}
className={clsx(
"w-full appearance-none [-moz-range-thumb:background:#04AA6D]",
"h-6 bg-blue-300 accent-blue-600",
className
)}
name={name}
min={min}
max={max}
step={step}
defaultValue={defaultValue}
value={value}
onChange={(e) => onChange?.(e.target.valueAsNumber)}
disabled={disabled}
/>
);
}

View File

@@ -7,6 +7,7 @@ export default function TextArea({
className,
inputClassName,
labelClassName,
name,
maxLength,
rows,
cols,
@@ -20,20 +21,21 @@ export default function TextArea({
return (
<div
className={clsx(
"flex flex-row items-center justify-center rounded-lg border-2",
"flex flex-row items-center justify-center rounded-lg border-2 w-full",
className
)}
>
<div
className="relative flex flex-row items-center justify-center px-2 py-1"
className="relative flex flex-row items-center justify-center px-2 py-1 w-full"
>
<textarea
id={id}
className={clsx(
"peer bg-transparent outline-none placeholder-transparent resize",
"peer bg-transparent outline-none placeholder-transparent w-full",
inputClassName
)}
placeholder={placeholder}
name={name}
maxLength={maxLength}
rows={rows}
cols={cols}
@@ -46,7 +48,7 @@ export default function TextArea({
<label
className={clsx(
"absolute ml-2 -top-3 left-0 text-sm rounded-md px-1 select-none cursor-default",
"peer-placeholder-shown:top-0 peer-placeholder-shown:-left-1 peer-placeholder-shown:text-inherit peer-placeholder-shown:text-base peer-placeholder-shown:bg-transparent peer-placeholder-shown:cursor-text",
"peer-placeholder-shown:top-0 peer-placeholder-shown:-left-1 peer-placeholder-shown:text-inherit peer-placeholder-shown:text-base peer-placeholder-shown:bg-transparent peer-placeholder-shown:cursor-text peer-placeholder-shown:w-[99%]",
"flex items-center",
labelClassName
)}

View File

@@ -7,6 +7,7 @@ export default function TextInput({
className,
inputClassName,
labelClassName,
name,
maxLength,
spellCheck,
placeholder,
@@ -18,20 +19,21 @@ export default function TextInput({
return (
<div
className={clsx(
"flex flex-row items-center justify-center rounded-lg border-2",
"flex flex-row items-center justify-center rounded-lg border-2 w-full",
className
)}
>
<div
className="relative flex flex-row items-center justify-center px-2 py-1"
className="relative flex flex-row items-center justify-center px-2 py-1 w-full"
>
<input
type="text"
id={id}
className={clsx(
"peer bg-transparent outline-none placeholder-transparent",
"peer bg-transparent outline-none placeholder-transparent w-full",
inputClassName
)}
name={name}
placeholder={placeholder}
maxLength={maxLength}
defaultValue={defaultValue}
@@ -43,7 +45,7 @@ export default function TextInput({
<label
className={clsx(
"absolute ml-2 -top-3 left-0 text-sm rounded-md px-1 select-none cursor-default",
"peer-placeholder-shown:top-0 peer-placeholder-shown:-left-1 peer-placeholder-shown:text-inherit peer-placeholder-shown:text-base peer-placeholder-shown:bg-transparent peer-placeholder-shown:h-full peer-placeholder-shown:cursor-text",
"peer-placeholder-shown:top-0 peer-placeholder-shown:-left-1 peer-placeholder-shown:text-inherit peer-placeholder-shown:text-base peer-placeholder-shown:bg-transparent peer-placeholder-shown:h-full peer-placeholder-shown:cursor-text peer-placeholder-shown:w-[99%]",
"flex items-center",
labelClassName
)}

44
lib/types/Input.d.ts vendored
View File

@@ -7,6 +7,7 @@ export interface TextInputProps {
className?: string;
inputClassName?: string;
labelClassName?: string;
name?: string;
maxLength?: number;
spellCheck?: boolean;
placeholder?: string;
@@ -67,3 +68,46 @@ export interface MattrixwvButtonSwitchProps {
onNode: React.ReactNode;
offNode: React.ReactNode;
}
export interface NumberInputProps {
id?: string;
className?: string;
inputClassName?: string;
labelClassName?: string;
name?: string;
min?: number;
max?: number;
defaultValue?: number;
value?: number;
onChange?: (newValue: number) => void;
disabled?: boolean;
children?: React.ReactNode;
}
export interface NumberSliderProps {
id?: string;
className?: string;
name?: string;
min?: number;
max?: number;
step?: number;
defaultValue?: number;
value?: number;
onChange?: (newValue: number) => void;
disabled?: boolean;
}
export interface FileInputProps {
id?: string;
className?: string;
name?: string;
minSize?: number;
maxSize?: number;
showFileName?: boolean;
showSize?: boolean;
defaultValue?: File;
value?: File;
onChange?: (newFile: File | undefined) => void;
disabled?: boolean;
children?: React.ReactNode;
}

9
lib/util/FileUtil.ts Normal file
View File

@@ -0,0 +1,9 @@
export function humanReadableBytes(bytes: number, decimals: number = 2): string{
if(bytes === 0){
return "0 Bytes";
}
const sizes = [ "Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
const power = Math.floor(Math.log(bytes) / Math.log(1024));
return `${parseFloat((bytes / Math.pow(1024, power)).toFixed(decimals))} ${sizes[power]}`;
}