@umituz/web-design-system 2.1.0 → 2.3.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.1.0",
3
+ "version": "2.3.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",
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Comments Component (Organism)
3
+ * @description Giscus-based GitHub Discussions comment widget
4
+ */
5
+
6
+ import { useEffect, useRef } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps } from '../../domain/types';
9
+
10
+ export interface GiscusConfig {
11
+ repo: string;
12
+ repoId: string;
13
+ category?: string;
14
+ mapping?: 'pathname' | 'url' | 'title' | 'og:title' | 'custom';
15
+ term?: string;
16
+ strict?: '0' | '1';
17
+ reactionsEnabled?: '0' | '1';
18
+ emitMetadata?: '0' | '1';
19
+ inputPosition?: 'top' | 'bottom';
20
+ theme?: 'light' | 'dark' | 'dark_dimmed' | 'transparent_dark' | 'preferred_color_scheme';
21
+ lang?: string;
22
+ }
23
+
24
+ export interface CommentsProps extends BaseProps {
25
+ slug: string;
26
+ config?: Partial<GiscusConfig>;
27
+ title?: string;
28
+ description?: string;
29
+ }
30
+
31
+ declare global {
32
+ interface Window {
33
+ giscus?: {
34
+ render: (element: HTMLElement, config: Record<string, unknown>) => void;
35
+ };
36
+ }
37
+ }
38
+
39
+ export const Comments = ({
40
+ slug,
41
+ config,
42
+ title = 'Comments',
43
+ description = 'Join the discussion! Share your thoughts and questions below.',
44
+ className,
45
+ }: CommentsProps) => {
46
+ const rootRef = useRef<HTMLDivElement>(null);
47
+
48
+ useEffect(() => {
49
+ // Load Giscus script dynamically
50
+ const script = document.createElement('script');
51
+ script.src = 'https://giscus.app/client.js';
52
+ script.async = true;
53
+ script.crossOrigin = 'anonymous';
54
+
55
+ script.addEventListener('load', () => {
56
+ if (window.giscus && rootRef.current) {
57
+ const defaultConfig: GiscusConfig = {
58
+ repo: 'umituz/umituz-apps',
59
+ repoId: 'R_kgDONJ7RJw',
60
+ category: 'Announcements',
61
+ mapping: 'pathname',
62
+ term: slug,
63
+ strict: '0',
64
+ reactionsEnabled: '1',
65
+ emitMetadata: '0',
66
+ inputPosition: 'top',
67
+ theme: 'dark',
68
+ lang: 'en',
69
+ ...config,
70
+ };
71
+
72
+ window.giscus.render(rootRef.current, defaultConfig);
73
+ }
74
+ });
75
+
76
+ document.head.appendChild(script);
77
+
78
+ return () => {
79
+ // Only remove if script exists and is in DOM
80
+ if (script.parentNode === document.head) {
81
+ document.head.removeChild(script);
82
+ }
83
+ };
84
+ }, [slug, config]);
85
+
86
+ return (
87
+ <div className={cn('mt-8', className)}>
88
+ <div className="bg-bg-card rounded-xl p-6 border border-border transition-theme">
89
+ <h3 className="text-xl font-bold text-text-primary mb-4">{title}</h3>
90
+ <p className="text-text-secondary text-sm mb-4">
91
+ {description}
92
+ </p>
93
+ <div
94
+ ref={rootRef}
95
+ id="giscus-comments"
96
+ className="min-h-[200px]"
97
+ />
98
+ </div>
99
+ </div>
100
+ );
101
+ };
102
+
103
+ Comments.displayName = 'Comments';
@@ -26,7 +26,6 @@ export const ImageLightbox = ({
26
26
  initialIndex = 0,
27
27
  isOpen,
28
28
  onClose,
29
- className,
30
29
  }: ImageLightboxProps) => {
31
30
  // Early return if closed
32
31
  if (!isOpen) return null;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * NewsletterSignup Component (Organism)
3
+ * @description Newsletter subscription form with email validation
4
+ */
5
+
6
+ import { useState, useEffect, useMemo } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps } from '../../domain/types';
9
+ import { Icon } from '../atoms/Icon';
10
+
11
+ const SUBSCRIBER_COUNT_MIN = 1000;
12
+ const SUBSCRIBER_COUNT_MAX = 6000;
13
+
14
+ export interface NewsletterSignupProps extends BaseProps {
15
+ onSubscribe?: (email: string) => Promise<void>;
16
+ subscriberCountMin?: number;
17
+ subscriberCountMax?: number;
18
+ }
19
+
20
+ export const NewsletterSignup = ({
21
+ onSubscribe,
22
+ subscriberCountMin = SUBSCRIBER_COUNT_MIN,
23
+ subscriberCountMax = SUBSCRIBER_COUNT_MAX,
24
+ className,
25
+ }: NewsletterSignupProps) => {
26
+ const [email, setEmail] = useState('');
27
+ const [isSubscribed, setIsSubscribed] = useState(false);
28
+ const [isLoading, setIsLoading] = useState(false);
29
+ const [error, setError] = useState('');
30
+
31
+ // FIXED: Generate random count per component instance, not at module load
32
+ const subscriberCount = useMemo(
33
+ () => Math.floor(Math.random() * (subscriberCountMax - subscriberCountMin + 1)) + subscriberCountMin,
34
+ [subscriberCountMin, subscriberCountMax]
35
+ );
36
+
37
+ const validateEmail = (email: string) => {
38
+ const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
39
+ return re.test(email);
40
+ };
41
+
42
+ // Reset success message after 5 seconds with cleanup
43
+ useEffect(() => {
44
+ if (isSubscribed) {
45
+ const timeout = setTimeout(() => {
46
+ setIsSubscribed(false);
47
+ }, 5000);
48
+ return () => clearTimeout(timeout);
49
+ }
50
+ }, [isSubscribed]);
51
+
52
+ const handleSubmit = async (e: React.FormEvent) => {
53
+ e.preventDefault();
54
+ setError('');
55
+
56
+ // Validate email
57
+ if (!email) {
58
+ setError('Please enter your email address');
59
+ return;
60
+ }
61
+
62
+ if (!validateEmail(email)) {
63
+ setError('Please enter a valid email address');
64
+ return;
65
+ }
66
+
67
+ setIsLoading(true);
68
+
69
+ try {
70
+ // Call the subscribe function if provided
71
+ if (onSubscribe) {
72
+ await onSubscribe(email);
73
+ } else {
74
+ // Simulate API call for demo purposes
75
+ await new Promise(resolve => setTimeout(resolve, 1000));
76
+ }
77
+
78
+ setIsLoading(false);
79
+ setIsSubscribed(true);
80
+ setEmail('');
81
+ } catch (err) {
82
+ setIsLoading(false);
83
+ setError('Failed to subscribe. Please try again.');
84
+ }
85
+ };
86
+
87
+ if (isSubscribed) {
88
+ return (
89
+ <div className={cn('bg-bg-card rounded-xl p-4 border border-border', className)}>
90
+ <div className="flex items-center gap-3 text-green-600">
91
+ <Icon className="text-green-600" size="lg">
92
+ <path
93
+ strokeLinecap="round"
94
+ strokeLinejoin="round"
95
+ strokeWidth={2}
96
+ d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
97
+ />
98
+ </Icon>
99
+ <div>
100
+ <div className="font-semibold text-sm">Thanks for subscribing!</div>
101
+ <div className="text-xs text-text-secondary">Check your inbox to confirm your subscription</div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ );
106
+ }
107
+
108
+ return (
109
+ <div className={cn('bg-bg-card rounded-xl p-4 border border-border', className)}>
110
+ <div className="flex items-center gap-2 mb-3">
111
+ <Icon className="text-primary-light" size="lg">
112
+ <path
113
+ strokeLinecap="round"
114
+ strokeLinejoin="round"
115
+ strokeWidth={2}
116
+ d="M3 8l7.89 5.26a2 2 0 002.22 0l7.89-5.26a2 2 0 002.22 0L21 8V5a2 2 0 00-2-2H5a2 2 0 00-2 2v3a2 2 0 002.22 0z"
117
+ />
118
+ </Icon>
119
+ <h3 className="text-sm font-semibold text-text-primary">Newsletter</h3>
120
+ </div>
121
+ <p className="text-xs text-text-secondary mb-3">
122
+ Get the latest articles delivered straight to your inbox. No spam, ever.
123
+ </p>
124
+ <form onSubmit={handleSubmit} className="space-y-2">
125
+ <input
126
+ type="email"
127
+ placeholder="your@email.com"
128
+ value={email}
129
+ onChange={(e) => setEmail(e.target.value)}
130
+ className="w-full px-3 py-2 bg-bg-secondary text-text-primary rounded-lg border border-border focus:border-primary-light focus:outline-none placeholder-text-secondary/50 transition-theme text-sm"
131
+ disabled={isLoading}
132
+ />
133
+ {error && (
134
+ <p className="text-xs text-red-500">{error}</p>
135
+ )}
136
+ <button
137
+ type="submit"
138
+ disabled={isLoading}
139
+ className="w-full flex items-center justify-center gap-2 px-4 py-2 bg-primary-gradient text-text-primary rounded-lg font-medium hover:shadow-lg transition-all disabled:opacity-50 disabled:cursor-not-allowed text-sm"
140
+ >
141
+ {isLoading ? (
142
+ <>Subscribing...</>
143
+ ) : (
144
+ <>
145
+ <Icon size="sm">
146
+ <path
147
+ strokeLinecap="round"
148
+ strokeLinejoin="round"
149
+ strokeWidth={2}
150
+ d="M22 2L11 13M22 2l-7 20M2 2l15 15L2 2l15-15"
151
+ />
152
+ </Icon>
153
+ Subscribe
154
+ </>
155
+ )}
156
+ </button>
157
+ </form>
158
+ <p className="text-xs text-text-secondary mt-2">
159
+ Join {subscriberCount.toLocaleString()}+ subscribers
160
+ </p>
161
+ </div>
162
+ );
163
+ };
164
+
165
+ NewsletterSignup.displayName = 'NewsletterSignup';
@@ -1,24 +1,75 @@
1
1
  /**
2
2
  * Organisms Export
3
- * @description Complex UI components composed of molecules and atoms
3
+ * @description Organism components - complex UI combinations
4
4
  * Subpath: @umituz/web-design-system/organisms
5
5
  */
