diff --git a/next.config.ts b/next.config.ts index e40cb1e2..0ba97d79 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,7 @@ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { + // output: 'standalone', /* config options here */ // 외부 이미지 도메인 허용 설정 추가 images: { diff --git a/package.json b/package.json index 83f1931e..d0ae9f0f 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@radix-ui/react-slot": "^1.2.2", "@radix-ui/react-switch": "^1.2.4", "@radix-ui/react-toggle": "^1.1.9", + "@radix-ui/react-tooltip": "^1.2.8", "@tailwindcss/postcss": "^4.0.6", "axios": "^1.9.0", "class-variance-authority": "^0.7.1", diff --git a/src/entities/user/model/use-user-profile-query.ts b/src/entities/user/model/use-user-profile-query.ts index 374fbcf9..d31a5f19 100644 --- a/src/entities/user/model/use-user-profile-query.ts +++ b/src/entities/user/model/use-user-profile-query.ts @@ -1,13 +1,10 @@ import { sendGTMEvent } from '@next/third-parties/google'; -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { getUserProfile, patchAutoMatching, } from '@/entities/user/api/get-user-profile'; -import type { - GetUserProfileResponse, - PatchAutoMatchingParams, -} from '@/entities/user/api/types'; +import type { GetUserProfileResponse } from '@/entities/user/api/types'; import { hashValue } from '@/shared/lib/hash'; export const useUserProfileQuery = (memberId: number) => { @@ -19,23 +16,62 @@ export const useUserProfileQuery = (memberId: number) => { }); }; +export interface PatchAutoMatchingParams { + memberId: number; + autoMatching: boolean; +} + export const usePatchAutoMatchingMutation = () => { - return useMutation({ + const qc = useQueryClient(); + + return useMutation< + void, + unknown, + PatchAutoMatchingParams, + { prev?: unknown } + >({ mutationFn: patchAutoMatching, - onSuccess: (_, variables) => { - if (variables.autoMatching) { - sendGTMEvent({ - event: 'custom_member_study_toggle_on', - dl_timestamp: new Date().toISOString(), - dl_member_id: hashValue(String(variables.memberId)), - }); - } else { - sendGTMEvent({ - event: 'custom_member_study_toggle_off', - dl_timestamp: new Date().toISOString(), - dl_member_id: hashValue(String(variables.memberId)), + + onMutate: async ({ memberId, autoMatching }) => { + await qc.cancelQueries({ queryKey: ['userProfile', memberId] }); + const prev = qc.getQueryData(['userProfile', memberId]); + if (prev && typeof prev === 'object') { + qc.setQueryData(['userProfile', memberId], { + ...(prev as any), + autoMatching, }); } + + return { prev }; + }, + + onError: (_err, { memberId }, ctx) => { + if (ctx?.prev) { + qc.setQueryData(['userProfile', memberId], ctx.prev); + } + }, + + onSuccess: async (_data, { memberId, autoMatching }) => { + await qc.invalidateQueries({ queryKey: ['userProfile', memberId] }); + + await qc.invalidateQueries({ + predicate: (q) => + Array.isArray(q.queryKey) && q.queryKey[0] === 'weeklyParticipation', + }); + + await qc.invalidateQueries({ + predicate: (q) => + Array.isArray(q.queryKey) && + q.queryKey[0] === 'weeklyReservationMembers', + }); + + sendGTMEvent({ + event: autoMatching + ? 'custom_member_study_toggle_on' + : 'custom_member_study_toggle_off', + dl_timestamp: new Date().toISOString(), + dl_member_id: hashValue(String(memberId)), + }); }, }); }; diff --git a/src/entities/user/ui/user-profile-modal.tsx b/src/entities/user/ui/user-profile-modal.tsx index a9ab7436..041aaa84 100644 --- a/src/entities/user/ui/user-profile-modal.tsx +++ b/src/entities/user/ui/user-profile-modal.tsx @@ -1,6 +1,7 @@ 'use client'; import { XIcon } from 'lucide-react'; +import { useState } from 'react'; import { useUserProfileQuery } from '@/entities/user/model/use-user-profile-query'; import KeywordReview from '@/entities/user/ui/keyword-review'; import ProfileInfoCard from '@/entities/user/ui/profile-info-card'; @@ -23,167 +24,212 @@ export default function UserProfileModal({ memberId, trigger, }: UserProfileModalProps) { + const [open, setOpen] = useState(false); + + return ( + + {trigger} + + {open && ( + + + + setOpen(false)} + /> + + + )} + + ); +} + +function UserProfileBody({ + memberId, + onClose, +}: { + memberId: number; + onClose: () => void; +}) { const { data: profile, isLoading, isError } = useUserProfileQuery(memberId); const { data: positiveKeywordsData } = useUserPositiveKeywordsQuery({ memberId, }); - if (isLoading || isError || !profile || !positiveKeywordsData) return null; - - const positiveKeywords = positiveKeywordsData?.keywords || []; + if (isLoading) { + return ( + <> +
+ 불러오는 중… + + ); + } + + if (isError || !profile || !positiveKeywordsData) { + return ( + <> +
+ 프로필을 불러오지 못했습니다. + + ); + } + + const positiveKeywords = positiveKeywordsData.keywords ?? []; const temperPreset = getSincerityPresetByLevelName( profile.sincerityTemp.levelName, ); return ( - - {trigger} - - - - - - {profile.memberProfile.memberName}님의 프로필 - - - - - - - -
- +
+ + +
+ + +
+
+ {profile.memberProfile.mbti && ( + {profile.memberProfile.mbti} + )} + {profile.memberProfile.interests.slice(0, 4).map((interest) => ( + + {interest.name} + + ))} +
+ +
+
+ {profile.memberProfile.memberName} +
+ +
+ +
+ + t.techStackName) + .join(', ')} + /> + t.label) + .join(', ')} + /> + + +
+ +
+ +
+ + 받은 평가 + + +
+ {/* todo: 기획 fix되면 수정 */} + {/* n명의 유저들이 이런 점이 좋다고 했어요. */} +
    + {positiveKeywords.length > 0 ? ( + positiveKeywords.map((keyword) => ( + + )) + ) : ( + + 아직 받은 평가가 없습니다. + + )} +
