181 lines
3.9 KiB
TypeScript
181 lines
3.9 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
|
|
/**
|
|
* Device type enum
|
|
*/
|
|
export type DeviceType = 'mobile' | 'tablet' | 'desktop';
|
|
|
|
/**
|
|
* Device detection result
|
|
*/
|
|
export interface DeviceInfo {
|
|
isMobile: boolean;
|
|
isTablet: boolean;
|
|
isDesktop: boolean;
|
|
deviceType: DeviceType;
|
|
isTouchDevice: boolean;
|
|
screenWidth: number;
|
|
screenHeight: number;
|
|
}
|
|
|
|
/**
|
|
* Breakpoints for device detection
|
|
*/
|
|
const BREAKPOINTS = {
|
|
mobile: 768, // < 768px
|
|
tablet: 1024, // 768px - 1024px
|
|
desktop: 1024, // >= 1024px
|
|
};
|
|
|
|
/**
|
|
* Detect device type based on User Agent
|
|
*/
|
|
const detectDeviceFromUserAgent = (): DeviceType => {
|
|
const ua = navigator.userAgent.toLowerCase();
|
|
|
|
// Mobile devices
|
|
if (/(android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini)/i.test(ua)) {
|
|
// Distinguish between tablet and mobile
|
|
if (/(ipad|tablet|playbook|silk)|(android(?!.*mobile))/i.test(ua)) {
|
|
return 'tablet';
|
|
}
|
|
return 'mobile';
|
|
}
|
|
|
|
return 'desktop';
|
|
};
|
|
|
|
/**
|
|
* Detect device type based on screen width
|
|
*/
|
|
const detectDeviceFromScreenWidth = (width: number): DeviceType => {
|
|
if (width < BREAKPOINTS.mobile) {
|
|
return 'mobile';
|
|
} else if (width < BREAKPOINTS.desktop) {
|
|
return 'tablet';
|
|
}
|
|
return 'desktop';
|
|
};
|
|
|
|
/**
|
|
* Check if device supports touch
|
|
*/
|
|
const isTouchDevice = (): boolean => {
|
|
return (
|
|
'ontouchstart' in window ||
|
|
navigator.maxTouchPoints > 0 ||
|
|
// @ts-ignore - for older browsers
|
|
navigator.msMaxTouchPoints > 0
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Get current device information
|
|
*/
|
|
const getDeviceInfo = (): DeviceInfo => {
|
|
const width = window.innerWidth;
|
|
const height = window.innerHeight;
|
|
|
|
// Combine User Agent and screen width detection
|
|
const uaDevice = detectDeviceFromUserAgent();
|
|
const screenDevice = detectDeviceFromScreenWidth(width);
|
|
|
|
// Prefer User Agent detection, but fall back to screen width
|
|
const deviceType = uaDevice !== 'desktop' ? uaDevice : screenDevice;
|
|
|
|
return {
|
|
isMobile: deviceType === 'mobile',
|
|
isTablet: deviceType === 'tablet',
|
|
isDesktop: deviceType === 'desktop',
|
|
deviceType,
|
|
isTouchDevice: isTouchDevice(),
|
|
screenWidth: width,
|
|
screenHeight: height,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Custom hook to detect device type and screen size
|
|
*
|
|
* @returns DeviceInfo object with device detection results
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const { isMobile, isDesktop, deviceType } = useDevice();
|
|
*
|
|
* return (
|
|
* <div>
|
|
* {isMobile && <MobileView />}
|
|
* {isDesktop && <DesktopView />}
|
|
* </div>
|
|
* );
|
|
* ```
|
|
*/
|
|
export const useDevice = (): DeviceInfo => {
|
|
const [deviceInfo, setDeviceInfo] = useState<DeviceInfo>(getDeviceInfo);
|
|
|
|
useEffect(() => {
|
|
const handleResize = () => {
|
|
setDeviceInfo(getDeviceInfo());
|
|
};
|
|
|
|
// Listen for window resize
|
|
window.addEventListener('resize', handleResize);
|
|
|
|
// Listen for orientation change (mobile devices)
|
|
window.addEventListener('orientationchange', handleResize);
|
|
|
|
return () => {
|
|
window.removeEventListener('resize', handleResize);
|
|
window.removeEventListener('orientationchange', handleResize);
|
|
};
|
|
}, []);
|
|
|
|
return deviceInfo;
|
|
};
|
|
|
|
/**
|
|
* Hook to check if current device is mobile
|
|
*
|
|
* @returns boolean indicating if device is mobile
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const isMobile = useIsMobile();
|
|
* ```
|
|
*/
|
|
export const useIsMobile = (): boolean => {
|
|
const { isMobile } = useDevice();
|
|
return isMobile;
|
|
};
|
|
|
|
/**
|
|
* Hook to check if current device is desktop
|
|
*
|
|
* @returns boolean indicating if device is desktop
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const isDesktop = useIsDesktop();
|
|
* ```
|
|
*/
|
|
export const useIsDesktop = (): boolean => {
|
|
const { isDesktop } = useDevice();
|
|
return isDesktop;
|
|
};
|
|
|
|
/**
|
|
* Hook to check if current device is tablet
|
|
*
|
|
* @returns boolean indicating if device is tablet
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const isTablet = useIsTablet();
|
|
* ```
|
|
*/
|
|
export const useIsTablet = (): boolean => {
|
|
const { isTablet } = useDevice();
|
|
return isTablet;
|
|
};
|