/** * useResponsiveValue Hook * 响应式值映射 - 根据当前断点返回对应值 */ import { useMemo } from 'react'; import { useWindowDimensions } from './useScreenSize'; import { getFineBreakpoint } from './useBreakpoint'; import type { FineBreakpointKey, ResponsiveValue } from './types'; const breakpointOrder: FineBreakpointKey[] = ['4xl', '3xl', '2xl', 'xl', 'lg', 'md', 'sm', 'xs']; /** * 根据当前断点从响应式值对象中选择合适的值 * * @param value - 响应式值,可以是单一值或断点映射对象 * @returns 选中的值 * * @example * const padding = useResponsiveValue({ xs: 8, md: 16, lg: 24 }); * // 在 xs 屏幕返回 8,md 屏幕返回 16,lg 及以上返回 24 */ export function useResponsiveValue(value: ResponsiveValue): T { const { width } = useWindowDimensions(); const fineBreakpoint = getFineBreakpoint(width); return useMemo(() => { // 如果不是对象,直接返回 if (typeof value !== 'object' || value === null || Array.isArray(value)) { return value as T; } const valueMap = value as Partial>; // 从当前断点开始向下查找 const currentIndex = breakpointOrder.indexOf(fineBreakpoint); for (let i = currentIndex; i < breakpointOrder.length; i++) { const bp = breakpointOrder[i]; if (bp in valueMap) { return valueMap[bp]!; } } // 如果没找到,返回 xs 的值或第一个值 return (valueMap.xs ?? Object.values(valueMap)[0]) as T; }, [value, fineBreakpoint]); } /** * 响应式样式生成器 * 根据断点生成响应式样式 * * @param styles - 响应式样式对象 * @returns 当前断点对应的样式 * * @example * const containerStyle = useResponsiveStyle({ * padding: { xs: 8, md: 16, lg: 24 }, * fontSize: { xs: 14, lg: 16 } * }); */ export function useResponsiveStyle>>( styles: T ): { [K in keyof T]: T[K] extends ResponsiveValue ? V : never } { const { width } = useWindowDimensions(); const fineBreakpoint = getFineBreakpoint(width); return useMemo(() => { const result = {} as { [K in keyof T]: T[K] extends ResponsiveValue ? V : never }; for (const key in styles) { const value = styles[key]; if (typeof value !== 'object' || value === null || Array.isArray(value)) { (result as Record)[key] = value; } else { const valueMap = value as Partial>; const currentIndex = breakpointOrder.indexOf(fineBreakpoint); let selectedValue: unknown = undefined; for (let i = currentIndex; i < breakpointOrder.length; i++) { const bp = breakpointOrder[i]; if (bp in valueMap) { selectedValue = valueMap[bp]; break; } } (result as Record)[key] = selectedValue ?? valueMap.xs ?? Object.values(valueMap)[0]; } } return result; }, [styles, fineBreakpoint]); }