@jaw.id/ui 0.0.3 → 0.0.5

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 (66) hide show
  1. package/cjs-error.cjs +3 -0
  2. package/dist/{ccip-BYa9WTP9.js → ccip-nTJDT8tV.js} +1 -1
  3. package/dist/{index-DBYNWeek.js → index-C2yN5Jh0.js} +20536 -20246
  4. package/dist/index.js +1 -1
  5. package/package.json +8 -2
  6. package/.babelrc +0 -12
  7. package/CHANGELOG.md +0 -23
  8. package/components.json +0 -22
  9. package/postcss.config.cjs +0 -6
  10. package/src/components/ConnectDialog/index.tsx +0 -270
  11. package/src/components/ConnectDialog/types.ts +0 -34
  12. package/src/components/DefaultDialog/index.tsx +0 -99
  13. package/src/components/Eip712Dialog/index.tsx +0 -525
  14. package/src/components/Eip712Dialog/types.ts +0 -27
  15. package/src/components/FeeTokenSelector/index.tsx +0 -308
  16. package/src/components/OnboardingDialog/index.tsx +0 -317
  17. package/src/components/OnboardingDialog/types.ts +0 -43
  18. package/src/components/OrSeparator/index.tsx +0 -13
  19. package/src/components/PermissionDialog/index.tsx +0 -598
  20. package/src/components/PermissionDialog/types.ts +0 -77
  21. package/src/components/SignatureDialog/index.tsx +0 -180
  22. package/src/components/SignatureDialog/types.ts +0 -27
  23. package/src/components/SiweDialog/index.tsx +0 -231
  24. package/src/components/SiweDialog/types.ts +0 -34
  25. package/src/components/TransactionDialog/DecodedCalldata.tsx +0 -79
  26. package/src/components/TransactionDialog/index.tsx +0 -663
  27. package/src/components/TransactionDialog/types.ts +0 -54
  28. package/src/components/ui/accordion.tsx +0 -66
  29. package/src/components/ui/avatar.tsx +0 -53
  30. package/src/components/ui/button.tsx +0 -59
  31. package/src/components/ui/card.tsx +0 -92
  32. package/src/components/ui/checkbox.tsx +0 -32
  33. package/src/components/ui/dialog.tsx +0 -183
  34. package/src/components/ui/dropdown-menu.tsx +0 -258
  35. package/src/components/ui/form.tsx +0 -167
  36. package/src/components/ui/input.tsx +0 -60
  37. package/src/components/ui/label.tsx +0 -24
  38. package/src/components/ui/popover.tsx +0 -48
  39. package/src/components/ui/scroll-area.tsx +0 -58
  40. package/src/components/ui/select.tsx +0 -160
  41. package/src/components/ui/separator.tsx +0 -28
  42. package/src/components/ui/sheet.tsx +0 -169
  43. package/src/components/ui/spinner.tsx +0 -18
  44. package/src/components/ui/tabs.tsx +0 -69
  45. package/src/components/ui/tooltip.tsx +0 -61
  46. package/src/hooks/index.ts +0 -5
  47. package/src/hooks/useChainIconURI.tsx +0 -117
  48. package/src/hooks/useDecodedCalldata.ts +0 -128
  49. package/src/hooks/useFeeTokenPrice.tsx +0 -36
  50. package/src/hooks/useGasEstimation.ts +0 -474
  51. package/src/hooks/useIsMobile.ts +0 -36
  52. package/src/icons/index.tsx +0 -114
  53. package/src/index.ts +0 -19
  54. package/src/lib/utils.ts +0 -6
  55. package/src/react/ReactUIHandler.tsx +0 -3004
  56. package/src/react/index.ts +0 -2
  57. package/src/styles.css +0 -210
  58. package/src/utils/formatAddress.ts +0 -24
  59. package/src/utils/index.ts +0 -4
  60. package/src/utils/justaNameInstance.ts +0 -25
  61. package/src/utils/tokenBalance.ts +0 -41
  62. package/src/utils/tokenPrice.ts +0 -58
  63. package/tailwind.config.js +0 -130
  64. package/tsconfig.json +0 -19
  65. package/tsconfig.lib.json +0 -43
  66. package/vite.config.ts +0 -45
