@moontra/moonui-pro 2.0.22 → 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 (96) hide show
  1. package/package.json +2 -1
  2. package/src/__tests__/use-intersection-observer.test.tsx +216 -0
  3. package/src/__tests__/use-local-storage.test.tsx +174 -0
  4. package/src/__tests__/use-pro-access.test.tsx +183 -0
  5. package/src/components/advanced-chart/advanced-chart.test.tsx +281 -0
  6. package/src/components/advanced-chart/index.tsx +412 -0
  7. package/src/components/advanced-forms/index.tsx +431 -0
  8. package/src/components/animated-button/index.tsx +202 -0
  9. package/src/components/calendar/event-dialog.tsx +372 -0
  10. package/src/components/calendar/index.tsx +531 -0
  11. package/src/components/color-picker/index.tsx +434 -0
  12. package/src/components/dashboard/index.tsx +334 -0
  13. package/src/components/data-table/data-table.test.tsx +187 -0
  14. package/src/components/data-table/index.tsx +368 -0
  15. package/src/components/draggable-list/index.tsx +100 -0
  16. package/src/components/enhanced/button.tsx +360 -0
  17. package/src/components/enhanced/card.tsx +272 -0
  18. package/src/components/enhanced/dialog.tsx +248 -0
  19. package/src/components/enhanced/index.ts +3 -0
  20. package/src/components/error-boundary/index.tsx +111 -0
  21. package/src/components/file-upload/file-upload.test.tsx +242 -0
  22. package/src/components/file-upload/index.tsx +362 -0
  23. package/src/components/floating-action-button/index.tsx +209 -0
  24. package/src/components/github-stars/index.tsx +414 -0
  25. package/src/components/health-check/index.tsx +441 -0
  26. package/src/components/hover-card-3d/index.tsx +170 -0
  27. package/src/components/index.ts +76 -0
  28. package/src/components/kanban/index.tsx +436 -0
  29. package/src/components/lazy-component/index.tsx +342 -0
  30. package/src/components/magnetic-button/index.tsx +170 -0
  31. package/src/components/memory-efficient-data/index.tsx +352 -0
  32. package/src/components/optimized-image/index.tsx +427 -0
  33. package/src/components/performance-debugger/index.tsx +591 -0
  34. package/src/components/performance-monitor/index.tsx +775 -0
  35. package/src/components/pinch-zoom/index.tsx +172 -0
  36. package/src/components/rich-text-editor/index-old-backup.tsx +443 -0
  37. package/src/components/rich-text-editor/index.tsx +1537 -0
  38. package/src/components/rich-text-editor/slash-commands-extension.ts +220 -0
  39. package/src/components/rich-text-editor/slash-commands.css +35 -0
  40. package/src/components/rich-text-editor/table-styles.css +65 -0
  41. package/src/components/spotlight-card/index.tsx +194 -0
  42. package/src/components/swipeable-card/index.tsx +100 -0
  43. package/src/components/timeline/index.tsx +333 -0
  44. package/src/components/ui/animated-button.tsx +185 -0
  45. package/src/components/ui/avatar.tsx +135 -0
  46. package/src/components/ui/badge.tsx +225 -0
  47. package/src/components/ui/button.tsx +221 -0
  48. package/src/components/ui/card.tsx +141 -0
  49. package/src/components/ui/checkbox.tsx +256 -0
  50. package/src/components/ui/color-picker.tsx +95 -0
  51. package/src/components/ui/dialog.tsx +332 -0
  52. package/src/components/ui/dropdown-menu.tsx +200 -0
  53. package/src/components/ui/hover-card-3d.tsx +103 -0
  54. package/src/components/ui/index.ts +33 -0
  55. package/src/components/ui/input.tsx +219 -0
  56. package/src/components/ui/label.tsx +26 -0
  57. package/src/components/ui/magnetic-button.tsx +129 -0
  58. package/src/components/ui/popover.tsx +183 -0
  59. package/src/components/ui/select.tsx +273 -0
  60. package/src/components/ui/separator.tsx +140 -0
  61. package/src/components/ui/slider.tsx +351 -0
  62. package/src/components/ui/spotlight-card.tsx +119 -0
  63. package/src/components/ui/switch.tsx +83 -0
  64. package/src/components/ui/tabs.tsx +195 -0
  65. package/src/components/ui/textarea.tsx +25 -0
  66. package/src/components/ui/toast.tsx +313 -0
  67. package/src/components/ui/tooltip.tsx +152 -0
  68. package/src/components/virtual-list/index.tsx +369 -0
  69. package/src/hooks/use-chart.ts +205 -0
  70. package/src/hooks/use-data-table.ts +182 -0
  71. package/src/hooks/use-docs-pro-access.ts +13 -0
  72. package/src/hooks/use-license-check.ts +65 -0
  73. package/src/hooks/use-subscription.ts +19 -0
  74. package/src/index.ts +11 -0
  75. package/src/lib/micro-interactions.ts +255 -0
  76. package/src/lib/utils.ts +6 -0
  77. package/src/patterns/login-form/index.tsx +276 -0
  78. package/src/patterns/login-form/types.ts +67 -0
  79. package/src/setupTests.ts +41 -0
  80. package/src/styles/design-system.css +365 -0
  81. package/src/styles/index.css +4 -0
  82. package/src/styles/tailwind.css +6 -0
  83. package/src/styles/tokens.css +453 -0
  84. package/src/types/moonui.d.ts +22 -0
  85. package/src/use-intersection-observer.tsx +154 -0
  86. package/src/use-local-storage.tsx +71 -0
  87. package/src/use-paddle.ts +138 -0
  88. package/src/use-performance-optimizer.ts +379 -0
  89. package/src/use-pro-access.ts +141 -0
  90. package/src/use-scroll-animation.ts +221 -0
  91. package/src/use-subscription.ts +37 -0
  92. package/src/use-toast.ts +32 -0
  93. package/src/utils/chart-helpers.ts +257 -0
  94. package/src/utils/cn.ts +69 -0
  95. package/src/utils/data-processing.ts +151 -0
  96. package/src/utils/license-validator.tsx +183 -0
