@umituz/web-design-system 5.3.1 → 6.0.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.
@@ -1,4 +1,3 @@
1
-
2
1
  export { FormField } from './FormField';
3
2
  export type { FormFieldProps } from './FormField';
4
3
 
@@ -17,7 +16,18 @@ export type { ChipProps } from './Chip';
17
16
  export { Toggle } from './Toggle';
18
17
  export type { ToggleProps } from './Toggle';
19
18
 
20
- export { Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator, SelectScrollUpButton, SelectScrollDownButton } from './Select';
19
+ export {
20
+ Select,
21
+ SelectGroup,
22
+ SelectValue,
23
+ SelectTrigger,
24
+ SelectContent,
25
+ SelectLabel,
26
+ SelectItem,
27
+ SelectSeparator,
28
+ SelectScrollUpButton,
29
+ SelectScrollDownButton,
30
+ } from './Select';
21
31
 
22
32
  export { Textarea } from './Textarea';
23
33
  export type { TextareaProps } from './Textarea';
@@ -64,3 +74,16 @@ export type { TagInputProps } from './TagInput';
64
74
 
65
75
  export { MarkdownText } from './MarkdownText';
66
76
  export type { MarkdownTextProps } from './MarkdownText';
77
+
78
+ // New Productivity Molecules
79
+ export { PasswordInput } from './PasswordInput';
80
+ export type { PasswordInputProps } from './PasswordInput';
81
+
82
+ export { SearchInput } from './SearchInput';
83
+ export type { SearchInputProps } from './SearchInput';
84
+
85
+ export { ConfirmButton } from './ConfirmButton';
86
+ export type { ConfirmButtonProps } from './ConfirmButton';
87
+
88
+ export { Toolbar } from './Toolbar';
89
+ export type { ToolbarProps } from './Toolbar';
@@ -0,0 +1,113 @@
1
+ import * as React from 'react';
2
+ import { Card } from './Card';
3
+ import { Badge, type BadgeVariant } from '../atoms/Badge';
4
+ import { CopyButton } from '../atoms/CopyButton';
5
+ import { cn } from '../../infrastructure/utils';
6
+
7
+ export interface DescriptionItem {
8
+ key?: string;
9
+ label: string;
10
+ value: React.ReactNode;
11
+ copyable?: boolean;
12
+ copyText?: string;
13
+ badge?: string;
14
+ badgeVariant?: BadgeVariant;
15
+ hint?: string;
16
+ }
17
+
18
+ export interface DescriptionListProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
19
+ title?: React.ReactNode;
20
+ description?: React.ReactNode;
21
+ action?: React.ReactNode;
22
+ items: DescriptionItem[];
23
+ columns?: 1 | 2;
24
+ bordered?: boolean;
25
+ }
26
+
27
+ export const DescriptionList = React.forwardRef<HTMLDivElement, DescriptionListProps>(
28
+ (
29
+ {
30
+ title,
31
+ description,
32
+ action,
33
+ items,
34
+ columns = 1,
35
+ bordered = true,
36
+ className,
37
+ ...props
38
+ },
39
+ ref
40
+ ) => {
41
+ const content = (
42
+ <div
43
+ ref={ref}
44
+ className={cn('w-full flex flex-col gap-4', className)}
45
+ {...props}
46
+ >
47
+ {(title || action || description) && (
48
+ <div className="flex items-start justify-between gap-4 pb-3 border-b border-border">
49
+ <div>
50
+ {title && (
51
+ <h3 className="text-base font-semibold text-foreground tracking-tight">
52
+ {title}
53
+ </h3>
54
+ )}
55
+ {description && (
56
+ <p className="text-sm text-muted-foreground mt-0.5">
57
+ {description}
58
+ </p>
59
+ )}
60
+ </div>
61
+ {action && <div className="shrink-0">{action}</div>}
62
+ </div>
63
+ )}
64
+
65
+ <dl
66
+ className={cn(
67
+ 'grid gap-x-6 gap-y-3',
68
+ columns === 2 ? 'grid-cols-1 sm:grid-cols-2' : 'grid-cols-1'
69
+ )}
70
+ >
71
+ {items.map((item, idx) => {
72
+ const rawCopyText = item.copyText ?? (typeof item.value === 'string' ? item.value : '');
73
+
74
+ return (
75
+ <div
76
+ key={item.key ?? idx}
77
+ className="flex items-center justify-between py-2 border-b border-border/60 last:border-0 gap-3"
78
+ >
79
+ <dt className="text-sm font-medium text-muted-foreground shrink-0">
80
+ {item.label}
81
+ {item.hint && (
82
+ <span className="block text-xs font-normal text-muted-foreground/75">
83
+ {item.hint}
84
+ </span>
85
+ )}
86
+ </dt>
87
+ <dd className="text-sm font-medium text-foreground flex items-center gap-2 text-right break-all">
88
+ {item.value}
89
+ {item.badge && (
90
+ <Badge variant={item.badgeVariant ?? 'secondary'} size="sm">
91
+ {item.badge}
92
+ </Badge>
93
+ )}
94
+ {item.copyable && rawCopyText && (
95
+ <CopyButton text={rawCopyText} size="xs" />
96
+ )}
97
+ </dd>
98
+ </div>
99
+ );
100
+ })}
101
+ </dl>
102
+ </div>
103
+ );
104
+
105
+ if (bordered) {
106
+ return <Card className="p-5">{content}</Card>;
107
+ }
108
+
109
+ return content;
110
+ }
111
+ );
112
+
113
+ DescriptionList.displayName = 'DescriptionList';
@@ -0,0 +1,110 @@
1
+ import * as React from 'react';
2
+ import { Card } from './Card';
3
+ import { Skeleton } from '../atoms/Skeleton';
4
+ import { TrendingUp, TrendingDown, Minus } from 'lucide-react';
5
+ import { cn } from '../../infrastructure/utils';
6
+
7
+ export interface StatItem {
8
+ id?: string;
9
+ title: string;
10
+ value: string | number;
11
+ change?: string;
12
+ trend?: 'up' | 'down' | 'neutral';
13
+ icon?: React.ReactNode;
14
+ description?: string;
15
+ }
16
+
17
+ export interface StatsGridProps extends React.HTMLAttributes<HTMLDivElement> {
18
+ stats: StatItem[];
19
+ columns?: 2 | 3 | 4;
20
+ loading?: boolean;
21
+ }
22
+
23
+ const colStyles = {
24
+ 2: 'grid-cols-1 sm:grid-cols-2',
25
+ 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
26
+ 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',
27
+ };
28
+
29
+ export const StatsGrid = React.forwardRef<HTMLDivElement, StatsGridProps>(
30
+ ({ stats, columns = 4, loading = false, className, ...props }, ref) => {
31
+ if (loading) {
32
+ return (
33
+ <div
34
+ ref={ref}
35
+ className={cn('grid gap-4 w-full', colStyles[columns], className)}
36
+ {...props}
37
+ >
38
+ {Array.from({ length: columns }).map((_, i) => (
39
+ <Card key={i} className="p-5 flex flex-col gap-3">
40
+ <Skeleton className="h-4 w-24" />
41
+ <Skeleton className="h-8 w-32" />
42
+ <Skeleton className="h-3 w-40" />
43
+ </Card>
44
+ ))}
45
+ </div>
46
+ );
47
+ }
48
+
49
+ return (
50
+ <div
51
+ ref={ref}
52
+ className={cn('grid gap-4 w-full', colStyles[columns], className)}
53
+ {...props}
54
+ >
55
+ {stats.map((stat, idx) => {
56
+ const isUp = stat.trend === 'up';
57
+ const isDown = stat.trend === 'down';
58
+
59
+ return (
60
+ <Card
61
+ key={stat.id ?? idx}
62
+ className="p-5 flex flex-col justify-between transition-shadow hover:shadow-md"
63
+ >
64
+ <div className="flex items-start justify-between gap-2">
65
+ <span className="text-sm font-medium text-muted-foreground truncate">
66
+ {stat.title}
67
+ </span>
68
+ {stat.icon && (
69
+ <div className="size-9 rounded-lg bg-accent/60 flex items-center justify-center text-foreground shrink-0 [&_svg]:size-4">
70
+ {stat.icon}
71
+ </div>
72
+ )}
73
+ </div>
74
+
75
+ <div className="mt-3 flex items-baseline justify-between gap-2">
76
+ <div className="text-2xl font-bold tracking-tight text-foreground">
77
+ {stat.value}
78
+ </div>
79
+
80
+ {stat.change && (
81
+ <span
82
+ className={cn(
83
+ 'inline-flex items-center gap-1 text-xs font-semibold px-2 py-0.5 rounded-full',
84
+ isUp && 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400',
85
+ isDown && 'bg-rose-500/10 text-rose-600 dark:text-rose-400',
86
+ !isUp && !isDown && 'bg-accent text-muted-foreground'
87
+ )}
88
+ >
89
+ {isUp && <TrendingUp className="size-3" />}
90
+ {isDown && <TrendingDown className="size-3" />}
91
+ {!isUp && !isDown && <Minus className="size-3" />}
92
+ <span>{stat.change}</span>
93
+ </span>
94
+ )}
95
+ </div>
96
+
97
+ {stat.description && (
98
+ <p className="mt-2 text-xs text-muted-foreground line-clamp-1">
99
+ {stat.description}
100
+ </p>
101
+ )}
102
+ </Card>
103
+ );
104
+ })}
105
+ </div>
106
+ );
107
+ }
108
+ );
109
+
110
+ StatsGrid.displayName = 'StatsGrid';
@@ -1,4 +1,3 @@
1
-
2
1
  export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './Card';