@@ -1,160 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as SelectPrimitive from "@radix-ui/react-select"
5
- import { Check, ChevronDown, ChevronUp } from "lucide-react"
6
-
7
- import { cn } from "../../lib/utils"
8
-
9
- const Select = SelectPrimitive.Root
10
-
11
- const SelectGroup = SelectPrimitive.Group
12
-
13
- const SelectValue = SelectPrimitive.Value
14
-
15
- const SelectTrigger = React.forwardRef<
16
- React.ElementRef<typeof SelectPrimitive.Trigger>,
17
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
18
- >(({ className, children, ...props }, ref) => (
19
- <SelectPrimitive.Trigger
20
- ref={ref}
21
- className={cn(
22
- "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
23
- className
24
- )}
25
- {...props}
26
- >
27
- {children}
28
- <SelectPrimitive.Icon asChild>
29
- <ChevronDown className="h-4 w-4 opacity-50" />
30
- </SelectPrimitive.Icon>
31
- </SelectPrimitive.Trigger>
32
- ))
33
- SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
34
-
35
- const SelectScrollUpButton = React.forwardRef<
36
- React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
37
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
38
- >(({ className, ...props }, ref) => (
39
- <SelectPrimitive.ScrollUpButton
40
- ref={ref}
41
- className={cn(
42
- "flex cursor-default items-center justify-center py-1",
43
- className
44
- )}
45
- {...props}
46
- >
47
- <ChevronUp className="h-4 w-4" />
48
- </SelectPrimitive.ScrollUpButton>
49
- ))
50
- SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
51
-
52
- const SelectScrollDownButton = React.forwardRef<
53
- React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
54
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
55
- >(({ className, ...props }, ref) => (
56
- <SelectPrimitive.ScrollDownButton
57
- ref={ref}
58
- className={cn(
59
- "flex cursor-default items-center justify-center py-1",
60
- className
61
- )}
62
- {...props}
63
- >
64
- <ChevronDown className="h-4 w-4" />
65
- </SelectPrimitive.ScrollDownButton>
66
- ))
67
- SelectScrollDownButton.displayName =
68
- SelectPrimitive.ScrollDownButton.displayName
69
-
70
- const SelectContent = React.forwardRef<
71
- React.ElementRef<typeof SelectPrimitive.Content>,
72
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
73
- >(({ className, children, position = "popper", ...props }, ref) => (
74
- <SelectPrimitive.Portal>
75
- <SelectPrimitive.Content
76
- ref={ref}
77
- className={cn(
78
- "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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",
79
- position === "popper" &&
80
- "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
81
- className
82
- )}
83
- position={position}
84
- {...props}
85
- >
86
- <SelectScrollUpButton />
87
- <SelectPrimitive.Viewport
88
- className={cn(
89
- "p-1",
90
- position === "popper" &&
91
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
92
- )}
93
- >
94
- {children}
95
- </SelectPrimitive.Viewport>
96
- <SelectScrollDownButton />
97
- </SelectPrimitive.Content>
98
- </SelectPrimitive.Portal>
99
- ))
100
- SelectContent.displayName = SelectPrimitive.Content.displayName
101
-
102
- const SelectLabel = React.forwardRef<
103
- React.ElementRef<typeof SelectPrimitive.Label>,
104
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
105
- >(({ className, ...props }, ref) => (
106
- <SelectPrimitive.Label
107
- ref={ref}
108
- className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
109
- {...props}
110
- />
111
- ))
112
- SelectLabel.displayName = SelectPrimitive.Label.displayName
113
-
114
- const SelectItem = React.forwardRef<
115
- React.ElementRef<typeof SelectPrimitive.Item>,
116
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
117
- >(({ className, children, ...props }, ref) => (
118
- <SelectPrimitive.Item
119
- ref={ref}
120
- className={cn(
121
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
122
- className
123
- )}
124
- {...props}
125
- >
126
- <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
127
- <SelectPrimitive.ItemIndicator>
128
- <Check className="h-4 w-4" />
129
- </SelectPrimitive.ItemIndicator>
130
- </span>
131
-
132
- <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
133
- </SelectPrimitive.Item>
134
- ))
135
- SelectItem.displayName = SelectPrimitive.Item.displayName
136
-
137
- const SelectSeparator = React.forwardRef<
138
- React.ElementRef<typeof SelectPrimitive.Separator>,
139
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
140
- >(({ className, ...props }, ref) => (
141
- <SelectPrimitive.Separator
142
- ref={ref}
143
- className={cn("-mx-1 my-1 h-px bg-muted", className)}
144
- {...props}
145
- />
146
- ))
147
- SelectSeparator.displayName = SelectPrimitive.Separator.displayName
148
-
149
- export {
150
- Select,
151
- SelectGroup,
152
- SelectValue,
153
- SelectTrigger,
154
- SelectContent,
155
- SelectLabel,
156
- SelectItem,
157
- SelectSeparator,
158
- SelectScrollUpButton,
159
- SelectScrollDownButton,
160
- }
@@ -1,28 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as SeparatorPrimitive from "@radix-ui/react-separator"
5
-
6
- import { cn } from "../../lib/utils"
7
-
8
- function Separator({
9
- className,
10
- orientation = "horizontal",
11
- decorative = true,
12
- ...props
13
- }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
14
- return (
15
- <SeparatorPrimitive.Root
16
- data-slot="separator"
17
- decorative={decorative}
18
- orientation={orientation}
19
- className={cn(
20
- "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
21
- className
22
- )}
23
- {...props}
24
- />
25
- )
26
- }
27
-
28
- export { Separator }
@@ -1,169 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as SheetPrimitive from "@radix-ui/react-dialog"
5
- import { XIcon } from "lucide-react"
6
-
7
- import { cn } from "../../lib/utils"
8
-
9
- function Sheet({
10
- open,
11
- ...props
12
- }: React.ComponentProps<typeof SheetPrimitive.Root>) {
13
- const prevOpenRef = React.useRef(open);
14
-
15
- // Cleanup pointer-events when sheet closes OR unmounts
16
- React.useEffect(() => {
17
- // Track when sheet transitions from open to closed
18
- if (prevOpenRef.current === true && open === false) {
19
- // Wait for close animation to complete (300ms duration-300) plus buffer
20
- const cleanup = setTimeout(() => {
21
- document.body.style.removeProperty('pointer-events');
22
- }, 350);
23
-
24
- prevOpenRef.current = open;
25
- return () => clearTimeout(cleanup);
26
- }
27
-
28
- prevOpenRef.current = open;
29
-
30
- // Cleanup on unmount - remove pointer-events if sheet was open
31
- return () => {
32
- if (open === true) {
33
- setTimeout(() => {
34
- document.body.style.removeProperty('pointer-events');
35
- }, 350);
36
- }
37
- };
38
- }, [open]);
39
-
40
- return <SheetPrimitive.Root data-slot="sheet" open={open} {...props} />
41
- }
42
-
43
- function SheetTrigger({
44
- ...props
45
- }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
46
- return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
47
- }
48
-
49
- function SheetClose({
50
- ...props
51
- }: React.ComponentProps<typeof SheetPrimitive.Close>) {
52
- return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
53
- }
54
-
55
- function SheetPortal({
56
- ...props
57
- }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
58
- return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
59
- }
60
-
61
- function SheetOverlay({
62
- className,
63
- ...props
64
- }: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
65
- return (
66
- <SheetPrimitive.Overlay
67
- data-slot="sheet-overlay"
68
- className={cn(
69
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
70
- className
71
- )}
72
- {...props}
73
- />
74
- )
75
- }
76
-
77
- function SheetContent({
78
- className,
79
- children,
80
- side = "right",
81
- ...props
82
- }: React.ComponentProps<typeof SheetPrimitive.Content> & {
83
- side?: "top" | "right" | "bottom" | "left"
84
- }) {
85
- return (
86
- <SheetPortal>
87
- <SheetOverlay />
88
- <SheetPrimitive.Content
89
- data-slot="sheet-content"
90
- className={cn(
91
- "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
92
- side === "right" &&
93
- "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
94
- side === "left" &&
95
- "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
96
- side === "top" &&
97
- "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
98
- side === "bottom" &&
99
- "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
100
- className
101
- )}
102
- {...props}
103
- >
104
- {children}
105
- <SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
106
- <XIcon className="size-4" />
107
- <span className="sr-only">Close</span>
108
- </SheetPrimitive.Close>
109
- </SheetPrimitive.Content>
110
- </SheetPortal>
111
- )
112
- }
113
-
114
- function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
115
- return (
116
- <div
117
- data-slot="sheet-header"
118
- className={cn("flex flex-col gap-1.5 p-4", className)}
119
- {...props}
120
- />
121
- )
122
- }
123
-
124
- function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
125
- return (
126
- <div
127
- data-slot="sheet-footer"
128
- className={cn("mt-auto flex flex-col gap-2 p-4", className)}
129
- {...props}
130
- />
131
- )
132
- }
133
-
134
- function SheetTitle({
135
- className,
136
- ...props
137
- }: React.ComponentProps<typeof SheetPrimitive.Title>) {
138
- return (
139
- <SheetPrimitive.Title
140
- data-slot="sheet-title"
141
- className={cn("text-foreground font-semibold", className)}
142
- {...props}
143
- />
144
- )
145
- }
146
-
147
- function SheetDescription({
148
- className,
149
- ...props
150
- }: React.ComponentProps<typeof SheetPrimitive.Description>) {
151
- return (
152
- <SheetPrimitive.Description
153
- data-slot="sheet-description"
154
- className={cn("text-muted-foreground text-sm", className)}
155
- {...props}
156
- />
157
- )
158
- }
159
-
160
- export {
161
- Sheet,
162
- SheetTrigger,
163
- SheetClose,
164
- SheetContent,
165
- SheetHeader,
166
- SheetFooter,
167
- SheetTitle,
168
- SheetDescription,
169
- }
@@ -1,18 +0,0 @@
1
- import { Loader2Icon } from "lucide-react"
2
-
3
- import { cn } from "../../lib/utils"
4
-
5
- function Spinner({ className, ...props }: React.ComponentProps<"div">) {
6
- return (
7
- <div
8
- role="status"
9
- aria-label="Loading"
10
- className={cn("size-4 animate-spin", className)}
11
- {...props}
12
- >
13
- <Loader2Icon className="size-full" />
14
- </div>
15
- )
16
- }
17
-
18
- export { Spinner }
@@ -1,69 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as TabsPrimitive from "@radix-ui/react-tabs"
5
-
6
- import { cn } from "../../lib/utils"
7
-
8
- function Tabs({
9
- className,
10
- ...props
11
- }: React.ComponentProps<typeof TabsPrimitive.Root>) {
12
- return (
13
- <TabsPrimitive.Root
14
- data-slot="tabs"
15
- className={cn("flex flex-col", className)}
16
- {...props}
17
- />
18
- )
19
- }
20
-
21
- function TabsList({
22
- className,
23
- ...props
24
- }: React.ComponentProps<typeof TabsPrimitive.List>) {
25
- return (
26
- <TabsPrimitive.List
27
- data-slot="tabs-list"
28
- className={cn(
29
- "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-1",
30
- className
31
- )}
32
- {...props}
33
- />
34
- )
35
- }
36
-
37
- function TabsTrigger({
38
- className,
39
- ...props
40
- }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
41
- return (
42
- <TabsPrimitive.Trigger
43
- data-slot="tabs-trigger"
44
- className={cn(
45
- "data-[state=active]:bg-background data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-[color,background-color,box-shadow] outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm",
46
- className
47
- )}
48
- {...props}
49
- />
50
- )
51
- }
52
-
53
- function TabsContent({
54
- className,
55
- ...props
56
- }: React.ComponentProps<typeof TabsPrimitive.Content>) {
57
- return (
58
- <TabsPrimitive.Content
59
- data-slot="tabs-content"
60
- className={cn(
61
- "focus-visible:border-ring focus-visible:ring-ring/50 mt-2 outline-none focus-visible:ring-[3px]",
62
- className
63
- )}
64
- {...props}
65
- />
66
- )
67
- }
68
-
69
- export { Tabs, TabsList, TabsTrigger, TabsContent }
@@ -1,61 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as TooltipPrimitive from "@radix-ui/react-tooltip"
5
-
6
- import { cn } from "../../lib/utils"
7
-
8
- function TooltipProvider({
9
- delayDuration = 0,
10
- ...props
11
- }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12
- return (
13
- <TooltipPrimitive.Provider
14
- data-slot="tooltip-provider"
15
- delayDuration={delayDuration}
16
- {...props}
17
- />
18
- )
19
- }
20
-
21
- function Tooltip({
22
- ...props
23
- }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
24
- return (
25
- <TooltipProvider>
26
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
27
- </TooltipProvider>
28
- )
29
- }
30
-
31
- function TooltipTrigger({
32
- ...props
33
- }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
34
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
35
- }
36
-
37
- function TooltipContent({
38
- className,
39
- sideOffset = 0,
40
- children,
41
- ...props
42
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
43
- return (
44
- <TooltipPrimitive.Portal>
45
- <TooltipPrimitive.Content
46
- data-slot="tooltip-content"
47
- sideOffset={sideOffset}
48
- className={cn(
49
- "bg-primary text-primary-foreground 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 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
50
- className
51
- )}
52
- {...props}
53
- >
54
- {children}
55
- <TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
56
- </TooltipPrimitive.Content>
57
- </TooltipPrimitive.Portal>
58
- )
59
- }
60
-
61
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -1,5 +0,0 @@
1
- export * from './useIsMobile';
2
- export * from './useChainIconURI';
3
- export * from './useFeeTokenPrice';
4
- export * from './useGasEstimation';
5
- export * from './useDecodedCalldata';
@@ -1,117 +0,0 @@
1
- import { JSX, useState, useEffect, useMemo } from 'react';
2
- import { handleGetCapabilitiesRequest, type ChainMetadataCapability } from '@jaw.id/core';
3
-
4
- // Simple in-memory cache for chain icons to avoid redundant API calls
5
- const chainIconCache = new Map<string, string | null>();
6
-
7
- /**
8
- * Hook to fetch chain icon from wallet_getCapabilities chainMetadata
9
- * Returns a JSX element (img or fallback) similar to useChainIcon
10
- *
11
- * @param chainId - The chain ID to get the icon for
12
- * @param apiKey - The API key for authentication
13
- * @param size - The size of the icon in pixels (default: 24)
14
- * @returns JSX.Element - The chain icon or a fallback element
15
- */
16
- export const useChainIconURI = (chainId: number, apiKey?: string, size?: number): JSX.Element => {
17
- const iconSize = size ?? 24;
18
- const cacheKey = `${chainId}-${apiKey}`;
19
-
20
- const [iconURI, setIconURI] = useState<string | null>(() => {
21
- // Check cache first
22
- return chainIconCache.get(cacheKey) ?? null;
23
- });
24
- const [isLoading, setIsLoading] = useState(!chainIconCache.has(cacheKey));
25
-
26
- useEffect(() => {
27
- if (!apiKey || !chainId) {
28
- setIsLoading(false);
29
- return;
30
- }
31
-
32
- // If already cached, don't refetch
33
- if (chainIconCache.has(cacheKey)) {
34
- setIconURI(chainIconCache.get(cacheKey) ?? null);
35
- setIsLoading(false);
36
- return;
37
- }
38
-
39
- let isMounted = true;
40
-
41
- const fetchCapabilities = async () => {
42
- setIsLoading(true);
43
- try {
44
- const chainIdHex = `0x${chainId.toString(16)}` as `0x${string}`;
45
- const capabilities = await handleGetCapabilitiesRequest(
46
- { method: 'wallet_getCapabilities', params: [undefined, [chainIdHex]] },
47
- apiKey,
48
- true // showTestnets to get all chains
49
- );
50
-
51
- if (isMounted) {
52
- const chainCapabilities = capabilities[chainIdHex];
53
- const chainMetadata = chainCapabilities?.chainMetadata as ChainMetadataCapability | undefined;
54
- const icon = chainMetadata?.icon ?? null;
55
-
56
- // Cache the result
57
- chainIconCache.set(cacheKey, icon);
58
- setIconURI(icon);
59
- setIsLoading(false);
60
- }
61
- } catch (error) {
62
- console.warn(`Failed to fetch capabilities for chain ${chainId}:`, error);
63
- if (isMounted) {
64
- // Cache null to prevent repeated failed requests
65
- chainIconCache.set(cacheKey, null);
66
- setIconURI(null);
67
- setIsLoading(false);
68
- }
69
- }
70
- };
71
-
72
- fetchCapabilities();
73
-
74
- return () => {
75
- isMounted = false;
76
- };
77
- }, [chainId, apiKey, cacheKey]);
78
-
79
- // Memoize the JSX to prevent unnecessary re-renders
80
- const icon = useMemo(() => {
81
- // If we have a URI from capabilities, use it
82
- if (iconURI) {
83
- return (
84
- <img
85
- src={iconURI}
86
- alt={`Chain ${chainId} icon`}
87
- style={{ width: iconSize, height: iconSize, minWidth: iconSize, borderRadius: '50%' }}
88
- />
89
- );
90
- }
91
-
92
- // Show loading state or fallback
93
- return (
94
- <div
95
- style={{
96
- backgroundColor: isLoading ? '#f0f0f0' : '#e0e0e0',
97
- borderColor: '#ccc',
98
- border: '1px solid #ccc',
99
- display: 'flex',
100
- height: `${iconSize}px`,
101
- width: `${iconSize}px`,
102
- minWidth: `${iconSize}px`,
103
- alignItems: 'center',
104
- justifyContent: 'center',
105
- textAlign: 'center',
106
- borderRadius: '50%',
107
- fontSize: `${Math.max(10, iconSize / 3)}px`,
108
- color: '#666',
109
- }}
110
- >
111
- {isLoading ? '...' : '?'}
112
- </div>
113
- );
114
- }, [iconURI, chainId, iconSize, isLoading]);
115
-
116
- return icon;
117
- };