@moontra/moonui-pro 2.0.21 → 2.0.23

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 (97) hide show
  1. package/dist/index.mjs +771 -20
  2. package/package.json +2 -1
  3. package/src/__tests__/use-intersection-observer.test.tsx +216 -0
  4. package/src/__tests__/use-local-storage.test.tsx +174 -0
  5. package/src/__tests__/use-pro-access.test.tsx +183 -0
  6. package/src/components/advanced-chart/advanced-chart.test.tsx +281 -0
  7. package/src/components/advanced-chart/index.tsx +412 -0
  8. package/src/components/advanced-forms/index.tsx +431 -0
  9. package/src/components/animated-button/index.tsx +202 -0
  10. package/src/components/calendar/event-dialog.tsx +372 -0
  11. package/src/components/calendar/index.tsx +531 -0
  12. package/src/components/color-picker/index.tsx +434 -0
  13. package/src/components/dashboard/index.tsx +334 -0
  14. package/src/components/data-table/data-table.test.tsx +187 -0
  15. package/src/components/data-table/index.tsx +368 -0
  16. package/src/components/draggable-list/index.tsx +100 -0
  17. package/src/components/enhanced/button.tsx +360 -0
  18. package/src/components/enhanced/card.tsx +272 -0
  19. package/src/components/enhanced/dialog.tsx +248 -0
  20. package/src/components/enhanced/index.ts +3 -0
  21. package/src/components/error-boundary/index.tsx +111 -0
  22. package/src/components/file-upload/file-upload.test.tsx +242 -0
  23. package/src/components/file-upload/index.tsx +362 -0
  24. package/src/components/floating-action-button/index.tsx +209 -0
  25. package/src/components/github-stars/index.tsx +414 -0
  26. package/src/components/health-check/index.tsx +441 -0
  27. package/src/components/hover-card-3d/index.tsx +170 -0
  28. package/src/components/index.ts +76 -0
  29. package/src/components/kanban/index.tsx +436 -0
  30. package/src/components/lazy-component/index.tsx +342 -0
  31. package/src/components/magnetic-button/index.tsx +170 -0
  32. package/src/components/memory-efficient-data/index.tsx +352 -0
  33. package/src/components/optimized-image/index.tsx +427 -0
  34. package/src/components/performance-debugger/index.tsx +591 -0
  35. package/src/components/performance-monitor/index.tsx +775 -0
  36. package/src/components/pinch-zoom/index.tsx +172 -0
  37. package/src/components/rich-text-editor/index-old-backup.tsx +443 -0
  38. package/src/components/rich-text-editor/index.tsx +1537 -0
  39. package/src/components/rich-text-editor/slash-commands-extension.ts +220 -0
  40. package/src/components/rich-text-editor/slash-commands.css +35 -0
  41. package/src/components/rich-text-editor/table-styles.css +65 -0
  42. package/src/components/spotlight-card/index.tsx +194 -0
  43. package/src/components/swipeable-card/index.tsx +100 -0
  44. package/src/components/timeline/index.tsx +333 -0
  45. package/src/components/ui/animated-button.tsx +185 -0
  46. package/src/components/ui/avatar.tsx +135 -0
  47. package/src/components/ui/badge.tsx +225 -0
  48. package/src/components/ui/button.tsx +221 -0
  49. package/src/components/ui/card.tsx +141 -0
  50. package/src/components/ui/checkbox.tsx +256 -0
  51. package/src/components/ui/color-picker.tsx +95 -0
  52. package/src/components/ui/dialog.tsx +332 -0
  53. package/src/components/ui/dropdown-menu.tsx +200 -0
  54. package/src/components/ui/hover-card-3d.tsx +103 -0
  55. package/src/components/ui/index.ts +33 -0
  56. package/src/components/ui/input.tsx +219 -0
  57. package/src/components/ui/label.tsx +26 -0
  58. package/src/components/ui/magnetic-button.tsx +129 -0
  59. package/src/components/ui/popover.tsx +183 -0
  60. package/src/components/ui/select.tsx +273 -0
  61. package/src/components/ui/separator.tsx +140 -0
  62. package/src/components/ui/slider.tsx +351 -0
  63. package/src/components/ui/spotlight-card.tsx +119 -0
  64. package/src/components/ui/switch.tsx +83 -0
  65. package/src/components/ui/tabs.tsx +195 -0
  66. package/src/components/ui/textarea.tsx +25 -0
  67. package/src/components/ui/toast.tsx +313 -0
  68. package/src/components/ui/tooltip.tsx +152 -0
  69. package/src/components/virtual-list/index.tsx +369 -0
  70. package/src/hooks/use-chart.ts +205 -0
  71. package/src/hooks/use-data-table.ts +182 -0
  72. package/src/hooks/use-docs-pro-access.ts +13 -0
  73. package/src/hooks/use-license-check.ts +65 -0
  74. package/src/hooks/use-subscription.ts +19 -0
  75. package/src/index.ts +11 -0
  76. package/src/lib/micro-interactions.ts +255 -0
  77. package/src/lib/utils.ts +6 -0
  78. package/src/patterns/login-form/index.tsx +276 -0
  79. package/src/patterns/login-form/types.ts +67 -0
  80. package/src/setupTests.ts +41 -0
  81. package/src/styles/design-system.css +365 -0
  82. package/src/styles/index.css +4 -0
  83. package/src/styles/tailwind.css +6 -0
  84. package/src/styles/tokens.css +453 -0
  85. package/src/types/moonui.d.ts +22 -0
  86. package/src/use-intersection-observer.tsx +154 -0
  87. package/src/use-local-storage.tsx +71 -0
  88. package/src/use-paddle.ts +138 -0
  89. package/src/use-performance-optimizer.ts +379 -0
  90. package/src/use-pro-access.ts +141 -0
  91. package/src/use-scroll-animation.ts +221 -0
  92. package/src/use-subscription.ts +37 -0
  93. package/src/use-toast.ts +32 -0
  94. package/src/utils/chart-helpers.ts +257 -0
  95. package/src/utils/cn.ts +69 -0
  96. package/src/utils/data-processing.ts +151 -0
  97. package/src/utils/license-validator.tsx +183 -0
