utils/clickHandlers

This commit is contained in:
Anthony Stirling 2025-09-03 14:24:41 +01:00
parent 4e703bef34
commit 90451f7172
7 changed files with 59 additions and 48 deletions

View File

@ -5,6 +5,7 @@ import { Tooltip } from './Tooltip';
import AppsIcon from '@mui/icons-material/AppsRounded'; import AppsIcon from '@mui/icons-material/AppsRounded';
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext'; import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import { useSidebarNavigation } from '../../hooks/useSidebarNavigation'; import { useSidebarNavigation } from '../../hooks/useSidebarNavigation';
import { handleUnlessSpecialClick } from '../../utils/clickHandlers';
interface AllToolsNavButtonProps { interface AllToolsNavButtonProps {
activeButton: string; activeButton: string;
@ -29,14 +30,7 @@ const AllToolsNavButton: React.FC<AllToolsNavButtonProps> = ({ activeButton, set
const navProps = getHomeNavigation(); const navProps = getHomeNavigation();
const handleNavClick = (e: React.MouseEvent) => { const handleNavClick = (e: React.MouseEvent) => {
// Check if it's a special click (middle click, ctrl+click, etc.) handleUnlessSpecialClick(e, handleClick);
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) {
return; // Let browser handle it via href
}
// For regular clicks, prevent default and use SPA navigation
e.preventDefault();
handleClick();
}; };
const iconNode = ( const iconNode = (

View File

@ -8,6 +8,7 @@ import { useIsOverflowing } from '../../hooks/useIsOverflowing';
import { useFilesModalContext } from '../../contexts/FilesModalContext'; import { useFilesModalContext } from '../../contexts/FilesModalContext';
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext'; import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import { useSidebarNavigation } from '../../hooks/useSidebarNavigation'; import { useSidebarNavigation } from '../../hooks/useSidebarNavigation';
import { handleUnlessSpecialClick } from '../../utils/clickHandlers';
import { ButtonConfig } from '../../types/sidebar'; import { ButtonConfig } from '../../types/sidebar';
import './quickAccessBar/QuickAccessBar.css'; import './quickAccessBar/QuickAccessBar.css';
import AllToolsNavButton from './AllToolsNavButton'; import AllToolsNavButton from './AllToolsNavButton';
@ -50,14 +51,10 @@ const QuickAccessBar = forwardRef<HTMLDivElement>(({
const handleClick = (e?: React.MouseEvent) => { const handleClick = (e?: React.MouseEvent) => {
if (navProps && e) { if (navProps && e) {
// Check if it's a special click (middle click, ctrl+click, etc.) handleUnlessSpecialClick(e, config.onClick);
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { } else {
return; // Let browser handle it via href config.onClick();
}
// For regular clicks, prevent default and use SPA navigation
e.preventDefault();
} }
config.onClick();
}; };
// Render with URL navigation if available, otherwise regular div // Render with URL navigation if available, otherwise regular div

View File

@ -17,6 +17,7 @@ import { ActionIcon } from '@mantine/core';
import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';
import { useToolWorkflow } from '../../../contexts/ToolWorkflowContext'; import { useToolWorkflow } from '../../../contexts/ToolWorkflowContext';
import { useSidebarNavigation } from '../../../hooks/useSidebarNavigation'; import { useSidebarNavigation } from '../../../hooks/useSidebarNavigation';
import { handleUnlessSpecialClick } from '../../../utils/clickHandlers';
import FitText from '../FitText'; import FitText from '../FitText';
import { Tooltip } from '../Tooltip'; import { Tooltip } from '../Tooltip';
@ -147,15 +148,10 @@ const ActiveToolButton: React.FC<ActiveToolButtonProps> = ({ activeButton, setAc
component="a" component="a"
href={getHomeNavigation().href} href={getHomeNavigation().href}
onClick={(e: React.MouseEvent) => { onClick={(e: React.MouseEvent) => {
// Check if it's a special click (middle click, ctrl+click, etc.) handleUnlessSpecialClick(e, () => {
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { setActiveButton('tools');
return; // Let browser handle it via href handleBackToTools();
} });
// For regular clicks, prevent default and use SPA navigation
e.preventDefault();
setActiveButton('tools');
handleBackToTools();
}} }}
size={'xl'} size={'xl'}
variant="subtle" variant="subtle"

View File

@ -1,8 +1,9 @@
import React from "react"; import React from "react";
import { Button, Anchor } from "@mantine/core"; import { Button } from "@mantine/core";
import { Tooltip } from "../../shared/Tooltip"; import { Tooltip } from "../../shared/Tooltip";
import { ToolRegistryEntry } from "../../../data/toolsTaxonomy"; import { ToolRegistryEntry } from "../../../data/toolsTaxonomy";
import { useToolNavigation } from "../../../hooks/useToolNavigation"; import { useToolNavigation } from "../../../hooks/useToolNavigation";
import { handleUnlessSpecialClick } from "../../../utils/clickHandlers";
import FitText from "../../shared/FitText"; import FitText from "../../shared/FitText";
interface ToolButtonProps { interface ToolButtonProps {
@ -49,14 +50,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect
); );
const handleExternalClick = (e: React.MouseEvent) => { const handleExternalClick = (e: React.MouseEvent) => {
// Check if it's a special click (ctrl+click, etc.) handleUnlessSpecialClick(e, () => handleClick(id));
if (e.metaKey || e.ctrlKey || e.shiftKey) {
return; // Let browser handle it via href
}
// For regular clicks, prevent default and use window.open
e.preventDefault();
handleClick(id);
}; };
const buttonElement = navProps ? ( const buttonElement = navProps ? (

View File

@ -1,6 +1,7 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useToolNavigation } from './useToolNavigation'; import { useToolNavigation } from './useToolNavigation';
import { useToolManagement } from './useToolManagement'; import { useToolManagement } from './useToolManagement';
import { handleUnlessSpecialClick } from '../utils/clickHandlers';
export interface SidebarNavigationProps { export interface SidebarNavigationProps {
/** Full URL for the navigation (for href attribute) */ /** Full URL for the navigation (for href attribute) */
@ -21,8 +22,9 @@ export function useSidebarNavigation(): {
const { getSelectedTool } = useToolManagement(); const { getSelectedTool } = useToolManagement();
const defaultNavClick = useCallback((e: React.MouseEvent) => { const defaultNavClick = useCallback((e: React.MouseEvent) => {
if (e.metaKey || e.ctrlKey || e.shiftKey) return; handleUnlessSpecialClick(e, () => {
e.preventDefault(); // SPA navigation will be handled by the calling component
});
}, []); }, []);
const getHomeNavigation = useCallback((): SidebarNavigationProps => { const getHomeNavigation = useCallback((): SidebarNavigationProps => {

View File

@ -2,6 +2,7 @@ import { useCallback } from 'react';
import { ToolId } from '../types/toolId'; import { ToolId } from '../types/toolId';
import { ToolRegistryEntry, getToolUrlPath } from '../data/toolsTaxonomy'; import { ToolRegistryEntry, getToolUrlPath } from '../data/toolsTaxonomy';
import { useToolWorkflow } from '../contexts/ToolWorkflowContext'; import { useToolWorkflow } from '../contexts/ToolWorkflowContext';
import { handleUnlessSpecialClick } from '../utils/clickHandlers';
export interface ToolNavigationProps { export interface ToolNavigationProps {
/** Full URL for the tool (for href attribute) */ /** Full URL for the tool (for href attribute) */
@ -26,20 +27,16 @@ export function useToolNavigation(): {
// Click handler that maintains SPA behavior // Click handler that maintains SPA behavior
const onClick = (e: React.MouseEvent) => { const onClick = (e: React.MouseEvent) => {
// Check if it's a special click (ctrl+click, etc.) handleUnlessSpecialClick(e, () => {
if (e.metaKey || e.ctrlKey || e.shiftKey) { // Handle external links normally
return; // Let browser handle it via href if (tool.link) {
} window.open(tool.link, '_blank', 'noopener,noreferrer');
return;
}
// Handle external links normally // Use SPA navigation for internal tools
if (tool.link) { handleToolSelect(toolId);
window.open(tool.link, '_blank', 'noopener,noreferrer'); });
return;
}
// For regular clicks, prevent default and use SPA navigation
e.preventDefault();
handleToolSelect(toolId);
}; };
return { href, onClick }; return { href, onClick };

View File

@ -0,0 +1,31 @@
/**
* Utility functions for handling click events in navigation components
*/
/**
* Determines if a click event is a "special" click that should use browser's default navigation
* instead of SPA navigation. Special clicks include:
* - Ctrl+click (or Cmd+click on Mac)
* - Shift+click
* - Middle mouse button click
*/
export function isSpecialClick(e: React.MouseEvent): boolean {
return e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1;
}
/**
* Handles a click event for SPA navigation, but allows special clicks to use browser defaults
*
* @param e - The click event
* @param handleClick - Function to execute for regular clicks (SPA navigation)
* @returns true if the event was handled as a special click, false if it was handled as regular click
*/
export function handleUnlessSpecialClick(e: React.MouseEvent, handleClick: () => void): boolean {
if (isSpecialClick(e)) {
return true; // Let browser handle via href
}
e.preventDefault();
handleClick();
return false;
}