@moontra/moonui-pro 2.2.19 → 2.3.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moontra/moonui-pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -313,13 +313,13 @@ export function Calendar({
|
|
|
313
313
|
</div>
|
|
314
314
|
</div>
|
|
315
315
|
</CardHeader>
|
|
316
|
-
<CardContent
|
|
316
|
+
<CardContent>
|
|
317
317
|
<div className="space-y-4">
|
|
318
318
|
{/* Calendar Grid */}
|
|
319
|
-
<div className="grid grid-cols-7 gap-1 w-full"
|
|
319
|
+
<div className="grid grid-cols-7 gap-1 w-full">
|
|
320
320
|
{/* Day Headers */}
|
|
321
321
|
{visibleDaysOfWeek.map((day) => (
|
|
322
|
-
<div key={day} className="p-1 text-center text-xs font-medium text-muted-foreground
|
|
322
|
+
<div key={day} className="p-1 text-center text-xs font-medium text-muted-foreground">
|
|
323
323
|
{day}
|
|
324
324
|
</div>
|
|
325
325
|
))}
|
|
@@ -336,7 +336,7 @@ export function Calendar({
|
|
|
336
336
|
<div
|
|
337
337
|
key={index}
|
|
338
338
|
className={cn(
|
|
339
|
-
"min-h-[80px]
|
|
339
|
+
"min-h-[80px] p-1 border border-border/50 cursor-pointer hover:bg-muted/50 transition-colors text-xs flex flex-col",
|
|
340
340
|
!isCurrentMonthDate && "text-muted-foreground bg-muted/20",
|
|
341
341
|
isTodayDate && highlightToday && "bg-primary/10 border-primary/50",
|
|
342
342
|
isSelectedDate && "bg-primary/20 border-primary",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* MoonUI DataTable Component Styles */
|
|
2
|
+
.moonui-data-table-container {
|
|
3
|
+
/* Container specific styles */
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.moonui-data-table-wrapper {
|
|
7
|
+
/* Wrapper specific styles */
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.moonui-data-table {
|
|
11
|
+
/* Table specific styles */
|
|
12
|
+
border-collapse: collapse;
|
|
13
|
+
table-layout: fixed;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.moonui-data-table-header {
|
|
17
|
+
/* Header specific styles */
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.moonui-data-table-body {
|
|
21
|
+
/* Body specific styles */
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.moonui-data-table-row {
|
|
25
|
+
/* Row specific styles */
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.moonui-data-table-th {
|
|
29
|
+
/* Table header cell specific styles */
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.moonui-data-table-td {
|
|
33
|
+
/* Table data cell specific styles */
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.moonui-data-table-toolbar {
|
|
37
|
+
/* Toolbar specific styles */
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Prevent style leakage */
|
|
41
|
+
.moonui-data-table-container * {
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Ensure DataTable styles don't affect global styles */
|
|
46
|
+
.moonui-data-table-container table {
|
|
47
|
+
margin: 0;
|
|
48
|
+
padding: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.moonui-data-table-container th,
|
|
52
|
+
.moonui-data-table-container td {
|
|
53
|
+
margin: 0;
|
|
54
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import React from 'react'
|
|
4
|
+
import './data-table.css'
|
|
4
5
|
import {
|
|
5
6
|
useReactTable,
|
|
6
7
|
getCoreRowModel,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
ChevronRight,
|
|
22
23
|
ChevronsLeft,
|
|
23
24
|
ChevronsRight,
|
|
25
|
+
ChevronDown,
|
|
24
26
|
ArrowUpDown,
|
|
25
27
|
ArrowUp,
|
|
26
28
|
ArrowDown,
|
|
@@ -29,10 +31,12 @@ import {
|
|
|
29
31
|
Download,
|
|
30
32
|
Settings,
|
|
31
33
|
Lock,
|
|
32
|
-
Sparkles
|
|
34
|
+
Sparkles,
|
|
35
|
+
Loader2
|
|
33
36
|
} from 'lucide-react'
|
|
34
37
|
import { cn } from '../../lib/utils'
|
|
35
38
|
import { useSubscription } from '../../hooks/use-subscription'
|
|
39
|
+
import { motion, AnimatePresence } from 'framer-motion'
|
|
36
40
|
|
|
37
41
|
interface DataTableProps<TData, TValue> {
|
|
38
42
|
columns: ColumnDef<TData, TValue>[]
|
|
@@ -46,6 +50,10 @@ interface DataTableProps<TData, TValue> {
|
|
|
46
50
|
className?: string
|
|
47
51
|
onRowSelect?: (rows: TData[]) => void
|
|
48
52
|
onExport?: (data: TData[]) => void
|
|
53
|
+
enableExpandable?: boolean
|
|
54
|
+
renderSubComponent?: (props: { row: { original: TData; id: string } }) => React.ReactNode
|
|
55
|
+
expandedRows?: Set<string>
|
|
56
|
+
onRowExpandChange?: (expandedRows: Set<string>) => void
|
|
49
57
|
features?: {
|
|
50
58
|
sorting?: boolean
|
|
51
59
|
filtering?: boolean
|
|
@@ -89,6 +97,10 @@ export function DataTable<TData, TValue>({
|
|
|
89
97
|
className,
|
|
90
98
|
onRowSelect,
|
|
91
99
|
onExport,
|
|
100
|
+
enableExpandable = false,
|
|
101
|
+
renderSubComponent,
|
|
102
|
+
expandedRows: controlledExpandedRows,
|
|
103
|
+
onRowExpandChange,
|
|
92
104
|
features = {},
|
|
93
105
|
theme = {},
|
|
94
106
|
texts = {},
|
|
@@ -131,6 +143,14 @@ export function DataTable<TData, TValue>({
|
|
|
131
143
|
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
|
|
132
144
|
const [rowSelection, setRowSelection] = React.useState({})
|
|
133
145
|
const [globalFilter, setGlobalFilter] = React.useState('')
|
|
146
|
+
const [isPaginationLoading, setIsPaginationLoading] = React.useState(false)
|
|
147
|
+
const [internalExpandedRows, setInternalExpandedRows] = React.useState<Set<string>>(new Set())
|
|
148
|
+
|
|
149
|
+
// Use controlled or internal expanded state
|
|
150
|
+
const expandedRows = controlledExpandedRows || internalExpandedRows
|
|
151
|
+
const setExpandedRows = onRowExpandChange ?
|
|
152
|
+
(newExpanded: Set<string>) => onRowExpandChange(newExpanded) :
|
|
153
|
+
setInternalExpandedRows
|
|
134
154
|
|
|
135
155
|
const table = useReactTable({
|
|
136
156
|
data,
|
|
@@ -188,9 +208,9 @@ export function DataTable<TData, TValue>({
|
|
|
188
208
|
}
|
|
189
209
|
|
|
190
210
|
return (
|
|
191
|
-
<div className={cn("space-y-4", className)}>
|
|
211
|
+
<div className={cn("moonui-data-table-container space-y-4", className)}>
|
|
192
212
|
{/* Toolbar */}
|
|
193
|
-
<div className="flex items-center justify-between">
|
|
213
|
+
<div className="moonui-data-table-toolbar flex items-center justify-between">
|
|
194
214
|
<div className="flex items-center space-x-2">
|
|
195
215
|
{searchable && (
|
|
196
216
|
<div className="relative">
|
|
@@ -228,15 +248,15 @@ export function DataTable<TData, TValue>({
|
|
|
228
248
|
</div>
|
|
229
249
|
|
|
230
250
|
{/* Table */}
|
|
231
|
-
<div className="rounded-md border">
|
|
232
|
-
<table className="w-full">
|
|
233
|
-
<thead>
|
|
251
|
+
<div className="moonui-data-table-wrapper rounded-md border overflow-hidden">
|
|
252
|
+
<table className="moonui-data-table w-full">
|
|
253
|
+
<thead className="moonui-data-table-header">
|
|
234
254
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
235
|
-
<tr key={headerGroup.id} className="border-b">
|
|
255
|
+
<tr key={headerGroup.id} className="moonui-data-table-row border-b">
|
|
236
256
|
{headerGroup.headers.map((header) => (
|
|
237
257
|
<th
|
|
238
258
|
key={header.id}
|
|
239
|
-
className="h-12 px-4 text-left align-middle font-medium text-muted-foreground"
|
|
259
|
+
className="moonui-data-table-th h-12 px-4 text-left align-middle font-medium text-muted-foreground"
|
|
240
260
|
>
|
|
241
261
|
{header.isPlaceholder ? null : (
|
|
242
262
|
<div
|
|
@@ -265,30 +285,86 @@ export function DataTable<TData, TValue>({
|
|
|
265
285
|
</tr>
|
|
266
286
|
))}
|
|
267
287
|
</thead>
|
|
268
|
-
<tbody>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
<tr
|
|
272
|
-
key=
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
288
|
+
<tbody className="moonui-data-table-body">
|
|
289
|
+
<AnimatePresence mode="wait">
|
|
290
|
+
{isPaginationLoading ? (
|
|
291
|
+
<motion.tr
|
|
292
|
+
key="loading"
|
|
293
|
+
initial={{ opacity: 0 }}
|
|
294
|
+
animate={{ opacity: 1 }}
|
|
295
|
+
exit={{ opacity: 0 }}
|
|
296
|
+
transition={{ duration: 0.2 }}
|
|
277
297
|
>
|
|
278
|
-
{
|
|
279
|
-
<
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
298
|
+
<td colSpan={columns.length} className="h-24 text-center">
|
|
299
|
+
<div className="flex items-center justify-center space-x-2">
|
|
300
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
301
|
+
<span className="text-sm text-muted-foreground">Loading...</span>
|
|
302
|
+
</div>
|
|
303
|
+
</td>
|
|
304
|
+
</motion.tr>
|
|
305
|
+
) : table.getRowModel().rows?.length ? (
|
|
306
|
+
table.getRowModel().rows.flatMap((row, index) => {
|
|
307
|
+
const rowId = (row.original as any).id || row.id
|
|
308
|
+
const isExpanded = enableExpandable && expandedRows.has(rowId)
|
|
309
|
+
|
|
310
|
+
const mainRow = (
|
|
311
|
+
<motion.tr
|
|
312
|
+
key={row.id}
|
|
313
|
+
initial={{ opacity: 0, y: 20 }}
|
|
314
|
+
animate={{ opacity: 1, y: 0 }}
|
|
315
|
+
exit={{ opacity: 0, y: -20 }}
|
|
316
|
+
transition={{
|
|
317
|
+
duration: 0.3,
|
|
318
|
+
delay: index * 0.05,
|
|
319
|
+
ease: "easeOut"
|
|
320
|
+
}}
|
|
321
|
+
className={cn(
|
|
322
|
+
"border-b transition-colors hover:bg-muted/50",
|
|
323
|
+
row.getIsSelected() && "bg-muted",
|
|
324
|
+
isExpanded && "border-b-0"
|
|
325
|
+
)}
|
|
326
|
+
>
|
|
327
|
+
{row.getVisibleCells().map((cell) => (
|
|
328
|
+
<td key={cell.id} className="moonui-data-table-td p-4 align-middle">
|
|
329
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
330
|
+
</td>
|
|
331
|
+
))}
|
|
332
|
+
</motion.tr>
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
const expandedRow = isExpanded && renderSubComponent ? (
|
|
336
|
+
<motion.tr
|
|
337
|
+
key={`${row.id}-expanded`}
|
|
338
|
+
initial={{ opacity: 0, height: 0 }}
|
|
339
|
+
animate={{ opacity: 1, height: "auto" }}
|
|
340
|
+
exit={{ opacity: 0, height: 0 }}
|
|
341
|
+
transition={{ duration: 0.3, ease: "easeOut" }}
|
|
342
|
+
className="border-b"
|
|
343
|
+
>
|
|
344
|
+
<td colSpan={columns.length} className="p-0">
|
|
345
|
+
<div className="border-t border-border/50">
|
|
346
|
+
{renderSubComponent({ row: { original: row.original, id: rowId } })}
|
|
347
|
+
</div>
|
|
348
|
+
</td>
|
|
349
|
+
</motion.tr>
|
|
350
|
+
) : null;
|
|
351
|
+
|
|
352
|
+
return expandedRow ? [mainRow, expandedRow] : [mainRow];
|
|
353
|
+
})
|
|
354
|
+
) : (
|
|
355
|
+
<motion.tr
|
|
356
|
+
key="no-results"
|
|
357
|
+
initial={{ opacity: 0 }}
|
|
358
|
+
animate={{ opacity: 1 }}
|
|
359
|
+
exit={{ opacity: 0 }}
|
|
360
|
+
transition={{ duration: 0.2 }}
|
|
361
|
+
>
|
|
362
|
+
<td colSpan={columns.length} className="h-24 text-center">
|
|
363
|
+
No results found.
|
|
364
|
+
</td>
|
|
365
|
+
</motion.tr>
|
|
366
|
+
)}
|
|
367
|
+
</AnimatePresence>
|
|
292
368
|
</tbody>
|
|
293
369
|
</table>
|
|
294
370
|
</div>
|
|
@@ -309,8 +385,14 @@ export function DataTable<TData, TValue>({
|
|
|
309
385
|
<p className="text-sm font-medium">Rows per page</p>
|
|
310
386
|
<select
|
|
311
387
|
value={table.getState().pagination.pageSize}
|
|
312
|
-
onChange={(e) =>
|
|
388
|
+
onChange={async (e) => {
|
|
389
|
+
setIsPaginationLoading(true)
|
|
390
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
391
|
+
table.setPageSize(Number(e.target.value))
|
|
392
|
+
setIsPaginationLoading(false)
|
|
393
|
+
}}
|
|
313
394
|
className="h-8 w-[70px] rounded border border-input bg-background px-3 py-1 text-sm"
|
|
395
|
+
disabled={isPaginationLoading}
|
|
314
396
|
>
|
|
315
397
|
{[10, 20, 30, 40, 50].map((pageSize) => (
|
|
316
398
|
<option key={pageSize} value={pageSize}>
|
|
@@ -327,32 +409,52 @@ export function DataTable<TData, TValue>({
|
|
|
327
409
|
<Button
|
|
328
410
|
variant="outline"
|
|
329
411
|
className="hidden h-8 w-8 p-0 lg:flex"
|
|
330
|
-
onClick={() =>
|
|
331
|
-
|
|
412
|
+
onClick={async () => {
|
|
413
|
+
setIsPaginationLoading(true)
|
|
414
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
415
|
+
table.setPageIndex(0)
|
|
416
|
+
setIsPaginationLoading(false)
|
|
417
|
+
}}
|
|
418
|
+
disabled={!table.getCanPreviousPage() || isPaginationLoading}
|
|
332
419
|
>
|
|
333
420
|
<ChevronsLeft className="h-4 w-4" />
|
|
334
421
|
</Button>
|
|
335
422
|
<Button
|
|
336
423
|
variant="outline"
|
|
337
424
|
className="h-8 w-8 p-0"
|
|
338
|
-
onClick={() =>
|
|
339
|
-
|
|
425
|
+
onClick={async () => {
|
|
426
|
+
setIsPaginationLoading(true)
|
|
427
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
428
|
+
table.previousPage()
|
|
429
|
+
setIsPaginationLoading(false)
|
|
430
|
+
}}
|
|
431
|
+
disabled={!table.getCanPreviousPage() || isPaginationLoading}
|
|
340
432
|
>
|
|
341
433
|
<ChevronLeft className="h-4 w-4" />
|
|
342
434
|
</Button>
|
|
343
435
|
<Button
|
|
344
436
|
variant="outline"
|
|
345
437
|
className="h-8 w-8 p-0"
|
|
346
|
-
onClick={() =>
|
|
347
|
-
|
|
438
|
+
onClick={async () => {
|
|
439
|
+
setIsPaginationLoading(true)
|
|
440
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
441
|
+
table.nextPage()
|
|
442
|
+
setIsPaginationLoading(false)
|
|
443
|
+
}}
|
|
444
|
+
disabled={!table.getCanNextPage() || isPaginationLoading}
|
|
348
445
|
>
|
|
349
446
|
<ChevronRight className="h-4 w-4" />
|
|
350
447
|
</Button>
|
|
351
448
|
<Button
|
|
352
449
|
variant="outline"
|
|
353
450
|
className="hidden h-8 w-8 p-0 lg:flex"
|
|
354
|
-
onClick={() =>
|
|
355
|
-
|
|
451
|
+
onClick={async () => {
|
|
452
|
+
setIsPaginationLoading(true)
|
|
453
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
454
|
+
table.setPageIndex(table.getPageCount() - 1)
|
|
455
|
+
setIsPaginationLoading(false)
|
|
456
|
+
}}
|
|
457
|
+
disabled={!table.getCanNextPage() || isPaginationLoading}
|
|
356
458
|
>
|
|
357
459
|
<ChevronsRight className="h-4 w-4" />
|
|
358
460
|
</Button>
|
|
@@ -363,3 +465,81 @@ export function DataTable<TData, TValue>({
|
|
|
363
465
|
</div>
|
|
364
466
|
)
|
|
365
467
|
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Helper function to create an expandable column
|
|
471
|
+
* @param expandedRows - Set of expanded row IDs
|
|
472
|
+
* @param onToggle - Function to toggle row expansion
|
|
473
|
+
* @returns ColumnDef for expandable rows
|
|
474
|
+
*/
|
|
475
|
+
export function getExpandableColumn<TData>(
|
|
476
|
+
expandedRows: Set<string>,
|
|
477
|
+
onToggle: (id: string) => void
|
|
478
|
+
): ColumnDef<TData, any> {
|
|
479
|
+
return {
|
|
480
|
+
id: "expander",
|
|
481
|
+
header: () => null,
|
|
482
|
+
size: 50,
|
|
483
|
+
cell: ({ row }) => {
|
|
484
|
+
const rowId = (row.original as any).id || row.id;
|
|
485
|
+
const isExpanded = expandedRows.has(rowId);
|
|
486
|
+
|
|
487
|
+
return (
|
|
488
|
+
<button
|
|
489
|
+
onClick={(e) => {
|
|
490
|
+
e.stopPropagation();
|
|
491
|
+
onToggle(rowId);
|
|
492
|
+
}}
|
|
493
|
+
className="p-2 hover:bg-muted rounded-md transition-colors"
|
|
494
|
+
aria-label={isExpanded ? "Collapse row" : "Expand row"}
|
|
495
|
+
>
|
|
496
|
+
{isExpanded ? (
|
|
497
|
+
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
|
498
|
+
) : (
|
|
499
|
+
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
|
500
|
+
)}
|
|
501
|
+
</button>
|
|
502
|
+
);
|
|
503
|
+
},
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Hook for managing expandable rows
|
|
509
|
+
* @param initialExpanded - Initial set of expanded row IDs
|
|
510
|
+
* @returns Object with expandedRows, toggleRow, and expandAll/collapseAll functions
|
|
511
|
+
*/
|
|
512
|
+
export function useExpandableRows(initialExpanded: Set<string> = new Set()) {
|
|
513
|
+
const [expandedRows, setExpandedRows] = React.useState<Set<string>>(initialExpanded);
|
|
514
|
+
|
|
515
|
+
const toggleRow = React.useCallback((id: string) => {
|
|
516
|
+
setExpandedRows(prev => {
|
|
517
|
+
const newExpanded = new Set(prev);
|
|
518
|
+
if (newExpanded.has(id)) {
|
|
519
|
+
newExpanded.delete(id);
|
|
520
|
+
} else {
|
|
521
|
+
newExpanded.add(id);
|
|
522
|
+
}
|
|
523
|
+
return newExpanded;
|
|
524
|
+
});
|
|
525
|
+
}, []);
|
|
526
|
+
|
|
527
|
+
const expandAll = React.useCallback((rowIds: string[]) => {
|
|
528
|
+
setExpandedRows(new Set(rowIds));
|
|
529
|
+
}, []);
|
|
530
|
+
|
|
531
|
+
const collapseAll = React.useCallback(() => {
|
|
532
|
+
setExpandedRows(new Set());
|
|
533
|
+
}, []);
|
|
534
|
+
|
|
535
|
+
return {
|
|
536
|
+
expandedRows,
|
|
537
|
+
setExpandedRows,
|
|
538
|
+
toggleRow,
|
|
539
|
+
expandAll,
|
|
540
|
+
collapseAll,
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Re-export types for convenience
|
|
545
|
+
export { type ColumnDef } from "@tanstack/react-table";
|