@therealtinhtute/baroiplayer 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +38 -1
- package/dist/package.json +22 -2
- package/dist/src/components/media-player-controls.tsx +1 -1
- package/dist/src/components/media-player-root.tsx +1 -1
- package/dist/src/components/ui/button.tsx +49 -0
- package/dist/src/components/ui/dropdown-menu.tsx +137 -0
- package/dist/src/components/ui/icons.tsx +174 -0
- package/dist/src/components/ui/slider.tsx +55 -0
- package/dist/src/components/ui/tooltip.tsx +131 -0
- package/dist/src/hooks/use-mobile.ts +125 -0
- package/dist/src/hooks/use-touch-gestures.js +1 -1
- package/package.json +22 -2
- package/src/components/media-player-controls.tsx +1 -1
- package/src/components/media-player-root.tsx +1 -1
- package/src/components/ui/button.tsx +49 -0
- package/src/components/ui/dropdown-menu.tsx +137 -0
- package/src/components/ui/icons.tsx +174 -0
- package/src/components/ui/slider.tsx +55 -0
- package/src/components/ui/tooltip.tsx +131 -0
- package/src/hooks/use-mobile.ts +125 -0
- package/src/hooks/use-touch-gestures.js +1 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect, forwardRef } from 'react';
|
|
2
|
+
import { cn } from '../lib/utils';
|
|
3
|
+
|
|
4
|
+
interface TooltipProviderProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
delayDuration?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const TooltipProvider: React.FC<TooltipProviderProps> = ({ children, delayDuration = 0 }) => {
|
|
10
|
+
return <>{children}</>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
interface TooltipProps {
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
content: React.ReactNode;
|
|
16
|
+
delayDuration?: number;
|
|
17
|
+
sideOffset?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const Tooltip: React.FC<TooltipProps> = ({ children, content, delayDuration = 0, sideOffset = 0 }) => {
|
|
21
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
22
|
+
const [position, setPosition] = useState({ top: 0, left: 0 });
|
|
23
|
+
const triggerRef = useRef<HTMLDivElement>(null);
|
|
24
|
+
const timeoutRef = useRef<NodeJS.Timeout>();
|
|
25
|
+
|
|
26
|
+
const updatePosition = () => {
|
|
27
|
+
if (triggerRef.current) {
|
|
28
|
+
const rect = triggerRef.current.getBoundingClientRect();
|
|
29
|
+
setPosition({
|
|
30
|
+
top: rect.bottom + sideOffset,
|
|
31
|
+
left: rect.left + rect.width / 2
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const showTooltip = () => {
|
|
37
|
+
if (timeoutRef.current) {
|
|
38
|
+
clearTimeout(timeoutRef.current);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (delayDuration > 0) {
|
|
42
|
+
timeoutRef.current = setTimeout(() => {
|
|
43
|
+
setIsOpen(true);
|
|
44
|
+
updatePosition();
|
|
45
|
+
}, delayDuration);
|
|
46
|
+
} else {
|
|
47
|
+
setIsOpen(true);
|
|
48
|
+
updatePosition();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const hideTooltip = () => {
|
|
53
|
+
if (timeoutRef.current) {
|
|
54
|
+
clearTimeout(timeoutRef.current);
|
|
55
|
+
}
|
|
56
|
+
setIsOpen(false);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (isOpen) {
|
|
61
|
+
updatePosition();
|
|
62
|
+
window.addEventListener('scroll', updatePosition);
|
|
63
|
+
window.addEventListener('resize', updatePosition);
|
|
64
|
+
|
|
65
|
+
return () => {
|
|
66
|
+
window.removeEventListener('scroll', updatePosition);
|
|
67
|
+
window.removeEventListener('resize', updatePosition);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}, [isOpen]);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
return () => {
|
|
74
|
+
if (timeoutRef.current) {
|
|
75
|
+
clearTimeout(timeoutRef.current);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}, []);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<TooltipProvider delayDuration={delayDuration}>
|
|
82
|
+
<div
|
|
83
|
+
ref={triggerRef}
|
|
84
|
+
onMouseEnter={showTooltip}
|
|
85
|
+
onMouseLeave={hideTooltip}
|
|
86
|
+
onFocus={showTooltip}
|
|
87
|
+
onBlur={hideTooltip}
|
|
88
|
+
>
|
|
89
|
+
{children}
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
{isOpen && (
|
|
93
|
+
<div
|
|
94
|
+
className={cn(
|
|
95
|
+
'fixed z-50 px-3 py-2 text-sm bg-popover text-popover-foreground shadow-md rounded-md border',
|
|
96
|
+
'max-w-xs break-words pointer-events-none'
|
|
97
|
+
)}
|
|
98
|
+
style={{
|
|
99
|
+
top: `${position.top}px`,
|
|
100
|
+
left: `${position.left}px`,
|
|
101
|
+
transform: 'translateX(-50%)'
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
{content}
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
</TooltipProvider>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
interface TooltipContentProps {
|
|
112
|
+
children: React.ReactNode;
|
|
113
|
+
className?: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const TooltipContent: React.FC<TooltipContentProps> = ({ children, className }) => {
|
|
117
|
+
return <div className={cn('z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md', className)}>
|
|
118
|
+
{children}
|
|
119
|
+
</div>;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
interface TooltipTriggerProps {
|
|
123
|
+
children: React.ReactNode;
|
|
124
|
+
asChild?: boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const TooltipTrigger: React.FC<TooltipTriggerProps> = ({ children, asChild = false }) => {
|
|
128
|
+
return <>{children}</>;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
interface DeviceInfo {
|
|
4
|
+
isMobile: boolean;
|
|
5
|
+
isTablet: boolean;
|
|
6
|
+
isDesktop: boolean;
|
|
7
|
+
userAgent: string;
|
|
8
|
+
screenWidth: number;
|
|
9
|
+
screenHeight: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useDeviceInfo(): DeviceInfo {
|
|
13
|
+
const [deviceInfo, setDeviceInfo] = useState<DeviceInfo>(() => {
|
|
14
|
+
if (typeof window === 'undefined') {
|
|
15
|
+
return {
|
|
16
|
+
isMobile: false,
|
|
17
|
+
isTablet: false,
|
|
18
|
+
isDesktop: true,
|
|
19
|
+
userAgent: '',
|
|
20
|
+
screenWidth: 1920,
|
|
21
|
+
screenHeight: 1080
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const userAgent = navigator.userAgent;
|
|
26
|
+
const screenWidth = window.innerWidth;
|
|
27
|
+
const screenHeight = window.innerHeight;
|
|
28
|
+
|
|
29
|
+
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent) && screenWidth < 768;
|
|
30
|
+
const isTablet = /iPad|Android/i.test(userAgent) && screenWidth >= 768 && screenWidth < 1024;
|
|
31
|
+
const isDesktop = !isMobile && !isTablet;
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
isMobile,
|
|
35
|
+
isTablet,
|
|
36
|
+
isDesktop,
|
|
37
|
+
userAgent,
|
|
38
|
+
screenWidth,
|
|
39
|
+
screenHeight
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (typeof window === 'undefined') return;
|
|
45
|
+
|
|
46
|
+
const handleResize = () => {
|
|
47
|
+
const userAgent = navigator.userAgent;
|
|
48
|
+
const screenWidth = window.innerWidth;
|
|
49
|
+
const screenHeight = window.innerHeight;
|
|
50
|
+
|
|
51
|
+
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent) && screenWidth < 768;
|
|
52
|
+
const isTablet = /iPad|Android/i.test(userAgent) && screenWidth >= 768 && screenWidth < 1024;
|
|
53
|
+
const isDesktop = !isMobile && !isTablet;
|
|
54
|
+
|
|
55
|
+
setDeviceInfo({
|
|
56
|
+
isMobile,
|
|
57
|
+
isTablet,
|
|
58
|
+
isDesktop,
|
|
59
|
+
userAgent,
|
|
60
|
+
screenWidth,
|
|
61
|
+
screenHeight
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
window.addEventListener('resize', handleResize);
|
|
66
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
return deviceInfo;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function useContainerQuery(): boolean {
|
|
73
|
+
const [isSmall, setIsSmall] = useState(false);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (typeof window === 'undefined') return;
|
|
77
|
+
|
|
78
|
+
const checkSize = () => {
|
|
79
|
+
setIsSmall(window.innerWidth < 640);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
checkSize();
|
|
83
|
+
window.addEventListener('resize', checkSize);
|
|
84
|
+
return () => window.removeEventListener('resize', checkSize);
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
return isSmall;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function useMobileOptimizations() {
|
|
91
|
+
const deviceInfo = useDeviceInfo();
|
|
92
|
+
const [isTouch, setIsTouch] = useState(false);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
setIsTouch('ontouchstart' in window || navigator.maxTouchPoints > 0);
|
|
96
|
+
}, []);
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
...deviceInfo,
|
|
100
|
+
isTouch,
|
|
101
|
+
preferReducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)').matches,
|
|
102
|
+
prefersDarkScheme: window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function useHapticFeedback() {
|
|
107
|
+
const deviceInfo = useDeviceInfo();
|
|
108
|
+
|
|
109
|
+
const triggerHaptic = (type: 'light' | 'medium' | 'heavy' = 'light') => {
|
|
110
|
+
if (!deviceInfo.isMobile || !('vibrate' in navigator)) return;
|
|
111
|
+
|
|
112
|
+
const patterns = {
|
|
113
|
+
light: [10],
|
|
114
|
+
medium: [20],
|
|
115
|
+
heavy: [50]
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
navigator.vibrate(patterns[type]);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
triggerHaptic,
|
|
123
|
+
supported: deviceInfo.isMobile && 'vibrate' in navigator
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import { useDeviceInfo, useHapticFeedback } from "
|
|
3
|
+
import { useDeviceInfo, useHapticFeedback } from "./use-mobile";
|
|
4
4
|
// Default configuration
|
|
5
5
|
const defaultConfig = {
|
|
6
6
|
enabledGestures: ['tap', 'double-tap', 'long-press', 'swipe-left', 'swipe-right', 'swipe-up', 'swipe-down', 'pinch-in', 'pinch-out'],
|