@turtleclub/ui 0.7.0-beta.3 → 0.7.0-beta.30

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 (69) hide show
  1. package/.turbo/turbo-build.log +143 -132
  2. package/CHANGELOG.md +152 -0
  3. package/dist/index.cjs +76 -36
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.js +36068 -17674
  6. package/dist/index.js.map +1 -1
  7. package/dist/styles.css +1 -1
  8. package/dist/types/components/charts/area-chart.d.ts +108 -0
  9. package/dist/types/components/charts/area-chart.d.ts.map +1 -0
  10. package/dist/types/components/charts/bar-chart.d.ts +110 -0
  11. package/dist/types/components/charts/bar-chart.d.ts.map +1 -0
  12. package/dist/types/components/charts/index.d.ts +5 -0
  13. package/dist/types/components/charts/index.d.ts.map +1 -0
  14. package/dist/types/components/charts/pie-chart.d.ts +94 -0
  15. package/dist/types/components/charts/pie-chart.d.ts.map +1 -0
  16. package/dist/types/components/charts/radial-chart.d.ts +151 -0
  17. package/dist/types/components/charts/radial-chart.d.ts.map +1 -0
  18. package/dist/types/components/features/data-table/data-table.d.ts +7 -4
  19. package/dist/types/components/features/data-table/data-table.d.ts.map +1 -1
  20. package/dist/types/components/features/data-table/sort-dropdown.d.ts.map +1 -1
  21. package/dist/types/components/features/search-bar.d.ts +1 -0
  22. package/dist/types/components/features/search-bar.d.ts.map +1 -1
  23. package/dist/types/components/molecules/swap-input.d.ts +3 -0
  24. package/dist/types/components/molecules/swap-input.d.ts.map +1 -1
  25. package/dist/types/components/molecules/token-selector.d.ts +2 -1
  26. package/dist/types/components/molecules/token-selector.d.ts.map +1 -1
  27. package/dist/types/components/ui/avatar.d.ts +2 -2
  28. package/dist/types/components/ui/avatar.d.ts.map +1 -1
  29. package/dist/types/components/ui/chart.d.ts +18 -4
  30. package/dist/types/components/ui/chart.d.ts.map +1 -1
  31. package/dist/types/components/ui/combobox.d.ts +21 -0
  32. package/dist/types/components/ui/combobox.d.ts.map +1 -1
  33. package/dist/types/components/ui/dialog.d.ts.map +1 -1
  34. package/dist/types/components/ui/dropdown.d.ts +2 -1
  35. package/dist/types/components/ui/dropdown.d.ts.map +1 -1
  36. package/dist/types/components/ui/index.d.ts +1 -0
  37. package/dist/types/components/ui/index.d.ts.map +1 -1
  38. package/dist/types/components/ui/multi-select.d.ts.map +1 -1
  39. package/dist/types/components/ui/segment-control.d.ts +1 -0
  40. package/dist/types/components/ui/segment-control.d.ts.map +1 -1
  41. package/dist/types/components/ui/slider.d.ts.map +1 -1
  42. package/dist/types/index.d.ts +1 -0
  43. package/dist/types/index.d.ts.map +1 -1
  44. package/package.json +3 -3
  45. package/src/components/charts/QUICK_REFERENCE.md +323 -0
  46. package/src/components/charts/README.md +658 -0
  47. package/src/components/charts/RECHARTS_FEATURES.md +458 -0
  48. package/src/components/charts/area-chart.tsx +248 -0
  49. package/src/components/charts/bar-chart.tsx +362 -0
  50. package/src/components/charts/index.ts +4 -0
  51. package/src/components/charts/pie-chart.tsx +277 -0
  52. package/src/components/charts/radial-chart.tsx +312 -0
  53. package/src/components/features/data-table/data-table.tsx +136 -125
  54. package/src/components/features/data-table/sort-dropdown.tsx +8 -11
  55. package/src/components/features/search-bar.tsx +6 -1
  56. package/src/components/molecules/swap-input.tsx +44 -30
  57. package/src/components/molecules/token-selector.tsx +10 -1
  58. package/src/components/ui/avatar.tsx +8 -15
  59. package/src/components/ui/chart.tsx +100 -109
  60. package/src/components/ui/combobox.tsx +150 -137
  61. package/src/components/ui/dialog.tsx +9 -23
  62. package/src/components/ui/dropdown.tsx +3 -1
  63. package/src/components/ui/index.ts +1 -0
  64. package/src/components/ui/multi-select.tsx +325 -307
  65. package/src/components/ui/segment-control.tsx +7 -2
  66. package/src/components/ui/slider.tsx +6 -11
  67. package/src/index.ts +1 -0
  68. package/src/styles/globals.css +4 -0
  69. package/src/styles/themes/semantic.css +26 -56
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import React, { Fragment, useMemo } from "react";
3
+ import React, { Fragment, useMemo, useState } from "react";
4
4
 
