From fde9c449a60b24670aceb6488b3feef30b284ba7 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 12 Nov 2025 08:57:06 +0700 Subject: [PATCH] feat(FE-Storyless): update UI form UI repo --- src/components/Modal.tsx | 61 +++++++++++++++++++------------ src/components/input/TextArea.tsx | 61 ++++++++++++++----------------- 2 files changed, 66 insertions(+), 56 deletions(-) diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index a84c1827..958d88dd 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,6 +1,13 @@ 'use client'; -import { ReactNode, RefObject, useCallback, useRef, useState } from 'react'; +import { + ReactNode, + RefObject, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; import { cn } from '@/lib/helper'; export const useModal = () => { @@ -8,31 +15,35 @@ export const useModal = () => { const [open, setOpen] = useState(false); const openModal = useCallback(() => { + if (!ref.current) return; + ref.current.showModal(); setOpen(true); - - ref.current?.showModal(); }, []); const closeModal = useCallback(() => { + if (!ref.current) return; + ref.current.close(); setOpen(false); - ref.current?.close(); }, []); const toggle = useCallback(() => { - if (open) { - closeModal(); - } else { - openModal(); - } + open ? closeModal() : openModal(); }, [open, closeModal, openModal]); - if (ref.current) { - ref.current.addEventListener('close', () => { - closeModal(); - }); - } + // Gunakan useEffect agar event listener tidak didaftarkan berulang kali + useEffect(() => { + const dialog = ref.current; + if (!dialog) return; - return { ref, open, setOpen, openModal, closeModal, toggle } as const; + const handleClose = () => setOpen(false); + dialog.addEventListener('close', handleClose); + + return () => { + dialog.removeEventListener('close', handleClose); + }; + }, []); + + return { ref, open, openModal, closeModal, toggle } as const; }; interface ModalProps { @@ -46,15 +57,19 @@ interface ModalProps { } const Modal = ({ ref, children, closeOnBackdrop, className }: ModalProps) => { - return ( - -
{children}
+ const handleBackdropClick = (e: React.MouseEvent) => { + if (closeOnBackdrop && e.target === ref.current) { + ref.current?.close(); + } + }; - {closeOnBackdrop && ( -
- -
- )} + return ( + +
{children}
); }; diff --git a/src/components/input/TextArea.tsx b/src/components/input/TextArea.tsx index 0e7891f7..10d1a92a 100644 --- a/src/components/input/TextArea.tsx +++ b/src/components/input/TextArea.tsx @@ -8,6 +8,7 @@ export interface TextAreaProps { label?: string; bottomLabel?: string; name: string; + rows?: number; value?: string | number; placeholder?: string; className?: { @@ -23,11 +24,8 @@ export interface TextAreaProps { required?: boolean; isLoading?: boolean; errorMessage?: string; - startAdornment?: ReactNode; - endAdornment?: ReactNode; onChange?: ChangeEventHandler; onBlur?: FocusEventHandler; - rows?: number; } const TextArea = ({ @@ -35,20 +33,18 @@ const TextArea = ({ bottomLabel, name, value, + rows = 3, placeholder, className, isError, isValid, errorMessage, - startAdornment, - endAdornment, disabled = false, required = false, onChange, onBlur, readOnly = false, isLoading = false, - rows = 3, }: TextAreaProps) => { return (
)} - {startAdornment && startAdornment} -