@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.
- package/.turbo/turbo-build.log +54 -56
- package/CHANGELOG.md +10 -0
- package/dist/index.cjs +17 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5222 -5320
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/components/features/data-table/data-table.d.ts.map +1 -1
- package/dist/types/components/molecules/index.d.ts +1 -1
- package/dist/types/components/molecules/index.d.ts.map +1 -1
- package/dist/types/components/molecules/opportunity/index.d.ts +0 -1
- package/dist/types/components/molecules/opportunity/index.d.ts.map +1 -1
- package/dist/types/components/molecules/route-details.d.ts +1 -1
- package/dist/types/components/molecules/route-details.d.ts.map +1 -1
- package/dist/types/components/molecules/swap-input.d.ts.map +1 -1
- package/dist/types/components/molecules/token-selector.d.ts +3 -2
- package/dist/types/components/molecules/token-selector.d.ts.map +1 -1
- package/dist/types/components/molecules/widget/base-selector.d.ts +9 -3
- package/dist/types/components/molecules/widget/base-selector.d.ts.map +1 -1
- package/dist/types/components/ui/combobox.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/components/features/data-table/data-table.tsx +134 -82
- package/src/components/molecules/index.ts +1 -1
- package/src/components/molecules/opportunity/index.ts +0 -1
- package/src/components/molecules/route-details.tsx +6 -4
- package/src/components/molecules/swap-input.tsx +2 -8
- package/src/components/molecules/token-selector.tsx +15 -4
- package/src/components/molecules/widget/base-selector.tsx +23 -6
- package/src/components/ui/combobox.tsx +67 -34
- package/src/components/ui/tooltip.tsx +1 -1
- package/dist/types/components/molecules/opportunity/opportunity-list/hooks/index.d.ts +0 -3
- package/dist/types/components/molecules/opportunity/opportunity-list/hooks/index.d.ts.map +0 -1
- package/dist/types/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-filtering.d.ts +0 -11
- package/dist/types/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-filtering.d.ts.map +0 -1
- package/dist/types/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-grouping.d.ts +0 -9
- package/dist/types/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-grouping.d.ts.map +0 -1
- package/dist/types/components/molecules/opportunity/opportunity-list/index.d.ts +0 -2
- package/dist/types/components/molecules/opportunity/opportunity-list/index.d.ts.map +0 -1
- package/dist/types/components/molecules/opportunity/opportunity-list/opportunity-list.d.ts +0 -22
- package/dist/types/components/molecules/opportunity/opportunity-list/opportunity-list.d.ts.map +0 -1
- package/src/components/molecules/opportunity/opportunity-list/hooks/index.ts +0 -2
- package/src/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-filtering.ts +0 -45
- package/src/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-grouping.ts +0 -85
- package/src/components/molecules/opportunity/opportunity-list/index.ts +0 -1
- 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
|
-
|
|
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 &&
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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 {
|
|
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<
|
|
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,>(
|
|
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,
|
|
337
|
+
[resetToDefault, onValueChange],
|
|
328
338
|
);
|
|
329
339
|
|
|
330
|
-
const [screenSize, setScreenSize] = React.useState<
|
|
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
|
-
|
|
395
|
+
|
|
384
396
|
const valueSet = new Set<T>();
|
|
385
|
-
const duplicates: T[] = [];
|
|
386
397
|
const uniqueOptions: ComboboxOption<T>[] = [];
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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,
|
|
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(
|
|
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">
|
|
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
|
|
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">
|
|
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>
|
|
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 ?
|
|
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 +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 +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
|
package/dist/types/components/molecules/opportunity/opportunity-list/opportunity-list.d.ts.map
DELETED
|
@@ -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"}
|
package/src/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-filtering.ts
DELETED
|
@@ -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
|
-
}
|
package/src/components/molecules/opportunity/opportunity-list/hooks/use-opportunity-grouping.ts
DELETED
|
@@ -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";
|