| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- import { useCallback, useEffect, useRef, useState } from 'react';
- import { useNavigate } from 'react-router-dom';
- import type { NavMenuItem } from '@/utils/navUtils';
- const OVERFLOW_BUTTON_WIDTH = 80;
- const MENU_ITEM_GAP = 10;
- interface UseActionParams {
- menuItems: NavMenuItem[];
- isMobile: boolean;
- }
- /**
- * Topbar UI 交互响应逻辑 Hook
- * 处理 UI 状态、事件处理、DOM 交互等响应逻辑
- */
- export function useAction({ menuItems, isMobile }: UseActionParams) {
- const navigate = useNavigate();
- const menuContainerRef = useRef<HTMLDivElement>(null);
- const menuItemsRef = useRef<Map<string, HTMLButtonElement>>(new Map());
- const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
- const [isMobileMenuClosing, setIsMobileMenuClosing] = useState(false);
- const [isOverflowMenuOpen, setIsOverflowMenuOpen] = useState(false);
- const [visibleMenuItems, setVisibleMenuItems] = useState<NavMenuItem[]>([]);
- const [overflowMenuItems, setOverflowMenuItems] = useState<NavMenuItem[]>([]);
- const calculateVisibleItems = useCallback(() => {
- if (isMobile || !menuContainerRef.current || menuItems.length === 0) {
- setVisibleMenuItems(menuItems);
- setOverflowMenuItems([]);
- return;
- }
- const container = menuContainerRef.current;
- const containerWidth = container.offsetWidth;
- let totalWidth = 0;
- const visible: NavMenuItem[] = [];
- const overflow: NavMenuItem[] = [];
- for (let i = 0; i < menuItems.length; i++) {
- const item = menuItems[i];
- const itemElement = menuItemsRef.current.get(item.name);
- if (itemElement) {
- const itemWidth = itemElement.offsetWidth + MENU_ITEM_GAP;
- const needsOverflowButton = i < menuItems.length - 1;
- if (
- totalWidth + itemWidth + (needsOverflowButton ? OVERFLOW_BUTTON_WIDTH : 0) <=
- containerWidth
- ) {
- visible.push(item);
- totalWidth += itemWidth;
- } else {
- overflow.push(...menuItems.slice(i));
- break;
- }
- } else {
- visible.push(item);
- }
- }
- setVisibleMenuItems(visible);
- setOverflowMenuItems(overflow);
- }, [isMobile, menuItems]);
- useEffect(() => {
- if (!isMobile && menuItems.length > 0) {
- const timer = setTimeout(() => {
- calculateVisibleItems();
- }, 0);
- return () => clearTimeout(timer);
- }
- }, [isMobile, menuItems.length, calculateVisibleItems]);
- useEffect(() => {
- if (isMobile) {
- return;
- }
- const handleResize = () => {
- calculateVisibleItems();
- };
- window.addEventListener('resize', handleResize);
- return () => window.removeEventListener('resize', handleResize);
- }, [isMobile, calculateVisibleItems]);
- const handleMenuClick = useCallback(
- (path: string) => {
- navigate(path);
- setIsMobileMenuOpen(false);
- setIsOverflowMenuOpen(false);
- },
- [navigate]
- );
- const toggleMobileMenu = useCallback(() => {
- if (isMobileMenuOpen) {
- setIsMobileMenuClosing(true);
- } else {
- setIsMobileMenuOpen(true);
- setIsMobileMenuClosing(false);
- }
- }, [isMobileMenuOpen]);
- const closeMobileMenu = useCallback(() => {
- setIsMobileMenuClosing(true);
- }, []);
- const handleMenuAnimationEnd = useCallback(() => {
- if (isMobileMenuClosing) {
- setIsMobileMenuOpen(false);
- setIsMobileMenuClosing(false);
- }
- }, [isMobileMenuClosing]);
- const toggleOverflowMenu = useCallback(() => {
- setIsOverflowMenuOpen((prev) => !prev);
- }, []);
- const setMenuItemRef = useCallback((itemName: string) => {
- return (el: HTMLButtonElement | null) => {
- if (el) {
- menuItemsRef.current.set(itemName, el);
- } else {
- menuItemsRef.current.delete(itemName);
- }
- };
- }, []);
- return {
- menuContainerRef,
- menuItemsRef,
- isMobileMenuOpen,
- isMobileMenuClosing,
- isOverflowMenuOpen,
- visibleMenuItems,
- overflowMenuItems,
- handleMenuClick,
- toggleMobileMenu,
- closeMobileMenu,
- handleMenuAnimationEnd,
- toggleOverflowMenu,
- setMenuItemRef,
- };
- }
|