From 422c9cbfe2fe505bf16120afc8e53cd40689a94c Mon Sep 17 00:00:00 2001 From: Ardeman Date: Sun, 9 Mar 2025 15:14:57 +0800 Subject: [PATCH] feat: add InputFile component for file uploads and integrate into FormContentsPage --- app/components/ui/input-file.tsx | 80 +++++++++++++++++++++++++++++++ app/components/ui/input.tsx | 2 +- app/pages/form-contents/index.tsx | 3 +- 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 app/components/ui/input-file.tsx diff --git a/app/components/ui/input-file.tsx b/app/components/ui/input-file.tsx new file mode 100644 index 0000000..c6bb7d8 --- /dev/null +++ b/app/components/ui/input-file.tsx @@ -0,0 +1,80 @@ +import { Field, Label, Input as HeadlessInput } from '@headlessui/react' +import { CloudArrowUpIcon } from '@heroicons/react/20/solid' +import { type ComponentProps, type ReactNode } from 'react' +import { + get, + type FieldError, + type FieldValues, + type Path, + type RegisterOptions, +} from 'react-hook-form' +import { useRemixFormContext } from 'remix-hook-form' +import { twMerge } from 'tailwind-merge' + +import { Button } from './button' + +type TInputProperties = Omit< + ComponentProps<'input'>, + 'size' +> & { + id: string + label?: ReactNode + name: Path + rules?: RegisterOptions + containerClassName?: string + labelClassName?: string +} + +export const InputFile = >( + properties: TInputProperties, +) => { + const { + id, + label, + name, + rules, + placeholder, + disabled, + className, + containerClassName, + labelClassName, + ...restProperties + } = properties + + const { + register, + formState: { errors }, + } = useRemixFormContext() + + const error: FieldError = get(errors, name) + + return ( + + + + + + ) +} diff --git a/app/components/ui/input.tsx b/app/components/ui/input.tsx index f4d73f1..ee6fb6a 100644 --- a/app/components/ui/input.tsx +++ b/app/components/ui/input.tsx @@ -75,7 +75,7 @@ export const Input = >( type="button" variant="icon" size="fit" - className="absolute right-3 h-[42px] text-gray-500" + className="absolute right-3 h-[42px]" onClick={() => setInputType(inputType === 'password' ? 'text' : 'password') } diff --git a/app/pages/form-contents/index.tsx b/app/pages/form-contents/index.tsx index 1953bcf..1e12a67 100644 --- a/app/pages/form-contents/index.tsx +++ b/app/pages/form-contents/index.tsx @@ -10,6 +10,7 @@ import { TextEditor } from '~/components/text-editor' import { Button } from '~/components/ui/button' import { Combobox } from '~/components/ui/combobox' import { Input } from '~/components/ui/input' +import { InputFile } from '~/components/ui/input-file' import { Switch } from '~/components/ui/switch' import { TitleDashboard } from '~/components/ui/title-dashboard' import type { loader } from '~/routes/_admin.lg-admin._dashboard' @@ -133,7 +134,7 @@ export const FormContentsPage = (properties: TProperties) => { containerClassName="flex-1" disabled={!!newsData} /> -