+
+
+ + + ); +} -
- -
- - 받은 평가 - - -
- {/* todo: 기획 fix되면 수정 */} - {/* n명의 유저들이 이런 점이 좋다고 했어요. */} - -
    - {positiveKeywords.length > 0 ? ( - positiveKeywords.map((keyword) => ( - - )) - ) : ( - - 아직 받은 평가가 없습니다. - - )} -
-
-
- - - - +function Header({ title, onClose }: { title: string; onClose: () => void }) { + return ( + + + {title} + + + + + + ); +} + +function Field({ icon, value }: { icon: React.ReactNode; value?: string }) { + return ( +
+ {icon} + + {value ?? ''} + +
); } diff --git a/src/features/my-page/ui/profile.tsx b/src/features/my-page/ui/profile.tsx index f1db1e9e..518e3571 100644 --- a/src/features/my-page/ui/profile.tsx +++ b/src/features/my-page/ui/profile.tsx @@ -10,6 +10,7 @@ import { cn } from '@/shared/shadcn/lib/utils'; import UserAvatar from '@/shared/ui/avatar'; import Badge from '@/shared/ui/badge'; import Progress from '@/shared/ui/progress'; +import Tooltip from '@/shared/ui/tooltip'; interface ProfileProps { memberId: number; @@ -80,12 +81,21 @@ export default function Profile({
- 성실 온도 - 성실온도 설명 + 성실 온도 + + + } />
{ queryKey: ['weeklyParticipation', params], queryFn: () => getWeeklyParticipation(params), staleTime: 60 * 1000, + enabled: !!params, }); }; diff --git a/src/features/study/participation/ui/reservation-list.tsx b/src/features/study/participation/ui/reservation-list.tsx index 284e9206..912afdef 100644 --- a/src/features/study/participation/ui/reservation-list.tsx +++ b/src/features/study/participation/ui/reservation-list.tsx @@ -13,20 +13,19 @@ import StartStudyModal from '../../ui/start-study-modal'; import { useInfiniteReservation } from '../model/use-participation-query'; interface ReservationListProps { - isParticipation?: boolean; + studyDate?: string; pageSize?: number; month: number; week: number; } export default function ReservationList({ - isParticipation = false, + studyDate, pageSize = 50, month, week, }: ReservationListProps) { const sentinelRef = useRef(null); - const calledRef = useRef(false); const [memberId, setMemberId] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); @@ -35,37 +34,20 @@ export default function ReservationList({ setMemberId(id ? Number(id) : null); }, []); + const { data: userProfile } = useUserProfileQuery(memberId ?? 0); + const autoMatching = userProfile?.autoMatching ?? false; + const firstMemberId = useMemo( - () => (isParticipation && memberId !== null ? memberId : null), - [isParticipation, memberId], + () => (autoMatching && memberId !== null ? memberId : null), + [autoMatching, memberId], ); const { data, isLoading, isFetchingNextPage, fetchNextPage, hasNextPage } = - useInfiniteReservation(firstMemberId, pageSize); - - const { data: userProfile } = useUserProfileQuery(memberId ?? 0); + useInfiniteReservation(firstMemberId ?? undefined, pageSize); const { mutate: patchAutoMatching, isPending } = usePatchAutoMatchingMutation(); - useEffect(() => { - if (!memberId || isParticipation || !userProfile) return; - - const { studyApplied, autoMatching } = userProfile; - - if (studyApplied && !autoMatching && !calledRef.current) { - calledRef.current = true; - patchAutoMatching( - { memberId, autoMatching: true }, - { - onError: () => { - calledRef.current = false; - }, - }, - ); - } - }, [memberId, isParticipation, userProfile, patchAutoMatching]); - useEffect(() => { if (!hasNextPage) return; @@ -124,10 +106,14 @@ export default function ReservationList({
{items.map((p) => ( - + ))} - {!isParticipation && ( + {!autoMatching && (
{participant.name}
- {isCurrentUser && 본인} + {isCurrentUser && ( + + 본인 + + )}
{participant.simpleIntroduction} diff --git a/src/features/study/ui/data-selector.tsx b/src/features/study/ui/data-selector.tsx index e1c94d2c..0f75625e 100644 --- a/src/features/study/ui/data-selector.tsx +++ b/src/features/study/ui/data-selector.tsx @@ -1,7 +1,8 @@ 'use client'; -import { startOfWeek, addDays, format, isSameDay } from 'date-fns'; +import { addDays, format, isSameDay } from 'date-fns'; import { useMemo } from 'react'; +import { getKoreaDisplayMonday } from '@/shared/lib/time'; interface Props { value: Date; @@ -9,12 +10,12 @@ interface Props { } export default function DateSelector({ value, onChange }: Props) { - const today = new Date(); - const monday = startOfWeek(today, { weekStartsOn: 1 }); const dayLabels = ['월', '화', '수', '목', '금']; + + const displayMonday = useMemo(() => getKoreaDisplayMonday(), []); const dates = useMemo( - () => Array.from({ length: 5 }, (_, i) => addDays(monday, i)), - [monday], + () => Array.from({ length: 5 }, (_, i) => addDays(displayMonday, i)), + [displayMonday], ); return ( diff --git a/src/features/study/ui/study-card.tsx b/src/features/study/ui/study-card.tsx index 0edb1e8d..453ccd4c 100644 --- a/src/features/study/ui/study-card.tsx +++ b/src/features/study/ui/study-card.tsx @@ -1,7 +1,12 @@ 'use client'; import { getMonth, getDay, startOfWeek, getDate } from 'date-fns'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; +import { + formatKoreaYMD, + getKoreaDate, + getKoreaDisplayMonday, +} from '@/shared/lib/time'; import DateSelector from './data-selector'; import TodayStudyCard from './today-study-card'; import StudyListSection from '../../../widgets/home/study-list-table'; @@ -12,13 +17,13 @@ import ReservationList from '../participation/ui/reservation-list'; // 스터디 주차 구하는 함수 function getWeekly(date: Date): { month: number; week: number } { const weekStartsOn = 0; - const target = new Date(date); - const currentWeekStart = startOfWeek(target, { weekStartsOn }); + const targetKST = getKoreaDate(date); + const currentWeekStart = startOfWeek(targetKST, { weekStartsOn }); const baseMonth = getMonth(currentWeekStart); // 목요일 기준 월의 첫 주 시작일 계산 - const firstOfMonth = new Date(target.getFullYear(), baseMonth, 1); + const firstOfMonth = new Date(targetKST.getFullYear(), baseMonth, 1); const firstWeekStart = startOfWeek(firstOfMonth, { weekStartsOn }); const firstDayOfWeek = getDay(firstOfMonth); const officialFirstWeekStart = @@ -56,17 +61,18 @@ function getWeekly(date: Date): { month: number; week: number } { export default function StudyCard() { const [selectedDate, setSelectedDate] = useState(new Date()); - const offset = selectedDate.getTimezoneOffset() * 60000; // ms단위라 60000곱해줌 - const dateOffset = new Date(selectedDate.getTime() - offset); - - const studyDate = dateOffset.toISOString().split('T')[0]; + const studyDate = formatKoreaYMD(selectedDate); const { data: status } = useStudyStatusQuery(); const { data: participationData } = useWeeklyParticipation(studyDate); const isParticipate = participationData?.isParticipate ?? false; - const { month, week } = getWeekly(selectedDate); + const displayMonday = useMemo( + () => getKoreaDisplayMonday(selectedDate), + [selectedDate], + ); + const { month, week } = getWeekly(displayMonday); return ( <> @@ -74,7 +80,7 @@ export default function StudyCard() { )} diff --git a/src/features/study/ui/study-review-modal.tsx b/src/features/study/ui/study-review-modal.tsx index e39d325b..4bfd7ace 100644 --- a/src/features/study/ui/study-review-modal.tsx +++ b/src/features/study/ui/study-review-modal.tsx @@ -61,7 +61,7 @@ export default function StudyReviewModal({ function StudyReviewForm({ onClose }: { onClose: () => void }) { const { data } = usePartnerStudyReviewQuery(); - const { mutate: addStudyReview } = useAddStudyReviewMutation(); + const { mutate: addStudyReview, isPending } = useAddStudyReviewMutation(); const [form, setForm] = useState({ studySpaceId: data?.studySpaceId, @@ -102,12 +102,12 @@ function StudyReviewForm({ onClose }: { onClose: () => void }) {
{ setForm({ ...form, - satisfactionId: 10, + satisfactionId: 30, keywordIds: [], content: '', }); @@ -130,12 +130,12 @@ function StudyReviewForm({ onClose }: { onClose: () => void }) { { setForm({ ...form, - satisfactionId: 30, + satisfactionId: 10, keywordIds: [], content: '', }); @@ -144,11 +144,11 @@ function StudyReviewForm({ onClose }: { onClose: () => void }) {
- {form.satisfactionId === 10 && ( + {form.satisfactionId === 30 && ( )} - {(form.satisfactionId === 20 || form.satisfactionId === 30) && ( + {(form.satisfactionId === 10 || form.satisfactionId === 20) && ( )} @@ -162,7 +162,9 @@ function StudyReviewForm({ onClose }: { onClose: () => void }) { color="primary" size="large" disabled={ - form.keywordIds.length === 0 || form.satisfactionId === null + form.keywordIds.length === 0 || + form.satisfactionId === null || + isPending } onClick={handleSubmit} > diff --git a/src/shared/lib/time.ts b/src/shared/lib/time.ts index 662bba84..9ef0d565 100644 --- a/src/shared/lib/time.ts +++ b/src/shared/lib/time.ts @@ -1,10 +1,13 @@ import { + addDays, differenceInDays, differenceInHours, differenceInMinutes, + format, + getDay, parseISO, + startOfWeek, } from 'date-fns'; -import { format } from 'path'; export const getKoreaDate = (targetDate?: Date) => { const date = targetDate || new Date(); @@ -17,6 +20,9 @@ export const getKoreaDate = (targetDate?: Date) => { return koreaNow; }; +export const formatKoreaYMD = (targetDate?: Date) => + format(getKoreaDate(targetDate), 'yyyy-MM-dd'); + export const formatKoreaRelativeTime = (targetDateStr: string): string => { const targetDate = parseISO(targetDateStr); const koreaTarget = getKoreaDate(targetDate); // 한국 시간 변환 @@ -35,3 +41,11 @@ export const formatKoreaRelativeTime = (targetDateStr: string): string => { return targetDateStr; }; + +export const getKoreaDisplayMonday = (base?: Date) => { + const todayKST = getKoreaDate(base); + const monday = startOfWeek(todayKST, { weekStartsOn: 1 }); // 월요일 시작 + const dow = getDay(todayKST); // 0=일, 6=토 + + return dow === 0 || dow === 6 ? addDays(monday, 7) : monday; +}; diff --git a/src/shared/ui/dropdown/multi.tsx b/src/shared/ui/dropdown/multi.tsx index fc1fc0ca..63d47850 100644 --- a/src/shared/ui/dropdown/multi.tsx +++ b/src/shared/ui/dropdown/multi.tsx @@ -42,10 +42,10 @@ export default function MultiDropdown({ [value], ); - const selectedLabels = useMemo(() => { + const selectedItems = useMemo(() => { const set = new Set(selected); - return options.filter((o) => set.has(o.value)).map((o) => o.label); + return options.filter((o) => set.has(o.value)); }, [options, selected]); const remainingOptions = useMemo(() => { @@ -99,20 +99,20 @@ export default function MultiDropdown({ )} - {selectedLabels.map((label, idx) => ( + {selectedItems.map((item) => ( - {label} + {item.label} diff --git a/src/shared/ui/tooltip/index.tsx b/src/shared/ui/tooltip/index.tsx new file mode 100644 index 00000000..63a933c5 --- /dev/null +++ b/src/shared/ui/tooltip/index.tsx @@ -0,0 +1,58 @@ +import * as RadixTooltip from '@radix-ui/react-tooltip'; +import * as React from 'react'; +import { cn } from '@/shared/shadcn/lib/utils'; + +interface TooltipProps { + trigger: React.ReactElement; + value: React.ReactNode; + side?: 'top' | 'right' | 'bottom' | 'left'; + align?: 'start' | 'center' | 'end'; + sideOffset?: number; + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (open: boolean) => void; + contentClassName?: string; + arrowClassName?: string; +} + +const Tooltip: React.FC = ({ + trigger, + value, + side = 'top', + align = 'center', + sideOffset = 5, + open, + defaultOpen, + onOpenChange, + contentClassName = 'font-designer-16m rounded-100', + arrowClassName, +}) => { + return ( + + + {trigger} + + + + {value} + + + + + + ); +}; + +export default Tooltip; diff --git a/src/widgets/home/calendar.tsx b/src/widgets/home/calendar.tsx index 1d4769a0..402fd09d 100644 --- a/src/widgets/home/calendar.tsx +++ b/src/widgets/home/calendar.tsx @@ -79,6 +79,11 @@ const Calendar = (props: React.ComponentProps) => { if (isLoading) return
로딩 중...
; + const monthlyCount = data?.monthlyCompletedCount; + const totalCount = data?.totalCompletedCount; + const showFooter = + typeof monthlyCount === 'number' && typeof totalCount === 'number'; + return (
) => { day: 'text-center font-designer-14m rounded-full', }} footer={ -
-
- {month}월은 {data.monthlyCompletedCount}번의 스터디를 완료했어요. -
-
- 총 {data.totalCompletedCount}번의 스터디를 완료했어요. + showFooter ? ( +
+
+ {month}월은 {data.monthlyCompletedCount}번의 스터디를 + 완료했어요. +
+
+ 총 {data.totalCompletedCount}번의 스터디를 완료했어요. +
-
+ ) : undefined } {...props} /> diff --git a/yarn.lock b/yarn.lock index 2f43b386..fb80f80e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2469,6 +2469,13 @@ __metadata: languageName: node linkType: hard +"@radix-ui/primitive@npm:1.1.3": + version: 1.1.3 + resolution: "@radix-ui/primitive@npm:1.1.3" + checksum: 10c0/88860165ee7066fa2c179f32ffcd3ee6d527d9dcdc0e8be85e9cb0e2c84834be8e3c1a976c74ba44b193f709544e12f54455d892b28e32f0708d89deda6b9f1d + languageName: node + linkType: hard + "@radix-ui/react-arrow@npm:1.1.7": version: 1.1.7 resolution: "@radix-ui/react-arrow@npm:1.1.7" @@ -2627,6 +2634,29 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-dismissable-layer@npm:1.1.11": + version: 1.1.11 + resolution: "@radix-ui/react-dismissable-layer@npm:1.1.11" + dependencies: + "@radix-ui/primitive": "npm:1.1.3" + "@radix-ui/react-compose-refs": "npm:1.1.2" + "@radix-ui/react-primitive": "npm:2.1.3" + "@radix-ui/react-use-callback-ref": "npm:1.1.1" + "@radix-ui/react-use-escape-keydown": "npm:1.1.1" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/c825572a64073c4d3853702029979f6658770ffd6a98eabc4984e1dee1b226b4078a2a4dc7003f96475b438985e9b21a58e75f51db74dd06848dcae1f2d395dc + languageName: node + linkType: hard + "@radix-ui/react-dismissable-layer@npm:1.1.7": version: 1.1.7 resolution: "@radix-ui/react-dismissable-layer@npm:1.1.7" @@ -2809,6 +2839,34 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-popper@npm:1.2.8": + version: 1.2.8 + resolution: "@radix-ui/react-popper@npm:1.2.8" + dependencies: + "@floating-ui/react-dom": "npm:^2.0.0" + "@radix-ui/react-arrow": "npm:1.1.7" + "@radix-ui/react-compose-refs": "npm:1.1.2" + "@radix-ui/react-context": "npm:1.1.2" + "@radix-ui/react-primitive": "npm:2.1.3" + "@radix-ui/react-use-callback-ref": "npm:1.1.1" + "@radix-ui/react-use-layout-effect": "npm:1.1.1" + "@radix-ui/react-use-rect": "npm:1.1.1" + "@radix-ui/react-use-size": "npm:1.1.1" + "@radix-ui/rect": "npm:1.1.1" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/48e3f13eac3b8c13aca8ded37d74db17e1bb294da8d69f142ab6b8719a06c3f90051668bed64520bf9f3abdd77b382ce7ce209d056bb56137cecc949b69b421c + languageName: node + linkType: hard + "@radix-ui/react-portal@npm:1.1.6": version: 1.1.6 resolution: "@radix-ui/react-portal@npm:1.1.6" @@ -2869,6 +2927,26 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-presence@npm:1.1.5": + version: 1.1.5 + resolution: "@radix-ui/react-presence@npm:1.1.5" + dependencies: + "@radix-ui/react-compose-refs": "npm:1.1.2" + "@radix-ui/react-use-layout-effect": "npm:1.1.1" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/d0e61d314250eeaef5369983cb790701d667f51734bafd98cf759072755562018052c594e6cdc5389789f4543cb0a4d98f03ff4e8f37338d6b5bf51a1700c1d1 + languageName: node + linkType: hard + "@radix-ui/react-primitive@npm:2.1.0": version: 2.1.0 resolution: "@radix-ui/react-primitive@npm:2.1.0" @@ -3064,6 +3142,36 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-tooltip@npm:^1.2.8": + version: 1.2.8 + resolution: "@radix-ui/react-tooltip@npm:1.2.8" + dependencies: + "@radix-ui/primitive": "npm:1.1.3" + "@radix-ui/react-compose-refs": "npm:1.1.2" + "@radix-ui/react-context": "npm:1.1.2" + "@radix-ui/react-dismissable-layer": "npm:1.1.11" + "@radix-ui/react-id": "npm:1.1.1" + "@radix-ui/react-popper": "npm:1.2.8" + "@radix-ui/react-portal": "npm:1.1.9" + "@radix-ui/react-presence": "npm:1.1.5" + "@radix-ui/react-primitive": "npm:2.1.3" + "@radix-ui/react-slot": "npm:1.2.3" + "@radix-ui/react-use-controllable-state": "npm:1.2.2" + "@radix-ui/react-visually-hidden": "npm:1.2.3" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/de0cbae9c571a00671f160928d819e59502f59be8749f536ab4b180181d9d70aee3925a5b2555f8f32d0bea622bc35f65b70ca7ff0449e4844f891302310cc48 + languageName: node + linkType: hard + "@radix-ui/react-use-callback-ref@npm:1.1.1": version: 1.1.1 resolution: "@radix-ui/react-use-callback-ref@npm:1.1.1" @@ -3194,6 +3302,25 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-visually-hidden@npm:1.2.3": + version: 1.2.3 + resolution: "@radix-ui/react-visually-hidden@npm:1.2.3" + dependencies: + "@radix-ui/react-primitive": "npm:2.1.3" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/cf86a37f1cbee50a964056f3dc4f6bb1ee79c76daa321f913aa20ff3e1ccdfafbf2b114d7bb616aeefc7c4b895e6ca898523fdb67710d89bd5d8edb739a0d9b6 + languageName: node + linkType: hard + "@radix-ui/rect@npm:1.1.1": version: 1.1.1 resolution: "@radix-ui/rect@npm:1.1.1" @@ -10629,6 +10756,7 @@ __metadata: "@radix-ui/react-slot": "npm:^1.2.2" "@radix-ui/react-switch": "npm:^1.2.4" "@radix-ui/react-toggle": "npm:^1.1.9" + "@radix-ui/react-tooltip": "npm:^1.2.8" "@rushstack/eslint-config": "npm:^4.1.1" "@storybook/addon-essentials": "npm:^8.6.12" "@storybook/addon-onboarding": "npm:^8.6.12"