xertica-ui 1.2.4 → 1.2.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/components/CodeBlock.tsx +9 -9
- package/components/index.ts +2 -1
- package/components/media/AudioPlayer.tsx +79 -79
- package/components/media/VideoPlayer.tsx +36 -36
- package/components/ui/drawer.tsx +12 -0
- package/components/ui/index.ts +132 -132
- package/components/ui/input.tsx +1 -1
- package/components/ui/map.tsx +1 -1
- package/components/ui/page-header.tsx +42 -12
- package/components/ui/sheet.tsx +5 -4
- package/components/ui/tree-view.tsx +1 -1
- package/dist/components/AssistenteXertica.d.ts +8 -0
- package/dist/components/AudioPlayer.d.ts +12 -0
- package/dist/components/CodeBlock.d.ts +8 -0
- package/dist/components/DocumentEditor.d.ts +7 -0
- package/dist/components/ForgotPasswordPage.d.ts +1 -0
- package/dist/components/FormattedDocument.d.ts +7 -0
- package/dist/components/HomeContent.d.ts +8 -0
- package/dist/components/HomePage.d.ts +8 -0
- package/dist/components/LanguageSelector.d.ts +7 -0
- package/dist/components/LoginPage.d.ts +5 -0
- package/dist/components/MarkdownMessage.d.ts +6 -0
- package/dist/components/ModernChatInput.d.ts +14 -0
- package/dist/components/PodcastPlayer.d.ts +15 -0
- package/dist/components/ResetPasswordPage.d.ts +1 -0
- package/dist/components/Sidebar.d.ts +14 -0
- package/dist/components/TemplateContent.d.ts +8 -0
- package/dist/components/TemplatePage.d.ts +8 -0
- package/dist/components/ThemeToggle.d.ts +8 -0
- package/dist/components/VerifyEmailPage.d.ts +1 -0
- package/dist/components/XerticaLogo.d.ts +7 -0
- package/dist/components/XerticaOrbe.d.ts +6 -0
- package/dist/components/XerticaXLogo.d.ts +7 -0
- package/dist/components/figma/ImageWithFallback.d.ts +2 -0
- package/dist/components/index.d.ts +15 -0
- package/dist/components/media/AudioPlayer.d.ts +9 -0
- package/dist/components/media/FloatingMediaWrapper.d.ts +14 -0
- package/dist/components/media/VideoPlayer.d.ts +8 -0
- package/dist/components/ui/accordion.d.ts +7 -0
- package/dist/components/ui/alert-dialog.d.ts +14 -0
- package/dist/components/ui/alert.d.ts +9 -0
- package/dist/components/ui/aspect-ratio.d.ts +3 -0
- package/dist/components/ui/avatar.d.ts +8 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/breadcrumb.d.ts +11 -0
- package/dist/components/ui/button.d.ts +14 -0
- package/dist/components/ui/calendar.d.ts +8 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/carousel.d.ts +19 -0
- package/dist/components/ui/chart.d.ts +40 -0
- package/dist/components/ui/checkbox.d.ts +4 -0
- package/dist/components/ui/collapsible.d.ts +5 -0
- package/dist/components/ui/command.d.ts +16 -0
- package/dist/components/ui/context-menu.d.ts +25 -0
- package/dist/components/ui/dialog.d.ts +15 -0
- package/dist/components/ui/drawer.d.ts +14 -0
- package/dist/components/ui/dropdown-menu.d.ts +25 -0
- package/dist/components/ui/empty.d.ts +8 -0
- package/dist/components/ui/file-upload.d.ts +10 -0
- package/dist/components/ui/form.d.ts +24 -0
- package/dist/components/ui/google-maps-loader.d.ts +30 -0
- package/dist/components/ui/hover-card.d.ts +6 -0
- package/dist/components/ui/index.d.ts +81 -0
- package/dist/components/ui/input-otp.d.ts +11 -0
- package/dist/components/ui/input.d.ts +6 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/map-config.d.ts +12 -0
- package/dist/components/ui/map-layers.d.ts +48 -0
- package/dist/components/ui/map.d.ts +53 -0
- package/dist/components/ui/map.exports.d.ts +25 -0
- package/dist/components/ui/menubar.d.ts +26 -0
- package/dist/components/ui/navigation-menu.d.ts +14 -0
- package/dist/components/ui/notification-badge.d.ts +11 -0
- package/dist/components/ui/page-header.d.ts +58 -0
- package/dist/components/ui/pagination.d.ts +13 -0
- package/dist/components/ui/popover.d.ts +7 -0
- package/dist/components/ui/progress.d.ts +4 -0
- package/dist/components/ui/radio-group.d.ts +5 -0
- package/dist/components/ui/rating.d.ts +12 -0
- package/dist/components/ui/resizable.d.ts +27 -0
- package/dist/components/ui/route-map.d.ts +26 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/search.d.ts +8 -0
- package/dist/components/ui/select.d.ts +15 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/sheet.d.ts +14 -0
- package/dist/components/ui/sidebar.d.ts +69 -0
- package/dist/components/ui/simple-map.d.ts +51 -0
- package/dist/components/ui/skeleton.d.ts +2 -0
- package/dist/components/ui/slider.d.ts +4 -0
- package/dist/components/ui/sonner.d.ts +23 -0
- package/dist/components/ui/stats-card.d.ts +14 -0
- package/dist/components/ui/stepper.d.ts +17 -0
- package/dist/components/ui/switch.d.ts +4 -0
- package/dist/components/ui/table.d.ts +10 -0
- package/dist/components/ui/tabs.d.ts +7 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/components/ui/timeline.d.ts +12 -0
- package/dist/components/ui/toggle-group.d.ts +12 -0
- package/dist/components/ui/toggle.d.ts +12 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/components/ui/tree-view.d.ts +15 -0
- package/dist/components/ui/use-mobile.d.ts +2 -0
- package/dist/components/ui/utils.d.ts +2 -0
- package/dist/components/ui/xertica-assistant.d.ts +196 -0
- package/dist/contexts/ApiKeyContext.d.ts +15 -0
- package/dist/contexts/AssistenteContext.d.ts +71 -0
- package/dist/contexts/LanguageContext.d.ts +11 -0
- package/dist/contexts/ThemeContext.d.ts +13 -0
- package/dist/index.es.js +79983 -0
- package/dist/index.umd.js +80002 -0
- package/dist/routes.d.ts +10 -0
- package/dist/utils/gemini.d.ts +8 -0
- package/dist/xertica-ui.css +4470 -0
- package/package.json +18 -4
- package/vite.config.ts +14 -5
package/components/CodeBlock.tsx
CHANGED
|
@@ -137,11 +137,11 @@ interface CodeBlockProps {
|
|
|
137
137
|
showLineNumbers?: boolean;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
export const CodeBlock = ({
|
|
141
|
-
code,
|
|
142
|
-
language = 'tsx',
|
|
140
|
+
export const CodeBlock = ({
|
|
141
|
+
code,
|
|
142
|
+
language = 'tsx',
|
|
143
143
|
filename,
|
|
144
|
-
showLineNumbers = false
|
|
144
|
+
showLineNumbers = false
|
|
145
145
|
}: CodeBlockProps) => {
|
|
146
146
|
const [copied, setCopied] = useState(false);
|
|
147
147
|
|
|
@@ -161,7 +161,7 @@ export const CodeBlock = ({
|
|
|
161
161
|
} else {
|
|
162
162
|
throw new Error('Clipboard API not available');
|
|
163
163
|
}
|
|
164
|
-
|
|
164
|
+
|
|
165
165
|
setCopied(true);
|
|
166
166
|
setTimeout(() => setCopied(false), 2000);
|
|
167
167
|
} catch (err) {
|
|
@@ -175,10 +175,10 @@ export const CodeBlock = ({
|
|
|
175
175
|
document.body.appendChild(textArea);
|
|
176
176
|
textArea.focus();
|
|
177
177
|
textArea.select();
|
|
178
|
-
|
|
178
|
+
|
|
179
179
|
const successful = document.execCommand('copy');
|
|
180
180
|
document.body.removeChild(textArea);
|
|
181
|
-
|
|
181
|
+
|
|
182
182
|
if (successful) {
|
|
183
183
|
setCopied(true);
|
|
184
184
|
setTimeout(() => setCopied(false), 2000);
|
|
@@ -202,7 +202,7 @@ export const CodeBlock = ({
|
|
|
202
202
|
<span className="text-xs text-muted-foreground uppercase">{language}</span>
|
|
203
203
|
</div>
|
|
204
204
|
)}
|
|
205
|
-
|
|
205
|
+
|
|
206
206
|
<div className="relative">
|
|
207
207
|
<Button
|
|
208
208
|
variant="ghost"
|
|
@@ -220,7 +220,7 @@ export const CodeBlock = ({
|
|
|
220
220
|
<div className="overflow-x-auto w-full max-w-full">
|
|
221
221
|
<SyntaxHighlighter
|
|
222
222
|
language={language}
|
|
223
|
-
style={elegantTheme}
|
|
223
|
+
style={elegantTheme as any}
|
|
224
224
|
showLineNumbers={showLineNumbers}
|
|
225
225
|
wrapLines={true}
|
|
226
226
|
customStyle={{
|
package/components/index.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import '../index.css';
|
|
1
2
|
// ============================================================================
|
|
2
3
|
// Xertica Assistant Exports
|
|
3
4
|
// ============================================================================
|
|
4
5
|
|
|
5
6
|
export { XerticaAssistant } from './ui/xertica-assistant';
|
|
6
|
-
export type {
|
|
7
|
+
export type {
|
|
7
8
|
XerticaAssistantProps,
|
|
8
9
|
Message,
|
|
9
10
|
Conversation,
|
|
@@ -14,12 +14,12 @@ export interface AudioPlayerProps {
|
|
|
14
14
|
className?: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function AudioPlayer({
|
|
18
|
-
src,
|
|
17
|
+
export function AudioPlayer({
|
|
18
|
+
src,
|
|
19
19
|
title = "Audio",
|
|
20
20
|
artist,
|
|
21
21
|
autoPlay = false,
|
|
22
|
-
className
|
|
22
|
+
className
|
|
23
23
|
}: AudioPlayerProps) {
|
|
24
24
|
const audioRef = useRef<HTMLAudioElement>(null);
|
|
25
25
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -38,9 +38,9 @@ export function AudioPlayer({
|
|
|
38
38
|
if (audioRef.current) {
|
|
39
39
|
setCurrentTime(audioRef.current.currentTime);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
setIsFloating(floating);
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
if (!floating) {
|
|
45
45
|
setIsManualFloating(false);
|
|
46
46
|
if (containerRef.current) {
|
|
@@ -55,21 +55,21 @@ export function AudioPlayer({
|
|
|
55
55
|
if (audio) {
|
|
56
56
|
// If we are switching modes, restore time and play state
|
|
57
57
|
if (Math.abs(audio.currentTime - currentTime) > 0.5) {
|
|
58
|
-
|
|
58
|
+
audio.currentTime = currentTime;
|
|
59
59
|
}
|
|
60
60
|
if (isPlaying) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
const playPromise = audio.play();
|
|
62
|
+
if (playPromise !== undefined) {
|
|
63
|
+
playPromise.catch(error => {
|
|
64
|
+
// Auto-play might be blocked or interrupted
|
|
65
|
+
console.log("Playback interrupted during switch:", error);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
// We depend on isFloating to trigger this check when the DOM node is recreated
|
|
71
71
|
// But we also need to attach listeners to the new node
|
|
72
|
-
}, [isFloating]);
|
|
72
|
+
}, [isFloating]);
|
|
73
73
|
|
|
74
74
|
// Auto-float on scroll logic
|
|
75
75
|
useEffect(() => {
|
|
@@ -79,17 +79,17 @@ export function AudioPlayer({
|
|
|
79
79
|
const observer = new IntersectionObserver(
|
|
80
80
|
([entry]) => {
|
|
81
81
|
if (isPlaying && !entry.isIntersecting && !isFloating) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
// Sync time before floating
|
|
83
|
+
if (audioRef.current) setCurrentTime(audioRef.current.currentTime);
|
|
84
|
+
setIsFloating(true);
|
|
85
85
|
}
|
|
86
86
|
else if (entry.isIntersecting && isFloating && !isManualFloating) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
// Sync time before docking
|
|
88
|
+
if (audioRef.current) setCurrentTime(audioRef.current.currentTime);
|
|
89
|
+
handleSetFloating(false);
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
|
-
{ threshold: 0.2 }
|
|
92
|
+
{ threshold: 0.2 }
|
|
93
93
|
);
|
|
94
94
|
|
|
95
95
|
observer.observe(container);
|
|
@@ -161,7 +161,7 @@ export function AudioPlayer({
|
|
|
161
161
|
|
|
162
162
|
const PlayerControls = ({ isCompact = false }) => (
|
|
163
163
|
<div className={cn("flex items-center gap-3 w-full", isCompact ? "px-3 py-1" : "px-4 py-3")}>
|
|
164
|
-
<Button
|
|
164
|
+
<Button
|
|
165
165
|
onClick={togglePlay}
|
|
166
166
|
size="icon"
|
|
167
167
|
variant="outline"
|
|
@@ -174,70 +174,70 @@ export function AudioPlayer({
|
|
|
174
174
|
</Button>
|
|
175
175
|
|
|
176
176
|
<div className="flex-1 min-w-0 flex flex-col justify-center gap-1.5">
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
</div>
|
|
182
|
-
<div className="text-muted-foreground font-medium tabular-nums shrink-0 text-[10px] sm:text-xs">
|
|
183
|
-
{formatTime(currentTime)} / {formatTime(duration || 0)}
|
|
184
|
-
</div>
|
|
177
|
+
<div className="flex items-center justify-between text-xs leading-none">
|
|
178
|
+
<div className="flex items-center gap-2 truncate">
|
|
179
|
+
<span className="font-medium text-foreground truncate">{title}</span>
|
|
180
|
+
{artist && !isCompact && <span className="text-muted-foreground hidden sm:inline-block border-l pl-2 border-border truncate">{artist}</span>}
|
|
185
181
|
</div>
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
182
|
+
<div className="text-muted-foreground font-medium tabular-nums shrink-0 text-[10px] sm:text-xs">
|
|
183
|
+
{formatTime(currentTime)} / {formatTime(duration || 0)}
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<Slider
|
|
188
|
+
value={[currentTime]}
|
|
189
|
+
max={duration || 100}
|
|
190
|
+
step={1}
|
|
191
|
+
onValueChange={handleSeek}
|
|
192
|
+
className="w-full"
|
|
193
|
+
/>
|
|
194
194
|
</div>
|
|
195
195
|
|
|
196
196
|
<div className="flex items-center gap-1 shrink-0">
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
<Button
|
|
200
|
-
variant="ghost"
|
|
201
|
-
size="icon"
|
|
202
|
-
className="h-8 w-8 text-muted-foreground hover:text-foreground"
|
|
203
|
-
onClick={() => {
|
|
204
|
-
const newMuted = !isMuted;
|
|
205
|
-
setIsMuted(newMuted);
|
|
206
|
-
if (audioRef.current) audioRef.current.muted = newMuted;
|
|
207
|
-
}}
|
|
208
|
-
>
|
|
209
|
-
{isMuted ? <VolumeX className="w-4 h-4" /> : <Volume2 className="w-4 h-4" />}
|
|
210
|
-
</Button>
|
|
211
|
-
<div className="w-0 overflow-hidden group-hover/volume:w-20 transition-all duration-300">
|
|
212
|
-
<Slider
|
|
213
|
-
value={[isMuted ? 0 : volume]}
|
|
214
|
-
max={1}
|
|
215
|
-
step={0.01}
|
|
216
|
-
onValueChange={handleVolumeChange}
|
|
217
|
-
className="w-20"
|
|
218
|
-
/>
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
)}
|
|
222
|
-
|
|
223
|
-
{!isCompact && (
|
|
224
|
-
<div className="w-px h-6 bg-border mx-1 hidden sm:block" />
|
|
225
|
-
)}
|
|
226
|
-
|
|
227
|
-
{!isCompact && (
|
|
197
|
+
{!isCompact && (
|
|
198
|
+
<div className="hidden sm:flex items-center gap-2 group/volume mr-2">
|
|
228
199
|
<Button
|
|
229
200
|
variant="ghost"
|
|
230
201
|
size="icon"
|
|
231
202
|
className="h-8 w-8 text-muted-foreground hover:text-foreground"
|
|
232
203
|
onClick={() => {
|
|
233
|
-
|
|
234
|
-
|
|
204
|
+
const newMuted = !isMuted;
|
|
205
|
+
setIsMuted(newMuted);
|
|
206
|
+
if (audioRef.current) audioRef.current.muted = newMuted;
|
|
235
207
|
}}
|
|
236
|
-
title="Modo Flutuante"
|
|
237
208
|
>
|
|
238
|
-
<
|
|
209
|
+
{isMuted ? <VolumeX className="w-4 h-4" /> : <Volume2 className="w-4 h-4" />}
|
|
239
210
|
</Button>
|
|
240
|
-
|
|
211
|
+
<div className="w-0 overflow-hidden group-hover/volume:w-20 transition-all duration-300">
|
|
212
|
+
<Slider
|
|
213
|
+
value={[isMuted ? 0 : volume]}
|
|
214
|
+
max={1}
|
|
215
|
+
step={0.01}
|
|
216
|
+
onValueChange={handleVolumeChange}
|
|
217
|
+
className="w-20"
|
|
218
|
+
/>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
)}
|
|
222
|
+
|
|
223
|
+
{!isCompact && (
|
|
224
|
+
<div className="w-px h-6 bg-border mx-1 hidden sm:block" />
|
|
225
|
+
)}
|
|
226
|
+
|
|
227
|
+
{!isCompact && (
|
|
228
|
+
<Button
|
|
229
|
+
variant="ghost"
|
|
230
|
+
size="icon"
|
|
231
|
+
className="h-8 w-8 text-muted-foreground hover:text-foreground"
|
|
232
|
+
onClick={() => {
|
|
233
|
+
setIsManualFloating(true);
|
|
234
|
+
handleSetFloating(true);
|
|
235
|
+
}}
|
|
236
|
+
title="Modo Flutuante"
|
|
237
|
+
>
|
|
238
|
+
<Maximize2 className="w-4 h-4" />
|
|
239
|
+
</Button>
|
|
240
|
+
)}
|
|
241
241
|
</div>
|
|
242
242
|
</div>
|
|
243
243
|
);
|
|
@@ -247,21 +247,21 @@ export function AudioPlayer({
|
|
|
247
247
|
<FloatingMediaWrapper
|
|
248
248
|
isFloating={isFloating}
|
|
249
249
|
setIsFloating={handleSetFloating}
|
|
250
|
-
|
|
250
|
+
onClose={() => {
|
|
251
251
|
setIsPlaying(false);
|
|
252
|
-
setIsFloating(false);
|
|
252
|
+
setIsFloating(false);
|
|
253
253
|
if (audioRef.current) audioRef.current.pause();
|
|
254
254
|
}}
|
|
255
255
|
title={title}
|
|
256
|
-
aspectRatio={320/110}
|
|
256
|
+
aspectRatio={320 / 110}
|
|
257
257
|
minHeight={110}
|
|
258
258
|
minWidth={320}
|
|
259
259
|
className="w-full"
|
|
260
260
|
>
|
|
261
261
|
<div className={cn(
|
|
262
262
|
"bg-card w-full overflow-hidden flex flex-col justify-center",
|
|
263
|
-
isFloating
|
|
264
|
-
? "h-full bg-transparent text-primary-foreground [&_.text-muted-foreground]:text-primary-foreground/80 [&_.text-foreground]:text-primary-foreground [&_.text-primary]:text-primary-foreground [&_.border-primary\\/20]:border-primary-foreground/30 [&_.hover\\:bg-primary\\/5]:hover:bg-primary-foreground/10 [&_[data-slot=slider-track]]:bg-primary-foreground/20 [&_[data-slot=slider-range]]:bg-primary-foreground [&_[data-slot=slider-thumb]]:border-primary-foreground"
|
|
263
|
+
isFloating
|
|
264
|
+
? "h-full bg-transparent text-primary-foreground [&_.text-muted-foreground]:text-primary-foreground/80 [&_.text-foreground]:text-primary-foreground [&_.text-primary]:text-primary-foreground [&_.border-primary\\/20]:border-primary-foreground/30 [&_.hover\\:bg-primary\\/5]:hover:bg-primary-foreground/10 [&_[data-slot=slider-track]]:bg-primary-foreground/20 [&_[data-slot=slider-range]]:bg-primary-foreground [&_[data-slot=slider-thumb]]:border-primary-foreground"
|
|
265
265
|
: "border rounded-md shadow-sm"
|
|
266
266
|
)}>
|
|
267
267
|
<audio
|
|
@@ -13,12 +13,12 @@ export interface VideoPlayerProps {
|
|
|
13
13
|
className?: string;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function VideoPlayer({
|
|
17
|
-
src,
|
|
18
|
-
poster,
|
|
16
|
+
export function VideoPlayer({
|
|
17
|
+
src,
|
|
18
|
+
poster,
|
|
19
19
|
title = "Untitled Video",
|
|
20
20
|
autoPlay = false,
|
|
21
|
-
className
|
|
21
|
+
className
|
|
22
22
|
}: VideoPlayerProps) {
|
|
23
23
|
const videoRef = useRef<HTMLVideoElement>(null);
|
|
24
24
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -31,7 +31,7 @@ export function VideoPlayer({
|
|
|
31
31
|
const [isFloating, setIsFloating] = useState(false);
|
|
32
32
|
const [isManualFloating, setIsManualFloating] = useState(false);
|
|
33
33
|
const [showControls, setShowControls] = useState(true);
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
let controlsTimeout: NodeJS.Timeout;
|
|
36
36
|
|
|
37
37
|
// Wrapper for setIsFloating to handle scroll-on-restore and sync state
|
|
@@ -39,9 +39,9 @@ export function VideoPlayer({
|
|
|
39
39
|
if (videoRef.current) {
|
|
40
40
|
setCurrentTime(videoRef.current.currentTime);
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
setIsFloating(floating);
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
if (!floating) {
|
|
46
46
|
setIsManualFloating(false);
|
|
47
47
|
if (containerRef.current) {
|
|
@@ -55,15 +55,15 @@ export function VideoPlayer({
|
|
|
55
55
|
const video = videoRef.current;
|
|
56
56
|
if (video) {
|
|
57
57
|
if (Math.abs(video.currentTime - currentTime) > 0.5) {
|
|
58
|
-
|
|
58
|
+
video.currentTime = currentTime;
|
|
59
59
|
}
|
|
60
60
|
if (isPlaying) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
const playPromise = video.play();
|
|
62
|
+
if (playPromise !== undefined) {
|
|
63
|
+
playPromise.catch(error => {
|
|
64
|
+
console.log("Playback interrupted during switch:", error);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}, [isFloating]);
|
|
@@ -76,11 +76,11 @@ export function VideoPlayer({
|
|
|
76
76
|
const observer = new IntersectionObserver(
|
|
77
77
|
([entry]) => {
|
|
78
78
|
if (isPlaying && !entry.isIntersecting && !isFloating) {
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
if (videoRef.current) setCurrentTime(videoRef.current.currentTime);
|
|
80
|
+
setIsFloating(true);
|
|
81
81
|
} else if (entry.isIntersecting && isFloating && !isManualFloating) {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
if (videoRef.current) setCurrentTime(videoRef.current.currentTime);
|
|
83
|
+
handleSetFloating(false);
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
86
|
{ threshold: 0.2 }
|
|
@@ -179,16 +179,16 @@ export function VideoPlayer({
|
|
|
179
179
|
<FloatingMediaWrapper
|
|
180
180
|
isFloating={isFloating}
|
|
181
181
|
setIsFloating={handleSetFloating}
|
|
182
|
-
|
|
182
|
+
onClose={() => {
|
|
183
183
|
setIsPlaying(false);
|
|
184
|
-
setIsFloating(false);
|
|
184
|
+
setIsFloating(false);
|
|
185
185
|
if (videoRef.current) videoRef.current.pause();
|
|
186
186
|
}}
|
|
187
187
|
title={title}
|
|
188
|
-
aspectRatio={16/9}
|
|
188
|
+
aspectRatio={16 / 9}
|
|
189
189
|
className="w-full h-full"
|
|
190
190
|
>
|
|
191
|
-
<div
|
|
191
|
+
<div
|
|
192
192
|
className="relative w-full h-full bg-black group overflow-hidden"
|
|
193
193
|
onMouseMove={handleMouseMove}
|
|
194
194
|
onMouseLeave={() => isPlaying && setShowControls(false)}
|
|
@@ -205,12 +205,12 @@ export function VideoPlayer({
|
|
|
205
205
|
{!isPlaying && (
|
|
206
206
|
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
|
207
207
|
<div className="w-16 h-16 rounded-full bg-black/40 backdrop-blur-sm flex items-center justify-center border border-white/20 shadow-2xl">
|
|
208
|
-
|
|
208
|
+
<Play className="w-8 h-8 text-white ml-1" fill="white" />
|
|
209
209
|
</div>
|
|
210
210
|
</div>
|
|
211
211
|
)}
|
|
212
212
|
|
|
213
|
-
<div
|
|
213
|
+
<div
|
|
214
214
|
className={cn(
|
|
215
215
|
"absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-4 transition-opacity duration-300",
|
|
216
216
|
showControls ? "opacity-100" : "opacity-0"
|
|
@@ -263,18 +263,18 @@ export function VideoPlayer({
|
|
|
263
263
|
</div>
|
|
264
264
|
|
|
265
265
|
<div className="flex items-center gap-2">
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
266
|
+
<Button
|
|
267
|
+
variant="ghost"
|
|
268
|
+
size="icon"
|
|
269
|
+
className="text-white hover:bg-white/20 h-8 w-8"
|
|
270
|
+
onClick={() => {
|
|
271
|
+
if (!isFloating) setIsManualFloating(true);
|
|
272
|
+
handleSetFloating(!isFloating);
|
|
273
|
+
}}
|
|
274
|
+
title={isFloating ? "Restaurar" : "Pop-out Player"}
|
|
275
|
+
>
|
|
276
|
+
{isFloating ? <Maximize className="w-4 h-4" /> : <PictureInPicture className="w-4 h-4" />}
|
|
277
|
+
</Button>
|
|
278
278
|
</div>
|
|
279
279
|
</div>
|
|
280
280
|
</div>
|
package/components/ui/drawer.tsx
CHANGED
|
@@ -117,6 +117,17 @@ function DrawerDescription({
|
|
|
117
117
|
className={cn("text-muted-foreground text-sm", className)}
|
|
118
118
|
{...props}
|
|
119
119
|
/>
|
|
120
|
+
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function DrawerHandle({ className, ...props }: React.ComponentProps<"div">) {
|
|
125
|
+
return (
|
|
126
|
+
<div
|
|
127
|
+
data-slot="drawer-handle"
|
|
128
|
+
className={cn("bg-muted mx-auto mt-4 h-2 w-[100px] shrink-0 rounded-full", className)}
|
|
129
|
+
{...props}
|
|
130
|
+
/>
|
|
120
131
|
);
|
|
121
132
|
}
|
|
122
133
|
|
|
@@ -131,4 +142,5 @@ export {
|
|
|
131
142
|
DrawerFooter,
|
|
132
143
|
DrawerTitle,
|
|
133
144
|
DrawerDescription,
|
|
145
|
+
DrawerHandle,
|
|
134
146
|
};
|