| | import { useEffect, useCallback } from 'react'; |
| |
|
| | |
| | |
| | |
| | |
| | export const useArrowNavigation = ( |
| | selectedFont, |
| | fonts, |
| | filter, |
| | searchTerm, |
| | onFontSelect |
| | ) => { |
| | |
| | const getFilteredFonts = useCallback(() => { |
| | if (!fonts || fonts.length === 0) return []; |
| | |
| | return fonts.filter(font => { |
| | |
| | const familyMatch = filter === 'all' || font.family === filter; |
| | |
| | |
| | const searchMatch = !searchTerm || |
| | font.name.toLowerCase().includes(searchTerm.toLowerCase()) || |
| | font.family.toLowerCase().includes(searchTerm.toLowerCase()); |
| | |
| | return familyMatch && searchMatch; |
| | }); |
| | }, [fonts, filter, searchTerm]); |
| |
|
| | |
| |
|
| | |
| | const findNearestFontInDirection = useCallback((direction) => { |
| | if (!selectedFont || !onFontSelect) return; |
| | |
| | const filteredFonts = getFilteredFonts(); |
| | if (filteredFonts.length <= 1) return; |
| | |
| | |
| | const currentFont = filteredFonts.find(font => font.name === selectedFont.name); |
| | if (!currentFont) return; |
| | |
| | let bestFont = null; |
| | let bestDistance = Infinity; |
| | |
| | |
| | filteredFonts.forEach(font => { |
| | if (font.name === selectedFont.name) return; |
| | |
| | const dx = font.x - currentFont.x; |
| | const dy = font.y - currentFont.y; |
| | const distance = Math.sqrt(dx * dx + dy * dy); |
| | |
| | if (distance === 0) return; |
| | |
| | let isInDirection = false; |
| | |
| | |
| | switch (direction) { |
| | case 'ArrowUp': |
| | |
| | isInDirection = dy > 0 && Math.abs(dx) <= Math.abs(dy) * 2; |
| | break; |
| | case 'ArrowDown': |
| | |
| | isInDirection = dy < 0 && Math.abs(dx) <= Math.abs(dy) * 2; |
| | break; |
| | case 'ArrowLeft': |
| | |
| | isInDirection = dx < 0 && Math.abs(dy) <= Math.abs(dx) * 2; |
| | break; |
| | case 'ArrowRight': |
| | |
| | isInDirection = dx > 0 && Math.abs(dy) <= Math.abs(dx) * 2; |
| | break; |
| | default: |
| | return; |
| | } |
| | |
| | |
| | if (isInDirection && distance < bestDistance) { |
| | bestDistance = distance; |
| | bestFont = font; |
| | } |
| | }); |
| | |
| | |
| | if (bestFont) { |
| | onFontSelect(bestFont); |
| | } |
| | }, [selectedFont, getFilteredFonts, onFontSelect]); |
| |
|
| | |
| | const handleKeyDown = useCallback((event) => { |
| | |
| | if (!selectedFont) return; |
| | |
| | |
| | const activeElement = document.activeElement; |
| | if (activeElement && ( |
| | activeElement.tagName === 'INPUT' || |
| | activeElement.tagName === 'TEXTAREA' || |
| | activeElement.contentEditable === 'true' |
| | )) { |
| | return; |
| | } |
| | |
| | |
| | if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) { |
| | event.preventDefault(); |
| | findNearestFontInDirection(event.key); |
| | } |
| | }, [selectedFont, findNearestFontInDirection]); |
| |
|
| | |
| | useEffect(() => { |
| | window.addEventListener('keydown', handleKeyDown); |
| | |
| | return () => { |
| | window.removeEventListener('keydown', handleKeyDown); |
| | }; |
| | }, [handleKeyDown]); |
| |
|
| | |
| | return { |
| | canNavigate: selectedFont && getFilteredFonts().length > 1, |
| | filteredFontsCount: getFilteredFonts().length, |
| | selectedFontName: selectedFont?.name |
| | }; |
| | }; |
| |
|