@moontra/moonui-pro 2.2.18 → 2.3.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.
- package/dist/index.d.ts +31 -2
- package/dist/index.mjs +156 -24
- package/package.json +1 -1
- package/src/components/calendar/index.tsx +4 -4
- package/src/components/data-table/index.tsx +211 -32
- package/src/components/ui/pagination.tsx +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moontra/moonui-pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
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",
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
ChevronRight,
|
|
22
22
|
ChevronsLeft,
|
|
23
23
|
ChevronsRight,
|
|
24
|
+
ChevronDown,
|
|
24
25
|
ArrowUpDown,
|
|
25
26
|
ArrowUp,
|
|
26
27
|
ArrowDown,
|
|
@@ -29,10 +30,12 @@ import {
|
|
|
29
30
|
Download,
|
|
30
31
|
Settings,
|
|
31
32
|
Lock,
|
|
32
|
-
Sparkles
|
|
33
|
+
Sparkles,
|
|
34
|
+
Loader2
|
|
33
35
|
} from 'lucide-react'
|
|
34
36
|
import { cn } from '../../lib/utils'
|
|
35
37
|
import { useSubscription } from '../../hooks/use-subscription'
|
|
38
|
+
import { motion, AnimatePresence } from 'framer-motion'
|
|
36
39
|
|
|
37
40
|
interface DataTableProps<TData, TValue> {
|
|
38
41
|
columns: ColumnDef<TData, TValue>[]
|
|
@@ -46,6 +49,10 @@ interface DataTableProps<TData, TValue> {
|
|
|
46
49
|
className?: string
|
|
47
50
|
onRowSelect?: (rows: TData[]) => void
|
|
48
51
|
onExport?: (data: TData[]) => void
|
|
52
|
+
enableExpandable?: boolean
|
|
53
|
+
renderSubComponent?: (props: { row: { original: TData; id: string } }) => React.ReactNode
|
|
54
|
+
expandedRows?: Set<string>
|
|
55
|
+
onRowExpandChange?: (expandedRows: Set<string>) => void
|
|
49
56
|
features?: {
|
|
50
57
|
sorting?: boolean
|
|
51
58
|
filtering?: boolean
|
|
@@ -89,6 +96,10 @@ export function DataTable<TData, TValue>({
|
|
|
89
96
|
className,
|
|
90
97
|
onRowSelect,
|
|
91
98
|
onExport,
|
|
99
|
+
enableExpandable = false,
|
|
100
|
+
renderSubComponent,
|
|
101
|
+
expandedRows: controlledExpandedRows,
|
|
102
|
+
onRowExpandChange,
|
|
92
103
|
features = {},
|
|
93
104
|
theme = {},
|
|
94
105
|
texts = {},
|
|
@@ -131,6 +142,14 @@ export function DataTable<TData, TValue>({
|
|
|
131
142
|
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
|
|
132
143
|
const [rowSelection, setRowSelection] = React.useState({})
|
|
133
144
|
const [globalFilter, setGlobalFilter] = React.useState('')
|
|
145
|
+
const [isPaginationLoading, setIsPaginationLoading] = React.useState(false)
|
|
146
|
+
const [internalExpandedRows, setInternalExpandedRows] = React.useState<Set<string>>(new Set())
|
|
147
|
+
|
|
148
|
+
// Use controlled or internal expanded state
|
|
149
|
+
const expandedRows = controlledExpandedRows || internalExpandedRows
|
|
150
|
+
const setExpandedRows = onRowExpandChange ?
|
|
151
|
+
(newExpanded: Set<string>) => onRowExpandChange(newExpanded) :
|
|
152
|
+
setInternalExpandedRows
|
|
134
153
|
|
|
135
154
|
const table = useReactTable({
|
|
136
155
|
data,
|
|
@@ -266,29 +285,85 @@ export function DataTable<TData, TValue>({
|
|
|
266
285
|
))}
|
|
267
286
|
</thead>
|
|
268
287
|
<tbody>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
<tr
|
|
272
|
-
key=
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
288
|
+
<AnimatePresence mode="wait">
|
|
289
|
+
{isPaginationLoading ? (
|
|
290
|
+
<motion.tr
|
|
291
|
+
key="loading"
|
|
292
|
+
initial={{ opacity: 0 }}
|
|
293
|
+
animate={{ opacity: 1 }}
|
|
294
|
+
exit={{ opacity: 0 }}
|
|
295
|
+
transition={{ duration: 0.2 }}
|
|
277
296
|
>
|
|
278
|
-
{
|
|
279
|
-
<
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
297
|
+
<td colSpan={columns.length} className="h-24 text-center">
|
|
298
|
+
<div className="flex items-center justify-center space-x-2">
|
|
299
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
300
|
+
<span className="text-sm text-muted-foreground">Loading...</span>
|
|
301
|
+
</div>
|
|
302
|
+
</td>
|
|
303
|
+
</motion.tr>
|
|
304
|
+
) : table.getRowModel().rows?.length ? (
|
|
305
|
+
table.getRowModel().rows.flatMap((row, index) => {
|
|
306
|
+
const rowId = (row.original as any).id || row.id
|
|
307
|
+
const isExpanded = enableExpandable && expandedRows.has(rowId)
|
|
308
|
+
|
|
309
|
+
const mainRow = (
|
|
310
|
+
<motion.tr
|
|
311
|
+
key={row.id}
|
|
312
|
+
initial={{ opacity: 0, y: 20 }}
|
|
313
|
+
animate={{ opacity: 1, y: 0 }}
|
|
314
|
+
exit={{ opacity: 0, y: -20 }}
|
|
315
|
+
transition={{
|
|
316
|
+
duration: 0.3,
|
|
317
|
+
delay: index * 0.05,
|
|
318
|
+
ease: "easeOut"
|
|
319
|
+
}}
|
|
320
|
+
className={cn(
|
|
321
|
+
"border-b transition-colors hover:bg-muted/50",
|
|
322
|
+
row.getIsSelected() && "bg-muted",
|
|
323
|
+
isExpanded && "border-b-0"
|
|
324
|
+
)}
|
|
325
|
+
>
|
|
326
|
+
{row.getVisibleCells().map((cell) => (
|
|
327
|
+
<td key={cell.id} className="p-4 align-middle">
|
|
328
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
329
|
+
</td>
|
|
330
|
+
))}
|
|
331
|
+
</motion.tr>
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
const expandedRow = isExpanded && renderSubComponent ? (
|
|
335
|
+
<motion.tr
|
|
336
|
+
key={`${row.id}-expanded`}
|
|
337
|
+
initial={{ opacity: 0, height: 0 }}
|
|
338
|
+
animate={{ opacity: 1, height: "auto" }}
|
|
339
|
+
exit={{ opacity: 0, height: 0 }}
|
|
340
|
+
transition={{ duration: 0.3, ease: "easeOut" }}
|
|
341
|
+
className="border-b"
|
|
342
|
+
>
|
|
343
|
+
<td colSpan={columns.length} className="p-0">
|
|
344
|
+
<div className="border-t border-border/50">
|
|
345
|
+
{renderSubComponent({ row: { original: row.original, id: rowId } })}
|
|
346
|
+
</div>
|
|
347
|
+
</td>
|
|
348
|
+
</motion.tr>
|
|
349
|
+
) : null;
|
|
350
|
+
|
|
351
|
+
return expandedRow ? [mainRow, expandedRow] : [mainRow];
|
|
352
|
+
})
|
|
353
|
+
) : (
|
|
354
|
+
<motion.tr
|
|
355
|
+
key="no-results"
|
|
356
|
+
initial={{ opacity: 0 }}
|
|
357
|
+
animate={{ opacity: 1 }}
|
|
358
|
+
exit={{ opacity: 0 }}
|
|
359
|
+
transition={{ duration: 0.2 }}
|
|
360
|
+
>
|
|
361
|
+
<td colSpan={columns.length} className="h-24 text-center">
|
|
362
|
+
No results found.
|
|
363
|
+
</td>
|
|
364
|
+
</motion.tr>
|
|
365
|
+
)}
|
|
366
|
+
</AnimatePresence>
|
|
292
367
|
</tbody>
|
|
293
368
|
</table>
|
|
294
369
|
</div>
|
|
@@ -309,8 +384,14 @@ export function DataTable<TData, TValue>({
|
|
|
309
384
|
<p className="text-sm font-medium">Rows per page</p>
|
|
310
385
|
<select
|
|
311
386
|
value={table.getState().pagination.pageSize}
|
|
312
|
-
onChange={(e) =>
|
|
387
|
+
onChange={async (e) => {
|
|
388
|
+
setIsPaginationLoading(true)
|
|
389
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
390
|
+
table.setPageSize(Number(e.target.value))
|
|
391
|
+
setIsPaginationLoading(false)
|
|
392
|
+
}}
|
|
313
393
|
className="h-8 w-[70px] rounded border border-input bg-background px-3 py-1 text-sm"
|
|
394
|
+
disabled={isPaginationLoading}
|
|
314
395
|
>
|
|
315
396
|
{[10, 20, 30, 40, 50].map((pageSize) => (
|
|
316
397
|
<option key={pageSize} value={pageSize}>
|
|
@@ -327,32 +408,52 @@ export function DataTable<TData, TValue>({
|
|
|
327
408
|
<Button
|
|
328
409
|
variant="outline"
|
|
329
410
|
className="hidden h-8 w-8 p-0 lg:flex"
|
|
330
|
-
onClick={() =>
|
|
331
|
-
|
|
411
|
+
onClick={async () => {
|
|
412
|
+
setIsPaginationLoading(true)
|
|
413
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
414
|
+
table.setPageIndex(0)
|
|
415
|
+
setIsPaginationLoading(false)
|
|
416
|
+
}}
|
|
417
|
+
disabled={!table.getCanPreviousPage() || isPaginationLoading}
|
|
332
418
|
>
|
|
333
419
|
<ChevronsLeft className="h-4 w-4" />
|
|
334
420
|
</Button>
|
|
335
421
|
<Button
|
|
336
422
|
variant="outline"
|
|
337
423
|
className="h-8 w-8 p-0"
|
|
338
|
-
onClick={() =>
|
|
339
|
-
|
|
424
|
+
onClick={async () => {
|
|
425
|
+
setIsPaginationLoading(true)
|
|
426
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
427
|
+
table.previousPage()
|
|
428
|
+
setIsPaginationLoading(false)
|
|
429
|
+
}}
|
|
430
|
+
disabled={!table.getCanPreviousPage() || isPaginationLoading}
|
|
340
431
|
>
|
|
341
432
|
<ChevronLeft className="h-4 w-4" />
|
|
342
433
|
</Button>
|
|
343
434
|
<Button
|
|
344
435
|
variant="outline"
|
|
345
436
|
className="h-8 w-8 p-0"
|
|
346
|
-
onClick={() =>
|
|
347
|
-
|
|
437
|
+
onClick={async () => {
|
|
438
|
+
setIsPaginationLoading(true)
|
|
439
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
440
|
+
table.nextPage()
|
|
441
|
+
setIsPaginationLoading(false)
|
|
442
|
+
}}
|
|
443
|
+
disabled={!table.getCanNextPage() || isPaginationLoading}
|
|
348
444
|
>
|
|
349
445
|
<ChevronRight className="h-4 w-4" />
|
|
350
446
|
</Button>
|
|
351
447
|
<Button
|
|
352
448
|
variant="outline"
|
|
353
449
|
className="hidden h-8 w-8 p-0 lg:flex"
|
|
354
|
-
onClick={() =>
|
|
355
|
-
|
|
450
|
+
onClick={async () => {
|
|
451
|
+
setIsPaginationLoading(true)
|
|
452
|
+
await new Promise(resolve => setTimeout(resolve, 300))
|
|
453
|
+
table.setPageIndex(table.getPageCount() - 1)
|
|
454
|
+
setIsPaginationLoading(false)
|
|
455
|
+
}}
|
|
456
|
+
disabled={!table.getCanNextPage() || isPaginationLoading}
|
|
356
457
|
>
|
|
357
458
|
<ChevronsRight className="h-4 w-4" />
|
|
358
459
|
</Button>
|
|
@@ -363,3 +464,81 @@ export function DataTable<TData, TValue>({
|
|
|
363
464
|
</div>
|
|
364
465
|
)
|
|
365
466
|
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Helper function to create an expandable column
|
|
470
|
+
* @param expandedRows - Set of expanded row IDs
|
|
471
|
+
* @param onToggle - Function to toggle row expansion
|
|
472
|
+
* @returns ColumnDef for expandable rows
|
|
473
|
+
*/
|
|
474
|
+
export function getExpandableColumn<TData>(
|
|
475
|
+
expandedRows: Set<string>,
|
|
476
|
+
onToggle: (id: string) => void
|
|
477
|
+
): ColumnDef<TData, any> {
|
|
478
|
+
return {
|
|
479
|
+
id: "expander",
|
|
480
|
+
header: () => null,
|
|
481
|
+
size: 50,
|
|
482
|
+
cell: ({ row }) => {
|
|
483
|
+
const rowId = (row.original as any).id || row.id;
|
|
484
|
+
const isExpanded = expandedRows.has(rowId);
|
|
485
|
+
|
|
486
|
+
return (
|
|
487
|
+
<button
|
|
488
|
+
onClick={(e) => {
|
|
489
|
+
e.stopPropagation();
|
|
490
|
+
onToggle(rowId);
|
|
491
|
+
}}
|
|
492
|
+
className="p-2 hover:bg-muted rounded-md transition-colors"
|
|
493
|
+
aria-label={isExpanded ? "Collapse row" : "Expand row"}
|
|
494
|
+
>
|
|
495
|
+
{isExpanded ? (
|
|
496
|
+
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
|
497
|
+
) : (
|
|
498
|
+
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
|
499
|
+
)}
|
|
500
|
+
</button>
|
|
501
|
+
);
|
|
502
|
+
},
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Hook for managing expandable rows
|
|
508
|
+
* @param initialExpanded - Initial set of expanded row IDs
|
|
509
|
+
* @returns Object with expandedRows, toggleRow, and expandAll/collapseAll functions
|
|
510
|
+
*/
|
|
511
|
+
export function useExpandableRows(initialExpanded: Set<string> = new Set()) {
|
|
512
|
+
const [expandedRows, setExpandedRows] = React.useState<Set<string>>(initialExpanded);
|
|
513
|
+
|
|
514
|
+
const toggleRow = React.useCallback((id: string) => {
|
|
515
|
+
setExpandedRows(prev => {
|
|
516
|
+
const newExpanded = new Set(prev);
|
|
517
|
+
if (newExpanded.has(id)) {
|
|
518
|
+
newExpanded.delete(id);
|
|
519
|
+
} else {
|
|
520
|
+
newExpanded.add(id);
|
|
521
|
+
}
|
|
522
|
+
return newExpanded;
|
|
523
|
+
});
|
|
524
|
+
}, []);
|
|
525
|
+
|
|
526
|
+
const expandAll = React.useCallback((rowIds: string[]) => {
|
|
527
|
+
setExpandedRows(new Set(rowIds));
|
|
528
|
+
}, []);
|
|
529
|
+
|
|
530
|
+
const collapseAll = React.useCallback(() => {
|
|
531
|
+
setExpandedRows(new Set());
|
|
532
|
+
}, []);
|
|
533
|
+
|
|
534
|
+
return {
|
|
535
|
+
expandedRows,
|
|
536
|
+
setExpandedRows,
|
|
537
|
+
toggleRow,
|
|
538
|
+
expandAll,
|
|
539
|
+
collapseAll,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Re-export types for convenience
|
|
544
|
+
export { type ColumnDef } from "@tanstack/react-table";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
2
|
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
|
|
3
3
|
import { cn } from "../../lib/utils"
|
|
4
|
-
import {
|
|
4
|
+
import { moonUIButtonProVariants } from "./button"
|
|
5
|
+
import type { ButtonProps } from "./button"
|
|
5
6
|
|
|
6
7
|
const MoonUIPaginationPro = ({ className, ...props }: React.ComponentProps<"nav">) => (
|
|
7
8
|
<nav
|