Initial commit for new panel
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { type HTMLInputTypeAttribute, useId } from "react";
|
||||
import {
|
||||
type FieldErrors,
|
||||
type FieldValues,
|
||||
type Path,
|
||||
type UseFormRegister,
|
||||
type UseFormRegisterReturn,
|
||||
} from "react-hook-form";
|
||||
|
||||
type InputGroupProps<T extends FieldValues> = {
|
||||
name: Path<T>;
|
||||
label: string;
|
||||
type: HTMLInputTypeAttribute;
|
||||
placeholder: string;
|
||||
className?: string;
|
||||
fileStyleVariant?: "style1" | "style2";
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
active?: boolean;
|
||||
handleChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
value?: string;
|
||||
icon?: React.ReactNode;
|
||||
iconPosition?: "left" | "right";
|
||||
height?: "sm" | "default";
|
||||
defaultValue?: string;
|
||||
register?: UseFormRegister<T>;
|
||||
errors?: FieldErrors<T>;
|
||||
} & Partial<UseFormRegisterReturn>;
|
||||
|
||||
const InputGroup = <T extends FieldValues>({
|
||||
className,
|
||||
label,
|
||||
type,
|
||||
placeholder,
|
||||
required,
|
||||
disabled,
|
||||
active,
|
||||
handleChange,
|
||||
icon,
|
||||
name,
|
||||
register,
|
||||
errors,
|
||||
|
||||
onChange,
|
||||
onBlur,
|
||||
ref,
|
||||
...props
|
||||
}: InputGroupProps<T>): React.ReactElement => {
|
||||
const id = useId();
|
||||
|
||||
const error = errors && errors[name];
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="text-body-sm font-medium text-dark dark:text-white"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="text-red ml-1 select-none">*</span>}
|
||||
</label>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"relative mt-3 [&_svg]:absolute [&_svg]:top-1/2 [&_svg]:-translate-y-1/2",
|
||||
props.iconPosition === "left"
|
||||
? "[&_svg]:left-4.5"
|
||||
: "[&_svg]:right-4.5",
|
||||
)}
|
||||
>
|
||||
<input
|
||||
id={id}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
name={name}
|
||||
ref={ref}
|
||||
onChange={(e) => {
|
||||
onChange?.(e);
|
||||
handleChange?.(e);
|
||||
}}
|
||||
onBlur={onBlur}
|
||||
{...(!onChange &&
|
||||
props.value !== undefined && { value: props.value })}
|
||||
{...(!onChange &&
|
||||
props.defaultValue !== undefined && {
|
||||
defaultValue: props.defaultValue,
|
||||
})}
|
||||
className={cn(
|
||||
"w-full rounded-lg border-[1.5px] border-stroke bg-transparent outline-none transition focus:border-primary disabled:cursor-default disabled:bg-gray-2 data-[active=true]:border-primary dark:border-dark-3 dark:bg-dark-2 dark:focus:border-primary dark:disabled:bg-dark dark:data-[active=true]:border-primary",
|
||||
// Add error styles conditionally
|
||||
error && "border-red focus:border-red dark:border-red",
|
||||
type === "file"
|
||||
? getFileStyles(props.fileStyleVariant!)
|
||||
: "px-5.5 py-3 text-dark placeholder:text-dark-6 dark:text-white",
|
||||
props.iconPosition === "left" && "pl-12.5",
|
||||
props.height === "sm" && "py-2.5",
|
||||
)}
|
||||
disabled={disabled}
|
||||
data-active={active}
|
||||
/>
|
||||
|
||||
{icon}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className="text-red mt-1.5 text-sm">{error.message as string}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputGroup;
|
||||
|
||||
function getFileStyles(variant: "style1" | "style2") {
|
||||
switch (variant) {
|
||||
case "style1":
|
||||
return `file:mr-5 file:border-collapse file:cursor-pointer file:border-0 file:border-r file:border-solid file:border-stroke file:bg-[#E2E8F0] file:px-6.5 file:py-[13px] file:text-body-sm file:font-medium file:text-dark-5 file:hover:bg-primary file:hover:bg-opacity-10 dark:file:border-dark-3 dark:file:bg-white/30 dark:file:text-white`;
|
||||
default:
|
||||
return `file:mr-4 file:rounded file:border-[0.5px] file:border-stroke file:bg-stroke file:px-2.5 file:py-1 file:text-body-xs file:font-medium file:text-dark-5 file:focus:border-primary dark:file:border-dark-3 dark:file:bg-white/30 dark:file:text-white px-3 py-[9px]`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useId } from "react";
|
||||
import {
|
||||
type FieldErrors,
|
||||
type FieldValues,
|
||||
type Path,
|
||||
type UseFormRegister,
|
||||
type UseFormRegisterReturn,
|
||||
} from "react-hook-form";
|
||||
|
||||
type PropsType<T extends FieldValues> = {
|
||||
name: Path<T>;
|
||||
label: string;
|
||||
placeholder: string;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
active?: boolean;
|
||||
className?: string;
|
||||
icon?: React.ReactNode;
|
||||
defaultValue?: string;
|
||||
handleChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
||||
register?: UseFormRegister<T>;
|
||||
errors?: FieldErrors<T>;
|
||||
} & Partial<UseFormRegisterReturn>;
|
||||
|
||||
export function TextAreaGroup<T extends FieldValues>({
|
||||
name,
|
||||
label,
|
||||
placeholder,
|
||||
required,
|
||||
disabled,
|
||||
active,
|
||||
className,
|
||||
icon,
|
||||
defaultValue,
|
||||
handleChange,
|
||||
register,
|
||||
errors,
|
||||
onChange,
|
||||
onBlur,
|
||||
ref,
|
||||
...props
|
||||
}: PropsType<T>) {
|
||||
const id = useId();
|
||||
|
||||
const error = errors && errors[name];
|
||||
|
||||
return (
|
||||
<div className={cn(className)}>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="mb-3 block text-body-sm font-medium text-dark dark:text-white"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="text-red ml-1 select-none">*</span>}
|
||||
</label>
|
||||
|
||||
<div className="relative mt-3 [&_svg]:pointer-events-none [&_svg]:absolute [&_svg]:left-5.5 [&_svg]:top-5.5">
|
||||
<textarea
|
||||
id={id}
|
||||
name={name}
|
||||
ref={ref}
|
||||
rows={6}
|
||||
placeholder={placeholder}
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e) => {
|
||||
onChange?.(e);
|
||||
handleChange?.(e);
|
||||
}}
|
||||
onBlur={onBlur}
|
||||
className={cn(
|
||||
"w-full rounded-lg border-[1.5px] border-stroke bg-transparent px-5.5 py-3 text-dark outline-none transition focus:border-primary disabled:cursor-default disabled:bg-gray-2 data-[active=true]:border-primary dark:border-dark-3 dark:bg-dark-2 dark:text-white dark:focus:border-primary dark:disabled:bg-dark dark:data-[active=true]:border-primary",
|
||||
error && "border-red focus:border-red dark:border-red",
|
||||
icon && "py-5 pl-13 pr-5",
|
||||
)}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
data-active={active}
|
||||
/>
|
||||
|
||||
{icon}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className="text-red mt-1.5 text-sm">{error.message as string}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user