diff --git a/.claude/settings.json b/.claude/settings.json index 1bcd18fa..e9a8e70b 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,5 +1,13 @@ { "enabledPlugins": { - "playwright@claude-plugins-official": true + "playwright@claude-plugins-official": true, + "context7@claude-plugins-official": true + }, + "mcpServers": { + "playwright": { + "command": "npx", + "args": ["-y", "@playwright/mcp@latest"] + }, + "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } diff --git a/.gitignore b/.gitignore index 13e7f8ae..828b65c0 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,6 @@ storybook-static AGENTS.md # docs -docs \ No newline at end of file +docs + +.playwright-mcp \ No newline at end of file diff --git a/src/components/Badge/Badge.tsx b/src/components/Badge/Badge.tsx index f0252873..2f7cc723 100644 --- a/src/components/Badge/Badge.tsx +++ b/src/components/Badge/Badge.tsx @@ -1,4 +1,4 @@ -import { forwardRef, type HTMLAttributes } from "react"; +import { type HTMLAttributes } from "react"; import { cva, type VariantProps } from "class-variance-authority"; @@ -47,33 +47,35 @@ export interface BadgeProps children: React.ReactNode; showIcon?: boolean; onIconClick?: () => void; + ref?: React.Ref; } -export const Badge = forwardRef( - ({ className, status, radius, showIcon = false, onIconClick, children, ...props }, ref) => { - const canRenderIconButton = showIcon && typeof onIconClick === "function"; +export function Badge({ + className, + status, + radius, + showIcon = false, + onIconClick, + children, + ref, + ...props +}: BadgeProps) { + const canRenderIconButton = showIcon && typeof onIconClick === "function"; - return ( - - {children} - {canRenderIconButton && ( - - )} - - ); - } -); - -Badge.displayName = "Badge"; + return ( + + {children} + {canRenderIconButton && ( + + )} + + ); +} export { BADGE_VARIANTS }; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 12f1d0c0..4e4af62e 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,4 +1,4 @@ -import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from "react"; +import { type ButtonHTMLAttributes, type ReactNode } from "react"; import { cva, type VariantProps } from "class-variance-authority"; @@ -112,46 +112,41 @@ export type ButtonProps = ButtonHTMLAttributes & ButtonVariantProps & { leftIcon?: ReactNode; rightIcon?: ReactNode; + ref?: React.Ref; }; -export const Button = forwardRef( - ( - { - className, - variant, - colorScheme, - size, - iconOnly, - leftIcon, - rightIcon, - children, - disabled, - ...props - }, - ref - ) => { - return ( - - ); - } -); - -Button.displayName = "Button"; +export function Button({ + className, + variant, + colorScheme, + size, + iconOnly, + leftIcon, + rightIcon, + children, + disabled, + ref, + ...props +}: ButtonProps) { + return ( + + ); +} export { buttonVariants }; diff --git a/src/components/ButtonGroup/ButtonGroup.tsx b/src/components/ButtonGroup/ButtonGroup.tsx index 1f478ca7..f54376d8 100644 --- a/src/components/ButtonGroup/ButtonGroup.tsx +++ b/src/components/ButtonGroup/ButtonGroup.tsx @@ -1,11 +1,13 @@ -import { forwardRef, type HTMLAttributes } from "react"; +import { type HTMLAttributes } from "react"; import { cn } from "@/lib/utils/utils"; -export type ButtonGroupProps = HTMLAttributes; +export type ButtonGroupProps = HTMLAttributes & { + ref?: React.Ref; +}; -export const ButtonGroup = forwardRef( - ({ className, children, ...props }, ref) => ( +export function ButtonGroup({ className, children, ref, ...props }: ButtonGroupProps) { + return (
( > {children}
- ) -); - -ButtonGroup.displayName = "ButtonGroup"; + ); +} diff --git a/src/components/CategoryFilterButton/CategoryFilterButton.tsx b/src/components/CategoryFilterButton/CategoryFilterButton.tsx index 61d70996..d1340510 100644 --- a/src/components/CategoryFilterButton/CategoryFilterButton.tsx +++ b/src/components/CategoryFilterButton/CategoryFilterButton.tsx @@ -1,4 +1,4 @@ -import { forwardRef, type ButtonHTMLAttributes } from "react"; +import { type ButtonHTMLAttributes } from "react"; import { cva, type VariantProps } from "class-variance-authority"; @@ -34,23 +34,26 @@ const CATEGORY_FILTER_BUTTON_VARIANTS = cva( export type CategoryFilterButtonProps = ButtonHTMLAttributes & Omit, "isSelected"> & { isSelected?: boolean; + ref?: React.Ref; }; -export const CategoryFilterButton = forwardRef( - ({ className, isSelected = false, children, ...props }, ref) => { - return ( - - ); - } -); - -CategoryFilterButton.displayName = "CategoryFilterButton"; +export function CategoryFilterButton({ + className, + isSelected = false, + children, + ref, + ...props +}: CategoryFilterButtonProps) { + return ( + + ); +} export { CATEGORY_FILTER_BUTTON_VARIANTS }; diff --git a/src/components/ChipGroup/ChipGroup.tsx b/src/components/ChipGroup/ChipGroup.tsx index 349775e1..ea2a0a6b 100644 --- a/src/components/ChipGroup/ChipGroup.tsx +++ b/src/components/ChipGroup/ChipGroup.tsx @@ -1,4 +1,4 @@ -import { forwardRef, type HTMLAttributes } from "react"; +import { type HTMLAttributes } from "react"; import { cva, type VariantProps } from "class-variance-authority"; @@ -9,18 +9,15 @@ const CHIPGROUP_VARIANTS = cva(["inline-flex", "flex-row", "flex-wrap", "items-c export interface ChipGroupProps extends HTMLAttributes, VariantProps { children: React.ReactNode; + ref?: React.Ref; } -export const ChipGroup = forwardRef( - ({ className, children, ...props }, ref) => { - return ( -
- {children} -
- ); - } -); - -ChipGroup.displayName = "ChipGroup"; +export function ChipGroup({ className, children, ref, ...props }: ChipGroupProps) { + return ( +
+ {children} +
+ ); +} export { CHIPGROUP_VARIANTS }; diff --git a/src/components/DatePicker/DatePicker.tsx b/src/components/DatePicker/DatePicker.tsx index f1f93d37..163991e4 100644 --- a/src/components/DatePicker/DatePicker.tsx +++ b/src/components/DatePicker/DatePicker.tsx @@ -1,7 +1,5 @@ "use client"; -import { forwardRef, useState, useCallback } from "react"; - import { CalendarIcon } from "@/components/Icon/CalendarIcon"; import { ChevronDownIcon } from "@/components/Icon/ChevronDownIcon"; import { cn } from "@/lib/utils/utils"; @@ -21,283 +19,277 @@ function isRangeProps(props: DatePickerProps): props is DatePickerRangeProps { return props.mode !== "single"; } -const DatePickerRange = forwardRef( - ({ value, defaultValue, onChange, disabled, className }, ref) => { - const { - displayYearMonth, - displayText, - calendarDays, - goToPrevMonth, - goToNextMonth, - handleDateClick, - } = useDatePicker({ value, defaultValue, onChange }); +function DatePickerRange({ + value, + defaultValue, + onChange, + disabled, + className, + ref, +}: DatePickerRangeProps) { + const { + displayYearMonth, + displayText, + calendarDays, + goToPrevMonth, + goToNextMonth, + handleDateClick, + } = useDatePicker({ value, defaultValue, onChange }); - return ( -
- {/* Header */} -
-
- {displayYearMonth} -
- - {displayText} -
+ return ( +
+ {/* Header */} +
+
+ {displayYearMonth} +
+ + {displayText}
- -
- - -
-
- - {/* Weekday Headers */} -
- {WEEKDAYS.map((day) => ( -
- {day} -
- ))}
- {/* Calendar Grid */} -
- {calendarDays.map((day, index) => ( - - ))} +
+ +
- ); - } -); -DatePickerRange.displayName = "DatePickerRange"; + {/* Weekday Headers */} +
+ {WEEKDAYS.map((day) => ( +
+ {day} +
+ ))} +
-const DatePickerSingle = forwardRef( - ({ value, defaultValue, onChange, disabled, className, showTimePicker }, ref) => { - const [hour, setHour] = useState(() => (value ? value.getHours() : new Date().getHours())); - const [minute, setMinute] = useState(() => { - const mins = value ? value.getMinutes() : new Date().getMinutes(); - return Math.floor(mins / 5) * 5; - }); + {/* Calendar Grid */} +
+ {calendarDays.map((day, index) => ( + + ))} +
+
+ ); +} - const { - displayYearMonth, - displayText, - calendarDays, - selectedDate, - goToPrevMonth, - goToNextMonth, - handleDateClick: baseHandleDateClick, - } = useDatePickerSingle({ value, defaultValue, onChange }); +function DatePickerSingle({ + value, + defaultValue, + onChange, + disabled, + className, + showTimePicker, + ref, +}: DatePickerSingleProps) { + const { + displayYearMonth, + displayText, + calendarDays, + selectedDate, + goToPrevMonth, + goToNextMonth, + handleDateClick: baseHandleDateClick, + } = useDatePickerSingle({ value, defaultValue, onChange }); - const handleDateClick = useCallback( - (date: Date) => { - if (showTimePicker) { - const newDate = new Date(date); - newDate.setHours(hour, minute, 0, 0); - onChange?.(newDate); - } else { - baseHandleDateClick(date); - } - }, - [showTimePicker, hour, minute, onChange, baseHandleDateClick] - ); + const timeSource = selectedDate ?? defaultValue; + const hour = timeSource ? timeSource.getHours() : new Date().getHours(); + const minute = timeSource + ? Math.floor(timeSource.getMinutes() / 5) * 5 + : Math.floor(new Date().getMinutes() / 5) * 5; - const handleHourChange = useCallback( - (newHour: number) => { - setHour(newHour); - if (selectedDate) { - const newDate = new Date(selectedDate); - newDate.setHours(newHour, minute, 0, 0); - onChange?.(newDate); - } - }, - [selectedDate, minute, onChange] - ); + const handleDateClick = (date: Date) => { + if (showTimePicker) { + const newDate = new Date(date); + newDate.setHours(hour, minute, 0, 0); + baseHandleDateClick(newDate); + } else { + baseHandleDateClick(date); + } + }; - const handleMinuteChange = useCallback( - (newMinute: number) => { - setMinute(newMinute); - if (selectedDate) { - const newDate = new Date(selectedDate); - newDate.setHours(hour, newMinute, 0, 0); - onChange?.(newDate); - } - }, - [selectedDate, hour, onChange] - ); + const handleHourChange = (newHour: number) => { + if (selectedDate) { + const newDate = new Date(selectedDate); + newDate.setHours(newHour, minute, 0, 0); + baseHandleDateClick(newDate); + } + }; - return ( -
-
- {/* Calendar Section */} -
- {/* Header */} -
-
- - {displayYearMonth} - -
- - {displayText} -
-
+ const handleMinuteChange = (newMinute: number) => { + if (selectedDate) { + const newDate = new Date(selectedDate); + newDate.setHours(hour, newMinute, 0, 0); + baseHandleDateClick(newDate); + } + }; -
- - + return ( +
+
+ {/* Calendar Section */} +
+ {/* Header */} +
+
+ {displayYearMonth} +
+ + {displayText}
- {/* Weekday Headers */} -
- {WEEKDAYS.map((day) => ( -
- {day} -
- ))} +
+ +
+
- {/* Calendar Grid */} -
- {calendarDays.map((day, index) => ( - - ))} -
+ {/* Weekday Headers */} +
+ {WEEKDAYS.map((day) => ( +
+ {day} +
+ ))}
- {/* Time Picker Section */} - {showTimePicker && ( -
- 시간 - -
- )} + {/* Calendar Grid */} +
+ {calendarDays.map((day, index) => ( + + ))} +
-
- ); - } -); -DatePickerSingle.displayName = "DatePickerSingle"; + {/* Time Picker Section */} + {showTimePicker && ( +
+ 시간 + +
+ )} +
+
+ ); +} -export const DatePicker = forwardRef((props, ref) => { +export function DatePicker(props: DatePickerProps) { if (isRangeProps(props)) { - return ; + return ; } - return ; -}); - -DatePicker.displayName = "DatePicker"; + return ; +} diff --git a/src/components/DatePicker/DatePicker.types.ts b/src/components/DatePicker/DatePicker.types.ts index bebe0d9e..2a4e42d5 100644 --- a/src/components/DatePicker/DatePicker.types.ts +++ b/src/components/DatePicker/DatePicker.types.ts @@ -8,6 +8,7 @@ export interface DatePickerBaseProps { disabled?: boolean; /** Custom class name */ className?: string; + ref?: React.Ref; } export interface DatePickerRangeProps extends DatePickerBaseProps { diff --git a/src/components/DatePicker/TimePickerPanel.tsx b/src/components/DatePicker/TimePickerPanel.tsx index 98d10842..d63f1a82 100644 --- a/src/components/DatePicker/TimePickerPanel.tsx +++ b/src/components/DatePicker/TimePickerPanel.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useRef, useEffect } from "react"; +import { useRef, useEffect } from "react"; import { cn } from "@/lib/utils/utils"; @@ -25,24 +25,21 @@ export function TimePickerPanel({ const hourListRef = useRef(null); const minuteListRef = useRef(null); - const scrollToSelected = useCallback( - (ref: React.RefObject, index: number) => { - if (ref.current) { - const itemHeight = 36; - ref.current.scrollTop = index * itemHeight; - } - }, - [] - ); + const scrollToSelected = (ref: React.RefObject, index: number) => { + if (ref.current) { + const itemHeight = 36; + ref.current.scrollTop = index * itemHeight; + } + }; useEffect(() => { scrollToSelected(hourListRef, hour); - }, [hour, scrollToSelected]); + }, [hour]); useEffect(() => { const minuteIndex = Math.floor(minute / 5); scrollToSelected(minuteListRef, minuteIndex); - }, [minute, scrollToSelected]); + }, [minute]); return (
diff --git a/src/components/DatePicker/useDatePicker.ts b/src/components/DatePicker/useDatePicker.ts index c38b76b1..5d9edde7 100644 --- a/src/components/DatePicker/useDatePicker.ts +++ b/src/components/DatePicker/useDatePicker.ts @@ -1,4 +1,4 @@ -import { useState, useMemo, useCallback } from "react"; +import { useState } from "react"; import { formatDateRangeDisplay, @@ -51,14 +51,12 @@ export function useDatePicker({ const selectedRange = value ?? internalRange; const displayYearMonth = formatYearMonth(currentMonth); - const displayText = useMemo(() => { - if (selectedRange.startDate && selectedRange.endDate) { - return formatDateRangeDisplay(selectedRange.startDate, selectedRange.endDate); - } - return formatDateWithDay(new Date()); - }, [selectedRange.startDate, selectedRange.endDate]); + const displayText = + selectedRange.startDate && selectedRange.endDate + ? formatDateRangeDisplay(selectedRange.startDate, selectedRange.endDate) + : formatDateWithDay(new Date()); - const calendarDays = useMemo((): CalendarDay[] => { + const calendarDays = ((): CalendarDay[] => { const year = currentMonth.getFullYear(); const month = currentMonth.getMonth(); @@ -139,44 +137,41 @@ export function useDatePicker({ } return days; - }, [currentMonth, selectedRange]); + })(); - const goToPrevMonth = useCallback(() => { + const goToPrevMonth = () => { setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1)); - }, []); + }; - const goToNextMonth = useCallback(() => { + const goToNextMonth = () => { setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1)); - }, []); - - const handleDateClick = useCallback( - (date: Date) => { - let newRange: DateRange; + }; - if (selectionPhase === "start") { - newRange = { startDate: date, endDate: null }; - setSelectionPhase("end"); - } else { - if (selectedRange.startDate) { - // 클릭한 날짜가 startDate 이후면 그대로, 이전이면 swap - if (date >= selectedRange.startDate) { - newRange = { startDate: selectedRange.startDate, endDate: date }; - } else { - newRange = { startDate: date, endDate: selectedRange.startDate }; - } + const handleDateClick = (date: Date) => { + let newRange: DateRange; + + if (selectionPhase === "start") { + newRange = { startDate: date, endDate: null }; + setSelectionPhase("end"); + } else { + if (selectedRange.startDate) { + // 클릭한 날짜가 startDate 이후면 그대로, 이전이면 swap + if (date >= selectedRange.startDate) { + newRange = { startDate: selectedRange.startDate, endDate: date }; } else { - newRange = { startDate: date, endDate: null }; + newRange = { startDate: date, endDate: selectedRange.startDate }; } - setSelectionPhase("start"); + } else { + newRange = { startDate: date, endDate: null }; } + setSelectionPhase("start"); + } - if (!value) { - setInternalRange(newRange); - } - onChange?.(newRange); - }, - [selectionPhase, selectedRange.startDate, value, onChange] - ); + if (!value) { + setInternalRange(newRange); + } + onChange?.(newRange); + }; return { currentMonth, @@ -201,14 +196,11 @@ export function useDatePickerSingle({ const selectedDate = value !== undefined ? value : internalDate; const displayYearMonth = formatYearMonth(currentMonth); - const displayText = useMemo(() => { - if (selectedDate) { - return formatDateWithDay(selectedDate); - } - return formatDateWithDay(new Date()); - }, [selectedDate]); + const displayText = selectedDate + ? formatDateWithDay(selectedDate) + : formatDateWithDay(new Date()); - const calendarDays = useMemo((): CalendarDay[] => { + const calendarDays = ((): CalendarDay[] => { const year = currentMonth.getFullYear(); const month = currentMonth.getMonth(); @@ -277,25 +269,22 @@ export function useDatePickerSingle({ } return days; - }, [currentMonth, selectedDate]); + })(); - const goToPrevMonth = useCallback(() => { + const goToPrevMonth = () => { setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1)); - }, []); + }; - const goToNextMonth = useCallback(() => { + const goToNextMonth = () => { setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1)); - }, []); + }; - const handleDateClick = useCallback( - (date: Date) => { - if (value === undefined) { - setInternalDate(date); - } - onChange?.(date); - }, - [value, onChange] - ); + const handleDateClick = (date: Date) => { + if (value === undefined) { + setInternalDate(date); + } + onChange?.(date); + }; return { currentMonth, diff --git a/src/components/Dropdown/useDropdown.ts b/src/components/Dropdown/useDropdown.ts index 47e01108..58225373 100644 --- a/src/components/Dropdown/useDropdown.ts +++ b/src/components/Dropdown/useDropdown.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import type { UseDropdownProps, UseDropdownReturn } from "./Dropdown.types"; @@ -19,7 +19,7 @@ export function useDropdown({ const isControlled = value !== undefined; const selectedValue = isControlled ? value : internalValue; - const toggle = useCallback(() => { + const toggle = () => { if (disabled) return; setIsOpen((prev) => { if (!prev) { @@ -28,19 +28,16 @@ export function useDropdown({ } return !prev; }); - }, [disabled]); + }; - const selectOption = useCallback( - (optionValue: string) => { - if (!isControlled) { - setInternalValue(optionValue); - } - onChange?.(optionValue); - setIsOpen(false); - setFocusedIndex(-1); - }, - [isControlled, onChange] - ); + const selectOption = (optionValue: string) => { + if (!isControlled) { + setInternalValue(optionValue); + } + onChange?.(optionValue); + setIsOpen(false); + setFocusedIndex(-1); + }; // Click outside & Escape key handling useEffect(() => { diff --git a/src/components/Filter/Filter.tsx b/src/components/Filter/Filter.tsx index 67bbba30..e0727446 100644 --- a/src/components/Filter/Filter.tsx +++ b/src/components/Filter/Filter.tsx @@ -1,4 +1,4 @@ -import { forwardRef, type ButtonHTMLAttributes } from "react"; +import { type ButtonHTMLAttributes } from "react"; import { cva, type VariantProps } from "class-variance-authority"; @@ -45,38 +45,42 @@ const FILTER_VARIANTS = cva( export interface FilterProps extends ButtonHTMLAttributes, VariantProps { isOpen?: boolean; + ref?: React.Ref; } -export const Filter = forwardRef( - ({ className, size, radius, bordered, isOpen = false, children, ...props }, ref) => { - const iconSize = size === "medium" ? "small" : "medium"; +export function Filter({ + className, + size, + radius, + bordered, + isOpen = false, + children, + ref, + ...props +}: FilterProps) { + const iconSize = size === "medium" ? "small" : "medium"; - return ( - - ); - } -); - -Filter.displayName = "Filter"; + return ( + + ); +} export { FILTER_VARIANTS }; diff --git a/src/components/Icon/AlertIcon.tsx b/src/components/Icon/AlertIcon.tsx index 01ba1575..fbd98ca4 100644 --- a/src/components/Icon/AlertIcon.tsx +++ b/src/components/Icon/AlertIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const AlertSvg = ( @@ -19,18 +17,19 @@ const AlertSvg = ( export type AlertIconProps = Omit; -export const AlertIcon = forwardRef( - ({ size = "xlarge", className, ...props }, ref) => { - return ( - - ); - } -); - -AlertIcon.displayName = "AlertIcon"; +export function AlertIcon({ + ref, + size = "xlarge", + className, + ...props +}: AlertIconProps & { ref?: React.Ref }) { + return ( + + ); +} diff --git a/src/components/Icon/ArrowLeftIcon.tsx b/src/components/Icon/ArrowLeftIcon.tsx index 6ae93111..a49c775c 100644 --- a/src/components/Icon/ArrowLeftIcon.tsx +++ b/src/components/Icon/ArrowLeftIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ARROW_LEFT_SVG = ( @@ -19,8 +17,9 @@ const ARROW_LEFT_SVG = ( export type ArrowLeftIconProps = Omit; -export const ArrowLeftIcon = forwardRef((props, ref) => { +export function ArrowLeftIcon({ + ref, + ...props +}: ArrowLeftIconProps & { ref?: React.Ref }) { return ; -}); - -ArrowLeftIcon.displayName = "ArrowLeftIcon"; +} diff --git a/src/components/Icon/ArrowRightIcon.tsx b/src/components/Icon/ArrowRightIcon.tsx index f40f1ddf..472e241f 100644 --- a/src/components/Icon/ArrowRightIcon.tsx +++ b/src/components/Icon/ArrowRightIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ARROW_RIGHT_SVG = ( @@ -19,8 +17,9 @@ const ARROW_RIGHT_SVG = ( export type ArrowRightIconProps = Omit; -export const ArrowRightIcon = forwardRef((props, ref) => { +export function ArrowRightIcon({ + ref, + ...props +}: ArrowRightIconProps & { ref?: React.Ref }) { return ; -}); - -ArrowRightIcon.displayName = "ArrowRightIcon"; +} diff --git a/src/components/Icon/ArrowRotateRightIcon.tsx b/src/components/Icon/ArrowRotateRightIcon.tsx index a4ca50a2..73dbf3ea 100644 --- a/src/components/Icon/ArrowRotateRightIcon.tsx +++ b/src/components/Icon/ArrowRotateRightIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ArrowRotateRightSvg = ( @@ -19,10 +17,9 @@ const ArrowRotateRightSvg = ( export type ArrowRotateRightIconProps = Omit; -export const ArrowRotateRightIcon = forwardRef( - (props, ref) => { - return ; - } -); - -ArrowRotateRightIcon.displayName = "ArrowRotateRightIcon"; +export function ArrowRotateRightIcon({ + ref, + ...props +}: ArrowRotateRightIconProps & { ref?: React.Ref }) { + return ; +} diff --git a/src/components/Icon/BarGraphIcon.tsx b/src/components/Icon/BarGraphIcon.tsx index 53d739d5..3bc863cf 100644 --- a/src/components/Icon/BarGraphIcon.tsx +++ b/src/components/Icon/BarGraphIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const BarGraphSvg = ( @@ -19,8 +17,9 @@ const BarGraphSvg = ( export type BarGraphIconProps = Omit; -export const BarGraphIcon = forwardRef((props, ref) => { +export function BarGraphIcon({ + ref, + ...props +}: BarGraphIconProps & { ref?: React.Ref }) { return ; -}); - -BarGraphIcon.displayName = "BarGraphIcon"; +} diff --git a/src/components/Icon/CalendarIcon.tsx b/src/components/Icon/CalendarIcon.tsx index 9c148df6..061e428e 100644 --- a/src/components/Icon/CalendarIcon.tsx +++ b/src/components/Icon/CalendarIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const CalendarSvg = ( @@ -43,8 +41,9 @@ const CalendarSvg = ( export type CalendarIconProps = Omit; -export const CalendarIcon = forwardRef((props, ref) => { +export function CalendarIcon({ + ref, + ...props +}: CalendarIconProps & { ref?: React.Ref }) { return ; -}); - -CalendarIcon.displayName = "CalendarIcon"; +} diff --git a/src/components/Icon/ChatIcon.tsx b/src/components/Icon/ChatIcon.tsx index c314ae37..65918fa3 100644 --- a/src/components/Icon/ChatIcon.tsx +++ b/src/components/Icon/ChatIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ChatSvg = ( @@ -19,8 +17,6 @@ const ChatSvg = ( export type ChatIconProps = Omit; -export const ChatIcon = forwardRef((props, ref) => { +export function ChatIcon({ ref, ...props }: ChatIconProps & { ref?: React.Ref }) { return ; -}); - -ChatIcon.displayName = "ChatIcon"; +} diff --git a/src/components/Icon/CheckIcon.tsx b/src/components/Icon/CheckIcon.tsx index 72f3b3dc..a20f415c 100644 --- a/src/components/Icon/CheckIcon.tsx +++ b/src/components/Icon/CheckIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const CheckSvg = ( @@ -19,8 +17,9 @@ const CheckSvg = ( export type CheckIconProps = Omit; -export const CheckIcon = forwardRef((props, ref) => { +export function CheckIcon({ + ref, + ...props +}: CheckIconProps & { ref?: React.Ref }) { return ; -}); - -CheckIcon.displayName = "CheckIcon"; +} diff --git a/src/components/Icon/ChevronDownIcon.tsx b/src/components/Icon/ChevronDownIcon.tsx index 86a2f17c..455c83d7 100644 --- a/src/components/Icon/ChevronDownIcon.tsx +++ b/src/components/Icon/ChevronDownIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ChevronDownSvg = ( @@ -19,8 +17,9 @@ const ChevronDownSvg = ( export type ChevronDownIconProps = Omit; -export const ChevronDownIcon = forwardRef((props, ref) => { +export function ChevronDownIcon({ + ref, + ...props +}: ChevronDownIconProps & { ref?: React.Ref }) { return ; -}); - -ChevronDownIcon.displayName = "ChevronDownIcon"; +} diff --git a/src/components/Icon/ChevronLeftIcon.tsx b/src/components/Icon/ChevronLeftIcon.tsx index ce2709a8..9e5800cb 100644 --- a/src/components/Icon/ChevronLeftIcon.tsx +++ b/src/components/Icon/ChevronLeftIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const CHEVRON_LEFT_SVG = ( @@ -16,8 +14,9 @@ const CHEVRON_LEFT_SVG = ( export type ChevronLeftIconProps = Omit; -export const ChevronLeftIcon = forwardRef((props, ref) => { +export function ChevronLeftIcon({ + ref, + ...props +}: ChevronLeftIconProps & { ref?: React.Ref }) { return ; -}); - -ChevronLeftIcon.displayName = "ChevronLeftIcon"; +} diff --git a/src/components/Icon/ChevronRightIcon.tsx b/src/components/Icon/ChevronRightIcon.tsx index b13d352d..c38d8764 100644 --- a/src/components/Icon/ChevronRightIcon.tsx +++ b/src/components/Icon/ChevronRightIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const CHEVRON_RIGHT_SVG = ( @@ -16,8 +14,9 @@ const CHEVRON_RIGHT_SVG = ( export type ChevronRightIconProps = Omit; -export const ChevronRightIcon = forwardRef((props, ref) => { +export function ChevronRightIcon({ + ref, + ...props +}: ChevronRightIconProps & { ref?: React.Ref }) { return ; -}); - -ChevronRightIcon.displayName = "ChevronRightIcon"; +} diff --git a/src/components/Icon/ClearIcon.tsx b/src/components/Icon/ClearIcon.tsx index 67ea6771..3b87e8ad 100644 --- a/src/components/Icon/ClearIcon.tsx +++ b/src/components/Icon/ClearIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ClearSvg = ( @@ -23,8 +21,9 @@ const ClearSvg = ( export type ClearIconProps = Omit; -export const ClearIcon = forwardRef((props, ref) => { +export function ClearIcon({ + ref, + ...props +}: ClearIconProps & { ref?: React.Ref }) { return ; -}); - -ClearIcon.displayName = "ClearIcon"; +} diff --git a/src/components/Icon/ClockIcon.tsx b/src/components/Icon/ClockIcon.tsx index 04d9bb64..345d52d0 100644 --- a/src/components/Icon/ClockIcon.tsx +++ b/src/components/Icon/ClockIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ClockSvg = ( @@ -29,8 +27,9 @@ const ClockSvg = ( export type ClockIconProps = Omit; -export const ClockIcon = forwardRef((props, ref) => { +export function ClockIcon({ + ref, + ...props +}: ClockIconProps & { ref?: React.Ref }) { return ; -}); - -ClockIcon.displayName = "ClockIcon"; +} diff --git a/src/components/Icon/CloseIcon.tsx b/src/components/Icon/CloseIcon.tsx index 8770ef03..32e74ee3 100644 --- a/src/components/Icon/CloseIcon.tsx +++ b/src/components/Icon/CloseIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const CloseSvg = ( @@ -19,18 +17,19 @@ const CloseSvg = ( export type CloseIconProps = Omit; -export const CloseIcon = forwardRef( - ({ size = "medium", className, ...props }, ref) => { - return ( - - ); - } -); - -CloseIcon.displayName = "CloseIcon"; +export function CloseIcon({ + ref, + size = "medium", + className, + ...props +}: CloseIconProps & { ref?: React.Ref }) { + return ( + + ); +} diff --git a/src/components/Icon/CloudUploadIcon.tsx b/src/components/Icon/CloudUploadIcon.tsx index 37d31fdb..f691a459 100644 --- a/src/components/Icon/CloudUploadIcon.tsx +++ b/src/components/Icon/CloudUploadIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const CloudUploadSvg = ( @@ -19,8 +17,9 @@ const CloudUploadSvg = ( export type CloudUploadIconProps = Omit; -export const CloudUploadIcon = forwardRef((props, ref) => { +export function CloudUploadIcon({ + ref, + ...props +}: CloudUploadIconProps & { ref?: React.Ref }) { return ; -}); - -CloudUploadIcon.displayName = "CloudUploadIcon"; +} diff --git a/src/components/Icon/DefaultProfileIcon.tsx b/src/components/Icon/DefaultProfileIcon.tsx index f7444090..692b9f98 100644 --- a/src/components/Icon/DefaultProfileIcon.tsx +++ b/src/components/Icon/DefaultProfileIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; export type DefaultProfileIconProps = Omit; @@ -26,10 +24,9 @@ const DEFAULT_PROFILE_SVG = ( ); -export const DefaultProfileIcon = forwardRef( - (props, ref) => { - return ; - } -); - -DefaultProfileIcon.displayName = "DefaultProfileIcon"; +export function DefaultProfileIcon({ + ref, + ...props +}: DefaultProfileIconProps & { ref?: React.Ref }) { + return ; +} diff --git a/src/components/Icon/EditContainedIcon.tsx b/src/components/Icon/EditContainedIcon.tsx index 7e32673a..22f7b34b 100644 --- a/src/components/Icon/EditContainedIcon.tsx +++ b/src/components/Icon/EditContainedIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const EditContainedSvg = ( @@ -19,10 +17,9 @@ const EditContainedSvg = ( export type EditContainedIconProps = Omit; -export const EditContainedIcon = forwardRef( - (props, ref) => { - return ; - } -); - -EditContainedIcon.displayName = "EditContainedIcon"; +export function EditContainedIcon({ + ref, + ...props +}: EditContainedIconProps & { ref?: React.Ref }) { + return ; +} diff --git a/src/components/Icon/EditIcon.tsx b/src/components/Icon/EditIcon.tsx index eb318870..1a6ec4a6 100644 --- a/src/components/Icon/EditIcon.tsx +++ b/src/components/Icon/EditIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const EditSvg = ( @@ -19,8 +17,6 @@ const EditSvg = ( export type EditIconProps = Omit; -export const EditIcon = forwardRef((props, ref) => { +export function EditIcon({ ref, ...props }: EditIconProps & { ref?: React.Ref }) { return ; -}); - -EditIcon.displayName = "EditIcon"; +} diff --git a/src/components/Icon/EditProfileIcon.tsx b/src/components/Icon/EditProfileIcon.tsx index 7a8b763b..d8dd3b9f 100644 --- a/src/components/Icon/EditProfileIcon.tsx +++ b/src/components/Icon/EditProfileIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; export type EditProfileIconProps = Omit; @@ -26,8 +24,9 @@ const EDIT_PROFILE_SVG = ( ); -export const EditProfileIcon = forwardRef((props, ref) => { +export function EditProfileIcon({ + ref, + ...props +}: EditProfileIconProps & { ref?: React.Ref }) { return ; -}); - -EditProfileIcon.displayName = "EditProfileIcon"; +} diff --git a/src/components/Icon/FileIcon.tsx b/src/components/Icon/FileIcon.tsx index a25bb6ec..2a6cfb1d 100644 --- a/src/components/Icon/FileIcon.tsx +++ b/src/components/Icon/FileIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const FileSvg = ( @@ -23,8 +21,6 @@ const FileSvg = ( export type FileIconProps = Omit; -export const FileIcon = forwardRef((props, ref) => { +export function FileIcon({ ref, ...props }: FileIconProps & { ref?: React.Ref }) { return ; -}); - -FileIcon.displayName = "FileIcon"; +} diff --git a/src/components/Icon/FrameIcon.tsx b/src/components/Icon/FrameIcon.tsx index 245c222b..f4363128 100644 --- a/src/components/Icon/FrameIcon.tsx +++ b/src/components/Icon/FrameIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; export type FrameIconProps = Omit; @@ -36,8 +34,9 @@ const FrameSvg = ( ); -export const FrameIcon = forwardRef((props, ref) => { +export function FrameIcon({ + ref, + ...props +}: FrameIconProps & { ref?: React.Ref }) { return ; -}); - -FrameIcon.displayName = "FrameIcon"; +} diff --git a/src/components/Icon/GoogleIcon.tsx b/src/components/Icon/GoogleIcon.tsx index 9a3f6c57..2b716b0c 100644 --- a/src/components/Icon/GoogleIcon.tsx +++ b/src/components/Icon/GoogleIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const GoogleSvg = ( @@ -40,8 +38,9 @@ const GoogleSvg = ( export type GoogleIconProps = Omit; -export const GoogleIcon = forwardRef((props, ref) => { +export function GoogleIcon({ + ref, + ...props +}: GoogleIconProps & { ref?: React.Ref }) { return ; -}); - -GoogleIcon.displayName = "GoogleIcon"; +} diff --git a/src/components/Icon/HeartFillIcon.tsx b/src/components/Icon/HeartFillIcon.tsx index 5ee9f647..12783992 100644 --- a/src/components/Icon/HeartFillIcon.tsx +++ b/src/components/Icon/HeartFillIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const HeartFillSvg = ( @@ -19,8 +17,9 @@ const HeartFillSvg = ( export type HeartFillIconProps = Omit; -export const HeartFillIcon = forwardRef((props, ref) => { +export function HeartFillIcon({ + ref, + ...props +}: HeartFillIconProps & { ref?: React.Ref }) { return ; -}); - -HeartFillIcon.displayName = "HeartFillIcon"; +} diff --git a/src/components/Icon/HeartIcon.tsx b/src/components/Icon/HeartIcon.tsx index 2abe9720..c33e6437 100644 --- a/src/components/Icon/HeartIcon.tsx +++ b/src/components/Icon/HeartIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const HeartSvg = ( @@ -19,8 +17,9 @@ const HeartSvg = ( export type HeartIconProps = Omit; -export const HeartIcon = forwardRef((props, ref) => { +export function HeartIcon({ + ref, + ...props +}: HeartIconProps & { ref?: React.Ref }) { return ; -}); - -HeartIcon.displayName = "HeartIcon"; +} diff --git a/src/components/Icon/Icon.tsx b/src/components/Icon/Icon.tsx index 3ba34014..2a984d24 100644 --- a/src/components/Icon/Icon.tsx +++ b/src/components/Icon/Icon.tsx @@ -1,4 +1,4 @@ -import { forwardRef, type ComponentPropsWithoutRef } from "react"; +import { type ComponentPropsWithoutRef } from "react"; import { cva, type VariantProps } from "class-variance-authority"; @@ -22,18 +22,15 @@ const iconVariants = cva("inline-block shrink-0", { export interface IconProps extends Omit, "children">, VariantProps { svg: React.ReactNode; + ref?: React.Ref; } -export const Icon = forwardRef( - ({ className, size, svg, ...props }, ref) => { - return ( - - {svg} - - ); - } -); - -Icon.displayName = "Icon"; +export function Icon({ className, size, svg, ref, ...props }: IconProps) { + return ( + + {svg} + + ); +} export { iconVariants }; diff --git a/src/components/Icon/InfoIcon.tsx b/src/components/Icon/InfoIcon.tsx index e4ebc427..500beca7 100644 --- a/src/components/Icon/InfoIcon.tsx +++ b/src/components/Icon/InfoIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const InfoSvg = ( @@ -22,8 +20,6 @@ const InfoSvg = ( export type InfoIconProps = Omit; -export const InfoIcon = forwardRef((props, ref) => { +export function InfoIcon({ ref, ...props }: InfoIconProps & { ref?: React.Ref }) { return ; -}); - -InfoIcon.displayName = "InfoIcon"; +} diff --git a/src/components/Icon/KakaoIcon.tsx b/src/components/Icon/KakaoIcon.tsx index 8621db39..f6d0bdcf 100644 --- a/src/components/Icon/KakaoIcon.tsx +++ b/src/components/Icon/KakaoIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const KakaoSvg = ( @@ -21,8 +19,9 @@ const KakaoSvg = ( export type KakaoIconProps = Omit; -export const KakaoIcon = forwardRef((props, ref) => { +export function KakaoIcon({ + ref, + ...props +}: KakaoIconProps & { ref?: React.Ref }) { return ; -}); - -KakaoIcon.displayName = "KakaoIcon"; +} diff --git a/src/components/Icon/LogoutIcon.tsx b/src/components/Icon/LogoutIcon.tsx index 510dabbe..bd286eaf 100644 --- a/src/components/Icon/LogoutIcon.tsx +++ b/src/components/Icon/LogoutIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; export type LogoutIconProps = Omit; @@ -19,8 +17,9 @@ const LogoutSvg = ( ); -export const LogoutIcon = forwardRef((props, ref) => { +export function LogoutIcon({ + ref, + ...props +}: LogoutIconProps & { ref?: React.Ref }) { return ; -}); - -LogoutIcon.displayName = "LogoutIcon"; +} diff --git a/src/components/Icon/MinusIcon.tsx b/src/components/Icon/MinusIcon.tsx index f5837c03..9edbb447 100644 --- a/src/components/Icon/MinusIcon.tsx +++ b/src/components/Icon/MinusIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const MinusSvg = ( @@ -19,8 +17,9 @@ const MinusSvg = ( export type MinusIconProps = Omit; -export const MinusIcon = forwardRef((props, ref) => { +export function MinusIcon({ + ref, + ...props +}: MinusIconProps & { ref?: React.Ref }) { return ; -}); - -MinusIcon.displayName = "MinusIcon"; +} diff --git a/src/components/Icon/NoteIcon.tsx b/src/components/Icon/NoteIcon.tsx index caf9ed08..bb7cfe79 100644 --- a/src/components/Icon/NoteIcon.tsx +++ b/src/components/Icon/NoteIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; export type NoteIconProps = Omit; @@ -55,8 +53,6 @@ const NoteSvg = ( ); -export const NoteIcon = forwardRef((props, ref) => { +export function NoteIcon({ ref, ...props }: NoteIconProps & { ref?: React.Ref }) { return ; -}); - -NoteIcon.displayName = "NoteIcon"; +} diff --git a/src/components/Icon/PencilIcon.tsx b/src/components/Icon/PencilIcon.tsx index f18e7752..c8e04efb 100644 --- a/src/components/Icon/PencilIcon.tsx +++ b/src/components/Icon/PencilIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const PencilSvg = ( @@ -19,8 +17,9 @@ const PencilSvg = ( export type PencilIconProps = Omit; -export const PencilIcon = forwardRef((props, ref) => { +export function PencilIcon({ + ref, + ...props +}: PencilIconProps & { ref?: React.Ref }) { return ; -}); - -PencilIcon.displayName = "PencilIcon"; +} diff --git a/src/components/Icon/PlusIcon.tsx b/src/components/Icon/PlusIcon.tsx index 28f564d0..beeb85e0 100644 --- a/src/components/Icon/PlusIcon.tsx +++ b/src/components/Icon/PlusIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const PlusSvg = ( @@ -19,8 +17,6 @@ const PlusSvg = ( export type PlusIconProps = Omit; -export const PlusIcon = forwardRef((props, ref) => { +export function PlusIcon({ ref, ...props }: PlusIconProps & { ref?: React.Ref }) { return ; -}); - -PlusIcon.displayName = "PlusIcon"; +} diff --git a/src/components/Icon/ProfileCircleIcon.tsx b/src/components/Icon/ProfileCircleIcon.tsx index 82fc004d..9f3615e2 100644 --- a/src/components/Icon/ProfileCircleIcon.tsx +++ b/src/components/Icon/ProfileCircleIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; export type ProfileCircleIconProps = Omit; @@ -23,10 +21,9 @@ const ProfileCircleSvg = ( ); -export const ProfileCircleIcon = forwardRef( - (props, ref) => { - return ; - } -); - -ProfileCircleIcon.displayName = "ProfileCircleIcon"; +export function ProfileCircleIcon({ + ref, + ...props +}: ProfileCircleIconProps & { ref?: React.Ref }) { + return ; +} diff --git a/src/components/Icon/ProfileIcon.tsx b/src/components/Icon/ProfileIcon.tsx index 06c3a395..881b8373 100644 --- a/src/components/Icon/ProfileIcon.tsx +++ b/src/components/Icon/ProfileIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; export type ProfileIconProps = Omit; @@ -26,8 +24,9 @@ const ProfileSvg = ( ); -export const ProfileIcon = forwardRef((props, ref) => { +export function ProfileIcon({ + ref, + ...props +}: ProfileIconProps & { ref?: React.Ref }) { return ; -}); - -ProfileIcon.displayName = "ProfileIcon"; +} diff --git a/src/components/Icon/SearchIcon.tsx b/src/components/Icon/SearchIcon.tsx index ef6e0c97..932e02f5 100644 --- a/src/components/Icon/SearchIcon.tsx +++ b/src/components/Icon/SearchIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const SearchSvg = ( @@ -28,8 +26,9 @@ const SearchSvg = ( export type SearchIconProps = Omit; -export const SearchIcon = forwardRef((props, ref) => { +export function SearchIcon({ + ref, + ...props +}: SearchIconProps & { ref?: React.Ref }) { return ; -}); - -SearchIcon.displayName = "SearchIcon"; +} diff --git a/src/components/Icon/ShareIcon.tsx b/src/components/Icon/ShareIcon.tsx index e75e44e2..9fa6f991 100644 --- a/src/components/Icon/ShareIcon.tsx +++ b/src/components/Icon/ShareIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ShareSvg = ( @@ -24,18 +22,19 @@ const ShareSvg = ( export type ShareIconProps = Omit; -export const ShareIcon = forwardRef( - ({ size = "medium", className, ...props }, ref) => { - return ( - - ); - } -); - -ShareIcon.displayName = "ShareIcon"; +export function ShareIcon({ + ref, + size = "medium", + className, + ...props +}: ShareIconProps & { ref?: React.Ref }) { + return ( + + ); +} diff --git a/src/components/Icon/StarIcon.tsx b/src/components/Icon/StarIcon.tsx index 17e036dd..051497ac 100644 --- a/src/components/Icon/StarIcon.tsx +++ b/src/components/Icon/StarIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const StarSvg = ( @@ -19,8 +17,6 @@ const StarSvg = ( export type StarIconProps = Omit; -export const StarIcon = forwardRef((props, ref) => { +export function StarIcon({ ref, ...props }: StarIconProps & { ref?: React.Ref }) { return ; -}); - -StarIcon.displayName = "StarIcon"; +} diff --git a/src/components/Icon/ThumbDownIcon.tsx b/src/components/Icon/ThumbDownIcon.tsx index 1af11483..863b242d 100644 --- a/src/components/Icon/ThumbDownIcon.tsx +++ b/src/components/Icon/ThumbDownIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ThumbDownSvg = ( @@ -19,8 +17,9 @@ const ThumbDownSvg = ( export type ThumbDownIconProps = Omit; -export const ThumbDownIcon = forwardRef((props, ref) => { +export function ThumbDownIcon({ + ref, + ...props +}: ThumbDownIconProps & { ref?: React.Ref }) { return ; -}); - -ThumbDownIcon.displayName = "ThumbDownIcon"; +} diff --git a/src/components/Icon/ThumbUpIcon.tsx b/src/components/Icon/ThumbUpIcon.tsx index ac953181..2bf5d98c 100644 --- a/src/components/Icon/ThumbUpIcon.tsx +++ b/src/components/Icon/ThumbUpIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ThumbUpSvg = ( @@ -19,8 +17,9 @@ const ThumbUpSvg = ( export type ThumbUpIconProps = Omit; -export const ThumbUpIcon = forwardRef((props, ref) => { +export function ThumbUpIcon({ + ref, + ...props +}: ThumbUpIconProps & { ref?: React.Ref }) { return ; -}); - -ThumbUpIcon.displayName = "ThumbUpIcon"; +} diff --git a/src/components/Icon/ThumbsDownIcon.tsx b/src/components/Icon/ThumbsDownIcon.tsx index ebe1d759..f184032b 100644 --- a/src/components/Icon/ThumbsDownIcon.tsx +++ b/src/components/Icon/ThumbsDownIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ThumbsDownSvg = ( @@ -19,8 +17,9 @@ const ThumbsDownSvg = ( export type ThumbsDownIconProps = Omit; -export const ThumbsDownIcon = forwardRef((props, ref) => { +export function ThumbsDownIcon({ + ref, + ...props +}: ThumbsDownIconProps & { ref?: React.Ref }) { return ; -}); - -ThumbsDownIcon.displayName = "ThumbsDownIcon"; +} diff --git a/src/components/Icon/ThumbsUpIcon.tsx b/src/components/Icon/ThumbsUpIcon.tsx index 29d4fa47..05c09af5 100644 --- a/src/components/Icon/ThumbsUpIcon.tsx +++ b/src/components/Icon/ThumbsUpIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const ThumbsUpSvg = ( @@ -19,8 +17,9 @@ const ThumbsUpSvg = ( export type ThumbsUpIconProps = Omit; -export const ThumbsUpIcon = forwardRef((props, ref) => { +export function ThumbsUpIcon({ + ref, + ...props +}: ThumbsUpIconProps & { ref?: React.Ref }) { return ; -}); - -ThumbsUpIcon.displayName = "ThumbsUpIcon"; +} diff --git a/src/components/Icon/UsersIcon.tsx b/src/components/Icon/UsersIcon.tsx index 4e2b3439..953d6690 100644 --- a/src/components/Icon/UsersIcon.tsx +++ b/src/components/Icon/UsersIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const UsersSvg = ( @@ -43,8 +41,9 @@ const UsersSvg = ( export type UsersIconProps = Omit; -export const UsersIcon = forwardRef((props, ref) => { +export function UsersIcon({ + ref, + ...props +}: UsersIconProps & { ref?: React.Ref }) { return ; -}); - -UsersIcon.displayName = "UsersIcon"; +} diff --git a/src/components/Icon/XIcon.tsx b/src/components/Icon/XIcon.tsx index bff82c34..64dace8e 100644 --- a/src/components/Icon/XIcon.tsx +++ b/src/components/Icon/XIcon.tsx @@ -1,5 +1,3 @@ -import { forwardRef } from "react"; - import { Icon, type IconProps } from "./Icon"; const XSvg = ( @@ -19,18 +17,19 @@ const XSvg = ( export type XIconProps = Omit; -export const XIcon = forwardRef( - ({ size = "xsmall", className, ...props }, ref) => { - return ( - - ); - } -); - -XIcon.displayName = "XIcon"; +export function XIcon({ + ref, + size = "xsmall", + className, + ...props +}: XIconProps & { ref?: React.Ref }) { + return ( + + ); +} diff --git a/src/components/ImageUploader/ImageUploader.tsx b/src/components/ImageUploader/ImageUploader.tsx index 2a5953d1..e71dbdeb 100644 --- a/src/components/ImageUploader/ImageUploader.tsx +++ b/src/components/ImageUploader/ImageUploader.tsx @@ -1,6 +1,6 @@ "use client"; -import { forwardRef, useRef, useCallback, useId, type InputHTMLAttributes } from "react"; +import { useRef, useId, type InputHTMLAttributes } from "react"; import { cva, type VariantProps } from "class-variance-authority"; @@ -80,167 +80,153 @@ export interface ImageUploaderProps containerClassName?: string; /** 업로드 취소 시 콜백 */ onCancel?: () => void; + ref?: React.Ref; } -export const ImageUploader = forwardRef( - ( - { - className, - containerClassName, - onFileSelect, - uploadProgress, - accept = "image/*", - maxFileSize = DEFAULT_MAX_FILE_SIZE, - onFileSizeError, - disabled = false, - hintText = "최대 5MB 파일만 허용 가능", - uploadingText = "업로드 중...", - onCancel, - id, - ...props - }, - ref - ) => { - const generatedId = useId(); - const inputId = id ?? generatedId; - const internalInputRef = useRef(null); - const containerRef = useRef(null); - - const isUploading = uploadProgress !== undefined && uploadProgress >= 0 && uploadProgress < 100; - - const handleFileSelection = useCallback( - (file: File | null) => { - if (!file) { - onFileSelect?.(null); - return; - } - - if (file.size > maxFileSize) { - onFileSizeError?.(file, maxFileSize); - return; - } - - onFileSelect?.(file); - }, - [maxFileSize, onFileSelect, onFileSizeError] - ); - - const { isDragging, dragFileName, mousePosition, dragHandlers } = useDragAndDrop({ - disabled: disabled || isUploading, - onFileDrop: handleFileSelection, - containerRef, - }); - - const state: UploadState = isUploading ? "uploading" : isDragging ? "dragging" : "default"; - - const handleClick = useCallback(() => { - if (disabled || isUploading) return; - internalInputRef.current?.click(); - }, [disabled, isUploading]); - - const handleFileChange = useCallback( - (e: React.ChangeEvent) => { - const file = e.target.files?.[0] || null; - handleFileSelection(file); - e.target.value = ""; - }, - [handleFileSelection] - ); - - const handleKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); - handleClick(); - } - }, - [handleClick] - ); - - return ( -
-
- {/* 숨겨진 파일 input */} - { - internalInputRef.current = node; - if (typeof ref === "function") { - ref(node); - } else if (ref) { - ref.current = node; - } +export function ImageUploader({ + className, + containerClassName, + onFileSelect, + uploadProgress, + accept = "image/*", + maxFileSize = DEFAULT_MAX_FILE_SIZE, + onFileSizeError, + disabled = false, + hintText = "최대 5MB 파일만 허용 가능", + uploadingText = "업로드 중...", + onCancel, + id, + ref, + ...props +}: ImageUploaderProps) { + const generatedId = useId(); + const inputId = id ?? generatedId; + const internalInputRef = useRef(null); + const containerRef = useRef(null); + + const isUploading = uploadProgress !== undefined && uploadProgress >= 0 && uploadProgress < 100; + + const handleFileSelection = (file: File | null) => { + if (!file) { + onFileSelect?.(null); + return; + } + + if (file.size > maxFileSize) { + onFileSizeError?.(file, maxFileSize); + return; + } + + onFileSelect?.(file); + }; + + const { isDragging, dragFileName, mousePosition, dragHandlers } = useDragAndDrop({ + disabled: disabled || isUploading, + onFileDrop: handleFileSelection, + containerRef, + }); + + const state: UploadState = isUploading ? "uploading" : isDragging ? "dragging" : "default"; + + const handleClick = () => { + if (disabled || isUploading) return; + internalInputRef.current?.click(); + }; + + const handleFileChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0] || null; + handleFileSelection(file); + e.target.value = ""; + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + handleClick(); + } + }; + + return ( +
+
+ {/* 숨겨진 파일 input */} + { + internalInputRef.current = node; + if (typeof ref === "function") { + ref(node); + } else if (ref) { + ref.current = node; + } + }} + type="file" + id={inputId} + accept={accept} + disabled={disabled || isUploading} + onChange={handleFileChange} + className="sr-only" + aria-describedby={`${inputId}-hint`} + {...props} + /> + + {/* 드래그 중 파일명 툴팁 - OS 프리뷰 위에 표시되도록 위쪽에 배치 */} + {isDragging && dragFileName && ( +
- - {/* 드래그 중 파일명 툴팁 - OS 프리뷰 위에 표시되도록 위쪽에 배치 */} - {isDragging && dragFileName && ( -
+ + {dragFileName} +
+ )} + + {/* 상태별 콘텐츠 렌더링 */} + {isUploading ? ( +
+ + + {uploadingText} + +
- )} - - {/* 상태별 콘텐츠 렌더링 */} - {isUploading ? ( -
- - - {uploadingText} - - -
- ) : ( - <> - - 커버 사진을 등록해주세요 - - {hintText} - - - )} -
+ 취소하기 + +
+ ) : ( + <> + + 커버 사진을 등록해주세요 + + {hintText} + + + )}
- ); - } -); - -ImageUploader.displayName = "ImageUploader"; +
+ ); +} export { imageUploaderVariants }; diff --git a/src/components/Input/TextInput.tsx b/src/components/Input/TextInput.tsx index 84cf32ed..7572cb9d 100644 --- a/src/components/Input/TextInput.tsx +++ b/src/components/Input/TextInput.tsx @@ -1,7 +1,5 @@ import { - forwardRef, useState, - useCallback, useId, type InputHTMLAttributes, type FocusEvent, @@ -80,158 +78,142 @@ export interface TextInputProps containerClassName?: string; fullWidth?: boolean; showCharacterCount?: boolean; + ref?: React.Ref; } -export const TextInput = forwardRef( - ( - { - className, - containerClassName, - fullWidth = false, - label, - error = false, - errorMessage, - helperText, - helperTextType = "default", - disabled = false, - value, - defaultValue, - size = "md", - onClear, - onFocus, - onBlur, - onChange, - showCharacterCount = false, - maxLength, - id, - ...props - }, - ref - ) => { - const generatedId = useId(); - const inputId = id ?? generatedId; - const errorMessageId = `${inputId}-error`; - - const [internalValue, setInternalValue] = useState(defaultValue ?? ""); - const [isFocused, setIsFocused] = useState(false); - - const isControlled = value !== undefined; - const currentValue = isControlled ? value : internalValue; - const characterCount = String(currentValue).length; - const hasValue = characterCount > 0; - const showCount = showCharacterCount && maxLength !== undefined; - - const getState = () => { - if (disabled) return "disabled"; - if (error) return "error"; - if (hasValue) return "filled"; - return "default"; - }; - - const handleFocus = useCallback( - (e: FocusEvent) => { - setIsFocused(true); - onFocus?.(e); - }, - [onFocus] - ); - - const handleBlur = useCallback( - (e: FocusEvent) => { - setIsFocused(false); - onBlur?.(e); - }, - [onBlur] - ); - - const handleChange = useCallback( - (e: ChangeEvent) => { - if (!isControlled) { - setInternalValue(e.target.value); - } - onChange?.(e); - }, - [isControlled, onChange] - ); - - const handleClear = useCallback(() => { - if (!isControlled) { - setInternalValue(""); - } - onClear?.(); - }, [isControlled, onClear]); - - const showClearButton = hasValue && isFocused && !disabled && !error; - - return ( -
- {label && ( - +export function TextInput({ + className, + containerClassName, + fullWidth = false, + label, + error = false, + errorMessage, + helperText, + helperTextType = "default", + disabled = false, + value, + defaultValue, + size = "md", + onClear, + onFocus, + onBlur, + onChange, + showCharacterCount = false, + maxLength, + id, + ref, + ...props +}: TextInputProps) { + const generatedId = useId(); + const inputId = id ?? generatedId; + const errorMessageId = `${inputId}-error`; + + const [internalValue, setInternalValue] = useState(defaultValue ?? ""); + const [isFocused, setIsFocused] = useState(false); + + const isControlled = value !== undefined; + const currentValue = isControlled ? value : internalValue; + const characterCount = String(currentValue).length; + const hasValue = characterCount > 0; + const showCount = showCharacterCount && maxLength !== undefined; + + const getState = () => { + if (disabled) return "disabled"; + if (error) return "error"; + if (hasValue) return "filled"; + return "default"; + }; + + const handleFocus = (e: FocusEvent) => { + setIsFocused(true); + onFocus?.(e); + }; + + const handleBlur = (e: FocusEvent) => { + setIsFocused(false); + onBlur?.(e); + }; + + const handleChange = (e: ChangeEvent) => { + if (!isControlled) { + setInternalValue(e.target.value); + } + onChange?.(e); + }; + + const handleClear = () => { + if (!isControlled) { + setInternalValue(""); + } + onClear?.(); + }; + + const showClearButton = hasValue && isFocused && !disabled && !error; + + return ( +
+ {label && ( + + )} + +
+ , "size">)} + /> + + {showClearButton && ( + )} +
-
- + , "size">)} - /> - - {showClearButton && ( - - )} + aria-live="polite" + > + {characterCount}/{maxLength} +
- - {showCount && ( -
- - {characterCount}/{maxLength} - -
- )} - - {/* Priority: errorMessage > helperText */} - {error && errorMessage ? ( - - ) : helperText ? ( - - ) : null} -
- ); - } -); - -TextInput.displayName = "TextInput"; + )} + + {/* Priority: errorMessage > helperText */} + {error && errorMessage ? ( + + ) : helperText ? ( + + ) : null} +
+ ); +} export { textInputVariants }; diff --git a/src/components/Input/Textarea.tsx b/src/components/Input/Textarea.tsx index 15207c6b..3a09a8cd 100644 --- a/src/components/Input/Textarea.tsx +++ b/src/components/Input/Textarea.tsx @@ -1,7 +1,5 @@ import { - forwardRef, useState, - useCallback, useId, type TextareaHTMLAttributes, type FocusEvent, @@ -81,123 +79,109 @@ export interface TextareaProps showCharacterCount?: boolean; containerClassName?: string; size?: keyof typeof SIZE_STYLES; + ref?: React.Ref; } -export const Textarea = forwardRef( - ( - { - className, - containerClassName, - label, - error = false, - errorMessage, - helperText, - helperTextType = "default", - disabled = false, - value, - defaultValue, - size = "medium", - showCharacterCount = false, - maxLength, - onFocus, - onBlur, - onChange, - id, - ...props - }, - ref - ) => { - const generatedId = useId(); - const textareaId = id ?? generatedId; - const errorMessageId = `${textareaId}-error`; - - const [internalValue, setInternalValue] = useState(defaultValue ?? ""); - - const isControlled = value !== undefined; - const currentValue = isControlled ? value : internalValue; - const characterCount = String(currentValue).length; - const hasValue = characterCount > 0; - - const getState = () => { - if (disabled) return "disabled"; - if (error) return "error"; - if (hasValue) return "filled"; - return "default"; - }; - - const handleFocus = useCallback( - (e: FocusEvent) => { - onFocus?.(e); - }, - [onFocus] - ); - - const handleBlur = useCallback( - (e: FocusEvent) => { - onBlur?.(e); - }, - [onBlur] - ); - - const handleChange = useCallback( - (e: ChangeEvent) => { - if (!isControlled) { - setInternalValue(e.target.value); - } - onChange?.(e); - }, - [isControlled, onChange] - ); - - const showCount = showCharacterCount && maxLength !== undefined; - - return ( -
- {label && ( - - )} - -