@@ -0,0 +1,372 @@
1
+ "use client"
2
+
3
+ import React, { useState, useEffect } from 'react'
4
+ import { Button } from '../ui/button'
5
+ import { Input } from '../ui/input'
6
+ import { Label } from '../ui/label'
7
+ import { Textarea } from '../ui/textarea'
8
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
9
+ import {
10
+ Dialog,
11
+ DialogContent,
12
+ DialogDescription,
13
+ DialogHeader,
14
+ DialogTitle,
15
+ DialogFooter,
16
+ } from '../ui/dialog'
17
+ import { CalendarEvent } from './index'
18
+ import { Calendar, Clock, MapPin, Palette, Type } from 'lucide-react'
19
+ import { cn } from '../../lib/utils'
20
+
21
+ interface EventDialogProps {
22
+ open: boolean
23
+ onOpenChange: (open: boolean) => void
24
+ event?: CalendarEvent | null
25
+ selectedDate?: Date | null
26
+ onSave: (event: Omit<CalendarEvent, 'id'> & { id?: string }) => void
27
+ onDelete?: (eventId: string) => void
28
+ mode?: 'create' | 'edit'
29
+ }
30
+
31
+ const EVENT_TYPES = [
32
+ { value: 'meeting', label: 'Meeting', color: '#3b82f6' },
33
+ { value: 'task', label: 'Task', color: '#10b981' },
34
+ { value: 'reminder', label: 'Reminder', color: '#f59e0b' },
35
+ { value: 'event', label: 'Event', color: '#8b5cf6' },
36
+ ] as const
37
+
38
+ const PRESET_COLORS = [
39
+ '#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444',
40
+ '#06b6d4', '#84cc16', '#f97316', '#ec4899', '#6366f1',
41
+ '#14b8a6', '#eab308', '#f43f5e', '#a855f7', '#22c55e',
42
+ ]
43
+
44
+ export function EventDialog({
45
+ open,
46
+ onOpenChange,
47
+ event,
48
+ selectedDate,
49
+ onSave,
50
+ onDelete,
51
+ mode = 'create'
52
+ }: EventDialogProps) {
53
+ const [formData, setFormData] = useState<{
54
+ title: string
55
+ description: string
56
+ date: string
57
+ startTime: string
58
+ endTime: string
59
+ location: string
60
+ type: 'meeting' | 'task' | 'reminder' | 'event'
61
+ color: string
62
+ attendees: string
63
+ }>({
64
+ title: '',
65
+ description: '',
66
+ date: '',
67
+ startTime: '',
68
+ endTime: '',
69
+ location: '',
70
+ type: 'meeting',
71
+ color: '#3b82f6',
72
+ attendees: ''
73
+ })
74
+
75
+ const [errors, setErrors] = useState<Record<string, string>>({})
76
+ const [isSubmitting, setIsSubmitting] = useState(false)
77
+
78
+ // Initialize form data
79
+ useEffect(() => {
80
+ if (mode === 'edit' && event) {
81
+ setFormData({
82
+ title: event.title || '',
83
+ description: event.description || '',
84
+ date: event.date.toISOString().split('T')[0],
85
+ startTime: event.startTime || '',
86
+ endTime: event.endTime || '',
87
+ location: event.location || '',
88
+ type: event.type || 'meeting',
89
+ color: event.color || '#3b82f6',
90
+ attendees: event.attendees?.join(', ') || ''
91
+ })
92
+ } else if (mode === 'create') {
93
+ const dateStr = selectedDate ? selectedDate.toISOString().split('T')[0] : new Date().toISOString().split('T')[0]
94
+ setFormData({
95
+ title: '',
96
+ description: '',
97
+ date: dateStr,
98
+ startTime: '',
99
+ endTime: '',
100
+ location: '',
101
+ type: 'meeting',
102
+ color: '#3b82f6',
103
+ attendees: ''
104
+ })
105
+ }
106
+ setErrors({})
107
+ }, [event, selectedDate, mode, open])
108
+
109
+ const validateForm = () => {
110
+ const newErrors: Record<string, string> = {}
111
+
112
+ if (!formData.title.trim()) {
113
+ newErrors.title = 'Title is required'
114
+ }
115
+
116
+ if (!formData.date) {
117
+ newErrors.date = 'Date is required'
118
+ }
119
+
120
+ if (formData.startTime && formData.endTime) {
121
+ if (formData.startTime >= formData.endTime) {
122
+ newErrors.endTime = 'End time must be after start time'
123
+ }
124
+ }
125
+
126
+ setErrors(newErrors)
127
+ return Object.keys(newErrors).length === 0
128
+ }
129
+
130
+ const handleSubmit = async (e: React.FormEvent) => {
131
+ e.preventDefault()
132
+
133
+ if (!validateForm()) {
134
+ return
135
+ }
136
+
137
+ setIsSubmitting(true)
138
+
139
+ try {
140
+ const eventData = {
141
+ ...(mode === 'edit' && event ? { id: event.id } : {}),
142
+ title: formData.title.trim(),
143
+ description: formData.description.trim(),
144
+ date: new Date(formData.date + 'T00:00:00'),
145
+ startTime: formData.startTime || undefined,
146
+ endTime: formData.endTime || undefined,
147
+ location: formData.location.trim() || undefined,
148
+ type: formData.type,
149
+ color: formData.color,
150
+ attendees: formData.attendees
151
+ ? formData.attendees.split(',').map(a => a.trim()).filter(Boolean)
152
+ : undefined
153
+ }
154
+
155
+ await onSave(eventData)
156
+ onOpenChange(false)
157
+ } catch (error) {
158
+ console.error('Error saving event:', error)
159
+ } finally {
160
+ setIsSubmitting(false)
161
+ }
162
+ }
163
+
164
+ const handleDelete = () => {
165
+ if (event && onDelete) {
166
+ onDelete(event.id)
167
+ onOpenChange(false)
168
+ }
169
+ }
170
+
171
+ const handleTypeChange = (type: string) => {
172
+ const selectedType = EVENT_TYPES.find(t => t.value === type)
173
+ setFormData(prev => ({
174
+ ...prev,
175
+ type: type as typeof formData.type,
176
+ color: selectedType?.color || prev.color
177
+ }))
178
+ }
179
+
180
+ return (
181
+ <Dialog open={open} onOpenChange={onOpenChange}>
182
+ <DialogContent className="sm:max-w-[500px]">
183
+ <DialogHeader>
184
+ <DialogTitle className="flex items-center gap-2">
185
+ <Calendar className="w-5 h-5" />
186
+ {mode === 'create' ? 'Create Event' : 'Edit Event'}
187
+ </DialogTitle>
188
+ <DialogDescription>
189
+ {mode === 'create'
190
+ ? 'Add a new event to your calendar.'
191
+ : 'Edit the event details.'}
192
+ </DialogDescription>
193
+ </DialogHeader>
194
+
195
+ <form onSubmit={handleSubmit} className="space-y-4">
196
+ {/* Title */}
197
+ <div className="space-y-2">
198
+ <Label htmlFor="title">Title *</Label>
199
+ <Input
200
+ id="title"
201
+ value={formData.title}
202
+ onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
203
+ placeholder="Event title"
204
+ className={cn(errors.title && "border-red-500")}
205
+ />
206
+ {errors.title && <p className="text-sm text-red-500">{errors.title}</p>}
207
+ </div>
208
+
209
+ {/* Description */}
210
+ <div className="space-y-2">
211
+ <Label htmlFor="description">Description</Label>
212
+ <Textarea
213
+ id="description"
214
+ value={formData.description}
215
+ onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
216
+ placeholder="Event description (optional)"
217
+ rows={3}
218
+ />
219
+ </div>
220
+
221
+ {/* Date and Time */}
222
+ <div className="grid grid-cols-3 gap-4">
223
+ <div className="space-y-2">
224
+ <Label htmlFor="date">Date *</Label>
225
+ <Input
226
+ id="date"
227
+ type="date"
228
+ value={formData.date}
229
+ onChange={(e) => setFormData(prev => ({ ...prev, date: e.target.value }))}
230
+ className={cn(errors.date && "border-red-500")}
231
+ />
232
+ {errors.date && <p className="text-sm text-red-500">{errors.date}</p>}
233
+ </div>
234
+
235
+ <div className="space-y-2">
236
+ <Label htmlFor="startTime">Start Time</Label>
237
+ <Input
238
+ id="startTime"
239
+ type="time"
240
+ value={formData.startTime}
241
+ onChange={(e) => setFormData(prev => ({ ...prev, startTime: e.target.value }))}
242
+ placeholder="09:00"
243
+ />
244
+ </div>
245
+
246
+ <div className="space-y-2">
247
+ <Label htmlFor="endTime">End Time</Label>
248
+ <Input
249
+ id="endTime"
250
+ type="time"
251
+ value={formData.endTime}
252
+ onChange={(e) => setFormData(prev => ({ ...prev, endTime: e.target.value }))}
253
+ placeholder="10:00"
254
+ className={cn(errors.endTime && "border-red-500")}
255
+ />
256
+ {errors.endTime && <p className="text-sm text-red-500">{errors.endTime}</p>}
257
+ </div>
258
+ </div>
259
+
260
+ {/* Location */}
261
+ <div className="space-y-2">
262
+ <Label htmlFor="location" className="flex items-center gap-2">
263
+ <MapPin className="w-4 h-4" />
264
+ Location
265
+ </Label>
266
+ <Input
267
+ id="location"
268
+ value={formData.location}
269
+ onChange={(e) => setFormData(prev => ({ ...prev, location: e.target.value }))}
270
+ placeholder="Meeting room, address, or link"
271
+ />
272
+ </div>
273
+
274
+ {/* Type and Color */}
275
+ <div className="grid grid-cols-2 gap-4">
276
+ <div className="space-y-2">
277
+ <Label className="flex items-center gap-2">
278
+ <Type className="w-4 h-4" />
279
+ Type
280
+ </Label>
281
+ <Select value={formData.type} onValueChange={handleTypeChange}>
282
+ <SelectTrigger>
283
+ <SelectValue />
284
+ </SelectTrigger>
285
+ <SelectContent>
286
+ {EVENT_TYPES.map((type) => (
287
+ <SelectItem key={type.value} value={type.value}>
288
+ <div className="flex items-center gap-2">
289
+ <div
290
+ className="w-3 h-3 rounded-full"
291
+ style={{ backgroundColor: type.color }}
292
+ />
293
+ {type.label}
294
+ </div>
295
+ </SelectItem>
296
+ ))}
297
+ </SelectContent>
298
+ </Select>
299
+ </div>
300
+
301
+ <div className="space-y-2">
302
+ <Label className="flex items-center gap-2">
303
+ <Palette className="w-4 h-4" />
304
+ Color
305
+ </Label>
306
+ <div className="flex items-center gap-2">
307
+ <input
308
+ type="color"
309
+ value={formData.color}
310
+ onChange={(e) => setFormData(prev => ({ ...prev, color: e.target.value }))}
311
+ className="w-8 h-8 rounded border cursor-pointer"
312
+ />
313
+ <div className="flex gap-1">
314
+ {PRESET_COLORS.map((color) => (
315
+ <button
316
+ key={color}
317
+ type="button"
318
+ onClick={() => setFormData(prev => ({ ...prev, color }))}
319
+ className={cn(
320
+ "w-6 h-6 rounded border-2 hover:scale-110 transition-transform",
321
+ formData.color === color ? "border-foreground" : "border-transparent"
322
+ )}
323
+ style={{ backgroundColor: color }}
324
+ />
325
+ ))}
326
+ </div>
327
+ </div>
328
+ </div>
329
+ </div>
330
+
331
+ {/* Attendees */}
332
+ <div className="space-y-2">
333
+ <Label htmlFor="attendees">Attendees</Label>
334
+ <Input
335
+ id="attendees"
336
+ value={formData.attendees}
337
+ onChange={(e) => setFormData(prev => ({ ...prev, attendees: e.target.value }))}
338
+ placeholder="John Doe, Jane Smith (comma-separated)"
339
+ />
340
+ </div>
341
+
342
+ <DialogFooter className="gap-2">
343
+ {mode === 'edit' && onDelete && (
344
+ <Button
345
+ type="button"
346
+ variant="destructive"
347
+ onClick={handleDelete}
348
+ className="mr-auto"
349
+ >
350
+ Delete Event
351
+ </Button>
352
+ )}
353
+ <Button
354
+ type="button"
355
+ variant="outline"
356
+ onClick={() => onOpenChange(false)}
357
+ disabled={isSubmitting}
358
+ >
359
+ Cancel
360
+ </Button>
361
+ <Button type="submit" disabled={isSubmitting}>
362
+ {isSubmitting
363
+ ? (mode === 'create' ? 'Creating...' : 'Saving...')
364
+ : (mode === 'create' ? 'Create Event' : 'Save Changes')
365
+ }
366
+ </Button>
367
+ </DialogFooter>
368
+ </form>
369
+ </DialogContent>
370
+ </Dialog>
371
+ )
372
+ }