feat: Implement automatic validation for modal forms
- Introduced a new hook `useDialogAutoValidation` to handle automatic validation of required fields in modals. - Added visual feedback for empty required fields, including red borders and error messages. - Disabled action buttons when required fields are not filled, enhancing user experience. - Updated `DialogContent` to integrate the new validation logic, ensuring that only user mode modals are validated. Made-with: Cursor
This commit is contained in:
@@ -44,7 +44,14 @@ function Button({
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
|
||||
return <Comp data-slot="button" className={cn(buttonVariants({ variant, size, className }))} {...props} />;
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
data-variant={variant || "default"}
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Button, buttonVariants };
|
||||
|
||||
@@ -8,6 +8,7 @@ import { cn } from "@/lib/utils";
|
||||
import { useModalPortal } from "@/lib/modalPortalRef";
|
||||
import { useTabId } from "@/contexts/TabIdContext";
|
||||
import { useTabStore } from "@/stores/tabStore";
|
||||
import { useDialogAutoValidation } from "@/lib/hooks/useDialogAutoValidation";
|
||||
|
||||
// Dialog: 탭 시스템 내에서 자동으로 modal={false} + 비활성 탭이면 open={false} 처리
|
||||
const Dialog: React.FC<React.ComponentProps<typeof DialogPrimitive.Root>> = ({
|
||||
@@ -82,6 +83,18 @@ const DialogContent = React.forwardRef<
|
||||
const container = explicitContainer !== undefined ? explicitContainer : autoContainer;
|
||||
const scoped = !!container;
|
||||
|
||||
// 모달 자동 검증용 내부 ref
|
||||
const internalRef = React.useRef<HTMLDivElement>(null);
|
||||
const mergedRef = React.useCallback(
|
||||
(node: HTMLDivElement | null) => {
|
||||
internalRef.current = node;
|
||||
if (typeof ref === "function") ref(node);
|
||||
else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
||||
},
|
||||
[ref],
|
||||
);
|
||||
useDialogAutoValidation(internalRef);
|
||||
|
||||
const handleInteractOutside = React.useCallback(
|
||||
(e: any) => {
|
||||
if (scoped && container) {
|
||||
@@ -125,7 +138,7 @@ const DialogContent = React.forwardRef<
|
||||
<DialogPrimitive.Overlay className="fixed inset-0 z-999 bg-black/60" />
|
||||
)}
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
ref={mergedRef}
|
||||
onInteractOutside={handleInteractOutside}
|
||||
onFocusOutside={handleFocusOutside}
|
||||
className={cn(
|
||||
@@ -156,7 +169,7 @@ const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivEleme
|
||||
DialogHeader.displayName = "DialogHeader";
|
||||
|
||||
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 shrink-0", className)} {...props} />
|
||||
<div data-slot="dialog-footer" className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 shrink-0", className)} {...props} />
|
||||
);
|
||||
DialogFooter.displayName = "DialogFooter";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user