@turtleclub/ui 0.7.0-beta.0 → 0.7.0-beta.2

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 (45) hide show
  1. package/.turbo/turbo-build.log +54 -56
  2. package/CHANGELOG.md +10 -0
  3. package/dist/index.cjs +17 -17
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.js +5222 -5320
  6. package/dist/index.js.map +1 -1
  7. package/dist/styles.css +1 -1
  8. package/dist/types/components/features/data-table/data-table.d.ts.map +1 -1
  9. package/dist/types/components/molecules/index.d.ts +1 -1
  10. package/dist/types/components/molecules/index.d.ts.map +1 -1
  11. package/dist/types/components/molecules/opportunity/index.d.ts +0 -1
  12. package/dist/types/components/molecules/opportunity/index.d.ts.map +1 -1
  13. package/dist/types/components/molecules/route-details.d.ts +1 -1
  14. package/dist/types/components/molecules/route-details.d.ts.map +1 -1
  15. package/dist/types/components/molecules/swap-input.d.ts.map +1 -1
  16. package/dist/types/components/molecules/token-selector.d.ts +3 -2
  17. package/dist/types/components/molecules/token-selector.d.ts.map +1 -1
  18. package/dist/types/components/molecules/widget/base-selector.d.ts +9 -3
  19. package/dist/types/components/molecules/widget/base-selector.d.ts.map +1 -1
  20. package/dist/types/components/ui/combobox.d.ts.map +1 -1
  21. package/package.json +3 -3
  22. package/src/components/features/data-table/data-table.tsx +134 -82
  23. package/src/components/molecules/index.ts +1 -1
  24. package/src/components/molecules/opportunity/index.ts +0 -1
  25. package/src/components/molecules/route-details.tsx +6 -4
  26. package/src/components/molecules/swap-input.tsx +2 -8
  27. package/src/components/molecules/token-selector.tsx +15 -4
  28. package/src/components/molecules/widget/base-selector.tsx +23 -6
  29. package/src/components/ui/combobox.tsx +67 -34
  30. package/src/components/ui/tooltip.tsx +1 -1
  31. package/dist/types/components/molecules/opportunity/opportunity-list/hooks/index.d.ts +0 -3
  32. package/dist/types/components/molecules/opportunity/opportunity-list/hooks/index.d.ts.map +0 -1
  33. package/dist/types/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-filtering.d.ts +0 -11
  34. package/dist/types/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-filtering.d.ts.map +0 -1
  35. package/dist/types/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-grouping.d.ts +0 -9
  36. package/dist/types/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-grouping.d.ts.map +0 -1
  37. package/dist/types/components/molecules/opportunity/opportunity-list/index.d.ts +0 -2
  38. package/dist/types/components/molecules/opportunity/opportunity-list/index.d.ts.map +0 -1
  39. package/dist/types/components/molecules/opportunity/opportunity-list/opportunity-list.d.ts +0 -22
  40. package/dist/types/components/molecules/opportunity/opportunity-list/opportunity-list.d.ts.map +0 -1
  41. package/src/components/molecules/opportunity/opportunity-list/hooks/index.ts +0 -2
  42. package/src/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-filtering.ts +0 -45
  43. package/src/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-grouping.ts +0 -85
  44. package/src/components/molecules/opportunity/opportunity-list/index.ts +0 -1
  45. package/src/components/molecules/opportunity/opportunity-list/opportunity-list.tsx +0 -142
@@ -3,6 +3,12 @@ import * as React from "react";
3
3
  import { cn } from "@/lib/utils";
4
4
  import type { ReactNode } from "react";
5
5
 
