@umituz/web-design-system 2.0.1 → 2.1.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/web-design-system",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "private": false,
5
5
  "description": "Web Design System - Atomic Design components (Atoms, Molecules, Organisms, Templates) for React applications",
6
6
  "main": "./src/index.ts",
@@ -77,6 +77,7 @@
77
77
  "react-day-picker": ">=9.0.0",
78
78
  "react-dom": ">=19.0.0",
79
79
  "react-i18next": ">=13.0.0",
80
+ "react-syntax-highlighter": ">=15.0.0",
80
81
  "tailwind-merge": ">=2.0.0"
81
82
  },
82
83
  "devDependencies": {
@@ -99,6 +100,7 @@
99
100
  "@radix-ui/react-tooltip": "^1.2.7",
100
101
  "@types/react": "^19.0.0",
101
102
  "@types/react-dom": "^19.0.0",
103
+ "@types/react-syntax-highlighter": "^15.5.13",
102
104
  "class-variance-authority": "^0.7.1",
103
105
  "clsx": "^2.1.1",
104
106
  "lucide-react": "^0.577.0",
@@ -106,6 +108,7 @@
106
108
  "react-day-picker": "^9.14.0",
107
109
  "react-dom": "^19.0.0",
108
110
  "react-i18next": "^13.0.0",
111
+ "react-syntax-highlighter": "^16.1.1",
109
112
  "tailwind-merge": "^3.5.0",
110
113
  "typescript": "~5.9.2"
111
114
  },
