@moontra/moonui 2.1.9 โ†’ 2.2.1

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 (64) hide show
  1. package/dist/index.d.mts +1656 -0
  2. package/dist/index.d.ts +1656 -0
  3. package/dist/index.js +1847 -469
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1679 -471
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +4 -4
  8. package/src/components/ui/accordion.tsx +1 -1
  9. package/src/components/ui/alert.tsx +123 -93
  10. package/src/components/ui/aspect-ratio.tsx +1 -1
  11. package/src/components/ui/avatar.tsx +8 -6
  12. package/src/components/ui/badge.tsx +20 -18
  13. package/src/components/ui/breadcrumb.tsx +12 -10
  14. package/src/components/ui/button.stories.tsx +4 -2
  15. package/src/components/ui/button.tsx +71 -68
  16. package/src/components/ui/calendar.tsx +2 -2
  17. package/src/components/ui/card-input.tsx +231 -0
  18. package/src/components/ui/card.stories.tsx +2 -0
  19. package/src/components/ui/card.tsx +9 -7
  20. package/src/components/ui/checkbox.tsx +1 -1
  21. package/src/components/ui/collapsible.tsx +3 -3
  22. package/src/components/ui/color-picker.tsx +2 -2
  23. package/src/components/ui/command.tsx +4 -4
  24. package/src/components/ui/data-table.stories.tsx +4 -2
  25. package/src/components/ui/data-table.tsx +8 -8
  26. package/src/components/ui/date-picker.stories.tsx +2 -0
  27. package/src/components/ui/date-picker.tsx +25 -15
  28. package/src/components/ui/dialog.tsx +2 -2
  29. package/src/components/ui/draggable-list.tsx +3 -1
  30. package/src/components/ui/dropdown-menu.tsx +2 -2
  31. package/src/components/ui/file-upload.tsx +2 -2
  32. package/src/components/ui/github-stars.tsx +1 -1
  33. package/src/components/ui/index.ts +357 -45
  34. package/src/components/ui/input.stories.tsx +2 -0
  35. package/src/components/ui/input.tsx +3 -1
  36. package/src/components/ui/locked-component.tsx +222 -0
  37. package/src/components/ui/moon-logo.tsx +1 -1
  38. package/src/components/ui/pagination.tsx +10 -8
  39. package/src/components/ui/phone-input.tsx +172 -0
  40. package/src/components/ui/popover-pro.tsx +424 -0
  41. package/src/components/ui/popover.tsx +3 -3
  42. package/src/components/ui/progress.tsx +1 -1
  43. package/src/components/ui/radio-group.tsx +1 -1
  44. package/src/components/ui/rich-text-editor/index.tsx +2 -1
  45. package/src/components/ui/scroll-area.tsx +48 -0
  46. package/src/components/ui/select.tsx +7 -3
  47. package/src/components/ui/separator.tsx +2 -2
  48. package/src/components/ui/skeleton.tsx +19 -7
  49. package/src/components/ui/slider.tsx +1 -1
  50. package/src/components/ui/swipeable-card.tsx +2 -0
  51. package/src/components/ui/switch.tsx +1 -1
  52. package/src/components/ui/table.tsx +7 -5
  53. package/src/components/ui/tabs.tsx +6 -1
  54. package/src/components/ui/tags-input.tsx +130 -0
  55. package/src/components/ui/textarea.tsx +3 -1
  56. package/src/components/ui/toast.tsx +5 -3
  57. package/src/components/ui/toggle.tsx +2 -3
  58. package/src/components/ui/tooltip.tsx +2 -5
  59. package/src/index.tsx +4 -46
  60. package/src/lib/micro-interactions.ts +2 -1
  61. package/src/lib/performance-profiler.ts +79 -0
  62. package/src/styles/index.css +315 -2
  63. package/src/use-performance-optimizer.ts +26 -26
  64. package/src/use-scroll-animation.ts +5 -7
