Spaces:
Running
Running
| 'use client' | |
| import React, { useState, useRef } from 'react' | |
| import Draggable from 'react-draggable' | |
| import { | |
| Folder, | |
| Calendar, | |
| Clock, | |
| Globe, | |
| Sparkle, | |
| Flask, | |
| DeviceMobile, | |
| Function, | |
| HardDrives, | |
| Code, | |
| Lightning, | |
| Key, | |
| Brain, | |
| ChatCircleDots, | |
| MusicNote | |
| } from '@phosphor-icons/react' | |
| import { DynamicClockIcon } from './DynamicClockIcon' | |
| import { DynamicCalendarIcon } from './DynamicCalendarIcon' | |
| interface DraggableDesktopIconProps { | |
| id: string | |
| label: string | |
| iconType: string | |
| initialPosition?: { x: number; y: number } | |
| onClick: () => void | |
| onDoubleClick: () => void | |
| } | |
| export function DraggableDesktopIcon({ | |
| id, | |
| label, | |
| iconType, | |
| initialPosition = { x: 0, y: 0 }, | |
| onClick, | |
| onDoubleClick | |
| }: DraggableDesktopIconProps) { | |
| const [selected, setSelected] = useState(false) | |
| const nodeRef = useRef<HTMLDivElement>(null) as React.MutableRefObject<HTMLDivElement> | |
| const handleClick = () => { | |
| setSelected(true) | |
| onClick() | |
| // Deselect after a short time | |
| setTimeout(() => setSelected(false), 3000) | |
| } | |
| const getIcon = () => { | |
| switch (iconType) { | |
| case 'files': | |
| return ( | |
| <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={32} weight="regular" className="text-blue-900" /> | |
| </div> | |
| ) | |
| case 'calendar': | |
| return ( | |
| <div className="w-full h-full"> | |
| <DynamicCalendarIcon /> | |
| </div> | |
| ) | |
| case 'clock': | |
| return ( | |
| <div className="w-full h-full"> | |
| <DynamicClockIcon /> | |
| </div> | |
| ) | |
| case 'messages': | |
| return ( | |
| <div className="bg-gradient-to-b from-[#4CD964] to-[#2E8B57] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300"> | |
| <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" /> | |
| <ChatCircleDots size={36} weight="fill" className="text-white relative z-10 drop-shadow-md" /> | |
| </div> | |
| ) | |
| case 'flutter': | |
| return ( | |
| <div className="bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300"> | |
| <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" /> | |
| <DeviceMobile size={36} weight="fill" className="text-white relative z-10 drop-shadow-md" /> | |
| <div className="absolute top-1.5 right-1.5"> | |
| <Lightning size={12} weight="fill" className="text-yellow-300 drop-shadow-md" /> | |
| </div> | |
| </div> | |
| ) | |
| case 'latex': | |
| return ( | |
| <div className="bg-gradient-to-b from-slate-700 to-slate-900 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300"> | |
| <div className="absolute inset-0 bg-gradient-to-b from-white/10 to-transparent opacity-30" /> | |
| <div className="relative z-10 flex flex-col items-center justify-center"> | |
| <Function size={32} weight="bold" className="text-green-400 drop-shadow-md" /> | |
| <span className="text-[8px] font-black text-gray-300 tracking-widest mt-0.5">TEX</span> | |
| </div> | |
| </div> | |
| ) | |
| case 'gemini': | |
| return ( | |
| <div className="bg-gradient-to-b from-white to-blue-50 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/50 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300"> | |
| <div className="absolute inset-0 bg-gradient-to-b from-white/40 to-transparent" /> | |
| <Sparkle size={36} weight="fill" className="text-blue-500 relative z-10 drop-shadow-sm" /> | |
| </div> | |
| ) | |
| case 'harddrive': | |
| return ( | |
| <div className="bg-gray-200 w-full h-full rounded-lg flex items-center justify-center border border-gray-300 relative"> | |
| <HardDrives size={32} weight="regular" className="text-gray-600" /> | |
| <div className="absolute bottom-2 right-2 w-1.5 h-1.5 bg-green-400 rounded-full animate-pulse" /> | |
| </div> | |
| ) | |
| case 'vscode': | |
| return ( | |
| <div className="bg-gradient-to-br from-blue-600 to-blue-800 w-full h-full rounded-xl flex items-center justify-center border border-blue-900/30 shadow-inner"> | |
| <Code size={32} weight="bold" className="text-white" /> | |
| </div> | |
| ) | |
| case 'playground': | |
| return ( | |
| <div className="bg-gradient-to-br from-yellow-500 to-orange-600 w-full h-full rounded-xl flex items-center justify-center border border-orange-700/30 shadow-inner"> | |
| <Lightning size={32} weight="fill" className="text-white" /> | |
| </div> | |
| ) | |
| case 'key': | |
| return ( | |
| <div className="bg-gradient-to-br from-purple-600 to-purple-800 w-full h-full rounded-xl flex items-center justify-center border border-purple-900/30 shadow-inner"> | |
| <Key size={32} weight="bold" className="text-white" /> | |
| </div> | |
| ) | |
| case 'quiz': | |
| return ( | |
| <div className="bg-gradient-to-br from-teal-400 to-emerald-500 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300"> | |
| <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" /> | |
| <Brain size={36} weight="fill" className="text-white relative z-10 drop-shadow-md" /> | |
| </div> | |
| ) | |
| case 'voice-app': | |
| return ( | |
| <div className="bg-gradient-to-br from-purple-500 to-pink-500 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300"> | |
| <div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" /> | |
| <MusicNote size={36} weight="fill" className="text-white relative z-10 drop-shadow-md" /> | |
| </div> | |
| ) | |
| default: | |
| return ( | |
| <div className="bg-gray-400 w-full h-full rounded-xl flex items-center justify-center"> | |
| <Folder size={32} weight="regular" className="text-white" /> | |
| </div> | |
| ) | |
| } | |
| } | |
| return ( | |
| <Draggable | |
| nodeRef={nodeRef} | |
| defaultPosition={initialPosition} | |
| grid={[25, 25]} | |
| handle=".icon-handle" | |
| > | |
| <div | |
| ref={nodeRef} | |
| className="absolute flex flex-col items-center w-20 group cursor-pointer transition-all hover:bg-white/10 rounded p-1" | |
| onClick={handleClick} | |
| onDoubleClick={onDoubleClick} | |
| > | |
| <div className="icon-handle"> | |
| <div | |
| className={`w-14 h-14 shadow-lg transition-transform hover:scale-110 ${iconType === 'clock' ? '' : 'rounded-xl' | |
| }`} | |
| > | |
| {getIcon()} | |
| </div> | |
| </div> | |
| <span | |
| className={`mt-1 text-xs font-medium drop-shadow-lg text-center leading-tight px-1 py-0.5 rounded transition-colors ${selected | |
| ? 'bg-blue-600/80 text-white' | |
| : 'text-white bg-black/20 group-hover:bg-blue-600/80' | |
| }`} | |
| > | |
| {label} | |
| </span> | |
| </div> | |
| </Draggable> | |
| ) | |
| } |