@moontra/moonui-pro 2.20.2 → 2.20.3
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 +8 -3
- package/plugin/index.d.ts +86 -0
- package/plugin/index.js +308 -0
- package/scripts/postinstall.js +176 -23
- package/src/components/advanced-chart/index.tsx +0 -1246
- package/src/components/advanced-forms/index.tsx +0 -585
- package/src/components/animated-button/index.tsx +0 -385
- package/src/components/calendar/event-dialog.tsx +0 -377
- package/src/components/calendar/index.tsx +0 -1220
- package/src/components/calendar-pro/index.tsx +0 -1697
- package/src/components/color-picker/index.tsx +0 -432
- package/src/components/credit-card-input/index.tsx +0 -406
- package/src/components/dashboard/dashboard-grid.tsx +0 -480
- package/src/components/dashboard/demo.tsx +0 -425
- package/src/components/dashboard/index.tsx +0 -1046
- package/src/components/dashboard/time-range-picker.tsx +0 -336
- package/src/components/dashboard/types.ts +0 -225
- package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
- package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
- package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
- package/src/components/dashboard/widgets/index.ts +0 -5
- package/src/components/dashboard/widgets/metric-card.tsx +0 -363
- package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
- package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
- package/src/components/data-table/data-table-column-toggle.tsx +0 -169
- package/src/components/data-table/data-table-export.ts +0 -156
- package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
- package/src/components/data-table/index.tsx +0 -845
- package/src/components/draggable-list/index.tsx +0 -100
- package/src/components/error-boundary/index.tsx +0 -232
- package/src/components/file-upload/index.tsx +0 -1660
- package/src/components/floating-action-button/index.tsx +0 -206
- package/src/components/form-wizard/form-wizard-context.tsx +0 -335
- package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
- package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
- package/src/components/form-wizard/form-wizard-step.tsx +0 -111
- package/src/components/form-wizard/index.tsx +0 -102
- package/src/components/form-wizard/types.ts +0 -77
- package/src/components/gesture-drawer/index.tsx +0 -551
- package/src/components/github-stars/github-api.ts +0 -426
- package/src/components/github-stars/hooks.ts +0 -517
- package/src/components/github-stars/index.tsx +0 -375
- package/src/components/github-stars/types.ts +0 -148
- package/src/components/github-stars/variants.tsx +0 -515
- package/src/components/health-check/index.tsx +0 -439
- package/src/components/hover-card-3d/index.tsx +0 -529
- package/src/components/index.ts +0 -130
- package/src/components/internal/index.ts +0 -78
- package/src/components/kanban/add-card-modal.tsx +0 -502
- package/src/components/kanban/card-detail-modal.tsx +0 -761
- package/src/components/kanban/index.ts +0 -13
- package/src/components/kanban/kanban.tsx +0 -1689
- package/src/components/kanban/types.ts +0 -168
- package/src/components/lazy-component/index.tsx +0 -823
- package/src/components/license-error/index.tsx +0 -31
- package/src/components/magnetic-button/index.tsx +0 -216
- package/src/components/memory-efficient-data/index.tsx +0 -1018
- package/src/components/moonui-quiz-form/index.tsx +0 -817
- package/src/components/navbar/index.tsx +0 -781
- package/src/components/optimized-image/index.tsx +0 -425
- package/src/components/performance-debugger/index.tsx +0 -613
- package/src/components/performance-monitor/index.tsx +0 -808
- package/src/components/phone-number-input/index.tsx +0 -343
- package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
- package/src/components/pinch-zoom/index.tsx +0 -566
- package/src/components/quiz-form/index.tsx +0 -479
- package/src/components/rich-text-editor/index.tsx +0 -2322
- package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
- package/src/components/rich-text-editor/slash-commands.css +0 -35
- package/src/components/rich-text-editor/table-styles.css +0 -65
- package/src/components/sidebar/index.tsx +0 -884
- package/src/components/spotlight-card/index.tsx +0 -191
- package/src/components/swipeable-card/index.tsx +0 -100
- package/src/components/timeline/index.tsx +0 -1183
- package/src/components/ui/accordion.tsx +0 -581
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/alert.tsx +0 -141
- package/src/components/ui/aspect-ratio.tsx +0 -245
- package/src/components/ui/avatar.tsx +0 -155
- package/src/components/ui/badge.tsx +0 -230
- package/src/components/ui/breadcrumb.tsx +0 -216
- package/src/components/ui/button.tsx +0 -228
- package/src/components/ui/calendar.tsx +0 -387
- package/src/components/ui/card.tsx +0 -216
- package/src/components/ui/checkbox.tsx +0 -259
- package/src/components/ui/collapsible.tsx +0 -631
- package/src/components/ui/color-picker.tsx +0 -97
- package/src/components/ui/command.tsx +0 -948
- package/src/components/ui/dialog.tsx +0 -752
- package/src/components/ui/dropdown-menu.tsx +0 -706
- package/src/components/ui/gesture-drawer.tsx +0 -11
- package/src/components/ui/hover-card.tsx +0 -29
- package/src/components/ui/index.ts +0 -222
- package/src/components/ui/input.tsx +0 -224
- package/src/components/ui/label.tsx +0 -29
- package/src/components/ui/lightbox.tsx +0 -606
- package/src/components/ui/magnetic-button.tsx +0 -129
- package/src/components/ui/media-gallery.tsx +0 -611
- package/src/components/ui/navigation-menu.tsx +0 -130
- package/src/components/ui/pagination.tsx +0 -125
- package/src/components/ui/popover.tsx +0 -185
- package/src/components/ui/progress.tsx +0 -30
- package/src/components/ui/radio-group.tsx +0 -257
- package/src/components/ui/scroll-area.tsx +0 -47
- package/src/components/ui/select.tsx +0 -378
- package/src/components/ui/separator.tsx +0 -145
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/skeleton.tsx +0 -20
- package/src/components/ui/slider.tsx +0 -354
- package/src/components/ui/spotlight-card.tsx +0 -119
- package/src/components/ui/switch.tsx +0 -86
- package/src/components/ui/table.tsx +0 -331
- package/src/components/ui/tabs-pro.tsx +0 -542
- package/src/components/ui/tabs.tsx +0 -54
- package/src/components/ui/textarea.tsx +0 -28
- package/src/components/ui/toast.tsx +0 -317
- package/src/components/ui/toggle.tsx +0 -119
- package/src/components/ui/tooltip.tsx +0 -151
- package/src/components/virtual-list/index.tsx +0 -668
- package/src/hooks/use-chart.ts +0 -205
- package/src/hooks/use-data-table.ts +0 -182
- package/src/hooks/use-docs-pro-access.ts +0 -13
- package/src/hooks/use-license-check.ts +0 -65
- package/src/hooks/use-subscription.ts +0 -19
- package/src/hooks/use-toast.ts +0 -15
- package/src/index.ts +0 -22
- package/src/lib/ai-providers.ts +0 -377
- package/src/lib/component-metadata.ts +0 -18
- package/src/lib/micro-interactions.ts +0 -255
- package/src/lib/paddle.ts +0 -17
- package/src/lib/utils.ts +0 -6
- package/src/patterns/login-form/index.tsx +0 -276
- package/src/patterns/login-form/types.ts +0 -67
- package/src/setupTests.ts +0 -41
- package/src/styles/advanced-chart.css +0 -239
- package/src/styles/calendar.css +0 -35
- package/src/styles/design-system.css +0 -363
- package/src/styles/index.css +0 -681
- package/src/styles/tailwind.css +0 -7
- package/src/styles/tokens.css +0 -455
- package/src/types/next-auth.d.ts +0 -21
- package/src/use-intersection-observer.tsx +0 -154
- package/src/use-local-storage.tsx +0 -71
- package/src/use-paddle.ts +0 -138
- package/src/use-performance-optimizer.ts +0 -389
- package/src/use-pro-access.ts +0 -141
- package/src/use-scroll-animation.ts +0 -219
- package/src/use-subscription.ts +0 -37
- package/src/use-toast.ts +0 -32
- package/src/utils/chart-helpers.ts +0 -357
- package/src/utils/cn.ts +0 -6
- package/src/utils/data-processing.ts +0 -151
- package/src/utils/license-validator.tsx +0 -183
|
@@ -1,845 +0,0 @@
|
|
|
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
|
-
OnChangeFn,
|
|
16
|
-
Row,
|
|
17
|
-
} from '@tanstack/react-table'
|
|
18
|
-
import { Button } from '../ui/button'
|
|
19
|
-
import { Input } from '../ui/input'
|
|
20
|
-
import { Card, CardContent } from '../ui/card'
|
|
21
|
-
import {
|
|
22
|
-
ChevronLeft,
|
|
23
|
-
ChevronRight,
|
|
24
|
-
ChevronsLeft,
|
|
25
|
-
ChevronsRight,
|
|
26
|
-
ChevronDown,
|
|
27
|
-
ArrowUpDown,
|
|
28
|
-
ArrowUp,
|
|
29
|
-
ArrowDown,
|
|
30
|
-
Search,
|
|
31
|
-
Filter,
|
|
32
|
-
Download,
|
|
33
|
-
Settings,
|
|
34
|
-
Lock,
|
|
35
|
-
Sparkles,
|
|
36
|
-
Loader2,
|
|
37
|
-
FileDown,
|
|
38
|
-
FileJson,
|
|
39
|
-
FileSpreadsheet
|
|
40
|
-
} from 'lucide-react'
|
|
41
|
-
import { cn } from '../../lib/utils'
|
|
42
|
-
import { useSubscription } from '../../hooks/use-subscription'
|
|
43
|
-
import { motion, AnimatePresence } from 'framer-motion'
|
|
44
|
-
import { DataTableColumnToggle } from './data-table-column-toggle'
|
|
45
|
-
import { DataTableBulkActions, type BulkAction } from './data-table-bulk-actions'
|
|
46
|
-
import { exportData, type ExportFormat, getVisibleColumns } from './data-table-export'
|
|
47
|
-
import { DataTableFilterDrawer, type FilterCondition, type FilterOperator } from './data-table-filter-drawer'
|
|
48
|
-
import {
|
|
49
|
-
DropdownMenu,
|
|
50
|
-
DropdownMenuContent,
|
|
51
|
-
DropdownMenuItem,
|
|
52
|
-
DropdownMenuTrigger,
|
|
53
|
-
} from '../ui/dropdown-menu'
|
|
54
|
-
|
|
55
|
-
interface DataTableProps<TData, TValue> {
|
|
56
|
-
columns: ColumnDef<TData, TValue>[]
|
|
57
|
-
data: TData[]
|
|
58
|
-
searchable?: boolean
|
|
59
|
-
filterable?: boolean
|
|
60
|
-
exportable?: boolean | {
|
|
61
|
-
formats?: ExportFormat[]
|
|
62
|
-
filename?: string
|
|
63
|
-
onExport?: (data: TData[], format: ExportFormat) => void
|
|
64
|
-
}
|
|
65
|
-
selectable?: boolean
|
|
66
|
-
pagination?: boolean
|
|
67
|
-
pageSize?: number
|
|
68
|
-
className?: string
|
|
69
|
-
onRowSelect?: (rows: TData[]) => void
|
|
70
|
-
onExport?: (data: TData[]) => void
|
|
71
|
-
enableExpandable?: boolean
|
|
72
|
-
renderSubComponent?: (props: { row: { original: TData; id: string } }) => React.ReactNode
|
|
73
|
-
expandedRows?: Set<string>
|
|
74
|
-
onRowExpandChange?: (expandedRows: Set<string>) => void
|
|
75
|
-
bulkActions?: BulkAction<TData>[]
|
|
76
|
-
// Additional props for compatibility
|
|
77
|
-
enableSorting?: boolean
|
|
78
|
-
enableFiltering?: boolean
|
|
79
|
-
enablePagination?: boolean
|
|
80
|
-
enableColumnVisibility?: boolean
|
|
81
|
-
enableRowSelection?: boolean
|
|
82
|
-
filterPlaceholder?: string
|
|
83
|
-
defaultPageSize?: number
|
|
84
|
-
manualPagination?: boolean
|
|
85
|
-
pageCount?: number
|
|
86
|
-
onPaginationChange?: (updater: any) => void
|
|
87
|
-
onSortingChange?: OnChangeFn<SortingState>
|
|
88
|
-
onColumnFiltersChange?: OnChangeFn<ColumnFiltersState>
|
|
89
|
-
state?: any
|
|
90
|
-
features?: {
|
|
91
|
-
sorting?: boolean
|
|
92
|
-
filtering?: boolean
|
|
93
|
-
pagination?: boolean
|
|
94
|
-
search?: boolean
|
|
95
|
-
columnVisibility?: boolean
|
|
96
|
-
rowSelection?: boolean
|
|
97
|
-
export?: boolean | string[]
|
|
98
|
-
density?: boolean
|
|
99
|
-
fullscreen?: boolean
|
|
100
|
-
print?: boolean
|
|
101
|
-
}
|
|
102
|
-
theme?: {
|
|
103
|
-
headerBg?: string
|
|
104
|
-
headerText?: string
|
|
105
|
-
borderColor?: string
|
|
106
|
-
rowHoverBg?: string
|
|
107
|
-
selectedRowBg?: string
|
|
108
|
-
}
|
|
109
|
-
texts?: {
|
|
110
|
-
searchPlaceholder?: string
|
|
111
|
-
noResults?: string
|
|
112
|
-
rowsPerPage?: string
|
|
113
|
-
selectedRows?: string
|
|
114
|
-
exportButton?: string
|
|
115
|
-
columnsButton?: string
|
|
116
|
-
densityButton?: string
|
|
117
|
-
filterButton?: string
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function DataTable<TData, TValue>({
|
|
122
|
-
columns: originalColumns,
|
|
123
|
-
data,
|
|
124
|
-
searchable = true,
|
|
125
|
-
filterable = true,
|
|
126
|
-
exportable = true,
|
|
127
|
-
selectable = false,
|
|
128
|
-
pagination = true,
|
|
129
|
-
pageSize = 10,
|
|
130
|
-
className,
|
|
131
|
-
onRowSelect,
|
|
132
|
-
onExport,
|
|
133
|
-
enableExpandable = false,
|
|
134
|
-
renderSubComponent,
|
|
135
|
-
expandedRows: controlledExpandedRows,
|
|
136
|
-
onRowExpandChange,
|
|
137
|
-
bulkActions = [],
|
|
138
|
-
features = {},
|
|
139
|
-
theme = {},
|
|
140
|
-
texts = {},
|
|
141
|
-
// Additional props
|
|
142
|
-
enableSorting = true,
|
|
143
|
-
enableFiltering = true,
|
|
144
|
-
enablePagination = true,
|
|
145
|
-
enableColumnVisibility = true,
|
|
146
|
-
enableRowSelection,
|
|
147
|
-
filterPlaceholder = "Search all columns...",
|
|
148
|
-
defaultPageSize,
|
|
149
|
-
manualPagination = false,
|
|
150
|
-
pageCount,
|
|
151
|
-
onPaginationChange,
|
|
152
|
-
onSortingChange,
|
|
153
|
-
onColumnFiltersChange,
|
|
154
|
-
state: externalState,
|
|
155
|
-
}: DataTableProps<TData, TValue>) {
|
|
156
|
-
// Process columns to ensure they can be hidden and use custom filter
|
|
157
|
-
const columns = React.useMemo(() => {
|
|
158
|
-
return originalColumns.map(col => {
|
|
159
|
-
// Remove any enableHiding: false to avoid conflicts
|
|
160
|
-
const { enableHiding, ...restCol } = col as any;
|
|
161
|
-
return {
|
|
162
|
-
...restCol,
|
|
163
|
-
enableHiding: true, // Force all columns to be hideable
|
|
164
|
-
filterFn: 'custom', // Use our custom filter function
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
}, [originalColumns])
|
|
168
|
-
// Check if we're in docs mode or have pro access
|
|
169
|
-
const { hasProAccess, isLoading } = useSubscription()
|
|
170
|
-
|
|
171
|
-
// In docs mode, always show the component
|
|
172
|
-
|
|
173
|
-
// If not in docs mode and no pro access, show upgrade prompt
|
|
174
|
-
if (!isLoading && !hasProAccess) {
|
|
175
|
-
return (
|
|
176
|
-
<Card className={cn("w-full", className)}>
|
|
177
|
-
<CardContent className="py-12 text-center">
|
|
178
|
-
<div className="max-w-md mx-auto space-y-4">
|
|
179
|
-
<div className="rounded-full bg-purple-100 dark:bg-purple-900/30 p-3 w-fit mx-auto">
|
|
180
|
-
<Lock className="h-6 w-6 text-purple-600 dark:text-purple-400" />
|
|
181
|
-
</div>
|
|
182
|
-
<div>
|
|
183
|
-
<h3 className="font-semibold text-lg mb-2">Pro Feature</h3>
|
|
184
|
-
<p className="text-muted-foreground text-sm mb-4">
|
|
185
|
-
Data Table is available exclusively to MoonUI Pro subscribers.
|
|
186
|
-
</p>
|
|
187
|
-
<div className="flex gap-3 justify-center">
|
|
188
|
-
<a href="/pricing">
|
|
189
|
-
<Button size="sm">
|
|
190
|
-
<Sparkles className="mr-2 h-4 w-4" />
|
|
191
|
-
Upgrade to Pro
|
|
192
|
-
</Button>
|
|
193
|
-
</a>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
</div>
|
|
197
|
-
</CardContent>
|
|
198
|
-
</Card>
|
|
199
|
-
)
|
|
200
|
-
}
|
|
201
|
-
const [sorting, setSorting] = React.useState<SortingState>([])
|
|
202
|
-
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
|
|
203
|
-
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
|
|
204
|
-
const [rowSelection, setRowSelection] = React.useState({})
|
|
205
|
-
const [globalFilter, setGlobalFilter] = React.useState('')
|
|
206
|
-
const [isPaginationLoading, setIsPaginationLoading] = React.useState(false)
|
|
207
|
-
const [internalExpandedRows, setInternalExpandedRows] = React.useState<Set<string>>(new Set())
|
|
208
|
-
const [filterDrawerOpen, setFilterDrawerOpen] = React.useState(false)
|
|
209
|
-
|
|
210
|
-
// Use controlled or internal expanded state
|
|
211
|
-
const expandedRows = controlledExpandedRows || internalExpandedRows
|
|
212
|
-
const setExpandedRows = onRowExpandChange ?
|
|
213
|
-
(newExpanded: Set<string>) => onRowExpandChange(newExpanded) :
|
|
214
|
-
setInternalExpandedRows
|
|
215
|
-
|
|
216
|
-
const actualPageSize = defaultPageSize || pageSize
|
|
217
|
-
|
|
218
|
-
// Memoize data to prevent unnecessary re-renders
|
|
219
|
-
const stableData = React.useMemo(() => data, [data])
|
|
220
|
-
|
|
221
|
-
const table = useReactTable({
|
|
222
|
-
data: stableData,
|
|
223
|
-
columns,
|
|
224
|
-
onSortingChange: onSortingChange !== undefined ? onSortingChange : setSorting,
|
|
225
|
-
onColumnFiltersChange: onColumnFiltersChange !== undefined ? onColumnFiltersChange : setColumnFilters,
|
|
226
|
-
getCoreRowModel: getCoreRowModel(),
|
|
227
|
-
getPaginationRowModel: getPaginationRowModel(),
|
|
228
|
-
getSortedRowModel: getSortedRowModel(),
|
|
229
|
-
getFilteredRowModel: getFilteredRowModel(),
|
|
230
|
-
onColumnVisibilityChange: setColumnVisibility,
|
|
231
|
-
onRowSelectionChange: setRowSelection,
|
|
232
|
-
onGlobalFilterChange: setGlobalFilter,
|
|
233
|
-
globalFilterFn: 'includesString',
|
|
234
|
-
filterFns: {
|
|
235
|
-
custom: (row, columnId, filterValue) => {
|
|
236
|
-
if (!filterValue?.custom || !filterValue?.filters) return true
|
|
237
|
-
|
|
238
|
-
const filters = filterValue.filters as FilterCondition[]
|
|
239
|
-
const matchAll = filterValue.matchAll !== undefined ? filterValue.matchAll : true
|
|
240
|
-
|
|
241
|
-
// Get all filter conditions (not just for this column)
|
|
242
|
-
const allFilterResults = filters.map(filterCondition => {
|
|
243
|
-
const cellValue = row.getValue(filterCondition.column)
|
|
244
|
-
const filterVal = filterCondition.value
|
|
245
|
-
|
|
246
|
-
switch (filterCondition.operator) {
|
|
247
|
-
case 'equals':
|
|
248
|
-
return cellValue === filterVal
|
|
249
|
-
case 'notEquals':
|
|
250
|
-
return cellValue !== filterVal
|
|
251
|
-
case 'contains':
|
|
252
|
-
return String(cellValue).toLowerCase().includes(String(filterVal).toLowerCase())
|
|
253
|
-
case 'notContains':
|
|
254
|
-
return !String(cellValue).toLowerCase().includes(String(filterVal).toLowerCase())
|
|
255
|
-
case 'startsWith':
|
|
256
|
-
return String(cellValue).toLowerCase().startsWith(String(filterVal).toLowerCase())
|
|
257
|
-
case 'endsWith':
|
|
258
|
-
return String(cellValue).toLowerCase().endsWith(String(filterVal).toLowerCase())
|
|
259
|
-
case 'greaterThan':
|
|
260
|
-
return Number(cellValue) > Number(filterVal)
|
|
261
|
-
case 'lessThan':
|
|
262
|
-
return Number(cellValue) < Number(filterVal)
|
|
263
|
-
case 'greaterThanOrEqual':
|
|
264
|
-
return Number(cellValue) >= Number(filterVal)
|
|
265
|
-
case 'lessThanOrEqual':
|
|
266
|
-
return Number(cellValue) <= Number(filterVal)
|
|
267
|
-
case 'isNull':
|
|
268
|
-
return cellValue == null || cellValue === ''
|
|
269
|
-
case 'isNotNull':
|
|
270
|
-
return cellValue != null && cellValue !== ''
|
|
271
|
-
default:
|
|
272
|
-
return true
|
|
273
|
-
}
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
// Apply match logic
|
|
277
|
-
if (matchAll) {
|
|
278
|
-
return allFilterResults.every(result => result)
|
|
279
|
-
} else {
|
|
280
|
-
return allFilterResults.some(result => result)
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
manualPagination,
|
|
285
|
-
pageCount,
|
|
286
|
-
state: {
|
|
287
|
-
sorting: externalState?.sorting ?? sorting,
|
|
288
|
-
columnFilters: externalState?.columnFilters ?? columnFilters,
|
|
289
|
-
columnVisibility: externalState?.columnVisibility ?? columnVisibility,
|
|
290
|
-
rowSelection: externalState?.rowSelection ?? rowSelection,
|
|
291
|
-
globalFilter: externalState?.globalFilter ?? globalFilter,
|
|
292
|
-
...(externalState || {}),
|
|
293
|
-
},
|
|
294
|
-
initialState: {
|
|
295
|
-
pagination: {
|
|
296
|
-
pageSize: actualPageSize,
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
// Prevent re-renders on state changes
|
|
300
|
-
autoResetAll: false,
|
|
301
|
-
autoResetPageIndex: false,
|
|
302
|
-
autoResetExpanded: false,
|
|
303
|
-
getRowId: (row: TData) => (row as any).id || (row as any).orderId || Math.random().toString(),
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
React.useEffect(() => {
|
|
307
|
-
if (onRowSelect && selectable) {
|
|
308
|
-
const selectedRows = table.getFilteredSelectedRowModel().rows.map(row => row.original)
|
|
309
|
-
onRowSelect(selectedRows)
|
|
310
|
-
}
|
|
311
|
-
}, [rowSelection, onRowSelect, selectable, table])
|
|
312
|
-
|
|
313
|
-
// Memoize row model to prevent unnecessary re-renders when only expanded state changes
|
|
314
|
-
const tableState = table.getState()
|
|
315
|
-
const rowModel = table.getRowModel()
|
|
316
|
-
|
|
317
|
-
// Use a ref to track if rows actually changed
|
|
318
|
-
const rowsRef = React.useRef(rowModel.rows)
|
|
319
|
-
const rowsChanged = React.useMemo(() => {
|
|
320
|
-
const changed = rowsRef.current !== rowModel.rows
|
|
321
|
-
if (changed) {
|
|
322
|
-
rowsRef.current = rowModel.rows
|
|
323
|
-
}
|
|
324
|
-
return changed
|
|
325
|
-
}, [rowModel.rows])
|
|
326
|
-
|
|
327
|
-
const rows = rowsRef.current
|
|
328
|
-
|
|
329
|
-
// Merge features with defaults
|
|
330
|
-
const enabledFeatures = {
|
|
331
|
-
sorting: features.sorting !== false,
|
|
332
|
-
filtering: features.filtering !== false || filterable,
|
|
333
|
-
pagination: features.pagination !== false || pagination,
|
|
334
|
-
search: features.search !== false || searchable,
|
|
335
|
-
columnVisibility: features.columnVisibility !== false,
|
|
336
|
-
rowSelection: features.rowSelection !== false || selectable,
|
|
337
|
-
export: features.export !== false || exportable,
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const handleExport = async (format: ExportFormat) => {
|
|
341
|
-
const selectedRows = table.getFilteredSelectedRowModel().rows
|
|
342
|
-
const dataToExport = selectedRows.length > 0
|
|
343
|
-
? selectedRows.map(row => row.original)
|
|
344
|
-
: table.getFilteredRowModel().rows.map(row => row.original)
|
|
345
|
-
|
|
346
|
-
// Use custom export handler if provided
|
|
347
|
-
if (typeof exportable === 'object' && exportable.onExport) {
|
|
348
|
-
exportable.onExport(dataToExport, format)
|
|
349
|
-
return
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Use legacy onExport if provided
|
|
353
|
-
if (onExport) {
|
|
354
|
-
onExport(dataToExport)
|
|
355
|
-
return
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Default export behavior
|
|
359
|
-
const filename = typeof exportable === 'object' && exportable.filename
|
|
360
|
-
? exportable.filename
|
|
361
|
-
: 'data-export'
|
|
362
|
-
|
|
363
|
-
const visibleColumns = getVisibleColumns(columns as any, columnVisibility)
|
|
364
|
-
|
|
365
|
-
await exportData(dataToExport as Record<string, any>[], {
|
|
366
|
-
format,
|
|
367
|
-
filename,
|
|
368
|
-
columns: visibleColumns,
|
|
369
|
-
includeHeaders: true
|
|
370
|
-
})
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Parse export options
|
|
374
|
-
const exportFormats: ExportFormat[] = React.useMemo(() => {
|
|
375
|
-
if (!exportable) return []
|
|
376
|
-
if (exportable === true) return ['csv', 'json']
|
|
377
|
-
if (typeof exportable === 'object' && exportable.formats) {
|
|
378
|
-
return exportable.formats
|
|
379
|
-
}
|
|
380
|
-
return ['csv', 'json']
|
|
381
|
-
}, [exportable])
|
|
382
|
-
|
|
383
|
-
const clearRowSelection = () => {
|
|
384
|
-
table.resetRowSelection()
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
return (
|
|
388
|
-
<div className={cn("moonui-pro-datatable-container flex flex-col gap-4", className)}>
|
|
389
|
-
{/* Toolbar */}
|
|
390
|
-
<div className="moonui-pro-datatable-toolbar flex items-center justify-between">
|
|
391
|
-
<div className="flex items-center space-x-2">
|
|
392
|
-
{searchable && (
|
|
393
|
-
<div className="relative">
|
|
394
|
-
<span suppressHydrationWarning>
|
|
395
|
-
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
|
|
396
|
-
</span>
|
|
397
|
-
<Input
|
|
398
|
-
placeholder={filterPlaceholder}
|
|
399
|
-
value={globalFilter}
|
|
400
|
-
onChange={(e) => setGlobalFilter(e.target.value)}
|
|
401
|
-
className="pl-8 w-64"
|
|
402
|
-
/>
|
|
403
|
-
</div>
|
|
404
|
-
)}
|
|
405
|
-
|
|
406
|
-
{filterable && (
|
|
407
|
-
<Button
|
|
408
|
-
variant="outline"
|
|
409
|
-
size="sm"
|
|
410
|
-
onClick={() => setFilterDrawerOpen(true)}
|
|
411
|
-
>
|
|
412
|
-
<span suppressHydrationWarning><Filter className="mr-2 h-4 w-4" /></span>
|
|
413
|
-
Filters
|
|
414
|
-
{columnFilters.length > 0 && (
|
|
415
|
-
<span className="ml-2 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground">
|
|
416
|
-
{columnFilters.length}
|
|
417
|
-
</span>
|
|
418
|
-
)}
|
|
419
|
-
</Button>
|
|
420
|
-
)}
|
|
421
|
-
|
|
422
|
-
{/* Bulk actions */}
|
|
423
|
-
{selectable && bulkActions.length > 0 && (
|
|
424
|
-
<DataTableBulkActions
|
|
425
|
-
selectedRows={table.getFilteredSelectedRowModel().rows.map(row => row.original)}
|
|
426
|
-
actions={bulkActions}
|
|
427
|
-
onClearSelection={clearRowSelection}
|
|
428
|
-
/>
|
|
429
|
-
)}
|
|
430
|
-
</div>
|
|
431
|
-
|
|
432
|
-
<div className="flex items-center space-x-2">
|
|
433
|
-
{/* Export dropdown */}
|
|
434
|
-
{exportable && exportFormats.length > 0 && (
|
|
435
|
-
<DropdownMenu>
|
|
436
|
-
<DropdownMenuTrigger asChild>
|
|
437
|
-
<Button variant="outline" size="sm">
|
|
438
|
-
<span suppressHydrationWarning><Download className="mr-2 h-4 w-4" /></span>
|
|
439
|
-
Export
|
|
440
|
-
</Button>
|
|
441
|
-
</DropdownMenuTrigger>
|
|
442
|
-
<DropdownMenuContent align="end">
|
|
443
|
-
{exportFormats.includes('csv') && (
|
|
444
|
-
<DropdownMenuItem onClick={() => handleExport('csv')}>
|
|
445
|
-
<FileSpreadsheet className="mr-2 h-4 w-4" />
|
|
446
|
-
Export as CSV
|
|
447
|
-
</DropdownMenuItem>
|
|
448
|
-
)}
|
|
449
|
-
{exportFormats.includes('json') && (
|
|
450
|
-
<DropdownMenuItem onClick={() => handleExport('json')}>
|
|
451
|
-
<FileJson className="mr-2 h-4 w-4" />
|
|
452
|
-
Export as JSON
|
|
453
|
-
</DropdownMenuItem>
|
|
454
|
-
)}
|
|
455
|
-
{exportFormats.includes('xlsx') && (
|
|
456
|
-
<DropdownMenuItem onClick={() => handleExport('xlsx')}>
|
|
457
|
-
<FileDown className="mr-2 h-4 w-4" />
|
|
458
|
-
Export as Excel
|
|
459
|
-
</DropdownMenuItem>
|
|
460
|
-
)}
|
|
461
|
-
</DropdownMenuContent>
|
|
462
|
-
</DropdownMenu>
|
|
463
|
-
)}
|
|
464
|
-
|
|
465
|
-
{/* Column visibility toggle */}
|
|
466
|
-
<DataTableColumnToggle table={table} />
|
|
467
|
-
</div>
|
|
468
|
-
</div>
|
|
469
|
-
|
|
470
|
-
{/* Table */}
|
|
471
|
-
<div className="moonui-pro-datatable-wrapper rounded-md border overflow-hidden" style={{ contain: 'layout style' }}>
|
|
472
|
-
<div style={{ overflowX: 'auto' }}>
|
|
473
|
-
<table className="moonui-pro-datatable" style={{ width: '100%', tableLayout: 'auto' }}>
|
|
474
|
-
<thead className="moonui-data-table-header">
|
|
475
|
-
{table.getHeaderGroups().map((headerGroup) => (
|
|
476
|
-
<tr key={headerGroup.id} className="moonui-data-table-row border-b">
|
|
477
|
-
{headerGroup.headers
|
|
478
|
-
.filter((header) => header.column.getIsVisible())
|
|
479
|
-
.map((header) => (
|
|
480
|
-
<th
|
|
481
|
-
key={header.id}
|
|
482
|
-
className="moonui-data-table-th h-12 px-4 text-left align-middle font-medium text-muted-foreground"
|
|
483
|
-
>
|
|
484
|
-
{header.isPlaceholder ? null : (
|
|
485
|
-
<div
|
|
486
|
-
className={cn(
|
|
487
|
-
"flex items-center space-x-2",
|
|
488
|
-
header.column.getCanSort() && "cursor-pointer select-none"
|
|
489
|
-
)}
|
|
490
|
-
onClick={header.column.getToggleSortingHandler()}
|
|
491
|
-
>
|
|
492
|
-
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
493
|
-
{header.column.getCanSort() && (
|
|
494
|
-
<div className="ml-2">
|
|
495
|
-
{header.column.getIsSorted() === 'asc' ? (
|
|
496
|
-
<span suppressHydrationWarning><ArrowUp className="h-4 w-4" /></span>
|
|
497
|
-
) : header.column.getIsSorted() === 'desc' ? (
|
|
498
|
-
<span suppressHydrationWarning><ArrowDown className="h-4 w-4" /></span>
|
|
499
|
-
) : (
|
|
500
|
-
<span suppressHydrationWarning><ArrowUpDown className="h-4 w-4" /></span>
|
|
501
|
-
)}
|
|
502
|
-
</div>
|
|
503
|
-
)}
|
|
504
|
-
</div>
|
|
505
|
-
)}
|
|
506
|
-
</th>
|
|
507
|
-
))}
|
|
508
|
-
</tr>
|
|
509
|
-
))}
|
|
510
|
-
</thead>
|
|
511
|
-
<tbody className="moonui-data-table-body">
|
|
512
|
-
{isPaginationLoading ? (
|
|
513
|
-
<motion.tr
|
|
514
|
-
key="loading"
|
|
515
|
-
initial={{ opacity: 0 }}
|
|
516
|
-
animate={{ opacity: 1 }}
|
|
517
|
-
exit={{ opacity: 0 }}
|
|
518
|
-
transition={{ duration: 0.2 }}
|
|
519
|
-
>
|
|
520
|
-
<td colSpan={table.getAllLeafColumns().filter(col => col.getIsVisible()).length} className="h-24 text-center">
|
|
521
|
-
<div className="flex items-center justify-center space-x-2">
|
|
522
|
-
<span suppressHydrationWarning><Loader2 className="h-4 w-4 animate-spin" /></span>
|
|
523
|
-
<span className="text-sm text-muted-foreground">Loading...</span>
|
|
524
|
-
</div>
|
|
525
|
-
</td>
|
|
526
|
-
</motion.tr>
|
|
527
|
-
) : rows?.length ? (
|
|
528
|
-
<>
|
|
529
|
-
{rows.map((row, index) => {
|
|
530
|
-
const rowId = (row.original as any).id || row.id
|
|
531
|
-
const isExpanded = enableExpandable && expandedRows.has(rowId)
|
|
532
|
-
|
|
533
|
-
return (
|
|
534
|
-
<TableRow
|
|
535
|
-
key={rowId}
|
|
536
|
-
row={row}
|
|
537
|
-
columns={columns}
|
|
538
|
-
isExpanded={isExpanded}
|
|
539
|
-
enableExpandable={enableExpandable}
|
|
540
|
-
renderSubComponent={renderSubComponent}
|
|
541
|
-
visibilityState={table.getState().columnVisibility}
|
|
542
|
-
/>
|
|
543
|
-
);
|
|
544
|
-
})}
|
|
545
|
-
</>
|
|
546
|
-
) : (
|
|
547
|
-
<motion.tr
|
|
548
|
-
key="no-results"
|
|
549
|
-
initial={{ opacity: 0 }}
|
|
550
|
-
animate={{ opacity: 1 }}
|
|
551
|
-
exit={{ opacity: 0 }}
|
|
552
|
-
transition={{ duration: 0.2 }}
|
|
553
|
-
>
|
|
554
|
-
<td colSpan={table.getAllLeafColumns().filter(col => col.getIsVisible()).length} className="h-24 text-center">
|
|
555
|
-
No results found.
|
|
556
|
-
</td>
|
|
557
|
-
</motion.tr>
|
|
558
|
-
)}
|
|
559
|
-
</tbody>
|
|
560
|
-
</table>
|
|
561
|
-
</div>
|
|
562
|
-
</div>
|
|
563
|
-
|
|
564
|
-
{/* Pagination */}
|
|
565
|
-
{pagination && (
|
|
566
|
-
<div className="flex items-center justify-between px-2">
|
|
567
|
-
<div className="flex-1 text-sm text-muted-foreground">
|
|
568
|
-
{selectable && table.getFilteredSelectedRowModel().rows.length > 0 && (
|
|
569
|
-
<span>
|
|
570
|
-
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
|
571
|
-
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
572
|
-
</span>
|
|
573
|
-
)}
|
|
574
|
-
</div>
|
|
575
|
-
<div className="flex items-center space-x-6 lg:space-x-8">
|
|
576
|
-
<div className="flex items-center space-x-2">
|
|
577
|
-
<p className="text-sm font-medium">Rows per page</p>
|
|
578
|
-
<select
|
|
579
|
-
value={table.getState().pagination.pageSize}
|
|
580
|
-
onChange={async (e) => {
|
|
581
|
-
setIsPaginationLoading(true)
|
|
582
|
-
await new Promise(resolve => setTimeout(resolve, 300))
|
|
583
|
-
table.setPageSize(Number(e.target.value))
|
|
584
|
-
setIsPaginationLoading(false)
|
|
585
|
-
}}
|
|
586
|
-
className="h-8 w-[70px] rounded border border-input bg-background px-3 py-1 text-sm"
|
|
587
|
-
disabled={isPaginationLoading}
|
|
588
|
-
>
|
|
589
|
-
{[10, 20, 30, 40, 50].map((pageSize) => (
|
|
590
|
-
<option key={pageSize} value={pageSize}>
|
|
591
|
-
{pageSize}
|
|
592
|
-
</option>
|
|
593
|
-
))}
|
|
594
|
-
</select>
|
|
595
|
-
</div>
|
|
596
|
-
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
|
597
|
-
Page {table.getState().pagination.pageIndex + 1} of{" "}
|
|
598
|
-
{table.getPageCount()}
|
|
599
|
-
</div>
|
|
600
|
-
<div className="flex items-center space-x-2">
|
|
601
|
-
<Button
|
|
602
|
-
variant="outline"
|
|
603
|
-
className="hidden h-8 w-8 p-0 lg:flex"
|
|
604
|
-
onClick={async () => {
|
|
605
|
-
if (onPaginationChange) {
|
|
606
|
-
onPaginationChange({ pageIndex: 0, pageSize: table.getState().pagination.pageSize })
|
|
607
|
-
} else {
|
|
608
|
-
setIsPaginationLoading(true)
|
|
609
|
-
await new Promise(resolve => setTimeout(resolve, 300))
|
|
610
|
-
table.setPageIndex(0)
|
|
611
|
-
setIsPaginationLoading(false)
|
|
612
|
-
}
|
|
613
|
-
}}
|
|
614
|
-
disabled={!table.getCanPreviousPage() || isPaginationLoading}
|
|
615
|
-
>
|
|
616
|
-
<span suppressHydrationWarning><ChevronsLeft className="h-4 w-4" /></span>
|
|
617
|
-
</Button>
|
|
618
|
-
<Button
|
|
619
|
-
variant="outline"
|
|
620
|
-
className="h-8 w-8 p-0"
|
|
621
|
-
onClick={async () => {
|
|
622
|
-
if (onPaginationChange) {
|
|
623
|
-
const currentIndex = table.getState().pagination.pageIndex
|
|
624
|
-
onPaginationChange({ pageIndex: currentIndex - 1, pageSize: table.getState().pagination.pageSize })
|
|
625
|
-
} else {
|
|
626
|
-
setIsPaginationLoading(true)
|
|
627
|
-
await new Promise(resolve => setTimeout(resolve, 300))
|
|
628
|
-
table.previousPage()
|
|
629
|
-
setIsPaginationLoading(false)
|
|
630
|
-
}
|
|
631
|
-
}}
|
|
632
|
-
disabled={!table.getCanPreviousPage() || isPaginationLoading}
|
|
633
|
-
>
|
|
634
|
-
<span suppressHydrationWarning><ChevronLeft className="h-4 w-4" /></span>
|
|
635
|
-
</Button>
|
|
636
|
-
<Button
|
|
637
|
-
variant="outline"
|
|
638
|
-
className="h-8 w-8 p-0"
|
|
639
|
-
onClick={async () => {
|
|
640
|
-
setIsPaginationLoading(true)
|
|
641
|
-
await new Promise(resolve => setTimeout(resolve, 300))
|
|
642
|
-
table.nextPage()
|
|
643
|
-
setIsPaginationLoading(false)
|
|
644
|
-
}}
|
|
645
|
-
disabled={!table.getCanNextPage() || isPaginationLoading}
|
|
646
|
-
>
|
|
647
|
-
<span suppressHydrationWarning><ChevronRight className="h-4 w-4" /></span>
|
|
648
|
-
</Button>
|
|
649
|
-
<Button
|
|
650
|
-
variant="outline"
|
|
651
|
-
className="hidden h-8 w-8 p-0 lg:flex"
|
|
652
|
-
onClick={async () => {
|
|
653
|
-
setIsPaginationLoading(true)
|
|
654
|
-
await new Promise(resolve => setTimeout(resolve, 300))
|
|
655
|
-
table.setPageIndex(table.getPageCount() - 1)
|
|
656
|
-
setIsPaginationLoading(false)
|
|
657
|
-
}}
|
|
658
|
-
disabled={!table.getCanNextPage() || isPaginationLoading}
|
|
659
|
-
>
|
|
660
|
-
<span suppressHydrationWarning><ChevronsRight className="h-4 w-4" /></span>
|
|
661
|
-
</Button>
|
|
662
|
-
</div>
|
|
663
|
-
</div>
|
|
664
|
-
</div>
|
|
665
|
-
)}
|
|
666
|
-
|
|
667
|
-
{/* Filter Drawer */}
|
|
668
|
-
{filterable && (
|
|
669
|
-
<DataTableFilterDrawer
|
|
670
|
-
table={table}
|
|
671
|
-
open={filterDrawerOpen}
|
|
672
|
-
onOpenChange={setFilterDrawerOpen}
|
|
673
|
-
/>
|
|
674
|
-
)}
|
|
675
|
-
</div>
|
|
676
|
-
)
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* Helper function to create an expandable column
|
|
681
|
-
* @param expandedRows - Set of expanded row IDs
|
|
682
|
-
* @param onToggle - Function to toggle row expansion
|
|
683
|
-
* @returns ColumnDef for expandable rows
|
|
684
|
-
*/
|
|
685
|
-
export function getExpandableColumn<TData>(
|
|
686
|
-
expandedRows: Set<string>,
|
|
687
|
-
onToggle: (id: string) => void
|
|
688
|
-
): ColumnDef<TData, any> {
|
|
689
|
-
return {
|
|
690
|
-
id: "expander",
|
|
691
|
-
header: () => null,
|
|
692
|
-
size: 50,
|
|
693
|
-
cell: ({ row }) => {
|
|
694
|
-
const rowId = (row.original as any).id || row.id;
|
|
695
|
-
const isExpanded = expandedRows.has(rowId);
|
|
696
|
-
|
|
697
|
-
return (
|
|
698
|
-
<button
|
|
699
|
-
onClick={(e) => {
|
|
700
|
-
e.stopPropagation();
|
|
701
|
-
onToggle(rowId);
|
|
702
|
-
}}
|
|
703
|
-
className="p-2 hover:bg-muted rounded-md transition-colors"
|
|
704
|
-
aria-label={isExpanded ? "Collapse row" : "Expand row"}
|
|
705
|
-
>
|
|
706
|
-
<span suppressHydrationWarning>
|
|
707
|
-
{isExpanded ? (
|
|
708
|
-
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
|
709
|
-
) : (
|
|
710
|
-
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
|
711
|
-
)}
|
|
712
|
-
</span>
|
|
713
|
-
</button>
|
|
714
|
-
);
|
|
715
|
-
},
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
/**
|
|
720
|
-
* Hook for managing expandable rows
|
|
721
|
-
* @param initialExpanded - Initial set of expanded row IDs
|
|
722
|
-
* @returns Object with expandedRows, toggleRow, and expandAll/collapseAll functions
|
|
723
|
-
*/
|
|
724
|
-
export function useExpandableRows(initialExpanded: Set<string> = new Set()) {
|
|
725
|
-
const [expandedRows, setExpandedRows] = React.useState<Set<string>>(initialExpanded);
|
|
726
|
-
|
|
727
|
-
const toggleRow = React.useCallback((id: string) => {
|
|
728
|
-
setExpandedRows(prev => {
|
|
729
|
-
const newExpanded = new Set(prev);
|
|
730
|
-
if (newExpanded.has(id)) {
|
|
731
|
-
newExpanded.delete(id);
|
|
732
|
-
} else {
|
|
733
|
-
newExpanded.add(id);
|
|
734
|
-
}
|
|
735
|
-
return newExpanded;
|
|
736
|
-
});
|
|
737
|
-
}, []);
|
|
738
|
-
|
|
739
|
-
const expandAll = React.useCallback((rowIds: string[]) => {
|
|
740
|
-
setExpandedRows(new Set(rowIds));
|
|
741
|
-
}, []);
|
|
742
|
-
|
|
743
|
-
const collapseAll = React.useCallback(() => {
|
|
744
|
-
setExpandedRows(new Set());
|
|
745
|
-
}, []);
|
|
746
|
-
|
|
747
|
-
return {
|
|
748
|
-
expandedRows,
|
|
749
|
-
setExpandedRows,
|
|
750
|
-
toggleRow,
|
|
751
|
-
expandAll,
|
|
752
|
-
collapseAll,
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// Memoized table row component
|
|
757
|
-
interface TableRowProps {
|
|
758
|
-
row: Row<any>
|
|
759
|
-
columns: ColumnDef<any, any>[]
|
|
760
|
-
isExpanded: boolean
|
|
761
|
-
enableExpandable: boolean
|
|
762
|
-
renderSubComponent?: (props: { row: { original: any; id: string } }) => React.ReactNode
|
|
763
|
-
visibilityState: Record<string, boolean>
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
const TableRow = React.memo(({
|
|
767
|
-
row,
|
|
768
|
-
columns,
|
|
769
|
-
isExpanded,
|
|
770
|
-
enableExpandable,
|
|
771
|
-
renderSubComponent,
|
|
772
|
-
visibilityState
|
|
773
|
-
}: TableRowProps) => {
|
|
774
|
-
const rowId = (row.original as any).id || row.id
|
|
775
|
-
|
|
776
|
-
return (
|
|
777
|
-
<>
|
|
778
|
-
<tr
|
|
779
|
-
className={cn(
|
|
780
|
-
"border-b transition-colors hover:bg-muted/50",
|
|
781
|
-
row.getIsSelected() && "bg-muted",
|
|
782
|
-
isExpanded && "border-b-0"
|
|
783
|
-
)}
|
|
784
|
-
>
|
|
785
|
-
{row.getAllCells()
|
|
786
|
-
.filter((cell) => {
|
|
787
|
-
// Manual visibility check
|
|
788
|
-
const isVisible = visibilityState[cell.column.id] !== false;
|
|
789
|
-
return isVisible;
|
|
790
|
-
})
|
|
791
|
-
.map((cell) => {
|
|
792
|
-
return (
|
|
793
|
-
<td key={cell.id} className="moonui-data-table-td p-4 align-middle">
|
|
794
|
-
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
795
|
-
</td>
|
|
796
|
-
);
|
|
797
|
-
})}
|
|
798
|
-
</tr>
|
|
799
|
-
|
|
800
|
-
{isExpanded && renderSubComponent && (
|
|
801
|
-
<tr className="border-b">
|
|
802
|
-
<td colSpan={row.getAllCells().filter(cell =>
|
|
803
|
-
visibilityState[cell.column.id] !== false
|
|
804
|
-
).length || 1} className="p-0 overflow-hidden">
|
|
805
|
-
<div
|
|
806
|
-
className="transition-all duration-300 ease-out"
|
|
807
|
-
style={{
|
|
808
|
-
maxHeight: isExpanded ? '1000px' : '0',
|
|
809
|
-
opacity: isExpanded ? 1 : 0,
|
|
810
|
-
}}
|
|
811
|
-
>
|
|
812
|
-
<div className="border-t border-border/50">
|
|
813
|
-
{renderSubComponent({ row: { original: row.original, id: rowId } })}
|
|
814
|
-
</div>
|
|
815
|
-
</div>
|
|
816
|
-
</td>
|
|
817
|
-
</tr>
|
|
818
|
-
)}
|
|
819
|
-
</>
|
|
820
|
-
)
|
|
821
|
-
}, (prevProps, nextProps) => {
|
|
822
|
-
// Custom comparison - only re-render if row data, expanded state, or visibility changed
|
|
823
|
-
const prevRowId = (prevProps.row.original as any).id || prevProps.row.id
|
|
824
|
-
const nextRowId = (nextProps.row.original as any).id || nextProps.row.id
|
|
825
|
-
|
|
826
|
-
// Include visibility state in comparison
|
|
827
|
-
const prevVisibilityKeys = Object.keys(prevProps.visibilityState).sort().join(',')
|
|
828
|
-
const nextVisibilityKeys = Object.keys(nextProps.visibilityState).sort().join(',')
|
|
829
|
-
const prevVisibilityValues = Object.values(prevProps.visibilityState).join(',')
|
|
830
|
-
const nextVisibilityValues = Object.values(nextProps.visibilityState).join(',')
|
|
831
|
-
|
|
832
|
-
return prevRowId === nextRowId &&
|
|
833
|
-
prevProps.isExpanded === nextProps.isExpanded &&
|
|
834
|
-
prevProps.row.getIsSelected() === nextProps.row.getIsSelected() &&
|
|
835
|
-
prevVisibilityKeys === nextVisibilityKeys &&
|
|
836
|
-
prevVisibilityValues === nextVisibilityValues
|
|
837
|
-
})
|
|
838
|
-
|
|
839
|
-
TableRow.displayName = 'TableRow'
|
|
840
|
-
|
|
841
|
-
// Re-export types for convenience
|
|
842
|
-
export { type ColumnDef } from "@tanstack/react-table";
|
|
843
|
-
export type { BulkAction } from './data-table-bulk-actions';
|
|
844
|
-
export type { ExportFormat } from './data-table-export';
|
|
845
|
-
export type { FilterCondition, FilterOperator } from './data-table-filter-drawer';
|