@turtleclub/ui 0.7.0-beta.32 → 0.7.0-beta.34

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.
Files changed (139) hide show
  1. package/dist/index.cjs +10331 -110
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +7652 -47844
  4. package/dist/index.js.map +1 -1
  5. package/dist/types/components/features/sidebar-layout.d.ts +2 -0
  6. package/dist/types/components/features/sidebar-layout.d.ts.map +1 -1
  7. package/dist/types/components/ui/chart.d.ts +1 -1
  8. package/dist/types/components/ui/chart.d.ts.map +1 -1
  9. package/package.json +26 -22
  10. package/.prettierrc.json +0 -4
  11. package/.turbo/turbo-build.log +0 -182
  12. package/CHANGELOG.md +0 -795
  13. package/components.json +0 -21
  14. package/src/components/charts/QUICK_REFERENCE.md +0 -323
  15. package/src/components/charts/README.md +0 -658
  16. package/src/components/charts/RECHARTS_FEATURES.md +0 -458
  17. package/src/components/charts/area-chart.tsx +0 -248
  18. package/src/components/charts/bar-chart.tsx +0 -362
  19. package/src/components/charts/index.ts +0 -4
  20. package/src/components/charts/pie-chart.tsx +0 -277
  21. package/src/components/charts/radial-chart.tsx +0 -312
  22. package/src/components/features/api-status/index.tsx +0 -23
  23. package/src/components/features/data-table/data-table.tsx +0 -538
  24. package/src/components/features/data-table/expand-toggle.tsx +0 -17
  25. package/src/components/features/data-table/fuzzy-filter.tsx +0 -34
  26. package/src/components/features/data-table/index.ts +0 -3
  27. package/src/components/features/data-table/item-info.tsx +0 -19
  28. package/src/components/features/data-table/skeleton.tsx +0 -23
  29. package/src/components/features/data-table/sort-dropdown.tsx +0 -118
  30. package/src/components/features/data-table/sortable-header.tsx +0 -37
  31. package/src/components/features/index.ts +0 -6
  32. package/src/components/features/page-heading.tsx +0 -27
  33. package/src/components/features/search-bar.tsx +0 -55
  34. package/src/components/features/segmented-navigation.tsx +0 -18
  35. package/src/components/features/sidebar-layout.tsx +0 -193
  36. package/src/components/features/turtle-tooltip.tsx +0 -67
  37. package/src/components/icons/arrow.tsx +0 -23
  38. package/src/components/icons/beta.tsx +0 -95
  39. package/src/components/icons/dot.tsx +0 -102
  40. package/src/components/icons/index.ts +0 -7
  41. package/src/components/icons/issue.tsx +0 -106
  42. package/src/components/icons/turtle.tsx +0 -156
  43. package/src/components/icons/update.tsx +0 -113
  44. package/src/components/icons/warning.tsx +0 -95
  45. package/src/components/molecules/index.ts +0 -9
  46. package/src/components/molecules/opportunity/index.ts +0 -10
  47. package/src/components/molecules/opportunity/opportunity-apr.tsx +0 -129
  48. package/src/components/molecules/opportunity/opportunity-disclaimer.tsx +0 -46
  49. package/src/components/molecules/opportunity/opportunity-rate-estimator.tsx +0 -62
  50. package/src/components/molecules/opportunity/opportunity-section.tsx +0 -113
  51. package/src/components/molecules/opportunity/opportunity-selector.tsx +0 -30
  52. package/src/components/molecules/opportunity/opportunity-type.tsx +0 -16
  53. package/src/components/molecules/route-details.tsx +0 -112
  54. package/src/components/molecules/slippage-selector.tsx +0 -200
  55. package/src/components/molecules/swap-details.tsx +0 -55
  56. package/src/components/molecules/swap-input.tsx +0 -186
  57. package/src/components/molecules/tabs.tsx +0 -79
  58. package/src/components/molecules/token-selector.tsx +0 -180
  59. package/src/components/molecules/tx-status.tsx +0 -312
  60. package/src/components/molecules/widget/asset-list/asset-filters.tsx +0 -113
  61. package/src/components/molecules/widget/asset-list/asset-list.tsx +0 -178
  62. package/src/components/molecules/widget/asset-list/asset-row.tsx +0 -45
  63. package/src/components/molecules/widget/asset-list/hooks/index.ts +0 -2
  64. package/src/components/molecules/widget/asset-list/hooks/use-asset-filtering.ts +0 -44
  65. package/src/components/molecules/widget/asset-list/hooks/use-asset-grouping.ts +0 -87
  66. package/src/components/molecules/widget/asset-list/index.ts +0 -3
  67. package/src/components/molecules/widget/base-selector.tsx +0 -121
  68. package/src/components/molecules/widget/campaign-item.tsx +0 -82
  69. package/src/components/molecules/widget/deal-item.tsx +0 -92
  70. package/src/components/molecules/widget/index.ts +0 -36
  71. package/src/components/molecules/widget/opportunity-item.tsx +0 -105
  72. package/src/components/molecules/widget/widget-item-stats.tsx +0 -50
  73. package/src/components/molecules/widget/widget-item.tsx +0 -139
  74. package/src/components/molecules/widget/widget-list-items.tsx +0 -86
  75. package/src/components/ui/alert-dialog.tsx +0 -163
  76. package/src/components/ui/animated-background/animated-background.tsx +0 -182
  77. package/src/components/ui/animated-background/index.ts +0 -1
  78. package/src/components/ui/avatar.tsx +0 -73
  79. package/src/components/ui/badge.tsx +0 -59
  80. package/src/components/ui/banner.tsx +0 -84
  81. package/src/components/ui/button.tsx +0 -100
  82. package/src/components/ui/card.tsx +0 -119
  83. package/src/components/ui/chart.tsx +0 -346
  84. package/src/components/ui/checkbox.tsx +0 -32
  85. package/src/components/ui/chip.tsx +0 -52
  86. package/src/components/ui/collapsible.tsx +0 -34
  87. package/src/components/ui/combobox.tsx +0 -730
  88. package/src/components/ui/command.tsx +0 -184
  89. package/src/components/ui/dialog.tsx +0 -129
  90. package/src/components/ui/dropdown.tsx +0 -316
  91. package/src/components/ui/field.tsx +0 -244
  92. package/src/components/ui/heading.tsx +0 -74
  93. package/src/components/ui/hover-card.tsx +0 -139
  94. package/src/components/ui/icon-animation.tsx +0 -82
  95. package/src/components/ui/icon-list.tsx +0 -168
  96. package/src/components/ui/index.ts +0 -48
  97. package/src/components/ui/info-card.tsx +0 -110
  98. package/src/components/ui/input-group.tsx +0 -170
  99. package/src/components/ui/input.tsx +0 -72
  100. package/src/components/ui/label-with-icon.tsx +0 -122
  101. package/src/components/ui/label.tsx +0 -24
  102. package/src/components/ui/multi-select.tsx +0 -1090
  103. package/src/components/ui/navigation-bar.tsx +0 -153
  104. package/src/components/ui/navigation-menu.tsx +0 -188
  105. package/src/components/ui/opportunity-details-v1.tsx +0 -104
  106. package/src/components/ui/pagination.tsx +0 -127
  107. package/src/components/ui/popover.tsx +0 -48
  108. package/src/components/ui/scroll-area.tsx +0 -64
  109. package/src/components/ui/segment-control.tsx +0 -146
  110. package/src/components/ui/select.tsx +0 -199
  111. package/src/components/ui/separator.tsx +0 -26
  112. package/src/components/ui/sheet.tsx +0 -139
  113. package/src/components/ui/sidebar.tsx +0 -728
  114. package/src/components/ui/skeleton.tsx +0 -14
  115. package/src/components/ui/slider.tsx +0 -58
  116. package/src/components/ui/sonner.tsx +0 -24
  117. package/src/components/ui/switch.tsx +0 -29
  118. package/src/components/ui/table-shadcn.tsx +0 -110
  119. package/src/components/ui/table.tsx +0 -117
  120. package/src/components/ui/textarea.tsx +0 -22
  121. package/src/components/ui/toggle-group.tsx +0 -71
  122. package/src/components/ui/toggle.tsx +0 -47
  123. package/src/components/ui/tooltip.tsx +0 -66
  124. package/src/hooks/index.ts +0 -1
  125. package/src/hooks/useIsMobile.ts +0 -77
  126. package/src/index.ts +0 -16
  127. package/src/lib/utils.ts +0 -6
  128. package/src/styles/globals.css +0 -181
  129. package/src/styles/themes/index.css +0 -9
  130. package/src/styles/themes/semantic.css +0 -117
  131. package/src/styles/tokens/colors.css +0 -124
  132. package/src/styles/tokens/index.css +0 -15
  133. package/src/styles/tokens/radius.css +0 -18
  134. package/src/styles/tokens/spacing.css +0 -58
  135. package/src/styles/tokens/typography.css +0 -87
  136. package/src/tokens/index.ts +0 -108
  137. package/tsconfig.json +0 -20
  138. package/vite.config.js +0 -49
  139. /package/{src/images/enso.png → dist/enso-22FJ4GNK.png} +0 -0
