@moontra/moonui-pro 2.20.0 → 2.20.2

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.
Files changed (76) hide show
  1. package/dist/index.d.ts +691 -261
  2. package/dist/index.mjs +7419 -4935
  3. package/package.json +4 -3
  4. package/scripts/postbuild.js +27 -0
  5. package/src/components/advanced-chart/index.tsx +5 -1
  6. package/src/components/advanced-forms/index.tsx +175 -16
  7. package/src/components/calendar/event-dialog.tsx +18 -13
  8. package/src/components/calendar/index.tsx +197 -50
  9. package/src/components/dashboard/dashboard-grid.tsx +21 -3
  10. package/src/components/dashboard/types.ts +3 -0
  11. package/src/components/dashboard/widgets/activity-feed.tsx +6 -1
  12. package/src/components/dashboard/widgets/comparison-widget.tsx +177 -0
  13. package/src/components/dashboard/widgets/index.ts +5 -0
  14. package/src/components/dashboard/widgets/metric-card.tsx +21 -1
  15. package/src/components/dashboard/widgets/progress-widget.tsx +113 -0
  16. package/src/components/error-boundary/index.tsx +160 -37
  17. package/src/components/form-wizard/form-wizard-context.tsx +54 -26
  18. package/src/components/form-wizard/form-wizard-progress.tsx +33 -2
  19. package/src/components/form-wizard/types.ts +2 -1
  20. package/src/components/github-stars/hooks.ts +1 -0
  21. package/src/components/github-stars/variants.tsx +3 -1
  22. package/src/components/health-check/index.tsx +14 -14
  23. package/src/components/hover-card-3d/index.tsx +2 -3
  24. package/src/components/index.ts +5 -3
  25. package/src/components/kanban/kanban.tsx +23 -18
  26. package/src/components/license-error/index.tsx +2 -0
  27. package/src/components/magnetic-button/index.tsx +56 -7
  28. package/src/components/memory-efficient-data/index.tsx +117 -115
  29. package/src/components/navbar/index.tsx +781 -0
  30. package/src/components/performance-debugger/index.tsx +62 -38
  31. package/src/components/performance-monitor/index.tsx +47 -33
  32. package/src/components/phone-number-input/index.tsx +32 -27
  33. package/src/components/phone-number-input/phone-number-input-simple.tsx +167 -0
  34. package/src/components/rich-text-editor/index.tsx +26 -28
  35. package/src/components/rich-text-editor/slash-commands-extension.ts +15 -5
  36. package/src/components/sidebar/index.tsx +32 -13
  37. package/src/components/timeline/index.tsx +84 -49
  38. package/src/components/ui/accordion.tsx +550 -42
  39. package/src/components/ui/avatar.tsx +2 -0
  40. package/src/components/ui/badge.tsx +2 -0
  41. package/src/components/ui/breadcrumb.tsx +2 -0
  42. package/src/components/ui/button.tsx +39 -33
  43. package/src/components/ui/card.tsx +2 -0
  44. package/src/components/ui/collapsible.tsx +546 -50
  45. package/src/components/ui/command.tsx +790 -67
  46. package/src/components/ui/dialog.tsx +510 -92
  47. package/src/components/ui/dropdown-menu.tsx +540 -52
  48. package/src/components/ui/index.ts +37 -5
  49. package/src/components/ui/input.tsx +2 -0
  50. package/src/components/ui/magnetic-button.tsx +1 -1
  51. package/src/components/ui/media-gallery.tsx +1 -2
  52. package/src/components/ui/navigation-menu.tsx +130 -0
  53. package/src/components/ui/pagination.tsx +2 -0
  54. package/src/components/ui/select.tsx +6 -2
  55. package/src/components/ui/spotlight-card.tsx +1 -1
  56. package/src/components/ui/table.tsx +2 -0
  57. package/src/components/ui/tabs-pro.tsx +542 -0
  58. package/src/components/ui/tabs.tsx +23 -167
  59. package/src/components/ui/toggle.tsx +13 -13
  60. package/src/index.ts +11 -3
  61. package/src/styles/index.css +596 -0
  62. package/src/use-performance-optimizer.ts +1 -1
  63. package/src/utils/chart-helpers.ts +1 -1
  64. package/src/__tests__/use-intersection-observer.test.tsx +0 -216
  65. package/src/__tests__/use-local-storage.test.tsx +0 -174
  66. package/src/__tests__/use-pro-access.test.tsx +0 -183
  67. package/src/components/advanced-chart/advanced-chart.test.tsx +0 -281
  68. package/src/components/data-table/data-table.test.tsx +0 -187
  69. package/src/components/enhanced/badge.tsx +0 -191
  70. package/src/components/enhanced/button.tsx +0 -362
  71. package/src/components/enhanced/card.tsx +0 -266
  72. package/src/components/enhanced/dialog.tsx +0 -246
  73. package/src/components/enhanced/index.ts +0 -4
  74. package/src/components/file-upload/file-upload.test.tsx +0 -243
  75. package/src/components/rich-text-editor/index-old-backup.tsx +0 -437
  76. package/src/types/moonui.d.ts +0 -22