3
2
  export type { CardProps, CardHeaderProps } from './Card';
4
3
 
@@ -13,7 +12,7 @@ export {
13
12
  TableRow,
14
13
  TableHead,
15
14
  TableCell,
16
- TableCaption
15
+ TableCaption,
17
16
  } from './Table';
18
17
  export type { TableProps } from './Table';
19
18
 
@@ -36,10 +35,10 @@ export {
36
35
  } from './Dialog';
37
36
 
38
37
  export { CommandPalette } from './CommandPalette';
38
+ export type { CommandPaletteProps, CommandGroup, CommandItem } from './CommandPalette';
39
39
 
40
40
  export { ErrorBoundary } from './ErrorBoundary';
41
41
  export type { ErrorBoundaryProps } from './ErrorBoundary';
42
- export type { CommandPaletteProps, CommandGroup, CommandItem } from './CommandPalette';
43
42
 
44
43
  export { Breadcrumbs } from './Breadcrumb';
45
44
  export type { BreadcrumbsProps, BreadcrumbItem } from './Breadcrumb';
@@ -112,7 +111,7 @@ export {
112
111
  export { ToggleGroup, ToggleGroupItem } from './ToggleGroup';
113
112
 
114
113
  export { ToastProvider, useToast } from './Toaster';
115
- export type { ToastKind, ToastOptions } from './Toaster';
114
+ export type { ToastKind, ToastOptions, ToastShorthandOptions } from './Toaster';
116
115
 
117
116
  export { KeyValueList } from './KeyValueList';
118
117
  export type { KeyValueListProps, KeyValueItem } from './KeyValueList';
@@ -149,8 +148,6 @@ export type { PhotoGalleryProps, PhotoTile } from './PhotoGallery';
149
148
  export { Lightbox } from './Lightbox';
150
149
  export type { LightboxProps } from './Lightbox';
151
150
 
152
- export type { ToastShorthandOptions } from './Toaster';
153
-
154
151
  export { Timeline } from './Timeline';
155
152
  export type { TimelineProps, TimelineItem, TimelineTone } from './Timeline';
156
153
 
@@ -181,3 +178,10 @@ export type { PlanPickerProps, PlanPickerPlan, PlanPickerCycle } from './PlanPic
181
178
 
182
179
  export { CoverArt } from './CoverArt';
183
180
  export type { CoverArtProps } from './CoverArt';
181
+
182
+ // New Productivity Organisms
183
+ export { StatsGrid } from './StatsGrid';
184
+ export type { StatsGridProps, StatItem } from './StatsGrid';
185
+
186
+ export { DescriptionList } from './DescriptionList';
187
+ export type { DescriptionListProps, DescriptionItem } from './DescriptionList';
@@ -0,0 +1,69 @@
1
+ import * as React from 'react';
2
+ import { Breadcrumbs, type BreadcrumbItem } from '../organisms/Breadcrumb';
3
+ import { cn } from '../../infrastructure/utils';
4
+
5
+ export interface CrudPageTemplateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
6
+ title: React.ReactNode;
7
+ description?: React.ReactNode;
8
+ breadcrumbItems?: BreadcrumbItem[];
9
+ actions?: React.ReactNode;
10
+ stats?: React.ReactNode;
11
+ toolbar?: React.ReactNode;
12
+ children: React.ReactNode;
13
+ }
14
+
15
+ export const CrudPageTemplate = React.forwardRef<HTMLDivElement, CrudPageTemplateProps>(
16
+ (
17
+ {
18
+ title,
19
+ description,
20
+ breadcrumbItems,
21
+ actions,
22
+ stats,
23
+ toolbar,
24
+ children,
25
+ className,
26
+ ...props
27
+ },
28
+ ref
29
+ ) => {
30
+ return (
31
+ <div
32
+ ref={ref}
33
+ className={cn('w-full flex flex-col gap-6 p-4 sm:p-6 lg:p-8', className)}
34
+ {...props}
35
+ >
36
+ {/* Breadcrumbs */}
37
+ {breadcrumbItems && breadcrumbItems.length > 0 && (
38
+ <div>
39
+ <Breadcrumbs items={breadcrumbItems} />
40
+ </div>
41
+ )}
42
+
43
+ {/* Page Header */}
44
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
45
+ <div>
46
+ <h1 className="text-2xl sm:text-3xl font-bold tracking-tight text-foreground">
47
+ {title}
48
+ </h1>
49
+ {description && (
50
+ <p className="text-sm text-muted-foreground mt-1">{description}</p>
51
+ )}
52
+ </div>
53
+ {actions && <div className="flex items-center gap-2 shrink-0">{actions}</div>}
54
+ </div>
55
+
56
+ {/* Optional Stats Grid */}
57
+ {stats && <div>{stats}</div>}
58
+
59
+ {/* Optional Search / Filter Toolbar */}
60
+ {toolbar && <div>{toolbar}</div>}
61
+
62
+ {/* Main Content (Table / List / Cards) */}
63
+ <div className="w-full">{children}</div>
64
+ </div>
65
+ );
66
+ }
67
+ );
68
+
69
+ CrudPageTemplate.displayName = 'CrudPageTemplate';
@@ -0,0 +1,102 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../../infrastructure/utils';
3
+
4
+ export interface SettingsNavItem {
5
+ id: string;
6
+ label: string;
7
+ icon?: React.ReactNode;
8
+ description?: string;
9
+ badge?: string;
10
+ disabled?: boolean;
11
+ }
12
+
13
+ export interface SettingsLayoutProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title' | 'onSelect'> {
14
+ title?: React.ReactNode;
15
+ description?: React.ReactNode;
16
+ navItems: SettingsNavItem[];
17
+ activeId: string;
18
+ onSelect: (id: string) => void;
19
+ actions?: React.ReactNode;
20
+ }
21
+
22
+ export const SettingsLayout = React.forwardRef<HTMLDivElement, SettingsLayoutProps>(
23
+ (
24
+ {
25
+ title = 'Ayarlar',
26
+ description,
27
+ navItems,
28
+ activeId,
29
+ onSelect,
30
+ actions,
31
+ children,
32
+ className,
33
+ ...props
34
+ },
35
+ ref
36
+ ) => {
37
+ return (
38
+ <div
39
+ ref={ref}
40
+ className={cn('w-full max-w-6xl mx-auto flex flex-col gap-6 p-4 sm:p-6 lg:p-8', className)}
41
+ {...props}
42
+ >
43
+ {/* Header */}
44
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 pb-4 border-b border-border">
45
+ <div>
46
+ <h1 className="text-2xl font-bold tracking-tight text-foreground">{title}</h1>
47
+ {description && (
48
+ <p className="text-sm text-muted-foreground mt-1">{description}</p>
49
+ )}
50
+ </div>
51
+ {actions && <div className="flex items-center gap-2 shrink-0">{actions}</div>}
52
+ </div>
53
+
54
+ {/* Content Layout */}
55
+ <div className="flex flex-col lg:flex-row gap-8 items-start">
56
+ {/* Navigation Sidebar */}
57
+ <nav
58
+ aria-label="Settings navigation"
59
+ className="w-full lg:w-64 shrink-0 flex flex-row lg:flex-col gap-1 overflow-x-auto pb-2 lg:pb-0"
60
+ >
61
+ {navItems.map((item) => {
62
+ const isActive = item.id === activeId;
63
+
64
+ return (
65
+ <button
66
+ key={item.id}
67
+ type="button"
68
+ disabled={item.disabled}
69
+ onClick={() => onSelect(item.id)}
70
+ className={cn(
71
+ 'flex items-center gap-3 px-3.5 py-2.5 rounded-lg text-sm font-medium transition-colors text-left shrink-0 whitespace-nowrap lg:whitespace-normal',
72
+ isActive
73
+ ? 'bg-primary/10 text-primary font-semibold'
74
+ : 'text-muted-foreground hover:bg-accent hover:text-foreground',
75
+ item.disabled && 'opacity-50 pointer-events-none'
76
+ )}
77
+ >
78
+ {item.icon && (
79
+ <span className="shrink-0 size-4 flex items-center justify-center [&_svg]:size-4">
80
+ {item.icon}
81
+ </span>
82
+ )}
83
+ <span className="flex-1 truncate">{item.label}</span>
84
+ {item.badge && (
85
+ <span className="text-xs px-2 py-0.5 rounded-full bg-accent text-accent-foreground ml-auto">
86
+ {item.badge}
87
+ </span>
88
+ )}
89
+ </button>
90
+ );
91
+ })}
92
+ </nav>
93
+
94
+ {/* Main Panel Content */}
95
+ <main className="flex-1 w-full min-w-0">{children}</main>
96
+ </div>
97
+ </div>
98
+ );
99
+ }
100
+ );
101
+
102
+ SettingsLayout.displayName = 'SettingsLayout';
@@ -1,4 +1,3 @@
1
-
2
1
  export { Form, FormActions } from './Form';
3
2
  export type { FormProps } from './Form';
4
3
 
@@ -19,3 +18,10 @@ export type { ProjectSkeletonProps } from './ProjectSkeleton';
19
18
 
20
19
  export { ResponsiveContainer } from './ResponsiveContainer';
21
20
  export type { ResponsiveContainerProps, ResponsiveSize } from './ResponsiveContainer';
21
+
22
+ // New Productivity Templates
23
+ export { SettingsLayout } from './SettingsLayout';
24
+ export type { SettingsLayoutProps, SettingsNavItem } from './SettingsLayout';
25
+
26
+ export { CrudPageTemplate } from './CrudPageTemplate';
27
+ export type { CrudPageTemplateProps } from './CrudPageTemplate';