+
setIsOpen(!isOpen)}
+ onClick={isCustomInput ? undefined : toggleDropdown}
className={cn(
- "cursor-pointer rounded-md border border-transparent bg-background-200 p-2",
- "hover:border-grayscale-200 hover:bg-background-300",
+ "rounded-md border border-transparent bg-background-200 p-2",
+ !isCustomInput && "cursor-pointer hover:border-grayscale-200 hover:bg-background-300",
isOpen && "ring-1 ring-grayscale-300"
)}
>
@@ -62,23 +121,32 @@ const InputDropdown = forwardRef(
type="text"
ref={ref}
value={selectedValue}
- onChange={(e) => {
- if (isCustomInput) {
- setSelectedValue(e.target.value);
- setValue(name, e.target.value);
- }
- }}
+ onChange={handleInputChange}
+ onClick={handleInputClick}
+ readOnly={!isCustomInput}
className={cn(
- "text-grayscale-700 flex w-full items-center justify-between px-4 py-2 font-medium focus:outline-none",
- "cursor-pointer bg-transparent"
+ "flex w-full items-center justify-between px-4 py-2 font-medium focus:outline-none",
+ isCustomInput ? "cursor-text bg-transparent" : "cursor-pointer bg-transparent",
+ "text-grayscale-700"
)}
placeholder={isCustomInput ? "직접 입력하세요" : "선택"}
/>
-
- {isOpen &&
}
+ {isOpen &&
}
{errormessage &&
{errormessage}
}
);
diff --git a/src/app/components/button/dropdown/dropdownComponent/DropdownList.tsx b/src/app/components/button/dropdown/dropdownComponent/DropdownList.tsx
index 2ff46193..94b36876 100644
--- a/src/app/components/button/dropdown/dropdownComponent/DropdownList.tsx
+++ b/src/app/components/button/dropdown/dropdownComponent/DropdownList.tsx
@@ -8,12 +8,18 @@ const DropdownItem = ({
itemStyle,
}: {
item: string;
- onSelect: (item: string) => void;
+ onSelect: (item: string | null) => void;
itemStyle?: string;
}) => {
+ const handleClick = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ onSelect(item);
+ };
+
return (
onSelect(item)}
+ onClick={handleClick}
className={cn(
"flex w-full cursor-pointer bg-grayscale-50 px-[10px] py-2 text-sm font-normal leading-[18px] text-black-100 hover:bg-primary-orange-50 lg:text-lg lg:leading-[26px]",
itemStyle
@@ -23,6 +29,7 @@ const DropdownItem = ({
);
};
+
const DropdownList = ({
list,
onSelect,
@@ -30,7 +37,7 @@ const DropdownList = ({
itemStyle,
}: {
list: string[];
- onSelect: (item: string) => void;
+ onSelect: (item: string | null) => void;
wrapperStyle?: string;
itemStyle?: string;
}) => {
@@ -39,15 +46,20 @@ const DropdownList = ({
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
- onSelect("");
+ event.preventDefault();
+ onSelect(null);
}
};
- document.addEventListener("mousedown", handleClickOutside);
+ document.addEventListener("mousedown", handleClickOutside, { capture: true });
return () => {
- document.removeEventListener("mousedown", handleClickOutside);
+ document.removeEventListener("mousedown", handleClickOutside, { capture: true });
};
- }, []);
+ }, [onSelect]);
+
+ const handleContainerClick = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ };
return (
((props, ref) => {
const { value, onChange, errormessage } = props;
+ const dropdownRef = useRef
(null);
+ const { isOpen, handleOpenDropdown, setIsOpen } = useDropdownOpen();
- const handleTimeSelect = (time: string) => {
- if (onChange) {
- onChange({ target: { value: time } } as React.ChangeEvent);
- }
- handleOpenDropdown();
- };
- const { isOpen, handleOpenDropdown } = useDropdownOpen();
- const beforeIconStyle = "text-grayscale-400 size-5 lg:size-8";
- const afterIconStyle =
- "text-black-400 size-6 lg:size-9 transition-all transition-transform duration-200 ease-in-out";
- const width = "w-[150px] lg:w-[210px]";
+ // 외부 클릭 감지하여 드롭다운 닫기
+ const handleClickOutside = useCallback(
+ (event: MouseEvent) => {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
+ setIsOpen(false);
+ }
+ },
+ [setIsOpen]
+ );
+
+ // 외부 클릭 이벤트 리스너 등록
+ useEffect(() => {
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, [handleClickOutside]);
+
+ // 시간 선택 핸들러
+ const handleSelect = useCallback(
+ (time: string | null) => {
+ if (!time) {
+ setIsOpen(false);
+ return;
+ }
+
+ if (onChange) {
+ const event = {
+ target: { value: time, name: props.name },
+ } as React.ChangeEvent;
+ onChange(event);
+ }
+
+ setIsOpen(false);
+ },
+ [onChange, props.name, setIsOpen]
+ );
+ // 24시간 형식의 시간 옵션 생성 (00:00 ~ 23:00)
const timeOption = Array.from({ length: 24 }, (_, index) => {
const hour = index.toString().padStart(2, "0");
return `${hour}:00`;
});
+ // 클릭 이벤트 핸들러
+ const handleClick = useCallback(
+ (e: React.MouseEvent) => {
+ e.preventDefault();
+ handleOpenDropdown();
+ },
+ [handleOpenDropdown]
+ );
+
return (
-
-
{isOpen}
-
}
- afterIcon={
}
- errormessage={errormessage}
- />
+
+
+ }
+ afterIcon={
+
+ }
+ errormessage={errormessage}
+ />
+
{isOpen && (
)}
);
});
+
TimePickerInput.displayName = "TimePickerInput";
export default TimePickerInput;
diff --git a/src/app/components/layout/forms/SearchSection.tsx b/src/app/components/layout/forms/SearchSection.tsx
index 77ed9476..332bda1b 100644
--- a/src/app/components/layout/forms/SearchSection.tsx
+++ b/src/app/components/layout/forms/SearchSection.tsx
@@ -5,7 +5,7 @@ import { useState } from "react";
import SearchInput from "@/app/components/input/text/SearchInput";
import Button from "../../button/default/Button";
-export default function SearchSection() {
+export default function SearchSection({ pathname }: { pathname: string }) {
const router = useRouter();
const searchParams = useSearchParams();
const [keyword, setKeyword] = useState(searchParams.get("keyword") || "");
@@ -20,7 +20,7 @@ export default function SearchSection() {
params.delete("keyword");
}
- router.push(`/albalist?${params.toString()}`);
+ router.push(`${pathname}?${params.toString()}`);
};
return (
diff --git a/src/hooks/useDropdownOpen.ts b/src/hooks/useDropdownOpen.ts
index c6a08a0a..f0f9e291 100644
--- a/src/hooks/useDropdownOpen.ts
+++ b/src/hooks/useDropdownOpen.ts
@@ -1,13 +1,15 @@
"use client";
-import { useState } from "react";
+import { useState, useCallback } from "react";
+// 드롭다운 메뉴의 열림/닫힘 상태를 관리하는 커스텀 훅
export const useDropdownOpen = () => {
- const [isOpen, setIsOpen] = useState
(false);
+ const [isOpen, setIsOpen] = useState(false);
- const handleOpenDropdown = (): void => {
- setIsOpen(!isOpen);
- };
+ // 드롭다운 메뉴 토글 핸들러
+ const handleOpenDropdown = useCallback(() => {
+ setIsOpen((prev) => !prev);
+ }, []);
- return { isOpen, handleOpenDropdown };
+ return { isOpen, setIsOpen, handleOpenDropdown };
};