|
3 | 3 | import { useState } from 'react'; |
4 | 4 | import { motion, AnimatePresence } from 'framer-motion'; |
5 | 5 | import { useLanguage } from '@/contexts/LanguageContext'; |
6 | | -import { MousePointer, Smartphone, ChevronDown, CheckCircle, XCircle, ArrowLeft } from 'lucide-react'; |
| 6 | +import { MousePointer, ChevronDown, CheckCircle, XCircle, ArrowLeft, Copy } from 'lucide-react'; |
7 | 7 | import { useRouter } from 'next/navigation'; |
8 | 8 | import Header from '@/components/layout/Header'; |
9 | 9 | import Footer from '@/components/layout/Footer'; |
@@ -31,55 +31,197 @@ const UXPatternsPage = () => { |
31 | 31 | id: 'onLoad', |
32 | 32 | title: 'While loading', |
33 | 33 | description: 'Skeleton screens, loading states, and progressive disclosure patterns that keep users engaged during wait times.', |
34 | | - tags: ['Skeleton UI', 'Loading States', 'Progressive Disclosure'] |
| 34 | + tags: ['Skeleton UI', 'Loading States', 'Progressive Disclosure'], |
| 35 | + code: `// Skeleton Loading Component |
| 36 | +const SkeletonCard = () => ( |
| 37 | + <div className="animate-pulse"> |
| 38 | + <div className="h-4 bg-gray-300 rounded w-3/4 mb-2"></div> |
| 39 | + <div className="h-4 bg-gray-300 rounded w-1/2"></div> |
| 40 | + </div> |
| 41 | +); |
| 42 | +
|
| 43 | +// Usage |
| 44 | +{isLoading ? <SkeletonCard /> : <ActualContent />}` |
35 | 45 | }, |
36 | 46 | { |
37 | 47 | id: 'onScroll', |
38 | 48 | title: 'Page Scroll', |
39 | 49 | description: 'Default vertical scrolling as the primary interaction pattern, with parallax and reveal animations.', |
40 | | - tags: ['Vertical Scroll', 'Parallax', 'Reveal Animations'] |
| 50 | + tags: ['Vertical Scroll', 'Parallax', 'Reveal Animations'], |
| 51 | + code: `// Scroll-triggered Animation |
| 52 | +const useScrollAnimation = () => { |
| 53 | + const [isVisible, setIsVisible] = useState(false); |
| 54 | + |
| 55 | + useEffect(() => { |
| 56 | + const handleScroll = () => { |
| 57 | + const element = document.getElementById('target'); |
| 58 | + if (element) { |
| 59 | + const rect = element.getBoundingClientRect(); |
| 60 | + setIsVisible(rect.top < window.innerHeight); |
| 61 | + } |
| 62 | + }; |
| 63 | + |
| 64 | + window.addEventListener('scroll', handleScroll); |
| 65 | + return () => window.removeEventListener('scroll', handleScroll); |
| 66 | + }, []); |
| 67 | + |
| 68 | + return isVisible; |
| 69 | +};` |
41 | 70 | }, |
42 | 71 | { |
43 | 72 | id: 'notify', |
44 | 73 | title: 'Notify', |
45 | 74 | description: 'Toast notifications, banners, and system messages that provide feedback without interrupting user flow.', |
46 | | - tags: ['Toast', 'Banner', 'System Messages'] |
| 75 | + tags: ['Toast', 'Banner', 'System Messages'], |
| 76 | + code: `// Toast Notification Hook |
| 77 | +const useToast = () => { |
| 78 | + const [toasts, setToasts] = useState([]); |
| 79 | + |
| 80 | + const showToast = (message, type = 'info') => { |
| 81 | + const id = Date.now(); |
| 82 | + setToasts(prev => [...prev, { id, message, type }]); |
| 83 | + |
| 84 | + setTimeout(() => { |
| 85 | + setToasts(prev => prev.filter(toast => toast.id !== id)); |
| 86 | + }, 3000); |
| 87 | + }; |
| 88 | + |
| 89 | + return { toasts, showToast }; |
| 90 | +};` |
47 | 91 | }, |
48 | 92 | { |
49 | 93 | id: 'alert', |
50 | 94 | title: 'Alert', |
51 | 95 | description: 'Critical notifications and warnings that require immediate user attention and action.', |
52 | | - tags: ['Critical Alerts', 'Warnings', 'User Attention'] |
| 96 | + tags: ['Critical Alerts', 'Warnings', 'User Attention'], |
| 97 | + code: `// Alert Modal Component |
| 98 | +const AlertModal = ({ isOpen, onClose, title, message, type }) => { |
| 99 | + if (!isOpen) return null; |
| 100 | + |
| 101 | + return ( |
| 102 | + <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"> |
| 103 | + <div className="bg-white rounded-lg p-6 max-w-md mx-4"> |
| 104 | + <div className="flex items-center mb-4"> |
| 105 | + <AlertCircle className="w-6 h-6 text-red-500 mr-2" /> |
| 106 | + <h3 className="text-lg font-semibold">{title}</h3> |
| 107 | + </div> |
| 108 | + <p className="text-gray-600 mb-4">{message}</p> |
| 109 | + <button onClick={onClose} className="bg-red-500 text-white px-4 py-2 rounded"> |
| 110 | + Dismiss |
| 111 | + </button> |
| 112 | + </div> |
| 113 | + </div> |
| 114 | + ); |
| 115 | +};` |
53 | 116 | }, |
54 | 117 | { |
55 | 118 | id: 'pauseAsk', |
56 | 119 | title: 'Pause & Ask', |
57 | 120 | description: 'Modal dialogs and popups that pause user flow to gather information or confirm actions.', |
58 | | - tags: ['Modal', 'Popup', 'Confirmation'] |
| 121 | + tags: ['Modal', 'Popup', 'Confirmation'], |
| 122 | + code: `// Confirmation Dialog |
| 123 | +const ConfirmationDialog = ({ isOpen, onConfirm, onCancel, message }) => { |
| 124 | + if (!isOpen) return null; |
| 125 | + |
| 126 | + return ( |
| 127 | + <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"> |
| 128 | + <div className="bg-white rounded-lg p-6 max-w-sm mx-4"> |
| 129 | + <p className="mb-4">{message}</p> |
| 130 | + <div className="flex space-x-3"> |
| 131 | + <button onClick={onCancel} className="flex-1 px-4 py-2 border rounded"> |
| 132 | + Cancel |
| 133 | + </button> |
| 134 | + <button onClick={onConfirm} className="flex-1 px-4 py-2 bg-blue-500 text-white rounded"> |
| 135 | + Confirm |
| 136 | + </button> |
| 137 | + </div> |
| 138 | + </div> |
| 139 | + </div> |
| 140 | + ); |
| 141 | +};` |
59 | 142 | }, |
60 | 143 | { |
61 | 144 | id: 'magnify', |
62 | 145 | title: 'Magnify', |
63 | 146 | description: 'Bottom sheets and expandable content that provides detailed information without leaving the current context.', |
64 | | - tags: ['Bottom Sheet', 'Expandable', 'Detail View'] |
| 147 | + tags: ['Bottom Sheet', 'Expandable', 'Detail View'], |
| 148 | + code: `// Bottom Sheet Component |
| 149 | +const BottomSheet = ({ isOpen, onClose, children }) => { |
| 150 | + return ( |
| 151 | + <div className={\`fixed inset-0 z-50 \${isOpen ? 'block' : 'hidden'}\`}> |
| 152 | + <div className="absolute inset-0 bg-black/50" onClick={onClose} /> |
| 153 | + <div className="absolute bottom-0 left-0 right-0 bg-white rounded-t-xl max-h-96 overflow-y-auto"> |
| 154 | + <div className="w-12 h-1 bg-gray-300 rounded mx-auto mt-2 mb-4" /> |
| 155 | + {children} |
| 156 | + </div> |
| 157 | + </div> |
| 158 | + ); |
| 159 | +};` |
65 | 160 | }, |
66 | 161 | { |
67 | 162 | id: 'screenToScreen', |
68 | 163 | title: 'Screen to Screen', |
69 | 164 | description: 'Navigation patterns and transitions between different screens and sections of the application.', |
70 | | - tags: ['Navigation', 'Transitions', 'Screen Flow'] |
| 165 | + tags: ['Navigation', 'Transitions', 'Screen Flow'], |
| 166 | + code: `// Page Transition Hook |
| 167 | +const usePageTransition = () => { |
| 168 | + const [isTransitioning, setIsTransitioning] = useState(false); |
| 169 | + |
| 170 | + const navigateWithTransition = (path) => { |
| 171 | + setIsTransitioning(true); |
| 172 | + |
| 173 | + setTimeout(() => { |
| 174 | + router.push(path); |
| 175 | + setIsTransitioning(false); |
| 176 | + }, 300); |
| 177 | + }; |
| 178 | + |
| 179 | + return { isTransitioning, navigateWithTransition }; |
| 180 | +};` |
71 | 181 | }, |
72 | 182 | { |
73 | 183 | id: 'feedback', |
74 | 184 | title: 'Feedback', |
75 | 185 | description: 'Touch, swipe, and gesture-based interactions that provide immediate visual and haptic feedback.', |
76 | | - tags: ['Touch', 'Swipe', 'Gestures', 'Haptic'] |
| 186 | + tags: ['Touch', 'Swipe', 'Gestures', 'Haptic'], |
| 187 | + code: `// Touch Feedback Hook |
| 188 | +const useTouchFeedback = () => { |
| 189 | + const [isPressed, setIsPressed] = useState(false); |
| 190 | + |
| 191 | + const handleTouchStart = () => { |
| 192 | + setIsPressed(true); |
| 193 | + // Add haptic feedback if available |
| 194 | + if (navigator.vibrate) { |
| 195 | + navigator.vibrate(50); |
| 196 | + } |
| 197 | + }; |
| 198 | + |
| 199 | + const handleTouchEnd = () => { |
| 200 | + setIsPressed(false); |
| 201 | + }; |
| 202 | + |
| 203 | + return { isPressed, handleTouchStart, handleTouchEnd }; |
| 204 | +};` |
77 | 205 | }, |
78 | 206 | { |
79 | 207 | id: 'moreToCome', |
80 | 208 | title: 'More to come', |
81 | 209 | description: 'Additional interaction patterns are continuously being developed and refined based on user needs.', |
82 | | - tags: ['Coming Soon', 'Development', 'User Needs'] |
| 210 | + tags: ['Coming Soon', 'Development', 'User Needs'], |
| 211 | + code: `// Coming Soon Placeholder |
| 212 | +const ComingSoonPattern = () => ( |
| 213 | + <div className="text-center py-8"> |
| 214 | + <div className="w-16 h-16 bg-gray-200 rounded-full mx-auto mb-4 flex items-center justify-center"> |
| 215 | + <Plus className="w-8 h-8 text-gray-400" /> |
| 216 | + </div> |
| 217 | + <h3 className="text-lg font-semibold text-gray-600 mb-2"> |
| 218 | + New Pattern Coming Soon |
| 219 | + </h3> |
| 220 | + <p className="text-gray-500"> |
| 221 | + We're constantly developing new interaction patterns based on user needs. |
| 222 | + </p> |
| 223 | + </div> |
| 224 | +);` |
83 | 225 | } |
84 | 226 | ]; |
85 | 227 |
|
@@ -274,22 +416,45 @@ const UXPatternsPage = () => { |
274 | 416 | </h5> |
275 | 417 | <div className="relative"> |
276 | 418 | {/* Mobile Frame with 9:16 ratio (vertical phone) */} |
277 | | - <div className="w-full max-w-[200px] mx-auto bg-black rounded-[2rem] p-2 shadow-2xl"> |
278 | | - <div className="bg-muted/20 rounded-[1.5rem] h-[400px] flex items-center justify-center" style={{ aspectRatio: '9/16' }}> |
279 | | - <div className="text-center space-y-3 px-4"> |
280 | | - <Smartphone className="w-8 h-8 text-muted-foreground mx-auto" /> |
281 | | - <p className="text-sm text-muted-foreground break-words font-medium"> |
| 419 | + <div className="w-full max-w-[180px] mx-auto bg-gray-900 rounded-[2rem] p-1 shadow-2xl border border-gray-700"> |
| 420 | + <div className="bg-gray-800 rounded-[1.5rem] h-[320px] flex items-center justify-center" style={{ aspectRatio: '9/16' }}> |
| 421 | + <div className="text-center space-y-2 px-3"> |
| 422 | + <div className="w-6 h-6 bg-gray-600 rounded-full mx-auto mb-2"></div> |
| 423 | + <p className="text-xs text-gray-400 break-words font-medium"> |
282 | 424 | {pattern.title} |
283 | 425 | </p> |
284 | | - <p className="text-xs text-muted-foreground/70"> |
285 | | - Preview coming soon |
| 426 | + <p className="text-xs text-gray-500"> |
| 427 | + Preview |
286 | 428 | </p> |
287 | 429 | </div> |
288 | 430 | </div> |
289 | 431 | </div> |
290 | 432 | </div> |
291 | 433 | </div> |
292 | 434 | </div> |
| 435 | + |
| 436 | + {/* Code Preview */} |
| 437 | + {pattern.code && ( |
| 438 | + <div className="mt-6 space-y-3"> |
| 439 | + <div className="flex items-center justify-between"> |
| 440 | + <h5 className="text-sm font-medium text-white"> |
| 441 | + Code Example |
| 442 | + </h5> |
| 443 | + <button |
| 444 | + onClick={() => navigator.clipboard.writeText(pattern.code)} |
| 445 | + className="flex items-center space-x-1 text-xs text-muted-foreground hover:text-white transition-colors" |
| 446 | + > |
| 447 | + <Copy className="w-3 h-3" /> |
| 448 | + <span>Copy</span> |
| 449 | + </button> |
| 450 | + </div> |
| 451 | + <div className="bg-gray-900 rounded-lg p-4 overflow-x-auto"> |
| 452 | + <pre className="text-xs text-gray-300 font-mono leading-relaxed"> |
| 453 | + <code>{pattern.code}</code> |
| 454 | + </pre> |
| 455 | + </div> |
| 456 | + </div> |
| 457 | + )} |
293 | 458 | </div> |
294 | 459 | </motion.div> |
295 | 460 | )} |
|
0 commit comments