📱(frontend) add touch detection
We rely on the size of the screen to determine if we are on a touch device, but this is not always accurate, for example on laptops when we resize the window to a smaller size, it could be misinterpreted as a touch device. To improve this, we add detection for touch input so we can more accurately determine if the user is using touch or mouse input.
This commit is contained in:
@@ -52,14 +52,17 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
|
||||
const { theme } = useCunninghamTheme();
|
||||
const { replace } = useRouter();
|
||||
|
||||
const initializeResizeListener = useResponsiveStore(
|
||||
(state) => state.initializeResizeListener,
|
||||
);
|
||||
const { initializeResizeListener, initializeInputDetection } =
|
||||
useResponsiveStore();
|
||||
|
||||
useEffect(() => {
|
||||
return initializeResizeListener();
|
||||
}, [initializeResizeListener]);
|
||||
|
||||
useEffect(() => {
|
||||
return initializeInputDetection();
|
||||
}, [initializeInputDetection]);
|
||||
|
||||
/**
|
||||
* Update the global router replace function
|
||||
* This allows us to use the router replace function globally
|
||||
|
||||
+2
-3
@@ -35,7 +35,7 @@ export const BlockNoteToolbar = () => {
|
||||
const [onConfirm, setOnConfirm] = useState<() => void | Promise<void>>();
|
||||
const { t } = useTranslation();
|
||||
const { data: conf } = useConfig();
|
||||
const { isMobile, isTablet } = useResponsiveStore();
|
||||
const { isTablet, isInputTouch } = useResponsiveStore();
|
||||
|
||||
const toolbarItems = useMemo(() => {
|
||||
let toolbarItems = getFormattingToolbarItems([
|
||||
@@ -96,7 +96,7 @@ export const BlockNoteToolbar = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{isMobile || isTablet ? (
|
||||
{isInputTouch && isTablet ? (
|
||||
<MobileFormattingToolbarController
|
||||
formattingToolbar={formattingToolbar}
|
||||
/>
|
||||
@@ -133,7 +133,6 @@ const MobileFormattingToolbarController = ({
|
||||
|
||||
return (
|
||||
<Box
|
||||
$margin="auto"
|
||||
$position="absolute"
|
||||
$css={`
|
||||
& > div {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type ScreenSize = 'small-mobile' | 'mobile' | 'tablet' | 'desktop';
|
||||
export type InputMethod = 'touch' | 'mouse' | 'unknown';
|
||||
|
||||
export interface UseResponsiveStore {
|
||||
isMobile: boolean;
|
||||
@@ -10,7 +11,11 @@ export interface UseResponsiveStore {
|
||||
screenWidth: number;
|
||||
setScreenSize: (size: ScreenSize) => void;
|
||||
isDesktop: boolean;
|
||||
isTouchCapable: boolean;
|
||||
isInputTouch: boolean;
|
||||
inputMethod: InputMethod;
|
||||
initializeResizeListener: () => () => void;
|
||||
initializeInputDetection: () => () => void;
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
@@ -20,6 +25,9 @@ const initialState = {
|
||||
isDesktop: false,
|
||||
screenSize: 'desktop' as ScreenSize,
|
||||
screenWidth: 0,
|
||||
isTouchCapable: false,
|
||||
isInputTouch: false,
|
||||
inputMethod: 'unknown' as InputMethod,
|
||||
};
|
||||
|
||||
export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
|
||||
@@ -29,6 +37,9 @@ export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
|
||||
isTablet: initialState.isTablet,
|
||||
screenSize: initialState.screenSize,
|
||||
screenWidth: initialState.screenWidth,
|
||||
isTouchCapable: initialState.isTouchCapable,
|
||||
isInputTouch: initialState.isInputTouch,
|
||||
inputMethod: initialState.inputMethod,
|
||||
setScreenSize: (size: ScreenSize) => set(() => ({ screenSize: size })),
|
||||
initializeResizeListener: () => {
|
||||
const resizeHandler = () => {
|
||||
@@ -84,4 +95,32 @@ export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
|
||||
window.removeEventListener('resize', debouncedResizeHandler);
|
||||
};
|
||||
},
|
||||
initializeInputDetection: () => {
|
||||
// Detect if device has touch capability
|
||||
const isTouchCapable =
|
||||
'ontouchstart' in window ||
|
||||
navigator.maxTouchPoints > 0 ||
|
||||
// @ts-ignore - for older browsers
|
||||
navigator.msMaxTouchPoints > 0;
|
||||
|
||||
set({ isTouchCapable });
|
||||
|
||||
// Track actual input method being used
|
||||
const handleTouchStart = () => {
|
||||
set({ inputMethod: 'touch', isInputTouch: true });
|
||||
};
|
||||
|
||||
const handleMouseMove = () => {
|
||||
set({ inputMethod: 'mouse', isInputTouch: false });
|
||||
};
|
||||
|
||||
// Listen for first interaction to determine input method
|
||||
window.addEventListener('touchstart', handleTouchStart, { once: false });
|
||||
window.addEventListener('mousemove', handleMouseMove, { once: false });
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('touchstart', handleTouchStart);
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user