@@ -0,0 +1,222 @@
1
+ "use client"
2
+
3
+ // Locked Component UI
4
+ // Pro componentlerin locked state'ini gรถsteren wrapper component
5
+
6
+ import React, { useState } from 'react'
7
+ import { cn } from '../../lib/utils'
8
+ import { Button } from './button'
9
+ import { Badge } from './badge'
10
+ import { Lock, Crown, Zap } from 'lucide-react'
11
+
12
+ interface LockedComponentProps {
13
+ children: React.ReactNode
14
+ componentName: string
15
+ className?: string
16
+ showPreview?: boolean
17
+ previewDuration?: number // ms
18
+ }
19
+
20
+ export function LockedComponent({
21
+ children,
22
+ componentName,
23
+ className,
24
+ showPreview = true,
25
+ previewDuration = 2000
26
+ }: LockedComponentProps) {
27
+ const [isPreviewActive, setIsPreviewActive] = useState(false)
28
+ const [previewTimeout, setPreviewTimeout] = useState<NodeJS.Timeout | null>(null)
29
+
30
+ const handleMouseEnter = () => {
31
+ if (!showPreview) return
32
+
33
+ setIsPreviewActive(true)
34
+
35
+ // Clear existing timeout
36
+ if (previewTimeout) {
37
+ clearTimeout(previewTimeout)
38
+ }
39
+
40
+ // Set new timeout
41
+ const timeout = setTimeout(() => {
42
+ setIsPreviewActive(false)
43
+ }, previewDuration)
44
+
45
+ setPreviewTimeout(timeout)
46
+ }
47
+
48
+ const handleMouseLeave = () => {
49
+ if (previewTimeout) {
50
+ clearTimeout(previewTimeout)
51
+ setPreviewTimeout(null)
52
+ }
53
+ setIsPreviewActive(false)
54
+ }
55
+
56
+ return (
57
+ <div
58
+ className={cn(
59
+ "relative group cursor-pointer transition-all duration-300",
60
+ className
61
+ )}
62
+ onMouseEnter={handleMouseEnter}
63
+ onMouseLeave={handleMouseLeave}
64
+ >
65
+ {/* Component Content */}
66
+ <div
67
+ className={cn(
68
+ "transition-all duration-300",
69
+ !isPreviewActive && "blur-sm grayscale-[0.3] opacity-60"
70
+ )}
71
+ >
72
+ {children}
73
+ </div>
74
+
75
+ {/* Overlay */}
76
+ <div
77
+ className={cn(
78
+ "absolute inset-0 bg-gradient-to-br from-amber-500/10 via-transparent to-purple-500/10",
79
+ "border-2 border-dashed border-amber-400/30 rounded-lg",
80
+ "flex items-center justify-center",
81
+ "transition-all duration-300",
82
+ isPreviewActive ? "opacity-0" : "opacity-100"
83
+ )}
84
+ >
85
+ <div className="text-center space-y-3 p-6">
86
+ {/* Pro Badge */}
87
+ <div className="flex justify-center">
88
+ <Badge
89
+ variant="secondary"
90
+ className="bg-gradient-to-r from-amber-500 to-orange-500 text-white border-0 px-3 py-1"
91
+ >
92
+ <Crown className="w-3 h-3 mr-1" />
93
+ PRO ONLY
94
+ </Badge>
95
+ </div>
96
+
97
+ {/* Lock Icon */}
98
+ <div className="flex justify-center">
99
+ <div className="p-3 rounded-full bg-amber-100 dark:bg-amber-900/30">
100
+ <Lock className="w-6 h-6 text-amber-600 dark:text-amber-400" />
101
+ </div>
102
+ </div>
103
+
104
+ {/* Component Name */}
105
+ <h3 className="font-semibold text-gray-900 dark:text-gray-100">
106
+ {componentName}
107
+ </h3>
108
+
109
+ {/* Description */}
110
+ <p className="text-sm text-gray-600 dark:text-gray-400 max-w-xs">
111
+ This premium component is available with Pro subscription
112
+ </p>
113
+
114
+ {/* Upgrade Button */}
115
+ <Button
116
+ size="sm"
117
+ className="bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600 border-0"
118
+ onClick={() => {
119
+ if (typeof window !== 'undefined') {
120
+ window.location.href = '/pricing'
121
+ }
122
+ }}
123
+ >
124
+ <Zap className="w-4 h-4 mr-2" />
125
+ Upgrade to Pro
126
+ </Button>
127
+ </div>
128
+ </div>
129
+
130
+ {/* Preview Hint */}
131
+ {showPreview && !isPreviewActive && (
132
+ <div className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
133
+ <div className="bg-black/80 text-white text-xs px-2 py-1 rounded">
134
+ Hover to preview
135
+ </div>
136
+ </div>
137
+ )}
138
+ </div>
139
+ )
140
+ }
141
+
142
+ // Pro Component Wrapper - Otomatik eriลŸim kontrolรผ ile
143
+ interface ProComponentWrapperProps {
144
+ children: React.ReactNode
145
+ componentId: string
146
+ componentName?: string
147
+ className?: string
148
+ fallback?: React.ReactNode
149
+ }
150
+
151
+ export function ProComponentWrapper({
152
+ children,
153
+ componentId,
154
+ componentName,
155
+ className,
156
+ fallback
157
+ }: ProComponentWrapperProps) {
158
+ // For now, always show the component (no access control)
159
+ // This can be updated later when proper access control is implemented
160
+ const canUse = true
161
+ const isLocked = false
162
+ const isLoading = false
163
+
164
+ // Loading state
165
+ if (isLoading) {
166
+ return (
167
+ <div className={cn("animate-pulse bg-gray-200 dark:bg-gray-800 rounded-lg h-32", className)}>
168
+ <div className="flex items-center justify-center h-full">
169
+ <div className="text-sm text-gray-500">Loading...</div>
170
+ </div>
171
+ </div>
172
+ )
173
+ }
174
+
175
+ // Unlocked - show actual component
176
+ if (canUse) {
177
+ return <div className={className}>{children}</div>
178
+ }
179
+
180
+ // Locked - show locked state
181
+ if (isLocked) {
182
+ return (
183
+ <LockedComponent
184
+ componentName={componentName || componentId}
185
+ className={className}
186
+ >
187
+ {fallback || children}
188
+ </LockedComponent>
189
+ )
190
+ }
191
+
192
+ // Default fallback
193
+ return <div className={className}>{children}</div>
194
+ }
195
+
196
+ // Pro Badge Component
197
+ export function ProBadge({ className }: { className?: string }) {
198
+ return (
199
+ <Badge
200
+ variant="secondary"
201
+ className={cn(
202
+ "bg-gradient-to-r from-amber-500 to-orange-500 text-white border-0 text-xs",
203
+ className
204
+ )}
205
+ >
206
+ <Crown className="w-3 h-3 mr-1" />
207
+ PRO
208
+ </Badge>
209
+ )
210
+ }
211
+
212
+ // Free Badge Component
213
+ export function FreeBadge({ className }: { className?: string }) {
214
+ return (
215
+ <Badge
216
+ variant="outline"
217
+ className={cn("border-green-500 text-green-600 dark:text-green-400 text-xs", className)}
218
+ >
219
+ FREE
220
+ </Badge>
221
+ )
222
+ }
@@ -18,7 +18,7 @@ export function MoonLogo({
18
18
  const gradientId = `moon-gradient-${logoId}`
19
19
 
20
20
  return (
21
- <div className={cn("flex items-center gap-2", className)}>
21
+ <div className={cn("moonui-theme", "flex items-center gap-2", className)}>
22
22
  {/* Moon Icon */}
23
23
  <svg
24
24
  width="32"
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  import * as React from "react"
2
4
  import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
3
5
  import { cn } from "../../lib/utils"
@@ -7,7 +9,7 @@ const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
7
9
  <nav
8
10
  role="navigation"
9
11
  aria-label="pagination"
10
- className={cn("mx-auto flex w-full justify-center", className)}
12
+ className={cn("moonui-theme", "mx-auto flex w-full justify-center", className)}
11
13
  {...props}
12
14
  />
13
15
  )
@@ -19,7 +21,7 @@ const PaginationContent = React.forwardRef<
19
21
  >(({ className, ...props }, ref) => (
20
22
  <ul
21
23
  ref={ref}
22
- className={cn("flex flex-row items-center gap-1", className)}
24
+ className={cn("moonui-theme", "flex flex-row items-center gap-1", className)}
23
25
  {...props}
24
26
  />
25
27
  ))
@@ -29,7 +31,7 @@ const PaginationItem = React.forwardRef<
29
31
  HTMLLIElement,
30
32
  React.ComponentProps<"li">
31
33
  >(({ className, ...props }, ref) => (
32
- <li ref={ref} className={cn("", className)} {...props} />
34
+ <li ref={ref} className={cn("moonui-theme", "", className)} {...props} />
33
35
  ))
34
36
  PaginationItem.displayName = "PaginationItem"
35
37
 
@@ -64,8 +66,8 @@ const PaginationPrevious = ({
64
66
  }: React.ComponentProps<typeof PaginationLink>) => (
65
67
  <PaginationLink
66
68
  aria-label="Go to previous page"
67
- size="default"
68
- className={cn("gap-1 pl-2.5", className)}
69
+ size="md"
70
+ className={cn("moonui-theme", "gap-1 pl-2.5", className)}
69
71
  {...props}
70
72
  >
71
73
  <ChevronLeft className="h-4 w-4" />
@@ -80,8 +82,8 @@ const PaginationNext = ({
80
82
  }: React.ComponentProps<typeof PaginationLink>) => (
81
83
  <PaginationLink
82
84
  aria-label="Go to next page"
83
- size="default"
84
- className={cn("gap-1 pr-2.5", className)}
85
+ size="md"
86
+ className={cn("moonui-theme", "gap-1 pr-2.5", className)}
85
87
  {...props}
86
88
  >
87
89
  <span>Next</span>
@@ -96,7 +98,7 @@ const PaginationEllipsis = ({
96
98
  }: React.ComponentProps<"span">) => (
97
99
  <span
98
100
  aria-hidden
99
- className={cn("flex h-9 w-9 items-center justify-center", className)}
101
+ className={cn("moonui-theme", "flex h-9 w-9 items-center justify-center", className)}
100
102
  {...props}
101
103
  >
102
104
  <MoreHorizontal className="h-4 w-4" />
@@ -0,0 +1,172 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../lib/utils"
5
+ import { Input } from "./input"
6
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select"
7
+ import { Phone } from "lucide-react"
8
+
9
+ // Country codes data
10
+ const countryCodes = [
11
+ { code: "+1", country: "US", flag: "๐Ÿ‡บ๐Ÿ‡ธ", name: "United States" },
12
+ { code: "+44", country: "GB", flag: "๐Ÿ‡ฌ๐Ÿ‡ง", name: "United Kingdom" },
13
+ { code: "+49", country: "DE", flag: "๐Ÿ‡ฉ๐Ÿ‡ช", name: "Germany" },
14
+ { code: "+33", country: "FR", flag: "๐Ÿ‡ซ๐Ÿ‡ท", name: "France" },
15
+ { code: "+39", country: "IT", flag: "๐Ÿ‡ฎ๐Ÿ‡น", name: "Italy" },
16
+ { code: "+34", country: "ES", flag: "๐Ÿ‡ช๐Ÿ‡ธ", name: "Spain" },
17
+ { code: "+31", country: "NL", flag: "๐Ÿ‡ณ๐Ÿ‡ฑ", name: "Netherlands" },
18
+ { code: "+46", country: "SE", flag: "๐Ÿ‡ธ๐Ÿ‡ช", name: "Sweden" },
19
+ { code: "+47", country: "NO", flag: "๐Ÿ‡ณ๐Ÿ‡ด", name: "Norway" },
20
+ { code: "+45", country: "DK", flag: "๐Ÿ‡ฉ๐Ÿ‡ฐ", name: "Denmark" },
21
+ { code: "+358", country: "FI", flag: "๐Ÿ‡ซ๐Ÿ‡ฎ", name: "Finland" },
22
+ { code: "+48", country: "PL", flag: "๐Ÿ‡ต๐Ÿ‡ฑ", name: "Poland" },
23
+ { code: "+90", country: "TR", flag: "๐Ÿ‡น๐Ÿ‡ท", name: "Turkey" },
24
+ { code: "+86", country: "CN", flag: "๐Ÿ‡จ๐Ÿ‡ณ", name: "China" },
25
+ { code: "+81", country: "JP", flag: "๐Ÿ‡ฏ๐Ÿ‡ต", name: "Japan" },
26
+ { code: "+82", country: "KR", flag: "๐Ÿ‡ฐ๐Ÿ‡ท", name: "South Korea" },
27
+ { code: "+91", country: "IN", flag: "๐Ÿ‡ฎ๐Ÿ‡ณ", name: "India" },
28
+ { code: "+61", country: "AU", flag: "๐Ÿ‡ฆ๐Ÿ‡บ", name: "Australia" },
29
+ { code: "+64", country: "NZ", flag: "๐Ÿ‡ณ๐Ÿ‡ฟ", name: "New Zealand" },
30
+ { code: "+27", country: "ZA", flag: "๐Ÿ‡ฟ๐Ÿ‡ฆ", name: "South Africa" },
31
+ { code: "+55", country: "BR", flag: "๐Ÿ‡ง๐Ÿ‡ท", name: "Brazil" },
32
+ { code: "+52", country: "MX", flag: "๐Ÿ‡ฒ๐Ÿ‡ฝ", name: "Mexico" },
33
+ { code: "+54", country: "AR", flag: "๐Ÿ‡ฆ๐Ÿ‡ท", name: "Argentina" },
34
+ { code: "+20", country: "EG", flag: "๐Ÿ‡ช๐Ÿ‡ฌ", name: "Egypt" },
35
+ { code: "+966", country: "SA", flag: "๐Ÿ‡ธ๐Ÿ‡ฆ", name: "Saudi Arabia" },
36
+ { code: "+971", country: "AE", flag: "๐Ÿ‡ฆ๐Ÿ‡ช", name: "UAE" },
37
+ ]
38
+
39
+ // Format phone number based on country
40
+ const formatPhoneNumber = (value: string, countryCode: string): string => {
41
+ const cleaned = value.replace(/\D/g, "")
42
+
43
+ switch (countryCode) {
44
+ case "+1": // US/Canada
45
+ if (cleaned.length <= 3) return cleaned
46
+ if (cleaned.length <= 6) return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3)}`
47
+ return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6, 10)}`
48
+
49
+ case "+44": // UK
50
+ if (cleaned.length <= 4) return cleaned
51
+ if (cleaned.length <= 7) return `${cleaned.slice(0, 4)} ${cleaned.slice(4)}`
52
+ return `${cleaned.slice(0, 4)} ${cleaned.slice(4, 7)} ${cleaned.slice(7, 11)}`
53
+
54
+ case "+90": // Turkey
55
+ if (cleaned.length <= 3) return cleaned
56
+ if (cleaned.length <= 6) return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3)}`
57
+ if (cleaned.length <= 9) return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)} ${cleaned.slice(6)}`
58
+ return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)} ${cleaned.slice(6, 8)} ${cleaned.slice(8, 10)}`
59
+
60
+ default: // Generic formatting
61
+ let formatted = ""
62
+ for (let i = 0; i < cleaned.length; i++) {
63
+ if (i > 0 && i % 3 === 0) formatted += " "
64
+ formatted += cleaned[i]
65
+ }
66
+ return formatted
67
+ }
68
+ }
69
+
70
+ // Get max length based on country
71
+ const getMaxLength = (countryCode: string): number => {
72
+ switch (countryCode) {
73
+ case "+1": return 10 // US/Canada
74
+ case "+44": return 11 // UK
75
+ case "+90": return 10 // Turkey
76
+ default: return 15 // Generic max
77
+ }
78
+ }
79
+
80
+ export interface PhoneInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange" | "value"> {
81
+ value?: string
82
+ onChange?: (value: string, countryCode: string) => void
83
+ defaultCountry?: string
84
+ showIcon?: boolean
85
+ showCountrySelect?: boolean
86
+ countries?: typeof countryCodes
87
+ }
88
+
89
+ export const PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(
90
+ ({
91
+ className,
92
+ value = "",
93
+ onChange,
94
+ defaultCountry = "+1",
95
+ showIcon = true,
96
+ showCountrySelect = true,
97
+ countries = countryCodes,
98
+ size,
99
+ ...props
100
+ }, ref) => {
101
+ const [countryCode, setCountryCode] = React.useState(defaultCountry)
102
+ const [phoneNumber, setPhoneNumber] = React.useState(value)
103
+
104
+ const selectedCountry = countries.find(c => c.code === countryCode) || countries[0]
105
+
106
+ const handlePhoneChange = (e: React.ChangeEvent<HTMLInputElement>) => {
107
+ const rawValue = e.target.value.replace(/\D/g, "")
108
+ const maxLength = getMaxLength(countryCode)
109
+ const truncated = rawValue.slice(0, maxLength)
110
+ const formatted = formatPhoneNumber(truncated, countryCode)
111
+
112
+ setPhoneNumber(truncated)
113
+ onChange?.(truncated, countryCode)
114
+
115
+ // Update the input value with formatted version
116
+ e.target.value = formatted
117
+ }
118
+
119
+ const handleCountryChange = (newCountryCode: string) => {
120
+ setCountryCode(newCountryCode)
121
+ onChange?.(phoneNumber, newCountryCode)
122
+ }
123
+
124
+ const formattedValue = formatPhoneNumber(phoneNumber, countryCode)
125
+
126
+ return (
127
+ <div className={cn("relative flex gap-2", className)}>
128
+ {showCountrySelect && (
129
+ <Select value={countryCode} onValueChange={handleCountryChange}>
130
+ <SelectTrigger className="w-[120px]">
131
+ <SelectValue>
132
+ <div className="flex items-center gap-2">
133
+ <span>{selectedCountry.flag}</span>
134
+ <span>{selectedCountry.code}</span>
135
+ </div>
136
+ </SelectValue>
137
+ </SelectTrigger>
138
+ <SelectContent>
139
+ {countries.map((country) => (
140
+ <SelectItem key={country.code} value={country.code}>
141
+ <div className="flex items-center gap-2">
142
+ <span>{country.flag}</span>
143
+ <span>{country.code}</span>
144
+ <span className="text-muted-foreground">{country.name}</span>
145
+ </div>
146
+ </SelectItem>
147
+ ))}
148
+ </SelectContent>
149
+ </Select>
150
+ )}
151
+
152
+ <div className="relative flex-1">
153
+ <Input
154
+ ref={ref}
155
+ type="tel"
156
+ inputMode="numeric"
157
+ autoComplete="tel"
158
+ placeholder={countryCode === "+1" ? "(555) 123-4567" : "123 456 789"}
159
+ value={formattedValue}
160
+ onChange={handlePhoneChange}
161
+ className={cn(showIcon && "pl-10", className)}
162
+ {...props}
163
+ />
164
+ {showIcon && (
165
+ <Phone className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
166
+ )}
167
+ </div>
168
+ </div>
169
+ )
170
+ }
171
+ )
172
+ PhoneInput.displayName = "PhoneInput"