Spaces:
Running
Running
| 'use client' | |
| import React, { useState } from 'react' | |
| import { | |
| Folder, | |
| Calendar as CalendarIcon, | |
| Clock as ClockIcon, | |
| Sparkle, | |
| Trash, | |
| FolderOpen, | |
| Compass, | |
| Flask, | |
| DeviceMobile, | |
| Function, | |
| ChatCircleDots | |
| } from '@phosphor-icons/react' | |
| import { motion } from 'framer-motion' | |
| import { DynamicClockIcon } from './DynamicClockIcon' | |
| import { DynamicCalendarIcon } from './DynamicCalendarIcon' | |
| interface MinimizedApp { | |
| id: string | |
| label: string | |
| icon: React.ReactNode | |
| onRestore: () => void | |
| } | |
| interface DockProps { | |
| onOpenFileManager: (path: string) => void | |
| onOpenCalendar: () => void | |
| onOpenClock: () => void | |
| onOpenMessages: () => void | |
| onOpenGeminiChat: () => void | |
| onCloseAllApps?: () => void | |
| openApps: { [key: string]: boolean } | |
| minimizedApps?: MinimizedApp[] | |
| } | |
| interface DockItemProps { | |
| icon: React.ReactNode | |
| label: string | |
| onClick: () => void | |
| isActive?: boolean | |
| className?: string | |
| mouseX: React.MutableRefObject<number | null> | |
| } | |
| const DockItem: React.FC<DockItemProps> = ({ icon, label, onClick, isActive = false, className = '', mouseX }) => { | |
| const [isHovered, setIsHovered] = useState(false) | |
| const ref = React.useRef<HTMLDivElement>(null) | |
| const handleClick = () => { | |
| console.log('Dock item clicked:', label) | |
| if (onClick) { | |
| onClick() | |
| } | |
| } | |
| return ( | |
| <div className="dock-item group" ref={ref}> | |
| <motion.button | |
| whileHover={{ scale: 1.2, y: -10 }} | |
| whileTap={{ scale: 0.95 }} | |
| onMouseEnter={() => setIsHovered(true)} | |
| onMouseLeave={() => setIsHovered(false)} | |
| onClick={handleClick} | |
| className={`app-icon w-10 h-10 sm:w-11 sm:h-11 md:w-12 md:h-12 rounded-xl shadow-lg flex items-center justify-center cursor-pointer border transition-all ${className}`} | |
| title={label} | |
| > | |
| {icon} | |
| </motion.button> | |
| <div | |
| className={`dock-dot ${isActive ? 'opacity-100' : ''}`} | |
| id={`dot-${label.toLowerCase().replace(/\s+/g, '-')}`} | |
| /> | |
| </div> | |
| ) | |
| } | |
| export function Dock({ | |
| onOpenFileManager, | |
| onOpenCalendar, | |
| onOpenClock, | |
| onOpenMessages, | |
| onOpenGeminiChat, | |
| onCloseAllApps, | |
| openApps, | |
| minimizedApps = [] | |
| }: DockProps) { | |
| const mouseX = React.useRef<number | null>(null) | |
| const dockItems = [ | |
| { | |
| icon: ( | |
| <div className="bg-gradient-to-br from-blue-400 to-cyan-200 w-full h-full rounded-xl flex items-center justify-center border border-white/30"> | |
| <Folder size={24} weight="regular" className="text-blue-900 md:scale-110" /> | |
| </div> | |
| ), | |
| label: 'Files', | |
| onClick: () => onOpenFileManager(''), | |
| isActive: openApps['files'], | |
| className: '' | |
| }, | |
| { | |
| icon: ( | |
| <div className="bg-gradient-to-b from-white to-blue-50 w-full h-full rounded-xl flex items-center justify-center shadow-lg border-[0.5px] border-white/50 relative overflow-hidden"> | |
| <div className="absolute inset-0 bg-gradient-to-b from-white/40 to-transparent" /> | |
| <Sparkle size={24} weight="fill" className="text-blue-500 md:scale-110 drop-shadow-sm" /> | |
| </div> | |
| ), | |
| label: 'Gemini', | |
| onClick: onOpenGeminiChat, | |
| isActive: openApps['gemini'], | |
| className: '' | |
| }, | |
| { | |
| icon: ( | |
| <div className="bg-gradient-to-b from-[#4CD964] to-[#2E8B57] w-full h-full rounded-xl flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden"> | |
| <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" /> | |
| <ChatCircleDots size={24} weight="fill" className="text-white md:scale-110 drop-shadow-md" /> | |
| </div> | |
| ), | |
| label: 'Messages', | |
| onClick: onOpenMessages, | |
| isActive: openApps['messages'], | |
| className: '' | |
| }, | |
| { | |
| icon: ( | |
| <div className="w-full h-full"> | |
| <DynamicClockIcon /> | |
| </div> | |
| ), | |
| label: 'Clock', | |
| onClick: onOpenClock, | |
| isActive: openApps['clock'], | |
| className: 'rounded-full' | |
| }, | |
| { | |
| icon: ( | |
| <div className="w-full h-full"> | |
| <DynamicCalendarIcon /> | |
| </div> | |
| ), | |
| label: 'Calendar', | |
| onClick: onOpenCalendar, | |
| isActive: openApps['calendar'], | |
| className: '' | |
| } | |
| ] | |
| return ( | |
| <div className="dock-container"> | |
| <div | |
| className="dock-glass px-1 pb-1 pt-1.5 sm:px-2 sm:pb-1.5 sm:pt-2 md:px-3 md:pb-2 md:pt-3 rounded-t-2xl md:rounded-2xl flex items-end gap-1 sm:gap-2 md:gap-3 shadow-2xl border border-white/20 transition-all duration-300 overflow-x-auto max-w-full" | |
| onMouseMove={(e) => mouseX.current = e.pageX} | |
| onMouseLeave={() => mouseX.current = null} | |
| > | |
| {dockItems.map((item, index) => ( | |
| <DockItem key={item.label} {...item} mouseX={mouseX} /> | |
| ))} | |
| {/* Minimized apps section */} | |
| {minimizedApps.length > 0 && ( | |
| <> | |
| <div className="w-px h-8 md:h-10 bg-gray-400/30 mx-0.5 md:mx-1" /> | |
| {minimizedApps.map((app) => ( | |
| <div key={app.id} className="relative"> | |
| <DockItem | |
| icon={app.icon} | |
| label={`${app.label} (minimized)`} | |
| onClick={app.onRestore} | |
| className="opacity-70 ring-2 ring-yellow-400/50" | |
| mouseX={mouseX} | |
| /> | |
| </div> | |
| ))} | |
| </> | |
| )} | |
| {/* Trash */} | |
| <div className="w-px h-8 md:h-10 bg-gray-400/30 mx-0.5 md:mx-1" /> | |
| <DockItem | |
| icon={ | |
| <div className="bg-gradient-to-b from-gray-200 to-gray-300 w-full h-full rounded-xl flex items-center justify-center border border-white/40"> | |
| <Trash size={20} weight="regular" className="text-gray-600 md:scale-110" /> | |
| </div> | |
| } | |
| label="Trash" | |
| onClick={() => onCloseAllApps?.()} | |
| className="" | |
| mouseX={mouseX} | |
| /> | |
| </div> | |
| </div> | |
| ) | |
| } |