diff --git a/package-lock.json b/package-lock.json index e9f84a0e..70df68cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@hookform/resolvers": "^3.9.0", "@tailwindcss/typography": "^0.5.15", "@tanstack/react-query": "^5.51.23", + "@tippyjs/react": "^4.2.6", "apexcharts": "^4.1.0", "axios": "^1.7.7", "date-fns": "^4.1.0", @@ -34,6 +35,7 @@ "react-icons": "^5.3.0", "react-router-dom": "^6.26.1", "swiper": "^11.1.14", + "tippy.js": "^6.3.7", "zod": "^3.23.8" }, "devDependencies": { @@ -1966,6 +1968,15 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@remix-run/router": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", @@ -2580,6 +2591,18 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tippyjs/react": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz", + "integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==", + "dependencies": { + "tippy.js": "^6.3.1" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@trivago/prettier-plugin-sort-imports": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", @@ -8805,6 +8828,14 @@ "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", "dev": true }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", diff --git a/package.json b/package.json index 40e578bb..3ff91c6c 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@hookform/resolvers": "^3.9.0", "@tailwindcss/typography": "^0.5.15", "@tanstack/react-query": "^5.51.23", + "@tippyjs/react": "^4.2.6", "apexcharts": "^4.1.0", "axios": "^1.7.7", "date-fns": "^4.1.0", @@ -47,6 +48,7 @@ "react-icons": "^5.3.0", "react-router-dom": "^6.26.1", "swiper": "^11.1.14", + "tippy.js": "^6.3.7", "zod": "^3.23.8" }, "devDependencies": { diff --git a/src/components/calendar/Calendar.tsx b/src/components/calendar/Calendar.tsx index 5b5b15e8..6d6a4232 100644 --- a/src/components/calendar/Calendar.tsx +++ b/src/components/calendar/Calendar.tsx @@ -10,6 +10,8 @@ import koLocale from '@fullcalendar/core/locales/ko'; import dayGridPlugin from '@fullcalendar/daygrid'; import FullCalendar from '@fullcalendar/react'; import rrulePlugin from '@fullcalendar/rrule'; +import tippy from 'tippy.js'; +import 'tippy.js/dist/tippy.css'; import SmallCalendarBottomEvent from '@/components/calendar/SmallCalendarBottomEvent'; import LoopLoading from '@/components/common/LoopLoading'; @@ -89,6 +91,12 @@ export default function Calendar({ dayCellContent={({ dayNumberText }) => `${dayNumberText.slice(0, -1)}` } + eventMouseEnter={({ el, event }) => { + tippy(el, { + content: event.title, + placement: 'top', + }); + }} eventClick={event => { event.jsEvent.preventDefault(); navigate(event.event.url); @@ -119,14 +127,16 @@ export default function Calendar({ ((sideViewEvents as FullCalendarEvent[]).length !== 0 ? ( ) : ( diff --git a/src/components/calendar/SmallCalendarBottomEvent.tsx b/src/components/calendar/SmallCalendarBottomEvent.tsx index 7275eb9a..5bf6fe6d 100644 --- a/src/components/calendar/SmallCalendarBottomEvent.tsx +++ b/src/components/calendar/SmallCalendarBottomEvent.tsx @@ -1,24 +1,33 @@ +import { Link } from 'react-router-dom'; + interface SmallCalendarBottomEventProps { title: string; date: string; time: string; + to: string; } export default function SmallCalendarBottomEvent({ date, title, time, + to, }: SmallCalendarBottomEventProps) { return ( -
  • +
    {date} {time}
    + | {title} -
  • + +
    + {title} +
    + ); } diff --git a/src/components/common/button/ChevronButton.tsx b/src/components/common/button/ChevronButton.tsx new file mode 100644 index 00000000..f10a2b6b --- /dev/null +++ b/src/components/common/button/ChevronButton.tsx @@ -0,0 +1,24 @@ +import Icon from '@/components/common/Icon'; + +interface ChevronButtonProps { + direction: 'ChevronRight' | 'ChevronLeft'; + handleClose: () => void; + className?: string; +} + +export default function ChevronButton({ + direction, + handleClose, + className, +}: ChevronButtonProps) { + return ( + + ); +} diff --git a/src/components/common/button/FavoriteButton.tsx b/src/components/common/button/FavoriteButton.tsx index b81c1b9c..18d4a3ba 100644 --- a/src/components/common/button/FavoriteButton.tsx +++ b/src/components/common/button/FavoriteButton.tsx @@ -18,7 +18,7 @@ export default function FavoriteButton({ onClick, disabled, }: FavoriteButtonProps) { - const iconClassName = 'cursor-pointer text-mainGreen'; + const iconClassName = 'cursor-pointer mt-0.5 text-mainGreen'; return ( - )} + <> +
    + {!hideButton && ( + + )} - {headerContent} -
    - )} + {headerContent} + - {mainContent} + {mainContent} + + )} ); } diff --git a/src/components/common/dropdown/MultiSelectDropdown.tsx b/src/components/common/dropdown/MultiSelectDropdown.tsx index 502c7c48..46d576fe 100644 --- a/src/components/common/dropdown/MultiSelectDropdown.tsx +++ b/src/components/common/dropdown/MultiSelectDropdown.tsx @@ -53,6 +53,7 @@ export default function MultiSelectDropdown({ }: MultiSelectDropdownProps) { const [open, setOpen] = useState(false); + const activeOptions = options.filter(({ isDisabled }) => !isDisabled); const selectedOptions = options.filter(({ id }) => value?.includes(id)); const checkIsSelected = useCallback( @@ -79,9 +80,10 @@ export default function MultiSelectDropdown({ const handleFullCheck = useCallback(() => { const updatedOptions = - selectedOptions.length !== options.length ? options : []; + selectedOptions.length !== activeOptions.length ? activeOptions : []; + return onChangeValue(updatedOptions); - }, [onChangeValue, options, selectedOptions.length]); + }, [activeOptions, onChangeValue, selectedOptions.length]); const onResetClick = useCallback(() => { onChangeValue([]); @@ -120,14 +122,14 @@ export default function MultiSelectDropdown({ {hasFullCheck && (