@@ -1,538 +0,0 @@
1
- "use client";
2
-
3
- import React, { Fragment, useMemo, useState } from "react";
4
-
5
- import {
6
- ColumnDef,
7
- ExpandedState,
8
- flexRender,
9
- getCoreRowModel,
10
- getExpandedRowModel,
11
- getFilteredRowModel,
12
- getPaginationRowModel,
13
- getSortedRowModel,
14
- PaginationState,
15
- Row,
16
- RowSelectionState,
17
- SortingState,
18
- useReactTable,
19
- } from "@tanstack/react-table";
20
- import {
21
- Card,
22
- CardContent,
23
- CardHeader,
24
- CardTitle,
25
- ScrollArea,
26
- ShadTableCell,
27
- Table,
28
- TableBody,
29
- TableHead,
30
- TableHeader,
31
- TableRow,
32
- Input,
33
- Combobox,
34
- } from "../../ui";
35
- import SortableHeader from "./sortable-header";
36
- import { SearchBar } from "../search-bar";
37
- import { fuzzyFilter } from "./fuzzy-filter";
38
- import { cn } from "@/lib/utils";
39
- import { ItemInfo } from "./item-info";
40
- import { GridSkeleton, RowsSkeleton } from "./skeleton";
41
- import { SortDropdown } from "./sort-dropdown";
42
- import {
43
- Pagination,
44
- PaginationContent,
45
- PaginationEllipsis,
46
- PaginationItem,
47
- PaginationLink,
48
- PaginationNext,
49
- PaginationPrevious,
50
- } from "../../ui/pagination";
51
- import { useIsMobile } from "@/hooks";
52
-
53
- const getScrollAreaHeight = (size: string | "full" | undefined) => {
54
- if (!size) return "auto";
55
- if (size === "full") return "100%";
56
- return size;
57
- };
58
-
59
- type DataTableProps<TData, TValue> = {
60
- columns: ColumnDef<TData, TValue>[];
61
- data: TData[];
62
- // Loading state
63
- isLoading?: boolean;
64
- // Slot for control appart from the ones baked in
65
- slot?: React.ReactNode;
66
- // Enabling baked in features
67
- enableSearch?: boolean;
68
- enableExpand?: boolean;
69
- enablePagination?: boolean;
70
- enableSorting?: boolean;
71
- // enableSelection?: boolean;
72
- renderSubComponent?: (props: { row: Row<TData> }) => React.ReactElement;
73
-
74
- // Initial setup to match data coming in if server side manipulated
75
- initialSorting?: SortingState;
76
- onRowClick?: (row: TData) => void;
77
- initialColumnVisibility?: Record<keyof TData, boolean>;
78
-
79
- // Server-side control options
80
- manualFiltering?: boolean;
81
- manualSorting?: boolean;
82
- manualPagination?: boolean;
83
-
84
- pageCount?: number; // Total page count from server (required when manualPagination is true)
85
- rowCount?: number; // Total row count from server (optional, for display purposes)
86
-
87
- // Controlled state (for server-side control)
88
- sorting?: SortingState;
89
- onSortingChange?: (sorting: SortingState) => void;
90
- globalFilter?: string;
91
- onGlobalFilterChange?: (filter: string) => void;
92
- pagination?: PaginationState;
93
- onPaginationChange?: (pagination: PaginationState) => void;
94
-
95
- // Exclusive card view configuration
96
- grid?: {
97
- displayAsGrid: boolean;
98
- className?: string;
99
- headerSlot: keyof TData | string;
100
- rightSlot?: keyof TData | string;
101
- excludeColumns?: (keyof TData | string)[];
102
- headerTextSize?: string;
103
- rightSlotTextSize?: string;
104
- };
105
- // ScrollArea size configuration
106
- size?: string | "full";
107
- emptyState?: React.ReactNode;
108
- className?: string;
109
- slotClassName?: string;
110
- };
111
-
112
- export function DataTable<TData, TValue>({
113
- columns,
114
- data,
115
- isLoading,
116
- slot,
117
- enableExpand,
118
- enableSearch,
119
- enablePagination,
120
- enableSorting,
121
- renderSubComponent,
122
- initialSorting = [],
123
- initialColumnVisibility,
124
- size = undefined,
125
- grid,
126
- emptyState,
127
- className,
128
- slotClassName,
129
- onRowClick,
130
- // Server-side props
131
- manualFiltering = false,
132
- manualSorting = false,
133
- manualPagination = false,
134
- pageCount,
135
- rowCount,
136
- sorting: controlledSorting,
137
- onSortingChange: onControlledSortingChange,
138
- globalFilter: controlledGlobalFilter,
139
- onGlobalFilterChange: onControlledGlobalFilterChange,
140
- pagination: controlledPagination,
141
- onPaginationChange: onControlledPaginationChange,
142
- }: DataTableProps<TData, TValue>) {
143
- const [columnVisibility, setColumnVisibility] = React.useState(initialColumnVisibility ?? {});
144
- const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
145
- const [expanded, setExpanded] = React.useState<ExpandedState>({});
146
-
147
- // Internal state (used when not controlled from outside)
148
- const [internalGlobalFilter, setInternalGlobalFilter] = React.useState<string>("");
149
- const [internalSorting, setInternalSorting] = React.useState<SortingState>(initialSorting);
150
- const [internalPagination, setInternalPagination] = React.useState<PaginationState>({
151
- pageIndex: 0,
152
- pageSize: 10,
153
- });
154
-
155
- // Store the default page size for reset purposes
156
- const defaultPageSize = React.useMemo(
157
- () => controlledPagination?.pageSize ?? internalPagination.pageSize,
158
- [controlledPagination?.pageSize]
159
- );
160
-
161
- // Use controlled state if provided, otherwise use internal state
162
- const globalFilter = controlledGlobalFilter ?? internalGlobalFilter;
163
- const sorting = controlledSorting ?? internalSorting;
164
- const pagination = controlledPagination ?? internalPagination;
165
-
166
- const setGlobalFilter = (value: string) => {
167
- if (onControlledGlobalFilterChange) {
168
- onControlledGlobalFilterChange(value);
169
- } else {
170
- setInternalGlobalFilter(value);
171
- }
172
- };
173
-
174
- const setSorting = (updaterOrValue: SortingState | ((old: SortingState) => SortingState)) => {
175
- const newSorting =
176
- typeof updaterOrValue === "function" ? updaterOrValue(sorting) : updaterOrValue;
177
-
178
- if (onControlledSortingChange) {
179
- onControlledSortingChange(newSorting);
180
- } else {
181
- setInternalSorting(newSorting);
182
- }
183
- };
184
-
185
- const setPagination = (
186
- updaterOrValue: PaginationState | ((old: PaginationState) => PaginationState)
187
- ) => {
188
- const newPagination =
189
- typeof updaterOrValue === "function" ? updaterOrValue(pagination) : updaterOrValue;
190
-
191
- if (onControlledPaginationChange) {
192
- onControlledPaginationChange(newPagination);
193
- } else {
194
- setInternalPagination(newPagination);
195
- }
196
- };
197
-
198
- const { isMobile } = useIsMobile();
199
-
200
- const table = useReactTable({
201
- data,
202
- columns,
203
- filterFns: {
204
- fuzzy: fuzzyFilter,
205
- },
206
- state: {
207
- expanded,
208
- rowSelection,
209
- sorting,
210
- globalFilter,
211
- columnVisibility,
212
- pagination,
213
- },
214
- onColumnVisibilityChange: setColumnVisibility,
215
- onRowSelectionChange: setRowSelection,
216
- onSortingChange: setSorting,
217
- // onColumnFiltersChange: setColumnFilters,
218
- onGlobalFilterChange: setGlobalFilter,
219
- onPaginationChange: setPagination,
220
- onExpandedChange: setExpanded,
221
- globalFilterFn: "fuzzy" as any,
222
- getRowCanExpand: enableExpand ? () => true : undefined,
223
-
224
- // Server-side control
225
- manualFiltering: manualFiltering,
226
- manualSorting,
227
- manualPagination,
228
- pageCount: manualPagination ? pageCount : undefined,
229
- rowCount: manualPagination ? rowCount : undefined,
230
-
231
- // Core row model is always needed
232
- getCoreRowModel: getCoreRowModel(),
233
- // When manualFiltering: server does the filtering; don't filter current page by globalFilter (so search works across all data).
234
- // When !manualFiltering: use client-side filtered row model.
235
- getFilteredRowModel: manualFiltering
236
- ? (table) => () => table.getCoreRowModel()
237
- : getFilteredRowModel(),
238
- getSortedRowModel: !manualSorting ? getSortedRowModel() : undefined,
239
- getPaginationRowModel:
240
- enablePagination && !manualPagination ? getPaginationRowModel() : undefined,
241
- getExpandedRowModel: getExpandedRowModel(),
242
-
243
- // debugTable: true,
244
- // debugHeaders: true,
245
- // debugColumns: false,
246
- });
247
-
248
- const headers = useMemo(
249
- () =>
250
- table
251
- .getHeaderGroups()
252
- .map((headerGroup) => headerGroup.headers)
253
- .flat(),
254
- [table]
255
- );
256
-
257
- return (
258
- <div className="space-y-3">
259
- {slot || enableSearch || (enableSorting && grid?.displayAsGrid) ? (
260
- <div className={cn("flex items-start gap-1", slotClassName)}>
261
- {slot}
262
- <span className="grow" />
263
- {enableSorting && grid?.displayAsGrid && (
264
- <SortDropdown
265
- headers={headers}
266
- currentSort={sorting.length > 0 ? sorting[0] : null}
267
- onSortChange={(columnId, desc) => {
268
- setSorting([{ id: columnId, desc }]);
269
- }}
270
- onClearSort={() => setSorting([])}
271
- />
272
- )}
273
- {enableSearch && (
274
- <SearchBar
275
- id="search-bar"
276
- value={globalFilter ?? ""}
277
- onChange={(value) => setGlobalFilter(String(value))}
278
- placeholder="Search..."
279
- />
280
- )}
281
- </div>
282
- ) : undefined}
283
- <ScrollArea style={{ height: getScrollAreaHeight(size) }} className={className}>
284
- {table.getRowModel().rows?.length || isLoading ? (
285
- grid?.displayAsGrid ? (
286
- isLoading ? (
287
- <GridSkeleton
288
- className={cn(
289
- "grid gap-1 pr-1.5",
290
- "grid-cols-1 md:grid-cols-2 xl:grid-cols-3",
291
- grid.className
292
- )}
293
- />
294
- ) : (
295
- <div
296
- className={cn(
297
- "grid gap-1 pr-1.5",
298
- "grid-cols-1 md:grid-cols-2 xl:grid-cols-3",
299
- grid.className
300
- )}
301
- >
302
- {table.getRowModel().rows.map((row) => (
303
- <div onClick={() => onRowClick?.(row.original)} key={row.id}>
304
- <Card
305
- className={cn(
306
- "max-w-none",
307
- onRowClick ? "hover:bg-neutral-alpha-5 cursor-pointer" : ""
308
- )}
309
- data-state={row.getIsSelected() && "selected"}
310
- variant="border"
311
- >
312
- <CardHeader className="flex items-center gap-2">
313
- {/* Slot for header, pick the colum you display here */}
314
- <CardTitle className={cn("line-clamp-1 grow", grid.headerTextSize || "text-base")}>
315
- {row
316
- .getVisibleCells()
317
- .filter((cell) => cell.column.id === grid.headerSlot)
318
- ?.map((cell) => (
319
- <Fragment key={cell.id}>
320
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
321
- </Fragment>
322
- ))}
323
- </CardTitle>
324
- {/* Slot for extra right section, pick the colum you display here */}
325
- {row
326
- .getVisibleCells()
327
- .filter((cell) => cell.column.id === grid?.rightSlot)
328
- ?.map((cell) => (
329
- <div key={cell.id} className={cn(grid.rightSlotTextSize || "text-lg")}>
330
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
331
- </div>
332
- ))}
333
- </CardHeader>
334
- <CardContent className="flex flex-wrap justify-between">
335
- {row
336
- .getVisibleCells()
337
- .filter(
338
- (cell) =>
339
- cell.column.id !== grid.headerSlot &&
340
- cell.column.id !== grid.rightSlot &&
341
- !grid.excludeColumns?.includes(cell.column.id)
342
- )
343
- .map((cell) => {
344
- return (
345
- <ItemInfo
346
- key={cell.id}
347
- title={flexRender(
348
- cell.column.columnDef.header,
349
- headers
350
- .find((header) => header.id === cell.column.id)
351
- ?.getContext()
352
- )}
353
- value={flexRender(cell.column.columnDef.cell, cell.getContext())}
354
- className="justify-between gap-3 *:text-xs"
355
- />
356
- );
357
- })}
358
- </CardContent>
359
- {row.getIsExpanded() && renderSubComponent({ row })}
360
- </Card>
361
- </div>
362
- ))}
363
- </div>
364
- )
365
- ) : (
366
- <Table>
367
- <TableHeader>
368
- <TableRow>
369
- {headers.map((header) => {
370
- return (
371
- <TableHead
372
- key={header.id}
373
- style={{
374
- width: header.column.columnDef.size,
375
- minWidth: header.column.columnDef.minSize,
376
- maxWidth: header.column.columnDef.maxSize,
377
- }}
378
- className={(header.column.columnDef.meta as any)?.className}
379
- >
380
- <SortableHeader header={header} />
381
- </TableHead>
382
- );
383
- })}
384
- </TableRow>
385
- </TableHeader>
386
- <TableBody>
387
- {isLoading ? (
388
- <RowsSkeleton headers={headers.map((header) => header.id)} />
389
- ) : (
390
- table.getRowModel().rows.map((row) => (
391
- <Fragment key={row.id}>
392
- <TableRow
393
- onClick={() => onRowClick?.(row.original)}
394
- data-state={row.getIsSelected() && "selected"}
395
- className={onRowClick ? "hover:bg-neutral-alpha-5 cursor-pointer" : ""}
396
- >
397
- {row.getVisibleCells().map((cell) => (
398
- <ShadTableCell key={cell.id}>
399
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
400
- </ShadTableCell>
401
- ))}
402
- </TableRow>
403
- {row.getIsExpanded() && (
404
- <tr>
405
- {/* 2nd row is a custom 1 cell row */}
406
- <td colSpan={row.getVisibleCells().length}>
407
- {renderSubComponent({ row })}
408
- </td>
409
- </tr>
410
- )}
411
- </Fragment>
412
- ))
413
- )}
414
- </TableBody>
415
- </Table>
416
- )
417
- ) : (
418
- (emptyState ?? (
419
- <div className="bg-neutral-alpha-2 m-12 flex h-48 items-center justify-center rounded-lg shadow">
420
- No results available.
421
- </div>
422
- ))
423
- )}
424
- </ScrollArea>
425
- {enablePagination && (
426
- <div className={cn(isMobile ? "flex flex-col gap-2" : "flex justify-between gap-1")}>
427
- <Combobox
428
- options={[10, 20, 30, 40, 50, 100].map((size) => ({
429
- label: `Show ${size.toString()} rows per page`,
430
- value: size,
431
- }))}
432
- defaultValue={table.getState().pagination.pageSize}
433
- onValueChange={(value) => {
434
- // If value is cleared/null/0, reset to default page size
435
- const newPageSize = value || defaultPageSize;
436
- table.setPageSize(newPageSize);
437
- }}
438
- autoSize
439
- className="h-9"
440
- searchable={false}
441
- closeOnSelect
442
- placeholder="Rows shown per page"
443
- />
444
-
445
- <Pagination>
446
- <PaginationContent>
447
- <PaginationItem>
448
- <PaginationPrevious
449
- onClick={() => table.previousPage()}
450
- className={
451
- !table.getCanPreviousPage()
452
- ? "pointer-events-none opacity-50"
453
- : "cursor-pointer"
454
- }
455
- />
456
- </PaginationItem>
457
- {Array.from({ length: table.getPageCount() }, (_, i) => i).map((pageIndex) => {
458
- const currentPage = table.getState().pagination.pageIndex;
459
- const totalPages = table.getPageCount();
460
-
461
- // Show first page, last page, current page, and pages around current
462
- const showPage =
463
- pageIndex === 0 ||
464
- pageIndex === totalPages - 1 ||
465
- (pageIndex >= currentPage - 1 && pageIndex <= currentPage + 1);
466
-
467
- // Show ellipsis before current range (but not right after first page)
468
- const showEllipsisBefore = pageIndex === currentPage - 2 && currentPage > 2;
469
-
470
- // Show ellipsis after current range (but not right before last page)
471
- const showEllipsisAfter =
472
- pageIndex === currentPage + 2 && currentPage < totalPages - 3;
473
-
474
- if (showEllipsisBefore || showEllipsisAfter) {
475
- return (
476
- <PaginationItem key={pageIndex}>
477
- <PaginationEllipsis />
478
- </PaginationItem>
479
- );
480
- }
481
-
482
- if (!showPage) return null;
483
-
484
- return (
485
- <PaginationItem key={pageIndex}>
486
- <PaginationLink
487
- onClick={() => table.setPageIndex(pageIndex)}
488
- isActive={currentPage === pageIndex}
489
- className="cursor-pointer"
490
- >
491
- {pageIndex + 1}
492
- </PaginationLink>
493
- </PaginationItem>
494
- );
495
- })}
496
- <PaginationItem>
497
- <PaginationNext
498
- onClick={() => table.nextPage()}
499
- className={
500
- !table.getCanNextPage() ? "pointer-events-none opacity-50" : "cursor-pointer"
501
- }
502
- />
503
- </PaginationItem>
504
- </PaginationContent>
505
- </Pagination>
506
-
507
- <div className="mx-4 flex items-center gap-1">
508
- <span className="text-muted-foreground truncate text-xs">Go to page:</span>
509
- <Input
510
- min={1}
511
- max={table.getPageCount()}
512
- pattern="[0-9]*"
513
- defaultValue={table.getState().pagination.pageIndex + 1}
514
- onBlurCapture={(e) => {
515
- const page = e.target.value ? Number(e.target.value) - 1 : 0;
516
- if (page >= 0 && page < table.getPageCount()) {
517
- table.setPageIndex(page);
518
- }
519
- }}
520
- onKeyDown={(e) => {
521
- if (e.key === "Enter") {
522
- const page = e.currentTarget.value ? Number(e.currentTarget.value) - 1 : 0;
523
- if (page >= 0 && page < table.getPageCount()) {
524
- table.setPageIndex(page);
525
- }
526
- }
527
- }}
528
- className="h-9! w-12"
529
- />
530
- <span className="text-muted-foreground truncate text-xs">
531
- of {table.getPageCount()}
532
- </span>
533
- </div>
534
- </div>
535
- )}
536
- </div>
537
- );
538
- }
@@ -1,17 +0,0 @@
1
- import { Row } from "@tanstack/react-table";
2
- import { ChevronDown, ChevronRight } from "lucide-react";
3
-
4
- export function ExpandToggle({ row }: { row: Row<any> }) {
5
- return row.getCanExpand() ? (
6
- <button
7
- onClick={row.getToggleExpandedHandler()}
8
- className="flex cursor-pointer items-center justify-center"
9
- >
10
- {row.getIsExpanded() ? (
11
- <ChevronDown className="text-foreground size-3.5" />
12
- ) : (
13
- <ChevronRight className="text-muted-foreground size-3.5" />
14
- )}
15
- </button>
16
- ) : undefined;
17
- }
@@ -1,34 +0,0 @@
1
- import { FilterFn, SortingFn, sortingFns } from "@tanstack/react-table";
2
- import { rankItem, compareItems } from "@tanstack/match-sorter-utils";
3
-
4
- // Define a custom fuzzy filter function that will apply ranking info to rows (using match-sorter utils)
5
- export const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
6
- // Rank the item
7
- const itemRank = rankItem(row.getValue(columnId), value);
8
-
9
- // Store the itemRank info
10
- addMeta({
11
- itemRank,
12
- });
13
-
14
- // Return if the item should be filtered in/out
15
- return itemRank.passed;
16
- };
17
-
18
- // Define a custom fuzzy sort function that will sort by rank if the row has ranking information
19
- export const fuzzySort: SortingFn<any> = (rowA, rowB, columnId) => {
20
- let dir = 0;
21
-
22
- // Only sort by rank if the column has ranking information
23
- if (rowA.columnFiltersMeta[columnId]) {
24
- dir = compareItems(
25
- // @ts-expect-error not in module
26
- rowA.columnFiltersMeta[columnId]?.itemRank,
27
- // @ts-expect-error not in module
28
- rowB.columnFiltersMeta[columnId]?.itemRank,
29
- );
30
- }
31
-
32
- // Provide an alphanumeric fallback for when the item ranks are equal
33
- return dir === 0 ? sortingFns.alphanumeric(rowA, rowB, columnId) : dir;
34
- };
@@ -1,3 +0,0 @@
1
- export * from "./data-table";
2
- export * from "./expand-toggle";
3
- export * from "./sort-dropdown";
@@ -1,19 +0,0 @@
1
- import { cn } from "@/lib/utils";
2
- import React from "react";
3
-
4
- export function ItemInfo({
5
- title,
6
- value,
7
- className,
8
- }: {
9
- title: React.ReactNode;
10
- value: React.ReactNode;
11
- className?: string;
12
- }) {
13
- return (
14
- <div className={cn("flex flex-col items-start gap-1", className)}>
15
- <div className="text-muted-foreground text-xs">{title}</div>
16
- <div className="text-sm">{value}</div>
17
- </div>
18
- );
19
- }
@@ -1,23 +0,0 @@
1
- import { Card, ShadTableCell, TableRow } from "@/components/ui";
2
-
3
- export function RowsSkeleton({ headers }: { headers: string[] }) {
4
- return [...Array(12).keys()].map((i) => (
5
- <TableRow key={i}>
6
- {headers.map((e) => (
7
- <ShadTableCell key={e} className="h-12 animate-pulse">
8
- <div />
9
- </ShadTableCell>
10
- ))}
11
- </TableRow>
12
- ));
13
- }
14
-
15
- export function GridSkeleton({ className }: { className?: string }) {
16
- return (
17
- <div className={className}>
18
- {[...Array(12).keys()].map((i) => (
19
- <Card key={i} className="h-40 animate-pulse" variant="border" />
20
- ))}
21
- </div>
22
- );
23
- }