Files
ember-market-frontend/hooks/use-chromebook-keyboard.tsx
NotII 130ecac208 Add Chromebook compatibility fixes and optimizations
Implemented comprehensive Chromebook-specific fixes including viewport adjustments, enhanced touch and keyboard detection, improved scrolling and keyboard navigation hooks, and extensive CSS optimizations for better usability. Updated chat and dashboard interfaces for larger touch targets, better focus management, and responsive layouts. Added documentation in docs/CHROMEBOOK-FIXES.md and new hooks for Chromebook scroll and keyboard handling.
2025-10-26 18:29:23 +00:00

161 lines
5.2 KiB
TypeScript

import { useEffect, useCallback } from 'react';
/**
* Hook for enhanced keyboard navigation on Chromebooks
*/
export function useChromebookKeyboard() {
const handleKeyDown = useCallback((e: KeyboardEvent) => {
// Enhanced keyboard shortcuts for Chromebooks
const { key, ctrlKey, metaKey, altKey, shiftKey } = e;
// Chromebook-specific shortcuts
if (metaKey || ctrlKey) {
switch (key) {
case 'k':
// Focus search or command palette
e.preventDefault();
const searchInput = document.querySelector('input[type="search"], input[placeholder*="search" i]') as HTMLInputElement;
if (searchInput) {
searchInput.focus();
searchInput.select();
}
break;
case 'Enter':
// Submit forms with Ctrl/Cmd + Enter
e.preventDefault();
const form = document.querySelector('form') as HTMLFormElement;
if (form) {
const submitButton = form.querySelector('button[type="submit"]') as HTMLButtonElement;
if (submitButton && !submitButton.disabled) {
submitButton.click();
}
}
break;
case 'ArrowUp':
case 'ArrowDown':
// Navigate through messages or list items
e.preventDefault();
const focusableElements = document.querySelectorAll(
'button, input, textarea, [tabindex]:not([tabindex="-1"]), [role="button"], [role="tab"]'
) as NodeListOf<HTMLElement>;
const currentIndex = Array.from(focusableElements).indexOf(document.activeElement as HTMLElement);
let nextIndex;
if (key === 'ArrowUp') {
nextIndex = currentIndex > 0 ? currentIndex - 1 : focusableElements.length - 1;
} else {
nextIndex = currentIndex < focusableElements.length - 1 ? currentIndex + 1 : 0;
}
focusableElements[nextIndex]?.focus();
break;
}
}
// Escape key handling
if (key === 'Escape') {
// Close modals, clear inputs, or go back
const activeElement = document.activeElement as HTMLElement;
if (activeElement?.tagName === 'INPUT' || activeElement?.tagName === 'TEXTAREA') {
// Clear input on Escape
(activeElement as HTMLInputElement).value = '';
activeElement.blur();
} else {
// Look for close buttons or back buttons
const closeButton = document.querySelector('[aria-label*="close" i], [aria-label*="back" i]') as HTMLElement;
if (closeButton) {
closeButton.click();
}
}
}
// Tab navigation enhancement
if (key === 'Tab') {
// Ensure proper tab order for Chromebooks
const focusableElements = document.querySelectorAll(
'button:not([disabled]), input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), [role="button"]:not([disabled]), [role="tab"]'
);
// Add visual focus indicators
const addFocusIndicator = (element: Element) => {
element.classList.add('keyboard-focus');
};
const removeFocusIndicator = (element: Element) => {
element.classList.remove('keyboard-focus');
};
// Handle focus events
focusableElements.forEach(element => {
element.addEventListener('focus', () => addFocusIndicator(element));
element.addEventListener('blur', () => removeFocusIndicator(element));
});
}
}, []);
useEffect(() => {
// Add global keyboard event listener
document.addEventListener('keydown', handleKeyDown);
// Cleanup
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [handleKeyDown]);
return {
handleKeyDown
};
}
/**
* Hook for managing focus in chat interfaces
*/
export function useChatFocus() {
const focusMessageInput = useCallback(() => {
const messageInput = document.querySelector('input[aria-label*="message" i], textarea[aria-label*="message" i]') as HTMLInputElement;
if (messageInput) {
messageInput.focus();
messageInput.select();
}
}, []);
const focusNextMessage = useCallback(() => {
const messages = document.querySelectorAll('[role="article"]');
const currentMessage = document.activeElement?.closest('[role="article"]');
if (currentMessage) {
const currentIndex = Array.from(messages).indexOf(currentMessage);
const nextMessage = messages[currentIndex + 1] as HTMLElement;
if (nextMessage) {
nextMessage.focus();
}
} else if (messages.length > 0) {
(messages[0] as HTMLElement).focus();
}
}, []);
const focusPreviousMessage = useCallback(() => {
const messages = document.querySelectorAll('[role="article"]');
const currentMessage = document.activeElement?.closest('[role="article"]');
if (currentMessage) {
const currentIndex = Array.from(messages).indexOf(currentMessage);
const previousMessage = messages[currentIndex - 1] as HTMLElement;
if (previousMessage) {
previousMessage.focus();
}
}
}, []);
return {
focusMessageInput,
focusNextMessage,
focusPreviousMessage
};
}