@@ -0,0 +1,253 @@
1
+ /**
2
+ * ImageLightbox Component (Organism)
3
+ * @description Full-screen image gallery with zoom, navigation, and thumbnails
4
+ */
5
+
6
+ import { useState, useCallback, useEffect } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps } from '../../domain/types';
9
+ import { Icon } from '../atoms/Icon';
10
+
11
+ export interface ImageLightboxImage {
12
+ src: string;
13
+ alt: string;
14
+ title?: string;
15
+ }
16
+
17
+ export interface ImageLightboxProps extends BaseProps {
18
+ images: ImageLightboxImage[];
19
+ initialIndex?: number;
20
+ isOpen: boolean;
21
+ onClose: () => void;
22
+ }
23
+
24
+ export const ImageLightbox = ({
25
+ images,
26
+ initialIndex = 0,
27
+ isOpen,
28
+ onClose,
29
+ className,
30
+ }: ImageLightboxProps) => {
31
+ // Early return if closed
32
+ if (!isOpen) return null;
33
+
34
+ // Early return if no images
35
+ if (!images || images.length === 0) {
36
+ return null;
37
+ }
38
+
39
+ const [currentIndex, setCurrentIndex] = useState(initialIndex);
40
+ const [isZoomed, setIsZoomed] = useState(false);
41
+
42
+ // Ensure initialIndex is within bounds
43
+ const safeIndex = Math.min(Math.max(initialIndex, 0), images.length - 1);
44
+ const currentImage = images[safeIndex];
45
+
46
+ // Use useCallback to memoize navigation functions
47
+ const goToNext = useCallback(() => {
48
+ setCurrentIndex((prev) => (prev + 1) % images.length);
49
+ }, [images.length]);
50
+
51
+ const goToPrevious = useCallback(() => {
52
+ setCurrentIndex((prev) => (prev - 1 + images.length) % images.length);
53
+ }, [images.length]);
54
+
55
+ const handleImageClick = useCallback(() => {
56
+ setIsZoomed(!isZoomed);
57
+ }, [isZoomed]);
58
+
59
+ // Handle keyboard navigation
60
+ useEffect(() => {
61
+ const handleKeyDown = (e: KeyboardEvent) => {
62
+ switch (e.key) {
63
+ case 'Escape':
64
+ onClose();
65
+ break;
66
+ case 'ArrowLeft':
67
+ goToPrevious();
68
+ break;
69
+ case 'ArrowRight':
70
+ goToNext();
71
+ break;
72
+ case '+':
73
+ case '=':
74
+ setIsZoomed(!isZoomed);
75
+ break;
76
+ }
77
+ };
78
+
79
+ window.addEventListener('keydown', handleKeyDown);
80
+ return () => window.removeEventListener('keydown', handleKeyDown);
81
+ }, [onClose, goToPrevious, goToNext, isZoomed]);
82
+
83
+ // Prevent body scroll when lightbox is open
84
+ useEffect(() => {
85
+ const originalOverflow = document.body.style.overflow;
86
+ document.body.style.overflow = 'hidden';
87
+
88
+ return () => {
89
+ document.body.style.overflow = originalOverflow;
90
+ };
91
+ }, []);
92
+
93
+ if (!currentImage) return null;
94
+
95
+ return (
96
+ <div
97
+ className="fixed inset-0 z-50 bg-black/90 flex items-center justify-center p-4"
98
+ onClick={onClose}
99
+ >
100
+ {/* Close button */}
101
+ <button
102
+ onClick={onClose}
103
+ className="absolute top-4 right-4 p-2 bg-black/50 hover:bg-black/70 rounded-full text-white transition-all"
104
+ aria-label="Close lightbox"
105
+ >
106
+ <Icon className="text-white">
107
+ <path
108
+ strokeLinecap="round"
109
+ strokeLinejoin="round"
110
+ strokeWidth={2}
111
+ d="M6 18L18 6M6 6l12 12"
112
+ />
113
+ </Icon>
114
+ </button>
115
+
116
+ {/* Navigation - Previous */}
117
+ {images.length > 1 && (
118
+ <button
119
+ onClick={(e) => {
120
+ e.stopPropagation();
121
+ goToPrevious();
122
+ }}
123
+ className="absolute left-4 p-2 bg-black/50 hover:bg-black/70 rounded-full text-white transition-all"
124
+ aria-label="Previous image"
125
+ >
126
+ <Icon className="text-white" size="lg">
127
+ <path
128
+ strokeLinecap="round"
129
+ strokeLinejoin="round"
130
+ strokeWidth={2}
131
+ d="M15 19l-7-7 7-7"
132
+ />
133
+ </Icon>
134
+ </button>
135
+ )}
136
+
137
+ {/* Navigation - Next */}
138
+ {images.length > 1 && (
139
+ <button
140
+ onClick={(e) => {
141
+ e.stopPropagation();
142
+ goToNext();
143
+ }}
144
+ className="absolute right-4 p-2 bg-black/50 hover:bg-black/70 rounded-full text-white transition-all"
145
+ aria-label="Next image"
146
+ >
147
+ <Icon className="text-white" size="lg">
148
+ <path
149
+ strokeLinecap="round"
150
+ strokeLinejoin="round"
151
+ strokeWidth={2}
152
+ d="M9 5l7 7-7 7"
153
+ />
154
+ </Icon>
155
+ </button>
156
+ )}
157
+
158
+ {/* Main Image */}
159
+ <div
160
+ className="relative max-w-5xl max-h-[90vh] w-full"
161
+ onClick={handleImageClick}
162
+ >
163
+ <img
164
+ src={currentImage.src}
165
+ alt={currentImage.alt}
166
+ className={cn(
167
+ 'w-full h-full object-contain',
168
+ isZoomed ? 'cursor-zoom-out' : 'cursor-zoom-in'
169
+ )}
170
+ style={{
171
+ maxHeight: '90vh',
172
+ objectFit: 'contain',
173
+ }}
174
+ />
175
+
176
+ {/* Zoom indicator */}
177
+ <div className="absolute bottom-4 right-4 flex items-center gap-2 bg-black/50 px-3 py-2 rounded-full">
178
+ <button
179
+ onClick={(e) => {
180
+ e.stopPropagation();
181
+ setIsZoomed(!isZoomed);
182
+ }}
183
+ className="p-1 hover:bg-black/70 rounded-full transition-all text-white"
184
+ aria-label={isZoomed ? 'Zoom out' : 'Zoom in'}
185
+ >
186
+ {isZoomed ? (
187
+ <Icon className="text-white" size="sm">
188
+ <path
189
+ strokeLinecap="round"
190
+ strokeLinejoin="round"
191
+ strokeWidth={2}
192
+ d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7"
193
+ />
194
+ </Icon>
195
+ ) : (
196
+ <Icon className="text-white" size="sm">
197
+ <path
198
+ strokeLinecap="round"
199
+ strokeLinejoin="round"
200
+ strokeWidth={2}
201
+ d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v6m3-3H7"
202
+ />
203
+ </Icon>
204
+ )}
205
+ </button>
206
+ </div>
207
+ </div>
208
+
209
+ {/* Image Counter */}
210
+ {images.length > 1 && (
211
+ <div className="absolute bottom-4 left-4 bg-black/50 px-4 py-2 rounded-full text-white text-sm">
212
+ {currentIndex + 1} / {images.length}
213
+ </div>
214
+ )}
215
+
216
+ {/* Image Title */}
217
+ {currentImage.title && (
218
+ <div className="absolute bottom-4 left-1/2 -translate-x-1/2 bg-black/50 px-4 py-2 rounded-full text-white text-sm max-w-md text-center">
219
+ {currentImage.title}
220
+ </div>
221
+ )}
222
+
223
+ {/* Thumbnails */}
224
+ {images.length > 1 && (
225
+ <div className="absolute bottom-20 left-1/2 -translate-x-1/2 flex gap-2 max-w-xl overflow-x-auto">
226
+ {images.map((image, index) => (
227
+ <button
228
+ key={index}
229
+ onClick={(e) => {
230
+ e.stopPropagation();
231
+ setCurrentIndex(index);
232
+ }}
233
+ className={cn(
234
+ 'flex-shrink-0 w-16 h-16 rounded-lg overflow-hidden border-2 transition-all',
235
+ index === currentIndex
236
+ ? 'border-white scale-110'
237
+ : 'border-transparent opacity-50 hover:opacity-100'
238
+ )}
239
+ >
240
+ <img
241
+ src={image.src}
242
+ alt={image.alt}
243
+ className="w-full h-full object-cover"
244
+ />
245
+ </button>
246
+ ))}
247
+ </div>
248
+ )}
249
+ </div>
250
+ );
251
+ };
252
+
253
+ ImageLightbox.displayName = 'ImageLightbox';
@@ -1,75 +1,24 @@
1
1
  /**
2
2
  * Organisms Export
3
- * @description Organism components - complex UI combinations
3
+ * @description Complex UI components composed of molecules and atoms
4
4
  * Subpath: @umituz/web-design-system/organisms
5
5
  */
