@turtleclub/core 0.1.0-beta.12 → 0.1.0-beta.121
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 +195 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -10
- package/dist/index.d.ts +19 -10
- package/dist/index.js +234 -85
- package/dist/index.js.map +1 -1
- package/package.json +11 -9
- package/src/filters/RangeSliderFilter.tsx +135 -8
- package/src/selectors/ChainSelector.tsx +4 -1
- package/src/selectors/ChainsSelector.tsx +4 -1
- package/src/selectors/OpportunitiesSelector.tsx +4 -27
- package/src/selectors/OpportunitySelector.tsx +4 -27
- package/src/selectors/TokenSelector.tsx +2 -2
- package/src/selectors/TokensSelector.tsx +2 -2
- package/src/wrappers/FiltersPopover.tsx +36 -8
- package/src/wrappers/FiltersWrapper.tsx +36 -11
|
@@ -30,7 +30,10 @@ export function ChainSelector({
|
|
|
30
30
|
closeOnSelect = true,
|
|
31
31
|
searchable = true,
|
|
32
32
|
}: ChainSelectorProps) {
|
|
33
|
-
const { chains, isLoading } = useSupportedChains(
|
|
33
|
+
const { chains, isLoading } = useSupportedChains({
|
|
34
|
+
page: 1,
|
|
35
|
+
limit: 100, // High limit to fetch all chains
|
|
36
|
+
});
|
|
34
37
|
|
|
35
38
|
const chainOptions = useMemo(() => {
|
|
36
39
|
if (!chains || chains.length === 0) return [];
|
|
@@ -33,7 +33,10 @@ export function ChainsSelector({
|
|
|
33
33
|
closeOnSelect = true,
|
|
34
34
|
searchable = true,
|
|
35
35
|
}: ChainSelectorProps) {
|
|
36
|
-
const { chains, isLoading } = useSupportedChains(
|
|
36
|
+
const { chains, isLoading } = useSupportedChains({
|
|
37
|
+
page: 1,
|
|
38
|
+
limit: 100, // High limit to fetch all chains
|
|
39
|
+
});
|
|
37
40
|
|
|
38
41
|
const chainOptions = useMemo(() => {
|
|
39
42
|
if (!chains || chains.length === 0) return [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useMemo
|
|
3
|
+
import { useMemo } from "react";
|
|
4
4
|
import { MultiSelect } from "@turtleclub/ui";
|
|
5
5
|
import { useOpportunities } from "@turtleclub/hooks";
|
|
6
6
|
|
|
@@ -9,8 +9,6 @@ interface OpportunitySelectorProps {
|
|
|
9
9
|
value: string[];
|
|
10
10
|
/** Callback when selection changes */
|
|
11
11
|
onValueChange: (values: string[]) => void;
|
|
12
|
-
/** Selected product ID to filter opportunities */
|
|
13
|
-
productId?: string;
|
|
14
12
|
/** Placeholder text */
|
|
15
13
|
placeholder?: string;
|
|
16
14
|
/** Whether the selector is disabled */
|
|
@@ -28,7 +26,6 @@ interface OpportunitySelectorProps {
|
|
|
28
26
|
export function OpportunitiesSelector({
|
|
29
27
|
value,
|
|
30
28
|
onValueChange,
|
|
31
|
-
productId,
|
|
32
29
|
placeholder = "Select opportunities",
|
|
33
30
|
disabled = false,
|
|
34
31
|
className,
|
|
@@ -36,14 +33,7 @@ export function OpportunitiesSelector({
|
|
|
36
33
|
closeOnSelect = true,
|
|
37
34
|
searchable = true,
|
|
38
35
|
}: OpportunitySelectorProps) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const { data: opportunitiesData, isLoading } = useOpportunities({
|
|
42
|
-
filters: {
|
|
43
|
-
productId,
|
|
44
|
-
},
|
|
45
|
-
enabled: isProductSelected,
|
|
46
|
-
});
|
|
36
|
+
const { data: opportunitiesData, isLoading } = useOpportunities();
|
|
47
37
|
|
|
48
38
|
const opportunities = useMemo(
|
|
49
39
|
() => opportunitiesData?.opportunities ?? [],
|
|
@@ -73,27 +63,14 @@ export function OpportunitiesSelector({
|
|
|
73
63
|
}));
|
|
74
64
|
}, [opportunities]);
|
|
75
65
|
|
|
76
|
-
// Clear selection when product changes
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (!isProductSelected && value.length > 0) {
|
|
79
|
-
onValueChange([]);
|
|
80
|
-
}
|
|
81
|
-
}, [isProductSelected, value, onValueChange]);
|
|
82
|
-
|
|
83
66
|
return (
|
|
84
67
|
<MultiSelect
|
|
85
68
|
searchable={searchable}
|
|
86
69
|
options={opportunityOptions}
|
|
87
70
|
value={value}
|
|
88
71
|
onValueChange={onValueChange}
|
|
89
|
-
disabled={disabled || isLoading
|
|
90
|
-
placeholder={
|
|
91
|
-
!isProductSelected
|
|
92
|
-
? "Select a product first"
|
|
93
|
-
: isLoading
|
|
94
|
-
? "Loading opportunities..."
|
|
95
|
-
: placeholder
|
|
96
|
-
}
|
|
72
|
+
disabled={disabled || isLoading}
|
|
73
|
+
placeholder={isLoading ? "Loading opportunities..." : placeholder}
|
|
97
74
|
closeOnSelect={closeOnSelect}
|
|
98
75
|
maxCount={maxCount}
|
|
99
76
|
className={className}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useMemo
|
|
3
|
+
import { useMemo } from "react";
|
|
4
4
|
import { Combobox } from "@turtleclub/ui";
|
|
5
5
|
import { useOpportunities } from "@turtleclub/hooks";
|
|
6
6
|
|
|
@@ -9,8 +9,6 @@ interface OpportunitySelectorProps {
|
|
|
9
9
|
value: string;
|
|
10
10
|
/** Callback when selection changes */
|
|
11
11
|
onValueChange: (value: string) => void;
|
|
12
|
-
/** Selected product ID to filter opportunities */
|
|
13
|
-
productId?: string;
|
|
14
12
|
/** Placeholder text */
|
|
15
13
|
placeholder?: string;
|
|
16
14
|
/** Whether the selector is disabled */
|
|
@@ -26,21 +24,13 @@ interface OpportunitySelectorProps {
|
|
|
26
24
|
export function OpportunitySelector({
|
|
27
25
|
value,
|
|
28
26
|
onValueChange,
|
|
29
|
-
productId,
|
|
30
27
|
placeholder = "Select opportunity",
|
|
31
28
|
disabled = false,
|
|
32
29
|
className,
|
|
33
30
|
closeOnSelect = true,
|
|
34
31
|
searchable = true,
|
|
35
32
|
}: OpportunitySelectorProps) {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const { data: opportunitiesData, isLoading } = useOpportunities({
|
|
39
|
-
filters: {
|
|
40
|
-
productId,
|
|
41
|
-
},
|
|
42
|
-
enabled: isProductSelected,
|
|
43
|
-
});
|
|
33
|
+
const { data: opportunitiesData, isLoading } = useOpportunities();
|
|
44
34
|
|
|
45
35
|
const opportunities = useMemo(
|
|
46
36
|
() => opportunitiesData?.opportunities ?? [],
|
|
@@ -70,27 +60,14 @@ export function OpportunitySelector({
|
|
|
70
60
|
}));
|
|
71
61
|
}, [opportunities]);
|
|
72
62
|
|
|
73
|
-
// Clear selection when product changes
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
if (!isProductSelected && value) {
|
|
76
|
-
onValueChange("");
|
|
77
|
-
}
|
|
78
|
-
}, [isProductSelected, value, onValueChange]);
|
|
79
|
-
|
|
80
63
|
return (
|
|
81
64
|
<Combobox
|
|
82
65
|
searchable={searchable}
|
|
83
66
|
options={opportunityOptions}
|
|
84
67
|
value={value}
|
|
85
68
|
onValueChange={onValueChange}
|
|
86
|
-
disabled={disabled || isLoading
|
|
87
|
-
placeholder={
|
|
88
|
-
!isProductSelected
|
|
89
|
-
? "Select a product first"
|
|
90
|
-
: isLoading
|
|
91
|
-
? "Loading opportunities..."
|
|
92
|
-
: placeholder
|
|
93
|
-
}
|
|
69
|
+
disabled={disabled || isLoading}
|
|
70
|
+
placeholder={isLoading ? "Loading opportunities..." : placeholder}
|
|
94
71
|
closeOnSelect={closeOnSelect}
|
|
95
72
|
className={className}
|
|
96
73
|
/>
|
|
@@ -36,8 +36,8 @@ export function TokenSelector({
|
|
|
36
36
|
const isChainSelected = !!chainId && chainId.trim() !== "";
|
|
37
37
|
|
|
38
38
|
const { tokens, isLoading } = useSupportedTokens({
|
|
39
|
-
|
|
40
|
-
limit:
|
|
39
|
+
chainId: isChainSelected ? chainId : "",
|
|
40
|
+
limit: 9000,
|
|
41
41
|
enabled: isChainSelected,
|
|
42
42
|
});
|
|
43
43
|
|
|
@@ -39,8 +39,8 @@ export function TokensSelector({
|
|
|
39
39
|
const isChainSelected = !!chainId && chainId.trim() !== "";
|
|
40
40
|
|
|
41
41
|
const { tokens, isLoading } = useSupportedTokens({
|
|
42
|
-
|
|
43
|
-
limit:
|
|
42
|
+
chainId: isChainSelected ? chainId : "",
|
|
43
|
+
limit: 9000,
|
|
44
44
|
enabled: isChainSelected,
|
|
45
45
|
});
|
|
46
46
|
|
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import React from "react";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Popover,
|
|
6
|
+
PopoverContent,
|
|
7
|
+
PopoverTrigger,
|
|
8
|
+
buttonVariants,
|
|
9
|
+
cn,
|
|
10
|
+
Label,
|
|
11
|
+
Badge,
|
|
12
|
+
} from "@turtleclub/ui";
|
|
5
13
|
import { SlidersHorizontal } from "lucide-react";
|
|
6
14
|
import { FilterConfig } from "./FiltersGrid";
|
|
7
15
|
|
|
8
16
|
export interface FiltersPopoverProps {
|
|
9
17
|
filters: FilterConfig[];
|
|
10
|
-
triggerLabel?:
|
|
18
|
+
triggerLabel?: React.ReactNode;
|
|
11
19
|
columns?: number;
|
|
12
20
|
className?: string;
|
|
13
21
|
popoverClassName?: string;
|
|
14
22
|
sectionClassName?: string;
|
|
23
|
+
align?: "start" | "center" | "end";
|
|
24
|
+
/** Callback function to clear all filters */
|
|
25
|
+
onClearAll?: () => void;
|
|
15
26
|
}
|
|
16
27
|
|
|
17
28
|
export function FiltersPopover({
|
|
@@ -21,6 +32,8 @@ export function FiltersPopover({
|
|
|
21
32
|
className,
|
|
22
33
|
popoverClassName,
|
|
23
34
|
sectionClassName,
|
|
35
|
+
align = "start",
|
|
36
|
+
onClearAll,
|
|
24
37
|
}: FiltersPopoverProps) {
|
|
25
38
|
const enabledFilters = filters.filter((filter) => filter.enabled);
|
|
26
39
|
|
|
@@ -45,19 +58,34 @@ export function FiltersPopover({
|
|
|
45
58
|
|
|
46
59
|
return (
|
|
47
60
|
<Popover>
|
|
48
|
-
<PopoverTrigger
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
<PopoverTrigger
|
|
62
|
+
className={cn(
|
|
63
|
+
buttonVariants({
|
|
64
|
+
variant: "default",
|
|
65
|
+
size: "default",
|
|
66
|
+
border: "bordered",
|
|
67
|
+
}),
|
|
68
|
+
"!bg-neutral-alpha-2",
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
>
|
|
72
|
+
{triggerLabel}
|
|
53
73
|
</PopoverTrigger>
|
|
54
74
|
<PopoverContent
|
|
55
75
|
className={cn("w-auto min-w-[400px] max-w-[600px]", popoverClassName)}
|
|
56
|
-
align=
|
|
76
|
+
align={align}
|
|
57
77
|
>
|
|
58
78
|
<div className="space-y-4">
|
|
59
79
|
<div className="flex items-center justify-between">
|
|
60
80
|
<h4 className="font-medium text-sm">Filters</h4>
|
|
81
|
+
{onClearAll && (
|
|
82
|
+
<button
|
|
83
|
+
onClick={onClearAll}
|
|
84
|
+
className="text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
85
|
+
>
|
|
86
|
+
Reset all
|
|
87
|
+
</button>
|
|
88
|
+
)}
|
|
61
89
|
</div>
|
|
62
90
|
<div className="space-y-4">
|
|
63
91
|
{sectionNames.map((sectionName, sectionIndex) => (
|
|
@@ -18,6 +18,12 @@ export interface FiltersWrapperProps {
|
|
|
18
18
|
className?: string;
|
|
19
19
|
/** Custom className for the active badges section */
|
|
20
20
|
badgesClassName?: string;
|
|
21
|
+
/** Custom className for the slot container */
|
|
22
|
+
slotClassName?: string;
|
|
23
|
+
/** Optional content to render on the left side of filters */
|
|
24
|
+
leftSlot?: React.ReactNode;
|
|
25
|
+
/** Optional content to render on the right side of filters */
|
|
26
|
+
rightSlot?: React.ReactNode;
|
|
21
27
|
|
|
22
28
|
// Grid-specific props
|
|
23
29
|
/** Number of columns for grid layout (default: 3) */
|
|
@@ -32,6 +38,8 @@ export interface FiltersWrapperProps {
|
|
|
32
38
|
popoverClassName?: string;
|
|
33
39
|
/** Custom className for popover content */
|
|
34
40
|
popoverContentClassName?: string;
|
|
41
|
+
/** Alignment for popover (default: "start") */
|
|
42
|
+
align?: "start" | "center" | "end";
|
|
35
43
|
|
|
36
44
|
// Active badges props
|
|
37
45
|
/** Label for clear all button (default: "Clear All") */
|
|
@@ -88,11 +96,15 @@ export function FiltersWrapper({
|
|
|
88
96
|
layout,
|
|
89
97
|
className,
|
|
90
98
|
badgesClassName,
|
|
99
|
+
slotClassName,
|
|
100
|
+
leftSlot,
|
|
101
|
+
rightSlot,
|
|
91
102
|
columns = 3,
|
|
92
103
|
triggerLabel = "Filters",
|
|
93
104
|
popoverColumns = 2,
|
|
94
105
|
popoverClassName,
|
|
95
106
|
popoverContentClassName,
|
|
107
|
+
align = "start",
|
|
96
108
|
clearAllLabel = "Clear All",
|
|
97
109
|
showClearAll = true,
|
|
98
110
|
renderBadge,
|
|
@@ -105,17 +117,30 @@ export function FiltersWrapper({
|
|
|
105
117
|
return (
|
|
106
118
|
<div className={cn("space-y-3", className)}>
|
|
107
119
|
{/* Filter Controls */}
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
<div className={cn("flex items-center gap-2", slotClassName)}>
|
|
121
|
+
{leftSlot}
|
|
122
|
+
<>
|
|
123
|
+
{layout === "grid" ? (
|
|
124
|
+
<FiltersGrid filters={filters} columns={columns} className={popoverClassName} />
|
|
125
|
+
) : (
|
|
126
|
+
<FiltersPopover
|
|
127
|
+
filters={filters}
|
|
128
|
+
triggerLabel={triggerLabel}
|
|
129
|
+
columns={popoverColumns}
|
|
130
|
+
className={popoverClassName}
|
|
131
|
+
popoverClassName={popoverContentClassName}
|
|
132
|
+
align={align}
|
|
133
|
+
onClearAll={() => {
|
|
134
|
+
const allQueryKeys = activeFilters
|
|
135
|
+
.map((filter) => filter.queryKeys)
|
|
136
|
+
.filter((keys): keys is string | string[] => keys !== undefined);
|
|
137
|
+
onClearAll(allQueryKeys);
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
141
|
+
</>
|
|
142
|
+
{rightSlot}
|
|
143
|
+
</div>
|
|
119
144
|
|
|
120
145
|
{/* Active Filter Badges */}
|
|
121
146
|
{showActiveBadges && hasActiveFilters && (
|