@@ -0,0 +1,368 @@
1
+ "use client"
2
+
3
+ import React from 'react'
4
+ import {
5
+ useReactTable,
6
+ getCoreRowModel,
7
+ getFilteredRowModel,
8
+ getPaginationRowModel,
9
+ getSortedRowModel,
10
+ ColumnDef,
11
+ flexRender,
12
+ SortingState,
13
+ ColumnFiltersState,
14
+ VisibilityState,
15
+ } from '@tanstack/react-table'
16
+ import { Button } from './button'
17
+ import { Input } from './input'
18
+ import { Card, CardContent } from './card'
19
+ import {
20
+ ChevronLeft,
21
+ ChevronRight,
22
+ ChevronsLeft,
23
+ ChevronsRight,
24
+ ArrowUpDown,
25
+ ArrowUp,
26
+ ArrowDown,
27
+ Search,
28
+ Filter,
29
+ Download,
30
+ Settings,
31
+ Lock,
32
+ Sparkles
33
+ } from 'lucide-react'
34
+ import { cn } from '../../lib/utils'
35
+ import { useDocsProAccess } from '@/components/docs/docs-pro-provider'
36
+ import { useSubscription } from '../../hooks/use-subscription'
37
+
38
+ export interface DataTableProps<TData, TValue> {
39
+ columns: ColumnDef<TData, TValue>[]
40
+ data: TData[]
41
+ searchable?: boolean
42
+ filterable?: boolean
43
+ exportable?: boolean
44
+ selectable?: boolean
45
+ pagination?: boolean
46
+ pageSize?: number
47
+ className?: string
48
+ onRowSelect?: (rows: TData[]) => void
49
+ onExport?: (data: TData[]) => void
50
+ features?: {
51
+ sorting?: boolean
52
+ filtering?: boolean
53
+ pagination?: boolean
54
+ search?: boolean
55
+ columnVisibility?: boolean
56
+ rowSelection?: boolean
57
+ export?: boolean | string[]
58
+ density?: boolean
59
+ fullscreen?: boolean
60
+ print?: boolean
61
+ }
62
+ theme?: {
63
+ headerBg?: string
64
+ headerText?: string
65
+ borderColor?: string
66
+ rowHoverBg?: string
67
+ selectedRowBg?: string
68
+ }
69
+ texts?: {
70
+ searchPlaceholder?: string
71
+ noResults?: string
72
+ rowsPerPage?: string
73
+ selectedRows?: string
74
+ exportButton?: string
75
+ columnsButton?: string
76
+ densityButton?: string
77
+ filterButton?: string
78
+ }
79
+ }
80
+
81
+ export function DataTable<TData, TValue>({
82
+ columns,
83
+ data,
84
+ searchable = true,
85
+ filterable = true,
86
+ exportable = true,
87
+ selectable = false,
88
+ pagination = true,
89
+ pageSize = 10,
90
+ className,
91
+ onRowSelect,
92
+ onExport,
93
+ features = {},
94
+ theme = {},
95
+ texts = {},
96
+ }: DataTableProps<TData, TValue>) {
97
+ // Check if we're in docs mode or have pro access
98
+ const docsProAccess = useDocsProAccess()
99
+ const { hasProAccess, isLoading } = useSubscription()
100
+
101
+ // In docs mode, always show the component
102
+ const canShowComponent = docsProAccess.isDocsMode || hasProAccess
103
+
104
+ // If not in docs mode and no pro access, show upgrade prompt
105
+ if (!docsProAccess.isDocsMode && !isLoading && !hasProAccess) {
106
+ return (
107
+ <Card className={cn("w-full", className)}>
108
+ <CardContent className="py-12 text-center">
109
+ <div className="max-w-md mx-auto space-y-4">
110
+ <div className="rounded-full bg-purple-100 dark:bg-purple-900/30 p-3 w-fit mx-auto">
111
+ <Lock className="h-6 w-6 text-purple-600 dark:text-purple-400" />
112
+ </div>
113
+ <div>
114
+ <h3 className="font-semibold text-lg mb-2">Pro Feature</h3>
115
+ <p className="text-muted-foreground text-sm mb-4">
116
+ Data Table is available exclusively to MoonUI Pro subscribers.
117
+ </p>
118
+ <div className="flex gap-3 justify-center">
119
+ <a href="/pricing">
120
+ <Button size="sm">
121
+ <Sparkles className="mr-2 h-4 w-4" />
122
+ Upgrade to Pro
123
+ </Button>
124
+ </a>
125
+ </div>
126
+ </div>
127
+ </div>
128
+ </CardContent>
129
+ </Card>
130
+ )
131
+ }
132
+ const [sorting, setSorting] = React.useState<SortingState>([])
133
+ const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
134
+ const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
135
+ const [rowSelection, setRowSelection] = React.useState({})
136
+ const [globalFilter, setGlobalFilter] = React.useState('')
137
+
138
+ const table = useReactTable({
139
+ data,
140
+ columns,
141
+ onSortingChange: setSorting,
142
+ onColumnFiltersChange: setColumnFilters,
143
+ getCoreRowModel: getCoreRowModel(),
144
+ getPaginationRowModel: getPaginationRowModel(),
145
+ getSortedRowModel: getSortedRowModel(),
146
+ getFilteredRowModel: getFilteredRowModel(),
147
+ onColumnVisibilityChange: setColumnVisibility,
148
+ onRowSelectionChange: setRowSelection,
149
+ onGlobalFilterChange: setGlobalFilter,
150
+ globalFilterFn: 'includesString',
151
+ state: {
152
+ sorting,
153
+ columnFilters,
154
+ columnVisibility,
155
+ rowSelection,
156
+ globalFilter,
157
+ },
158
+ initialState: {
159
+ pagination: {
160
+ pageSize,
161
+ },
162
+ },
163
+ })
164
+
165
+ React.useEffect(() => {
166
+ if (onRowSelect && selectable) {
167
+ const selectedRows = table.getFilteredSelectedRowModel().rows.map(row => row.original)
168
+ onRowSelect(selectedRows)
169
+ }
170
+ }, [rowSelection, onRowSelect, selectable, table])
171
+
172
+ // Merge features with defaults
173
+ const enabledFeatures = {
174
+ sorting: features.sorting !== false,
175
+ filtering: features.filtering !== false || filterable,
176
+ pagination: features.pagination !== false || pagination,
177
+ search: features.search !== false || searchable,
178
+ columnVisibility: features.columnVisibility !== false,
179
+ rowSelection: features.rowSelection !== false || selectable,
180
+ export: features.export !== false || exportable,
181
+ }
182
+
183
+ const handleExport = () => {
184
+ if (onExport) {
185
+ const selectedRows = table.getFilteredSelectedRowModel().rows
186
+ const dataToExport = selectedRows.length > 0
187
+ ? selectedRows.map(row => row.original)
188
+ : table.getFilteredRowModel().rows.map(row => row.original)
189
+ onExport(dataToExport)
190
+ }
191
+ }
192
+
193
+ return (
194
+ <div className={cn("space-y-4", className)}>
195
+ {/* Toolbar */}
196
+ <div className="flex items-center justify-between">
197
+ <div className="flex items-center space-x-2">
198
+ {searchable && (
199
+ <div className="relative">
200
+ <Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
201
+ <Input
202
+ placeholder="Search all columns..."
203
+ value={globalFilter}
204
+ onChange={(e) => setGlobalFilter(e.target.value)}
205
+ className="pl-8 w-64"
206
+ />
207
+ </div>
208
+ )}
209
+
210
+ {filterable && (
211
+ <Button variant="outline" size="sm">
212
+ <Filter className="mr-2 h-4 w-4" />
213
+ Filters
214
+ </Button>
215
+ )}
216
+ </div>
217
+
218
+ <div className="flex items-center space-x-2">
219
+ {exportable && (
220
+ <Button variant="outline" size="sm" onClick={handleExport}>
221
+ <Download className="mr-2 h-4 w-4" />
222
+ Export
223
+ </Button>
224
+ )}
225
+
226
+ <Button variant="outline" size="sm">
227
+ <Settings className="mr-2 h-4 w-4" />
228
+ Columns
229
+ </Button>
230
+ </div>
231
+ </div>
232
+
233
+ {/* Table */}
234
+ <div className="rounded-md border">
235
+ <table className="w-full">
236
+ <thead>
237
+ {table.getHeaderGroups().map((headerGroup) => (
238
+ <tr key={headerGroup.id} className="border-b">
239
+ {headerGroup.headers.map((header) => (
240
+ <th
241
+ key={header.id}
242
+ className="h-12 px-4 text-left align-middle font-medium text-muted-foreground"
243
+ >
244
+ {header.isPlaceholder ? null : (
245
+ <div
246
+ className={cn(
247
+ "flex items-center space-x-2",
248
+ header.column.getCanSort() && "cursor-pointer select-none"
249
+ )}
250
+ onClick={header.column.getToggleSortingHandler()}
251
+ >
252
+ {flexRender(header.column.columnDef.header, header.getContext())}
253
+ {header.column.getCanSort() && (
254
+ <div className="ml-2">
255
+ {header.column.getIsSorted() === 'asc' ? (
256
+ <ArrowUp className="h-4 w-4" />
257
+ ) : header.column.getIsSorted() === 'desc' ? (
258
+ <ArrowDown className="h-4 w-4" />
259
+ ) : (
260
+ <ArrowUpDown className="h-4 w-4" />
261
+ )}
262
+ </div>
263
+ )}
264
+ </div>
265
+ )}
266
+ </th>
267
+ ))}
268
+ </tr>
269
+ ))}
270
+ </thead>
271
+ <tbody>
272
+ {table.getRowModel().rows?.length ? (
273
+ table.getRowModel().rows.map((row) => (
274
+ <tr
275
+ key={row.id}
276
+ className={cn(
277
+ "border-b transition-colors hover:bg-muted/50",
278
+ row.getIsSelected() && "bg-muted"
279
+ )}
280
+ >
281
+ {row.getVisibleCells().map((cell) => (
282
+ <td key={cell.id} className="p-4 align-middle">
283
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
284
+ </td>
285
+ ))}
286
+ </tr>
287
+ ))
288
+ ) : (
289
+ <tr>
290
+ <td colSpan={columns.length} className="h-24 text-center">
291
+ No results found.
292
+ </td>
293
+ </tr>
294
+ )}
295
+ </tbody>
296
+ </table>
297
+ </div>
298
+
299
+ {/* Pagination */}
300
+ {pagination && (
301
+ <div className="flex items-center justify-between px-2">
302
+ <div className="flex-1 text-sm text-muted-foreground">
303
+ {selectable && table.getFilteredSelectedRowModel().rows.length > 0 && (
304
+ <span>
305
+ {table.getFilteredSelectedRowModel().rows.length} of{" "}
306
+ {table.getFilteredRowModel().rows.length} row(s) selected.
307
+ </span>
308
+ )}
309
+ </div>
310
+ <div className="flex items-center space-x-6 lg:space-x-8">
311
+ <div className="flex items-center space-x-2">
312
+ <p className="text-sm font-medium">Rows per page</p>
313
+ <select
314
+ value={table.getState().pagination.pageSize}
315
+ onChange={(e) => table.setPageSize(Number(e.target.value))}
316
+ className="h-8 w-[70px] rounded border border-input bg-background px-3 py-1 text-sm"
317
+ >
318
+ {[10, 20, 30, 40, 50].map((pageSize) => (
319
+ <option key={pageSize} value={pageSize}>
320
+ {pageSize}
321
+ </option>
322
+ ))}
323
+ </select>
324
+ </div>
325
+ <div className="flex w-[100px] items-center justify-center text-sm font-medium">
326
+ Page {table.getState().pagination.pageIndex + 1} of{" "}
327
+ {table.getPageCount()}
328
+ </div>
329
+ <div className="flex items-center space-x-2">
330
+ <Button
331
+ variant="outline"
332
+ className="hidden h-8 w-8 p-0 lg:flex"
333
+ onClick={() => table.setPageIndex(0)}
334
+ disabled={!table.getCanPreviousPage()}
335
+ >
336
+ <ChevronsLeft className="h-4 w-4" />
337
+ </Button>
338
+ <Button
339
+ variant="outline"
340
+ className="h-8 w-8 p-0"
341
+ onClick={() => table.previousPage()}
342
+ disabled={!table.getCanPreviousPage()}
343
+ >
344
+ <ChevronLeft className="h-4 w-4" />
345
+ </Button>
346
+ <Button
347
+ variant="outline"
348
+ className="h-8 w-8 p-0"
349
+ onClick={() => table.nextPage()}
350
+ disabled={!table.getCanNextPage()}
351
+ >
352
+ <ChevronRight className="h-4 w-4" />
353
+ </Button>
354
+ <Button
355
+ variant="outline"
356
+ className="hidden h-8 w-8 p-0 lg:flex"
357
+ onClick={() => table.setPageIndex(table.getPageCount() - 1)}
358
+ disabled={!table.getCanNextPage()}
359
+ >
360
+ <ChevronsRight className="h-4 w-4" />
361
+ </Button>
362
+ </div>
363
+ </div>
364
+ </div>
365
+ )}
366
+ </div>
367
+ )
368
+ }
@@ -0,0 +1,100 @@
1
+ "use client"
2
+
3
+ import React, { ReactNode } from "react"
4
+ import { DragDropContext, Droppable, Draggable, DropResult } from "@hello-pangea/dnd"
5
+ import { cn } from "@moontra/moonui"
6
+
7
+ export interface DraggableListProps<T> {
8
+ items: T[]
9
+ onReorder: (items: T[]) => void
10
+ renderItem: (item: T, index: number) => ReactNode
11
+ keyExtractor: (item: T) => string
12
+ direction?: "vertical" | "horizontal"
13
+ className?: string
14
+ droppableId?: string
15
+ disabled?: boolean
16
+ }
17
+
18
+ export function DraggableList<T>({
19
+ items,
20
+ onReorder,
21
+ renderItem,
22
+ keyExtractor,
23
+ direction = "vertical",
24
+ className,
25
+ droppableId = "draggable-list",
26
+ disabled = false,
27
+ }: DraggableListProps<T>) {
28
+ const handleDragEnd = (result: DropResult) => {
29
+ if (!result.destination || disabled) {
30
+ return
31
+ }
32
+
33
+ const sourceIndex = result.source.index
34
+ const destinationIndex = result.destination.index
35
+
36
+ if (sourceIndex === destinationIndex) {
37
+ return
38
+ }
39
+
40
+ const newItems = Array.from(items)
41
+ const [reorderedItem] = newItems.splice(sourceIndex, 1)
42
+ newItems.splice(destinationIndex, 0, reorderedItem)
43
+
44
+ onReorder(newItems)
45
+ }
46
+
47
+ return (
48
+ <DragDropContext onDragEnd={handleDragEnd}>
49
+ <Droppable
50
+ droppableId={droppableId}
51
+ direction={direction}
52
+ isDropDisabled={disabled}
53
+ >
54
+ {(provided, snapshot) => (
55
+ <div
56
+ {...provided.droppableProps}
57
+ ref={provided.innerRef}
58
+ className={cn(
59
+ "space-y-2",
60
+ direction === "horizontal" && "flex space-y-0 space-x-2",
61
+ snapshot.isDraggingOver && "bg-muted/50 rounded-lg",
62
+ className
63
+ )}
64
+ >
65
+ {items.map((item, index) => (
66
+ <Draggable
67
+ key={keyExtractor(item)}
68
+ draggableId={keyExtractor(item)}
69
+ index={index}
70
+ isDragDisabled={disabled}
71
+ >
72
+ {(provided, snapshot) => (
73
+ <div
74
+ ref={provided.innerRef}
75
+ {...provided.draggableProps}
76
+ {...provided.dragHandleProps}
77
+ className={cn(
78
+ "transition-all duration-200",
79
+ snapshot.isDragging && "rotate-2 scale-105 shadow-lg z-50",
80
+ disabled && "cursor-not-allowed opacity-50"
81
+ )}
82
+ style={{
83
+ ...provided.draggableProps.style,
84
+ transform: snapshot.isDragging
85
+ ? provided.draggableProps.style?.transform
86
+ : "none",
87
+ }}
88
+ >
89
+ {renderItem(item, index)}
90
+ </div>
91
+ )}
92
+ </Draggable>
93
+ ))}
94
+ {provided.placeholder}
95
+ </div>
96
+ )}
97
+ </Droppable>
98
+ </DragDropContext>
99
+ )
100
+ }