@moontra/moonui-pro 2.20.2 → 2.20.3

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 (153) hide show
  1. package/package.json +8 -3
  2. package/plugin/index.d.ts +86 -0
  3. package/plugin/index.js +308 -0
  4. package/scripts/postinstall.js +176 -23
  5. package/src/components/advanced-chart/index.tsx +0 -1246
  6. package/src/components/advanced-forms/index.tsx +0 -585
  7. package/src/components/animated-button/index.tsx +0 -385
  8. package/src/components/calendar/event-dialog.tsx +0 -377
  9. package/src/components/calendar/index.tsx +0 -1220
  10. package/src/components/calendar-pro/index.tsx +0 -1697
  11. package/src/components/color-picker/index.tsx +0 -432
  12. package/src/components/credit-card-input/index.tsx +0 -406
  13. package/src/components/dashboard/dashboard-grid.tsx +0 -480
  14. package/src/components/dashboard/demo.tsx +0 -425
  15. package/src/components/dashboard/index.tsx +0 -1046
  16. package/src/components/dashboard/time-range-picker.tsx +0 -336
  17. package/src/components/dashboard/types.ts +0 -225
  18. package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
  19. package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
  20. package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
  21. package/src/components/dashboard/widgets/index.ts +0 -5
  22. package/src/components/dashboard/widgets/metric-card.tsx +0 -363
  23. package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
  24. package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
  25. package/src/components/data-table/data-table-column-toggle.tsx +0 -169
  26. package/src/components/data-table/data-table-export.ts +0 -156
  27. package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
  28. package/src/components/data-table/index.tsx +0 -845
  29. package/src/components/draggable-list/index.tsx +0 -100
  30. package/src/components/error-boundary/index.tsx +0 -232
  31. package/src/components/file-upload/index.tsx +0 -1660
  32. package/src/components/floating-action-button/index.tsx +0 -206
  33. package/src/components/form-wizard/form-wizard-context.tsx +0 -335
  34. package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
  35. package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
  36. package/src/components/form-wizard/form-wizard-step.tsx +0 -111
  37. package/src/components/form-wizard/index.tsx +0 -102
  38. package/src/components/form-wizard/types.ts +0 -77
  39. package/src/components/gesture-drawer/index.tsx +0 -551
  40. package/src/components/github-stars/github-api.ts +0 -426
  41. package/src/components/github-stars/hooks.ts +0 -517
  42. package/src/components/github-stars/index.tsx +0 -375
  43. package/src/components/github-stars/types.ts +0 -148
  44. package/src/components/github-stars/variants.tsx +0 -515
  45. package/src/components/health-check/index.tsx +0 -439
  46. package/src/components/hover-card-3d/index.tsx +0 -529
  47. package/src/components/index.ts +0 -130
  48. package/src/components/internal/index.ts +0 -78
  49. package/src/components/kanban/add-card-modal.tsx +0 -502
  50. package/src/components/kanban/card-detail-modal.tsx +0 -761
  51. package/src/components/kanban/index.ts +0 -13
  52. package/src/components/kanban/kanban.tsx +0 -1689
  53. package/src/components/kanban/types.ts +0 -168
  54. package/src/components/lazy-component/index.tsx +0 -823
  55. package/src/components/license-error/index.tsx +0 -31
  56. package/src/components/magnetic-button/index.tsx +0 -216
  57. package/src/components/memory-efficient-data/index.tsx +0 -1018
  58. package/src/components/moonui-quiz-form/index.tsx +0 -817
  59. package/src/components/navbar/index.tsx +0 -781
  60. package/src/components/optimized-image/index.tsx +0 -425
  61. package/src/components/performance-debugger/index.tsx +0 -613
  62. package/src/components/performance-monitor/index.tsx +0 -808
  63. package/src/components/phone-number-input/index.tsx +0 -343
  64. package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
  65. package/src/components/pinch-zoom/index.tsx +0 -566
  66. package/src/components/quiz-form/index.tsx +0 -479
  67. package/src/components/rich-text-editor/index.tsx +0 -2322
  68. package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
  69. package/src/components/rich-text-editor/slash-commands.css +0 -35
  70. package/src/components/rich-text-editor/table-styles.css +0 -65
  71. package/src/components/sidebar/index.tsx +0 -884
  72. package/src/components/spotlight-card/index.tsx +0 -191
  73. package/src/components/swipeable-card/index.tsx +0 -100
  74. package/src/components/timeline/index.tsx +0 -1183
  75. package/src/components/ui/accordion.tsx +0 -581
  76. package/src/components/ui/alert-dialog.tsx +0 -141
  77. package/src/components/ui/alert.tsx +0 -141
  78. package/src/components/ui/aspect-ratio.tsx +0 -245
  79. package/src/components/ui/avatar.tsx +0 -155
  80. package/src/components/ui/badge.tsx +0 -230
  81. package/src/components/ui/breadcrumb.tsx +0 -216
  82. package/src/components/ui/button.tsx +0 -228
  83. package/src/components/ui/calendar.tsx +0 -387
  84. package/src/components/ui/card.tsx +0 -216
  85. package/src/components/ui/checkbox.tsx +0 -259
  86. package/src/components/ui/collapsible.tsx +0 -631
  87. package/src/components/ui/color-picker.tsx +0 -97
  88. package/src/components/ui/command.tsx +0 -948
  89. package/src/components/ui/dialog.tsx +0 -752
  90. package/src/components/ui/dropdown-menu.tsx +0 -706
  91. package/src/components/ui/gesture-drawer.tsx +0 -11
  92. package/src/components/ui/hover-card.tsx +0 -29
  93. package/src/components/ui/index.ts +0 -222
  94. package/src/components/ui/input.tsx +0 -224
  95. package/src/components/ui/label.tsx +0 -29
  96. package/src/components/ui/lightbox.tsx +0 -606
  97. package/src/components/ui/magnetic-button.tsx +0 -129
  98. package/src/components/ui/media-gallery.tsx +0 -611
  99. package/src/components/ui/navigation-menu.tsx +0 -130
  100. package/src/components/ui/pagination.tsx +0 -125
  101. package/src/components/ui/popover.tsx +0 -185
  102. package/src/components/ui/progress.tsx +0 -30
  103. package/src/components/ui/radio-group.tsx +0 -257
  104. package/src/components/ui/scroll-area.tsx +0 -47
  105. package/src/components/ui/select.tsx +0 -378
  106. package/src/components/ui/separator.tsx +0 -145
  107. package/src/components/ui/sheet.tsx +0 -139
  108. package/src/components/ui/skeleton.tsx +0 -20
  109. package/src/components/ui/slider.tsx +0 -354
  110. package/src/components/ui/spotlight-card.tsx +0 -119
  111. package/src/components/ui/switch.tsx +0 -86
  112. package/src/components/ui/table.tsx +0 -331
  113. package/src/components/ui/tabs-pro.tsx +0 -542
  114. package/src/components/ui/tabs.tsx +0 -54
  115. package/src/components/ui/textarea.tsx +0 -28
  116. package/src/components/ui/toast.tsx +0 -317
  117. package/src/components/ui/toggle.tsx +0 -119
  118. package/src/components/ui/tooltip.tsx +0 -151
  119. package/src/components/virtual-list/index.tsx +0 -668
  120. package/src/hooks/use-chart.ts +0 -205
  121. package/src/hooks/use-data-table.ts +0 -182
  122. package/src/hooks/use-docs-pro-access.ts +0 -13
  123. package/src/hooks/use-license-check.ts +0 -65
  124. package/src/hooks/use-subscription.ts +0 -19
  125. package/src/hooks/use-toast.ts +0 -15
  126. package/src/index.ts +0 -22
  127. package/src/lib/ai-providers.ts +0 -377
  128. package/src/lib/component-metadata.ts +0 -18
  129. package/src/lib/micro-interactions.ts +0 -255
  130. package/src/lib/paddle.ts +0 -17
  131. package/src/lib/utils.ts +0 -6
  132. package/src/patterns/login-form/index.tsx +0 -276
  133. package/src/patterns/login-form/types.ts +0 -67
  134. package/src/setupTests.ts +0 -41
  135. package/src/styles/advanced-chart.css +0 -239
  136. package/src/styles/calendar.css +0 -35
  137. package/src/styles/design-system.css +0 -363
  138. package/src/styles/index.css +0 -681
  139. package/src/styles/tailwind.css +0 -7
  140. package/src/styles/tokens.css +0 -455
  141. package/src/types/next-auth.d.ts +0 -21
  142. package/src/use-intersection-observer.tsx +0 -154
  143. package/src/use-local-storage.tsx +0 -71
  144. package/src/use-paddle.ts +0 -138
  145. package/src/use-performance-optimizer.ts +0 -389
  146. package/src/use-pro-access.ts +0 -141
  147. package/src/use-scroll-animation.ts +0 -219
  148. package/src/use-subscription.ts +0 -37
  149. package/src/use-toast.ts +0 -32
  150. package/src/utils/chart-helpers.ts +0 -357
  151. package/src/utils/cn.ts +0 -6
  152. package/src/utils/data-processing.ts +0 -151
  153. package/src/utils/license-validator.tsx +0 -183
