@moontra/moonui-pro 2.0.22 → 2.1.0

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 (99) hide show
  1. package/dist/index.mjs +215 -214
  2. package/package.json +4 -2
  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 +557 -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 +14 -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-guard.tsx +177 -0
  98. package/src/utils/license-validator.tsx +183 -0
  99. package/src/utils/package-guard.ts +60 -0
@@ -0,0 +1,436 @@
1
+ "use client"
2
+
3
+ import React from 'react'
4
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card'
5
+ import { Button } from '../ui/button'
6
+ import { Badge } from '../ui/badge'
7
+ import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar'
8
+ import {
9
+ Plus,
10
+ MoreHorizontal,
11
+ User,
12
+ Calendar,
13
+ MessageCircle,
14
+ Paperclip,
15
+ Edit,
16
+ Trash2,
17
+ GripVertical,
18
+ Lock,
19
+ Sparkles
20
+ } from 'lucide-react'
21
+ import { cn } from '../../lib/utils'
22
+ // Note: DocsProAccess should be handled by consuming application
23
+ import { useSubscription } from '../../hooks/use-subscription'
24
+
25
+ export interface KanbanCard {
26
+ id: string
27
+ title: string
28
+ description?: string
29
+ assignee?: {
30
+ name: string
31
+ avatar?: string
32
+ email?: string
33
+ }
34
+ dueDate?: Date
35
+ priority?: 'low' | 'medium' | 'high' | 'urgent'
36
+ tags?: string[]
37
+ attachments?: number
38
+ comments?: number
39
+ completed?: boolean
40
+ }
41
+
42
+ export interface KanbanColumn {
43
+ id: string
44
+ title: string
45
+ color?: string
46
+ cards: KanbanCard[]
47
+ limit?: number
48
+ }
49
+
50
+ export interface KanbanProps {
51
+ columns: KanbanColumn[]
52
+ onCardMove?: (cardId: string, fromColumn: string, toColumn: string, newIndex: number) => void
53
+ onCardClick?: (card: KanbanCard) => void
54
+ onCardEdit?: (card: KanbanCard) => void
55
+ onCardDelete?: (card: KanbanCard) => void
56
+ onAddCard?: (columnId: string) => void
57
+ onAddColumn?: () => void
58
+ className?: string
59
+ showAddColumn?: boolean
60
+ showCardDetails?: boolean
61
+ disabled?: boolean
62
+ }
63
+
64
+ const PRIORITY_COLORS = {
65
+ low: 'bg-green-100 text-green-800 border-green-200',
66
+ medium: 'bg-yellow-100 text-yellow-800 border-yellow-200',
67
+ high: 'bg-orange-100 text-orange-800 border-orange-200',
68
+ urgent: 'bg-red-100 text-red-800 border-red-200'
69
+ }
70
+
71
+ const PRIORITY_DOTS = {
72
+ low: 'bg-green-500',
73
+ medium: 'bg-yellow-500',
74
+ high: 'bg-orange-500',
75
+ urgent: 'bg-red-500'
76
+ }
77
+
78
+ export function Kanban({
79
+ columns,
80
+ onCardMove,
81
+ onCardClick,
82
+ onCardEdit,
83
+ onCardDelete,
84
+ onAddCard,
85
+ onAddColumn,
86
+ className,
87
+ showAddColumn = true,
88
+ showCardDetails = true,
89
+ disabled = false
90
+ }: KanbanProps) {
91
+ // Check if we're in docs mode or have pro access
92
+ const docsProAccess = { hasAccess: true } // Pro access assumed in package
93
+ const { hasProAccess, isLoading } = useSubscription()
94
+
95
+ // In docs mode, always show the component
96
+ const canShowComponent = docsProAccess.isDocsMode || hasProAccess
97
+
98
+ // If not in docs mode and no pro access, show upgrade prompt
99
+ if (!docsProAccess.isDocsMode && !isLoading && !hasProAccess) {
100
+ return (
101
+ <Card className={cn("w-full", className)}>
102
+ <CardContent className="py-12 text-center">
103
+ <div className="max-w-md mx-auto space-y-4">
104
+ <div className="rounded-full bg-purple-100 dark:bg-purple-900/30 p-3 w-fit mx-auto">
105
+ <Lock className="h-6 w-6 text-purple-600 dark:text-purple-400" />
106
+ </div>
107
+ <div>
108
+ <h3 className="font-semibold text-lg mb-2">Pro Feature</h3>
109
+ <p className="text-muted-foreground text-sm mb-4">
110
+ Kanban Board is available exclusively to MoonUI Pro subscribers.
111
+ </p>
112
+ <div className="flex gap-3 justify-center">
113
+ <a href="/pricing">
114
+ <Button size="sm">
115
+ <Sparkles className="mr-2 h-4 w-4" />
116
+ Upgrade to Pro
117
+ </Button>
118
+ </a>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </CardContent>
123
+ </Card>
124
+ )
125
+ }
126
+
127
+ const [draggedCard, setDraggedCard] = React.useState<string | null>(null)
128
+ const [draggedOverColumn, setDraggedOverColumn] = React.useState<string | null>(null)
129
+ const [draggedFromColumn, setDraggedFromColumn] = React.useState<string | null>(null)
130
+
131
+ const handleDragStart = (e: React.DragEvent, cardId: string) => {
132
+ if (disabled) return
133
+
134
+ // Find which column this card belongs to
135
+ const sourceColumn = columns.find(col =>
136
+ col.cards.some(card => card.id === cardId)
137
+ )
138
+
139
+ setDraggedCard(cardId)
140
+ setDraggedFromColumn(sourceColumn?.id || null)
141
+ e.dataTransfer.effectAllowed = 'move'
142
+ e.dataTransfer.setData('text/plain', cardId)
143
+
144
+ // Add visual feedback
145
+ e.currentTarget.classList.add('opacity-50')
146
+ }
147
+
148
+ const handleDragEnd = (e: React.DragEvent) => {
149
+ if (disabled) return
150
+
151
+ // Reset all states
152
+ setDraggedCard(null)
153
+ setDraggedOverColumn(null)
154
+ setDraggedFromColumn(null)
155
+
156
+ // Remove visual feedback
157
+ e.currentTarget.classList.remove('opacity-50')
158
+ }
159
+
160
+ const handleDragOver = (e: React.DragEvent, columnId: string) => {
161
+ if (disabled) return
162
+ e.preventDefault()
163
+ e.dataTransfer.dropEffect = 'move'
164
+ setDraggedOverColumn(columnId)
165
+ }
166
+
167
+ const handleDragEnter = (e: React.DragEvent, columnId: string) => {
168
+ if (disabled) return
169
+ e.preventDefault()
170
+ setDraggedOverColumn(columnId)
171
+ }
172
+
173
+ const handleDragLeave = (e: React.DragEvent, columnId: string) => {
174
+ if (disabled) return
175
+ e.preventDefault()
176
+
177
+ // Only clear if we're leaving the column entirely
178
+ const rect = e.currentTarget.getBoundingClientRect()
179
+ const isLeavingColumn = (
180
+ e.clientX < rect.left ||
181
+ e.clientX > rect.right ||
182
+ e.clientY < rect.top ||
183
+ e.clientY > rect.bottom
184
+ )
185
+
186
+ if (isLeavingColumn) {
187
+ setDraggedOverColumn(null)
188
+ }
189
+ }
190
+
191
+ const handleDrop = (e: React.DragEvent, columnId: string) => {
192
+ if (disabled) return
193
+ e.preventDefault()
194
+
195
+ const cardId = e.dataTransfer.getData('text/plain') || draggedCard
196
+
197
+ if (cardId && onCardMove && draggedFromColumn && draggedFromColumn !== columnId) {
198
+ const targetColumn = columns.find(col => col.id === columnId)
199
+ const newIndex = targetColumn?.cards.length || 0
200
+ onCardMove(cardId, draggedFromColumn, columnId, newIndex)
201
+ }
202
+
203
+ // Reset all states
204
+ setDraggedCard(null)
205
+ setDraggedOverColumn(null)
206
+ setDraggedFromColumn(null)
207
+ }
208
+
209
+ const handleCardClick = (card: KanbanCard) => {
210
+ if (disabled) return
211
+ onCardClick?.(card)
212
+ }
213
+
214
+ const handleCardEdit = (card: KanbanCard, e: React.MouseEvent) => {
215
+ if (disabled) return
216
+ e.stopPropagation()
217
+ onCardEdit?.(card)
218
+ }
219
+
220
+ const handleCardDelete = (card: KanbanCard, e: React.MouseEvent) => {
221
+ if (disabled) return
222
+ e.stopPropagation()
223
+ onCardDelete?.(card)
224
+ }
225
+
226
+ const formatDate = (date: Date) => {
227
+ return date.toLocaleDateString('en-US', {
228
+ month: 'short',
229
+ day: 'numeric'
230
+ })
231
+ }
232
+
233
+ const isOverdue = (dueDate: Date) => {
234
+ return dueDate < new Date()
235
+ }
236
+
237
+ const getInitials = (name: string) => {
238
+ return name.split(' ').map(n => n[0]).join('').toUpperCase()
239
+ }
240
+
241
+ return (
242
+ <div className={cn("w-full", className)}>
243
+ <div className="flex gap-6 overflow-x-auto pb-4">
244
+ {columns.map((column) => {
245
+ const isOverLimit = column.limit && column.cards.length > column.limit
246
+ const isDraggedOver = draggedOverColumn === column.id
247
+
248
+ return (
249
+ <div
250
+ key={column.id}
251
+ className={cn(
252
+ "flex-shrink-0 w-80 transition-colors duration-200",
253
+ isDraggedOver && "bg-primary/5 rounded-lg border-2 border-primary/20"
254
+ )}
255
+ onDragOver={(e) => handleDragOver(e, column.id)}
256
+ onDragEnter={(e) => handleDragEnter(e, column.id)}
257
+ onDragLeave={(e) => handleDragLeave(e, column.id)}
258
+ onDrop={(e) => handleDrop(e, column.id)}
259
+ >
260
+ <Card className="h-full">
261
+ <CardHeader className="pb-3">
262
+ <div className="flex items-center justify-between">
263
+ <div className="flex items-center gap-2">
264
+ {column.color && (
265
+ <div
266
+ className="w-3 h-3 rounded-full"
267
+ style={{ backgroundColor: column.color }}
268
+ />
269
+ )}
270
+ <CardTitle className="text-sm font-medium">
271
+ {column.title}
272
+ </CardTitle>
273
+ <Badge variant="secondary" className="text-xs">
274
+ {column.cards.length}
275
+ </Badge>
276
+ </div>
277
+ <Button variant="ghost" size="sm">
278
+ <MoreHorizontal className="h-4 w-4" />
279
+ </Button>
280
+ </div>
281
+ {column.limit && (
282
+ <CardDescription className={cn(
283
+ "text-xs",
284
+ isOverLimit && "text-destructive"
285
+ )}>
286
+ {isOverLimit ? 'Over limit' : `${column.cards.length}/${column.limit} cards`}
287
+ </CardDescription>
288
+ )}
289
+ </CardHeader>
290
+
291
+ <CardContent className="space-y-3">
292
+ {/* Cards */}
293
+ {column.cards.map((card) => (
294
+ <div
295
+ key={card.id}
296
+ draggable={!disabled}
297
+ onDragStart={(e) => handleDragStart(e, card.id)}
298
+ onDragEnd={handleDragEnd}
299
+ onClick={() => handleCardClick(card)}
300
+ className={cn(
301
+ "p-3 bg-background border rounded-lg cursor-pointer hover:shadow-md transition-all duration-200",
302
+ "group relative select-none",
303
+ draggedCard === card.id && "opacity-50 scale-95",
304
+ disabled && "cursor-not-allowed"
305
+ )}
306
+ >
307
+ <div className="flex items-start justify-between gap-2">
308
+ <div className="flex-1">
309
+ <h4 className="font-medium text-sm mb-1">{card.title}</h4>
310
+ {card.description && (
311
+ <p className="text-xs text-muted-foreground mb-2 line-clamp-2">
312
+ {card.description}
313
+ </p>
314
+ )}
315
+ </div>
316
+ <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
317
+ <Button
318
+ variant="ghost"
319
+ size="sm"
320
+ className="h-6 w-6 p-0"
321
+ onClick={(e) => handleCardEdit(card, e)}
322
+ >
323
+ <Edit className="h-3 w-3" />
324
+ </Button>
325
+ <Button
326
+ variant="ghost"
327
+ size="sm"
328
+ className="h-6 w-6 p-0"
329
+ onClick={(e) => handleCardDelete(card, e)}
330
+ >
331
+ <Trash2 className="h-3 w-3" />
332
+ </Button>
333
+ <div className="cursor-move">
334
+ <GripVertical className="h-3 w-3 text-muted-foreground" />
335
+ </div>
336
+ </div>
337
+ </div>
338
+
339
+ {/* Tags */}
340
+ {card.tags && card.tags.length > 0 && (
341
+ <div className="flex flex-wrap gap-1 mb-2">
342
+ {card.tags.map((tag, index) => (
343
+ <Badge key={index} variant="outline" className="text-xs px-1 py-0">
344
+ {tag}
345
+ </Badge>
346
+ ))}
347
+ </div>
348
+ )}
349
+
350
+ {/* Card Details */}
351
+ {showCardDetails && (
352
+ <div className="flex items-center justify-between text-xs text-muted-foreground">
353
+ <div className="flex items-center gap-2">
354
+ {card.priority && (
355
+ <div className="flex items-center gap-1">
356
+ <div className={cn("w-2 h-2 rounded-full", PRIORITY_DOTS[card.priority])} />
357
+ <span className="capitalize">{card.priority}</span>
358
+ </div>
359
+ )}
360
+ {card.dueDate && (
361
+ <div className={cn(
362
+ "flex items-center gap-1",
363
+ isOverdue(card.dueDate) && "text-destructive"
364
+ )}>
365
+ <Calendar className="h-3 w-3" />
366
+ <span>{formatDate(card.dueDate)}</span>
367
+ </div>
368
+ )}
369
+ </div>
370
+
371
+ <div className="flex items-center gap-2">
372
+ {card.comments && card.comments > 0 && (
373
+ <div className="flex items-center gap-1">
374
+ <MessageCircle className="h-3 w-3" />
375
+ <span>{card.comments}</span>
376
+ </div>
377
+ )}
378
+ {card.attachments && card.attachments > 0 && (
379
+ <div className="flex items-center gap-1">
380
+ <Paperclip className="h-3 w-3" />
381
+ <span>{card.attachments}</span>
382
+ </div>
383
+ )}
384
+ {card.assignee && (
385
+ <Avatar className="h-5 w-5">
386
+ <AvatarImage src={card.assignee.avatar} />
387
+ <AvatarFallback className="text-xs">
388
+ {getInitials(card.assignee.name)}
389
+ </AvatarFallback>
390
+ </Avatar>
391
+ )}
392
+ </div>
393
+ </div>
394
+ )}
395
+ </div>
396
+ ))}
397
+
398
+ {/* Add Card Button */}
399
+ {onAddCard && (
400
+ <Button
401
+ variant="ghost"
402
+ size="sm"
403
+ onClick={() => onAddCard(column.id)}
404
+ className="w-full justify-start text-muted-foreground hover:text-foreground"
405
+ disabled={disabled}
406
+ >
407
+ <Plus className="h-4 w-4 mr-2" />
408
+ Add card
409
+ </Button>
410
+ )}
411
+ </CardContent>
412
+ </Card>
413
+ </div>
414
+ )
415
+ })}
416
+
417
+ {/* Add Column Button */}
418
+ {showAddColumn && onAddColumn && (
419
+ <div className="flex-shrink-0 w-80">
420
+ <Button
421
+ variant="outline"
422
+ onClick={onAddColumn}
423
+ className="w-full h-full min-h-[200px] border-dashed justify-center items-center"
424
+ disabled={disabled}
425
+ >
426
+ <Plus className="h-6 w-6 mr-2" />
427
+ Add column
428
+ </Button>
429
+ </div>
430
+ )}
431
+ </div>
432
+ </div>
433
+ )
434
+ }
435
+
436
+ export default Kanban