5
5
  import {
6
6
  ColumnDef,
@@ -29,6 +29,8 @@ import {
29
29
  TableHead,
30
30
  TableHeader,
31
31
  TableRow,
32
+ Input,
33
+ Combobox,
32
34
  } from "../../ui";
33
35
  import SortableHeader from "./sortable-header";
34
36
  import { SearchBar } from "../search-bar";
@@ -46,6 +48,7 @@ import {
46
48
  PaginationNext,
47
49
  PaginationPrevious,
48
50
  } from "../../ui/pagination";
51
+ import { useIsMobile } from "@/hooks";
49
52
 
50
53
  const getScrollAreaHeight = (size: string | "full" | undefined) => {
51
54
  if (!size) return "auto";
@@ -77,6 +80,7 @@ type DataTableProps<TData, TValue> = {
77
80
  manualFiltering?: boolean;
78
81
  manualSorting?: boolean;
79
82
  manualPagination?: boolean;
83
+
80
84
  pageCount?: number; // Total page count from server (required when manualPagination is true)
81
85
  rowCount?: number; // Total row count from server (optional, for display purposes)
82
86
 
@@ -92,14 +96,17 @@ type DataTableProps<TData, TValue> = {
92
96
  grid?: {
93
97
  displayAsGrid: boolean;
94
98
  className?: string;
95
- headerSlot: keyof TData;
96
- rightSlot?: keyof TData;
97
- excludeColumns?: (keyof TData)[];
99
+ headerSlot: keyof TData | string;
100
+ rightSlot?: keyof TData | string;
101
+ excludeColumns?: (keyof TData | string)[];
102
+ headerTextSize?: string;
103
+ rightSlotTextSize?: string;
98
104
  };
99
105
  // ScrollArea size configuration
100
106
  size?: string | "full";
101
107
  emptyState?: React.ReactNode;
102
108
  className?: string;
109
+ slotClassName?: string;
103
110
  };
104
111
 
105
112
  export function DataTable<TData, TValue>({
@@ -118,6 +125,7 @@ export function DataTable<TData, TValue>({
118
125
  grid,
119
126
  emptyState,
120
127
  className,
128
+ slotClassName,
121
129
  onRowClick,
122
130
  // Server-side props
123
131
  manualFiltering = false,
@@ -132,22 +140,23 @@ export function DataTable<TData, TValue>({
132
140
  pagination: controlledPagination,
133
141
  onPaginationChange: onControlledPaginationChange,
134
142
  }: DataTableProps<TData, TValue>) {
135
- const [columnVisibility, setColumnVisibility] = React.useState(
136
- initialColumnVisibility ?? {},
137
- );
143
+ const [columnVisibility, setColumnVisibility] = React.useState(initialColumnVisibility ?? {});
138
144
  const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
139
145
  const [expanded, setExpanded] = React.useState<ExpandedState>({});
140
146
 
141
147
  // Internal state (used when not controlled from outside)
142
- const [internalGlobalFilter, setInternalGlobalFilter] =
143
- React.useState<string>("");
144
- const [internalSorting, setInternalSorting] =
145
- React.useState<SortingState>(initialSorting);
146
- const [internalPagination, setInternalPagination] =
147
- React.useState<PaginationState>({
148
- pageIndex: 0,
149
- pageSize: 10,
150
- });
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
+ );
151
160
 
152
161
  // Use controlled state if provided, otherwise use internal state
153
162
  const globalFilter = controlledGlobalFilter ?? internalGlobalFilter;
@@ -162,13 +171,9 @@ export function DataTable<TData, TValue>({
162
171
  }
163
172
  };
164
173
 
165
- const setSorting = (
166
- updaterOrValue: SortingState | ((old: SortingState) => SortingState),
167
- ) => {
174
+ const setSorting = (updaterOrValue: SortingState | ((old: SortingState) => SortingState)) => {
168
175
  const newSorting =
169
- typeof updaterOrValue === "function"
170
- ? updaterOrValue(sorting)
171
- : updaterOrValue;
176
+ typeof updaterOrValue === "function" ? updaterOrValue(sorting) : updaterOrValue;
172
177
 
173
178
  if (onControlledSortingChange) {
174
179
  onControlledSortingChange(newSorting);
@@ -178,14 +183,10 @@ export function DataTable<TData, TValue>({
178
183
  };
179
184
 
180
185
  const setPagination = (
181
- updaterOrValue:
182
- | PaginationState
183
- | ((old: PaginationState) => PaginationState),
186
+ updaterOrValue: PaginationState | ((old: PaginationState) => PaginationState)
184
187
  ) => {
185
188
  const newPagination =
186
- typeof updaterOrValue === "function"
187
- ? updaterOrValue(pagination)
188
- : updaterOrValue;
189
+ typeof updaterOrValue === "function" ? updaterOrValue(pagination) : updaterOrValue;
189
190
 
190
191
  if (onControlledPaginationChange) {
191
192
  onControlledPaginationChange(newPagination);
@@ -194,6 +195,8 @@ export function DataTable<TData, TValue>({
194
195
  }
195
196
  };
196
197
 
198
+ const { isMobile } = useIsMobile();
199
+
197
200
  const table = useReactTable({
198
201
  data,
199
202
  columns,
@@ -219,7 +222,7 @@ export function DataTable<TData, TValue>({
219
222
  getRowCanExpand: enableExpand ? () => true : undefined,
220
223
 
221
224
  // Server-side control
222
- manualFiltering,
225
+ manualFiltering: manualFiltering,
223
226
  manualSorting,
224
227
  manualPagination,
225
228
  pageCount: manualPagination ? pageCount : undefined,
@@ -231,9 +234,7 @@ export function DataTable<TData, TValue>({
231
234
  getFilteredRowModel: !manualFiltering ? getFilteredRowModel() : undefined,
232
235
  getSortedRowModel: !manualSorting ? getSortedRowModel() : undefined,
233
236
  getPaginationRowModel:
234
- enablePagination && !manualPagination
235
- ? getPaginationRowModel()
236
- : undefined,
237
+ enablePagination && !manualPagination ? getPaginationRowModel() : undefined,
237
238
  getExpandedRowModel: getExpandedRowModel(),
238
239
 
239
240
  // debugTable: true,
@@ -247,13 +248,13 @@ export function DataTable<TData, TValue>({
247
248
  .getHeaderGroups()
248
249
  .map((headerGroup) => headerGroup.headers)
249
250
  .flat(),
250
- [table],
251
+ [table]
251
252
  );
252
253
 
253
254
  return (
254
255
  <div className="space-y-3">
255
256
  {slot || enableSearch || (enableSorting && grid?.displayAsGrid) ? (
256
- <div className="flex items-center gap-1">
257
+ <div className={cn("flex items-start gap-1", slotClassName)}>
257
258
  {slot}
258
259
  <span className="grow" />
259
260
  {enableSorting && grid?.displayAsGrid && (
@@ -276,10 +277,7 @@ export function DataTable<TData, TValue>({
276
277
  )}
277
278
  </div>
278
279
  ) : undefined}
279
- <ScrollArea
280
- style={{ height: getScrollAreaHeight(size) }}
281
- className={className}
282
- >
280
+ <ScrollArea style={{ height: getScrollAreaHeight(size) }} className={className}>
283
281
  {table.getRowModel().rows?.length || isLoading ? (
284
282
  grid?.displayAsGrid ? (
285
283
  isLoading ? (
@@ -287,15 +285,15 @@ export function DataTable<TData, TValue>({
287
285
  className={cn(
288
286
  "grid gap-1 pr-1.5",
289
287
  "grid-cols-1 md:grid-cols-2 xl:grid-cols-3",
290
- grid.className,
288
+ grid.className
291
289
  )}
292
290
  />
293
291
  ) : (
294
292
  <div
295
293
  className={cn(
296
294
  "grid gap-1 pr-1.5",
297
- // "grid-cols-1 md:grid-cols-2 xl:grid-cols-3",
298
- grid.className,
295
+ "grid-cols-1 md:grid-cols-2 xl:grid-cols-3",
296
+ grid.className
299
297
  )}
300
298
  >
301
299
  {table.getRowModel().rows.map((row) => (
@@ -303,27 +301,20 @@ export function DataTable<TData, TValue>({
303
301
  <Card
304
302
  className={cn(
305
303
  "max-w-none",
306
- onRowClick
307
- ? "hover:bg-neutral-alpha-5 cursor-pointer"
308
- : "",
304
+ onRowClick ? "hover:bg-neutral-alpha-5 cursor-pointer" : ""
309
305
  )}
310
306
  data-state={row.getIsSelected() && "selected"}
311
307
  variant="border"
312
308
  >
313
309
  <CardHeader className="flex items-center gap-2">
314
310
  {/* Slot for header, pick the colum you display here */}
315
- <CardTitle className="line-clamp-1 grow text-base">
311
+ <CardTitle className={cn("line-clamp-1 grow", grid.headerTextSize || "text-base")}>
316
312
  {row
317
313
  .getVisibleCells()
318
- .filter(
319
- (cell) => cell.column.id === grid.headerSlot,
320
- )
314
+ .filter((cell) => cell.column.id === grid.headerSlot)
321
315
  ?.map((cell) => (
322
316
  <Fragment key={cell.id}>
323
- {flexRender(
324
- cell.column.columnDef.cell,
325
- cell.getContext(),
326
- )}
317
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
327
318
  </Fragment>
328
319
  ))}
329
320
  </CardTitle>
@@ -332,11 +323,8 @@ export function DataTable<TData, TValue>({
332
323
  .getVisibleCells()
333
324
  .filter((cell) => cell.column.id === grid?.rightSlot)
334
325
  ?.map((cell) => (
335
- <div key={cell.id} className="text-lg">
336
- {flexRender(
337
- cell.column.columnDef.cell,
338
- cell.getContext(),
339
- )}
326
+ <div key={cell.id} className={cn(grid.rightSlotTextSize || "text-lg")}>
327
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
340
328
  </div>
341
329
  ))}
342
330
  </CardHeader>
@@ -347,8 +335,7 @@ export function DataTable<TData, TValue>({
347
335
  (cell) =>
348
336
  cell.column.id !== grid.headerSlot &&
349
337
  cell.column.id !== grid.rightSlot &&
350
- // @ts-expect-error I know that column id by defintion is keyof TData
351
- !grid.excludeColumns?.includes(cell.column.id),
338
+ !grid.excludeColumns?.includes(cell.column.id)
352
339
  )
353
340
  .map((cell) => {
354
341
  return (
@@ -357,15 +344,10 @@ export function DataTable<TData, TValue>({
357
344
  title={flexRender(
358
345
  cell.column.columnDef.header,
359
346
  headers
360
- .find(
361
- (header) => header.id === cell.column.id,
362
- )
363
- ?.getContext(),
364
- )}
365
- value={flexRender(
366
- cell.column.columnDef.cell,
367
- cell.getContext(),
347
+ .find((header) => header.id === cell.column.id)
348
+ ?.getContext()
368
349
  )}
350
+ value={flexRender(cell.column.columnDef.cell, cell.getContext())}
369
351
  className="justify-between gap-3 *:text-xs"
370
352
  />
371
353
  );
@@ -390,9 +372,7 @@ export function DataTable<TData, TValue>({
390
372
  minWidth: header.column.columnDef.minSize,
391
373
  maxWidth: header.column.columnDef.maxSize,
392
374
  }}
393
- className={
394
- (header.column.columnDef.meta as any)?.className
395
- }
375
+ className={(header.column.columnDef.meta as any)?.className}
396
376
  >
397
377
  <SortableHeader header={header} />
398
378
  </TableHead>
@@ -409,18 +389,11 @@ export function DataTable<TData, TValue>({
409
389
  <TableRow
410
390
  onClick={() => onRowClick?.(row.original)}
411
391
  data-state={row.getIsSelected() && "selected"}
412
- className={
413
- onRowClick
414
- ? "hover:bg-neutral-alpha-5 cursor-pointer"
415
- : ""
416
- }
392
+ className={onRowClick ? "hover:bg-neutral-alpha-5 cursor-pointer" : ""}
417
393
  >
418
394
  {row.getVisibleCells().map((cell) => (
419
395
  <ShadTableCell key={cell.id}>
420
- {flexRender(
421
- cell.column.columnDef.cell,
422
- cell.getContext(),
423
- )}
396
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
424
397
  </ShadTableCell>
425
398
  ))}
426
399
  </TableRow>
@@ -446,10 +419,26 @@ export function DataTable<TData, TValue>({
446
419
  ))
447
420
  )}
448
421
  </ScrollArea>
449
- {enablePagination &&
450
- (manualPagination
451
- ? (pageCount ?? 0) > 1
452
- : table.getPageCount() > 1) && (
422
+ {enablePagination && (
423
+ <div className={cn(isMobile ? "flex flex-col gap-2" : "flex justify-between gap-1")}>
424
+ <Combobox
425
+ options={[10, 20, 30, 40, 50, 100].map((size) => ({
426
+ label: `Show ${size.toString()} rows per page`,
427
+ value: size,
428
+ }))}
429
+ defaultValue={table.getState().pagination.pageSize}
430
+ onValueChange={(value) => {
431
+ // If value is cleared/null/0, reset to default page size
432
+ const newPageSize = value || defaultPageSize;
433
+ table.setPageSize(newPageSize);
434
+ }}
435
+ autoSize
436
+ className="h-9"
437
+ searchable={false}
438
+ closeOnSelect
439
+ placeholder="Rows shown per page"
440
+ />
441
+
453
442
  <Pagination>
454
443
  <PaginationContent>
455
444
  <PaginationItem>
@@ -462,63 +451,85 @@ export function DataTable<TData, TValue>({
462
451
  }
463
452
  />
464
453
  </PaginationItem>
465
- {Array.from({ length: table.getPageCount() }, (_, i) => i).map(
466
- (pageIndex) => {
467
- const currentPage = table.getState().pagination.pageIndex;
468
- const totalPages = table.getPageCount();
469
-
470
- // Show first page, last page, current page, and pages around current
471
- const showPage =
472
- pageIndex === 0 ||
473
- pageIndex === totalPages - 1 ||
474
- (pageIndex >= currentPage - 1 &&
475
- pageIndex <= currentPage + 1);
476
-
477
- // Show ellipsis before current range (but not right after first page)
478
- const showEllipsisBefore =
479
- pageIndex === currentPage - 2 && currentPage > 2;
480
-
481
- // Show ellipsis after current range (but not right before last page)
482
- const showEllipsisAfter =
483
- pageIndex === currentPage + 2 &&
484
- currentPage < totalPages - 3;
485
-
486
- if (showEllipsisBefore || showEllipsisAfter) {
487
- return (
488
- <PaginationItem key={pageIndex}>
489
- <PaginationEllipsis />
490
- </PaginationItem>
491
- );
492
- }
454
+ {Array.from({ length: table.getPageCount() }, (_, i) => i).map((pageIndex) => {
455
+ const currentPage = table.getState().pagination.pageIndex;
456
+ const totalPages = table.getPageCount();
457
+
458
+ // Show first page, last page, current page, and pages around current
459
+ const showPage =
460
+ pageIndex === 0 ||
461
+ pageIndex === totalPages - 1 ||
462
+ (pageIndex >= currentPage - 1 && pageIndex <= currentPage + 1);
493
463
 
494
- if (!showPage) return null;
464
+ // Show ellipsis before current range (but not right after first page)
465
+ const showEllipsisBefore = pageIndex === currentPage - 2 && currentPage > 2;
495
466
 
467
+ // Show ellipsis after current range (but not right before last page)
468
+ const showEllipsisAfter =
469
+ pageIndex === currentPage + 2 && currentPage < totalPages - 3;
470
+
471
+ if (showEllipsisBefore || showEllipsisAfter) {
496
472
  return (
497
473
  <PaginationItem key={pageIndex}>
498
- <PaginationLink
499
- onClick={() => table.setPageIndex(pageIndex)}
500
- isActive={currentPage === pageIndex}
501
- className="cursor-pointer"
502
- >
503
- {pageIndex + 1}
504
- </PaginationLink>
474
+ <PaginationEllipsis />
505
475
  </PaginationItem>
506
476
  );
507
- },
508
- )}
477
+ }
478
+
479
+ if (!showPage) return null;
480
+
481
+ return (
482
+ <PaginationItem key={pageIndex}>
483
+ <PaginationLink
484
+ onClick={() => table.setPageIndex(pageIndex)}
485
+ isActive={currentPage === pageIndex}
486
+ className="cursor-pointer"
487
+ >
488
+ {pageIndex + 1}
489
+ </PaginationLink>
490
+ </PaginationItem>
491
+ );
492
+ })}
509
493
  <PaginationItem>
510
494
  <PaginationNext
511
495
  onClick={() => table.nextPage()}
512
496
  className={
513
- !table.getCanNextPage()
514
- ? "pointer-events-none opacity-50"
515
- : "cursor-pointer"
497
+ !table.getCanNextPage() ? "pointer-events-none opacity-50" : "cursor-pointer"
516
498
  }
517
499
  />
518
500
  </PaginationItem>
519
501
  </PaginationContent>
520
502
  </Pagination>
521
- )}
503
+
504
+ <div className="mx-4 flex items-center gap-1">
505
+ <span className="text-muted-foreground truncate text-xs">Go to page:</span>
506
+ <Input
507
+ min={1}
508
+ max={table.getPageCount()}
509
+ pattern="[0-9]*"
510
+ defaultValue={table.getState().pagination.pageIndex + 1}
511
+ onBlurCapture={(e) => {
512
+ const page = e.target.value ? Number(e.target.value) - 1 : 0;
513
+ if (page >= 0 && page < table.getPageCount()) {
514
+ table.setPageIndex(page);
515
+ }
516
+ }}
517
+ onKeyDown={(e) => {
518
+ if (e.key === "Enter") {
519
+ const page = e.currentTarget.value ? Number(e.currentTarget.value) - 1 : 0;
520
+ if (page >= 0 && page < table.getPageCount()) {
521
+ table.setPageIndex(page);
522
+ }
523
+ }
524
+ }}
525
+ className="h-9! w-12"
526
+ />
527
+ <span className="text-muted-foreground truncate text-xs">
528
+ of {table.getPageCount()}
529
+ </span>
530
+ </div>
531
+ </div>
532
+ )}
522
533
  </div>
523
534
  );
524
535
  }
@@ -27,7 +27,7 @@ export function SortDropdown<TData>({
27
27
  }: SortDropdownProps<TData>) {
28
28
  // Filter only sortable columns
29
29
  const sortableHeaders = headers.filter(
30
- (header) => header.column.getCanSort() && !header.isPlaceholder,
30
+ (header) => header.column.getCanSort() && !header.isPlaceholder
31
31
  );
32
32
 
33
33
  const getCurrentSortLabel = () => {
@@ -79,23 +79,20 @@ export function SortDropdown<TData>({
79
79
  <DropdownMenuLabel>Sort by</DropdownMenuLabel>
80
80
  <DropdownMenuSeparator />
81
81
  {sortableHeaders.map((header) => {
82
- const isCurrentAsc =
83
- currentSort?.id === header.column.id && !currentSort.desc;
84
- const isCurrentDesc =
85
- currentSort?.id === header.column.id && currentSort.desc;
82
+ const isCurrentAsc = currentSort?.id === header.column.id && !currentSort.desc;
83
+ const isCurrentDesc = currentSort?.id === header.column.id && currentSort.desc;
86
84
  const isActive = currentSort?.id === header.column.id;
87
85
 
88
86
  return (
89
87
  <DropdownMenuItem
90
88
  key={header.column.id}
91
89
  onClick={() => handleColumnClick(header.column.id)}
92
- className={isActive ? "bg-secondary" : ""}
90
+ className={
91
+ isActive ? "bg-secondary flex items-center gap-2" : "flex items-center gap-2"
92
+ }
93
93
  >
94
- <span className="flex-1">
95
- {flexRender(
96
- header.column.columnDef.header,
97
- header.getContext(),
98
- )}
94
+ <span className="w-fit truncate">
95
+ {flexRender(header.column.columnDef.header, header.getContext())}
99
96
  </span>
100
97
  {isActive &&
101
98
  (isCurrentDesc ? (
@@ -1,17 +1,20 @@
1
1
  import * as React from "react";
2
2
  import { Input, InputProps } from "../ui";
3
3
  import { Search } from "lucide-react";
4
+ import { cn } from "@/lib/utils";
4
5
 
5
6
  type Props = {
6
7
  value: string;
7
8
  onChange: (value: string) => void;
8
9
  debounce?: number;
10
+ size?: "default" | "sm";
9
11
  } & Omit<InputProps, "onChange">;
10
12
 
11
13
  export const SearchBar: React.FC<Props> = ({
12
14
  value: initialValue,
13
15
  onChange,
14
16
  debounce = 300,
17
+ size = "default",
15
18
  ...props
16
19
  }) => {
17
20
  const [value, setValue] = React.useState<string>(initialValue);
@@ -31,12 +34,14 @@ export const SearchBar: React.FC<Props> = ({
31
34
  return () => clearTimeout(timeout);
32
35
  }, [value]);
33
36
 
37
+ const inputClasses = size === "sm" ? "text-sm" : "";
38
+
34
39
  return (
35
40
  <div className="border-border relative w-full max-w-md rounded-full border">
36
41
  <Input
37
42
  value={value}
38
43
  onChange={handleInputChange}
39
- className="-my-px h-9"
44
+ className={cn("-my-px h-9", inputClasses)}
40
45
  {...props}
41
46
  />
42
47
  <button
@@ -6,8 +6,10 @@ import { Chip } from "@/components/ui/chip";
6
6
  import { TokenSelector, type Token } from "./token-selector";
7
7
  import { BaseSelector } from "./widget/base-selector";
8
8
 
9
- interface SwapInputProps
10
- extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
9
+ interface SwapInputProps extends Omit<
10
+ React.HTMLAttributes<HTMLDivElement>,
11
+ "onChange"
12
+ > {
11
13
  value?: string;
12
14
  tokens: Token[];
13
15
  selectedToken?: string;
@@ -24,6 +26,9 @@ interface SwapInputProps
24
26
  useCustomTokenSelector?: boolean;
25
27
  selectedTokenData?: Token | null;
26
28
  onCustomTokenSelectorClick?: () => void;
29
+ showTokenSelectorBalance?: boolean;
30
+ showInputBalance?: boolean;
31
+ triggerTokenSelectorWidth?: number;
27
32
  }
28
33
 
29
34
  const SwapInput = React.forwardRef<HTMLDivElement, SwapInputProps>(
@@ -46,6 +51,9 @@ const SwapInput = React.forwardRef<HTMLDivElement, SwapInputProps>(
46
51
  useCustomTokenSelector = false,
47
52
  selectedTokenData,
48
53
  onCustomTokenSelectorClick,
54
+ showTokenSelectorBalance = true,
55
+ showInputBalance = true,
56
+ triggerTokenSelectorWidth,
49
57
  ...props
50
58
  },
51
59
  ref,
@@ -127,40 +135,46 @@ const SwapInput = React.forwardRef<HTMLDivElement, SwapInputProps>(
127
135
  size="sm"
128
136
  variant="default"
129
137
  className="rounded-full"
138
+ showBalance={showTokenSelectorBalance}
139
+ triggerWidth={triggerTokenSelectorWidth}
130
140
  />
131
141
  ))}
132
142
  </div>
133
143
 
134
144
  {/* Bottom row: USD value and balance with MAX */}
135
- <div className="text-muted-foreground flex items-center justify-between text-sm">
136
- <div>
137
- {usdValue ? (
138
- <span>≈ {usdValue}</span>
139
- ) : value ? (
140
- <span className="text-destructive/50">Insufficient balance</span>
141
- ) : null}
145
+ {showInputBalance && (
146
+ <div className="text-muted-foreground flex items-center justify-between text-sm">
147
+ <div>
148
+ {usdValue ? (
149
+ <span>≈ {usdValue}</span>
150
+ ) : value ? (
151
+ <span className="text-destructive/50">
152
+ Insufficient balance
153
+ </span>
154
+ ) : null}
155
+ </div>
156
+ <div className="flex items-center gap-2">
157
+ {balance && (
158
+ <span className="text-sm">
159
+ Balance: {balance} {currentToken?.symbol || ""}
160
+ </span>
161
+ )}
162
+ {onMaxClick && (
163
+ <Chip
164
+ variant="default"
165
+ size="xs"
166
+ onClick={onMaxClick}
167
+ className={cn(
168
+ "hover:bg-primary hover:text-primary-foreground h-5 cursor-pointer px-2 py-0.5 text-xs transition-colors",
169
+ disabled && "cursor-not-allowed opacity-50",
170
+ )}
171
+ >
172
+ MAX
173
+ </Chip>
174
+ )}
175
+ </div>
142
176
  </div>
143
- <div className="flex items-center gap-2">
144
- {balance && (
145
- <span className="text-sm">
146
- Balance: {balance} {currentToken?.symbol || ""}
147
- </span>
148
- )}
149
- {onMaxClick && (
150
- <Chip
151
- variant="default"
152
- size="xs"
153
- onClick={onMaxClick}
154
- className={cn(
155
- "hover:bg-primary hover:text-primary-foreground h-5 cursor-pointer px-2 py-0.5 text-xs transition-colors",
156
- disabled && "cursor-not-allowed opacity-50",
157
- )}
158
- >
159
- MAX
160
- </Chip>
161
- )}
162
- </div>
163
- </div>
177
+ )}
164
178
  </Card>
165
179
  );
166
180
  },