diff --git a/ui/src/pages/dashboard/components/memberInfo.tsx b/ui/src/pages/dashboard/components/memberInfo.tsx index 7b1ef53..7d01be5 100644 --- a/ui/src/pages/dashboard/components/memberInfo.tsx +++ b/ui/src/pages/dashboard/components/memberInfo.tsx @@ -1,10 +1,9 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState, useCallback } from 'react'; import Card from '@/components/card'; import dayjs from 'dayjs'; import { Box, Stack, - Typography, Tooltip, useTheme, IconButton, @@ -21,7 +20,6 @@ const getRecent1YearData = ( data: DomainUserHeatmap[] = [], max_count: number ) => { - const average = max_count / 5; const today = dayjs(); const lastYearToday = today.subtract(1, 'year'); const diffInDays = today.diff(lastYearToday, 'day'); @@ -31,21 +29,133 @@ const getRecent1YearData = ( dateMap[dayjs.unix(item.date!).format('YYYY-MM-DD')] = item.count!; }); + const getLevel = (count: number) => { + if (count === 0) return 0; + if (count === 1) return 1; + if (count <= Math.max(2, max_count * 0.25)) return 2; + if (count <= Math.max(3, max_count * 0.6)) return 3; + return 4; + }; + for (let i = 0; i < diffInDays; i++) { const time = today.subtract(i, 'day').format('YYYY-MM-DD'); - if (dateMap[time]) { - result.unshift({ - count: dateMap[time], - date: time, - level: Math.ceil(dateMap[time] / average) - 1, - }); - } else { - result.unshift({ count: 0, date: time, level: 0 }); - } + const count = dateMap[time] || 0; + result.unshift({ + count, + date: time, + level: getLevel(count), + }); } return result; }; +// 自定义Hook:处理ActivityCalendar自动滚动 +const useActivityCalendarAutoScroll = () => { + const scrollToLatest = useCallback(() => { + // 尝试多种可能的选择器策略 + const selectors = [ + '.react-activity-calendar__scroll-container', + '.react-activity-calendar [style*="overflow"]', + '.react-activity-calendar > div:first-child', + ]; + + let scrollContainer: HTMLElement | null = null; + + // 按优先级尝试找到滚动容器 + for (const selector of selectors) { + scrollContainer = document.querySelector(selector); + if (scrollContainer && scrollContainer.scrollWidth > scrollContainer.clientWidth) { + break; + } + } + + if (scrollContainer) { + // 滚动到最右侧(最新数据) + scrollContainer.scrollLeft = scrollContainer.scrollWidth - scrollContainer.clientWidth; + } + }, []); + + const setupAutoScroll = useCallback(() => { + // 延迟执行确保组件完全渲染 + const timeoutId = setTimeout(scrollToLatest, 100); + + // 使用ResizeObserver监听容器大小变化 + let resizeObserver: ResizeObserver | null = null; + + const setupResizeObserver = () => { + const container = document.querySelector('.react-activity-calendar'); + if (container && 'ResizeObserver' in window) { + resizeObserver = new ResizeObserver(() => { + scrollToLatest(); + }); + resizeObserver.observe(container); + } else { + // 降级方案:使用window resize事件 + window.addEventListener('resize', scrollToLatest); + } + }; + + // 延迟设置ResizeObserver确保DOM已渲染 + const observerTimeoutId = setTimeout(setupResizeObserver, 150); + + // 清理函数 + return () => { + clearTimeout(timeoutId); + clearTimeout(observerTimeoutId); + if (resizeObserver) { + resizeObserver.disconnect(); + } else { + window.removeEventListener('resize', scrollToLatest); + } + }; + }, [scrollToLatest]); + + return { setupAutoScroll }; +}; + +// 简化的blockSize计算Hook - 保持原有大小 +const useBlockSize = (containerRef: React.RefObject) => { + const [blockSize, setBlockSize] = useState(8); + + useEffect(() => { + const calculateBlockSize = () => { + if (!containerRef.current) return; + + const containerWidth = containerRef.current.offsetWidth; + const baseWidth = 980; + const blockIncrement = 54; + + // 只在桌面端进行计算,保持原有逻辑 + const increment = Math.max(0, Math.ceil((containerWidth - baseWidth) / blockIncrement)); + setBlockSize(increment + 8); + }; + + // 初始计算 + calculateBlockSize(); + + // 使用ResizeObserver监听容器大小变化 + let resizeObserver: ResizeObserver | null = null; + + if ('ResizeObserver' in window && containerRef.current) { + resizeObserver = new ResizeObserver(calculateBlockSize); + resizeObserver.observe(containerRef.current); + } else { + // 降级方案 + window.addEventListener('resize', calculateBlockSize); + } + + return () => { + if (resizeObserver) { + resizeObserver.disconnect(); + } else { + window.removeEventListener('resize', calculateBlockSize); + } + }; + }, [containerRef]); + + return blockSize; +}; + const MemberInfo = ({ data, memberData, @@ -58,22 +168,27 @@ const MemberInfo = ({ onMemberChange?: (data: DomainUser) => void; }) => { const theme = useTheme(); - const [blockSize, setBlockSize] = useState(8); const ref = useRef(null); const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); + + // 使用自定义Hooks + const blockSize = useBlockSize(ref as React.RefObject); + const { setupAutoScroll } = useActivityCalendarAutoScroll(); + const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; + const handleClose = () => { setAnchorEl(null); }; + + // 设置自动滚动 useEffect(() => { - const innerWidth = ref.current?.offsetWidth; - const dis = Math.max(0, Math.ceil((innerWidth! - 980) / 54)); - console.log(dis); - setBlockSize(dis + 8); - }, []); + const cleanup = setupAutoScroll(); + return cleanup; + }, [setupAutoScroll, data, memberData]); return ( (