@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui-pro",
3
- "version": "2.2.18",
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 className="overflow-x-auto">
316
+ <CardContent>
317
317
  <div className="space-y-4">
318
318
  {/* Calendar Grid */}
319
- <div className="grid grid-cols-7 gap-1 w-full" style={{ minWidth: "500px" }}>
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 min-w-[70px]">
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] min-w-[70px] p-1 border border-border/50 cursor-pointer hover:bg-muted/50 transition-colors text-xs flex flex-col",
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
- {table.getRowModel().rows?.length ? (
270
- table.getRowModel().rows.map((row) => (
271
- <tr
272
- key={row.id}
273
- className={cn(
274
- "border-b transition-colors hover:bg-muted/50",
275
- row.getIsSelected() && "bg-muted"
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
- {row.getVisibleCells().map((cell) => (
279
- <td key={cell.id} className="p-4 align-middle">
280
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
281
- </td>
282
- ))}
283
- </tr>
284
- ))
285
- ) : (
286
- <tr>
287
- <td colSpan={columns.length} className="h-24 text-center">
288
- No results found.
289
- </td>
290
- </tr>
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) => table.setPageSize(Number(e.target.value))}
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={() => table.setPageIndex(0)}
331
- disabled={!table.getCanPreviousPage()}
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={() => table.previousPage()}
339
- disabled={!table.getCanPreviousPage()}
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={() => table.nextPage()}
347
- disabled={!table.getCanNextPage()}
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={() => table.setPageIndex(table.getPageCount() - 1)}
355
- disabled={!table.getCanNextPage()}
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 { ButtonProps, moonUIButtonProVariants } from "./button"
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