@@ -348,8 +348,8 @@ const PerformanceDebuggerInternal: React.FC<PerformanceDebuggerProps> = ({
348
348
  }
349
349
 
350
350
  return (
351
- <Card className={cn("w-full max-w-4xl", className)}>
352
- <CardHeader>
351
+ <Card className={cn("w-full max-w-6xl", className)}>
352
+ <CardHeader className="pb-6">
353
353
  <div className="flex items-center justify-between">
354
354
  <div>
355
355
  <CardTitle className="flex items-center gap-2">
@@ -362,48 +362,72 @@ const PerformanceDebuggerInternal: React.FC<PerformanceDebuggerProps> = ({
362
362
  </div>
363
363
 
364
364
  <div className="flex items-center gap-2">
365
- <Badge variant={isCapturing ? "secondary" : "secondary"} className="gap-1">
366
- {isCapturing ? <Activity className="h-3 w-3 animate-pulse" /> : <Clock className="h-3 w-3" />}
367
- {isCapturing ? "Live" : "Paused"}
368
- </Badge>
369
-
370
- <Button
371
- variant="outline"
372
- size="sm"
373
- onClick={() => setIsCapturing(!isCapturing)}
374
- >
375
- {isCapturing ? "Pause" : "Start"}
376
- </Button>
377
-
378
- <Button
379
- variant="outline"
380
- size="sm"
381
- onClick={capture}
382
- >
383
- <RefreshCw className="h-4 w-4" />
384
- </Button>
385
-
386
- <Button
387
- variant="outline"
388
- size="sm"
389
- onClick={exportData}
390
- >
391
- <Download className="h-4 w-4" />
392
- </Button>
365
+ <div className={cn(
366
+ "inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium",
367
+ isCapturing
368
+ ? "border border-border bg-background text-foreground"
369
+ : "bg-secondary text-secondary-foreground"
370
+ )}>
371
+ {isCapturing ? (
372
+ <Activity className="h-3 w-3 animate-pulse" />
373
+ ) : (
374
+ <Clock className="h-3 w-3" />
375
+ )}
376
+ <span>{isCapturing ? "Live" : "Paused"}</span>
377
+ </div>
393
378
 
394
- <Button
395
- variant="ghost"
396
- size="sm"
397
- onClick={() => setIsVisible(false)}
398
- >
399
- <EyeOff className="h-4 w-4" />
400
- </Button>
379
+ <div className="flex items-center gap-1">
380
+ <Button
381
+ variant="outline"
382
+ size="icon"
383
+ className="h-7 w-7"
384
+ onClick={() => setIsCapturing(!isCapturing)}
385
+ >
386
+ {isCapturing ? (
387
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
388
+ <rect x="6" y="4" width="4" height="16"></rect>
389
+ <rect x="14" y="4" width="4" height="16"></rect>
390
+ </svg>
391
+ ) : (
392
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
393
+ <polygon points="5 3 19 12 5 21 5 3"></polygon>
394
+ </svg>
395
+ )}
396
+ </Button>
397
+
398
+ <Button
399
+ variant="outline"
400
+ size="icon"
401
+ className="h-7 w-7"
402
+ onClick={capture}
403
+ >
404
+ <RefreshCw className="h-3.5 w-3.5" />
405
+ </Button>
406
+
407
+ <Button
408
+ variant="outline"
409
+ size="icon"
410
+ className="h-7 w-7"
411
+ onClick={exportData}
412
+ >
413
+ <Download className="h-3.5 w-3.5" />
414
+ </Button>
415
+
416
+ <Button
417
+ variant="ghost"
418
+ size="icon"
419
+ className="h-7 w-7"
420
+ onClick={() => setIsVisible(false)}
421
+ >
422
+ <EyeOff className="h-3.5 w-3.5" />
423
+ </Button>
424
+ </div>
401
425
  </div>
402
426
  </div>
403
427
  </CardHeader>
404
428
 
405
429
  <CardContent>
406
- <Tabs defaultValue="current" className="w-full">
430
+ <Tabs defaultValue="current" className="w-full mt-2">
407
431
  <TabsList className="grid w-full grid-cols-3">
408
432
  <TabsTrigger value="current">Current Metrics</TabsTrigger>
409
433
  <TabsTrigger value="history">History</TabsTrigger>
@@ -351,7 +351,7 @@ const PerformanceMonitorInternal: React.FC<PerformanceMonitorProps> = ({
351
351
 
352
352
  return (
353
353
  <Card className={cn("w-full max-w-6xl", className)}>
354
- <CardHeader>
354
+ <CardHeader className="pb-6">
355
355
  <div className="flex items-center justify-between">
356
356
  <div>
357
357
  <CardTitle className="flex items-center gap-2">
@@ -364,48 +364,62 @@ const PerformanceMonitorInternal: React.FC<PerformanceMonitorProps> = ({
364
364
  </div>
365
365
 
366
366
  <div className="flex items-center gap-2">
367
- <Badge variant={autoRefresh ? "outline" : "secondary"} className="gap-1">
368
- {autoRefresh ? <Activity className="h-3 w-3 animate-pulse" /> : <Timer className="h-3 w-3" />}
369
- {autoRefresh ? "Live" : "Paused"}
370
- </Badge>
367
+ <div className={cn(
368
+ "inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium",
369
+ autoRefresh
370
+ ? "border border-border bg-background text-foreground"
371
+ : "bg-secondary text-secondary-foreground"
372
+ )}>
373
+ {autoRefresh ? (
374
+ <Activity className="h-3 w-3 animate-pulse" />
375
+ ) : (
376
+ <Timer className="h-3 w-3" />
377
+ )}
378
+ <span>{autoRefresh ? "Live" : "Paused"}</span>
379
+ </div>
371
380
 
372
381
  {showAlerts && alerts.length > 0 && (
373
- <Badge variant="destructive" className="gap-1">
382
+ <div className="inline-flex items-center gap-1 rounded-md bg-destructive px-2 py-0.5 text-xs font-medium text-destructive-foreground">
374
383
  <AlertTriangle className="h-3 w-3" />
375
- {alerts.length}
376
- </Badge>
384
+ <span>{alerts.length}</span>
385
+ </div>
377
386
  )}
378
387
 
379
- <Button
380
- variant="outline"
381
- size="sm"
382
- onClick={updateMetrics}
383
- disabled={isRefreshing}
384
- >
385
- <RefreshCw className={cn("h-4 w-4", isRefreshing && "animate-spin")} />
386
- </Button>
387
-
388
- <Button
389
- variant="outline"
390
- size="sm"
391
- onClick={exportMetrics}
392
- >
393
- <Download className="h-4 w-4" />
394
- </Button>
395
-
396
- <Button
397
- variant="ghost"
398
- size="sm"
399
- onClick={() => setIsVisible(false)}
400
- >
401
- <EyeOff className="h-4 w-4" />
402
- </Button>
388
+ <div className="flex items-center gap-1">
389
+ <Button
390
+ variant="outline"
391
+ size="icon"
392
+ className="h-7 w-7"
393
+ onClick={updateMetrics}
394
+ disabled={isRefreshing}
395
+ >
396
+ <RefreshCw className={cn("h-3.5 w-3.5", isRefreshing && "animate-spin")} />
397
+ </Button>
398
+
399
+ <Button
400
+ variant="outline"
401
+ size="icon"
402
+ className="h-7 w-7"
403
+ onClick={exportMetrics}
404
+ >
405
+ <Download className="h-3.5 w-3.5" />
406
+ </Button>
407
+
408
+ <Button
409
+ variant="ghost"
410
+ size="icon"
411
+ className="h-7 w-7"
412
+ onClick={() => setIsVisible(false)}
413
+ >
414
+ <EyeOff className="h-3.5 w-3.5" />
415
+ </Button>
416
+ </div>
403
417
  </div>
404
418
  </div>
405
419
  </CardHeader>
406
420
 
407
421
  <CardContent>
408
- <Tabs defaultValue="overview" className="w-full">
422
+ <Tabs defaultValue="overview" className="w-full mt-2">
409
423
  <TabsList className="grid w-full grid-cols-4">
410
424
  <TabsTrigger value="overview">Overview</TabsTrigger>
411
425
  <TabsTrigger value="details">Details</TabsTrigger>
@@ -100,8 +100,10 @@ function toInternationalFormat(countryCode: string, phoneNumber: string): string
100
100
  return `${country.dialCode}${cleaned}`
101
101
  }
102
102
 
103
+ const defaultValue = { country: 'US', number: '' }
104
+
103
105
  export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneNumberInputProps>(({
104
- value = { country: 'US', number: '' },
106
+ value,
105
107
  onChange,
106
108
  defaultCountry = 'US',
107
109
  countries: customCountries,
@@ -123,31 +125,31 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
123
125
  allowInternationalFormat = true,
124
126
  ...props
125
127
  }, ref) => {
126
- const [isValid, setIsValid] = useState(false)
128
+ // Stable default value
129
+ const phoneValue = React.useMemo(() => {
130
+ return value || defaultValue
131
+ }, [value])
127
132
  const [isFocused, setIsFocused] = useState(false)
128
133
  const inputRef = useRef<HTMLInputElement>(null)
129
134
 
130
135
  const countryList = customCountries || countries
131
- const selectedCountry = countryList.find(c => c.code === value.country) || countryList[0]
136
+ const selectedCountry = countryList.find(c => c.code === phoneValue.country) || countryList[0]
132
137
 
133
- // Doğrulama
134
- useEffect(() => {
135
- if (validateOnChange && value.number) {
136
- const valid = validatePhoneNumber(value.number, value.country)
137
- setIsValid(valid)
138
- }
139
- }, [value, validateOnChange])
138
+ // Doğrulama - useEffect yerine doğrudan hesapla
139
+ const isValid = validateOnChange && phoneValue.number
140
+ ? validatePhoneNumber(phoneValue.number, phoneValue.country)
141
+ : false
140
142
 
141
143
  const handleCountryChange = (countryCode: string) => {
142
144
  const fullNumber = allowInternationalFormat
143
- ? toInternationalFormat(countryCode, value.number)
144
- : value.number
145
+ ? toInternationalFormat(countryCode, phoneValue.number)
146
+ : phoneValue.number
145
147
 
146
148
  onChange?.({
147
149
  country: countryCode,
148
- number: value.number,
150
+ number: phoneValue.number,
149
151
  fullNumber,
150
- isValid: validatePhoneNumber(value.number, countryCode)
152
+ isValid: validatePhoneNumber(phoneValue.number, countryCode)
151
153
  })
152
154
  }
153
155
 
@@ -161,13 +163,13 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
161
163
  newValue = formatPhoneNumber(cleaned, selectedCountry.format)
162
164
  }
163
165
 
164
- const valid = validatePhoneNumber(newValue, value.country)
166
+ const valid = validatePhoneNumber(newValue, phoneValue.country)
165
167
  const fullNumber = allowInternationalFormat
166
- ? toInternationalFormat(value.country, newValue)
168
+ ? toInternationalFormat(phoneValue.country, newValue)
167
169
  : newValue
168
170
 
169
171
  onChange?.({
170
- country: value.country,
172
+ country: phoneValue.country,
171
173
  number: newValue,
172
174
  fullNumber,
173
175
  isValid: valid
@@ -182,13 +184,13 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
182
184
  const cleaned = pastedText.replace(/\D/g, '')
183
185
  const formatted = formatPhoneNumber(cleaned, selectedCountry.format)
184
186
 
185
- const valid = validatePhoneNumber(formatted, value.country)
187
+ const valid = validatePhoneNumber(formatted, phoneValue.country)
186
188
  const fullNumber = allowInternationalFormat
187
- ? toInternationalFormat(value.country, formatted)
189
+ ? toInternationalFormat(phoneValue.country, formatted)
188
190
  : formatted
189
191
 
190
192
  onChange?.({
191
- country: value.country,
193
+ country: phoneValue.country,
192
194
  number: formatted,
193
195
  fullNumber,
194
196
  isValid: valid
@@ -196,7 +198,7 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
196
198
  }
197
199
 
198
200
  return (
199
- <div ref={ref} className={cn("space-y-2", className)} {...props}>
201
+ <div ref={ref} className={cn("space-y-2", className)}>
200
202
  {label && (
201
203
  <Label htmlFor="phone-number" className={labelClassName}>
202
204
  {label}
@@ -207,7 +209,7 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
207
209
  <div className="flex gap-2">
208
210
  {/* Ülke Seçici */}
209
211
  <Select
210
- value={value.country}
212
+ value={phoneValue.country}
211
213
  onValueChange={handleCountryChange}
212
214
  disabled={disabled}
213
215
  >
@@ -245,7 +247,7 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
245
247
  ref={inputRef}
246
248
  id="phone-number"
247
249
  type="tel"
248
- value={value.number}
250
+ value={phoneValue.number}
249
251
  onChange={handleNumberChange}
250
252
  onPaste={handlePaste}
251
253
  onFocus={() => setIsFocused(true)}
@@ -260,7 +262,7 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
260
262
  />
261
263
 
262
264
  {/* Doğrulama İkonu */}
263
- {showValidationIcon && value.number && (
265
+ {showValidationIcon && phoneValue.number && (
264
266
  <div className="absolute right-3 top-1/2 -translate-y-1/2">
265
267
  <AnimatePresence mode="wait">
266
268
  {isValid ? (
@@ -318,14 +320,14 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
318
320
  )}
319
321
 
320
322
  {/* Uluslararası Format Gösterimi */}
321
- {allowInternationalFormat && value.number && isValid && (
323
+ {allowInternationalFormat && phoneValue.number && isValid && (
322
324
  <motion.div
323
325
  initial={{ opacity: 0, y: -10 }}
324
326
  animate={{ opacity: 1, y: 0 }}
325
327
  className="flex items-center gap-2 text-sm text-muted-foreground"
326
328
  >
327
329
  <Globe className="w-4 h-4" />
328
- <span>International: {toInternationalFormat(value.country, value.number)}</span>
330
+ <span>International: {toInternationalFormat(phoneValue.country, phoneValue.number)}</span>
329
331
  </motion.div>
330
332
  )}
331
333
  </div>
@@ -335,4 +337,7 @@ export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneN
335
337
  MoonUIPhoneNumberInputPro.displayName = "MoonUIPhoneNumberInputPro"
336
338
 
337
339
  // Ülke listesini de export edelim
338
- export { countries as phoneCountries }
340
+ export { countries as phoneCountries }
341
+
342
+ // Basit versiyon
343
+ export { MoonUIPhoneNumberInputSimple } from "./phone-number-input-simple"
@@ -0,0 +1,167 @@
1
+ "use client"
2
+
3
+ import React, { useState, useRef } from "react"
4
+ import { cn } from "../../lib/utils"
5
+ import { Input } from "../ui/input"
6
+ import { Label } from "../ui/label"
7
+ import { Phone, Check, X } from "lucide-react"
8
+ import { motion, AnimatePresence } from "framer-motion"
9
+
10
+ export interface PhoneNumberInputSimpleProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value' | 'size'> {
11
+ value?: string
12
+ onChange?: (value: string) => void
13
+ label?: string
14
+ error?: string
15
+ helperText?: string
16
+ required?: boolean
17
+ showValidationIcon?: boolean
18
+ inputClassName?: string
19
+ labelClassName?: string
20
+ errorClassName?: string
21
+ size?: 'sm' | 'md' | 'lg'
22
+ }
23
+
24
+ // Basit telefon numarası doğrulama
25
+ function validatePhoneNumber(number: string): boolean {
26
+ if (!number) return false
27
+ const cleaned = number.replace(/\D/g, '')
28
+ return cleaned.length >= 10 && cleaned.length <= 15
29
+ }
30
+
31
+ // Telefon numarası formatlama
32
+ function formatPhoneNumber(number: string): string {
33
+ const cleaned = number.replace(/\D/g, '')
34
+
35
+ // Türkiye formatı için basit formatlama
36
+ if (cleaned.length <= 3) {
37
+ return cleaned
38
+ } else if (cleaned.length <= 6) {
39
+ return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3)}`
40
+ } else if (cleaned.length <= 10) {
41
+ return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)} ${cleaned.slice(6)}`
42
+ } else {
43
+ return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)} ${cleaned.slice(6, 8)} ${cleaned.slice(8, 10)}`
44
+ }
45
+ }
46
+
47
+ export const MoonUIPhoneNumberInputSimple = React.forwardRef<HTMLInputElement, PhoneNumberInputSimpleProps>(({
48
+ value = '',
49
+ onChange,
50
+ label,
51
+ error,
52
+ helperText,
53
+ required,
54
+ showValidationIcon = true,
55
+ className,
56
+ inputClassName,
57
+ labelClassName,
58
+ errorClassName,
59
+ disabled,
60
+ placeholder = "(555) 123 45 67",
61
+ size,
62
+ ...props
63
+ }, ref) => {
64
+ const [isFocused, setIsFocused] = useState(false)
65
+ const [localValue, setLocalValue] = useState(value)
66
+ const isValid = validatePhoneNumber(localValue)
67
+
68
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
69
+ const newValue = e.target.value
70
+ const formattedValue = formatPhoneNumber(newValue)
71
+
72
+ setLocalValue(formattedValue)
73
+ onChange?.(formattedValue)
74
+ }
75
+
76
+ return (
77
+ <div className={cn("space-y-2", className)}>
78
+ {label && (
79
+ <Label htmlFor={props.id} className={labelClassName}>
80
+ {label}
81
+ {required && <span className="text-destructive ml-1">*</span>}
82
+ </Label>
83
+ )}
84
+
85
+ <div className="relative">
86
+ <div className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">
87
+ <Phone className="h-4 w-4" />
88
+ </div>
89
+
90
+ <Input
91
+ ref={ref}
92
+ type="tel"
93
+ value={localValue}
94
+ onChange={handleChange}
95
+ onFocus={() => setIsFocused(true)}
96
+ onBlur={() => setIsFocused(false)}
97
+ placeholder={placeholder}
98
+ className={cn(
99
+ "pl-10 pr-10",
100
+ error && "border-destructive",
101
+ inputClassName
102
+ )}
103
+ disabled={disabled}
104
+ {...props}
105
+ />
106
+
107
+ {/* Doğrulama İkonu */}
108
+ {showValidationIcon && localValue && (
109
+ <div className="absolute right-3 top-1/2 -translate-y-1/2">
110
+ <AnimatePresence mode="wait">
111
+ {isValid ? (
112
+ <motion.div
113
+ key="valid"
114
+ initial={{ scale: 0, opacity: 0 }}
115
+ animate={{ scale: 1, opacity: 1 }}
116
+ exit={{ scale: 0, opacity: 0 }}
117
+ transition={{ duration: 0.2 }}
118
+ >
119
+ <Check className="w-4 h-4 text-green-500" />
120
+ </motion.div>
121
+ ) : (
122
+ <motion.div
123
+ key="invalid"
124
+ initial={{ scale: 0, opacity: 0 }}
125
+ animate={{ scale: 1, opacity: 1 }}
126
+ exit={{ scale: 0, opacity: 0 }}
127
+ transition={{ duration: 0.2 }}
128
+ >
129
+ <X className="w-4 h-4 text-destructive" />
130
+ </motion.div>
131
+ )}
132
+ </AnimatePresence>
133
+ </div>
134
+ )}
135
+ </div>
136
+
137
+ {/* Helper Text veya Error */}
138
+ {(error || helperText) && (
139
+ <AnimatePresence mode="wait">
140
+ {error ? (
141
+ <motion.p
142
+ key="error"
143
+ initial={{ opacity: 0, height: 0 }}
144
+ animate={{ opacity: 1, height: 'auto' }}
145
+ exit={{ opacity: 0, height: 0 }}
146
+ className={cn("text-sm text-destructive", errorClassName)}
147
+ >
148
+ {error}
149
+ </motion.p>
150
+ ) : (
151
+ <motion.p
152
+ key="helper"
153
+ initial={{ opacity: 0, height: 0 }}
154
+ animate={{ opacity: 1, height: 'auto' }}
155
+ exit={{ opacity: 0, height: 0 }}
156
+ className="text-sm text-muted-foreground"
157
+ >
158
+ {helperText}
159
+ </motion.p>
160
+ )}
161
+ </AnimatePresence>
162
+ )}
163
+ </div>
164
+ )
165
+ })
166
+
167
+ MoonUIPhoneNumberInputSimple.displayName = "MoonUIPhoneNumberInputSimple"