@moontra/moonui 2.0.8 → 2.0.9
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 +9 -2
- package/src/__tests__/use-intersection-observer.test.tsx +216 -0
- package/src/__tests__/use-local-storage.test.tsx +174 -0
- package/src/__tests__/use-pro-access.test.tsx +183 -0
- package/src/components/ui/__tests__/accordion.test.tsx +571 -0
- package/src/components/ui/__tests__/alert.test.tsx +484 -0
- package/src/components/ui/__tests__/aspect-ratio.test.tsx +492 -0
- package/src/components/ui/__tests__/avatar.test.tsx +382 -0
- package/src/components/ui/__tests__/badge.test.tsx +364 -0
- package/src/components/ui/__tests__/breadcrumb.test.tsx +687 -0
- package/src/components/ui/__tests__/button.test.tsx +91 -0
- package/src/components/ui/__tests__/card.test.tsx +104 -0
- package/src/components/ui/__tests__/checkbox.test.tsx +591 -0
- package/src/components/ui/__tests__/collapsible.test.tsx +592 -0
- package/src/components/ui/__tests__/color-picker.test.tsx +365 -0
- package/src/components/ui/__tests__/command.test.tsx +677 -0
- package/src/components/ui/__tests__/data-table.test.tsx +256 -0
- package/src/components/ui/__tests__/date-picker.test.tsx +320 -0
- package/src/components/ui/__tests__/dialog.test.tsx +764 -0
- package/src/components/ui/__tests__/input.test.tsx +318 -0
- package/src/components/ui/__tests__/label.test.tsx +103 -0
- package/src/components/ui/__tests__/popover.test.tsx +637 -0
- package/src/components/ui/__tests__/progress.test.tsx +382 -0
- package/src/components/ui/__tests__/radio-group.test.tsx +555 -0
- package/src/components/ui/__tests__/select.test.tsx +705 -0
- package/src/components/ui/__tests__/skeleton.test.tsx +253 -0
- package/src/components/ui/__tests__/slider.test.tsx +493 -0
- package/src/components/ui/__tests__/switch.test.tsx +372 -0
- package/src/components/ui/__tests__/table.test.tsx +824 -0
- package/src/components/ui/__tests__/tabs.test.tsx +887 -0
- package/src/components/ui/__tests__/toast.test.tsx +458 -0
- package/src/components/ui/__tests__/tooltip.test.tsx +583 -0
- package/src/components/ui/accordion.tsx +63 -0
- package/src/components/ui/alert.tsx +130 -0
- package/src/components/ui/aspect-ratio.tsx +72 -0
- package/src/components/ui/avatar.tsx +135 -0
- package/src/components/ui/badge.tsx +225 -0
- package/src/components/ui/breadcrumb.tsx +207 -0
- package/src/components/ui/button.stories.tsx +162 -0
- package/src/components/ui/button.tsx +221 -0
- package/src/components/ui/calendar.tsx +262 -0
- package/src/components/ui/card.stories.tsx +208 -0
- package/src/components/ui/card.tsx +141 -0
- package/src/components/ui/checkbox.tsx +256 -0
- package/src/components/ui/collapsible.tsx +129 -0
- package/src/components/ui/color-picker.tsx +664 -0
- package/src/components/ui/command.tsx +218 -0
- package/src/components/ui/data-table.stories.tsx +509 -0
- package/src/components/ui/data-table.tsx +623 -0
- package/src/components/ui/date-picker.stories.tsx +321 -0
- package/src/components/ui/date-picker.tsx +603 -0
- package/src/components/ui/dialog.tsx +332 -0
- package/src/components/ui/dropdown-menu.tsx +200 -0
- package/src/components/ui/file-upload.tsx +400 -0
- package/src/components/ui/github-stars.tsx +201 -0
- package/src/components/ui/index.ts +12 -0
- package/src/components/ui/input.stories.tsx +255 -0
- package/src/components/ui/input.tsx +219 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/locked-component.tsx +215 -0
- package/src/components/ui/moon-logo.tsx +97 -0
- package/src/components/ui/pagination.tsx +116 -0
- package/src/components/ui/popover.tsx +183 -0
- package/src/components/ui/progress.tsx +182 -0
- package/src/components/ui/radio-group.tsx +243 -0
- package/src/components/ui/select.tsx +273 -0
- package/src/components/ui/separator.tsx +140 -0
- package/src/components/ui/simple-editor.tsx +652 -0
- package/src/components/ui/skeleton.tsx +351 -0
- package/src/components/ui/slider.tsx +351 -0
- package/src/components/ui/switch.tsx +83 -0
- package/src/components/ui/table.tsx +322 -0
- package/src/components/ui/tabs.tsx +195 -0
- package/src/components/ui/textarea.tsx +46 -0
- package/src/components/ui/toast.tsx +313 -0
- package/src/components/ui/toggle.tsx +47 -0
- package/src/components/ui/tooltip.tsx +152 -0
- package/src/docs/theme-system.md +1194 -0
- package/src/index.tsx +39 -0
- package/src/lib/micro-interactions.ts +255 -0
- package/src/lib/theme.tsx +398 -0
- package/src/lib/utils.ts +69 -0
- package/src/styles/design-system.css +365 -0
- package/src/styles/index.css +4 -0
- package/src/styles/tailwind.css +6 -0
- package/src/styles/tokens.css +453 -0
- package/src/use-intersection-observer.tsx +154 -0
- package/src/use-local-storage.tsx +71 -0
- package/src/use-paddle.ts +138 -0
- package/src/use-performance-optimizer.ts +379 -0
- package/src/use-pro-access.ts +141 -0
- package/src/use-scroll-animation.ts +221 -0
- package/src/use-subscription.ts +37 -0
- package/src/use-toast.ts +32 -0
package/src/index.tsx
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Import CSS - Must be first
|
|
2
|
+
import "./styles/index.css"
|
|
3
|
+
|
|
4
|
+
// FREE UI Components Export - Source of Truth
|
|
5
|
+
export * from "./components/ui/accordion"
|
|
6
|
+
export * from "./components/ui/alert"
|
|
7
|
+
export * from "./components/ui/aspect-ratio"
|
|
8
|
+
export * from "./components/ui/avatar"
|
|
9
|
+
export * from "./components/ui/badge"
|
|
10
|
+
export * from "./components/ui/breadcrumb"
|
|
11
|
+
export * from "./components/ui/button"
|
|
12
|
+
export * from "./components/ui/calendar"
|
|
13
|
+
export * from "./components/ui/card"
|
|
14
|
+
export * from "./components/ui/checkbox"
|
|
15
|
+
export * from "./components/ui/collapsible"
|
|
16
|
+
export * from "./components/ui/command"
|
|
17
|
+
export * from "./components/ui/date-picker"
|
|
18
|
+
export * from "./components/ui/dialog"
|
|
19
|
+
export * from "./components/ui/dropdown-menu"
|
|
20
|
+
export * from "./components/ui/input"
|
|
21
|
+
export * from "./components/ui/label"
|
|
22
|
+
export * from "./components/ui/pagination"
|
|
23
|
+
export * from "./components/ui/popover"
|
|
24
|
+
export * from "./components/ui/progress"
|
|
25
|
+
export * from "./components/ui/radio-group"
|
|
26
|
+
export * from "./components/ui/select"
|
|
27
|
+
export * from "./components/ui/separator"
|
|
28
|
+
export * from "./components/ui/skeleton"
|
|
29
|
+
export * from "./components/ui/slider"
|
|
30
|
+
export * from "./components/ui/switch"
|
|
31
|
+
export * from "./components/ui/table"
|
|
32
|
+
export * from "./components/ui/tabs"
|
|
33
|
+
export * from "./components/ui/textarea"
|
|
34
|
+
export * from "./components/ui/toast"
|
|
35
|
+
export * from "./components/ui/toggle"
|
|
36
|
+
export * from "./components/ui/tooltip"
|
|
37
|
+
|
|
38
|
+
// Utility exports
|
|
39
|
+
export * from "./lib/utils"
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// MoonUI Micro-interactions System
|
|
2
|
+
// Provides consistent hover, focus, and active states across all components
|
|
3
|
+
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Micro-interaction variants for consistent component behavior
|
|
8
|
+
*/
|
|
9
|
+
export const microInteractionVariants = cva("", {
|
|
10
|
+
variants: {
|
|
11
|
+
hover: {
|
|
12
|
+
lift: [
|
|
13
|
+
"transition-all duration-200 ease-out",
|
|
14
|
+
"hover:-translate-y-0.5",
|
|
15
|
+
"hover:shadow-md",
|
|
16
|
+
],
|
|
17
|
+
glow: [
|
|
18
|
+
"transition-all duration-300 ease-out",
|
|
19
|
+
"hover:shadow-lg",
|
|
20
|
+
"hover:shadow-primary/20",
|
|
21
|
+
],
|
|
22
|
+
scale: [
|
|
23
|
+
"transition-transform duration-200 ease-out",
|
|
24
|
+
"hover:scale-105",
|
|
25
|
+
"active:scale-95",
|
|
26
|
+
],
|
|
27
|
+
brightness: [
|
|
28
|
+
"transition-all duration-200 ease-out",
|
|
29
|
+
"hover:brightness-110",
|
|
30
|
+
"active:brightness-95",
|
|
31
|
+
],
|
|
32
|
+
border: [
|
|
33
|
+
"transition-all duration-200 ease-out",
|
|
34
|
+
"hover:border-primary/50",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
focus: {
|
|
38
|
+
ring: [
|
|
39
|
+
"focus:outline-none",
|
|
40
|
+
"focus-visible:ring-2",
|
|
41
|
+
"focus-visible:ring-primary/50",
|
|
42
|
+
"focus-visible:ring-offset-2",
|
|
43
|
+
],
|
|
44
|
+
glow: [
|
|
45
|
+
"focus:outline-none",
|
|
46
|
+
"focus-visible:shadow-lg",
|
|
47
|
+
"focus-visible:shadow-primary/30",
|
|
48
|
+
],
|
|
49
|
+
border: [
|
|
50
|
+
"focus:outline-none",
|
|
51
|
+
"focus-visible:border-primary",
|
|
52
|
+
"focus-visible:border-2",
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
active: {
|
|
56
|
+
scale: [
|
|
57
|
+
"active:scale-95",
|
|
58
|
+
"transition-transform duration-100",
|
|
59
|
+
],
|
|
60
|
+
darken: [
|
|
61
|
+
"active:brightness-90",
|
|
62
|
+
"transition-all duration-100",
|
|
63
|
+
],
|
|
64
|
+
depress: [
|
|
65
|
+
"active:translate-y-0.5",
|
|
66
|
+
"active:shadow-sm",
|
|
67
|
+
"transition-all duration-100",
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
cursor: {
|
|
71
|
+
pointer: "cursor-pointer",
|
|
72
|
+
grab: "cursor-grab active:cursor-grabbing",
|
|
73
|
+
text: "cursor-text",
|
|
74
|
+
wait: "cursor-wait",
|
|
75
|
+
help: "cursor-help",
|
|
76
|
+
notAllowed: "cursor-not-allowed",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
defaultVariants: {
|
|
80
|
+
hover: "lift",
|
|
81
|
+
focus: "ring",
|
|
82
|
+
active: "scale",
|
|
83
|
+
cursor: "pointer",
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Spring animation configurations
|
|
89
|
+
*/
|
|
90
|
+
export const springAnimations = {
|
|
91
|
+
// Smooth spring for general use
|
|
92
|
+
smooth: {
|
|
93
|
+
type: "spring",
|
|
94
|
+
stiffness: 260,
|
|
95
|
+
damping: 20,
|
|
96
|
+
},
|
|
97
|
+
// Bouncy spring for playful interactions
|
|
98
|
+
bouncy: {
|
|
99
|
+
type: "spring",
|
|
100
|
+
stiffness: 300,
|
|
101
|
+
damping: 15,
|
|
102
|
+
},
|
|
103
|
+
// Stiff spring for quick responses
|
|
104
|
+
stiff: {
|
|
105
|
+
type: "spring",
|
|
106
|
+
stiffness: 400,
|
|
107
|
+
damping: 25,
|
|
108
|
+
},
|
|
109
|
+
// Gentle spring for subtle movements
|
|
110
|
+
gentle: {
|
|
111
|
+
type: "spring",
|
|
112
|
+
stiffness: 150,
|
|
113
|
+
damping: 15,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Hover animation presets
|
|
119
|
+
*/
|
|
120
|
+
export const hoverAnimations = {
|
|
121
|
+
lift: {
|
|
122
|
+
y: -2,
|
|
123
|
+
transition: springAnimations.smooth,
|
|
124
|
+
},
|
|
125
|
+
scale: {
|
|
126
|
+
scale: 1.05,
|
|
127
|
+
transition: springAnimations.smooth,
|
|
128
|
+
},
|
|
129
|
+
glow: {
|
|
130
|
+
boxShadow: "0 0 20px rgba(var(--primary), 0.3)",
|
|
131
|
+
transition: springAnimations.smooth,
|
|
132
|
+
},
|
|
133
|
+
rotate: {
|
|
134
|
+
rotate: 2,
|
|
135
|
+
transition: springAnimations.bouncy,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Tap/Click animation presets
|
|
141
|
+
*/
|
|
142
|
+
export const tapAnimations = {
|
|
143
|
+
scale: {
|
|
144
|
+
scale: 0.95,
|
|
145
|
+
transition: { duration: 0.1 },
|
|
146
|
+
},
|
|
147
|
+
depress: {
|
|
148
|
+
y: 1,
|
|
149
|
+
transition: { duration: 0.1 },
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Focus animation presets
|
|
155
|
+
*/
|
|
156
|
+
export const focusAnimations = {
|
|
157
|
+
ring: {
|
|
158
|
+
boxShadow: "0 0 0 2px var(--background), 0 0 0 4px var(--primary)",
|
|
159
|
+
transition: springAnimations.smooth,
|
|
160
|
+
},
|
|
161
|
+
glow: {
|
|
162
|
+
boxShadow: "0 0 0 3px rgba(var(--primary), 0.3)",
|
|
163
|
+
transition: springAnimations.smooth,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Stagger animation for lists
|
|
169
|
+
*/
|
|
170
|
+
export const staggerAnimation = {
|
|
171
|
+
container: {
|
|
172
|
+
hidden: { opacity: 0 },
|
|
173
|
+
show: {
|
|
174
|
+
opacity: 1,
|
|
175
|
+
transition: {
|
|
176
|
+
staggerChildren: 0.05,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
item: {
|
|
181
|
+
hidden: { opacity: 0, y: 20 },
|
|
182
|
+
show: {
|
|
183
|
+
opacity: 1,
|
|
184
|
+
y: 0,
|
|
185
|
+
transition: springAnimations.smooth,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Page transition animations
|
|
192
|
+
*/
|
|
193
|
+
export const pageTransitions = {
|
|
194
|
+
fadeIn: {
|
|
195
|
+
initial: { opacity: 0 },
|
|
196
|
+
animate: { opacity: 1 },
|
|
197
|
+
exit: { opacity: 0 },
|
|
198
|
+
transition: { duration: 0.3 },
|
|
199
|
+
},
|
|
200
|
+
slideUp: {
|
|
201
|
+
initial: { opacity: 0, y: 20 },
|
|
202
|
+
animate: { opacity: 1, y: 0 },
|
|
203
|
+
exit: { opacity: 0, y: -20 },
|
|
204
|
+
transition: springAnimations.smooth,
|
|
205
|
+
},
|
|
206
|
+
slideRight: {
|
|
207
|
+
initial: { opacity: 0, x: -20 },
|
|
208
|
+
animate: { opacity: 1, x: 0 },
|
|
209
|
+
exit: { opacity: 0, x: 20 },
|
|
210
|
+
transition: springAnimations.smooth,
|
|
211
|
+
},
|
|
212
|
+
scale: {
|
|
213
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
214
|
+
animate: { opacity: 1, scale: 1 },
|
|
215
|
+
exit: { opacity: 0, scale: 1.05 },
|
|
216
|
+
transition: springAnimations.smooth,
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Skeleton loading animation
|
|
222
|
+
*/
|
|
223
|
+
export const skeletonAnimation = {
|
|
224
|
+
initial: { opacity: 0.5 },
|
|
225
|
+
animate: {
|
|
226
|
+
opacity: [0.5, 0.8, 0.5],
|
|
227
|
+
transition: {
|
|
228
|
+
duration: 1.5,
|
|
229
|
+
repeat: Infinity,
|
|
230
|
+
ease: "easeInOut" as const,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Tooltip animation
|
|
237
|
+
*/
|
|
238
|
+
export const tooltipAnimation = {
|
|
239
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
240
|
+
animate: { opacity: 1, scale: 1 },
|
|
241
|
+
exit: { opacity: 0, scale: 0.95 },
|
|
242
|
+
transition: { duration: 0.15 },
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Notification animation
|
|
247
|
+
*/
|
|
248
|
+
export const notificationAnimation = {
|
|
249
|
+
initial: { opacity: 0, y: -20, scale: 0.95 },
|
|
250
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
251
|
+
exit: { opacity: 0, scale: 0.95, transition: { duration: 0.15 } },
|
|
252
|
+
transition: springAnimations.bouncy,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export type MicroInteractionProps = VariantProps<typeof microInteractionVariants>;
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createContext, useContext, useState, useMemo, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tema türleri için temel renk paletini tanımlayan tip
|
|
6
|
+
*/
|
|
7
|
+
export type ThemeColors = {
|
|
8
|
+
primary: string;
|
|
9
|
+
secondary: string;
|
|
10
|
+
success: string;
|
|
11
|
+
warning: string;
|
|
12
|
+
error: string;
|
|
13
|
+
background: string;
|
|
14
|
+
foreground: string;
|
|
15
|
+
card: string;
|
|
16
|
+
cardForeground: string;
|
|
17
|
+
border: string;
|
|
18
|
+
muted: string;
|
|
19
|
+
mutedForeground: string;
|
|
20
|
+
accent: string;
|
|
21
|
+
accentForeground: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Tema yapılandırma tipi
|
|
26
|
+
*/
|
|
27
|
+
export type ThemeConfig = {
|
|
28
|
+
colors: ThemeColors;
|
|
29
|
+
borderRadius: {
|
|
30
|
+
sm: string;
|
|
31
|
+
md: string;
|
|
32
|
+
lg: string;
|
|
33
|
+
full: string;
|
|
34
|
+
};
|
|
35
|
+
fontSizes: {
|
|
36
|
+
xs: string;
|
|
37
|
+
sm: string;
|
|
38
|
+
base: string;
|
|
39
|
+
lg: string;
|
|
40
|
+
xl: string;
|
|
41
|
+
'2xl': string;
|
|
42
|
+
'3xl': string;
|
|
43
|
+
};
|
|
44
|
+
spacing: {
|
|
45
|
+
1: string;
|
|
46
|
+
2: string;
|
|
47
|
+
4: string;
|
|
48
|
+
6: string;
|
|
49
|
+
8: string;
|
|
50
|
+
};
|
|
51
|
+
// Kullanıcıların kendi özel değişkenlerini ekleyebilmesi için
|
|
52
|
+
extensions?: Record<string, Record<string, string> | string>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Tema hazır şablonları
|
|
57
|
+
*/
|
|
58
|
+
export type ThemePreset = 'default' | 'modern' | 'rounded' | 'minimal';
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Varsayılan açık tema
|
|
62
|
+
*/
|
|
63
|
+
// Modern teması için değerler
|
|
64
|
+
const modernTheme: Partial<ThemeConfig> = {
|
|
65
|
+
borderRadius: {
|
|
66
|
+
sm: '0.5rem',
|
|
67
|
+
md: '0.75rem',
|
|
68
|
+
lg: '1rem',
|
|
69
|
+
full: '9999px',
|
|
70
|
+
},
|
|
71
|
+
colors: {
|
|
72
|
+
primary: 'hsl(250, 95%, 64%)',
|
|
73
|
+
secondary: 'hsl(250, 70%, 80%)',
|
|
74
|
+
success: 'hsl(142, 71%, 45%)',
|
|
75
|
+
warning: 'hsl(38, 92%, 50%)',
|
|
76
|
+
error: 'hsl(0, 84%, 60%)',
|
|
77
|
+
background: 'hsl(0, 0%, 100%)',
|
|
78
|
+
foreground: 'hsl(221, 39%, 11%)',
|
|
79
|
+
card: 'hsl(0, 0%, 100%)',
|
|
80
|
+
cardForeground: 'hsl(221, 39%, 11%)',
|
|
81
|
+
border: 'hsl(210, 20%, 90%)',
|
|
82
|
+
muted: 'hsl(210, 20%, 96%)',
|
|
83
|
+
mutedForeground: 'hsl(215, 16%, 47%)',
|
|
84
|
+
accent: 'hsl(328, 85%, 70%)',
|
|
85
|
+
accentForeground: 'hsl(210, 20%, 98%)',
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Yuvarlak teması için değerler
|
|
90
|
+
const roundedTheme = {
|
|
91
|
+
borderRadius: {
|
|
92
|
+
sm: '1rem',
|
|
93
|
+
md: '1.25rem',
|
|
94
|
+
lg: '1.5rem',
|
|
95
|
+
full: '9999px',
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Minimal teması için değerler
|
|
100
|
+
const minimalTheme: Partial<ThemeConfig> = {
|
|
101
|
+
borderRadius: {
|
|
102
|
+
sm: '0.125rem',
|
|
103
|
+
md: '0.25rem',
|
|
104
|
+
lg: '0.375rem',
|
|
105
|
+
full: '9999px',
|
|
106
|
+
},
|
|
107
|
+
colors: {
|
|
108
|
+
primary: 'hsl(220, 14%, 40%)',
|
|
109
|
+
secondary: 'hsl(220, 9%, 60%)',
|
|
110
|
+
success: 'hsl(142, 71%, 45%)',
|
|
111
|
+
warning: 'hsl(38, 92%, 50%)',
|
|
112
|
+
error: 'hsl(0, 84%, 60%)',
|
|
113
|
+
background: 'hsl(0, 0%, 100%)',
|
|
114
|
+
foreground: 'hsl(221, 39%, 11%)',
|
|
115
|
+
card: 'hsl(0, 0%, 100%)',
|
|
116
|
+
cardForeground: 'hsl(221, 39%, 11%)',
|
|
117
|
+
border: 'hsl(220, 13%, 91%)',
|
|
118
|
+
muted: 'hsl(220, 14%, 96%)',
|
|
119
|
+
mutedForeground: 'hsl(215, 16%, 47%)',
|
|
120
|
+
accent: 'hsl(210, 20%, 96%)',
|
|
121
|
+
accentForeground: 'hsl(221, 39%, 11%)',
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const defaultLightTheme: ThemeConfig = {
|
|
126
|
+
colors: {
|
|
127
|
+
primary: 'hsl(221, 83%, 53%)',
|
|
128
|
+
secondary: 'hsl(215, 20%, 65%)',
|
|
129
|
+
success: 'hsl(142, 71%, 45%)',
|
|
130
|
+
warning: 'hsl(38, 92%, 50%)',
|
|
131
|
+
error: 'hsl(0, 84%, 60%)',
|
|
132
|
+
background: 'hsl(0, 0%, 100%)',
|
|
133
|
+
foreground: 'hsl(221, 39%, 11%)',
|
|
134
|
+
card: 'hsl(0, 0%, 100%)',
|
|
135
|
+
cardForeground: 'hsl(221, 39%, 11%)',
|
|
136
|
+
border: 'hsl(210, 20%, 90%)',
|
|
137
|
+
muted: 'hsl(210, 20%, 96%)',
|
|
138
|
+
mutedForeground: 'hsl(215, 16%, 47%)',
|
|
139
|
+
accent: 'hsl(210, 20%, 96%)',
|
|
140
|
+
accentForeground: 'hsl(221, 39%, 11%)',
|
|
141
|
+
},
|
|
142
|
+
borderRadius: {
|
|
143
|
+
sm: '0.25rem',
|
|
144
|
+
md: '0.375rem',
|
|
145
|
+
lg: '0.5rem',
|
|
146
|
+
full: '9999px',
|
|
147
|
+
},
|
|
148
|
+
fontSizes: {
|
|
149
|
+
xs: '0.75rem',
|
|
150
|
+
sm: '0.875rem',
|
|
151
|
+
base: '1rem',
|
|
152
|
+
lg: '1.125rem',
|
|
153
|
+
xl: '1.25rem',
|
|
154
|
+
'2xl': '1.5rem',
|
|
155
|
+
'3xl': '1.875rem',
|
|
156
|
+
},
|
|
157
|
+
spacing: {
|
|
158
|
+
1: '0.25rem',
|
|
159
|
+
2: '0.5rem',
|
|
160
|
+
4: '1rem',
|
|
161
|
+
6: '1.5rem',
|
|
162
|
+
8: '2rem',
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Varsayılan koyu tema
|
|
168
|
+
*/
|
|
169
|
+
export const defaultDarkTheme: ThemeConfig = {
|
|
170
|
+
...defaultLightTheme,
|
|
171
|
+
colors: {
|
|
172
|
+
primary: 'hsl(221, 83%, 53%)',
|
|
173
|
+
secondary: 'hsl(215, 20%, 65%)',
|
|
174
|
+
success: 'hsl(142, 71%, 45%)',
|
|
175
|
+
warning: 'hsl(38, 92%, 50%)',
|
|
176
|
+
error: 'hsl(0, 84%, 60%)',
|
|
177
|
+
background: 'hsl(221, 39%, 11%)',
|
|
178
|
+
foreground: 'hsl(210, 20%, 98%)',
|
|
179
|
+
card: 'hsl(220, 26%, 14%)',
|
|
180
|
+
cardForeground: 'hsl(210, 20%, 98%)',
|
|
181
|
+
border: 'hsl(215, 28%, 17%)',
|
|
182
|
+
muted: 'hsl(215, 27%, 16%)',
|
|
183
|
+
mutedForeground: 'hsl(215, 16%, 57%)',
|
|
184
|
+
accent: 'hsl(215, 27%, 16%)',
|
|
185
|
+
accentForeground: 'hsl(210, 20%, 98%)',
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Tema bağlamı için tip
|
|
191
|
+
*/
|
|
192
|
+
export interface ThemeContextType {
|
|
193
|
+
theme: ThemeConfig;
|
|
194
|
+
colorMode: 'light' | 'dark';
|
|
195
|
+
setColorMode: (mode: 'light' | 'dark') => void;
|
|
196
|
+
updateTheme: (config: Partial<ThemeConfig>) => void;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Tema bağlamı
|
|
200
|
+
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
|
201
|
+
|
|
202
|
+
// Tema sağlayıcı props
|
|
203
|
+
export interface ThemeProviderProps {
|
|
204
|
+
children: ReactNode;
|
|
205
|
+
defaultColorMode?: 'light' | 'dark';
|
|
206
|
+
preset?: ThemePreset;
|
|
207
|
+
lightTheme?: Partial<ThemeConfig>;
|
|
208
|
+
darkTheme?: Partial<ThemeConfig>;
|
|
209
|
+
extensions?: Record<string, Record<string, string> | string>;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Tema önayarını getir
|
|
214
|
+
*/
|
|
215
|
+
const getPresetTheme = (preset: ThemePreset): Partial<ThemeConfig> => {
|
|
216
|
+
switch (preset) {
|
|
217
|
+
case 'modern':
|
|
218
|
+
return modernTheme;
|
|
219
|
+
case 'rounded':
|
|
220
|
+
return roundedTheme;
|
|
221
|
+
case 'minimal':
|
|
222
|
+
return minimalTheme;
|
|
223
|
+
default:
|
|
224
|
+
return {};
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* MoonUI tema sağlayıcısı
|
|
230
|
+
*/
|
|
231
|
+
export const ThemeProvider = ({
|
|
232
|
+
children,
|
|
233
|
+
defaultColorMode = 'light',
|
|
234
|
+
preset = 'default',
|
|
235
|
+
lightTheme = {},
|
|
236
|
+
darkTheme = {},
|
|
237
|
+
extensions = {},
|
|
238
|
+
}: ThemeProviderProps) => {
|
|
239
|
+
// Renk modu state'i
|
|
240
|
+
const [colorMode, setColorMode] = useState<'light' | 'dark'>(defaultColorMode);
|
|
241
|
+
|
|
242
|
+
// Preset tema değerlerini al
|
|
243
|
+
const presetThemeValues = getPresetTheme(preset);
|
|
244
|
+
|
|
245
|
+
// Özelleştirilmiş temalar
|
|
246
|
+
const [customLightTheme, setCustomLightTheme] = useState<ThemeConfig>({
|
|
247
|
+
...defaultLightTheme,
|
|
248
|
+
...presetThemeValues,
|
|
249
|
+
...lightTheme,
|
|
250
|
+
colors: {
|
|
251
|
+
...defaultLightTheme.colors,
|
|
252
|
+
...(presetThemeValues.colors || {}),
|
|
253
|
+
...(lightTheme.colors || {}),
|
|
254
|
+
},
|
|
255
|
+
extensions: extensions,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const [customDarkTheme, setCustomDarkTheme] = useState<ThemeConfig>({
|
|
259
|
+
...defaultDarkTheme,
|
|
260
|
+
...presetThemeValues,
|
|
261
|
+
...darkTheme,
|
|
262
|
+
colors: {
|
|
263
|
+
...defaultDarkTheme.colors,
|
|
264
|
+
...(presetThemeValues.colors || {}),
|
|
265
|
+
...(darkTheme.colors || {}),
|
|
266
|
+
},
|
|
267
|
+
extensions: extensions,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Aktif tema
|
|
271
|
+
const theme = useMemo(() => {
|
|
272
|
+
return colorMode === 'light' ? customLightTheme : customDarkTheme;
|
|
273
|
+
}, [colorMode, customLightTheme, customDarkTheme]);
|
|
274
|
+
|
|
275
|
+
// Tema güncelleme fonksiyonu
|
|
276
|
+
const updateTheme = (config: Partial<ThemeConfig>) => {
|
|
277
|
+
if (colorMode === 'light') {
|
|
278
|
+
setCustomLightTheme(prev => ({
|
|
279
|
+
...prev,
|
|
280
|
+
...config,
|
|
281
|
+
colors: {
|
|
282
|
+
...prev.colors,
|
|
283
|
+
...(config.colors || {}),
|
|
284
|
+
},
|
|
285
|
+
extensions: {
|
|
286
|
+
...(prev.extensions || {}),
|
|
287
|
+
...(config.extensions || {}),
|
|
288
|
+
},
|
|
289
|
+
}));
|
|
290
|
+
} else {
|
|
291
|
+
setCustomDarkTheme(prev => ({
|
|
292
|
+
...prev,
|
|
293
|
+
...config,
|
|
294
|
+
colors: {
|
|
295
|
+
...prev.colors,
|
|
296
|
+
...(config.colors || {}),
|
|
297
|
+
},
|
|
298
|
+
extensions: {
|
|
299
|
+
...(prev.extensions || {}),
|
|
300
|
+
...(config.extensions || {}),
|
|
301
|
+
},
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// Tema değiştirme fonksiyonu
|
|
307
|
+
const setThemePreset = (preset: ThemePreset) => {
|
|
308
|
+
const presetValues = getPresetTheme(preset);
|
|
309
|
+
|
|
310
|
+
updateTheme({
|
|
311
|
+
...presetValues,
|
|
312
|
+
colors: presetValues.colors as ThemeColors,
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const value = {
|
|
317
|
+
theme,
|
|
318
|
+
colorMode,
|
|
319
|
+
setColorMode,
|
|
320
|
+
updateTheme,
|
|
321
|
+
setThemePreset,
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Tema bağlamı için tip
|
|
329
|
+
*/
|
|
330
|
+
export interface ThemeContextType {
|
|
331
|
+
theme: ThemeConfig;
|
|
332
|
+
colorMode: 'light' | 'dark';
|
|
333
|
+
setColorMode: (mode: 'light' | 'dark') => void;
|
|
334
|
+
updateTheme: (config: Partial<ThemeConfig>) => void;
|
|
335
|
+
setThemePreset: (preset: ThemePreset) => void;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Tema kullanma kancası
|
|
340
|
+
*/
|
|
341
|
+
export const useTheme = (): ThemeContextType => {
|
|
342
|
+
const context = useContext(ThemeContext);
|
|
343
|
+
|
|
344
|
+
if (context === undefined) {
|
|
345
|
+
throw new Error('useTheme must be used within a ThemeProvider');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return context;
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* CSS değişkenlerini tema konfigürasyonundan oluşturma fonksiyonu
|
|
353
|
+
*/
|
|
354
|
+
export const createCssVariables = (theme: ThemeConfig): Record<string, string> => {
|
|
355
|
+
const variables: Record<string, string> = {};
|
|
356
|
+
|
|
357
|
+
// Renkleri CSS değişkenlerine dönüştür
|
|
358
|
+
Object.entries(theme.colors).forEach(([key, value]) => {
|
|
359
|
+
variables[`--color-${key}`] = value;
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Border radius değerlerini CSS değişkenlerine dönüştür
|
|
363
|
+
Object.entries(theme.borderRadius).forEach(([key, value]) => {
|
|
364
|
+
variables[`--radius-${key}`] = value;
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Font boyutlarını CSS değişkenlerine dönüştür
|
|
368
|
+
Object.entries(theme.fontSizes).forEach(([key, value]) => {
|
|
369
|
+
variables[`--font-size-${key}`] = value;
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Boşluk değerlerini CSS değişkenlerine dönüştür
|
|
373
|
+
Object.entries(theme.spacing).forEach(([key, value]) => {
|
|
374
|
+
variables[`--space-${key}`] = value;
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// Özel uzantı değişkenlerini CSS'e dönüştür
|
|
378
|
+
if (theme.extensions) {
|
|
379
|
+
Object.entries(theme.extensions).forEach(([categoryKey, categoryValue]) => {
|
|
380
|
+
if (typeof categoryValue === 'object' && categoryValue !== null) {
|
|
381
|
+
Object.entries(categoryValue as Record<string, string>).forEach(([key, value]) => {
|
|
382
|
+
variables[`--${categoryKey}-${key}`] = value;
|
|
383
|
+
});
|
|
384
|
+
} else if (typeof categoryValue === 'string') {
|
|
385
|
+
variables[`--${categoryKey}`] = categoryValue;
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return variables;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* CSS sınıflarını tema değerlerine göre üretme yardımcı fonksiyonu
|
|
395
|
+
*/
|
|
396
|
+
export const getCssClass = (themeKey: keyof ThemeConfig, value: string): string => {
|
|
397
|
+
return `var(--${themeKey}-${value})`;
|
|
398
|
+
};
|