@@ -1,343 +0,0 @@
1
- "use client"
2
-
3
- import React, { useState, useRef, useEffect } from "react"
4
- import { cn } from "../../lib/utils"
5
- import { Input } from "../ui/input"
6
- import { Label } from "../ui/label"
7
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
8
- import { Phone, Globe, Check, X } from "lucide-react"
9
- import { motion, AnimatePresence } from "framer-motion"
10
-
11
- // Ülke kodları ve bayrakları
12
- const countries = [
13
- { code: 'US', name: 'United States', dialCode: '+1', flag: '🇺🇸', format: '(xxx) xxx-xxxx' },
14
- { code: 'GB', name: 'United Kingdom', dialCode: '+44', flag: '🇬🇧', format: 'xxxx xxxxxx' },
15
- { code: 'TR', name: 'Turkey', dialCode: '+90', flag: '🇹🇷', format: '(xxx) xxx xx xx' },
16
- { code: 'DE', name: 'Germany', dialCode: '+49', flag: '🇩🇪', format: 'xxx xxxxxxxx' },
17
- { code: 'FR', name: 'France', dialCode: '+33', flag: '🇫🇷', format: 'x xx xx xx xx' },
18
- { code: 'IT', name: 'Italy', dialCode: '+39', flag: '🇮🇹', format: 'xxx xxxxxxx' },
19
- { code: 'ES', name: 'Spain', dialCode: '+34', flag: '🇪🇸', format: 'xxx xxx xxx' },
20
- { code: 'CN', name: 'China', dialCode: '+86', flag: '🇨🇳', format: 'xxx xxxx xxxx' },
21
- { code: 'JP', name: 'Japan', dialCode: '+81', flag: '🇯🇵', format: 'xx xxxx xxxx' },
22
- { code: 'KR', name: 'South Korea', dialCode: '+82', flag: '🇰🇷', format: 'xx xxxx xxxx' },
23
- { code: 'IN', name: 'India', dialCode: '+91', flag: '🇮🇳', format: 'xxxxx xxxxx' },
24
- { code: 'BR', name: 'Brazil', dialCode: '+55', flag: '🇧🇷', format: '(xx) xxxxx-xxxx' },
25
- { code: 'MX', name: 'Mexico', dialCode: '+52', flag: '🇲🇽', format: 'xxx xxx xxxx' },
26
- { code: 'CA', name: 'Canada', dialCode: '+1', flag: '🇨🇦', format: '(xxx) xxx-xxxx' },
27
- { code: 'AU', name: 'Australia', dialCode: '+61', flag: '🇦🇺', format: 'xxx xxx xxx' },
28
- { code: 'RU', name: 'Russia', dialCode: '+7', flag: '🇷🇺', format: '(xxx) xxx-xx-xx' },
29
- { code: 'NL', name: 'Netherlands', dialCode: '+31', flag: '🇳🇱', format: 'x xxxxxxxx' },
30
- { code: 'SE', name: 'Sweden', dialCode: '+46', flag: '🇸🇪', format: 'xx xxx xx xx' },
31
- { code: 'NO', name: 'Norway', dialCode: '+47', flag: '🇳🇴', format: 'xxx xx xxx' },
32
- { code: 'FI', name: 'Finland', dialCode: '+358', flag: '🇫🇮', format: 'xx xxxxxxx' }
33
- ]
34
-
35
- export interface PhoneNumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value'> {
36
- value?: {
37
- country: string
38
- number: string
39
- }
40
- onChange?: (value: { country: string; number: string; fullNumber: string; isValid: boolean }) => void
41
- defaultCountry?: string
42
- countries?: typeof countries
43
- showFlags?: boolean
44
- showDialCode?: boolean
45
- autoFormat?: boolean
46
- validateOnChange?: boolean
47
- showValidationIcon?: boolean
48
- label?: string
49
- error?: string
50
- helperText?: string
51
- required?: boolean
52
- className?: string
53
- inputClassName?: string
54
- selectClassName?: string
55
- labelClassName?: string
56
- errorClassName?: string
57
- allowInternationalFormat?: boolean
58
- }
59
-
60
- // Telefon numarası formatlama
61
- function formatPhoneNumber(number: string, format: string): string {
62
- if (!number || !format) return ''
63
- const cleaned = number.replace(/\D/g, '')
64
- let formatted = ''
65
- let digitIndex = 0
66
-
67
- for (let i = 0; i < format.length && digitIndex < cleaned.length; i++) {
68
- if (format[i] === 'x') {
69
- formatted += cleaned[digitIndex]
70
- digitIndex++
71
- } else {
72
- formatted += format[i]
73
- }
74
- }
75
-
76
- return formatted
77
- }
78
-
79
- // Telefon numarası doğrulama
80
- function validatePhoneNumber(number: string, countryCode: string): boolean {
81
- if (!number || !countryCode) return false
82
- const cleaned = number.replace(/\D/g, '')
83
- const country = countries.find(c => c.code === countryCode)
84
-
85
- if (!country) return false
86
-
87
- // Format'taki x sayısını say
88
- const expectedLength = country.format.split('').filter(c => c === 'x').length
89
-
90
- return cleaned.length === expectedLength
91
- }
92
-
93
- // Uluslararası format dönüştürme
94
- function toInternationalFormat(countryCode: string, phoneNumber: string): string {
95
- if (!countryCode || !phoneNumber) return ''
96
- const country = countries.find(c => c.code === countryCode)
97
- if (!country) return phoneNumber
98
-
99
- const cleaned = phoneNumber.replace(/\D/g, '')
100
- return `${country.dialCode}${cleaned}`
101
- }
102
-
103
- const defaultValue = { country: 'US', number: '' }
104
-
105
- export const MoonUIPhoneNumberInputPro = React.forwardRef<HTMLDivElement, PhoneNumberInputProps>(({
106
- value,
107
- onChange,
108
- defaultCountry = 'US',
109
- countries: customCountries,
110
- showFlags = true,
111
- showDialCode = true,
112
- autoFormat = true,
113
- validateOnChange = true,
114
- showValidationIcon = true,
115
- label,
116
- error,
117
- helperText,
118
- required,
119
- className,
120
- inputClassName,
121
- selectClassName,
122
- labelClassName,
123
- errorClassName,
124
- disabled,
125
- allowInternationalFormat = true,
126
- ...props
127
- }, ref) => {
128
- // Stable default value
129
- const phoneValue = React.useMemo(() => {
130
- return value || defaultValue
131
- }, [value])
132
- const [isFocused, setIsFocused] = useState(false)
133
- const inputRef = useRef<HTMLInputElement>(null)
134
-
135
- const countryList = customCountries || countries
136
- const selectedCountry = countryList.find(c => c.code === phoneValue.country) || countryList[0]
137
-
138
- // Doğrulama - useEffect yerine doğrudan hesapla
139
- const isValid = validateOnChange && phoneValue.number
140
- ? validatePhoneNumber(phoneValue.number, phoneValue.country)
141
- : false
142
-
143
- const handleCountryChange = (countryCode: string) => {
144
- const fullNumber = allowInternationalFormat
145
- ? toInternationalFormat(countryCode, phoneValue.number)
146
- : phoneValue.number
147
-
148
- onChange?.({
149
- country: countryCode,
150
- number: phoneValue.number,
151
- fullNumber,
152
- isValid: validatePhoneNumber(phoneValue.number, countryCode)
153
- })
154
- }
155
-
156
- const handleNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {
157
- let newValue = e.target.value
158
-
159
- if (autoFormat) {
160
- // Sadece sayıları al
161
- const cleaned = newValue.replace(/\D/g, '')
162
- // Format uygula
163
- newValue = formatPhoneNumber(cleaned, selectedCountry.format)
164
- }
165
-
166
- const valid = validatePhoneNumber(newValue, phoneValue.country)
167
- const fullNumber = allowInternationalFormat
168
- ? toInternationalFormat(phoneValue.country, newValue)
169
- : newValue
170
-
171
- onChange?.({
172
- country: phoneValue.country,
173
- number: newValue,
174
- fullNumber,
175
- isValid: valid
176
- })
177
- }
178
-
179
- const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
180
- if (!autoFormat) return
181
-
182
- e.preventDefault()
183
- const pastedText = e.clipboardData.getData('text')
184
- const cleaned = pastedText.replace(/\D/g, '')
185
- const formatted = formatPhoneNumber(cleaned, selectedCountry.format)
186
-
187
- const valid = validatePhoneNumber(formatted, phoneValue.country)
188
- const fullNumber = allowInternationalFormat
189
- ? toInternationalFormat(phoneValue.country, formatted)
190
- : formatted
191
-
192
- onChange?.({
193
- country: phoneValue.country,
194
- number: formatted,
195
- fullNumber,
196
- isValid: valid
197
- })
198
- }
199
-
200
- return (
201
- <div ref={ref} className={cn("space-y-2", className)}>
202
- {label && (
203
- <Label htmlFor="phone-number" className={labelClassName}>
204
- {label}
205
- {required && <span className="text-destructive ml-1">*</span>}
206
- </Label>
207
- )}
208
-
209
- <div className="flex gap-2">
210
- {/* Ülke Seçici */}
211
- <Select
212
- value={phoneValue.country}
213
- onValueChange={handleCountryChange}
214
- disabled={disabled}
215
- >
216
- <SelectTrigger
217
- className={cn(
218
- "w-[140px]",
219
- error && "border-destructive",
220
- selectClassName
221
- )}
222
- >
223
- <SelectValue>
224
- <div className="flex items-center gap-2">
225
- {showFlags && <span className="text-lg">{selectedCountry.flag}</span>}
226
- {showDialCode && <span className="text-sm">{selectedCountry.dialCode}</span>}
227
- {!showFlags && !showDialCode && <span className="text-sm">{selectedCountry.code}</span>}
228
- </div>
229
- </SelectValue>
230
- </SelectTrigger>
231
- <SelectContent>
232
- {countryList.map((country) => (
233
- <SelectItem key={country.code} value={country.code}>
234
- <div className="flex items-center gap-2">
235
- {showFlags && <span className="text-lg">{country.flag}</span>}
236
- <span className="text-sm">{country.name}</span>
237
- {showDialCode && <span className="text-muted-foreground text-sm">{country.dialCode}</span>}
238
- </div>
239
- </SelectItem>
240
- ))}
241
- </SelectContent>
242
- </Select>
243
-
244
- {/* Telefon Numarası Input */}
245
- <div className="relative flex-1">
246
- <Input
247
- ref={inputRef}
248
- id="phone-number"
249
- type="tel"
250
- value={phoneValue.number}
251
- onChange={handleNumberChange}
252
- onPaste={handlePaste}
253
- onFocus={() => setIsFocused(true)}
254
- onBlur={() => setIsFocused(false)}
255
- placeholder={selectedCountry?.format?.replace(/x/g, '•') || 'Enter phone number'}
256
- className={cn(
257
- "pr-10",
258
- error && "border-destructive",
259
- inputClassName
260
- )}
261
- disabled={disabled}
262
- />
263
-
264
- {/* Doğrulama İkonu */}
265
- {showValidationIcon && phoneValue.number && (
266
- <div className="absolute right-3 top-1/2 -translate-y-1/2">
267
- <AnimatePresence mode="wait">
268
- {isValid ? (
269
- <motion.div
270
- key="valid"
271
- initial={{ scale: 0, opacity: 0 }}
272
- animate={{ scale: 1, opacity: 1 }}
273
- exit={{ scale: 0, opacity: 0 }}
274
- transition={{ duration: 0.2 }}
275
- >
276
- <Check className="w-4 h-4 text-green-500" />
277
- </motion.div>
278
- ) : (
279
- <motion.div
280
- key="invalid"
281
- initial={{ scale: 0, opacity: 0 }}
282
- animate={{ scale: 1, opacity: 1 }}
283
- exit={{ scale: 0, opacity: 0 }}
284
- transition={{ duration: 0.2 }}
285
- >
286
- <X className="w-4 h-4 text-destructive" />
287
- </motion.div>
288
- )}
289
- </AnimatePresence>
290
- </div>
291
- )}
292
- </div>
293
- </div>
294
-
295
- {/* Helper Text veya Error */}
296
- {(error || helperText) && (
297
- <AnimatePresence mode="wait">
298
- {error ? (
299
- <motion.p
300
- key="error"
301
- initial={{ opacity: 0, height: 0 }}
302
- animate={{ opacity: 1, height: 'auto' }}
303
- exit={{ opacity: 0, height: 0 }}
304
- className={cn("text-sm text-destructive", errorClassName)}
305
- >
306
- {error}
307
- </motion.p>
308
- ) : (
309
- <motion.p
310
- key="helper"
311
- initial={{ opacity: 0, height: 0 }}
312
- animate={{ opacity: 1, height: 'auto' }}
313
- exit={{ opacity: 0, height: 0 }}
314
- className="text-sm text-muted-foreground"
315
- >
316
- {helperText}
317
- </motion.p>
318
- )}
319
- </AnimatePresence>
320
- )}
321
-
322
- {/* Uluslararası Format Gösterimi */}
323
- {allowInternationalFormat && phoneValue.number && isValid && (
324
- <motion.div
325
- initial={{ opacity: 0, y: -10 }}
326
- animate={{ opacity: 1, y: 0 }}
327
- className="flex items-center gap-2 text-sm text-muted-foreground"
328
- >
329
- <Globe className="w-4 h-4" />
330
- <span>International: {toInternationalFormat(phoneValue.country, phoneValue.number)}</span>
331
- </motion.div>
332
- )}
333
- </div>
334
- )
335
- })
336
-
337
- MoonUIPhoneNumberInputPro.displayName = "MoonUIPhoneNumberInputPro"
338
-
339
- // Ülke listesini de export edelim
340
- export { countries as phoneCountries }
341
-
342
- // Basit versiyon
343
- export { MoonUIPhoneNumberInputSimple } from "./phone-number-input-simple"
@@ -1,167 +0,0 @@
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"