6
+ // Minimal Token type - only the fields we need
7
+ interface TokenLike {
8
+ symbol: string;
9
+ logoUrl?: string;
10
+ }
11
+
6
12
  interface BaseSelectorProps {
7
13
  icon?: React.ReactNode;
8
14
  text: string;
@@ -12,6 +18,8 @@ interface BaseSelectorProps {
12
18
  size?: "xs" | "sm" | "default";
13
19
  placeholder?: string;
14
20
  showIcon?: boolean;
21
+ // New: Accept token directly
22
+ token?: TokenLike;
15
23
  }
16
24
 
17
25
  // Utility convert icon url to img element
@@ -25,6 +33,11 @@ const iconUrlToImg = (iconUrl?: string, alt: string = ""): ReactNode => {
25
33
  );
26
34
  };
27
35
 
36
+ // Generate icon from token
37
+ const getTokenIcon = (token: TokenLike): ReactNode => {
38
+ return iconUrlToImg(token.logoUrl, token.symbol);
39
+ };
40
+
28
41
  const BaseSelector = ({
29
42
  icon,
30
43
  text,
@@ -34,8 +47,12 @@ const BaseSelector = ({
34
47
  size = "default",
35
48
  placeholder = "Select",
36
49
  showIcon = true,
50
+ token,
37
51
  }: BaseSelectorProps) => {
38
- const hasContent = text || icon;
52
+ // Determine the actual icon to display
53
+ const displayIcon = token ? getTokenIcon(token) : icon;
54
+ const displayText = token?.symbol || text;
55
+ const hasContent = displayText || displayIcon;
39
56
 
40
57
  return (
41
58
  <div
@@ -61,7 +78,7 @@ const BaseSelector = ({
61
78
  size === "default" && "gap-1.5",
62
79
  )}
63
80
  >
64
- {showIcon && icon && (
81
+ {showIcon && displayIcon && (
65
82
  <span
66
83
  className={cn(
67
84
  "flex items-center justify-center",
@@ -70,7 +87,7 @@ const BaseSelector = ({
70
87
  size === "default" && "h-4 w-5",
71
88
  )}
72
89
  >
73
- {icon}
90
+ {displayIcon}
74
91
  </span>
75
92
  )}
76
93
  <span
@@ -81,7 +98,7 @@ const BaseSelector = ({
81
98
  size === "default" && "text-sm",
82
99
  )}
83
100
  >
84
- {text}
101
+ {displayText}
85
102
  </span>
86
103
  </div>
87
104
  ) : (
@@ -100,5 +117,5 @@ const BaseSelector = ({
100
117
  );
101
118
  };
102
119
 
103
- export { BaseSelector, iconUrlToImg };
104
- export type { BaseSelectorProps };
120
+ export { BaseSelector, iconUrlToImg, getTokenIcon };
121
+ export type { BaseSelectorProps, TokenLike };
@@ -12,7 +12,11 @@ import {
12
12
  CommandItem,
13
13
  CommandList,
14
14
  } from "@/components/ui/command";
15
- import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
15
+ import {
16
+ Popover,
17
+ PopoverContent,
18
+ PopoverTrigger,
19
+ } from "@/components/ui/popover";
16
20
  import { Separator } from "@/components/ui/separator";
17
21
  import { cn } from "@/lib/utils";
18
22
 
@@ -85,7 +89,10 @@ interface ComboboxOption<T = string> {
85
89
  * />
86
90
  */
87
91
  interface ComboboxProps<T = string>
88
- extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "animationConfig" | "defaultValue"> {
92
+ extends Omit<
93
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
94
+ "animationConfig" | "defaultValue"
95
+ > {
89
96
  /**
90
97
  * An array of option objects or groups to be displayed in the multi-select component.
91
98
  */
@@ -230,7 +237,10 @@ export interface ComboboxRef {
230
237
  focus: () => void;
231
238
  }
232
239
 
233
- const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<ComboboxRef>) => {
240
+ const ComboboxComponent = <T = string,>(
241
+ props: ComboboxProps<T>,
242
+ ref: React.Ref<ComboboxRef>,
243
+ ) => {
234
244
  const {
235
245
  options,
236
246
  onValueChange,
@@ -283,7 +293,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
283
293
  setTimeout(() => setPoliteMessage(""), 100);
284
294
  }
285
295
  },
286
- []
296
+ [],
287
297
  );
288
298
 
289
299
  const multiSelectId = React.useId();
@@ -324,10 +334,12 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
324
334
  }
325
335
  },
326
336
  }),
327
- [resetToDefault, selectedValue, onValueChange]
337
+ [resetToDefault, onValueChange],
328
338
  );
329
339
 
330
- const [screenSize, setScreenSize] = React.useState<"mobile" | "tablet" | "desktop">("desktop");
340
+ const [screenSize, setScreenSize] = React.useState<
341
+ "mobile" | "tablet" | "desktop"
342
+ >("desktop");
331
343
 
332
344
  React.useEffect(() => {
333
345
  if (typeof window === "undefined") return;
@@ -380,30 +392,31 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
380
392
 
381
393
  const getAllOptions = React.useCallback((): ComboboxOption<T>[] => {
382
394
  if (options.length === 0) return [];
383
- const allOptions: ComboboxOption<T>[] = options;
395
+
384
396
  const valueSet = new Set<T>();
385
- const duplicates: T[] = [];
386
397
  const uniqueOptions: ComboboxOption<T>[] = [];
387
- allOptions.forEach((option) => {
388
- if (valueSet.has(option.value)) {
389
- duplicates.push(option.value);
390
- } else {
398
+
399
+ options.forEach((option) => {
400
+ if (!valueSet.has(option.value)) {
391
401
  valueSet.add(option.value);
392
402
  uniqueOptions.push(option);
393
403
  }
394
404
  });
395
- return allOptions;
405
+
406
+ return uniqueOptions;
396
407
  }, [options]);
397
408
 
398
409
  const getOptionByValue = React.useCallback(
399
410
  (value: T): ComboboxOption<T> | undefined => {
400
411
  const option = getAllOptions().find((option) => option.value === value);
401
412
  if (!option && process.env.NODE_ENV === "development") {
402
- console.warn(`Combobox: Option with value "${value}" not found in options list`);
413
+ console.warn(
414
+ `Combobox: Option with value "${value}" not found in options list`,
415
+ );
403
416
  }
404
417
  return option;
405
418
  },
406
- [getAllOptions]
419
+ [getAllOptions],
407
420
  );
408
421
 
409
422
  const filteredOptions = React.useMemo(() => {
@@ -413,7 +426,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
413
426
  return options.filter(
414
427
  (option) =>
415
428
  option.label.toLowerCase().includes(searchValue.toLowerCase()) ||
416
- String(option.value).toLowerCase().includes(searchValue.toLowerCase())
429
+ String(option.value).toLowerCase().includes(searchValue.toLowerCase()),
417
430
  );
418
431
  }, [options, searchValue, searchable]);
419
432
 
@@ -464,7 +477,9 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
464
477
 
465
478
  if (isPopoverOpen !== prevIsOpen.current) {
466
479
  if (isPopoverOpen) {
467
- announce(`Dropdown opened. ${totalOptions} options available. Use arrow keys to navigate.`);
480
+ announce(
481
+ `Dropdown opened. ${totalOptions} options available. Use arrow keys to navigate.`,
482
+ );
468
483
  } else {
469
484
  announce("Dropdown closed.");
470
485
  }
@@ -476,11 +491,11 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
476
491
  const filteredCount = allOptions.filter(
477
492
  (opt) =>
478
493
  opt.label.toLowerCase().includes(searchValue.toLowerCase()) ||
479
- String(opt.value).toLowerCase().includes(searchValue.toLowerCase())
494
+ String(opt.value).toLowerCase().includes(searchValue.toLowerCase()),
480
495
  ).length;
481
496
 
482
497
  announce(
483
- `${filteredCount} option${filteredCount === 1 ? "" : "s"} found for "${searchValue}"`
498
+ `${filteredCount} option${filteredCount === 1 ? "" : "s"} found for "${searchValue}"`,
484
499
  );
485
500
  }
486
501
  prevSearchValue.current = searchValue;
@@ -498,9 +513,14 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
498
513
  </div>
499
514
  </div>
500
515
 
501
- <Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen} modal={modalPopover}>
516
+ <Popover
517
+ open={isPopoverOpen}
518
+ onOpenChange={setIsPopoverOpen}
519
+ modal={modalPopover}
520
+ >
502
521
  <div id={triggerDescriptionId} className="sr-only">
503
- Multi-select dropdown. Use arrow keys to navigate, Enter to select, and Escape to close.
522
+ Multi-select dropdown. Use arrow keys to navigate, Enter to select,
523
+ and Escape to close.
504
524
  </div>
505
525
  <PopoverTrigger asChild>
506
526
  <Button
@@ -521,7 +541,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
521
541
  responsiveSettings.compactMode && "h-8 text-sm",
522
542
  screenSize === "mobile" && "h-12 text-base",
523
543
  disabled && "cursor-not-allowed opacity-50",
524
- className
544
+ className,
525
545
  )}
526
546
  style={{
527
547
  ...widthConstraints,
@@ -532,7 +552,9 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
532
552
  <div className="mx-auto flex w-full items-center justify-between">
533
553
  <div className="flex items-center gap-2">
534
554
  {(() => {
535
- const selectedOption = options.find((op) => op.value === selectedValue);
555
+ const selectedOption = options.find(
556
+ (op) => op.value === selectedValue,
557
+ );
536
558
  return (
537
559
  <>
538
560
  {selectedOption?.icon && (
@@ -541,7 +563,9 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
541
563
  aria-hidden="true"
542
564
  />
543
565
  )}
544
- <span className="text-foreground text-sm">{selectedOption?.label}</span>
566
+ <span className="text-foreground text-sm">
567
+ {selectedOption?.label}
568
+ </span>
545
569
  </>
546
570
  );
547
571
  })()}
@@ -565,7 +589,10 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
565
589
  >
566
590
  <XIcon className="size-3.5" />
567
591
  </div>
568
- <Separator orientation="vertical" className="flex h-full min-h-6" />
592
+ <Separator
593
+ orientation="vertical"
594
+ className="flex h-full min-h-6"
595
+ />
569
596
  <ChevronDown
570
597
  className="text-muted-foreground mx-2 h-4 cursor-pointer"
571
598
  aria-hidden="true"
@@ -574,7 +601,9 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
574
601
  </div>
575
602
  ) : (
576
603
  <div className="mx-auto flex w-full items-center justify-between">
577
- <span className="text-muted-foreground text-sm">{placeholder}</span>
604
+ <span className="text-muted-foreground text-sm">
605
+ {placeholder}
606
+ </span>
578
607
  <ChevronDown className="text-muted-foreground mx-2 h-4 cursor-pointer" />
579
608
  </div>
580
609
  )}
@@ -590,7 +619,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
590
619
  screenSize === "mobile" && "w-[85vw] max-w-[280px]",
591
620
  screenSize === "tablet" && "w-[70vw] max-w-md",
592
621
  screenSize === "desktop" && "min-w-[300px]",
593
- popoverClassName
622
+ popoverClassName,
594
623
  )}
595
624
  style={{
596
625
  maxWidth: `min(${widthConstraints.maxWidth}, 85vw)`,
@@ -600,7 +629,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
600
629
  align="start"
601
630
  onEscapeKeyDown={() => setIsPopoverOpen(false)}
602
631
  >
603
- <Command>
632
+ <Command shouldFilter={false}>
604
633
  {searchable && (
605
634
  <CommandInput
606
635
  placeholder="Search options..."
@@ -620,10 +649,12 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
620
649
  className={cn(
621
650
  "multiselect-scrollbar max-h-[40vh] overflow-y-auto",
622
651
  screenSize === "mobile" && "max-h-[50vh]",
623
- "overscroll-behavior-y-contain"
652
+ "overscroll-behavior-y-contain",
624
653
  )}
625
654
  >
626
- <CommandEmpty>{emptyIndicator || "No results found."}</CommandEmpty>{" "}
655
+ <CommandEmpty>
656
+ {emptyIndicator || "No results found."}
657
+ </CommandEmpty>{" "}
627
658
  <CommandGroup>
628
659
  {filteredOptions.map((option) => {
629
660
  const isSelected = selectedValue === option.value;
@@ -639,7 +670,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
639
670
  }${option.disabled ? ", disabled" : ""}`}
640
671
  className={cn(
641
672
  "cursor-pointer",
642
- option.disabled && "cursor-not-allowed opacity-50"
673
+ option.disabled && "cursor-not-allowed opacity-50",
643
674
  )}
644
675
  disabled={option.disabled}
645
676
  >
@@ -650,7 +681,9 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
650
681
  />
651
682
  )}
652
683
  <span className="grow">{option.label}</span>
653
- {isSelected ? <CheckIcon className="text-muted-foreground size-3.5" /> : null}
684
+ {isSelected ? (
685
+ <CheckIcon className="text-muted-foreground size-3.5" />
686
+ ) : null}
654
687
  </CommandItem>
655
688
  );
656
689
  })}
@@ -662,7 +695,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
662
695
  <WandSparkles
663
696
  className={cn(
664
697
  "text-foreground bg-background my-2 h-3 w-3 cursor-pointer",
665
- isAnimating ? "" : "text-muted-foreground"
698
+ isAnimating ? "" : "text-muted-foreground",
666
699
  )}
667
700
  onClick={() => setIsAnimating(!isAnimating)}
668
701
  />
@@ -674,7 +707,7 @@ const ComboboxComponent = <T = string,>(props: ComboboxProps<T>, ref: React.Ref<
674
707
 
675
708
  // Create the forwardRef wrapper with proper generic typing
676
709
  const ComboboxForwardRef = React.forwardRef(ComboboxComponent) as <T = string>(
677
- props: ComboboxProps<T> & { ref?: React.Ref<ComboboxRef> }
710
+ props: ComboboxProps<T> & { ref?: React.Ref<ComboboxRef> },
678
711
  ) => React.ReactElement;
679
712
 
680
713
  // Set displayName on a mutable object
@@ -50,7 +50,7 @@ function TooltipContent({
50
50
  data-slot="tooltip-content"
51
51
  sideOffset={sideOffset}
52
52
  className={cn(
53
- "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-neutral-alpha-10 border-border z-50 origin-(--radix-tooltip-content-transform-origin) rounded-lg border p-2 backdrop-blur-lg transition-all",
53
+ "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-neutral-alpha-10 border-border z-50 max-w-xs origin-(--radix-tooltip-content-transform-origin) rounded-lg border p-2 text-xs backdrop-blur-lg transition-all",
54
54
  gradient === "primary" && "border-gradient-primary",
55
55
  gradient === "white" && "border-gradient-white",
56
56
  className,
@@ -1,3 +0,0 @@
1
- export * from "./use-opportunity-filtering";
2
- export * from "./use-opportunity-grouping";
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/molecules/opportunity/opportunity-list/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC"}
@@ -1,11 +0,0 @@
1
- import { OpportunityFilter } from "../opportunity-list";
2
- import { OpportunityItemValue as Opportunity } from "../../../widget/opportunity-item";
3
- interface UseOpportunityFilteringProps {
4
- opportunities: Opportunity[];
5
- searchQuery: string;
6
- activeFilterIds: string[];
7
- filters: OpportunityFilter[];
8
- }
9
- export declare function useOpportunityFiltering({ opportunities, searchQuery, activeFilterIds, filters, }: UseOpportunityFilteringProps): Opportunity[];
10
- export {};
11
- //# sourceMappingURL=use-opportunity-filtering.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-opportunity-filtering.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-filtering.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,oBAAoB,IAAI,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAEvF,UAAU,4BAA4B;IACpC,aAAa,EAAE,WAAW,EAAE,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,wBAAgB,uBAAuB,CAAC,EACtC,aAAa,EACb,WAAW,EACX,eAAe,EACf,OAAO,GACR,EAAE,4BAA4B,GAAG,WAAW,EAAE,CA4B9C"}
@@ -1,9 +0,0 @@
1
- import { OpportunityItemValue } from "../../../widget/opportunity-item";
2
- import { WidgetListGroup } from "../../../widget/widget-list-items";
3
- interface UseOpportunityGroupingProps {
4
- opportunities: OpportunityItemValue[];
5
- groupBy: "none" | "partner" | "chain";
6
- }
7
- export declare function useOpportunityGrouping({ opportunities, groupBy, }: UseOpportunityGroupingProps): WidgetListGroup<OpportunityItemValue>[];
8
- export {};
9
- //# sourceMappingURL=use-opportunity-grouping.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-opportunity-grouping.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-grouping.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEpE,UAAU,2BAA2B;IACnC,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;CACvC;AAED,wBAAgB,sBAAsB,CAAC,EACrC,aAAa,EACb,OAAO,GACR,EAAE,2BAA2B,GAAG,eAAe,CAAC,oBAAoB,CAAC,EAAE,CAwEvE"}
@@ -1,2 +0,0 @@
1
- export * from "./opportunity-list";
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/components/molecules/opportunity/opportunity-list/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
@@ -1,22 +0,0 @@
1
- import React from "react";
2
- import { OpportunityItemValue } from "../../widget/opportunity-item";
3
- import { AssetFilterItem } from "../../widget/asset-list/asset-filters";
4
- export interface OpportunityFilter extends AssetFilterItem {
5
- filterFn: (opportunity: OpportunityItemValue) => boolean;
6
- }
7
- export interface OpportunityListProps {
8
- opportunities: OpportunityItemValue[];
9
- onOpportunityClick?: (opportunity: OpportunityItemValue) => void;
10
- groupBy?: "none" | "partner" | "chain";
11
- filters?: OpportunityFilter[];
12
- filterVariant?: "badge" | "navigation";
13
- multipleFilters?: boolean;
14
- showSearch?: boolean;
15
- searchPlaceholder?: string;
16
- searchDebounceDelay?: number;
17
- className?: string;
18
- itemClassName?: string;
19
- emptyState?: React.ReactNode;
20
- }
21
- export declare const OpportunityList: React.FC<OpportunityListProps>;
22
- //# sourceMappingURL=opportunity-list.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"opportunity-list.d.ts","sourceRoot":"","sources":["../../../../../../src/components/molecules/opportunity/opportunity-list/opportunity-list.tsx"],"names":[],"mappings":"AACA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,EAEL,oBAAoB,EACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,eAAe,EAChB,MAAM,uCAAuC,CAAC;AAK/C,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,QAAQ,EAAE,CAAC,WAAW,EAAE,oBAAoB,KAAK,OAAO,CAAC;CAC1D;AAED,MAAM,WAAW,oBAAoB;IAEnC,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAGjE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAGvC,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;IACvC,eAAe,CAAC,EAAE,OAAO,CAAC;IAG1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAG7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC9B;AA0BD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA0E1D,CAAC"}
@@ -1,2 +0,0 @@
1
- export * from "./use-opportunity-filtering";
2
- export * from "./use-opportunity-grouping";
@@ -1,45 +0,0 @@
1
- import { useMemo } from "react";
2
- import { OpportunityFilter } from "../opportunity-list";
3
- import { OpportunityItemValue as Opportunity } from "../../../widget/opportunity-item";
4
-
5
- interface UseOpportunityFilteringProps {
6
- opportunities: Opportunity[];
7
- searchQuery: string;
8
- activeFilterIds: string[];
9
- filters: OpportunityFilter[];
10
- }
11
-
12
- export function useOpportunityFiltering({
13
- opportunities,
14
- searchQuery,
15
- activeFilterIds,
16
- filters,
17
- }: UseOpportunityFilteringProps): Opportunity[] {
18
- return useMemo(() => {
19
- let result = opportunities;
20
-
21
- // Apply search filter
22
- if (searchQuery) {
23
- const query = searchQuery.toLowerCase();
24
- result = result.filter(
25
- (opportunity) =>
26
- opportunity.name.toLowerCase().includes(query) ||
27
- opportunity.partner?.name.toLowerCase().includes(query) ||
28
- opportunity.chain.name.toLowerCase().includes(query),
29
- );
30
- }
31
-
32
- // Apply active filters
33
- if (activeFilterIds.length > 0) {
34
- const activeFilterFns = filters
35
- .filter((f) => activeFilterIds.includes(f.id))
36
- .map((f) => f.filterFn);
37
-
38
- result = result.filter((opportunity) =>
39
- activeFilterFns.every((filterFn) => filterFn(opportunity)),
40
- );
41
- }
42
-
43
- return result;
44
- }, [opportunities, searchQuery, activeFilterIds, filters]);
45
- }
@@ -1,85 +0,0 @@
1
- import { useMemo } from "react";
2
- import { OpportunityItemValue } from "../../../widget/opportunity-item";
3
- import { WidgetListGroup } from "../../../widget/widget-list-items";
4
-
5
- interface UseOpportunityGroupingProps {
6
- opportunities: OpportunityItemValue[];
7
- groupBy: "none" | "partner" | "chain";
8
- }
9
-
10
- export function useOpportunityGrouping({
11
- opportunities,
12
- groupBy,
13
- }: UseOpportunityGroupingProps): WidgetListGroup<OpportunityItemValue>[] {
14
- return useMemo(() => {
15
- // First, sort all opportunities by yield (APY) in descending order
16
- const sortedOpportunities = [...opportunities].sort((a, b) => {
17
- const aprA = parseFloat(a.apr?.replace("%", "") || "0");
18
- const aprB = parseFloat(b.apr?.replace("%", "") || "0");
19
- return aprB - aprA;
20
- });
21
-
22
- if (groupBy === "none") {
23
- return [
24
- {
25
- id: "all-opportunities",
26
- items: sortedOpportunities,
27
- },
28
- ];
29
- }
30
-
31
- // Group by the selected field
32
- const groups = sortedOpportunities.reduce(
33
- (acc, opportunity) => {
34
- let groupKey: string;
35
- let groupTitle: string;
36
- let groupIcon: string | undefined;
37
-
38
- switch (groupBy) {
39
- case "partner":
40
- groupKey = opportunity.partner?.name || "other";
41
- groupTitle = opportunity.partner?.name || "Other";
42
- groupIcon = opportunity.partner?.icon;
43
- break;
44
- case "chain":
45
- groupKey = opportunity.chain.name || "other";
46
- groupTitle = opportunity.chain.name || "Other";
47
- groupIcon = opportunity.chain.icon;
48
- break;
49
- default:
50
- groupKey = "all";
51
- groupTitle = "All";
52
- }
53
-
54
- if (!acc[groupKey]) {
55
- acc[groupKey] = {
56
- id: groupKey,
57
- title: groupTitle,
58
- icon: groupIcon,
59
- items: [],
60
- };
61
- }
62
- acc[groupKey].items.push(opportunity);
63
- return acc;
64
- },
65
- {} as Record<string, WidgetListGroup<OpportunityItemValue>>,
66
- );
67
-
68
- // Sort groups by the highest APY in each group
69
- const groupsArray = Object.values(groups);
70
- groupsArray.sort((a, b) => {
71
- // Get the highest APY in each group (items are already sorted)
72
- const maxYieldA =
73
- a.items.length > 0
74
- ? parseFloat(a.items[0].apr?.replace("%", "") || "0")
75
- : 0;
76
- const maxYieldB =
77
- b.items.length > 0
78
- ? parseFloat(b.items[0].apr?.replace("%", "") || "0")
79
- : 0;
80
- return maxYieldB - maxYieldA;
81
- });
82
-
83
- return groupsArray;
84
- }, [opportunities, groupBy]);
85
- }
@@ -1 +0,0 @@
1
- export * from "./opportunity-list";