@therealtinhtute/baroiplayer 1.0.4 → 1.0.6
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.js +1 -1
- package/dist/src/components/media-player-controls.tsx +1 -1
- package/dist/src/components/media-player-error.js +2 -2
- package/dist/src/components/media-player-error.tsx +2 -2
- package/dist/src/components/media-player-fullscreen.js +2 -2
- package/dist/src/components/media-player-fullscreen.tsx +2 -2
- package/dist/src/components/media-player-play.js +2 -2
- package/dist/src/components/media-player-play.tsx +2 -2
- package/dist/src/components/media-player-root.js +1 -1
- package/dist/src/components/media-player-root.tsx +1 -1
- package/dist/src/components/media-player-seek.js +1 -1
- package/dist/src/components/media-player-seek.tsx +1 -1
- package/dist/src/components/media-player-tooltip.js +1 -1
- package/dist/src/components/media-player-tooltip.tsx +1 -1
- package/dist/src/components/media-player-volume.js +2 -2
- package/dist/src/components/media-player-volume.tsx +3 -3
- package/dist/src/components/react-player-wrapper.js +5 -5
- package/dist/src/components/react-player-wrapper.tsx +5 -5
- 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/dist/src/hooks/use-touch-gestures.ts +1 -1
- package/package.json +22 -2
- package/src/components/media-player-controls.js +1 -1
- package/src/components/media-player-controls.tsx +1 -1
- package/src/components/media-player-error.js +2 -2
- package/src/components/media-player-error.tsx +2 -2
- package/src/components/media-player-fullscreen.js +2 -2
- package/src/components/media-player-fullscreen.tsx +2 -2
- package/src/components/media-player-play.js +2 -2
- package/src/components/media-player-play.tsx +2 -2
- package/src/components/media-player-root.js +1 -1
- package/src/components/media-player-root.tsx +1 -1
- package/src/components/media-player-seek.js +1 -1
- package/src/components/media-player-seek.tsx +1 -1
- package/src/components/media-player-tooltip.js +1 -1
- package/src/components/media-player-tooltip.tsx +1 -1
- package/src/components/media-player-volume.js +2 -2
- package/src/components/media-player-volume.tsx +3 -3
- package/src/components/react-player-wrapper.js +5 -5
- package/src/components/react-player-wrapper.tsx +5 -5
- 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
- package/src/hooks/use-touch-gestures.ts +1 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface IconProps {
|
|
4
|
+
className?: string;
|
|
5
|
+
size?: number | string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Button Icons
|
|
9
|
+
export const PlayIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
10
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
11
|
+
<polygon points="5 3 19 12 5 21 5 3"></polygon>
|
|
12
|
+
</svg>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export const PauseIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
16
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
17
|
+
<rect x="6" y="4" width="4" height="16"></rect>
|
|
18
|
+
<rect x="14" y="4" width="4" height="16"></rect>
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export const VolumeIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
23
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
24
|
+
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
|
|
25
|
+
<path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path>
|
|
26
|
+
</svg>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export const Volume1Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
30
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
31
|
+
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
|
|
32
|
+
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>
|
|
33
|
+
</svg>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export const Volume2Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
37
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
38
|
+
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
|
|
39
|
+
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>
|
|
40
|
+
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"></path>
|
|
41
|
+
</svg>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export const VolumeXIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
45
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
46
|
+
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
|
|
47
|
+
<line x1="23" y1="9" x2="17" y2="15"></line>
|
|
48
|
+
<line x1="17" y1="9" x2="23" y2="15"></line>
|
|
49
|
+
</svg>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
export const MuteIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
53
|
+
<VolumeXIcon className={className} size={size} />
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Fullscreen Icons
|
|
57
|
+
export const Maximize2Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
58
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
59
|
+
<polyline points="15 3 21 3 21 9"></polyline>
|
|
60
|
+
<polyline points="9 21 3 21 3 15"></polyline>
|
|
61
|
+
<line x1="21" y1="3" x2="14" y2="10"></line>
|
|
62
|
+
<line x1="3" y1="21" x2="10" y2="14"></line>
|
|
63
|
+
</svg>
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
export const Minimize2Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
67
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
68
|
+
<polyline points="4 14 10 14 10 20"></polyline>
|
|
69
|
+
<polyline points="20 10 14 10 14 4"></polyline>
|
|
70
|
+
<line x1="14" y1="10" x2="21" y2="3"></line>
|
|
71
|
+
<line x1="3" y1="21" x2="10" y2="14"></line>
|
|
72
|
+
</svg>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
export const FullscreenIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
76
|
+
<Maximize2Icon className={className} size={size} />
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
export const ExitFullscreenIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
80
|
+
<Minimize2Icon className={className} size={size} />
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Control Icons
|
|
84
|
+
export const Rewind10Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
85
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
86
|
+
<polygon points="1 4 1 10 7 7"></polygon>
|
|
87
|
+
<polygon points="15 4 15 10 21 7"></polygon>
|
|
88
|
+
<path d="M7 4a9 9 0 0 1 9 9"></path>
|
|
89
|
+
</svg>
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
export const Forward10Icon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
93
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
94
|
+
<polygon points="5 4 5 10 11 7"></polygon>
|
|
95
|
+
<polygon points="19 4 19 10 13 7"></polygon>
|
|
96
|
+
<path d="M17 20a9 9 0 0 1-9-9"></path>
|
|
97
|
+
</svg>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
export const SpeedIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
101
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
102
|
+
<path d="M3 12h18"></path>
|
|
103
|
+
<path d="M3 6h18"></path>
|
|
104
|
+
<path d="M3 18h18"></path>
|
|
105
|
+
</svg>
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
export const QualityIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
109
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
110
|
+
<path d="M12 2L2 7l10 5 10-5-10-5z"></path>
|
|
111
|
+
<path d="M2 17l10 5 10-5"></path>
|
|
112
|
+
<path d="M2 12l10 5 10-5"></path>
|
|
113
|
+
</svg>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
export const SubtitlesIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
117
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
118
|
+
<rect x="3" y="5" width="18" height="14" rx="2" ry="2"></rect>
|
|
119
|
+
<line x1="8" y1="10" x2="16" y2="10"></line>
|
|
120
|
+
<line x1="8" y1="14" x2="12" y2="14"></line>
|
|
121
|
+
</svg>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
export const PictureInPictureIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
125
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
126
|
+
<path d="M8 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"></path>
|
|
127
|
+
<rect x="10" y="8" width="8" height="8" rx="1"></rect>
|
|
128
|
+
</svg>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
export const DownloadIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
132
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
133
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
|
134
|
+
<polyline points="7 10 12 15 17 10"></polyline>
|
|
135
|
+
<line x1="12" y1="15" x2="12" y2="3"></line>
|
|
136
|
+
</svg>
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
export const ShareIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
140
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
141
|
+
<circle cx="18" cy="5" r="3"></circle>
|
|
142
|
+
<circle cx="6" cy="12" r="3"></circle>
|
|
143
|
+
<circle cx="18" cy="19" r="3"></circle>
|
|
144
|
+
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
|
|
145
|
+
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
|
|
146
|
+
</svg>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
export const LoadingIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
150
|
+
<svg className={className + ' animate-spin'} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
151
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56"></path>
|
|
152
|
+
</svg>
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// Error Icons
|
|
156
|
+
export const AlertTriangleIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
157
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
158
|
+
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
|
|
159
|
+
<line x1="12" y1="9" x2="12" y2="13"></line>
|
|
160
|
+
<line x1="12" y1="17" x2="12.01" y2="17"></line>
|
|
161
|
+
</svg>
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
export const RefreshCcwIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
165
|
+
<svg className={className} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
166
|
+
<polyline points="1 4 1 10 7 10"></polyline>
|
|
167
|
+
<polyline points="23 20 23 14 17 14"></polyline>
|
|
168
|
+
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>
|
|
169
|
+
</svg>
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
export const RotateCcwIcon: React.FC<IconProps> = ({ className = '', size = 16 }) => (
|
|
173
|
+
<RefreshCcwIcon className={className} size={size} />
|
|
174
|
+
);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import { cn } from '../lib/utils';
|
|
3
|
+
|
|
4
|
+
export interface SliderProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
5
|
+
min?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
step?: number;
|
|
8
|
+
value?: number[];
|
|
9
|
+
onValueChange?: (value: number[]) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const Slider = forwardRef<HTMLInputElement, SliderProps>(
|
|
13
|
+
({ className, min = 0, max = 100, step = 1, value = [0], onValueChange, ...props }, ref) => {
|
|
14
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
15
|
+
const newValue = [Number(e.target.value)];
|
|
16
|
+
onValueChange?.(newValue);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className={cn('relative flex w-full touch-none select-none items-center', className)}>
|
|
21
|
+
<div className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
|
22
|
+
<div
|
|
23
|
+
className="absolute h-full bg-primary"
|
|
24
|
+
style={{
|
|
25
|
+
left: '0%',
|
|
26
|
+
right: `${100 - ((value[0] - min) / (max - min)) * 100}%`
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
<input
|
|
31
|
+
type="range"
|
|
32
|
+
min={min}
|
|
33
|
+
max={max}
|
|
34
|
+
step={step}
|
|
35
|
+
value={value[0]}
|
|
36
|
+
onChange={handleChange}
|
|
37
|
+
className="absolute h-2 w-full cursor-pointer opacity-0 outline-none"
|
|
38
|
+
ref={ref}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
<div
|
|
42
|
+
className="absolute h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
|
43
|
+
style={{
|
|
44
|
+
left: `${((value[0] - min) / (max - min)) * 100}%`,
|
|
45
|
+
transform: 'translateX(-50%)'
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
Slider.displayName = 'Slider';
|
|
54
|
+
|
|
55
|
+
export { Slider };
|
|
@@ -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'],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@therealtinhtute/baroiplayer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A customizable media player component library for React",
|
|
@@ -84,11 +84,31 @@
|
|
|
84
84
|
"peerDependencies": {
|
|
85
85
|
"react": ">=18.0.0",
|
|
86
86
|
"react-dom": ">=18.0.0",
|
|
87
|
-
"tailwindcss": ">=4.0.0"
|
|
87
|
+
"tailwindcss": ">=4.0.0",
|
|
88
|
+
"video.js": ">=7.0.0",
|
|
89
|
+
"hls.js": ">=1.0.0",
|
|
90
|
+
"@videojs/http-streaming": ">=2.0.0",
|
|
91
|
+
"videojs-contrib-quality-levels": ">=2.0.0",
|
|
92
|
+
"videojs-hls-quality-selector": ">=2.0.0"
|
|
88
93
|
},
|
|
89
94
|
"peerDependenciesMeta": {
|
|
90
95
|
"tailwindcss": {
|
|
91
96
|
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"video.js": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"hls.js": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"@videojs/http-streaming": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"videojs-contrib-quality-levels": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"videojs-hls-quality-selector": {
|
|
111
|
+
"optional": true
|
|
92
112
|
}
|
|
93
113
|
},
|
|
94
114
|
"keywords": [
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useMediaSelector } from "../context/media-context";
|
|
5
|
-
import { useDeviceInfo, useContainerQuery } from "
|
|
5
|
+
import { useDeviceInfo, useContainerQuery } from "../hooks/use-mobile";
|
|
6
6
|
import { cn } from "../lib/utils";
|
|
7
7
|
export function MediaPlayerControls({ children, className, asChild = false, variant, adaptiveLayout = true, ...props }) {
|
|
8
8
|
const isFullscreen = useMediaSelector((state) => state.isFullscreen);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React from "react"
|
|
4
4
|
import { useMediaSelector } from "../context/media-context"
|
|
5
|
-
import { useDeviceInfo, useContainerQuery } from "
|
|
5
|
+
import { useDeviceInfo, useContainerQuery } from "../hooks/use-mobile"
|
|
6
6
|
import { cn } from "../lib/utils"
|
|
7
7
|
|
|
8
8
|
export interface MediaPlayerControlsProps {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Button } from "
|
|
4
|
-
import { AlertTriangleIcon, RefreshCcwIcon, RotateCcwIcon } from "
|
|
3
|
+
import { Button } from "../components/ui/button";
|
|
4
|
+
import { AlertTriangleIcon, RefreshCcwIcon, RotateCcwIcon } from "../components/ui/icons";
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { useMediaContext, useMediaSelector } from "../context/media-context";
|
|
7
7
|
import { cn } from "../lib/utils";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import { Button } from "
|
|
4
|
-
import { AlertTriangleIcon, RefreshCcwIcon, RotateCcwIcon } from "
|
|
3
|
+
import { Button } from "../components/ui/button"
|
|
4
|
+
import { AlertTriangleIcon, RefreshCcwIcon, RotateCcwIcon } from "../components/ui/icons"
|
|
5
5
|
import React from "react"
|
|
6
6
|
import { useMediaContext, useMediaSelector } from "../context/media-context"
|
|
7
7
|
import { cn } from "../lib/utils"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { Button } from "
|
|
4
|
-
import { Maximize2Icon, Minimize2Icon } from "
|
|
3
|
+
import { Button } from "../components/ui/button";
|
|
4
|
+
import { Maximize2Icon, Minimize2Icon } from "../components/ui/icons";
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { MediaActionTypes, useMediaContext, useMediaDispatch, useMediaSelector } from "../context/media-context";
|
|
7
7
|
import { MediaPlayerTooltip } from "./media-player-tooltip";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import { Button } from "
|
|
4
|
-
import { Maximize2Icon, Minimize2Icon } from "
|
|
3
|
+
import { Button } from "../components/ui/button"
|
|
4
|
+
import { Maximize2Icon, Minimize2Icon } from "../components/ui/icons"
|
|
5
5
|
import React from "react"
|
|
6
6
|
import {
|
|
7
7
|
MediaActionTypes,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { Button } from "
|
|
4
|
-
import { PauseIcon, PlayIcon } from "
|
|
3
|
+
import { Button } from "../components/ui/button";
|
|
4
|
+
import { PauseIcon, PlayIcon } from "../components/ui/icons";
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { MediaActionTypes, useMediaContext, useMediaDispatch, useMediaSelector } from "../context/media-context";
|
|
7
7
|
import { MediaPlayerTooltip } from "./media-player-tooltip";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import { Button } from "
|
|
4
|
-
import { PauseIcon, PlayIcon } from "
|
|
3
|
+
import { Button } from "../components/ui/button"
|
|
4
|
+
import { PauseIcon, PlayIcon } from "../components/ui/icons"
|
|
5
5
|
import React from "react"
|
|
6
6
|
import {
|
|
7
7
|
MediaActionTypes,
|
|
@@ -4,7 +4,7 @@ import React from "react";
|
|
|
4
4
|
import { MediaActionTypes, useMediaContext, useMediaDispatch, useMediaSelector } from "../context/media-context";
|
|
5
5
|
import { MediaProvider } from "../context/media-provider";
|
|
6
6
|
import { useMediaGestures } from "../hooks/use-touch-gestures";
|
|
7
|
-
import { useMobileOptimizations, useDeviceInfo } from "
|
|
7
|
+
import { useMobileOptimizations, useDeviceInfo } from "../hooks/use-mobile";
|
|
8
8
|
import { cn } from "../lib/utils";
|
|
9
9
|
// Utility function to compose refs
|
|
10
10
|
function composeRefs(...refs) {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "../context/media-context"
|
|
10
10
|
import { MediaProvider } from "../context/media-provider"
|
|
11
11
|
import { useMediaGestures } from "../hooks/use-touch-gestures"
|
|
12
|
-
import { useMobileOptimizations, useDeviceInfo } from "
|
|
12
|
+
import { useMobileOptimizations, useDeviceInfo } from "../hooks/use-mobile"
|
|
13
13
|
import { cn } from "../lib/utils"
|
|
14
14
|
|
|
15
15
|
// Utility function to compose refs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { Slider } from "
|
|
3
|
+
import { Slider } from "../components/ui/slider";
|
|
4
4
|
import React from "react";
|
|
5
5
|
import { MediaActionTypes, useMediaContext, useMediaDispatch, useMediaSelector } from "../context/media-context";
|
|
6
6
|
import { formatTime } from "../lib/format-time";
|