|
| 1 | +"use client"; |
| 2 | + |
1 | 3 | import { MultiSelect } from "@/components/blocks/multi-select"; |
| 4 | +import { SelectWithSearch } from "@/components/blocks/select-with-search"; |
2 | 5 | import { Badge } from "@/components/ui/badge"; |
3 | | -import { Select } from "chakra-react-select"; |
4 | | -import type { SizeProp } from "chakra-react-select"; |
5 | 6 | import { useCallback, useMemo } from "react"; |
6 | | -import { useFormContext } from "react-hook-form"; |
7 | 7 | import { useAllChainsData } from "../../../hooks/chains/allChains"; |
8 | 8 |
|
9 | | -interface NetworkDropdownProps { |
10 | | - useCleanChainName?: boolean; |
11 | | - isDisabled?: boolean; |
12 | | - onSingleChange: (networksEnabled: number) => void; |
13 | | - value: number | undefined; |
14 | | - size?: SizeProp; |
15 | | -} |
16 | | - |
17 | 9 | function cleanChainName(chainName: string) { |
18 | 10 | return chainName.replace("Mainnet", ""); |
19 | 11 | } |
20 | 12 |
|
21 | | -export const NetworkDropdown: React.FC<NetworkDropdownProps> = ({ |
22 | | - useCleanChainName = true, |
23 | | - onSingleChange, |
24 | | - value, |
25 | | - size = "md", |
26 | | -}) => { |
27 | | - const form = useFormContext(); |
28 | | - const { allChains } = useAllChainsData(); |
29 | | - |
30 | | - const options = useMemo(() => { |
31 | | - return allChains.map((chain) => { |
32 | | - return { |
33 | | - label: useCleanChainName |
34 | | - ? cleanChainName(chain.name) |
35 | | - : `${chain.name} (${chain.chainId})`, |
36 | | - value: chain.chainId, |
37 | | - }; |
38 | | - }); |
39 | | - }, [allChains, useCleanChainName]); |
40 | | - |
41 | | - const defaultValues = useMemo(() => { |
42 | | - const networksEnabled = form?.watch( |
43 | | - "networksForDeployment.networksEnabled", |
44 | | - ); |
45 | | - |
46 | | - if (networksEnabled) { |
47 | | - return options.filter(({ value: val }) => |
48 | | - form.watch("networksForDeployment.networksEnabled")?.includes(val), |
49 | | - ); |
50 | | - } |
51 | | - return options; |
52 | | - }, [form, options]); |
53 | | - |
54 | | - return ( |
55 | | - <div className="flex w-full flex-row items-center gap-2"> |
56 | | - <Select |
57 | | - size={size} |
58 | | - placeholder={"Select a network"} |
59 | | - selectedOptionStyle="check" |
60 | | - hideSelectedOptions={false} |
61 | | - options={options} |
62 | | - defaultValue={defaultValues} |
63 | | - onChange={(selectedChain) => { |
64 | | - if (selectedChain) { |
65 | | - if (onSingleChange) { |
66 | | - onSingleChange(selectedChain.value); |
67 | | - } |
68 | | - } |
69 | | - }} |
70 | | - chakraStyles={{ |
71 | | - container: (provided) => ({ |
72 | | - ...provided, |
73 | | - width: "full", |
74 | | - }), |
75 | | - downChevron: (provided) => ({ |
76 | | - ...provided, |
77 | | - color: "hsl(var(--text-muted-foreground)/50%)", |
78 | | - }), |
79 | | - dropdownIndicator: (provided) => ({ |
80 | | - ...provided, |
81 | | - color: "hsl(var(--text-muted-foreground)/50%)", |
82 | | - }), |
83 | | - control: (provided) => ({ |
84 | | - ...provided, |
85 | | - borderRadius: "lg", |
86 | | - minWidth: "178px", |
87 | | - }), |
88 | | - }} |
89 | | - value={options.find(({ value: val }) => val === value)} |
90 | | - /> |
91 | | - </div> |
92 | | - ); |
93 | | -}; |
94 | | - |
95 | 13 | type Option = { label: string; value: string }; |
96 | 14 |
|
97 | 15 | export function MultiNetworkSelector(props: { |
@@ -160,3 +78,68 @@ export function MultiNetworkSelector(props: { |
160 | 78 | /> |
161 | 79 | ); |
162 | 80 | } |
| 81 | + |
| 82 | +export function SingleNetworkSelector(props: { |
| 83 | + chainId: number | undefined; |
| 84 | + onChange: (chainId: number) => void; |
| 85 | +}) { |
| 86 | + const { allChains, idToChain } = useAllChainsData(); |
| 87 | + |
| 88 | + const options = useMemo(() => { |
| 89 | + return allChains.map((chain) => { |
| 90 | + return { |
| 91 | + label: chain.name, |
| 92 | + value: String(chain.chainId), |
| 93 | + }; |
| 94 | + }); |
| 95 | + }, [allChains]); |
| 96 | + |
| 97 | + const searchFn = useCallback( |
| 98 | + (option: Option, searchValue: string) => { |
| 99 | + const chain = idToChain.get(Number(option.value)); |
| 100 | + if (!chain) { |
| 101 | + return false; |
| 102 | + } |
| 103 | + |
| 104 | + if (Number.isInteger(Number.parseInt(searchValue))) { |
| 105 | + return String(chain.chainId).startsWith(searchValue); |
| 106 | + } |
| 107 | + return chain.name.toLowerCase().includes(searchValue.toLowerCase()); |
| 108 | + }, |
| 109 | + [idToChain], |
| 110 | + ); |
| 111 | + |
| 112 | + const renderOption = useCallback( |
| 113 | + (option: Option) => { |
| 114 | + const chain = idToChain.get(Number(option.value)); |
| 115 | + if (!chain) { |
| 116 | + return option.label; |
| 117 | + } |
| 118 | + |
| 119 | + return ( |
| 120 | + <div className="flex justify-between gap-4"> |
| 121 | + <span className="grow truncate text-left">{chain.name}</span> |
| 122 | + <Badge variant="outline" className="gap-2"> |
| 123 | + <span className="text-muted-foreground">Chain ID</span> |
| 124 | + {chain.chainId} |
| 125 | + </Badge> |
| 126 | + </div> |
| 127 | + ); |
| 128 | + }, |
| 129 | + [idToChain], |
| 130 | + ); |
| 131 | + |
| 132 | + return ( |
| 133 | + <SelectWithSearch |
| 134 | + searchPlaceholder="Search by Name or Chain Id" |
| 135 | + value={String(props.chainId)} |
| 136 | + options={options} |
| 137 | + onValueChange={(chainId) => { |
| 138 | + props.onChange(Number(chainId)); |
| 139 | + }} |
| 140 | + placeholder="Select Chain" |
| 141 | + overrideSearchFn={searchFn} |
| 142 | + renderOption={renderOption} |
| 143 | + /> |
| 144 | + ); |
| 145 | +} |
0 commit comments