6
6
 
7
- export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './Card';
8
- export type { CardProps } from './Card';
9
-
10
- export { Alert, AlertTitle, AlertDescription } from './Alert';
11
- export type { AlertProps } from './Alert';
12
-
13
- export { Modal, ModalHeader, ModalTitle, ModalDescription, ModalContent, ModalFooter } from './Modal';
14
- export type { ModalProps } from './Modal';
15
-
7
+ // Layout & Structure
16
8
  export { Navbar, NavbarBrand, NavbarLinks, NavbarActions } from './Navbar';
17
9
  export type { NavbarProps } from './Navbar';
18
10
 
19
- export {
20
- Table,
21
- TableHeader,
22
- TableBody,
23
- TableFooter,
24
- TableRow,
25
- TableHead,
26
- TableCell,
27
- TableCaption
28
- } from './Table';
29
- export type { TableProps } from './Table';
30
-
31
- export { Tabs } from './Tabs';
32
- export type { TabsProps, Tab } from './Tabs';
33
-
34
- export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './Accordion';
35
-
36
- export {
37
- Dialog,
38
- DialogPortal,
39
- DialogOverlay,
40
- DialogClose,
41
- DialogTrigger,
42
- DialogContent,
43
- DialogHeader,
44
- DialogFooter,
45
- DialogTitle,
46
- DialogDescription,
47
- } from './Dialog';
11
+ export { Footer } from './Footer';
48
12
 
49
13
  export { Breadcrumbs } from './Breadcrumb';
50
- export type { BreadcrumbsProps, BreadcrumbItem } from './Breadcrumb';
14
+ export type { BreadcrumbItem, BreadcrumbsProps } from './Breadcrumb';
51
15
 
52
- export { HoverCard, HoverCardTrigger, HoverCardContent } from './HoverCard';
53
-
54
- export { Popover, PopoverTrigger, PopoverContent } from './Popover';
55
-
56
- export { Collapsible, CollapsibleTrigger, CollapsibleContent } from './Collapsible';
57
-
58
- export {
59
- Sheet,
60
- SheetClose,
61
- SheetContent,
62
- SheetDescription,
63
- SheetFooter,
64
- SheetHeader,
65
- SheetOverlay,
66
- SheetPortal,
67
- SheetTitle,
68
- SheetTrigger,
69
- } from './Sheet';
16
+ // Data Display
17
+ export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './Card';
18
+ export type { CardProps, CardHeaderProps, CardTitleProps, CardDescriptionProps, CardContentProps, CardFooterProps } from './Card';
70
19
 
71
- export { Footer } from './Footer';
72
- export type { FooterProps } from './Footer';
20
+ export { StatCard } from './StatCard';
21
+ export type { StatCardProps } from './StatCard';
73
22
 
74
23
  export { MetricCard } from './MetricCard';
75
24
  export type { MetricCardProps } from './MetricCard';
@@ -77,57 +26,80 @@ export type { MetricCardProps } from './MetricCard';
77
26
  export { QuickActionCard } from './QuickActionCard';
78
27
  export type { QuickActionCardProps } from './QuickActionCard';
79
28
 
29
+ // Lists & Tables
30
+ export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell } from './Table';
31
+ export type { TableProps, TableHeaderProps, TableBodyProps, TableFooterProps, TableHeadProps, TableRowProps, TableCellProps } from './Table';
32
+
33
+ export { DataTable } from './DataTable';
34
+ export type { DataTableProps, ColumnDef } from './DataTable';
35
+
36
+ export { List, ListItem, ListHeader } from './List';
37
+ export type { ListProps, ListItemProps, ListHeaderProps } from './List';
38
+
39
+ // Feedback & Empty States
40
+ export { Alert, AlertTitle, AlertDescription } from './Alert';
41
+ export type { AlertProps, AlertTitleProps, AlertDescriptionProps } from './Alert';
42
+
80
43
  export { EmptyState } from './EmptyState';
