rujira.ui 0.1.0

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 (303) hide show
  1. package/.eslintrc.cjs +21 -0
  2. package/.tool-versions +1 -0
  3. package/i18n/.github/workflows/validate.yml +13 -0
  4. package/i18n/README.md +7 -0
  5. package/i18n/translations/template.json +448 -0
  6. package/i18n/update-template.js +19 -0
  7. package/i18n/validate.js +10 -0
  8. package/lib/esm/i18n/translations/template.json +448 -0
  9. package/lib/esm/src/components/buttons/Button.js +37 -0
  10. package/lib/esm/src/components/buttons/Popout.js +56 -0
  11. package/lib/esm/src/components/buttons/TxButton.js +42 -0
  12. package/lib/esm/src/components/buttons/__Popout.js +34 -0
  13. package/lib/esm/src/components/cards/Card.js +8 -0
  14. package/lib/esm/src/components/cards/GradientCard.js +8 -0
  15. package/lib/esm/src/components/footer/Footer.js +11 -0
  16. package/lib/esm/src/components/header/Accounts.js +211 -0
  17. package/lib/esm/src/components/header/Header.js +69 -0
  18. package/lib/esm/src/components/header/QuickLauncher.js +10 -0
  19. package/lib/esm/src/components/header/ResolveLink.js +13 -0
  20. package/lib/esm/src/components/icons/IconDenom.js +358 -0
  21. package/lib/esm/src/components/icons/Icons.js +228 -0
  22. package/lib/esm/src/components/icons/Networks.js +32 -0
  23. package/lib/esm/src/components/icons/Wallets.js +62 -0
  24. package/lib/esm/src/components/inputs/Checkbox.js +12 -0
  25. package/lib/esm/src/components/inputs/DecimalInput.js +35 -0
  26. package/lib/esm/src/components/inputs/DenomInput.js +22 -0
  27. package/lib/esm/src/components/inputs/DenomSelect.js +66 -0
  28. package/lib/esm/src/components/inputs/Input.js +25 -0
  29. package/lib/esm/src/components/inputs/Numeric.js +18 -0
  30. package/lib/esm/src/components/inputs/Radio.js +12 -0
  31. package/lib/esm/src/components/inputs/Select.js +29 -0
  32. package/lib/esm/src/components/inputs/Textarea.js +25 -0
  33. package/lib/esm/src/components/inputs/Toggle.js +13 -0
  34. package/lib/esm/src/components/loader/Loader.js +3 -0
  35. package/lib/esm/src/components/logos/RujiraLogo.js +2 -0
  36. package/lib/esm/src/components/notices/Warning.js +10 -0
  37. package/lib/esm/src/components/numbers/Decimal.js +14 -0
  38. package/lib/esm/src/components/numbers/Fiat.js +19 -0
  39. package/lib/esm/src/components/progress/Progress.js +9 -0
  40. package/lib/esm/src/components/slider/Slider.js +5 -0
  41. package/lib/esm/src/context/Affiliate.js +34 -0
  42. package/lib/esm/src/context/GlobalModal.js +35 -0
  43. package/lib/esm/src/d.js +1 -0
  44. package/lib/esm/src/helpers/index.js +65 -0
  45. package/lib/esm/src/helpers/number.js +8 -0
  46. package/lib/esm/src/hooks/useClickOutside.js +19 -0
  47. package/lib/esm/src/hooks/useLocalStorage.js +12 -0
  48. package/lib/esm/src/hooks/useQueryParam.js +31 -0
  49. package/lib/esm/src/hooks/useWindowSize.js +19 -0
  50. package/lib/esm/src/i18n/i18n.js +90 -0
  51. package/lib/esm/src/i18n/index.js +1 -0
  52. package/lib/esm/src/index.js +38 -0
  53. package/lib/esm/src/services/account.js +42 -0
  54. package/lib/esm/tsconfig.tsbuildinfo +1 -0
  55. package/package.json +47 -0
  56. package/src/assets/ghost-tokens/akt.png +0 -0
  57. package/src/assets/ghost-tokens/ampkuji.png +0 -0
  58. package/src/assets/ghost-tokens/arb.png +0 -0
  59. package/src/assets/ghost-tokens/atom.png +0 -0
  60. package/src/assets/ghost-tokens/axlusdc.png +0 -0
  61. package/src/assets/ghost-tokens/cro.png +0 -0
  62. package/src/assets/ghost-tokens/dot.png +0 -0
  63. package/src/assets/ghost-tokens/fet.png +0 -0
  64. package/src/assets/ghost-tokens/fuzn.png +0 -0
  65. package/src/assets/ghost-tokens/glmr.png +0 -0
  66. package/src/assets/ghost-tokens/gpaxg.png +0 -0
  67. package/src/assets/ghost-tokens/inj.png +0 -0
  68. package/src/assets/ghost-tokens/juno.png +0 -0
  69. package/src/assets/ghost-tokens/kuji.png +0 -0
  70. package/src/assets/ghost-tokens/luna.png +0 -0
  71. package/src/assets/ghost-tokens/lunc.png +0 -0
  72. package/src/assets/ghost-tokens/mnta.png +0 -0
  73. package/src/assets/ghost-tokens/ntrn.png +0 -0
  74. package/src/assets/ghost-tokens/osmo.png +0 -0
  75. package/src/assets/ghost-tokens/scrt.png +0 -0
  76. package/src/assets/ghost-tokens/shd.png +0 -0
  77. package/src/assets/ghost-tokens/sol.png +0 -0
  78. package/src/assets/ghost-tokens/stars.png +0 -0
  79. package/src/assets/ghost-tokens/statom.png +0 -0
  80. package/src/assets/ghost-tokens/stosmo.png +0 -0
  81. package/src/assets/ghost-tokens/usdc.png +0 -0
  82. package/src/assets/ghost-tokens/usk.png +0 -0
  83. package/src/assets/ghost-tokens/wavax.png +0 -0
  84. package/src/assets/ghost-tokens/wbnb.png +0 -0
  85. package/src/assets/ghost-tokens/wbtc.png +0 -0
  86. package/src/assets/ghost-tokens/weth.png +0 -0
  87. package/src/assets/ghost-tokens/wftm.png +0 -0
  88. package/src/assets/ghost-tokens/wglmr.png +0 -0
  89. package/src/assets/ghost-tokens/wmatic.png +0 -0
  90. package/src/assets/mono.woff +0 -0
  91. package/src/assets/tokens/acre.png +0 -0
  92. package/src/assets/tokens/akt.png +0 -0
  93. package/src/assets/tokens/amber.png +0 -0
  94. package/src/assets/tokens/ampkuji.png +0 -0
  95. package/src/assets/tokens/ampluna.png +0 -0
  96. package/src/assets/tokens/ampmnta.png +0 -0
  97. package/src/assets/tokens/ampwhale.png +0 -0
  98. package/src/assets/tokens/andr.png +0 -0
  99. package/src/assets/tokens/aqla.png +0 -0
  100. package/src/assets/tokens/aqua.png +0 -0
  101. package/src/assets/tokens/arb.png +0 -0
  102. package/src/assets/tokens/arch.png +0 -0
  103. package/src/assets/tokens/astro.png +0 -0
  104. package/src/assets/tokens/atom.png +0 -0
  105. package/src/assets/tokens/auto.png +0 -0
  106. package/src/assets/tokens/avax.png +0 -0
  107. package/src/assets/tokens/axl.png +0 -0
  108. package/src/assets/tokens/axlusdc.png +0 -0
  109. package/src/assets/tokens/axlusdt.png +0 -0
  110. package/src/assets/tokens/bad.png +0 -0
  111. package/src/assets/tokens/bch.png +0 -0
  112. package/src/assets/tokens/bfit.png +0 -0
  113. package/src/assets/tokens/bnb.png +0 -0
  114. package/src/assets/tokens/btc.png +0 -0
  115. package/src/assets/tokens/cheq.png +0 -0
  116. package/src/assets/tokens/cmdx.png +0 -0
  117. package/src/assets/tokens/cmst.png +0 -0
  118. package/src/assets/tokens/cnto.png +0 -0
  119. package/src/assets/tokens/core.png +0 -0
  120. package/src/assets/tokens/crbrus.png +0 -0
  121. package/src/assets/tokens/cre.png +0 -0
  122. package/src/assets/tokens/cro.png +0 -0
  123. package/src/assets/tokens/cub.png +0 -0
  124. package/src/assets/tokens/dai.png +0 -0
  125. package/src/assets/tokens/default.png +0 -0
  126. package/src/assets/tokens/doge.png +0 -0
  127. package/src/assets/tokens/dot.png +0 -0
  128. package/src/assets/tokens/dvpn.png +0 -0
  129. package/src/assets/tokens/dydx.png +0 -0
  130. package/src/assets/tokens/dym.png +0 -0
  131. package/src/assets/tokens/eth.png +0 -0
  132. package/src/assets/tokens/evmos.png +0 -0
  133. package/src/assets/tokens/flix.png +0 -0
  134. package/src/assets/tokens/frnz.png +0 -0
  135. package/src/assets/tokens/fury.legacy.png +0 -0
  136. package/src/assets/tokens/fury.png +0 -0
  137. package/src/assets/tokens/fuzn.png +0 -0
  138. package/src/assets/tokens/glmr.png +0 -0
  139. package/src/assets/tokens/glto.png +0 -0
  140. package/src/assets/tokens/gpaxg.png +0 -0
  141. package/src/assets/tokens/grav.png +0 -0
  142. package/src/assets/tokens/inj.png +0 -0
  143. package/src/assets/tokens/jkl.png +0 -0
  144. package/src/assets/tokens/juno.png +0 -0
  145. package/src/assets/tokens/kart.png +0 -0
  146. package/src/assets/tokens/kuji.png +0 -0
  147. package/src/assets/tokens/kune.png +0 -0
  148. package/src/assets/tokens/link.png +0 -0
  149. package/src/assets/tokens/loop.png +0 -0
  150. package/src/assets/tokens/ltc.png +0 -0
  151. package/src/assets/tokens/luna.png +0 -0
  152. package/src/assets/tokens/lunc.png +0 -0
  153. package/src/assets/tokens/lvn.png +0 -0
  154. package/src/assets/tokens/mars.png +0 -0
  155. package/src/assets/tokens/mnta.png +0 -0
  156. package/src/assets/tokens/mntl.png +0 -0
  157. package/src/assets/tokens/nami.png +0 -0
  158. package/src/assets/tokens/nausd.png +0 -0
  159. package/src/assets/tokens/nbtc.png +0 -0
  160. package/src/assets/tokens/neok.png +0 -0
  161. package/src/assets/tokens/newt.png +0 -0
  162. package/src/assets/tokens/nstk.png +0 -0
  163. package/src/assets/tokens/ntrn.png +0 -0
  164. package/src/assets/tokens/odin.png +0 -0
  165. package/src/assets/tokens/osmo.png +0 -0
  166. package/src/assets/tokens/pepe.png +0 -0
  167. package/src/assets/tokens/plnk.png +0 -0
  168. package/src/assets/tokens/plq.png +0 -0
  169. package/src/assets/tokens/qcaqla.png +0 -0
  170. package/src/assets/tokens/qcatom.png +0 -0
  171. package/src/assets/tokens/qcfuzn.png +0 -0
  172. package/src/assets/tokens/qckuji.png +0 -0
  173. package/src/assets/tokens/qcmnta.png +0 -0
  174. package/src/assets/tokens/rac.legacy.png +0 -0
  175. package/src/assets/tokens/rac.png +0 -0
  176. package/src/assets/tokens/ratom.png +0 -0
  177. package/src/assets/tokens/regen.png +0 -0
  178. package/src/assets/tokens/rfuzn.png +0 -0
  179. package/src/assets/tokens/rio.png +0 -0
  180. package/src/assets/tokens/rkuji.png +0 -0
  181. package/src/assets/tokens/roar.png +0 -0
  182. package/src/assets/tokens/ruji.png +0 -0
  183. package/src/assets/tokens/rune.png +0 -0
  184. package/src/assets/tokens/sayve.png +0 -0
  185. package/src/assets/tokens/scrt.png +0 -0
  186. package/src/assets/tokens/shd.legacy.png +0 -0
  187. package/src/assets/tokens/shd.png +0 -0
  188. package/src/assets/tokens/silk.png +0 -0
  189. package/src/assets/tokens/sol.png +0 -0
  190. package/src/assets/tokens/somm.png +0 -0
  191. package/src/assets/tokens/stars.png +0 -0
  192. package/src/assets/tokens/statom.png +0 -0
  193. package/src/assets/tokens/stinj.png +0 -0
  194. package/src/assets/tokens/stluna.png +0 -0
  195. package/src/assets/tokens/stosmo.png +0 -0
  196. package/src/assets/tokens/strd.png +0 -0
  197. package/src/assets/tokens/swth.png +0 -0
  198. package/src/assets/tokens/tia.png +0 -0
  199. package/src/assets/tokens/tori.png +0 -0
  200. package/src/assets/tokens/umee.png +0 -0
  201. package/src/assets/tokens/uni.png +0 -0
  202. package/src/assets/tokens/usdc.png +0 -0
  203. package/src/assets/tokens/usk.png +0 -0
  204. package/src/assets/tokens/usk_black.png +0 -0
  205. package/src/assets/tokens/ustc.png +0 -0
  206. package/src/assets/tokens/wavax.png +0 -0
  207. package/src/assets/tokens/wbtc.png +0 -0
  208. package/src/assets/tokens/wftm.png +0 -0
  209. package/src/assets/tokens/whale.png +0 -0
  210. package/src/assets/tokens/whlocal.png +0 -0
  211. package/src/assets/tokens/wink.png +0 -0
  212. package/src/assets/tokens/wmatic.png +0 -0
  213. package/src/assets/tokens/wsteth.png +0 -0
  214. package/src/assets/tokens/wtao.png +0 -0
  215. package/src/assets/tokens/xastro.png +0 -0
  216. package/src/assets/tokens/yieldeth.png +0 -0
  217. package/src/assets/tokens/yum.png +0 -0
  218. package/src/components/buttons/Button.tsx +80 -0
  219. package/src/components/buttons/Popout.tsx +74 -0
  220. package/src/components/buttons/TxButton.tsx +103 -0
  221. package/src/components/buttons/__Popout.tsx +56 -0
  222. package/src/components/cards/Card.tsx +17 -0
  223. package/src/components/cards/GradientCard.tsx +17 -0
  224. package/src/components/footer/Footer.tsx +117 -0
  225. package/src/components/header/Accounts.tsx +1021 -0
  226. package/src/components/header/Header.tsx +745 -0
  227. package/src/components/header/QuickLauncher.tsx +67 -0
  228. package/src/components/header/ResolveLink.tsx +81 -0
  229. package/src/components/icons/IconDenom.tsx +400 -0
  230. package/src/components/icons/Icons.tsx +686 -0
  231. package/src/components/icons/Networks.tsx +687 -0
  232. package/src/components/icons/Wallets.tsx +947 -0
  233. package/src/components/inputs/Checkbox.tsx +35 -0
  234. package/src/components/inputs/DecimalInput.tsx +72 -0
  235. package/src/components/inputs/DenomInput.tsx +65 -0
  236. package/src/components/inputs/DenomSelect.tsx +178 -0
  237. package/src/components/inputs/Input.tsx +66 -0
  238. package/src/components/inputs/Numeric.tsx +49 -0
  239. package/src/components/inputs/Radio.tsx +33 -0
  240. package/src/components/inputs/Select.tsx +69 -0
  241. package/src/components/inputs/Textarea.tsx +65 -0
  242. package/src/components/inputs/Toggle.tsx +38 -0
  243. package/src/components/loader/Loader.tsx +103 -0
  244. package/src/components/logos/RujiraLogo.tsx +83 -0
  245. package/src/components/notices/Warning.tsx +42 -0
  246. package/src/components/numbers/Decimal.tsx +43 -0
  247. package/src/components/numbers/Fiat.tsx +56 -0
  248. package/src/components/progress/Progress.tsx +40 -0
  249. package/src/components/slider/Slider.tsx +12 -0
  250. package/src/context/Affiliate.tsx +65 -0
  251. package/src/context/GlobalModal.tsx +115 -0
  252. package/src/d.ts +4 -0
  253. package/src/helpers/index.ts +73 -0
  254. package/src/helpers/number.ts +12 -0
  255. package/src/hooks/useClickOutside.ts +27 -0
  256. package/src/hooks/useLocalStorage.ts +20 -0
  257. package/src/hooks/useQueryParam.ts +46 -0
  258. package/src/hooks/useWindowSize.ts +26 -0
  259. package/src/i18n/i18n.tsx +102 -0
  260. package/src/i18n/index.ts +1 -0
  261. package/src/index.ts +54 -0
  262. package/src/scss/base/_colors.scss +23 -0
  263. package/src/scss/base/_display.scss +85 -0
  264. package/src/scss/base/_filters.scss +20 -0
  265. package/src/scss/base/_flex.scss +612 -0
  266. package/src/scss/base/_important.scss +3 -0
  267. package/src/scss/base/_normalize.scss +351 -0
  268. package/src/scss/base/_spacing.scss +290 -0
  269. package/src/scss/base/_tooltip.scss +9 -0
  270. package/src/scss/base/_typography.scss +279 -0
  271. package/src/scss/base/_variables.scss +72 -0
  272. package/src/scss/components/_button-group.scss +61 -0
  273. package/src/scss/components/_button.scss +459 -0
  274. package/src/scss/components/_decimal.scss +40 -0
  275. package/src/scss/components/_denom-select.scss +270 -0
  276. package/src/scss/components/_fiat.scss +34 -0
  277. package/src/scss/components/_footer.scss +27 -0
  278. package/src/scss/components/_header.scss +665 -0
  279. package/src/scss/components/_input.scss +82 -0
  280. package/src/scss/components/_loader.scss +20 -0
  281. package/src/scss/components/_modal.scss +138 -0
  282. package/src/scss/components/_numeric-input.scss +98 -0
  283. package/src/scss/components/_popout.scss +63 -0
  284. package/src/scss/components/_progress.scss +62 -0
  285. package/src/scss/components/_radio-checkbox.scss +79 -0
  286. package/src/scss/components/_select.scss +106 -0
  287. package/src/scss/components/_slider.scss +34 -0
  288. package/src/scss/components/_toggle.scss +120 -0
  289. package/src/scss/components/_warning.scss +65 -0
  290. package/src/scss/index.scss +37 -0
  291. package/src/scss/styledcomponents/_card.scss +130 -0
  292. package/src/scss/styledcomponents/_drawer.scss +36 -0
  293. package/src/scss/styledcomponents/_general.scss +20 -0
  294. package/src/scss/styledcomponents/_table.scss +302 -0
  295. package/src/scss/styledcomponents/_tabs.scss +97 -0
  296. package/src/scss/styledcomponents/_tag.scss +155 -0
  297. package/src/scss/unsorted/_general.scss +259 -0
  298. package/src/services/account.ts +53 -0
  299. package/tsconfig.json +25 -0
  300. package/tsconfig.node.json +11 -0
  301. package/vite.config.ts +7 -0
  302. package/vitest-setup.js +1 -0
  303. package/vitest.config.js +13 -0
