@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.
@@ -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, useEffect } from "react";
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 isProductSelected = !!productId && productId.trim() !== "";
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 || !isProductSelected}
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, useEffect } from "react";
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 isProductSelected = !!productId && productId.trim() !== "";
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 || !isProductSelected}
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
- // chainId: isChainSelected ? chainId : undefined,
40
- limit: 100,
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
- // chainId: isChainSelected ? chainId : undefined,
43
- limit: 100,
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 { Popover, PopoverContent, PopoverTrigger, Button, cn, Label, Badge } from "@turtleclub/ui";
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?: string;
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 asChild>
49
- <Button variant="outline" className={cn(className)}>
50
- <SlidersHorizontal className="mr-2 h-4 w-4" />
51
- {triggerLabel}
52
- </Button>
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="start"
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
- {layout === "grid" ? (
109
- <FiltersGrid filters={filters} columns={columns} className={popoverClassName} />
110
- ) : (
111
- <FiltersPopover
112
- filters={filters}
113
- triggerLabel={triggerLabel}
114
- columns={popoverColumns}
115
- className={popoverClassName}
116
- popoverClassName={popoverContentClassName}
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 && (