6
6
 
7
- // Layout & Structure
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
+
8
16
  export { Navbar, NavbarBrand, NavbarLinks, NavbarActions } from './Navbar';
9
17
  export type { NavbarProps } from './Navbar';
10
18
 
11
- export { Footer } from './Footer';
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';
12
48
 
13
49
  export { Breadcrumbs } from './Breadcrumb';
14
- export type { BreadcrumbItem, BreadcrumbsProps } from './Breadcrumb';
50
+ export type { BreadcrumbsProps, BreadcrumbItem } from './Breadcrumb';
15
51
 
16
- // Data Display
17
- export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './Card';
18
- export type { CardProps, CardHeaderProps, CardTitleProps, CardDescriptionProps, CardContentProps, CardFooterProps } from './Card';
52
+ export { HoverCard, HoverCardTrigger, HoverCardContent } from './HoverCard';
19
53
 
20
- export { StatCard } from './StatCard';
21
- export type { StatCardProps } from './StatCard';
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';
70
+
71
+ export { Footer } from './Footer';
72
+ export type { FooterProps } from './Footer';
22
73
 
23
74
  export { MetricCard } from './MetricCard';
24
75
  export type { MetricCardProps } from './MetricCard';
@@ -26,80 +77,68 @@ export type { MetricCardProps } from './MetricCard';
26
77
  export { QuickActionCard } from './QuickActionCard';
