@moontra/moonui-pro 2.3.8 → 2.4.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/dist/index.d.ts +30 -3
- package/dist/index.mjs +1045 -91
- package/package.json +4 -3
- package/scripts/postinstall.js +26 -0
- package/src/components/data-table/data-table-bulk-actions.tsx +204 -0
- package/src/components/data-table/data-table-column-toggle.tsx +166 -0
- package/src/components/data-table/data-table-export.ts +156 -0
- package/src/components/data-table/data-table-filter-drawer.tsx +442 -0
- package/src/components/data-table/index.tsx +218 -87
- package/src/components/ui/alert-dialog.tsx +141 -0
|
@@ -33,18 +33,35 @@ import {
|
|
|
33
33
|
Settings,
|
|
34
34
|
Lock,
|
|
35
35
|
Sparkles,
|
|
36
|
-
Loader2
|
|
36
|
+
Loader2,
|
|
37
|
+
FileDown,
|
|
38
|
+
FileJson,
|
|
39
|
+
FileSpreadsheet
|
|
37
40
|
} from 'lucide-react'
|
|
38
41
|
import { cn } from '../../lib/utils'
|
|
39
42
|
import { useSubscription } from '../../hooks/use-subscription'
|
|
40
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 } from './data-table-filter-drawer'
|
|
48
|
+
import {
|
|
49
|
+
DropdownMenu,
|
|
50
|
+
DropdownMenuContent,
|
|
51
|
+
DropdownMenuItem,
|
|
52
|
+
DropdownMenuTrigger,
|
|
53
|
+
} from '../ui/dropdown-menu'
|
|
41
54
|
|
|
42
55
|
interface DataTableProps<TData, TValue> {
|
|
43
56
|
columns: ColumnDef<TData, TValue>[]
|
|
44
57
|
data: TData[]
|
|
45
58
|
searchable?: boolean
|
|
46
59
|
filterable?: boolean
|
|
47
|
-
exportable?: boolean
|
|
60
|
+
exportable?: boolean | {
|
|
61
|
+
formats?: ExportFormat[]
|
|
62
|
+
filename?: string
|
|
63
|
+
onExport?: (data: TData[], format: ExportFormat) => void
|
|
64
|
+
}
|
|
48
65
|
selectable?: boolean
|
|
49
66
|
pagination?: boolean
|
|
50
67
|
pageSize?: number
|
|
@@ -55,6 +72,7 @@ interface DataTableProps<TData, TValue> {
|
|
|
55
72
|
renderSubComponent?: (props: { row: { original: TData; id: string } }) => React.ReactNode
|
|
56
73
|
expandedRows?: Set<string>
|
|
57
74
|
onRowExpandChange?: (expandedRows: Set<string>) => void
|
|
75
|
+
bulkActions?: BulkAction<TData>[]
|
|
58
76
|
// Additional props for compatibility
|
|
59
77
|
enableSorting?: boolean
|
|
60
78
|
enableFiltering?: boolean
|
|
@@ -116,6 +134,7 @@ export function DataTable<TData, TValue>({
|
|
|
116
134
|
renderSubComponent,
|
|
117
135
|
expandedRows: controlledExpandedRows,
|
|
118
136
|
onRowExpandChange,
|
|
137
|
+
bulkActions = [],
|
|
119
138
|
features = {},
|
|
120
139
|
theme = {},
|
|
121
140
|
texts = {},
|
|
@@ -174,6 +193,7 @@ export function DataTable<TData, TValue>({
|
|
|
174
193
|
const [globalFilter, setGlobalFilter] = React.useState('')
|
|
175
194
|
const [isPaginationLoading, setIsPaginationLoading] = React.useState(false)
|
|
176
195
|
const [internalExpandedRows, setInternalExpandedRows] = React.useState<Set<string>>(new Set())
|
|
196
|
+
const [filterDrawerOpen, setFilterDrawerOpen] = React.useState(false)
|
|
177
197
|
|
|
178
198
|
// Use controlled or internal expanded state
|
|
179
199
|
const expandedRows = controlledExpandedRows || internalExpandedRows
|
|
@@ -213,6 +233,11 @@ export function DataTable<TData, TValue>({
|
|
|
213
233
|
pageSize: actualPageSize,
|
|
214
234
|
},
|
|
215
235
|
},
|
|
236
|
+
// Prevent re-renders on state changes
|
|
237
|
+
autoResetAll: false,
|
|
238
|
+
autoResetPageIndex: false,
|
|
239
|
+
autoResetExpanded: false,
|
|
240
|
+
getRowId: (row: TData) => (row as any).id || (row as any).orderId || Math.random().toString(),
|
|
216
241
|
})
|
|
217
242
|
|
|
218
243
|
React.useEffect(() => {
|
|
@@ -224,11 +249,19 @@ export function DataTable<TData, TValue>({
|
|
|
224
249
|
|
|
225
250
|
// Memoize row model to prevent unnecessary re-renders when only expanded state changes
|
|
226
251
|
const tableState = table.getState()
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)
|
|
252
|
+
const rowModel = table.getRowModel()
|
|
253
|
+
|
|
254
|
+
// Use a ref to track if rows actually changed
|
|
255
|
+
const rowsRef = React.useRef(rowModel.rows)
|
|
256
|
+
const rowsChanged = React.useMemo(() => {
|
|
257
|
+
const changed = rowsRef.current !== rowModel.rows
|
|
258
|
+
if (changed) {
|
|
259
|
+
rowsRef.current = rowModel.rows
|
|
260
|
+
}
|
|
261
|
+
return changed
|
|
262
|
+
}, [rowModel.rows])
|
|
263
|
+
|
|
264
|
+
const rows = rowsRef.current
|
|
232
265
|
|
|
233
266
|
// Merge features with defaults
|
|
234
267
|
const enabledFeatures = {
|
|
@@ -241,14 +274,51 @@ export function DataTable<TData, TValue>({
|
|
|
241
274
|
export: features.export !== false || exportable,
|
|
242
275
|
}
|
|
243
276
|
|
|
244
|
-
const handleExport = () => {
|
|
277
|
+
const handleExport = async (format: ExportFormat) => {
|
|
278
|
+
const selectedRows = table.getFilteredSelectedRowModel().rows
|
|
279
|
+
const dataToExport = selectedRows.length > 0
|
|
280
|
+
? selectedRows.map(row => row.original)
|
|
281
|
+
: table.getFilteredRowModel().rows.map(row => row.original)
|
|
282
|
+
|
|
283
|
+
// Use custom export handler if provided
|
|
284
|
+
if (typeof exportable === 'object' && exportable.onExport) {
|
|
285
|
+
exportable.onExport(dataToExport, format)
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Use legacy onExport if provided
|
|
245
290
|
if (onExport) {
|
|
246
|
-
const selectedRows = table.getFilteredSelectedRowModel().rows
|
|
247
|
-
const dataToExport = selectedRows.length > 0
|
|
248
|
-
? selectedRows.map(row => row.original)
|
|
249
|
-
: table.getFilteredRowModel().rows.map(row => row.original)
|
|
250
291
|
onExport(dataToExport)
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Default export behavior
|
|
296
|
+
const filename = typeof exportable === 'object' && exportable.filename
|
|
297
|
+
? exportable.filename
|
|
298
|
+
: 'data-export'
|
|
299
|
+
|
|
300
|
+
const visibleColumns = getVisibleColumns(columns as any, columnVisibility)
|
|
301
|
+
|
|
302
|
+
await exportData(dataToExport as Record<string, any>[], {
|
|
303
|
+
format,
|
|
304
|
+
filename,
|
|
305
|
+
columns: visibleColumns,
|
|
306
|
+
includeHeaders: true
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Parse export options
|
|
311
|
+
const exportFormats: ExportFormat[] = React.useMemo(() => {
|
|
312
|
+
if (!exportable) return []
|
|
313
|
+
if (exportable === true) return ['csv', 'json']
|
|
314
|
+
if (typeof exportable === 'object' && exportable.formats) {
|
|
315
|
+
return exportable.formats
|
|
251
316
|
}
|
|
317
|
+
return ['csv', 'json']
|
|
318
|
+
}, [exportable])
|
|
319
|
+
|
|
320
|
+
const clearRowSelection = () => {
|
|
321
|
+
table.resetRowSelection()
|
|
252
322
|
}
|
|
253
323
|
|
|
254
324
|
return (
|
|
@@ -271,25 +341,66 @@ export function DataTable<TData, TValue>({
|
|
|
271
341
|
)}
|
|
272
342
|
|
|
273
343
|
{filterable && (
|
|
274
|
-
<Button
|
|
344
|
+
<Button
|
|
345
|
+
variant="outline"
|
|
346
|
+
size="sm"
|
|
347
|
+
onClick={() => setFilterDrawerOpen(true)}
|
|
348
|
+
>
|
|
275
349
|
<span suppressHydrationWarning><Filter className="mr-2 h-4 w-4" /></span>
|
|
276
350
|
Filters
|
|
351
|
+
{columnFilters.length > 0 && (
|
|
352
|
+
<span className="ml-2 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground">
|
|
353
|
+
{columnFilters.length}
|
|
354
|
+
</span>
|
|
355
|
+
)}
|
|
277
356
|
</Button>
|
|
278
357
|
)}
|
|
358
|
+
|
|
359
|
+
{/* Bulk actions */}
|
|
360
|
+
{selectable && bulkActions.length > 0 && (
|
|
361
|
+
<DataTableBulkActions
|
|
362
|
+
selectedRows={table.getFilteredSelectedRowModel().rows.map(row => row.original)}
|
|
363
|
+
actions={bulkActions}
|
|
364
|
+
onClearSelection={clearRowSelection}
|
|
365
|
+
/>
|
|
366
|
+
)}
|
|
279
367
|
</div>
|
|
280
368
|
|
|
281
369
|
<div className="flex items-center space-x-2">
|
|
282
|
-
{
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
370
|
+
{/* Export dropdown */}
|
|
371
|
+
{exportable && exportFormats.length > 0 && (
|
|
372
|
+
<DropdownMenu>
|
|
373
|
+
<DropdownMenuTrigger asChild>
|
|
374
|
+
<Button variant="outline" size="sm">
|
|
375
|
+
<span suppressHydrationWarning><Download className="mr-2 h-4 w-4" /></span>
|
|
376
|
+
Export
|
|
377
|
+
</Button>
|
|
378
|
+
</DropdownMenuTrigger>
|
|
379
|
+
<DropdownMenuContent align="end">
|
|
380
|
+
{exportFormats.includes('csv') && (
|
|
381
|
+
<DropdownMenuItem onClick={() => handleExport('csv')}>
|
|
382
|
+
<FileSpreadsheet className="mr-2 h-4 w-4" />
|
|
383
|
+
Export as CSV
|
|
384
|
+
</DropdownMenuItem>
|
|
385
|
+
)}
|
|
386
|
+
{exportFormats.includes('json') && (
|
|
387
|
+
<DropdownMenuItem onClick={() => handleExport('json')}>
|
|
388
|
+
<FileJson className="mr-2 h-4 w-4" />
|
|
389
|
+
Export as JSON
|
|
390
|
+
</DropdownMenuItem>
|
|
391
|
+
)}
|
|
392
|
+
{exportFormats.includes('xlsx') && (
|
|
393
|
+
<DropdownMenuItem onClick={() => handleExport('xlsx')}>
|
|
394
|
+
<FileDown className="mr-2 h-4 w-4" />
|
|
395
|
+
Export as Excel
|
|
396
|
+
</DropdownMenuItem>
|
|
397
|
+
)}
|
|
398
|
+
</DropdownMenuContent>
|
|
399
|
+
</DropdownMenu>
|
|
287
400
|
)}
|
|
288
401
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
Columns
|
|
292
|
-
</Button>
|
|
402
|
+
{/* Column visibility toggle */}
|
|
403
|
+
<DataTableColumnToggle table={table} />
|
|
293
404
|
</div>
|
|
294
405
|
</div>
|
|
295
406
|
|
|
@@ -355,71 +466,14 @@ export function DataTable<TData, TValue>({
|
|
|
355
466
|
const isExpanded = enableExpandable && expandedRows.has(rowId)
|
|
356
467
|
|
|
357
468
|
return (
|
|
358
|
-
<
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
{row.getVisibleCells().map((cell) => (
|
|
367
|
-
<td key={cell.id} className="moonui-data-table-td p-4 align-middle">
|
|
368
|
-
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
369
|
-
</td>
|
|
370
|
-
))}
|
|
371
|
-
</tr>
|
|
372
|
-
|
|
373
|
-
<AnimatePresence initial={false}>
|
|
374
|
-
{isExpanded && renderSubComponent && (
|
|
375
|
-
<motion.tr
|
|
376
|
-
key={`${row.id}-expanded`}
|
|
377
|
-
initial={{ height: 0, opacity: 0 }}
|
|
378
|
-
animate={{
|
|
379
|
-
height: "auto",
|
|
380
|
-
opacity: 1,
|
|
381
|
-
transition: {
|
|
382
|
-
height: {
|
|
383
|
-
duration: 0.3,
|
|
384
|
-
ease: "easeOut"
|
|
385
|
-
},
|
|
386
|
-
opacity: {
|
|
387
|
-
duration: 0.2,
|
|
388
|
-
delay: 0.1
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}}
|
|
392
|
-
exit={{
|
|
393
|
-
height: 0,
|
|
394
|
-
opacity: 0,
|
|
395
|
-
transition: {
|
|
396
|
-
height: {
|
|
397
|
-
duration: 0.3,
|
|
398
|
-
ease: "easeIn"
|
|
399
|
-
},
|
|
400
|
-
opacity: {
|
|
401
|
-
duration: 0.2
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}}
|
|
405
|
-
style={{ overflow: "hidden" }}
|
|
406
|
-
className="border-b"
|
|
407
|
-
>
|
|
408
|
-
<td colSpan={columns.length} className="p-0">
|
|
409
|
-
<motion.div
|
|
410
|
-
initial={{ y: -10 }}
|
|
411
|
-
animate={{ y: 0 }}
|
|
412
|
-
exit={{ y: -10 }}
|
|
413
|
-
transition={{ duration: 0.2 }}
|
|
414
|
-
className="border-t border-border/50"
|
|
415
|
-
>
|
|
416
|
-
{renderSubComponent({ row: { original: row.original, id: rowId } })}
|
|
417
|
-
</motion.div>
|
|
418
|
-
</td>
|
|
419
|
-
</motion.tr>
|
|
420
|
-
)}
|
|
421
|
-
</AnimatePresence>
|
|
422
|
-
</React.Fragment>
|
|
469
|
+
<TableRow
|
|
470
|
+
key={rowId}
|
|
471
|
+
row={row}
|
|
472
|
+
columns={columns}
|
|
473
|
+
isExpanded={isExpanded}
|
|
474
|
+
enableExpandable={enableExpandable}
|
|
475
|
+
renderSubComponent={renderSubComponent}
|
|
476
|
+
/>
|
|
423
477
|
);
|
|
424
478
|
})}
|
|
425
479
|
</>
|
|
@@ -543,6 +597,15 @@ export function DataTable<TData, TValue>({
|
|
|
543
597
|
</div>
|
|
544
598
|
</div>
|
|
545
599
|
)}
|
|
600
|
+
|
|
601
|
+
{/* Filter Drawer */}
|
|
602
|
+
{filterable && (
|
|
603
|
+
<DataTableFilterDrawer
|
|
604
|
+
table={table}
|
|
605
|
+
open={filterDrawerOpen}
|
|
606
|
+
onOpenChange={setFilterDrawerOpen}
|
|
607
|
+
/>
|
|
608
|
+
)}
|
|
546
609
|
</div>
|
|
547
610
|
)
|
|
548
611
|
}
|
|
@@ -624,5 +687,73 @@ export function useExpandableRows(initialExpanded: Set<string> = new Set()) {
|
|
|
624
687
|
};
|
|
625
688
|
}
|
|
626
689
|
|
|
690
|
+
// Memoized table row component
|
|
691
|
+
interface TableRowProps {
|
|
692
|
+
row: Row<any>
|
|
693
|
+
columns: ColumnDef<any, any>[]
|
|
694
|
+
isExpanded: boolean
|
|
695
|
+
enableExpandable: boolean
|
|
696
|
+
renderSubComponent?: (props: { row: { original: any; id: string } }) => React.ReactNode
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const TableRow = React.memo(({
|
|
700
|
+
row,
|
|
701
|
+
columns,
|
|
702
|
+
isExpanded,
|
|
703
|
+
enableExpandable,
|
|
704
|
+
renderSubComponent
|
|
705
|
+
}: TableRowProps) => {
|
|
706
|
+
const rowId = (row.original as any).id || row.id
|
|
707
|
+
|
|
708
|
+
return (
|
|
709
|
+
<>
|
|
710
|
+
<tr
|
|
711
|
+
className={cn(
|
|
712
|
+
"border-b transition-colors hover:bg-muted/50",
|
|
713
|
+
row.getIsSelected() && "bg-muted",
|
|
714
|
+
isExpanded && "border-b-0"
|
|
715
|
+
)}
|
|
716
|
+
>
|
|
717
|
+
{row.getVisibleCells().map((cell) => (
|
|
718
|
+
<td key={cell.id} className="moonui-data-table-td p-4 align-middle">
|
|
719
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
720
|
+
</td>
|
|
721
|
+
))}
|
|
722
|
+
</tr>
|
|
723
|
+
|
|
724
|
+
{isExpanded && renderSubComponent && (
|
|
725
|
+
<tr className="border-b">
|
|
726
|
+
<td colSpan={columns.length} className="p-0 overflow-hidden">
|
|
727
|
+
<div
|
|
728
|
+
className="transition-all duration-300 ease-out"
|
|
729
|
+
style={{
|
|
730
|
+
maxHeight: isExpanded ? '1000px' : '0',
|
|
731
|
+
opacity: isExpanded ? 1 : 0,
|
|
732
|
+
}}
|
|
733
|
+
>
|
|
734
|
+
<div className="border-t border-border/50">
|
|
735
|
+
{renderSubComponent({ row: { original: row.original, id: rowId } })}
|
|
736
|
+
</div>
|
|
737
|
+
</div>
|
|
738
|
+
</td>
|
|
739
|
+
</tr>
|
|
740
|
+
)}
|
|
741
|
+
</>
|
|
742
|
+
)
|
|
743
|
+
}, (prevProps, nextProps) => {
|
|
744
|
+
// Custom comparison - only re-render if row data or expanded state changed
|
|
745
|
+
const prevRowId = (prevProps.row.original as any).id || prevProps.row.id
|
|
746
|
+
const nextRowId = (nextProps.row.original as any).id || nextProps.row.id
|
|
747
|
+
|
|
748
|
+
return prevRowId === nextRowId &&
|
|
749
|
+
prevProps.isExpanded === nextProps.isExpanded &&
|
|
750
|
+
prevProps.row.getIsSelected() === nextProps.row.getIsSelected()
|
|
751
|
+
})
|
|
752
|
+
|
|
753
|
+
TableRow.displayName = 'TableRow'
|
|
754
|
+
|
|
627
755
|
// Re-export types for convenience
|
|
628
756
|
export { type ColumnDef } from "@tanstack/react-table";
|
|
757
|
+
export type { BulkAction } from './data-table-bulk-actions';
|
|
758
|
+
export type { ExportFormat } from './data-table-export';
|
|
759
|
+
export type { FilterCondition, FilterOperator } from './data-table-filter-drawer';
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
import { buttonVariants } from "./button"
|
|
8
|
+
|
|
9
|
+
const AlertDialog = AlertDialogPrimitive.Root
|
|
10
|
+
|
|
11
|
+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
|
12
|
+
|
|
13
|
+
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
|
14
|
+
|
|
15
|
+
const AlertDialogOverlay = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
|
18
|
+
>(({ className, ...props }, ref) => (
|
|
19
|
+
<AlertDialogPrimitive.Overlay
|
|
20
|
+
className={cn(
|
|
21
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
ref={ref}
|
|
26
|
+
/>
|
|
27
|
+
))
|
|
28
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
|
|
29
|
+
|
|
30
|
+
const AlertDialogContent = React.forwardRef<
|
|
31
|
+
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
|
32
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
|
33
|
+
>(({ className, ...props }, ref) => (
|
|
34
|
+
<AlertDialogPortal>
|
|
35
|
+
<AlertDialogOverlay />
|
|
36
|
+
<AlertDialogPrimitive.Content
|
|
37
|
+
ref={ref}
|
|
38
|
+
className={cn(
|
|
39
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
40
|
+
className
|
|
41
|
+
)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
</AlertDialogPortal>
|
|
45
|
+
))
|
|
46
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
|
47
|
+
|
|
48
|
+
const AlertDialogHeader = ({
|
|
49
|
+
className,
|
|
50
|
+
...props
|
|
51
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
52
|
+
<div
|
|
53
|
+
className={cn(
|
|
54
|
+
"flex flex-col space-y-2 text-center sm:text-left",
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
AlertDialogHeader.displayName = "AlertDialogHeader"
|
|
61
|
+
|
|
62
|
+
const AlertDialogFooter = ({
|
|
63
|
+
className,
|
|
64
|
+
...props
|
|
65
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
66
|
+
<div
|
|
67
|
+
className={cn(
|
|
68
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
AlertDialogFooter.displayName = "AlertDialogFooter"
|
|
75
|
+
|
|
76
|
+
const AlertDialogTitle = React.forwardRef<
|
|
77
|
+
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
|
78
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
|
79
|
+
>(({ className, ...props }, ref) => (
|
|
80
|
+
<AlertDialogPrimitive.Title
|
|
81
|
+
ref={ref}
|
|
82
|
+
className={cn("text-lg font-semibold", className)}
|
|
83
|
+
{...props}
|
|
84
|
+
/>
|
|
85
|
+
))
|
|
86
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
|
|
87
|
+
|
|
88
|
+
const AlertDialogDescription = React.forwardRef<
|
|
89
|
+
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
|
90
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
|
91
|
+
>(({ className, ...props }, ref) => (
|
|
92
|
+
<AlertDialogPrimitive.Description
|
|
93
|
+
ref={ref}
|
|
94
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
95
|
+
{...props}
|
|
96
|
+
/>
|
|
97
|
+
))
|
|
98
|
+
AlertDialogDescription.displayName =
|
|
99
|
+
AlertDialogPrimitive.Description.displayName
|
|
100
|
+
|
|
101
|
+
const AlertDialogAction = React.forwardRef<
|
|
102
|
+
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
|
103
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
|
104
|
+
>(({ className, ...props }, ref) => (
|
|
105
|
+
<AlertDialogPrimitive.Action
|
|
106
|
+
ref={ref}
|
|
107
|
+
className={cn(buttonVariants(), className)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
))
|
|
111
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
|
112
|
+
|
|
113
|
+
const AlertDialogCancel = React.forwardRef<
|
|
114
|
+
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
|
115
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
|
116
|
+
>(({ className, ...props }, ref) => (
|
|
117
|
+
<AlertDialogPrimitive.Cancel
|
|
118
|
+
ref={ref}
|
|
119
|
+
className={cn(
|
|
120
|
+
buttonVariants({ variant: "outline" }),
|
|
121
|
+
"mt-2 sm:mt-0",
|
|
122
|
+
className
|
|
123
|
+
)}
|
|
124
|
+
{...props}
|
|
125
|
+
/>
|
|
126
|
+
))
|
|
127
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
|
128
|
+
|
|
129
|
+
export {
|
|
130
|
+
AlertDialog,
|
|
131
|
+
AlertDialogPortal,
|
|
132
|
+
AlertDialogOverlay,
|
|
133
|
+
AlertDialogTrigger,
|
|
134
|
+
AlertDialogContent,
|
|
135
|
+
AlertDialogHeader,
|
|
136
|
+
AlertDialogFooter,
|
|
137
|
+
AlertDialogTitle,
|
|
138
|
+
AlertDialogDescription,
|
|
139
|
+
AlertDialogAction,
|
|
140
|
+
AlertDialogCancel,
|
|
141
|
+
}
|