@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.
- package/dist/index.cjs +10331 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7652 -47844
- package/dist/index.js.map +1 -1
- package/dist/types/components/features/sidebar-layout.d.ts +2 -0
- package/dist/types/components/features/sidebar-layout.d.ts.map +1 -1
- package/dist/types/components/ui/chart.d.ts +1 -1
- package/dist/types/components/ui/chart.d.ts.map +1 -1
- package/package.json +26 -22
- package/.prettierrc.json +0 -4
- package/.turbo/turbo-build.log +0 -182
- package/CHANGELOG.md +0 -795
- package/components.json +0 -21
- package/src/components/charts/QUICK_REFERENCE.md +0 -323
- package/src/components/charts/README.md +0 -658
- package/src/components/charts/RECHARTS_FEATURES.md +0 -458
- package/src/components/charts/area-chart.tsx +0 -248
- package/src/components/charts/bar-chart.tsx +0 -362
- package/src/components/charts/index.ts +0 -4
- package/src/components/charts/pie-chart.tsx +0 -277
- package/src/components/charts/radial-chart.tsx +0 -312
- package/src/components/features/api-status/index.tsx +0 -23
- package/src/components/features/data-table/data-table.tsx +0 -538
- package/src/components/features/data-table/expand-toggle.tsx +0 -17
- package/src/components/features/data-table/fuzzy-filter.tsx +0 -34
- package/src/components/features/data-table/index.ts +0 -3
- package/src/components/features/data-table/item-info.tsx +0 -19
- package/src/components/features/data-table/skeleton.tsx +0 -23
- package/src/components/features/data-table/sort-dropdown.tsx +0 -118
- package/src/components/features/data-table/sortable-header.tsx +0 -37
- package/src/components/features/index.ts +0 -6
- package/src/components/features/page-heading.tsx +0 -27
- package/src/components/features/search-bar.tsx +0 -55
- package/src/components/features/segmented-navigation.tsx +0 -18
- package/src/components/features/sidebar-layout.tsx +0 -193
- package/src/components/features/turtle-tooltip.tsx +0 -67
- package/src/components/icons/arrow.tsx +0 -23
- package/src/components/icons/beta.tsx +0 -95
- package/src/components/icons/dot.tsx +0 -102
- package/src/components/icons/index.ts +0 -7
- package/src/components/icons/issue.tsx +0 -106
- package/src/components/icons/turtle.tsx +0 -156
- package/src/components/icons/update.tsx +0 -113
- package/src/components/icons/warning.tsx +0 -95
- package/src/components/molecules/index.ts +0 -9
- package/src/components/molecules/opportunity/index.ts +0 -10
- package/src/components/molecules/opportunity/opportunity-apr.tsx +0 -129
- package/src/components/molecules/opportunity/opportunity-disclaimer.tsx +0 -46
- package/src/components/molecules/opportunity/opportunity-rate-estimator.tsx +0 -62
- package/src/components/molecules/opportunity/opportunity-section.tsx +0 -113
- package/src/components/molecules/opportunity/opportunity-selector.tsx +0 -30
- package/src/components/molecules/opportunity/opportunity-type.tsx +0 -16
- package/src/components/molecules/route-details.tsx +0 -112
- package/src/components/molecules/slippage-selector.tsx +0 -200
- package/src/components/molecules/swap-details.tsx +0 -55
- package/src/components/molecules/swap-input.tsx +0 -186
- package/src/components/molecules/tabs.tsx +0 -79
- package/src/components/molecules/token-selector.tsx +0 -180
- package/src/components/molecules/tx-status.tsx +0 -312
- package/src/components/molecules/widget/asset-list/asset-filters.tsx +0 -113
- package/src/components/molecules/widget/asset-list/asset-list.tsx +0 -178
- package/src/components/molecules/widget/asset-list/asset-row.tsx +0 -45
- package/src/components/molecules/widget/asset-list/hooks/index.ts +0 -2
- package/src/components/molecules/widget/asset-list/hooks/use-asset-filtering.ts +0 -44
- package/src/components/molecules/widget/asset-list/hooks/use-asset-grouping.ts +0 -87
- package/src/components/molecules/widget/asset-list/index.ts +0 -3
- package/src/components/molecules/widget/base-selector.tsx +0 -121
- package/src/components/molecules/widget/campaign-item.tsx +0 -82
- package/src/components/molecules/widget/deal-item.tsx +0 -92
- package/src/components/molecules/widget/index.ts +0 -36
- package/src/components/molecules/widget/opportunity-item.tsx +0 -105
- package/src/components/molecules/widget/widget-item-stats.tsx +0 -50
- package/src/components/molecules/widget/widget-item.tsx +0 -139
- package/src/components/molecules/widget/widget-list-items.tsx +0 -86
- package/src/components/ui/alert-dialog.tsx +0 -163
- package/src/components/ui/animated-background/animated-background.tsx +0 -182
- package/src/components/ui/animated-background/index.ts +0 -1
- package/src/components/ui/avatar.tsx +0 -73
- package/src/components/ui/badge.tsx +0 -59
- package/src/components/ui/banner.tsx +0 -84
- package/src/components/ui/button.tsx +0 -100
- package/src/components/ui/card.tsx +0 -119
- package/src/components/ui/chart.tsx +0 -346
- package/src/components/ui/checkbox.tsx +0 -32
- package/src/components/ui/chip.tsx +0 -52
- package/src/components/ui/collapsible.tsx +0 -34
- package/src/components/ui/combobox.tsx +0 -730
- package/src/components/ui/command.tsx +0 -184
- package/src/components/ui/dialog.tsx +0 -129
- package/src/components/ui/dropdown.tsx +0 -316
- package/src/components/ui/field.tsx +0 -244
- package/src/components/ui/heading.tsx +0 -74
- package/src/components/ui/hover-card.tsx +0 -139
- package/src/components/ui/icon-animation.tsx +0 -82
- package/src/components/ui/icon-list.tsx +0 -168
- package/src/components/ui/index.ts +0 -48
- package/src/components/ui/info-card.tsx +0 -110
- package/src/components/ui/input-group.tsx +0 -170
- package/src/components/ui/input.tsx +0 -72
- package/src/components/ui/label-with-icon.tsx +0 -122
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/multi-select.tsx +0 -1090
- package/src/components/ui/navigation-bar.tsx +0 -153
- package/src/components/ui/navigation-menu.tsx +0 -188
- package/src/components/ui/opportunity-details-v1.tsx +0 -104
- package/src/components/ui/pagination.tsx +0 -127
- package/src/components/ui/popover.tsx +0 -48
- package/src/components/ui/scroll-area.tsx +0 -64
- package/src/components/ui/segment-control.tsx +0 -146
- package/src/components/ui/select.tsx +0 -199
- package/src/components/ui/separator.tsx +0 -26
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/sidebar.tsx +0 -728
- package/src/components/ui/skeleton.tsx +0 -14
- package/src/components/ui/slider.tsx +0 -58
- package/src/components/ui/sonner.tsx +0 -24
- package/src/components/ui/switch.tsx +0 -29
- package/src/components/ui/table-shadcn.tsx +0 -110
- package/src/components/ui/table.tsx +0 -117
- package/src/components/ui/textarea.tsx +0 -22
- package/src/components/ui/toggle-group.tsx +0 -71
- package/src/components/ui/toggle.tsx +0 -47
- package/src/components/ui/tooltip.tsx +0 -66
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useIsMobile.ts +0 -77
- package/src/index.ts +0 -16
- package/src/lib/utils.ts +0 -6
- package/src/styles/globals.css +0 -181
- package/src/styles/themes/index.css +0 -9
- package/src/styles/themes/semantic.css +0 -117
- package/src/styles/tokens/colors.css +0 -124
- package/src/styles/tokens/index.css +0 -15
- package/src/styles/tokens/radius.css +0 -18
- package/src/styles/tokens/spacing.css +0 -58
- package/src/styles/tokens/typography.css +0 -87
- package/src/tokens/index.ts +0 -108
- package/tsconfig.json +0 -20
- package/vite.config.js +0 -49
- /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,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
|
-
}
|