27
78
  export type { QuickActionCardProps } from './QuickActionCard';
28
79
 
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
-
43
80
  export { EmptyState } from './EmptyState';
44
81
  export type { EmptyStateProps } from './EmptyState';
45
82
 
46
83
  export { LoadingState } from './LoadingState';
47
84
  export type { LoadingStateProps } from './LoadingState';
48
85
 
49
- // Overlays & Modals
50
- export { Dialog, DialogHeader, DialogTitle, DialogDescription, DialogContent, DialogFooter } from './Dialog';
51
- export type { DialogProps } from './Dialog';
86
+ export { DataTable } from './DataTable';
87
+ export type { DataTableProps, Column } from './DataTable';
52
88
 
53
- export { Modal } from './Modal';
54
- export type { ModalProps } from './Modal';
89
+ export { StatCard } from './StatCard';
90
+ export type { StatCardProps } from './StatCard';
55
91
 
56
92
  export { FormModal } from './FormModal';
57
93
  export type { FormModalProps } from './FormModal';
58
94
 
59
- export { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from './AlertDialog';
60
- export type { AlertDialogProps } from './AlertDialog';
61
-
62
- export { Popover, PopoverTrigger, PopoverContent } from './Popover';
63
- export type { PopoverProps } from './Popover';
95
+ export { ConfirmDialog } from './ConfirmDialog';
96
+ export type { ConfirmDialogProps } from './ConfirmDialog';
64
97
 
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';
73
-
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';
98
+ export { Calendar } from './Calendar';
99
+ export type { CalendarProps } from './Calendar';
80
100
 
81
- export { Toggle } from './Toggle';
82
- export type { ToggleProps } from './Toggle';
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';
83
132
 
84
133
  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
134
 
103
135
  // NEW: Media & Content Components
104
136
  export { ImageLightbox } from './ImageLightbox';
105
137
  export type { ImageLightboxProps, ImageLightboxImage } from './ImageLightbox';
138
+
139
+ // NEW: Content & Engagement Components
140
+ export { NewsletterSignup } from './NewsletterSignup';
141
+ export type { NewsletterSignupProps } from './NewsletterSignup';
142
+
143
+ export { Comments } from './Comments';
144
+ export type { CommentsProps, GiscusConfig } from './Comments';