@@ -0,0 +1,1021 @@
1
+ import clsx from "clsx";
2
+ import { AnimatePresence, motion, useAnimate } from "motion/react";
3
+ import { Dispatch, FC, SetStateAction, useEffect, useState } from "react";
4
+ import toast from "react-hot-toast";
5
+ import {
6
+ Account,
7
+ AccountProvider,
8
+ Network,
9
+ networkLabel,
10
+ Provider,
11
+ } from "rujira.js";
12
+ import { useGlobalModalContext } from "../../context/GlobalModal";
13
+ import { useLocalStorage } from "../../hooks/useLocalStorage";
14
+ import { i18n } from "../../i18n";
15
+ import {
16
+ AngleDown,
17
+ CircleUser,
18
+ Deposit,
19
+ LinkConnect,
20
+ LinkDisconnect,
21
+ Plus,
22
+ Users,
23
+ } from "../icons/Icons";
24
+ import {
25
+ Ctrl,
26
+ Keplr,
27
+ KeplrSimple,
28
+ Leap,
29
+ LeapSimple,
30
+ MetaMask,
31
+ MetaMaskSimple,
32
+ Sonar,
33
+ SonarSimple,
34
+ StationSimple,
35
+ StationText,
36
+ Vultisig,
37
+ VultisigSimple,
38
+ } from "../icons/Wallets";
39
+ import { Fiat } from "../numbers/Fiat";
40
+ import { ResolveLink } from "./ResolveLink";
41
+ import { Button, Input, NetworkIcons } from "../..";
42
+
43
+ interface Group {
44
+ primary: Account;
45
+ other: Account[];
46
+ }
47
+
48
+ let referral = localStorage.getItem("referral");
49
+
50
+ export const Accounts: FC<{
51
+ provider: AccountProvider;
52
+ routingElement: any;
53
+ }> = ({ provider, routingElement }) => {
54
+ const { accounts, disconnectAll, selected } = provider;
55
+ const [hoverBalance, setHoverBalance] = useState(false);
56
+ const [showBlur, setShowBlur] = useState(false);
57
+ const { hideModal, showModal } = useGlobalModalContext();
58
+
59
+ const ConnectModal = () => {
60
+ showModal({
61
+ title: "Connect Your Account",
62
+ backgroundClose: true,
63
+ children: <Wallets modalLayout={true} provider={provider} />,
64
+ });
65
+ };
66
+
67
+ const ReferralModal = () => {
68
+ showModal({
69
+ title: "Refer And Earn",
70
+ backgroundClose: true,
71
+ children: (
72
+ <>
73
+ <p className="p balance">
74
+ Share your unique referral link and earn affiliate fees on trades,
75
+ swaps, and all DeFi interactions.
76
+ </p>
77
+ <div
78
+ className="pointer"
79
+ onClick={() => {
80
+ toast.success("referral copied to clipboard");
81
+ navigator.clipboard.writeText(
82
+ `https://rujira.network/?ref=${referral}`
83
+ );
84
+ }}>
85
+ <Input
86
+ value={`https://rujira.network/?ref=${referral}`}
87
+ readOnly={true}
88
+ label="Click to copy"
89
+ />
90
+ </div>
91
+ <p className="fs-12 color-grey mt-2">
92
+ This referral link is valid until ...
93
+ </p>
94
+ <div className="modal__footer mt-4 px-3 py-2 text-right">
95
+ <Button label="Close" onClick={hideModal} />
96
+ </div>
97
+ </>
98
+ ),
99
+ });
100
+ };
101
+
102
+ const grouped: Group[] = accounts.reduce((a: Group[], v) => {
103
+ const current = a.find((x) => x.primary.provider === v.provider);
104
+ return [
105
+ ...a.filter((x) => x.primary.provider !== v.provider),
106
+ current
107
+ ? // If this is the currently selected provider, the selected account needs to be primary
108
+ selected?.provider === v.provider && selected.network === v.network
109
+ ? { primary: v, other: [...current.other, current.primary] }
110
+ : { ...current, other: [...current.other, v] }
111
+ : { primary: v, other: [] },
112
+ ];
113
+ }, []);
114
+
115
+ return (
116
+ <>
117
+ {accounts.length === 0 ? (
118
+ <ConnectAnimation>
119
+ <Wallets provider={provider} routingElement={routingElement} />
120
+ </ConnectAnimation>
121
+ ) : (
122
+ <MyAccount
123
+ routingElement={routingElement}
124
+ address={accounts[0].address}
125
+ onHide={() => setShowBlur(false)}>
126
+ <ResolveLink
127
+ as={routingElement}
128
+ to="/portfolio"
129
+ className="balance"
130
+ onMouseEnter={() => setHoverBalance(true)}
131
+ onMouseLeave={() => setHoverBalance(false)}>
132
+ <Fiat
133
+ amount={0n}
134
+ symbol="~$"
135
+ padSymbol={false}
136
+ className="color-white fs-26 condensed"
137
+ />
138
+ <p className="fs-12 condensed color-grey mt-0.5">
139
+ {!hoverBalance
140
+ ? i18n.t("Approximate value for selected account")
141
+ : i18n.t("View portfolio")}
142
+ </p>
143
+ </ResolveLink>
144
+ {grouped.map((g) => (
145
+ <Address
146
+ provider={provider}
147
+ key={g.primary.provider}
148
+ account={g.primary}
149
+ other={g.other}
150
+ showBlur={setShowBlur}
151
+ selected={!!selected && selected.provider === g.primary.provider}
152
+ />
153
+ ))}
154
+ <hr className="hr hr--dark mt-1.5 mb-0" />
155
+ <nav className="actions">
156
+ {referral && (
157
+ <button className="action transparent" onClick={ReferralModal}>
158
+ <Users />
159
+ <span>Referral Link</span>
160
+ </button>
161
+ )}
162
+ <button className="action transparent" onClick={ConnectModal}>
163
+ <Plus />
164
+ <span>Connect another account</span>
165
+ </button>
166
+ <button className="action transparent">
167
+ <Deposit />
168
+ <span>Deposit</span>
169
+ </button>
170
+ <button className="action transparent" onClick={disconnectAll}>
171
+ <LinkDisconnect />
172
+ <span>Disconnect all</span>
173
+ </button>
174
+ </nav>
175
+ {showBlur && <div className="blur" />}
176
+ </MyAccount>
177
+ )}
178
+ </>
179
+ );
180
+ };
181
+
182
+ const ConnectAnimation = ({ children }: { children: React.ReactNode }) => {
183
+ const [show, setShow] = useState(false);
184
+ const [scope, animate] = useAnimate();
185
+
186
+ useEffect(() => {
187
+ if (show) {
188
+ animate(scope.current, { width: 98, color: "#ffffff" });
189
+ } else {
190
+ animate(scope.current, {
191
+ width: scope.current.clientHeight,
192
+ color: "#161721",
193
+ });
194
+ }
195
+ }, [show]);
196
+
197
+ return (
198
+ <div
199
+ id="connect-wallet"
200
+ className="relative py-q1"
201
+ onMouseOver={() => setShow(true)}
202
+ onMouseOut={() => setShow(false)}>
203
+ <motion.a
204
+ className="rujira-header__connect"
205
+ ref={scope}
206
+ initial={{ width: 36 }}>
207
+ <AnimatePresence>
208
+ {show && (
209
+ <motion.div
210
+ className="fs-14 fw-500 condensed"
211
+ initial={{ opacity: 0 }}
212
+ animate={{ opacity: 1 }}
213
+ exit={{ opacity: 0 }}>
214
+ {i18n.t("Connect")}
215
+ </motion.div>
216
+ )}
217
+ </AnimatePresence>
218
+ <LinkConnect />
219
+ </motion.a>
220
+ <AnimatePresence>
221
+ {show && (
222
+ <motion.div
223
+ className="rujira-header__popup right condensed w-32 text-center p-2"
224
+ initial={{ opacity: 0, marginTop: -4 }}
225
+ animate={{ opacity: 1, marginTop: 4 }}
226
+ exit={{ opacity: 0, marginTop: -4 }}>
227
+ <i18n.p className="color-white">Connect Your Account</i18n.p>
228
+ <i18n.small className="color-grey mb-1">
229
+ Create or connect an existing account
230
+ </i18n.small>
231
+ {children}
232
+ </motion.div>
233
+ )}
234
+ </AnimatePresence>
235
+ </div>
236
+ );
237
+ };
238
+
239
+ const Wallets: FC<{
240
+ modalLayout?: boolean;
241
+ provider: AccountProvider;
242
+ routingElement?: any;
243
+ }> = ({ modalLayout = false, provider, routingElement }) => {
244
+ const { connect } = provider;
245
+ const { hideModal } = useGlobalModalContext();
246
+ return (
247
+ <div className="rujira-header__wallets">
248
+ <div
249
+ className={clsx({
250
+ "flex w-full ai-c jc-c wrap": true,
251
+ "mb-1": modalLayout,
252
+ })}>
253
+ <button
254
+ id="connect-station"
255
+ className={clsx({
256
+ "transparent block pointer big": true,
257
+ //"desaturate opacity-10": !window.station,
258
+ })}
259
+ //disabled={!window.station}
260
+ onClick={() => {
261
+ connect(Provider.Station).then(hideModal);
262
+ }}>
263
+ <StationText />
264
+ </button>
265
+ </div>
266
+ <div className="flex w-full ai-c jc-c wrap">
267
+ <button
268
+ id="connect-vultisig"
269
+ className={clsx({
270
+ "transparent block pointer": true,
271
+ medium: modalLayout,
272
+ //"desaturate opacity-10": !window.vultisig,
273
+ })}
274
+ //disabled={!window.vultisig}
275
+ onClick={() => {
276
+ connect(Provider.Vultisig).then(hideModal);
277
+ }}>
278
+ <Vultisig />
279
+ </button>
280
+ <button
281
+ className={clsx({
282
+ "transparent block pointer": true,
283
+ medium: modalLayout,
284
+ })}
285
+ onClick={() => {
286
+ connect(Provider.Sonar).then(hideModal);
287
+ }}>
288
+ <Sonar />
289
+ </button>
290
+ <button
291
+ id="connect-keplr"
292
+ className={clsx({
293
+ "transparent block pointer": true,
294
+ medium: modalLayout,
295
+ //"desaturate opacity-10": !window.keplr,
296
+ })}
297
+ //disabled={!window.keplr}
298
+ onClick={() => {
299
+ connect(Provider.Keplr).then(hideModal);
300
+ }}>
301
+ <Keplr />
302
+ </button>
303
+ <button
304
+ id="connect-leap"
305
+ className={clsx({
306
+ "transparent block pointer": true,
307
+ medium: modalLayout,
308
+ // "desaturate opacity-10": !window.leap,
309
+ })}
310
+ //disabled={!window.leap}
311
+ onClick={() => {
312
+ connect(Provider.Leap).then(hideModal);
313
+ }}>
314
+ <Leap />
315
+ </button>
316
+ {!modalLayout && <div className="break" />}
317
+ <button
318
+ id="connect-ctrl"
319
+ className={clsx({
320
+ "transparent block pointer": true,
321
+ medium: modalLayout,
322
+ //"desaturate opacity-10": !window.xfi,
323
+ })}
324
+ //disabled={!window.xfi}
325
+ onClick={() => {
326
+ connect(Provider.Ctrl).then(hideModal);
327
+ }}>
328
+ <Ctrl className="color-white" />
329
+ </button>
330
+ <button
331
+ id="connect-mm"
332
+ className={clsx({
333
+ "transparent block pointer": true,
334
+ medium: modalLayout,
335
+ })}
336
+ onClick={() => {
337
+ connect(Provider.Metamask).then(hideModal);
338
+ }}>
339
+ <MetaMask />
340
+ </button>
341
+ </div>
342
+
343
+ <hr
344
+ className={clsx({
345
+ hr: true,
346
+ "my-1.5": !modalLayout,
347
+ "my-2": modalLayout,
348
+ })}
349
+ />
350
+ <ResolveLink as={routingElement} to="/ecosystem/wallets">
351
+ Need help deciding?
352
+ </ResolveLink>
353
+ </div>
354
+ );
355
+ };
356
+
357
+ const MyAccount = ({
358
+ address,
359
+ children,
360
+ onHide,
361
+ routingElement,
362
+ }: {
363
+ address: string;
364
+ children: React.ReactNode;
365
+ onHide: () => void;
366
+ routingElement?: any;
367
+ }) => {
368
+ const [show, setShow] = useState(false);
369
+ const [avatar] = useLocalStorage("avatar:" + address, "");
370
+
371
+ useEffect(() => {
372
+ onHide();
373
+ }, [show]);
374
+
375
+ return (
376
+ <div
377
+ className="relative py-q1"
378
+ onMouseEnter={() => setShow(true)}
379
+ onMouseLeave={() => setShow(false)}>
380
+ <ResolveLink
381
+ as={routingElement}
382
+ path="/portfolio"
383
+ className="rujira-header__pfp"
384
+ style={
385
+ avatar !== ""
386
+ ? {
387
+ backgroundImage: `url(${avatar})`,
388
+ }
389
+ : {}
390
+ }>
391
+ {avatar === "" && <CircleUser />}
392
+ </ResolveLink>
393
+ <AnimatePresence>
394
+ {show && (
395
+ <motion.div
396
+ className="rujira-header__popup right condensed p-1.5 br-2"
397
+ initial={{ opacity: 0, marginTop: -4, right: -8 }}
398
+ animate={{ opacity: 1, marginTop: 4, right: -8 }}
399
+ exit={{ opacity: 0, marginTop: -4, right: -8 }}>
400
+ {children}
401
+ </motion.div>
402
+ )}
403
+ </AnimatePresence>
404
+ </div>
405
+ );
406
+ };
407
+
408
+ const ProviderIcon: FC<{ provider: Provider; selected: boolean }> = ({
409
+ provider,
410
+ selected,
411
+ }) => {
412
+ switch (provider) {
413
+ case Provider.Keplr:
414
+ return selected ? (
415
+ <Keplr className="address__wallet" />
416
+ ) : (
417
+ <KeplrSimple className="address__wallet" />
418
+ );
419
+ case Provider.Leap:
420
+ return selected ? (
421
+ <Leap className="address__wallet" />
422
+ ) : (
423
+ <LeapSimple className="address__wallet" />
424
+ );
425
+ case Provider.Metamask:
426
+ return selected ? (
427
+ <MetaMask className="address__wallet" />
428
+ ) : (
429
+ <MetaMaskSimple className="address__wallet" />
430
+ );
431
+ case Provider.Sonar:
432
+ return (
433
+ <SonarSimple
434
+ className={clsx({ address__wallet: true, "color-white": selected })}
435
+ />
436
+ );
437
+ case Provider.Station:
438
+ return <StationSimple className="address__wallet" gradient={selected} />;
439
+ case Provider.Vultisig:
440
+ return selected ? (
441
+ <Vultisig className="address__wallet" />
442
+ ) : (
443
+ <VultisigSimple className="address__wallet" />
444
+ );
445
+ case Provider.Ctrl:
446
+ return (
447
+ <Ctrl
448
+ className={clsx({ address__wallet: true, "color-white": selected })}
449
+ />
450
+ );
451
+ }
452
+ };
453
+
454
+ export const NetworkIcon: FC<{ network: Network; className?: string }> = ({
455
+ network,
456
+ className,
457
+ }) => {
458
+ switch (network) {
459
+ case Network.Avalanche:
460
+ return <NetworkIcons.Avalanche className={className} />;
461
+ case Network.Bitcoin:
462
+ return <NetworkIcons.Bitcoin className={className} />;
463
+ case Network.BitcoinCash:
464
+ return <NetworkIcons.BitcoinCash className={className} />;
465
+ case Network.Bsc:
466
+ return <NetworkIcons.BinanceSmartChain className={className} />;
467
+ case Network.Dogecoin:
468
+ return <NetworkIcons.Doge className={className} />;
469
+ case Network.Ethereum:
470
+ return <NetworkIcons.Ethereum className={className} />;
471
+ case Network.Gaia:
472
+ return <NetworkIcons.Cosmos className={className} />;
473
+ case Network.Kujira:
474
+ return <NetworkIcons.Kujira className={className} />;
475
+ case Network.Litecoin:
476
+ return <NetworkIcons.Litecoin className={className} />;
477
+ case Network.Thorchain:
478
+ return <NetworkIcons.Thorchain className={className} />;
479
+ }
480
+ };
481
+
482
+ const Address: FC<{
483
+ account: Account;
484
+ showBlur: Dispatch<SetStateAction<boolean>>;
485
+ selected: boolean;
486
+ other: Account[];
487
+ provider: AccountProvider;
488
+ }> = ({ account, showBlur, selected, other, provider }) => {
489
+ const [showNetworkSelect, setShowNetworkSelect] = useState(false);
490
+ const { select, disconnect } = provider;
491
+
492
+ const networks = [account.network, ...other.map((x) => x.network)].sort(
493
+ (a, b) => a.localeCompare(b)
494
+ );
495
+ return (
496
+ <div
497
+ className={clsx({
498
+ "flex ai-c px-1 address": true,
499
+ "address--selected": selected,
500
+ })}>
501
+ <ProviderIcon provider={account.provider} selected={selected} />
502
+ <p
503
+ className="address__address pointer"
504
+ onClick={() => {
505
+ if (selected) {
506
+ toast.success("address copied to clipboard");
507
+ navigator.clipboard.writeText(account.address);
508
+ } else {
509
+ select(account);
510
+ }
511
+ }}>
512
+ {account.address.substring(0, 10)}...
513
+ {account.address.slice(-12)}
514
+ </p>
515
+ {selected && (
516
+ <div className="relative ml-a mr-1">
517
+ <button
518
+ className="address__network transparent"
519
+ onClick={() => {
520
+ setShowNetworkSelect(true);
521
+ showBlur(true);
522
+ }}>
523
+ <NetworkIcon network={account.network} />
524
+ <AngleDown />
525
+ </button>
526
+ {showNetworkSelect && (
527
+ <div className="address__network-select">
528
+ {networks.map((a) => (
529
+ <button
530
+ key={a}
531
+ className={clsx({
532
+ transparent: true,
533
+ selected: account.network === a,
534
+ })}
535
+ onClick={() => {
536
+ select({ network: a, provider: account.provider });
537
+ showBlur(false);
538
+ setShowNetworkSelect(false);
539
+ }}>
540
+ <NetworkIcon network={a} />
541
+ <span>{networkLabel(a)}</span>
542
+ </button>
543
+ ))}
544
+ </div>
545
+ )}
546
+ </div>
547
+ )}
548
+
549
+ <LinkDisconnect
550
+ className="address__disconnect"
551
+ onClick={() => disconnect(account.provider)}
552
+ />
553
+ </div>
554
+ );
555
+ };
556
+
557
+ /* import { getSnaps } from "@leapwallet/cosmos-snap-provider";
558
+ import clsx from "clsx";
559
+ import {
560
+ AnimatePresence,
561
+ motion,
562
+ useAnimate,
563
+ usePresence,
564
+ } from "motion/react";
565
+ import {
566
+ Decimal,
567
+ GradientButton,
568
+ ResolveLink,
569
+ usePrices,
570
+ } from "kujira-core";
571
+ import { Denom, mulDec } from "kujira.js";
572
+ import { useLocalStorage } from "kujira.ui/src/hooks";
573
+ import * as i18n from "kujira.ui/src/i18n";
574
+ import {
575
+ IconExternal,
576
+ IconSonar,
577
+ IconStation,
578
+ IconXDefi,
579
+ } from "kujira.ui/src/icons";
580
+ import { useEffect, useState } from "react";
581
+ import { toast } from "react-hot-toast";
582
+ import { Link } from "react-router-dom";
583
+ import ReactTooltip from "react-tooltip";
584
+
585
+ import {
586
+ AngleDown,
587
+ AngleUp,
588
+ Chain,
589
+ CircleUser,
590
+ Copy,
591
+ Deposit,
592
+ Disconnect,
593
+ GearHead,
594
+ Key,
595
+ } from "kujira.ui/src/icons/Icons";
596
+ import { useNetwork } from "kujira.ui/src/providers/network";
597
+ import {
598
+ Adapter,
599
+ IWallet,
600
+ useWallet,
601
+ } from "kujira.ui/src/providers/wallet";
602
+ //import { appLink, coinSort } from "kujira.ui/src/utils";
603
+ import MM from "../../../beluga/src/assets/logos/metamask.svg";
604
+ //import IconMetamaskConnected from "kujira.ui/src/assets/metamask_connected.png";
605
+ import { BigNumber } from "ethers";
606
+ import { IconKeplr } from "kujira.ui/src/icons/IconKeplr";
607
+ import { IconLeap } from "kujira.ui/src/icons/IconLeap";
608
+ import { PasskeyModal } from "../../../beluga/src/components/PasskeyModal";
609
+ import {
610
+ MODAL_TYPES,
611
+ useGlobalModalContext,
612
+ } from "../../../beluga/src/context/GlobalModal";
613
+
614
+ export function Account({
615
+ adapter = useWallet,
616
+ }: {
617
+ adapter?: () => Pick<
618
+ IWallet,
619
+ | "account"
620
+ | "adapter"
621
+ | "balance"
622
+ | "balances"
623
+ | "chainInfo"
624
+ | "connect"
625
+ | "delegations"
626
+ | "disconnect"
627
+ >;
628
+ }) {
629
+ const {
630
+ adapter: a,
631
+ account,
632
+ balances,
633
+ connect,
634
+ delegations,
635
+ disconnect,
636
+ } = adapter();
637
+ const [showWalletSelect] = useState(false);
638
+ const [showAdvanced, setShowAdvanced] = useState(true);
639
+ const [hoverBalance, setHoverBalance] = useState(false);
640
+ const [createModal, setCreateModal] = useState(false);
641
+ const [{ chainInfo }] = useNetwork();
642
+ const [snapsSupported, setSnapsSupported] = useLocalStorage(
643
+ "snaps",
644
+ ""
645
+ );
646
+ const { price, fetchPrices } = usePrices();
647
+ const { showModal } = useGlobalModalContext();
648
+
649
+ useEffect(() => {
650
+ getSnaps()
651
+ .then(() => {
652
+ setSnapsSupported("y");
653
+ })
654
+ .catch(() => {
655
+ setSnapsSupported("");
656
+ });
657
+
658
+ const listener = makeKeySequenceListener(
659
+ cheatcode,
660
+ () => connect && connect(Adapter.ReadOnly)
661
+ );
662
+ window.addEventListener("keyup", listener);
663
+ return () => window.removeEventListener("keyup", listener);
664
+ }, []);
665
+
666
+ useEffect(() => {
667
+ if (account && balances.length > 0) {
668
+ fetchPrices(balances.map((b) => b.denom || ""));
669
+ }
670
+ }, [balances]);
671
+
672
+ useEffect(() => {
673
+ ReactTooltip.rebuild();
674
+ }, [showWalletSelect]);
675
+
676
+ if (account) {
677
+ const delegated = delegations?.reduce(
678
+ (a, v) =>
679
+ v.balance?.amount ? parseInt(v.balance.amount) + a : a,
680
+ 0
681
+ );
682
+
683
+ const totalUSD = balances.reduce((a, b) => {
684
+ const total = BigNumber.from(b.amount).div(
685
+ BigNumber.from(10).pow(Denom.from(b.denom).decimals - 6)
686
+ );
687
+ return a.add(mulDec(total, price(b.denom)));
688
+ }, mulDec(BigNumber.from(delegated || 0), price("ukuji")));
689
+
690
+ return (
691
+ <>
692
+ <MyAccount address={account.address}>
693
+ <div
694
+ className="action flex ai-c"
695
+ onClick={() => {
696
+ navigator.clipboard.writeText(account.address || "");
697
+ toast.success(i18n.t("Copied address to clipboard"));
698
+ }}>
699
+ <div className="fs-12 fw-500">
700
+ {account.address.substring(0, 14)}...
701
+ {account.address.slice(-14)}
702
+ </div>
703
+ <div className="ml-a mr-0">
704
+ <Copy />
705
+ </div>
706
+ </div>
707
+ <ResolveLink
708
+ onMouseEnter={() => setHoverBalance(true)}
709
+ onMouseLeave={() => setHoverBalance(false)}
710
+ app=""
711
+ path="/portfolio"
712
+ className="balance mt-2 text-center">
713
+ <Decimal
714
+ labelLeft={true}
715
+ label="~$"
716
+ amount={totalUSD}
717
+ round={2}
718
+ decimals={6}
719
+ className="fs-28 mono fw-400"
720
+ />
721
+ <span className="fs-11 fw-500 condensed mt-q1 msg">
722
+ {!hoverBalance
723
+ ? i18n.t("Approximate value")
724
+ : i18n.t("View portfolio")}
725
+ </span>
726
+ </ResolveLink>
727
+
728
+ <div
729
+ className="btn flex ai-c mt-1"
730
+ onClick={() => showModal(MODAL_TYPES.DEPOSIT_MODAL)}>
731
+ <div className="w-q5">
732
+ <Deposit className="mx-a" />
733
+ </div>
734
+ <div className="fs-14 ml-1 fw-500">Deposit</div>
735
+ </div>
736
+
737
+ <div className="btn flex ai-c" onClick={() => disconnect()}>
738
+ <div className="w-q5">
739
+ <Disconnect className="mx-a" />
740
+ </div>
741
+ <div className="fs-14 ml-1 fw-500">Disconnect</div>
742
+ </div>
743
+ </MyAccount>
744
+ </>
745
+ );
746
+ }
747
+ return (
748
+ <>
749
+ <ConnectAnimation>
750
+ <GradientButton
751
+ className="md-button--grey md-button--icon-left md-button--full mt-q3"
752
+ onClick={() => setCreateModal(true)}>
753
+ <Key />
754
+ <i18n.span>Connect with Passkey</i18n.span>
755
+ </GradientButton>
756
+ <a
757
+ href="https://winkhub.app/posts/the-world-of-passkeys"
758
+ target="_blank"
759
+ className="fs-12 fw-500 mt-1 color-grey hover-teal no-underline md-flex ai-c w-full jc-c">
760
+ Find out more about Passkey
761
+ <IconExternal className="w-1 h-a ml-q1" />
762
+ </a>
763
+ <hr className="hr mt-2 mb-q3" />
764
+ <a
765
+ onClick={() => setShowAdvanced(!showAdvanced)}
766
+ className="fs-14 md-flex w-full ai-c jc-c cursor">
767
+ {showAdvanced ? (
768
+ <AngleUp className="block mr-1 w-a h-2" />
769
+ ) : (
770
+ <AngleDown className="block mr-1 w-a h-2" />
771
+ )}
772
+ {i18n.t(
773
+ `${showAdvanced ? "Hide" : "Show"} advanced options`
774
+ )}
775
+ </a>
776
+ {showAdvanced && (
777
+ <div className="rujira-header-wallets">
778
+ <button
779
+ className="transparent block pointer sonar"
780
+ onClick={() => {
781
+ connect && connect(Adapter.Sonar);
782
+ }}>
783
+ <IconSonar />
784
+ </button>
785
+
786
+ <div className="md-flex w-full ai-c jc-c wrap">
787
+ <button
788
+ id="connect-keplr"
789
+ className={clsx({
790
+ "transparent block pointer": true,
791
+ "desaturate opacity-10": !window.keplr,
792
+ })}
793
+ disabled={!window.keplr}
794
+ onClick={() => {
795
+ connect && connect(Adapter.Keplr);
796
+ }}>
797
+ <IconKeplr />
798
+ </button>
799
+
800
+ <button
801
+ id="connect-leap"
802
+ className={clsx({
803
+ "transparent block pointer": true,
804
+ "desaturate opacity-10": !window.leap,
805
+ })}
806
+ disabled={!window.leap}
807
+ onClick={() => {
808
+ connect && connect(Adapter.Leap);
809
+ }}>
810
+ <IconLeap />
811
+ </button>
812
+
813
+ <button
814
+ id="connect-station"
815
+ className={clsx({
816
+ "transparent block pointer": true,
817
+ "desaturate opacity-10": !window.station,
818
+ })}
819
+ disabled={!window.station}
820
+ onClick={() => {
821
+ connect && connect(Adapter.Station);
822
+ }}>
823
+ <IconStation />
824
+ </button>
825
+ <button
826
+ id="connect-xdefi"
827
+ className={clsx({
828
+ "transparent block pointer": true,
829
+ "desaturate opacity-10": !window.xfi,
830
+ })}
831
+ disabled={!window.xfi}
832
+ onClick={() => {
833
+ connect && connect(Adapter.Xfi);
834
+ }}>
835
+ <IconXDefi />
836
+ </button>
837
+ <button
838
+ id="connect-mm"
839
+ className={clsx({
840
+ "transparent block pointer": true,
841
+ "desaturate opacity-10": true,
842
+ })}
843
+ disabled={true}>
844
+ <img src={MM} alt="MetaMask Logo" />
845
+ </button>
846
+ </div>
847
+
848
+ <hr className="hr my-q3" />
849
+ <ResolveLink app="" path="/ecosystem/wallets">
850
+ Need help deciding?
851
+ </ResolveLink>
852
+ </div>
853
+ )}
854
+ </ConnectAnimation>
855
+
856
+ <PasskeyModal
857
+ close={() => setCreateModal(false)}
858
+ show={createModal}
859
+ direct
860
+ />
861
+ </>
862
+ );
863
+ }
864
+
865
+ const MyAccount = ({
866
+ address,
867
+ children,
868
+ }: {
869
+ address: string;
870
+ children: React.ReactNode;
871
+ }) => {
872
+ const [show, setShow] = useState(false);
873
+ const [avatar, setAvatar] = useLocalStorage(
874
+ "avatar:" + address,
875
+ ""
876
+ );
877
+
878
+ return (
879
+ <div
880
+ className="relative py-q1"
881
+ onMouseEnter={() => setShow(true)}
882
+ onMouseLeave={() => setShow(false)}>
883
+ <Link
884
+ to="/portfolio"
885
+ className="rujira-header-pfp"
886
+ style={
887
+ avatar !== ""
888
+ ? {
889
+ backgroundImage: `url(${avatar})`,
890
+ }
891
+ : {}
892
+ }>
893
+ {avatar === "" && <CircleUser />}
894
+ </Link>
895
+ <AnimatePresence>
896
+ {show && (
897
+ <motion.div
898
+ className="rujira-header-popup right condensed w-28"
899
+ initial={{ opacity: 0, marginTop: -4 }}
900
+ animate={{ opacity: 1, marginTop: 0 }}
901
+ exit={{ opacity: 0, marginTop: -4 }}>
902
+ {children}
903
+ </motion.div>
904
+ )}
905
+ </AnimatePresence>
906
+ </div>
907
+ );
908
+ };
909
+
910
+ const ConnectAnimation = ({
911
+ children,
912
+ }: {
913
+ children: React.ReactNode;
914
+ }) => {
915
+ const [show, setShow] = useState(false);
916
+ const [scope, animate] = useAnimate();
917
+
918
+ useEffect(() => {
919
+ if (show) {
920
+ animate(scope.current, { width: 98, color: "#ffffff" });
921
+ } else {
922
+ animate(scope.current, {
923
+ width: scope.current.clientHeight,
924
+ color: "#161721",
925
+ });
926
+ }
927
+ }, [show]);
928
+
929
+ return (
930
+ <div
931
+ id="connect-wallet"
932
+ className="relative py-q1"
933
+ onMouseOver={() => setShow(true)}
934
+ onMouseOut={() => setShow(false)}>
935
+ <motion.a className="rujira-header-connect" ref={scope}>
936
+ <AnimatePresence>
937
+ {show && (
938
+ <motion.div
939
+ className="fs-14 fw-500 condensed"
940
+ initial={{ opacity: 0 }}
941
+ animate={{ opacity: 1 }}
942
+ exit={{ opacity: 0 }}>
943
+ {i18n.t("Connect")}
944
+ </motion.div>
945
+ )}
946
+ </AnimatePresence>
947
+ <Chain />
948
+ </motion.a>
949
+ <AnimatePresence>
950
+ {show && <DropDown>{children}</DropDown>}
951
+ </AnimatePresence>
952
+ </div>
953
+ );
954
+ };
955
+
956
+ const DropDown = ({ children }: { children: React.ReactNode }) => {
957
+ const [isPresent, safeToRemove] = usePresence();
958
+
959
+ useEffect(() => {
960
+ !isPresent && setTimeout(safeToRemove, 250);
961
+ }, [isPresent]);
962
+
963
+ return (
964
+ <motion.div
965
+ className="rujira-header-popup right condensed w-32"
966
+ initial={{ opacity: 0, marginTop: -4 }}
967
+ animate={{ opacity: 1, marginTop: 0 }}
968
+ exit={{ opacity: 0, marginTop: -4 }}>
969
+ <i18n.p>Connect Your Account</i18n.p>
970
+ <i18n.small className="color-grey mb-1">
971
+ Create or connect an existing account
972
+ </i18n.small>
973
+ {children}
974
+ </motion.div>
975
+ );
976
+ };
977
+
978
+ const cheatcode = [
979
+ {
980
+ key: "i",
981
+ timeToNextKey: 1000,
982
+ },
983
+ {
984
+ key: "d",
985
+ timeToNextKey: 1000,
986
+ },
987
+ {
988
+ key: "d",
989
+ timeToNextKey: 1000,
990
+ },
991
+ {
992
+ key: "q",
993
+ timeToNextKey: 1000,
994
+ },
995
+ {
996
+ key: "d",
997
+ },
998
+ ];
999
+
1000
+ const makeKeySequenceListener = (keySequence, callback) => {
1001
+ let index = 0;
1002
+ let timeoutId;
1003
+
1004
+ return (e) => {
1005
+ const { key, timeToNextKey } = keySequence[index];
1006
+ if (e.key === key) {
1007
+ if (index === keySequence.length - 1) {
1008
+ callback();
1009
+ }
1010
+ clearTimeout(timeoutId);
1011
+ if (timeToNextKey) {
1012
+ timeoutId = setTimeout(() => (index = 0), timeToNextKey);
1013
+ }
1014
+ index = (index + 1) % keySequence.length;
1015
+ } else {
1016
+ index = 0;
1017
+ clearTimeout(timeoutId);
1018
+ }
1019
+ };
1020
+ };
1021
+ */