81
44
  export type { EmptyStateProps } from './EmptyState';
82
45
 
83
46
  export { LoadingState } from './LoadingState';
84
47
  export type { LoadingStateProps } from './LoadingState';
85
48
 
86
- export { DataTable } from './DataTable';
87
- export type { DataTableProps, Column } from './DataTable';
49
+ // Overlays & Modals
50
+ export { Dialog, DialogHeader, DialogTitle, DialogDescription, DialogContent, DialogFooter } from './Dialog';
51
+ export type { DialogProps } from './Dialog';
88
52
 
89
- export { StatCard } from './StatCard';
90
- export type { StatCardProps } from './StatCard';
53
+ export { Modal } from './Modal';
54
+ export type { ModalProps } from './Modal';
91
55
 
92
56
  export { FormModal } from './FormModal';
93
57
  export type { FormModalProps } from './FormModal';
94
58
 
95
- export { ConfirmDialog } from './ConfirmDialog';
96
- export type { ConfirmDialogProps } from './ConfirmDialog';
59
+ export { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from './AlertDialog';
60
+ export type { AlertDialogProps } from './AlertDialog';
97
61
 
98
- export { Calendar } from './Calendar';
99
- export type { CalendarProps } from './Calendar';
62
+ export { Popover, PopoverTrigger, PopoverContent } from './Popover';
63
+ export type { PopoverProps } from './Popover';
64
+
65
+ export { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter } from './Sheet';
66
+ export type { SheetProps, SheetSide } from './Sheet';
67
+
68
+ export { HoverCard, HoverCardTrigger, HoverCardContent } from './HoverCard';
69
+ export type { HoverCardProps } from './HoverCard';
70
+
71
+ export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel } from './DropdownMenu';
72
+ export type { DropdownMenuProps } from './DropdownMenu';
100
73
 
101
- export {
102
- AlertDialog,
103
- AlertDialogPortal,
104
- AlertDialogOverlay,
105
- AlertDialogTrigger,
106
- AlertDialogContent,
107
- AlertDialogHeader,
108
- AlertDialogFooter,
109
- AlertDialogTitle,
110
- AlertDialogDescription,
111
- AlertDialogAction,
112
- AlertDialogCancel,
113
- } from './AlertDialog';
114
-
115
- export {
116
- DropdownMenu,
117
- DropdownMenuTrigger,
118
- DropdownMenuContent,
119
- DropdownMenuItem,
120
- DropdownMenuCheckboxItem,
121
- DropdownMenuRadioItem,
122
- DropdownMenuLabel,
123
- DropdownMenuSeparator,
124
- DropdownMenuShortcut,
125
- DropdownMenuGroup,
126
- DropdownMenuPortal,
127
- DropdownMenuSub,
128
- DropdownMenuSubContent,
129
- DropdownMenuSubTrigger,
130
- DropdownMenuRadioGroup,
131
- } from './DropdownMenu';
74
+ // Interactive Components
75
+ export { Tabs, TabsList, TabsTrigger, TabsContent } from './Tabs';
76
+ export type { TabsProps } from './Tabs';
77
+
78
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './Accordion';
79
+ export type { AccordionProps } from './Accordion';
80
+
81
+ export { Toggle } from './Toggle';
82
+ export type { ToggleProps } from './Toggle';
132
83
 
133
84
  export { ToggleGroup, ToggleGroupItem } from './ToggleGroup';
85
+ export type { ToggleGroupProps, ToggleGroupItemProps } from './ToggleGroup';
86
+
87
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent } from './Collapsible';
88
+ export type { CollapsibleProps } from './Collapsible';
89
+
90
+ // Form Components
91
+ export { CheckboxGroup } from './CheckboxGroup';
92
+ export type { CheckboxGroupProps } from './CheckboxGroup';
93
+
94
+ export { RadioGroup, RadioGroupItem } from './RadioGroup';
95
+ export type { RadioGroupProps, RadioGroupItemProps } from './RadioGroup';
96
+
97
+ export { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator } from './Select';
98
+ export type { SelectProps } from './Select';
99
+
100
+ export { Calendar } from './Calendar';
101
+ export type { CalendarProps } from './Calendar';
102
+
103
+ // NEW: Media & Content Components
104
+ export { ImageLightbox } from './ImageLightbox';
105
+ export type { ImageLightboxProps, ImageLightboxImage } from './ImageLightbox';